blob: fc34591fcd30422e5a3a99cb6ef3648e55d547ab [file] [log] [blame]
robertphillips@google.com58b20212012-06-27 20:44:52 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrSWMaskHelper.h"
9#include "GrDrawState.h"
10#include "GrGpu.h"
11
sugoi@google.com5f74cf82012-12-17 21:16:45 +000012#include "SkStrokeRec.h"
sugoi@google.com12b4e272012-12-06 20:13:11 +000013
robertphillips@google.com58b20212012-06-27 20:44:52 +000014// TODO: try to remove this #include
15#include "GrContext.h"
16
17namespace {
18/*
19 * Convert a boolean operation into a transfer mode code
20 */
21SkXfermode::Mode op_to_mode(SkRegion::Op op) {
22
23 static const SkXfermode::Mode modeMap[] = {
24 SkXfermode::kDstOut_Mode, // kDifference_Op
reed@google.com8d3cd7a2013-01-30 21:36:11 +000025 SkXfermode::kModulate_Mode, // kIntersect_Op
robertphillips@google.com58b20212012-06-27 20:44:52 +000026 SkXfermode::kSrcOver_Mode, // kUnion_Op
27 SkXfermode::kXor_Mode, // kXOR_Op
28 SkXfermode::kClear_Mode, // kReverseDifference_Op
29 SkXfermode::kSrc_Mode, // kReplace_Op
30 };
31
32 return modeMap[op];
33}
34
robertphillips@google.com58b20212012-06-27 20:44:52 +000035}
36
37/**
38 * Draw a single rect element of the clip stack into the accumulation bitmap
39 */
rmistry@google.comfbfcd562012-08-23 18:09:54 +000040void GrSWMaskHelper::draw(const GrRect& rect, SkRegion::Op op,
robertphillips@google.com366f1c62012-06-29 21:38:47 +000041 bool antiAlias, uint8_t alpha) {
robertphillips@google.com58b20212012-06-27 20:44:52 +000042 SkPaint paint;
43
44 SkXfermode* mode = SkXfermode::Create(op_to_mode(op));
45
46 paint.setXfermode(mode);
47 paint.setAntiAlias(antiAlias);
robertphillips@google.com366f1c62012-06-29 21:38:47 +000048 paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
robertphillips@google.com58b20212012-06-27 20:44:52 +000049
robertphillips@google.com366f1c62012-06-29 21:38:47 +000050 fDraw.drawRect(rect, paint);
robertphillips@google.com58b20212012-06-27 20:44:52 +000051
52 SkSafeUnref(mode);
53}
54
55/**
56 * Draw a single path element of the clip stack into the accumulation bitmap
57 */
sugoi@google.com5f74cf82012-12-17 21:16:45 +000058void GrSWMaskHelper::draw(const SkPath& path, const SkStrokeRec& stroke, SkRegion::Op op,
sugoi@google.com12b4e272012-12-06 20:13:11 +000059 bool antiAlias, uint8_t alpha) {
robertphillips@google.com58b20212012-06-27 20:44:52 +000060
61 SkPaint paint;
sugoi@google.com5f74cf82012-12-17 21:16:45 +000062 if (stroke.isHairlineStyle()) {
robertphillips@google.com58b20212012-06-27 20:44:52 +000063 paint.setStyle(SkPaint::kStroke_Style);
64 paint.setStrokeWidth(SK_Scalar1);
65 } else {
sugoi@google.com5f74cf82012-12-17 21:16:45 +000066 if (stroke.isFillStyle()) {
sugoi@google.com12b4e272012-12-06 20:13:11 +000067 paint.setStyle(SkPaint::kFill_Style);
68 } else {
69 paint.setStyle(SkPaint::kStroke_Style);
70 paint.setStrokeJoin(stroke.getJoin());
71 paint.setStrokeCap(stroke.getCap());
sugoi@google.com5f74cf82012-12-17 21:16:45 +000072 paint.setStrokeWidth(stroke.getWidth());
robertphillips@google.com58b20212012-06-27 20:44:52 +000073 }
74 }
sugoi@google.com5f74cf82012-12-17 21:16:45 +000075
robertphillips@google.com58b20212012-06-27 20:44:52 +000076 SkXfermode* mode = SkXfermode::Create(op_to_mode(op));
77
78 paint.setXfermode(mode);
79 paint.setAntiAlias(antiAlias);
robertphillips@google.com366f1c62012-06-29 21:38:47 +000080 paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
robertphillips@google.com58b20212012-06-27 20:44:52 +000081
sugoi@google.com12b4e272012-12-06 20:13:11 +000082 fDraw.drawPath(path, paint);
robertphillips@google.com58b20212012-06-27 20:44:52 +000083
84 SkSafeUnref(mode);
85}
86
rmistry@google.comfbfcd562012-08-23 18:09:54 +000087bool GrSWMaskHelper::init(const GrIRect& resultBounds,
bsalomon@google.comb9086a02012-11-01 18:02:54 +000088 const SkMatrix* matrix) {
robertphillips@google.com366f1c62012-06-29 21:38:47 +000089 if (NULL != matrix) {
90 fMatrix = *matrix;
robertphillips@google.com58b20212012-06-27 20:44:52 +000091 } else {
92 fMatrix.setIdentity();
93 }
94
robertphillips@google.com366f1c62012-06-29 21:38:47 +000095 // Now translate so the bound's UL corner is at the origin
96 fMatrix.postTranslate(-resultBounds.fLeft * SK_Scalar1,
97 -resultBounds.fTop * SK_Scalar1);
98 GrIRect bounds = GrIRect::MakeWH(resultBounds.width(),
99 resultBounds.height());
robertphillips@google.com58b20212012-06-27 20:44:52 +0000100
101 fBM.setConfig(SkBitmap::kA8_Config, bounds.fRight, bounds.fBottom);
102 if (!fBM.allocPixels()) {
103 return false;
104 }
105 sk_bzero(fBM.getPixels(), fBM.getSafeSize());
106
107 sk_bzero(&fDraw, sizeof(fDraw));
108 fRasterClip.setRect(bounds);
109 fDraw.fRC = &fRasterClip;
110 fDraw.fClip = &fRasterClip.bwRgn();
111 fDraw.fMatrix = &fMatrix;
112 fDraw.fBitmap = &fBM;
113 return true;
114}
115
116/**
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000117 * Get a texture (from the texture cache) of the correct size & format.
118 * Return true on success; false on failure.
robertphillips@google.com58b20212012-06-27 20:44:52 +0000119 */
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000120bool GrSWMaskHelper::getTexture(GrAutoScratchTexture* texture) {
robertphillips@google.com58b20212012-06-27 20:44:52 +0000121 GrTextureDesc desc;
122 desc.fWidth = fBM.width();
123 desc.fHeight = fBM.height();
124 desc.fConfig = kAlpha_8_GrPixelConfig;
125
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000126 texture->set(fContext, desc);
127 return NULL != texture->texture();
robertphillips@google.com58b20212012-06-27 20:44:52 +0000128}
129
130/**
131 * Move the result of the software mask generation back to the gpu
132 */
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000133void GrSWMaskHelper::toTexture(GrTexture *texture, uint8_t alpha) {
robertphillips@google.com58b20212012-06-27 20:44:52 +0000134 SkAutoLockPixels alp(fBM);
135
136 // The destination texture is almost always larger than "fBM". Clear
137 // it appropriately so we don't get mask artifacts outside of the path's
138 // bounding box
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000139
robertphillips@google.com58b20212012-06-27 20:44:52 +0000140 // "texture" needs to be installed as the render target for the clear
141 // and the texture upload but cannot remain the render target upon
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000142 // return. Callers typically use it as a texture and it would then
robertphillips@google.com58b20212012-06-27 20:44:52 +0000143 // be both source and dest.
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000144 GrDrawState::AutoRenderTargetRestore artr(fContext->getGpu()->drawState(),
robertphillips@google.com58b20212012-06-27 20:44:52 +0000145 texture->asRenderTarget());
146
bsalomon@google.com4c2443e2012-12-06 20:58:57 +0000147 fContext->getGpu()->clear(NULL, GrColorPackRGBA(alpha, alpha, alpha, alpha));
robertphillips@google.com58b20212012-06-27 20:44:52 +0000148
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000149 texture->writePixels(0, 0, fBM.width(), fBM.height(),
robertphillips@google.com58b20212012-06-27 20:44:52 +0000150 kAlpha_8_GrPixelConfig,
151 fBM.getPixels(), fBM.rowBytes());
152}
153
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000154////////////////////////////////////////////////////////////////////////////////
155/**
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000156 * Software rasterizes path to A8 mask (possibly using the context's matrix)
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000157 * and uploads the result to a scratch texture. Returns the resulting
158 * texture on success; NULL on failure.
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000159 */
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000160GrTexture* GrSWMaskHelper::DrawPathMaskToTexture(GrContext* context,
161 const SkPath& path,
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000162 const SkStrokeRec& stroke,
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000163 const GrIRect& resultBounds,
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000164 bool antiAlias,
bsalomon@google.comb9086a02012-11-01 18:02:54 +0000165 SkMatrix* matrix) {
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000166 GrAutoScratchTexture ast;
167
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000168 GrSWMaskHelper helper(context);
169
170 if (!helper.init(resultBounds, matrix)) {
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000171 return NULL;
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000172 }
173
sugoi@google.com12b4e272012-12-06 20:13:11 +0000174 helper.draw(path, stroke, SkRegion::kReplace_Op, antiAlias, 0xFF);
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000175
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000176 if (!helper.getTexture(&ast)) {
177 return NULL;
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000178 }
179
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000180 helper.toTexture(ast.texture(), 0x00);
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000181
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000182 return ast.detach();
183}
184
185void GrSWMaskHelper::DrawToTargetWithPathMask(GrTexture* texture,
186 GrDrawTarget* target,
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000187 const GrIRect& rect) {
188 GrDrawState* drawState = target->drawState();
189
bsalomon@google.com137f1342013-05-29 21:27:53 +0000190 GrDrawState::AutoViewMatrixRestore avmr;
191 if (!avmr.setIdentity(drawState)) {
bsalomon@google.come3d32162012-07-20 13:37:06 +0000192 return;
193 }
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000194 enum {
195 // the SW path renderer shares this stage with glyph
commit-bot@chromium.orgff6ea262013-03-12 12:26:08 +0000196 // rendering (kGlyphMaskStage in GrTextContext)
197 // && edge rendering (kEdgeEffectStage in GrContext)
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000198 kPathMaskStage = GrPaint::kTotalStages,
199 };
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000200
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000201 GrRect dstRect = GrRect::MakeLTRB(
202 SK_Scalar1 * rect.fLeft,
203 SK_Scalar1 * rect.fTop,
204 SK_Scalar1 * rect.fRight,
205 SK_Scalar1 * rect.fBottom);
bsalomon@google.comc7818882013-03-20 19:19:53 +0000206
207 // We want to use device coords to compute the texture coordinates. We set our matrix to be
208 // equal to the view matrix followed by a translation so that the top-left of the device bounds
209 // maps to 0,0, and then a scaling matrix to normalized coords. We apply this matrix to the
210 // vertex positions rather than local coords.
211 SkMatrix maskMatrix;
212 maskMatrix.setIDiv(texture->width(), texture->height());
213 maskMatrix.preTranslate(SkIntToScalar(-rect.fLeft), SkIntToScalar(-rect.fTop));
214 maskMatrix.preConcat(drawState->getViewMatrix());
215
216 GrAssert(!drawState->isStageEnabled(kPathMaskStage));
217 drawState->setEffect(kPathMaskStage,
218 GrSimpleTextureEffect::Create(texture,
219 maskMatrix,
220 false,
221 GrEffect::kPosition_CoordsType))->unref();
222
223 target->drawSimpleRect(dstRect);
tomhudson@google.com676e6602012-07-10 17:21:48 +0000224 drawState->disableStage(kPathMaskStage);
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000225}