54f0d1b711
I removed unused parameters in the tests wherever it was trivial to do so. I'm trying to get the easy ones out of the way before we get into more involved discussions around this. Review URL: https://codereview.appspot.com/7394055 git-svn-id: http://skia.googlecode.com/svn/trunk@7891 2bbb7eff-a529-9590-31e7-b0007b416f81
480 lines
17 KiB
C++
480 lines
17 KiB
C++
|
|
/*
|
|
* Copyright 2011 Google Inc.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include "Test.h"
|
|
#include "SkCanvas.h"
|
|
#include "SkColorPriv.h"
|
|
#include "SkDevice.h"
|
|
#include "SkMathPriv.h"
|
|
#include "SkRegion.h"
|
|
#if SK_SUPPORT_GPU
|
|
#include "SkGpuDevice.h"
|
|
#include "GrContextFactory.h"
|
|
#else
|
|
class GrContext;
|
|
class GrContextFactory;
|
|
#endif
|
|
|
|
static const int DEV_W = 100, DEV_H = 100;
|
|
static const SkIRect DEV_RECT = SkIRect::MakeWH(DEV_W, DEV_H);
|
|
static const SkRect DEV_RECT_S = SkRect::MakeWH(DEV_W * SK_Scalar1,
|
|
DEV_H * SK_Scalar1);
|
|
static const U8CPU DEV_PAD = 0xee;
|
|
|
|
namespace {
|
|
SkPMColor getCanvasColor(int x, int y) {
|
|
SkASSERT(x >= 0 && x < DEV_W);
|
|
SkASSERT(y >= 0 && y < DEV_H);
|
|
|
|
U8CPU r = x;
|
|
U8CPU g = y;
|
|
U8CPU b = 0xc;
|
|
|
|
U8CPU a = 0x0;
|
|
switch ((x+y) % 5) {
|
|
case 0:
|
|
a = 0xff;
|
|
break;
|
|
case 1:
|
|
a = 0x80;
|
|
break;
|
|
case 2:
|
|
a = 0xCC;
|
|
break;
|
|
case 3:
|
|
a = 0x00;
|
|
break;
|
|
case 4:
|
|
a = 0x01;
|
|
break;
|
|
}
|
|
return SkPremultiplyARGBInline(a, r, g, b);
|
|
}
|
|
|
|
bool config8888IsPremul(SkCanvas::Config8888 config8888) {
|
|
switch (config8888) {
|
|
case SkCanvas::kNative_Premul_Config8888:
|
|
case SkCanvas::kBGRA_Premul_Config8888:
|
|
case SkCanvas::kRGBA_Premul_Config8888:
|
|
return true;
|
|
case SkCanvas::kNative_Unpremul_Config8888:
|
|
case SkCanvas::kBGRA_Unpremul_Config8888:
|
|
case SkCanvas::kRGBA_Unpremul_Config8888:
|
|
return false;
|
|
default:
|
|
SkASSERT(0);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// assumes any premu/.unpremul has been applied
|
|
uint32_t packConfig8888(SkCanvas::Config8888 config8888,
|
|
U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
|
|
uint32_t r32;
|
|
uint8_t* result = reinterpret_cast<uint8_t*>(&r32);
|
|
switch (config8888) {
|
|
case SkCanvas::kNative_Premul_Config8888:
|
|
case SkCanvas::kNative_Unpremul_Config8888:
|
|
r32 = SkPackARGB32NoCheck(a, r, g, b);
|
|
break;
|
|
case SkCanvas::kBGRA_Premul_Config8888:
|
|
case SkCanvas::kBGRA_Unpremul_Config8888:
|
|
result[0] = b;
|
|
result[1] = g;
|
|
result[2] = r;
|
|
result[3] = a;
|
|
break;
|
|
case SkCanvas::kRGBA_Premul_Config8888:
|
|
case SkCanvas::kRGBA_Unpremul_Config8888:
|
|
result[0] = r;
|
|
result[1] = g;
|
|
result[2] = b;
|
|
result[3] = a;
|
|
break;
|
|
default:
|
|
SkASSERT(0);
|
|
return 0;
|
|
}
|
|
return r32;
|
|
}
|
|
|
|
uint32_t getBitmapColor(int x, int y, int w, SkCanvas::Config8888 config8888) {
|
|
int n = y * w + x;
|
|
U8CPU b = n & 0xff;
|
|
U8CPU g = (n >> 8) & 0xff;
|
|
U8CPU r = (n >> 16) & 0xff;
|
|
U8CPU a = 0;
|
|
switch ((x+y) % 5) {
|
|
case 4:
|
|
a = 0xff;
|
|
break;
|
|
case 3:
|
|
a = 0x80;
|
|
break;
|
|
case 2:
|
|
a = 0xCC;
|
|
break;
|
|
case 1:
|
|
a = 0x01;
|
|
break;
|
|
case 0:
|
|
a = 0x00;
|
|
break;
|
|
}
|
|
if (config8888IsPremul(config8888)) {
|
|
r = SkMulDiv255Ceiling(r, a);
|
|
g = SkMulDiv255Ceiling(g, a);
|
|
b = SkMulDiv255Ceiling(b, a);
|
|
}
|
|
return packConfig8888(config8888, a, r, g , b);
|
|
}
|
|
|
|
void fillCanvas(SkCanvas* canvas) {
|
|
static SkBitmap bmp;
|
|
if (bmp.isNull()) {
|
|
bmp.setConfig(SkBitmap::kARGB_8888_Config, DEV_W, DEV_H);
|
|
SkDEBUGCODE(bool alloc = ) bmp.allocPixels();
|
|
SkASSERT(alloc);
|
|
SkAutoLockPixels alp(bmp);
|
|
intptr_t pixels = reinterpret_cast<intptr_t>(bmp.getPixels());
|
|
for (int y = 0; y < DEV_H; ++y) {
|
|
for (int x = 0; x < DEV_W; ++x) {
|
|
SkPMColor* pixel = reinterpret_cast<SkPMColor*>(pixels + y * bmp.rowBytes() + x * bmp.bytesPerPixel());
|
|
*pixel = getCanvasColor(x, y);
|
|
}
|
|
}
|
|
}
|
|
canvas->save();
|
|
canvas->setMatrix(SkMatrix::I());
|
|
canvas->clipRect(DEV_RECT_S, SkRegion::kReplace_Op);
|
|
SkPaint paint;
|
|
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
|
|
canvas->drawBitmap(bmp, 0, 0, &paint);
|
|
canvas->restore();
|
|
}
|
|
|
|
SkPMColor convertConfig8888ToPMColor(SkCanvas::Config8888 config8888,
|
|
uint32_t color,
|
|
bool* premul) {
|
|
const uint8_t* c = reinterpret_cast<uint8_t*>(&color);
|
|
U8CPU a,r,g,b;
|
|
*premul = false;
|
|
switch (config8888) {
|
|
case SkCanvas::kNative_Premul_Config8888:
|
|
return color;
|
|
case SkCanvas::kNative_Unpremul_Config8888:
|
|
*premul = true;
|
|
a = SkGetPackedA32(color);
|
|
r = SkGetPackedR32(color);
|
|
g = SkGetPackedG32(color);
|
|
b = SkGetPackedB32(color);
|
|
break;
|
|
case SkCanvas::kBGRA_Unpremul_Config8888:
|
|
*premul = true; // fallthru
|
|
case SkCanvas::kBGRA_Premul_Config8888:
|
|
a = static_cast<U8CPU>(c[3]);
|
|
r = static_cast<U8CPU>(c[2]);
|
|
g = static_cast<U8CPU>(c[1]);
|
|
b = static_cast<U8CPU>(c[0]);
|
|
break;
|
|
case SkCanvas::kRGBA_Unpremul_Config8888:
|
|
*premul = true; // fallthru
|
|
case SkCanvas::kRGBA_Premul_Config8888:
|
|
a = static_cast<U8CPU>(c[3]);
|
|
r = static_cast<U8CPU>(c[0]);
|
|
g = static_cast<U8CPU>(c[1]);
|
|
b = static_cast<U8CPU>(c[2]);
|
|
break;
|
|
default:
|
|
SkDEBUGFAIL("Unexpected Config8888");
|
|
return 0;
|
|
}
|
|
if (*premul) {
|
|
r = SkMulDiv255Ceiling(r, a);
|
|
g = SkMulDiv255Ceiling(g, a);
|
|
b = SkMulDiv255Ceiling(b, a);
|
|
}
|
|
return SkPackARGB32(a, r, g, b);
|
|
}
|
|
|
|
bool checkPixel(SkPMColor a, SkPMColor b, bool didPremulConversion) {
|
|
if (!didPremulConversion) {
|
|
return a == b;
|
|
}
|
|
int32_t aA = static_cast<int32_t>(SkGetPackedA32(a));
|
|
int32_t aR = static_cast<int32_t>(SkGetPackedR32(a));
|
|
int32_t aG = static_cast<int32_t>(SkGetPackedG32(a));
|
|
int32_t aB = SkGetPackedB32(a);
|
|
|
|
int32_t bA = static_cast<int32_t>(SkGetPackedA32(b));
|
|
int32_t bR = static_cast<int32_t>(SkGetPackedR32(b));
|
|
int32_t bG = static_cast<int32_t>(SkGetPackedG32(b));
|
|
int32_t bB = static_cast<int32_t>(SkGetPackedB32(b));
|
|
|
|
return aA == bA &&
|
|
SkAbs32(aR - bR) <= 1 &&
|
|
SkAbs32(aG - bG) <= 1 &&
|
|
SkAbs32(aB - bB) <= 1;
|
|
}
|
|
|
|
bool checkWrite(skiatest::Reporter* reporter,
|
|
SkCanvas* canvas,
|
|
const SkBitmap& bitmap,
|
|
int writeX, int writeY,
|
|
SkCanvas::Config8888 config8888) {
|
|
SkDevice* dev = canvas->getDevice();
|
|
if (!dev) {
|
|
return false;
|
|
}
|
|
SkBitmap devBmp = dev->accessBitmap(false);
|
|
if (devBmp.width() != DEV_W ||
|
|
devBmp.height() != DEV_H ||
|
|
devBmp.config() != SkBitmap::kARGB_8888_Config ||
|
|
devBmp.isNull()) {
|
|
return false;
|
|
}
|
|
SkAutoLockPixels alp(devBmp);
|
|
|
|
intptr_t canvasPixels = reinterpret_cast<intptr_t>(devBmp.getPixels());
|
|
size_t canvasRowBytes = devBmp.rowBytes();
|
|
SkIRect writeRect = SkIRect::MakeXYWH(writeX, writeY, bitmap.width(), bitmap.height());
|
|
for (int cy = 0; cy < DEV_H; ++cy) {
|
|
const SkPMColor* canvasRow = reinterpret_cast<const SkPMColor*>(canvasPixels);
|
|
for (int cx = 0; cx < DEV_W; ++cx) {
|
|
SkPMColor canvasPixel = canvasRow[cx];
|
|
if (writeRect.contains(cx, cy)) {
|
|
int bx = cx - writeX;
|
|
int by = cy - writeY;
|
|
uint32_t bmpColor8888 = getBitmapColor(bx, by, bitmap.width(), config8888);
|
|
bool mul;
|
|
SkPMColor bmpPMColor = convertConfig8888ToPMColor(config8888, bmpColor8888, &mul);
|
|
bool check;
|
|
REPORTER_ASSERT(reporter, check = checkPixel(bmpPMColor, canvasPixel, mul));
|
|
if (!check) {
|
|
return false;
|
|
}
|
|
} else {
|
|
bool check;
|
|
SkPMColor testColor = getCanvasColor(cx, cy);
|
|
REPORTER_ASSERT(reporter, check = (canvasPixel == testColor));
|
|
if (!check) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
if (cy != DEV_H -1) {
|
|
const char* pad = reinterpret_cast<const char*>(canvasPixels + 4 * DEV_W);
|
|
for (size_t px = 0; px < canvasRowBytes - 4 * DEV_W; ++px) {
|
|
bool check;
|
|
REPORTER_ASSERT(reporter, check = (pad[px] == static_cast<char>(DEV_PAD)));
|
|
if (!check) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
canvasPixels += canvasRowBytes;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
enum DevType {
|
|
kRaster_DevType,
|
|
#if SK_SUPPORT_GPU
|
|
kGpu_BottomLeft_DevType,
|
|
kGpu_TopLeft_DevType,
|
|
#endif
|
|
};
|
|
|
|
struct CanvasConfig {
|
|
DevType fDevType;
|
|
bool fTightRowBytes;
|
|
};
|
|
|
|
static const CanvasConfig gCanvasConfigs[] = {
|
|
{kRaster_DevType, true},
|
|
{kRaster_DevType, false},
|
|
#if SK_SUPPORT_GPU && defined(SK_SCALAR_IS_FLOAT)
|
|
{kGpu_BottomLeft_DevType, true}, // row bytes has no meaning on gpu devices
|
|
{kGpu_TopLeft_DevType, true}, // row bytes has no meaning on gpu devices
|
|
#endif
|
|
};
|
|
|
|
SkDevice* createDevice(const CanvasConfig& c, GrContext* grCtx) {
|
|
switch (c.fDevType) {
|
|
case kRaster_DevType: {
|
|
SkBitmap bmp;
|
|
size_t rowBytes = c.fTightRowBytes ? 0 : 4 * DEV_W + 100;
|
|
bmp.setConfig(SkBitmap::kARGB_8888_Config, DEV_W, DEV_H, rowBytes);
|
|
if (!bmp.allocPixels()) {
|
|
sk_throw();
|
|
return NULL;
|
|
}
|
|
// if rowBytes isn't tight then set the padding to a known value
|
|
if (rowBytes) {
|
|
SkAutoLockPixels alp(bmp);
|
|
memset(bmp.getPixels(), DEV_PAD, bmp.getSafeSize());
|
|
}
|
|
return new SkDevice(bmp);
|
|
}
|
|
#if SK_SUPPORT_GPU
|
|
case kGpu_BottomLeft_DevType:
|
|
case kGpu_TopLeft_DevType:
|
|
GrTextureDesc desc;
|
|
desc.fFlags = kRenderTarget_GrTextureFlagBit;
|
|
desc.fWidth = DEV_W;
|
|
desc.fHeight = DEV_H;
|
|
desc.fConfig = kSkia8888_GrPixelConfig;
|
|
desc.fOrigin = kGpu_TopLeft_DevType == c.fDevType ?
|
|
kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
|
|
GrAutoScratchTexture ast(grCtx, desc, GrContext::kExact_ScratchTexMatch);
|
|
SkAutoTUnref<GrTexture> tex(ast.detach());
|
|
return new SkGpuDevice(grCtx, tex);
|
|
#endif
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
bool setupBitmap(SkBitmap* bitmap,
|
|
SkCanvas::Config8888 config8888,
|
|
int w, int h,
|
|
bool tightRowBytes) {
|
|
size_t rowBytes = tightRowBytes ? 0 : 4 * w + 60;
|
|
bitmap->setConfig(SkBitmap::kARGB_8888_Config, w, h, rowBytes);
|
|
if (!bitmap->allocPixels()) {
|
|
return false;
|
|
}
|
|
SkAutoLockPixels alp(*bitmap);
|
|
intptr_t pixels = reinterpret_cast<intptr_t>(bitmap->getPixels());
|
|
for (int y = 0; y < h; ++y) {
|
|
for (int x = 0; x < w; ++x) {
|
|
uint32_t* pixel = reinterpret_cast<uint32_t*>(pixels + y * bitmap->rowBytes() + x * 4);
|
|
*pixel = getBitmapColor(x, y, w, config8888);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void WritePixelsTest(skiatest::Reporter* reporter, GrContextFactory* factory) {
|
|
SkCanvas canvas;
|
|
|
|
const SkIRect testRects[] = {
|
|
// entire thing
|
|
DEV_RECT,
|
|
// larger on all sides
|
|
SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H + 10),
|
|
// fully contained
|
|
SkIRect::MakeLTRB(DEV_W / 4, DEV_H / 4, 3 * DEV_W / 4, 3 * DEV_H / 4),
|
|
// outside top left
|
|
SkIRect::MakeLTRB(-10, -10, -1, -1),
|
|
// touching top left corner
|
|
SkIRect::MakeLTRB(-10, -10, 0, 0),
|
|
// overlapping top left corner
|
|
SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H / 4),
|
|
// overlapping top left and top right corners
|
|
SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H / 4),
|
|
// touching entire top edge
|
|
SkIRect::MakeLTRB(-10, -10, DEV_W + 10, 0),
|
|
// overlapping top right corner
|
|
SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H / 4),
|
|
// contained in x, overlapping top edge
|
|
SkIRect::MakeLTRB(DEV_W / 4, -10, 3 * DEV_W / 4, DEV_H / 4),
|
|
// outside top right corner
|
|
SkIRect::MakeLTRB(DEV_W + 1, -10, DEV_W + 10, -1),
|
|
// touching top right corner
|
|
SkIRect::MakeLTRB(DEV_W, -10, DEV_W + 10, 0),
|
|
// overlapping top left and bottom left corners
|
|
SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H + 10),
|
|
// touching entire left edge
|
|
SkIRect::MakeLTRB(-10, -10, 0, DEV_H + 10),
|
|
// overlapping bottom left corner
|
|
SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W / 4, DEV_H + 10),
|
|
// contained in y, overlapping left edge
|
|
SkIRect::MakeLTRB(-10, DEV_H / 4, DEV_W / 4, 3 * DEV_H / 4),
|
|
// outside bottom left corner
|
|
SkIRect::MakeLTRB(-10, DEV_H + 1, -1, DEV_H + 10),
|
|
// touching bottom left corner
|
|
SkIRect::MakeLTRB(-10, DEV_H, 0, DEV_H + 10),
|
|
// overlapping bottom left and bottom right corners
|
|
SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10),
|
|
// touching entire left edge
|
|
SkIRect::MakeLTRB(0, DEV_H, DEV_W, DEV_H + 10),
|
|
// overlapping bottom right corner
|
|
SkIRect::MakeLTRB(3 * DEV_W / 4, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10),
|
|
// overlapping top right and bottom right corners
|
|
SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H + 10),
|
|
};
|
|
|
|
for (size_t i = 0; i < SK_ARRAY_COUNT(gCanvasConfigs); ++i) {
|
|
int glCtxTypeCnt = 1;
|
|
#if SK_SUPPORT_GPU
|
|
bool isGPUDevice = kGpu_TopLeft_DevType == gCanvasConfigs[i].fDevType ||
|
|
kGpu_BottomLeft_DevType == gCanvasConfigs[i].fDevType;
|
|
if (isGPUDevice) {
|
|
glCtxTypeCnt = GrContextFactory::kGLContextTypeCnt;
|
|
}
|
|
#endif
|
|
for (int glCtxType = 0; glCtxType < glCtxTypeCnt; ++glCtxType) {
|
|
GrContext* context = NULL;
|
|
#if SK_SUPPORT_GPU
|
|
if (isGPUDevice) {
|
|
GrContextFactory::GLContextType type =
|
|
static_cast<GrContextFactory::GLContextType>(glCtxType);
|
|
if (!GrContextFactory::IsRenderingGLContext(type)) {
|
|
continue;
|
|
}
|
|
context = factory->get(type);
|
|
if (NULL == context) {
|
|
continue;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
SkAutoTUnref<SkDevice> device(createDevice(gCanvasConfigs[i], context));
|
|
SkCanvas canvas(device);
|
|
|
|
static const SkCanvas::Config8888 gSrcConfigs[] = {
|
|
SkCanvas::kNative_Premul_Config8888,
|
|
SkCanvas::kNative_Unpremul_Config8888,
|
|
SkCanvas::kBGRA_Premul_Config8888,
|
|
SkCanvas::kBGRA_Unpremul_Config8888,
|
|
SkCanvas::kRGBA_Premul_Config8888,
|
|
SkCanvas::kRGBA_Unpremul_Config8888,
|
|
};
|
|
for (size_t r = 0; r < SK_ARRAY_COUNT(testRects); ++r) {
|
|
const SkIRect& rect = testRects[r];
|
|
for (int tightBmp = 0; tightBmp < 2; ++tightBmp) {
|
|
for (size_t c = 0; c < SK_ARRAY_COUNT(gSrcConfigs); ++c) {
|
|
fillCanvas(&canvas);
|
|
SkCanvas::Config8888 config8888 = gSrcConfigs[c];
|
|
SkBitmap bmp;
|
|
REPORTER_ASSERT(reporter, setupBitmap(&bmp, config8888, rect.width(), rect.height(), SkToBool(tightBmp)));
|
|
uint32_t idBefore = canvas.getDevice()->accessBitmap(false).getGenerationID();
|
|
canvas.writePixels(bmp, rect.fLeft, rect.fTop, config8888);
|
|
uint32_t idAfter = canvas.getDevice()->accessBitmap(false).getGenerationID();
|
|
REPORTER_ASSERT(reporter, checkWrite(reporter, &canvas, bmp, rect.fLeft, rect.fTop, config8888));
|
|
|
|
// we should change the genID iff pixels were actually written.
|
|
SkIRect canvasRect = SkIRect::MakeSize(canvas.getDeviceSize());
|
|
SkIRect writeRect = SkIRect::MakeXYWH(rect.fLeft, rect.fTop,
|
|
bmp.width(), bmp.height());
|
|
bool intersects = SkIRect::Intersects(canvasRect, writeRect) ;
|
|
REPORTER_ASSERT(reporter, intersects == (idBefore != idAfter));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef SK_BUILD_FOR_ANDROID
|
|
#include "TestClassDef.h"
|
|
DEFINE_GPUTESTCLASS("WritePixels", WritePixelsTestClass, WritePixelsTest)
|
|
#endif
|