blob: c13d2d839d0be95b586f7ef39795f72a3938bc3f [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
egdaniel8dd688b2015-01-22 10:16:09 -080010#include "GrPipelineBuilder.h"
bsalomoneb1cb5c2015-05-22 08:01:09 -070011#include "GrCaps.h"
kkinnunencabe20c2015-06-01 01:37:26 -070012#include "GrDrawTarget.h"
robertphillips@google.com58b20212012-06-27 20:44:52 +000013#include "GrGpu.h"
14
krajcevski5c2fca02014-06-10 17:25:28 -070015#include "SkData.h"
jvanverthfa38a302014-10-06 05:59:05 -070016#include "SkDistanceFieldGen.h"
sugoi@google.com5f74cf82012-12-17 21:16:45 +000017#include "SkStrokeRec.h"
sugoi@google.com12b4e272012-12-06 20:13:11 +000018
robertphillips@google.com58b20212012-06-27 20:44:52 +000019// TODO: try to remove this #include
20#include "GrContext.h"
21
22namespace {
krajcevski5c2fca02014-06-10 17:25:28 -070023
robertphillips@google.com58b20212012-06-27 20:44:52 +000024/*
25 * Convert a boolean operation into a transfer mode code
26 */
27SkXfermode::Mode op_to_mode(SkRegion::Op op) {
28
29 static const SkXfermode::Mode modeMap[] = {
30 SkXfermode::kDstOut_Mode, // kDifference_Op
reed@google.com8d3cd7a2013-01-30 21:36:11 +000031 SkXfermode::kModulate_Mode, // kIntersect_Op
robertphillips@google.com58b20212012-06-27 20:44:52 +000032 SkXfermode::kSrcOver_Mode, // kUnion_Op
33 SkXfermode::kXor_Mode, // kXOR_Op
34 SkXfermode::kClear_Mode, // kReverseDifference_Op
35 SkXfermode::kSrc_Mode, // kReplace_Op
36 };
37
38 return modeMap[op];
39}
40
krajcevski25a67bc2014-07-29 11:44:26 -070041static inline GrPixelConfig fmt_to_config(SkTextureCompressor::Format fmt) {
krajcevski25a67bc2014-07-29 11:44:26 -070042
krajcevski95b1b3d2014-08-07 12:58:38 -070043 GrPixelConfig config;
44 switch (fmt) {
45 case SkTextureCompressor::kLATC_Format:
46 config = kLATC_GrPixelConfig;
47 break;
48
49 case SkTextureCompressor::kR11_EAC_Format:
50 config = kR11_EAC_GrPixelConfig;
51 break;
52
53 case SkTextureCompressor::kASTC_12x12_Format:
54 config = kASTC_12x12_GrPixelConfig;
55 break;
56
57 case SkTextureCompressor::kETC1_Format:
58 config = kETC1_GrPixelConfig;
59 break;
60
61 default:
62 SkDEBUGFAIL("No GrPixelConfig for compression format!");
63 // Best guess
64 config = kAlpha_8_GrPixelConfig;
65 break;
66 }
67
68 return config;
krajcevski25a67bc2014-07-29 11:44:26 -070069}
70
bsalomon4b91f762015-05-19 09:29:46 -070071static bool choose_compressed_fmt(const GrCaps* caps,
krajcevskib3abe902014-07-30 13:08:11 -070072 SkTextureCompressor::Format *fmt) {
73 if (NULL == fmt) {
74 return false;
75 }
76
77 // We can't use scratch textures without the ability to update
78 // compressed textures...
79 if (!(caps->compressedTexSubImageSupport())) {
80 return false;
81 }
82
83 // Figure out what our preferred texture type is. If ASTC is available, that always
84 // gives the biggest win. Otherwise, in terms of compression speed and accuracy,
85 // LATC has a slight edge over R11 EAC.
86 if (caps->isConfigTexturable(kASTC_12x12_GrPixelConfig)) {
87 *fmt = SkTextureCompressor::kASTC_12x12_Format;
88 return true;
89 } else if (caps->isConfigTexturable(kLATC_GrPixelConfig)) {
90 *fmt = SkTextureCompressor::kLATC_Format;
91 return true;
92 } else if (caps->isConfigTexturable(kR11_EAC_GrPixelConfig)) {
93 *fmt = SkTextureCompressor::kR11_EAC_Format;
94 return true;
95 }
96
97 return false;
98}
krajcevskib3abe902014-07-30 13:08:11 -070099
robertphillips@google.com58b20212012-06-27 20:44:52 +0000100}
101
102/**
103 * Draw a single rect element of the clip stack into the accumulation bitmap
104 */
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000105void GrSWMaskHelper::draw(const SkRect& rect, SkRegion::Op op,
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000106 bool antiAlias, uint8_t alpha) {
robertphillips@google.com58b20212012-06-27 20:44:52 +0000107 SkPaint paint;
108
109 SkXfermode* mode = SkXfermode::Create(op_to_mode(op));
110
krajcevski71614ac2014-08-13 10:36:18 -0700111 SkASSERT(kNone_CompressionMode == fCompressionMode);
112
robertphillips@google.com58b20212012-06-27 20:44:52 +0000113 paint.setXfermode(mode);
114 paint.setAntiAlias(antiAlias);
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000115 paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
robertphillips@google.com58b20212012-06-27 20:44:52 +0000116
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000117 fDraw.drawRect(rect, paint);
robertphillips@google.com58b20212012-06-27 20:44:52 +0000118
119 SkSafeUnref(mode);
120}
121
122/**
123 * Draw a single path element of the clip stack into the accumulation bitmap
124 */
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000125void GrSWMaskHelper::draw(const SkPath& path, const SkStrokeRec& stroke, SkRegion::Op op,
sugoi@google.com12b4e272012-12-06 20:13:11 +0000126 bool antiAlias, uint8_t alpha) {
robertphillips@google.com58b20212012-06-27 20:44:52 +0000127
128 SkPaint paint;
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000129 if (stroke.isHairlineStyle()) {
robertphillips@google.com58b20212012-06-27 20:44:52 +0000130 paint.setStyle(SkPaint::kStroke_Style);
131 paint.setStrokeWidth(SK_Scalar1);
132 } else {
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000133 if (stroke.isFillStyle()) {
sugoi@google.com12b4e272012-12-06 20:13:11 +0000134 paint.setStyle(SkPaint::kFill_Style);
135 } else {
136 paint.setStyle(SkPaint::kStroke_Style);
137 paint.setStrokeJoin(stroke.getJoin());
138 paint.setStrokeCap(stroke.getCap());
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000139 paint.setStrokeWidth(stroke.getWidth());
robertphillips@google.com58b20212012-06-27 20:44:52 +0000140 }
141 }
reed@google.com84e922b2013-11-04 20:57:36 +0000142 paint.setAntiAlias(antiAlias);
reed@google.com84e922b2013-11-04 20:57:36 +0000143
krajcevskib8ccc2f2014-08-07 08:15:14 -0700144 SkTBlitterAllocator allocator;
145 SkBlitter* blitter = NULL;
146 if (kBlitter_CompressionMode == fCompressionMode) {
bsalomon49f085d2014-09-05 13:34:00 -0700147 SkASSERT(fCompressedBuffer.get());
krajcevskib8ccc2f2014-08-07 08:15:14 -0700148 blitter = SkTextureCompressor::CreateBlitterForFormat(
149 fBM.width(), fBM.height(), fCompressedBuffer.get(), &allocator, fCompressedFormat);
150 }
151
reed@google.com126f7f52013-11-07 16:06:53 +0000152 if (SkRegion::kReplace_Op == op && 0xFF == alpha) {
153 SkASSERT(0xFF == paint.getAlpha());
krajcevskib8ccc2f2014-08-07 08:15:14 -0700154 fDraw.drawPathCoverage(path, paint, blitter);
reed@google.com126f7f52013-11-07 16:06:53 +0000155 } else {
156 paint.setXfermodeMode(op_to_mode(op));
157 paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
krajcevskib8ccc2f2014-08-07 08:15:14 -0700158 fDraw.drawPath(path, paint, blitter);
reed@google.com126f7f52013-11-07 16:06:53 +0000159 }
robertphillips@google.com58b20212012-06-27 20:44:52 +0000160}
161
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000162bool GrSWMaskHelper::init(const SkIRect& resultBounds,
krajcevski71614ac2014-08-13 10:36:18 -0700163 const SkMatrix* matrix,
164 bool allowCompression) {
bsalomon49f085d2014-09-05 13:34:00 -0700165 if (matrix) {
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000166 fMatrix = *matrix;
robertphillips@google.com58b20212012-06-27 20:44:52 +0000167 } else {
168 fMatrix.setIdentity();
169 }
170
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000171 // Now translate so the bound's UL corner is at the origin
172 fMatrix.postTranslate(-resultBounds.fLeft * SK_Scalar1,
173 -resultBounds.fTop * SK_Scalar1);
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000174 SkIRect bounds = SkIRect::MakeWH(resultBounds.width(),
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000175 resultBounds.height());
krajcevski25a67bc2014-07-29 11:44:26 -0700176
krajcevski71614ac2014-08-13 10:36:18 -0700177 if (allowCompression &&
bsalomon76228632015-05-29 08:02:10 -0700178 fContext->caps()->drawPathMasksToCompressedTexturesSupport() &&
179 choose_compressed_fmt(fContext->caps(), &fCompressedFormat)) {
krajcevskib8ccc2f2014-08-07 08:15:14 -0700180 fCompressionMode = kCompress_CompressionMode;
181 }
krajcevski25a67bc2014-07-29 11:44:26 -0700182
krajcevskib8ccc2f2014-08-07 08:15:14 -0700183 // Make sure that the width is a multiple of the desired block dimensions
184 // to allow for specialized SIMD instructions that compress multiple blocks at a time.
185 int cmpWidth = bounds.fRight;
186 int cmpHeight = bounds.fBottom;
187 if (kCompress_CompressionMode == fCompressionMode) {
krajcevskib3abe902014-07-30 13:08:11 -0700188 int dimX, dimY;
189 SkTextureCompressor::GetBlockDimensions(fCompressedFormat, &dimX, &dimY);
krajcevskib8ccc2f2014-08-07 08:15:14 -0700190 cmpWidth = dimX * ((cmpWidth + (dimX - 1)) / dimX);
191 cmpHeight = dimY * ((cmpHeight + (dimY - 1)) / dimY);
192
193 // Can we create a blitter?
194 if (SkTextureCompressor::ExistsBlitterForFormat(fCompressedFormat)) {
195 int cmpSz = SkTextureCompressor::GetCompressedDataSize(
196 fCompressedFormat, cmpWidth, cmpHeight);
197
198 SkASSERT(cmpSz > 0);
199 SkASSERT(NULL == fCompressedBuffer.get());
200 fCompressedBuffer.reset(cmpSz);
201 fCompressionMode = kBlitter_CompressionMode;
202 }
203 }
204
205 // If we don't have a custom blitter, then we either need a bitmap to compress
206 // from or a bitmap that we're going to use as a texture. In any case, we should
207 // allocate the pixels for a bitmap
208 const SkImageInfo bmImageInfo = SkImageInfo::MakeA8(cmpWidth, cmpHeight);
209 if (kBlitter_CompressionMode != fCompressionMode) {
reed84825042014-09-02 12:50:45 -0700210 if (!fBM.tryAllocPixels(bmImageInfo)) {
krajcevskib8ccc2f2014-08-07 08:15:14 -0700211 return false;
212 }
213
214 sk_bzero(fBM.getPixels(), fBM.getSafeSize());
krajcevskib3abe902014-07-30 13:08:11 -0700215 } else {
krajcevskib8ccc2f2014-08-07 08:15:14 -0700216 // Otherwise, we just need to remember how big the buffer is...
217 fBM.setInfo(bmImageInfo);
krajcevskib3abe902014-07-30 13:08:11 -0700218 }
robertphillips@google.com58b20212012-06-27 20:44:52 +0000219
robertphillips@google.com58b20212012-06-27 20:44:52 +0000220 sk_bzero(&fDraw, sizeof(fDraw));
krajcevskib8ccc2f2014-08-07 08:15:14 -0700221
robertphillips@google.com58b20212012-06-27 20:44:52 +0000222 fRasterClip.setRect(bounds);
223 fDraw.fRC = &fRasterClip;
224 fDraw.fClip = &fRasterClip.bwRgn();
225 fDraw.fMatrix = &fMatrix;
226 fDraw.fBitmap = &fBM;
227 return true;
228}
229
230/**
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000231 * Get a texture (from the texture cache) of the correct size & format.
robertphillips@google.com58b20212012-06-27 20:44:52 +0000232 */
bsalomone3059732014-10-14 11:47:22 -0700233GrTexture* GrSWMaskHelper::createTexture() {
bsalomonf2703d82014-10-28 14:33:06 -0700234 GrSurfaceDesc desc;
robertphillips@google.com58b20212012-06-27 20:44:52 +0000235 desc.fWidth = fBM.width();
236 desc.fHeight = fBM.height();
krajcevskib3abe902014-07-30 13:08:11 -0700237 desc.fConfig = kAlpha_8_GrPixelConfig;
krajcevskifb4f5cb2014-06-12 09:20:38 -0700238
krajcevskib8ccc2f2014-08-07 08:15:14 -0700239 if (kNone_CompressionMode != fCompressionMode) {
krajcevskib577c552014-07-16 13:26:43 -0700240
krajcevski25a67bc2014-07-29 11:44:26 -0700241#ifdef SK_DEBUG
krajcevskib3abe902014-07-30 13:08:11 -0700242 int dimX, dimY;
243 SkTextureCompressor::GetBlockDimensions(fCompressedFormat, &dimX, &dimY);
244 SkASSERT((desc.fWidth % dimX) == 0);
245 SkASSERT((desc.fHeight % dimY) == 0);
krajcevski25a67bc2014-07-29 11:44:26 -0700246#endif
247
krajcevskib3abe902014-07-30 13:08:11 -0700248 desc.fConfig = fmt_to_config(fCompressedFormat);
bsalomon76228632015-05-29 08:02:10 -0700249 SkASSERT(fContext->caps()->isConfigTexturable(desc.fConfig));
krajcevskifb4f5cb2014-06-12 09:20:38 -0700250 }
robertphillips@google.com58b20212012-06-27 20:44:52 +0000251
bsalomond309e7a2015-04-30 14:18:54 -0700252 return fContext->textureProvider()->refScratchTexture(
253 desc, GrTextureProvider::kApprox_ScratchTexMatch);
robertphillips@google.com58b20212012-06-27 20:44:52 +0000254}
255
bsalomonf2703d82014-10-28 14:33:06 -0700256void GrSWMaskHelper::sendTextureData(GrTexture *texture, const GrSurfaceDesc& desc,
bsalomonef3fcd82014-12-12 08:51:38 -0800257 const void *data, size_t rowbytes) {
krajcevskifb4f5cb2014-06-12 09:20:38 -0700258 // If we aren't reusing scratch textures we don't need to flush before
259 // writing since no one else will be using 'texture'
bsalomon76228632015-05-29 08:02:10 -0700260 bool reuseScratch = fContext->caps()->reuseScratchTextures();
krajcevski5c2fca02014-06-10 17:25:28 -0700261
krajcevskifb4f5cb2014-06-12 09:20:38 -0700262 // Since we're uploading to it, and it's compressed, 'texture' shouldn't
263 // have a render target.
264 SkASSERT(NULL == texture->asRenderTarget());
265
266 texture->writePixels(0, 0, desc.fWidth, desc.fHeight,
267 desc.fConfig, data, rowbytes,
268 reuseScratch ? 0 : GrContext::kDontFlush_PixelOpsFlag);
krajcevski5c2fca02014-06-10 17:25:28 -0700269}
270
bsalomonf2703d82014-10-28 14:33:06 -0700271void GrSWMaskHelper::compressTextureData(GrTexture *texture, const GrSurfaceDesc& desc) {
krajcevskib577c552014-07-16 13:26:43 -0700272
273 SkASSERT(GrPixelConfigIsCompressed(desc.fConfig));
krajcevskib3abe902014-07-30 13:08:11 -0700274 SkASSERT(fmt_to_config(fCompressedFormat) == desc.fConfig);
krajcevskib577c552014-07-16 13:26:43 -0700275
krajcevskib3abe902014-07-30 13:08:11 -0700276 SkAutoDataUnref cmpData(SkTextureCompressor::CompressBitmapToFormat(fBM, fCompressedFormat));
bsalomon49f085d2014-09-05 13:34:00 -0700277 SkASSERT(cmpData);
krajcevskib577c552014-07-16 13:26:43 -0700278
279 this->sendTextureData(texture, desc, cmpData->data(), 0);
280}
281
robertphillips@google.com58b20212012-06-27 20:44:52 +0000282/**
283 * Move the result of the software mask generation back to the gpu
284 */
robertphillips@google.comd92cf2e2013-07-19 18:13:02 +0000285void GrSWMaskHelper::toTexture(GrTexture *texture) {
robertphillips@google.com58b20212012-06-27 20:44:52 +0000286 SkAutoLockPixels alp(fBM);
287
bsalomonf2703d82014-10-28 14:33:06 -0700288 GrSurfaceDesc desc;
krajcevskifb4f5cb2014-06-12 09:20:38 -0700289 desc.fWidth = fBM.width();
290 desc.fHeight = fBM.height();
291 desc.fConfig = texture->config();
292
293 // First see if we should compress this texture before uploading.
krajcevskib8ccc2f2014-08-07 08:15:14 -0700294 switch (fCompressionMode) {
295 case kNone_CompressionMode:
296 this->sendTextureData(texture, desc, fBM.getPixels(), fBM.rowBytes());
297 break;
298
299 case kCompress_CompressionMode:
300 this->compressTextureData(texture, desc);
301 break;
302
303 case kBlitter_CompressionMode:
bsalomon49f085d2014-09-05 13:34:00 -0700304 SkASSERT(fCompressedBuffer.get());
krajcevskib8ccc2f2014-08-07 08:15:14 -0700305 this->sendTextureData(texture, desc, fCompressedBuffer.get(), 0);
306 break;
krajcevskifb4f5cb2014-06-12 09:20:38 -0700307 }
robertphillips@google.com58b20212012-06-27 20:44:52 +0000308}
309
jvanverthfa38a302014-10-06 05:59:05 -0700310/**
311 * Convert mask generation results to a signed distance field
312 */
313void GrSWMaskHelper::toSDF(unsigned char* sdf) {
314 SkAutoLockPixels alp(fBM);
315
316 SkGenerateDistanceFieldFromA8Image(sdf, (const unsigned char*)fBM.getPixels(),
317 fBM.width(), fBM.height(), fBM.rowBytes());
318}
319
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000320////////////////////////////////////////////////////////////////////////////////
321/**
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000322 * Software rasterizes path to A8 mask (possibly using the context's matrix)
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000323 * and uploads the result to a scratch texture. Returns the resulting
324 * texture on success; NULL on failure.
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000325 */
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000326GrTexture* GrSWMaskHelper::DrawPathMaskToTexture(GrContext* context,
327 const SkPath& path,
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000328 const SkStrokeRec& stroke,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000329 const SkIRect& resultBounds,
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000330 bool antiAlias,
joshualitt8059eb92014-12-29 15:10:07 -0800331 const SkMatrix* matrix) {
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000332 GrSWMaskHelper helper(context);
333
reed@google.com84e922b2013-11-04 20:57:36 +0000334 if (!helper.init(resultBounds, matrix)) {
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000335 return NULL;
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000336 }
337
sugoi@google.com12b4e272012-12-06 20:13:11 +0000338 helper.draw(path, stroke, SkRegion::kReplace_Op, antiAlias, 0xFF);
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000339
bsalomone3059732014-10-14 11:47:22 -0700340 GrTexture* texture(helper.createTexture());
341 if (!texture) {
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000342 return NULL;
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000343 }
344
bsalomone3059732014-10-14 11:47:22 -0700345 helper.toTexture(texture);
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000346
bsalomone3059732014-10-14 11:47:22 -0700347 return texture;
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000348}
349
350void GrSWMaskHelper::DrawToTargetWithPathMask(GrTexture* texture,
351 GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800352 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800353 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800354 const SkMatrix& viewMatrix,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000355 const SkIRect& rect) {
joshualittd27f73e2014-12-29 07:43:36 -0800356 SkMatrix invert;
joshualitt8059eb92014-12-29 15:10:07 -0800357 if (!viewMatrix.invert(&invert)) {
bsalomon@google.come3d32162012-07-20 13:37:06 +0000358 return;
359 }
bsalomon6be6f7c2015-02-26 13:05:21 -0800360 GrPipelineBuilder::AutoRestoreFragmentProcessors arfp(pipelineBuilder);
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000361
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000362 SkRect dstRect = SkRect::MakeLTRB(SK_Scalar1 * rect.fLeft,
363 SK_Scalar1 * rect.fTop,
364 SK_Scalar1 * rect.fRight,
365 SK_Scalar1 * rect.fBottom);
bsalomon@google.comc7818882013-03-20 19:19:53 +0000366
bsalomon309d4d52014-12-18 10:17:44 -0800367 // We use device coords to compute the texture coordinates. We take the device coords and apply
368 // a translation so that the top-left of the device bounds maps to 0,0, and then a scaling
369 // matrix to normalized coords.
bsalomon@google.comc7818882013-03-20 19:19:53 +0000370 SkMatrix maskMatrix;
371 maskMatrix.setIDiv(texture->width(), texture->height());
372 maskMatrix.preTranslate(SkIntToScalar(-rect.fLeft), SkIntToScalar(-rect.fTop));
bsalomon@google.comc7818882013-03-20 19:19:53 +0000373
egdaniel8dd688b2015-01-22 10:16:09 -0800374 pipelineBuilder->addCoverageProcessor(
bsalomon@google.comc7818882013-03-20 19:19:53 +0000375 GrSimpleTextureEffect::Create(texture,
376 maskMatrix,
humper@google.comb86add12013-07-25 18:49:07 +0000377 GrTextureParams::kNone_FilterMode,
bsalomon309d4d52014-12-18 10:17:44 -0800378 kDevice_GrCoordSet))->unref();
bsalomon@google.comc7818882013-03-20 19:19:53 +0000379
robertphillipsea461502015-05-26 11:38:03 -0700380 target->drawBWRect(pipelineBuilder, color, SkMatrix::I(), dstRect, NULL, &invert);
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000381}