blob: fe32110b8359eb1d47f90ef527eb456a9e983ba4 [file] [log] [blame]
robertphillips@google.com58b20212012-06-27 20:44:52 +00001/*
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// TODO: try to remove this #include
13#include "GrContext.h"
14
15namespace {
16/*
17 * Convert a boolean operation into a transfer mode code
18 */
19SkXfermode::Mode op_to_mode(SkRegion::Op op) {
20
21 static const SkXfermode::Mode modeMap[] = {
22 SkXfermode::kDstOut_Mode, // kDifference_Op
23 SkXfermode::kMultiply_Mode, // kIntersect_Op
24 SkXfermode::kSrcOver_Mode, // kUnion_Op
25 SkXfermode::kXor_Mode, // kXOR_Op
26 SkXfermode::kClear_Mode, // kReverseDifference_Op
27 SkXfermode::kSrc_Mode, // kReplace_Op
28 };
29
30 return modeMap[op];
31}
32
33////////////////////////////////////////////////////////////////////////////////
34SkPath::FillType gr_fill_to_sk_fill(GrPathFill fill) {
35 switch (fill) {
36 case kWinding_GrPathFill:
37 return SkPath::kWinding_FillType;
38 case kEvenOdd_GrPathFill:
39 return SkPath::kEvenOdd_FillType;
40 case kInverseWinding_GrPathFill:
41 return SkPath::kInverseWinding_FillType;
42 case kInverseEvenOdd_GrPathFill:
43 return SkPath::kInverseEvenOdd_FillType;
44 default:
45 GrCrash("Unexpected fill.");
46 return SkPath::kWinding_FillType;
47 }
48}
49
50}
51
52/**
53 * Draw a single rect element of the clip stack into the accumulation bitmap
54 */
robertphillips@google.com366f1c62012-06-29 21:38:47 +000055void GrSWMaskHelper::draw(const GrRect& rect, SkRegion::Op op,
56 bool antiAlias, uint8_t alpha) {
robertphillips@google.com58b20212012-06-27 20:44:52 +000057 SkPaint paint;
58
59 SkXfermode* mode = SkXfermode::Create(op_to_mode(op));
60
61 paint.setXfermode(mode);
62 paint.setAntiAlias(antiAlias);
robertphillips@google.com366f1c62012-06-29 21:38:47 +000063 paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
robertphillips@google.com58b20212012-06-27 20:44:52 +000064
robertphillips@google.com366f1c62012-06-29 21:38:47 +000065 fDraw.drawRect(rect, paint);
robertphillips@google.com58b20212012-06-27 20:44:52 +000066
67 SkSafeUnref(mode);
68}
69
70/**
71 * Draw a single path element of the clip stack into the accumulation bitmap
72 */
robertphillips@google.com366f1c62012-06-29 21:38:47 +000073void GrSWMaskHelper::draw(const SkPath& path, SkRegion::Op op,
74 GrPathFill fill, bool antiAlias, uint8_t alpha) {
robertphillips@google.com58b20212012-06-27 20:44:52 +000075
76 SkPaint paint;
77 SkPath tmpPath;
robertphillips@google.com366f1c62012-06-29 21:38:47 +000078 const SkPath* pathToDraw = &path;
robertphillips@google.com58b20212012-06-27 20:44:52 +000079 if (kHairLine_GrPathFill == fill) {
80 paint.setStyle(SkPaint::kStroke_Style);
81 paint.setStrokeWidth(SK_Scalar1);
82 } else {
83 paint.setStyle(SkPaint::kFill_Style);
84 SkPath::FillType skfill = gr_fill_to_sk_fill(fill);
85 if (skfill != pathToDraw->getFillType()) {
86 tmpPath = *pathToDraw;
87 tmpPath.setFillType(skfill);
88 pathToDraw = &tmpPath;
89 }
90 }
91 SkXfermode* mode = SkXfermode::Create(op_to_mode(op));
92
93 paint.setXfermode(mode);
94 paint.setAntiAlias(antiAlias);
robertphillips@google.com366f1c62012-06-29 21:38:47 +000095 paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
robertphillips@google.com58b20212012-06-27 20:44:52 +000096
97 fDraw.drawPath(*pathToDraw, paint);
98
99 SkSafeUnref(mode);
100}
101
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000102bool GrSWMaskHelper::init(const GrIRect& resultBounds,
103 const GrMatrix* matrix) {
104 if (NULL != matrix) {
105 fMatrix = *matrix;
robertphillips@google.com58b20212012-06-27 20:44:52 +0000106 } else {
107 fMatrix.setIdentity();
108 }
109
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000110 // Now translate so the bound's UL corner is at the origin
111 fMatrix.postTranslate(-resultBounds.fLeft * SK_Scalar1,
112 -resultBounds.fTop * SK_Scalar1);
113 GrIRect bounds = GrIRect::MakeWH(resultBounds.width(),
114 resultBounds.height());
robertphillips@google.com58b20212012-06-27 20:44:52 +0000115
116 fBM.setConfig(SkBitmap::kA8_Config, bounds.fRight, bounds.fBottom);
117 if (!fBM.allocPixels()) {
118 return false;
119 }
120 sk_bzero(fBM.getPixels(), fBM.getSafeSize());
121
122 sk_bzero(&fDraw, sizeof(fDraw));
123 fRasterClip.setRect(bounds);
124 fDraw.fRC = &fRasterClip;
125 fDraw.fClip = &fRasterClip.bwRgn();
126 fDraw.fMatrix = &fMatrix;
127 fDraw.fBitmap = &fBM;
128 return true;
129}
130
131/**
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000132 * Get a texture (from the texture cache) of the correct size & format.
133 * Return true on success; false on failure.
robertphillips@google.com58b20212012-06-27 20:44:52 +0000134 */
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000135bool GrSWMaskHelper::getTexture(GrAutoScratchTexture* texture) {
robertphillips@google.com58b20212012-06-27 20:44:52 +0000136 GrTextureDesc desc;
137 desc.fWidth = fBM.width();
138 desc.fHeight = fBM.height();
139 desc.fConfig = kAlpha_8_GrPixelConfig;
140
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000141 texture->set(fContext, desc);
142 return NULL != texture->texture();
robertphillips@google.com58b20212012-06-27 20:44:52 +0000143}
144
145/**
146 * Move the result of the software mask generation back to the gpu
147 */
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000148void GrSWMaskHelper::toTexture(GrTexture *texture, uint8_t alpha) {
robertphillips@google.com58b20212012-06-27 20:44:52 +0000149 SkAutoLockPixels alp(fBM);
150
151 // The destination texture is almost always larger than "fBM". Clear
152 // it appropriately so we don't get mask artifacts outside of the path's
153 // bounding box
154
155 // "texture" needs to be installed as the render target for the clear
156 // and the texture upload but cannot remain the render target upon
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000157 // return. Callers typically use it as a texture and it would then
robertphillips@google.com58b20212012-06-27 20:44:52 +0000158 // be both source and dest.
159 GrDrawState::AutoRenderTargetRestore artr(fContext->getGpu()->drawState(),
160 texture->asRenderTarget());
161
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000162 fContext->getGpu()->clear(NULL, SkColorSetARGB(alpha, alpha, alpha, alpha));
robertphillips@google.com58b20212012-06-27 20:44:52 +0000163
164 texture->writePixels(0, 0, fBM.width(), fBM.height(),
165 kAlpha_8_GrPixelConfig,
166 fBM.getPixels(), fBM.rowBytes());
167}
168
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000169////////////////////////////////////////////////////////////////////////////////
170/**
171 * Software rasterizes path to A8 mask (possibly using the context's matrix)
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000172 * and uploads the result to a scratch texture. Returns the resulting
173 * texture on success; NULL on failure.
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000174 */
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000175GrTexture* GrSWMaskHelper::DrawPathMaskToTexture(GrContext* context,
176 const SkPath& path,
177 const GrIRect& resultBounds,
178 GrPathFill fill,
179 bool antiAlias,
180 GrMatrix* matrix) {
181 GrAutoScratchTexture ast;
182
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000183 GrSWMaskHelper helper(context);
184
185 if (!helper.init(resultBounds, matrix)) {
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000186 return NULL;
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000187 }
188
189 helper.draw(path, SkRegion::kReplace_Op, fill, antiAlias, 0xFF);
190
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000191 if (!helper.getTexture(&ast)) {
192 return NULL;
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000193 }
194
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000195 helper.toTexture(ast.texture(), 0x00);
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000196
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000197 return ast.detach();
198}
199
200void GrSWMaskHelper::DrawToTargetWithPathMask(GrTexture* texture,
201 GrDrawTarget* target,
202 GrDrawState::StageMask stageMask,
203 const GrIRect& rect) {
204 GrDrawState* drawState = target->drawState();
205
206 GrDrawTarget::AutoDeviceCoordDraw adcd(target, stageMask);
207 enum {
208 // the SW path renderer shares this stage with glyph
209 // rendering (kGlyphMaskStage in GrBatchedTextContext)
210 kPathMaskStage = GrPaint::kTotalStages,
211 };
212 GrAssert(!drawState->isStageEnabled(kPathMaskStage));
213 drawState->setTexture(kPathMaskStage, texture);
214 drawState->sampler(kPathMaskStage)->reset();
215 GrScalar w = GrIntToScalar(rect.width());
216 GrScalar h = GrIntToScalar(rect.height());
217 GrRect maskRect = GrRect::MakeWH(w / texture->width(),
218 h / texture->height());
219
220 const GrRect* srcRects[GrDrawState::kNumStages] = { NULL };
221 srcRects[kPathMaskStage] = &maskRect;
222 stageMask |= 1 << kPathMaskStage;
223 GrRect dstRect = GrRect::MakeLTRB(
224 SK_Scalar1 * rect.fLeft,
225 SK_Scalar1 * rect.fTop,
226 SK_Scalar1 * rect.fRight,
227 SK_Scalar1 * rect.fBottom);
228 target->drawRect(dstRect, NULL, stageMask, srcRects, NULL);
229 drawState->setTexture(kPathMaskStage, NULL);
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000230}
231