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
This commit is contained in:
msarett 2016-08-02 08:05:56 -07:00 committed by Commit bot
parent 7d0e3bc785
commit c573a40ed5
24 changed files with 688 additions and 131 deletions

View File

@ -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");)

147
gm/lattice.cpp Normal file
View File

@ -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; )

View File

@ -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',

View File

@ -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,

View File

@ -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.

View File

@ -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;

View File

@ -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()");

View File

@ -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)) {

230
src/core/SkLatticeIter.cpp Normal file
View File

@ -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;
}

49
src/core/SkLatticeIter.h Normal file
View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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*

View File

@ -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);

View File

@ -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

View File

@ -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,

View File

@ -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);
}

View File

@ -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);

View File

@ -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,

View File

@ -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)) {

View File

@ -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)) {

View File

@ -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);
}
}

View File

@ -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;
};

View File

@ -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) {