add peekPixels to SkCanvas and SkSurface

clone of https://codereview.chromium.org/159723006/

Review URL: https://codereview.chromium.org/161733002

git-svn-id: http://skia.googlecode.com/svn/trunk@13427 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
mike@reedtribe.org 2014-02-13 15:11:11 +00:00
parent deee496cd3
commit b2d93a9122
35 changed files with 402 additions and 303 deletions

View File

@ -9,203 +9,11 @@
#include "SkCanvas.h"
#include "SkPath.h"
static void test_quadstroke(SkCanvas* canvas) {
SkPath path;
path.moveTo(6, 0);
path.quadTo(150, 150, 0, 6);
SkPaint paint;
paint.setAntiAlias(true);
paint.setStyle(SkPaint::kStroke_Style);
canvas->translate(20, 20);
#if 1
canvas->drawPath(path, paint);
canvas->translate(100, 0);
#endif
paint.setStrokeWidth(1.01f);
canvas->drawPath(path, paint);
}
static void draw_conic(SkCanvas* canvas, SkScalar weight, const SkPaint& paint) {
SkPath path;
path.moveTo(100, 100);
path.conicTo(300, 100, 300, 300, weight);
canvas->drawPath(path, paint);
}
static void test_conic(SkCanvas* canvas) {
SkPaint paint;
paint.setAntiAlias(true);
paint.setStyle(SkPaint::kStroke_Style);
static const struct {
SkScalar fWeight;
SkColor fColor;
} gRec[] = {
{ 2 , SK_ColorRED },
{ 1 , SK_ColorGREEN },
{ 0.5f, SK_ColorBLUE },
};
for (SkScalar width = 0; width <= 20; width += 20) {
canvas->save();
paint.setStrokeWidth(width);
for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
paint.setColor(gRec[i].fColor);
draw_conic(canvas, gRec[i].fWeight, paint);
canvas->translate(-30, 30);
}
canvas->restore();
canvas->translate(300, 0);
}
}
#include "SkGradientShader.h"
static void test_shallow_gradient(SkCanvas* canvas, SkScalar width, SkScalar height) {
SkColor colors[] = { 0xFF7F7F7F, 0xFF7F7F7F, 0xFF000000 };
SkScalar pos[] = { 0, 0.35f, SK_Scalar1 };
SkPoint pts[] = { { 0, 0 }, { width, height } };
SkShader* s = SkGradientShader::CreateLinear(pts, colors, pos,
SK_ARRAY_COUNT(colors),
SkShader::kClamp_TileMode);
SkPaint paint;
paint.setShader(s)->unref();
canvas->drawPaint(paint);
}
#include "SkDashPathEffect.h"
static void test_giant_dash(SkCanvas* canvas) {
SkPaint paint;
const SkScalar intervals[] = { SK_Scalar1, SK_Scalar1 };
paint.setStrokeWidth(2);
paint.setPathEffect(new SkDashPathEffect(intervals, 2, 0))->unref();
SkScalar big = 500 * 1000;
canvas->drawLine(10, 10, big, 10, paint);
canvas->drawLine(-big, 20, 500, 20, paint);
canvas->drawLine(-big, 30, big, 30, paint);
const SkScalar intervals2[] = { 20, 5, 10, 5 };
paint.setPathEffect(new SkDashPathEffect(intervals2, 4, 17))->unref();
canvas->translate(0, 40);
SkScalar x = -500;
SkScalar width = 3173;
for (int i = 0; i < 40; ++i) {
if (i > 10)
canvas->drawLine(x, 0, x + width, 0, paint);
x += 1;
canvas->translate(0, 4);
}
}
// Reproduces bug found here: http://jsfiddle.net/R8Cu5/1/
//
#include "SkGradientShader.h"
static void test_grad(SkCanvas* canvas) {
SkPoint pts[] = {
{ 478.544067f, -84.2041016f },
{ 602.455933f, 625.204102f },
};
SkColor colors[] = { SK_ColorBLACK, SK_ColorBLACK, SK_ColorRED, SK_ColorRED };
SkScalar pos[] = { 0, 0.3f, 0.3f, 1.0f };
SkShader* s = SkGradientShader::CreateLinear(pts, colors, pos, 4, SkShader::kClamp_TileMode);
SkPaint p;
p.setShader(s)->unref();
canvas->drawPaint(p);
}
static SkCanvas* MakeCanvas(const SkIRect& bounds) {
SkBitmap bm;
bm.allocN32Pixels(bounds.width(), bounds.height());
bm.eraseColor(SK_ColorTRANSPARENT);
SkCanvas* canvas = new SkCanvas(bm);
canvas->translate(-SkIntToScalar(bounds.fLeft), -SkIntToScalar(bounds.fTop));
return canvas;
}
#ifdef SK_DEBUG
static void GetBitmap(const SkCanvas* canvas, SkBitmap* bm) {
*bm = canvas->getDevice()->accessBitmap(false);
}
#endif
static void compare_canvas(const SkCanvas* a, const SkCanvas* b) {
#ifdef SK_DEBUG
SkBitmap bma, bmb;
GetBitmap(a, &bma);
GetBitmap(b, &bmb);
SkASSERT(bma.width() == bmb.width());
SkASSERT(bma.height() == bmb.height());
bma.lockPixels();
bmb.lockPixels();
for (int y = 0; y < bma.height(); ++y) {
const SkPMColor* rowa = bma.getAddr32(0, y);
const SkPMColor* rowb = bmb.getAddr32(0, y);
SkASSERT(!memcmp(rowa, rowb, bma.width() << 2));
for (int x = 1; x < bma.width() - 1; ++x) {
SkASSERT(0xFF000000 == rowa[x]);
SkASSERT(0xFF000000 == rowb[x]);
}
}
#endif
}
static void drawRectAsPath(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
SkPath path;
path.addRect(r);
canvas->drawPath(path, p);
}
static void test_maskFromPath(const SkPath& path) {
SkIRect bounds;
path.getBounds().roundOut(&bounds);
SkPaint paint;
paint.setAntiAlias(true);
SkAutoTUnref<SkCanvas> path_canvas(MakeCanvas(bounds));
path_canvas->drawPath(path, paint);
SkAutoTUnref<SkCanvas> rect_canvas(MakeCanvas(bounds));
drawRectAsPath(rect_canvas, path.getBounds(), paint);
compare_canvas(path_canvas, rect_canvas);
}
static void test_mask() {
for (int i = 1; i <= 20; ++i) {
const SkScalar dx = SK_Scalar1 / i;
const SkRect constr = SkRect::MakeWH(dx, SkIntToScalar(2));
for (int n = 2; n < 20; ++n) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
SkRect r = constr;
while (r.fRight < SkIntToScalar(4)) {
path.addRect(r);
r.offset(dx, 0);
}
test_maskFromPath(path);
}
}
}
/** Draw a 2px border around the target, then red behind the target;
set the clip to match the target, then draw >> the target in blue.
*/
static void draw (SkCanvas* canvas, SkRect& target, int x, int y) {
static void draw(SkCanvas* canvas, SkRect& target, int x, int y) {
SkPaint borderPaint;
borderPaint.setColor(SkColorSetRGB(0x0, 0xDD, 0x0));
borderPaint.setAntiAlias(true);
@ -228,22 +36,22 @@ static void draw (SkCanvas* canvas, SkRect& target, int x, int y) {
canvas->restore();
}
static void draw_square (SkCanvas* canvas, int x, int y) {
static void draw_square(SkCanvas* canvas, int x, int y) {
SkRect target (SkRect::MakeWH(10 * SK_Scalar1, 10 * SK_Scalar1));
draw(canvas, target, x, y);
}
static void draw_column (SkCanvas* canvas, int x, int y) {
static void draw_column(SkCanvas* canvas, int x, int y) {
SkRect target (SkRect::MakeWH(1 * SK_Scalar1, 10 * SK_Scalar1));
draw(canvas, target, x, y);
}
static void draw_bar (SkCanvas* canvas, int x, int y) {
static void draw_bar(SkCanvas* canvas, int x, int y) {
SkRect target (SkRect::MakeWH(10 * SK_Scalar1, 1 * SK_Scalar1));
draw(canvas, target, x, y);
}
static void draw_rect_tests (SkCanvas* canvas) {
static void draw_rect_tests(SkCanvas* canvas) {
draw_square(canvas, 10, 10);
draw_column(canvas, 30, 10);
draw_bar(canvas, 10, 30);
@ -272,23 +80,6 @@ protected:
}
virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
if (false) { test_quadstroke(canvas); return; }
if (false) { test_conic(canvas); return; }
if (false) {
SkRect bounds;
canvas->getClipBounds(&bounds);
test_shallow_gradient(canvas, bounds.width(), bounds.height()); return;
}
if (false) {
test_giant_dash(canvas); return;
}
if (false) {
test_grad(canvas); return;
}
if (false) { // avoid bit rot, suppress warning
test_mask();
}
// Initial pixel-boundary-aligned draw
draw_rect_tests(canvas);

View File

@ -62,14 +62,14 @@ protected:
virtual void onDraw(SkCanvas* canvas) {
SkPaint paint;
SkScalar horizMargin(SkIntToScalar(10));
SkScalar vertMargin(SkIntToScalar(10));
SkScalar horizMargin = 10;
SkScalar vertMargin = 10;
SkBitmapDevice devTmp(SkBitmap::kARGB_8888_Config, 40, 40, false);
SkCanvas canvasTmp(&devTmp);
SkBitmap src;
src.allocN32Pixels(40, 40);
SkCanvas canvasTmp(src);
draw_checks(&canvasTmp, 40, 40);
SkBitmap src = canvasTmp.getTopDevice()->accessBitmap(false);
for (unsigned i = 0; i < NUM_CONFIGS; ++i) {
if (!src.deepCopyTo(&fDst[i], gConfigs[i])) {

View File

@ -23,8 +23,7 @@ protected:
void makeBitmap() {
fBitmap.allocN32Pixels(100, 100);
SkBitmapDevice device(fBitmap);
SkCanvas canvas(&device);
SkCanvas canvas(fBitmap);
canvas.clear(0x00000000);
SkPaint paint;
paint.setAntiAlias(true);

View File

@ -5,6 +5,7 @@
* found in the LICENSE file.
*/
#include "gm.h"
#include "SkBitmapDevice.h"
#include "SkTypeface.h"
namespace skiagm {

View File

@ -33,8 +33,7 @@ protected:
void make_bitmap() {
fBitmap.allocN32Pixels(80, 80);
SkBitmapDevice device(fBitmap);
SkCanvas canvas(&device);
SkCanvas canvas(fBitmap);
canvas.clear(0x00000000);
SkPaint paint;
paint.setAntiAlias(true);
@ -46,8 +45,7 @@ protected:
void make_checkerboard(SkBitmap* bitmap, int w, int h) {
bitmap->allocN32Pixels(w, h);
SkBitmapDevice device(*bitmap);
SkCanvas canvas(&device);
SkCanvas canvas(*bitmap);
canvas.clear(0x00000000);
SkPaint darkPaint;
darkPaint.setColor(0xFF804020);

View File

@ -38,14 +38,14 @@ static bool setFont(SkPaint* paint, const char name[]) {
#import <ApplicationServices/ApplicationServices.h>
#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
static CGContextRef makeCG(const SkBitmap& bm) {
if (SkBitmap::kARGB_8888_Config != bm.config() ||
NULL == bm.getPixels()) {
static CGContextRef makeCG(const SkImageInfo& info, const void* addr,
size_t rowBytes) {
if (kPMColor_SkColorType != info.colorType() || NULL == addr) {
return NULL;
}
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGContextRef cg = CGBitmapContextCreate(bm.getPixels(), bm.width(), bm.height(),
8, bm.rowBytes(), space, BITMAP_INFO_RGB);
CGContextRef cg = CGBitmapContextCreate((void*)addr, info.width(), info.height(),
8, rowBytes, space, BITMAP_INFO_RGB);
CFRelease(space);
CGContextSetAllowsFontSubpixelQuantization(cg, false);
@ -143,7 +143,15 @@ protected:
virtual void onDraw(SkCanvas* canvas) {
#ifdef SK_BUILD_FOR_MAC
CGContextRef cg = makeCG(canvas->getDevice()->accessBitmap(false));
CGContextRef cg = 0;
{
SkImageInfo info;
size_t rowBytes;
const void* addr = canvas->peekPixels(&info, &rowBytes);
if (addr) {
cg = makeCG(info, addr, rowBytes);
}
}
#endif
drawGrad(canvas);

View File

@ -1,15 +1,14 @@
/*
* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef skiagm_DEFINED
#define skiagm_DEFINED
#include "SkBitmap.h"
#include "SkBitmapDevice.h"
#include "SkCanvas.h"
#include "SkPaint.h"
#include "SkSize.h"

View File

@ -35,8 +35,7 @@ protected:
void make_checkerboard() {
fCheckerboard.allocN32Pixels(64, 64);
SkBitmapDevice device(fCheckerboard);
SkCanvas canvas(&device);
SkCanvas canvas(fCheckerboard);
canvas.clear(0x00000000);
SkPaint darkPaint;
darkPaint.setColor(0xFF404040);
@ -58,10 +57,9 @@ protected:
void make_gradient_circle(int width, int height) {
SkScalar x = SkIntToScalar(width / 2);
SkScalar y = SkIntToScalar(height / 2);
SkScalar radius = SkScalarMul(SkMinScalar(x, y), SkIntToScalar(4) / SkIntToScalar(5));
SkScalar radius = SkMinScalar(x, y) * 0.8f;
fGradientCircle.allocN32Pixels(width, height);
SkBitmapDevice device(fGradientCircle);
SkCanvas canvas(&device);
SkCanvas canvas(fGradientCircle);
canvas.clear(0x00000000);
SkColor colors[2];
colors[0] = SK_ColorWHITE;

View File

@ -8,6 +8,7 @@
#include "gm.h"
#include "SkArithmeticMode.h"
#include "SkDevice.h"
#include "SkBitmapSource.h"
#include "SkBlurImageFilter.h"
#include "SkColorFilter.h"
@ -93,8 +94,7 @@ protected:
void make_bitmap() {
fBitmap.allocN32Pixels(100, 100);
SkBitmapDevice device(fBitmap);
SkCanvas canvas(&device);
SkCanvas canvas(fBitmap);
canvas.clear(0x00000000);
SkPaint paint;
paint.setAntiAlias(true);

View File

@ -35,8 +35,7 @@ protected:
void make_checkerboard() {
fCheckerboard.allocN32Pixels(64, 64);
SkBitmapDevice device(fCheckerboard);
SkCanvas canvas(&device);
SkCanvas canvas(fCheckerboard);
canvas.clear(0x00000000);
SkPaint darkPaint;
darkPaint.setColor(0xFF404040);
@ -60,8 +59,7 @@ protected:
SkScalar y = SkIntToScalar(height / 2);
SkScalar radius = SkScalarMul(SkMinScalar(x, y), SkIntToScalar(4) / SkIntToScalar(5));
fGradientCircle.allocN32Pixels(width, height);
SkBitmapDevice device(fGradientCircle);
SkCanvas canvas(&device);
SkCanvas canvas(fGradientCircle);
canvas.clear(0x00000000);
SkColor colors[2];
colors[0] = SK_ColorWHITE;

View File

@ -26,8 +26,7 @@ protected:
void make_bitmap() {
fBitmap.allocN32Pixels(100, 100);
SkBitmapDevice device(fBitmap);
SkCanvas canvas(&device);
SkCanvas canvas(fBitmap);
canvas.clear(0x00000000);
SkPaint paint;
paint.setAntiAlias(true);
@ -38,7 +37,7 @@ protected:
}
virtual SkISize onISize() {
return make_isize(WIDTH, HEIGHT);
return SkISize::Make(WIDTH, HEIGHT);
}
void drawClippedBitmap(SkCanvas* canvas, const SkPaint& paint, int x, int y) {

View File

@ -25,8 +25,7 @@ protected:
void make_bitmap() {
fBitmap.allocN32Pixels(80, 80);
SkBitmapDevice device(fBitmap);
SkCanvas canvas(&device);
SkCanvas canvas(fBitmap);
canvas.clear(0x00000000);
SkPaint paint;
paint.setAntiAlias(true);

View File

@ -27,8 +27,7 @@ protected:
void make_bitmap() {
fBitmap.allocN32Pixels(135, 135);
SkBitmapDevice device(fBitmap);
SkCanvas canvas(&device);
SkCanvas canvas(fBitmap);
canvas.clear(0x0);
SkPaint paint;
paint.setAntiAlias(true);

View File

@ -13,10 +13,7 @@ static void make_bitmap(SkBitmap* bitmap, SkIRect* center) {
const int kSize = 2*kFixed + kStretchy;
bitmap->allocN32Pixels(kSize, kSize);
SkBaseDevice* dev = new SkBitmapDevice(*bitmap);
SkCanvas canvas(dev);
dev->unref();
SkCanvas canvas(*bitmap);
canvas.clear(SK_ColorTRANSPARENT);
SkRect r = SkRect::MakeWH(SkIntToScalar(kSize), SkIntToScalar(kSize));

View File

@ -28,8 +28,7 @@ protected:
void make_bitmap() {
fBitmap.allocN32Pixels(80, 80);
SkBitmapDevice device(fBitmap);
SkCanvas canvas(&device);
SkCanvas canvas(fBitmap);
canvas.clear(0x00000000);
SkPaint paint;
paint.setAntiAlias(true);
@ -40,10 +39,8 @@ protected:
}
void make_checkerboard() {
fCheckerboard.setConfig(SkBitmap::kARGB_8888_Config, 80, 80);
fCheckerboard.allocPixels();
SkBitmapDevice device(fCheckerboard);
SkCanvas canvas(&device);
fCheckerboard.allocN32Pixels(80, 80);
SkCanvas canvas(fCheckerboard);
canvas.clear(0x00000000);
SkPaint darkPaint;
darkPaint.setColor(0xFF404040);

78
gm/peekpixels.cpp Normal file
View File

@ -0,0 +1,78 @@
/*
* 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 "gm.h"
#include "SkCanvas.h"
#include "SkPath.h"
#include "SkSurface.h"
#include "SkPicture.h"
static void draw_content(SkCanvas* canvas) {
SkImageInfo info = canvas->imageInfo();
SkPaint paint;
paint.setAntiAlias(true);
canvas->drawCircle(SkScalarHalf(info.width()), SkScalarHalf(info.height()),
SkScalarHalf(info.width()), paint);
}
class PeekPixelsGM : public skiagm::GM {
public:
PeekPixelsGM() {}
protected:
virtual SkString onShortName() SK_OVERRIDE {
return SkString("peekpixels");
}
virtual SkISize onISize() SK_OVERRIDE {
return SkISize::Make(640, 480);
}
virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
SkAutoTUnref<SkSurface> surface(canvas->newSurface(info));
if (surface.get()) {
SkCanvas* surfCanvas = surface->getCanvas();
draw_content(surfCanvas);
SkBitmap bitmap;
// test peekPixels
{
SkImageInfo info;
size_t rowBytes;
const void* addr = surfCanvas->peekPixels(&info, &rowBytes);
if (addr && bitmap.installPixels(info, const_cast<void*>(addr),
rowBytes, NULL, NULL)) {
canvas->drawBitmap(bitmap, 0, 0, NULL);
}
}
// test ROCanvasPixels
canvas->translate(120, 0);
SkAutoROCanvasPixels ropixels(surfCanvas);
if (ropixels.asROBitmap(&bitmap)) {
canvas->drawBitmap(bitmap, 0, 0, NULL);
}
// test Surface
canvas->translate(120, 0);
surface->draw(canvas, 0, 0, NULL);
}
}
virtual uint32_t onGetFlags() const {
// we explicitly test peekPixels and readPixels, neither of which
// return something for a picture-backed canvas, so we skip that test.
return kSkipPicture_Flag;
}
private:
typedef skiagm::GM INHERITED;
};
DEF_GM( return SkNEW(PeekPixelsGM); )

View File

@ -28,8 +28,7 @@ protected:
void make_bitmap() {
fBitmap.allocN32Pixels(80, 80);
SkBitmapDevice device(fBitmap);
SkCanvas canvas(&device);
SkCanvas canvas(fBitmap);
canvas.clear(0xFF000000);
SkPaint paint;
paint.setAntiAlias(true);
@ -41,8 +40,7 @@ protected:
void make_checkerboard() {
fCheckerboard.allocN32Pixels(80, 80);
SkBitmapDevice device(fCheckerboard);
SkCanvas canvas(&device);
SkCanvas canvas(fCheckerboard);
canvas.clear(0x00000000);
SkPaint darkPaint;
darkPaint.setColor(0xFF404040);

View File

@ -30,8 +30,7 @@ protected:
void make_bitmap() {
fBitmap.allocN32Pixels(80, 80);
SkBitmapDevice device(fBitmap);
SkCanvas canvas(&device);
SkCanvas canvas(fBitmap);
canvas.clear(0x00000000);
SkPaint paint;
paint.setAntiAlias(true);
@ -43,8 +42,7 @@ protected:
void make_checkerboard() {
fCheckerboard.allocN32Pixels(80, 80);
SkBitmapDevice device(fCheckerboard);
SkCanvas canvas(&device);
SkCanvas canvas(fCheckerboard);
canvas.clear(0x00000000);
SkPaint darkPaint;
darkPaint.setColor(0xFF404040);

View File

@ -121,6 +121,7 @@
'../gm/pathopsinverse.cpp',
'../gm/pathopsskpclip.cpp',
'../gm/pathreverse.cpp',
'../gm/peekpixels.cpp',
'../gm/perlinnoise.cpp',
'../gm/pictureimagefilter.cpp',
'../gm/points.cpp',

View File

@ -897,4 +897,11 @@ inline SkPMColor SkBitmap::getIndex8Color(int x, int y) const {
return (*fColorTable)[*((const uint8_t*)fPixels + y * fRowBytes + x)];
}
///////////////////////////////////////////////////////////////////////////////
//
// Helpers until we can fully deprecate SkBitmap::Config
//
extern SkBitmap::Config SkColorTypeToBitmapConfig(SkColorType);
extern SkColorType SkBitmapConfigToColorType(SkBitmap::Config);
#endif

View File

@ -82,6 +82,8 @@ public:
*/
virtual SkBitmap::Config config() const SK_OVERRIDE { return fBitmap.config(); }
virtual SkImageInfo imageInfo() const SK_OVERRIDE;
/**
* DEPRECATED: This will be made protected once WebKit stops using it.
* Instead use Canvas' writePixels method.
@ -278,6 +280,7 @@ private:
virtual void flush() SK_OVERRIDE {}
virtual SkSurface* newSurface(const SkImageInfo&) SK_OVERRIDE;
virtual const void* peekPixels(SkImageInfo*, size_t* rowBytes) SK_OVERRIDE;
SkBitmap fBitmap;

View File

@ -76,6 +76,12 @@ public:
SkMetaData& getMetaData();
/**
* Return ImageInfo for this canvas. If the canvas is not backed by pixels
* (cpu or gpu), then the info's ColorType will be kUnknown_SkColorType.
*/
SkImageInfo imageInfo() const;
///////////////////////////////////////////////////////////////////////////
/**
@ -84,16 +90,19 @@ public:
void flush();
/**
* DEPRECATED: call imageInfo() instead.
* Return the width/height of the underlying device. The current drawable
* area may be small (due to clipping or saveLayer). For a canvas with
* no device, 0,0 will be returned.
*/
SkISize getDeviceSize() const;
/** Return the canvas' device object, which may be null. The device holds
the bitmap of the pixels that the canvas draws into. The reference count
of the returned device is not changed by this call.
*/
/**
* DEPRECATED.
* Return the canvas' device object, which may be null. The device holds
* the bitmap of the pixels that the canvas draws into. The reference count
* of the returned device is not changed by this call.
*/
SkBaseDevice* getDevice() const;
/**
@ -125,6 +134,20 @@ public:
///////////////////////////////////////////////////////////////////////////
/**
* If the canvas has pixels (and is not recording to a picture or other
* non-raster target) and has direct access to its pixels (i.e. they are in
* local RAM) return the const-address of those pixels, and if not null,
* return the ImageInfo and rowBytes. The returned address is only valid
* while the canvas object is in scope and unchanged. Any API calls made on
* canvas (or its parent surface if any) will invalidate the
* returned address (and associated information).
*
* On failure, returns NULL and the info and rowBytes parameters are
* ignored.
*/
const void* peekPixels(SkImageInfo* info, size_t* rowBytes);
/**
* This enum can be used with read/writePixels to perform a pixel ops to or
* from an 8888 config other than Skia's native config (SkPMColor). There
@ -1019,6 +1042,9 @@ protected:
// default impl defers to getDevice()->newSurface(info)
virtual SkSurface* onNewSurface(const SkImageInfo&);
// default impl defers to its device
virtual const void* onPeekPixels(SkImageInfo*, size_t* rowBytes);
// Returns the canvas to be used by DrawIter. Default implementation
// returns this. Subclasses that encapsulate an indirect canvas may
// need to overload this method. The impl must keep track of this, as it
@ -1206,4 +1232,47 @@ private:
};
#define SkAutoCommentBlock(...) SK_REQUIRE_LOCAL_VAR(SkAutoCommentBlock)
/**
* If the caller wants read-only access to the pixels in a canvas, it can just
* call canvas->peekPixels(), since that is the fastest way to "peek" at the
* pixels on a raster-backed canvas.
*
* If the canvas has pixels, but they are not readily available to the CPU
* (e.g. gpu-backed), then peekPixels() will fail, but readPixels() will
* succeed (though be slower, since it will return a copy of the pixels).
*
* SkAutoROCanvasPixels encapsulates these two techniques, trying first to call
* peekPixels() (for performance), but if that fails, calling readPixels() and
* storing the copy locally.
*
* The caller must respect the restrictions associated with peekPixels(), since
* that may have been called: The returned information is invalidated if...
* - any API is called on the canvas (or its parent surface if present)
* - the canvas goes out of scope
*/
class SkAutoROCanvasPixels : SkNoncopyable {
public:
SkAutoROCanvasPixels(SkCanvas* canvas);
// returns NULL on failure
const void* addr() const { return fAddr; }
// undefined if addr() == NULL
size_t rowBytes() const { return fRowBytes; }
// undefined if addr() == NULL
const SkImageInfo& info() const { return fInfo; }
// helper that, if returns true, installs the pixels into the bitmap. Note
// that the bitmap may reference the address returned by peekPixels(), so
// the caller must respect the restrictions associated with peekPixels().
bool asROBitmap(SkBitmap*) const;
private:
SkBitmap fBitmap; // used if peekPixels() fails
const void* fAddr; // NULL on failure
SkImageInfo fInfo;
size_t fRowBytes;
};
#endif

View File

@ -76,6 +76,12 @@ public:
return fLeakyProperties;
}
/**
* Return ImageInfo for this device. If the canvas is not backed by pixels
* (cpu or gpu), then the info's ColorType will be kUnknown_SkColorType.
*/
virtual SkImageInfo imageInfo() const;
/**
* Return the bounds of the device in the coordinate space of the root
* canvas. The root device will have its top-left at 0,0, but other devices
@ -369,7 +375,10 @@ protected:
protected:
// default impl returns NULL
virtual SkSurface* newSurface(const SkImageInfo&);
// default impl returns NULL
virtual const void* peekPixels(SkImageInfo*, size_t* rowBytes);
/**
* Leaky properties are those which the device should be applying but it isn't.
* These properties will be applied by the draw, when and as it can.

View File

@ -151,6 +151,18 @@ public:
*/
void draw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*);
/**
* If the surface has direct access to its pixels (i.e. they are in local
* RAM) return the const-address of those pixels, and if not null, return
* the ImageInfo and rowBytes. The returned address is only valid while
* the surface object is in scope, and no API call is made on the surface
* or its canvas.
*
* On failure, returns NULL and the info and rowBytes parameters are
* ignored.
*/
const void* peekPixels(SkImageInfo* info, size_t* rowBytes);
protected:
SkSurface(int width, int height);
SkSurface(const SkImageInfo&);

View File

@ -1128,8 +1128,9 @@ void SampleWindow::listTitles() {
static SkBitmap capture_bitmap(SkCanvas* canvas) {
SkBitmap bm;
const SkBitmap& src = canvas->getDevice()->accessBitmap(false);
src.copyTo(&bm, src.config());
if (bm.allocPixels(canvas->imageInfo())) {
canvas->readPixels(&bm, 0, 0);
}
return bm;
}

View File

@ -71,12 +71,6 @@ protected:
}
}
static void blowup(SkCanvas* canvas, const SkIRect& src, const SkRect& dst) {
SkBaseDevice* device = canvas->getDevice();
const SkBitmap& bm = device->accessBitmap(false);
canvas->drawBitmapRect(bm, &src, dst, NULL);
}
static void make_poly(SkPath* path, int n) {
if (n <= 0) {
return;

View File

@ -42,10 +42,10 @@ static void dump_layers(const char label[], SkCanvas* canvas) {
SkCanvas::LayerIter iter(canvas, true);
int index = 0;
while (!iter.done()) {
const SkBitmap& bm = iter.device()->accessBitmap(false);
SkImageInfo info = iter.device()->imageInfo();
const SkIRect& clip = iter.clip().getBounds();
SkDebugf("Layer[%d] bitmap [%d %d] X=%d Y=%d clip=[%d %d %d %d] alpha=%d\n", index++,
bm.width(), bm.height(), iter.x(), iter.y(),
info.width(), info.height(), iter.x(), iter.y(),
clip.fLeft, clip.fTop, clip.fRight, clip.fBottom,
iter.paint().getAlpha());
iter.next();

View File

@ -54,6 +54,10 @@ SkBitmapDevice::SkBitmapDevice(SkBitmap::Config config, int width, int height, b
SkBitmapDevice::~SkBitmapDevice() {
}
SkImageInfo SkBitmapDevice::imageInfo() const {
return fBitmap.info();
}
void SkBitmapDevice::replaceBitmapBackendForRasterSurface(const SkBitmap& bm) {
SkASSERT(bm.width() == fBitmap.width());
SkASSERT(bm.height() == fBitmap.height());
@ -386,6 +390,16 @@ SkSurface* SkBitmapDevice::newSurface(const SkImageInfo& info) {
return SkSurface::NewRaster(info);
}
const void* SkBitmapDevice::peekPixels(SkImageInfo* info, size_t* rowBytes) {
if (fBitmap.getPixels() && fBitmap.asImageInfo(info)) {
if (rowBytes) {
*rowBytes = fBitmap.rowBytes();
}
return fBitmap.getPixels();
}
return NULL;
}
///////////////////////////////////////////////////////////////////////////////
bool SkBitmapDevice::filterTextFlags(const SkPaint& paint, TextFlags* flags) {

View File

@ -992,6 +992,55 @@ SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
return dev ? dev->newSurface(info) : NULL;
}
SkImageInfo SkCanvas::imageInfo() const {
SkBaseDevice* dev = this->getDevice();
if (dev) {
return dev->imageInfo();
} else {
// TODO: need a real unknown for alphatype it seems.
SkAlphaType unknownAlphaType = kIgnore_SkAlphaType;
return SkImageInfo::Make(0, 0, kUnknown_SkColorType, unknownAlphaType);
}
}
const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
return this->onPeekPixels(info, rowBytes);
}
const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
SkBaseDevice* dev = this->getDevice();
return dev ? dev->peekPixels(info, rowBytes) : NULL;
}
SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
if (NULL == fAddr) {
fInfo = canvas->imageInfo();
if (kUnknown_SkColorType == fInfo.colorType() ||
!fBitmap.allocPixels(fInfo))
{
return; // failure, fAddr is NULL
}
fBitmap.lockPixels();
if (!canvas->readPixels(&fBitmap, 0, 0)) {
return; // failure, fAddr is NULL
}
fAddr = fBitmap.getPixels();
fRowBytes = fBitmap.rowBytes();
}
SkASSERT(fAddr); // success
}
bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
if (fAddr) {
return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes,
NULL, NULL);
} else {
bitmap->reset();
return false;
}
}
/////////////////////////////////////////////////////////////////////////////
void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,

View File

@ -65,6 +65,12 @@ SkMetaData& SkBaseDevice::getMetaData() {
return *fMetaData;
}
// TODO: should make this guy pure-virtual.
SkImageInfo SkBaseDevice::imageInfo() const {
return SkImageInfo::Make(this->width(), this->height(),
kUnknown_SkColorType, kIgnore_SkAlphaType);
}
const SkBitmap& SkBaseDevice::accessBitmap(bool changePixels) {
const SkBitmap& bitmap = this->onAccessBitmap();
if (changePixels) {
@ -117,3 +123,5 @@ bool SkBaseDevice::readPixels(SkBitmap* bitmap, int x, int y,
}
SkSurface* SkBaseDevice::newSurface(const SkImageInfo&) { return NULL; }
const void* SkBaseDevice::peekPixels(SkImageInfo*, size_t*) { return NULL; }

View File

@ -221,6 +221,9 @@ public:
protected:
virtual SkSurface* onNewSurface(const SkImageInfo&) SK_OVERRIDE;
const void* onPeekPixels(SkImageInfo*, size_t*) SK_OVERRIDE {
return NULL;
}
// Return fontmetrics.fTop,fBottom in topbot[0,1], after they have been
// tweaked by paint.computeFastBounds().

View File

@ -14,8 +14,6 @@
class SkPicture;
extern SkBitmap::Config SkImageInfoToBitmapConfig(const SkImageInfo&);
extern SkBitmap::Config SkColorTypeToBitmapConfig(SkColorType);
extern SkColorType SkBitmapConfigToColorType(SkBitmap::Config);
// Call this if you explicitly want to use/share this pixelRef in the image
extern SkImage* SkNewImageFromPixelRef(const SkImageInfo&, SkPixelRef*,

View File

@ -121,3 +121,8 @@ void SkSurface::draw(SkCanvas* canvas, SkScalar x, SkScalar y,
const SkPaint* paint) {
return asSB(this)->onDraw(canvas, x, y, paint);
}
const void* SkSurface::peekPixels(SkImageInfo* info, size_t* rowBytes) {
return this->getCanvas()->peekPixels(info, rowBytes);
}

View File

@ -453,7 +453,11 @@ void TestResult::testOne() {
SkGpuDevice grDevice(context, texture.get());
SkCanvas grCanvas(&grDevice);
drawPict(pic, &grCanvas, fScaleOversized ? scale : 1);
const SkBitmap& grBitmap = grDevice.accessBitmap(false);
SkBitmap grBitmap;
grBitmap.allocPixels(grCanvas.imageInfo());
grCanvas.readPixels(&grBitmap, 0, 0);
if (fTestStep == kCompareBits) {
fPixelError = similarBits(grBitmap, bitmap);
int skTime = timePict(pic, &skCanvas);

View File

@ -22,27 +22,37 @@ class GrContext;
enum SurfaceType {
kRaster_SurfaceType,
kRasterDirect_SurfaceType,
kGpu_SurfaceType,
kPicture_SurfaceType
};
static SkSurface* createSurface(SurfaceType surfaceType, GrContext* context) {
static const SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
static const int gSurfaceSize = 10;
static SkPMColor gSurfaceStorage[gSurfaceSize * gSurfaceSize];
static SkSurface* createSurface(SurfaceType surfaceType, GrContext* context,
SkImageInfo* requestedInfo = NULL) {
static const SkImageInfo info = SkImageInfo::MakeN32Premul(gSurfaceSize,
gSurfaceSize);
if (requestedInfo) {
*requestedInfo = info;
}
switch (surfaceType) {
case kRaster_SurfaceType:
return SkSurface::NewRaster(info);
case kGpu_SurfaceType:
case kRaster_SurfaceType:
return SkSurface::NewRaster(info);
case kRasterDirect_SurfaceType:
return SkSurface::NewRasterDirect(info, gSurfaceStorage,
info.minRowBytes());
case kGpu_SurfaceType:
#if SK_SUPPORT_GPU
SkASSERT(NULL != context);
return SkSurface::NewRenderTarget(context, info);
#else
SkASSERT(0);
return context ? SkSurface::NewRenderTarget(context, info) : NULL;
#endif
case kPicture_SurfaceType:
return SkSurface::NewPicture(info.fWidth, info.fHeight);
break;
case kPicture_SurfaceType:
return SkSurface::NewPicture(info.fWidth, info.fHeight);
}
SkASSERT(0);
return NULL;
}
@ -117,14 +127,14 @@ static void test_imagepeek(skiatest::Reporter* reporter) {
{ kPicture_ImageType, false },
{ kCodec_ImageType, false },
};
const SkColor color = SK_ColorRED;
const SkPMColor pmcolor = SkPreMultiplyColor(color);
for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
SkImageInfo info;
size_t rowBytes;
SkAutoTUnref<SkImage> image(createImage(gRec[i].fType, NULL, color));
if (!image.get()) {
continue; // gpu may not be enabled
@ -137,13 +147,65 @@ static void test_imagepeek(skiatest::Reporter* reporter) {
REPORTER_ASSERT(reporter, 10 == info.fHeight);
REPORTER_ASSERT(reporter, kPMColor_SkColorType == info.fColorType);
REPORTER_ASSERT(reporter, kPremul_SkAlphaType == info.fAlphaType ||
kOpaque_SkAlphaType == info.fAlphaType);
kOpaque_SkAlphaType == info.fAlphaType);
REPORTER_ASSERT(reporter, info.minRowBytes() <= rowBytes);
REPORTER_ASSERT(reporter, pmcolor == *(const SkPMColor*)addr);
}
}
}
static void test_canvaspeek(skiatest::Reporter* reporter,
GrContextFactory* factory) {
static const struct {
SurfaceType fType;
bool fPeekShouldSucceed;
} gRec[] = {
{ kRaster_SurfaceType, true },
{ kRasterDirect_SurfaceType, true },
#if SK_SUPPORT_GPU
{ kGpu_SurfaceType, false },
#endif
{ kPicture_SurfaceType, false },
};
const SkColor color = SK_ColorRED;
const SkPMColor pmcolor = SkPreMultiplyColor(color);
GrContext* context = NULL;
#if SK_SUPPORT_GPU
context = factory->get(GrContextFactory::kNative_GLContextType);
#endif
for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
SkImageInfo info, requestInfo;
size_t rowBytes;
SkAutoTUnref<SkSurface> surface(createSurface(gRec[i].fType, context,
&requestInfo));
surface->getCanvas()->clear(color);
const void* addr = surface->getCanvas()->peekPixels(&info, &rowBytes);
bool success = (NULL != addr);
REPORTER_ASSERT(reporter, gRec[i].fPeekShouldSucceed == success);
SkImageInfo info2;
size_t rb2;
const void* addr2 = surface->peekPixels(&info2, &rb2);
if (success) {
REPORTER_ASSERT(reporter, requestInfo == info);
REPORTER_ASSERT(reporter, requestInfo.minRowBytes() <= rowBytes);
REPORTER_ASSERT(reporter, pmcolor == *(const SkPMColor*)addr);
REPORTER_ASSERT(reporter, addr2 == addr);
REPORTER_ASSERT(reporter, info2 == info);
REPORTER_ASSERT(reporter, rb2 == rowBytes);
} else {
REPORTER_ASSERT(reporter, NULL == addr2);
}
}
}
static void TestSurfaceCopyOnWrite(skiatest::Reporter* reporter, SurfaceType surfaceType,
GrContext* context) {
// Verify that the right canvas commands trigger a copy on write
@ -335,7 +397,10 @@ DEF_GPUTEST(Surface, reporter, factory) {
TestSurfaceWritableAfterSnapshotRelease(reporter, kPicture_SurfaceType, NULL);
TestSurfaceNoCanvas(reporter, kRaster_SurfaceType, NULL, SkSurface::kDiscard_ContentChangeMode);
TestSurfaceNoCanvas(reporter, kRaster_SurfaceType, NULL, SkSurface::kRetain_ContentChangeMode);
test_imagepeek(reporter);
test_canvaspeek(reporter, factory);
#if SK_SUPPORT_GPU
TestGetTexture(reporter, kRaster_SurfaceType, NULL);
TestGetTexture(reporter, kPicture_SurfaceType, NULL);