Pull in Chromium's version of GatherPixelRefs

https://codereview.chromium.org/134473002/



git-svn-id: http://skia.googlecode.com/svn/trunk@13038 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
robertphillips@google.com 2014-01-13 13:33:26 +00:00
parent 15ac322aae
commit 56bf6e4bce
6 changed files with 666 additions and 80 deletions

View File

@ -42,6 +42,7 @@
'../include/utils/SkDebugUtils.h', '../include/utils/SkDebugUtils.h',
'../include/utils/SkDeferredCanvas.h', '../include/utils/SkDeferredCanvas.h',
'../include/utils/SkDumpCanvas.h', '../include/utils/SkDumpCanvas.h',
'../include/utils/SkGatherPixelRefsAndRects.h',
'../include/utils/SkInterpolator.h', '../include/utils/SkInterpolator.h',
'../include/utils/SkLayer.h', '../include/utils/SkLayer.h',
'../include/utils/SkMatrix44.h', '../include/utils/SkMatrix44.h',
@ -76,6 +77,7 @@
'../src/utils/SkDeferredCanvas.cpp', '../src/utils/SkDeferredCanvas.cpp',
'../src/utils/SkDumpCanvas.cpp', '../src/utils/SkDumpCanvas.cpp',
'../src/utils/SkFloatUtils.h', '../src/utils/SkFloatUtils.h',
'../src/utils/SkGatherPixelRefsAndRects.cpp',
'../src/utils/SkInterpolator.cpp', '../src/utils/SkInterpolator.cpp',
'../src/utils/SkLayer.cpp', '../src/utils/SkLayer.cpp',
'../src/utils/SkMatrix44.cpp', '../src/utils/SkMatrix44.cpp',

View File

@ -9,6 +9,7 @@
#define SkPictureUtils_DEFINED #define SkPictureUtils_DEFINED
#include "SkPicture.h" #include "SkPicture.h"
#include "SkTDArray.h"
class SkData; class SkData;
struct SkRect; struct SkRect;
@ -26,6 +27,57 @@ public:
* and remains unchanged. * and remains unchanged.
*/ */
static SkData* GatherPixelRefs(SkPicture* pict, const SkRect& area); static SkData* GatherPixelRefs(SkPicture* pict, const SkRect& area);
/**
* SkPixelRefContainer provides a base class for more elaborate pixel ref
* query structures (e.g., rtrees, quad-trees, etc.)
*/
class SkPixelRefContainer : public SkRefCnt {
public:
virtual void add(SkPixelRef* pr, const SkRect& rect) = 0;
// The returned array may contain duplicates
virtual void query(const SkRect& queryRect, SkTDArray<SkPixelRef*> *result) = 0;
private:
typedef SkRefCnt INHERITED;
};
// Simple query structure that just stores a linked list of pixel refs
// and rects.
class SkPixelRefsAndRectsList : public SkPixelRefContainer {
public:
virtual void add(SkPixelRef* pr, const SkRect& rect) SK_OVERRIDE {
PixelRefAndRect *dst = fArray.append();
dst->fPixelRef = pr;
dst->fRect = rect;
}
virtual void query(const SkRect& queryRect, SkTDArray<SkPixelRef*> *result) SK_OVERRIDE {
for (int i = 0; i < fArray.count(); ++i) {
if (SkRect::Intersects(fArray[i].fRect, queryRect)) {
*result->append() = fArray[i].fPixelRef;
}
}
}
private:
struct PixelRefAndRect {
SkPixelRef* fPixelRef;
SkRect fRect;
};
SkTDArray<PixelRefAndRect> fArray;
typedef SkPixelRefContainer INHERITED;
};
/**
* Fill the provided pixel ref container with the picture's pixel ref
* and rect information.
*/
static void GatherPixelRefsAndRects(SkPicture* pict, SkPixelRefContainer* prCont);
}; };
#endif #endif

View File

