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 "GrContextPriv.h"
13 #include "GrPipelineBuilder.h"
14 #include "GrRenderTargetContext.h"
15 #include "GrShape.h"
16 #include "GrSurfaceContext.h"
17 #include "GrTextureProxy.h"
18 #include "ops/GrDrawOp.h"
19 
20 #include "SkDistanceFieldGen.h"
21 
22 #include "ops/GrRectOpFactory.h"
23 
24 /*
25  * Convert a boolean operation into a transfer mode code
26  */
op_to_mode(SkRegion::Op op)27 static SkBlendMode op_to_mode(SkRegion::Op op) {
28 
29     static const SkBlendMode modeMap[] = {
30         SkBlendMode::kDstOut,   // kDifference_Op
31         SkBlendMode::kModulate, // kIntersect_Op
32         SkBlendMode::kSrcOver,  // kUnion_Op
33         SkBlendMode::kXor,      // kXOR_Op
34         SkBlendMode::kClear,    // kReverseDifference_Op
35         SkBlendMode::kSrc,      // kReplace_Op
36     };
37 
38     return modeMap[op];
39 }
40 
41 /**
42  * Draw a single rect element of the clip stack into the accumulation bitmap
43  */
drawRect(const SkRect & rect,SkRegion::Op op,GrAA aa,uint8_t alpha)44 void GrSWMaskHelper::drawRect(const SkRect& rect, SkRegion::Op op, GrAA aa, uint8_t alpha) {
45     SkPaint paint;
46 
47     paint.setBlendMode(op_to_mode(op));
48     paint.setAntiAlias(GrAA::kYes == aa);
49     paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
50 
51     fDraw.drawRect(rect, paint);
52 }
53 
54 /**
55  * Draw a single path element of the clip stack into the accumulation bitmap
56  */
drawShape(const GrShape & shape,SkRegion::Op op,GrAA aa,uint8_t alpha)57 void GrSWMaskHelper::drawShape(const GrShape& shape, SkRegion::Op op, GrAA aa, uint8_t alpha) {
58     SkPaint paint;
59     paint.setPathEffect(shape.style().refPathEffect());
60     shape.style().strokeRec().applyToPaint(&paint);
61     paint.setAntiAlias(GrAA::kYes == aa);
62 
63     SkPath path;
64     shape.asPath(&path);
65     if (SkRegion::kReplace_Op == op && 0xFF == alpha) {
66         SkASSERT(0xFF == paint.getAlpha());
67         fDraw.drawPathCoverage(path, paint);
68     } else {
69         paint.setBlendMode(op_to_mode(op));
70         paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
71         fDraw.drawPath(path, paint);
72     }
73 }
74 
init(const SkIRect & resultBounds,const SkMatrix * matrix)75 bool GrSWMaskHelper::init(const SkIRect& resultBounds, const SkMatrix* matrix) {
76     if (matrix) {
77         fMatrix = *matrix;
78     } else {
79         fMatrix.setIdentity();
80     }
81 
82     // Now translate so the bound's UL corner is at the origin
83     fMatrix.postTranslate(-SkIntToScalar(resultBounds.fLeft), -SkIntToScalar(resultBounds.fTop));
84     SkIRect bounds = SkIRect::MakeWH(resultBounds.width(), resultBounds.height());
85 
86     const SkImageInfo bmImageInfo = SkImageInfo::MakeA8(bounds.width(), bounds.height());
87     if (!fPixels.tryAlloc(bmImageInfo)) {
88         return false;
89     }
90     fPixels.erase(0);
91 
92     sk_bzero(&fDraw, sizeof(fDraw));
93     fDraw.fDst      = fPixels;
94     fRasterClip.setRect(bounds);
95     fDraw.fRC       = &fRasterClip;
96     fDraw.fMatrix   = &fMatrix;
97     return true;
98 }
99 
toTextureProxy(GrContext * context,SkBackingFit fit)100 sk_sp<GrTextureProxy> GrSWMaskHelper::toTextureProxy(GrContext* context, SkBackingFit fit) {
101     GrSurfaceDesc desc;
102     desc.fOrigin = kTopLeft_GrSurfaceOrigin;
103     desc.fWidth = fPixels.width();
104     desc.fHeight = fPixels.height();
105     desc.fConfig = kAlpha_8_GrPixelConfig;
106 
107     sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeDeferredSurfaceContext(
108                                                                                 desc,
109                                                                                 fit,
110                                                                                 SkBudgeted::kYes);
111     if (!sContext || !sContext->asTextureProxy()) {
112         return nullptr;
113     }
114 
115     SkImageInfo ii = SkImageInfo::MakeA8(desc.fWidth, desc.fHeight);
116     if (!sContext->writePixels(ii, fPixels.addr(), fPixels.rowBytes(), 0, 0)) {
117         return nullptr;
118     }
119 
120     return sContext->asTextureProxyRef();
121 }
122 
123 /**
124  * Convert mask generation results to a signed distance field
125  */
toSDF(unsigned char * sdf)126 void GrSWMaskHelper::toSDF(unsigned char* sdf) {
127     SkGenerateDistanceFieldFromA8Image(sdf, (const unsigned char*)fPixels.addr(),
128                                        fPixels.width(), fPixels.height(), fPixels.rowBytes());
129 }
130 
131 ////////////////////////////////////////////////////////////////////////////////
132 /**
133  * Software rasterizes shape to A8 mask and uploads the result to a scratch texture. Returns the
134  * resulting texture on success; nullptr on failure.
135  */
DrawShapeMaskToTexture(GrContext * context,const GrShape & shape,const SkIRect & resultBounds,GrAA aa,SkBackingFit fit,const SkMatrix * matrix)136 sk_sp<GrTextureProxy> GrSWMaskHelper::DrawShapeMaskToTexture(GrContext* context,
137                                                              const GrShape& shape,
138                                                              const SkIRect& resultBounds,
139                                                              GrAA aa,
140                                                              SkBackingFit fit,
141                                                              const SkMatrix* matrix) {
142     GrSWMaskHelper helper;
143 
144     if (!helper.init(resultBounds, matrix)) {
145         return nullptr;
146     }
147 
148     helper.drawShape(shape, SkRegion::kReplace_Op, aa, 0xFF);
149 
150     return helper.toTextureProxy(context, fit);
151 }
152 
DrawToTargetWithShapeMask(sk_sp<GrTextureProxy> proxy,GrRenderTargetContext * renderTargetContext,GrPaint && paint,const GrUserStencilSettings & userStencilSettings,const GrClip & clip,const SkMatrix & viewMatrix,const SkIPoint & textureOriginInDeviceSpace,const SkIRect & deviceSpaceRectToDraw)153 void GrSWMaskHelper::DrawToTargetWithShapeMask(sk_sp<GrTextureProxy> proxy,
154                                                GrRenderTargetContext* renderTargetContext,
155                                                GrPaint&& paint,
156                                                const GrUserStencilSettings& userStencilSettings,
157                                                const GrClip& clip,
158                                                const SkMatrix& viewMatrix,
159                                                const SkIPoint& textureOriginInDeviceSpace,
160                                                const SkIRect& deviceSpaceRectToDraw) {
161     SkMatrix invert;
162     if (!viewMatrix.invert(&invert)) {
163         return;
164     }
165 
166     GrResourceProvider* resourceProvider = renderTargetContext->resourceProvider();
167 
168     SkRect dstRect = SkRect::Make(deviceSpaceRectToDraw);
169 
170     // We use device coords to compute the texture coordinates. We take the device coords and apply
171     // a translation so that the top-left of the device bounds maps to 0,0, and then a scaling
172     // matrix to normalized coords.
173     SkMatrix maskMatrix = SkMatrix::MakeTrans(SkIntToScalar(-textureOriginInDeviceSpace.fX),
174                                               SkIntToScalar(-textureOriginInDeviceSpace.fY));
175     maskMatrix.preConcat(viewMatrix);
176     std::unique_ptr<GrMeshDrawOp> op = GrRectOpFactory::MakeNonAAFill(
177             paint.getColor(), SkMatrix::I(), dstRect, nullptr, &invert);
178     paint.addCoverageFragmentProcessor(GrSimpleTextureEffect::Make(
179             resourceProvider, std::move(proxy), nullptr, maskMatrix,
180             GrSamplerParams::kNone_FilterMode));
181     GrPipelineBuilder pipelineBuilder(std::move(paint), GrAAType::kNone);
182     pipelineBuilder.setUserStencil(&userStencilSettings);
183     renderTargetContext->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
184 }
185