From 11a253b686d2f6e9def2de8a4507591fcadfe987 Mon Sep 17 00:00:00 2001 From: "skia.committer@gmail.com" Date: Tue, 12 Nov 2013 07:02:05 +0000 Subject: Sanitizing source files in Housekeeper-Nightly git-svn-id: http://skia.googlecode.com/svn/trunk@12232 2bbb7eff-a529-9590-31e7-b0007b416f81 --- src/gpu/GrDistanceFieldTextContext.cpp | 4 +- src/gpu/GrTextStrike.cpp | 17 +- src/gpu/SkGpuDevice.cpp | 3718 +++++++++++----------- src/gpu/effects/GrDistanceFieldTextureEffect.cpp | 4 +- src/gpu/effects/GrDistanceFieldTextureEffect.h | 2 +- 5 files changed, 1872 insertions(+), 1873 deletions(-) (limited to 'src/gpu') diff --git a/src/gpu/GrDistanceFieldTextContext.cpp b/src/gpu/GrDistanceFieldTextContext.cpp index 87be8cad37..2f67c4915a 100755 --- a/src/gpu/GrDistanceFieldTextContext.cpp +++ b/src/gpu/GrDistanceFieldTextContext.cpp @@ -23,7 +23,7 @@ SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false, "Dump the contents of the font cache before every purge."); -GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context, +GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context, const GrPaint& paint, SkColor color, SkScalar textRatio) @@ -137,7 +137,7 @@ void GrDistanceFieldTextContext::drawPackedGlyph(GrGlyph::PackedID packed, // not valid, need to find a different solution for this vx += SkIntToFixed(glyph->fBounds.fLeft); vy += SkIntToFixed(glyph->fBounds.fTop); - + // keep them as ints until we've done the clip-test GrFixed width = glyph->fBounds.width(); GrFixed height = glyph->fBounds.height(); diff --git a/src/gpu/GrTextStrike.cpp b/src/gpu/GrTextStrike.cpp index 609cb5b6ab..ddab1e95a9 100644 --- a/src/gpu/GrTextStrike.cpp +++ b/src/gpu/GrTextStrike.cpp @@ -361,12 +361,12 @@ bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) { for (int i = 0; i < dfWidth*dfHeight; ++i) { unsigned char val; double outerval = outerDist[i]; - if (outerval < 0.0) { - outerval = 0.0; + if (outerval < 0.0) { + outerval = 0.0; } double innerval = innerDist[i]; - if (innerval < 0.0) { - innerval = 0.0; + if (innerval < 0.0) { + innerval = 0.0; } double dist = outerval - innerval; if (dist <= -DISTANCE_FIELD_RANGE) { @@ -378,9 +378,9 @@ bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) { } *dfPtr++ = val; } - + // copy to atlas - plot = fAtlasMgr->addToAtlas(&fAtlas, dfWidth, dfHeight, dfStorage.get(), + plot = fAtlasMgr->addToAtlas(&fAtlas, dfWidth, dfHeight, dfStorage.get(), &glyph->fAtlasLocation); } else { @@ -394,8 +394,8 @@ bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) { return false; } - plot = fAtlasMgr->addToAtlas(&fAtlas, glyph->width(), - glyph->height(), storage.get(), + plot = fAtlasMgr->addToAtlas(&fAtlas, glyph->width(), + glyph->height(), storage.get(), &glyph->fAtlasLocation); #if SK_DISTANCEFIELD_FONTS } @@ -408,4 +408,3 @@ bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) { glyph->fPlot = plot; return true; } - diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 92ac326fd9..429ee9f8e2 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -1,1859 +1,1859 @@ -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkGpuDevice.h" - -#include "effects/GrTextureDomainEffect.h" -#include "effects/GrSimpleTextureEffect.h" - -#include "GrContext.h" -#include "GrBitmapTextContext.h" -#if SK_DISTANCEFIELD_FONTS -#include "GrDistanceFieldTextContext.h" -#endif - -#include "SkGrTexturePixelRef.h" - -#include "SkColorFilter.h" -#include "SkDeviceImageFilterProxy.h" -#include "SkDrawProcs.h" -#include "SkGlyphCache.h" -#include "SkImageFilter.h" -#include "SkPathEffect.h" -#include "SkRRect.h" -#include "SkStroke.h" -#include "SkUtils.h" -#include "SkErrorInternals.h" - -#define CACHE_COMPATIBLE_DEVICE_TEXTURES 1 - -#if 0 - extern bool (*gShouldDrawProc)(); - #define CHECK_SHOULD_DRAW(draw, forceI) \ - do { \ - if (gShouldDrawProc && !gShouldDrawProc()) return; \ - this->prepareDraw(draw, forceI); \ - } while (0) -#else - #define CHECK_SHOULD_DRAW(draw, forceI) this->prepareDraw(draw, forceI) -#endif - -// This constant represents the screen alignment criterion in texels for -// requiring texture domain clamping to prevent color bleeding when drawing -// a sub region of a larger source image. -#define COLOR_BLEED_TOLERANCE SkFloatToScalar(0.001f) - -#define DO_DEFERRED_CLEAR() \ - do { \ - if (fNeedClear) { \ - this->clear(SK_ColorTRANSPARENT); \ - } \ - } while (false) \ - -/////////////////////////////////////////////////////////////////////////////// - -#define CHECK_FOR_ANNOTATION(paint) \ - do { if (paint.getAnnotation()) { return; } } while (0) - -/////////////////////////////////////////////////////////////////////////////// - - -class SkGpuDevice::SkAutoCachedTexture : public ::SkNoncopyable { -public: - SkAutoCachedTexture() - : fDevice(NULL) - , fTexture(NULL) { - } - - SkAutoCachedTexture(SkGpuDevice* device, - const SkBitmap& bitmap, - const GrTextureParams* params, - GrTexture** texture) - : fDevice(NULL) - , fTexture(NULL) { - SkASSERT(NULL != texture); - *texture = this->set(device, bitmap, params); - } - - ~SkAutoCachedTexture() { - if (NULL != fTexture) { - GrUnlockAndUnrefCachedBitmapTexture(fTexture); - } - } - - GrTexture* set(SkGpuDevice* device, - const SkBitmap& bitmap, - const GrTextureParams* params) { - if (NULL != fTexture) { - GrUnlockAndUnrefCachedBitmapTexture(fTexture); - fTexture = NULL; - } - fDevice = device; - GrTexture* result = (GrTexture*)bitmap.getTexture(); - if (NULL == result) { - // Cannot return the native texture so look it up in our cache - fTexture = GrLockAndRefCachedBitmapTexture(device->context(), bitmap, params); - result = fTexture; - } - return result; - } - -private: - SkGpuDevice* fDevice; - GrTexture* fTexture; -}; - -/////////////////////////////////////////////////////////////////////////////// - -struct GrSkDrawProcs : public SkDrawProcs { -public: - GrContext* fContext; - GrTextContext* fTextContext; - GrFontScaler* fFontScaler; // cached in the skia glyphcache -}; - -/////////////////////////////////////////////////////////////////////////////// - -static SkBitmap::Config grConfig2skConfig(GrPixelConfig config, bool* isOpaque) { - switch (config) { - case kAlpha_8_GrPixelConfig: - *isOpaque = false; - return SkBitmap::kA8_Config; - case kRGB_565_GrPixelConfig: - *isOpaque = true; - return SkBitmap::kRGB_565_Config; - case kRGBA_4444_GrPixelConfig: - *isOpaque = false; - return SkBitmap::kARGB_4444_Config; - case kSkia8888_GrPixelConfig: - // we don't currently have a way of knowing whether - // a 8888 is opaque based on the config. - *isOpaque = false; - return SkBitmap::kARGB_8888_Config; - default: - *isOpaque = false; - return SkBitmap::kNo_Config; - } -} - -/* - * GrRenderTarget does not know its opaqueness, only its config, so we have - * to make conservative guesses when we return an "equivalent" bitmap. - */ -static SkBitmap make_bitmap(GrContext* context, GrRenderTarget* renderTarget) { - bool isOpaque; - SkBitmap::Config config = grConfig2skConfig(renderTarget->config(), &isOpaque); - - SkBitmap bitmap; - bitmap.setConfig(config, renderTarget->width(), renderTarget->height(), 0, - isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType); - return bitmap; -} - -SkGpuDevice* SkGpuDevice::Create(GrSurface* surface) { - SkASSERT(NULL != surface); - if (NULL == surface->asRenderTarget() || NULL == surface->getContext()) { - return NULL; - } - if (surface->asTexture()) { - return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asTexture())); - } else { - return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asRenderTarget())); - } -} - -SkGpuDevice::SkGpuDevice(GrContext* context, GrTexture* texture) - : SkBitmapDevice(make_bitmap(context, texture->asRenderTarget())) { - this->initFromRenderTarget(context, texture->asRenderTarget(), false); -} - -SkGpuDevice::SkGpuDevice(GrContext* context, GrRenderTarget* renderTarget) - : SkBitmapDevice(make_bitmap(context, renderTarget)) { - this->initFromRenderTarget(context, renderTarget, false); -} - -void SkGpuDevice::initFromRenderTarget(GrContext* context, - GrRenderTarget* renderTarget, - bool cached) { - fDrawProcs = NULL; - - fContext = context; - fContext->ref(); - - fRenderTarget = NULL; - fNeedClear = false; - - SkASSERT(NULL != renderTarget); - fRenderTarget = renderTarget; - fRenderTarget->ref(); - - // Hold onto to the texture in the pixel ref (if there is one) because the texture holds a ref - // on the RT but not vice-versa. - // TODO: Remove this trickery once we figure out how to make SkGrPixelRef do this without - // busting chrome (for a currently unknown reason). - GrSurface* surface = fRenderTarget->asTexture(); - if (NULL == surface) { - surface = fRenderTarget; - } - SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (surface, cached)); - - this->setPixelRef(pr, 0)->unref(); -} - -SkGpuDevice::SkGpuDevice(GrContext* context, - SkBitmap::Config config, - int width, - int height, - int sampleCount) - : SkBitmapDevice(config, width, height, false /*isOpaque*/) { - - fDrawProcs = NULL; - - fContext = context; - fContext->ref(); - - fRenderTarget = NULL; - fNeedClear = false; - - if (config != SkBitmap::kRGB_565_Config) { - config = SkBitmap::kARGB_8888_Config; - } - - GrTextureDesc desc; - desc.fFlags = kRenderTarget_GrTextureFlagBit; - desc.fWidth = width; - desc.fHeight = height; - desc.fConfig = SkBitmapConfig2GrPixelConfig(config); - desc.fSampleCnt = sampleCount; - - SkAutoTUnref texture(fContext->createUncachedTexture(desc, NULL, 0)); - - if (NULL != texture) { - fRenderTarget = texture->asRenderTarget(); - fRenderTarget->ref(); - - SkASSERT(NULL != fRenderTarget); - - // wrap the bitmap with a pixelref to expose our texture - SkGrPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (texture)); - this->setPixelRef(pr, 0)->unref(); - } else { - GrPrintf("--- failed to create gpu-offscreen [%d %d]\n", - width, height); - SkASSERT(false); - } -} - -SkGpuDevice::~SkGpuDevice() { - if (fDrawProcs) { - delete fDrawProcs; - } - - // The GrContext takes a ref on the target. We don't want to cause the render - // target to be unnecessarily kept alive. - if (fContext->getRenderTarget() == fRenderTarget) { - fContext->setRenderTarget(NULL); - } - - if (fContext->getClip() == &fClipData) { - fContext->setClip(NULL); - } - - SkSafeUnref(fRenderTarget); - fContext->unref(); -} - -/////////////////////////////////////////////////////////////////////////////// - -void SkGpuDevice::makeRenderTargetCurrent() { - DO_DEFERRED_CLEAR(); - fContext->setRenderTarget(fRenderTarget); -} - -/////////////////////////////////////////////////////////////////////////////// - -namespace { -GrPixelConfig config8888_to_grconfig_and_flags(SkCanvas::Config8888 config8888, uint32_t* flags) { - switch (config8888) { - case SkCanvas::kNative_Premul_Config8888: - *flags = 0; - return kSkia8888_GrPixelConfig; - case SkCanvas::kNative_Unpremul_Config8888: - *flags = GrContext::kUnpremul_PixelOpsFlag; - return kSkia8888_GrPixelConfig; - case SkCanvas::kBGRA_Premul_Config8888: - *flags = 0; - return kBGRA_8888_GrPixelConfig; - case SkCanvas::kBGRA_Unpremul_Config8888: - *flags = GrContext::kUnpremul_PixelOpsFlag; - return kBGRA_8888_GrPixelConfig; - case SkCanvas::kRGBA_Premul_Config8888: - *flags = 0; - return kRGBA_8888_GrPixelConfig; - case SkCanvas::kRGBA_Unpremul_Config8888: - *flags = GrContext::kUnpremul_PixelOpsFlag; - return kRGBA_8888_GrPixelConfig; - default: - GrCrash("Unexpected Config8888."); - *flags = 0; // suppress warning - return kSkia8888_GrPixelConfig; - } -} -} - -bool SkGpuDevice::onReadPixels(const SkBitmap& bitmap, - int x, int y, - SkCanvas::Config8888 config8888) { - DO_DEFERRED_CLEAR(); - SkASSERT(SkBitmap::kARGB_8888_Config == bitmap.config()); - SkASSERT(!bitmap.isNull()); - SkASSERT(SkIRect::MakeWH(this->width(), this->height()).contains(SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))); - - SkAutoLockPixels alp(bitmap); - GrPixelConfig config; - uint32_t flags; - config = config8888_to_grconfig_and_flags(config8888, &flags); - return fContext->readRenderTargetPixels(fRenderTarget, - x, y, - bitmap.width(), - bitmap.height(), - config, - bitmap.getPixels(), - bitmap.rowBytes(), - flags); -} - -void SkGpuDevice::writePixels(const SkBitmap& bitmap, int x, int y, - SkCanvas::Config8888 config8888) { - SkAutoLockPixels alp(bitmap); - if (!bitmap.readyToDraw()) { - return; - } - - GrPixelConfig config; - uint32_t flags; - if (SkBitmap::kARGB_8888_Config == bitmap.config()) { - config = config8888_to_grconfig_and_flags(config8888, &flags); - } else { - flags = 0; - config= SkBitmapConfig2GrPixelConfig(bitmap.config()); - } - - fRenderTarget->writePixels(x, y, bitmap.width(), bitmap.height(), - config, bitmap.getPixels(), bitmap.rowBytes(), flags); -} - -void SkGpuDevice::onAttachToCanvas(SkCanvas* canvas) { - INHERITED::onAttachToCanvas(canvas); - - // Canvas promises that this ptr is valid until onDetachFromCanvas is called - fClipData.fClipStack = canvas->getClipStack(); -} - -void SkGpuDevice::onDetachFromCanvas() { - INHERITED::onDetachFromCanvas(); - fClipData.fClipStack = NULL; -} - -// call this every draw call, to ensure that the context reflects our state, -// and not the state from some other canvas/device -void SkGpuDevice::prepareDraw(const SkDraw& draw, bool forceIdentity) { - SkASSERT(NULL != fClipData.fClipStack); - - fContext->setRenderTarget(fRenderTarget); - - SkASSERT(draw.fClipStack && draw.fClipStack == fClipData.fClipStack); - - if (forceIdentity) { - fContext->setIdentityMatrix(); - } else { - fContext->setMatrix(*draw.fMatrix); - } - fClipData.fOrigin = this->getOrigin(); - - fContext->setClip(&fClipData); - - DO_DEFERRED_CLEAR(); -} - -GrRenderTarget* SkGpuDevice::accessRenderTarget() { - DO_DEFERRED_CLEAR(); - return fRenderTarget; -} - -/////////////////////////////////////////////////////////////////////////////// - -SK_COMPILE_ASSERT(SkShader::kNone_BitmapType == 0, shader_type_mismatch); -SK_COMPILE_ASSERT(SkShader::kDefault_BitmapType == 1, shader_type_mismatch); -SK_COMPILE_ASSERT(SkShader::kRadial_BitmapType == 2, shader_type_mismatch); -SK_COMPILE_ASSERT(SkShader::kSweep_BitmapType == 3, shader_type_mismatch); -SK_COMPILE_ASSERT(SkShader::kTwoPointRadial_BitmapType == 4, - shader_type_mismatch); -SK_COMPILE_ASSERT(SkShader::kTwoPointConical_BitmapType == 5, - shader_type_mismatch); -SK_COMPILE_ASSERT(SkShader::kLinear_BitmapType == 6, shader_type_mismatch); -SK_COMPILE_ASSERT(SkShader::kLast_BitmapType == 6, shader_type_mismatch); - -namespace { - -// converts a SkPaint to a GrPaint, ignoring the skPaint's shader -// justAlpha indicates that skPaint's alpha should be used rather than the color -// Callers may subsequently modify the GrPaint. Setting constantColor indicates -// that the final paint will draw the same color at every pixel. This allows -// an optimization where the the color filter can be applied to the skPaint's -// color once while converting to GrPaint and then ignored. -inline bool skPaint2GrPaintNoShader(SkGpuDevice* dev, - const SkPaint& skPaint, - bool justAlpha, - bool constantColor, - GrPaint* grPaint) { - - grPaint->setDither(skPaint.isDither()); - grPaint->setAntiAlias(skPaint.isAntiAlias()); - - SkXfermode::Coeff sm; - SkXfermode::Coeff dm; - - SkXfermode* mode = skPaint.getXfermode(); - GrEffectRef* xferEffect = NULL; - if (SkXfermode::AsNewEffectOrCoeff(mode, &xferEffect, &sm, &dm)) { - if (NULL != xferEffect) { - grPaint->addColorEffect(xferEffect)->unref(); - sm = SkXfermode::kOne_Coeff; - dm = SkXfermode::kZero_Coeff; - } - } else { - //SkDEBUGCODE(SkDebugf("Unsupported xfer mode.\n");) -#if 0 - return false; -#else - // Fall back to src-over - sm = SkXfermode::kOne_Coeff; - dm = SkXfermode::kISA_Coeff; -#endif - } - grPaint->setBlendFunc(sk_blend_to_grblend(sm), sk_blend_to_grblend(dm)); - - if (justAlpha) { - uint8_t alpha = skPaint.getAlpha(); - grPaint->setColor(GrColorPackRGBA(alpha, alpha, alpha, alpha)); - // justAlpha is currently set to true only if there is a texture, - // so constantColor should not also be true. - SkASSERT(!constantColor); - } else { - grPaint->setColor(SkColor2GrColor(skPaint.getColor())); - } - - SkColorFilter* colorFilter = skPaint.getColorFilter(); - if (NULL != colorFilter) { - // if the source color is a constant then apply the filter here once rather than per pixel - // in a shader. - if (constantColor) { - SkColor filtered = colorFilter->filterColor(skPaint.getColor()); - grPaint->setColor(SkColor2GrColor(filtered)); - } else { - SkAutoTUnref effect(colorFilter->asNewEffect(dev->context())); - if (NULL != effect.get()) { - grPaint->addColorEffect(effect); - } - } - } - - return true; -} - -// This function is similar to skPaint2GrPaintNoShader but also converts -// skPaint's shader to a GrTexture/GrEffectStage if possible. The texture to -// be used is set on grPaint and returned in param act. constantColor has the -// same meaning as in skPaint2GrPaintNoShader. -inline bool skPaint2GrPaintShader(SkGpuDevice* dev, - const SkPaint& skPaint, - bool constantColor, - GrPaint* grPaint) { - SkShader* shader = skPaint.getShader(); - if (NULL == shader) { - return skPaint2GrPaintNoShader(dev, skPaint, false, constantColor, grPaint); - } - - // SkShader::asNewEffect() may do offscreen rendering. Setup default drawing state - // Also require shader to set the render target . - GrContext::AutoWideOpenIdentityDraw awo(dev->context(), NULL); - GrContext::AutoRenderTarget(dev->context(), NULL); - - // setup the shader as the first color effect on the paint - SkAutoTUnref effect(shader->asNewEffect(dev->context(), skPaint)); - if (NULL != effect.get()) { - grPaint->addColorEffect(effect); - // Now setup the rest of the paint. - return skPaint2GrPaintNoShader(dev, skPaint, true, false, grPaint); - } else { - // We still don't have SkColorShader::asNewEffect() implemented. - SkShader::GradientInfo info; - SkColor color; - - info.fColors = &color; - info.fColorOffsets = NULL; - info.fColorCount = 1; - if (SkShader::kColor_GradientType == shader->asAGradient(&info)) { - SkPaint copy(skPaint); - copy.setShader(NULL); - // modulate the paint alpha by the shader's solid color alpha - U8CPU newA = SkMulDiv255Round(SkColorGetA(color), copy.getAlpha()); - copy.setColor(SkColorSetA(color, newA)); - return skPaint2GrPaintNoShader(dev, copy, false, constantColor, grPaint); - } else { - return false; - } - } -} -} - -/////////////////////////////////////////////////////////////////////////////// -void SkGpuDevice::getGlobalBounds(SkIRect* bounds) const { - if (NULL != bounds) { - const SkIPoint& origin = this->getOrigin(); - bounds->setXYWH(origin.x(), origin.y(), - this->width(), this->height()); - } -} - -SkBitmap::Config SkGpuDevice::config() const { - if (NULL == fRenderTarget) { - return SkBitmap::kNo_Config; - } - - bool isOpaque; - return grConfig2skConfig(fRenderTarget->config(), &isOpaque); -} - -void SkGpuDevice::clear(SkColor color) { - SkIRect rect = SkIRect::MakeWH(this->width(), this->height()); - fContext->clear(&rect, SkColor2GrColor(color), true, fRenderTarget); - fNeedClear = false; -} - -void SkGpuDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) { - CHECK_SHOULD_DRAW(draw, false); - - GrPaint grPaint; - if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { - return; - } - - fContext->drawPaint(grPaint); -} - -// must be in SkCanvas::PointMode order -static const GrPrimitiveType gPointMode2PrimtiveType[] = { - kPoints_GrPrimitiveType, - kLines_GrPrimitiveType, - kLineStrip_GrPrimitiveType -}; - -void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, - size_t count, const SkPoint pts[], const SkPaint& paint) { - CHECK_FOR_ANNOTATION(paint); - CHECK_SHOULD_DRAW(draw, false); - - SkScalar width = paint.getStrokeWidth(); - if (width < 0) { - return; - } - - // we only handle hairlines and paints without path effects or mask filters, - // else we let the SkDraw call our drawPath() - if (width > 0 || paint.getPathEffect() || paint.getMaskFilter()) { - draw.drawPoints(mode, count, pts, paint, true); - return; - } - - GrPaint grPaint; - if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { - return; - } - - fContext->drawVertices(grPaint, - gPointMode2PrimtiveType[mode], - count, - (GrPoint*)pts, - NULL, - NULL, - NULL, - 0); -} - -/////////////////////////////////////////////////////////////////////////////// - -void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect, - const SkPaint& paint) { - CHECK_FOR_ANNOTATION(paint); - CHECK_SHOULD_DRAW(draw, false); - - bool doStroke = paint.getStyle() != SkPaint::kFill_Style; - SkScalar width = paint.getStrokeWidth(); - - /* - We have special code for hairline strokes, miter-strokes, bevel-stroke - and fills. Anything else we just call our path code. - */ - bool usePath = doStroke && width > 0 && - (paint.getStrokeJoin() == SkPaint::kRound_Join || - (paint.getStrokeJoin() == SkPaint::kBevel_Join && rect.isEmpty())); - // another two reasons we might need to call drawPath... - if (paint.getMaskFilter() || paint.getPathEffect()) { - usePath = true; - } - if (!usePath && paint.isAntiAlias() && !fContext->getMatrix().rectStaysRect()) { -#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT) - if (doStroke) { -#endif - usePath = true; -#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT) - } else { - usePath = !fContext->getMatrix().preservesRightAngles(); - } -#endif - } - // until we can both stroke and fill rectangles - if (paint.getStyle() == SkPaint::kStrokeAndFill_Style) { - usePath = true; - } - - if (usePath) { - SkPath path; - path.addRect(rect); - this->drawPath(draw, path, paint, NULL, true); - return; - } - - GrPaint grPaint; - if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { - return; - } - - if (!doStroke) { - fContext->drawRect(grPaint, rect); - } else { - SkStrokeRec stroke(paint); - fContext->drawRect(grPaint, rect, &stroke); - } -} - -/////////////////////////////////////////////////////////////////////////////// - -void SkGpuDevice::drawRRect(const SkDraw& draw, const SkRRect& rect, - const SkPaint& paint) { - CHECK_FOR_ANNOTATION(paint); - CHECK_SHOULD_DRAW(draw, false); - - bool usePath = !rect.isSimple(); - // another two reasons we might need to call drawPath... - if (paint.getMaskFilter() || paint.getPathEffect()) { - usePath = true; - } - // until we can rotate rrects... - if (!usePath && !fContext->getMatrix().rectStaysRect()) { - usePath = true; - } - - if (usePath) { - SkPath path; - path.addRRect(rect); - this->drawPath(draw, path, paint, NULL, true); - return; - } - - GrPaint grPaint; - if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { - return; - } - - SkStrokeRec stroke(paint); - fContext->drawRRect(grPaint, rect, stroke); -} - -/////////////////////////////////////////////////////////////////////////////// - -void SkGpuDevice::drawOval(const SkDraw& draw, const SkRect& oval, - const SkPaint& paint) { - CHECK_FOR_ANNOTATION(paint); - CHECK_SHOULD_DRAW(draw, false); - - bool usePath = false; - // some basic reasons we might need to call drawPath... - if (paint.getMaskFilter() || paint.getPathEffect()) { - usePath = true; - } - - if (usePath) { - SkPath path; - path.addOval(oval); - this->drawPath(draw, path, paint, NULL, true); - return; - } - - GrPaint grPaint; - if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { - return; - } - SkStrokeRec stroke(paint); - - fContext->drawOval(grPaint, oval, stroke); -} - -#include "SkMaskFilter.h" -#include "SkBounder.h" - -/////////////////////////////////////////////////////////////////////////////// - -// helpers for applying mask filters -namespace { - -// Draw a mask using the supplied paint. Since the coverage/geometry -// is already burnt into the mask this boils down to a rect draw. -// Return true if the mask was successfully drawn. -bool draw_mask(GrContext* context, const SkRect& maskRect, - GrPaint* grp, GrTexture* mask) { - GrContext::AutoMatrix am; - if (!am.setIdentity(context, grp)) { - return false; - } - - SkMatrix matrix; - matrix.setTranslate(-maskRect.fLeft, -maskRect.fTop); - matrix.postIDiv(mask->width(), mask->height()); - - grp->addCoverageEffect(GrSimpleTextureEffect::Create(mask, matrix))->unref(); - context->drawRect(*grp, maskRect); - return true; -} - -bool draw_with_mask_filter(GrContext* context, const SkPath& devPath, - SkMaskFilter* filter, const SkRegion& clip, SkBounder* bounder, - GrPaint* grp, SkPaint::Style style) { - SkMask srcM, dstM; - - if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), filter, &context->getMatrix(), &srcM, - SkMask::kComputeBoundsAndRenderImage_CreateMode, style)) { - return false; - } - SkAutoMaskFreeImage autoSrc(srcM.fImage); - - if (!filter->filterMask(&dstM, srcM, context->getMatrix(), NULL)) { - return false; - } - // this will free-up dstM when we're done (allocated in filterMask()) - SkAutoMaskFreeImage autoDst(dstM.fImage); - - if (clip.quickReject(dstM.fBounds)) { - return false; - } - if (bounder && !bounder->doIRect(dstM.fBounds)) { - return false; - } - - // we now have a device-aligned 8bit mask in dstM, ready to be drawn using - // the current clip (and identity matrix) and GrPaint settings - GrTextureDesc desc; - desc.fWidth = dstM.fBounds.width(); - desc.fHeight = dstM.fBounds.height(); - desc.fConfig = kAlpha_8_GrPixelConfig; - - GrAutoScratchTexture ast(context, desc); - GrTexture* texture = ast.texture(); - - if (NULL == texture) { - return false; - } - texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig, - dstM.fImage, dstM.fRowBytes); - - SkRect maskRect = SkRect::Make(dstM.fBounds); - - return draw_mask(context, maskRect, grp, texture); -} - -// Create a mask of 'devPath' and place the result in 'mask'. Return true on -// success; false otherwise. -bool create_mask_GPU(GrContext* context, - const SkRect& maskRect, - const SkPath& devPath, - const SkStrokeRec& stroke, - bool doAA, - GrAutoScratchTexture* mask) { - GrTextureDesc desc; - desc.fFlags = kRenderTarget_GrTextureFlagBit; - desc.fWidth = SkScalarCeilToInt(maskRect.width()); - desc.fHeight = SkScalarCeilToInt(maskRect.height()); - // We actually only need A8, but it often isn't supported as a - // render target so default to RGBA_8888 - desc.fConfig = kRGBA_8888_GrPixelConfig; - if (context->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { - desc.fConfig = kAlpha_8_GrPixelConfig; - } - - mask->set(context, desc); - if (NULL == mask->texture()) { - return false; - } - - GrTexture* maskTexture = mask->texture(); - SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height()); - - GrContext::AutoRenderTarget art(context, maskTexture->asRenderTarget()); - GrContext::AutoClip ac(context, clipRect); - - context->clear(NULL, 0x0, true); - - GrPaint tempPaint; - if (doAA) { - tempPaint.setAntiAlias(true); - // AA uses the "coverage" stages on GrDrawTarget. Coverage with a dst - // blend coeff of zero requires dual source blending support in order - // to properly blend partially covered pixels. This means the AA - // code path may not be taken. So we use a dst blend coeff of ISA. We - // could special case AA draws to a dst surface with known alpha=0 to - // use a zero dst coeff when dual source blending isn't available. - tempPaint.setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff); - } - - GrContext::AutoMatrix am; - - // Draw the mask into maskTexture with the path's top-left at the origin using tempPaint. - SkMatrix translate; - translate.setTranslate(-maskRect.fLeft, -maskRect.fTop); - am.set(context, translate); - context->drawPath(tempPaint, devPath, stroke); - return true; -} - -SkBitmap wrap_texture(GrTexture* texture) { - SkBitmap result; - bool dummy; - SkBitmap::Config config = grConfig2skConfig(texture->config(), &dummy); - result.setConfig(config, texture->width(), texture->height()); - result.setPixelRef(SkNEW_ARGS(SkGrPixelRef, (texture)))->unref(); - return result; -} - -}; - -void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath, - const SkPaint& paint, const SkMatrix* prePathMatrix, - bool pathIsMutable) { - CHECK_FOR_ANNOTATION(paint); - CHECK_SHOULD_DRAW(draw, false); - - GrPaint grPaint; - if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { - return; - } - - // can we cheat, and treat a thin stroke as a hairline w/ coverage - // if we can, we draw lots faster (raster device does this same test) - SkScalar hairlineCoverage; - bool doHairLine = SkDrawTreatAsHairline(paint, fContext->getMatrix(), &hairlineCoverage); - if (doHairLine) { - grPaint.setCoverage(SkScalarRoundToInt(hairlineCoverage * grPaint.getCoverage())); - } - - // If we have a prematrix, apply it to the path, optimizing for the case - // where the original path can in fact be modified in place (even though - // its parameter type is const). - SkPath* pathPtr = const_cast(&origSrcPath); - SkPath tmpPath, effectPath; - - if (prePathMatrix) { - SkPath* result = pathPtr; - - if (!pathIsMutable) { - result = &tmpPath; - pathIsMutable = true; - } - // should I push prePathMatrix on our MV stack temporarily, instead - // of applying it here? See SkDraw.cpp - pathPtr->transform(*prePathMatrix, result); - pathPtr = result; - } - // at this point we're done with prePathMatrix - SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;) - - SkStrokeRec stroke(paint); - SkPathEffect* pathEffect = paint.getPathEffect(); - const SkRect* cullRect = NULL; // TODO: what is our bounds? - if (pathEffect && pathEffect->filterPath(&effectPath, *pathPtr, &stroke, - cullRect)) { - pathPtr = &effectPath; - } - - if (!pathEffect && doHairLine) { - stroke.setHairlineStyle(); - } - - if (paint.getMaskFilter()) { - if (!stroke.isHairlineStyle()) { - if (stroke.applyToPath(&tmpPath, *pathPtr)) { - pathPtr = &tmpPath; - pathIsMutable = true; - stroke.setFillStyle(); - } - } - - // avoid possibly allocating a new path in transform if we can - SkPath* devPathPtr = pathIsMutable ? pathPtr : &tmpPath; - - // transform the path into device space - pathPtr->transform(fContext->getMatrix(), devPathPtr); - - SkRect maskRect; - if (paint.getMaskFilter()->canFilterMaskGPU(devPathPtr->getBounds(), - draw.fClip->getBounds(), - fContext->getMatrix(), - &maskRect)) { - SkIRect finalIRect; - maskRect.roundOut(&finalIRect); - if (draw.fClip->quickReject(finalIRect)) { - // clipped out - return; - } - if (NULL != draw.fBounder && !draw.fBounder->doIRect(finalIRect)) { - // nothing to draw - return; - } - - GrAutoScratchTexture mask; - - if (create_mask_GPU(fContext, maskRect, *devPathPtr, stroke, - grPaint.isAntiAlias(), &mask)) { - GrTexture* filtered; - - if (paint.getMaskFilter()->filterMaskGPU(mask.texture(), maskRect, &filtered, true)) { - // filterMaskGPU gives us ownership of a ref to the result - SkAutoTUnref atu(filtered); - - // If the scratch texture that we used as the filter src also holds the filter - // result then we must detach so that this texture isn't recycled for a later - // draw. - if (filtered == mask.texture()) { - mask.detach(); - filtered->unref(); // detach transfers GrAutoScratchTexture's ref to us. - } - - if (draw_mask(fContext, maskRect, &grPaint, filtered)) { - // This path is completely drawn - return; - } - } - } - } - - // draw the mask on the CPU - this is a fallthrough path in case the - // GPU path fails - SkPaint::Style style = stroke.isHairlineStyle() ? SkPaint::kStroke_Style : - SkPaint::kFill_Style; - draw_with_mask_filter(fContext, *devPathPtr, paint.getMaskFilter(), - *draw.fClip, draw.fBounder, &grPaint, style); - return; - } - - fContext->drawPath(grPaint, *pathPtr, stroke); -} - -static const int kBmpSmallTileSize = 1 << 10; - -static inline int get_tile_count(const SkIRect& srcRect, int tileSize) { - int tilesX = (srcRect.fRight / tileSize) - (srcRect.fLeft / tileSize) + 1; - int tilesY = (srcRect.fBottom / tileSize) - (srcRect.fTop / tileSize) + 1; - return tilesX * tilesY; -} - -static int determine_tile_size(const SkBitmap& bitmap, const SkIRect& src, int maxTileSize) { - if (maxTileSize <= kBmpSmallTileSize) { - return maxTileSize; - } - - size_t maxTileTotalTileSize = get_tile_count(src, maxTileSize); - size_t smallTotalTileSize = get_tile_count(src, kBmpSmallTileSize); - - maxTileTotalTileSize *= maxTileSize * maxTileSize; - smallTotalTileSize *= kBmpSmallTileSize * kBmpSmallTileSize; - - if (maxTileTotalTileSize > 2 * smallTotalTileSize) { - return kBmpSmallTileSize; - } else { - return maxTileSize; - } -} - -// Given a bitmap, an optional src rect, and a context with a clip and matrix determine what -// pixels from the bitmap are necessary. -static void determine_clipped_src_rect(const GrContext* context, - const SkBitmap& bitmap, - const SkRect* srcRectPtr, - SkIRect* clippedSrcIRect) { - const GrClipData* clip = context->getClip(); - clip->getConservativeBounds(context->getRenderTarget(), clippedSrcIRect, NULL); - SkMatrix inv; - if (!context->getMatrix().invert(&inv)) { - clippedSrcIRect->setEmpty(); - return; - } - SkRect clippedSrcRect = SkRect::Make(*clippedSrcIRect); - inv.mapRect(&clippedSrcRect); - if (NULL != srcRectPtr) { - if (!clippedSrcRect.intersect(*srcRectPtr)) { - clippedSrcIRect->setEmpty(); - return; - } - } - clippedSrcRect.roundOut(clippedSrcIRect); - SkIRect bmpBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height()); - if (!clippedSrcIRect->intersect(bmpBounds)) { - clippedSrcIRect->setEmpty(); - } -} - -bool SkGpuDevice::shouldTileBitmap(const SkBitmap& bitmap, - const GrTextureParams& params, - const SkRect* srcRectPtr, - int maxTileSize, - int* tileSize, - SkIRect* clippedSrcRect) const { - // if bitmap is explictly texture backed then just use the texture - if (NULL != bitmap.getTexture()) { - return false; - } - - // if it's larger than the max tile size, then we have no choice but tiling. - if (bitmap.width() > maxTileSize || bitmap.height() > maxTileSize) { - determine_clipped_src_rect(fContext, bitmap, srcRectPtr, clippedSrcRect); - *tileSize = determine_tile_size(bitmap, *clippedSrcRect, maxTileSize); - return true; - } - - if (bitmap.width() * bitmap.height() < 4 * kBmpSmallTileSize * kBmpSmallTileSize) { - return false; - } - - // if the entire texture is already in our cache then no reason to tile it - if (GrIsBitmapInCache(fContext, bitmap, ¶ms)) { - return false; - } - - // At this point we know we could do the draw by uploading the entire bitmap - // as a texture. However, if the texture would be large compared to the - // cache size and we don't require most of it for this draw then tile to - // reduce the amount of upload and cache spill. - - // assumption here is that sw bitmap size is a good proxy for its size as - // a texture - size_t bmpSize = bitmap.getSize(); - size_t cacheSize; - fContext->getTextureCacheLimits(NULL, &cacheSize); - if (bmpSize < cacheSize / 2) { - return false; - } - - // Figure out how much of the src we will need based on the src rect and clipping. - determine_clipped_src_rect(fContext, bitmap, srcRectPtr, clippedSrcRect); - *tileSize = kBmpSmallTileSize; // already know whole bitmap fits in one max sized tile. - size_t usedTileBytes = get_tile_count(*clippedSrcRect, kBmpSmallTileSize) * - kBmpSmallTileSize * kBmpSmallTileSize; - - return usedTileBytes < 2 * bmpSize; -} - -void SkGpuDevice::drawBitmap(const SkDraw& draw, - const SkBitmap& bitmap, - const SkMatrix& m, - const SkPaint& paint) { - // We cannot call drawBitmapRect here since 'm' could be anything - this->drawBitmapCommon(draw, bitmap, NULL, m, paint, - SkCanvas::kNone_DrawBitmapRectFlag); -} - -// This method outsets 'iRect' by 1 all around and then clamps its extents to -// 'clamp'. 'offset' is adjusted to remain positioned over the top-left corner -// of 'iRect' for all possible outsets/clamps. -static inline void clamped_unit_outset_with_offset(SkIRect* iRect, SkPoint* offset, - const SkIRect& clamp) { - iRect->outset(1, 1); - - if (iRect->fLeft < clamp.fLeft) { - iRect->fLeft = clamp.fLeft; - } else { - offset->fX -= SK_Scalar1; - } - if (iRect->fTop < clamp.fTop) { - iRect->fTop = clamp.fTop; - } else { - offset->fY -= SK_Scalar1; - } - - if (iRect->fRight > clamp.fRight) { - iRect->fRight = clamp.fRight; - } - if (iRect->fBottom > clamp.fBottom) { - iRect->fBottom = clamp.fBottom; - } -} - -void SkGpuDevice::drawBitmapCommon(const SkDraw& draw, - const SkBitmap& bitmap, - const SkRect* srcRectPtr, - const SkMatrix& m, - const SkPaint& paint, - SkCanvas::DrawBitmapRectFlags flags) { - CHECK_SHOULD_DRAW(draw, false); - - SkRect srcRect; - if (NULL == srcRectPtr) { - srcRect.set(0, 0, SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height())); - } else { - srcRect = *srcRectPtr; - } - - if (paint.getMaskFilter()){ - // Convert the bitmap to a shader so that the rect can be drawn - // through drawRect, which supports mask filters. - SkMatrix newM(m); - SkBitmap tmp; // subset of bitmap, if necessary - const SkBitmap* bitmapPtr = &bitmap; - if (NULL != srcRectPtr) { - SkIRect iSrc; - srcRect.roundOut(&iSrc); - - SkPoint offset = SkPoint::Make(SkIntToScalar(iSrc.fLeft), - SkIntToScalar(iSrc.fTop)); - - if (SkCanvas::kBleed_DrawBitmapRectFlag & flags) { - // In bleed mode we want to expand the src rect on all sides - // but stay within the bitmap bounds - SkIRect iClampRect = SkIRect::MakeWH(bitmap.width(), bitmap.height()); - clamped_unit_outset_with_offset(&iSrc, &offset, iClampRect); - } - - if (!bitmap.extractSubset(&tmp, iSrc)) { - return; // extraction failed - } - bitmapPtr = &tmp; - srcRect.offset(-offset.fX, -offset.fY); - // The source rect has changed so update the matrix - newM.preTranslate(offset.fX, offset.fY); - } - - SkPaint paintWithTexture(paint); - paintWithTexture.setShader(SkShader::CreateBitmapShader(*bitmapPtr, - SkShader::kClamp_TileMode, SkShader::kClamp_TileMode))->unref(); - - // Transform 'newM' needs to be concatenated to the current matrix, - // rather than transforming the primitive directly, so that 'newM' will - // also affect the behavior of the mask filter. - SkMatrix drawMatrix; - drawMatrix.setConcat(fContext->getMatrix(), newM); - SkDraw transformedDraw(draw); - transformedDraw.fMatrix = &drawMatrix; - - this->drawRect(transformedDraw, srcRect, paintWithTexture); - - return; - } - - fContext->concatMatrix(m); - - GrTextureParams params; - SkPaint::FilterLevel paintFilterLevel = paint.getFilterLevel(); - GrTextureParams::FilterMode textureFilterMode; - switch(paintFilterLevel) { - case SkPaint::kNone_FilterLevel: - textureFilterMode = GrTextureParams::kNone_FilterMode; - break; - case SkPaint::kLow_FilterLevel: - textureFilterMode = GrTextureParams::kBilerp_FilterMode; - break; - case SkPaint::kMedium_FilterLevel: - textureFilterMode = GrTextureParams::kMipMap_FilterMode; - break; - case SkPaint::kHigh_FilterLevel: - // Fall back to mips for now - textureFilterMode = GrTextureParams::kMipMap_FilterMode; - break; - default: - SkErrorInternals::SetError( kInvalidPaint_SkError, - "Sorry, I don't understand the filtering " - "mode you asked for. Falling back to " - "MIPMaps."); - textureFilterMode = GrTextureParams::kMipMap_FilterMode; - break; - - } - - params.setFilterMode(textureFilterMode); - - int maxTileSize = fContext->getMaxTextureSize(); - if (SkPaint::kNone_FilterLevel != paint.getFilterLevel()) { - // We may need a skosh more room if we have to bump out the tile - // by 1 pixel all around - maxTileSize -= 2; - } - int tileSize; - - SkIRect clippedSrcRect; - if (this->shouldTileBitmap(bitmap, params, srcRectPtr, maxTileSize, &tileSize, - &clippedSrcRect)) { - this->drawTiledBitmap(bitmap, srcRect, clippedSrcRect, params, paint, flags, tileSize); - } else { - // take the simple case - this->internalDrawBitmap(bitmap, srcRect, params, paint, flags); - } -} - -// Break 'bitmap' into several tiles to draw it since it has already -// been determined to be too large to fit in VRAM -void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap, - const SkRect& srcRect, - const SkIRect& clippedSrcIRect, - const GrTextureParams& params, - const SkPaint& paint, - SkCanvas::DrawBitmapRectFlags flags, - int tileSize) { - SkRect clippedSrcRect = SkRect::Make(clippedSrcIRect); - - int nx = bitmap.width() / tileSize; - int ny = bitmap.height() / tileSize; - for (int x = 0; x <= nx; x++) { - for (int y = 0; y <= ny; y++) { - SkRect tileR; - tileR.set(SkIntToScalar(x * tileSize), - SkIntToScalar(y * tileSize), - SkIntToScalar((x + 1) * tileSize), - SkIntToScalar((y + 1) * tileSize)); - - if (!SkRect::Intersects(tileR, clippedSrcRect)) { - continue; - } - - if (!tileR.intersect(srcRect)) { - continue; - } - - SkBitmap tmpB; - SkIRect iTileR; - tileR.roundOut(&iTileR); - SkPoint offset = SkPoint::Make(SkIntToScalar(iTileR.fLeft), - SkIntToScalar(iTileR.fTop)); - - if (SkPaint::kNone_FilterLevel != paint.getFilterLevel()) { - SkIRect iClampRect; - - if (SkCanvas::kBleed_DrawBitmapRectFlag & flags) { - // In bleed mode we want to always expand the tile on all edges - // but stay within the bitmap bounds - iClampRect = SkIRect::MakeWH(bitmap.width(), bitmap.height()); - } else { - // In texture-domain/clamp mode we only want to expand the - // tile on edges interior to "srcRect" (i.e., we want to - // not bleed across the original clamped edges) - srcRect.roundOut(&iClampRect); - } - - clamped_unit_outset_with_offset(&iTileR, &offset, iClampRect); - } - - if (bitmap.extractSubset(&tmpB, iTileR)) { - // now offset it to make it "local" to our tmp bitmap - tileR.offset(-offset.fX, -offset.fY); - SkMatrix tmpM; - tmpM.setTranslate(offset.fX, offset.fY); - GrContext::AutoMatrix am; - am.setPreConcat(fContext, tmpM); - this->internalDrawBitmap(tmpB, tileR, params, paint, flags); - } - } - } -} - -static bool has_aligned_samples(const SkRect& srcRect, - const SkRect& transformedRect) { - // detect pixel disalignment - if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) - - transformedRect.left()) < COLOR_BLEED_TOLERANCE && - SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) - - transformedRect.top()) < COLOR_BLEED_TOLERANCE && - SkScalarAbs(transformedRect.width() - srcRect.width()) < - COLOR_BLEED_TOLERANCE && - SkScalarAbs(transformedRect.height() - srcRect.height()) < - COLOR_BLEED_TOLERANCE) { - return true; - } - return false; -} - -static bool may_color_bleed(const SkRect& srcRect, - const SkRect& transformedRect, - const SkMatrix& m) { - // Only gets called if has_aligned_samples returned false. - // So we can assume that sampling is axis aligned but not texel aligned. - SkASSERT(!has_aligned_samples(srcRect, transformedRect)); - SkRect innerSrcRect(srcRect), innerTransformedRect, - outerTransformedRect(transformedRect); - innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf); - m.mapRect(&innerTransformedRect, innerSrcRect); - - // The gap between outerTransformedRect and innerTransformedRect - // represents the projection of the source border area, which is - // problematic for color bleeding. We must check whether any - // destination pixels sample the border area. - outerTransformedRect.inset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE); - innerTransformedRect.outset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE); - SkIRect outer, inner; - outerTransformedRect.round(&outer); - innerTransformedRect.round(&inner); - // If the inner and outer rects round to the same result, it means the - // border does not overlap any pixel centers. Yay! - return inner != outer; -} - - -/* - * This is called by drawBitmap(), which has to handle images that may be too - * large to be represented by a single texture. - * - * internalDrawBitmap assumes that the specified bitmap will fit in a texture - * and that non-texture portion of the GrPaint has already been setup. - */ -void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap, - const SkRect& srcRect, - const GrTextureParams& params, - const SkPaint& paint, - SkCanvas::DrawBitmapRectFlags flags) { - SkASSERT(bitmap.width() <= fContext->getMaxTextureSize() && - bitmap.height() <= fContext->getMaxTextureSize()); - - GrTexture* texture; - SkAutoCachedTexture act(this, bitmap, ¶ms, &texture); - if (NULL == texture) { - return; - } - - SkRect dstRect(srcRect); - SkRect paintRect; - SkScalar wInv = SkScalarInvert(SkIntToScalar(texture->width())); - SkScalar hInv = SkScalarInvert(SkIntToScalar(texture->height())); - paintRect.setLTRB(SkScalarMul(srcRect.fLeft, wInv), - SkScalarMul(srcRect.fTop, hInv), - SkScalarMul(srcRect.fRight, wInv), - SkScalarMul(srcRect.fBottom, hInv)); - - bool needsTextureDomain = false; - if (!(flags & SkCanvas::kBleed_DrawBitmapRectFlag) && - params.filterMode() != GrTextureParams::kNone_FilterMode) { - // Need texture domain if drawing a sub rect. - needsTextureDomain = srcRect.width() < bitmap.width() || - srcRect.height() < bitmap.height(); - if (needsTextureDomain && fContext->getMatrix().rectStaysRect()) { - const SkMatrix& matrix = fContext->getMatrix(); - // sampling is axis-aligned - SkRect transformedRect; - matrix.mapRect(&transformedRect, srcRect); - - if (has_aligned_samples(srcRect, transformedRect)) { - // We could also turn off filtering here (but we already did a cache lookup with - // params). - needsTextureDomain = false; - } else { - needsTextureDomain = may_color_bleed(srcRect, transformedRect, matrix); - } - } - } - - SkRect textureDomain = SkRect::MakeEmpty(); - SkAutoTUnref effect; - if (needsTextureDomain) { - // Use a constrained texture domain to avoid color bleeding - SkScalar left, top, right, bottom; - if (srcRect.width() > SK_Scalar1) { - SkScalar border = SK_ScalarHalf / texture->width(); - left = paintRect.left() + border; - right = paintRect.right() - border; - } else { - left = right = SkScalarHalf(paintRect.left() + paintRect.right()); - } - if (srcRect.height() > SK_Scalar1) { - SkScalar border = SK_ScalarHalf / texture->height(); - top = paintRect.top() + border; - bottom = paintRect.bottom() - border; - } else { - top = bottom = SkScalarHalf(paintRect.top() + paintRect.bottom()); - } - textureDomain.setLTRB(left, top, right, bottom); - effect.reset(GrTextureDomainEffect::Create(texture, - SkMatrix::I(), - textureDomain, - GrTextureDomainEffect::kClamp_WrapMode, - params.filterMode())); - } else { - effect.reset(GrSimpleTextureEffect::Create(texture, SkMatrix::I(), params)); - } - - // Construct a GrPaint by setting the bitmap texture as the first effect and then configuring - // the rest from the SkPaint. - GrPaint grPaint; - grPaint.addColorEffect(effect); - bool alphaOnly = !(SkBitmap::kA8_Config == bitmap.config()); - if (!skPaint2GrPaintNoShader(this, paint, alphaOnly, false, &grPaint)) { - return; - } - - fContext->drawRectToRect(grPaint, dstRect, paintRect, NULL); -} - -static bool filter_texture(SkBaseDevice* device, GrContext* context, - GrTexture* texture, SkImageFilter* filter, - int w, int h, const SkMatrix& ctm, SkBitmap* result, - SkIPoint* offset) { - SkASSERT(filter); - SkDeviceImageFilterProxy proxy(device); - - if (filter->canFilterImageGPU()) { - // Save the render target and set it to NULL, so we don't accidentally draw to it in the - // filter. Also set the clip wide open and the matrix to identity. - GrContext::AutoWideOpenIdentityDraw awo(context, NULL); - return filter->filterImageGPU(&proxy, wrap_texture(texture), ctm, result, offset); - } else { - return false; - } -} - -void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, - int left, int top, const SkPaint& paint) { - // drawSprite is defined to be in device coords. - CHECK_SHOULD_DRAW(draw, true); - - SkAutoLockPixels alp(bitmap, !bitmap.getTexture()); - if (!bitmap.getTexture() && !bitmap.readyToDraw()) { - return; - } - - int w = bitmap.width(); - int h = bitmap.height(); - - GrTexture* texture; - // draw sprite uses the default texture params - SkAutoCachedTexture act(this, bitmap, NULL, &texture); - - SkImageFilter* filter = paint.getImageFilter(); - SkIPoint offset = SkIPoint::Make(left, top); - // This bitmap will own the filtered result as a texture. - SkBitmap filteredBitmap; - - if (NULL != filter) { - SkMatrix matrix(*draw.fMatrix); - matrix.postTranslate(SkIntToScalar(-left), SkIntToScalar(-top)); - if (filter_texture(this, fContext, texture, filter, w, h, matrix, &filteredBitmap, - &offset)) { - texture = (GrTexture*) filteredBitmap.getTexture(); - w = filteredBitmap.width(); - h = filteredBitmap.height(); - } else { - return; - } - } - - GrPaint grPaint; - grPaint.addColorTextureEffect(texture, SkMatrix::I()); - - if(!skPaint2GrPaintNoShader(this, paint, true, false, &grPaint)) { - return; - } - - fContext->drawRectToRect(grPaint, - SkRect::MakeXYWH(SkIntToScalar(offset.fX), - SkIntToScalar(offset.fY), - SkIntToScalar(w), - SkIntToScalar(h)), - SkRect::MakeXYWH(0, - 0, - SK_Scalar1 * w / texture->width(), - SK_Scalar1 * h / texture->height())); -} - -void SkGpuDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap, - const SkRect* src, const SkRect& dst, - const SkPaint& paint, - SkCanvas::DrawBitmapRectFlags flags) { - SkMatrix matrix; - SkRect bitmapBounds, tmpSrc; - - bitmapBounds.set(0, 0, - SkIntToScalar(bitmap.width()), - SkIntToScalar(bitmap.height())); - - // Compute matrix from the two rectangles - if (NULL != src) { - tmpSrc = *src; - } else { - tmpSrc = bitmapBounds; - } - matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit); - - // clip the tmpSrc to the bounds of the bitmap. No check needed if src==null. - if (NULL != src) { - if (!bitmapBounds.contains(tmpSrc)) { - if (!tmpSrc.intersect(bitmapBounds)) { - return; // nothing to draw - } - } - } - - this->drawBitmapCommon(draw, bitmap, &tmpSrc, matrix, paint, flags); -} - -void SkGpuDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device, - int x, int y, const SkPaint& paint) { - // clear of the source device must occur before CHECK_SHOULD_DRAW - SkGpuDevice* dev = static_cast(device); - if (dev->fNeedClear) { - // TODO: could check here whether we really need to draw at all - dev->clear(0x0); - } - - // drawDevice is defined to be in device coords. - CHECK_SHOULD_DRAW(draw, true); - - GrRenderTarget* devRT = dev->accessRenderTarget(); - GrTexture* devTex; - if (NULL == (devTex = devRT->asTexture())) { - return; - } - - const SkBitmap& bm = dev->accessBitmap(false); - int w = bm.width(); - int h = bm.height(); - - SkImageFilter* filter = paint.getImageFilter(); - // This bitmap will own the filtered result as a texture. - SkBitmap filteredBitmap; - - if (NULL != filter) { - SkIPoint offset = SkIPoint::Make(0, 0); - SkMatrix matrix(*draw.fMatrix); - matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y)); - if (filter_texture(this, fContext, devTex, filter, w, h, matrix, &filteredBitmap, - &offset)) { - devTex = filteredBitmap.getTexture(); - w = filteredBitmap.width(); - h = filteredBitmap.height(); - x += offset.fX; - y += offset.fY; - } else { - return; - } - } - - GrPaint grPaint; - grPaint.addColorTextureEffect(devTex, SkMatrix::I()); - - if (!skPaint2GrPaintNoShader(this, paint, true, false, &grPaint)) { - return; - } - - SkRect dstRect = SkRect::MakeXYWH(SkIntToScalar(x), - SkIntToScalar(y), - SkIntToScalar(w), - SkIntToScalar(h)); - - // The device being drawn may not fill up its texture (e.g. saveLayer uses approximate - // scratch texture). - SkRect srcRect = SkRect::MakeWH(SK_Scalar1 * w / devTex->width(), - SK_Scalar1 * h / devTex->height()); - - fContext->drawRectToRect(grPaint, dstRect, srcRect); -} - -bool SkGpuDevice::canHandleImageFilter(SkImageFilter* filter) { - return filter->canFilterImageGPU(); -} - -bool SkGpuDevice::filterImage(SkImageFilter* filter, const SkBitmap& src, - const SkMatrix& ctm, - SkBitmap* result, SkIPoint* offset) { - // want explicitly our impl, so guard against a subclass of us overriding it - if (!this->SkGpuDevice::canHandleImageFilter(filter)) { - return false; - } - - SkAutoLockPixels alp(src, !src.getTexture()); - if (!src.getTexture() && !src.readyToDraw()) { - return false; - } - - GrTexture* texture; - // We assume here that the filter will not attempt to tile the src. Otherwise, this cache lookup - // must be pushed upstack. - SkAutoCachedTexture act(this, src, NULL, &texture); - - return filter_texture(this, fContext, texture, filter, src.width(), src.height(), ctm, result, - offset); -} - -/////////////////////////////////////////////////////////////////////////////// - -// must be in SkCanvas::VertexMode order -static const GrPrimitiveType gVertexMode2PrimitiveType[] = { - kTriangles_GrPrimitiveType, - kTriangleStrip_GrPrimitiveType, - kTriangleFan_GrPrimitiveType, -}; - -void SkGpuDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode, - int vertexCount, const SkPoint vertices[], - const SkPoint texs[], const SkColor colors[], - SkXfermode* xmode, - const uint16_t indices[], int indexCount, - const SkPaint& paint) { - CHECK_SHOULD_DRAW(draw, false); - - GrPaint grPaint; - // we ignore the shader if texs is null. - if (NULL == texs) { - if (!skPaint2GrPaintNoShader(this, paint, false, NULL == colors, &grPaint)) { - return; - } - } else { - if (!skPaint2GrPaintShader(this, paint, NULL == colors, &grPaint)) { - return; - } - } - - if (NULL != xmode && NULL != texs && NULL != colors) { - if (!SkXfermode::IsMode(xmode, SkXfermode::kModulate_Mode)) { - SkDebugf("Unsupported vertex-color/texture xfer mode.\n"); -#if 0 - return -#endif - } - } - - SkAutoSTMalloc<128, GrColor> convertedColors(0); - if (NULL != colors) { - // need to convert byte order and from non-PM to PM - convertedColors.reset(vertexCount); - for (int i = 0; i < vertexCount; ++i) { - convertedColors[i] = SkColor2GrColor(colors[i]); - } - colors = convertedColors.get(); - } - fContext->drawVertices(grPaint, - gVertexMode2PrimitiveType[vmode], - vertexCount, - (GrPoint*) vertices, - (GrPoint*) texs, - colors, - indices, - indexCount); -} - -/////////////////////////////////////////////////////////////////////////////// - -static void GlyphCacheAuxProc(void* data) { - GrFontScaler* scaler = (GrFontScaler*)data; - SkSafeUnref(scaler); -} - -static GrFontScaler* get_gr_font_scaler(SkGlyphCache* cache) { - void* auxData; - GrFontScaler* scaler = NULL; - if (cache->getAuxProcData(GlyphCacheAuxProc, &auxData)) { - scaler = (GrFontScaler*)auxData; - } - if (NULL == scaler) { - scaler = SkNEW_ARGS(SkGrFontScaler, (cache)); - cache->setAuxProc(GlyphCacheAuxProc, scaler); - } - return scaler; -} - -static void SkGPU_Draw1Glyph(const SkDraw1Glyph& state, - SkFixed fx, SkFixed fy, - const SkGlyph& glyph) { - SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0); - - GrSkDrawProcs* procs = static_cast(state.fDraw->fProcs); - - if (NULL == procs->fFontScaler) { - procs->fFontScaler = get_gr_font_scaler(state.fCache); - } - - procs->fTextContext->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(), - glyph.getSubXFixed(), - glyph.getSubYFixed()), - SkFixedFloorToFixed(fx), - SkFixedFloorToFixed(fy), - procs->fFontScaler); -} - -SkDrawProcs* SkGpuDevice::initDrawForText(GrTextContext* context) { - - // deferred allocation - if (NULL == fDrawProcs) { - fDrawProcs = SkNEW(GrSkDrawProcs); - fDrawProcs->fD1GProc = SkGPU_Draw1Glyph; - fDrawProcs->fContext = fContext; -#if SK_DISTANCEFIELD_FONTS - fDrawProcs->fFlags = 0; - fDrawProcs->fFlags |= SkDrawProcs::kSkipBakedGlyphTransform_Flag; - fDrawProcs->fFlags |= SkDrawProcs::kUseScaledGlyphs_Flag; -#endif - } - - // init our (and GL's) state - fDrawProcs->fTextContext = context; - fDrawProcs->fFontScaler = NULL; - return fDrawProcs; -} - -void SkGpuDevice::drawText(const SkDraw& draw, const void* text, - size_t byteLength, SkScalar x, SkScalar y, - const SkPaint& paint) { - CHECK_SHOULD_DRAW(draw, false); - - if (fContext->getMatrix().hasPerspective()) { - // this guy will just call our drawPath() - draw.drawText((const char*)text, byteLength, x, y, paint); - } else { - SkDraw myDraw(draw); - - GrPaint grPaint; - if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { - return; - } -#if SK_DISTANCEFIELD_FONTS - GrDistanceFieldTextContext context(fContext, grPaint, paint.getColor(), - paint.getTextSize()); -#else - GrBitmapTextContext context(fContext, grPaint, paint.getColor()); -#endif - myDraw.fProcs = this->initDrawForText(&context); - this->INHERITED::drawText(myDraw, text, byteLength, x, y, paint); - } -} - -void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text, - size_t byteLength, const SkScalar pos[], - SkScalar constY, int scalarsPerPos, - const SkPaint& paint) { - CHECK_SHOULD_DRAW(draw, false); - - if (fContext->getMatrix().hasPerspective()) { - // this guy will just call our drawPath() - draw.drawPosText((const char*)text, byteLength, pos, constY, - scalarsPerPos, paint); - } else { - SkDraw myDraw(draw); - - GrPaint grPaint; - if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { - return; - } -#if SK_DISTANCEFIELD_FONTS - GrDistanceFieldTextContext context(fContext, grPaint, paint.getColor(), - paint.getTextSize()/SkDrawProcs::kBaseDFFontSize); -#else - GrBitmapTextContext context(fContext, grPaint, paint.getColor()); -#endif - myDraw.fProcs = this->initDrawForText(&context); - this->INHERITED::drawPosText(myDraw, text, byteLength, pos, constY, - scalarsPerPos, paint); - } -} - -void SkGpuDevice::drawTextOnPath(const SkDraw& draw, const void* text, - size_t len, const SkPath& path, - const SkMatrix* m, const SkPaint& paint) { - CHECK_SHOULD_DRAW(draw, false); - - SkASSERT(draw.fDevice == this); - draw.drawTextOnPath((const char*)text, len, path, m, paint); -} - -/////////////////////////////////////////////////////////////////////////////// - -bool SkGpuDevice::filterTextFlags(const SkPaint& paint, TextFlags* flags) { - if (!paint.isLCDRenderText()) { - // we're cool with the paint as is - return false; - } - - if (paint.getShader() || - paint.getXfermode() || // unless its srcover - paint.getMaskFilter() || - paint.getRasterizer() || - paint.getColorFilter() || - paint.getPathEffect() || - paint.isFakeBoldText() || - paint.getStyle() != SkPaint::kFill_Style) { - // turn off lcd - flags->fFlags = paint.getFlags() & ~SkPaint::kLCDRenderText_Flag; - flags->fHinting = paint.getHinting(); - return true; - } - // we're cool with the paint as is - return false; -} - -void SkGpuDevice::flush() { - DO_DEFERRED_CLEAR(); - fContext->resolveRenderTarget(fRenderTarget); -} - -/////////////////////////////////////////////////////////////////////////////// - -SkBaseDevice* SkGpuDevice::onCreateCompatibleDevice(SkBitmap::Config config, - int width, int height, - bool isOpaque, - Usage usage) { - GrTextureDesc desc; - desc.fConfig = fRenderTarget->config(); - desc.fFlags = kRenderTarget_GrTextureFlagBit; - desc.fWidth = width; - desc.fHeight = height; - desc.fSampleCnt = fRenderTarget->numSamples(); - - SkAutoTUnref texture; - // Skia's convention is to only clear a device if it is non-opaque. - bool needClear = !isOpaque; - -#if CACHE_COMPATIBLE_DEVICE_TEXTURES - // layers are never draw in repeat modes, so we can request an approx - // match and ignore any padding. - const GrContext::ScratchTexMatch match = (kSaveLayer_Usage == usage) ? - GrContext::kApprox_ScratchTexMatch : - GrContext::kExact_ScratchTexMatch; - texture.reset(fContext->lockAndRefScratchTexture(desc, match)); -#else - texture.reset(fContext->createUncachedTexture(desc, NULL, 0)); -#endif - if (NULL != texture.get()) { - return SkNEW_ARGS(SkGpuDevice,(fContext, texture, needClear)); - } else { - GrPrintf("---- failed to create compatible device texture [%d %d]\n", width, height); - return NULL; - } -} - -SkGpuDevice::SkGpuDevice(GrContext* context, - GrTexture* texture, - bool needClear) - : SkBitmapDevice(make_bitmap(context, texture->asRenderTarget())) { - - SkASSERT(texture && texture->asRenderTarget()); - // This constructor is called from onCreateCompatibleDevice. It has locked the RT in the texture - // cache. We pass true for the third argument so that it will get unlocked. - this->initFromRenderTarget(context, texture->asRenderTarget(), true); - fNeedClear = needClear; -} +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkGpuDevice.h" + +#include "effects/GrTextureDomainEffect.h" +#include "effects/GrSimpleTextureEffect.h" + +#include "GrContext.h" +#include "GrBitmapTextContext.h" +#if SK_DISTANCEFIELD_FONTS +#include "GrDistanceFieldTextContext.h" +#endif + +#include "SkGrTexturePixelRef.h" + +#include "SkColorFilter.h" +#include "SkDeviceImageFilterProxy.h" +#include "SkDrawProcs.h" +#include "SkGlyphCache.h" +#include "SkImageFilter.h" +#include "SkPathEffect.h" +#include "SkRRect.h" +#include "SkStroke.h" +#include "SkUtils.h" +#include "SkErrorInternals.h" + +#define CACHE_COMPATIBLE_DEVICE_TEXTURES 1 + +#if 0 + extern bool (*gShouldDrawProc)(); + #define CHECK_SHOULD_DRAW(draw, forceI) \ + do { \ + if (gShouldDrawProc && !gShouldDrawProc()) return; \ + this->prepareDraw(draw, forceI); \ + } while (0) +#else + #define CHECK_SHOULD_DRAW(draw, forceI) this->prepareDraw(draw, forceI) +#endif + +// This constant represents the screen alignment criterion in texels for +// requiring texture domain clamping to prevent color bleeding when drawing +// a sub region of a larger source image. +#define COLOR_BLEED_TOLERANCE SkFloatToScalar(0.001f) + +#define DO_DEFERRED_CLEAR() \ + do { \ + if (fNeedClear) { \ + this->clear(SK_ColorTRANSPARENT); \ + } \ + } while (false) \ + +/////////////////////////////////////////////////////////////////////////////// + +#define CHECK_FOR_ANNOTATION(paint) \ + do { if (paint.getAnnotation()) { return; } } while (0) + +/////////////////////////////////////////////////////////////////////////////// + + +class SkGpuDevice::SkAutoCachedTexture : public ::SkNoncopyable { +public: + SkAutoCachedTexture() + : fDevice(NULL) + , fTexture(NULL) { + } + + SkAutoCachedTexture(SkGpuDevice* device, + const SkBitmap& bitmap, + const GrTextureParams* params, + GrTexture** texture) + : fDevice(NULL) + , fTexture(NULL) { + SkASSERT(NULL != texture); + *texture = this->set(device, bitmap, params); + } + + ~SkAutoCachedTexture() { + if (NULL != fTexture) { + GrUnlockAndUnrefCachedBitmapTexture(fTexture); + } + } + + GrTexture* set(SkGpuDevice* device, + const SkBitmap& bitmap, + const GrTextureParams* params) { + if (NULL != fTexture) { + GrUnlockAndUnrefCachedBitmapTexture(fTexture); + fTexture = NULL; + } + fDevice = device; + GrTexture* result = (GrTexture*)bitmap.getTexture(); + if (NULL == result) { + // Cannot return the native texture so look it up in our cache + fTexture = GrLockAndRefCachedBitmapTexture(device->context(), bitmap, params); + result = fTexture; + } + return result; + } + +private: + SkGpuDevice* fDevice; + GrTexture* fTexture; +}; + +/////////////////////////////////////////////////////////////////////////////// + +struct GrSkDrawProcs : public SkDrawProcs { +public: + GrContext* fContext; + GrTextContext* fTextContext; + GrFontScaler* fFontScaler; // cached in the skia glyphcache +}; + +/////////////////////////////////////////////////////////////////////////////// + +static SkBitmap::Config grConfig2skConfig(GrPixelConfig config, bool* isOpaque) { + switch (config) { + case kAlpha_8_GrPixelConfig: + *isOpaque = false; + return SkBitmap::kA8_Config; + case kRGB_565_GrPixelConfig: + *isOpaque = true; + return SkBitmap::kRGB_565_Config; + case kRGBA_4444_GrPixelConfig: + *isOpaque = false; + return SkBitmap::kARGB_4444_Config; + case kSkia8888_GrPixelConfig: + // we don't currently have a way of knowing whether + // a 8888 is opaque based on the config. + *isOpaque = false; + return SkBitmap::kARGB_8888_Config; + default: + *isOpaque = false; + return SkBitmap::kNo_Config; + } +} + +/* + * GrRenderTarget does not know its opaqueness, only its config, so we have + * to make conservative guesses when we return an "equivalent" bitmap. + */ +static SkBitmap make_bitmap(GrContext* context, GrRenderTarget* renderTarget) { + bool isOpaque; + SkBitmap::Config config = grConfig2skConfig(renderTarget->config(), &isOpaque); + + SkBitmap bitmap; + bitmap.setConfig(config, renderTarget->width(), renderTarget->height(), 0, + isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType); + return bitmap; +} + +SkGpuDevice* SkGpuDevice::Create(GrSurface* surface) { + SkASSERT(NULL != surface); + if (NULL == surface->asRenderTarget() || NULL == surface->getContext()) { + return NULL; + } + if (surface->asTexture()) { + return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asTexture())); + } else { + return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asRenderTarget())); + } +} + +SkGpuDevice::SkGpuDevice(GrContext* context, GrTexture* texture) + : SkBitmapDevice(make_bitmap(context, texture->asRenderTarget())) { + this->initFromRenderTarget(context, texture->asRenderTarget(), false); +} + +SkGpuDevice::SkGpuDevice(GrContext* context, GrRenderTarget* renderTarget) + : SkBitmapDevice(make_bitmap(context, renderTarget)) { + this->initFromRenderTarget(context, renderTarget, false); +} + +void SkGpuDevice::initFromRenderTarget(GrContext* context, + GrRenderTarget* renderTarget, + bool cached) { + fDrawProcs = NULL; + + fContext = context; + fContext->ref(); + + fRenderTarget = NULL; + fNeedClear = false; + + SkASSERT(NULL != renderTarget); + fRenderTarget = renderTarget; + fRenderTarget->ref(); + + // Hold onto to the texture in the pixel ref (if there is one) because the texture holds a ref + // on the RT but not vice-versa. + // TODO: Remove this trickery once we figure out how to make SkGrPixelRef do this without + // busting chrome (for a currently unknown reason). + GrSurface* surface = fRenderTarget->asTexture(); + if (NULL == surface) { + surface = fRenderTarget; + } + SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (surface, cached)); + + this->setPixelRef(pr, 0)->unref(); +} + +SkGpuDevice::SkGpuDevice(GrContext* context, + SkBitmap::Config config, + int width, + int height, + int sampleCount) + : SkBitmapDevice(config, width, height, false /*isOpaque*/) { + + fDrawProcs = NULL; + + fContext = context; + fContext->ref(); + + fRenderTarget = NULL; + fNeedClear = false; + + if (config != SkBitmap::kRGB_565_Config) { + config = SkBitmap::kARGB_8888_Config; + } + + GrTextureDesc desc; + desc.fFlags = kRenderTarget_GrTextureFlagBit; + desc.fWidth = width; + desc.fHeight = height; + desc.fConfig = SkBitmapConfig2GrPixelConfig(config); + desc.fSampleCnt = sampleCount; + + SkAutoTUnref texture(fContext->createUncachedTexture(desc, NULL, 0)); + + if (NULL != texture) { + fRenderTarget = texture->asRenderTarget(); + fRenderTarget->ref(); + + SkASSERT(NULL != fRenderTarget); + + // wrap the bitmap with a pixelref to expose our texture + SkGrPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (texture)); + this->setPixelRef(pr, 0)->unref(); + } else { + GrPrintf("--- failed to create gpu-offscreen [%d %d]\n", + width, height); + SkASSERT(false); + } +} + +SkGpuDevice::~SkGpuDevice() { + if (fDrawProcs) { + delete fDrawProcs; + } + + // The GrContext takes a ref on the target. We don't want to cause the render + // target to be unnecessarily kept alive. + if (fContext->getRenderTarget() == fRenderTarget) { + fContext->setRenderTarget(NULL); + } + + if (fContext->getClip() == &fClipData) { + fContext->setClip(NULL); + } + + SkSafeUnref(fRenderTarget); + fContext->unref(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkGpuDevice::makeRenderTargetCurrent() { + DO_DEFERRED_CLEAR(); + fContext->setRenderTarget(fRenderTarget); +} + +/////////////////////////////////////////////////////////////////////////////// + +namespace { +GrPixelConfig config8888_to_grconfig_and_flags(SkCanvas::Config8888 config8888, uint32_t* flags) { + switch (config8888) { + case SkCanvas::kNative_Premul_Config8888: + *flags = 0; + return kSkia8888_GrPixelConfig; + case SkCanvas::kNative_Unpremul_Config8888: + *flags = GrContext::kUnpremul_PixelOpsFlag; + return kSkia8888_GrPixelConfig; + case SkCanvas::kBGRA_Premul_Config8888: + *flags = 0; + return kBGRA_8888_GrPixelConfig; + case SkCanvas::kBGRA_Unpremul_Config8888: + *flags = GrContext::kUnpremul_PixelOpsFlag; + return kBGRA_8888_GrPixelConfig; + case SkCanvas::kRGBA_Premul_Config8888: + *flags = 0; + return kRGBA_8888_GrPixelConfig; + case SkCanvas::kRGBA_Unpremul_Config8888: + *flags = GrContext::kUnpremul_PixelOpsFlag; + return kRGBA_8888_GrPixelConfig; + default: + GrCrash("Unexpected Config8888."); + *flags = 0; // suppress warning + return kSkia8888_GrPixelConfig; + } +} +} + +bool SkGpuDevice::onReadPixels(const SkBitmap& bitmap, + int x, int y, + SkCanvas::Config8888 config8888) { + DO_DEFERRED_CLEAR(); + SkASSERT(SkBitmap::kARGB_8888_Config == bitmap.config()); + SkASSERT(!bitmap.isNull()); + SkASSERT(SkIRect::MakeWH(this->width(), this->height()).contains(SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))); + + SkAutoLockPixels alp(bitmap); + GrPixelConfig config; + uint32_t flags; + config = config8888_to_grconfig_and_flags(config8888, &flags); + return fContext->readRenderTargetPixels(fRenderTarget, + x, y, + bitmap.width(), + bitmap.height(), + config, + bitmap.getPixels(), + bitmap.rowBytes(), + flags); +} + +void SkGpuDevice::writePixels(const SkBitmap& bitmap, int x, int y, + SkCanvas::Config8888 config8888) { + SkAutoLockPixels alp(bitmap); + if (!bitmap.readyToDraw()) { + return; + } + + GrPixelConfig config; + uint32_t flags; + if (SkBitmap::kARGB_8888_Config == bitmap.config()) { + config = config8888_to_grconfig_and_flags(config8888, &flags); + } else { + flags = 0; + config= SkBitmapConfig2GrPixelConfig(bitmap.config()); + } + + fRenderTarget->writePixels(x, y, bitmap.width(), bitmap.height(), + config, bitmap.getPixels(), bitmap.rowBytes(), flags); +} + +void SkGpuDevice::onAttachToCanvas(SkCanvas* canvas) { + INHERITED::onAttachToCanvas(canvas); + + // Canvas promises that this ptr is valid until onDetachFromCanvas is called + fClipData.fClipStack = canvas->getClipStack(); +} + +void SkGpuDevice::onDetachFromCanvas() { + INHERITED::onDetachFromCanvas(); + fClipData.fClipStack = NULL; +} + +// call this every draw call, to ensure that the context reflects our state, +// and not the state from some other canvas/device +void SkGpuDevice::prepareDraw(const SkDraw& draw, bool forceIdentity) { + SkASSERT(NULL != fClipData.fClipStack); + + fContext->setRenderTarget(fRenderTarget); + + SkASSERT(draw.fClipStack && draw.fClipStack == fClipData.fClipStack); + + if (forceIdentity) { + fContext->setIdentityMatrix(); + } else { + fContext->setMatrix(*draw.fMatrix); + } + fClipData.fOrigin = this->getOrigin(); + + fContext->setClip(&fClipData); + + DO_DEFERRED_CLEAR(); +} + +GrRenderTarget* SkGpuDevice::accessRenderTarget() { + DO_DEFERRED_CLEAR(); + return fRenderTarget; +} + +/////////////////////////////////////////////////////////////////////////////// + +SK_COMPILE_ASSERT(SkShader::kNone_BitmapType == 0, shader_type_mismatch); +SK_COMPILE_ASSERT(SkShader::kDefault_BitmapType == 1, shader_type_mismatch); +SK_COMPILE_ASSERT(SkShader::kRadial_BitmapType == 2, shader_type_mismatch); +SK_COMPILE_ASSERT(SkShader::kSweep_BitmapType == 3, shader_type_mismatch); +SK_COMPILE_ASSERT(SkShader::kTwoPointRadial_BitmapType == 4, + shader_type_mismatch); +SK_COMPILE_ASSERT(SkShader::kTwoPointConical_BitmapType == 5, + shader_type_mismatch); +SK_COMPILE_ASSERT(SkShader::kLinear_BitmapType == 6, shader_type_mismatch); +SK_COMPILE_ASSERT(SkShader::kLast_BitmapType == 6, shader_type_mismatch); + +namespace { + +// converts a SkPaint to a GrPaint, ignoring the skPaint's shader +// justAlpha indicates that skPaint's alpha should be used rather than the color +// Callers may subsequently modify the GrPaint. Setting constantColor indicates +// that the final paint will draw the same color at every pixel. This allows +// an optimization where the the color filter can be applied to the skPaint's +// color once while converting to GrPaint and then ignored. +inline bool skPaint2GrPaintNoShader(SkGpuDevice* dev, + const SkPaint& skPaint, + bool justAlpha, + bool constantColor, + GrPaint* grPaint) { + + grPaint->setDither(skPaint.isDither()); + grPaint->setAntiAlias(skPaint.isAntiAlias()); + + SkXfermode::Coeff sm; + SkXfermode::Coeff dm; + + SkXfermode* mode = skPaint.getXfermode(); + GrEffectRef* xferEffect = NULL; + if (SkXfermode::AsNewEffectOrCoeff(mode, &xferEffect, &sm, &dm)) { + if (NULL != xferEffect) { + grPaint->addColorEffect(xferEffect)->unref(); + sm = SkXfermode::kOne_Coeff; + dm = SkXfermode::kZero_Coeff; + } + } else { + //SkDEBUGCODE(SkDebugf("Unsupported xfer mode.\n");) +#if 0 + return false; +#else + // Fall back to src-over + sm = SkXfermode::kOne_Coeff; + dm = SkXfermode::kISA_Coeff; +#endif + } + grPaint->setBlendFunc(sk_blend_to_grblend(sm), sk_blend_to_grblend(dm)); + + if (justAlpha) { + uint8_t alpha = skPaint.getAlpha(); + grPaint->setColor(GrColorPackRGBA(alpha, alpha, alpha, alpha)); + // justAlpha is currently set to true only if there is a texture, + // so constantColor should not also be true. + SkASSERT(!constantColor); + } else { + grPaint->setColor(SkColor2GrColor(skPaint.getColor())); + } + + SkColorFilter* colorFilter = skPaint.getColorFilter(); + if (NULL != colorFilter) { + // if the source color is a constant then apply the filter here once rather than per pixel + // in a shader. + if (constantColor) { + SkColor filtered = colorFilter->filterColor(skPaint.getColor()); + grPaint->setColor(SkColor2GrColor(filtered)); + } else { + SkAutoTUnref effect(colorFilter->asNewEffect(dev->context())); + if (NULL != effect.get()) { + grPaint->addColorEffect(effect); + } + } + } + + return true; +} + +// This function is similar to skPaint2GrPaintNoShader but also converts +// skPaint's shader to a GrTexture/GrEffectStage if possible. The texture to +// be used is set on grPaint and returned in param act. constantColor has the +// same meaning as in skPaint2GrPaintNoShader. +inline bool skPaint2GrPaintShader(SkGpuDevice* dev, + const SkPaint& skPaint, + bool constantColor, + GrPaint* grPaint) { + SkShader* shader = skPaint.getShader(); + if (NULL == shader) { + return skPaint2GrPaintNoShader(dev, skPaint, false, constantColor, grPaint); + } + + // SkShader::asNewEffect() may do offscreen rendering. Setup default drawing state + // Also require shader to set the render target . + GrContext::AutoWideOpenIdentityDraw awo(dev->context(), NULL); + GrContext::AutoRenderTarget(dev->context(), NULL); + + // setup the shader as the first color effect on the paint + SkAutoTUnref effect(shader->asNewEffect(dev->context(), skPaint)); + if (NULL != effect.get()) { + grPaint->addColorEffect(effect); + // Now setup the rest of the paint. + return skPaint2GrPaintNoShader(dev, skPaint, true, false, grPaint); + } else { + // We still don't have SkColorShader::asNewEffect() implemented. + SkShader::GradientInfo info; + SkColor color; + + info.fColors = &color; + info.fColorOffsets = NULL; + info.fColorCount = 1; + if (SkShader::kColor_GradientType == shader->asAGradient(&info)) { + SkPaint copy(skPaint); + copy.setShader(NULL); + // modulate the paint alpha by the shader's solid color alpha + U8CPU newA = SkMulDiv255Round(SkColorGetA(color), copy.getAlpha()); + copy.setColor(SkColorSetA(color, newA)); + return skPaint2GrPaintNoShader(dev, copy, false, constantColor, grPaint); + } else { + return false; + } + } +} +} + +/////////////////////////////////////////////////////////////////////////////// +void SkGpuDevice::getGlobalBounds(SkIRect* bounds) const { + if (NULL != bounds) { + const SkIPoint& origin = this->getOrigin(); + bounds->setXYWH(origin.x(), origin.y(), + this->width(), this->height()); + } +} + +SkBitmap::Config SkGpuDevice::config() const { + if (NULL == fRenderTarget) { + return SkBitmap::kNo_Config; + } + + bool isOpaque; + return grConfig2skConfig(fRenderTarget->config(), &isOpaque); +} + +void SkGpuDevice::clear(SkColor color) { + SkIRect rect = SkIRect::MakeWH(this->width(), this->height()); + fContext->clear(&rect, SkColor2GrColor(color), true, fRenderTarget); + fNeedClear = false; +} + +void SkGpuDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) { + CHECK_SHOULD_DRAW(draw, false); + + GrPaint grPaint; + if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { + return; + } + + fContext->drawPaint(grPaint); +} + +// must be in SkCanvas::PointMode order +static const GrPrimitiveType gPointMode2PrimtiveType[] = { + kPoints_GrPrimitiveType, + kLines_GrPrimitiveType, + kLineStrip_GrPrimitiveType +}; + +void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, + size_t count, const SkPoint pts[], const SkPaint& paint) { + CHECK_FOR_ANNOTATION(paint); + CHECK_SHOULD_DRAW(draw, false); + + SkScalar width = paint.getStrokeWidth(); + if (width < 0) { + return; + } + + // we only handle hairlines and paints without path effects or mask filters, + // else we let the SkDraw call our drawPath() + if (width > 0 || paint.getPathEffect() || paint.getMaskFilter()) { + draw.drawPoints(mode, count, pts, paint, true); + return; + } + + GrPaint grPaint; + if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { + return; + } + + fContext->drawVertices(grPaint, + gPointMode2PrimtiveType[mode], + count, + (GrPoint*)pts, + NULL, + NULL, + NULL, + 0); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect, + const SkPaint& paint) { + CHECK_FOR_ANNOTATION(paint); + CHECK_SHOULD_DRAW(draw, false); + + bool doStroke = paint.getStyle() != SkPaint::kFill_Style; + SkScalar width = paint.getStrokeWidth(); + + /* + We have special code for hairline strokes, miter-strokes, bevel-stroke + and fills. Anything else we just call our path code. + */ + bool usePath = doStroke && width > 0 && + (paint.getStrokeJoin() == SkPaint::kRound_Join || + (paint.getStrokeJoin() == SkPaint::kBevel_Join && rect.isEmpty())); + // another two reasons we might need to call drawPath... + if (paint.getMaskFilter() || paint.getPathEffect()) { + usePath = true; + } + if (!usePath && paint.isAntiAlias() && !fContext->getMatrix().rectStaysRect()) { +#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT) + if (doStroke) { +#endif + usePath = true; +#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT) + } else { + usePath = !fContext->getMatrix().preservesRightAngles(); + } +#endif + } + // until we can both stroke and fill rectangles + if (paint.getStyle() == SkPaint::kStrokeAndFill_Style) { + usePath = true; + } + + if (usePath) { + SkPath path; + path.addRect(rect); + this->drawPath(draw, path, paint, NULL, true); + return; + } + + GrPaint grPaint; + if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { + return; + } + + if (!doStroke) { + fContext->drawRect(grPaint, rect); + } else { + SkStrokeRec stroke(paint); + fContext->drawRect(grPaint, rect, &stroke); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkGpuDevice::drawRRect(const SkDraw& draw, const SkRRect& rect, + const SkPaint& paint) { + CHECK_FOR_ANNOTATION(paint); + CHECK_SHOULD_DRAW(draw, false); + + bool usePath = !rect.isSimple(); + // another two reasons we might need to call drawPath... + if (paint.getMaskFilter() || paint.getPathEffect()) { + usePath = true; + } + // until we can rotate rrects... + if (!usePath && !fContext->getMatrix().rectStaysRect()) { + usePath = true; + } + + if (usePath) { + SkPath path; + path.addRRect(rect); + this->drawPath(draw, path, paint, NULL, true); + return; + } + + GrPaint grPaint; + if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { + return; + } + + SkStrokeRec stroke(paint); + fContext->drawRRect(grPaint, rect, stroke); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkGpuDevice::drawOval(const SkDraw& draw, const SkRect& oval, + const SkPaint& paint) { + CHECK_FOR_ANNOTATION(paint); + CHECK_SHOULD_DRAW(draw, false); + + bool usePath = false; + // some basic reasons we might need to call drawPath... + if (paint.getMaskFilter() || paint.getPathEffect()) { + usePath = true; + } + + if (usePath) { + SkPath path; + path.addOval(oval); + this->drawPath(draw, path, paint, NULL, true); + return; + } + + GrPaint grPaint; + if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { + return; + } + SkStrokeRec stroke(paint); + + fContext->drawOval(grPaint, oval, stroke); +} + +#include "SkMaskFilter.h" +#include "SkBounder.h" + +/////////////////////////////////////////////////////////////////////////////// + +// helpers for applying mask filters +namespace { + +// Draw a mask using the supplied paint. Since the coverage/geometry +// is already burnt into the mask this boils down to a rect draw. +// Return true if the mask was successfully drawn. +bool draw_mask(GrContext* context, const SkRect& maskRect, + GrPaint* grp, GrTexture* mask) { + GrContext::AutoMatrix am; + if (!am.setIdentity(context, grp)) { + return false; + } + + SkMatrix matrix; + matrix.setTranslate(-maskRect.fLeft, -maskRect.fTop); + matrix.postIDiv(mask->width(), mask->height()); + + grp->addCoverageEffect(GrSimpleTextureEffect::Create(mask, matrix))->unref(); + context->drawRect(*grp, maskRect); + return true; +} + +bool draw_with_mask_filter(GrContext* context, const SkPath& devPath, + SkMaskFilter* filter, const SkRegion& clip, SkBounder* bounder, + GrPaint* grp, SkPaint::Style style) { + SkMask srcM, dstM; + + if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), filter, &context->getMatrix(), &srcM, + SkMask::kComputeBoundsAndRenderImage_CreateMode, style)) { + return false; + } + SkAutoMaskFreeImage autoSrc(srcM.fImage); + + if (!filter->filterMask(&dstM, srcM, context->getMatrix(), NULL)) { + return false; + } + // this will free-up dstM when we're done (allocated in filterMask()) + SkAutoMaskFreeImage autoDst(dstM.fImage); + + if (clip.quickReject(dstM.fBounds)) { + return false; + } + if (bounder && !bounder->doIRect(dstM.fBounds)) { + return false; + } + + // we now have a device-aligned 8bit mask in dstM, ready to be drawn using + // the current clip (and identity matrix) and GrPaint settings + GrTextureDesc desc; + desc.fWidth = dstM.fBounds.width(); + desc.fHeight = dstM.fBounds.height(); + desc.fConfig = kAlpha_8_GrPixelConfig; + + GrAutoScratchTexture ast(context, desc); + GrTexture* texture = ast.texture(); + + if (NULL == texture) { + return false; + } + texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig, + dstM.fImage, dstM.fRowBytes); + + SkRect maskRect = SkRect::Make(dstM.fBounds); + + return draw_mask(context, maskRect, grp, texture); +} + +// Create a mask of 'devPath' and place the result in 'mask'. Return true on +// success; false otherwise. +bool create_mask_GPU(GrContext* context, + const SkRect& maskRect, + const SkPath& devPath, + const SkStrokeRec& stroke, + bool doAA, + GrAutoScratchTexture* mask) { + GrTextureDesc desc; + desc.fFlags = kRenderTarget_GrTextureFlagBit; + desc.fWidth = SkScalarCeilToInt(maskRect.width()); + desc.fHeight = SkScalarCeilToInt(maskRect.height()); + // We actually only need A8, but it often isn't supported as a + // render target so default to RGBA_8888 + desc.fConfig = kRGBA_8888_GrPixelConfig; + if (context->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { + desc.fConfig = kAlpha_8_GrPixelConfig; + } + + mask->set(context, desc); + if (NULL == mask->texture()) { + return false; + } + + GrTexture* maskTexture = mask->texture(); + SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height()); + + GrContext::AutoRenderTarget art(context, maskTexture->asRenderTarget()); + GrContext::AutoClip ac(context, clipRect); + + context->clear(NULL, 0x0, true); + + GrPaint tempPaint; + if (doAA) { + tempPaint.setAntiAlias(true); + // AA uses the "coverage" stages on GrDrawTarget. Coverage with a dst + // blend coeff of zero requires dual source blending support in order + // to properly blend partially covered pixels. This means the AA + // code path may not be taken. So we use a dst blend coeff of ISA. We + // could special case AA draws to a dst surface with known alpha=0 to + // use a zero dst coeff when dual source blending isn't available. + tempPaint.setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff); + } + + GrContext::AutoMatrix am; + + // Draw the mask into maskTexture with the path's top-left at the origin using tempPaint. + SkMatrix translate; + translate.setTranslate(-maskRect.fLeft, -maskRect.fTop); + am.set(context, translate); + context->drawPath(tempPaint, devPath, stroke); + return true; +} + +SkBitmap wrap_texture(GrTexture* texture) { + SkBitmap result; + bool dummy; + SkBitmap::Config config = grConfig2skConfig(texture->config(), &dummy); + result.setConfig(config, texture->width(), texture->height()); + result.setPixelRef(SkNEW_ARGS(SkGrPixelRef, (texture)))->unref(); + return result; +} + +}; + +void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath, + const SkPaint& paint, const SkMatrix* prePathMatrix, + bool pathIsMutable) { + CHECK_FOR_ANNOTATION(paint); + CHECK_SHOULD_DRAW(draw, false); + + GrPaint grPaint; + if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { + return; + } + + // can we cheat, and treat a thin stroke as a hairline w/ coverage + // if we can, we draw lots faster (raster device does this same test) + SkScalar hairlineCoverage; + bool doHairLine = SkDrawTreatAsHairline(paint, fContext->getMatrix(), &hairlineCoverage); + if (doHairLine) { + grPaint.setCoverage(SkScalarRoundToInt(hairlineCoverage * grPaint.getCoverage())); + } + + // If we have a prematrix, apply it to the path, optimizing for the case + // where the original path can in fact be modified in place (even though + // its parameter type is const). + SkPath* pathPtr = const_cast(&origSrcPath); + SkPath tmpPath, effectPath; + + if (prePathMatrix) { + SkPath* result = pathPtr; + + if (!pathIsMutable) { + result = &tmpPath; + pathIsMutable = true; + } + // should I push prePathMatrix on our MV stack temporarily, instead + // of applying it here? See SkDraw.cpp + pathPtr->transform(*prePathMatrix, result); + pathPtr = result; + } + // at this point we're done with prePathMatrix + SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;) + + SkStrokeRec stroke(paint); + SkPathEffect* pathEffect = paint.getPathEffect(); + const SkRect* cullRect = NULL; // TODO: what is our bounds? + if (pathEffect && pathEffect->filterPath(&effectPath, *pathPtr, &stroke, + cullRect)) { + pathPtr = &effectPath; + } + + if (!pathEffect && doHairLine) { + stroke.setHairlineStyle(); + } + + if (paint.getMaskFilter()) { + if (!stroke.isHairlineStyle()) { + if (stroke.applyToPath(&tmpPath, *pathPtr)) { + pathPtr = &tmpPath; + pathIsMutable = true; + stroke.setFillStyle(); + } + } + + // avoid possibly allocating a new path in transform if we can + SkPath* devPathPtr = pathIsMutable ? pathPtr : &tmpPath; + + // transform the path into device space + pathPtr->transform(fContext->getMatrix(), devPathPtr); + + SkRect maskRect; + if (paint.getMaskFilter()->canFilterMaskGPU(devPathPtr->getBounds(), + draw.fClip->getBounds(), + fContext->getMatrix(), + &maskRect)) { + SkIRect finalIRect; + maskRect.roundOut(&finalIRect); + if (draw.fClip->quickReject(finalIRect)) { + // clipped out + return; + } + if (NULL != draw.fBounder && !draw.fBounder->doIRect(finalIRect)) { + // nothing to draw + return; + } + + GrAutoScratchTexture mask; + + if (create_mask_GPU(fContext, maskRect, *devPathPtr, stroke, + grPaint.isAntiAlias(), &mask)) { + GrTexture* filtered; + + if (paint.getMaskFilter()->filterMaskGPU(mask.texture(), maskRect, &filtered, true)) { + // filterMaskGPU gives us ownership of a ref to the result + SkAutoTUnref atu(filtered); + + // If the scratch texture that we used as the filter src also holds the filter + // result then we must detach so that this texture isn't recycled for a later + // draw. + if (filtered == mask.texture()) { + mask.detach(); + filtered->unref(); // detach transfers GrAutoScratchTexture's ref to us. + } + + if (draw_mask(fContext, maskRect, &grPaint, filtered)) { + // This path is completely drawn + return; + } + } + } + } + + // draw the mask on the CPU - this is a fallthrough path in case the + // GPU path fails + SkPaint::Style style = stroke.isHairlineStyle() ? SkPaint::kStroke_Style : + SkPaint::kFill_Style; + draw_with_mask_filter(fContext, *devPathPtr, paint.getMaskFilter(), + *draw.fClip, draw.fBounder, &grPaint, style); + return; + } + + fContext->drawPath(grPaint, *pathPtr, stroke); +} + +static const int kBmpSmallTileSize = 1 << 10; + +static inline int get_tile_count(const SkIRect& srcRect, int tileSize) { + int tilesX = (srcRect.fRight / tileSize) - (srcRect.fLeft / tileSize) + 1; + int tilesY = (srcRect.fBottom / tileSize) - (srcRect.fTop / tileSize) + 1; + return tilesX * tilesY; +} + +static int determine_tile_size(const SkBitmap& bitmap, const SkIRect& src, int maxTileSize) { + if (maxTileSize <= kBmpSmallTileSize) { + return maxTileSize; + } + + size_t maxTileTotalTileSize = get_tile_count(src, maxTileSize); + size_t smallTotalTileSize = get_tile_count(src, kBmpSmallTileSize); + + maxTileTotalTileSize *= maxTileSize * maxTileSize; + smallTotalTileSize *= kBmpSmallTileSize * kBmpSmallTileSize; + + if (maxTileTotalTileSize > 2 * smallTotalTileSize) { + return kBmpSmallTileSize; + } else { + return maxTileSize; + } +} + +// Given a bitmap, an optional src rect, and a context with a clip and matrix determine what +// pixels from the bitmap are necessary. +static void determine_clipped_src_rect(const GrContext* context, + const SkBitmap& bitmap, + const SkRect* srcRectPtr, + SkIRect* clippedSrcIRect) { + const GrClipData* clip = context->getClip(); + clip->getConservativeBounds(context->getRenderTarget(), clippedSrcIRect, NULL); + SkMatrix inv; + if (!context->getMatrix().invert(&inv)) { + clippedSrcIRect->setEmpty(); + return; + } + SkRect clippedSrcRect = SkRect::Make(*clippedSrcIRect); + inv.mapRect(&clippedSrcRect); + if (NULL != srcRectPtr) { + if (!clippedSrcRect.intersect(*srcRectPtr)) { + clippedSrcIRect->setEmpty(); + return; + } + } + clippedSrcRect.roundOut(clippedSrcIRect); + SkIRect bmpBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height()); + if (!clippedSrcIRect->intersect(bmpBounds)) { + clippedSrcIRect->setEmpty(); + } +} + +bool SkGpuDevice::shouldTileBitmap(const SkBitmap& bitmap, + const GrTextureParams& params, + const SkRect* srcRectPtr, + int maxTileSize, + int* tileSize, + SkIRect* clippedSrcRect) const { + // if bitmap is explictly texture backed then just use the texture + if (NULL != bitmap.getTexture()) { + return false; + } + + // if it's larger than the max tile size, then we have no choice but tiling. + if (bitmap.width() > maxTileSize || bitmap.height() > maxTileSize) { + determine_clipped_src_rect(fContext, bitmap, srcRectPtr, clippedSrcRect); + *tileSize = determine_tile_size(bitmap, *clippedSrcRect, maxTileSize); + return true; + } + + if (bitmap.width() * bitmap.height() < 4 * kBmpSmallTileSize * kBmpSmallTileSize) { + return false; + } + + // if the entire texture is already in our cache then no reason to tile it + if (GrIsBitmapInCache(fContext, bitmap, ¶ms)) { + return false; + } + + // At this point we know we could do the draw by uploading the entire bitmap + // as a texture. However, if the texture would be large compared to the + // cache size and we don't require most of it for this draw then tile to + // reduce the amount of upload and cache spill. + + // assumption here is that sw bitmap size is a good proxy for its size as + // a texture + size_t bmpSize = bitmap.getSize(); + size_t cacheSize; + fContext->getTextureCacheLimits(NULL, &cacheSize); + if (bmpSize < cacheSize / 2) { + return false; + } + + // Figure out how much of the src we will need based on the src rect and clipping. + determine_clipped_src_rect(fContext, bitmap, srcRectPtr, clippedSrcRect); + *tileSize = kBmpSmallTileSize; // already know whole bitmap fits in one max sized tile. + size_t usedTileBytes = get_tile_count(*clippedSrcRect, kBmpSmallTileSize) * + kBmpSmallTileSize * kBmpSmallTileSize; + + return usedTileBytes < 2 * bmpSize; +} + +void SkGpuDevice::drawBitmap(const SkDraw& draw, + const SkBitmap& bitmap, + const SkMatrix& m, + const SkPaint& paint) { + // We cannot call drawBitmapRect here since 'm' could be anything + this->drawBitmapCommon(draw, bitmap, NULL, m, paint, + SkCanvas::kNone_DrawBitmapRectFlag); +} + +// This method outsets 'iRect' by 1 all around and then clamps its extents to +// 'clamp'. 'offset' is adjusted to remain positioned over the top-left corner +// of 'iRect' for all possible outsets/clamps. +static inline void clamped_unit_outset_with_offset(SkIRect* iRect, SkPoint* offset, + const SkIRect& clamp) { + iRect->outset(1, 1); + + if (iRect->fLeft < clamp.fLeft) { + iRect->fLeft = clamp.fLeft; + } else { + offset->fX -= SK_Scalar1; + } + if (iRect->fTop < clamp.fTop) { + iRect->fTop = clamp.fTop; + } else { + offset->fY -= SK_Scalar1; + } + + if (iRect->fRight > clamp.fRight) { + iRect->fRight = clamp.fRight; + } + if (iRect->fBottom > clamp.fBottom) { + iRect->fBottom = clamp.fBottom; + } +} + +void SkGpuDevice::drawBitmapCommon(const SkDraw& draw, + const SkBitmap& bitmap, + const SkRect* srcRectPtr, + const SkMatrix& m, + const SkPaint& paint, + SkCanvas::DrawBitmapRectFlags flags) { + CHECK_SHOULD_DRAW(draw, false); + + SkRect srcRect; + if (NULL == srcRectPtr) { + srcRect.set(0, 0, SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height())); + } else { + srcRect = *srcRectPtr; + } + + if (paint.getMaskFilter()){ + // Convert the bitmap to a shader so that the rect can be drawn + // through drawRect, which supports mask filters. + SkMatrix newM(m); + SkBitmap tmp; // subset of bitmap, if necessary + const SkBitmap* bitmapPtr = &bitmap; + if (NULL != srcRectPtr) { + SkIRect iSrc; + srcRect.roundOut(&iSrc); + + SkPoint offset = SkPoint::Make(SkIntToScalar(iSrc.fLeft), + SkIntToScalar(iSrc.fTop)); + + if (SkCanvas::kBleed_DrawBitmapRectFlag & flags) { + // In bleed mode we want to expand the src rect on all sides + // but stay within the bitmap bounds + SkIRect iClampRect = SkIRect::MakeWH(bitmap.width(), bitmap.height()); + clamped_unit_outset_with_offset(&iSrc, &offset, iClampRect); + } + + if (!bitmap.extractSubset(&tmp, iSrc)) { + return; // extraction failed + } + bitmapPtr = &tmp; + srcRect.offset(-offset.fX, -offset.fY); + // The source rect has changed so update the matrix + newM.preTranslate(offset.fX, offset.fY); + } + + SkPaint paintWithTexture(paint); + paintWithTexture.setShader(SkShader::CreateBitmapShader(*bitmapPtr, + SkShader::kClamp_TileMode, SkShader::kClamp_TileMode))->unref(); + + // Transform 'newM' needs to be concatenated to the current matrix, + // rather than transforming the primitive directly, so that 'newM' will + // also affect the behavior of the mask filter. + SkMatrix drawMatrix; + drawMatrix.setConcat(fContext->getMatrix(), newM); + SkDraw transformedDraw(draw); + transformedDraw.fMatrix = &drawMatrix; + + this->drawRect(transformedDraw, srcRect, paintWithTexture); + + return; + } + + fContext->concatMatrix(m); + + GrTextureParams params; + SkPaint::FilterLevel paintFilterLevel = paint.getFilterLevel(); + GrTextureParams::FilterMode textureFilterMode; + switch(paintFilterLevel) { + case SkPaint::kNone_FilterLevel: + textureFilterMode = GrTextureParams::kNone_FilterMode; + break; + case SkPaint::kLow_FilterLevel: + textureFilterMode = GrTextureParams::kBilerp_FilterMode; + break; + case SkPaint::kMedium_FilterLevel: + textureFilterMode = GrTextureParams::kMipMap_FilterMode; + break; + case SkPaint::kHigh_FilterLevel: + // Fall back to mips for now + textureFilterMode = GrTextureParams::kMipMap_FilterMode; + break; + default: + SkErrorInternals::SetError( kInvalidPaint_SkError, + "Sorry, I don't understand the filtering " + "mode you asked for. Falling back to " + "MIPMaps."); + textureFilterMode = GrTextureParams::kMipMap_FilterMode; + break; + + } + + params.setFilterMode(textureFilterMode); + + int maxTileSize = fContext->getMaxTextureSize(); + if (SkPaint::kNone_FilterLevel != paint.getFilterLevel()) { + // We may need a skosh more room if we have to bump out the tile + // by 1 pixel all around + maxTileSize -= 2; + } + int tileSize; + + SkIRect clippedSrcRect; + if (this->shouldTileBitmap(bitmap, params, srcRectPtr, maxTileSize, &tileSize, + &clippedSrcRect)) { + this->drawTiledBitmap(bitmap, srcRect, clippedSrcRect, params, paint, flags, tileSize); + } else { + // take the simple case + this->internalDrawBitmap(bitmap, srcRect, params, paint, flags); + } +} + +// Break 'bitmap' into several tiles to draw it since it has already +// been determined to be too large to fit in VRAM +void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap, + const SkRect& srcRect, + const SkIRect& clippedSrcIRect, + const GrTextureParams& params, + const SkPaint& paint, + SkCanvas::DrawBitmapRectFlags flags, + int tileSize) { + SkRect clippedSrcRect = SkRect::Make(clippedSrcIRect); + + int nx = bitmap.width() / tileSize; + int ny = bitmap.height() / tileSize; + for (int x = 0; x <= nx; x++) { + for (int y = 0; y <= ny; y++) { + SkRect tileR; + tileR.set(SkIntToScalar(x * tileSize), + SkIntToScalar(y * tileSize), + SkIntToScalar((x + 1) * tileSize), + SkIntToScalar((y + 1) * tileSize)); + + if (!SkRect::Intersects(tileR, clippedSrcRect)) { + continue; + } + + if (!tileR.intersect(srcRect)) { + continue; + } + + SkBitmap tmpB; + SkIRect iTileR; + tileR.roundOut(&iTileR); + SkPoint offset = SkPoint::Make(SkIntToScalar(iTileR.fLeft), + SkIntToScalar(iTileR.fTop)); + + if (SkPaint::kNone_FilterLevel != paint.getFilterLevel()) { + SkIRect iClampRect; + + if (SkCanvas::kBleed_DrawBitmapRectFlag & flags) { + // In bleed mode we want to always expand the tile on all edges + // but stay within the bitmap bounds + iClampRect = SkIRect::MakeWH(bitmap.width(), bitmap.height()); + } else { + // In texture-domain/clamp mode we only want to expand the + // tile on edges interior to "srcRect" (i.e., we want to + // not bleed across the original clamped edges) + srcRect.roundOut(&iClampRect); + } + + clamped_unit_outset_with_offset(&iTileR, &offset, iClampRect); + } + + if (bitmap.extractSubset(&tmpB, iTileR)) { + // now offset it to make it "local" to our tmp bitmap + tileR.offset(-offset.fX, -offset.fY); + SkMatrix tmpM; + tmpM.setTranslate(offset.fX, offset.fY); + GrContext::AutoMatrix am; + am.setPreConcat(fContext, tmpM); + this->internalDrawBitmap(tmpB, tileR, params, paint, flags); + } + } + } +} + +static bool has_aligned_samples(const SkRect& srcRect, + const SkRect& transformedRect) { + // detect pixel disalignment + if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) - + transformedRect.left()) < COLOR_BLEED_TOLERANCE && + SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) - + transformedRect.top()) < COLOR_BLEED_TOLERANCE && + SkScalarAbs(transformedRect.width() - srcRect.width()) < + COLOR_BLEED_TOLERANCE && + SkScalarAbs(transformedRect.height() - srcRect.height()) < + COLOR_BLEED_TOLERANCE) { + return true; + } + return false; +} + +static bool may_color_bleed(const SkRect& srcRect, + const SkRect& transformedRect, + const SkMatrix& m) { + // Only gets called if has_aligned_samples returned false. + // So we can assume that sampling is axis aligned but not texel aligned. + SkASSERT(!has_aligned_samples(srcRect, transformedRect)); + SkRect innerSrcRect(srcRect), innerTransformedRect, + outerTransformedRect(transformedRect); + innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf); + m.mapRect(&innerTransformedRect, innerSrcRect); + + // The gap between outerTransformedRect and innerTransformedRect + // represents the projection of the source border area, which is + // problematic for color bleeding. We must check whether any + // destination pixels sample the border area. + outerTransformedRect.inset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE); + innerTransformedRect.outset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE); + SkIRect outer, inner; + outerTransformedRect.round(&outer); + innerTransformedRect.round(&inner); + // If the inner and outer rects round to the same result, it means the + // border does not overlap any pixel centers. Yay! + return inner != outer; +} + + +/* + * This is called by drawBitmap(), which has to handle images that may be too + * large to be represented by a single texture. + * + * internalDrawBitmap assumes that the specified bitmap will fit in a texture + * and that non-texture portion of the GrPaint has already been setup. + */ +void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap, + const SkRect& srcRect, + const GrTextureParams& params, + const SkPaint& paint, + SkCanvas::DrawBitmapRectFlags flags) { + SkASSERT(bitmap.width() <= fContext->getMaxTextureSize() && + bitmap.height() <= fContext->getMaxTextureSize()); + + GrTexture* texture; + SkAutoCachedTexture act(this, bitmap, ¶ms, &texture); + if (NULL == texture) { + return; + } + + SkRect dstRect(srcRect); + SkRect paintRect; + SkScalar wInv = SkScalarInvert(SkIntToScalar(texture->width())); + SkScalar hInv = SkScalarInvert(SkIntToScalar(texture->height())); + paintRect.setLTRB(SkScalarMul(srcRect.fLeft, wInv), + SkScalarMul(srcRect.fTop, hInv), + SkScalarMul(srcRect.fRight, wInv), + SkScalarMul(srcRect.fBottom, hInv)); + + bool needsTextureDomain = false; + if (!(flags & SkCanvas::kBleed_DrawBitmapRectFlag) && + params.filterMode() != GrTextureParams::kNone_FilterMode) { + // Need texture domain if drawing a sub rect. + needsTextureDomain = srcRect.width() < bitmap.width() || + srcRect.height() < bitmap.height(); + if (needsTextureDomain && fContext->getMatrix().rectStaysRect()) { + const SkMatrix& matrix = fContext->getMatrix(); + // sampling is axis-aligned + SkRect transformedRect; + matrix.mapRect(&transformedRect, srcRect); + + if (has_aligned_samples(srcRect, transformedRect)) { + // We could also turn off filtering here (but we already did a cache lookup with + // params). + needsTextureDomain = false; + } else { + needsTextureDomain = may_color_bleed(srcRect, transformedRect, matrix); + } + } + } + + SkRect textureDomain = SkRect::MakeEmpty(); + SkAutoTUnref effect; + if (needsTextureDomain) { + // Use a constrained texture domain to avoid color bleeding + SkScalar left, top, right, bottom; + if (srcRect.width() > SK_Scalar1) { + SkScalar border = SK_ScalarHalf / texture->width(); + left = paintRect.left() + border; + right = paintRect.right() - border; + } else { + left = right = SkScalarHalf(paintRect.left() + paintRect.right()); + } + if (srcRect.height() > SK_Scalar1) { + SkScalar border = SK_ScalarHalf / texture->height(); + top = paintRect.top() + border; + bottom = paintRect.bottom() - border; + } else { + top = bottom = SkScalarHalf(paintRect.top() + paintRect.bottom()); + } + textureDomain.setLTRB(left, top, right, bottom); + effect.reset(GrTextureDomainEffect::Create(texture, + SkMatrix::I(), + textureDomain, + GrTextureDomainEffect::kClamp_WrapMode, + params.filterMode())); + } else { + effect.reset(GrSimpleTextureEffect::Create(texture, SkMatrix::I(), params)); + } + + // Construct a GrPaint by setting the bitmap texture as the first effect and then configuring + // the rest from the SkPaint. + GrPaint grPaint; + grPaint.addColorEffect(effect); + bool alphaOnly = !(SkBitmap::kA8_Config == bitmap.config()); + if (!skPaint2GrPaintNoShader(this, paint, alphaOnly, false, &grPaint)) { + return; + } + + fContext->drawRectToRect(grPaint, dstRect, paintRect, NULL); +} + +static bool filter_texture(SkBaseDevice* device, GrContext* context, + GrTexture* texture, SkImageFilter* filter, + int w, int h, const SkMatrix& ctm, SkBitmap* result, + SkIPoint* offset) { + SkASSERT(filter); + SkDeviceImageFilterProxy proxy(device); + + if (filter->canFilterImageGPU()) { + // Save the render target and set it to NULL, so we don't accidentally draw to it in the + // filter. Also set the clip wide open and the matrix to identity. + GrContext::AutoWideOpenIdentityDraw awo(context, NULL); + return filter->filterImageGPU(&proxy, wrap_texture(texture), ctm, result, offset); + } else { + return false; + } +} + +void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, + int left, int top, const SkPaint& paint) { + // drawSprite is defined to be in device coords. + CHECK_SHOULD_DRAW(draw, true); + + SkAutoLockPixels alp(bitmap, !bitmap.getTexture()); + if (!bitmap.getTexture() && !bitmap.readyToDraw()) { + return; + } + + int w = bitmap.width(); + int h = bitmap.height(); + + GrTexture* texture; + // draw sprite uses the default texture params + SkAutoCachedTexture act(this, bitmap, NULL, &texture); + + SkImageFilter* filter = paint.getImageFilter(); + SkIPoint offset = SkIPoint::Make(left, top); + // This bitmap will own the filtered result as a texture. + SkBitmap filteredBitmap; + + if (NULL != filter) { + SkMatrix matrix(*draw.fMatrix); + matrix.postTranslate(SkIntToScalar(-left), SkIntToScalar(-top)); + if (filter_texture(this, fContext, texture, filter, w, h, matrix, &filteredBitmap, + &offset)) { + texture = (GrTexture*) filteredBitmap.getTexture(); + w = filteredBitmap.width(); + h = filteredBitmap.height(); + } else { + return; + } + } + + GrPaint grPaint; + grPaint.addColorTextureEffect(texture, SkMatrix::I()); + + if(!skPaint2GrPaintNoShader(this, paint, true, false, &grPaint)) { + return; + } + + fContext->drawRectToRect(grPaint, + SkRect::MakeXYWH(SkIntToScalar(offset.fX), + SkIntToScalar(offset.fY), + SkIntToScalar(w), + SkIntToScalar(h)), + SkRect::MakeXYWH(0, + 0, + SK_Scalar1 * w / texture->width(), + SK_Scalar1 * h / texture->height())); +} + +void SkGpuDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap, + const SkRect* src, const SkRect& dst, + const SkPaint& paint, + SkCanvas::DrawBitmapRectFlags flags) { + SkMatrix matrix; + SkRect bitmapBounds, tmpSrc; + + bitmapBounds.set(0, 0, + SkIntToScalar(bitmap.width()), + SkIntToScalar(bitmap.height())); + + // Compute matrix from the two rectangles + if (NULL != src) { + tmpSrc = *src; + } else { + tmpSrc = bitmapBounds; + } + matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit); + + // clip the tmpSrc to the bounds of the bitmap. No check needed if src==null. + if (NULL != src) { + if (!bitmapBounds.contains(tmpSrc)) { + if (!tmpSrc.intersect(bitmapBounds)) { + return; // nothing to draw + } + } + } + + this->drawBitmapCommon(draw, bitmap, &tmpSrc, matrix, paint, flags); +} + +void SkGpuDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device, + int x, int y, const SkPaint& paint) { + // clear of the source device must occur before CHECK_SHOULD_DRAW + SkGpuDevice* dev = static_cast(device); + if (dev->fNeedClear) { + // TODO: could check here whether we really need to draw at all + dev->clear(0x0); + } + + // drawDevice is defined to be in device coords. + CHECK_SHOULD_DRAW(draw, true); + + GrRenderTarget* devRT = dev->accessRenderTarget(); + GrTexture* devTex; + if (NULL == (devTex = devRT->asTexture())) { + return; + } + + const SkBitmap& bm = dev->accessBitmap(false); + int w = bm.width(); + int h = bm.height(); + + SkImageFilter* filter = paint.getImageFilter(); + // This bitmap will own the filtered result as a texture. + SkBitmap filteredBitmap; + + if (NULL != filter) { + SkIPoint offset = SkIPoint::Make(0, 0); + SkMatrix matrix(*draw.fMatrix); + matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y)); + if (filter_texture(this, fContext, devTex, filter, w, h, matrix, &filteredBitmap, + &offset)) { + devTex = filteredBitmap.getTexture(); + w = filteredBitmap.width(); + h = filteredBitmap.height(); + x += offset.fX; + y += offset.fY; + } else { + return; + } + } + + GrPaint grPaint; + grPaint.addColorTextureEffect(devTex, SkMatrix::I()); + + if (!skPaint2GrPaintNoShader(this, paint, true, false, &grPaint)) { + return; + } + + SkRect dstRect = SkRect::MakeXYWH(SkIntToScalar(x), + SkIntToScalar(y), + SkIntToScalar(w), + SkIntToScalar(h)); + + // The device being drawn may not fill up its texture (e.g. saveLayer uses approximate + // scratch texture). + SkRect srcRect = SkRect::MakeWH(SK_Scalar1 * w / devTex->width(), + SK_Scalar1 * h / devTex->height()); + + fContext->drawRectToRect(grPaint, dstRect, srcRect); +} + +bool SkGpuDevice::canHandleImageFilter(SkImageFilter* filter) { + return filter->canFilterImageGPU(); +} + +bool SkGpuDevice::filterImage(SkImageFilter* filter, const SkBitmap& src, + const SkMatrix& ctm, + SkBitmap* result, SkIPoint* offset) { + // want explicitly our impl, so guard against a subclass of us overriding it + if (!this->SkGpuDevice::canHandleImageFilter(filter)) { + return false; + } + + SkAutoLockPixels alp(src, !src.getTexture()); + if (!src.getTexture() && !src.readyToDraw()) { + return false; + } + + GrTexture* texture; + // We assume here that the filter will not attempt to tile the src. Otherwise, this cache lookup + // must be pushed upstack. + SkAutoCachedTexture act(this, src, NULL, &texture); + + return filter_texture(this, fContext, texture, filter, src.width(), src.height(), ctm, result, + offset); +} + +/////////////////////////////////////////////////////////////////////////////// + +// must be in SkCanvas::VertexMode order +static const GrPrimitiveType gVertexMode2PrimitiveType[] = { + kTriangles_GrPrimitiveType, + kTriangleStrip_GrPrimitiveType, + kTriangleFan_GrPrimitiveType, +}; + +void SkGpuDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode, + int vertexCount, const SkPoint vertices[], + const SkPoint texs[], const SkColor colors[], + SkXfermode* xmode, + const uint16_t indices[], int indexCount, + const SkPaint& paint) { + CHECK_SHOULD_DRAW(draw, false); + + GrPaint grPaint; + // we ignore the shader if texs is null. + if (NULL == texs) { + if (!skPaint2GrPaintNoShader(this, paint, false, NULL == colors, &grPaint)) { + return; + } + } else { + if (!skPaint2GrPaintShader(this, paint, NULL == colors, &grPaint)) { + return; + } + } + + if (NULL != xmode && NULL != texs && NULL != colors) { + if (!SkXfermode::IsMode(xmode, SkXfermode::kModulate_Mode)) { + SkDebugf("Unsupported vertex-color/texture xfer mode.\n"); +#if 0 + return +#endif + } + } + + SkAutoSTMalloc<128, GrColor> convertedColors(0); + if (NULL != colors) { + // need to convert byte order and from non-PM to PM + convertedColors.reset(vertexCount); + for (int i = 0; i < vertexCount; ++i) { + convertedColors[i] = SkColor2GrColor(colors[i]); + } + colors = convertedColors.get(); + } + fContext->drawVertices(grPaint, + gVertexMode2PrimitiveType[vmode], + vertexCount, + (GrPoint*) vertices, + (GrPoint*) texs, + colors, + indices, + indexCount); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void GlyphCacheAuxProc(void* data) { + GrFontScaler* scaler = (GrFontScaler*)data; + SkSafeUnref(scaler); +} + +static GrFontScaler* get_gr_font_scaler(SkGlyphCache* cache) { + void* auxData; + GrFontScaler* scaler = NULL; + if (cache->getAuxProcData(GlyphCacheAuxProc, &auxData)) { + scaler = (GrFontScaler*)auxData; + } + if (NULL == scaler) { + scaler = SkNEW_ARGS(SkGrFontScaler, (cache)); + cache->setAuxProc(GlyphCacheAuxProc, scaler); + } + return scaler; +} + +static void SkGPU_Draw1Glyph(const SkDraw1Glyph& state, + SkFixed fx, SkFixed fy, + const SkGlyph& glyph) { + SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0); + + GrSkDrawProcs* procs = static_cast(state.fDraw->fProcs); + + if (NULL == procs->fFontScaler) { + procs->fFontScaler = get_gr_font_scaler(state.fCache); + } + + procs->fTextContext->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(), + glyph.getSubXFixed(), + glyph.getSubYFixed()), + SkFixedFloorToFixed(fx), + SkFixedFloorToFixed(fy), + procs->fFontScaler); +} + +SkDrawProcs* SkGpuDevice::initDrawForText(GrTextContext* context) { + + // deferred allocation + if (NULL == fDrawProcs) { + fDrawProcs = SkNEW(GrSkDrawProcs); + fDrawProcs->fD1GProc = SkGPU_Draw1Glyph; + fDrawProcs->fContext = fContext; +#if SK_DISTANCEFIELD_FONTS + fDrawProcs->fFlags = 0; + fDrawProcs->fFlags |= SkDrawProcs::kSkipBakedGlyphTransform_Flag; + fDrawProcs->fFlags |= SkDrawProcs::kUseScaledGlyphs_Flag; +#endif + } + + // init our (and GL's) state + fDrawProcs->fTextContext = context; + fDrawProcs->fFontScaler = NULL; + return fDrawProcs; +} + +void SkGpuDevice::drawText(const SkDraw& draw, const void* text, + size_t byteLength, SkScalar x, SkScalar y, + const SkPaint& paint) { + CHECK_SHOULD_DRAW(draw, false); + + if (fContext->getMatrix().hasPerspective()) { + // this guy will just call our drawPath() + draw.drawText((const char*)text, byteLength, x, y, paint); + } else { + SkDraw myDraw(draw); + + GrPaint grPaint; + if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { + return; + } +#if SK_DISTANCEFIELD_FONTS + GrDistanceFieldTextContext context(fContext, grPaint, paint.getColor(), + paint.getTextSize()); +#else + GrBitmapTextContext context(fContext, grPaint, paint.getColor()); +#endif + myDraw.fProcs = this->initDrawForText(&context); + this->INHERITED::drawText(myDraw, text, byteLength, x, y, paint); + } +} + +void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text, + size_t byteLength, const SkScalar pos[], + SkScalar constY, int scalarsPerPos, + const SkPaint& paint) { + CHECK_SHOULD_DRAW(draw, false); + + if (fContext->getMatrix().hasPerspective()) { + // this guy will just call our drawPath() + draw.drawPosText((const char*)text, byteLength, pos, constY, + scalarsPerPos, paint); + } else { + SkDraw myDraw(draw); + + GrPaint grPaint; + if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { + return; + } +#if SK_DISTANCEFIELD_FONTS + GrDistanceFieldTextContext context(fContext, grPaint, paint.getColor(), + paint.getTextSize()/SkDrawProcs::kBaseDFFontSize); +#else + GrBitmapTextContext context(fContext, grPaint, paint.getColor()); +#endif + myDraw.fProcs = this->initDrawForText(&context); + this->INHERITED::drawPosText(myDraw, text, byteLength, pos, constY, + scalarsPerPos, paint); + } +} + +void SkGpuDevice::drawTextOnPath(const SkDraw& draw, const void* text, + size_t len, const SkPath& path, + const SkMatrix* m, const SkPaint& paint) { + CHECK_SHOULD_DRAW(draw, false); + + SkASSERT(draw.fDevice == this); + draw.drawTextOnPath((const char*)text, len, path, m, paint); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool SkGpuDevice::filterTextFlags(const SkPaint& paint, TextFlags* flags) { + if (!paint.isLCDRenderText()) { + // we're cool with the paint as is + return false; + } + + if (paint.getShader() || + paint.getXfermode() || // unless its srcover + paint.getMaskFilter() || + paint.getRasterizer() || + paint.getColorFilter() || + paint.getPathEffect() || + paint.isFakeBoldText() || + paint.getStyle() != SkPaint::kFill_Style) { + // turn off lcd + flags->fFlags = paint.getFlags() & ~SkPaint::kLCDRenderText_Flag; + flags->fHinting = paint.getHinting(); + return true; + } + // we're cool with the paint as is + return false; +} + +void SkGpuDevice::flush() { + DO_DEFERRED_CLEAR(); + fContext->resolveRenderTarget(fRenderTarget); +} + +/////////////////////////////////////////////////////////////////////////////// + +SkBaseDevice* SkGpuDevice::onCreateCompatibleDevice(SkBitmap::Config config, + int width, int height, + bool isOpaque, + Usage usage) { + GrTextureDesc desc; + desc.fConfig = fRenderTarget->config(); + desc.fFlags = kRenderTarget_GrTextureFlagBit; + desc.fWidth = width; + desc.fHeight = height; + desc.fSampleCnt = fRenderTarget->numSamples(); + + SkAutoTUnref texture; + // Skia's convention is to only clear a device if it is non-opaque. + bool needClear = !isOpaque; + +#if CACHE_COMPATIBLE_DEVICE_TEXTURES + // layers are never draw in repeat modes, so we can request an approx + // match and ignore any padding. + const GrContext::ScratchTexMatch match = (kSaveLayer_Usage == usage) ? + GrContext::kApprox_ScratchTexMatch : + GrContext::kExact_ScratchTexMatch; + texture.reset(fContext->lockAndRefScratchTexture(desc, match)); +#else + texture.reset(fContext->createUncachedTexture(desc, NULL, 0)); +#endif + if (NULL != texture.get()) { + return SkNEW_ARGS(SkGpuDevice,(fContext, texture, needClear)); + } else { + GrPrintf("---- failed to create compatible device texture [%d %d]\n", width, height); + return NULL; + } +} + +SkGpuDevice::SkGpuDevice(GrContext* context, + GrTexture* texture, + bool needClear) + : SkBitmapDevice(make_bitmap(context, texture->asRenderTarget())) { + + SkASSERT(texture && texture->asRenderTarget()); + // This constructor is called from onCreateCompatibleDevice. It has locked the RT in the texture + // cache. We pass true for the third argument so that it will get unlocked. + this->initFromRenderTarget(context, texture->asRenderTarget(), true); + fNeedClear = needClear; +} diff --git a/src/gpu/effects/GrDistanceFieldTextureEffect.cpp b/src/gpu/effects/GrDistanceFieldTextureEffect.cpp index fd27f2b31d..8c38f9bebf 100755 --- a/src/gpu/effects/GrDistanceFieldTextureEffect.cpp +++ b/src/gpu/effects/GrDistanceFieldTextureEffect.cpp @@ -14,7 +14,7 @@ #include "GrTexture.h" // The distance field is constructed as unsigned char values, so that the zero value is at 128. -// Hence our zero threshold is 128/255. +// Hence our zero threshold is 128/255. #define THRESHOLD "0.50196078431" class GrGLDistanceFieldTextureEffect : public GrGLVertexEffect { @@ -47,7 +47,7 @@ public: kVec2f_GrSLType); builder->fsCodeAppend(";\n"); builder->fsCodeAppend("\tfloat distance = texColor.r;\n"); - // this gives us a smooth step across approximately one fragment + // this gives us a smooth step across approximately one fragment // (assuming a radius of the diagonal of the fragment, hence a factor of sqrt(2)/2) builder->fsCodeAppend("\tfloat afwidth = 0.7071*length(vec2(dFdx(distance), dFdy(distance)));\n"); builder->fsCodeAppend("\tfloat val = smoothstep("THRESHOLD"-afwidth, "THRESHOLD"+afwidth, distance);\n"); diff --git a/src/gpu/effects/GrDistanceFieldTextureEffect.h b/src/gpu/effects/GrDistanceFieldTextureEffect.h index 34d4ae5363..fc37ddb99a 100755 --- a/src/gpu/effects/GrDistanceFieldTextureEffect.h +++ b/src/gpu/effects/GrDistanceFieldTextureEffect.h @@ -14,7 +14,7 @@ class GrGLDistanceFieldTextureEffect; /** - * The output color of this effect is a modulation of the input color and a sample from a + * The output color of this effect is a modulation of the input color and a sample from a * distance field texture (using a smoothed step function near 0.5). * It allows explicit specification of the filtering and wrap modes (GrTextureParams). The input * coords are a custom attribute. -- cgit v1.2.3