|
1 /* |
|
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 "GrDrawTargetCaps.h" |
|
11 #include "GrGpu.h" |
|
12 |
|
13 #include "SkStrokeRec.h" |
|
14 |
|
15 // TODO: try to remove this #include |
|
16 #include "GrContext.h" |
|
17 |
|
18 namespace { |
|
19 /* |
|
20 * Convert a boolean operation into a transfer mode code |
|
21 */ |
|
22 SkXfermode::Mode op_to_mode(SkRegion::Op op) { |
|
23 |
|
24 static const SkXfermode::Mode modeMap[] = { |
|
25 SkXfermode::kDstOut_Mode, // kDifference_Op |
|
26 SkXfermode::kModulate_Mode, // kIntersect_Op |
|
27 SkXfermode::kSrcOver_Mode, // kUnion_Op |
|
28 SkXfermode::kXor_Mode, // kXOR_Op |
|
29 SkXfermode::kClear_Mode, // kReverseDifference_Op |
|
30 SkXfermode::kSrc_Mode, // kReplace_Op |
|
31 }; |
|
32 |
|
33 return modeMap[op]; |
|
34 } |
|
35 |
|
36 } |
|
37 |
|
38 /** |
|
39 * Draw a single rect element of the clip stack into the accumulation bitmap |
|
40 */ |
|
41 void GrSWMaskHelper::draw(const SkRect& rect, SkRegion::Op op, |
|
42 bool antiAlias, uint8_t alpha) { |
|
43 SkPaint paint; |
|
44 |
|
45 SkXfermode* mode = SkXfermode::Create(op_to_mode(op)); |
|
46 |
|
47 paint.setXfermode(mode); |
|
48 paint.setAntiAlias(antiAlias); |
|
49 paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha)); |
|
50 |
|
51 fDraw.drawRect(rect, paint); |
|
52 |
|
53 SkSafeUnref(mode); |
|
54 } |
|
55 |
|
56 /** |
|
57 * Draw a single path element of the clip stack into the accumulation bitmap |
|
58 */ |
|
59 void GrSWMaskHelper::draw(const SkPath& path, const SkStrokeRec& stroke, SkRegion::Op op, |
|
60 bool antiAlias, uint8_t alpha) { |
|
61 |
|
62 SkPaint paint; |
|
63 if (stroke.isHairlineStyle()) { |
|
64 paint.setStyle(SkPaint::kStroke_Style); |
|
65 paint.setStrokeWidth(SK_Scalar1); |
|
66 } else { |
|
67 if (stroke.isFillStyle()) { |
|
68 paint.setStyle(SkPaint::kFill_Style); |
|
69 } else { |
|
70 paint.setStyle(SkPaint::kStroke_Style); |
|
71 paint.setStrokeJoin(stroke.getJoin()); |
|
72 paint.setStrokeCap(stroke.getCap()); |
|
73 paint.setStrokeWidth(stroke.getWidth()); |
|
74 } |
|
75 } |
|
76 paint.setAntiAlias(antiAlias); |
|
77 |
|
78 if (SkRegion::kReplace_Op == op && 0xFF == alpha) { |
|
79 SkASSERT(0xFF == paint.getAlpha()); |
|
80 fDraw.drawPathCoverage(path, paint); |
|
81 } else { |
|
82 paint.setXfermodeMode(op_to_mode(op)); |
|
83 paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha)); |
|
84 fDraw.drawPath(path, paint); |
|
85 } |
|
86 } |
|
87 |
|
88 bool GrSWMaskHelper::init(const SkIRect& resultBounds, |
|
89 const SkMatrix* matrix) { |
|
90 if (NULL != matrix) { |
|
91 fMatrix = *matrix; |
|
92 } else { |
|
93 fMatrix.setIdentity(); |
|
94 } |
|
95 |
|
96 // Now translate so the bound's UL corner is at the origin |
|
97 fMatrix.postTranslate(-resultBounds.fLeft * SK_Scalar1, |
|
98 -resultBounds.fTop * SK_Scalar1); |
|
99 SkIRect bounds = SkIRect::MakeWH(resultBounds.width(), |
|
100 resultBounds.height()); |
|
101 |
|
102 if (!fBM.allocPixels(SkImageInfo::MakeA8(bounds.fRight, bounds.fBottom))) { |
|
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 /** |
|
117 * Get a texture (from the texture cache) of the correct size & format. |
|
118 * Return true on success; false on failure. |
|
119 */ |
|
120 bool GrSWMaskHelper::getTexture(GrAutoScratchTexture* texture) { |
|
121 GrTextureDesc desc; |
|
122 desc.fWidth = fBM.width(); |
|
123 desc.fHeight = fBM.height(); |
|
124 desc.fConfig = kAlpha_8_GrPixelConfig; |
|
125 |
|
126 texture->set(fContext, desc); |
|
127 return NULL != texture->texture(); |
|
128 } |
|
129 |
|
130 /** |
|
131 * Move the result of the software mask generation back to the gpu |
|
132 */ |
|
133 void GrSWMaskHelper::toTexture(GrTexture *texture) { |
|
134 SkAutoLockPixels alp(fBM); |
|
135 |
|
136 // If we aren't reusing scratch textures we don't need to flush before |
|
137 // writing since no one else will be using 'texture' |
|
138 bool reuseScratch = fContext->getGpu()->caps()->reuseScratchTextures(); |
|
139 |
|
140 // Since we're uploading to it, 'texture' shouldn't have a render target. |
|
141 SkASSERT(NULL == texture->asRenderTarget()); |
|
142 |
|
143 texture->writePixels(0, 0, fBM.width(), fBM.height(), |
|
144 kAlpha_8_GrPixelConfig, |
|
145 fBM.getPixels(), fBM.rowBytes(), |
|
146 reuseScratch ? 0 : GrContext::kDontFlush_PixelOpsFlag); |
|
147 } |
|
148 |
|
149 //////////////////////////////////////////////////////////////////////////////// |
|
150 /** |
|
151 * Software rasterizes path to A8 mask (possibly using the context's matrix) |
|
152 * and uploads the result to a scratch texture. Returns the resulting |
|
153 * texture on success; NULL on failure. |
|
154 */ |
|
155 GrTexture* GrSWMaskHelper::DrawPathMaskToTexture(GrContext* context, |
|
156 const SkPath& path, |
|
157 const SkStrokeRec& stroke, |
|
158 const SkIRect& resultBounds, |
|
159 bool antiAlias, |
|
160 SkMatrix* matrix) { |
|
161 GrAutoScratchTexture ast; |
|
162 |
|
163 GrSWMaskHelper helper(context); |
|
164 |
|
165 if (!helper.init(resultBounds, matrix)) { |
|
166 return NULL; |
|
167 } |
|
168 |
|
169 helper.draw(path, stroke, SkRegion::kReplace_Op, antiAlias, 0xFF); |
|
170 |
|
171 if (!helper.getTexture(&ast)) { |
|
172 return NULL; |
|
173 } |
|
174 |
|
175 helper.toTexture(ast.texture()); |
|
176 |
|
177 return ast.detach(); |
|
178 } |
|
179 |
|
180 void GrSWMaskHelper::DrawToTargetWithPathMask(GrTexture* texture, |
|
181 GrDrawTarget* target, |
|
182 const SkIRect& rect) { |
|
183 GrDrawState* drawState = target->drawState(); |
|
184 |
|
185 GrDrawState::AutoViewMatrixRestore avmr; |
|
186 if (!avmr.setIdentity(drawState)) { |
|
187 return; |
|
188 } |
|
189 GrDrawState::AutoRestoreEffects are(drawState); |
|
190 |
|
191 SkRect dstRect = SkRect::MakeLTRB(SK_Scalar1 * rect.fLeft, |
|
192 SK_Scalar1 * rect.fTop, |
|
193 SK_Scalar1 * rect.fRight, |
|
194 SK_Scalar1 * rect.fBottom); |
|
195 |
|
196 // We want to use device coords to compute the texture coordinates. We set our matrix to be |
|
197 // equal to the view matrix followed by a translation so that the top-left of the device bounds |
|
198 // maps to 0,0, and then a scaling matrix to normalized coords. We apply this matrix to the |
|
199 // vertex positions rather than local coords. |
|
200 SkMatrix maskMatrix; |
|
201 maskMatrix.setIDiv(texture->width(), texture->height()); |
|
202 maskMatrix.preTranslate(SkIntToScalar(-rect.fLeft), SkIntToScalar(-rect.fTop)); |
|
203 maskMatrix.preConcat(drawState->getViewMatrix()); |
|
204 |
|
205 drawState->addCoverageEffect( |
|
206 GrSimpleTextureEffect::Create(texture, |
|
207 maskMatrix, |
|
208 GrTextureParams::kNone_FilterMode, |
|
209 kPosition_GrCoordSet))->unref(); |
|
210 |
|
211 target->drawSimpleRect(dstRect); |
|
212 } |