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