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