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