| Index: src/gpu/SkGpuDevice.cpp
|
| ===================================================================
|
| --- src/gpu/SkGpuDevice.cpp (revision 9883)
|
| +++ src/gpu/SkGpuDevice.cpp (working copy)
|
| @@ -47,18 +47,6 @@
|
| kXfermodeEffectIdx = 2,
|
| };
|
|
|
| -#define MAX_BLUR_SIGMA 4.0f
|
| -// FIXME: This value comes from from SkBlurMaskFilter.cpp.
|
| -// Should probably be put in a common header someplace.
|
| -#define MAX_BLUR_RADIUS SkIntToScalar(128)
|
| -// This constant approximates the scaling done in the software path's
|
| -// "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)).
|
| -// IMHO, it actually should be 1: we blur "less" than we should do
|
| -// according to the CSS and canvas specs, simply because Safari does the same.
|
| -// Firefox used to do the same too, until 4.0 where they fixed it. So at some
|
| -// point we should probably get rid of these scaling constants and rebaseline
|
| -// all the blur tests.
|
| -#define BLUR_SIGMA_SCALE 0.6f
|
| // 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.
|
| @@ -776,139 +764,12 @@
|
| // helpers for applying mask filters
|
| namespace {
|
|
|
| -// We prefer to blur small rect with small radius via CPU.
|
| -#define MIN_GPU_BLUR_SIZE SkIntToScalar(64)
|
| -#define MIN_GPU_BLUR_RADIUS SkIntToScalar(32)
|
| -inline bool shouldDrawBlurWithCPU(const SkRect& rect, SkScalar radius) {
|
| - if (rect.width() <= MIN_GPU_BLUR_SIZE &&
|
| - rect.height() <= MIN_GPU_BLUR_SIZE &&
|
| - radius <= MIN_GPU_BLUR_RADIUS) {
|
| - return true;
|
| - }
|
| - return false;
|
| -}
|
| +// 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) {
|
|
|
| -bool drawWithGPUMaskFilter(GrContext* context, const SkPath& devPath, const SkStrokeRec& stroke,
|
| - SkMaskFilter* filter, const SkRegion& clip,
|
| - SkBounder* bounder, GrPaint* grp) {
|
| - SkMaskFilter::BlurInfo info;
|
| - SkMaskFilter::BlurType blurType = filter->asABlur(&info);
|
| - if (SkMaskFilter::kNone_BlurType == blurType) {
|
| - return false;
|
| - }
|
| - SkScalar radius = info.fIgnoreTransform ? info.fRadius
|
| - : context->getMatrix().mapRadius(info.fRadius);
|
| - radius = SkMinScalar(radius, MAX_BLUR_RADIUS);
|
| - if (radius <= 0) {
|
| - return false;
|
| - }
|
| -
|
| - SkRect srcRect = devPath.getBounds();
|
| - if (shouldDrawBlurWithCPU(srcRect, radius)) {
|
| - return false;
|
| - }
|
| -
|
| - float sigma = SkScalarToFloat(radius) * BLUR_SIGMA_SCALE;
|
| - float sigma3 = sigma * 3.0f;
|
| -
|
| - SkRect clipRect;
|
| - clipRect.set(clip.getBounds());
|
| -
|
| - // Outset srcRect and clipRect by 3 * sigma, to compute affected blur area.
|
| - srcRect.inset(SkFloatToScalar(-sigma3), SkFloatToScalar(-sigma3));
|
| - clipRect.inset(SkFloatToScalar(-sigma3), SkFloatToScalar(-sigma3));
|
| - srcRect.intersect(clipRect);
|
| - SkRect finalRect = srcRect;
|
| - SkIRect finalIRect;
|
| - finalRect.roundOut(&finalIRect);
|
| - if (clip.quickReject(finalIRect)) {
|
| - return true;
|
| - }
|
| - if (bounder && !bounder->doIRect(finalIRect)) {
|
| - return true;
|
| - }
|
| - GrPoint offset = GrPoint::Make(-srcRect.fLeft, -srcRect.fTop);
|
| - srcRect.offset(offset);
|
| - GrTextureDesc desc;
|
| - desc.fFlags = kRenderTarget_GrTextureFlagBit;
|
| - desc.fWidth = SkScalarCeilToInt(srcRect.width());
|
| - desc.fHeight = SkScalarCeilToInt(srcRect.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)) {
|
| - desc.fConfig = kAlpha_8_GrPixelConfig;
|
| - }
|
| -
|
| - GrAutoScratchTexture pathEntry(context, desc);
|
| - GrTexture* pathTexture = pathEntry.texture();
|
| - if (NULL == pathTexture) {
|
| - return false;
|
| - }
|
| -
|
| - SkAutoTUnref<GrTexture> blurTexture;
|
| -
|
| - {
|
| - GrContext::AutoRenderTarget art(context, pathTexture->asRenderTarget());
|
| - GrContext::AutoClip ac(context, srcRect);
|
| -
|
| - context->clear(NULL, 0);
|
| -
|
| - GrPaint tempPaint;
|
| - if (grp->isAntiAlias()) {
|
| - 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.f
|
| - tempPaint.setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff);
|
| - }
|
| -
|
| - GrContext::AutoMatrix am;
|
| -
|
| - // Draw hard shadow to pathTexture with path top-left at origin using tempPaint.
|
| - SkMatrix translate;
|
| - translate.setTranslate(offset.fX, offset.fY);
|
| - am.set(context, translate);
|
| - context->drawPath(tempPaint, devPath, stroke);
|
| -
|
| - // If we're doing a normal blur, we can clobber the pathTexture in the
|
| - // gaussianBlur. Otherwise, we need to save it for later compositing.
|
| - bool isNormalBlur = blurType == SkMaskFilter::kNormal_BlurType;
|
| - blurTexture.reset(context->gaussianBlur(pathTexture, isNormalBlur,
|
| - srcRect, sigma, sigma));
|
| - if (NULL == blurTexture) {
|
| - return false;
|
| - }
|
| -
|
| - if (!isNormalBlur) {
|
| - context->setIdentityMatrix();
|
| - GrPaint paint;
|
| - SkMatrix matrix;
|
| - matrix.setIDiv(pathTexture->width(), pathTexture->height());
|
| - // Blend pathTexture over blurTexture.
|
| - context->setRenderTarget(blurTexture->asRenderTarget());
|
| - paint.colorStage(0)->setEffect(
|
| - GrSimpleTextureEffect::Create(pathTexture, matrix))->unref();
|
| - if (SkMaskFilter::kInner_BlurType == blurType) {
|
| - // inner: dst = dst * src
|
| - paint.setBlendFunc(kDC_GrBlendCoeff, kZero_GrBlendCoeff);
|
| - } else if (SkMaskFilter::kSolid_BlurType == blurType) {
|
| - // solid: dst = src + dst - src * dst
|
| - // = (1 - dst) * src + 1 * dst
|
| - paint.setBlendFunc(kIDC_GrBlendCoeff, kOne_GrBlendCoeff);
|
| - } else if (SkMaskFilter::kOuter_BlurType == blurType) {
|
| - // outer: dst = dst * (1 - src)
|
| - // = 0 * src + (1 - src) * dst
|
| - paint.setBlendFunc(kZero_GrBlendCoeff, kISC_GrBlendCoeff);
|
| - }
|
| - context->drawRect(paint, srcRect);
|
| - }
|
| - }
|
| -
|
| GrContext::AutoMatrix am;
|
| if (!am.setIdentity(context, grp)) {
|
| return false;
|
| @@ -919,19 +780,18 @@
|
| GrAssert(!grp->isCoverageStageEnabled(MASK_IDX));
|
|
|
| SkMatrix matrix;
|
| - matrix.setTranslate(-finalRect.fLeft, -finalRect.fTop);
|
| - matrix.postIDiv(blurTexture->width(), blurTexture->height());
|
| + matrix.setTranslate(-maskRect.fLeft, -maskRect.fTop);
|
| + matrix.postIDiv(mask->width(), mask->height());
|
|
|
| grp->coverageStage(MASK_IDX)->reset();
|
| - grp->coverageStage(MASK_IDX)->setEffect(
|
| - GrSimpleTextureEffect::Create(blurTexture, matrix))->unref();
|
| - context->drawRect(*grp, finalRect);
|
| + grp->coverageStage(MASK_IDX)->setEffect(GrSimpleTextureEffect::Create(mask, matrix))->unref();
|
| + context->drawRect(*grp, maskRect);
|
| return true;
|
| }
|
|
|
| -bool drawWithMaskFilter(GrContext* context, const SkPath& devPath,
|
| - SkMaskFilter* filter, const SkRegion& clip, SkBounder* bounder,
|
| - GrPaint* grp, SkPaint::Style style) {
|
| +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,
|
| @@ -955,9 +815,6 @@
|
|
|
| // we now have a device-aligned 8bit mask in dstM, ready to be drawn using
|
| // the current clip (and identity matrix) and GrPaint settings
|
| - GrContext::AutoMatrix am;
|
| - am.setIdentity(context, grp);
|
| -
|
| GrTextureDesc desc;
|
| desc.fWidth = dstM.fBounds.width();
|
| desc.fHeight = dstM.fBounds.height();
|
| @@ -972,28 +829,75 @@
|
| texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
|
| dstM.fImage, dstM.fRowBytes);
|
|
|
| - static const int MASK_IDX = GrPaint::kMaxCoverageStages - 1;
|
| - // we assume the last mask index is available for use
|
| - GrAssert(!grp->isCoverageStageEnabled(MASK_IDX));
|
| + SkRect maskRect = SkRect::MakeFromIRect(dstM.fBounds);
|
|
|
| - SkMatrix m;
|
| - m.setTranslate(-dstM.fBounds.fLeft*SK_Scalar1, -dstM.fBounds.fTop*SK_Scalar1);
|
| - m.postIDiv(texture->width(), texture->height());
|
| + return draw_mask(context, maskRect, grp, texture);
|
| +}
|
|
|
| - grp->coverageStage(MASK_IDX)->setEffect(GrSimpleTextureEffect::Create(texture, m))->unref();
|
| - GrRect d;
|
| - d.setLTRB(SkIntToScalar(dstM.fBounds.fLeft),
|
| - SkIntToScalar(dstM.fBounds.fTop),
|
| - SkIntToScalar(dstM.fBounds.fRight),
|
| - SkIntToScalar(dstM.fBounds.fBottom));
|
| +// 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)) {
|
| + desc.fConfig = kAlpha_8_GrPixelConfig;
|
| + }
|
|
|
| - context->drawRect(*grp, d);
|
| + 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);
|
| +
|
| + 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,
|
| @@ -1051,6 +955,7 @@
|
| if (!stroke.isHairlineStyle()) {
|
| if (stroke.applyToPath(&tmpPath, *pathPtr)) {
|
| pathPtr = &tmpPath;
|
| + pathIsMutable = true;
|
| stroke.setFillStyle();
|
| }
|
| }
|
| @@ -1060,13 +965,46 @@
|
|
|
| // transform the path into device space
|
| pathPtr->transform(fContext->getMatrix(), devPathPtr);
|
| - if (!drawWithGPUMaskFilter(fContext, *devPathPtr, stroke, paint.getMaskFilter(),
|
| - *draw.fClip, draw.fBounder, &grPaint)) {
|
| - SkPaint::Style style = stroke.isHairlineStyle() ? SkPaint::kStroke_Style :
|
| - SkPaint::kFill_Style;
|
| - drawWithMaskFilter(fContext, *devPathPtr, paint.getMaskFilter(),
|
| - *draw.fClip, draw.fBounder, &grPaint, style);
|
| +
|
| + 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)) {
|
| + SkAutoTUnref<GrTexture> atu(filtered);
|
| +
|
| + 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;
|
| }
|
|
|
| @@ -1437,15 +1375,6 @@
|
| fContext->drawRectToRect(*grPaint, dstRect, paintRect, &m);
|
| }
|
|
|
| -static 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;
|
| -}
|
| -
|
| static bool filter_texture(SkDevice* device, GrContext* context,
|
| GrTexture* texture, SkImageFilter* filter,
|
| int w, int h, SkBitmap* result) {
|
| @@ -1608,8 +1537,6 @@
|
| return false;
|
| }
|
|
|
| - GrPaint paint;
|
| -
|
| GrTexture* texture;
|
| // We assume here that the filter will not attempt to tile the src. Otherwise, this cache lookup
|
| // must be pushed upstack.
|
|
|