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