Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / third_party / skia / src / gpu / GrSWMaskHelper.cpp
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 "GrDrawState.h"
11 #include "GrDrawTargetCaps.h"
12 #include "GrGpu.h"
13
14 #include "SkData.h"
15 #include "SkDistanceFieldGen.h"
16 #include "SkStrokeRec.h"
17
18 // TODO: try to remove this #include
19 #include "GrContext.h"
20
21 namespace {
22
23 /*
24  * Convert a boolean operation into a transfer mode code
25  */
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
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
70 static bool choose_compressed_fmt(const GrDrawTargetCaps* caps,
71                                   SkTextureCompressor::Format *fmt) {
72     if (NULL == 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  */
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  */
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 = NULL;
145     if (kBlitter_CompressionMode == fCompressionMode) {
146         SkASSERT(fCompressedBuffer.get());
147         blitter = SkTextureCompressor::CreateBlitterForFormat(
148             fBM.width(), fBM.height(), fCompressedBuffer.get(), &allocator, fCompressedFormat);
149     }
150
151     if (SkRegion::kReplace_Op == op && 0xFF == alpha) {
152         SkASSERT(0xFF == paint.getAlpha());
153         fDraw.drawPathCoverage(path, paint, blitter);
154     } else {
155         paint.setXfermodeMode(op_to_mode(op));
156         paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
157         fDraw.drawPath(path, paint, blitter);
158     }
159 }
160
161 bool GrSWMaskHelper::init(const SkIRect& resultBounds,
162                           const SkMatrix* matrix,
163                           bool allowCompression) {
164     if (matrix) {
165         fMatrix = *matrix;
166     } else {
167         fMatrix.setIdentity();
168     }
169
170     // Now translate so the bound's UL corner is at the origin
171     fMatrix.postTranslate(-resultBounds.fLeft * SK_Scalar1,
172                           -resultBounds.fTop * SK_Scalar1);
173     SkIRect bounds = SkIRect::MakeWH(resultBounds.width(),
174                                      resultBounds.height());
175
176     if (allowCompression &&
177         fContext->getOptions().fDrawPathToCompressedTexture &&
178         choose_compressed_fmt(fContext->getGpu()->caps(), &fCompressedFormat)) {
179         fCompressionMode = kCompress_CompressionMode;
180     }
181
182     // Make sure that the width is a multiple of the desired block dimensions
183     // to allow for specialized SIMD instructions that compress multiple blocks at a time.
184     int cmpWidth = bounds.fRight;
185     int cmpHeight = bounds.fBottom;
186     if (kCompress_CompressionMode == fCompressionMode) {
187         int dimX, dimY;
188         SkTextureCompressor::GetBlockDimensions(fCompressedFormat, &dimX, &dimY);
189         cmpWidth = dimX * ((cmpWidth + (dimX - 1)) / dimX);
190         cmpHeight = dimY * ((cmpHeight + (dimY - 1)) / dimY);
191
192         // Can we create a blitter?
193         if (SkTextureCompressor::ExistsBlitterForFormat(fCompressedFormat)) {
194             int cmpSz = SkTextureCompressor::GetCompressedDataSize(
195                 fCompressedFormat, cmpWidth, cmpHeight);
196
197             SkASSERT(cmpSz > 0);
198             SkASSERT(NULL == fCompressedBuffer.get());
199             fCompressedBuffer.reset(cmpSz);
200             fCompressionMode = kBlitter_CompressionMode;
201         }
202     } 
203
204     // If we don't have a custom blitter, then we either need a bitmap to compress
205     // from or a bitmap that we're going to use as a texture. In any case, we should
206     // allocate the pixels for a bitmap
207     const SkImageInfo bmImageInfo = SkImageInfo::MakeA8(cmpWidth, cmpHeight);
208     if (kBlitter_CompressionMode != fCompressionMode) {
209         if (!fBM.tryAllocPixels(bmImageInfo)) {
210             return false;
211         }
212
213         sk_bzero(fBM.getPixels(), fBM.getSafeSize());
214     } else {
215         // Otherwise, we just need to remember how big the buffer is...
216         fBM.setInfo(bmImageInfo);
217     }
218
219     sk_bzero(&fDraw, sizeof(fDraw));
220
221     fRasterClip.setRect(bounds);
222     fDraw.fRC    = &fRasterClip;
223     fDraw.fClip  = &fRasterClip.bwRgn();
224     fDraw.fMatrix = &fMatrix;
225     fDraw.fBitmap = &fBM;
226     return true;
227 }
228
229 /**
230  * Get a texture (from the texture cache) of the correct size & format.
231  */
232 GrTexture* GrSWMaskHelper::createTexture() {
233     GrSurfaceDesc desc;
234     desc.fWidth = fBM.width();
235     desc.fHeight = fBM.height();
236     desc.fConfig = kAlpha_8_GrPixelConfig;
237
238     if (kNone_CompressionMode != fCompressionMode) {
239
240 #ifdef SK_DEBUG
241         int dimX, dimY;
242         SkTextureCompressor::GetBlockDimensions(fCompressedFormat, &dimX, &dimY);
243         SkASSERT((desc.fWidth % dimX) == 0);
244         SkASSERT((desc.fHeight % dimY) == 0);
245 #endif
246
247         desc.fConfig = fmt_to_config(fCompressedFormat);
248         SkASSERT(fContext->getGpu()->caps()->isConfigTexturable(desc.fConfig));
249     }
250
251     return fContext->refScratchTexture(desc, GrContext::kApprox_ScratchTexMatch);
252 }
253
254 void GrSWMaskHelper::sendTextureData(GrTexture *texture, const GrSurfaceDesc& desc,
255                                      const void *data, int rowbytes) {
256     // If we aren't reusing scratch textures we don't need to flush before
257     // writing since no one else will be using 'texture'
258     bool reuseScratch = fContext->getGpu()->caps()->reuseScratchTextures();
259
260     // Since we're uploading to it, and it's compressed, 'texture' shouldn't
261     // have a render target.
262     SkASSERT(NULL == texture->asRenderTarget());
263
264     texture->writePixels(0, 0, desc.fWidth, desc.fHeight,
265                          desc.fConfig, data, rowbytes,
266                          reuseScratch ? 0 : GrContext::kDontFlush_PixelOpsFlag);
267 }
268
269 void GrSWMaskHelper::compressTextureData(GrTexture *texture, const GrSurfaceDesc& desc) {
270
271     SkASSERT(GrPixelConfigIsCompressed(desc.fConfig));
272     SkASSERT(fmt_to_config(fCompressedFormat) == desc.fConfig);
273
274     SkAutoDataUnref cmpData(SkTextureCompressor::CompressBitmapToFormat(fBM, fCompressedFormat));
275     SkASSERT(cmpData);
276
277     this->sendTextureData(texture, desc, cmpData->data(), 0);
278 }
279
280 /**
281  * Move the result of the software mask generation back to the gpu
282  */
283 void GrSWMaskHelper::toTexture(GrTexture *texture) {
284     SkAutoLockPixels alp(fBM);
285
286     GrSurfaceDesc desc;
287     desc.fWidth = fBM.width();
288     desc.fHeight = fBM.height();
289     desc.fConfig = texture->config();
290         
291     // First see if we should compress this texture before uploading.
292     switch (fCompressionMode) {
293         case kNone_CompressionMode:
294             this->sendTextureData(texture, desc, fBM.getPixels(), fBM.rowBytes());
295             break;
296
297         case kCompress_CompressionMode:
298             this->compressTextureData(texture, desc);
299             break;
300
301         case kBlitter_CompressionMode:
302             SkASSERT(fCompressedBuffer.get());
303             this->sendTextureData(texture, desc, fCompressedBuffer.get(), 0);
304             break;
305     }
306 }
307
308 /**
309  * Convert mask generation results to a signed distance field
310  */
311 void GrSWMaskHelper::toSDF(unsigned char* sdf) {
312     SkAutoLockPixels alp(fBM);
313     
314     SkGenerateDistanceFieldFromA8Image(sdf, (const unsigned char*)fBM.getPixels(),
315                                        fBM.width(), fBM.height(), fBM.rowBytes());
316 }
317
318 ////////////////////////////////////////////////////////////////////////////////
319 /**
320  * Software rasterizes path to A8 mask (possibly using the context's matrix)
321  * and uploads the result to a scratch texture. Returns the resulting
322  * texture on success; NULL on failure.
323  */
324 GrTexture* GrSWMaskHelper::DrawPathMaskToTexture(GrContext* context,
325                                                  const SkPath& path,
326                                                  const SkStrokeRec& stroke,
327                                                  const SkIRect& resultBounds,
328                                                  bool antiAlias,
329                                                  SkMatrix* matrix) {
330     GrSWMaskHelper helper(context);
331
332     if (!helper.init(resultBounds, matrix)) {
333         return NULL;
334     }
335
336     helper.draw(path, stroke, SkRegion::kReplace_Op, antiAlias, 0xFF);
337
338     GrTexture* texture(helper.createTexture());
339     if (!texture) {
340         return NULL;
341     }
342
343     helper.toTexture(texture);
344
345     return texture;
346 }
347
348 void GrSWMaskHelper::DrawToTargetWithPathMask(GrTexture* texture,
349                                               GrDrawTarget* target,
350                                               const SkIRect& rect) {
351     GrDrawState* drawState = target->drawState();
352
353     GrDrawState::AutoViewMatrixRestore avmr;
354     if (!avmr.setIdentity(drawState)) {
355         return;
356     }
357     GrDrawState::AutoRestoreEffects are(drawState);
358
359     SkRect dstRect = SkRect::MakeLTRB(SK_Scalar1 * rect.fLeft,
360                                       SK_Scalar1 * rect.fTop,
361                                       SK_Scalar1 * rect.fRight,
362                                       SK_Scalar1 * rect.fBottom);
363
364     // We want to use device coords to compute the texture coordinates. We set our matrix to be
365     // equal to the view matrix followed by a translation so that the top-left of the device bounds
366     // maps to 0,0, and then a scaling matrix to normalized coords. We apply this matrix to the
367     // vertex positions rather than local coords.
368     SkMatrix maskMatrix;
369     maskMatrix.setIDiv(texture->width(), texture->height());
370     maskMatrix.preTranslate(SkIntToScalar(-rect.fLeft), SkIntToScalar(-rect.fTop));
371     maskMatrix.preConcat(drawState->getViewMatrix());
372
373     drawState->addCoverageProcessor(
374                          GrSimpleTextureEffect::Create(texture,
375                                                        maskMatrix,
376                                                        GrTextureParams::kNone_FilterMode,
377                                                        kPosition_GrCoordSet))->unref();
378
379     target->drawSimpleRect(dstRect);
380 }