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
10 #include "GrCaps.h"
11 #include "GrContext.h"
12 #include "batches/GrDrawBatch.h"
13 #include "GrDrawContext.h"
14 #include "GrPipelineBuilder.h"
15 #include "GrShape.h"
16
17 #include "SkDistanceFieldGen.h"
18
19 #include "batches/GrRectBatchFactory.h"
20
21 /*
22 * Convert a boolean operation into a transfer mode code
23 */
op_to_mode(SkRegion::Op op)24 static SkBlendMode op_to_mode(SkRegion::Op op) {
25
26 static const SkBlendMode modeMap[] = {
27 SkBlendMode::kDstOut, // kDifference_Op
28 SkBlendMode::kModulate, // kIntersect_Op
29 SkBlendMode::kSrcOver, // kUnion_Op
30 SkBlendMode::kXor, // kXOR_Op
31 SkBlendMode::kClear, // kReverseDifference_Op
32 SkBlendMode::kSrc, // kReplace_Op
33 };
34
35 return modeMap[op];
36 }
37
38 /**
39 * Draw a single rect element of the clip stack into the accumulation bitmap
40 */
drawRect(const SkRect & rect,SkRegion::Op op,bool antiAlias,uint8_t alpha)41 void GrSWMaskHelper::drawRect(const SkRect& rect, SkRegion::Op op,
42 bool antiAlias, uint8_t alpha) {
43 SkPaint paint;
44
45 paint.setBlendMode(op_to_mode(op));
46 paint.setAntiAlias(antiAlias);
47 paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
48
49 fDraw.drawRect(rect, paint);
50 }
51
52 /**
53 * Draw a single path element of the clip stack into the accumulation bitmap
54 */
drawShape(const GrShape & shape,SkRegion::Op op,bool antiAlias,uint8_t alpha)55 void GrSWMaskHelper::drawShape(const GrShape& shape, SkRegion::Op op, bool antiAlias,
56 uint8_t alpha) {
57 SkPaint paint;
58 paint.setPathEffect(sk_ref_sp(shape.style().pathEffect()));
59 shape.style().strokeRec().applyToPaint(&paint);
60 paint.setAntiAlias(antiAlias);
61
62 SkPath path;
63 shape.asPath(&path);
64 if (SkRegion::kReplace_Op == op && 0xFF == alpha) {
65 SkASSERT(0xFF == paint.getAlpha());
66 fDraw.drawPathCoverage(path, paint);
67 } else {
68 paint.setBlendMode(op_to_mode(op));
69 paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
70 fDraw.drawPath(path, paint);
71 }
72 }
73
init(const SkIRect & resultBounds,const SkMatrix * matrix)74 bool GrSWMaskHelper::init(const SkIRect& resultBounds, const SkMatrix* matrix) {
75 if (matrix) {
76 fMatrix = *matrix;
77 } else {
78 fMatrix.setIdentity();
79 }
80
81 // Now translate so the bound's UL corner is at the origin
82 fMatrix.postTranslate(-SkIntToScalar(resultBounds.fLeft), -SkIntToScalar(resultBounds.fTop));
83 SkIRect bounds = SkIRect::MakeWH(resultBounds.width(), resultBounds.height());
84
85 const SkImageInfo bmImageInfo = SkImageInfo::MakeA8(bounds.width(), bounds.height());
86 if (!fPixels.tryAlloc(bmImageInfo)) {
87 return false;
88 }
89 fPixels.erase(0);
90
91 sk_bzero(&fDraw, sizeof(fDraw));
92 fDraw.fDst = fPixels;
93 fRasterClip.setRect(bounds);
94 fDraw.fRC = &fRasterClip;
95 fDraw.fMatrix = &fMatrix;
96 return true;
97 }
98
99 /**
100 * Get a texture (from the texture cache) of the correct size & format.
101 */
createTexture(TextureType textureType)102 GrTexture* GrSWMaskHelper::createTexture(TextureType textureType) {
103 GrSurfaceDesc desc;
104 desc.fWidth = fPixels.width();
105 desc.fHeight = fPixels.height();
106 desc.fConfig = kAlpha_8_GrPixelConfig;
107
108 if (TextureType::kApproximateFit == textureType) {
109 return fTexProvider->createApproxTexture(desc);
110 } else {
111 return fTexProvider->createTexture(desc, SkBudgeted::kYes);
112 }
113 }
114
115 /**
116 * Move the result of the software mask generation back to the gpu
117 */
toTexture(GrTexture * texture)118 void GrSWMaskHelper::toTexture(GrTexture *texture) {
119 // Since we're uploading to it, and it's compressed, 'texture' shouldn't
120 // have a render target.
121 SkASSERT(!texture->asRenderTarget());
122
123 texture->writePixels(0, 0, fPixels.width(), fPixels.height(), texture->config(),
124 fPixels.addr(), fPixels.rowBytes());
125
126 }
127
128 /**
129 * Convert mask generation results to a signed distance field
130 */
toSDF(unsigned char * sdf)131 void GrSWMaskHelper::toSDF(unsigned char* sdf) {
132 SkGenerateDistanceFieldFromA8Image(sdf, (const unsigned char*)fPixels.addr(),
133 fPixels.width(), fPixels.height(), fPixels.rowBytes());
134 }
135
136 ////////////////////////////////////////////////////////////////////////////////
137 /**
138 * Software rasterizes shape to A8 mask and uploads the result to a scratch texture. Returns the
139 * resulting texture on success; nullptr on failure.
140 */
DrawShapeMaskToTexture(GrTextureProvider * texProvider,const GrShape & shape,const SkIRect & resultBounds,bool antiAlias,TextureType textureType,const SkMatrix * matrix)141 GrTexture* GrSWMaskHelper::DrawShapeMaskToTexture(GrTextureProvider* texProvider,
142 const GrShape& shape,
143 const SkIRect& resultBounds,
144 bool antiAlias,
145 TextureType textureType,
146 const SkMatrix* matrix) {
147 GrSWMaskHelper helper(texProvider);
148
149 if (!helper.init(resultBounds, matrix)) {
150 return nullptr;
151 }
152
153 helper.drawShape(shape, SkRegion::kReplace_Op, antiAlias, 0xFF);
154
155 GrTexture* texture(helper.createTexture(textureType));
156 if (!texture) {
157 return nullptr;
158 }
159
160 helper.toTexture(texture);
161
162 return texture;
163 }
164
DrawToTargetWithShapeMask(GrTexture * texture,GrDrawContext * drawContext,const GrPaint & paint,const GrUserStencilSettings & userStencilSettings,const GrClip & clip,const SkMatrix & viewMatrix,const SkIPoint & textureOriginInDeviceSpace,const SkIRect & deviceSpaceRectToDraw)165 void GrSWMaskHelper::DrawToTargetWithShapeMask(GrTexture* texture,
166 GrDrawContext* drawContext,
167 const GrPaint& paint,
168 const GrUserStencilSettings& userStencilSettings,
169 const GrClip& clip,
170 const SkMatrix& viewMatrix,
171 const SkIPoint& textureOriginInDeviceSpace,
172 const SkIRect& deviceSpaceRectToDraw) {
173 SkMatrix invert;
174 if (!viewMatrix.invert(&invert)) {
175 return;
176 }
177
178 SkRect dstRect = SkRect::Make(deviceSpaceRectToDraw);
179
180 // We use device coords to compute the texture coordinates. We take the device coords and apply
181 // a translation so that the top-left of the device bounds maps to 0,0, and then a scaling
182 // matrix to normalized coords.
183 SkMatrix maskMatrix;
184 maskMatrix.setIDiv(texture->width(), texture->height());
185 maskMatrix.preTranslate(SkIntToScalar(-textureOriginInDeviceSpace.fX),
186 SkIntToScalar(-textureOriginInDeviceSpace.fY));
187 maskMatrix.preConcat(viewMatrix);
188 GrPipelineBuilder pipelineBuilder(paint, drawContext->mustUseHWAA(paint));
189 pipelineBuilder.setUserStencil(&userStencilSettings);
190
191 pipelineBuilder.addCoverageFragmentProcessor(
192 GrSimpleTextureEffect::Make(texture,
193 nullptr,
194 maskMatrix,
195 GrTextureParams::kNone_FilterMode));
196
197 SkAutoTUnref<GrDrawBatch> batch(GrRectBatchFactory::CreateNonAAFill(paint.getColor(),
198 SkMatrix::I(),
199 dstRect, nullptr, &invert));
200 drawContext->drawBatch(pipelineBuilder, clip, batch);
201 }
202