blob: b5dc81475b9c10f3d7c5424cb758e08c775c3705 [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
bsalomoneb1cb5c2015-05-22 08:01:09 -070010#include "GrCaps.h"
robertphillips976f5f02016-06-03 10:59:20 -070011#include "GrContext.h"
12#include "batches/GrDrawBatch.h"
Brian Osman11052242016-10-27 14:47:55 -040013#include "GrRenderTargetContext.h"
bsalomonbb243832016-07-22 07:10:19 -070014#include "GrPipelineBuilder.h"
bsalomon8acedde2016-06-24 10:42:16 -070015#include "GrShape.h"
robertphillips@google.com58b20212012-06-27 20:44:52 +000016
jvanverthfa38a302014-10-06 05:59:05 -070017#include "SkDistanceFieldGen.h"
sugoi@google.com12b4e272012-12-06 20:13:11 +000018
joshualitt04194f32016-01-13 10:08:27 -080019#include "batches/GrRectBatchFactory.h"
robertphillips@google.com58b20212012-06-27 20:44:52 +000020
robertphillips@google.com58b20212012-06-27 20:44:52 +000021/*
22 * Convert a boolean operation into a transfer mode code
23 */
reed374772b2016-10-05 17:33:02 -070024static SkBlendMode op_to_mode(SkRegion::Op op) {
robertphillips@google.com58b20212012-06-27 20:44:52 +000025
reed374772b2016-10-05 17:33:02 -070026 static const SkBlendMode modeMap[] = {
27 SkBlendMode::kDstOut, // kDifference_Op
28 SkBlendMode::kModulate, // kIntersect_Op
29 SkBlendMode::kSrcOver, // kUnion_Op
30 SkBlendMode::kXor, // kXOR_Op
31 SkBlendMode::kClear, // kReverseDifference_Op
32 SkBlendMode::kSrc, // kReplace_Op
robertphillips@google.com58b20212012-06-27 20:44:52 +000033 };
34
35 return modeMap[op];
36}
37
robertphillips@google.com58b20212012-06-27 20:44:52 +000038/**
39 * Draw a single rect element of the clip stack into the accumulation bitmap
40 */
robertphillips98377402016-05-13 05:47:23 -070041void GrSWMaskHelper::drawRect(const SkRect& rect, SkRegion::Op op,
42 bool antiAlias, uint8_t alpha) {
robertphillips@google.com58b20212012-06-27 20:44:52 +000043 SkPaint paint;
44
reed374772b2016-10-05 17:33:02 -070045 paint.setBlendMode(op_to_mode(op));
robertphillips@google.com58b20212012-06-27 20:44:52 +000046 paint.setAntiAlias(antiAlias);
robertphillips@google.com366f1c62012-06-29 21:38:47 +000047 paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
robertphillips@google.com58b20212012-06-27 20:44:52 +000048
robertphillips@google.com366f1c62012-06-29 21:38:47 +000049 fDraw.drawRect(rect, paint);
robertphillips@google.com58b20212012-06-27 20:44:52 +000050}
51
52/**
53 * Draw a single path element of the clip stack into the accumulation bitmap
54 */
bsalomon8acedde2016-06-24 10:42:16 -070055void GrSWMaskHelper::drawShape(const GrShape& shape, SkRegion::Op op, bool antiAlias,
56 uint8_t alpha) {
robertphillips@google.com58b20212012-06-27 20:44:52 +000057 SkPaint paint;
bsalomon8acedde2016-06-24 10:42:16 -070058 paint.setPathEffect(sk_ref_sp(shape.style().pathEffect()));
59 shape.style().strokeRec().applyToPaint(&paint);
reed@google.com84e922b2013-11-04 20:57:36 +000060 paint.setAntiAlias(antiAlias);
reed@google.com84e922b2013-11-04 20:57:36 +000061
bsalomon8acedde2016-06-24 10:42:16 -070062 SkPath path;
63 shape.asPath(&path);
reed@google.com126f7f52013-11-07 16:06:53 +000064 if (SkRegion::kReplace_Op == op && 0xFF == alpha) {
65 SkASSERT(0xFF == paint.getAlpha());
robertphillips98377402016-05-13 05:47:23 -070066 fDraw.drawPathCoverage(path, paint);
reed@google.com126f7f52013-11-07 16:06:53 +000067 } else {
reed374772b2016-10-05 17:33:02 -070068 paint.setBlendMode(op_to_mode(op));
reed@google.com126f7f52013-11-07 16:06:53 +000069 paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
robertphillips98377402016-05-13 05:47:23 -070070 fDraw.drawPath(path, paint);
reed@google.com126f7f52013-11-07 16:06:53 +000071 }
robertphillips@google.com58b20212012-06-27 20:44:52 +000072}
73
robertphillips98377402016-05-13 05:47:23 -070074bool GrSWMaskHelper::init(const SkIRect& resultBounds, const SkMatrix* matrix) {
bsalomon49f085d2014-09-05 13:34:00 -070075 if (matrix) {
robertphillips@google.com366f1c62012-06-29 21:38:47 +000076 fMatrix = *matrix;
robertphillips@google.com58b20212012-06-27 20:44:52 +000077 } else {
78 fMatrix.setIdentity();
79 }
80
robertphillips@google.com366f1c62012-06-29 21:38:47 +000081 // Now translate so the bound's UL corner is at the origin
robertphillips98377402016-05-13 05:47:23 -070082 fMatrix.postTranslate(-SkIntToScalar(resultBounds.fLeft), -SkIntToScalar(resultBounds.fTop));
83 SkIRect bounds = SkIRect::MakeWH(resultBounds.width(), resultBounds.height());
krajcevski25a67bc2014-07-29 11:44:26 -070084
robertphillips98377402016-05-13 05:47:23 -070085 const SkImageInfo bmImageInfo = SkImageInfo::MakeA8(bounds.width(), bounds.height());
86 if (!fPixels.tryAlloc(bmImageInfo)) {
87 return false;
krajcevskib8ccc2f2014-08-07 08:15:14 -070088 }
robertphillips98377402016-05-13 05:47:23 -070089 fPixels.erase(0);
krajcevskib8ccc2f2014-08-07 08:15:14 -070090
reed41e010c2015-06-09 12:16:53 -070091 sk_bzero(&fDraw, sizeof(fDraw));
reed41e010c2015-06-09 12:16:53 -070092 fDraw.fDst = fPixels;
robertphillips@google.com58b20212012-06-27 20:44:52 +000093 fRasterClip.setRect(bounds);
reed41e010c2015-06-09 12:16:53 -070094 fDraw.fRC = &fRasterClip;
reed41e010c2015-06-09 12:16:53 -070095 fDraw.fMatrix = &fMatrix;
robertphillips@google.com58b20212012-06-27 20:44:52 +000096 return true;
97}
98
99/**
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000100 * Get a texture (from the texture cache) of the correct size & format.
robertphillips@google.com58b20212012-06-27 20:44:52 +0000101 */
bsalomon39ef7fb2016-09-21 11:16:05 -0700102GrTexture* GrSWMaskHelper::createTexture(TextureType textureType) {
bsalomonf2703d82014-10-28 14:33:06 -0700103 GrSurfaceDesc desc;
reed41e010c2015-06-09 12:16:53 -0700104 desc.fWidth = fPixels.width();
105 desc.fHeight = fPixels.height();
krajcevskib3abe902014-07-30 13:08:11 -0700106 desc.fConfig = kAlpha_8_GrPixelConfig;
krajcevskifb4f5cb2014-06-12 09:20:38 -0700107
bsalomon39ef7fb2016-09-21 11:16:05 -0700108 if (TextureType::kApproximateFit == textureType) {
109 return fTexProvider->createApproxTexture(desc);
110 } else {
111 return fTexProvider->createTexture(desc, SkBudgeted::kYes);
112 }
robertphillips@google.com58b20212012-06-27 20:44:52 +0000113}
114
115/**
116 * Move the result of the software mask generation back to the gpu
117 */
robertphillips@google.comd92cf2e2013-07-19 18:13:02 +0000118void GrSWMaskHelper::toTexture(GrTexture *texture) {
robertphillips98377402016-05-13 05:47:23 -0700119 // Since we're uploading to it, and it's compressed, 'texture' shouldn't
120 // have a render target.
121 SkASSERT(!texture->asRenderTarget());
cblumeed828002016-02-16 13:00:01 -0800122
robertphillips98377402016-05-13 05:47:23 -0700123 texture->writePixels(0, 0, fPixels.width(), fPixels.height(), texture->config(),
124 fPixels.addr(), fPixels.rowBytes());
krajcevskib8ccc2f2014-08-07 08:15:14 -0700125
robertphillips@google.com58b20212012-06-27 20:44:52 +0000126}
127
jvanverthfa38a302014-10-06 05:59:05 -0700128/**
129 * Convert mask generation results to a signed distance field
130 */
131void GrSWMaskHelper::toSDF(unsigned char* sdf) {
reed41e010c2015-06-09 12:16:53 -0700132 SkGenerateDistanceFieldFromA8Image(sdf, (const unsigned char*)fPixels.addr(),
133 fPixels.width(), fPixels.height(), fPixels.rowBytes());
jvanverthfa38a302014-10-06 05:59:05 -0700134}
135
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000136////////////////////////////////////////////////////////////////////////////////
137/**
bsalomon8acedde2016-06-24 10:42:16 -0700138 * Software rasterizes shape to A8 mask and uploads the result to a scratch texture. Returns the
139 * resulting texture on success; nullptr on failure.
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000140 */
bsalomon8acedde2016-06-24 10:42:16 -0700141GrTexture* GrSWMaskHelper::DrawShapeMaskToTexture(GrTextureProvider* texProvider,
142 const GrShape& shape,
143 const SkIRect& resultBounds,
144 bool antiAlias,
bsalomon39ef7fb2016-09-21 11:16:05 -0700145 TextureType textureType,
bsalomon8acedde2016-06-24 10:42:16 -0700146 const SkMatrix* matrix) {
robertphillips0152d732016-05-20 06:38:43 -0700147 GrSWMaskHelper helper(texProvider);
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000148
reed@google.com84e922b2013-11-04 20:57:36 +0000149 if (!helper.init(resultBounds, matrix)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700150 return nullptr;
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000151 }
152
bsalomon8acedde2016-06-24 10:42:16 -0700153 helper.drawShape(shape, SkRegion::kReplace_Op, antiAlias, 0xFF);
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000154
bsalomon39ef7fb2016-09-21 11:16:05 -0700155 GrTexture* texture(helper.createTexture(textureType));
bsalomone3059732014-10-14 11:47:22 -0700156 if (!texture) {
halcanary96fcdcc2015-08-27 07:41:13 -0700157 return nullptr;
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000158 }
159
bsalomone3059732014-10-14 11:47:22 -0700160 helper.toTexture(texture);
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000161
bsalomone3059732014-10-14 11:47:22 -0700162 return texture;
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000163}
164
bsalomon8acedde2016-06-24 10:42:16 -0700165void GrSWMaskHelper::DrawToTargetWithShapeMask(GrTexture* texture,
Brian Osman11052242016-10-27 14:47:55 -0400166 GrRenderTargetContext* renderTargetContext,
robertphillips3950f0d2016-07-07 07:33:13 -0700167 const GrPaint& paint,
robertphillipsd2b6d642016-07-21 08:55:08 -0700168 const GrUserStencilSettings& userStencilSettings,
bsalomon8acedde2016-06-24 10:42:16 -0700169 const GrClip& clip,
bsalomon8acedde2016-06-24 10:42:16 -0700170 const SkMatrix& viewMatrix,
bsalomon39ef7fb2016-09-21 11:16:05 -0700171 const SkIPoint& textureOriginInDeviceSpace,
172 const SkIRect& deviceSpaceRectToDraw) {
joshualittd27f73e2014-12-29 07:43:36 -0800173 SkMatrix invert;
joshualitt8059eb92014-12-29 15:10:07 -0800174 if (!viewMatrix.invert(&invert)) {
bsalomon@google.come3d32162012-07-20 13:37:06 +0000175 return;
176 }
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000177
bsalomon39ef7fb2016-09-21 11:16:05 -0700178 SkRect dstRect = SkRect::Make(deviceSpaceRectToDraw);
bsalomon@google.comc7818882013-03-20 19:19:53 +0000179
bsalomon309d4d52014-12-18 10:17:44 -0800180 // We use device coords to compute the texture coordinates. We take the device coords and apply
181 // a translation so that the top-left of the device bounds maps to 0,0, and then a scaling
182 // matrix to normalized coords.
bsalomon@google.comc7818882013-03-20 19:19:53 +0000183 SkMatrix maskMatrix;
184 maskMatrix.setIDiv(texture->width(), texture->height());
bsalomon39ef7fb2016-09-21 11:16:05 -0700185 maskMatrix.preTranslate(SkIntToScalar(-textureOriginInDeviceSpace.fX),
186 SkIntToScalar(-textureOriginInDeviceSpace.fY));
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400187 maskMatrix.preConcat(viewMatrix);
Brian Osman11052242016-10-27 14:47:55 -0400188 GrPipelineBuilder pipelineBuilder(paint, renderTargetContext->mustUseHWAA(paint));
bsalomonbb243832016-07-22 07:10:19 -0700189 pipelineBuilder.setUserStencil(&userStencilSettings);
robertphillips976f5f02016-06-03 10:59:20 -0700190
bsalomonbb243832016-07-22 07:10:19 -0700191 pipelineBuilder.addCoverageFragmentProcessor(
bungeman06ca8ec2016-06-09 08:01:03 -0700192 GrSimpleTextureEffect::Make(texture,
brianosman54f30c12016-07-18 10:53:52 -0700193 nullptr,
bungeman06ca8ec2016-06-09 08:01:03 -0700194 maskMatrix,
Brian Salomon514baff2016-11-17 15:17:07 -0500195 GrSamplerParams::kNone_FilterMode));
bsalomon@google.comc7818882013-03-20 19:19:53 +0000196
Hal Canary144caf52016-11-07 17:57:18 -0500197 sk_sp<GrDrawBatch> batch(GrRectBatchFactory::CreateNonAAFill(paint.getColor(),
198 SkMatrix::I(),
199 dstRect, nullptr, &invert));
200 renderTargetContext->drawBatch(pipelineBuilder, clip, batch.get());
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000201}