aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--expectations/gm/ignored-tests.txt4
-rw-r--r--gm/pictureshader.cpp165
-rw-r--r--gyp/core.gypi2
-rw-r--r--gyp/gmslides.gypi1
-rw-r--r--include/core/SkShader.h11
-rw-r--r--src/core/SkPictureShader.cpp185
-rw-r--r--src/core/SkPictureShader.h60
-rw-r--r--src/core/SkShader.cpp6
-rw-r--r--src/ports/SkGlobalInitialization_chromium.cpp2
-rw-r--r--src/ports/SkGlobalInitialization_default.cpp2
10 files changed, 434 insertions, 4 deletions
diff --git a/expectations/gm/ignored-tests.txt b/expectations/gm/ignored-tests.txt
index c83f6ea08f..2451f43be1 100644
--- a/expectations/gm/ignored-tests.txt
+++ b/expectations/gm/ignored-tests.txt
@@ -56,10 +56,6 @@ filltypespersp
# I will likely just delete the GM.
canvas-layer-state
-# fmalita: https://codereview.chromium.org/221923007/
-# Suppress pictureshader GM failures, pending baselines.
-pictureshader
-
# yunchao: https://codereview.chromium.org/204143004/
# This change add some cases to bitmapshader to avoid potential drawing error for kA8_Config
bitmapshaders
diff --git a/gm/pictureshader.cpp b/gm/pictureshader.cpp
new file mode 100644
index 0000000000..5d37c907ac
--- /dev/null
+++ b/gm/pictureshader.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+
+#include "SkBitmap.h"
+#include "SkPaint.h"
+#include "SkPicture.h"
+#include "SkShader.h"
+
+namespace skiagm {
+
+static struct {
+ SkShader::TileMode tmx;
+ SkShader::TileMode tmy;
+} kTileConfigs[] = {
+ { SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode },
+ { SkShader::kRepeat_TileMode, SkShader::kClamp_TileMode },
+ { SkShader::kMirror_TileMode, SkShader::kRepeat_TileMode },
+};
+
+class PictureShaderGM : public GM {
+public:
+
+ PictureShaderGM(SkScalar tileSize, SkScalar sceneSize)
+ : fTileSize(tileSize)
+ , fSceneSize(sceneSize) {
+
+ // Build the picture.
+ SkAutoTUnref<SkPicture> p(SkNEW(SkPicture));
+ SkCanvas* pictureCanvas = p->beginRecording(SkScalarRoundToInt(tileSize),
+ SkScalarRoundToInt(tileSize));
+ this->drawTile(pictureCanvas);
+ p->endRecording();
+
+ // Build a reference bitmap.
+ SkBitmap bm;
+ bm.allocN32Pixels(SkScalarRoundToInt(tileSize), SkScalarRoundToInt(tileSize));
+ bm.eraseColor(SK_ColorTRANSPARENT);
+ SkCanvas bitmapCanvas(bm);
+ this->drawTile(&bitmapCanvas);
+
+ for (unsigned i = 0; i < SK_ARRAY_COUNT(kTileConfigs); ++i) {
+ fPictureShaders[i].reset(SkShader::CreatePictureShader(p,
+ kTileConfigs[i].tmx,
+ kTileConfigs[i].tmy));
+
+ fBitmapShaders[i].reset(SkShader::CreateBitmapShader(bm,
+ kTileConfigs[i].tmx,
+ kTileConfigs[i].tmy));
+ }
+ }
+
+protected:
+ virtual SkString onShortName() SK_OVERRIDE {
+ return SkString("pictureshader");
+ }
+
+ virtual SkISize onISize() SK_OVERRIDE {
+ return SkISize::Make(1400, 1250);
+ }
+
+ virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+ this->drawSceneColumn(canvas, SkPoint::Make(0, 0), 1, 1, 0);
+ this->drawSceneColumn(canvas, SkPoint::Make(0, fSceneSize * 6.4f), 1, 2, 0);
+ this->drawSceneColumn(canvas, SkPoint::Make(fSceneSize * 2.4f, 0), 1, 1, 1);
+ this->drawSceneColumn(canvas, SkPoint::Make(fSceneSize * 2.4f, fSceneSize * 6.4f), 1, 1, 2);
+ this->drawSceneColumn(canvas, SkPoint::Make(fSceneSize * 4.8f, 0), 2, 1, 0);
+ this->drawSceneColumn(canvas, SkPoint::Make(fSceneSize * 9.6f, 0), 2, 2, 0);
+ }
+
+private:
+ void drawSceneColumn(SkCanvas* canvas, const SkPoint& pos, SkScalar scale, SkScalar localScale,
+ unsigned tileMode) {
+ SkMatrix ctm, localMatrix;
+
+ ctm.setTranslate(pos.x(), pos.y());
+ ctm.preScale(scale, scale);
+ localMatrix.setScale(localScale, localScale);
+ this->drawScene(canvas, ctm, localMatrix, tileMode);
+
+ ctm.setTranslate(pos.x(), pos.y() + fSceneSize * 1.2f * scale);
+ ctm.preScale(scale, scale);
+ localMatrix.setTranslate(fTileSize / 4, fTileSize / 4);
+ localMatrix.preScale(localScale, localScale);
+ this->drawScene(canvas, ctm, localMatrix, tileMode);
+
+ ctm.setTranslate(pos.x(), pos.y() + fSceneSize * 2.4f * scale);
+ ctm.preScale(scale, scale);
+ localMatrix.setRotate(45);
+ localMatrix.preScale(localScale, localScale);
+ this->drawScene(canvas, ctm, localMatrix, tileMode);
+
+ ctm.setTranslate(pos.x(), pos.y() + fSceneSize * 3.6f * scale);
+ ctm.preScale(scale, scale);
+ localMatrix.setSkew(1, 0);
+ localMatrix.preScale(localScale, localScale);
+ this->drawScene(canvas, ctm, localMatrix, tileMode);
+
+ ctm.setTranslate(pos.x(), pos.y() + fSceneSize * 4.8f * scale);
+ ctm.preScale(scale, scale);
+ localMatrix.setTranslate(fTileSize / 4, fTileSize / 4);
+ localMatrix.preRotate(45);
+ localMatrix.preScale(localScale, localScale);
+ this->drawScene(canvas, ctm, localMatrix, tileMode);
+ }
+
+ void drawTile(SkCanvas* canvas) {
+ SkPaint paint;
+ paint.setColor(SK_ColorGREEN);
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setAntiAlias(true);
+
+ canvas->drawCircle(fTileSize / 4, fTileSize / 4, fTileSize / 4, paint);
+ canvas->drawRect(SkRect::MakeXYWH(fTileSize / 2, fTileSize / 2,
+ fTileSize / 2, fTileSize / 2), paint);
+
+ paint.setColor(SK_ColorRED);
+ canvas->drawLine(fTileSize / 2, fTileSize * 1 / 3,
+ fTileSize / 2, fTileSize * 2 / 3, paint);
+ canvas->drawLine(fTileSize * 1 / 3, fTileSize / 2,
+ fTileSize * 2 / 3, fTileSize / 2, paint);
+ }
+
+ void drawScene(SkCanvas* canvas, const SkMatrix& matrix, const SkMatrix& localMatrix,
+ unsigned tileMode) {
+ SkASSERT(tileMode < SK_ARRAY_COUNT(kTileConfigs));
+
+ SkPaint paint;
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(SK_ColorLTGRAY);
+
+ canvas->save();
+ canvas->concat(matrix);
+ canvas->drawRect(SkRect::MakeWH(fSceneSize, fSceneSize), paint);
+ canvas->drawRect(SkRect::MakeXYWH(fSceneSize * 1.1f, 0, fSceneSize, fSceneSize), paint);
+
+ fPictureShaders[tileMode]->setLocalMatrix(localMatrix);
+ paint.setShader(fPictureShaders[tileMode].get());
+ canvas->drawRect(SkRect::MakeWH(fSceneSize, fSceneSize), paint);
+
+ canvas->translate(fSceneSize * 1.1f, 0);
+
+ fBitmapShaders[tileMode]->setLocalMatrix(localMatrix);
+ paint.setShader(fBitmapShaders[tileMode].get());
+ canvas->drawRect(SkRect::MakeWH(fSceneSize, fSceneSize), paint);
+
+ canvas->restore();
+ }
+
+ SkScalar fTileSize;
+ SkScalar fSceneSize;
+
+ SkAutoTUnref<SkShader> fPictureShaders[SK_ARRAY_COUNT(kTileConfigs)];
+ SkAutoTUnref<SkShader> fBitmapShaders[SK_ARRAY_COUNT(kTileConfigs)];
+
+ typedef GM INHERITED;
+};
+
+DEF_GM( return SkNEW_ARGS(PictureShaderGM, (50, 100)); )
+}
diff --git a/gyp/core.gypi b/gyp/core.gypi
index df026a4957..4a923d7a84 100644
--- a/gyp/core.gypi
+++ b/gyp/core.gypi
@@ -134,6 +134,8 @@
'<(skia_src_path)/core/SkPicturePlayback.h',
'<(skia_src_path)/core/SkPictureRecord.cpp',
'<(skia_src_path)/core/SkPictureRecord.h',
+ '<(skia_src_path)/core/SkPictureShader.cpp',
+ '<(skia_src_path)/core/SkPictureShader.h',
'<(skia_src_path)/core/SkPictureStateTree.cpp',
'<(skia_src_path)/core/SkPictureStateTree.h',
'<(skia_src_path)/core/SkPixelRef.cpp',
diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi
index 6db871d131..e381743b6a 100644
--- a/gyp/gmslides.gypi
+++ b/gyp/gmslides.gypi
@@ -127,6 +127,7 @@
'../gm/peekpixels.cpp',
'../gm/perlinnoise.cpp',
'../gm/pictureimagefilter.cpp',
+ '../gm/pictureshader.cpp',
'../gm/points.cpp',
'../gm/poly2poly.cpp',
'../gm/polygons.cpp',
diff --git a/include/core/SkShader.h b/include/core/SkShader.h
index acff959949..076ecf5460 100644
--- a/include/core/SkShader.h
+++ b/include/core/SkShader.h
@@ -16,6 +16,7 @@
#include "SkPaint.h"
class SkPath;
+class SkPicture;
class GrContext;
class GrEffectRef;
@@ -345,6 +346,16 @@ public:
static SkShader* CreateBitmapShader(const SkBitmap& src,
TileMode tmx, TileMode tmy);
+ /** Call this to create a new shader that will draw with the specified picture.
+ *
+ * @param src The picture to use inside the shader (if not NULL, its ref count
+ * is incremented).
+ * @param tmx The tiling mode to use when sampling the bitmap in the x-direction.
+ * @param tmy The tiling mode to use when sampling the bitmap in the y-direction.
+ * @return Returns a new shader object. Note: this function never returns null.
+ */
+ static SkShader* CreatePictureShader(SkPicture* src, TileMode tmx, TileMode tmy);
+
SK_TO_STRING_VIRT()
SK_DEFINE_FLATTENABLE_TYPE(SkShader)
diff --git a/src/core/SkPictureShader.cpp b/src/core/SkPictureShader.cpp
new file mode 100644
index 0000000000..15df3a37a5
--- /dev/null
+++ b/src/core/SkPictureShader.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkPictureShader.h"
+
+#include "SkBitmap.h"
+#include "SkBitmapProcShader.h"
+#include "SkCanvas.h"
+#include "SkMatrixUtils.h"
+#include "SkPicture.h"
+#include "SkReadBuffer.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#endif
+
+SkPictureShader::SkPictureShader(SkPicture* picture, TileMode tmx, TileMode tmy)
+ : fPicture(picture)
+ , fTmx(tmx)
+ , fTmy(tmy) {
+ SkSafeRef(fPicture);
+}
+
+SkPictureShader::SkPictureShader(SkReadBuffer& buffer)
+ : INHERITED(buffer) {
+ fTmx = static_cast<SkShader::TileMode>(buffer.read32());
+ fTmy = static_cast<SkShader::TileMode>(buffer.read32());
+ if (buffer.readBool()) {
+ fPicture = SkPicture::CreateFromBuffer(buffer);
+ } else {
+ fPicture = NULL;
+ }
+}
+
+SkPictureShader::~SkPictureShader() {
+ SkSafeUnref(fPicture);
+}
+
+SkPictureShader* SkPictureShader::Create(SkPicture* picture, TileMode tmx, TileMode tmy) {
+ return SkNEW_ARGS(SkPictureShader, (picture, tmx, tmy));
+}
+
+void SkPictureShader::flatten(SkWriteBuffer& buffer) const {
+ this->INHERITED::flatten(buffer);
+
+ buffer.write32(fTmx);
+ buffer.write32(fTmy);
+ buffer.writeBool(NULL != fPicture);
+ if (fPicture) {
+ fPicture->flatten(buffer);
+ }
+}
+
+bool SkPictureShader::buildBitmapShader(const SkMatrix& matrix) const {
+ if (!fPicture || (0 == fPicture->width() && 0 == fPicture->height())) {
+ return false;
+ }
+
+ SkMatrix m;
+ if (this->hasLocalMatrix()) {
+ m.setConcat(matrix, this->getLocalMatrix());
+ } else {
+ m = matrix;
+ }
+
+ // Use a rotation-invariant scale
+ SkPoint scale;
+ if (!SkDecomposeUpper2x2(m, NULL, &scale, NULL)) {
+ // Decomposition failed, use an approximation.
+ scale.set(SkScalarSqrt(m.getScaleX() * m.getScaleX() + m.getSkewX() * m.getSkewX()),
+ SkScalarSqrt(m.getScaleY() * m.getScaleY() + m.getSkewY() * m.getSkewY()));
+ }
+ SkSize scaledSize = SkSize::Make(scale.x() * fPicture->width(), scale.y() * fPicture->height());
+
+ SkISize tileSize = scaledSize.toRound();
+ if (tileSize.isEmpty()) {
+ return false;
+ }
+
+ // The actual scale, compensating for rounding.
+ SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fPicture->width(),
+ SkIntToScalar(tileSize.height()) / fPicture->height());
+
+ if (!fCachedShader || tileScale != fCachedTileScale) {
+ SkBitmap bm;
+ if (!bm.allocN32Pixels(tileSize.width(), tileSize.height())) {
+ return false;
+ }
+ bm.eraseColor(SK_ColorTRANSPARENT);
+
+ SkCanvas canvas(bm);
+ canvas.scale(tileScale.width(), tileScale.height());
+ canvas.drawPicture(*fPicture);
+
+ fCachedShader.reset(CreateBitmapShader(bm, fTmx, fTmy));
+ fCachedTileScale = tileScale;
+ }
+
+ SkMatrix shaderMatrix = this->getLocalMatrix();
+ shaderMatrix.preScale(1 / tileScale.width(), 1 / tileScale.height());
+ fCachedShader->setLocalMatrix(shaderMatrix);
+
+ return true;
+}
+
+bool SkPictureShader::setContext(const SkBitmap& device,
+ const SkPaint& paint,
+ const SkMatrix& matrix) {
+ if (!this->buildBitmapShader(matrix)) {
+ return false;
+ }
+
+ if (!this->INHERITED::setContext(device, paint, matrix)) {
+ return false;
+ }
+
+ SkASSERT(fCachedShader);
+ if (!fCachedShader->setContext(device, paint, matrix)) {
+ this->INHERITED::endContext();
+ return false;
+ }
+
+ return true;
+}
+
+void SkPictureShader::endContext() {
+ SkASSERT(fCachedShader);
+ fCachedShader->endContext();
+
+ this->INHERITED::endContext();
+}
+
+uint32_t SkPictureShader::getFlags() {
+ if (NULL != fCachedShader) {
+ return fCachedShader->getFlags();
+ }
+ return 0;
+}
+
+SkShader::ShadeProc SkPictureShader::asAShadeProc(void** ctx) {
+ if (fCachedShader) {
+ return fCachedShader->asAShadeProc(ctx);
+ }
+ return NULL;
+}
+
+void SkPictureShader::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
+ SkASSERT(fCachedShader);
+ fCachedShader->shadeSpan(x, y, dstC, count);
+}
+
+void SkPictureShader::shadeSpan16(int x, int y, uint16_t dstC[], int count) {
+ SkASSERT(fCachedShader);
+ fCachedShader->shadeSpan16(x, y, dstC, count);
+}
+
+#ifndef SK_IGNORE_TO_STRING
+void SkPictureShader::toString(SkString* str) const {
+ static const char* gTileModeName[SkShader::kTileModeCount] = {
+ "clamp", "repeat", "mirror"
+ };
+
+ str->appendf("PictureShader: [%d:%d] ",
+ fPicture ? fPicture->width() : 0,
+ fPicture ? fPicture->height() : 0);
+
+ str->appendf("(%s, %s)", gTileModeName[fTmx], gTileModeName[fTmy]);
+
+ this->INHERITED::toString(str);
+}
+#endif
+
+#if SK_SUPPORT_GPU
+GrEffectRef* SkPictureShader::asNewEffect(GrContext* context, const SkPaint& paint) const {
+ if (!this->buildBitmapShader(context->getMatrix())) {
+ return NULL;
+ }
+ SkASSERT(fCachedShader);
+ return fCachedShader->asNewEffect(context, paint);
+}
+#endif
diff --git a/src/core/SkPictureShader.h b/src/core/SkPictureShader.h
new file mode 100644
index 0000000000..ea74b56d73
--- /dev/null
+++ b/src/core/SkPictureShader.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkPictureShader_DEFINED
+#define SkPictureShader_DEFINED
+
+#include "SkShader.h"
+
+class SkBitmap;
+class SkPicture;
+
+/*
+ * An SkPictureShader can be used to draw SkPicture-based patterns.
+ *
+ * The SkPicture is first rendered into a tile, which is then used to shade the area according
+ * to specified tiling rules.
+ */
+class SkPictureShader : public SkShader {
+public:
+ static SkPictureShader* Create(SkPicture*, TileMode, TileMode);
+ virtual ~SkPictureShader();
+
+ virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
+ virtual void endContext() SK_OVERRIDE;
+ virtual uint32_t getFlags() SK_OVERRIDE;
+
+ virtual ShadeProc asAShadeProc(void** ctx) SK_OVERRIDE;
+ virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
+ virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
+
+ SK_TO_STRING_OVERRIDE()
+ SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPictureShader)
+
+#if SK_SUPPORT_GPU
+ GrEffectRef* asNewEffect(GrContext*, const SkPaint&) const SK_OVERRIDE;
+#endif
+
+protected:
+ SkPictureShader(SkReadBuffer&);
+ virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+
+private:
+ SkPictureShader(SkPicture*, TileMode, TileMode);
+
+ bool buildBitmapShader(const SkMatrix&) const;
+
+ SkPicture* fPicture;
+ TileMode fTmx, fTmy;
+
+ mutable SkAutoTUnref<SkShader> fCachedShader;
+ mutable SkSize fCachedTileScale;
+
+ typedef SkShader INHERITED;
+};
+
+#endif // SkPictureShader_DEFINED
diff --git a/src/core/SkShader.cpp b/src/core/SkShader.cpp
index 31b208e2cc..e337b7d906 100644
--- a/src/core/SkShader.cpp
+++ b/src/core/SkShader.cpp
@@ -9,6 +9,8 @@
#include "SkReadBuffer.h"
#include "SkMallocPixelRef.h"
#include "SkPaint.h"
+#include "SkPicture.h"
+#include "SkPictureShader.h"
#include "SkScalar.h"
#include "SkShader.h"
#include "SkWriteBuffer.h"
@@ -179,6 +181,10 @@ SkShader* SkShader::CreateBitmapShader(const SkBitmap& src,
return ::CreateBitmapShader(src, tmx, tmy, NULL);
}
+SkShader* SkShader::CreatePictureShader(SkPicture* src, TileMode tmx, TileMode tmy) {
+ return SkPictureShader::Create(src, tmx, tmy);
+}
+
#ifndef SK_IGNORE_TO_STRING
void SkShader::toString(SkString* str) const {
if (this->hasLocalMatrix()) {
diff --git a/src/ports/SkGlobalInitialization_chromium.cpp b/src/ports/SkGlobalInitialization_chromium.cpp
index d0900a8f09..a3ef5b24ef 100644
--- a/src/ports/SkGlobalInitialization_chromium.cpp
+++ b/src/ports/SkGlobalInitialization_chromium.cpp
@@ -51,6 +51,7 @@
#include "SkOnce.h"
#include "SkPerlinNoiseShader.h"
#include "SkPictureImageFilter.h"
+#include "SkPictureShader.h"
#include "SkPixelXorXfermode.h"
#include "SkRectShaderImageFilter.h"
#include "SkStippleMaskFilter.h"
@@ -90,6 +91,7 @@ static void InitializeFlattenables(int*) {
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPath2DPathEffect)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPerlinNoiseShader)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPictureImageFilter)
+ SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPictureShader)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPixelXorXfermode)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkRectShaderImageFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkStippleMaskFilter)
diff --git a/src/ports/SkGlobalInitialization_default.cpp b/src/ports/SkGlobalInitialization_default.cpp
index d0900a8f09..a3ef5b24ef 100644
--- a/src/ports/SkGlobalInitialization_default.cpp
+++ b/src/ports/SkGlobalInitialization_default.cpp
@@ -51,6 +51,7 @@
#include "SkOnce.h"
#include "SkPerlinNoiseShader.h"
#include "SkPictureImageFilter.h"
+#include "SkPictureShader.h"
#include "SkPixelXorXfermode.h"
#include "SkRectShaderImageFilter.h"
#include "SkStippleMaskFilter.h"
@@ -90,6 +91,7 @@ static void InitializeFlattenables(int*) {
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPath2DPathEffect)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPerlinNoiseShader)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPictureImageFilter)
+ SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPictureShader)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPixelXorXfermode)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkRectShaderImageFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkStippleMaskFilter)