2012-09-14 17:26:37 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2012 Google Inc.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
2013-12-12 21:11:12 +00:00
|
|
|
|
2014-05-06 12:02:22 +00:00
|
|
|
#include "SkBlurImageFilter.h"
|
2012-10-02 17:42:15 +00:00
|
|
|
#include "SkCanvas.h"
|
2012-11-29 21:00:39 +00:00
|
|
|
#include "SkColorPriv.h"
|
2014-04-24 21:53:13 +00:00
|
|
|
#include "SkDashPathEffect.h"
|
2012-11-29 21:00:39 +00:00
|
|
|
#include "SkData.h"
|
2013-12-13 19:45:58 +00:00
|
|
|
#include "SkDecodingImageGenerator.h"
|
2013-04-25 18:29:32 +00:00
|
|
|
#include "SkError.h"
|
Add Options to SkDecodingImageGenerator, simplify API.
Motivation: We want to remove redundant classes from Skia. To
that end we want to remove SkImageRef and its subclasses and
replace their uses with SkDiscardablePixelRef +
SkDecodingImageGenerator. Since Android uses SkImageRef, we need
to make sure that SkDecodingImageGenerator allows all of the
settings that Android exposes in BitmapFactory.Options.
To that end, we have created an Options struct for the
SkDecodingImageGenerator which lets the client of the generator set
sample size, dithering, and bitmap config.
We have made the SkDecodingImageGenerator constructor private
and replaced the SkDecodingImageGenerator::Install functions
with a SkDecodingImageGenerator::Create functions (one for
SkData and one for SkStream) which now take a
SkDecodingImageGenerator::Options struct.
Also added a ImageDecoderOptions test which loops through a list
of sets of options and tries them on a set of 5 small encoded
images.
Also updated several users of SkDecodingImageGenerator::Install to
follow new call signature - gm/factory.cpp, LazyDecodeBitmap.cpp,
and PictureTest.cpp, CachedDecodingPixelRefTest.cpp.
We also added a new ImprovedBitmapFactory Test which simulates the
exact function that Android will need to modify to use this,
installPixelRef() in BitmapFactory.
R=reed@google.com, scroggo@google.com
Committed: https://code.google.com/p/skia/source/detail?r=12744
Review URL: https://codereview.chromium.org/93703004
git-svn-id: http://skia.googlecode.com/svn/trunk@12855 2bbb7eff-a529-9590-31e7-b0007b416f81
2014-01-02 13:15:13 +00:00
|
|
|
#include "SkImageEncoder.h"
|
|
|
|
#include "SkImageGenerator.h"
|
2012-10-02 17:42:15 +00:00
|
|
|
#include "SkPaint.h"
|
2012-09-14 17:26:37 +00:00
|
|
|
#include "SkPicture.h"
|
2014-04-18 18:04:41 +00:00
|
|
|
#include "SkPictureRecorder.h"
|
2013-08-29 11:54:56 +00:00
|
|
|
#include "SkPictureUtils.h"
|
2013-01-24 18:27:42 +00:00
|
|
|
#include "SkRRect.h"
|
2014-01-24 20:56:26 +00:00
|
|
|
#include "SkRandom.h"
|
2012-11-29 21:00:39 +00:00
|
|
|
#include "SkShader.h"
|
2012-09-14 17:26:37 +00:00
|
|
|
#include "SkStream.h"
|
2014-05-06 12:02:22 +00:00
|
|
|
|
|
|
|
#if SK_SUPPORT_GPU
|
|
|
|
#include "SkSurface.h"
|
|
|
|
#include "GrContextFactory.h"
|
|
|
|
#include "GrPictureUtils.h"
|
|
|
|
#endif
|
2014-01-24 20:56:26 +00:00
|
|
|
#include "Test.h"
|
2012-09-14 17:26:37 +00:00
|
|
|
|
2014-05-14 18:58:16 +00:00
|
|
|
#include "SkLumaColorFilter.h"
|
|
|
|
#include "SkColorFilterImageFilter.h"
|
|
|
|
|
2014-01-09 19:20:45 +00:00
|
|
|
static const int gColorScale = 30;
|
|
|
|
static const int gColorOffset = 60;
|
2012-11-29 21:00:39 +00:00
|
|
|
|
|
|
|
static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
|
2014-02-13 14:41:43 +00:00
|
|
|
bm->allocN32Pixels(w, h);
|
2012-11-29 21:00:39 +00:00
|
|
|
bm->eraseColor(color);
|
|
|
|
if (immutable) {
|
|
|
|
bm->setImmutable();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-09 19:45:29 +00:00
|
|
|
static void make_checkerboard(SkBitmap* bm, int w, int h, bool immutable) {
|
2014-01-09 19:20:45 +00:00
|
|
|
SkASSERT(w % 2 == 0);
|
|
|
|
SkASSERT(h % 2 == 0);
|
2014-02-13 14:41:43 +00:00
|
|
|
bm->allocPixels(SkImageInfo::Make(w, h, kAlpha_8_SkColorType,
|
|
|
|
kPremul_SkAlphaType));
|
2014-01-09 19:20:45 +00:00
|
|
|
SkAutoLockPixels lock(*bm);
|
|
|
|
for (int y = 0; y < h; y += 2) {
|
|
|
|
uint8_t* s = bm->getAddr8(0, y);
|
|
|
|
for (int x = 0; x < w; x += 2) {
|
|
|
|
*s++ = 0xFF;
|
|
|
|
*s++ = 0x00;
|
|
|
|
}
|
|
|
|
s = bm->getAddr8(0, y + 1);
|
|
|
|
for (int x = 0; x < w; x += 2) {
|
|
|
|
*s++ = 0x00;
|
|
|
|
*s++ = 0xFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (immutable) {
|
|
|
|
bm->setImmutable();
|
|
|
|
}
|
|
|
|
}
|
2012-11-29 21:00:39 +00:00
|
|
|
|
2014-01-09 19:20:45 +00:00
|
|
|
static void init_paint(SkPaint* paint, const SkBitmap &bm) {
|
|
|
|
SkShader* shader = SkShader::CreateBitmapShader(bm,
|
|
|
|
SkShader::kClamp_TileMode,
|
|
|
|
SkShader::kClamp_TileMode);
|
|
|
|
paint->setShader(shader)->unref();
|
2012-11-29 21:00:39 +00:00
|
|
|
}
|
|
|
|
|
2014-01-14 07:01:42 +00:00
|
|
|
typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&,
|
2014-01-13 13:33:26 +00:00
|
|
|
const SkBitmap&, const SkPoint&,
|
|
|
|
SkTDArray<SkPixelRef*>* usedPixRefs);
|
2014-01-09 19:20:45 +00:00
|
|
|
|
2014-01-10 07:08:11 +00:00
|
|
|
static void drawpaint_proc(SkCanvas* canvas, const SkBitmap& bm,
|
2014-01-13 13:33:26 +00:00
|
|
|
const SkBitmap& altBM, const SkPoint& pos,
|
|
|
|
SkTDArray<SkPixelRef*>* usedPixRefs) {
|
2014-01-09 19:20:45 +00:00
|
|
|
SkPaint paint;
|
|
|
|
init_paint(&paint, bm);
|
|
|
|
|
|
|
|
canvas->drawPaint(paint);
|
2014-01-13 13:33:26 +00:00
|
|
|
*usedPixRefs->append() = bm.pixelRef();
|
2012-11-29 21:00:39 +00:00
|
|
|
}
|
|
|
|
|
2014-01-10 07:08:11 +00:00
|
|
|
static void drawpoints_proc(SkCanvas* canvas, const SkBitmap& bm,
|
2014-01-13 13:33:26 +00:00
|
|
|
const SkBitmap& altBM, const SkPoint& pos,
|
|
|
|
SkTDArray<SkPixelRef*>* usedPixRefs) {
|
2014-01-09 19:20:45 +00:00
|
|
|
SkPaint paint;
|
|
|
|
init_paint(&paint, bm);
|
|
|
|
|
2014-01-13 13:33:26 +00:00
|
|
|
// draw a rect
|
2014-01-09 19:20:45 +00:00
|
|
|
SkPoint points[5] = {
|
2014-01-14 07:01:42 +00:00
|
|
|
{ pos.fX, pos.fY },
|
|
|
|
{ pos.fX + bm.width() - 1, pos.fY },
|
|
|
|
{ pos.fX + bm.width() - 1, pos.fY + bm.height() - 1 },
|
|
|
|
{ pos.fX, pos.fY + bm.height() - 1 },
|
|
|
|
{ pos.fX, pos.fY },
|
2012-11-29 21:00:39 +00:00
|
|
|
};
|
|
|
|
|
2014-01-09 19:20:45 +00:00
|
|
|
canvas->drawPoints(SkCanvas::kPolygon_PointMode, 5, points, paint);
|
2014-01-13 13:33:26 +00:00
|
|
|
*usedPixRefs->append() = bm.pixelRef();
|
2014-01-09 19:20:45 +00:00
|
|
|
}
|
|
|
|
|
2014-01-10 07:08:11 +00:00
|
|
|
static void drawrect_proc(SkCanvas* canvas, const SkBitmap& bm,
|
2014-01-13 13:33:26 +00:00
|
|
|
const SkBitmap& altBM, const SkPoint& pos,
|
|
|
|
SkTDArray<SkPixelRef*>* usedPixRefs) {
|
2012-11-29 21:00:39 +00:00
|
|
|
SkPaint paint;
|
2014-01-09 19:20:45 +00:00
|
|
|
init_paint(&paint, bm);
|
|
|
|
|
|
|
|
SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) };
|
|
|
|
r.offset(pos.fX, pos.fY);
|
|
|
|
|
2012-11-29 21:00:39 +00:00
|
|
|
canvas->drawRect(r, paint);
|
2014-01-13 13:33:26 +00:00
|
|
|
*usedPixRefs->append() = bm.pixelRef();
|
2014-01-09 19:20:45 +00:00
|
|
|
}
|
|
|
|
|
2014-01-10 07:08:11 +00:00
|
|
|
static void drawoval_proc(SkCanvas* canvas, const SkBitmap& bm,
|
2014-01-13 13:33:26 +00:00
|
|
|
const SkBitmap& altBM, const SkPoint& pos,
|
|
|
|
SkTDArray<SkPixelRef*>* usedPixRefs) {
|
2014-01-09 19:20:45 +00:00
|
|
|
SkPaint paint;
|
|
|
|
init_paint(&paint, bm);
|
|
|
|
|
|
|
|
SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) };
|
|
|
|
r.offset(pos.fX, pos.fY);
|
|
|
|
|
2013-01-24 18:27:42 +00:00
|
|
|
canvas->drawOval(r, paint);
|
2014-01-13 13:33:26 +00:00
|
|
|
*usedPixRefs->append() = bm.pixelRef();
|
2014-01-09 19:20:45 +00:00
|
|
|
}
|
|
|
|
|
2014-01-10 07:08:11 +00:00
|
|
|
static void drawrrect_proc(SkCanvas* canvas, const SkBitmap& bm,
|
2014-01-13 13:33:26 +00:00
|
|
|
const SkBitmap& altBM, const SkPoint& pos,
|
|
|
|
SkTDArray<SkPixelRef*>* usedPixRefs) {
|
2014-01-09 19:20:45 +00:00
|
|
|
SkPaint paint;
|
|
|
|
init_paint(&paint, bm);
|
2014-01-10 07:08:11 +00:00
|
|
|
|
2014-01-09 19:20:45 +00:00
|
|
|
SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) };
|
|
|
|
r.offset(pos.fX, pos.fY);
|
|
|
|
|
2013-01-24 18:27:42 +00:00
|
|
|
SkRRect rr;
|
2014-01-09 19:20:45 +00:00
|
|
|
rr.setRectXY(r, SkIntToScalar(bm.width())/4, SkIntToScalar(bm.height())/4);
|
2013-01-24 18:27:42 +00:00
|
|
|
canvas->drawRRect(rr, paint);
|
2014-01-13 13:33:26 +00:00
|
|
|
*usedPixRefs->append() = bm.pixelRef();
|
2012-11-29 21:00:39 +00:00
|
|
|
}
|
|
|
|
|
2014-01-10 07:08:11 +00:00
|
|
|
static void drawpath_proc(SkCanvas* canvas, const SkBitmap& bm,
|
2014-01-13 13:33:26 +00:00
|
|
|
const SkBitmap& altBM, const SkPoint& pos,
|
|
|
|
SkTDArray<SkPixelRef*>* usedPixRefs) {
|
2014-01-09 19:20:45 +00:00
|
|
|
SkPaint paint;
|
|
|
|
init_paint(&paint, bm);
|
|
|
|
|
|
|
|
SkPath path;
|
|
|
|
path.lineTo(bm.width()/2.0f, SkIntToScalar(bm.height()));
|
|
|
|
path.lineTo(SkIntToScalar(bm.width()), 0);
|
|
|
|
path.close();
|
|
|
|
path.offset(pos.fX, pos.fY);
|
|
|
|
|
|
|
|
canvas->drawPath(path, paint);
|
2014-01-13 13:33:26 +00:00
|
|
|
*usedPixRefs->append() = bm.pixelRef();
|
2014-01-09 19:20:45 +00:00
|
|
|
}
|
|
|
|
|
2014-01-10 07:08:11 +00:00
|
|
|
static void drawbitmap_proc(SkCanvas* canvas, const SkBitmap& bm,
|
2014-01-13 13:33:26 +00:00
|
|
|
const SkBitmap& altBM, const SkPoint& pos,
|
|
|
|
SkTDArray<SkPixelRef*>* usedPixRefs) {
|
2014-01-09 19:20:45 +00:00
|
|
|
canvas->drawBitmap(bm, pos.fX, pos.fY, NULL);
|
2014-01-13 13:33:26 +00:00
|
|
|
*usedPixRefs->append() = bm.pixelRef();
|
2014-01-09 19:20:45 +00:00
|
|
|
}
|
|
|
|
|
2014-01-10 07:08:11 +00:00
|
|
|
static void drawbitmap_withshader_proc(SkCanvas* canvas, const SkBitmap& bm,
|
2014-01-13 13:33:26 +00:00
|
|
|
const SkBitmap& altBM, const SkPoint& pos,
|
|
|
|
SkTDArray<SkPixelRef*>* usedPixRefs) {
|
2014-01-09 19:20:45 +00:00
|
|
|
SkPaint paint;
|
|
|
|
init_paint(&paint, bm);
|
|
|
|
|
|
|
|
// The bitmap in the paint is ignored unless we're drawing an A8 bitmap
|
|
|
|
canvas->drawBitmap(altBM, pos.fX, pos.fY, &paint);
|
2014-01-13 13:33:26 +00:00
|
|
|
*usedPixRefs->append() = bm.pixelRef();
|
|
|
|
*usedPixRefs->append() = altBM.pixelRef();
|
2014-01-09 19:20:45 +00:00
|
|
|
}
|
|
|
|
|
2014-01-10 07:08:11 +00:00
|
|
|
static void drawsprite_proc(SkCanvas* canvas, const SkBitmap& bm,
|
2014-01-13 13:33:26 +00:00
|
|
|
const SkBitmap& altBM, const SkPoint& pos,
|
|
|
|
SkTDArray<SkPixelRef*>* usedPixRefs) {
|
2014-01-09 19:20:45 +00:00
|
|
|
const SkMatrix& ctm = canvas->getTotalMatrix();
|
|
|
|
|
|
|
|
SkPoint p(pos);
|
|
|
|
ctm.mapPoints(&p, 1);
|
|
|
|
|
|
|
|
canvas->drawSprite(bm, (int)p.fX, (int)p.fY, NULL);
|
2014-01-13 13:33:26 +00:00
|
|
|
*usedPixRefs->append() = bm.pixelRef();
|
2014-01-09 19:20:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
// Although specifiable, this case doesn't seem to make sense (i.e., the
|
|
|
|
// bitmap in the shader is never used).
|
2014-01-10 07:08:11 +00:00
|
|
|
static void drawsprite_withshader_proc(SkCanvas* canvas, const SkBitmap& bm,
|
2014-01-13 13:33:26 +00:00
|
|
|
const SkBitmap& altBM, const SkPoint& pos,
|
|
|
|
SkTDArray<SkPixelRef*>* usedPixRefs) {
|
2014-01-09 19:20:45 +00:00
|
|
|
SkPaint paint;
|
|
|
|
init_paint(&paint, bm);
|
|
|
|
|
|
|
|
const SkMatrix& ctm = canvas->getTotalMatrix();
|
|
|
|
|
|
|
|
SkPoint p(pos);
|
|
|
|
ctm.mapPoints(&p, 1);
|
|
|
|
|
|
|
|
canvas->drawSprite(altBM, (int)p.fX, (int)p.fY, &paint);
|
2014-01-13 13:33:26 +00:00
|
|
|
*usedPixRefs->append() = bm.pixelRef();
|
|
|
|
*usedPixRefs->append() = altBM.pixelRef();
|
2014-01-09 19:20:45 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-01-10 07:08:11 +00:00
|
|
|
static void drawbitmaprect_proc(SkCanvas* canvas, const SkBitmap& bm,
|
2014-01-13 13:33:26 +00:00
|
|
|
const SkBitmap& altBM, const SkPoint& pos,
|
|
|
|
SkTDArray<SkPixelRef*>* usedPixRefs) {
|
2014-01-09 19:20:45 +00:00
|
|
|
SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) };
|
|
|
|
|
|
|
|
r.offset(pos.fX, pos.fY);
|
|
|
|
canvas->drawBitmapRectToRect(bm, NULL, r, NULL);
|
2014-01-13 13:33:26 +00:00
|
|
|
*usedPixRefs->append() = bm.pixelRef();
|
2014-01-09 19:20:45 +00:00
|
|
|
}
|
|
|
|
|
2014-01-10 07:08:11 +00:00
|
|
|
static void drawbitmaprect_withshader_proc(SkCanvas* canvas,
|
|
|
|
const SkBitmap& bm,
|
|
|
|
const SkBitmap& altBM,
|
2014-01-13 13:33:26 +00:00
|
|
|
const SkPoint& pos,
|
|
|
|
SkTDArray<SkPixelRef*>* usedPixRefs) {
|
2014-01-09 19:20:45 +00:00
|
|
|
SkPaint paint;
|
|
|
|
init_paint(&paint, bm);
|
|
|
|
|
|
|
|
SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) };
|
|
|
|
r.offset(pos.fX, pos.fY);
|
|
|
|
|
|
|
|
// The bitmap in the paint is ignored unless we're drawing an A8 bitmap
|
|
|
|
canvas->drawBitmapRectToRect(altBM, NULL, r, &paint);
|
2014-01-13 13:33:26 +00:00
|
|
|
*usedPixRefs->append() = bm.pixelRef();
|
|
|
|
*usedPixRefs->append() = altBM.pixelRef();
|
2014-01-09 19:20:45 +00:00
|
|
|
}
|
|
|
|
|
2014-01-10 07:08:11 +00:00
|
|
|
static void drawtext_proc(SkCanvas* canvas, const SkBitmap& bm,
|
2014-01-13 13:33:26 +00:00
|
|
|
const SkBitmap& altBM, const SkPoint& pos,
|
|
|
|
SkTDArray<SkPixelRef*>* usedPixRefs) {
|
2014-01-09 19:20:45 +00:00
|
|
|
SkPaint paint;
|
|
|
|
init_paint(&paint, bm);
|
|
|
|
paint.setTextSize(SkIntToScalar(1.5*bm.width()));
|
|
|
|
|
|
|
|
canvas->drawText("0", 1, pos.fX, pos.fY+bm.width(), paint);
|
2014-01-13 13:33:26 +00:00
|
|
|
*usedPixRefs->append() = bm.pixelRef();
|
2014-01-09 19:20:45 +00:00
|
|
|
}
|
|
|
|
|
2014-01-10 07:08:11 +00:00
|
|
|
static void drawpostext_proc(SkCanvas* canvas, const SkBitmap& bm,
|
2014-01-13 13:33:26 +00:00
|
|
|
const SkBitmap& altBM, const SkPoint& pos,
|
|
|
|
SkTDArray<SkPixelRef*>* usedPixRefs) {
|
2014-01-09 19:20:45 +00:00
|
|
|
SkPaint paint;
|
|
|
|
init_paint(&paint, bm);
|
|
|
|
paint.setTextSize(SkIntToScalar(1.5*bm.width()));
|
|
|
|
|
|
|
|
SkPoint point = { pos.fX, pos.fY + bm.height() };
|
|
|
|
canvas->drawPosText("O", 1, &point, paint);
|
2014-01-13 13:33:26 +00:00
|
|
|
*usedPixRefs->append() = bm.pixelRef();
|
2014-01-09 19:20:45 +00:00
|
|
|
}
|
|
|
|
|
2014-01-10 07:08:11 +00:00
|
|
|
static void drawtextonpath_proc(SkCanvas* canvas, const SkBitmap& bm,
|
2014-01-13 13:33:26 +00:00
|
|
|
const SkBitmap& altBM, const SkPoint& pos,
|
|
|
|
SkTDArray<SkPixelRef*>* usedPixRefs) {
|
2014-01-09 19:20:45 +00:00
|
|
|
SkPaint paint;
|
|
|
|
|
|
|
|
init_paint(&paint, bm);
|
|
|
|
paint.setTextSize(SkIntToScalar(1.5*bm.width()));
|
|
|
|
|
|
|
|
SkPath path;
|
|
|
|
path.lineTo(SkIntToScalar(bm.width()), 0);
|
|
|
|
path.offset(pos.fX, pos.fY+bm.height());
|
|
|
|
|
|
|
|
canvas->drawTextOnPath("O", 1, path, NULL, paint);
|
2014-01-13 13:33:26 +00:00
|
|
|
*usedPixRefs->append() = bm.pixelRef();
|
2014-01-09 19:20:45 +00:00
|
|
|
}
|
|
|
|
|
2014-01-10 07:08:11 +00:00
|
|
|
static void drawverts_proc(SkCanvas* canvas, const SkBitmap& bm,
|
2014-01-13 13:33:26 +00:00
|
|
|
const SkBitmap& altBM, const SkPoint& pos,
|
|
|
|
SkTDArray<SkPixelRef*>* usedPixRefs) {
|
2014-01-09 19:20:45 +00:00
|
|
|
SkPaint paint;
|
|
|
|
init_paint(&paint, bm);
|
|
|
|
|
2014-01-14 07:01:42 +00:00
|
|
|
SkPoint verts[4] = {
|
|
|
|
{ pos.fX, pos.fY },
|
|
|
|
{ pos.fX + bm.width(), pos.fY },
|
|
|
|
{ pos.fX + bm.width(), pos.fY + bm.height() },
|
|
|
|
{ pos.fX, pos.fY + bm.height() }
|
2014-01-09 19:20:45 +00:00
|
|
|
};
|
2014-01-10 07:08:11 +00:00
|
|
|
SkPoint texs[4] = { { 0, 0 },
|
|
|
|
{ SkIntToScalar(bm.width()), 0 },
|
|
|
|
{ SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) },
|
2014-01-09 19:20:45 +00:00
|
|
|
{ 0, SkIntToScalar(bm.height()) } };
|
|
|
|
uint16_t indices[6] = { 0, 1, 2, 0, 2, 3 };
|
|
|
|
|
2014-01-10 07:08:11 +00:00
|
|
|
canvas->drawVertices(SkCanvas::kTriangles_VertexMode, 4, verts, texs, NULL, NULL,
|
2014-01-09 19:20:45 +00:00
|
|
|
indices, 6, paint);
|
2014-01-13 13:33:26 +00:00
|
|
|
*usedPixRefs->append() = bm.pixelRef();
|
2014-01-09 19:20:45 +00:00
|
|
|
}
|
|
|
|
|
2012-11-29 21:00:39 +00:00
|
|
|
// Return a picture with the bitmaps drawn at the specified positions.
|
2014-01-14 07:01:42 +00:00
|
|
|
static SkPicture* record_bitmaps(const SkBitmap bm[],
|
2014-01-13 13:33:26 +00:00
|
|
|
const SkPoint pos[],
|
|
|
|
SkTDArray<SkPixelRef*> analytic[],
|
2014-01-14 07:01:42 +00:00
|
|
|
int count,
|
2014-01-13 13:33:26 +00:00
|
|
|
DrawBitmapProc proc) {
|
2014-04-13 19:09:42 +00:00
|
|
|
SkPictureRecorder recorder;
|
2014-06-09 13:25:34 +00:00
|
|
|
SkCanvas* canvas = recorder.beginRecording(1000, 1000);
|
2012-11-29 21:00:39 +00:00
|
|
|
for (int i = 0; i < count; ++i) {
|
2014-01-13 13:33:26 +00:00
|
|
|
analytic[i].rewind();
|
2014-01-09 19:20:45 +00:00
|
|
|
canvas->save();
|
|
|
|
SkRect clipRect = SkRect::MakeXYWH(pos[i].fX, pos[i].fY,
|
|
|
|
SkIntToScalar(bm[i].width()),
|
|
|
|
SkIntToScalar(bm[i].height()));
|
|
|
|
canvas->clipRect(clipRect, SkRegion::kIntersect_Op);
|
2014-01-13 13:33:26 +00:00
|
|
|
proc(canvas, bm[i], bm[count+i], pos[i], &analytic[i]);
|
2014-01-09 19:20:45 +00:00
|
|
|
canvas->restore();
|
2012-11-29 21:00:39 +00:00
|
|
|
}
|
2014-04-13 19:09:42 +00:00
|
|
|
return recorder.endRecording();
|
2012-11-29 21:00:39 +00:00
|
|
|
}
|
|
|
|
|
2013-09-09 20:09:12 +00:00
|
|
|
static void rand_rect(SkRect* rect, SkRandom& rand, SkScalar W, SkScalar H) {
|
2012-11-29 21:00:39 +00:00
|
|
|
rect->fLeft = rand.nextRangeScalar(-W, 2*W);
|
|
|
|
rect->fTop = rand.nextRangeScalar(-H, 2*H);
|
|
|
|
rect->fRight = rect->fLeft + rand.nextRangeScalar(0, W);
|
|
|
|
rect->fBottom = rect->fTop + rand.nextRangeScalar(0, H);
|
|
|
|
|
|
|
|
// we integralize rect to make our tests more predictable, since Gather is
|
|
|
|
// a little sloppy.
|
|
|
|
SkIRect ir;
|
|
|
|
rect->round(&ir);
|
|
|
|
rect->set(ir);
|
|
|
|
}
|
|
|
|
|
2014-01-09 19:20:45 +00:00
|
|
|
static void draw(SkPicture* pic, int width, int height, SkBitmap* result) {
|
|
|
|
make_bm(result, width, height, SK_ColorBLACK, false);
|
2012-11-29 21:00:39 +00:00
|
|
|
|
|
|
|
SkCanvas canvas(*result);
|
2014-06-04 12:40:44 +00:00
|
|
|
canvas.drawPicture(pic);
|
2012-11-29 21:00:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T> int find_index(const T* array, T elem, int count) {
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
|
|
if (array[i] == elem) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return true if 'ref' is found in array[]
|
|
|
|
static bool find(SkPixelRef const * const * array, SkPixelRef const * ref, int count) {
|
|
|
|
return find_index<const SkPixelRef*>(array, ref, count) >= 0;
|
|
|
|
}
|
|
|
|
|
2014-01-10 07:08:11 +00:00
|
|
|
// Look at each pixel that is inside 'subset', and if its color appears in
|
|
|
|
// colors[], find the corresponding value in refs[] and append that ref into
|
2014-01-09 19:20:45 +00:00
|
|
|
// array, skipping duplicates of the same value.
|
|
|
|
// Note that gathering pixelRefs from rendered colors suffers from the problem
|
|
|
|
// that multiple simultaneous textures (e.g., A8 for alpha and 8888 for color)
|
|
|
|
// isn't easy to reconstruct.
|
|
|
|
static void gather_from_image(const SkBitmap& bm, SkPixelRef* const refs[],
|
|
|
|
int count, SkTDArray<SkPixelRef*>* array,
|
|
|
|
const SkRect& subset) {
|
|
|
|
SkIRect ir;
|
|
|
|
subset.roundOut(&ir);
|
|
|
|
|
|
|
|
if (!ir.intersect(0, 0, bm.width()-1, bm.height()-1)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-11-29 21:00:39 +00:00
|
|
|
// Since we only want to return unique values in array, when we scan we just
|
|
|
|
// set a bit for each index'd color found. In practice we only have a few
|
|
|
|
// distinct colors, so we just use an int's bits as our array. Hence the
|
|
|
|
// assert that count <= number-of-bits-in-our-int.
|
|
|
|
SkASSERT((unsigned)count <= 32);
|
|
|
|
uint32_t bitarray = 0;
|
|
|
|
|
|
|
|
SkAutoLockPixels alp(bm);
|
|
|
|
|
2014-01-09 19:20:45 +00:00
|
|
|
for (int y = ir.fTop; y < ir.fBottom; ++y) {
|
|
|
|
for (int x = ir.fLeft; x < ir.fRight; ++x) {
|
2012-11-29 21:00:39 +00:00
|
|
|
SkPMColor pmc = *bm.getAddr32(x, y);
|
|
|
|
// the only good case where the color is not found would be if
|
|
|
|
// the color is transparent, meaning no bitmap was drawn in that
|
|
|
|
// pixel.
|
|
|
|
if (pmc) {
|
2013-01-08 17:24:44 +00:00
|
|
|
uint32_t index = SkGetPackedR32(pmc);
|
2012-11-29 21:00:39 +00:00
|
|
|
SkASSERT(SkGetPackedG32(pmc) == index);
|
|
|
|
SkASSERT(SkGetPackedB32(pmc) == index);
|
2014-01-09 19:20:45 +00:00
|
|
|
if (0 == index) {
|
|
|
|
continue; // background color
|
|
|
|
}
|
|
|
|
SkASSERT(0 == (index - gColorOffset) % gColorScale);
|
|
|
|
index = (index - gColorOffset) / gColorScale;
|
2013-01-08 18:42:20 +00:00
|
|
|
SkASSERT(static_cast<int>(index) < count);
|
2012-11-29 21:00:39 +00:00
|
|
|
bitarray |= 1 << index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
|
|
if (bitarray & (1 << i)) {
|
|
|
|
*array->append() = refs[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-13 13:46:45 +00:00
|
|
|
static void gather_from_analytic(const SkPoint pos[], SkScalar w, SkScalar h,
|
2014-01-14 07:01:42 +00:00
|
|
|
const SkTDArray<SkPixelRef*> analytic[],
|
|
|
|
int count,
|
|
|
|
SkTDArray<SkPixelRef*>* result,
|
2014-01-13 13:46:45 +00:00
|
|
|
const SkRect& subset) {
|
2014-01-13 13:33:26 +00:00
|
|
|
for (int i = 0; i < count; ++i) {
|
|
|
|
SkRect rect = SkRect::MakeXYWH(pos[i].fX, pos[i].fY, w, h);
|
|
|
|
|
|
|
|
if (SkRect::Intersects(subset, rect)) {
|
|
|
|
result->append(analytic[i].count(), analytic[i].begin());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-05 20:59:45 +00:00
|
|
|
|
|
|
|
static const struct {
|
|
|
|
const DrawBitmapProc proc;
|
|
|
|
const char* const desc;
|
|
|
|
} gProcs[] = {
|
|
|
|
{drawpaint_proc, "drawpaint"},
|
|
|
|
{drawpoints_proc, "drawpoints"},
|
|
|
|
{drawrect_proc, "drawrect"},
|
|
|
|
{drawoval_proc, "drawoval"},
|
|
|
|
{drawrrect_proc, "drawrrect"},
|
|
|
|
{drawpath_proc, "drawpath"},
|
|
|
|
{drawbitmap_proc, "drawbitmap"},
|
|
|
|
{drawbitmap_withshader_proc, "drawbitmap_withshader"},
|
|
|
|
{drawsprite_proc, "drawsprite"},
|
2014-01-13 13:33:26 +00:00
|
|
|
#if 0
|
2014-03-05 20:59:45 +00:00
|
|
|
{drawsprite_withshader_proc, "drawsprite_withshader"},
|
2014-01-13 13:33:26 +00:00
|
|
|
#endif
|
2014-03-05 20:59:45 +00:00
|
|
|
{drawbitmaprect_proc, "drawbitmaprect"},
|
|
|
|
{drawbitmaprect_withshader_proc, "drawbitmaprect_withshader"},
|
|
|
|
{drawtext_proc, "drawtext"},
|
|
|
|
{drawpostext_proc, "drawpostext"},
|
|
|
|
{drawtextonpath_proc, "drawtextonpath"},
|
|
|
|
{drawverts_proc, "drawverts"},
|
2014-01-13 13:33:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static void create_textures(SkBitmap* bm, SkPixelRef** refs, int num, int w, int h) {
|
2014-01-14 07:01:42 +00:00
|
|
|
// Our convention is that the color components contain an encoding of
|
|
|
|
// the index of their corresponding bitmap/pixelref. (0,0,0,0) is
|
2014-01-13 13:33:26 +00:00
|
|
|
// reserved for the background
|
|
|
|
for (int i = 0; i < num; ++i) {
|
2014-01-14 07:01:42 +00:00
|
|
|
make_bm(&bm[i], w, h,
|
|
|
|
SkColorSetARGB(0xFF,
|
|
|
|
gColorScale*i+gColorOffset,
|
|
|
|
gColorScale*i+gColorOffset,
|
|
|
|
gColorScale*i+gColorOffset),
|
2014-01-13 13:33:26 +00:00
|
|
|
true);
|
|
|
|
refs[i] = bm[i].pixelRef();
|
|
|
|
}
|
|
|
|
|
|
|
|
// The A8 alternate bitmaps are all BW checkerboards
|
|
|
|
for (int i = 0; i < num; ++i) {
|
|
|
|
make_checkerboard(&bm[num+i], w, h, true);
|
|
|
|
refs[num+i] = bm[num+i].pixelRef();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-29 21:00:39 +00:00
|
|
|
static void test_gatherpixelrefs(skiatest::Reporter* reporter) {
|
2014-01-09 19:20:45 +00:00
|
|
|
const int IW = 32;
|
2012-11-29 21:00:39 +00:00
|
|
|
const int IH = IW;
|
|
|
|
const SkScalar W = SkIntToScalar(IW);
|
|
|
|
const SkScalar H = W;
|
|
|
|
|
|
|
|
static const int N = 4;
|
2014-01-09 19:20:45 +00:00
|
|
|
SkBitmap bm[2*N];
|
|
|
|
SkPixelRef* refs[2*N];
|
2014-01-13 13:33:26 +00:00
|
|
|
SkTDArray<SkPixelRef*> analytic[N];
|
2012-11-29 21:00:39 +00:00
|
|
|
|
2014-01-09 19:20:45 +00:00
|
|
|
const SkPoint pos[N] = {
|
2012-11-29 21:00:39 +00:00
|
|
|
{ 0, 0 }, { W, 0 }, { 0, H }, { W, H }
|
|
|
|
};
|
|
|
|
|
2014-01-13 13:33:26 +00:00
|
|
|
create_textures(bm, refs, N, IW, IH);
|
2012-11-29 21:00:39 +00:00
|
|
|
|
2013-09-09 20:09:12 +00:00
|
|
|
SkRandom rand;
|
2014-01-13 13:33:26 +00:00
|
|
|
for (size_t k = 0; k < SK_ARRAY_COUNT(gProcs); ++k) {
|
2014-03-05 20:59:45 +00:00
|
|
|
SkAutoTUnref<SkPicture> pic(
|
|
|
|
record_bitmaps(bm, pos, analytic, N, gProcs[k].proc));
|
2012-11-29 21:00:39 +00:00
|
|
|
|
2013-10-24 11:12:47 +00:00
|
|
|
REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0);
|
2012-11-29 21:00:39 +00:00
|
|
|
// quick check for a small piece of each quadrant, which should just
|
2014-01-13 13:33:26 +00:00
|
|
|
// contain 1 or 2 bitmaps.
|
2012-11-29 21:00:39 +00:00
|
|
|
for (size_t i = 0; i < SK_ARRAY_COUNT(pos); ++i) {
|
|
|
|
SkRect r;
|
|
|
|
r.set(2, 2, W - 2, H - 2);
|
|
|
|
r.offset(pos[i].fX, pos[i].fY);
|
|
|
|
SkAutoDataUnref data(SkPictureUtils::GatherPixelRefs(pic, r));
|
2014-03-05 20:59:45 +00:00
|
|
|
if (!data) {
|
|
|
|
ERRORF(reporter, "SkPictureUtils::GatherPixelRefs returned "
|
|
|
|
"NULL for %s.", gProcs[k].desc);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
SkPixelRef** gatheredRefs = (SkPixelRef**)data->data();
|
|
|
|
int count = static_cast<int>(data->size() / sizeof(SkPixelRef*));
|
|
|
|
REPORTER_ASSERT(reporter, 1 == count || 2 == count);
|
|
|
|
if (1 == count) {
|
|
|
|
REPORTER_ASSERT(reporter, gatheredRefs[0] == refs[i]);
|
|
|
|
} else if (2 == count) {
|
|
|
|
REPORTER_ASSERT(reporter,
|
|
|
|
(gatheredRefs[0] == refs[i] && gatheredRefs[1] == refs[i+N]) ||
|
|
|
|
(gatheredRefs[1] == refs[i] && gatheredRefs[0] == refs[i+N]));
|
2013-07-09 16:04:32 +00:00
|
|
|
}
|
2012-11-29 21:00:39 +00:00
|
|
|
}
|
|
|
|
|
2014-01-09 19:20:45 +00:00
|
|
|
SkBitmap image;
|
|
|
|
draw(pic, 2*IW, 2*IH, &image);
|
|
|
|
|
2012-11-29 21:00:39 +00:00
|
|
|
// Test a bunch of random (mostly) rects, and compare the gather results
|
|
|
|
// with a deduced list of refs by looking at the colors drawn.
|
|
|
|
for (int j = 0; j < 100; ++j) {
|
|
|
|
SkRect r;
|
|
|
|
rand_rect(&r, rand, 2*W, 2*H);
|
|
|
|
|
2014-01-13 13:33:26 +00:00
|
|
|
SkTDArray<SkPixelRef*> fromImage;
|
|
|
|
gather_from_image(image, refs, N, &fromImage, r);
|
|
|
|
|
|
|
|
SkTDArray<SkPixelRef*> fromAnalytic;
|
|
|
|
gather_from_analytic(pos, W, H, analytic, N, &fromAnalytic, r);
|
2012-11-29 21:00:39 +00:00
|
|
|
|
|
|
|
SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
|
|
|
|
size_t dataSize = data ? data->size() : 0;
|
2013-10-16 17:48:11 +00:00
|
|
|
int gatherCount = static_cast<int>(dataSize / sizeof(SkPixelRef*));
|
2012-11-29 21:00:39 +00:00
|
|
|
SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize);
|
|
|
|
SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL;
|
|
|
|
SkAutoDataUnref adu(data);
|
|
|
|
|
2014-01-13 13:33:26 +00:00
|
|
|
// Everything that we saw drawn should appear in the analytic list
|
|
|
|
// but the analytic list may contain some pixelRefs that were not
|
|
|
|
// seen in the image (e.g., A8 textures used as masks)
|
|
|
|
for (int i = 0; i < fromImage.count(); ++i) {
|
2014-03-05 20:59:45 +00:00
|
|
|
if (-1 == fromAnalytic.find(fromImage[i])) {
|
|
|
|
ERRORF(reporter, "PixelRef missing %d %s",
|
|
|
|
i, gProcs[k].desc);
|
|
|
|
}
|
2014-01-13 13:33:26 +00:00
|
|
|
}
|
2012-11-30 02:01:24 +00:00
|
|
|
|
2012-11-29 21:00:39 +00:00
|
|
|
/*
|
|
|
|
* GatherPixelRefs is conservative, so it can return more bitmaps
|
2014-01-13 13:33:26 +00:00
|
|
|
* than are strictly required. Thus our check here is only that
|
|
|
|
* Gather didn't miss any that we actually needed. Even that isn't
|
2012-11-29 21:00:39 +00:00
|
|
|
* a strict requirement on Gather, which is meant to be quick and
|
|
|
|
* only mostly-correct, but at the moment this test should work.
|
|
|
|
*/
|
2014-01-13 13:33:26 +00:00
|
|
|
for (int i = 0; i < fromAnalytic.count(); ++i) {
|
|
|
|
bool found = find(gatherRefs, fromAnalytic[i], gatherCount);
|
2014-03-05 20:59:45 +00:00
|
|
|
if (!found) {
|
|
|
|
ERRORF(reporter, "PixelRef missing %d %s",
|
|
|
|
i, gProcs[k].desc);
|
|
|
|
}
|
2012-11-29 21:00:39 +00:00
|
|
|
#if 0
|
|
|
|
// enable this block of code to debug failures, as it will rerun
|
|
|
|
// the case that failed.
|
|
|
|
if (!found) {
|
|
|
|
SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
|
|
|
|
size_t dataSize = data ? data->size() : 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-13 13:33:26 +00:00
|
|
|
static void test_gatherpixelrefsandrects(skiatest::Reporter* reporter) {
|
|
|
|
const int IW = 32;
|
|
|
|
const int IH = IW;
|
|
|
|
const SkScalar W = SkIntToScalar(IW);
|
|
|
|
const SkScalar H = W;
|
|
|
|
|
|
|
|
static const int N = 4;
|
|
|
|
SkBitmap bm[2*N];
|
|
|
|
SkPixelRef* refs[2*N];
|
|
|
|
SkTDArray<SkPixelRef*> analytic[N];
|
|
|
|
|
|
|
|
const SkPoint pos[N] = {
|
|
|
|
{ 0, 0 }, { W, 0 }, { 0, H }, { W, H }
|
|
|
|
};
|
|
|
|
|
|
|
|
create_textures(bm, refs, N, IW, IH);
|
|
|
|
|
|
|
|
SkRandom rand;
|
|
|
|
for (size_t k = 0; k < SK_ARRAY_COUNT(gProcs); ++k) {
|
2014-03-05 20:59:45 +00:00
|
|
|
SkAutoTUnref<SkPicture> pic(
|
|
|
|
record_bitmaps(bm, pos, analytic, N, gProcs[k].proc));
|
2014-01-13 13:33:26 +00:00
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0);
|
|
|
|
|
|
|
|
SkAutoTUnref<SkPictureUtils::SkPixelRefContainer> prCont(
|
|
|
|
new SkPictureUtils::SkPixelRefsAndRectsList);
|
|
|
|
|
|
|
|
SkPictureUtils::GatherPixelRefsAndRects(pic, prCont);
|
|
|
|
|
|
|
|
// quick check for a small piece of each quadrant, which should just
|
|
|
|
// contain 1 or 2 bitmaps.
|
|
|
|
for (size_t i = 0; i < SK_ARRAY_COUNT(pos); ++i) {
|
|
|
|
SkRect r;
|
|
|
|
r.set(2, 2, W - 2, H - 2);
|
|
|
|
r.offset(pos[i].fX, pos[i].fY);
|
|
|
|
|
|
|
|
SkTDArray<SkPixelRef*> gatheredRefs;
|
|
|
|
prCont->query(r, &gatheredRefs);
|
|
|
|
|
|
|
|
int count = gatheredRefs.count();
|
|
|
|
REPORTER_ASSERT(reporter, 1 == count || 2 == count);
|
|
|
|
if (1 == count) {
|
|
|
|
REPORTER_ASSERT(reporter, gatheredRefs[0] == refs[i]);
|
|
|
|
} else if (2 == count) {
|
2014-01-14 07:01:42 +00:00
|
|
|
REPORTER_ASSERT(reporter,
|
2014-01-13 13:33:26 +00:00
|
|
|
(gatheredRefs[0] == refs[i] && gatheredRefs[1] == refs[i+N]) ||
|
|
|
|
(gatheredRefs[1] == refs[i] && gatheredRefs[0] == refs[i+N]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SkBitmap image;
|
|
|
|
draw(pic, 2*IW, 2*IH, &image);
|
|
|
|
|
|
|
|
// Test a bunch of random (mostly) rects, and compare the gather results
|
|
|
|
// with the analytic results and the pixel refs seen in a rendering.
|
|
|
|
for (int j = 0; j < 100; ++j) {
|
|
|
|
SkRect r;
|
|
|
|
rand_rect(&r, rand, 2*W, 2*H);
|
|
|
|
|
|
|
|
SkTDArray<SkPixelRef*> fromImage;
|
|
|
|
gather_from_image(image, refs, N, &fromImage, r);
|
|
|
|
|
|
|
|
SkTDArray<SkPixelRef*> fromAnalytic;
|
|
|
|
gather_from_analytic(pos, W, H, analytic, N, &fromAnalytic, r);
|
|
|
|
|
|
|
|
SkTDArray<SkPixelRef*> gatheredRefs;
|
|
|
|
prCont->query(r, &gatheredRefs);
|
|
|
|
|
|
|
|
// Everything that we saw drawn should appear in the analytic list
|
|
|
|
// but the analytic list may contain some pixelRefs that were not
|
|
|
|
// seen in the image (e.g., A8 textures used as masks)
|
|
|
|
for (int i = 0; i < fromImage.count(); ++i) {
|
|
|
|
REPORTER_ASSERT(reporter, -1 != fromAnalytic.find(fromImage[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Everything in the analytic list should appear in the gathered
|
2014-01-14 07:01:42 +00:00
|
|
|
// list.
|
2014-01-13 13:33:26 +00:00
|
|
|
for (int i = 0; i < fromAnalytic.count(); ++i) {
|
|
|
|
REPORTER_ASSERT(reporter, -1 != gatheredRefs.find(fromAnalytic[i]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-14 17:26:37 +00:00
|
|
|
#ifdef SK_DEBUG
|
2014-07-01 15:47:04 +00:00
|
|
|
// Ensure that deleting an empty SkPicture does not assert. Asserts only fire
|
|
|
|
// in debug mode, so only run in debug mode.
|
|
|
|
static void test_deleting_empty_picture() {
|
2014-04-13 19:09:42 +00:00
|
|
|
SkPictureRecorder recorder;
|
2012-09-14 17:26:37 +00:00
|
|
|
// Creates an SkPictureRecord
|
2014-06-09 13:25:34 +00:00
|
|
|
recorder.beginRecording(0, 0);
|
2014-07-01 15:47:04 +00:00
|
|
|
// Turns that into an SkPicture
|
2014-04-13 19:09:42 +00:00
|
|
|
SkAutoTUnref<SkPicture> picture(recorder.endRecording());
|
2014-07-01 15:47:04 +00:00
|
|
|
// Ceates a new SkPictureRecord
|
2014-06-09 13:25:34 +00:00
|
|
|
recorder.beginRecording(0, 0);
|
2012-09-14 17:26:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
|
|
|
|
static void test_serializing_empty_picture() {
|
2014-04-13 19:09:42 +00:00
|
|
|
SkPictureRecorder recorder;
|
2014-06-09 13:25:34 +00:00
|
|
|
recorder.beginRecording(0, 0);
|
2014-04-13 19:09:42 +00:00
|
|
|
SkAutoTUnref<SkPicture> picture(recorder.endRecording());
|
2012-09-14 17:26:37 +00:00
|
|
|
SkDynamicMemoryWStream stream;
|
2014-04-13 19:09:42 +00:00
|
|
|
picture->serialize(&stream);
|
2012-09-14 17:26:37 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-09-09 20:09:12 +00:00
|
|
|
static void rand_op(SkCanvas* canvas, SkRandom& rand) {
|
2012-10-02 17:42:15 +00:00
|
|
|
SkPaint paint;
|
|
|
|
SkRect rect = SkRect::MakeWH(50, 50);
|
|
|
|
|
|
|
|
SkScalar unit = rand.nextUScalar1();
|
|
|
|
if (unit <= 0.3) {
|
|
|
|
// SkDebugf("save\n");
|
|
|
|
canvas->save();
|
|
|
|
} else if (unit <= 0.6) {
|
|
|
|
// SkDebugf("restore\n");
|
|
|
|
canvas->restore();
|
|
|
|
} else if (unit <= 0.9) {
|
|
|
|
// SkDebugf("clip\n");
|
|
|
|
canvas->clipRect(rect);
|
|
|
|
} else {
|
|
|
|
// SkDebugf("draw\n");
|
|
|
|
canvas->drawPaint(paint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-25 00:02:12 +00:00
|
|
|
#if SK_SUPPORT_GPU
|
2014-04-24 21:53:13 +00:00
|
|
|
static void test_gpu_veto(skiatest::Reporter* reporter) {
|
|
|
|
|
|
|
|
SkPictureRecorder recorder;
|
|
|
|
|
2014-06-09 13:25:34 +00:00
|
|
|
SkCanvas* canvas = recorder.beginRecording(100, 100);
|
2014-04-24 21:53:13 +00:00
|
|
|
{
|
|
|
|
SkPath path;
|
|
|
|
path.moveTo(0, 0);
|
|
|
|
path.lineTo(50, 50);
|
|
|
|
|
|
|
|
SkScalar intervals[] = { 1.0f, 1.0f };
|
|
|
|
SkAutoTUnref<SkDashPathEffect> dash(SkDashPathEffect::Create(intervals, 2, 0));
|
|
|
|
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setStyle(SkPaint::kStroke_Style);
|
|
|
|
paint.setPathEffect(dash);
|
|
|
|
|
|
|
|
canvas->drawPath(path, paint);
|
|
|
|
}
|
|
|
|
SkAutoTUnref<SkPicture> picture(recorder.endRecording());
|
|
|
|
// path effects currently render an SkPicture undesireable for GPU rendering
|
2014-05-30 21:52:52 +00:00
|
|
|
|
|
|
|
const char *reason = NULL;
|
|
|
|
REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL, &reason));
|
|
|
|
REPORTER_ASSERT(reporter, NULL != reason);
|
2014-04-24 21:53:13 +00:00
|
|
|
|
2014-06-09 13:25:34 +00:00
|
|
|
canvas = recorder.beginRecording(100, 100);
|
2014-04-24 21:53:13 +00:00
|
|
|
{
|
|
|
|
SkPath path;
|
|
|
|
|
|
|
|
path.moveTo(0, 0);
|
|
|
|
path.lineTo(0, 50);
|
|
|
|
path.lineTo(25, 25);
|
|
|
|
path.lineTo(50, 50);
|
|
|
|
path.lineTo(50, 0);
|
|
|
|
path.close();
|
|
|
|
REPORTER_ASSERT(reporter, !path.isConvex());
|
|
|
|
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setAntiAlias(true);
|
|
|
|
for (int i = 0; i < 50; ++i) {
|
|
|
|
canvas->drawPath(path, paint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
picture.reset(recorder.endRecording());
|
|
|
|
// A lot of AA concave paths currently render an SkPicture undesireable for GPU rendering
|
|
|
|
REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL));
|
|
|
|
|
2014-06-09 13:25:34 +00:00
|
|
|
canvas = recorder.beginRecording(100, 100);
|
2014-04-24 21:53:13 +00:00
|
|
|
{
|
|
|
|
SkPath path;
|
|
|
|
|
|
|
|
path.moveTo(0, 0);
|
|
|
|
path.lineTo(0, 50);
|
|
|
|
path.lineTo(25, 25);
|
|
|
|
path.lineTo(50, 50);
|
|
|
|
path.lineTo(50, 0);
|
|
|
|
path.close();
|
|
|
|
REPORTER_ASSERT(reporter, !path.isConvex());
|
|
|
|
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setAntiAlias(true);
|
|
|
|
paint.setStyle(SkPaint::kStroke_Style);
|
|
|
|
paint.setStrokeWidth(0);
|
|
|
|
for (int i = 0; i < 50; ++i) {
|
|
|
|
canvas->drawPath(path, paint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
picture.reset(recorder.endRecording());
|
|
|
|
// hairline stroked AA concave paths are fine for GPU rendering
|
|
|
|
REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL));
|
|
|
|
}
|
2014-05-06 12:02:22 +00:00
|
|
|
|
|
|
|
static void test_gpu_picture_optimization(skiatest::Reporter* reporter,
|
|
|
|
GrContextFactory* factory) {
|
2014-07-17 17:50:59 +00:00
|
|
|
for (int i= 0; i < GrContextFactory::kGLContextTypeCnt; ++i) {
|
|
|
|
GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i;
|
2014-05-06 12:02:22 +00:00
|
|
|
|
2014-07-17 17:50:59 +00:00
|
|
|
if (!GrContextFactory::IsRenderingGLContext(glCtxType)) {
|
|
|
|
continue;
|
|
|
|
}
|
2014-05-06 12:02:22 +00:00
|
|
|
|
2014-07-17 17:50:59 +00:00
|
|
|
GrContext* context = factory->get(glCtxType);
|
2014-05-06 12:02:22 +00:00
|
|
|
|
2014-07-17 17:50:59 +00:00
|
|
|
if (NULL == context) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const int kWidth = 100;
|
|
|
|
static const int kHeight = 100;
|
2014-05-06 12:02:22 +00:00
|
|
|
|
2014-07-17 17:50:59 +00:00
|
|
|
SkAutoTUnref<SkPicture> pict;
|
|
|
|
|
|
|
|
// create a picture with the structure:
|
|
|
|
// 1)
|
|
|
|
// SaveLayer
|
|
|
|
// Restore
|
|
|
|
// 2)
|
|
|
|
// SaveLayer
|
|
|
|
// Translate
|
|
|
|
// SaveLayer w/ bound
|
|
|
|
// Restore
|
|
|
|
// Restore
|
2014-05-06 12:02:22 +00:00
|
|
|
// 3)
|
2014-07-17 17:50:59 +00:00
|
|
|
// SaveLayer w/ copyable paint
|
|
|
|
// Restore
|
2014-05-06 12:02:22 +00:00
|
|
|
// 4)
|
2014-07-17 17:50:59 +00:00
|
|
|
// SaveLayer w/ non-copyable paint
|
|
|
|
// Restore
|
2014-05-06 12:02:22 +00:00
|
|
|
{
|
2014-07-17 17:50:59 +00:00
|
|
|
SkPictureRecorder recorder;
|
|
|
|
|
|
|
|
SkCanvas* c = recorder.beginRecording(kWidth, kHeight);
|
|
|
|
// 1)
|
|
|
|
c->saveLayer(NULL, NULL);
|
2014-05-06 12:02:22 +00:00
|
|
|
c->restore();
|
|
|
|
|
2014-07-17 17:50:59 +00:00
|
|
|
// 2)
|
|
|
|
c->saveLayer(NULL, NULL);
|
|
|
|
c->translate(kWidth/2, kHeight/2);
|
|
|
|
SkRect r = SkRect::MakeXYWH(0, 0, kWidth/2, kHeight/2);
|
|
|
|
c->saveLayer(&r, NULL);
|
|
|
|
c->restore();
|
|
|
|
c->restore();
|
2014-05-06 12:02:22 +00:00
|
|
|
|
2014-07-17 17:50:59 +00:00
|
|
|
// 3)
|
|
|
|
{
|
|
|
|
SkPaint p;
|
|
|
|
p.setColor(SK_ColorRED);
|
|
|
|
c->saveLayer(NULL, &p);
|
|
|
|
c->restore();
|
|
|
|
}
|
|
|
|
// 4)
|
|
|
|
// TODO: this case will need to be removed once the paint's are immutable
|
|
|
|
{
|
|
|
|
SkPaint p;
|
|
|
|
SkAutoTUnref<SkColorFilter> cf(SkLumaColorFilter::Create());
|
|
|
|
p.setImageFilter(SkColorFilterImageFilter::Create(cf.get()))->unref();
|
|
|
|
c->saveLayer(NULL, &p);
|
|
|
|
c->restore();
|
|
|
|
}
|
2014-05-06 12:02:22 +00:00
|
|
|
|
2014-07-17 17:50:59 +00:00
|
|
|
pict.reset(recorder.endRecording());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now test out the SaveLayer extraction
|
|
|
|
{
|
|
|
|
SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight);
|
2014-05-06 12:02:22 +00:00
|
|
|
|
2014-07-17 17:50:59 +00:00
|
|
|
SkAutoTUnref<SkSurface> surface(SkSurface::NewScratchRenderTarget(context, info));
|
2014-05-06 12:02:22 +00:00
|
|
|
|
2014-07-17 17:50:59 +00:00
|
|
|
SkCanvas* canvas = surface->getCanvas();
|
2014-05-06 12:02:22 +00:00
|
|
|
|
2014-07-17 17:50:59 +00:00
|
|
|
canvas->EXPERIMENTAL_optimize(pict);
|
2014-05-06 12:02:22 +00:00
|
|
|
|
2014-07-17 17:50:59 +00:00
|
|
|
SkPicture::AccelData::Key key = GPUAccelData::ComputeAccelDataKey();
|
2014-05-06 12:02:22 +00:00
|
|
|
|
2014-07-17 17:50:59 +00:00
|
|
|
const SkPicture::AccelData* data = pict->EXPERIMENTAL_getAccelData(key);
|
|
|
|
REPORTER_ASSERT(reporter, NULL != data);
|
2014-05-06 12:02:22 +00:00
|
|
|
|
2014-07-17 17:50:59 +00:00
|
|
|
const GPUAccelData *gpuData = static_cast<const GPUAccelData*>(data);
|
|
|
|
REPORTER_ASSERT(reporter, 5 == gpuData->numSaveLayers());
|
|
|
|
|
|
|
|
const GPUAccelData::SaveLayerInfo& info0 = gpuData->saveLayerInfo(0);
|
|
|
|
// The parent/child layer appear in reverse order
|
|
|
|
const GPUAccelData::SaveLayerInfo& info1 = gpuData->saveLayerInfo(2);
|
|
|
|
const GPUAccelData::SaveLayerInfo& info2 = gpuData->saveLayerInfo(1);
|
|
|
|
const GPUAccelData::SaveLayerInfo& info3 = gpuData->saveLayerInfo(3);
|
2014-05-14 18:58:16 +00:00
|
|
|
// const GPUAccelData::SaveLayerInfo& info4 = gpuData->saveLayerInfo(4);
|
2014-05-06 12:02:22 +00:00
|
|
|
|
2014-07-17 17:50:59 +00:00
|
|
|
REPORTER_ASSERT(reporter, info0.fValid);
|
|
|
|
REPORTER_ASSERT(reporter, kWidth == info0.fSize.fWidth && kHeight == info0.fSize.fHeight);
|
|
|
|
REPORTER_ASSERT(reporter, info0.fCTM.isIdentity());
|
|
|
|
REPORTER_ASSERT(reporter, 0 == info0.fOffset.fX && 0 == info0.fOffset.fY);
|
|
|
|
REPORTER_ASSERT(reporter, NULL != info0.fPaint);
|
|
|
|
REPORTER_ASSERT(reporter, !info0.fIsNested && !info0.fHasNestedLayers);
|
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, info1.fValid);
|
|
|
|
REPORTER_ASSERT(reporter, kWidth == info1.fSize.fWidth && kHeight == info1.fSize.fHeight);
|
|
|
|
REPORTER_ASSERT(reporter, info1.fCTM.isIdentity());
|
|
|
|
REPORTER_ASSERT(reporter, 0 == info1.fOffset.fX && 0 == info1.fOffset.fY);
|
|
|
|
REPORTER_ASSERT(reporter, NULL != info1.fPaint);
|
|
|
|
REPORTER_ASSERT(reporter, !info1.fIsNested && info1.fHasNestedLayers); // has a nested SL
|
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, info2.fValid);
|
|
|
|
REPORTER_ASSERT(reporter, kWidth/2 == info2.fSize.fWidth &&
|
|
|
|
kHeight/2 == info2.fSize.fHeight); // bound reduces size
|
|
|
|
REPORTER_ASSERT(reporter, info2.fCTM.isIdentity()); // translated
|
|
|
|
REPORTER_ASSERT(reporter, kWidth/2 == info2.fOffset.fX &&
|
|
|
|
kHeight/2 == info2.fOffset.fY);
|
|
|
|
REPORTER_ASSERT(reporter, NULL != info1.fPaint);
|
|
|
|
REPORTER_ASSERT(reporter, info2.fIsNested && !info2.fHasNestedLayers); // is nested
|
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, info3.fValid);
|
|
|
|
REPORTER_ASSERT(reporter, kWidth == info3.fSize.fWidth && kHeight == info3.fSize.fHeight);
|
|
|
|
REPORTER_ASSERT(reporter, info3.fCTM.isIdentity());
|
|
|
|
REPORTER_ASSERT(reporter, 0 == info3.fOffset.fX && 0 == info3.fOffset.fY);
|
|
|
|
REPORTER_ASSERT(reporter, NULL != info3.fPaint);
|
|
|
|
REPORTER_ASSERT(reporter, !info3.fIsNested && !info3.fHasNestedLayers);
|
|
|
|
|
|
|
|
#if 0 // needs more though for GrGatherCanvas
|
|
|
|
REPORTER_ASSERT(reporter, !info4.fValid); // paint is/was uncopyable
|
|
|
|
REPORTER_ASSERT(reporter, kWidth == info4.fSize.fWidth && kHeight == info4.fSize.fHeight);
|
|
|
|
REPORTER_ASSERT(reporter, 0 == info4.fOffset.fX && 0 == info4.fOffset.fY);
|
|
|
|
REPORTER_ASSERT(reporter, info4.fCTM.isIdentity());
|
|
|
|
REPORTER_ASSERT(reporter, NULL == info4.fPaint); // paint is/was uncopyable
|
|
|
|
REPORTER_ASSERT(reporter, !info4.fIsNested && !info4.fHasNestedLayers);
|
|
|
|
#endif
|
|
|
|
}
|
2014-05-06 12:02:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-25 00:02:12 +00:00
|
|
|
#endif
|
2014-04-24 21:53:13 +00:00
|
|
|
|
2014-02-13 16:00:51 +00:00
|
|
|
static void set_canvas_to_save_count_4(SkCanvas* canvas) {
|
|
|
|
canvas->restoreToCount(1);
|
|
|
|
canvas->save();
|
|
|
|
canvas->save();
|
|
|
|
canvas->save();
|
|
|
|
}
|
|
|
|
|
2014-05-27 23:41:45 +00:00
|
|
|
/**
|
|
|
|
* A canvas that records the number of saves, saveLayers and restores.
|
|
|
|
*/
|
|
|
|
class SaveCountingCanvas : public SkCanvas {
|
|
|
|
public:
|
|
|
|
SaveCountingCanvas(int width, int height)
|
|
|
|
: INHERITED(width, height)
|
|
|
|
, fSaveCount(0)
|
|
|
|
, fSaveLayerCount(0)
|
|
|
|
, fRestoreCount(0){
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual SaveLayerStrategy willSaveLayer(const SkRect* bounds, const SkPaint* paint,
|
|
|
|
SaveFlags flags) SK_OVERRIDE {
|
|
|
|
++fSaveLayerCount;
|
|
|
|
return this->INHERITED::willSaveLayer(bounds, paint, flags);
|
|
|
|
}
|
|
|
|
|
2014-06-30 14:13:28 +00:00
|
|
|
virtual void willSave() SK_OVERRIDE {
|
2014-05-27 23:41:45 +00:00
|
|
|
++fSaveCount;
|
2014-06-30 14:13:28 +00:00
|
|
|
this->INHERITED::willSave();
|
2014-05-27 23:41:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
virtual void willRestore() SK_OVERRIDE {
|
|
|
|
++fRestoreCount;
|
|
|
|
this->INHERITED::willRestore();
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int getSaveCount() const { return fSaveCount; }
|
|
|
|
unsigned int getSaveLayerCount() const { return fSaveLayerCount; }
|
|
|
|
unsigned int getRestoreCount() const { return fRestoreCount; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
unsigned int fSaveCount;
|
|
|
|
unsigned int fSaveLayerCount;
|
|
|
|
unsigned int fRestoreCount;
|
|
|
|
|
|
|
|
typedef SkCanvas INHERITED;
|
|
|
|
};
|
|
|
|
|
2014-05-28 03:06:06 +00:00
|
|
|
void check_save_state(skiatest::Reporter* reporter, SkPicture* picture,
|
2014-05-27 23:41:45 +00:00
|
|
|
unsigned int numSaves, unsigned int numSaveLayers,
|
|
|
|
unsigned int numRestores) {
|
|
|
|
SaveCountingCanvas canvas(picture->width(), picture->height());
|
|
|
|
|
|
|
|
picture->draw(&canvas);
|
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, numSaves == canvas.getSaveCount());
|
|
|
|
REPORTER_ASSERT(reporter, numSaveLayers == canvas.getSaveLayerCount());
|
|
|
|
REPORTER_ASSERT(reporter, numRestores == canvas.getRestoreCount());
|
|
|
|
}
|
|
|
|
|
|
|
|
// This class exists so SkPicture can friend it and give it access to
|
|
|
|
// the 'partialReplay' method.
|
|
|
|
class SkPictureRecorderReplayTester {
|
|
|
|
public:
|
|
|
|
static SkPicture* Copy(SkPictureRecorder* recorder) {
|
|
|
|
SkPictureRecorder recorder2;
|
|
|
|
|
2014-06-09 13:25:34 +00:00
|
|
|
SkCanvas* canvas = recorder2.beginRecording(10, 10);
|
2014-05-27 23:41:45 +00:00
|
|
|
|
|
|
|
recorder->partialReplay(canvas);
|
|
|
|
|
|
|
|
return recorder2.endRecording();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-06-10 18:45:46 +00:00
|
|
|
static void create_imbalance(SkCanvas* canvas) {
|
|
|
|
SkRect clipRect = SkRect::MakeWH(2, 2);
|
|
|
|
SkRect drawRect = SkRect::MakeWH(10, 10);
|
|
|
|
canvas->save();
|
|
|
|
canvas->clipRect(clipRect, SkRegion::kReplace_Op);
|
|
|
|
canvas->translate(1.0f, 1.0f);
|
|
|
|
SkPaint p;
|
|
|
|
p.setColor(SK_ColorGREEN);
|
|
|
|
canvas->drawRect(drawRect, p);
|
|
|
|
// no restore
|
|
|
|
}
|
|
|
|
|
|
|
|
// This tests that replaying a potentially unbalanced picture into a canvas
|
|
|
|
// doesn't affect the canvas' save count or matrix/clip state.
|
|
|
|
static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) {
|
|
|
|
SkBitmap bm;
|
|
|
|
bm.allocN32Pixels(4, 3);
|
|
|
|
SkCanvas canvas(bm);
|
|
|
|
|
|
|
|
int beforeSaveCount = canvas.getSaveCount();
|
|
|
|
|
|
|
|
SkMatrix beforeMatrix = canvas.getTotalMatrix();
|
|
|
|
|
|
|
|
SkRect beforeClip;
|
|
|
|
|
|
|
|
canvas.getClipBounds(&beforeClip);
|
|
|
|
|
|
|
|
canvas.drawPicture(picture);
|
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount());
|
|
|
|
REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix());
|
|
|
|
|
|
|
|
SkRect afterClip;
|
|
|
|
|
|
|
|
canvas.getClipBounds(&afterClip);
|
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, afterClip == beforeClip);
|
|
|
|
}
|
|
|
|
|
2014-05-27 23:41:45 +00:00
|
|
|
// Test out SkPictureRecorder::partialReplay
|
|
|
|
DEF_TEST(PictureRecorder_replay, reporter) {
|
|
|
|
// check save/saveLayer state
|
|
|
|
{
|
|
|
|
SkPictureRecorder recorder;
|
|
|
|
|
2014-06-09 13:25:34 +00:00
|
|
|
SkCanvas* canvas = recorder.beginRecording(10, 10);
|
2014-05-27 23:41:45 +00:00
|
|
|
|
|
|
|
canvas->saveLayer(NULL, NULL);
|
|
|
|
|
|
|
|
SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
|
|
|
|
|
|
|
|
// The extra save and restore comes from the Copy process.
|
|
|
|
check_save_state(reporter, copy, 2, 1, 3);
|
|
|
|
|
|
|
|
canvas->saveLayer(NULL, NULL);
|
|
|
|
|
|
|
|
SkAutoTUnref<SkPicture> final(recorder.endRecording());
|
|
|
|
|
|
|
|
check_save_state(reporter, final, 1, 2, 3);
|
|
|
|
|
|
|
|
// The copy shouldn't pick up any operations added after it was made
|
|
|
|
check_save_state(reporter, copy, 2, 1, 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
// (partially) check leakage of draw ops
|
|
|
|
{
|
|
|
|
SkPictureRecorder recorder;
|
|
|
|
|
2014-06-09 13:25:34 +00:00
|
|
|
SkCanvas* canvas = recorder.beginRecording(10, 10);
|
2014-05-27 23:41:45 +00:00
|
|
|
|
|
|
|
SkRect r = SkRect::MakeWH(5, 5);
|
|
|
|
SkPaint p;
|
|
|
|
|
|
|
|
canvas->drawRect(r, p);
|
|
|
|
|
|
|
|
SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
|
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
|
|
|
|
|
|
|
|
SkBitmap bm;
|
|
|
|
make_bm(&bm, 10, 10, SK_ColorRED, true);
|
|
|
|
|
|
|
|
r.offset(5.0f, 5.0f);
|
|
|
|
canvas->drawBitmapRectToRect(bm, NULL, r);
|
|
|
|
|
|
|
|
SkAutoTUnref<SkPicture> final(recorder.endRecording());
|
|
|
|
REPORTER_ASSERT(reporter, final->willPlayBackBitmaps());
|
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, copy->uniqueID() != final->uniqueID());
|
|
|
|
|
|
|
|
// The snapshot shouldn't pick up any operations added after it was made
|
|
|
|
REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
|
|
|
|
}
|
2014-06-10 18:45:46 +00:00
|
|
|
|
|
|
|
// Recreate the Android partialReplay test case
|
|
|
|
{
|
|
|
|
SkPictureRecorder recorder;
|
|
|
|
|
|
|
|
SkCanvas* canvas = recorder.beginRecording(4, 3, NULL, 0);
|
|
|
|
create_imbalance(canvas);
|
|
|
|
|
|
|
|
int expectedSaveCount = canvas->getSaveCount();
|
|
|
|
|
|
|
|
SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
|
|
|
|
check_balance(reporter, copy);
|
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount());
|
|
|
|
|
|
|
|
// End the recording of source to test the picture finalization
|
|
|
|
// process isn't complicated by the partialReplay step
|
|
|
|
SkAutoTUnref<SkPicture> final(recorder.endRecording());
|
|
|
|
}
|
2014-05-27 23:41:45 +00:00
|
|
|
}
|
|
|
|
|
2014-02-13 16:00:51 +00:00
|
|
|
static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
|
|
|
|
SkCanvas testCanvas(100, 100);
|
|
|
|
set_canvas_to_save_count_4(&testCanvas);
|
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
|
|
|
|
|
|
|
|
SkPaint paint;
|
|
|
|
SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000);
|
|
|
|
|
2014-04-13 19:09:42 +00:00
|
|
|
SkPictureRecorder recorder;
|
2014-02-13 16:00:51 +00:00
|
|
|
|
2014-04-13 19:09:42 +00:00
|
|
|
{
|
|
|
|
// Create picture with 2 unbalanced saves
|
2014-06-09 13:25:34 +00:00
|
|
|
SkCanvas* canvas = recorder.beginRecording(100, 100);
|
2014-04-13 19:09:42 +00:00
|
|
|
canvas->save();
|
|
|
|
canvas->translate(10, 10);
|
|
|
|
canvas->drawRect(rect, paint);
|
|
|
|
canvas->save();
|
|
|
|
canvas->translate(10, 10);
|
|
|
|
canvas->drawRect(rect, paint);
|
|
|
|
SkAutoTUnref<SkPicture> extraSavePicture(recorder.endRecording());
|
|
|
|
|
2014-06-04 12:40:44 +00:00
|
|
|
testCanvas.drawPicture(extraSavePicture);
|
2014-04-13 19:09:42 +00:00
|
|
|
REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
|
|
|
|
}
|
2014-02-13 16:00:51 +00:00
|
|
|
|
|
|
|
set_canvas_to_save_count_4(&testCanvas);
|
|
|
|
|
2014-04-13 19:09:42 +00:00
|
|
|
{
|
|
|
|
// Create picture with 2 unbalanced restores
|
2014-06-09 13:25:34 +00:00
|
|
|
SkCanvas* canvas = recorder.beginRecording(100, 100);
|
2014-04-13 19:09:42 +00:00
|
|
|
canvas->save();
|
|
|
|
canvas->translate(10, 10);
|
|
|
|
canvas->drawRect(rect, paint);
|
|
|
|
canvas->save();
|
|
|
|
canvas->translate(10, 10);
|
|
|
|
canvas->drawRect(rect, paint);
|
|
|
|
canvas->restore();
|
|
|
|
canvas->restore();
|
|
|
|
canvas->restore();
|
|
|
|
canvas->restore();
|
|
|
|
SkAutoTUnref<SkPicture> extraRestorePicture(recorder.endRecording());
|
2014-02-13 16:00:51 +00:00
|
|
|
|
2014-06-04 12:40:44 +00:00
|
|
|
testCanvas.drawPicture(extraRestorePicture);
|
2014-04-13 19:09:42 +00:00
|
|
|
REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
|
|
|
|
}
|
2014-02-13 16:00:51 +00:00
|
|
|
|
2014-04-13 19:09:42 +00:00
|
|
|
set_canvas_to_save_count_4(&testCanvas);
|
|
|
|
|
|
|
|
{
|
2014-06-09 13:25:34 +00:00
|
|
|
SkCanvas* canvas = recorder.beginRecording(100, 100);
|
2014-04-13 19:09:42 +00:00
|
|
|
canvas->translate(10, 10);
|
|
|
|
canvas->drawRect(rect, paint);
|
|
|
|
SkAutoTUnref<SkPicture> noSavePicture(recorder.endRecording());
|
|
|
|
|
2014-06-04 12:40:44 +00:00
|
|
|
testCanvas.drawPicture(noSavePicture);
|
2014-04-13 19:09:42 +00:00
|
|
|
REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
|
|
|
|
REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity());
|
|
|
|
}
|
2014-02-13 16:00:51 +00:00
|
|
|
}
|
|
|
|
|
2013-02-27 19:17:41 +00:00
|
|
|
static void test_peephole() {
|
2013-09-09 20:09:12 +00:00
|
|
|
SkRandom rand;
|
2012-10-02 17:42:15 +00:00
|
|
|
|
2014-04-13 19:09:42 +00:00
|
|
|
SkPictureRecorder recorder;
|
|
|
|
|
2012-10-02 17:42:15 +00:00
|
|
|
for (int j = 0; j < 100; j++) {
|
2013-09-09 20:09:12 +00:00
|
|
|
SkRandom rand2(rand); // remember the seed
|
2012-10-02 17:42:15 +00:00
|
|
|
|
2014-06-09 13:25:34 +00:00
|
|
|
SkCanvas* canvas = recorder.beginRecording(100, 100);
|
2012-10-02 17:42:15 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < 1000; ++i) {
|
|
|
|
rand_op(canvas, rand);
|
|
|
|
}
|
2014-04-13 19:09:42 +00:00
|
|
|
SkAutoTUnref<SkPicture> picture(recorder.endRecording());
|
2013-03-04 13:56:38 +00:00
|
|
|
|
|
|
|
rand = rand2;
|
2012-10-02 17:42:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2014-06-09 13:25:34 +00:00
|
|
|
SkCanvas* canvas = recorder.beginRecording(100, 100);
|
2012-10-02 17:42:15 +00:00
|
|
|
SkRect rect = SkRect::MakeWH(50, 50);
|
2012-10-03 02:01:13 +00:00
|
|
|
|
2012-10-02 17:42:15 +00:00
|
|
|
for (int i = 0; i < 100; ++i) {
|
|
|
|
canvas->save();
|
|
|
|
}
|
|
|
|
while (canvas->getSaveCount() > 1) {
|
|
|
|
canvas->clipRect(rect);
|
|
|
|
canvas->restore();
|
|
|
|
}
|
2014-04-13 19:09:42 +00:00
|
|
|
SkAutoTUnref<SkPicture> picture(recorder.endRecording());
|
2012-10-02 17:42:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-04 15:08:56 +00:00
|
|
|
#ifndef SK_DEBUG
|
|
|
|
// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
|
|
|
|
// should never do this.
|
|
|
|
static void test_bad_bitmap() {
|
|
|
|
// This bitmap has a width and height but no pixels. As a result, attempting to record it will
|
|
|
|
// fail.
|
|
|
|
SkBitmap bm;
|
2014-05-30 13:26:10 +00:00
|
|
|
bm.setInfo(SkImageInfo::MakeN32Premul(100, 100));
|
2014-04-13 19:09:42 +00:00
|
|
|
SkPictureRecorder recorder;
|
2014-06-09 13:25:34 +00:00
|
|
|
SkCanvas* recordingCanvas = recorder.beginRecording(100, 100);
|
2012-12-04 15:08:56 +00:00
|
|
|
recordingCanvas->drawBitmap(bm, 0, 0);
|
2014-04-13 19:09:42 +00:00
|
|
|
SkAutoTUnref<SkPicture> picture(recorder.endRecording());
|
2012-12-04 15:08:56 +00:00
|
|
|
|
|
|
|
SkCanvas canvas;
|
2014-06-04 12:40:44 +00:00
|
|
|
canvas.drawPicture(picture);
|
2012-12-04 15:08:56 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-01-08 15:42:01 +00:00
|
|
|
static SkData* encode_bitmap_to_data(size_t*, const SkBitmap& bm) {
|
2013-05-21 20:31:23 +00:00
|
|
|
return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100);
|
2012-12-10 15:40:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) {
|
2014-04-13 19:09:42 +00:00
|
|
|
SkPictureRecorder recorder;
|
2014-06-09 13:25:34 +00:00
|
|
|
SkCanvas* canvas = recorder.beginRecording(bitmap.width(), bitmap.height());
|
2012-12-10 15:40:55 +00:00
|
|
|
canvas->drawBitmap(bitmap, 0, 0);
|
2014-04-13 19:09:42 +00:00
|
|
|
SkAutoTUnref<SkPicture> picture(recorder.endRecording());
|
|
|
|
|
2012-12-10 15:40:55 +00:00
|
|
|
SkDynamicMemoryWStream wStream;
|
2014-04-13 19:09:42 +00:00
|
|
|
picture->serialize(&wStream, &encode_bitmap_to_data);
|
2012-12-10 15:40:55 +00:00
|
|
|
return wStream.copyToData();
|
|
|
|
}
|
|
|
|
|
2013-04-25 18:29:32 +00:00
|
|
|
struct ErrorContext {
|
|
|
|
int fErrors;
|
|
|
|
skiatest::Reporter* fReporter;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void assert_one_parse_error_cb(SkError error, void* context) {
|
|
|
|
ErrorContext* errorContext = static_cast<ErrorContext*>(context);
|
|
|
|
errorContext->fErrors++;
|
|
|
|
// This test only expects one error, and that is a kParseError. If there are others,
|
|
|
|
// there is some unknown problem.
|
|
|
|
REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors,
|
|
|
|
"This threw more errors than expected.");
|
|
|
|
REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error,
|
|
|
|
SkGetLastErrorString());
|
|
|
|
}
|
|
|
|
|
2012-12-10 15:40:55 +00:00
|
|
|
static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) {
|
|
|
|
// Create a bitmap that will be encoded.
|
|
|
|
SkBitmap original;
|
|
|
|
make_bm(&original, 100, 100, SK_ColorBLUE, true);
|
|
|
|
SkDynamicMemoryWStream wStream;
|
|
|
|
if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
SkAutoDataUnref data(wStream.copyToData());
|
|
|
|
|
|
|
|
SkBitmap bm;
|
Add Options to SkDecodingImageGenerator, simplify API.
Motivation: We want to remove redundant classes from Skia. To
that end we want to remove SkImageRef and its subclasses and
replace their uses with SkDiscardablePixelRef +
SkDecodingImageGenerator. Since Android uses SkImageRef, we need
to make sure that SkDecodingImageGenerator allows all of the
settings that Android exposes in BitmapFactory.Options.
To that end, we have created an Options struct for the
SkDecodingImageGenerator which lets the client of the generator set
sample size, dithering, and bitmap config.
We have made the SkDecodingImageGenerator constructor private
and replaced the SkDecodingImageGenerator::Install functions
with a SkDecodingImageGenerator::Create functions (one for
SkData and one for SkStream) which now take a
SkDecodingImageGenerator::Options struct.
Also added a ImageDecoderOptions test which loops through a list
of sets of options and tries them on a set of 5 small encoded
images.
Also updated several users of SkDecodingImageGenerator::Install to
follow new call signature - gm/factory.cpp, LazyDecodeBitmap.cpp,
and PictureTest.cpp, CachedDecodingPixelRefTest.cpp.
We also added a new ImprovedBitmapFactory Test which simulates the
exact function that Android will need to modify to use this,
installPixelRef() in BitmapFactory.
R=reed@google.com, scroggo@google.com
Committed: https://code.google.com/p/skia/source/detail?r=12744
Review URL: https://codereview.chromium.org/93703004
git-svn-id: http://skia.googlecode.com/svn/trunk@12855 2bbb7eff-a529-9590-31e7-b0007b416f81
2014-01-02 13:15:13 +00:00
|
|
|
bool installSuccess = SkInstallDiscardablePixelRef(
|
2014-05-27 14:14:22 +00:00
|
|
|
SkDecodingImageGenerator::Create(data, SkDecodingImageGenerator::Options()), &bm);
|
2013-12-13 19:45:58 +00:00
|
|
|
REPORTER_ASSERT(reporter, installSuccess);
|
2012-12-10 15:40:55 +00:00
|
|
|
|
|
|
|
// Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
|
|
|
|
// Flattening original will follow the old path of performing an encode, while flattening bm
|
|
|
|
// will use the already encoded data.
|
|
|
|
SkAutoDataUnref picture1(serialized_picture_from_bitmap(original));
|
|
|
|
SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm));
|
|
|
|
REPORTER_ASSERT(reporter, picture1->equals(picture2));
|
2013-04-25 18:29:32 +00:00
|
|
|
// Now test that a parse error was generated when trying to create a new SkPicture without
|
|
|
|
// providing a function to decode the bitmap.
|
|
|
|
ErrorContext context;
|
|
|
|
context.fErrors = 0;
|
|
|
|
context.fReporter = reporter;
|
|
|
|
SkSetErrorCallback(assert_one_parse_error_cb, &context);
|
|
|
|
SkMemoryStream pictureStream(picture1);
|
|
|
|
SkClearLastError();
|
2013-06-28 21:32:00 +00:00
|
|
|
SkAutoUnref pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL));
|
|
|
|
REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL);
|
2013-04-25 18:29:32 +00:00
|
|
|
SkClearLastError();
|
|
|
|
SkSetErrorCallback(NULL, NULL);
|
2012-12-10 15:40:55 +00:00
|
|
|
}
|
|
|
|
|
2014-03-18 17:45:32 +00:00
|
|
|
static void test_draw_empty(skiatest::Reporter* reporter) {
|
|
|
|
SkBitmap result;
|
|
|
|
make_bm(&result, 2, 2, SK_ColorBLACK, false);
|
|
|
|
|
|
|
|
SkCanvas canvas(result);
|
|
|
|
|
|
|
|
{
|
|
|
|
// stock SkPicture
|
2014-04-13 19:09:42 +00:00
|
|
|
SkPictureRecorder recorder;
|
2014-06-09 13:25:34 +00:00
|
|
|
recorder.beginRecording(1, 1);
|
2014-04-13 19:09:42 +00:00
|
|
|
SkAutoTUnref<SkPicture> picture(recorder.endRecording());
|
2014-03-18 17:45:32 +00:00
|
|
|
|
2014-06-04 12:40:44 +00:00
|
|
|
canvas.drawPicture(picture);
|
2014-03-18 17:45:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// tile grid
|
2014-04-17 23:35:06 +00:00
|
|
|
SkTileGridFactory::TileGridInfo gridInfo;
|
2014-03-18 17:45:32 +00:00
|
|
|
gridInfo.fMargin.setEmpty();
|
|
|
|
gridInfo.fOffset.setZero();
|
|
|
|
gridInfo.fTileInterval.set(1, 1);
|
|
|
|
|
2014-04-17 23:35:06 +00:00
|
|
|
SkTileGridFactory factory(gridInfo);
|
|
|
|
SkPictureRecorder recorder;
|
2014-06-09 13:25:34 +00:00
|
|
|
recorder.beginRecording(1, 1, &factory);
|
2014-04-13 19:09:42 +00:00
|
|
|
SkAutoTUnref<SkPicture> picture(recorder.endRecording());
|
2014-03-18 17:45:32 +00:00
|
|
|
|
2014-06-04 12:40:44 +00:00
|
|
|
canvas.drawPicture(picture);
|
2014-03-18 17:45:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// RTree
|
2014-04-17 23:35:06 +00:00
|
|
|
SkRTreeFactory factory;
|
|
|
|
SkPictureRecorder recorder;
|
2014-06-09 13:25:34 +00:00
|
|
|
recorder.beginRecording(1, 1, &factory);
|
2014-04-13 19:09:42 +00:00
|
|
|
SkAutoTUnref<SkPicture> picture(recorder.endRecording());
|
2014-03-18 17:45:32 +00:00
|
|
|
|
2014-06-04 12:40:44 +00:00
|
|
|
canvas.drawPicture(picture);
|
2014-03-18 17:45:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// quad tree
|
2014-04-17 23:35:06 +00:00
|
|
|
SkQuadTreeFactory factory;
|
|
|
|
SkPictureRecorder recorder;
|
2014-06-09 13:25:34 +00:00
|
|
|
recorder.beginRecording(1, 1, &factory);
|
2014-04-13 19:09:42 +00:00
|
|
|
SkAutoTUnref<SkPicture> picture(recorder.endRecording());
|
2014-03-18 17:45:32 +00:00
|
|
|
|
2014-06-04 12:40:44 +00:00
|
|
|
canvas.drawPicture(picture);
|
2014-03-18 17:45:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-08 15:39:13 +00:00
|
|
|
static void test_clip_bound_opt(skiatest::Reporter* reporter) {
|
|
|
|
// Test for crbug.com/229011
|
|
|
|
SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
|
|
|
|
SkIntToScalar(2), SkIntToScalar(2));
|
|
|
|
SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7),
|
|
|
|
SkIntToScalar(1), SkIntToScalar(1));
|
|
|
|
SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6),
|
|
|
|
SkIntToScalar(1), SkIntToScalar(1));
|
|
|
|
|
|
|
|
SkPath invPath;
|
|
|
|
invPath.addOval(rect1);
|
|
|
|
invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
|
|
|
|
SkPath path;
|
|
|
|
path.addOval(rect2);
|
|
|
|
SkPath path2;
|
|
|
|
path2.addOval(rect3);
|
|
|
|
SkIRect clipBounds;
|
2014-04-13 19:09:42 +00:00
|
|
|
SkPictureRecorder recorder;
|
2013-05-08 15:39:13 +00:00
|
|
|
// Minimalist test set for 100% code coverage of
|
|
|
|
// SkPictureRecord::updateClipConservativelyUsingBounds
|
|
|
|
{
|
2014-06-09 13:25:34 +00:00
|
|
|
SkCanvas* canvas = recorder.beginRecording(10, 10);
|
2013-05-08 15:39:13 +00:00
|
|
|
canvas->clipPath(invPath, SkRegion::kIntersect_Op);
|
|
|
|
bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
|
|
|
|
REPORTER_ASSERT(reporter, true == nonEmpty);
|
|
|
|
REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
|
|
|
|
REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
|
|
|
|
REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
|
|
|
|
REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
|
|
|
|
}
|
|
|
|
{
|
2014-06-09 13:25:34 +00:00
|
|
|
SkCanvas* canvas = recorder.beginRecording(10, 10);
|
2013-05-08 15:39:13 +00:00
|
|
|
canvas->clipPath(path, SkRegion::kIntersect_Op);
|
|
|
|
canvas->clipPath(invPath, SkRegion::kIntersect_Op);
|
|
|
|
bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
|
|
|
|
REPORTER_ASSERT(reporter, true == nonEmpty);
|
|
|
|
REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
|
|
|
|
REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
|
|
|
|
REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
|
|
|
|
REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
|
|
|
|
}
|
|
|
|
{
|
2014-06-09 13:25:34 +00:00
|
|
|
SkCanvas* canvas = recorder.beginRecording(10, 10);
|
2013-05-08 15:39:13 +00:00
|
|
|
canvas->clipPath(path, SkRegion::kIntersect_Op);
|
|
|
|
canvas->clipPath(invPath, SkRegion::kUnion_Op);
|
|
|
|
bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
|
|
|
|
REPORTER_ASSERT(reporter, true == nonEmpty);
|
|
|
|
REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
|
|
|
|
REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
|
|
|
|
REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
|
|
|
|
REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
|
|
|
|
}
|
|
|
|
{
|
2014-06-09 13:25:34 +00:00
|
|
|
SkCanvas* canvas = recorder.beginRecording(10, 10);
|
2013-05-08 15:39:13 +00:00
|
|
|
canvas->clipPath(path, SkRegion::kDifference_Op);
|
|
|
|
bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
|
|
|
|
REPORTER_ASSERT(reporter, true == nonEmpty);
|
|
|
|
REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
|
|
|
|
REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
|
|
|
|
REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
|
|
|
|
REPORTER_ASSERT(reporter, 10 == clipBounds.fRight);
|
|
|
|
}
|
|
|
|
{
|
2014-06-09 13:25:34 +00:00
|
|
|
SkCanvas* canvas = recorder.beginRecording(10, 10);
|
2013-05-08 15:39:13 +00:00
|
|
|
canvas->clipPath(path, SkRegion::kReverseDifference_Op);
|
|
|
|
bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
|
|
|
|
// True clip is actually empty in this case, but the best
|
|
|
|
// determination we can make using only bounds as input is that the
|
|
|
|
// clip is included in the bounds of 'path'.
|
|
|
|
REPORTER_ASSERT(reporter, true == nonEmpty);
|
|
|
|
REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
|
|
|
|
REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
|
|
|
|
REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
|
|
|
|
REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
|
|
|
|
}
|
|
|
|
{
|
2014-06-09 13:25:34 +00:00
|
|
|
SkCanvas* canvas = recorder.beginRecording(10, 10);
|
2013-05-08 15:39:13 +00:00
|
|
|
canvas->clipPath(path, SkRegion::kIntersect_Op);
|
|
|
|
canvas->clipPath(path2, SkRegion::kXOR_Op);
|
|
|
|
bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
|
|
|
|
REPORTER_ASSERT(reporter, true == nonEmpty);
|
|
|
|
REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
|
|
|
|
REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
|
|
|
|
REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
|
|
|
|
REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-27 15:50:19 +00:00
|
|
|
/**
|
|
|
|
* A canvas that records the number of clip commands.
|
|
|
|
*/
|
|
|
|
class ClipCountingCanvas : public SkCanvas {
|
|
|
|
public:
|
2014-05-27 23:41:45 +00:00
|
|
|
ClipCountingCanvas(int width, int height)
|
2014-01-31 19:42:58 +00:00
|
|
|
: INHERITED(width, height)
|
2013-08-27 15:50:19 +00:00
|
|
|
, fClipCount(0){
|
|
|
|
}
|
|
|
|
|
2014-03-01 03:02:09 +00:00
|
|
|
virtual void onClipRect(const SkRect& r,
|
|
|
|
SkRegion::Op op,
|
2014-02-28 18:19:39 +00:00
|
|
|
ClipEdgeStyle edgeStyle) SK_OVERRIDE {
|
2013-08-27 15:50:19 +00:00
|
|
|
fClipCount += 1;
|
2014-02-28 18:19:39 +00:00
|
|
|
this->INHERITED::onClipRect(r, op, edgeStyle);
|
2013-08-27 15:50:19 +00:00
|
|
|
}
|
|
|
|
|
2014-03-01 03:02:09 +00:00
|
|
|
virtual void onClipRRect(const SkRRect& rrect,
|
|
|
|
SkRegion::Op op,
|
2014-02-28 18:19:39 +00:00
|
|
|
ClipEdgeStyle edgeStyle)SK_OVERRIDE {
|
2013-08-27 15:50:19 +00:00
|
|
|
fClipCount += 1;
|
2014-02-28 18:19:39 +00:00
|
|
|
this->INHERITED::onClipRRect(rrect, op, edgeStyle);
|
2013-08-27 15:50:19 +00:00
|
|
|
}
|
|
|
|
|
2014-03-01 03:02:09 +00:00
|
|
|
virtual void onClipPath(const SkPath& path,
|
|
|
|
SkRegion::Op op,
|
2014-02-28 18:19:39 +00:00
|
|
|
ClipEdgeStyle edgeStyle) SK_OVERRIDE {
|
2013-08-27 15:50:19 +00:00
|
|
|
fClipCount += 1;
|
2014-02-28 18:19:39 +00:00
|
|
|
this->INHERITED::onClipPath(path, op, edgeStyle);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) SK_OVERRIDE {
|
|
|
|
fClipCount += 1;
|
|
|
|
this->INHERITED::onClipRegion(deviceRgn, op);
|
2013-08-27 15:50:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned getClipCount() const { return fClipCount; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
unsigned fClipCount;
|
|
|
|
|
|
|
|
typedef SkCanvas INHERITED;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void test_clip_expansion(skiatest::Reporter* reporter) {
|
2014-04-13 19:09:42 +00:00
|
|
|
SkPictureRecorder recorder;
|
2014-06-09 13:25:34 +00:00
|
|
|
SkCanvas* canvas = recorder.beginRecording(10, 10);
|
2013-08-27 15:50:19 +00:00
|
|
|
|
|
|
|
canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op);
|
|
|
|
// The following expanding clip should not be skipped.
|
|
|
|
canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op);
|
|
|
|
// Draw something so the optimizer doesn't just fold the world.
|
|
|
|
SkPaint p;
|
|
|
|
p.setColor(SK_ColorBLUE);
|
|
|
|
canvas->drawPaint(p);
|
2014-04-13 19:09:42 +00:00
|
|
|
SkAutoTUnref<SkPicture> picture(recorder.endRecording());
|
2013-08-27 15:50:19 +00:00
|
|
|
|
2014-01-31 19:42:58 +00:00
|
|
|
ClipCountingCanvas testCanvas(10, 10);
|
2014-04-13 19:09:42 +00:00
|
|
|
picture->draw(&testCanvas);
|
2013-08-27 15:50:19 +00:00
|
|
|
|
|
|
|
// Both clips should be present on playback.
|
|
|
|
REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
|
|
|
|
}
|
|
|
|
|
2013-10-24 11:12:47 +00:00
|
|
|
static void test_hierarchical(skiatest::Reporter* reporter) {
|
|
|
|
SkBitmap bm;
|
|
|
|
make_bm(&bm, 10, 10, SK_ColorRED, true);
|
|
|
|
|
2014-04-13 19:09:42 +00:00
|
|
|
SkPictureRecorder recorder;
|
|
|
|
|
2014-06-09 13:25:34 +00:00
|
|
|
recorder.beginRecording(10, 10);
|
2014-04-13 19:09:42 +00:00
|
|
|
SkAutoTUnref<SkPicture> childPlain(recorder.endRecording());
|
|
|
|
REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0
|
|
|
|
|
2014-06-09 13:25:34 +00:00
|
|
|
recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0);
|
2014-04-13 19:09:42 +00:00
|
|
|
SkAutoTUnref<SkPicture> childWithBitmap(recorder.endRecording());
|
|
|
|
REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1
|
|
|
|
|
|
|
|
{
|
2014-06-09 13:25:34 +00:00
|
|
|
SkCanvas* canvas = recorder.beginRecording(10, 10);
|
2014-06-04 12:40:44 +00:00
|
|
|
canvas->drawPicture(childPlain);
|
2014-04-13 19:09:42 +00:00
|
|
|
SkAutoTUnref<SkPicture> parentPP(recorder.endRecording());
|
|
|
|
REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0
|
|
|
|
}
|
|
|
|
{
|
2014-06-09 13:25:34 +00:00
|
|
|
SkCanvas* canvas = recorder.beginRecording(10, 10);
|
2014-06-04 12:40:44 +00:00
|
|
|
canvas->drawPicture(childWithBitmap);
|
2014-04-13 19:09:42 +00:00
|
|
|
SkAutoTUnref<SkPicture> parentPWB(recorder.endRecording());
|
|
|
|
REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1
|
|
|
|
}
|
|
|
|
{
|
2014-06-09 13:25:34 +00:00
|
|
|
SkCanvas* canvas = recorder.beginRecording(10, 10);
|
2014-04-13 19:09:42 +00:00
|
|
|
canvas->drawBitmap(bm, 0, 0);
|
2014-06-04 12:40:44 +00:00
|
|
|
canvas->drawPicture(childPlain);
|
2014-04-13 19:09:42 +00:00
|
|
|
SkAutoTUnref<SkPicture> parentWBP(recorder.endRecording());
|
|
|
|
REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1
|
|
|
|
}
|
|
|
|
{
|
2014-06-09 13:25:34 +00:00
|
|
|
SkCanvas* canvas = recorder.beginRecording(10, 10);
|
2014-04-13 19:09:42 +00:00
|
|
|
canvas->drawBitmap(bm, 0, 0);
|
2014-06-04 12:40:44 +00:00
|
|
|
canvas->drawPicture(childWithBitmap);
|
2014-04-13 19:09:42 +00:00
|
|
|
SkAutoTUnref<SkPicture> parentWBWB(recorder.endRecording());
|
|
|
|
REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2
|
|
|
|
}
|
2013-10-24 11:12:47 +00:00
|
|
|
}
|
|
|
|
|
2014-04-02 23:51:13 +00:00
|
|
|
static void test_gen_id(skiatest::Reporter* reporter) {
|
|
|
|
|
2014-07-13 16:00:50 +00:00
|
|
|
SkPictureRecorder recorder;
|
|
|
|
recorder.beginRecording(0, 0);
|
|
|
|
SkAutoTUnref<SkPicture> empty(recorder.endRecording());
|
2014-04-02 23:51:13 +00:00
|
|
|
|
2014-04-13 19:09:42 +00:00
|
|
|
// Empty pictures should still have a valid ID
|
2014-07-13 16:00:50 +00:00
|
|
|
REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
|
2014-04-02 23:51:13 +00:00
|
|
|
|
2014-06-09 13:25:34 +00:00
|
|
|
SkCanvas* canvas = recorder.beginRecording(1, 1);
|
2014-04-13 19:09:42 +00:00
|
|
|
canvas->drawARGB(255, 255, 255, 255);
|
|
|
|
SkAutoTUnref<SkPicture> hasData(recorder.endRecording());
|
|
|
|
// picture should have a non-zero id after recording
|
|
|
|
REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
|
2014-04-02 23:51:13 +00:00
|
|
|
|
2014-04-13 19:09:42 +00:00
|
|
|
// both pictures should have different ids
|
2014-07-13 16:00:50 +00:00
|
|
|
REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
|
2014-04-02 23:51:13 +00:00
|
|
|
}
|
|
|
|
|
2013-12-12 21:11:12 +00:00
|
|
|
DEF_TEST(Picture, reporter) {
|
2012-09-14 17:26:37 +00:00
|
|
|
#ifdef SK_DEBUG
|
2014-07-01 15:47:04 +00:00
|
|
|
test_deleting_empty_picture();
|
2012-09-14 17:26:37 +00:00
|
|
|
test_serializing_empty_picture();
|
2012-12-04 15:08:56 +00:00
|
|
|
#else
|
|
|
|
test_bad_bitmap();
|
2012-09-14 17:26:37 +00:00
|
|
|
#endif
|
2014-02-13 16:00:51 +00:00
|
|
|
test_unbalanced_save_restores(reporter);
|
2013-02-27 19:17:41 +00:00
|
|
|
test_peephole();
|
2014-04-25 00:02:12 +00:00
|
|
|
#if SK_SUPPORT_GPU
|
2014-04-24 21:53:13 +00:00
|
|
|
test_gpu_veto(reporter);
|
2014-04-25 00:02:12 +00:00
|
|
|
#endif
|
2012-11-29 21:00:39 +00:00
|
|
|
test_gatherpixelrefs(reporter);
|
2014-01-13 13:33:26 +00:00
|
|
|
test_gatherpixelrefsandrects(reporter);
|
2012-12-10 15:40:55 +00:00
|
|
|
test_bitmap_with_encoded_data(reporter);
|
2014-03-18 17:45:32 +00:00
|
|
|
test_draw_empty(reporter);
|
2013-05-08 15:39:13 +00:00
|
|
|
test_clip_bound_opt(reporter);
|
2013-08-27 15:50:19 +00:00
|
|
|
test_clip_expansion(reporter);
|
2013-10-24 11:12:47 +00:00
|
|
|
test_hierarchical(reporter);
|
2014-04-02 23:51:13 +00:00
|
|
|
test_gen_id(reporter);
|
2012-09-14 17:26:37 +00:00
|
|
|
}
|
2014-02-10 18:29:10 +00:00
|
|
|
|
2014-05-06 12:02:22 +00:00
|
|
|
#if SK_SUPPORT_GPU
|
|
|
|
DEF_GPUTEST(GPUPicture, reporter, factory) {
|
|
|
|
test_gpu_picture_optimization(reporter, factory);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-02-10 18:29:10 +00:00
|
|
|
static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
|
|
|
|
const SkPaint paint;
|
|
|
|
const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
|
|
|
|
const SkIRect irect = { 2, 2, 3, 3 };
|
|
|
|
|
|
|
|
// Don't care what these record, as long as they're legal.
|
|
|
|
canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint);
|
|
|
|
canvas->drawBitmapRectToRect(bitmap, &rect, rect, &paint, SkCanvas::kNone_DrawBitmapRectFlag);
|
|
|
|
canvas->drawBitmapMatrix(bitmap, SkMatrix::I(), &paint);
|
|
|
|
canvas->drawBitmapNine(bitmap, irect, rect, &paint);
|
|
|
|
canvas->drawSprite(bitmap, 1, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void test_draw_bitmaps(SkCanvas* canvas) {
|
|
|
|
SkBitmap empty;
|
|
|
|
draw_bitmaps(empty, canvas);
|
2014-05-30 13:26:10 +00:00
|
|
|
empty.setInfo(SkImageInfo::MakeN32Premul(10, 10));
|
2014-02-10 18:29:10 +00:00
|
|
|
draw_bitmaps(empty, canvas);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(Picture_EmptyBitmap, r) {
|
2014-04-13 19:09:42 +00:00
|
|
|
SkPictureRecorder recorder;
|
2014-06-09 13:25:34 +00:00
|
|
|
test_draw_bitmaps(recorder.beginRecording(10, 10));
|
2014-04-13 19:09:42 +00:00
|
|
|
SkAutoTUnref<SkPicture> picture(recorder.endRecording());
|
2014-02-10 18:29:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(Canvas_EmptyBitmap, r) {
|
|
|
|
SkBitmap dst;
|
2014-02-13 14:41:43 +00:00
|
|
|
dst.allocN32Pixels(10, 10);
|
2014-02-10 18:29:10 +00:00
|
|
|
SkCanvas canvas(dst);
|
|
|
|
|
|
|
|
test_draw_bitmaps(&canvas);
|
|
|
|
}
|
Fix end-of-pattern matching for Skia recording optimization.
The recorder optimizer's pattern matcher was accepting command sequences
when it shouldn't have.
In the submitted case, and the pattern matcher was looking for:
saveLayer, drawBitmap, restore
and in the rendering for the submitted case, the sequence of commands
were:
saveLayer, drawBitmap, drawBitmap, restore
This sequence was improperly accepted by the matcher, and the optimizer
reduced the sequence to:
drawBitmap, drawBitmap
where the opacity from the saveLayer paint argument was applied
to the first drawBitmap only.
The user-visible effect in Chrome was a flashing effect on an image
caused by incorrect (too-high) opacity.
The patch adds a Skia test to check for pixel colour values in
a similarly structured recording. All other Skia tests pass.
Blink layout tests also pass with this change.
BUG=chromium:344987
R=robertphillips@google.com, reed@google.com, mtklein@google.com
Author: dneto@chromium.org
Review URL: https://codereview.chromium.org/430503004
2014-07-30 22:42:22 +00:00
|
|
|
|
|
|
|
DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
|
|
|
|
// This test is from crbug.com/344987.
|
|
|
|
// The commands are:
|
|
|
|
// saveLayer with paint that modifies alpha
|
|
|
|
// drawBitmapRectToRect
|
|
|
|
// drawBitmapRectToRect
|
|
|
|
// restore
|
|
|
|
// The bug was that this structure was modified so that:
|
|
|
|
// - The saveLayer and restore were eliminated
|
|
|
|
// - The alpha was only applied to the first drawBitmapRectToRect
|
|
|
|
|
|
|
|
// This test draws blue and red squares inside a 50% transparent
|
|
|
|
// layer. Both colours should show up muted.
|
|
|
|
// When the bug is present, the red square (the second bitmap)
|
|
|
|
// shows upwith full opacity.
|
|
|
|
|
|
|
|
SkBitmap blueBM;
|
|
|
|
make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
|
|
|
|
SkBitmap redBM;
|
|
|
|
make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
|
|
|
|
SkPaint semiTransparent;
|
|
|
|
semiTransparent.setAlpha(0x80);
|
|
|
|
|
|
|
|
SkPictureRecorder recorder;
|
|
|
|
SkCanvas* canvas = recorder.beginRecording(100, 100);
|
|
|
|
canvas->drawARGB(0, 0, 0, 0);
|
|
|
|
|
|
|
|
canvas->saveLayer(0, &semiTransparent);
|
|
|
|
canvas->drawBitmap(blueBM, 25, 25);
|
|
|
|
canvas->drawBitmap(redBM, 50, 50);
|
|
|
|
canvas->restore();
|
|
|
|
|
|
|
|
SkAutoTUnref<SkPicture> picture(recorder.endRecording());
|
|
|
|
|
|
|
|
// Now replay the picture back on another canvas
|
|
|
|
// and check a couple of its pixels.
|
|
|
|
SkBitmap replayBM;
|
|
|
|
make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
|
|
|
|
SkCanvas replayCanvas(replayBM);
|
|
|
|
picture->draw(&replayCanvas);
|
|
|
|
replayCanvas.flush();
|
|
|
|
|
|
|
|
// With the bug present, at (55, 55) we would get a fully opaque red
|
|
|
|
// intead of a dark red.
|
|
|
|
REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
|
|
|
|
REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
|
|
|
|
}
|