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 10 #include "GrDrawState.h" 11 #include "GrDrawTargetCaps.h" 12 #include "GrGpu.h" 13 14 #include "SkData.h" 15 #include "SkStrokeRec.h" 16 #include "SkTextureCompressor.h" 17 18 // TODO: try to remove this #include 19 #include "GrContext.h" 20 21 namespace { 22 23 /* 24 * Convert a boolean operation into a transfer mode code 25 */ 26 SkXfermode::Mode op_to_mode(SkRegion::Op op) { 27 28 static const SkXfermode::Mode modeMap[] = { 29 SkXfermode::kDstOut_Mode, // kDifference_Op 30 SkXfermode::kModulate_Mode, // kIntersect_Op 31 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 40 } 41 42 /** 43 * Draw a single rect element of the clip stack into the accumulation bitmap 44 */ 45 void GrSWMaskHelper::draw(const SkRect& rect, SkRegion::Op op, 46 bool antiAlias, uint8_t alpha) { 47 SkPaint paint; 48 49 SkXfermode* mode = SkXfermode::Create(op_to_mode(op)); 50 51 paint.setXfermode(mode); 52 paint.setAntiAlias(antiAlias); 53 paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha)); 54 55 fDraw.drawRect(rect, paint); 56 57 SkSafeUnref(mode); 58 } 59 60 /** 61 * Draw a single path element of the clip stack into the accumulation bitmap 62 */ 63 void GrSWMaskHelper::draw(const SkPath& path, const SkStrokeRec& stroke, SkRegion::Op op, 64 bool antiAlias, uint8_t alpha) { 65 66 SkPaint paint; 67 if (stroke.isHairlineStyle()) { 68 paint.setStyle(SkPaint::kStroke_Style); 69 paint.setStrokeWidth(SK_Scalar1); 70 } else { 71 if (stroke.isFillStyle()) { 72 paint.setStyle(SkPaint::kFill_Style); 73 } else { 74 paint.setStyle(SkPaint::kStroke_Style); 75 paint.setStrokeJoin(stroke.getJoin()); 76 paint.setStrokeCap(stroke.getCap()); 77 paint.setStrokeWidth(stroke.getWidth()); 78 } 79 } 80 paint.setAntiAlias(antiAlias); 81 82 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 } 90 } 91 92 bool GrSWMaskHelper::init(const SkIRect& resultBounds, 93 const SkMatrix* matrix) { 94 if (NULL != matrix) { 95 fMatrix = *matrix; 96 } else { 97 fMatrix.setIdentity(); 98 } 99 100 // Now translate so the bound's UL corner is at the origin 101 fMatrix.postTranslate(-resultBounds.fLeft * SK_Scalar1, 102 -resultBounds.fTop * SK_Scalar1); 103 SkIRect bounds = SkIRect::MakeWH(resultBounds.width(), 104 resultBounds.height()); 105 106 if (!fBM.allocPixels(SkImageInfo::MakeA8(bounds.fRight, bounds.fBottom))) { 107 return false; 108 } 109 sk_bzero(fBM.getPixels(), fBM.getSafeSize()); 110 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 /** 121 * Get a texture (from the texture cache) of the correct size & format. 122 * Return true on success; false on failure. 123 */ 124 bool GrSWMaskHelper::getTexture(GrAutoScratchTexture* texture) { 125 GrTextureDesc desc; 126 desc.fWidth = fBM.width(); 127 desc.fHeight = fBM.height(); 128 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 139 140 texture->set(fContext, desc); 141 return NULL != texture->texture(); 142 } 143 144 void 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(); 149 150 // 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); 157 } 158 159 /** 160 * Move the result of the software mask generation back to the gpu 161 */ 162 void GrSWMaskHelper::toTexture(GrTexture *texture) { 163 SkAutoLockPixels alp(fBM); 164 165 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); 175 176 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 } 181 } 182 183 //////////////////////////////////////////////////////////////////////////////// 184 /** 185 * Software rasterizes path to A8 mask (possibly using the context's matrix) 186 * and uploads the result to a scratch texture. Returns the resulting 187 * texture on success; NULL on failure. 188 */ 189 GrTexture* GrSWMaskHelper::DrawPathMaskToTexture(GrContext* context, 190 const SkPath& path, 191 const SkStrokeRec& stroke, 192 const SkIRect& resultBounds, 193 bool antiAlias, 194 SkMatrix* matrix) { 195 GrSWMaskHelper helper(context); 196 197 if (!helper.init(resultBounds, matrix)) { 198 return NULL; 199 } 200 201 helper.draw(path, stroke, SkRegion::kReplace_Op, antiAlias, 0xFF); 202 203 GrAutoScratchTexture ast; 204 if (!helper.getTexture(&ast)) { 205 return NULL; 206 } 207 208 helper.toTexture(ast.texture()); 209 210 return ast.detach(); 211 } 212 213 void GrSWMaskHelper::DrawToTargetWithPathMask(GrTexture* texture, 214 GrDrawTarget* target, 215 const SkIRect& rect) { 216 GrDrawState* drawState = target->drawState(); 217 218 GrDrawState::AutoViewMatrixRestore avmr; 219 if (!avmr.setIdentity(drawState)) { 220 return; 221 } 222 GrDrawState::AutoRestoreEffects are(drawState); 223 224 SkRect dstRect = SkRect::MakeLTRB(SK_Scalar1 * rect.fLeft, 225 SK_Scalar1 * rect.fTop, 226 SK_Scalar1 * rect.fRight, 227 SK_Scalar1 * rect.fBottom); 228 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 238 drawState->addCoverageEffect( 239 GrSimpleTextureEffect::Create(texture, 240 maskMatrix, 241 GrTextureParams::kNone_FilterMode, 242 kPosition_GrCoordSet))->unref(); 243 244 target->drawSimpleRect(dstRect); 245 } 246