blob: 15b5457cccc28e35f91a0a6dac6ab23bc22d7d15 [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"
krajcevski5c2fca02014-06-10 17:25:28 -07009
robertphillips@google.com58b20212012-06-27 20:44:52 +000010#include "GrDrawState.h"
robertphillips@google.com9528bdb2013-09-18 22:33:57 +000011#include "GrDrawTargetCaps.h"
robertphillips@google.com58b20212012-06-27 20:44:52 +000012#include "GrGpu.h"
13
krajcevski5c2fca02014-06-10 17:25:28 -070014#include "SkData.h"
sugoi@google.com5f74cf82012-12-17 21:16:45 +000015#include "SkStrokeRec.h"
krajcevski5c2fca02014-06-10 17:25:28 -070016#include "SkTextureCompressor.h"
sugoi@google.com12b4e272012-12-06 20:13:11 +000017
robertphillips@google.com58b20212012-06-27 20:44:52 +000018// TODO: try to remove this #include
19#include "GrContext.h"
20
21namespace {
krajcevski5c2fca02014-06-10 17:25:28 -070022
robertphillips@google.com58b20212012-06-27 20:44:52 +000023/*
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
reed@google.com8d3cd7a2013-01-30 21:36:11 +000030 SkXfermode::kModulate_Mode, // kIntersect_Op
robertphillips@google.com58b20212012-06-27 20:44:52 +000031 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
robertphillips@google.com58b20212012-06-27 20:44:52 +000040}
41
42/**
43 * Draw a single rect element of the clip stack into the accumulation bitmap
44 */
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +000045void GrSWMaskHelper::draw(const SkRect& rect, SkRegion::Op op,
robertphillips@google.com366f1c62012-06-29 21:38:47 +000046 bool antiAlias, uint8_t alpha) {
robertphillips@google.com58b20212012-06-27 20:44:52 +000047 SkPaint paint;
48
49 SkXfermode* mode = SkXfermode::Create(op_to_mode(op));
50
51 paint.setXfermode(mode);
52 paint.setAntiAlias(antiAlias);
robertphillips@google.com366f1c62012-06-29 21:38:47 +000053 paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
robertphillips@google.com58b20212012-06-27 20:44:52 +000054
robertphillips@google.com366f1c62012-06-29 21:38:47 +000055 fDraw.drawRect(rect, paint);
robertphillips@google.com58b20212012-06-27 20:44:52 +000056
57 SkSafeUnref(mode);
58}
59
60/**
61 * Draw a single path element of the clip stack into the accumulation bitmap
62 */
sugoi@google.com5f74cf82012-12-17 21:16:45 +000063void GrSWMaskHelper::draw(const SkPath& path, const SkStrokeRec& stroke, SkRegion::Op op,
sugoi@google.com12b4e272012-12-06 20:13:11 +000064 bool antiAlias, uint8_t alpha) {
robertphillips@google.com58b20212012-06-27 20:44:52 +000065
66 SkPaint paint;
sugoi@google.com5f74cf82012-12-17 21:16:45 +000067 if (stroke.isHairlineStyle()) {
robertphillips@google.com58b20212012-06-27 20:44:52 +000068 paint.setStyle(SkPaint::kStroke_Style);
69 paint.setStrokeWidth(SK_Scalar1);
70 } else {
sugoi@google.com5f74cf82012-12-17 21:16:45 +000071 if (stroke.isFillStyle()) {
sugoi@google.com12b4e272012-12-06 20:13:11 +000072 paint.setStyle(SkPaint::kFill_Style);
73 } else {
74 paint.setStyle(SkPaint::kStroke_Style);
75 paint.setStrokeJoin(stroke.getJoin());
76 paint.setStrokeCap(stroke.getCap());
sugoi@google.com5f74cf82012-12-17 21:16:45 +000077 paint.setStrokeWidth(stroke.getWidth());
robertphillips@google.com58b20212012-06-27 20:44:52 +000078 }
79 }
reed@google.com84e922b2013-11-04 20:57:36 +000080 paint.setAntiAlias(antiAlias);
reed@google.com84e922b2013-11-04 20:57:36 +000081
reed@google.com126f7f52013-11-07 16:06:53 +000082 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 }
robertphillips@google.com58b20212012-06-27 20:44:52 +000090}
91
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +000092bool GrSWMaskHelper::init(const SkIRect& resultBounds,
reed@google.com84e922b2013-11-04 20:57:36 +000093 const SkMatrix* matrix) {
robertphillips@google.com366f1c62012-06-29 21:38:47 +000094 if (NULL != matrix) {
95 fMatrix = *matrix;
robertphillips@google.com58b20212012-06-27 20:44:52 +000096 } else {
97 fMatrix.setIdentity();
98 }
99
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000100 // Now translate so the bound's UL corner is at the origin
101 fMatrix.postTranslate(-resultBounds.fLeft * SK_Scalar1,
102 -resultBounds.fTop * SK_Scalar1);
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000103 SkIRect bounds = SkIRect::MakeWH(resultBounds.width(),
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000104 resultBounds.height());
robertphillips@google.com58b20212012-06-27 20:44:52 +0000105
reed@google.com9ebcac52014-01-24 18:53:42 +0000106 if (!fBM.allocPixels(SkImageInfo::MakeA8(bounds.fRight, bounds.fBottom))) {
robertphillips@google.com58b20212012-06-27 20:44:52 +0000107 return false;
108 }
reed@google.com84e922b2013-11-04 20:57:36 +0000109 sk_bzero(fBM.getPixels(), fBM.getSafeSize());
robertphillips@google.com58b20212012-06-27 20:44:52 +0000110
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/**
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000121 * Get a texture (from the texture cache) of the correct size & format.
122 * Return true on success; false on failure.
robertphillips@google.com58b20212012-06-27 20:44:52 +0000123 */
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000124bool GrSWMaskHelper::getTexture(GrAutoScratchTexture* texture) {
robertphillips@google.com58b20212012-06-27 20:44:52 +0000125 GrTextureDesc desc;
126 desc.fWidth = fBM.width();
127 desc.fHeight = fBM.height();
krajcevskifb4f5cb2014-06-12 09:20:38 -0700128
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
robertphillips@google.com58b20212012-06-27 20:44:52 +0000139
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000140 texture->set(fContext, desc);
141 return NULL != texture->texture();
robertphillips@google.com58b20212012-06-27 20:44:52 +0000142}
143
krajcevskifb4f5cb2014-06-12 09:20:38 -0700144void 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();
krajcevski5c2fca02014-06-10 17:25:28 -0700149
krajcevskifb4f5cb2014-06-12 09:20:38 -0700150 // 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);
krajcevski5c2fca02014-06-10 17:25:28 -0700157}
158
robertphillips@google.com58b20212012-06-27 20:44:52 +0000159/**
160 * Move the result of the software mask generation back to the gpu
161 */
robertphillips@google.comd92cf2e2013-07-19 18:13:02 +0000162void GrSWMaskHelper::toTexture(GrTexture *texture) {
robertphillips@google.com58b20212012-06-27 20:44:52 +0000163 SkAutoLockPixels alp(fBM);
164
krajcevskifb4f5cb2014-06-12 09:20:38 -0700165 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);
robertphillips@google.com9528bdb2013-09-18 22:33:57 +0000175
krajcevskifb4f5cb2014-06-12 09:20:38 -0700176 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 }
robertphillips@google.com58b20212012-06-27 20:44:52 +0000181}
182
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000183////////////////////////////////////////////////////////////////////////////////
184/**
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000185 * Software rasterizes path to A8 mask (possibly using the context's matrix)
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000186 * and uploads the result to a scratch texture. Returns the resulting
187 * texture on success; NULL on failure.
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000188 */
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000189GrTexture* GrSWMaskHelper::DrawPathMaskToTexture(GrContext* context,
190 const SkPath& path,
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000191 const SkStrokeRec& stroke,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000192 const SkIRect& resultBounds,
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000193 bool antiAlias,
bsalomon@google.comb9086a02012-11-01 18:02:54 +0000194 SkMatrix* matrix) {
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000195 GrSWMaskHelper helper(context);
196
reed@google.com84e922b2013-11-04 20:57:36 +0000197 if (!helper.init(resultBounds, matrix)) {
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000198 return NULL;
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000199 }
200
sugoi@google.com12b4e272012-12-06 20:13:11 +0000201 helper.draw(path, stroke, SkRegion::kReplace_Op, antiAlias, 0xFF);
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000202
krajcevski5c2fca02014-06-10 17:25:28 -0700203 GrAutoScratchTexture ast;
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000204 if (!helper.getTexture(&ast)) {
205 return NULL;
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000206 }
207
robertphillips@google.comd92cf2e2013-07-19 18:13:02 +0000208 helper.toTexture(ast.texture());
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000209
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000210 return ast.detach();
211}
212
213void GrSWMaskHelper::DrawToTargetWithPathMask(GrTexture* texture,
214 GrDrawTarget* target,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000215 const SkIRect& rect) {
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000216 GrDrawState* drawState = target->drawState();
217
bsalomon@google.com137f1342013-05-29 21:27:53 +0000218 GrDrawState::AutoViewMatrixRestore avmr;
219 if (!avmr.setIdentity(drawState)) {
bsalomon@google.come3d32162012-07-20 13:37:06 +0000220 return;
221 }
bsalomon@google.comeb6879f2013-06-13 19:34:18 +0000222 GrDrawState::AutoRestoreEffects are(drawState);
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000223
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000224 SkRect dstRect = SkRect::MakeLTRB(SK_Scalar1 * rect.fLeft,
225 SK_Scalar1 * rect.fTop,
226 SK_Scalar1 * rect.fRight,
227 SK_Scalar1 * rect.fBottom);
bsalomon@google.comc7818882013-03-20 19:19:53 +0000228
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
bsalomon@google.comeb6879f2013-06-13 19:34:18 +0000238 drawState->addCoverageEffect(
bsalomon@google.comc7818882013-03-20 19:19:53 +0000239 GrSimpleTextureEffect::Create(texture,
240 maskMatrix,
humper@google.comb86add12013-07-25 18:49:07 +0000241 GrTextureParams::kNone_FilterMode,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000242 kPosition_GrCoordSet))->unref();
bsalomon@google.comc7818882013-03-20 19:19:53 +0000243
244 target->drawSimpleRect(dstRect);
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000245}