Index: src/gpu/SkGpuDevice.cpp |
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp |
index 4041c411fc1391618b5238c3c85ddd83c13461c3..f5b84a3136144c12333d003238003c47524a0b51 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, ¶ms)) { |
- return false; |
- } |
- |
- // At this point we know we could do the draw by uploading the entire bitmap |
- // as a texture. However, if the texture would be large compared to the |
- // cache size and we don't require most of it for this draw then tile to |
- // reduce the amount of upload and cache spill. |
- |
- // assumption here is that sw bitmap size is a good proxy for its size as |
- // a texture |
- size_t bmpSize = bitmap.getSize(); |
- size_t cacheSize; |
- fContext->getTextureCacheLimits(NULL, &cacheSize); |
- if (bmpSize < cacheSize / 2) { |
- return false; |
- } |
- |
- // Figure out how much of the src we will need based on the src rect and clipping. |
- determine_clipped_src_rect(fContext, bitmap, srcRectPtr, clippedSrcRect); |
- *tileSize = kBmpSmallTileSize; // already know whole bitmap fits in one max sized tile. |
- size_t usedTileBytes = get_tile_count(*clippedSrcRect, kBmpSmallTileSize) * |
- kBmpSmallTileSize * kBmpSmallTileSize; |
- |
- return usedTileBytes < 2 * bmpSize; |
-} |
- |
-void SkGpuDevice::drawBitmap(const SkDraw& draw, |
- const SkBitmap& bitmap, |
- const SkMatrix& m, |
- const SkPaint& paint) { |
- // We cannot call drawBitmapRect here since 'm' could be anything |
- this->drawBitmapCommon(draw, bitmap, NULL, m, paint, |
- SkCanvas::kNone_DrawBitmapRectFlag); |
-} |
- |
-// This method outsets 'iRect' by 1 all around and then clamps its extents to |
-// 'clamp'. 'offset' is adjusted to remain positioned over the top-left corner |
-// of 'iRect' for all possible outsets/clamps. |
-static inline void clamped_unit_outset_with_offset(SkIRect* iRect, SkPoint* offset, |
- const SkIRect& clamp) { |
- iRect->outset(1, 1); |
- |
- if (iRect->fLeft < clamp.fLeft) { |
- iRect->fLeft = clamp.fLeft; |
- } else { |
- offset->fX -= SK_Scalar1; |
- } |
- if (iRect->fTop < clamp.fTop) { |
- iRect->fTop = clamp.fTop; |
- } else { |
- offset->fY -= SK_Scalar1; |
- } |
- |
- if (iRect->fRight > clamp.fRight) { |
- iRect->fRight = clamp.fRight; |
- } |
- if (iRect->fBottom > clamp.fBottom) { |
- iRect->fBottom = clamp.fBottom; |
- } |
-} |
- |
-void SkGpuDevice::drawBitmapCommon(const SkDraw& draw, |
- const SkBitmap& bitmap, |
- const SkRect* srcRectPtr, |
- const SkMatrix& m, |
- const SkPaint& paint, |
- SkCanvas::DrawBitmapRectFlags flags) { |
- CHECK_SHOULD_DRAW(draw, false); |
- |
- SkRect srcRect; |
- if (NULL == srcRectPtr) { |
- srcRect.set(0, 0, SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height())); |
- } else { |
- srcRect = *srcRectPtr; |
- } |
- |
- if (paint.getMaskFilter()){ |
- // Convert the bitmap to a shader so that the rect can be drawn |
- // through drawRect, which supports mask filters. |
- SkMatrix newM(m); |
- SkBitmap tmp; // subset of bitmap, if necessary |
- const SkBitmap* bitmapPtr = &bitmap; |
- if (NULL != srcRectPtr) { |
- SkIRect iSrc; |
- srcRect.roundOut(&iSrc); |
- |
- SkPoint offset = SkPoint::Make(SkIntToScalar(iSrc.fLeft), |
- SkIntToScalar(iSrc.fTop)); |
- |
- if (SkCanvas::kBleed_DrawBitmapRectFlag & flags) { |
- // In bleed mode we want to expand the src rect on all sides |
- // but stay within the bitmap bounds |
- SkIRect iClampRect = SkIRect::MakeWH(bitmap.width(), bitmap.height()); |
- clamped_unit_outset_with_offset(&iSrc, &offset, iClampRect); |
- } |
- |
- if (!bitmap.extractSubset(&tmp, iSrc)) { |
- return; // extraction failed |
- } |
- bitmapPtr = &tmp; |
- srcRect.offset(-offset.fX, -offset.fY); |
- // The source rect has changed so update the matrix |
- newM.preTranslate(offset.fX, offset.fY); |
- } |
- |
- SkPaint paintWithTexture(paint); |
- paintWithTexture.setShader(SkShader::CreateBitmapShader(*bitmapPtr, |
- SkShader::kClamp_TileMode, SkShader::kClamp_TileMode))->unref(); |
- |
- // Transform 'newM' needs to be concatenated to the current matrix, |
- // rather than transforming the primitive directly, so that 'newM' will |
- // also affect the behavior of the mask filter. |
- SkMatrix drawMatrix; |
- drawMatrix.setConcat(fContext->getMatrix(), newM); |
- SkDraw transformedDraw(draw); |
- transformedDraw.fMatrix = &drawMatrix; |
- |
- this->drawRect(transformedDraw, srcRect, paintWithTexture); |
- |
- return; |
- } |
- |
- fContext->concatMatrix(m); |
- |
- GrTextureParams params; |
- SkPaint::FilterLevel paintFilterLevel = paint.getFilterLevel(); |
- GrTextureParams::FilterMode textureFilterMode; |
- switch(paintFilterLevel) { |
- case SkPaint::kNone_FilterLevel: |
- textureFilterMode = GrTextureParams::kNone_FilterMode; |
- break; |
- case SkPaint::kLow_FilterLevel: |
- textureFilterMode = GrTextureParams::kBilerp_FilterMode; |
- break; |
- case SkPaint::kMedium_FilterLevel: |
- textureFilterMode = GrTextureParams::kMipMap_FilterMode; |
- break; |
- case SkPaint::kHigh_FilterLevel: |
- // Fall back to mips for now |
- textureFilterMode = GrTextureParams::kMipMap_FilterMode; |
- break; |
- default: |
- SkErrorInternals::SetError( kInvalidPaint_SkError, |
- "Sorry, I don't understand the filtering " |
- "mode you asked for. Falling back to " |
- "MIPMaps."); |
- textureFilterMode = GrTextureParams::kMipMap_FilterMode; |
- break; |
- |
- } |
- |
- params.setFilterMode(textureFilterMode); |
- |
- int maxTileSize = fContext->getMaxTextureSize(); |
- if (SkPaint::kNone_FilterLevel != paint.getFilterLevel()) { |
- // We may need a skosh more room if we have to bump out the tile |
- // by 1 pixel all around |
- maxTileSize -= 2; |
- } |
- int tileSize; |
- |
- SkIRect clippedSrcRect; |
- if (this->shouldTileBitmap(bitmap, params, srcRectPtr, maxTileSize, &tileSize, |
- &clippedSrcRect)) { |
- this->drawTiledBitmap(bitmap, srcRect, clippedSrcRect, params, paint, flags, tileSize); |
- } else { |
- // take the simple case |
- this->internalDrawBitmap(bitmap, srcRect, params, paint, flags); |
- } |
-} |
- |
-// Break 'bitmap' into several tiles to draw it since it has already |
-// been determined to be too large to fit in VRAM |
-void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap, |
- const SkRect& srcRect, |
- const SkIRect& clippedSrcIRect, |
- const GrTextureParams& params, |
- const SkPaint& paint, |
- SkCanvas::DrawBitmapRectFlags flags, |
- int tileSize) { |
- SkRect clippedSrcRect = SkRect::Make(clippedSrcIRect); |
- |
- int nx = bitmap.width() / tileSize; |
- int ny = bitmap.height() / tileSize; |
- for (int x = 0; x <= nx; x++) { |
- for (int y = 0; y <= ny; y++) { |
- SkRect tileR; |
- tileR.set(SkIntToScalar(x * tileSize), |
- SkIntToScalar(y * tileSize), |
- SkIntToScalar((x + 1) * tileSize), |
- SkIntToScalar((y + 1) * tileSize)); |
- |
- if (!SkRect::Intersects(tileR, clippedSrcRect)) { |
- continue; |
- } |
- |
- if (!tileR.intersect(srcRect)) { |
- continue; |
- } |
- |
- SkBitmap tmpB; |
- SkIRect iTileR; |
- tileR.roundOut(&iTileR); |
- SkPoint offset = SkPoint::Make(SkIntToScalar(iTileR.fLeft), |
- SkIntToScalar(iTileR.fTop)); |
- |
- if (SkPaint::kNone_FilterLevel != paint.getFilterLevel()) { |
- SkIRect iClampRect; |
- |
- if (SkCanvas::kBleed_DrawBitmapRectFlag & flags) { |
- // In bleed mode we want to always expand the tile on all edges |
- // but stay within the bitmap bounds |
- iClampRect = SkIRect::MakeWH(bitmap.width(), bitmap.height()); |
- } else { |
- // In texture-domain/clamp mode we only want to expand the |
- // tile on edges interior to "srcRect" (i.e., we want to |
- // not bleed across the original clamped edges) |
- srcRect.roundOut(&iClampRect); |
- } |
- |
- clamped_unit_outset_with_offset(&iTileR, &offset, iClampRect); |
- } |
- |
- if (bitmap.extractSubset(&tmpB, iTileR)) { |
- // now offset it to make it "local" to our tmp bitmap |
- tileR.offset(-offset.fX, -offset.fY); |
- SkMatrix tmpM; |
- tmpM.setTranslate(offset.fX, offset.fY); |
- GrContext::AutoMatrix am; |
- am.setPreConcat(fContext, tmpM); |
- this->internalDrawBitmap(tmpB, tileR, params, paint, flags); |
- } |
- } |
- } |
-} |
- |
-static bool has_aligned_samples(const SkRect& srcRect, |
- const SkRect& transformedRect) { |
- // detect pixel disalignment |
- if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) - |
- transformedRect.left()) < COLOR_BLEED_TOLERANCE && |
- SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) - |
- transformedRect.top()) < COLOR_BLEED_TOLERANCE && |
- SkScalarAbs(transformedRect.width() - srcRect.width()) < |
- COLOR_BLEED_TOLERANCE && |
- SkScalarAbs(transformedRect.height() - srcRect.height()) < |
- COLOR_BLEED_TOLERANCE) { |
- return true; |
- } |
- return false; |
-} |
- |
-static bool may_color_bleed(const SkRect& srcRect, |
- const SkRect& transformedRect, |
- const SkMatrix& m) { |
- // Only gets called if has_aligned_samples returned false. |
- // So we can assume that sampling is axis aligned but not texel aligned. |
- SkASSERT(!has_aligned_samples(srcRect, transformedRect)); |
- SkRect innerSrcRect(srcRect), innerTransformedRect, |
- outerTransformedRect(transformedRect); |
- innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf); |
- m.mapRect(&innerTransformedRect, innerSrcRect); |
- |
- // The gap between outerTransformedRect and innerTransformedRect |
- // represents the projection of the source border area, which is |
- // problematic for color bleeding. We must check whether any |
- // destination pixels sample the border area. |
- outerTransformedRect.inset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE); |
- innerTransformedRect.outset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE); |
- SkIRect outer, inner; |
- outerTransformedRect.round(&outer); |
- innerTransformedRect.round(&inner); |
- // If the inner and outer rects round to the same result, it means the |
- // border does not overlap any pixel centers. Yay! |
- return inner != outer; |
-} |
- |
- |
-/* |
- * This is called by drawBitmap(), which has to handle images that may be too |
- * large to be represented by a single texture. |
- * |
- * internalDrawBitmap assumes that the specified bitmap will fit in a texture |
- * and that non-texture portion of the GrPaint has already been setup. |
- */ |
-void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap, |
- const SkRect& srcRect, |
- const GrTextureParams& params, |
- const SkPaint& paint, |
- SkCanvas::DrawBitmapRectFlags flags) { |
- SkASSERT(bitmap.width() <= fContext->getMaxTextureSize() && |
- bitmap.height() <= fContext->getMaxTextureSize()); |
- |
- GrTexture* texture; |
- SkAutoCachedTexture act(this, bitmap, ¶ms, &texture); |
- if (NULL == texture) { |
- return; |
- } |
- |
- SkRect dstRect(srcRect); |
- SkRect paintRect; |
- SkScalar wInv = SkScalarInvert(SkIntToScalar(texture->width())); |
- SkScalar hInv = SkScalarInvert(SkIntToScalar(texture->height())); |
- paintRect.setLTRB(SkScalarMul(srcRect.fLeft, wInv), |
- SkScalarMul(srcRect.fTop, hInv), |
- SkScalarMul(srcRect.fRight, wInv), |
- SkScalarMul(srcRect.fBottom, hInv)); |
- |
- bool needsTextureDomain = false; |
- if (!(flags & SkCanvas::kBleed_DrawBitmapRectFlag) && |
- params.filterMode() != GrTextureParams::kNone_FilterMode) { |
- // Need texture domain if drawing a sub rect. |
- needsTextureDomain = srcRect.width() < bitmap.width() || |
- srcRect.height() < bitmap.height(); |
- if (needsTextureDomain && fContext->getMatrix().rectStaysRect()) { |
- const SkMatrix& matrix = fContext->getMatrix(); |
- // sampling is axis-aligned |
- SkRect transformedRect; |
- matrix.mapRect(&transformedRect, srcRect); |
- |
- if (has_aligned_samples(srcRect, transformedRect)) { |
- // We could also turn off filtering here (but we already did a cache lookup with |
- // params). |
- needsTextureDomain = false; |
- } else { |
- needsTextureDomain = may_color_bleed(srcRect, transformedRect, matrix); |
- } |
- } |
- } |
- |
- SkRect textureDomain = SkRect::MakeEmpty(); |
- SkAutoTUnref<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" |
+#if SK_DISTANCEFIELD_FONTS |
+#include "GrDistanceFieldTextContext.h" |
+#endif |
+ |
+#include "SkGrTexturePixelRef.h" |
+ |
+#include "SkColorFilter.h" |
+#include "SkDeviceImageFilterProxy.h" |
+#include "SkDrawProcs.h" |
+#include "SkGlyphCache.h" |
+#include "SkImageFilter.h" |
+#include "SkPathEffect.h" |
+#include "SkRRect.h" |
+#include "SkStroke.h" |
+#include "SkUtils.h" |
+#include "SkErrorInternals.h" |
+ |
+#define CACHE_COMPATIBLE_DEVICE_TEXTURES 1 |
+ |
+#if 0 |
+ extern bool (*gShouldDrawProc)(); |
+ #define CHECK_SHOULD_DRAW(draw, forceI) \ |
+ do { \ |
+ if (gShouldDrawProc && !gShouldDrawProc()) return; \ |
+ this->prepareDraw(draw, forceI); \ |
+ } while (0) |
+#else |
+ #define CHECK_SHOULD_DRAW(draw, forceI) this->prepareDraw(draw, forceI) |
+#endif |
+ |
+// This constant represents the screen alignment criterion in texels for |
+// requiring texture domain clamping to prevent color bleeding when drawing |
+// a sub region of a larger source image. |
+#define COLOR_BLEED_TOLERANCE SkFloatToScalar(0.001f) |
+ |
+#define DO_DEFERRED_CLEAR() \ |
+ do { \ |
+ if (fNeedClear) { \ |
+ this->clear(SK_ColorTRANSPARENT); \ |
+ } \ |
+ } while (false) \ |
+ |
+/////////////////////////////////////////////////////////////////////////////// |
+ |
+#define CHECK_FOR_ANNOTATION(paint) \ |
+ do { if (paint.getAnnotation()) { return; } } while (0) |
+ |
+/////////////////////////////////////////////////////////////////////////////// |
+ |
+ |
+class SkGpuDevice::SkAutoCachedTexture : public ::SkNoncopyable { |
+public: |
+ SkAutoCachedTexture() |
+ : fDevice(NULL) |
+ , fTexture(NULL) { |
+ } |
+ |
+ SkAutoCachedTexture(SkGpuDevice* device, |
+ const SkBitmap& bitmap, |
+ const GrTextureParams* params, |
+ GrTexture** texture) |
+ : fDevice(NULL) |
+ , fTexture(NULL) { |
+ SkASSERT(NULL != texture); |
+ *texture = this->set(device, bitmap, params); |
+ } |
+ |
+ ~SkAutoCachedTexture() { |
+ if (NULL != fTexture) { |
+ GrUnlockAndUnrefCachedBitmapTexture(fTexture); |
+ } |
+ } |
+ |
+ GrTexture* set(SkGpuDevice* device, |
+ const SkBitmap& bitmap, |
+ const GrTextureParams* params) { |
+ if (NULL != fTexture) { |
+ GrUnlockAndUnrefCachedBitmapTexture(fTexture); |
+ fTexture = NULL; |
+ } |
+ fDevice = device; |
+ GrTexture* result = (GrTexture*)bitmap.getTexture(); |
+ if (NULL == result) { |
+ // Cannot return the native texture so look it up in our cache |
+ fTexture = GrLockAndRefCachedBitmapTexture(device->context(), bitmap, params); |
+ result = fTexture; |
+ } |
+ return result; |
+ } |
+ |
+private: |
+ SkGpuDevice* fDevice; |
+ GrTexture* fTexture; |
+}; |
+ |
+/////////////////////////////////////////////////////////////////////////////// |
+ |
+struct GrSkDrawProcs : public SkDrawProcs { |
+public: |
+ GrContext* fContext; |
+ GrTextContext* fTextContext; |
+ GrFontScaler* fFontScaler; // cached in the skia glyphcache |
+}; |
+ |
+/////////////////////////////////////////////////////////////////////////////// |
+ |
+static SkBitmap::Config grConfig2skConfig(GrPixelConfig config, bool* isOpaque) { |
+ switch (config) { |
+ case kAlpha_8_GrPixelConfig: |
+ *isOpaque = false; |
+ return SkBitmap::kA8_Config; |
+ case kRGB_565_GrPixelConfig: |
+ *isOpaque = true; |
+ return SkBitmap::kRGB_565_Config; |
+ case kRGBA_4444_GrPixelConfig: |
+ *isOpaque = false; |
+ return SkBitmap::kARGB_4444_Config; |
+ case kSkia8888_GrPixelConfig: |
+ // we don't currently have a way of knowing whether |
+ // a 8888 is opaque based on the config. |
+ *isOpaque = false; |
+ return SkBitmap::kARGB_8888_Config; |
+ default: |
+ *isOpaque = false; |
+ return SkBitmap::kNo_Config; |
+ } |
+} |
+ |
+/* |
+ * GrRenderTarget does not know its opaqueness, only its config, so we have |
+ * to make conservative guesses when we return an "equivalent" bitmap. |
+ */ |
+static SkBitmap make_bitmap(GrContext* context, GrRenderTarget* renderTarget) { |
+ bool isOpaque; |
+ SkBitmap::Config config = grConfig2skConfig(renderTarget->config(), &isOpaque); |
+ |
+ SkBitmap bitmap; |
+ bitmap.setConfig(config, renderTarget->width(), renderTarget->height(), 0, |
+ isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType); |
+ return bitmap; |
+} |
+ |
+SkGpuDevice* SkGpuDevice::Create(GrSurface* surface) { |
+ SkASSERT(NULL != surface); |
+ if (NULL == surface->asRenderTarget() || NULL == surface->getContext()) { |
+ return NULL; |
+ } |
+ if (surface->asTexture()) { |
+ return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asTexture())); |
+ } else { |
+ return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asRenderTarget())); |
+ } |
+} |
+ |
+SkGpuDevice::SkGpuDevice(GrContext* context, GrTexture* texture) |
+ : SkBitmapDevice(make_bitmap(context, texture->asRenderTarget())) { |
+ this->initFromRenderTarget(context, texture->asRenderTarget(), false); |
+} |
+ |
+SkGpuDevice::SkGpuDevice(GrContext* context, GrRenderTarget* renderTarget) |
+ : SkBitmapDevice(make_bitmap(context, renderTarget)) { |
+ this->initFromRenderTarget(context, renderTarget, false); |
+} |
+ |
+void SkGpuDevice::initFromRenderTarget(GrContext* context, |
+ GrRenderTarget* renderTarget, |
+ bool cached) { |
+ fDrawProcs = NULL; |
+ |
+ fContext = context; |
+ fContext->ref(); |
+ |
+ fRenderTarget = NULL; |
+ fNeedClear = false; |
+ |
+ SkASSERT(NULL != renderTarget); |
+ fRenderTarget = renderTarget; |
+ fRenderTarget->ref(); |
+ |
+ // Hold onto to the texture in the pixel ref (if there is one) because the texture holds a ref |
+ // on the RT but not vice-versa. |
+ // TODO: Remove this trickery once we figure out how to make SkGrPixelRef do this without |
+ // busting chrome (for a currently unknown reason). |
+ GrSurface* surface = fRenderTarget->asTexture(); |
+ if (NULL == surface) { |
+ surface = fRenderTarget; |
+ } |
+ SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (surface, cached)); |
+ |
+ this->setPixelRef(pr, 0)->unref(); |
+} |
+ |
+SkGpuDevice::SkGpuDevice(GrContext* context, |
+ SkBitmap::Config config, |
+ int width, |
+ int height, |
+ int sampleCount) |
+ : SkBitmapDevice(config, width, height, false /*isOpaque*/) { |
+ |
+ fDrawProcs = NULL; |
+ |
+ fContext = context; |
+ fContext->ref(); |
+ |
+ fRenderTarget = NULL; |
+ fNeedClear = false; |
+ |
+ if (config != SkBitmap::kRGB_565_Config) { |
+ config = SkBitmap::kARGB_8888_Config; |
+ } |
+ |
+ GrTextureDesc desc; |
+ desc.fFlags = kRenderTarget_GrTextureFlagBit; |
+ desc.fWidth = width; |
+ desc.fHeight = height; |
+ desc.fConfig = SkBitmapConfig2GrPixelConfig(config); |
+ desc.fSampleCnt = sampleCount; |
+ |
+ SkAutoTUnref<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, ¶ms)) { |
+ return false; |
+ } |
+ |
+ // At this point we know we could do the draw by uploading the entire bitmap |
+ // as a texture. However, if the texture would be large compared to the |
+ // cache size and we don't require most of it for this draw then tile to |
+ // reduce the amount of upload and cache spill. |
+ |
+ // assumption here is that sw bitmap size is a good proxy for its size as |
+ // a texture |
+ size_t bmpSize = bitmap.getSize(); |
+ size_t cacheSize; |
+ fContext->getTextureCacheLimits(NULL, &cacheSize); |
+ if (bmpSize < cacheSize / 2) { |
+ return false; |
+ } |
+ |
+ // Figure out how much of the src we will need based on the src rect and clipping. |
+ determine_clipped_src_rect(fContext, bitmap, srcRectPtr, clippedSrcRect); |
+ *tileSize = kBmpSmallTileSize; // already know whole bitmap fits in one max sized tile. |
+ size_t usedTileBytes = get_tile_count(*clippedSrcRect, kBmpSmallTileSize) * |
+ kBmpSmallTileSize * kBmpSmallTileSize; |
+ |
+ return usedTileBytes < 2 * bmpSize; |
+} |
+ |
+void SkGpuDevice::drawBitmap(const SkDraw& draw, |
+ const SkBitmap& bitmap, |
+ const SkMatrix& m, |
+ const SkPaint& paint) { |
+ // We cannot call drawBitmapRect here since 'm' could be anything |
+ this->drawBitmapCommon(draw, bitmap, NULL, m, paint, |
+ SkCanvas::kNone_DrawBitmapRectFlag); |
+} |
+ |
+// This method outsets 'iRect' by 1 all around and then clamps its extents to |
+// 'clamp'. 'offset' is adjusted to remain positioned over the top-left corner |
+// of 'iRect' for all possible outsets/clamps. |
+static inline void clamped_unit_outset_with_offset(SkIRect* iRect, SkPoint* offset, |
+ const SkIRect& clamp) { |
+ iRect->outset(1, 1); |
+ |
+ if (iRect->fLeft < clamp.fLeft) { |
+ iRect->fLeft = clamp.fLeft; |
+ } else { |
+ offset->fX -= SK_Scalar1; |
+ } |
+ if (iRect->fTop < clamp.fTop) { |
+ iRect->fTop = clamp.fTop; |
+ } else { |
+ offset->fY -= SK_Scalar1; |
+ } |
+ |
+ if (iRect->fRight > clamp.fRight) { |
+ iRect->fRight = clamp.fRight; |
+ } |
+ if (iRect->fBottom > clamp.fBottom) { |
+ iRect->fBottom = clamp.fBottom; |
+ } |
+} |
+ |
+void SkGpuDevice::drawBitmapCommon(const SkDraw& draw, |
+ const SkBitmap& bitmap, |
+ const SkRect* srcRectPtr, |
+ const SkMatrix& m, |
+ const SkPaint& paint, |
+ SkCanvas::DrawBitmapRectFlags flags) { |
+ CHECK_SHOULD_DRAW(draw, false); |
+ |
+ SkRect srcRect; |
+ if (NULL == srcRectPtr) { |
+ srcRect.set(0, 0, SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height())); |
+ } else { |
+ srcRect = *srcRectPtr; |
+ } |
+ |
+ if (paint.getMaskFilter()){ |
+ // Convert the bitmap to a shader so that the rect can be drawn |
+ // through drawRect, which supports mask filters. |
+ SkMatrix newM(m); |
+ SkBitmap tmp; // subset of bitmap, if necessary |
+ const SkBitmap* bitmapPtr = &bitmap; |
+ if (NULL != srcRectPtr) { |
+ SkIRect iSrc; |
+ srcRect.roundOut(&iSrc); |
+ |
+ SkPoint offset = SkPoint::Make(SkIntToScalar(iSrc.fLeft), |
+ SkIntToScalar(iSrc.fTop)); |
+ |
+ if (SkCanvas::kBleed_DrawBitmapRectFlag & flags) { |
+ // In bleed mode we want to expand the src rect on all sides |
+ // but stay within the bitmap bounds |
+ SkIRect iClampRect = SkIRect::MakeWH(bitmap.width(), bitmap.height()); |
+ clamped_unit_outset_with_offset(&iSrc, &offset, iClampRect); |
+ } |
+ |
+ if (!bitmap.extractSubset(&tmp, iSrc)) { |
+ return; // extraction failed |
+ } |
+ bitmapPtr = &tmp; |
+ srcRect.offset(-offset.fX, -offset.fY); |
+ // The source rect has changed so update the matrix |
+ newM.preTranslate(offset.fX, offset.fY); |
+ } |
+ |
+ SkPaint paintWithTexture(paint); |
+ paintWithTexture.setShader(SkShader::CreateBitmapShader(*bitmapPtr, |
+ SkShader::kClamp_TileMode, SkShader::kClamp_TileMode))->unref(); |
+ |
+ // Transform 'newM' needs to be concatenated to the current matrix, |
+ // rather than transforming the primitive directly, so that 'newM' will |
+ // also affect the behavior of the mask filter. |
+ SkMatrix drawMatrix; |
+ drawMatrix.setConcat(fContext->getMatrix(), newM); |
+ SkDraw transformedDraw(draw); |
+ transformedDraw.fMatrix = &drawMatrix; |
+ |
+ this->drawRect(transformedDraw, srcRect, paintWithTexture); |
+ |
+ return; |
+ } |
+ |
+ fContext->concatMatrix(m); |
+ |
+ GrTextureParams params; |
+ SkPaint::FilterLevel paintFilterLevel = paint.getFilterLevel(); |
+ GrTextureParams::FilterMode textureFilterMode; |
+ switch(paintFilterLevel) { |
+ case SkPaint::kNone_FilterLevel: |
+ textureFilterMode = GrTextureParams::kNone_FilterMode; |
+ break; |
+ case SkPaint::kLow_FilterLevel: |
+ textureFilterMode = GrTextureParams::kBilerp_FilterMode; |
+ break; |
+ case SkPaint::kMedium_FilterLevel: |
+ textureFilterMode = GrTextureParams::kMipMap_FilterMode; |
+ break; |
+ case SkPaint::kHigh_FilterLevel: |
+ // Fall back to mips for now |
+ textureFilterMode = GrTextureParams::kMipMap_FilterMode; |
+ break; |
+ default: |
+ SkErrorInternals::SetError( kInvalidPaint_SkError, |
+ "Sorry, I don't understand the filtering " |
+ "mode you asked for. Falling back to " |
+ "MIPMaps."); |
+ textureFilterMode = GrTextureParams::kMipMap_FilterMode; |
+ break; |
+ |
+ } |
+ |
+ params.setFilterMode(textureFilterMode); |
+ |
+ int maxTileSize = fContext->getMaxTextureSize(); |
+ if (SkPaint::kNone_FilterLevel != paint.getFilterLevel()) { |
+ // We may need a skosh more room if we have to bump out the tile |
+ // by 1 pixel all around |
+ maxTileSize -= 2; |
+ } |
+ int tileSize; |
+ |
+ SkIRect clippedSrcRect; |
+ if (this->shouldTileBitmap(bitmap, params, srcRectPtr, maxTileSize, &tileSize, |
+ &clippedSrcRect)) { |
+ this->drawTiledBitmap(bitmap, srcRect, clippedSrcRect, params, paint, flags, tileSize); |
+ } else { |
+ // take the simple case |
+ this->internalDrawBitmap(bitmap, srcRect, params, paint, flags); |
+ } |
+} |
+ |
+// Break 'bitmap' into several tiles to draw it since it has already |
+// been determined to be too large to fit in VRAM |
+void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap, |
+ const SkRect& srcRect, |
+ const SkIRect& clippedSrcIRect, |
+ const GrTextureParams& params, |
+ const SkPaint& paint, |
+ SkCanvas::DrawBitmapRectFlags flags, |
+ int tileSize) { |
+ SkRect clippedSrcRect = SkRect::Make(clippedSrcIRect); |
+ |
+ int nx = bitmap.width() / tileSize; |
+ int ny = bitmap.height() / tileSize; |
+ for (int x = 0; x <= nx; x++) { |
+ for (int y = 0; y <= ny; y++) { |
+ SkRect tileR; |
+ tileR.set(SkIntToScalar(x * tileSize), |
+ SkIntToScalar(y * tileSize), |
+ SkIntToScalar((x + 1) * tileSize), |
+ SkIntToScalar((y + 1) * tileSize)); |
+ |
+ if (!SkRect::Intersects(tileR, clippedSrcRect)) { |
+ continue; |
+ } |
+ |
+ if (!tileR.intersect(srcRect)) { |
+ continue; |
+ } |
+ |
+ SkBitmap tmpB; |
+ SkIRect iTileR; |
+ tileR.roundOut(&iTileR); |
+ SkPoint offset = SkPoint::Make(SkIntToScalar(iTileR.fLeft), |
+ SkIntToScalar(iTileR.fTop)); |
+ |
+ if (SkPaint::kNone_FilterLevel != paint.getFilterLevel()) { |
+ SkIRect iClampRect; |
+ |
+ if (SkCanvas::kBleed_DrawBitmapRectFlag & flags) { |
+ // In bleed mode we want to always expand the tile on all edges |
+ // but stay within the bitmap bounds |
+ iClampRect = SkIRect::MakeWH(bitmap.width(), bitmap.height()); |
+ } else { |
+ // In texture-domain/clamp mode we only want to expand the |
+ // tile on edges interior to "srcRect" (i.e., we want to |
+ // not bleed across the original clamped edges) |
+ srcRect.roundOut(&iClampRect); |
+ } |
+ |
+ clamped_unit_outset_with_offset(&iTileR, &offset, iClampRect); |
+ } |
+ |
+ if (bitmap.extractSubset(&tmpB, iTileR)) { |
+ // now offset it to make it "local" to our tmp bitmap |
+ tileR.offset(-offset.fX, -offset.fY); |
+ SkMatrix tmpM; |
+ tmpM.setTranslate(offset.fX, offset.fY); |
+ GrContext::AutoMatrix am; |
+ am.setPreConcat(fContext, tmpM); |
+ this->internalDrawBitmap(tmpB, tileR, params, paint, flags); |
+ } |
+ } |
+ } |
+} |
+ |
+static bool has_aligned_samples(const SkRect& srcRect, |
+ const SkRect& transformedRect) { |
+ // detect pixel disalignment |
+ if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) - |
+ transformedRect.left()) < COLOR_BLEED_TOLERANCE && |
+ SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) - |
+ transformedRect.top()) < COLOR_BLEED_TOLERANCE && |
+ SkScalarAbs(transformedRect.width() - srcRect.width()) < |
+ COLOR_BLEED_TOLERANCE && |
+ SkScalarAbs(transformedRect.height() - srcRect.height()) < |
+ COLOR_BLEED_TOLERANCE) { |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+static bool may_color_bleed(const SkRect& srcRect, |
+ const SkRect& transformedRect, |
+ const SkMatrix& m) { |
+ // Only gets called if has_aligned_samples returned false. |
+ // So we can assume that sampling is axis aligned but not texel aligned. |
+ SkASSERT(!has_aligned_samples(srcRect, transformedRect)); |
+ SkRect innerSrcRect(srcRect), innerTransformedRect, |
+ outerTransformedRect(transformedRect); |
+ innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf); |
+ m.mapRect(&innerTransformedRect, innerSrcRect); |
+ |
+ // The gap between outerTransformedRect and innerTransformedRect |
+ // represents the projection of the source border area, which is |
+ // problematic for color bleeding. We must check whether any |
+ // destination pixels sample the border area. |
+ outerTransformedRect.inset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE); |
+ innerTransformedRect.outset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE); |
+ SkIRect outer, inner; |
+ outerTransformedRect.round(&outer); |
+ innerTransformedRect.round(&inner); |
+ // If the inner and outer rects round to the same result, it means the |
+ // border does not overlap any pixel centers. Yay! |
+ return inner != outer; |
+} |
+ |
+ |
+/* |
+ * This is called by drawBitmap(), which has to handle images that may be too |
+ * large to be represented by a single texture. |
+ * |
+ * internalDrawBitmap assumes that the specified bitmap will fit in a texture |
+ * and that non-texture portion of the GrPaint has already been setup. |
+ */ |
+void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap, |
+ const SkRect& srcRect, |
+ const GrTextureParams& params, |
+ const SkPaint& paint, |
+ SkCanvas::DrawBitmapRectFlags flags) { |
+ SkASSERT(bitmap.width() <= fContext->getMaxTextureSize() && |
+ bitmap.height() <= fContext->getMaxTextureSize()); |
+ |
+ GrTexture* texture; |
+ SkAutoCachedTexture act(this, bitmap, ¶ms, &texture); |
+ if (NULL == texture) { |
+ return; |
+ } |
+ |
+ SkRect dstRect(srcRect); |
+ SkRect paintRect; |
+ SkScalar wInv = SkScalarInvert(SkIntToScalar(texture->width())); |
+ SkScalar hInv = SkScalarInvert(SkIntToScalar(texture->height())); |
+ paintRect.setLTRB(SkScalarMul(srcRect.fLeft, wInv), |
+ SkScalarMul(srcRect.fTop, hInv), |
+ SkScalarMul(srcRect.fRight, wInv), |
+ SkScalarMul(srcRect.fBottom, hInv)); |
+ |
+ bool needsTextureDomain = false; |
+ if (!(flags & SkCanvas::kBleed_DrawBitmapRectFlag) && |
+ params.filterMode() != GrTextureParams::kNone_FilterMode) { |
+ // Need texture domain if drawing a sub rect. |
+ needsTextureDomain = srcRect.width() < bitmap.width() || |
+ srcRect.height() < bitmap.height(); |
+ if (needsTextureDomain && fContext->getMatrix().rectStaysRect()) { |
+ const SkMatrix& matrix = fContext->getMatrix(); |
+ // sampling is axis-aligned |
+ SkRect transformedRect; |
+ matrix.mapRect(&transformedRect, srcRect); |
+ |
+ if (has_aligned_samples(srcRect, transformedRect)) { |
+ // We could also turn off filtering here (but we already did a cache lookup with |
+ // params). |
+ needsTextureDomain = false; |
+ } else { |
+ needsTextureDomain = may_color_bleed(srcRect, transformedRect, matrix); |
+ } |
+ } |
+ } |
+ |
+ SkRect textureDomain = SkRect::MakeEmpty(); |
+ SkAutoTUnref<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; |
+#if SK_DISTANCEFIELD_FONTS |
+ fDrawProcs->fFlags = 0; |
+ fDrawProcs->fFlags |= SkDrawProcs::kSkipBakedGlyphTransform_Flag; |
+ fDrawProcs->fFlags |= SkDrawProcs::kUseScaledGlyphs_Flag; |
+#endif |
+ } |
+ |
+ // init our (and GL's) state |
+ fDrawProcs->fTextContext = context; |
+ fDrawProcs->fFontScaler = NULL; |
+ return fDrawProcs; |
+} |
+ |
+void SkGpuDevice::drawText(const SkDraw& draw, const void* text, |
+ size_t byteLength, SkScalar x, SkScalar y, |
+ const SkPaint& paint) { |
+ CHECK_SHOULD_DRAW(draw, false); |
+ |
+ if (fContext->getMatrix().hasPerspective()) { |
+ // this guy will just call our drawPath() |
+ draw.drawText((const char*)text, byteLength, x, y, paint); |
+ } else { |
+ SkDraw myDraw(draw); |
+ |
+ GrPaint grPaint; |
+ if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { |
+ return; |
+ } |
+#if SK_DISTANCEFIELD_FONTS |
+ GrDistanceFieldTextContext context(fContext, grPaint, paint.getColor(), |
+ paint.getTextSize()/SkDrawProcs::kBaseDFFontSize); |
+#else |
+ GrBitmapTextContext context(fContext, grPaint, paint.getColor()); |
+#endif |
+ myDraw.fProcs = this->initDrawForText(&context); |
+ this->INHERITED::drawText(myDraw, text, byteLength, x, y, paint); |
+ } |
+} |
+ |
+void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text, |
+ size_t byteLength, const SkScalar pos[], |
+ SkScalar constY, int scalarsPerPos, |
+ const SkPaint& paint) { |
+ CHECK_SHOULD_DRAW(draw, false); |
+ |
+ if (fContext->getMatrix().hasPerspective()) { |
+ // this guy will just call our drawPath() |
+ draw.drawPosText((const char*)text, byteLength, pos, constY, |
+ scalarsPerPos, paint); |
+ } else { |
+ SkDraw myDraw(draw); |
+ |
+ GrPaint grPaint; |
+ if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { |
+ return; |
+ } |
+#if SK_DISTANCEFIELD_FONTS |
+ GrDistanceFieldTextContext context(fContext, grPaint, paint.getColor(), |
+ paint.getTextSize()); |
+#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; |
+} |