michael@0: /* michael@0: * Copyright 2012 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "GrSWMaskHelper.h" michael@0: #include "GrDrawState.h" michael@0: #include "GrDrawTargetCaps.h" michael@0: #include "GrGpu.h" michael@0: michael@0: #include "SkStrokeRec.h" michael@0: michael@0: // TODO: try to remove this #include michael@0: #include "GrContext.h" michael@0: michael@0: namespace { michael@0: /* michael@0: * Convert a boolean operation into a transfer mode code michael@0: */ michael@0: SkXfermode::Mode op_to_mode(SkRegion::Op op) { michael@0: michael@0: static const SkXfermode::Mode modeMap[] = { michael@0: SkXfermode::kDstOut_Mode, // kDifference_Op michael@0: SkXfermode::kModulate_Mode, // kIntersect_Op michael@0: SkXfermode::kSrcOver_Mode, // kUnion_Op michael@0: SkXfermode::kXor_Mode, // kXOR_Op michael@0: SkXfermode::kClear_Mode, // kReverseDifference_Op michael@0: SkXfermode::kSrc_Mode, // kReplace_Op michael@0: }; michael@0: michael@0: return modeMap[op]; michael@0: } michael@0: michael@0: } michael@0: michael@0: /** michael@0: * Draw a single rect element of the clip stack into the accumulation bitmap michael@0: */ michael@0: void GrSWMaskHelper::draw(const SkRect& rect, SkRegion::Op op, michael@0: bool antiAlias, uint8_t alpha) { michael@0: SkPaint paint; michael@0: michael@0: SkXfermode* mode = SkXfermode::Create(op_to_mode(op)); michael@0: michael@0: paint.setXfermode(mode); michael@0: paint.setAntiAlias(antiAlias); michael@0: paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha)); michael@0: michael@0: fDraw.drawRect(rect, paint); michael@0: michael@0: SkSafeUnref(mode); michael@0: } michael@0: michael@0: /** michael@0: * Draw a single path element of the clip stack into the accumulation bitmap michael@0: */ michael@0: void GrSWMaskHelper::draw(const SkPath& path, const SkStrokeRec& stroke, SkRegion::Op op, michael@0: bool antiAlias, uint8_t alpha) { michael@0: michael@0: SkPaint paint; michael@0: if (stroke.isHairlineStyle()) { michael@0: paint.setStyle(SkPaint::kStroke_Style); michael@0: paint.setStrokeWidth(SK_Scalar1); michael@0: } else { michael@0: if (stroke.isFillStyle()) { michael@0: paint.setStyle(SkPaint::kFill_Style); michael@0: } else { michael@0: paint.setStyle(SkPaint::kStroke_Style); michael@0: paint.setStrokeJoin(stroke.getJoin()); michael@0: paint.setStrokeCap(stroke.getCap()); michael@0: paint.setStrokeWidth(stroke.getWidth()); michael@0: } michael@0: } michael@0: paint.setAntiAlias(antiAlias); michael@0: michael@0: if (SkRegion::kReplace_Op == op && 0xFF == alpha) { michael@0: SkASSERT(0xFF == paint.getAlpha()); michael@0: fDraw.drawPathCoverage(path, paint); michael@0: } else { michael@0: paint.setXfermodeMode(op_to_mode(op)); michael@0: paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha)); michael@0: fDraw.drawPath(path, paint); michael@0: } michael@0: } michael@0: michael@0: bool GrSWMaskHelper::init(const SkIRect& resultBounds, michael@0: const SkMatrix* matrix) { michael@0: if (NULL != matrix) { michael@0: fMatrix = *matrix; michael@0: } else { michael@0: fMatrix.setIdentity(); michael@0: } michael@0: michael@0: // Now translate so the bound's UL corner is at the origin michael@0: fMatrix.postTranslate(-resultBounds.fLeft * SK_Scalar1, michael@0: -resultBounds.fTop * SK_Scalar1); michael@0: SkIRect bounds = SkIRect::MakeWH(resultBounds.width(), michael@0: resultBounds.height()); michael@0: michael@0: if (!fBM.allocPixels(SkImageInfo::MakeA8(bounds.fRight, bounds.fBottom))) { michael@0: return false; michael@0: } michael@0: sk_bzero(fBM.getPixels(), fBM.getSafeSize()); michael@0: michael@0: sk_bzero(&fDraw, sizeof(fDraw)); michael@0: fRasterClip.setRect(bounds); michael@0: fDraw.fRC = &fRasterClip; michael@0: fDraw.fClip = &fRasterClip.bwRgn(); michael@0: fDraw.fMatrix = &fMatrix; michael@0: fDraw.fBitmap = &fBM; michael@0: return true; michael@0: } michael@0: michael@0: /** michael@0: * Get a texture (from the texture cache) of the correct size & format. michael@0: * Return true on success; false on failure. michael@0: */ michael@0: bool GrSWMaskHelper::getTexture(GrAutoScratchTexture* texture) { michael@0: GrTextureDesc desc; michael@0: desc.fWidth = fBM.width(); michael@0: desc.fHeight = fBM.height(); michael@0: desc.fConfig = kAlpha_8_GrPixelConfig; michael@0: michael@0: texture->set(fContext, desc); michael@0: return NULL != texture->texture(); michael@0: } michael@0: michael@0: /** michael@0: * Move the result of the software mask generation back to the gpu michael@0: */ michael@0: void GrSWMaskHelper::toTexture(GrTexture *texture) { michael@0: SkAutoLockPixels alp(fBM); michael@0: michael@0: // If we aren't reusing scratch textures we don't need to flush before michael@0: // writing since no one else will be using 'texture' michael@0: bool reuseScratch = fContext->getGpu()->caps()->reuseScratchTextures(); michael@0: michael@0: // Since we're uploading to it, 'texture' shouldn't have a render target. michael@0: SkASSERT(NULL == texture->asRenderTarget()); michael@0: michael@0: texture->writePixels(0, 0, fBM.width(), fBM.height(), michael@0: kAlpha_8_GrPixelConfig, michael@0: fBM.getPixels(), fBM.rowBytes(), michael@0: reuseScratch ? 0 : GrContext::kDontFlush_PixelOpsFlag); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: /** michael@0: * Software rasterizes path to A8 mask (possibly using the context's matrix) michael@0: * and uploads the result to a scratch texture. Returns the resulting michael@0: * texture on success; NULL on failure. michael@0: */ michael@0: GrTexture* GrSWMaskHelper::DrawPathMaskToTexture(GrContext* context, michael@0: const SkPath& path, michael@0: const SkStrokeRec& stroke, michael@0: const SkIRect& resultBounds, michael@0: bool antiAlias, michael@0: SkMatrix* matrix) { michael@0: GrAutoScratchTexture ast; michael@0: michael@0: GrSWMaskHelper helper(context); michael@0: michael@0: if (!helper.init(resultBounds, matrix)) { michael@0: return NULL; michael@0: } michael@0: michael@0: helper.draw(path, stroke, SkRegion::kReplace_Op, antiAlias, 0xFF); michael@0: michael@0: if (!helper.getTexture(&ast)) { michael@0: return NULL; michael@0: } michael@0: michael@0: helper.toTexture(ast.texture()); michael@0: michael@0: return ast.detach(); michael@0: } michael@0: michael@0: void GrSWMaskHelper::DrawToTargetWithPathMask(GrTexture* texture, michael@0: GrDrawTarget* target, michael@0: const SkIRect& rect) { michael@0: GrDrawState* drawState = target->drawState(); michael@0: michael@0: GrDrawState::AutoViewMatrixRestore avmr; michael@0: if (!avmr.setIdentity(drawState)) { michael@0: return; michael@0: } michael@0: GrDrawState::AutoRestoreEffects are(drawState); michael@0: michael@0: SkRect dstRect = SkRect::MakeLTRB(SK_Scalar1 * rect.fLeft, michael@0: SK_Scalar1 * rect.fTop, michael@0: SK_Scalar1 * rect.fRight, michael@0: SK_Scalar1 * rect.fBottom); michael@0: michael@0: // We want to use device coords to compute the texture coordinates. We set our matrix to be michael@0: // equal to the view matrix followed by a translation so that the top-left of the device bounds michael@0: // maps to 0,0, and then a scaling matrix to normalized coords. We apply this matrix to the michael@0: // vertex positions rather than local coords. michael@0: SkMatrix maskMatrix; michael@0: maskMatrix.setIDiv(texture->width(), texture->height()); michael@0: maskMatrix.preTranslate(SkIntToScalar(-rect.fLeft), SkIntToScalar(-rect.fTop)); michael@0: maskMatrix.preConcat(drawState->getViewMatrix()); michael@0: michael@0: drawState->addCoverageEffect( michael@0: GrSimpleTextureEffect::Create(texture, michael@0: maskMatrix, michael@0: GrTextureParams::kNone_FilterMode, michael@0: kPosition_GrCoordSet))->unref(); michael@0: michael@0: target->drawSimpleRect(dstRect); michael@0: }