Revert r2584 (new test fails in fixed pt builds)

git-svn-id: http://skia.googlecode.com/svn/trunk@2585 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
bsalomon@google.com 2011-11-02 19:39:51 +00:00
parent 1a8ddf0a35
commit ace7bd5623
20 changed files with 108 additions and 520 deletions

View File

@ -251,9 +251,7 @@ static bool generate_image(GM* gm, const ConfigData& gRec,
// the device is as large as the current rendertarget, so we explicitly
// only readback the amount we expect (in size)
// overwrite our previous allocation
bitmap->setConfig(SkBitmap::kARGB_8888_Config, size.fWidth,
size.fHeight);
gc.readPixels(bitmap, 0, 0);
gc.readPixels(SkIRect::MakeSize(size), bitmap);
}
return true;
}

View File

@ -47,7 +47,6 @@
'../tests/PDFPrimitivesTest.cpp',
'../tests/PointTest.cpp',
'../tests/Reader32Test.cpp',
'../tests/ReadPixelsTest.cpp',
'../tests/RefDictTest.cpp',
'../tests/RegionTest.cpp',
'../tests/Sk64Test.cpp',

View File

@ -225,11 +225,12 @@ public:
/** Copies the bitmap's pixels to the location pointed at by dst and returns
true if possible, returns false otherwise.
In the case when the dstRowBytes matches the bitmap's rowBytes, the copy
may be made faster by copying over the dst's per-row padding (for all
rows but the last). By setting preserveDstPad to true the caller can
disable this optimization and ensure that pixels in the padding are not
overwritten.
In the event that the bitmap's stride is equal to dstRowBytes, and if
it is greater than strictly required by the bitmap's current config
(this may happen if the bitmap is an extracted subset of another), then
this function will copy bytes past the eand of each row, excluding the
last row. No copies are made outside of the declared size of dst,
however.
Always returns false for RLE formats.
@ -238,10 +239,8 @@ public:
pixels using indicated stride.
@param dstRowBytes Width of each line in the buffer. If -1, uses
bitmap's internal stride.
@param preserveDstPad Must we preserve padding in the dst
*/
bool copyPixelsTo(void* const dst, size_t dstSize, int dstRowBytes = -1,
bool preserveDstPad = false)
bool copyPixelsTo(void* const dst, size_t dstSize, int dstRowBytes = -1)
const;
/** Use the standard HeapAllocator to create the pixelref that manages the

View File

@ -105,45 +105,14 @@ public:
///////////////////////////////////////////////////////////////////////////
/**
* On success (returns true), copy the canvas pixels into the bitmap.
* On failure, the bitmap parameter is left unchanged and false is
* returned.
*
* If the canvas is backed by a non-raster device (e.g. PDF) then
* readPixels will fail.
*
* If the bitmap has pixels already allocated, the canvas pixels will be
* written there. If not, bitmap->allocPixels() will be called
* automatically. If the bitmap is backed by a texture readPixels will
* fail.
*
* The canvas' pixels are converted to the bitmap's config. The only
* supported config is kARGB_8888_Config, though this may be relaxed in
* future.
*
* The actual pixels written is the intersection of the canvas' bounds, and
* the rectangle formed by the bitmap's width,height and the specified x,y.
* If bitmap pixels extend outside of that intersection, they will not be
* modified.
*
* Example that reads the entire canvas into a bitmap:
* SkISize size = canvas->getDeviceSize();
* bitmap->setConfig(SkBitmap::kARGB_8888_Config, size.fWidth,
* size.fHeight);
* if (canvas->readPixels(bitmap, 0, 0)) {
* // use the pixels
* }
*/
bool readPixels(SkBitmap* bitmap, int x, int y);
/**
* DEPRECATED: This will be removed as soon as webkit is no longer relying
* on it. The bitmap is resized to the intersection of srcRect and the
* canvas bounds. New pixels are always allocated on success. Bitmap is
* unmodified on failure.
* Copy the pixels from the device into bitmap. Returns true on success.
* If false is returned, then the bitmap parameter is left unchanged.
* The bitmap parameter is treated as output-only, and will be completely
* overwritten (if the method returns true).
*/
bool readPixels(const SkIRect& srcRect, SkBitmap* bitmap);
bool readPixels(SkBitmap* bitmap);
/**
* Similar to draw sprite, this method will copy the pixels in bitmap onto

View File

@ -108,23 +108,11 @@ public:
/**
* Copy the pixels from the device into bitmap. Returns true on success.
* If false is returned, then the bitmap parameter is left unchanged. The
* rectangle read is defined by x, y and the bitmap's width and height.
*
* If the bitmap has pixels allocated the canvas will write directly to
* into that memory (if the call succeeds).
*
* The read is clipped to the device bounds. If bitmap pixels were
* preallocated then pixels outside the clip are left unmodified. If the
* call allocates bitmap pixels then pixels outside the clip will be
* uninitialized.
*
* Currently bitmap must have kARGB_8888_Config or readPixels will fail.
* This will likely be relaxed in the future.
*
* The bitmap parameter is not modified if the call fails.
* If false is returned, then the bitmap parameter is left unchanged.
* The bitmap parameter is treated as output-only, and will be completely
* overwritten (if the method returns true).
*/
bool readPixels(SkBitmap* bitmap, int x, int y);
virtual bool readPixels(const SkIRect& srcRect, SkBitmap* bitmap);
/**
* Similar to draw sprite, this method will copy the pixels in bitmap onto
@ -268,17 +256,6 @@ protected:
fBitmap.setPixelRef(pr, offset);
return pr;
}
/**
* Implements readPixels API. The caller will ensure that:
* 1. bitmap has pixel config kARGB_8888_Config.
* 2. bitmap has pixels.
* 3. The rectangle (x, y, x + bitmap->width(), y + bitmap->height()) is
* contained in the device bounds.
* 4. the bitmap struct is safe to partially overwrite in case of failure
*/
virtual bool onReadPixels(const SkBitmap* bitmap, int x, int y);
/** Called when this device is installed into a Canvas. Balanaced by a call
to unlockPixels() when the device is removed from a Canvas.

View File

@ -71,6 +71,11 @@ public:
return kVector_Capability;
}
virtual bool readPixels(const SkIRect& srcRect,
SkBitmap* bitmap) SK_OVERRIDE {
return false;
}
protected:
virtual void clear(SkColor color) SK_OVERRIDE;
@ -141,12 +146,6 @@ protected:
int x, int y,
const SkPaint& paint) SK_OVERRIDE;
virtual bool onReadPixels(const SkBitmap* bitmap,
int x,
int y) SK_OVERRIDE {
return false;
}
private:
class TypefaceUse : ::SkNoncopyable {
public:

View File

@ -432,8 +432,6 @@ public:
* @param height height of rectangle to read in pixels.
* @param config the pixel config of the destination buffer
* @param buffer memory to read the rectangle into.
* @param rowBytes number of bytes bewtween consecueive rows. Zero
* means rows are tightly packed.
*
* @return true if the read succeeded, false if not. The read can fail
* because of a unsupported pixel config or because no render
@ -441,8 +439,7 @@ public:
*/
bool readRenderTargetPixels(GrRenderTarget* target,
int left, int top, int width, int height,
GrPixelConfig config, void* buffer,
size_t rowBytes = 0);
GrPixelConfig config, void* buffer);
/**
* Reads a rectangle of pixels from a texture.

View File

@ -348,9 +348,7 @@
#define GR_GL_EXTENSIONS 0x1F03
/* Pixel Mode / Transfer */
#define GR_GL_UNPACK_ROW_LENGTH 0x0CF2
#define GR_GL_PACK_ROW_LENGTH 0x0D02
#define GR_GL_UNPACK_ROW_LENGTH 0x0CF2
/* TextureMagFilter */
#define GR_GL_NEAREST 0x2600

