/* * Copyright 2012 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "GrSWMaskHelper.h" #include "GrDrawState.h" #include "GrGpu.h" #include "SkStrokeRec.h" // TODO: try to remove this #include #include "GrContext.h" namespace { /* * Convert a boolean operation into a transfer mode code */ SkXfermode::Mode op_to_mode(SkRegion::Op op) { static const SkXfermode::Mode modeMap[] = { SkXfermode::kDstOut_Mode, // kDifference_Op SkXfermode::kMultiply_Mode, // kIntersect_Op SkXfermode::kSrcOver_Mode, // kUnion_Op SkXfermode::kXor_Mode, // kXOR_Op SkXfermode::kClear_Mode, // kReverseDifference_Op SkXfermode::kSrc_Mode, // kReplace_Op }; return modeMap[op]; } } /** * Draw a single rect element of the clip stack into the accumulation bitmap */ void GrSWMaskHelper::draw(const GrRect& rect, SkRegion::Op op, bool antiAlias, uint8_t alpha) { SkPaint paint; SkXfermode* mode = SkXfermode::Create(op_to_mode(op)); paint.setXfermode(mode); paint.setAntiAlias(antiAlias); paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha)); fDraw.drawRect(rect, paint); SkSafeUnref(mode); } /** * Draw a single path element of the clip stack into the accumulation bitmap */ void GrSWMaskHelper::draw(const SkPath& path, const SkStrokeRec& stroke, SkRegion::Op op, bool antiAlias, uint8_t alpha) { SkPaint paint; if (stroke.isHairlineStyle()) { paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(SK_Scalar1); } else { if (stroke.isFillStyle()) { paint.setStyle(SkPaint::kFill_Style); } else { paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeJoin(stroke.getJoin()); paint.setStrokeCap(stroke.getCap()); paint.setStrokeWidth(stroke.getWidth()); } } SkXfermode* mode = SkXfermode::Create(op_to_mode(op)); paint.setXfermode(mode); paint.setAntiAlias(antiAlias); paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha)); fDraw.drawPath(path, paint); SkSafeUnref(mode); } bool GrSWMaskHelper::init(const GrIRect& resultBounds, const SkMatrix* matrix) { if (NULL != matrix) { fMatrix = *matrix; } else { fMatrix.setIdentity(); } // Now translate so the bound's UL corner is at the origin fMatrix.postTranslate(-resultBounds.fLeft * SK_Scalar1, -resultBounds.fTop * SK_Scalar1); GrIRect bounds = GrIRect::MakeWH(resultBounds.width(), resultBounds.height()); fBM.setConfig(SkBitmap::kA8_Config, bounds.fRight, bounds.fBottom); if (!fBM.allocPixels()) { return false; } sk_bzero(fBM.getPixels(), fBM.getSafeSize()); sk_bzero(&fDraw, sizeof(fDraw)); fRasterClip.setRect(bounds); fDraw.fRC = &fRasterClip; fDraw.fClip = &fRasterClip.bwRgn(); fDraw.fMatrix = &fMatrix; fDraw.fBitmap = &fBM; return true; } /** * Get a texture (from the texture cache) of the correct size & format. * Return true on success; false on failure. */ bool GrSWMaskHelper::getTexture(GrAutoScratchTexture* texture) { GrTextureDesc desc; desc.fWidth = fBM.width(); desc.fHeight = fBM.height(); desc.fConfig = kAlpha_8_GrPixelConfig; texture->set(fContext, desc); return NULL != texture->texture(); } /** * Move the result of the software mask generation back to the gpu */ void GrSWMaskHelper::toTexture(GrTexture *texture, uint8_t alpha) { SkAutoLockPixels alp(fBM); // The destination texture is almost always larger than "fBM". Clear // it appropriately so we don't get mask artifacts outside of the path's // bounding box // "texture" needs to be installed as the render target for the clear // and the texture upload but cannot remain the render target upon // return. Callers typically use it as a texture and it would then // be both source and dest. GrDrawState::AutoRenderTargetRestore artr(fContext->getGpu()->drawState(), texture->asRenderTarget()); fContext->getGpu()->clear(NULL, GrColorPackRGBA(alpha, alpha, alpha, alpha)); texture->writePixels(0, 0, fBM.width(), fBM.height(), kAlpha_8_GrPixelConfig, fBM.getPixels(), fBM.rowBytes()); } //////////////////////////////////////////////////////////////////////////////// /** * Software rasterizes path to A8 mask (possibly using the context's matrix) * and uploads the result to a scratch texture. Returns the resulting * texture on success; NULL on failure. */ GrTexture* GrSWMaskHelper::DrawPathMaskToTexture(GrContext* context, const SkPath& path, const SkStrokeRec& stroke, const GrIRect& resultBounds, bool antiAlias, SkMatrix* matrix) { GrAutoScratchTexture ast; GrSWMaskHelper helper(context); if (!helper.init(resultBounds, matrix)) { return NULL; } helper.draw(path, stroke, SkRegion::kReplace_Op, antiAlias, 0xFF); if (!helper.getTexture(&ast)) { return NULL; } helper.toTexture(ast.texture(), 0x00); return ast.detach(); } void GrSWMaskHelper::DrawToTargetWithPathMask(GrTexture* texture, GrDrawTarget* target, const GrIRect& rect) { GrDrawState* drawState = target->drawState(); GrDrawState::AutoDeviceCoordDraw adcd(drawState); if (!adcd.succeeded()) { return; } enum { // the SW path renderer shares this stage with glyph // rendering (kGlyphMaskStage in GrBatchedTextContext) kPathMaskStage = GrPaint::kTotalStages, }; GrAssert(!drawState->isStageEnabled(kPathMaskStage)); drawState->stage(kPathMaskStage)->reset(); drawState->createTextureEffect(kPathMaskStage, texture, SkMatrix::I()); SkScalar w = SkIntToScalar(rect.width()); SkScalar h = SkIntToScalar(rect.height()); GrRect maskRect = GrRect::MakeWH(w / texture->width(), h / texture->height()); const GrRect* srcRects[GrDrawState::kNumStages] = { NULL }; srcRects[kPathMaskStage] = &maskRect; GrRect dstRect = GrRect::MakeLTRB( SK_Scalar1 * rect.fLeft, SK_Scalar1 * rect.fTop, SK_Scalar1 * rect.fRight, SK_Scalar1 * rect.fBottom); target->drawRect(dstRect, NULL, srcRects, NULL); drawState->disableStage(kPathMaskStage); }