241 lines
8.5 KiB
C++
241 lines
8.5 KiB
C++
/*
|
|
* Copyright 2017 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 "SkAutoPixmapStorage.h"
|
|
#include "SkColorPriv.h"
|
|
#include "SkImage.h"
|
|
#include "SkPath.h"
|
|
#include "SkSurface.h"
|
|
|
|
namespace skiagm {
|
|
|
|
static void draw_diff(SkCanvas* canvas, SkImage* imgA, SkImage* imgB) {
|
|
SkASSERT(imgA->dimensions() == imgB->dimensions());
|
|
|
|
int w = imgA->width(), h = imgA->height();
|
|
|
|
// First, draw the two images faintly overlaid
|
|
SkPaint paint;
|
|
paint.setAlpha(64);
|
|
paint.setBlendMode(SkBlendMode::kPlus);
|
|
canvas->drawImage(imgA, 0, 0, &paint);
|
|
canvas->drawImage(imgB, 0, 0, &paint);
|
|
|
|
// Next, read the pixels back, figure out if there are any differences
|
|
SkImageInfo info = SkImageInfo::MakeN32Premul(w, h);
|
|
SkAutoPixmapStorage pmapA;
|
|
SkAutoPixmapStorage pmapB;
|
|
pmapA.alloc(info);
|
|
pmapB.alloc(info);
|
|
if (!imgA->readPixels(pmapA, 0, 0) || !imgB->readPixels(pmapB, 0, 0)) {
|
|
return;
|
|
}
|
|
|
|
int maxDiffX = 0, maxDiffY = 0, maxDiff = 0;
|
|
SkBitmap highlight;
|
|
highlight.allocN32Pixels(w, h);
|
|
highlight.eraseColor(SK_ColorTRANSPARENT);
|
|
|
|
for (int y = 0; y < h; ++y) {
|
|
for (int x = 0; x < w; ++x) {
|
|
uint32_t pixelA = *pmapA.addr32(x, y);
|
|
uint32_t pixelB = *pmapB.addr32(x, y);
|
|
if (pixelA != pixelB) {
|
|
int diff =
|
|
SkTAbs((int)(SkColorGetR(pixelA) - SkColorGetR(pixelB))) +
|
|
SkTAbs((int)(SkColorGetG(pixelA) - SkColorGetG(pixelB))) +
|
|
SkTAbs((int)(SkColorGetB(pixelA) - SkColorGetB(pixelB))) +
|
|
SkTAbs((int)(SkColorGetA(pixelA) - SkColorGetA(pixelB)));
|
|
if (diff > maxDiff) {
|
|
maxDiffX = x;
|
|
maxDiffY = y;
|
|
maxDiff = diff;
|
|
}
|
|
*highlight.getAddr32(x, y) = SkPackARGB32(0xA0, 0xA0, 0x00, 0x00);
|
|
}
|
|
}
|
|
}
|
|
|
|
SkPaint outline;
|
|
outline.setStyle(SkPaint::kStroke_Style);
|
|
outline.setColor(maxDiff == 0 ? 0xFF007F00 : 0xFF7F0000);
|
|
|
|
if (maxDiff > 0) {
|
|
// Call extra attention to the region we're going to zoom
|
|
SkPMColor yellow = SkPackARGB32(0xFF, 0xFF, 0xFF, 0x00);
|
|
*highlight.getAddr32(maxDiffX, maxDiffY) = yellow;
|
|
*highlight.getAddr32(SkTMax(maxDiffX - 1, 0), maxDiffY) = yellow;
|
|
*highlight.getAddr32(maxDiffX, SkTMax(maxDiffY - 1, 0)) = yellow;
|
|
*highlight.getAddr32(SkTMin(maxDiffX + 1, w - 1), maxDiffY) = yellow;
|
|
*highlight.getAddr32(maxDiffX, SkTMin(maxDiffY + 1, h - 1)) = yellow;
|
|
|
|
// Draw the overlay
|
|
canvas->drawBitmap(highlight, 0, 0);
|
|
|
|
// Draw zoom of largest pixel diff
|
|
SkBitmap bmpA, bmpB;
|
|
SkAssertResult(bmpA.installPixels(pmapA));
|
|
SkAssertResult(bmpB.installPixels(pmapB));
|
|
canvas->drawBitmapRect(bmpA, SkRect::MakeXYWH(maxDiffX - 5, maxDiffY - 5, 10, 10),
|
|
SkRect::MakeXYWH(w, 0, w, h), nullptr);
|
|
canvas->drawBitmapRect(bmpB, SkRect::MakeXYWH(maxDiffX - 5, maxDiffY - 5, 10, 10),
|
|
SkRect::MakeXYWH(2 * w, 0, w, h), nullptr);
|
|
|
|
// Add lines to separate zoom boxes
|
|
canvas->drawLine(w, 0, w, h, outline);
|
|
canvas->drawLine(2 * w, 0, 2 * w, h, outline);
|
|
}
|
|
|
|
// Draw outline of whole test region
|
|
canvas->drawRect(SkRect::MakeWH(3 * w, h), outline);
|
|
}
|
|
|
|
namespace {
|
|
typedef std::function<void(SkCanvas*, const SkRect&, const SkPaint&)> ShapeDrawFunc;
|
|
}
|
|
|
|
/**
|
|
* Iterates over a variety of rect shapes, paint parameters, and matrices, calling two different
|
|
* user-supplied draw callbacks. Produces a grid clearly showing if the two callbacks produce the
|
|
* same visual results in all cases.
|
|
*/
|
|
static void draw_rect_geom_diff_grid(SkCanvas* canvas, ShapeDrawFunc f1, ShapeDrawFunc f2) {
|
|
// Variables:
|
|
// - Fill, hairline, wide stroke
|
|
// - Axis aligned, rotated, scaled, scaled negative, perspective
|
|
// - Source geometry (normal, collapsed, inverted)
|
|
//
|
|
// Things not (yet?) tested:
|
|
// - AntiAlias on/off
|
|
// - StrokeAndFill
|
|
// - Cap/join
|
|
// - Anything even more elaborate...
|
|
|
|
const SkRect kRects[] = {
|
|
SkRect::MakeXYWH(10, 10, 30, 30), // Normal
|
|
SkRect::MakeXYWH(10, 25, 30, 0), // Collapsed
|
|
SkRect::MakeXYWH(10, 40, 30, -30), // Inverted
|
|
};
|
|
|
|
const struct { SkPaint::Style fStyle; SkScalar fStrokeWidth; } kStyles[] = {
|
|
{ SkPaint::kFill_Style, 0 }, // Filled
|
|
{ SkPaint::kStroke_Style, 0 }, // Hairline
|
|
{ SkPaint::kStroke_Style, 5 }, // Wide stroke
|
|
};
|
|
|
|
SkMatrix mI = SkMatrix::I();
|
|
SkMatrix mRot;
|
|
mRot.setRotate(30, 25, 25);
|
|
SkMatrix mScale;
|
|
mScale.setScaleTranslate(0.5f, 1, 12.5f, 0);
|
|
SkMatrix mFlipX;
|
|
mFlipX.setScaleTranslate(-1, 1, 50, 0);
|
|
SkMatrix mFlipY;
|
|
mFlipY.setScaleTranslate(1, -1, 0, 50);
|
|
SkMatrix mFlipXY;
|
|
mFlipXY.setScaleTranslate(-1, -1, 50, 50);
|
|
SkMatrix mPersp;
|
|
mPersp.setIdentity();
|
|
mPersp.setPerspY(0.002f);
|
|
|
|
const SkMatrix* kMatrices[] = { &mI, &mRot, &mScale, &mFlipX, &mFlipY, &mFlipXY, &mPersp, };
|
|
|
|
canvas->translate(10, 10);
|
|
|
|
SkImageInfo info = canvas->imageInfo().makeWH(50, 50);
|
|
auto surface = canvas->makeSurface(info);
|
|
if (!surface) {
|
|
surface = SkSurface::MakeRasterN32Premul(50, 50);
|
|
}
|
|
|
|
for (const SkRect& rect : kRects) {
|
|
for (const auto& style : kStyles) {
|
|
canvas->save();
|
|
|
|
for (const SkMatrix* mat : kMatrices) {
|
|
SkPaint paint;
|
|
paint.setColor(SK_ColorWHITE);
|
|
paint.setAntiAlias(true);
|
|
paint.setStyle(style.fStyle);
|
|
paint.setStrokeWidth(style.fStrokeWidth);
|
|
|
|
// Do first draw
|
|
surface->getCanvas()->clear(SK_ColorBLACK);
|
|
surface->getCanvas()->save();
|
|
surface->getCanvas()->concat(*mat);
|
|
f1(surface->getCanvas(), rect, paint);
|
|
surface->getCanvas()->restore();
|
|
auto imgA = surface->makeImageSnapshot();
|
|
|
|
// Do second draw
|
|
surface->getCanvas()->clear(SK_ColorBLACK);
|
|
surface->getCanvas()->save();
|
|
surface->getCanvas()->concat(*mat);
|
|
f2(surface->getCanvas(), rect, paint);
|
|
surface->getCanvas()->restore();
|
|
auto imgB = surface->makeImageSnapshot();
|
|
|
|
draw_diff(canvas, imgA.get(), imgB.get());
|
|
canvas->translate(160, 0);
|
|
}
|
|
canvas->restore();
|
|
canvas->translate(0, 60);
|
|
}
|
|
}
|
|
}
|
|
|
|
static const int kNumRows = 9;
|
|
static const int kNumColumns = 7;
|
|
static const int kTotalWidth = kNumColumns * 160 + 10;
|
|
static const int kTotalHeight = kNumRows * 60 + 10;
|
|
|
|
DEF_SIMPLE_GM_BG(rects_as_paths, canvas, kTotalWidth, kTotalHeight, SK_ColorBLACK) {
|
|
// Drawing a rect vs. adding it to a path and drawing the path, should produce same results.
|
|
auto rectDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) {
|
|
canvas->drawRect(rect, paint);
|
|
};
|
|
auto pathDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) {
|
|
SkPath path;
|
|
path.addRect(rect);
|
|
canvas->drawPath(path, paint);
|
|
};
|
|
|
|
draw_rect_geom_diff_grid(canvas, rectDrawFunc, pathDrawFunc);
|
|
}
|
|
|
|
DEF_SIMPLE_GM_BG(ovals_as_paths, canvas, kTotalWidth, kTotalHeight, SK_ColorBLACK) {
|
|
// Drawing an oval vs. adding it to a path and drawing the path, should produce same results.
|
|
auto ovalDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) {
|
|
canvas->drawOval(rect, paint);
|
|
};
|
|
auto pathDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) {
|
|
SkPath path;
|
|
path.addOval(rect);
|
|
canvas->drawPath(path, paint);
|
|
};
|
|
|
|
draw_rect_geom_diff_grid(canvas, ovalDrawFunc, pathDrawFunc);
|
|
}
|
|
|
|
DEF_SIMPLE_GM_BG(arcs_as_paths, canvas, kTotalWidth, kTotalHeight, SK_ColorBLACK) {
|
|
// Drawing an arc vs. adding it to a path and drawing the path, should produce same results.
|
|
auto arcDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) {
|
|
canvas->drawArc(rect, 10, 200, false, paint);
|
|
};
|
|
auto pathDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) {
|
|
SkPath path;
|
|
path.addArc(rect, 10, 200);
|
|
canvas->drawPath(path, paint);
|
|
};
|
|
|
|
draw_rect_geom_diff_grid(canvas, arcDrawFunc, pathDrawFunc);
|
|
}
|
|
|
|
}
|