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