View File

@ -68,6 +68,7 @@ public:
// overrides from SkDevice
virtual void clear(SkColor color);
virtual bool readPixels(const SkIRect& srcRect, SkBitmap* bitmap);
virtual void writePixels(const SkBitmap& bitmap, int x, int y);
virtual void setMatrixClip(const SkMatrix& matrix, const SkRegion& clip,
@ -139,11 +140,6 @@ protected:
TexCache fTex;
};
friend class SkAutoTexCache;
// overrides from SkDevice
virtual bool onReadPixels(const SkBitmap* bitmap,
int x, int y) SK_OVERRIDE;
private:
GrContext* fContext;

View File

@ -66,6 +66,10 @@ public:
virtual void clear(SkColor color);
virtual bool readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
return false;
}
/** These are called inside the per-device-layer loop for each draw call.
When these are called, we have already applied any saveLayer operations,
and are handling any looping from the paint, and any effects from the
@ -156,13 +160,6 @@ public:
const SkPDFGlyphSetMap& getFontGlyphUsage() const {
return *(fFontGlyphUsage.get());
}
protected:
virtual bool onReadPixels(const SkBitmap* bitmap,
int x, int y) SK_OVERRIDE {
return false;
}
private:
// TODO(vandebo): push most of SkPDFDevice's state into a core object in

View File

@ -456,8 +456,8 @@ Sk64 SkBitmap::getSafeSize64() const {
return ComputeSafeSize64(getConfig(), fWidth, fHeight, fRowBytes);
}
bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize,
int dstRowBytes, bool preserveDstPad) const {
bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize, int dstRowBytes)
const {
if (dstRowBytes == -1)
dstRowBytes = fRowBytes;
@ -468,7 +468,7 @@ bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize,
dst == NULL || (getPixels() == NULL && pixelRef() == NULL))
return false;
if (!preserveDstPad && static_cast<uint32_t>(dstRowBytes) == fRowBytes) {
if (static_cast<uint32_t>(dstRowBytes) == fRowBytes) {
size_t safeSize = getSafeSize();
if (safeSize > dstSize || safeSize == 0)
return false;

View File

@ -550,32 +550,24 @@ SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
return device;
}
bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
SkDevice* device = this->getDevice();
if (!device) {
return false;
}
return device->readPixels(bitmap, x, y);
return device->readPixels(srcRect, bitmap);
}
bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
SkDevice* device = this->getDevice();
SkIRect bounds;
bounds.set(0, 0, device->width(), device->height());
if (!bounds.intersect(srcRect)) {
return false;
}
//////////////////////////////////////////////////////////////////////////////
SkBitmap tmp;
tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
bounds.height());
if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
bitmap->swap(tmp);
return true;
} else {
bool SkCanvas::readPixels(SkBitmap* bitmap) {
SkDevice* device = this->getDevice();
if (!device) {
return false;
}
SkIRect bounds;
bounds.set(0, 0, device->width(), device->height());
return this->readPixels(bounds, bitmap);
}
void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {

View File

@ -102,70 +102,27 @@ void SkDevice::setMatrixClip(const SkMatrix& matrix, const SkRegion& region,
///////////////////////////////////////////////////////////////////////////////
bool SkDevice::readPixels(SkBitmap* bitmap, int x, int y) {
if (SkBitmap::kARGB_8888_Config != bitmap->config() ||
NULL != bitmap->getTexture()) {
bool SkDevice::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
const SkBitmap& src = this->accessBitmap(false);
SkIRect bounds;
bounds.set(0, 0, src.width(), src.height());
if (!bounds.intersect(srcRect)) {
return false;
}
const SkBitmap& src = this->accessBitmap(false);
SkIRect srcRect = SkIRect::MakeXYWH(x, y, bitmap->width(),
bitmap->height());
SkIRect devbounds = SkIRect::MakeWH(src.width(), src.height());
if (!srcRect.intersect(devbounds)) {
SkBitmap subset;
if (!src.extractSubset(&subset, bounds)) {
return false;
}
SkBitmap tmp;
SkBitmap* bmp;
if (bitmap->isNull()) {
tmp.setConfig(SkBitmap::kARGB_8888_Config, bitmap->width(),
bitmap->height());
if (!tmp.allocPixels()) {
return false;
}
bmp = &tmp;
} else {
bmp = bitmap;
}
SkIRect subrect = srcRect;
subrect.offset(-x, -y);
SkBitmap bmpSubset;
bmp->extractSubset(&bmpSubset, subrect);
bool result = this->onReadPixels(&bmpSubset, srcRect.fLeft, srcRect.fTop);
if (result && bmp == &tmp) {
tmp.swap(*bitmap);
}
return result;
}
bool SkDevice::onReadPixels(const SkBitmap* bitmap, int x, int y) {
SkASSERT(SkBitmap::kARGB_8888_Config == bitmap->config());
SkASSERT(!bitmap->isNull());
SkASSERT(SkIRect::MakeWH(this->width(), this->height()).contains(SkIRect::MakeXYWH(x, y, bitmap->width(), bitmap->height())));
SkIRect srcRect = SkIRect::MakeXYWH(x, y, bitmap->width(),
bitmap->height());
const SkBitmap& src = this->accessBitmap(false);
SkBitmap subset;
if (!src.extractSubset(&subset, srcRect)) {
if (!subset.copyTo(&tmp, SkBitmap::kARGB_8888_Config)) {
return false;
}
if (SkBitmap::kARGB_8888_Config != subset.config()) {
// It'd be preferable to do this directly to bitmap.
// We'd need a SkBitmap::copyPixelsTo that takes a config
// or make copyTo lazily allocate.
subset.copyTo(&subset, SkBitmap::kARGB_8888_Config);
}
SkAutoLockPixels alp(*bitmap);
return subset.copyPixelsTo(bitmap->getPixels(),
bitmap->getSize(),
bitmap->rowBytes(),
true);
tmp.swap(*bitmap);
return true;
}
void SkDevice::writePixels(const SkBitmap& bitmap, int x, int y) {

View File

@ -1637,16 +1637,15 @@ bool GrContext::readTexturePixels(GrTexture* texture,
if (NULL != target) {
return fGpu->readPixels(target,
left, top, width, height,
config, buffer, 0);
config, buffer);
} else {
return false;
}
}
bool GrContext::readRenderTargetPixels(GrRenderTarget* target,
int left, int top, int width, int height,
GrPixelConfig config, void* buffer,
size_t rowBytes) {
int left, int top, int width, int height,
GrPixelConfig config, void* buffer) {
SK_TRACE_EVENT0("GrContext::readRenderTargetPixels");
uint32_t flushFlags = 0;
if (NULL == target) {
@ -1656,7 +1655,7 @@ bool GrContext::readRenderTargetPixels(GrRenderTarget* target,
this->flush(flushFlags);
return fGpu->readPixels(target,
left, top, width, height,
config, buffer, rowBytes);
config, buffer);
}
void GrContext::writePixels(int left, int top, int width, int height,

View File

@ -221,12 +221,10 @@ void GrGpu::forceRenderTargetFlush() {
bool GrGpu::readPixels(GrRenderTarget* target,
int left, int top, int width, int height,
GrPixelConfig config, void* buffer,
size_t rowBytes) {
GrPixelConfig config, void* buffer) {
this->handleDirtyContext();
return this->onReadPixels(target, left, top, width, height,
config, buffer, rowBytes);
return this->onReadPixels(target, left, top, width, height, config, buffer);
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -180,8 +180,6 @@ public:
* @param height height of rectangle to read in pixels.
* @param config the pixel config of the destination buffer
* @param buffer memory to read the rectangle into.
* @param rowBytes the number of bytes between consecutive rows. Zero
* means rows are tightly packed.
*
* @return true if the read succeeded, false if not. The read can fail
* because of a unsupported pixel config or because no render
@ -189,7 +187,7 @@ public:
*/
bool readPixels(GrRenderTarget* renderTarget,
int left, int top, int width, int height,
GrPixelConfig config, void* buffer, size_t rowBytes);
GrPixelConfig config, void* buffer);
const GrGpuStats& getStats() const;
void resetStats();
@ -323,7 +321,7 @@ protected:
// overridden by API-specific derived class to perform the read pixels.
virtual bool onReadPixels(GrRenderTarget* target,
int left, int top, int width, int height,
GrPixelConfig, void* buffer, size_t rowBytes) = 0;
GrPixelConfig, void* buffer) = 0;
// called to program the vertex data, indexCount will be 0 if drawing non-
// indexed geometry. The subclass may adjust the startVertex and/or

