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#include "SkTextureCompressor.h"
17
18// TODO: try to remove this #include
19#include "GrContext.h"
20
21namespace {
22
23/*
24 * Convert a boolean operation into a transfer mode code
25 */
26SkXfermode::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}
41
42/**
43 * Draw a single rect element of the clip stack into the accumulation bitmap
44 */
45void GrSWMaskHelper::draw(const SkRect& rect, SkRegion::Op op,
46                          bool antiAlias, uint8_t alpha) {
47    SkPaint paint;
48
49    SkXfermode* mode = SkXfermode::Create(op_to_mode(op));
50
51    paint.setXfermode(mode);
52    paint.setAntiAlias(antiAlias);
53    paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
54
55    fDraw.drawRect(rect, paint);
56
57    SkSafeUnref(mode);
58}
59
60/**
61 * Draw a single path element of the clip stack into the accumulation bitmap
62 */
63void GrSWMaskHelper::draw(const SkPath& path, const SkStrokeRec& stroke, SkRegion::Op op,
64                          bool antiAlias, uint8_t alpha) {
65
66    SkPaint paint;
67    if (stroke.isHairlineStyle()) {
68        paint.setStyle(SkPaint::kStroke_Style);
69        paint.setStrokeWidth(SK_Scalar1);
70    } else {
71        if (stroke.isFillStyle()) {
72            paint.setStyle(SkPaint::kFill_Style);
73        } else {
74            paint.setStyle(SkPaint::kStroke_Style);
75            paint.setStrokeJoin(stroke.getJoin());
76            paint.setStrokeCap(stroke.getCap());
77            paint.setStrokeWidth(stroke.getWidth());
78        }
79    }
80    paint.setAntiAlias(antiAlias);
81
82    if (SkRegion::kReplace_Op == op && 0xFF == alpha) {
83        SkASSERT(0xFF == paint.getAlpha());
84        fDraw.drawPathCoverage(path, paint);
85    } else {
86        paint.setXfermodeMode(op_to_mode(op));
87        paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
88        fDraw.drawPath(path, paint);
89    }
90}
91
92bool GrSWMaskHelper::init(const SkIRect& resultBounds,
93                          const SkMatrix* matrix) {
94    if (NULL != matrix) {
95        fMatrix = *matrix;
96    } else {
97        fMatrix.setIdentity();
98    }
99
100    // Now translate so the bound's UL corner is at the origin
101    fMatrix.postTranslate(-resultBounds.fLeft * SK_Scalar1,
102                          -resultBounds.fTop * SK_Scalar1);
103    SkIRect bounds = SkIRect::MakeWH(resultBounds.width(),
104                                     resultBounds.height());
105
106    if (!fBM.allocPixels(SkImageInfo::MakeA8(bounds.fRight, bounds.fBottom))) {
107        return false;
108    }
109    sk_bzero(fBM.getPixels(), fBM.getSafeSize());
110
111    sk_bzero(&fDraw, sizeof(fDraw));
112    fRasterClip.setRect(bounds);
113    fDraw.fRC    = &fRasterClip;
114    fDraw.fClip  = &fRasterClip.bwRgn();
115    fDraw.fMatrix = &fMatrix;
116    fDraw.fBitmap = &fBM;
117    return true;
118}
119
120/**
121 * Get a texture (from the texture cache) of the correct size & format.
122 * Return true on success; false on failure.
123 */
124bool GrSWMaskHelper::getTexture(GrAutoScratchTexture* texture) {
125    GrTextureDesc desc;
126    desc.fWidth = fBM.width();
127    desc.fHeight = fBM.height();
128
129#if GR_COMPRESS_ALPHA_MASK
130    static const int kLATCBlockSize = 4;
131    if (desc.fWidth % kLATCBlockSize == 0 && desc.fHeight % kLATCBlockSize == 0) {
132        desc.fConfig = kLATC_GrPixelConfig;
133    } else {
134#endif
135        desc.fConfig = kAlpha_8_GrPixelConfig;
136#if GR_COMPRESS_ALPHA_MASK
137    }
138#endif
139
140    texture->set(fContext, desc);
141    return NULL != texture->texture();
142}
143
144void GrSWMaskHelper::sendTextureData(GrTexture *texture, const GrTextureDesc& desc,
145                                     const void *data, int rowbytes) {
146    // If we aren't reusing scratch textures we don't need to flush before
147    // writing since no one else will be using 'texture'
148    bool reuseScratch = fContext->getGpu()->caps()->reuseScratchTextures();
149
150    // Since we're uploading to it, and it's compressed, 'texture' shouldn't
151    // have a render target.
152    SkASSERT(NULL == texture->asRenderTarget());
153
154    texture->writePixels(0, 0, desc.fWidth, desc.fHeight,
155                         desc.fConfig, data, rowbytes,
156                         reuseScratch ? 0 : GrContext::kDontFlush_PixelOpsFlag);
157}
158
159/**
160 * Move the result of the software mask generation back to the gpu
161 */
162void GrSWMaskHelper::toTexture(GrTexture *texture) {
163    SkAutoLockPixels alp(fBM);
164
165    GrTextureDesc desc;
166    desc.fWidth = fBM.width();
167    desc.fHeight = fBM.height();
168    desc.fConfig = texture->config();
169
170    // First see if we should compress this texture before uploading.
171    if (texture->config() == kLATC_GrPixelConfig) {
172        SkTextureCompressor::Format format = SkTextureCompressor::kLATC_Format;
173        SkAutoDataUnref latcData(SkTextureCompressor::CompressBitmapToFormat(fBM, format));
174        SkASSERT(NULL != latcData);
175
176        this->sendTextureData(texture, desc, latcData->data(), 0);
177    } else {
178        // Looks like we have to send a full A8 texture.
179        this->sendTextureData(texture, desc, fBM.getPixels(), fBM.rowBytes());
180    }
181}
182
183////////////////////////////////////////////////////////////////////////////////
184/**
185 * Software rasterizes path to A8 mask (possibly using the context's matrix)
186 * and uploads the result to a scratch texture. Returns the resulting
187 * texture on success; NULL on failure.
188 */
189GrTexture* GrSWMaskHelper::DrawPathMaskToTexture(GrContext* context,
190                                                 const SkPath& path,
191                                                 const SkStrokeRec& stroke,
192                                                 const SkIRect& resultBounds,
193                                                 bool antiAlias,
194                                                 SkMatrix* matrix) {
195    GrSWMaskHelper helper(context);
196
197    if (!helper.init(resultBounds, matrix)) {
198        return NULL;
199    }
200
201    helper.draw(path, stroke, SkRegion::kReplace_Op, antiAlias, 0xFF);
202
203    GrAutoScratchTexture ast;
204    if (!helper.getTexture(&ast)) {
205        return NULL;
206    }
207
208    helper.toTexture(ast.texture());
209
210    return ast.detach();
211}
212
213void GrSWMaskHelper::DrawToTargetWithPathMask(GrTexture* texture,
214                                              GrDrawTarget* target,
215                                              const SkIRect& rect) {
216    GrDrawState* drawState = target->drawState();
217
218    GrDrawState::AutoViewMatrixRestore avmr;
219    if (!avmr.setIdentity(drawState)) {
220        return;
221    }
222    GrDrawState::AutoRestoreEffects are(drawState);
223
224    SkRect dstRect = SkRect::MakeLTRB(SK_Scalar1 * rect.fLeft,
225                                      SK_Scalar1 * rect.fTop,
226                                      SK_Scalar1 * rect.fRight,
227                                      SK_Scalar1 * rect.fBottom);
228
229    // We want to use device coords to compute the texture coordinates. We set our matrix to be
230    // equal to the view matrix followed by a translation so that the top-left of the device bounds
231    // maps to 0,0, and then a scaling matrix to normalized coords. We apply this matrix to the
232    // vertex positions rather than local coords.
233    SkMatrix maskMatrix;
234    maskMatrix.setIDiv(texture->width(), texture->height());
235    maskMatrix.preTranslate(SkIntToScalar(-rect.fLeft), SkIntToScalar(-rect.fTop));
236    maskMatrix.preConcat(drawState->getViewMatrix());
237
238    drawState->addCoverageEffect(
239                         GrSimpleTextureEffect::Create(texture,
240                                                       maskMatrix,
241                                                       GrTextureParams::kNone_FilterMode,
242                                                       kPosition_GrCoordSet))->unref();
243
244    target->drawSimpleRect(dstRect);
245}
246