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