aboutsummaryrefslogtreecommitdiffstats
path: root/bench
diff options
context:
space:
mode:
Diffstat (limited to 'bench')
-rw-r--r--bench/Android.mk30
-rw-r--r--bench/BitmapBench.cpp164
-rw-r--r--bench/Makefile.am19
-rw-r--r--bench/RectBench.cpp133
-rw-r--r--bench/SkBenchmark.cpp33
-rw-r--r--bench/SkBenchmark.h54
-rw-r--r--bench/TextBench.cpp175
-rw-r--r--bench/benchmain.cpp365
8 files changed, 973 insertions, 0 deletions
diff --git a/bench/Android.mk b/bench/Android.mk
new file mode 100644
index 0000000..117fb38
--- /dev/null
+++ b/bench/Android.mk
@@ -0,0 +1,30 @@
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ BitmapBench.cpp \
+ RectBench.cpp \
+ TextBench.cpp \
+ SkBenchmark.cpp \
+ benchmain.cpp
+
+# additional optional class for this tool
+LOCAL_SRC_FILES += \
+ ../src/utils/SkNWayCanvas.cpp
+
+LOCAL_SHARED_LIBRARIES := libcutils libskia
+LOCAL_C_INCLUDES := \
+ external/skia/include/config \
+ external/skia/include/core \
+ external/skia/include/images \
+ external/skia/include/utils \
+ external/skia/include/effects
+
+#LOCAL_CFLAGS :=
+
+LOCAL_MODULE := skia_bench
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
diff --git a/bench/BitmapBench.cpp b/bench/BitmapBench.cpp
new file mode 100644
index 0000000..22c0a01
--- /dev/null
+++ b/bench/BitmapBench.cpp
@@ -0,0 +1,164 @@
+#include "SkBenchmark.h"
+#include "SkBitmap.h"
+#include "SkPaint.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkRandom.h"
+#include "SkString.h"
+
+static const char* gTileName[] = {
+ "clamp", "repeat", "mirror"
+};
+
+static const char* gConfigName[] = {
+ "ERROR", "a1", "a8", "index8", "565", "4444", "8888"
+};
+
+static void drawIntoBitmap(const SkBitmap& bm) {
+ const int w = bm.width();
+ const int h = bm.height();
+
+ SkCanvas canvas(bm);
+ SkPaint p;
+ p.setAntiAlias(true);
+ p.setColor(SK_ColorRED);
+ canvas.drawCircle(SkIntToScalar(w)/2, SkIntToScalar(h)/2,
+ SkIntToScalar(SkMin32(w, h))*3/8, p);
+
+ SkRect r;
+ r.set(0, 0, SkIntToScalar(w), SkIntToScalar(h));
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(SkIntToScalar(4));
+ p.setColor(SK_ColorBLUE);
+ canvas.drawRect(r, p);
+}
+
+static int conv6ToByte(int x) {
+ return x * 0xFF / 5;
+}
+
+static int convByteTo6(int x) {
+ return x * 5 / 255;
+}
+
+static uint8_t compute666Index(SkPMColor c) {
+ int r = SkGetPackedR32(c);
+ int g = SkGetPackedG32(c);
+ int b = SkGetPackedB32(c);
+
+ return convByteTo6(r) * 36 + convByteTo6(g) * 6 + convByteTo6(b);
+}
+
+static void convertToIndex666(const SkBitmap& src, SkBitmap* dst) {
+ SkColorTable* ctable = new SkColorTable(216);
+ SkPMColor* colors = ctable->lockColors();
+ // rrr ggg bbb
+ for (int r = 0; r < 6; r++) {
+ int rr = conv6ToByte(r);
+ for (int g = 0; g < 6; g++) {
+ int gg = conv6ToByte(g);
+ for (int b = 0; b < 6; b++) {
+ int bb = conv6ToByte(b);
+ *colors++ = SkPreMultiplyARGB(0xFF, rr, gg, bb);
+ }
+ }
+ }
+ ctable->unlockColors(true);
+ dst->setConfig(SkBitmap::kIndex8_Config, src.width(), src.height());
+ dst->allocPixels(ctable);
+ ctable->unref();
+
+ SkAutoLockPixels alps(src);
+ SkAutoLockPixels alpd(*dst);
+
+ for (int y = 0; y < src.height(); y++) {
+ const SkPMColor* srcP = src.getAddr32(0, y);
+ uint8_t* dstP = dst->getAddr8(0, y);
+ for (int x = src.width() - 1; x >= 0; --x) {
+ *dstP++ = compute666Index(*srcP++);
+ }
+ }
+}
+
+/* Variants for bitmaps
+
+ - src depth (32 w+w/o alpha), 565, 4444, index, a8
+ - paint options: filtering, dither, alpha
+ - matrix options: translate, scale, rotate, persp
+ - tiling: none, repeat, mirror, clamp
+
+ */
+
+class BitmapBench : public SkBenchmark {
+ SkBitmap fBitmap;
+ SkPaint fPaint;
+ int fTileX, fTileY; // -1 means don't use shader
+ SkString fName;
+ enum { N = 300 };
+public:
+ BitmapBench(SkBitmap::Config c, int tx = -1, int ty = -1) : fTileX(tx), fTileY(ty) {
+ const int w = 128;
+ const int h = 128;
+ SkBitmap bm;
+
+ if (SkBitmap::kIndex8_Config == c) {
+ bm.setConfig(SkBitmap::kARGB_8888_Config, w, h);
+ } else {
+ bm.setConfig(c, w, h);
+ }
+ bm.allocPixels();
+ bm.eraseColor(0);
+
+ drawIntoBitmap(bm);
+
+ if (SkBitmap::kIndex8_Config == c) {
+ convertToIndex666(bm, &fBitmap);
+ } else {
+ fBitmap = bm;
+ }
+ }
+
+protected:
+ virtual const char* onGetName() {
+ fName.set("bitmap");
+ if (fTileX >= 0) {
+ fName.appendf("_%s", gTileName[fTileX]);
+ if (fTileY != fTileX) {
+ fName.appendf("_%s", gTileName[fTileY]);
+ }
+ }
+ fName.appendf("_%s", gConfigName[fBitmap.config()]);
+ return fName.c_str();
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ SkIPoint dim = this->getSize();
+ SkRandom rand;
+
+ SkPaint paint(fPaint);
+ this->setupPaint(&paint);
+
+ const SkBitmap& bitmap = fBitmap;
+ const SkScalar x0 = SkIntToScalar(-bitmap.width() / 2);
+ const SkScalar y0 = SkIntToScalar(-bitmap.height() / 2);
+
+ for (int i = 0; i < N; i++) {
+ SkScalar x = x0 + rand.nextUScalar1() * dim.fX;
+ SkScalar y = y0 + rand.nextUScalar1() * dim.fY;
+ canvas->drawBitmap(bitmap, x, y, &paint);
+ }
+ }
+
+private:
+ typedef SkBenchmark INHERITED;
+};
+
+static SkBenchmark* Fact0(void*) { return new BitmapBench(SkBitmap::kARGB_8888_Config); }
+static SkBenchmark* Fact1(void*) { return new BitmapBench(SkBitmap::kRGB_565_Config); }
+static SkBenchmark* Fact2(void*) { return new BitmapBench(SkBitmap::kARGB_4444_Config); }
+static SkBenchmark* Fact3(void*) { return new BitmapBench(SkBitmap::kIndex8_Config); }
+
+static BenchRegistry gReg0(Fact0);
+static BenchRegistry gReg1(Fact1);
+static BenchRegistry gReg2(Fact2);
+static BenchRegistry gReg3(Fact3);
diff --git a/bench/Makefile.am b/bench/Makefile.am
new file mode 100644
index 0000000..be0ebdb
--- /dev/null
+++ b/bench/Makefile.am
@@ -0,0 +1,19 @@
+AM_CPPFLAGS = -I$(top_builddir)/include/core -I$(top_builddir)/include/images
+AM_LDFLAGS = -lpng -lpthread
+
+bin_PROGRAMS = Bench
+Bench_SOURCES = RectBench.cpp \
+ SkBenchmark.cpp \
+ BenchTool/main.cpp \
+ $(top_builddir)/src/images/SkImageDecoder.cpp \
+ $(top_builddir)/src/images/SkImageDecoder_libpng.cpp \
+ $(top_builddir)/src/images/SkScaledBitmapSampler.cpp \
+ $(top_builddir)/src/ports/SkGlobals_global.cpp \
+ $(top_builddir)/src/ports/SkOSFile_stdio.cpp \
+ $(top_builddir)/src/ports/SkThread_pthread.cpp \
+ $(top_builddir)/src/ports/SkTime_Unix.cpp \
+ $(top_builddir)/src/ports/SkFontHost_none.cpp
+
+
+Bench_LDADD = $(top_builddir)/src/core/libskia.a
+
diff --git a/bench/RectBench.cpp b/bench/RectBench.cpp
new file mode 100644
index 0000000..69e0b00
--- /dev/null
+++ b/bench/RectBench.cpp
@@ -0,0 +1,133 @@
+#include "SkBenchmark.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkRandom.h"
+#include "SkString.h"
+
+class RectBench : public SkBenchmark {
+public:
+ int fShift;
+ enum {
+ W = 640,
+ H = 480,
+ N = 300
+ };
+ SkRect fRects[N];
+ SkColor fColors[N];
+
+ RectBench(int shift) : fShift(shift) {
+ SkRandom rand;
+ for (int i = 0; i < N; i++) {
+ int x = rand.nextU() % W;
+ int y = rand.nextU() % H;
+ int w = rand.nextU() % W;
+ int h = rand.nextU() % H;
+ w >>= shift;
+ h >>= shift;
+ x -= w/2;
+ y -= h/2;
+ fRects[i].set(SkIntToScalar(x), SkIntToScalar(y),
+ SkIntToScalar(x+w), SkIntToScalar(y+h));
+ fColors[i] = rand.nextU() | 0xFF808080;
+ }
+ }
+
+ SkString fName;
+ const char* computeName(const char root[]) {
+ fName.set(root);
+ fName.appendS32(fShift);
+ return fName.c_str();
+ }
+
+protected:
+ virtual void drawThisRect(SkCanvas* c, const SkRect& r, const SkPaint& p) {
+ c->drawRect(r, p);
+ }
+
+ virtual const char* onGetName() { return computeName("rects"); }
+ virtual void onDraw(SkCanvas* canvas) {
+ SkPaint paint;
+ for (int i = 0; i < N; i++) {
+ paint.setColor(fColors[i]);
+ this->setupPaint(&paint);
+ this->drawThisRect(canvas, fRects[i], paint);
+ }
+ }
+};
+
+class OvalBench : public RectBench {
+public:
+ OvalBench(int shift) : RectBench(shift) {}
+protected:
+ virtual void drawThisRect(SkCanvas* c, const SkRect& r, const SkPaint& p) {
+ c->drawOval(r, p);
+ }
+ virtual const char* onGetName() { return computeName("ovals"); }
+};
+
+class RRectBench : public RectBench {
+public:
+ RRectBench(int shift) : RectBench(shift) {}
+protected:
+ virtual void drawThisRect(SkCanvas* c, const SkRect& r, const SkPaint& p) {
+ c->drawRoundRect(r, r.width() / 4, r.height() / 4, p);
+ }
+ virtual const char* onGetName() { return computeName("rrects"); }
+};
+
+class PointsBench : public RectBench {
+public:
+ SkCanvas::PointMode fMode;
+ const char* fName;
+
+ PointsBench(SkCanvas::PointMode mode, const char* name) :
+ RectBench(2), fMode(mode) {
+ fName = name;
+ }
+
+protected:
+ virtual void onDraw(SkCanvas* canvas) {
+ static const SkScalar gSizes[] = {
+ SkIntToScalar(7), 0
+ };
+
+ SkPaint paint;
+ paint.setStrokeCap(SkPaint::kRound_Cap);
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gSizes); i++) {
+ paint.setStrokeWidth(gSizes[i]);
+ this->setupPaint(&paint);
+ canvas->drawPoints(fMode, N * 2,
+ reinterpret_cast<const SkPoint*>(fRects), paint);
+ paint.setColor(fColors[i]);
+ }
+ }
+ virtual const char* onGetName() { return fName; }
+};
+
+static SkBenchmark* RectFactory1(void*) { return SkNEW_ARGS(RectBench, (1)); }
+static SkBenchmark* RectFactory2(void*) { return SkNEW_ARGS(RectBench, (3)); }
+static SkBenchmark* OvalFactory1(void*) { return SkNEW_ARGS(OvalBench, (1)); }
+static SkBenchmark* OvalFactory2(void*) { return SkNEW_ARGS(OvalBench, (3)); }
+static SkBenchmark* RRectFactory1(void*) { return SkNEW_ARGS(RRectBench, (1)); }
+static SkBenchmark* RRectFactory2(void*) { return SkNEW_ARGS(RRectBench, (3)); }
+static SkBenchmark* PointsFactory(void*) {
+ return SkNEW_ARGS(PointsBench, (SkCanvas::kPoints_PointMode, "points"));
+}
+static SkBenchmark* LinesFactory(void*) {
+ return SkNEW_ARGS(PointsBench, (SkCanvas::kLines_PointMode, "lines"));
+}
+static SkBenchmark* PolygonFactory(void*) {
+ return SkNEW_ARGS(PointsBench, (SkCanvas::kPolygon_PointMode, "polygon"));
+}
+
+static BenchRegistry gRectReg1(RectFactory1);
+static BenchRegistry gRectReg2(RectFactory2);
+static BenchRegistry gOvalReg1(OvalFactory1);
+static BenchRegistry gOvalReg2(OvalFactory2);
+static BenchRegistry gRRectReg1(RRectFactory1);
+static BenchRegistry gRRectReg2(RRectFactory2);
+static BenchRegistry gPointsReg(PointsFactory);
+static BenchRegistry gLinesReg(LinesFactory);
+static BenchRegistry gPolygonReg(PolygonFactory);
+
diff --git a/bench/SkBenchmark.cpp b/bench/SkBenchmark.cpp
new file mode 100644
index 0000000..7bc87ee
--- /dev/null
+++ b/bench/SkBenchmark.cpp
@@ -0,0 +1,33 @@
+#include "SkBenchmark.h"
+#include "SkPaint.h"
+
+template BenchRegistry* BenchRegistry::gHead;
+
+SkBenchmark::SkBenchmark() {
+ fForceAlpha = 0xFF;
+ fForceAA = true;
+}
+
+const char* SkBenchmark::getName() {
+ return this->onGetName();
+}
+
+SkIPoint SkBenchmark::getSize() {
+ return this->onGetSize();
+}
+
+void SkBenchmark::draw(SkCanvas* canvas) {
+ this->onDraw(canvas);
+}
+
+void SkBenchmark::setupPaint(SkPaint* paint) {
+ paint->setAlpha(fForceAlpha);
+ paint->setAntiAlias(fForceAA);
+ paint->setFilterBitmap(fForceFilter);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkIPoint SkBenchmark::onGetSize() {
+ return SkMakeIPoint(640, 480);
+}
diff --git a/bench/SkBenchmark.h b/bench/SkBenchmark.h
new file mode 100644
index 0000000..2058251
--- /dev/null
+++ b/bench/SkBenchmark.h
@@ -0,0 +1,54 @@
+#ifndef SkBenchmark_DEFINED
+#define SkBenchmark_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkPoint.h"
+#include "SkTRegistry.h"
+
+class SkCanvas;
+class SkPaint;
+
+class SkBenchmark : public SkRefCnt {
+public:
+ SkBenchmark();
+
+ const char* getName();
+ SkIPoint getSize();
+ void draw(SkCanvas*);
+
+ void setForceAlpha(int alpha) {
+ fForceAlpha = alpha;
+ }
+
+ void setForceAA(bool aa) {
+ fForceAA = aa;
+ }
+
+ void setForceFilter(bool filter) {
+ fForceFilter = filter;
+ }
+
+protected:
+ void setupPaint(SkPaint* paint);
+
+ virtual const char* onGetName() = 0;
+ virtual void onDraw(SkCanvas*) = 0;
+
+ virtual SkIPoint onGetSize();
+
+private:
+ int fForceAlpha;
+ bool fForceAA;
+ bool fForceFilter;
+};
+
+static inline SkIPoint SkMakeIPoint(int x, int y) {
+ SkIPoint p;
+ p.set(x, y);
+ return p;
+}
+
+typedef SkTRegistry<SkBenchmark*, void*> BenchRegistry;
+
+#endif
+
diff --git a/bench/TextBench.cpp b/bench/TextBench.cpp
new file mode 100644
index 0000000..67cd766
--- /dev/null
+++ b/bench/TextBench.cpp
@@ -0,0 +1,175 @@
+#include "SkBenchmark.h"
+#include "SkCanvas.h"
+#include "SkFontHost.h"
+#include "SkPaint.h"
+#include "SkRandom.h"
+#include "SkSfntUtils.h"
+#include "SkString.h"
+#include "SkTemplates.h"
+
+static void dump_font(const char name[], SkFontID fontID) {
+ SkDebugf("Font \"%s\" %x\n", name, fontID);
+ int count = SkFontHost::CountTables(fontID);
+ SkAutoTArray<SkFontTableTag> storage(count);
+ SkFontTableTag* tags = storage.get();
+ SkFontHost::GetTableTags(fontID, tags);
+ for (int i = 0; i < count; i++) {
+ uint32_t tag = tags[i];
+ uint8_t data[4];
+ size_t size = SkFontHost::GetTableSize(fontID, tag);
+ size_t bytes = SkFontHost::GetTableData(fontID, tag,
+ 0, sizeof(data), data);
+ SkDebugf(" tag=%c%c%c%c size=%d bytes=%d %x %x %x %x\n",
+ uint8_t(tag>>24), uint8_t(tag>>16), uint8_t(tag>>8), uint8_t(tag),
+ size, bytes, data[0], data[1], data[2], data[3]);
+ }
+
+ SkSfntTable_head head;
+ if (SkSfntUtils::ReadTable_head(fontID, &head)) {
+ SkDebugf("--- head: version=%x magic=%x upem=%d style=%x\n", head.fVersion,
+ head.fMagicNumber, head.fUnitsPerEm, head.fMacStyle);
+ } else {
+ SkDebugf("------- head wasn't read\n");
+ }
+
+ SkSfntTable_maxp maxp;
+ if (SkSfntUtils::ReadTable_maxp(fontID, &maxp)) {
+ SkDebugf("--- maxp: version=%x glyphs=%d points=%d ctrs=%d\n", maxp.fVersion,
+ maxp.fNumGlyphs, maxp.fMaxPoints, maxp.fMaxContours);
+ } else {
+ SkDebugf("------- maxp wasn't read\n");
+ }
+}
+
+static void test_tables() {
+ static bool gOnce;
+ if (gOnce) {
+ return;
+ }
+ gOnce = true;
+
+ static const char* gNames[] = {
+ "Arial", "Times", "Courier"
+ };
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gNames); i++) {
+ SkTypeface* tf = SkTypeface::CreateFromName(gNames[i], SkTypeface::kNormal);
+ if (tf) {
+ SkFontID fontID = tf->uniqueID();
+ dump_font(gNames[i], fontID);
+ tf->unref();
+ }
+ }
+}
+
+/* Some considerations for performance:
+ short -vs- long strings (measuring overhead)
+ tiny -vs- large pointsize (measure blit -vs- overhead)
+ 1 -vs- many point sizes (measure cache lookup)
+ normal -vs- subpixel -vs- lineartext (minor)
+ force purge after each draw to measure scaler
+ textencoding?
+ text -vs- postext - pathtext
+ */
+class TextBench : public SkBenchmark {
+ SkPaint fPaint;
+ int fCount;
+ SkPoint* fPos;
+ SkString fText;
+ SkString fName;
+ enum { N = 300 };
+public:
+ TextBench(const char text[], int ps, bool linearText, bool posText) {
+ if (false) {
+ test_tables();
+ }
+
+ fText.set(text);
+
+ fPaint.setAntiAlias(true);
+ fPaint.setTextSize(SkIntToScalar(ps));
+ fPaint.setLinearText(linearText);
+
+ if (posText) {
+ SkAutoTArray<SkScalar> storage(fText.size());
+ SkScalar* widths = storage.get();
+ fCount = fPaint.getTextWidths(fText.c_str(), fText.size(), widths);
+ fPos = new SkPoint[fCount];
+ SkScalar x = 0;
+ for (int i = 0; i < fCount; i++) {
+ fPos[i].set(x, 0);
+ x += widths[i];
+ }
+ } else {
+ fCount = 0;
+ fPos = NULL;
+ }
+ }
+
+ virtual ~TextBench() {
+ delete[] fPos;
+ }
+
+protected:
+ virtual const char* onGetName() {
+ fName.printf("text_%g", SkScalarToFloat(fPaint.getTextSize()));
+ if (fPaint.isLinearText()) {
+ fName.append("_linear");
+ }
+ if (fPos) {
+ fName.append("_pos");
+ }
+ return fName.c_str();
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ const SkIPoint dim = this->getSize();
+ SkRandom rand;
+
+ SkPaint paint(fPaint);
+ this->setupPaint(&paint);
+
+ const SkScalar x0 = SkIntToScalar(-10);
+ const SkScalar y0 = SkIntToScalar(-10);
+
+ for (int i = 0; i < N; i++) {
+ SkScalar x = x0 + rand.nextUScalar1() * dim.fX;
+ SkScalar y = y0 + rand.nextUScalar1() * dim.fY;
+ if (fPos) {
+ canvas->save(SkCanvas::kMatrix_SaveFlag);
+ canvas->translate(x, y);
+ canvas->drawPosText(fText.c_str(), fText.size(), fPos, paint);
+ canvas->restore();
+ } else {
+ canvas->drawText(fText.c_str(), fText.size(), x, y, paint);
+ }
+ }
+ }
+
+private:
+ typedef SkBenchmark INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define STR "Hamburgefons"
+#define SMALL 9
+#define BIG 48
+
+static SkBenchmark* Fact0(void*) { return new TextBench(STR, SMALL, false, false); }
+static SkBenchmark* Fact1(void*) { return new TextBench(STR, SMALL, false, true); }
+static SkBenchmark* Fact2(void*) { return new TextBench(STR, SMALL, true, false); }
+static SkBenchmark* Fact3(void*) { return new TextBench(STR, SMALL, true, true); }
+static SkBenchmark* Fact4(void*) { return new TextBench(STR, BIG, false, false); }
+static SkBenchmark* Fact5(void*) { return new TextBench(STR, BIG, false, true); }
+static SkBenchmark* Fact6(void*) { return new TextBench(STR, BIG, true, false); }
+static SkBenchmark* Fact7(void*) { return new TextBench(STR, BIG, true, true); }
+
+static BenchRegistry gReg0(Fact0);
+static BenchRegistry gReg1(Fact1);
+static BenchRegistry gReg2(Fact2);
+static BenchRegistry gReg3(Fact3);
+static BenchRegistry gReg4(Fact4);
+static BenchRegistry gReg5(Fact5);
+static BenchRegistry gReg6(Fact6);
+static BenchRegistry gReg7(Fact7);
diff --git a/bench/benchmain.cpp b/bench/benchmain.cpp
new file mode 100644
index 0000000..501e86a
--- /dev/null
+++ b/bench/benchmain.cpp
@@ -0,0 +1,365 @@
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkGraphics.h"
+#include "SkImageEncoder.h"
+#include "SkNWayCanvas.h"
+#include "SkPicture.h"
+#include "SkString.h"
+#include "SkTime.h"
+
+#include "SkBenchmark.h"
+
+#ifdef ANDROID
+static void log_error(const char msg[]) { SkDebugf("%s", msg); }
+static void log_progress(const char msg[]) { SkDebugf("%s", msg); }
+#else
+static void log_error(const char msg[]) { fprintf(stderr, "%s", msg); }
+static void log_progress(const char msg[]) { printf("%s", msg); }
+#endif
+
+static void log_error(const SkString& str) { log_error(str.c_str()); }
+static void log_progress(const SkString& str) { log_progress(str.c_str()); }
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void erase(SkBitmap& bm) {
+ if (bm.config() == SkBitmap::kA8_Config) {
+ bm.eraseColor(0);
+ } else {
+ bm.eraseColor(SK_ColorWHITE);
+ }
+}
+
+static bool equal(const SkBitmap& bm1, const SkBitmap& bm2) {
+ if (bm1.width() != bm2.width() ||
+ bm1.height() != bm2.height() ||
+ bm1.config() != bm2.config()) {
+ return false;
+ }
+
+ size_t pixelBytes = bm1.width() * bm1.bytesPerPixel();
+ for (int y = 0; y < bm1.height(); y++) {
+ if (memcmp(bm1.getAddr(0, y), bm2.getAddr(0, y), pixelBytes)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+class Iter {
+public:
+ Iter() {
+ fBench = BenchRegistry::Head();
+ }
+
+ SkBenchmark* next() {
+ if (fBench) {
+ BenchRegistry::Factory f = fBench->factory();
+ fBench = fBench->next();
+ return f(0);
+ }
+ return NULL;
+ }
+
+private:
+ const BenchRegistry* fBench;
+};
+
+static void make_filename(const char name[], SkString* path) {
+ path->set(name);
+ for (int i = 0; name[i]; i++) {
+ switch (name[i]) {
+ case '/':
+ case '\\':
+ case ' ':
+ case ':':
+ path->writable_str()[i] = '-';
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void saveFile(const char name[], const char config[], const char dir[],
+ const SkBitmap& bm) {
+ SkBitmap copy;
+ if (!bm.copyTo(&copy, SkBitmap::kARGB_8888_Config)) {
+ return;
+ }
+
+ if (bm.config() == SkBitmap::kA8_Config) {
+ // turn alpha into gray-scale
+ size_t size = copy.getSize() >> 2;
+ SkPMColor* p = copy.getAddr32(0, 0);
+ for (size_t i = 0; i < size; i++) {
+ int c = (*p >> SK_A32_SHIFT) & 0xFF;
+ c = 255 - c;
+ c |= (c << 24) | (c << 16) | (c << 8);
+ *p++ = c | (SK_A32_MASK << SK_A32_SHIFT);
+ }
+ }
+
+ SkString str;
+ make_filename(name, &str);
+ str.appendf("_%s.png", config);
+ str.prepend(dir);
+ ::remove(str.c_str());
+ SkImageEncoder::EncodeFile(str.c_str(), copy, SkImageEncoder::kPNG_Type,
+ 100);
+}
+
+static void performClip(SkCanvas* canvas, int w, int h) {
+ SkRect r;
+
+ r.set(SkIntToScalar(10), SkIntToScalar(10),
+ SkIntToScalar(w*2/3), SkIntToScalar(h*2/3));
+ canvas->clipRect(r, SkRegion::kIntersect_Op);
+
+ r.set(SkIntToScalar(w/3), SkIntToScalar(h/3),
+ SkIntToScalar(w-10), SkIntToScalar(h-10));
+ canvas->clipRect(r, SkRegion::kXOR_Op);
+}
+
+static void performRotate(SkCanvas* canvas, int w, int h) {
+ const SkScalar x = SkIntToScalar(w) / 2;
+ const SkScalar y = SkIntToScalar(h) / 2;
+
+ canvas->translate(x, y);
+ canvas->rotate(SkIntToScalar(35));
+ canvas->translate(-x, -y);
+}
+
+static void performScale(SkCanvas* canvas, int w, int h) {
+ const SkScalar x = SkIntToScalar(w) / 2;
+ const SkScalar y = SkIntToScalar(h) / 2;
+
+ canvas->translate(x, y);
+ // just enough so we can't take the sprite case
+ canvas->scale(SK_Scalar1 * 99/100, SK_Scalar1 * 99/100);
+ canvas->translate(-x, -y);
+}
+
+static void compare_pict_to_bitmap(SkPicture* pict, const SkBitmap& bm) {
+ SkBitmap bm2;
+
+ bm2.setConfig(bm.config(), bm.width(), bm.height());
+ bm2.allocPixels();
+ erase(bm2);
+
+ SkCanvas canvas(bm2);
+ canvas.drawPicture(*pict);
+
+ if (!equal(bm, bm2)) {
+ SkDebugf("----- compare_pict_to_bitmap failed\n");
+ }
+}
+
+static bool parse_bool_arg(char * const* argv, char* const* stop, bool* var) {
+ if (argv < stop) {
+ *var = atoi(*argv) != 0;
+ return true;
+ }
+ return false;
+}
+
+static const struct {
+ SkBitmap::Config fConfig;
+ const char* fName;
+} gConfigs[] = {
+ { SkBitmap::kARGB_8888_Config, "8888" },
+ { SkBitmap::kRGB_565_Config, "565", },
+ { SkBitmap::kARGB_4444_Config, "4444", },
+ { SkBitmap::kA8_Config, "A8", }
+};
+
+static int findConfig(const char config[]) {
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); i++) {
+ if (!strcmp(config, gConfigs[i].fName)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int main (int argc, char * const argv[]) {
+ SkAutoGraphics ag;
+
+ int repeatDraw = 1;
+ int forceAlpha = 0xFF;
+ bool forceAA = true;
+ bool forceFilter = false;
+ bool doScale = false;
+ bool doRotate = false;
+ bool doClip = false;
+ bool doPict = false;
+ const char* matchStr = NULL;
+
+ SkString outDir;
+ SkBitmap::Config outConfig = SkBitmap::kNo_Config;
+ const char* configName = "";
+ int configCount = SK_ARRAY_COUNT(gConfigs);
+
+ char* const* stop = argv + argc;
+ for (++argv; argv < stop; ++argv) {
+ if (strcmp(*argv, "-o") == 0) {
+ argv++;
+ if (argv < stop && **argv) {
+ outDir.set(*argv);
+ if (outDir.c_str()[outDir.size() - 1] != '/') {
+ outDir.append("/");
+ }
+ }
+ } else if (strcmp(*argv, "-pict") == 0) {
+ doPict = true;
+ } else if (strcmp(*argv, "-repeat") == 0) {
+ argv++;
+ if (argv < stop) {
+ repeatDraw = atoi(*argv);
+ if (repeatDraw < 1) {
+ repeatDraw = 1;
+ }
+ } else {
+ log_error("missing arg for -repeat\n");
+ return -1;
+ }
+ } else if (!strcmp(*argv, "-rotate")) {
+ doRotate = true;
+ } else if (!strcmp(*argv, "-scale")) {
+ doScale = true;
+ } else if (!strcmp(*argv, "-clip")) {
+ doClip = true;
+ } else if (strcmp(*argv, "-forceAA") == 0) {
+ if (!parse_bool_arg(++argv, stop, &forceAA)) {
+ log_error("missing arg for -forceAA\n");
+ return -1;
+ }
+ } else if (strcmp(*argv, "-forceFilter") == 0) {
+ if (!parse_bool_arg(++argv, stop, &forceFilter)) {
+ log_error("missing arg for -forceFilter\n");
+ return -1;
+ }
+ } else if (strcmp(*argv, "-forceBlend") == 0) {
+ bool wantAlpha = false;
+ if (!parse_bool_arg(++argv, stop, &wantAlpha)) {
+ log_error("missing arg for -forceBlend\n");
+ return -1;
+ }
+ forceAlpha = wantAlpha ? 0x80 : 0xFF;
+ } else if (strcmp(*argv, "-match") == 0) {
+ argv++;
+ if (argv < stop) {
+ matchStr = *argv;
+ } else {
+ log_error("missing arg for -match\n");
+ return -1;
+ }
+ } else if (strcmp(*argv, "-config") == 0) {
+ argv++;
+ if (argv < stop) {
+ int index = findConfig(*argv);
+ if (index >= 0) {
+ outConfig = gConfigs[index].fConfig;
+ configName = gConfigs[index].fName;
+ configCount = 1;
+ } else {
+ SkString str;
+ str.printf("unrecognized config %s\n", *argv);
+ log_error(str);
+ return -1;
+ }
+ } else {
+ log_error("missing arg for -config\n");
+ return -1;
+ }
+ } else {
+ SkString str;
+ str.printf("unrecognized arg %s\n", *argv);
+ log_error(str);
+ return -1;
+ }
+ }
+
+ Iter iter;
+ SkBenchmark* bench;
+ while ((bench = iter.next()) != NULL) {
+ SkIPoint dim = bench->getSize();
+ if (dim.fX <= 0 || dim.fY <= 0) {
+ continue;
+ }
+
+ bench->setForceAlpha(forceAlpha);
+ bench->setForceAA(forceAA);
+ bench->setForceFilter(forceFilter);
+
+ // only run benchmarks if their name contains matchStr
+ if (matchStr && strstr(bench->getName(), matchStr) == NULL) {
+ continue;
+ }
+
+ {
+ SkString str;
+ str.printf("running bench %16s", bench->getName());
+ log_progress(str);
+ }
+
+ for (int configIndex = 0; configIndex < configCount; configIndex++) {
+ if (configCount > 1) {
+ outConfig = gConfigs[configIndex].fConfig;
+ configName = gConfigs[configIndex].fName;
+ }
+
+ SkBitmap bm;
+ bm.setConfig(outConfig, dim.fX, dim.fY);
+ bm.allocPixels();
+ erase(bm);
+
+ SkCanvas canvas(bm);
+
+ if (doClip) {
+ performClip(&canvas, dim.fX, dim.fY);
+ }
+ if (doScale) {
+ performScale(&canvas, dim.fX, dim.fY);
+ }
+ if (doRotate) {
+ performRotate(&canvas, dim.fX, dim.fY);
+ }
+
+ SkMSec now = SkTime::GetMSecs();
+ for (int i = 0; i < repeatDraw; i++) {
+ SkCanvas* c = &canvas;
+
+ SkNWayCanvas nway;
+ SkPicture* pict = NULL;
+ if (doPict) {
+ pict = new SkPicture;
+ nway.addCanvas(pict->beginRecording(bm.width(), bm.height()));
+ nway.addCanvas(&canvas);
+ c = &nway;
+ }
+
+ SkAutoCanvasRestore acr(c, true);
+ bench->draw(c);
+
+ if (pict) {
+ compare_pict_to_bitmap(pict, bm);
+ pict->unref();
+ }
+ }
+ if (repeatDraw > 1) {
+ SkString str;
+ str.printf(" %4s:%7.2f", configName,
+ (SkTime::GetMSecs() - now) / (double)repeatDraw);
+ log_progress(str);
+ }
+ if (outDir.size() > 0) {
+ saveFile(bench->getName(), configName, outDir.c_str(), bm);
+ }
+ }
+ log_progress("\n");
+ }
+
+ return 0;
+}