diff options
author | msarett <msarett@google.com> | 2016-08-02 08:05:56 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-08-02 08:05:56 -0700 |
commit | c573a40ed5024b463e47088d307e3164a486dba5 (patch) | |
tree | 17969315cf64ce827395c4b8ad9273cc6f96886d | |
parent | 7d0e3bc785fc5aaf2ed0aa8f37a2bc85c2f82da0 (diff) |
Add drawImageLattice() and drawBitmapLattice() APIs
The specified image/bitmap is divided into rects, which
can be draw stretched, shrunk, or at a fixed size. Will be
used by Android to draw 9patch (which are acutally N-patch)
images.
BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1992283002
Review-Url: https://codereview.chromium.org/1992283002
-rw-r--r-- | bench/DrawLatticeBench.cpp | 59 | ||||
-rw-r--r-- | gm/lattice.cpp | 147 | ||||
-rw-r--r-- | gyp/core.gypi | 4 | ||||
-rw-r--r-- | include/core/SkCanvas.h | 45 | ||||
-rw-r--r-- | include/core/SkDevice.h | 4 | ||||
-rw-r--r-- | include/private/SkRecords.h | 9 | ||||
-rw-r--r-- | src/core/SkCanvas.cpp | 47 | ||||
-rw-r--r-- | src/core/SkDevice.cpp | 17 | ||||
-rw-r--r-- | src/core/SkLatticeIter.cpp | 230 | ||||
-rw-r--r-- | src/core/SkLatticeIter.h | 49 | ||||
-rw-r--r-- | src/core/SkNinePatchIter.cpp | 72 | ||||
-rw-r--r-- | src/core/SkNinePatchIter.h | 41 | ||||
-rw-r--r-- | src/core/SkPictureFlat.h | 3 | ||||
-rw-r--r-- | src/core/SkPicturePlayback.cpp | 12 | ||||
-rw-r--r-- | src/core/SkPictureRecord.cpp | 18 | ||||
-rw-r--r-- | src/core/SkPictureRecord.h | 2 | ||||
-rw-r--r-- | src/core/SkRecordDraw.cpp | 13 | ||||
-rw-r--r-- | src/core/SkRecorder.cpp | 10 | ||||
-rw-r--r-- | src/core/SkRecorder.h | 2 | ||||
-rw-r--r-- | src/gpu/SkGpuDevice.cpp | 4 | ||||
-rw-r--r-- | src/gpu/batches/GrNinePatch.cpp | 4 | ||||
-rw-r--r-- | src/pdf/SkPDFCanvas.cpp | 17 | ||||
-rw-r--r-- | src/pdf/SkPDFCanvas.h | 5 | ||||
-rw-r--r-- | tests/PictureTest.cpp | 5 |
24 files changed, 688 insertions, 131 deletions
diff --git a/bench/DrawLatticeBench.cpp b/bench/DrawLatticeBench.cpp new file mode 100644 index 0000000000..4c03ca1dfa --- /dev/null +++ b/bench/DrawLatticeBench.cpp @@ -0,0 +1,59 @@ +/* +* Copyright 2016 Google Inc. +* +* Use of this source code is governed by a BSD-style license that can be +* found in the LICENSE file. +*/ + +#include "Benchmark.h" +#include "SkCanvas.h" +#include "SkRect.h" +#include "SkString.h" + +class DrawLatticeBench : public Benchmark { +public: + DrawLatticeBench(int* xDivs, int xCount, int* yDivs, int yCount, const SkISize& srcSize, + const SkRect& dst, const char* desc) + : fSrcSize(srcSize) + , fDst(dst) + { + fLattice.fXDivs = xDivs; + fLattice.fXCount = xCount; + fLattice.fYDivs = yDivs; + fLattice.fYCount = yCount; + + fName = SkStringPrintf("DrawLattice_%s", desc); + } + + const char* onGetName() override { + return fName.c_str(); + } + + bool isSuitableFor(Backend backend) override { + return kRaster_Backend == backend || kGPU_Backend == backend; + } + + void onDelayedSetup() override { + fBitmap.allocN32Pixels(fSrcSize.width(), fSrcSize.height()); + fBitmap.eraseColor(0x880000FF); + } + + void onDraw(int loops, SkCanvas* canvas) override { + for (int i = 0; i < loops; i++) { + canvas->drawBitmapLattice(fBitmap, fLattice, fDst); + } + } + +private: + SkISize fSrcSize; + SkCanvas::Lattice fLattice; + SkRect fDst; + SkString fName; + SkBitmap fBitmap; + + typedef Benchmark INHERITED; +}; + +static int gDivs[2] = { 250, 750, }; +DEF_BENCH(return new DrawLatticeBench(gDivs, 2, gDivs, 2, SkISize::Make(1000, 1000), + SkRect::MakeWH(4000.0f, 4000.0f), "StandardNine");) diff --git a/gm/lattice.cpp b/gm/lattice.cpp new file mode 100644 index 0000000000..63de2a66b4 --- /dev/null +++ b/gm/lattice.cpp @@ -0,0 +1,147 @@ +/* + * Copyright 2016 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 "SkSurface.h" + +static sk_sp<SkSurface> make_surface(SkCanvas* root, int N) { + SkImageInfo info = SkImageInfo::MakeN32Premul(N, N); + auto surface = root->makeSurface(info); + if (!surface) { + surface = SkSurface::MakeRaster(info); + } + return surface; +} + +static sk_sp<SkImage> make_image(SkCanvas* root, int* xDivs, int* yDivs) { + const int kCap = 28; + const int kMid = 8; + const int kSize = 2*kCap + 3*kMid; + + auto surface(make_surface(root, kSize)); + SkCanvas* canvas = surface->getCanvas(); + + SkRect r = SkRect::MakeWH(SkIntToScalar(kSize), SkIntToScalar(kSize)); + const SkScalar strokeWidth = SkIntToScalar(6); + const SkScalar radius = SkIntToScalar(kCap) - strokeWidth/2; + + xDivs[0] = yDivs[0] = kCap; + xDivs[1] = yDivs[1] = kCap + kMid; + xDivs[2] = yDivs[2] = kCap + 2 * kMid; + xDivs[3] = yDivs[3] = kCap + 3 * kMid; + + SkPaint paint; + paint.setAntiAlias(true); + + paint.setColor(0xFFFFFF00); + canvas->drawRoundRect(r, radius, radius, paint); + + r.setXYWH(SkIntToScalar(kCap), 0, SkIntToScalar(kMid), SkIntToScalar(kSize)); + paint.setColor(0x8800FF00); + canvas->drawRect(r, paint); + r.setXYWH(SkIntToScalar(kCap + kMid), 0, SkIntToScalar(kMid), SkIntToScalar(kSize)); + paint.setColor(0x880000FF); + canvas->drawRect(r, paint); + r.setXYWH(SkIntToScalar(kCap + 2*kMid), 0, SkIntToScalar(kMid), SkIntToScalar(kSize)); + paint.setColor(0x88FF00FF); + canvas->drawRect(r, paint); + + r.setXYWH(0, SkIntToScalar(kCap), SkIntToScalar(kSize), SkIntToScalar(kMid)); + paint.setColor(0x8800FF00); + canvas->drawRect(r, paint); + r.setXYWH(0, SkIntToScalar(kCap + kMid), SkIntToScalar(kSize), SkIntToScalar(kMid)); + paint.setColor(0x880000FF); + canvas->drawRect(r, paint); + r.setXYWH(0, SkIntToScalar(kCap + 2*kMid), SkIntToScalar(kSize), SkIntToScalar(kMid)); + paint.setColor(0x88FF00FF); + canvas->drawRect(r, paint); + + return surface->makeImageSnapshot(); +} + +static void image_to_bitmap(const SkImage* image, SkBitmap* bm) { + SkImageInfo info = SkImageInfo::MakeN32Premul(image->width(), image->height()); + bm->allocPixels(info); + image->readPixels(info, bm->getPixels(), bm->rowBytes(), 0, 0); +} + +/** + * This is similar to NinePatchStretchGM, but it also tests "ninepatch" images with more + * than nine patches. + */ +class LatticeGM : public skiagm::GM { +public: + LatticeGM() {} + +protected: + SkString onShortName() override { + return SkString("lattice"); + } + + SkISize onISize() override { + return SkISize::Make(800, 400); + } + + void onDraw(SkCanvas* canvas) override { + int xDivs[5]; + int yDivs[5]; + xDivs[0] = 0; + yDivs[0] = 0; + + SkBitmap bitmap; + sk_sp<SkImage> image = make_image(canvas, xDivs + 1, yDivs + 1); + image_to_bitmap(image.get(), &bitmap); + + const SkTSize<SkScalar> size[] = { + { 50, 50, }, // shrink in both axes + { 50, 200, }, // shrink in X + { 200, 50, }, // shrink in Y + { 200, 200, }, + }; + + canvas->drawImage(image, 10, 10, nullptr); + + SkScalar x = SkIntToScalar(100); + SkScalar y = SkIntToScalar(100); + + SkCanvas::Lattice lattice; + lattice.fXCount = 4; + lattice.fXDivs = xDivs + 1; + lattice.fYCount = 4; + lattice.fYDivs = yDivs + 1; + + for (int iy = 0; iy < 2; ++iy) { + for (int ix = 0; ix < 2; ++ix) { + int i = ix * 2 + iy; + SkRect r = SkRect::MakeXYWH(x + ix * 60, y + iy * 60, + size[i].width(), size[i].height()); + canvas->drawBitmapLattice(bitmap, lattice, r); + } + } + + // Include the degenerate first div. While normally the first patch is "scalable", + // this will mean that the first non-degenerate patch is "fixed". + lattice.fXCount = 5; + lattice.fXDivs = xDivs; + lattice.fYCount = 5; + lattice.fYDivs = yDivs; + + canvas->translate(400, 0); + for (int iy = 0; iy < 2; ++iy) { + for (int ix = 0; ix < 2; ++ix) { + int i = ix * 2 + iy; + SkRect r = SkRect::MakeXYWH(x + ix * 60, y + iy * 60, + size[i].width(), size[i].height()); + canvas->drawImageLattice(image.get(), lattice, r); + } + } + } + +private: + typedef skiagm::GM INHERITED; +}; +DEF_GM( return new LatticeGM; ) diff --git a/gyp/core.gypi b/gyp/core.gypi index 35312563a2..6b1d9f4eb5 100644 --- a/gyp/core.gypi +++ b/gyp/core.gypi @@ -187,8 +187,8 @@ '<(skia_src_path)/core/SkModeColorFilter.cpp', '<(skia_src_path)/core/SkMultiPictureDraw.cpp', '<(skia_src_path)/core/SkNextID.h', - '<(skia_src_path)/core/SkNinePatchIter.cpp', - '<(skia_src_path)/core/SkNinePatchIter.h', + '<(skia_src_path)/core/SkLatticeIter.cpp', + '<(skia_src_path)/core/SkLatticeIter.h', '<(skia_src_path)/core/SkNormalBevelSource.cpp', '<(skia_src_path)/core/SkNormalBevelSource.h', '<(skia_src_path)/core/SkNormalMapSource.cpp', diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h index 3f9b034946..c3f8599db4 100644 --- a/include/core/SkCanvas.h +++ b/include/core/SkCanvas.h @@ -938,7 +938,7 @@ public: SrcRectConstraint = kStrict_SrcRectConstraint); /** - * Draw the bitmap stretched differentially to fit into dst. + * Draw the bitmap stretched or shrunk differentially to fit into dst. * center is a rect within the bitmap, and logically divides the bitmap * into 9 sections (3x3). For example, if the middle pixel of a [5x5] * bitmap is the "center", then the center-rect should be [2, 2, 3, 3]. @@ -954,6 +954,47 @@ public: void drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst, const SkPaint* paint = NULL); + /** + * Specifies coordinates to divide a bitmap into (xCount*yCount) rects. + */ + struct Lattice { + // An array of x-coordinates that divide the bitmap vertically. + // These must be unique, increasing, and in the set [0, width]. + // Does not have ownership. + const int* fXDivs; + + // The number of fXDivs. + int fXCount; + + // An array of y-coordinates that divide the bitmap horizontally. + // These must be unique, increasing, and in the set [0, height]. + // Does not have ownership. + const int* fYDivs; + + // The number of fYDivs. + int fYCount; + }; + + /** + * Draw the bitmap stretched or shrunk differentially to fit into dst. + * + * Moving horizontally across the bitmap, alternating rects will be "scalable" + * (in the x-dimension) to fit into dst or must be left "fixed". The first rect + * is treated as "fixed", but it's possible to specify an empty first rect by + * making lattice.fXDivs[0] = 0. + * + * The scale factor for all "scalable" rects will be the same, and may be greater + * than or less than 1 (meaning we can stretch or shrink). If the number of + * "fixed" pixels is greater than the width of the dst, we will collapse all of + * the "scalable" regions and appropriately downscale the "fixed" regions. + * + * The same interpretation also applies to the y-dimension. + */ + void drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst, + const SkPaint* paint = nullptr); + void drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst, + const SkPaint* paint = nullptr); + /** Draw the text, with origin at (x,y), using the specified paint. The origin is interpreted based on the Align setting in the paint. @param text The text to be drawn @@ -1435,6 +1476,8 @@ protected: SrcRectConstraint); virtual void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, const SkPaint*); + virtual void onDrawImageLattice(const SkImage*, const Lattice& lattice, const SkRect& dst, + const SkPaint*); enum ClipEdgeStyle { kHard_ClipEdgeStyle, diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h index f3b484e4be..99018df182 100644 --- a/include/core/SkDevice.h +++ b/include/core/SkDevice.h @@ -197,13 +197,15 @@ protected: const SkPaint& paint, SkCanvas::SrcRectConstraint) = 0; virtual void drawBitmapNine(const SkDraw&, const SkBitmap&, const SkIRect& center, - const SkRect& dst, const SkPaint&); + const SkRect& dst, const SkPaint&); virtual void drawImage(const SkDraw&, const SkImage*, SkScalar x, SkScalar y, const SkPaint&); virtual void drawImageRect(const SkDraw&, const SkImage*, const SkRect* src, const SkRect& dst, const SkPaint&, SkCanvas::SrcRectConstraint); virtual void drawImageNine(const SkDraw&, const SkImage*, const SkIRect& center, const SkRect& dst, const SkPaint&); + virtual void drawImageLattice(const SkDraw&, const SkImage*, const SkCanvas::Lattice&, + const SkRect& dst, const SkPaint&); /** * Does not handle text decoration. diff --git a/include/private/SkRecords.h b/include/private/SkRecords.h index 32da3dd377..a095452d1c 100644 --- a/include/private/SkRecords.h +++ b/include/private/SkRecords.h @@ -55,6 +55,7 @@ namespace SkRecords { M(ClipRegion) \ M(DrawDrawable) \ M(DrawImage) \ + M(DrawImageLattice) \ M(DrawImageRect) \ M(DrawImageNine) \ M(DrawDRRect) \ @@ -222,6 +223,14 @@ RECORD(DrawImage, kDraw_Tag|kHasImage_Tag|kHasPaint_Tag, sk_sp<const SkImage> image; SkScalar left; SkScalar top); +RECORD(DrawImageLattice, kDraw_Tag|kHasImage_Tag|kHasPaint_Tag, + Optional<SkPaint> paint; + sk_sp<const SkImage> image; + int xCount; + PODArray<int> xDivs; + int yCount; + PODArray<int> yDivs; + SkRect dst); RECORD(DrawImageRect, kDraw_Tag|kHasImage_Tag|kHasPaint_Tag, Optional<SkPaint> paint; sk_sp<const SkImage> image; diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index a86cd37cad..648059a196 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -19,9 +19,9 @@ #include "SkImage_Base.h" #include "SkImageFilter.h" #include "SkImageFilterCache.h" +#include "SkLatticeIter.h" #include "SkMatrixUtils.h" #include "SkMetaData.h" -#include "SkNinePatchIter.h" #include "SkPaintPriv.h" #include "SkPatchUtils.h" #include "SkPicture.h" @@ -1985,7 +1985,7 @@ void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const if (dst.isEmpty()) { return; } - if (!SkNinePatchIter::Valid(image->width(), image->height(), center)) { + if (!SkLatticeIter::Valid(image->width(), image->height(), center)) { this->drawImageRect(image, dst, paint); } this->onDrawImageNine(image, center, dst, paint); @@ -2022,12 +2022,30 @@ void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, con if (bitmap.drawsNothing() || dst.isEmpty()) { return; } - if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) { + if (!SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) { this->drawBitmapRect(bitmap, dst, paint); } this->onDrawBitmapNine(bitmap, center, dst, paint); } +void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst, + const SkPaint* paint) { + sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap); + this->drawImageLattice(image.get(), lattice, dst, paint); +} + +void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst, + const SkPaint* paint) { + RETURN_ON_NULL(image); + if (dst.isEmpty()) { + return; + } + if (!SkLatticeIter::Valid(image->width(), image->height(), lattice)) { + this->drawImageRect(image, dst, paint); + } + this->onDrawImageLattice(image, lattice, dst, paint); +} + void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[], const SkColor colors[], int count, SkXfermode::Mode mode, const SkRect* cull, const SkPaint* paint) { @@ -2332,6 +2350,29 @@ void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const S LOOPER_END } +void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst, + const SkPaint* paint) { + if (nullptr == paint || paint->canComputeFastBounds()) { + SkRect storage; + if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) { + return; + } + } + + SkLazyPaint lazy; + if (nullptr == paint) { + paint = lazy.init(); + } + + LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst) + + while (iter.next()) { + iter.fDevice->drawImageLattice(iter, image, lattice, dst, looper.paint()); + } + + LOOPER_END +} + void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, const SkPaint* paint, SrcRectConstraint constraint) { TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()"); diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp index 14250ab775..59408dc64a 100644 --- a/src/core/SkDevice.cpp +++ b/src/core/SkDevice.cpp @@ -12,8 +12,8 @@ #include "SkImage_Base.h" #include "SkImageFilter.h" #include "SkImageFilterCache.h" +#include "SkLatticeIter.h" #include "SkMetaData.h" -#include "SkNinePatchIter.h" #include "SkPatchUtils.h" #include "SkPathMeasure.h" #include "SkRasterClip.h" @@ -152,6 +152,17 @@ void SkBaseDevice::drawImage(const SkDraw& draw, const SkImage* image, SkScalar } } +void SkBaseDevice::drawImageLattice(const SkDraw& draw, const SkImage* image, + const SkCanvas::Lattice& lattice, const SkRect& dst, + const SkPaint& paint) { + SkLatticeIter iter(image->width(), image->height(), lattice, dst); + + SkRect srcR, dstR; + while (iter.next(&srcR, &dstR)) { + this->drawImageRect(draw, image, &srcR, dstR, paint, SkCanvas::kStrict_SrcRectConstraint); + } +} + void SkBaseDevice::drawImageRect(const SkDraw& draw, const SkImage* image, const SkRect* src, const SkRect& dst, const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) { @@ -164,7 +175,7 @@ void SkBaseDevice::drawImageRect(const SkDraw& draw, const SkImage* image, const void SkBaseDevice::drawImageNine(const SkDraw& draw, const SkImage* image, const SkIRect& center, const SkRect& dst, const SkPaint& paint) { - SkNinePatchIter iter(image->width(), image->height(), center, dst); + SkLatticeIter iter(image->width(), image->height(), center, dst); SkRect srcR, dstR; while (iter.next(&srcR, &dstR)) { @@ -174,7 +185,7 @@ void SkBaseDevice::drawImageNine(const SkDraw& draw, const SkImage* image, const void SkBaseDevice::drawBitmapNine(const SkDraw& draw, const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst, const SkPaint& paint) { - SkNinePatchIter iter(bitmap.width(), bitmap.height(), center, dst); + SkLatticeIter iter(bitmap.width(), bitmap.height(), center, dst); SkRect srcR, dstR; while (iter.next(&srcR, &dstR)) { diff --git a/src/core/SkLatticeIter.cpp b/src/core/SkLatticeIter.cpp new file mode 100644 index 0000000000..24ab3f1cbc --- /dev/null +++ b/src/core/SkLatticeIter.cpp @@ -0,0 +1,230 @@ +/* + * 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 "SkLatticeIter.h" +#include "SkRect.h" + +/** + * Divs must be in increasing order with no duplicates. + */ +static bool valid_divs(const int* divs, int count, int len) { + if (count <= 0) { + return false; + } + + int prev = -1; + for (int i = 0; i < count; i++) { + if (prev >= divs[i] || divs[i] > len) { + return false; + } + } + + return true; +} + +bool SkLatticeIter::Valid(int width, int height, const SkCanvas::Lattice& lattice) { + return valid_divs(lattice.fXDivs, lattice.fXCount, width) && + valid_divs(lattice.fYDivs, lattice.fYCount, height); +} + +/** + * Count the number of pixels that are in "scalable" patches. + */ +static int count_scalable_pixels(const int32_t* divs, int numDivs, bool firstIsScalable, + int length) { + if (0 == numDivs) { + return firstIsScalable ? length : 0; + } + + int i; + int count; + if (firstIsScalable) { + count = divs[0]; + i = 1; + } else { + count = 0; + i = 0; + } + + for (; i < numDivs; i += 2) { + // Alternatively, we could use |top| and |bottom| as variable names, instead of + // |left| and |right|. + int left = divs[i]; + int right = (i + 1 < numDivs) ? divs[i + 1] : length; + count += right - left; + } + + return count; +} + +/** + * Set points for the src and dst rects on subsequent draw calls. + */ +static void set_points(float* dst, float* src, const int* divs, int divCount, int srcFixed, + int srcScalable, float dstStart, float dstStop, bool isScalable) { + + float dstLen = dstStop - dstStart; + int srcLen = srcFixed + srcScalable; + float scale; + if (srcFixed <= dstLen) { + // This is the "normal" case, where we scale the "scalable" patches and leave + // the other patches fixed. + scale = (dstLen - ((float) srcFixed)) / ((float) srcScalable); + } else { + // In this case, we eliminate the "scalable" patches and scale the "fixed" patches. + scale = dstLen / ((float) srcFixed); + } + + src[0] = 0.0f; + dst[0] = dstStart; + for (int i = 0; i < divCount; i++) { + src[i + 1] = (float) (divs[i]); + float srcDelta = src[i + 1] - src[i]; + float dstDelta; + if (srcFixed <= dstLen) { + dstDelta = isScalable ? scale * srcDelta : srcDelta; + } else { + dstDelta = isScalable ? 0.0f : scale * srcDelta; + } + dst[i + 1] = dst[i] + dstDelta; + + // Alternate between "scalable" and "fixed" patches. + isScalable = !isScalable; + } + + src[divCount + 1] = (float) srcLen; + dst[divCount + 1] = dstStop; +} + +SkLatticeIter::SkLatticeIter(int srcWidth, int srcHeight, const SkCanvas::Lattice& lattice, + const SkRect& dst) +{ + const int* xDivs = lattice.fXDivs; + int xCount = lattice.fXCount; + const int* yDivs = lattice.fYDivs; + int yCount = lattice.fYCount; + + // In the x-dimension, the first rectangle always starts at x = 0 and is "scalable". + // If xDiv[0] is 0, it indicates that the first rectangle is degenerate, so the + // first real rectangle "scalable" in the x-direction. + // + // The same interpretation applies to the y-dimension. + // + // As we move left to right across the image, alternating patches will be "fixed" or + // "scalable" in the x-direction. Similarly, as move top to bottom, alternating + // patches will be "fixed" or "scalable" in the y-direction. + SkASSERT(xCount > 0 && yCount > 0); + bool xIsScalable = (0 == xDivs[0]); + if (xIsScalable) { + // Once we've decided that the first patch is "scalable", we don't need the + // xDiv. It is always implied that we start at zero. + xDivs++; + xCount--; + } + bool yIsScalable = (0 == yDivs[0]); + if (yIsScalable) { + // Once we've decided that the first patch is "scalable", we don't need the + // yDiv. It is always implied that we start at zero. + yDivs++; + yCount--; + } + + // We never need the final xDiv/yDiv if it is equal to the width/height. This is implied. + if (xCount > 0 && srcWidth == xDivs[xCount - 1]) { + xCount--; + } + if (yCount > 0 && srcHeight == yDivs[yCount - 1]) { + yCount--; + } + + // Count "scalable" and "fixed" pixels in each dimension. + int xCountScalable = count_scalable_pixels(xDivs, xCount, xIsScalable, srcWidth); + int xCountFixed = srcWidth - xCountScalable; + int yCountScalable = count_scalable_pixels(yDivs, yCount, yIsScalable, srcHeight); + int yCountFixed = srcHeight - yCountScalable; + + fSrcX.reset(xCount + 2); + fDstX.reset(xCount + 2); + set_points(fDstX.begin(), fSrcX.begin(), xDivs, xCount, xCountFixed, xCountScalable, + dst.fLeft, dst.fRight, xIsScalable); + + fSrcY.reset(yCount + 2); + fDstY.reset(yCount + 2); + set_points(fDstY.begin(), fSrcY.begin(), yDivs, yCount, yCountFixed, yCountScalable, + dst.fTop, dst.fBottom, yIsScalable); + + fCurrX = fCurrY = 0; + fDone = false; +} + +bool SkLatticeIter::Valid(int width, int height, const SkIRect& center) { + return !center.isEmpty() && SkIRect::MakeWH(width, height).contains(center); +} + +SkLatticeIter::SkLatticeIter(int w, int h, const SkIRect& c, const SkRect& dst) { + SkASSERT(SkIRect::MakeWH(w, h).contains(c)); + + fSrcX.reset(4); + fSrcY.reset(4); + fDstX.reset(4); + fDstY.reset(4); + + fSrcX[0] = 0; + fSrcX[1] = SkIntToScalar(c.fLeft); + fSrcX[2] = SkIntToScalar(c.fRight); + fSrcX[3] = SkIntToScalar(w); + + fSrcY[0] = 0; + fSrcY[1] = SkIntToScalar(c.fTop); + fSrcY[2] = SkIntToScalar(c.fBottom); + fSrcY[3] = SkIntToScalar(h); + + fDstX[0] = dst.fLeft; + fDstX[1] = dst.fLeft + SkIntToScalar(c.fLeft); + fDstX[2] = dst.fRight - SkIntToScalar(w - c.fRight); + fDstX[3] = dst.fRight; + + fDstY[0] = dst.fTop; + fDstY[1] = dst.fTop + SkIntToScalar(c.fTop); + fDstY[2] = dst.fBottom - SkIntToScalar(h - c.fBottom); + fDstY[3] = dst.fBottom; + + if (fDstX[1] > fDstX[2]) { + fDstX[1] = fDstX[0] + (fDstX[3] - fDstX[0]) * c.fLeft / (w - c.width()); + fDstX[2] = fDstX[1]; + } + + if (fDstY[1] > fDstY[2]) { + fDstY[1] = fDstY[0] + (fDstY[3] - fDstY[0]) * c.fTop / (h - c.height()); + fDstY[2] = fDstY[1]; + } + + fCurrX = fCurrY = 0; + fDone = false; +} + +bool SkLatticeIter::next(SkRect* src, SkRect* dst) { + if (fDone) { + return false; + } + + const int x = fCurrX; + const int y = fCurrY; + SkASSERT(x >= 0 && x < fSrcX.count() - 1); + SkASSERT(y >= 0 && y < fSrcY.count() - 1); + + src->set(fSrcX[x], fSrcY[y], fSrcX[x + 1], fSrcY[y + 1]); + dst->set(fDstX[x], fDstY[y], fDstX[x + 1], fDstY[y + 1]); + if (fSrcX.count() - 1 == ++fCurrX) { + fCurrX = 0; + fCurrY += 1; + if (fCurrY >= fSrcY.count() - 1) { + fDone = true; + } + } + return true; +} diff --git a/src/core/SkLatticeIter.h b/src/core/SkLatticeIter.h new file mode 100644 index 0000000000..e2d767f1c8 --- /dev/null +++ b/src/core/SkLatticeIter.h @@ -0,0 +1,49 @@ +/* + * 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 SkLatticeIter_DEFINED +#define SkLatticeIter_DEFINED + +#include "SkCanvas.h" +#include "SkScalar.h" +#include "SkTArray.h" + +struct SkIRect; +struct SkRect; + +/** + * Disect a lattice request into an sequence of src-rect / dst-rect pairs + */ +class SkLatticeIter { +public: + + static bool Valid(int imageWidth, int imageHeight, const SkCanvas::Lattice& lattice); + + SkLatticeIter(int imageWidth, int imageHeight, const SkCanvas::Lattice& lattice, + const SkRect& dst); + + static bool Valid(int imageWidth, int imageHeight, const SkIRect& center); + + SkLatticeIter(int imageWidth, int imageHeight, const SkIRect& center, const SkRect& dst); + + /** + * While it returns true, use src/dst to draw the image/bitmap + */ + bool next(SkRect* src, SkRect* dst); + +private: + SkTArray<SkScalar> fSrcX; + SkTArray<SkScalar> fSrcY; + SkTArray<SkScalar> fDstX; + SkTArray<SkScalar> fDstY; + + int fCurrX; + int fCurrY; + bool fDone; +}; + +#endif diff --git a/src/core/SkNinePatchIter.cpp b/src/core/SkNinePatchIter.cpp deleted file mode 100644 index 1a780a0c0d..0000000000 --- a/src/core/SkNinePatchIter.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 "SkNinePatchIter.h" -#include "SkRect.h" - -bool SkNinePatchIter::Valid(int width, int height, const SkIRect& center) { - return !center.isEmpty() && SkIRect::MakeWH(width, height).contains(center); -} - -SkNinePatchIter::SkNinePatchIter(int w, int h, const SkIRect& c, const SkRect& dst) { - SkASSERT(SkIRect::MakeWH(w, h).contains(c)); - - fSrcX[0] = 0; - fSrcX[1] = SkIntToScalar(c.fLeft); - fSrcX[2] = SkIntToScalar(c.fRight); - fSrcX[3] = SkIntToScalar(w); - - fSrcY[0] = 0; - fSrcY[1] = SkIntToScalar(c.fTop); - fSrcY[2] = SkIntToScalar(c.fBottom); - fSrcY[3] = SkIntToScalar(h); - - fDstX[0] = dst.fLeft; - fDstX[1] = dst.fLeft + SkIntToScalar(c.fLeft); - fDstX[2] = dst.fRight - SkIntToScalar(w - c.fRight); - fDstX[3] = dst.fRight; - - fDstY[0] = dst.fTop; - fDstY[1] = dst.fTop + SkIntToScalar(c.fTop); - fDstY[2] = dst.fBottom - SkIntToScalar(h - c.fBottom); - fDstY[3] = dst.fBottom; - - if (fDstX[1] > fDstX[2]) { - fDstX[1] = fDstX[0] + (fDstX[3] - fDstX[0]) * c.fLeft / (w - c.width()); - fDstX[2] = fDstX[1]; - } - - if (fDstY[1] > fDstY[2]) { - fDstY[1] = fDstY[0] + (fDstY[3] - fDstY[0]) * c.fTop / (h - c.height()); - fDstY[2] = fDstY[1]; - } - - fCurrX = fCurrY = 0; - fDone = false; -} - -bool SkNinePatchIter::next(SkRect* src, SkRect* dst) { - if (fDone) { - return false; - } - - const int x = fCurrX; - const int y = fCurrY; - SkASSERT(x >= 0 && x < 3); - SkASSERT(y >= 0 && y < 3); - - src->set(fSrcX[x], fSrcY[y], fSrcX[x + 1], fSrcY[y + 1]); - dst->set(fDstX[x], fDstY[y], fDstX[x + 1], fDstY[y + 1]); - if (3 == ++fCurrX) { - fCurrX = 0; - fCurrY += 1; - if (fCurrY >= 3) { - fDone = true; - } - } - return true; -} diff --git a/src/core/SkNinePatchIter.h b/src/core/SkNinePatchIter.h deleted file mode 100644 index df06c1f630..0000000000 --- a/src/core/SkNinePatchIter.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 SkNinePatchIter_DEFINED -#define SkNinePatchIter_DEFINED - -#include "SkScalar.h" - -struct SkIRect; -struct SkRect; - -/** - * Disect a ninepatch request into an sequence of src-rect / dst-rect pairs - */ -class SkNinePatchIter { -public: - static bool Valid(int imageWidth, int imageHeight, const SkIRect& center); - - SkNinePatchIter(int imageWidth, int imageHeight, const SkIRect& center, const SkRect& dst); - - /** - * While it returns true, use src/dst to draw the image/bitmap - */ - bool next(SkRect* src, SkRect* dst); - -private: - SkScalar fSrcX[4]; - SkScalar fSrcY[4]; - SkScalar fDstX[4]; - SkScalar fDstY[4]; - - int fCurrX; - int fCurrY; - bool fDone; -}; - -#endif diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h index 17b76817fe..c704f29e58 100644 --- a/src/core/SkPictureFlat.h +++ b/src/core/SkPictureFlat.h @@ -91,8 +91,9 @@ enum DrawType { TRANSLATE_Z, DRAW_SHADOWED_PICTURE_LIGHTS, + DRAW_IMAGE_LATTICE, - LAST_DRAWTYPE_ENUM = DRAW_SHADOWED_PICTURE_LIGHTS + LAST_DRAWTYPE_ENUM = DRAW_IMAGE_LATTICE }; // In the 'match' method, this constant will match any flavor of DRAW_BITMAP* diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp index 0bce09cdf2..307e946871 100644 --- a/src/core/SkPicturePlayback.cpp +++ b/src/core/SkPicturePlayback.cpp @@ -298,6 +298,18 @@ void SkPicturePlayback::handleOp(SkReadBuffer* reader, reader->readPoint(&loc); canvas->drawImage(image, loc.fX, loc.fY, paint); } break; + case DRAW_IMAGE_LATTICE: { + const SkPaint* paint = fPictureData->getPaint(reader); + const SkImage* image = fPictureData->getImage(reader); + SkCanvas::Lattice lattice; + lattice.fXCount = reader->readInt(); + lattice.fXDivs = (const int*) reader->skip(lattice.fXCount * sizeof(int32_t)); + lattice.fYCount = reader->readInt(); + lattice.fYDivs = (const int*) reader->skip(lattice.fYCount * sizeof(int32_t)); + SkRect dst; + reader->readRect(&dst); + canvas->drawImageLattice(image, lattice, dst, paint); + } break; case DRAW_IMAGE_NINE: { const SkPaint* paint = fPictureData->getPaint(reader); const SkImage* image = fPictureData->getImage(reader); diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp index 562f056c50..d85376a118 100644 --- a/src/core/SkPictureRecord.cpp +++ b/src/core/SkPictureRecord.cpp @@ -510,6 +510,24 @@ void SkPictureRecord::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, this->validate(initialOffset, size); } +void SkPictureRecord::onDrawImageLattice(const SkImage* image, const Lattice& lattice, + const SkRect& dst, const SkPaint* paint) { + // xCount + xDivs + yCount+ yDivs + size_t latticeSize = (1 + lattice.fXCount + 1 + lattice.fYCount) * kUInt32Size; + + // op + paint index + image index + lattice + dst rect + size_t size = 3 * kUInt32Size + latticeSize + sizeof(dst); + size_t initialOffset = this->addDraw(DRAW_IMAGE_LATTICE, &size); + this->addPaintPtr(paint); + this->addImage(image); + this->addInt(lattice.fXCount); + fWriter.writePad(lattice.fXDivs, lattice.fXCount * kUInt32Size); + this->addInt(lattice.fYCount); + fWriter.writePad(lattice.fYDivs, lattice.fYCount * kUInt32Size); + this->addRect(dst); + this->validate(initialOffset, size); +} + void SkPictureRecord::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, const SkPaint* paint, SrcRectConstraint constraint) { // id + paint_index + image_index + bool_for_src + constraint diff --git a/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h index b798234a72..637c46dbf8 100644 --- a/src/core/SkPictureRecord.h +++ b/src/core/SkPictureRecord.h @@ -194,6 +194,8 @@ protected: void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst, const SkPaint*, SrcRectConstraint) override; void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) override; + void onDrawImageLattice(const SkImage*, const SkCanvas::Lattice& lattice, const SkRect& dst, + const SkPaint*) override; void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst, const SkPaint*, SrcRectConstraint) override; void onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst, diff --git a/src/core/SkRecordDraw.cpp b/src/core/SkRecordDraw.cpp index 8897e5fcf3..dcfc0fbf90 100644 --- a/src/core/SkRecordDraw.cpp +++ b/src/core/SkRecordDraw.cpp @@ -97,6 +97,16 @@ template <> void Draw::draw(const TranslateZ& r) { } DRAW(DrawDRRect, drawDRRect(r.outer, r.inner, r.paint)); DRAW(DrawImage, drawImage(r.image.get(), r.left, r.top, r.paint)); + +template <> void Draw::draw(const DrawImageLattice& r) { + SkCanvas::Lattice lattice; + lattice.fXCount = r.xCount; + lattice.fXDivs = r.xDivs; + lattice.fYCount = r.yCount; + lattice.fYDivs = r.yDivs; + fCanvas->drawImageLattice(r.image.get(), lattice, r.dst, r.paint); +} + DRAW(DrawImageRect, legacy_drawImageRect(r.image.get(), r.src, r.dst, r.paint, r.constraint)); DRAW(DrawImageNine, drawImageNine(r.image.get(), r.center, r.dst, r.paint)); DRAW(DrawOval, drawOval(r.oval, r.paint)); @@ -412,6 +422,9 @@ private: return this->adjustAndMap(rect, op.paint); } + Bounds bounds(const DrawImageLattice& op) const { + return this->adjustAndMap(op.dst, op.paint); + } Bounds bounds(const DrawImageRect& op) const { return this->adjustAndMap(op.dst, op.paint); } diff --git a/src/core/SkRecorder.cpp b/src/core/SkRecorder.cpp index c822fdba87..7ffb1f4c44 100644 --- a/src/core/SkRecorder.cpp +++ b/src/core/SkRecorder.cpp @@ -211,6 +211,16 @@ void SkRecorder::onDrawImage(const SkImage* image, SkScalar left, SkScalar top, APPEND(DrawImage, this->copy(paint), sk_ref_sp(image), left, top); } +void SkRecorder::onDrawImageLattice(const SkImage* image, + const Lattice& lattice, + const SkRect& dst, + const SkPaint* paint) { + APPEND(DrawImageLattice, this->copy(paint), sk_ref_sp(image), + lattice.fXCount, this->copy(lattice.fXDivs, lattice.fXCount), + lattice.fYCount, this->copy(lattice.fYDivs, lattice.fYCount), dst); +} + + void SkRecorder::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, const SkPaint* paint, SrcRectConstraint constraint) { APPEND(DrawImageRect, this->copy(paint), sk_ref_sp(image), this->copy(src), dst, constraint); diff --git a/src/core/SkRecorder.h b/src/core/SkRecorder.h index 5282740c0c..6da3824123 100644 --- a/src/core/SkRecorder.h +++ b/src/core/SkRecorder.h @@ -111,6 +111,8 @@ public: void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst, const SkPaint*, SrcRectConstraint) override; void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) override; + void onDrawImageLattice(const SkImage*, const Lattice& lattice, const SkRect& dst, + const SkPaint*) override; void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst, const SkPaint*, SrcRectConstraint) override; void onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst, diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index de814ab883..7271819269 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -24,8 +24,8 @@ #include "SkImageCacherator.h" #include "SkImageFilter.h" #include "SkImageFilterCache.h" +#include "SkLatticeIter.h" #include "SkMaskFilter.h" -#include "SkNinePatchIter.h" #include "SkPathEffect.h" #include "SkPicture.h" #include "SkPictureData.h" @@ -1446,7 +1446,7 @@ void SkGpuDevice::drawProducerNine(const SkDraw& draw, GrTextureProducer* produc GrSkFilterQualityToGrFilterMode(paint.getFilterQuality(), *draw.fMatrix, SkMatrix::I(), &doBicubic); if (useFallback || doBicubic || GrTextureParams::kNone_FilterMode != textureFilterMode) { - SkNinePatchIter iter(producer->width(), producer->height(), center, dst); + SkLatticeIter iter(producer->width(), producer->height(), center, dst); SkRect srcR, dstR; while (iter.next(&srcR, &dstR)) { diff --git a/src/gpu/batches/GrNinePatch.cpp b/src/gpu/batches/GrNinePatch.cpp index 62af20b2cf..cde3d266ca 100644 --- a/src/gpu/batches/GrNinePatch.cpp +++ b/src/gpu/batches/GrNinePatch.cpp @@ -12,7 +12,7 @@ #include "GrResourceProvider.h" #include "GrVertexBatch.h" #include "SkBitmap.h" -#include "SkNinePatchIter.h" +#include "SkLatticeIter.h" #include "SkRect.h" static sk_sp<GrGeometryProcessor> create_gp(bool readsCoverage) { @@ -101,7 +101,7 @@ private: i * kRectsPerInstance * kVertsPerRect * vertexStride; const Patch& patch = fPatches[i]; - SkNinePatchIter iter(fImageWidth, fImageHeight, patch.fCenter, patch.fDst); + SkLatticeIter iter(fImageWidth, fImageHeight, patch.fCenter, patch.fDst); SkRect srcR, dstR; while (iter.next(&srcR, &dstR)) { diff --git a/src/pdf/SkPDFCanvas.cpp b/src/pdf/SkPDFCanvas.cpp index f3ba66b57a..1d3138541d 100644 --- a/src/pdf/SkPDFCanvas.cpp +++ b/src/pdf/SkPDFCanvas.cpp @@ -5,7 +5,7 @@ * found in the LICENSE file. */ -#include "SkNinePatchIter.h" +#include "SkLatticeIter.h" #include "SkPDFCanvas.h" #include "SkPDFDevice.h" @@ -35,7 +35,7 @@ void SkPDFCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst, const SkPaint* paint) { - SkNinePatchIter iter(bitmap.width(), bitmap.height(), center, dst); + SkLatticeIter iter(bitmap.width(), bitmap.height(), center, dst); SkRect srcR, dstR; while (iter.next(&srcR, &dstR)) { this->drawBitmapRect(bitmap, srcR, dstR, paint); @@ -46,7 +46,7 @@ void SkPDFCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst, const SkPaint* paint) { - SkNinePatchIter iter(image->width(), image->height(), center, dst); + SkLatticeIter iter(image->width(), image->height(), center, dst); SkRect srcR, dstR; while (iter.next(&srcR, &dstR)) { this->drawImageRect(image, srcR, dstR, paint); @@ -84,3 +84,14 @@ void SkPDFCanvas::onDrawBitmapRect(const SkBitmap& bitmap, SkMatrix::kFill_ScaleToFit)); this->drawBitmap(bitmap, 0, 0, paint); } + +void SkPDFCanvas::onDrawImageLattice(const SkImage* image, + const Lattice& lattice, + const SkRect& dst, + const SkPaint* paint) { + SkLatticeIter iter(image->width(), image->height(), lattice, dst); + SkRect srcR, dstR; + while (iter.next(&srcR, &dstR)) { + this->drawImageRect(image, srcR, dstR, paint); + } +} diff --git a/src/pdf/SkPDFCanvas.h b/src/pdf/SkPDFCanvas.h index 1c3ec715b7..76bb7e13e3 100644 --- a/src/pdf/SkPDFCanvas.h +++ b/src/pdf/SkPDFCanvas.h @@ -39,6 +39,11 @@ protected: const SkPaint*, SkCanvas::SrcRectConstraint) override; + void onDrawImageLattice(const SkImage*, + const Lattice& lattice, + const SkRect& dst, + const SkPaint*) override; + private: typedef SkCanvas INHERITED; }; diff --git a/tests/PictureTest.cpp b/tests/PictureTest.cpp index b02491661e..8f5ebd3f3b 100644 --- a/tests/PictureTest.cpp +++ b/tests/PictureTest.cpp @@ -994,12 +994,17 @@ static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) { const SkPaint paint; const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f }; const SkIRect irect = { 2, 2, 3, 3 }; + int divs[] = { 2, 3 }; + SkCanvas::Lattice lattice; + lattice.fXCount = lattice.fYCount = 2; + lattice.fXDivs = lattice.fYDivs = divs; // Don't care what these record, as long as they're legal. canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint); canvas->drawBitmapRect(bitmap, rect, rect, &paint, SkCanvas::kStrict_SrcRectConstraint); canvas->drawBitmapNine(bitmap, irect, rect, &paint); canvas->drawBitmap(bitmap, 1, 1); // drawSprite + canvas->drawBitmapLattice(bitmap, lattice, rect, &paint); } static void test_draw_bitmaps(SkCanvas* canvas) { |