@ -0,0 +1,25 @@
/*
* 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 "SkGatherPixelRefsAndRects.h"
#include "SkNoSaveLayerCanvas.h"
#include "SkPictureUtils.h"
void SkPictureUtils::GatherPixelRefsAndRects(SkPicture* pict,
SkPictureUtils::SkPixelRefContainer* prCont) {
if (0 == pict->width() || 0 == pict->height()) {
return ;
}
SkGatherPixelRefsAndRectsDevice device(pict->width(), pict->height(), prCont);
SkNoSaveLayerCanvas canvas(&device);
canvas.clipRect(SkRect::MakeWH(SkIntToScalar(pict->width()),
SkIntToScalar(pict->height())),
SkRegion::kIntersect_Op, false);
canvas.drawPicture(*pict);
}

View File

@ -0,0 +1,353 @@
/*
* 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 SkGatherPixelRefsAndRects_DEFINED
#define SkGatherPixelRefsAndRects_DEFINED
#include "SkBitmap.h"
#include "SkDevice.h"
#include "SkDraw.h"
#include "SkPictureUtils.h"
#include "SkRasterClip.h"
#include "SkRefCnt.h"
#include "SkRRect.h"
#include "SkTypes.h"
// This GatherPixelRefs device passes all discovered pixel refs and their
// device bounds to the user provided SkPixelRefContainer-derived object
class SkGatherPixelRefsAndRectsDevice : public SkBaseDevice {
public:
SK_DECLARE_INST_COUNT(SkGatherPixelRefsAndRectsDevice)
SkGatherPixelRefsAndRectsDevice(int width, int height,
SkPictureUtils::SkPixelRefContainer* prCont) {
fSize.set(width, height);
fPRCont = prCont;
SkSafeRef(fPRCont);
fEmptyBitmap.setConfig(SkBitmap::kNo_Config, width, height);
}
virtual ~SkGatherPixelRefsAndRectsDevice() {
SkSafeUnref(fPRCont);
}
virtual uint32_t getDeviceCapabilities() SK_OVERRIDE { return 0; }
virtual int width() const SK_OVERRIDE { return fSize.width(); }
virtual int height() const SK_OVERRIDE { return fSize.height(); }
virtual bool isOpaque() const SK_OVERRIDE { return false; }
virtual SkBitmap::Config config() const SK_OVERRIDE {
return SkBitmap::kNo_Config;
}
virtual void writePixels(const SkBitmap& bitmap, int x, int y,
SkCanvas::Config8888 config8888) SK_OVERRIDE {
NotSupported();
}
virtual GrRenderTarget* accessRenderTarget() SK_OVERRIDE { return NULL; }
protected:
virtual bool filterTextFlags(const SkPaint& paint, TextFlags*) SK_OVERRIDE {
return false;
}
virtual void clear(SkColor color) SK_OVERRIDE {
NothingToDo();
}
virtual void drawPaint(const SkDraw& draw, const SkPaint& paint) SK_OVERRIDE {
SkBitmap bm;
if (GetBitmapFromPaint(paint, &bm)) {
SkRect clipRect = SkRect::Make(draw.fRC->getBounds());
fPRCont->add(bm.pixelRef(), clipRect);
}
}
virtual void drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count,
const SkPoint points[], const SkPaint& paint) SK_OVERRIDE {
SkBitmap bm;
if (!GetBitmapFromPaint(paint, &bm)) {
return;
}
if (0 == count) {
return;
}
SkPoint min = points[0];
SkPoint max = points[0];
for (size_t i = 1; i < count; ++i) {
const SkPoint& point = points[i];
min.set(SkMinScalar(min.x(), point.x()), SkMinScalar(min.y(), point.y()));
max.set(SkMaxScalar(max.x(), point.x()), SkMaxScalar(max.y(), point.y()));
}
SkRect bounds = SkRect::MakeLTRB(min.x(), min.y(), max.x()+1, max.y()+1);
this->drawRect(draw, bounds, paint);
}
virtual void drawRect(const SkDraw& draw, const SkRect& rect,
const SkPaint& paint) SK_OVERRIDE {
SkBitmap bm;
if (GetBitmapFromPaint(paint, &bm)) {
SkRect mappedRect;
draw.fMatrix->mapRect(&mappedRect, rect);
SkRect clipRect = SkRect::Make(draw.fRC->getBounds());
mappedRect.intersect(clipRect);
fPRCont->add(bm.pixelRef(), mappedRect);
}
}
virtual void drawOval(const SkDraw& draw, const SkRect& rect,
const SkPaint& paint) SK_OVERRIDE {
this->drawRect(draw, rect, paint);
}
virtual void drawRRect(const SkDraw& draw, const SkRRect& rrect,
const SkPaint& paint) SK_OVERRIDE {
this->drawRect(draw, rrect.rect(), paint);
}
virtual void drawPath(const SkDraw& draw, const SkPath& path,
const SkPaint& paint, const SkMatrix* prePathMatrix,
bool pathIsMutable) SK_OVERRIDE {
SkBitmap bm;
if (!GetBitmapFromPaint(paint, &bm)) {
return;
}
SkRect pathBounds = path.getBounds();
if (NULL != prePathMatrix) {
prePathMatrix->mapRect(&pathBounds);
}
this->drawRect(draw, pathBounds, paint);
}
virtual void drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
const SkMatrix& matrix, const SkPaint& paint) SK_OVERRIDE {
SkMatrix totMatrix;
totMatrix.setConcat(*draw.fMatrix, matrix);
SkRect bitmapRect = SkRect::MakeWH(SkIntToScalar(bitmap.width()),
SkIntToScalar(bitmap.height()));
SkRect mappedRect;
totMatrix.mapRect(&mappedRect, bitmapRect);
fPRCont->add(bitmap.pixelRef(), mappedRect);
SkBitmap paintBitmap;
if (GetBitmapFromPaint(paint, &paintBitmap)) {
fPRCont->add(paintBitmap.pixelRef(), mappedRect);
}
}
virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
int x, int y, const SkPaint& paint) SK_OVERRIDE {
// Sprites aren't affected by current matrix, so we can't reuse drawRect.
SkMatrix matrix;
matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
SkRect bitmapRect = SkRect::MakeWH(SkIntToScalar(bitmap.width()),
SkIntToScalar(bitmap.height()));
SkRect mappedRect;
matrix.mapRect(&mappedRect, bitmapRect);
fPRCont->add(bitmap.pixelRef(), mappedRect);
SkBitmap paintBitmap;
if (GetBitmapFromPaint(paint, &paintBitmap)) {
fPRCont->add(paintBitmap.pixelRef(), mappedRect);
}
}
virtual void drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
const SkRect* srcOrNull, const SkRect& dst,
const SkPaint& paint,
SkCanvas::DrawBitmapRectFlags flags) SK_OVERRIDE {
SkRect bitmapRect = SkRect::MakeWH(SkIntToScalar(bitmap.width()),
SkIntToScalar(bitmap.height()));
SkMatrix matrix;
matrix.setRectToRect(bitmapRect, dst, SkMatrix::kFill_ScaleToFit);
this->drawBitmap(draw, bitmap, matrix, paint);
}
virtual void drawText(const SkDraw& draw, const void* text, size_t len,
SkScalar x, SkScalar y,
const SkPaint& paint) SK_OVERRIDE {
SkBitmap bitmap;
if (!GetBitmapFromPaint(paint, &bitmap)) {
return;
}
// Math is borrowed from SkBBoxRecord
SkRect bounds;
paint.measureText(text, len, &bounds);
SkPaint::FontMetrics metrics;
paint.getFontMetrics(&metrics);
if (paint.isVerticalText()) {
SkScalar h = bounds.fBottom - bounds.fTop;
if (paint.getTextAlign() == SkPaint::kCenter_Align) {
bounds.fTop -= h / 2;
bounds.fBottom -= h / 2;
}
bounds.fBottom += metrics.fBottom;
bounds.fTop += metrics.fTop;
} else {
SkScalar w = bounds.fRight - bounds.fLeft;
if (paint.getTextAlign() == SkPaint::kCenter_Align) {
bounds.fLeft -= w / 2;
bounds.fRight -= w / 2;
} else if (paint.getTextAlign() == SkPaint::kRight_Align) {
bounds.fLeft -= w;
bounds.fRight -= w;
}
bounds.fTop = metrics.fTop;
bounds.fBottom = metrics.fBottom;
}
SkScalar pad = (metrics.fBottom - metrics.fTop) / 2;
bounds.fLeft -= pad;
bounds.fRight += pad;
bounds.offset(x, y);
this->drawRect(draw, bounds, paint);
}
virtual void drawPosText(const SkDraw& draw, const void* text, size_t len,
const SkScalar pos[], SkScalar constY,
int scalarsPerPos, const SkPaint& paint) SK_OVERRIDE {
SkBitmap bitmap;
if (!GetBitmapFromPaint(paint, &bitmap)) {
return;
}
if (0 == len) {
return;
}
// Similar to SkDraw asserts.
SkASSERT(scalarsPerPos == 1 || scalarsPerPos == 2);
SkPoint min, max;
if (1 == scalarsPerPos) {
min.set(pos[0], constY);
max.set(pos[0], constY);
} else if (2 == scalarsPerPos) {
min.set(pos[0], constY + pos[1]);
max.set(pos[0], constY + pos[1]);
}
for (size_t i = 1; i < len; ++i) {
SkScalar x = pos[i * scalarsPerPos];
SkScalar y = constY;
if (2 == scalarsPerPos) {
y += pos[i * scalarsPerPos + 1];
}
min.set(SkMinScalar(x, min.x()), SkMinScalar(y, min.y()));
max.set(SkMaxScalar(x, max.x()), SkMaxScalar(y, max.y()));
}
SkRect bounds = SkRect::MakeLTRB(min.x(), min.y(), max.x(), max.y());
// Math is borrowed from SkBBoxRecord
SkPaint::FontMetrics metrics;
paint.getFontMetrics(&metrics);
bounds.fTop += metrics.fTop;
bounds.fBottom += metrics.fBottom;
SkScalar pad = (metrics.fTop - metrics.fBottom) / 2;
bounds.fLeft -= pad;
bounds.fRight += pad;
this->drawRect(draw, bounds, paint);
}
virtual void drawTextOnPath(const SkDraw& draw, const void* text, size_t len,
const SkPath& path, const SkMatrix* matrix,
const SkPaint& paint) SK_OVERRIDE {
SkBitmap bitmap;
if (!GetBitmapFromPaint(paint, &bitmap)) {
return;
}
// Math is borrowed from SkBBoxRecord
SkRect bounds = path.getBounds();
SkPaint::FontMetrics metrics;
paint.getFontMetrics(&metrics);
SkScalar pad = metrics.fTop;
// TODO: inset?!
bounds.fLeft += pad;
bounds.fRight -= pad;
bounds.fTop += pad;
bounds.fBottom -= pad;
this->drawRect(draw, bounds, paint);
}
virtual void drawVertices(const SkDraw& draw, SkCanvas::VertexMode, int vertexCount,
const SkPoint verts[], const SkPoint texs[],
const SkColor colors[], SkXfermode* xmode,
const uint16_t indices[], int indexCount,
const SkPaint& paint) SK_OVERRIDE {
this->drawPoints(draw, SkCanvas::kPolygon_PointMode, vertexCount, verts, paint);
}
virtual void drawDevice(const SkDraw&, SkBaseDevice*, int x, int y,
const SkPaint&) SK_OVERRIDE {
NothingToDo();
}
// TODO: allow this call to return failure, or move to SkBitmapDevice only.
virtual const SkBitmap& onAccessBitmap() SK_OVERRIDE {
return fEmptyBitmap;
}
virtual bool onReadPixels(const SkBitmap& bitmap,
int x, int y,
SkCanvas::Config8888 config8888) SK_OVERRIDE {
NotSupported();
return false;
}
virtual void lockPixels() SK_OVERRIDE { NothingToDo(); }
virtual void unlockPixels() SK_OVERRIDE { NothingToDo(); }
virtual bool allowImageFilter(SkImageFilter*) SK_OVERRIDE { return false; }
virtual bool canHandleImageFilter(SkImageFilter*) SK_OVERRIDE { return false; }
virtual bool filterImage(SkImageFilter*, const SkBitmap&, const SkMatrix&,
SkBitmap* result, SkIPoint* offset) SK_OVERRIDE {
return false;
}
private:
SkPictureUtils::SkPixelRefContainer* fPRCont;
SkISize fSize;
SkBitmap fEmptyBitmap; // legacy -- need to remove
static bool GetBitmapFromPaint(const SkPaint &paint, SkBitmap* bitmap) {
SkShader* shader = paint.getShader();
if (NULL != shader) {
if (SkShader::kNone_GradientType == shader->asAGradient(NULL)) {
return SkShader::kNone_BitmapType != shader->asABitmap(bitmap, NULL, NULL);
}
}
return false;
}
virtual void replaceBitmapBackendForRasterSurface(const SkBitmap&) SK_OVERRIDE {
NotSupported();
}
virtual SkBaseDevice* onCreateCompatibleDevice(SkBitmap::Config config,
int width, int height,
bool isOpaque,
Usage usage) SK_OVERRIDE {
// we expect to only get called via savelayer, in which case it is fine.
SkASSERT(kSaveLayer_Usage == usage);
return SkNEW_ARGS(SkGatherPixelRefsAndRectsDevice, (width, height, fPRCont));
}
virtual void flush() SK_OVERRIDE {}
static void NotSupported() {
SkDEBUGFAIL("this method should never be called");
}
static void NothingToDo() {}
typedef SkBaseDevice INHERITED;
};
#endif // SkGatherPixelRefsAndRects_DEFINED

View File

@ -50,6 +50,8 @@ static void nothing_to_do() {}
*/ */
class GatherPixelRefDevice : public SkBaseDevice { class GatherPixelRefDevice : public SkBaseDevice {
public: public:
SK_DECLARE_INST_COUNT(GatherPixelRefDevice)
GatherPixelRefDevice(int width, int height, PixelRefSet* prset) { GatherPixelRefDevice(int width, int height, PixelRefSet* prset) {
fSize.set(width, height); fSize.set(width, height);
fEmptyBitmap.setConfig(SkBitmap::kNo_Config, width, height); fEmptyBitmap.setConfig(SkBitmap::kNo_Config, width, height);

View File

@ -65,35 +65,42 @@ static void init_paint(SkPaint* paint, const SkBitmap &bm) {
paint->setShader(shader)->unref(); paint->setShader(shader)->unref();
} }
typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&, const SkBitmap&, const SkPoint&); typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&,
const SkBitmap&, const SkPoint&,
SkTDArray<SkPixelRef*>* usedPixRefs);
static void drawpaint_proc(SkCanvas* canvas, const SkBitmap& bm, static void drawpaint_proc(SkCanvas* canvas, const SkBitmap& bm,
const SkBitmap& altBM, const SkPoint& pos) { const SkBitmap& altBM, const SkPoint& pos,
SkTDArray<SkPixelRef*>* usedPixRefs) {
SkPaint paint; SkPaint paint;
init_paint(&paint, bm); init_paint(&paint, bm);
canvas->drawPaint(paint); canvas->drawPaint(paint);
*usedPixRefs->append() = bm.pixelRef();
} }
static void drawpoints_proc(SkCanvas* canvas, const SkBitmap& bm, static void drawpoints_proc(SkCanvas* canvas, const SkBitmap& bm,
const SkBitmap& altBM, const SkPoint& pos) { const SkBitmap& altBM, const SkPoint& pos,
SkTDArray<SkPixelRef*>* usedPixRefs) {
SkPaint paint; SkPaint paint;
init_paint(&paint, bm); init_paint(&paint, bm);
// draw a slightly inset rect // draw a rect
SkPoint points[5] = { SkPoint points[5] = {
{ pos.fX + 1, pos.fY + 1 }, { pos.fX, pos.fY },
{ pos.fX + bm.width() - 2, pos.fY + 1 }, { pos.fX + bm.width() - 1, pos.fY },
{ pos.fX + bm.width() - 2, pos.fY + bm.height() - 2 }, { pos.fX + bm.width() - 1, pos.fY + bm.height() - 1 },
{ pos.fX + 1, pos.fY + bm.height() - 2 }, { pos.fX, pos.fY + bm.height() - 1 },
{ pos.fX + 1, pos.fY + 1 }, { pos.fX, pos.fY },
}; };
canvas->drawPoints(SkCanvas::kPolygon_PointMode, 5, points, paint); canvas->drawPoints(SkCanvas::kPolygon_PointMode, 5, points, paint);
*usedPixRefs->append() = bm.pixelRef();
} }
static void drawrect_proc(SkCanvas* canvas, const SkBitmap& bm, static void drawrect_proc(SkCanvas* canvas, const SkBitmap& bm,
const SkBitmap& altBM, const SkPoint& pos) { const SkBitmap& altBM, const SkPoint& pos,
SkTDArray<SkPixelRef*>* usedPixRefs) {
SkPaint paint; SkPaint paint;
init_paint(&paint, bm); init_paint(&paint, bm);
@ -101,10 +108,12 @@ static void drawrect_proc(SkCanvas* canvas, const SkBitmap& bm,
r.offset(pos.fX, pos.fY); r.offset(pos.fX, pos.fY);
canvas->drawRect(r, paint); canvas->drawRect(r, paint);
*usedPixRefs->append() = bm.pixelRef();
} }
static void drawoval_proc(SkCanvas* canvas, const SkBitmap& bm, static void drawoval_proc(SkCanvas* canvas, const SkBitmap& bm,
const SkBitmap& altBM, const SkPoint& pos) { const SkBitmap& altBM, const SkPoint& pos,
SkTDArray<SkPixelRef*>* usedPixRefs) {
SkPaint paint; SkPaint paint;
init_paint(&paint, bm); init_paint(&paint, bm);
@ -112,10 +121,12 @@ static void drawoval_proc(SkCanvas* canvas, const SkBitmap& bm,
r.offset(pos.fX, pos.fY); r.offset(pos.fX, pos.fY);
canvas->drawOval(r, paint); canvas->drawOval(r, paint);
*usedPixRefs->append() = bm.pixelRef();
} }
static void drawrrect_proc(SkCanvas* canvas, const SkBitmap& bm, static void drawrrect_proc(SkCanvas* canvas, const SkBitmap& bm,
const SkBitmap& altBM, const SkPoint& pos) { const SkBitmap& altBM, const SkPoint& pos,
SkTDArray<SkPixelRef*>* usedPixRefs) {
SkPaint paint; SkPaint paint;
init_paint(&paint, bm); init_paint(&paint, bm);
@ -125,10 +136,12 @@ static void drawrrect_proc(SkCanvas* canvas, const SkBitmap& bm,
SkRRect rr; SkRRect rr;
rr.setRectXY(r, SkIntToScalar(bm.width())/4, SkIntToScalar(bm.height())/4); rr.setRectXY(r, SkIntToScalar(bm.width())/4, SkIntToScalar(bm.height())/4);
canvas->drawRRect(rr, paint); canvas->drawRRect(rr, paint);
*usedPixRefs->append() = bm.pixelRef();
} }
static void drawpath_proc(SkCanvas* canvas, const SkBitmap& bm, static void drawpath_proc(SkCanvas* canvas, const SkBitmap& bm,
const SkBitmap& altBM, const SkPoint& pos) { const SkBitmap& altBM, const SkPoint& pos,
SkTDArray<SkPixelRef*>* usedPixRefs) {
SkPaint paint; SkPaint paint;
init_paint(&paint, bm); init_paint(&paint, bm);
@ -139,37 +152,46 @@ static void drawpath_proc(SkCanvas* canvas, const SkBitmap& bm,
path.offset(pos.fX, pos.fY); path.offset(pos.fX, pos.fY);
canvas->drawPath(path, paint); canvas->drawPath(path, paint);
*usedPixRefs->append() = bm.pixelRef();
} }
static void drawbitmap_proc(SkCanvas* canvas, const SkBitmap& bm, static void drawbitmap_proc(SkCanvas* canvas, const SkBitmap& bm,
const SkBitmap& altBM, const SkPoint& pos) { const SkBitmap& altBM, const SkPoint& pos,
SkTDArray<SkPixelRef*>* usedPixRefs) {
canvas->drawBitmap(bm, pos.fX, pos.fY, NULL); canvas->drawBitmap(bm, pos.fX, pos.fY, NULL);
*usedPixRefs->append() = bm.pixelRef();
} }
static void drawbitmap_withshader_proc(SkCanvas* canvas, const SkBitmap& bm, static void drawbitmap_withshader_proc(SkCanvas* canvas, const SkBitmap& bm,
const SkBitmap& altBM, const SkPoint& pos) { const SkBitmap& altBM, const SkPoint& pos,
SkTDArray<SkPixelRef*>* usedPixRefs) {
SkPaint paint; SkPaint paint;
init_paint(&paint, bm); init_paint(&paint, bm);
// The bitmap in the paint is ignored unless we're drawing an A8 bitmap // The bitmap in the paint is ignored unless we're drawing an A8 bitmap
canvas->drawBitmap(altBM, pos.fX, pos.fY, &paint); canvas->drawBitmap(altBM, pos.fX, pos.fY, &paint);
*usedPixRefs->append() = bm.pixelRef();
*usedPixRefs->append() = altBM.pixelRef();
} }
static void drawsprite_proc(SkCanvas* canvas, const SkBitmap& bm, static void drawsprite_proc(SkCanvas* canvas, const SkBitmap& bm,
const SkBitmap& altBM, const SkPoint& pos) { const SkBitmap& altBM, const SkPoint& pos,
SkTDArray<SkPixelRef*>* usedPixRefs) {
const SkMatrix& ctm = canvas->getTotalMatrix(); const SkMatrix& ctm = canvas->getTotalMatrix();
SkPoint p(pos); SkPoint p(pos);
ctm.mapPoints(&p, 1); ctm.mapPoints(&p, 1);
canvas->drawSprite(bm, (int)p.fX, (int)p.fY, NULL); canvas->drawSprite(bm, (int)p.fX, (int)p.fY, NULL);
*usedPixRefs->append() = bm.pixelRef();
} }
#if 0 #if 0
// Although specifiable, this case doesn't seem to make sense (i.e., the // Although specifiable, this case doesn't seem to make sense (i.e., the
// bitmap in the shader is never used). // bitmap in the shader is never used).
static void drawsprite_withshader_proc(SkCanvas* canvas, const SkBitmap& bm, static void drawsprite_withshader_proc(SkCanvas* canvas, const SkBitmap& bm,
const SkBitmap& altBM, const SkPoint& pos) { const SkBitmap& altBM, const SkPoint& pos,
SkTDArray<SkPixelRef*>* usedPixRefs) {
SkPaint paint; SkPaint paint;
init_paint(&paint, bm); init_paint(&paint, bm);
@ -179,21 +201,26 @@ static void drawsprite_withshader_proc(SkCanvas* canvas, const SkBitmap& bm,
ctm.mapPoints(&p, 1); ctm.mapPoints(&p, 1);
canvas->drawSprite(altBM, (int)p.fX, (int)p.fY, &paint); canvas->drawSprite(altBM, (int)p.fX, (int)p.fY, &paint);
*usedPixRefs->append() = bm.pixelRef();
*usedPixRefs->append() = altBM.pixelRef();
} }
#endif #endif
static void drawbitmaprect_proc(SkCanvas* canvas, const SkBitmap& bm, static void drawbitmaprect_proc(SkCanvas* canvas, const SkBitmap& bm,
const SkBitmap& altBM, const SkPoint& pos) { const SkBitmap& altBM, const SkPoint& pos,
SkTDArray<SkPixelRef*>* usedPixRefs) {
SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }; SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) };
r.offset(pos.fX, pos.fY); r.offset(pos.fX, pos.fY);
canvas->drawBitmapRectToRect(bm, NULL, r, NULL); canvas->drawBitmapRectToRect(bm, NULL, r, NULL);
*usedPixRefs->append() = bm.pixelRef();
} }
static void drawbitmaprect_withshader_proc(SkCanvas* canvas, static void drawbitmaprect_withshader_proc(SkCanvas* canvas,
const SkBitmap& bm, const SkBitmap& bm,
const SkBitmap& altBM, const SkBitmap& altBM,
const SkPoint& pos) { const SkPoint& pos,
SkTDArray<SkPixelRef*>* usedPixRefs) {
SkPaint paint; SkPaint paint;
init_paint(&paint, bm); init_paint(&paint, bm);
@ -202,29 +229,36 @@ static void drawbitmaprect_withshader_proc(SkCanvas* canvas,
// The bitmap in the paint is ignored unless we're drawing an A8 bitmap // The bitmap in the paint is ignored unless we're drawing an A8 bitmap
canvas->drawBitmapRectToRect(altBM, NULL, r, &paint); canvas->drawBitmapRectToRect(altBM, NULL, r, &paint);
*usedPixRefs->append() = bm.pixelRef();
*usedPixRefs->append() = altBM.pixelRef();
} }
static void drawtext_proc(SkCanvas* canvas, const SkBitmap& bm, static void drawtext_proc(SkCanvas* canvas, const SkBitmap& bm,
const SkBitmap& altBM, const SkPoint& pos) { const SkBitmap& altBM, const SkPoint& pos,
SkTDArray<SkPixelRef*>* usedPixRefs) {
SkPaint paint; SkPaint paint;
init_paint(&paint, bm); init_paint(&paint, bm);
paint.setTextSize(SkIntToScalar(1.5*bm.width())); paint.setTextSize(SkIntToScalar(1.5*bm.width()));
canvas->drawText("0", 1, pos.fX, pos.fY+bm.width(), paint); canvas->drawText("0", 1, pos.fX, pos.fY+bm.width(), paint);
*usedPixRefs->append() = bm.pixelRef();
} }
static void drawpostext_proc(SkCanvas* canvas, const SkBitmap& bm, static void drawpostext_proc(SkCanvas* canvas, const SkBitmap& bm,
const SkBitmap& altBM, const SkPoint& pos) { const SkBitmap& altBM, const SkPoint& pos,
SkTDArray<SkPixelRef*>* usedPixRefs) {
SkPaint paint; SkPaint paint;
init_paint(&paint, bm); init_paint(&paint, bm);
paint.setTextSize(SkIntToScalar(1.5*bm.width())); paint.setTextSize(SkIntToScalar(1.5*bm.width()));
SkPoint point = { pos.fX, pos.fY + bm.height() }; SkPoint point = { pos.fX, pos.fY + bm.height() };
canvas->drawPosText("O", 1, &point, paint); canvas->drawPosText("O", 1, &point, paint);
*usedPixRefs->append() = bm.pixelRef();
} }
static void drawtextonpath_proc(SkCanvas* canvas, const SkBitmap& bm, static void drawtextonpath_proc(SkCanvas* canvas, const SkBitmap& bm,
const SkBitmap& altBM, const SkPoint& pos) { const SkBitmap& altBM, const SkPoint& pos,
SkTDArray<SkPixelRef*>* usedPixRefs) {
SkPaint paint; SkPaint paint;
init_paint(&paint, bm); init_paint(&paint, bm);
@ -235,18 +269,20 @@ static void drawtextonpath_proc(SkCanvas* canvas, const SkBitmap& bm,
path.offset(pos.fX, pos.fY+bm.height()); path.offset(pos.fX, pos.fY+bm.height());
canvas->drawTextOnPath("O", 1, path, NULL, paint); canvas->drawTextOnPath("O", 1, path, NULL, paint);
*usedPixRefs->append() = bm.pixelRef();
} }
static void drawverts_proc(SkCanvas* canvas, const SkBitmap& bm, static void drawverts_proc(SkCanvas* canvas, const SkBitmap& bm,
const SkBitmap& altBM, const SkPoint& pos) { const SkBitmap& altBM, const SkPoint& pos,
SkTDArray<SkPixelRef*>* usedPixRefs) {
SkPaint paint; SkPaint paint;
init_paint(&paint, bm); init_paint(&paint, bm);
SkPoint verts[4] = { SkPoint verts[4] = {
{ pos.fX+1, pos.fY+1 }, { pos.fX, pos.fY },
{ pos.fX + bm.width()-1, pos.fY+1 }, { pos.fX + bm.width(), pos.fY },
{ pos.fX + bm.width()-1, pos.fY + bm.height()-1 }, { pos.fX + bm.width(), pos.fY + bm.height() },
{ pos.fX+1, pos.fY + bm.height()-1 } { pos.fX, pos.fY + bm.height() }
}; };
SkPoint texs[4] = { { 0, 0 }, SkPoint texs[4] = { { 0, 0 },
{ SkIntToScalar(bm.width()), 0 }, { SkIntToScalar(bm.width()), 0 },
@ -256,20 +292,25 @@ static void drawverts_proc(SkCanvas* canvas, const SkBitmap& bm,
canvas->drawVertices(SkCanvas::kTriangles_VertexMode, 4, verts, texs, NULL, NULL, canvas->drawVertices(SkCanvas::kTriangles_VertexMode, 4, verts, texs, NULL, NULL,
indices, 6, paint); indices, 6, paint);
*usedPixRefs->append() = bm.pixelRef();
} }
// Return a picture with the bitmaps drawn at the specified positions. // Return a picture with the bitmaps drawn at the specified positions.
static SkPicture* record_bitmaps(const SkBitmap bm[], const SkPoint pos[], static SkPicture* record_bitmaps(const SkBitmap bm[],
int count, DrawBitmapProc proc) { const SkPoint pos[],
SkTDArray<SkPixelRef*> analytic[],
int count,
DrawBitmapProc proc) {
SkPicture* pic = new SkPicture; SkPicture* pic = new SkPicture;
SkCanvas* canvas = pic->beginRecording(1000, 1000); SkCanvas* canvas = pic->beginRecording(1000, 1000);
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
analytic[i].rewind();
canvas->save(); canvas->save();
SkRect clipRect = SkRect::MakeXYWH(pos[i].fX, pos[i].fY, SkRect clipRect = SkRect::MakeXYWH(pos[i].fX, pos[i].fY,
SkIntToScalar(bm[i].width()), SkIntToScalar(bm[i].width()),
SkIntToScalar(bm[i].height())); SkIntToScalar(bm[i].height()));
canvas->clipRect(clipRect, SkRegion::kIntersect_Op); canvas->clipRect(clipRect, SkRegion::kIntersect_Op);
proc(canvas, bm[i], bm[count+i], pos[i]); proc(canvas, bm[i], bm[count+i], pos[i], &analytic[i]);
canvas->restore(); canvas->restore();
} }
pic->endRecording(); pic->endRecording();
@ -363,6 +404,62 @@ static void gather_from_image(const SkBitmap& bm, SkPixelRef* const refs[],
} }
} }
void gather_from_analytic(const SkPoint pos[], SkScalar w, SkScalar h,
const SkTDArray<SkPixelRef*> analytic[],
int count,
SkTDArray<SkPixelRef*>* result,
const SkRect& subset) {
for (int i = 0; i < count; ++i) {
SkRect rect = SkRect::MakeXYWH(pos[i].fX, pos[i].fY, w, h);
if (SkRect::Intersects(subset, rect)) {
result->append(analytic[i].count(), analytic[i].begin());
}
}
}
static const DrawBitmapProc gProcs[] = {
drawpaint_proc,
drawpoints_proc,
drawrect_proc,
drawoval_proc,
drawrrect_proc,
drawpath_proc,
drawbitmap_proc,
drawbitmap_withshader_proc,
drawsprite_proc,
#if 0
drawsprite_withshader_proc,
#endif
drawbitmaprect_proc,
drawbitmaprect_withshader_proc,
drawtext_proc,
drawpostext_proc,
drawtextonpath_proc,
drawverts_proc,
};
static void create_textures(SkBitmap* bm, SkPixelRef** refs, int num, int w, int h) {
// Our convention is that the color components contain an encoding of
// the index of their corresponding bitmap/pixelref. (0,0,0,0) is
// reserved for the background
for (int i = 0; i < num; ++i) {
make_bm(&bm[i], w, h,
SkColorSetARGB(0xFF,
gColorScale*i+gColorOffset,
gColorScale*i+gColorOffset,
gColorScale*i+gColorOffset),
true);
refs[i] = bm[i].pixelRef();
}
// The A8 alternate bitmaps are all BW checkerboards
for (int i = 0; i < num; ++i) {
make_checkerboard(&bm[num+i], w, h, true);
refs[num+i] = bm[num+i].pixelRef();
}
}
static void test_gatherpixelrefs(skiatest::Reporter* reporter) { static void test_gatherpixelrefs(skiatest::Reporter* reporter) {
const int IW = 32; const int IW = 32;
const int IH = IW; const int IH = IW;
@ -372,58 +469,21 @@ static void test_gatherpixelrefs(skiatest::Reporter* reporter) {
static const int N = 4; static const int N = 4;
SkBitmap bm[2*N]; SkBitmap bm[2*N];
SkPixelRef* refs[2*N]; SkPixelRef* refs[2*N];
SkTDArray<SkPixelRef*> analytic[N];
const SkPoint pos[N] = { const SkPoint pos[N] = {
{ 0, 0 }, { W, 0 }, { 0, H }, { W, H } { 0, 0 }, { W, 0 }, { 0, H }, { W, H }
}; };
// Our convention is that the color components contain an encoding of create_textures(bm, refs, N, IW, IH);
// the index of their corresponding bitmap/pixelref. (0,0,0,0) is
// reserved for the background
for (int i = 0; i < N; ++i) {
make_bm(&bm[i], IW, IH,
SkColorSetARGB(0xFF,
gColorScale*i+gColorOffset,
gColorScale*i+gColorOffset,
gColorScale*i+gColorOffset),
true);
refs[i] = bm[i].pixelRef();
}
// The A8 alternate bitmaps are all BW checkerboards
for (int i = 0; i < N; ++i) {
make_checkerboard(&bm[N+i], IW, IH, true);
refs[N+i] = bm[N+i].pixelRef();
}
static const DrawBitmapProc procs[] = {
drawpaint_proc,
drawpoints_proc,
drawrect_proc,
drawoval_proc,
drawrrect_proc,
drawpath_proc,
drawbitmap_proc,
drawbitmap_withshader_proc,
drawsprite_proc,
#if 0
drawsprite_withshader_proc,
#endif
drawbitmaprect_proc,
drawbitmaprect_withshader_proc,
drawtext_proc,
drawpostext_proc,
drawtextonpath_proc,
drawverts_proc,
};
SkRandom rand; SkRandom rand;
for (size_t k = 0; k < SK_ARRAY_COUNT(procs); ++k) { for (size_t k = 0; k < SK_ARRAY_COUNT(gProcs); ++k) {
SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, N, procs[k])); SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, analytic, N, gProcs[k]));
REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0); REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0);
// quick check for a small piece of each quadrant, which should just // quick check for a small piece of each quadrant, which should just
// contain 1 bitmap. // contain 1 or 2 bitmaps.
for (size_t i = 0; i < SK_ARRAY_COUNT(pos); ++i) { for (size_t i = 0; i < SK_ARRAY_COUNT(pos); ++i) {
SkRect r; SkRect r;
r.set(2, 2, W - 2, H - 2); r.set(2, 2, W - 2, H - 2);
@ -453,7 +513,11 @@ static void test_gatherpixelrefs(skiatest::Reporter* reporter) {
SkRect r; SkRect r;
rand_rect(&r, rand, 2*W, 2*H); rand_rect(&r, rand, 2*W, 2*H);
SkTDArray<SkPixelRef*> array; SkTDArray<SkPixelRef*> fromImage;
gather_from_image(image, refs, N, &fromImage, r);
SkTDArray<SkPixelRef*> fromAnalytic;
gather_from_analytic(pos, W, H, analytic, N, &fromAnalytic, r);
SkData* data = SkPictureUtils::GatherPixelRefs(pic, r); SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
size_t dataSize = data ? data->size() : 0; size_t dataSize = data ? data->size() : 0;
@ -462,18 +526,22 @@ static void test_gatherpixelrefs(skiatest::Reporter* reporter) {
SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL; SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL;
SkAutoDataUnref adu(data); SkAutoDataUnref adu(data);
gather_from_image(image, refs, N, &array, r); // Everything that we saw drawn should appear in the analytic list
// but the analytic list may contain some pixelRefs that were not
// seen in the image (e.g., A8 textures used as masks)
for (int i = 0; i < fromImage.count(); ++i) {
REPORTER_ASSERT(reporter, -1 != fromAnalytic.find(fromImage[i]));
}
/* /*
* GatherPixelRefs is conservative, so it can return more bitmaps * GatherPixelRefs is conservative, so it can return more bitmaps
* that we actually can see (usually because of conservative bounds * than are strictly required. Thus our check here is only that
* inflation for antialiasing). Thus our check here is only that * Gather didn't miss any that we actually needed. Even that isn't
* Gather didn't miss any that we actually saw. Even that isn't
* a strict requirement on Gather, which is meant to be quick and * a strict requirement on Gather, which is meant to be quick and
* only mostly-correct, but at the moment this test should work. * only mostly-correct, but at the moment this test should work.
*/ */
for (int i = 0; i < array.count(); ++i) { for (int i = 0; i < fromAnalytic.count(); ++i) {
bool found = find(gatherRefs, array[i], gatherCount); bool found = find(gatherRefs, fromAnalytic[i], gatherCount);
REPORTER_ASSERT(reporter, found); REPORTER_ASSERT(reporter, found);
#if 0 #if 0
// enable this block of code to debug failures, as it will rerun // enable this block of code to debug failures, as it will rerun
@ -488,6 +556,89 @@ static void test_gatherpixelrefs(skiatest::Reporter* reporter) {
} }
} }
static void test_gatherpixelrefsandrects(skiatest::Reporter* reporter) {
const int IW = 32;
const int IH = IW;
const SkScalar W = SkIntToScalar(IW);
const SkScalar H = W;
static const int N = 4;
SkBitmap bm[2*N];
SkPixelRef* refs[2*N];
SkTDArray<SkPixelRef*> analytic[N];
const SkPoint pos[N] = {
{ 0, 0 }, { W, 0 }, { 0, H }, { W, H }
};
create_textures(bm, refs, N, IW, IH);
SkRandom rand;
for (size_t k = 0; k < SK_ARRAY_COUNT(gProcs); ++k) {
SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, analytic, N, gProcs[k]));
REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0);
SkAutoTUnref<SkPictureUtils::SkPixelRefContainer> prCont(
new SkPictureUtils::SkPixelRefsAndRectsList);
SkPictureUtils::GatherPixelRefsAndRects(pic, prCont);
// quick check for a small piece of each quadrant, which should just
// contain 1 or 2 bitmaps.
for (size_t i = 0; i < SK_ARRAY_COUNT(pos); ++i) {
SkRect r;
r.set(2, 2, W - 2, H - 2);
r.offset(pos[i].fX, pos[i].fY);
SkTDArray<SkPixelRef*> gatheredRefs;
prCont->query(r, &gatheredRefs);
int count = gatheredRefs.count();
REPORTER_ASSERT(reporter, 1 == count || 2 == count);
if (1 == count) {
REPORTER_ASSERT(reporter, gatheredRefs[0] == refs[i]);
} else if (2 == count) {
REPORTER_ASSERT(reporter,
(gatheredRefs[0] == refs[i] && gatheredRefs[1] == refs[i+N]) ||
(gatheredRefs[1] == refs[i] && gatheredRefs[0] == refs[i+N]));
}
}
SkBitmap image;
draw(pic, 2*IW, 2*IH, &image);
// Test a bunch of random (mostly) rects, and compare the gather results
// with the analytic results and the pixel refs seen in a rendering.
for (int j = 0; j < 100; ++j) {
SkRect r;
rand_rect(&r, rand, 2*W, 2*H);
SkTDArray<SkPixelRef*> fromImage;
gather_from_image(image, refs, N, &fromImage, r);
SkTDArray<SkPixelRef*> fromAnalytic;
gather_from_analytic(pos, W, H, analytic, N, &fromAnalytic, r);
SkTDArray<SkPixelRef*> gatheredRefs;
prCont->query(r, &gatheredRefs);
// Everything that we saw drawn should appear in the analytic list
// but the analytic list may contain some pixelRefs that were not
// seen in the image (e.g., A8 textures used as masks)
for (int i = 0; i < fromImage.count(); ++i) {
REPORTER_ASSERT(reporter, -1 != fromAnalytic.find(fromImage[i]));
}
// Everything in the analytic list should appear in the gathered
// list.
for (int i = 0; i < fromAnalytic.count(); ++i) {
REPORTER_ASSERT(reporter, -1 != gatheredRefs.find(fromAnalytic[i]));
}
}
}
}
#ifdef SK_DEBUG #ifdef SK_DEBUG
// Ensure that deleting SkPicturePlayback does not assert. Asserts only fire in debug mode, so only // Ensure that deleting SkPicturePlayback does not assert. Asserts only fire in debug mode, so only
// run in debug mode. // run in debug mode.
@ -875,6 +1026,7 @@ DEF_TEST(Picture, reporter) {
#endif #endif
test_peephole(); test_peephole();
test_gatherpixelrefs(reporter); test_gatherpixelrefs(reporter);
test_gatherpixelrefsandrects(reporter);
test_bitmap_with_encoded_data(reporter); test_bitmap_with_encoded_data(reporter);
test_clone_empty(reporter); test_clone_empty(reporter);
test_clip_bound_opt(reporter); test_clip_bound_opt(reporter);