diff --git a/gm/pictureshader.cpp b/gm/pictureshader.cpp index 8b26e48663..0b0a99e026 100644 --- a/gm/pictureshader.cpp +++ b/gm/pictureshader.cpp @@ -135,7 +135,8 @@ private: fPicture, kTileConfigs[tileMode].tmx, kTileConfigs[tileMode].tmy, - &localMatrix)); + &localMatrix, + NULL)); paint.setShader(pictureShader.get()); canvas->drawRect(SkRect::MakeWH(fSceneSize, fSceneSize), paint); diff --git a/gm/pictureshadertile.cpp b/gm/pictureshadertile.cpp new file mode 100644 index 0000000000..9343da3649 --- /dev/null +++ b/gm/pictureshadertile.cpp @@ -0,0 +1,147 @@ +/* + * 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 "SkPaint.h" +#include "SkPicture.h" +#include "SkPictureRecorder.h" +#include "SkShader.h" + +static const SkScalar kPictureSize = SK_Scalar1; +static const SkScalar kFillSize = 100; +static const unsigned kRowSize = 6; + +static const struct { + SkScalar x, y, w, h; + SkScalar offsetX, offsetY; +} tiles[] = { + { 0, 0, 1, 1, 0, 0 }, + { 0.5f, 0.5f, 1, 1, 0, 0 }, + { -0.5f, -0.5f, 1, 1, 0, 0 }, + + { 0, 0, 1.5f, 1.5f, 0, 0 }, + { 0.5f, 0.5f, 1.5f, 1.5f, 0, 0 }, + { -0.5f, -0.5f, 1.5f, 1.5f, 0, 0 }, + + { 0, 0, 0.5f, 0.5f, 0, 0 }, + { -0.25f, -0.25f, 0.5f, 0.5f, 0, 0 }, + { 0.25f, 0.25f, 0.5f, 0.5f, 0, 0 }, + + { 0, 0, 1, 1, 0.5f, 0.5f }, + { 0.5f, 0.5f, 1, 1, 0.5f, 0.5f }, + { -0.5f, -0.5f, 1, 1, 0.5f, 0.5f }, + + { 0, 0, 1.5f, 1.5f, 0.5f, 0.5f }, + { 0.5f, 0.5f, 1.5f, 1.5f, 0.5f, 0.5f }, + { -0.5f, -0.5f, 1.5f, 1.5f, 0.5f, 0.5f }, + + { 0, 0, 1.5f, 1, 0, 0 }, + { 0.5f, 0.5f, 1.5f, 1, 0, 0 }, + { -0.5f, -0.5f, 1.5f, 1, 0, 0 }, + + { 0, 0, 0.5f, 1, 0, 0 }, + { -0.25f, -0.25f, 0.5f, 1, 0, 0 }, + { 0.25f, 0.25f, 0.5f, 1, 0, 0 }, + + { 0, 0, 1, 1.5f, 0, 0 }, + { 0.5f, 0.5f, 1, 1.5f, 0, 0 }, + { -0.5f, -0.5f, 1, 1.5f, 0, 0 }, + + { 0, 0, 1, 0.5f, 0, 0 }, + { -0.25f, -0.25f, 1, 0.5f, 0, 0 }, + { 0.25f, 0.25f, 1, 0.5f, 0, 0 }, +}; + +class PictureShaderTileGM : public skiagm::GM { +public: + PictureShaderTileGM() { + SkPictureRecorder recorder; + SkCanvas* pictureCanvas = recorder.beginRecording(SkScalarCeilToInt(kPictureSize), + SkScalarCeilToInt(kPictureSize), + NULL, 0); + drawScene(pictureCanvas, kPictureSize); + SkAutoTUnref picture(recorder.endRecording()); + + for (unsigned i = 0; i < SK_ARRAY_COUNT(tiles); ++i) { + SkRect tile = SkRect::MakeXYWH(tiles[i].x * kPictureSize, + tiles[i].y * kPictureSize, + tiles[i].w * kPictureSize, + tiles[i].h * kPictureSize); + SkMatrix localMatrix; + localMatrix.setTranslate(tiles[i].offsetX * kPictureSize, + tiles[i].offsetY * kPictureSize); + localMatrix.postScale(kFillSize / (2 * kPictureSize), + kFillSize / (2 * kPictureSize)); + fShaders[i].reset(SkShader::CreatePictureShader(picture, + SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode, + &localMatrix, + &tile)); + } + } + +protected: + virtual uint32_t onGetFlags() const SK_OVERRIDE { + return kSkipTiled_Flag; + } + + virtual SkString onShortName() SK_OVERRIDE { + return SkString("pictureshadertile"); + } + + virtual SkISize onISize() SK_OVERRIDE { + return SkISize::Make(800, 600); + } + + virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE { + canvas->clear(SK_ColorBLACK); + + SkPaint paint; + paint.setStyle(SkPaint::kFill_Style); + + for (unsigned i = 0; i < SK_ARRAY_COUNT(fShaders); ++i) { + paint.setShader(fShaders[i]); + + canvas->save(); + canvas->translate((i % kRowSize) * kFillSize * 1.1f, + (i / kRowSize) * kFillSize * 1.1f); + canvas->drawRect(SkRect::MakeWH(kFillSize, kFillSize), paint); + canvas->restore(); + } + } + +private: + void drawScene(SkCanvas* canvas, SkScalar pictureSize) { + canvas->clear(SK_ColorWHITE); + + SkPaint paint; + paint.setColor(SK_ColorGREEN); + paint.setStyle(SkPaint::kFill_Style); + paint.setAntiAlias(true); + + canvas->drawCircle(pictureSize / 4, pictureSize / 4, pictureSize / 4, paint); + canvas->drawRect(SkRect::MakeXYWH(pictureSize / 2, pictureSize / 2, + pictureSize / 2, pictureSize / 2), paint); + + paint.setColor(SK_ColorRED); + canvas->drawLine(pictureSize / 2, pictureSize * 1 / 3, + pictureSize / 2, pictureSize * 2 / 3, paint); + canvas->drawLine(pictureSize * 1 / 3, pictureSize / 2, + pictureSize * 2 / 3, pictureSize / 2, paint); + + paint.setColor(SK_ColorBLACK); + paint.setStyle(SkPaint::kStroke_Style); + canvas->drawRect(SkRect::MakeWH(pictureSize, pictureSize), paint); + } + + SkAutoTUnref fShaders[SK_ARRAY_COUNT(tiles)]; + + typedef GM INHERITED; +}; + +DEF_GM( return SkNEW(PictureShaderTileGM); ) diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi index cb01ff05bf..d9c18722b1 100644 --- a/gyp/gmslides.gypi +++ b/gyp/gmslides.gypi @@ -135,6 +135,7 @@ '../gm/perlinnoise.cpp', '../gm/pictureimagefilter.cpp', '../gm/pictureshader.cpp', + '../gm/pictureshadertile.cpp', '../gm/points.cpp', '../gm/poly2poly.cpp', '../gm/polygons.cpp', diff --git a/include/core/SkShader.h b/include/core/SkShader.h index e5af40dd7a..4f3fd29693 100644 --- a/include/core/SkShader.h +++ b/include/core/SkShader.h @@ -428,10 +428,17 @@ public: * FIXME: src cannot be const due to SkCanvas::drawPicture * @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. + * @param tile The tile rectangle in picture coordinates: this represents the subset + * (or superset) of the picture used when building a tile. It is not + * affected by localMatrix and does not imply scaling (only translation + * and cropping). If null, the tile rect is considered equal to the picture + * bounds. * @return Returns a new shader object. Note: this function never returns null. */ - static SkShader* CreatePictureShader(SkPicture* src, TileMode tmx, TileMode tmy, - const SkMatrix* localMatrix = NULL); + static SkShader* CreatePictureShader(SkPicture* src, + TileMode tmx, TileMode tmy, + const SkMatrix* localMatrix, + const SkRect* tile); /** * Return a shader that will apply the specified localMatrix to the proxy shader. diff --git a/src/core/SkPictureShader.cpp b/src/core/SkPictureShader.cpp index 6ea67f8035..2a6aae5f4e 100644 --- a/src/core/SkPictureShader.cpp +++ b/src/core/SkPictureShader.cpp @@ -19,16 +19,20 @@ #endif SkPictureShader::SkPictureShader(const SkPicture* picture, TileMode tmx, TileMode tmy, - const SkMatrix* localMatrix) + const SkMatrix* localMatrix, const SkRect* tile) : INHERITED(localMatrix) , fPicture(SkRef(picture)) , fTmx(tmx) - , fTmy(tmy) { } + , fTmy(tmy) { + fTile = tile ? *tile : SkRect::MakeWH(SkIntToScalar(picture->width()), + SkIntToScalar(picture->height())); +} SkPictureShader::SkPictureShader(SkReadBuffer& buffer) : INHERITED(buffer) { fTmx = static_cast(buffer.read32()); fTmy = static_cast(buffer.read32()); + buffer.readRect(&fTile); fPicture = SkPicture::CreateFromBuffer(buffer); } @@ -37,11 +41,12 @@ SkPictureShader::~SkPictureShader() { } SkPictureShader* SkPictureShader::Create(const SkPicture* picture, TileMode tmx, TileMode tmy, - const SkMatrix* localMatrix) { - if (!picture || 0 == picture->width() || 0 == picture->height()) { + const SkMatrix* localMatrix, const SkRect* tile) { + if (!picture || 0 == picture->width() || 0 == picture->height() + || (NULL != tile && tile->isEmpty())) { return NULL; } - return SkNEW_ARGS(SkPictureShader, (picture, tmx, tmy, localMatrix)); + return SkNEW_ARGS(SkPictureShader, (picture, tmx, tmy, localMatrix, tile)); } void SkPictureShader::flatten(SkWriteBuffer& buffer) const { @@ -49,6 +54,7 @@ void SkPictureShader::flatten(SkWriteBuffer& buffer) const { buffer.write32(fTmx); buffer.write32(fTmy); + buffer.writeRect(fTile); fPicture->flatten(buffer); } @@ -68,7 +74,7 @@ SkShader* SkPictureShader::refBitmapShader(const SkMatrix& matrix, const SkMatri 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()); + SkSize scaledSize = SkSize::Make(scale.x() * fTile.width(), scale.y() * fTile.height()); SkISize tileSize = scaledSize.toRound(); if (tileSize.isEmpty()) { @@ -76,8 +82,8 @@ SkShader* SkPictureShader::refBitmapShader(const SkMatrix& matrix, const SkMatri } // The actual scale, compensating for rounding. - SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fPicture->width(), - SkIntToScalar(tileSize.height()) / fPicture->height()); + SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fTile.width(), + SkIntToScalar(tileSize.height()) / fTile.height()); SkAutoMutexAcquire ama(fCachedBitmapShaderMutex); @@ -90,6 +96,7 @@ SkShader* SkPictureShader::refBitmapShader(const SkMatrix& matrix, const SkMatri SkCanvas canvas(bm); canvas.scale(tileScale.width(), tileScale.height()); + canvas.translate(fTile.x(), fTile.y()); canvas.drawPicture(fPicture); fCachedTileScale = tileScale; diff --git a/src/core/SkPictureShader.h b/src/core/SkPictureShader.h index 00aee9b078..99c70c9465 100644 --- a/src/core/SkPictureShader.h +++ b/src/core/SkPictureShader.h @@ -21,7 +21,8 @@ class SkPicture; */ class SkPictureShader : public SkShader { public: - static SkPictureShader* Create(const SkPicture*, TileMode, TileMode, const SkMatrix* = NULL); + static SkPictureShader* Create(const SkPicture*, TileMode, TileMode, const SkMatrix*, + const SkRect*); virtual ~SkPictureShader(); virtual size_t contextSize() const SK_OVERRIDE; @@ -38,11 +39,12 @@ protected: virtual Context* onCreateContext(const ContextRec&, void* storage) const SK_OVERRIDE; private: - SkPictureShader(const SkPicture*, TileMode, TileMode, const SkMatrix* = NULL); + SkPictureShader(const SkPicture*, TileMode, TileMode, const SkMatrix*, const SkRect*); SkShader* refBitmapShader(const SkMatrix&, const SkMatrix* localMatrix) const; const SkPicture* fPicture; + SkRect fTile; TileMode fTmx, fTmy; mutable SkMutex fCachedBitmapShaderMutex; diff --git a/src/core/SkShader.cpp b/src/core/SkShader.cpp index d533cd69b4..305cf06695 100644 --- a/src/core/SkShader.cpp +++ b/src/core/SkShader.cpp @@ -228,8 +228,8 @@ SkShader* SkShader::CreateBitmapShader(const SkBitmap& src, TileMode tmx, TileMo } SkShader* SkShader::CreatePictureShader(SkPicture* src, TileMode tmx, TileMode tmy, - const SkMatrix* localMatrix) { - return SkPictureShader::Create(src, tmx, tmy, localMatrix); + const SkMatrix* localMatrix, const SkRect* tile) { + return SkPictureShader::Create(src, tmx, tmy, localMatrix, tile); } #ifndef SK_IGNORE_TO_STRING diff --git a/tests/PictureShaderTest.cpp b/tests/PictureShaderTest.cpp index 17ef5b56be..8d933db600 100644 --- a/tests/PictureShaderTest.cpp +++ b/tests/PictureShaderTest.cpp @@ -14,13 +14,13 @@ // empty picture returns NULL. DEF_TEST(PictureShader_empty, reporter) { SkShader* shader = SkShader::CreatePictureShader(NULL, - SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); + SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, NULL, NULL); REPORTER_ASSERT(reporter, NULL == shader); SkPictureRecorder factory; factory.beginRecording(0, 0, NULL, 0); SkAutoTUnref picture(factory.endRecording()); shader = SkShader::CreatePictureShader(picture.get(), - SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); + SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, NULL, NULL); REPORTER_ASSERT(reporter, NULL == shader); }