Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(644)

Unified Diff: src/gpu/SkGpuDevice.cpp

Issue 41213003: Hook in rough distance field support for fonts (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: Port over fix for alpha-blended paint color (may not be needed). Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: src/gpu/SkGpuDevice.cpp
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 4041c411fc1391618b5238c3c85ddd83c13461c3..29081001a1bd08f7f24ee514e90e977947567c8d 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1,1860 +1,1877 @@
-/*
- * 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"
-
-#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<GrTexture> 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);
-}
-
-namespace {
-void purgeClipCB(int genID, void* ) {
-
- if (SkClipStack::kInvalidGenID == genID ||
- SkClipStack::kEmptyGenID == genID ||
- SkClipStack::kWideOpenGenID == genID) {
- // none of these cases will have a cached clip mask
- return;
- }
-
-}
-};
-
-void SkGpuDevice::onAttachToCanvas(SkCanvas* canvas) {
- INHERITED::onAttachToCanvas(canvas);
-
- // Canvas promises that this ptr is valid until onDetachFromCanvas is called
- fClipData.fClipStack = canvas->getClipStack();
-
- fClipData.fClipStack->addPurgeClipCallback(purgeClipCB, fContext);
-}
-
-void SkGpuDevice::onDetachFromCanvas() {
- INHERITED::onDetachFromCanvas();
-
- // TODO: iterate through the clip stack and clean up any cached clip masks
- fClipData.fClipStack->removePurgeClipCallback(purgeClipCB, fContext);
-
- 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<GrEffectRef> 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<GrEffectRef> 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, and fills.
- Anything else we just call our path code.
- */
- bool usePath = doStroke && width > 0 &&
- paint.getStrokeJoin() != SkPaint::kMiter_Join;
- // 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
- }
- // small miter limit means right angles show bevel...
- if (SkPaint::kMiter_Join == paint.getStrokeJoin() &&
- paint.getStrokeMiter() < SK_ScalarSqrt2)
- {
- usePath = true;
- }
- // 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;
- }
- fContext->drawRect(grPaint, rect, doStroke ? width : -1);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-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<SkPath*>(&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<GrTexture> 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, &params)) {
- 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, &params, &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<GrEffectRef> 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<SkGpuDevice*>(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<GrSkDrawProcs*>(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;
- }
-
- // 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;
- }
-
- GrBitmapTextContext context(fContext, grPaint, paint.getColor());
- 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;
- }
- GrBitmapTextContext context(fContext, grPaint, paint.getColor());
- 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<GrTexture> 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"
+#include "GrDistanceFieldTextContext.h"
+
+#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
+
+#define USE_DISTANCE_FIELDS 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<GrTexture> 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);
+}
+
+namespace {
+void purgeClipCB(int genID, void* ) {
+
+ if (SkClipStack::kInvalidGenID == genID ||
+ SkClipStack::kEmptyGenID == genID ||
+ SkClipStack::kWideOpenGenID == genID) {
+ // none of these cases will have a cached clip mask
+ return;
+ }
+
+}
+};
+
+void SkGpuDevice::onAttachToCanvas(SkCanvas* canvas) {
+ INHERITED::onAttachToCanvas(canvas);
+
+ // Canvas promises that this ptr is valid until onDetachFromCanvas is called
+ fClipData.fClipStack = canvas->getClipStack();
+
+ fClipData.fClipStack->addPurgeClipCallback(purgeClipCB, fContext);
+}
+
+void SkGpuDevice::onDetachFromCanvas() {
+ INHERITED::onDetachFromCanvas();
+
+ // TODO: iterate through the clip stack and clean up any cached clip masks
+ fClipData.fClipStack->removePurgeClipCallback(purgeClipCB, fContext);
+
+ 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<GrEffectRef> 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<GrEffectRef> 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, and fills.
+ Anything else we just call our path code.
+ */
+ bool usePath = doStroke && width > 0 &&
+ paint.getStrokeJoin() != SkPaint::kMiter_Join;
+ // 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
+ }
+ // small miter limit means right angles show bevel...
+ if (SkPaint::kMiter_Join == paint.getStrokeJoin() &&
+ paint.getStrokeMiter() < SK_ScalarSqrt2)
+ {
+ usePath = true;
+ }
+ // 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;
+ }
+ fContext->drawRect(grPaint, rect, doStroke ? width : -1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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<SkPath*>(&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<GrTexture> 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, &params)) {
+ 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, &params, &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<GrEffectRef> 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<SkGpuDevice*>(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<GrSkDrawProcs*>(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;
+ fDrawProcs->fFlags = 0;
+#if USE_DISTANCE_FIELDS
+ 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 USE_DISTANCE_FIELDS
+ 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 USE_DISTANCE_FIELDS
+ GrDistanceFieldTextContext context(fContext, grPaint, paint.getColor(),
+ paint.getTextSize());
+#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<GrTexture> 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;
+}

Powered by Google App Engine
This is Rietveld 408576698