• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "GrDrawTarget.h"
12 #include "GrGpu.h"
13 #include "GrPipelineBuilder.h"
14 
15 #include "SkData.h"
16 #include "SkDistanceFieldGen.h"
17 #include "SkStrokeRec.h"
18 
19 #include "batches/GrRectBatchFactory.h"
20 
21 namespace {
22 
23 /*
24  * Convert a boolean operation into a transfer mode code
25  */
op_to_mode(SkRegion::Op op)26 SkXfermode::Mode op_to_mode(SkRegion::Op op) {
27 
28     static const SkXfermode::Mode modeMap[] = {
29         SkXfermode::kDstOut_Mode,   // kDifference_Op
30         SkXfermode::kModulate_Mode, // kIntersect_Op
31         SkXfermode::kSrcOver_Mode,  // kUnion_Op
32         SkXfermode::kXor_Mode,      // kXOR_Op
33         SkXfermode::kClear_Mode,    // kReverseDifference_Op
34         SkXfermode::kSrc_Mode,      // kReplace_Op
35     };
36 
37     return modeMap[op];
38 }
39 
fmt_to_config(SkTextureCompressor::Format fmt)40 static inline GrPixelConfig fmt_to_config(SkTextureCompressor::Format fmt) {
41 
42     GrPixelConfig config;
43     switch (fmt) {
44         case SkTextureCompressor::kLATC_Format:
45             config = kLATC_GrPixelConfig;
46             break;
47 
48         case SkTextureCompressor::kR11_EAC_Format:
49             config = kR11_EAC_GrPixelConfig;
50             break;
51 
52         case SkTextureCompressor::kASTC_12x12_Format:
53             config = kASTC_12x12_GrPixelConfig;
54             break;
55 
56         case SkTextureCompressor::kETC1_Format:
57             config = kETC1_GrPixelConfig;
58             break;
59 
60         default:
61             SkDEBUGFAIL("No GrPixelConfig for compression format!");
62             // Best guess
63             config = kAlpha_8_GrPixelConfig;
64             break;
65     }
66 
67     return config;
68 }
69 
choose_compressed_fmt(const GrCaps * caps,SkTextureCompressor::Format * fmt)70 static bool choose_compressed_fmt(const GrCaps* caps,
71                                   SkTextureCompressor::Format *fmt) {
72     if (nullptr == fmt) {
73         return false;
74     }
75 
76     // We can't use scratch textures without the ability to update
77     // compressed textures...
78     if (!(caps->compressedTexSubImageSupport())) {
79         return false;
80     }
81 
82     // Figure out what our preferred texture type is. If ASTC is available, that always
83     // gives the biggest win. Otherwise, in terms of compression speed and accuracy,
84     // LATC has a slight edge over R11 EAC.
85     if (caps->isConfigTexturable(kASTC_12x12_GrPixelConfig)) {
86         *fmt = SkTextureCompressor::kASTC_12x12_Format;
87         return true;
88     } else if (caps->isConfigTexturable(kLATC_GrPixelConfig)) {
89         *fmt = SkTextureCompressor::kLATC_Format;
90         return true;
91     } else if (caps->isConfigTexturable(kR11_EAC_GrPixelConfig)) {
92         *fmt = SkTextureCompressor::kR11_EAC_Format;
93         return true;
94     }
95 
96     return false;
97 }
98 
99 }
100 
101 /**
102  * Draw a single rect element of the clip stack into the accumulation bitmap
103  */
draw(const SkRect & rect,SkRegion::Op op,bool antiAlias,uint8_t alpha)104 void GrSWMaskHelper::draw(const SkRect& rect, SkRegion::Op op,
105                           bool antiAlias, uint8_t alpha) {
106     SkPaint paint;
107 
108     SkXfermode* mode = SkXfermode::Create(op_to_mode(op));
109 
110     SkASSERT(kNone_CompressionMode == fCompressionMode);
111 
112     paint.setXfermode(mode);
113     paint.setAntiAlias(antiAlias);
114     paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
115 
116     fDraw.drawRect(rect, paint);
117 
118     SkSafeUnref(mode);
119 }
120 
121 /**
122  * Draw a single path element of the clip stack into the accumulation bitmap
123  */
draw(const SkPath & path,const SkStrokeRec & stroke,SkRegion::Op op,bool antiAlias,uint8_t alpha)124 void GrSWMaskHelper::draw(const SkPath& path, const SkStrokeRec& stroke, SkRegion::Op op,
125                           bool antiAlias, uint8_t alpha) {
126 
127     SkPaint paint;
128     if (stroke.isHairlineStyle()) {
129         paint.setStyle(SkPaint::kStroke_Style);
130         paint.setStrokeWidth(SK_Scalar1);
131     } else {
132         if (stroke.isFillStyle()) {
133             paint.setStyle(SkPaint::kFill_Style);
134         } else {
135             paint.setStyle(SkPaint::kStroke_Style);
136             paint.setStrokeJoin(stroke.getJoin());
137             paint.setStrokeCap(stroke.getCap());
138             paint.setStrokeWidth(stroke.getWidth());
139         }
140     }
141     paint.setAntiAlias(antiAlias);
142 
143     SkTBlitterAllocator allocator;
144     SkBlitter* blitter = nullptr;
145     if (kBlitter_CompressionMode == fCompressionMode) {
146         SkASSERT(fCompressedBuffer.get());
147         blitter = SkTextureCompressor::CreateBlitterForFormat(
148             fPixels.width(), fPixels.height(), fCompressedBuffer.get(), &allocator,
149                                                               fCompressedFormat);
150     }
151 
152     if (SkRegion::kReplace_Op == op && 0xFF == alpha) {
153         SkASSERT(0xFF == paint.getAlpha());
154         fDraw.drawPathCoverage(path, paint, blitter);
155     } else {
156         paint.setXfermodeMode(op_to_mode(op));
157         paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
158         fDraw.drawPath(path, paint, blitter);
159     }
160 }
161 
init(const SkIRect & resultBounds,const SkMatrix * matrix,bool allowCompression)162 bool GrSWMaskHelper::init(const SkIRect& resultBounds,
163                           const SkMatrix* matrix,
164                           bool allowCompression) {
165     if (matrix) {
166         fMatrix = *matrix;
167     } else {
168         fMatrix.setIdentity();
169     }
170 
171     // Now translate so the bound's UL corner is at the origin
172     fMatrix.postTranslate(-resultBounds.fLeft * SK_Scalar1,
173                           -resultBounds.fTop * SK_Scalar1);
174     SkIRect bounds = SkIRect::MakeWH(resultBounds.width(),
175                                      resultBounds.height());
176 
177     if (allowCompression &&
178         fContext->caps()->drawPathMasksToCompressedTexturesSupport() &&
179         choose_compressed_fmt(fContext->caps(), &fCompressedFormat)) {
180         fCompressionMode = kCompress_CompressionMode;
181     }
182 
183     // Make sure that the width is a multiple of the desired block dimensions
184     // to allow for specialized SIMD instructions that compress multiple blocks at a time.
185     int cmpWidth = bounds.fRight;
186     int cmpHeight = bounds.fBottom;
187     if (kCompress_CompressionMode == fCompressionMode) {
188         int dimX, dimY;
189         SkTextureCompressor::GetBlockDimensions(fCompressedFormat, &dimX, &dimY);
190         cmpWidth = dimX * ((cmpWidth + (dimX - 1)) / dimX);
191         cmpHeight = dimY * ((cmpHeight + (dimY - 1)) / dimY);
192 
193         // Can we create a blitter?
194         if (SkTextureCompressor::ExistsBlitterForFormat(fCompressedFormat)) {
195             int cmpSz = SkTextureCompressor::GetCompressedDataSize(
196                 fCompressedFormat, cmpWidth, cmpHeight);
197 
198             SkASSERT(cmpSz > 0);
199             SkASSERT(nullptr == fCompressedBuffer.get());
200             fCompressedBuffer.reset(cmpSz);
201             fCompressionMode = kBlitter_CompressionMode;
202         }
203     }
204 
205     sk_bzero(&fDraw, sizeof(fDraw));
206 
207     // If we don't have a custom blitter, then we either need a bitmap to compress
208     // from or a bitmap that we're going to use as a texture. In any case, we should
209     // allocate the pixels for a bitmap
210     const SkImageInfo bmImageInfo = SkImageInfo::MakeA8(cmpWidth, cmpHeight);
211     if (kBlitter_CompressionMode != fCompressionMode) {
212         if (!fPixels.tryAlloc(bmImageInfo)) {
213             return false;
214         }
215         fPixels.erase(0);
216     } else {
217         // Otherwise, we just need to remember how big the buffer is...
218         fPixels.reset(bmImageInfo);
219     }
220     fDraw.fDst      = fPixels;
221     fRasterClip.setRect(bounds);
222     fDraw.fRC       = &fRasterClip;
223     fDraw.fClip     = &fRasterClip.bwRgn();
224     fDraw.fMatrix   = &fMatrix;
225     return true;
226 }
227 
228 /**
229  * Get a texture (from the texture cache) of the correct size & format.
230  */
createTexture()231 GrTexture* GrSWMaskHelper::createTexture() {
232     GrSurfaceDesc desc;
233     desc.fWidth = fPixels.width();
234     desc.fHeight = fPixels.height();
235     desc.fConfig = kAlpha_8_GrPixelConfig;
236 
237     if (kNone_CompressionMode != fCompressionMode) {
238 
239 #ifdef SK_DEBUG
240         int dimX, dimY;
241         SkTextureCompressor::GetBlockDimensions(fCompressedFormat, &dimX, &dimY);
242         SkASSERT((desc.fWidth % dimX) == 0);
243         SkASSERT((desc.fHeight % dimY) == 0);
244 #endif
245 
246         desc.fConfig = fmt_to_config(fCompressedFormat);
247         SkASSERT(fContext->caps()->isConfigTexturable(desc.fConfig));
248     }
249 
250     return fContext->textureProvider()->createApproxTexture(desc);
251 }
252 
sendTextureData(GrTexture * texture,const GrSurfaceDesc & desc,const void * data,size_t rowbytes)253 void GrSWMaskHelper::sendTextureData(GrTexture *texture, const GrSurfaceDesc& desc,
254                                      const void *data, size_t rowbytes) {
255     // Since we're uploading to it, and it's compressed, 'texture' shouldn't
256     // have a render target.
257     SkASSERT(nullptr == texture->asRenderTarget());
258 
259     texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig, data, rowbytes);
260 }
261 
compressTextureData(GrTexture * texture,const GrSurfaceDesc & desc)262 void GrSWMaskHelper::compressTextureData(GrTexture *texture, const GrSurfaceDesc& desc) {
263 
264     SkASSERT(GrPixelConfigIsCompressed(desc.fConfig));
265     SkASSERT(fmt_to_config(fCompressedFormat) == desc.fConfig);
266 
267     SkAutoDataUnref cmpData(SkTextureCompressor::CompressBitmapToFormat(fPixels,
268                                                                         fCompressedFormat));
269     SkASSERT(cmpData);
270 
271     this->sendTextureData(texture, desc, cmpData->data(), 0);
272 }
273 
274 /**
275  * Move the result of the software mask generation back to the gpu
276  */
toTexture(GrTexture * texture)277 void GrSWMaskHelper::toTexture(GrTexture *texture) {
278     GrSurfaceDesc desc;
279     desc.fWidth = fPixels.width();
280     desc.fHeight = fPixels.height();
281     desc.fConfig = texture->config();
282 
283     // First see if we should compress this texture before uploading.
284     switch (fCompressionMode) {
285         case kNone_CompressionMode:
286             this->sendTextureData(texture, desc, fPixels.addr(), fPixels.rowBytes());
287             break;
288 
289         case kCompress_CompressionMode:
290             this->compressTextureData(texture, desc);
291             break;
292 
293         case kBlitter_CompressionMode:
294             SkASSERT(fCompressedBuffer.get());
295             this->sendTextureData(texture, desc, fCompressedBuffer.get(), 0);
296             break;
297     }
298 }
299 
300 /**
301  * Convert mask generation results to a signed distance field
302  */
toSDF(unsigned char * sdf)303 void GrSWMaskHelper::toSDF(unsigned char* sdf) {
304     SkGenerateDistanceFieldFromA8Image(sdf, (const unsigned char*)fPixels.addr(),
305                                        fPixels.width(), fPixels.height(), fPixels.rowBytes());
306 }
307 
308 ////////////////////////////////////////////////////////////////////////////////
309 /**
310  * Software rasterizes path to A8 mask (possibly using the context's matrix)
311  * and uploads the result to a scratch texture. Returns the resulting
312  * texture on success; nullptr on failure.
313  */
DrawPathMaskToTexture(GrContext * context,const SkPath & path,const SkStrokeRec & stroke,const SkIRect & resultBounds,bool antiAlias,const SkMatrix * matrix)314 GrTexture* GrSWMaskHelper::DrawPathMaskToTexture(GrContext* context,
315                                                  const SkPath& path,
316                                                  const SkStrokeRec& stroke,
317                                                  const SkIRect& resultBounds,
318                                                  bool antiAlias,
319                                                  const SkMatrix* matrix) {
320     GrSWMaskHelper helper(context);
321 
322     if (!helper.init(resultBounds, matrix)) {
323         return nullptr;
324     }
325 
326     helper.draw(path, stroke, SkRegion::kReplace_Op, antiAlias, 0xFF);
327 
328     GrTexture* texture(helper.createTexture());
329     if (!texture) {
330         return nullptr;
331     }
332 
333     helper.toTexture(texture);
334 
335     return texture;
336 }
337 
DrawToTargetWithPathMask(GrTexture * texture,GrDrawTarget * target,GrPipelineBuilder * pipelineBuilder,GrColor color,const SkMatrix & viewMatrix,const SkIRect & rect)338 void GrSWMaskHelper::DrawToTargetWithPathMask(GrTexture* texture,
339                                               GrDrawTarget* target,
340                                               GrPipelineBuilder* pipelineBuilder,
341                                               GrColor color,
342                                               const SkMatrix& viewMatrix,
343                                               const SkIRect& rect) {
344     SkMatrix invert;
345     if (!viewMatrix.invert(&invert)) {
346         return;
347     }
348     GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps(*pipelineBuilder);
349 
350     SkRect dstRect = SkRect::MakeLTRB(SK_Scalar1 * rect.fLeft,
351                                       SK_Scalar1 * rect.fTop,
352                                       SK_Scalar1 * rect.fRight,
353                                       SK_Scalar1 * rect.fBottom);
354 
355     // We use device coords to compute the texture coordinates. We take the device coords and apply
356     // a translation so that the top-left of the device bounds maps to 0,0, and then a scaling
357     // matrix to normalized coords.
358     SkMatrix maskMatrix;
359     maskMatrix.setIDiv(texture->width(), texture->height());
360     maskMatrix.preTranslate(SkIntToScalar(-rect.fLeft), SkIntToScalar(-rect.fTop));
361 
362     pipelineBuilder->addCoverageFragmentProcessor(
363                          GrSimpleTextureEffect::Create(texture,
364                                                        maskMatrix,
365                                                        GrTextureParams::kNone_FilterMode,
366                                                        kDevice_GrCoordSet))->unref();
367 
368     SkAutoTUnref<GrDrawBatch> batch(GrRectBatchFactory::CreateNonAAFill(color, SkMatrix::I(),
369                                                                         dstRect, nullptr, &invert));
370     target->drawBatch(*pipelineBuilder, batch);
371 }
372