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