View File

@ -1384,18 +1384,14 @@ void GrGpuGL::onForceRenderTargetFlush() {
}
bool GrGpuGL::onReadPixels(GrRenderTarget* target,
int left, int top,
int width, int height,
GrPixelConfig config,
void* buffer, size_t rowBytes) {
int left, int top, int width, int height,
GrPixelConfig config, void* buffer) {
GrGLenum internalFormat; // we don't use this for glReadPixels
GrGLenum format;
GrGLenum type;
if (!this->canBeTexture(config, &internalFormat, &format, &type)) {
return false;
}
// resolve the render target if necessary
}
GrGLRenderTarget* tgt = static_cast<GrGLRenderTarget*>(target);
GrAutoTPtrValueRestore<GrRenderTarget*> autoTargetRestore;
switch (tgt->getResolveType()) {
@ -1421,62 +1417,26 @@ bool GrGpuGL::onReadPixels(GrRenderTarget* target,
// the read rect is viewport-relative
GrGLIRect readRect;
readRect.setRelativeTo(glvp, left, top, width, height);
size_t tightRowBytes = GrBytesPerPixel(config) * width;
if (0 == rowBytes) {
rowBytes = tightRowBytes;
}
size_t readDstRowBytes = tightRowBytes;
void* readDst = buffer;
// determine if GL can read using the passed rowBytes or if we need
// a scratch buffer.
SkAutoSMalloc<32 * sizeof(GrColor)> scratch;
if (rowBytes != tightRowBytes) {
if (kDesktop_GrGLBinding == this->glBinding()) {
GrAssert(!(rowBytes % sizeof(GrColor)));
GL_CALL(PixelStorei(GR_GL_PACK_ROW_LENGTH, rowBytes / sizeof(GrColor)));
readDstRowBytes = rowBytes;
} else {
scratch.reset(tightRowBytes * height);
readDst = scratch.get();
}
}
GL_CALL(ReadPixels(readRect.fLeft, readRect.fBottom,
readRect.fWidth, readRect.fHeight,
format, type, readDst));
if (readDstRowBytes != tightRowBytes) {
GL_CALL(PixelStorei(GR_GL_PACK_ROW_LENGTH, 0));
}
format, type, buffer));
// now reverse the order of the rows, since GL's are bottom-to-top, but our
// API presents top-to-bottom. We must preserve the padding contents. Note
// that the above readPixels did not overwrite the padding.
if (readDst == buffer) {
GrAssert(rowBytes == readDstRowBytes);
scratch.reset(tightRowBytes);
void* tmpRow = scratch.get();
// flip y in-place by rows
// API presents top-to-bottom
{
size_t stride = width * GrBytesPerPixel(config);
SkAutoMalloc rowStorage(stride);
void* tmp = rowStorage.get();
const int halfY = height >> 1;
char* top = reinterpret_cast<char*>(buffer);
char* bottom = top + (height - 1) * rowBytes;
char* bottom = top + (height - 1) * stride;
for (int y = 0; y < halfY; y++) {
memcpy(tmpRow, top, tightRowBytes);
memcpy(top, bottom, tightRowBytes);
memcpy(bottom, tmpRow, tightRowBytes);
top += rowBytes;
bottom -= rowBytes;
}
} else {
GrAssert(readDst != buffer);
// copy from readDst to buffer while flipping y
const int halfY = height >> 1;
const char* src = reinterpret_cast<const char*>(readDst);
char* dst = reinterpret_cast<char*>(buffer) + (height-1) * rowBytes;
for (int y = 0; y < height; y++) {
memcpy(dst, src, tightRowBytes);
src += readDstRowBytes;
dst -= rowBytes;
memcpy(tmp, top, stride);
memcpy(top, bottom, stride);
memcpy(bottom, tmp, stride);
top += stride;
bottom -= stride;
}
}
return true;

View File

@ -90,10 +90,8 @@ protected:
virtual void onForceRenderTargetFlush();
virtual bool onReadPixels(GrRenderTarget* target,
int left, int top,
int width, int height,
GrPixelConfig,
void* buffer, size_t rowBytes) SK_OVERRIDE;
int left, int top, int width, int height,
GrPixelConfig, void* buffer);
virtual void onGpuDrawIndexed(GrPrimitiveType type,
uint32_t startVertex,

View File

@ -256,19 +256,36 @@ void SkGpuDevice::makeRenderTargetCurrent() {
///////////////////////////////////////////////////////////////////////////////
bool SkGpuDevice::onReadPixels(const SkBitmap* bitmap, int x, int y) {
SkASSERT(SkBitmap::kARGB_8888_Config == bitmap->config());
SkASSERT(!bitmap->isNull());
SkASSERT(SkIRect::MakeWH(this->width(), this->height()).contains(SkIRect::MakeXYWH(x, y, bitmap->width(), bitmap->height())));
bool SkGpuDevice::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
SkIRect bounds;
bounds.set(0, 0, this->width(), this->height());
if (!bounds.intersect(srcRect)) {
return false;
}
SkAutoLockPixels alp(*bitmap);
return fContext->readRenderTargetPixels(fRenderTarget,
x, y,
bitmap->width(),
bitmap->height(),
kRGBA_8888_GrPixelConfig,
bitmap->getPixels(),
bitmap->rowBytes());
const int w = bounds.width();
const int h = bounds.height();
SkBitmap tmp;
// note we explicitly specify our rowBytes to be snug (no gap between rows)
tmp.setConfig(SkBitmap::kARGB_8888_Config, w, h, w * 4);
if (!tmp.allocPixels()) {
return false;
}
tmp.lockPixels();
bool read = fContext->readRenderTargetPixels(fRenderTarget,
bounds.fLeft, bounds.fTop,
bounds.width(), bounds.height(),
kRGBA_8888_GrPixelConfig,
tmp.getPixels());
tmp.unlockPixels();
if (!read) {
return false;
}
tmp.swap(*bitmap);
return true;
}
void SkGpuDevice::writePixels(const SkBitmap& bitmap, int x, int y) {

View File

@ -1,260 +0,0 @@
/*
* 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 "SkRegion.h"
#include "SkGpuDevice.h"
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);
namespace {
SkPMColor getCanvasColor(int x, int y) {
SkASSERT(x >= 0 && x < DEV_W);
SkASSERT(y >= 0 && y < DEV_H);
return SkPackARGB32(0xff, x, y, 0x0);
}
SkPMColor getBitmapColor(int x, int y, int w, int h) {
int n = y * w + x;
U8CPU b = n & 0xff;
U8CPU g = (n >> 8) & 0xff;
U8CPU r = (n >> 16) & 0xff;
return SkPackARGB32(0xff, r, g , b);
}
void fillCanvas(SkCanvas* canvas) {
static SkBitmap bmp;
if (bmp.isNull()) {
bmp.setConfig(SkBitmap::kARGB_8888_Config, DEV_W, DEV_H);
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();
}
void fillBitmap(SkBitmap* bitmap) {
SkASSERT(bitmap->lockPixelsAreWritable());
SkAutoLockPixels alp(*bitmap);
int w = bitmap->width();
int h = bitmap->height();
intptr_t pixels = reinterpret_cast<intptr_t>(bitmap->getPixels());
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
SkPMColor* pixel = reinterpret_cast<SkPMColor*>(pixels + y * bitmap->rowBytes() + x * bitmap->bytesPerPixel());
*pixel = getBitmapColor(x, y, w, h);
}
}
}
// checks the bitmap contains correct pixels after the readPixels
// if the bitmap was prefilled with pixels it checks that these weren't
// overwritten in the area outside the readPixels.
bool checkRead(skiatest::Reporter* reporter,
const SkBitmap& bitmap,
int x, int y,
bool preFilledBmp) {
SkASSERT(SkBitmap::kARGB_8888_Config == bitmap.config());
SkASSERT(!bitmap.isNull());
int bw = bitmap.width();
int bh = bitmap.height();
SkIRect srcRect = SkIRect::MakeXYWH(x, y, bw, bh);
SkIRect clippedSrcRect = DEV_RECT;
if (!clippedSrcRect.intersect(srcRect)) {
clippedSrcRect.setEmpty();
}
SkAutoLockPixels alp(bitmap);
intptr_t pixels = reinterpret_cast<intptr_t>(bitmap.getPixels());
for (int by = 0; by < bh; ++by) {
for (int bx = 0; bx < bw; ++bx) {
int devx = bx + srcRect.fLeft;
int devy = by + srcRect.fTop;
SkPMColor pixel = *reinterpret_cast<SkPMColor*>(pixels + by * bitmap.rowBytes() + bx * bitmap.bytesPerPixel());
if (clippedSrcRect.contains(devx, devy)) {
REPORTER_ASSERT(reporter, getCanvasColor(devx, devy) == pixel);
if (getCanvasColor(devx, devy) != pixel) {
return false;
}
} else if (preFilledBmp) {
REPORTER_ASSERT(reporter, getBitmapColor(bx, by, bw, bh) == pixel);
if (getBitmapColor(bx, by, bw, bh) != pixel) {
return false;
}
}
}
}
return true;
}
enum BitmapInit {
kFirstBitmapInit = 0,
kNoPixels_BitmapInit = kFirstBitmapInit,
kTight_BitmapInit,
kRowBytes_BitmapInit,
kBitmapInitCnt
};
BitmapInit nextBMI(BitmapInit bmi) {
int x = bmi;
return static_cast<BitmapInit>(++x);
}
void init_bitmap(SkBitmap* bitmap, const SkIRect& rect, BitmapInit init) {
int w = rect.width();
int h = rect.height();
int rowBytes = 0;
bool alloc = true;
switch (init) {
case kNoPixels_BitmapInit:
alloc = false;
case kTight_BitmapInit:
break;
case kRowBytes_BitmapInit:
rowBytes = w * sizeof(SkPMColor) + 16 * sizeof(SkPMColor);
break;
default:
SkASSERT(0);
break;
}
bitmap->setConfig(SkBitmap::kARGB_8888_Config, w, h, rowBytes);
if (alloc) {
bitmap->allocPixels();
}
}
void ReadPixelsTest(skiatest::Reporter* reporter, GrContext* context) {
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 (int dtype = 0; dtype < 2; ++dtype) {
if (0 == dtype) {
canvas.setDevice(new SkDevice(SkBitmap::kARGB_8888_Config, DEV_W, DEV_H, false))->unref();
} else {
canvas.setDevice(new SkGpuDevice(context, SkBitmap::kARGB_8888_Config, DEV_W, DEV_H))->unref();
}
fillCanvas(&canvas);
for (int rect = 0; rect < SK_ARRAY_COUNT(testRects); ++rect) {
SkBitmap bmp;
for (BitmapInit bmi = kFirstBitmapInit; bmi < kBitmapInitCnt; bmi = nextBMI(bmi)) {
const SkIRect& srcRect = testRects[rect];
init_bitmap(&bmp, srcRect, bmi);
// if the bitmap has pixels allocated before the readPixels, note
// that and fill them with pattern
bool startsWithPixels = !bmp.isNull();
if (startsWithPixels) {
fillBitmap(&bmp);
}
bool success = canvas.readPixels(&bmp, srcRect.fLeft, srcRect.fTop);
// determine whether we expected the read to succeed.
REPORTER_ASSERT(reporter, success == SkIRect::Intersects(srcRect, DEV_RECT));
if (success || startsWithPixels) {
checkRead(reporter, bmp, srcRect.fLeft, srcRect.fTop, startsWithPixels);
} else {
// if we had no pixels beforehand and the readPixels failed then
// our bitmap should still not have any pixels
REPORTER_ASSERT(reporter, bmp.isNull());
}
// check the old webkit version of readPixels that clips the bitmap size
SkBitmap wkbmp;
success = canvas.readPixels(srcRect, &wkbmp);
SkIRect clippedRect = DEV_RECT;
if (clippedRect.intersect(srcRect)) {
REPORTER_ASSERT(reporter, success);
checkRead(reporter, wkbmp, clippedRect.fLeft, clippedRect.fTop, false);
} else {
REPORTER_ASSERT(reporter, !success);
}
}
}
}
}
}
#include "TestClassDef.h"
DEFINE_GPUTESTCLASS("ReadPixels", ReadPixelsTestClass, ReadPixelsTest)