Initial implementation of GPU no filter NinePatch
TBR=bsalomon@google.com
BUG=skia:
Review URL: https://codereview.chromium.org/1454933002
diff --git a/src/gpu/GrDrawContext.cpp b/src/gpu/GrDrawContext.cpp
index 2613407..6718699 100644
--- a/src/gpu/GrDrawContext.cpp
+++ b/src/gpu/GrDrawContext.cpp
@@ -23,6 +23,7 @@
#include "batches/GrDrawAtlasBatch.h"
#include "batches/GrDrawVerticesBatch.h"
#include "batches/GrRectBatchFactory.h"
+#include "batches/GrNinePatch.h" // TODO Factory
#define ASSERT_OWNED_RESOURCE(R) SkASSERT(!(R) || (R)->getContext() == fDrawingManager->getContext())
#define RETURN_IF_ABANDONED if (fDrawingManager->abandoned()) { return; }
@@ -568,6 +569,27 @@
}
}
+void GrDrawContext::drawImageNine(const GrClip& clip,
+ const GrPaint& paint,
+ const SkMatrix& viewMatrix,
+ int imageWidth,
+ int imageHeight,
+ const SkIRect& center,
+ const SkRect& dst) {
+ RETURN_IF_ABANDONED
+ SkDEBUGCODE(this->validate();)
+
+ AutoCheckFlush acf(fDrawingManager);
+
+ SkAutoTUnref<GrDrawBatch> batch(GrNinePatch::CreateNonAA(paint.getColor(), viewMatrix,
+ imageWidth, imageHeight,
+ center, dst));
+
+ GrPipelineBuilder pipelineBuilder(paint, fRenderTarget, clip);
+ this->getDrawTarget()->drawBatch(pipelineBuilder, batch);
+}
+
+
// Can 'path' be drawn as a pair of filled nested rectangles?
static bool is_nested_rects(const SkMatrix& viewMatrix,
const SkPath& path,
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 7dfeba6..ccfed2c 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -30,6 +30,7 @@
#include "SkImageFilter.h"
#include "SkLayerInfo.h"
#include "SkMaskFilter.h"
+#include "SkNinePatchIter.h"
#include "SkPathEffect.h"
#include "SkPicture.h"
#include "SkPictureData.h"
@@ -1020,7 +1021,6 @@
}
}
-
/*
* This is called by drawBitmap(), which has to handle images that may be too
* large to be represented by a single texture.
@@ -1074,7 +1074,6 @@
// Construct a GrPaint by setting the bitmap texture as the first effect and then configuring
// the rest from the SkPaint.
- GrPaint grPaint;
SkAutoTUnref<const GrFragmentProcessor> fp;
if (needsTextureDomain && (SkCanvas::kStrict_SrcRectConstraint == constraint)) {
@@ -1112,27 +1111,9 @@
fp.reset(GrSimpleTextureEffect::Create(texture, texMatrix, params));
}
- SkAutoTUnref<const GrFragmentProcessor> shaderFP;
-
- if (kAlpha_8_SkColorType == bitmap.colorType()) {
- if (const SkShader* shader = paint.getShader()) {
- shaderFP.reset(shader->asFragmentProcessor(this->context(),
- viewMatrix,
- nullptr,
- paint.getFilterQuality()));
- if (!shaderFP) {
- return;
- }
- const GrFragmentProcessor* fpSeries[] = { shaderFP.get(), fp.get() };
- fp.reset(GrFragmentProcessor::RunInSeries(fpSeries, 2));
- } else {
- fp.reset(GrFragmentProcessor::MulOutputByInputUnpremulColor(fp));
- }
- } else {
- fp.reset(GrFragmentProcessor::MulOutputByInputAlpha(fp));
- }
-
- if (!SkPaintToGrPaintReplaceShader(this->context(), paint, fp, &grPaint)) {
+ GrPaint grPaint;
+ if (!SkPaintToGrPaintWithTexture(this->context(), paint, viewMatrix, fp,
+ kAlpha_8_SkColorType == bitmap.colorType(), &grPaint)) {
return;
}
@@ -1495,6 +1476,64 @@
this->drawBitmapRect(draw, bm, src, dst, paint, constraint);
}
+void SkGpuDevice::drawImageNine(const SkDraw& draw, const SkImage* image,
+ const SkIRect& center, const SkRect& dst, const SkPaint& paint) {
+ // TODO write native implementation
+ SkBitmap bitmap;
+ if (!wrap_as_bm(this->context(), image, &bitmap)) {
+ return;
+ }
+ return this->drawBitmapNine(draw, bitmap, center, dst, paint);
+}
+
+void SkGpuDevice::drawBitmapNine(const SkDraw& draw, const SkBitmap& bitmap, const SkIRect& center,
+ const SkRect& dst, const SkPaint& paint) {
+ GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice::drawBitmapNine", fContext);
+
+ CHECK_FOR_ANNOTATION(paint);
+ CHECK_SHOULD_DRAW(draw);
+
+ bool useFallback = paint.getMaskFilter() || paint.isAntiAlias();
+ bool doBicubic;
+ GrTextureParams::FilterMode textureFilterMode =
+ GrSkFilterQualityToGrFilterMode(paint.getFilterQuality(), *draw.fMatrix, SkMatrix::I(),
+ &doBicubic);
+
+ // TODO handle bilerp
+ if (useFallback || doBicubic || GrTextureParams::kNone_FilterMode != textureFilterMode) {
+ SkNinePatchIter iter(bitmap.width(), bitmap.height(), center, dst);
+
+ SkRect srcR, dstR;
+ while (iter.next(&srcR, &dstR)) {
+ this->drawBitmapRect(draw, bitmap, &srcR, dstR, paint,
+ SkCanvas::kStrict_SrcRectConstraint);
+ }
+ return;
+ }
+
+ GrTextureParams params = GrTextureParams::ClampNoFilter();
+
+ GrTexture* texture(GrRefCachedBitmapTexture(this->context(), bitmap, params));
+ if (nullptr == texture) {
+ return;
+ }
+
+ SkMatrix texMatrix;
+ texMatrix.setIDiv(texture->width(), texture->height());
+
+ SkAutoTUnref<const GrFragmentProcessor> fp(GrSimpleTextureEffect::Create(texture, texMatrix,
+ params));
+
+ GrPaint grPaint;
+ if (!SkPaintToGrPaintWithTexture(this->context(), paint, *draw.fMatrix, fp,
+ kAlpha_8_SkColorType == bitmap.colorType(), &grPaint)) {
+ return;
+ }
+
+ fDrawContext->drawImageNine(fClip, grPaint, *draw.fMatrix, bitmap.width(), bitmap.height(),
+ center, dst);
+}
+
///////////////////////////////////////////////////////////////////////////////
// must be in SkCanvas::VertexMode order
diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h
index 0c0987a..1408a71 100644
--- a/src/gpu/SkGpuDevice.h
+++ b/src/gpu/SkGpuDevice.h
@@ -119,6 +119,11 @@
void drawImageRect(const SkDraw&, const SkImage*, const SkRect* src, const SkRect& dst,
const SkPaint&, SkCanvas::SrcRectConstraint) override;
+ void drawImageNine(const SkDraw& draw, const SkImage* image, const SkIRect& center,
+ const SkRect& dst, const SkPaint& paint) override;
+ void drawBitmapNine(const SkDraw& draw, const SkBitmap& bitmap, const SkIRect& center,
+ const SkRect& dst, const SkPaint& paint) override;
+
void flush() override;
void onAttachToCanvas(SkCanvas* canvas) override;
diff --git a/src/gpu/SkGpuDevice_drawTexture.cpp b/src/gpu/SkGpuDevice_drawTexture.cpp
index 5344145..6a43d37 100644
--- a/src/gpu/SkGpuDevice_drawTexture.cpp
+++ b/src/gpu/SkGpuDevice_drawTexture.cpp
@@ -23,35 +23,6 @@
return textureIsAlphaOnly && paint.getShader();
}
-/** Determines how to combine the texture FP with the paint's color and SkShader, if any. */
-static const GrFragmentProcessor* mix_texture_fp_with_paint_color_and_shader(
- const GrFragmentProcessor* textureFP,
- bool textureIsAlphaOnly,
- GrContext* context,
- const SkMatrix& viewMatrix,
- const SkPaint& paint) {
- // According to the SkCanvas API, we only consider the shader if the bitmap or image being
- // rendered is alpha-only.
- if (textureIsAlphaOnly) {
- if (const SkShader* shader = paint.getShader()) {
- SkAutoTUnref<const GrFragmentProcessor> shaderFP(
- shader->asFragmentProcessor(context,
- viewMatrix,
- nullptr,
- paint.getFilterQuality()));
- if (!shaderFP) {
- return nullptr;
- }
- const GrFragmentProcessor* fpSeries[] = { shaderFP, textureFP };
- return GrFragmentProcessor::RunInSeries(fpSeries, 2);
- } else {
- return GrFragmentProcessor::MulOutputByInputUnpremulColor(textureFP);
- }
- } else {
- return GrFragmentProcessor::MulOutputByInputAlpha(textureFP);
- }
-}
-
//////////////////////////////////////////////////////////////////////////////
// Helper functions for dropping src rect constraint in bilerp mode.
@@ -231,10 +202,9 @@
if (!fp) {
return;
}
- fp.reset(mix_texture_fp_with_paint_color_and_shader(fp, alphaTexture, this->context(),
- viewMatrix, paint));
+
GrPaint grPaint;
- if (!SkPaintToGrPaintReplaceShader(fContext, paint, fp, &grPaint)) {
+ if (!SkPaintToGrPaintWithTexture(fContext, paint, viewMatrix, fp, alphaTexture, &grPaint)) {
return;
}
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index affd6cd..411b5b2 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -545,6 +545,34 @@
grPaint);
}
+bool SkPaintToGrPaintWithTexture(GrContext* context,
+ const SkPaint& paint,
+ const SkMatrix& viewM,
+ const GrFragmentProcessor* fp,
+ bool textureIsAlphaOnly,
+ GrPaint* grPaint) {
+ SkAutoTUnref<const GrFragmentProcessor> shaderFP;
+ if (textureIsAlphaOnly) {
+ if (const SkShader* shader = paint.getShader()) {
+ shaderFP.reset(shader->asFragmentProcessor(context,
+ viewM,
+ nullptr,
+ paint.getFilterQuality()));
+ if (!shaderFP) {
+ return false;
+ }
+ const GrFragmentProcessor* fpSeries[] = { shaderFP.get(), fp };
+ shaderFP.reset(GrFragmentProcessor::RunInSeries(fpSeries, 2));
+ } else {
+ shaderFP.reset(GrFragmentProcessor::MulOutputByInputUnpremulColor(fp));
+ }
+ } else {
+ shaderFP.reset(GrFragmentProcessor::MulOutputByInputAlpha(fp));
+ }
+
+ return SkPaintToGrPaintReplaceShader(context, paint, shaderFP.get(), grPaint);
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/SkGrPriv.h b/src/gpu/SkGrPriv.h
index 5924ea6..77a464c 100644
--- a/src/gpu/SkGrPriv.h
+++ b/src/gpu/SkGrPriv.h
@@ -84,6 +84,15 @@
false, grPaint);
}
+/** This is used when there may or may not be a shader, and the caller wants to plugin a texture
+ lookup. If there is a shader, then its output will only be used if the texture is alpha8. */
+bool SkPaintToGrPaintWithTexture(GrContext* context,
+ const SkPaint& paint,
+ const SkMatrix& viewM,
+ const GrFragmentProcessor* fp,
+ bool textureIsAlphaOnly,
+ GrPaint* grPaint);
+
//////////////////////////////////////////////////////////////////////////////
GrSurfaceDesc GrImageInfoToSurfaceDesc(const SkImageInfo&);
diff --git a/src/gpu/batches/GrNinePatch.cpp b/src/gpu/batches/GrNinePatch.cpp
new file mode 100644
index 0000000..4c6310c
--- /dev/null
+++ b/src/gpu/batches/GrNinePatch.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrNinePatch.h"
+
+#include "GrBatchFlushState.h"
+#include "GrDefaultGeoProcFactory.h"
+#include "GrResourceProvider.h"
+#include "GrVertexBatch.h"
+#include "SkBitmap.h"
+#include "SkNinePatchIter.h"
+#include "SkRect.h"
+
+static const GrGeometryProcessor* create_gp(bool readsCoverage) {
+ using namespace GrDefaultGeoProcFactory;
+ Color color(Color::kAttribute_Type);
+ Coverage coverage(readsCoverage ? Coverage::kSolid_Type : Coverage::kNone_Type);
+ LocalCoords localCoords(LocalCoords::kHasExplicit_Type);
+ return GrDefaultGeoProcFactory::Create(color, coverage, localCoords, SkMatrix::I());
+}
+
+class GrNonAANinePatchBatch : public GrVertexBatch {
+public:
+ DEFINE_BATCH_CLASS_ID
+
+ static const int kVertsPerRect = 4;
+ static const int kIndicesPerRect = 6;
+ static const int kRectsPerInstance = 9; // We could skip empty rects
+
+ struct Geometry {
+ SkMatrix fViewMatrix;
+ SkIRect fCenter;
+ SkRect fDst;
+ GrColor fColor;
+ };
+
+ GrNonAANinePatchBatch(GrColor color, const SkMatrix& viewMatrix, int imageWidth,
+ int imageHeight, const SkIRect& center, const SkRect &dst)
+ : INHERITED(ClassID()) {
+ Geometry& geo = fGeoData.push_back();
+ geo.fViewMatrix = viewMatrix;
+ geo.fColor = color;
+ geo.fCenter = center;
+ geo.fDst = dst;
+
+ fImageWidth = imageWidth;
+ fImageHeight = imageHeight;
+
+ // setup bounds
+ geo.fViewMatrix.mapRect(&fBounds, geo.fDst);
+ }
+
+ const char* name() const override { return "GrNonAANinePatchBatch"; }
+
+ void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
+ out->setUnknownFourComponents();
+ }
+
+ void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
+ out->setKnownSingleComponent(0xff);
+ }
+
+ SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
+
+private:
+ void onPrepareDraws(Target* target) override {
+ SkAutoTUnref<const GrGeometryProcessor> gp(create_gp(fOpts.readsCoverage()));
+ if (!gp) {
+ SkDebugf("Couldn't create GrGeometryProcessor\n");
+ return;
+ }
+
+ target->initDraw(gp, this->pipeline());
+
+ size_t vertexStride = gp->getVertexStride();
+ int instanceCount = fGeoData.count();
+
+ SkAutoTUnref<const GrIndexBuffer> indexBuffer(
+ target->resourceProvider()->refQuadIndexBuffer());
+ InstancedHelper helper;
+ void* vertices = helper.init(target, kTriangles_GrPrimitiveType, vertexStride,
+ indexBuffer, kVertsPerRect,
+ kIndicesPerRect, instanceCount * kRectsPerInstance);
+ if (!vertices || !indexBuffer) {
+ SkDebugf("Could not allocate vertices\n");
+ return;
+ }
+
+ for (int i = 0; i < instanceCount; i++) {
+ intptr_t verts = reinterpret_cast<intptr_t>(vertices) +
+ i * kRectsPerInstance * kVertsPerRect * vertexStride;
+
+ Geometry& geo = fGeoData[i];
+ SkNinePatchIter iter(fImageWidth, fImageHeight, geo.fCenter, geo.fDst);
+
+ SkRect srcR, dstR;
+ while (iter.next(&srcR, &dstR)) {
+ SkPoint* positions = reinterpret_cast<SkPoint*>(verts);
+
+ positions->setRectFan(dstR.fLeft, dstR.fTop,
+ dstR.fRight, dstR.fBottom, vertexStride);
+
+ SkASSERT(!geo.fViewMatrix.hasPerspective());
+ geo.fViewMatrix.mapPointsWithStride(positions, vertexStride, kVertsPerRect);
+
+ // Setup local coords
+ static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor);
+ SkPoint* coords = reinterpret_cast<SkPoint*>(verts + kLocalOffset);
+ coords->setRectFan(srcR.fLeft, srcR.fTop, srcR.fRight, srcR.fBottom, vertexStride);
+
+ static const int kColorOffset = sizeof(SkPoint);
+ GrColor* vertColor = reinterpret_cast<GrColor*>(verts + kColorOffset);
+ for (int j = 0; j < 4; ++j) {
+ *vertColor = geo.fColor;
+ vertColor = (GrColor*) ((intptr_t) vertColor + vertexStride);
+ }
+ verts += kVertsPerRect * vertexStride;
+ }
+ }
+ helper.recordDraw(target);
+ }
+
+ void initBatchTracker(const GrPipelineOptimizations& opt) override {
+ opt.getOverrideColorIfSet(&fGeoData[0].fColor);
+ fOpts = opt;
+ }
+
+ bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
+ GrNonAANinePatchBatch* that = t->cast<GrNonAANinePatchBatch>();
+ if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
+ that->bounds(), caps)) {
+ return false;
+ }
+
+ SkASSERT(this->fImageWidth == that->fImageWidth &&
+ this->fImageHeight == that->fImageHeight);
+
+ // In the event of two batches, one who can tweak, one who cannot, we just fall back to
+ // not tweaking
+ if (fOpts.canTweakAlphaForCoverage() && !that->fOpts.canTweakAlphaForCoverage()) {
+ fOpts = that->fOpts;
+ }
+
+ fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
+ this->joinBounds(that->bounds());
+ return true;
+ }
+
+ GrPipelineOptimizations fOpts;
+ int fImageWidth;
+ int fImageHeight;
+ SkSTArray<1, Geometry, true> fGeoData;
+
+ typedef GrVertexBatch INHERITED;
+};
+
+namespace GrNinePatch {
+GrDrawBatch* CreateNonAA(GrColor color, const SkMatrix& viewMatrix, int imageWidth, int imageHeight,
+ const SkIRect& center, const SkRect& dst) {
+ return new GrNonAANinePatchBatch(color, viewMatrix, imageWidth, imageHeight, center, dst);
+}
+};
diff --git a/src/gpu/batches/GrNinePatch.h b/src/gpu/batches/GrNinePatch.h
new file mode 100644
index 0000000..0a4ffd6
--- /dev/null
+++ b/src/gpu/batches/GrNinePatch.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrNinePatch_DEFINED
+#define GrNinePatch_DEFINED
+
+#include "GrColor.h"
+
+class GrDrawBatch;
+class SkBitmap;
+class SkMatrix;
+struct SkIRect;
+struct SkRect;
+
+namespace GrNinePatch {
+GrDrawBatch* CreateNonAA(GrColor color, const SkMatrix& viewMatrix, int imageWidth, int imageHeight,
+ const SkIRect& center, const SkRect& dst);
+};
+
+#endif