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
|
|
|
|
2016-07-13 20:27:16 +00:00
|
|
|
#include "SkBigPicture.h"
|
2014-08-18 20:39:11 +00:00
|
|
|
#include "SkBBoxHierarchy.h"
|
2014-05-06 12:02:22 +00:00
|
|
|
#include "SkBlurImageFilter.h"
|
2012-10-02 17:42:15 +00:00
|
|
|
#include "SkCanvas.h"
|
2014-10-30 23:45:02 +00:00
|
|
|
#include "SkColorMatrixFilter.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"
|
2015-01-08 02:04:45 +00:00
|
|
|
#include "SkImageGenerator.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"
|
2016-02-17 18:02:29 +00:00
|
|
|
#include "SkMD5.h"
|
2018-01-08 20:05:02 +00:00
|
|
|
#include "SkMiniRecorder.h"
|
2012-10-02 17:42:15 +00:00
|
|
|
#include "SkPaint.h"
|
2018-01-27 17:30:04 +00:00
|
|
|
#include "SkPicture.h"
|
2014-04-18 18:04:41 +00:00
|
|
|
#include "SkPictureRecorder.h"
|
2014-08-27 19:12:23 +00:00
|
|
|
#include "SkPixelRef.h"
|
2018-01-08 20:05:02 +00:00
|
|
|
#include "SkRectPriv.h"
|
2013-01-24 18:27:42 +00:00
|
|
|
#include "SkRRect.h"
|
2014-01-24 20:56:26 +00:00
|
|
|
#include "SkRandom.h"
|
2014-11-19 18:41:14 +00:00
|
|
|
#include "SkRecord.h"
|
2012-11-29 21:00:39 +00:00
|
|
|
#include "SkShader.h"
|
2012-09-14 17:26:37 +00:00
|
|
|
#include "SkStream.h"
|
2015-03-23 12:46:51 +00:00
|
|
|
#include "sk_tool_utils.h"
|
2014-05-06 12:02:22 +00:00
|
|
|
|
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"
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-14 17:26:37 +00:00
|
|
|
#ifdef SK_DEBUG
|
2014-08-18 20:39:11 +00:00
|
|
|
// Ensure that deleting an empty SkPicture does not assert. Asserts only fire
|
2014-07-01 15:47:04 +00:00
|
|
|
// 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
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
|
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);
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
|
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-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){
|
|
|
|
}
|
|
|
|
|
2015-12-18 15:09:18 +00:00
|
|
|
SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
|
2014-05-27 23:41:45 +00:00
|
|
|
++fSaveLayerCount;
|
2015-12-18 15:09:18 +00:00
|
|
|
return this->INHERITED::getSaveLayerStrategy(rec);
|
2014-05-27 23:41:45 +00:00
|
|
|
}
|
|
|
|
|
2015-03-26 01:17:31 +00:00
|
|
|
void willSave() 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
|
|
|
}
|
|
|
|
|
2015-03-26 01:17:31 +00:00
|
|
|
void willRestore() override {
|
2014-05-27 23:41:45 +00:00
|
|
|
++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) {
|
2014-09-08 14:31:18 +00:00
|
|
|
SaveCountingCanvas canvas(SkScalarCeilToInt(picture->cullRect().width()),
|
2014-08-29 15:03:56 +00:00
|
|
|
SkScalarCeilToInt(picture->cullRect().height()));
|
2014-05-27 23:41:45 +00:00
|
|
|
|
2014-09-04 15:42:50 +00:00
|
|
|
picture->playback(&canvas);
|
2014-05-27 23:41:45 +00:00
|
|
|
|
2014-09-08 14:31:18 +00:00
|
|
|
// Optimizations may have removed these,
|
|
|
|
// so expect to have seen no more than num{Saves,SaveLayers,Restores}.
|
|
|
|
REPORTER_ASSERT(reporter, numSaves >= canvas.getSaveCount());
|
|
|
|
REPORTER_ASSERT(reporter, numSaveLayers >= canvas.getSaveLayerCount());
|
|
|
|
REPORTER_ASSERT(reporter, numRestores >= canvas.getRestoreCount());
|
2014-05-27 23:41:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// This class exists so SkPicture can friend it and give it access to
|
|
|
|
// the 'partialReplay' method.
|
|
|
|
class SkPictureRecorderReplayTester {
|
|
|
|
public:
|
2016-03-18 14:25:55 +00:00
|
|
|
static sk_sp<SkPicture> Copy(SkPictureRecorder* recorder) {
|
2014-05-27 23:41:45 +00:00
|
|
|
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);
|
|
|
|
|
2016-03-18 14:25:55 +00:00
|
|
|
return recorder2.finishRecordingAsPicture();
|
2014-05-27 23:41:45 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
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();
|
2016-12-09 14:00:50 +00:00
|
|
|
canvas->clipRect(clipRect, kReplace_SkClipOp);
|
2014-06-10 18:45:46 +00:00
|
|
|
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();
|
|
|
|
|
2017-01-23 16:39:45 +00:00
|
|
|
SkRect beforeClip = canvas.getLocalClipBounds();
|
2014-06-10 18:45:46 +00:00
|
|
|
|
|
|
|
canvas.drawPicture(picture);
|
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount());
|
|
|
|
REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix());
|
|
|
|
|
2017-01-23 16:39:45 +00:00
|
|
|
SkRect afterClip = canvas.getLocalClipBounds();
|
2014-06-10 18:45:46 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
2015-08-27 14:41:13 +00:00
|
|
|
canvas->saveLayer(nullptr, nullptr);
|
2014-05-27 23:41:45 +00:00
|
|
|
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
|
2014-05-27 23:41:45 +00:00
|
|
|
|
|
|
|
// The extra save and restore comes from the Copy process.
|
2016-03-18 14:25:55 +00:00
|
|
|
check_save_state(reporter, copy.get(), 2, 1, 3);
|
2014-05-27 23:41:45 +00:00
|
|
|
|
2015-08-27 14:41:13 +00:00
|
|
|
canvas->saveLayer(nullptr, nullptr);
|
2014-05-27 23:41:45 +00:00
|
|
|
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
|
2014-05-27 23:41:45 +00:00
|
|
|
|
2016-03-18 14:25:55 +00:00
|
|
|
check_save_state(reporter, final.get(), 1, 2, 3);
|
2014-05-27 23:41:45 +00:00
|
|
|
|
|
|
|
// The copy shouldn't pick up any operations added after it was made
|
2016-03-18 14:25:55 +00:00
|
|
|
check_save_state(reporter, copy.get(), 2, 1, 3);
|
2014-05-27 23:41:45 +00:00
|
|
|
}
|
|
|
|
|
2014-06-10 18:45:46 +00:00
|
|
|
// Recreate the Android partialReplay test case
|
|
|
|
{
|
|
|
|
SkPictureRecorder recorder;
|
|
|
|
|
2015-08-27 14:41:13 +00:00
|
|
|
SkCanvas* canvas = recorder.beginRecording(4, 3, nullptr, 0);
|
2014-06-10 18:45:46 +00:00
|
|
|
create_imbalance(canvas);
|
|
|
|
|
|
|
|
int expectedSaveCount = canvas->getSaveCount();
|
|
|
|
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
|
|
|
|
check_balance(reporter, copy.get());
|
2014-06-10 18:45:46 +00:00
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount());
|
|
|
|
|
|
|
|
// End the recording of source to test the picture finalization
|
|
|
|
// process isn't complicated by the partialReplay step
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
|
2014-06-10 18:45:46 +00:00
|
|
|
}
|
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);
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> extraSavePicture(recorder.finishRecordingAsPicture());
|
2014-04-13 19:09:42 +00:00
|
|
|
|
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();
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> extraRestorePicture(recorder.finishRecordingAsPicture());
|
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);
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> noSavePicture(recorder.finishRecordingAsPicture());
|
2014-04-13 19:09:42 +00:00
|
|
|
|
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);
|
|
|
|
}
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
|
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();
|
|
|
|
}
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
|
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);
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
|
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
|
|
|
|
|
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;
|
Revert of Revert of allow canvas to force conservative clips (for speed) (patchset #1 id:1 of https://codereview.chromium.org/554033003/)
Reason for revert:
May just rebaseline, plus want to see the results of the chrome tests, so re-trying this CL.
Original issue's description:
> Revert of allow canvas to force conservative clips (for speed) (patchset #7 id:120001 of https://codereview.chromium.org/541593005/)
>
> Reason for revert:
> multipicturedraw failed on nvprmsaa -- don't know why yet
>
> Original issue's description:
> > Allow SkCanvas to be initialized to force conservative rasterclips. This has the following effects:
> >
> > 1. Queries to the current clip will be conservatively large. This can mean the quickReject may return false more often.
> >
> > 2. The conservative clips mean less work is done.
> >
> > 3. Enabled by default for Gpu, Record, and NoSaveLayer canvases.
> >
> > 4. API is private for now.
> >
> > Committed: https://skia.googlesource.com/skia/+/27a5e656c3d6ef22f9cb34de18e1b960da3aa241
>
> TBR=robertphillips@google.com,bsalomon@google.com,mtklein@google.com,junov@google.com
> NOTREECHECKS=true
> NOTRY=true
>
> Committed: https://skia.googlesource.com/skia/+/6f09709519b79a1159f3826645f1c5fbc101ee11
R=robertphillips@google.com, bsalomon@google.com, mtklein@google.com, junov@google.com, reed@google.com
TBR=bsalomon@google.com, junov@google.com, mtklein@google.com, reed@google.com, robertphillips@google.com
NOTREECHECKS=true
NOTRY=true
Author: reed@chromium.org
Review URL: https://codereview.chromium.org/560713002
2014-09-10 01:46:22 +00:00
|
|
|
|
|
|
|
// Testing conservative-raster-clip that is enabled by PictureRecord
|
2013-05-08 15:39:13 +00:00
|
|
|
{
|
2014-06-09 13:25:34 +00:00
|
|
|
SkCanvas* canvas = recorder.beginRecording(10, 10);
|
2016-09-20 15:42:38 +00:00
|
|
|
canvas->clipPath(invPath);
|
2017-01-23 16:39:45 +00:00
|
|
|
clipBounds = canvas->getDeviceClipBounds();
|
2013-05-08 15:39:13 +00:00
|
|
|
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);
|
2016-09-20 15:42:38 +00:00
|
|
|
canvas->clipPath(path);
|
|
|
|
canvas->clipPath(invPath);
|
2017-01-23 16:39:45 +00:00
|
|
|
clipBounds = canvas->getDeviceClipBounds();
|
2013-05-08 15:39:13 +00:00
|
|
|
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);
|
2016-09-20 15:42:38 +00:00
|
|
|
canvas->clipPath(path);
|
2016-12-09 14:00:50 +00:00
|
|
|
canvas->clipPath(invPath, kUnion_SkClipOp);
|
2017-01-23 16:39:45 +00:00
|
|
|
clipBounds = canvas->getDeviceClipBounds();
|
2013-05-08 15:39:13 +00:00
|
|
|
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);
|
2016-12-09 14:00:50 +00:00
|
|
|
canvas->clipPath(path, kDifference_SkClipOp);
|
2017-01-23 16:39:45 +00:00
|
|
|
clipBounds = canvas->getDeviceClipBounds();
|
2013-05-08 15:39:13 +00:00
|
|
|
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);
|
2016-12-09 14:00:50 +00:00
|
|
|
canvas->clipPath(path, kReverseDifference_SkClipOp);
|
2017-01-23 16:39:45 +00:00
|
|
|
clipBounds = canvas->getDeviceClipBounds();
|
2013-05-08 15:39:13 +00:00
|
|
|
// 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, 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);
|
2016-12-09 14:00:50 +00:00
|
|
|
canvas->clipPath(path, kIntersect_SkClipOp);
|
|
|
|
canvas->clipPath(path2, kXOR_SkClipOp);
|
2017-01-23 16:39:45 +00:00
|
|
|
clipBounds = canvas->getDeviceClipBounds();
|
2013-05-08 15:39:13 +00:00
|
|
|
REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
|
|
|
|
REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
|
|
|
|
REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
|
|
|
|
REPORTER_ASSERT(reporter, 8 == clipBounds.fRight);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-07 21:27:10 +00:00
|
|
|
static void test_cull_rect_reset(skiatest::Reporter* reporter) {
|
|
|
|
SkPictureRecorder recorder;
|
|
|
|
SkRect bounds = SkRect::MakeWH(10, 10);
|
|
|
|
SkRTreeFactory factory;
|
|
|
|
SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
|
|
|
|
bounds = SkRect::MakeWH(100, 100);
|
|
|
|
SkPaint paint;
|
|
|
|
canvas->drawRect(bounds, paint);
|
|
|
|
canvas->drawRect(bounds, paint);
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> p(recorder.finishRecordingAsPictureWithCull(bounds));
|
2018-01-27 17:30:04 +00:00
|
|
|
const SkBigPicture* picture = p->asSkBigPicture();
|
2015-07-07 21:27:10 +00:00
|
|
|
REPORTER_ASSERT(reporter, picture);
|
|
|
|
|
|
|
|
SkRect finalCullRect = picture->cullRect();
|
|
|
|
REPORTER_ASSERT(reporter, 0 == finalCullRect.fLeft);
|
|
|
|
REPORTER_ASSERT(reporter, 0 == finalCullRect.fTop);
|
|
|
|
REPORTER_ASSERT(reporter, 100 == finalCullRect.fBottom);
|
|
|
|
REPORTER_ASSERT(reporter, 100 == finalCullRect.fRight);
|
|
|
|
|
|
|
|
const SkBBoxHierarchy* pictureBBH = picture->bbh();
|
|
|
|
SkRect bbhCullRect = pictureBBH->getRootBound();
|
|
|
|
REPORTER_ASSERT(reporter, 0 == bbhCullRect.fLeft);
|
|
|
|
REPORTER_ASSERT(reporter, 0 == bbhCullRect.fTop);
|
|
|
|
REPORTER_ASSERT(reporter, 100 == bbhCullRect.fBottom);
|
|
|
|
REPORTER_ASSERT(reporter, 100 == bbhCullRect.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){
|
|
|
|
}
|
|
|
|
|
2016-12-09 14:00:50 +00:00
|
|
|
void onClipRect(const SkRect& r, SkClipOp op, ClipEdgeStyle edgeStyle) 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
|
|
|
}
|
|
|
|
|
2016-12-09 14:00:50 +00:00
|
|
|
void onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle)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
|
|
|
}
|
|
|
|
|
2016-12-09 14:00:50 +00:00
|
|
|
void onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) 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);
|
|
|
|
}
|
|
|
|
|
2016-12-09 14:00:50 +00:00
|
|
|
void onClipRegion(const SkRegion& deviceRgn, SkClipOp op) override {
|
2014-02-28 18:19:39 +00:00
|
|
|
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
|
|
|
|
2016-12-09 14:00:50 +00:00
|
|
|
canvas->clipRect(SkRect::MakeEmpty(), kReplace_SkClipOp);
|
2013-08-27 15:50:19 +00:00
|
|
|
// The following expanding clip should not be skipped.
|
2016-12-09 14:00:50 +00:00
|
|
|
canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), kUnion_SkClipOp);
|
2013-08-27 15:50:19 +00:00
|
|
|
// Draw something so the optimizer doesn't just fold the world.
|
|
|
|
SkPaint p;
|
|
|
|
p.setColor(SK_ColorBLUE);
|
|
|
|
canvas->drawPaint(p);
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
|
2013-08-27 15:50:19 +00:00
|
|
|
|
2014-01-31 19:42:58 +00:00
|
|
|
ClipCountingCanvas testCanvas(10, 10);
|
2014-09-04 15:42:50 +00:00
|
|
|
picture->playback(&testCanvas);
|
2013-08-27 15:50:19 +00:00
|
|
|
|
|
|
|
// Both clips should be present on playback.
|
|
|
|
REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2);
|
|
|
|
}
|
|
|
|
|
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);
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> empty(recorder.finishRecordingAsPicture());
|
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);
|
2017-02-22 18:21:42 +00:00
|
|
|
canvas->drawColor(SK_ColorWHITE);
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> hasData(recorder.finishRecordingAsPicture());
|
2014-04-13 19:09:42 +00:00
|
|
|
// 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
|
|
|
}
|
|
|
|
|
2015-08-31 16:22:38 +00:00
|
|
|
static void test_typeface(skiatest::Reporter* reporter) {
|
|
|
|
SkPictureRecorder recorder;
|
|
|
|
SkCanvas* canvas = recorder.beginRecording(10, 10);
|
|
|
|
SkPaint paint;
|
2017-07-27 14:45:29 +00:00
|
|
|
paint.setTypeface(SkTypeface::MakeFromName("Arial", SkFontStyle::Italic()));
|
2017-04-28 19:35:12 +00:00
|
|
|
canvas->drawString("Q", 0, 10, paint);
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
|
2015-08-31 16:22:38 +00:00
|
|
|
SkDynamicMemoryWStream stream;
|
|
|
|
picture->serialize(&stream);
|
|
|
|
}
|
|
|
|
|
2013-12-12 21:11:12 +00:00
|
|
|
DEF_TEST(Picture, reporter) {
|
2015-08-31 16:22:38 +00:00
|
|
|
test_typeface(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();
|
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);
|
2014-04-02 23:51:13 +00:00
|
|
|
test_gen_id(reporter);
|
2015-07-07 21:27:10 +00:00
|
|
|
test_cull_rect_reset(reporter);
|
2012-09-14 17:26:37 +00:00
|
|
|
}
|
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 };
|
2016-08-02 15:05:56 +00:00
|
|
|
int divs[] = { 2, 3 };
|
|
|
|
SkCanvas::Lattice lattice;
|
|
|
|
lattice.fXCount = lattice.fYCount = 2;
|
|
|
|
lattice.fXDivs = lattice.fYDivs = divs;
|
2014-02-10 18:29:10 +00:00
|
|
|
|
|
|
|
// Don't care what these record, as long as they're legal.
|
|
|
|
canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint);
|
2015-08-06 17:02:53 +00:00
|
|
|
canvas->drawBitmapRect(bitmap, rect, rect, &paint, SkCanvas::kStrict_SrcRectConstraint);
|
2014-02-10 18:29:10 +00:00
|
|
|
canvas->drawBitmapNine(bitmap, irect, rect, &paint);
|
2015-12-16 16:38:15 +00:00
|
|
|
canvas->drawBitmap(bitmap, 1, 1); // drawSprite
|
2016-08-02 15:05:56 +00:00
|
|
|
canvas->drawBitmapLattice(bitmap, lattice, rect, &paint);
|
2014-02-10 18:29:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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));
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
|
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
|
2015-07-17 14:09:43 +00:00
|
|
|
// drawBitmapRect
|
|
|
|
// drawBitmapRect
|
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
|
|
|
// 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);
|
2017-02-22 18:21:42 +00:00
|
|
|
canvas->drawColor(0);
|
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
|
|
|
|
2017-08-28 14:34:05 +00:00
|
|
|
canvas->saveLayer(nullptr, &semiTransparent);
|
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
|
|
|
canvas->drawBitmap(blueBM, 25, 25);
|
|
|
|
canvas->drawBitmap(redBM, 50, 50);
|
|
|
|
canvas->restore();
|
|
|
|
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
|
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
|
|
|
|
|
|
|
// 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);
|
2014-09-04 15:42:50 +00:00
|
|
|
picture->playback(&replayCanvas);
|
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
|
|
|
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);
|
|
|
|
}
|
2014-08-18 20:39:11 +00:00
|
|
|
|
|
|
|
struct CountingBBH : public SkBBoxHierarchy {
|
|
|
|
mutable int searchCalls;
|
2015-03-07 00:20:28 +00:00
|
|
|
SkRect rootBound;
|
2014-08-18 20:39:11 +00:00
|
|
|
|
2015-03-07 00:20:28 +00:00
|
|
|
CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {}
|
2014-08-18 20:39:11 +00:00
|
|
|
|
2015-08-19 16:51:00 +00:00
|
|
|
void search(const SkRect& query, SkTDArray<int>* results) const override {
|
2014-08-18 20:39:11 +00:00
|
|
|
this->searchCalls++;
|
|
|
|
}
|
|
|
|
|
2015-03-26 01:17:31 +00:00
|
|
|
void insert(const SkRect[], int) override {}
|
|
|
|
virtual size_t bytesUsed() const override { return 0; }
|
|
|
|
SkRect getRootBound() const override { return rootBound; }
|
2014-08-18 20:39:11 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class SpoonFedBBHFactory : public SkBBHFactory {
|
|
|
|
public:
|
|
|
|
explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {}
|
2015-03-26 01:17:31 +00:00
|
|
|
SkBBoxHierarchy* operator()(const SkRect&) const override {
|
2014-08-18 20:39:11 +00:00
|
|
|
return SkRef(fBBH);
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
SkBBoxHierarchy* fBBH;
|
|
|
|
};
|
|
|
|
|
|
|
|
// When the canvas clip covers the full picture, we don't need to call the BBH.
|
|
|
|
DEF_TEST(Picture_SkipBBH, r) {
|
2015-03-07 00:20:28 +00:00
|
|
|
SkRect bound = SkRect::MakeWH(320, 240);
|
|
|
|
CountingBBH bbh(bound);
|
2014-08-18 20:39:11 +00:00
|
|
|
SpoonFedBBHFactory factory(&bbh);
|
|
|
|
|
|
|
|
SkPictureRecorder recorder;
|
2015-05-19 18:11:26 +00:00
|
|
|
SkCanvas* c = recorder.beginRecording(bound, &factory);
|
|
|
|
// Record a few ops so we don't hit a small- or empty- picture optimization.
|
|
|
|
c->drawRect(bound, SkPaint());
|
|
|
|
c->drawRect(bound, SkPaint());
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
|
2014-08-18 20:39:11 +00:00
|
|
|
|
|
|
|
SkCanvas big(640, 480), small(300, 200);
|
|
|
|
|
2014-09-04 15:42:50 +00:00
|
|
|
picture->playback(&big);
|
2014-08-18 20:39:11 +00:00
|
|
|
REPORTER_ASSERT(r, bbh.searchCalls == 0);
|
|
|
|
|
2014-09-04 15:42:50 +00:00
|
|
|
picture->playback(&small);
|
2014-08-18 20:39:11 +00:00
|
|
|
REPORTER_ASSERT(r, bbh.searchCalls == 1);
|
|
|
|
}
|
2014-08-27 19:12:23 +00:00
|
|
|
|
|
|
|
DEF_TEST(Picture_BitmapLeak, r) {
|
|
|
|
SkBitmap mut, immut;
|
|
|
|
mut.allocN32Pixels(300, 200);
|
|
|
|
immut.allocN32Pixels(300, 200);
|
|
|
|
immut.setImmutable();
|
|
|
|
SkASSERT(!mut.isImmutable());
|
|
|
|
SkASSERT(immut.isImmutable());
|
|
|
|
|
|
|
|
// No one can hold a ref on our pixels yet.
|
|
|
|
REPORTER_ASSERT(r, mut.pixelRef()->unique());
|
|
|
|
REPORTER_ASSERT(r, immut.pixelRef()->unique());
|
|
|
|
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> pic;
|
2014-11-24 22:41:51 +00:00
|
|
|
{
|
|
|
|
// we want the recorder to go out of scope before our subsequent checks, so we
|
|
|
|
// place it inside local braces.
|
|
|
|
SkPictureRecorder rec;
|
|
|
|
SkCanvas* canvas = rec.beginRecording(1920, 1200);
|
|
|
|
canvas->drawBitmap(mut, 0, 0);
|
|
|
|
canvas->drawBitmap(immut, 800, 600);
|
2016-03-18 14:25:55 +00:00
|
|
|
pic = rec.finishRecordingAsPicture();
|
2014-11-24 22:41:51 +00:00
|
|
|
}
|
2014-08-27 19:12:23 +00:00
|
|
|
|
|
|
|
// The picture shares the immutable pixels but copies the mutable ones.
|
|
|
|
REPORTER_ASSERT(r, mut.pixelRef()->unique());
|
|
|
|
REPORTER_ASSERT(r, !immut.pixelRef()->unique());
|
|
|
|
|
|
|
|
// When the picture goes away, it's just our bitmaps holding the refs.
|
2016-03-18 14:25:55 +00:00
|
|
|
pic = nullptr;
|
2014-08-27 19:12:23 +00:00
|
|
|
REPORTER_ASSERT(r, mut.pixelRef()->unique());
|
|
|
|
REPORTER_ASSERT(r, immut.pixelRef()->unique());
|
|
|
|
}
|
2015-04-08 18:25:48 +00:00
|
|
|
|
|
|
|
// getRecordingCanvas() should return a SkCanvas when recording, null when not recording.
|
|
|
|
DEF_TEST(Picture_getRecordingCanvas, r) {
|
|
|
|
SkPictureRecorder rec;
|
|
|
|
REPORTER_ASSERT(r, !rec.getRecordingCanvas());
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
|
|
rec.beginRecording(100, 100);
|
|
|
|
REPORTER_ASSERT(r, rec.getRecordingCanvas());
|
2016-03-18 14:25:55 +00:00
|
|
|
rec.finishRecordingAsPicture();
|
2015-04-08 18:25:48 +00:00
|
|
|
REPORTER_ASSERT(r, !rec.getRecordingCanvas());
|
|
|
|
}
|
|
|
|
}
|
2015-05-19 18:11:26 +00:00
|
|
|
|
|
|
|
DEF_TEST(MiniRecorderLeftHanging, r) {
|
|
|
|
// Any shader or other ref-counted effect will do just fine here.
|
|
|
|
SkPaint paint;
|
2016-03-13 21:13:58 +00:00
|
|
|
paint.setShader(SkShader::MakeColorShader(SK_ColorRED));
|
2015-05-19 18:11:26 +00:00
|
|
|
|
|
|
|
SkMiniRecorder rec;
|
|
|
|
REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint));
|
|
|
|
// Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader.
|
|
|
|
}
|
2015-07-14 20:12:25 +00:00
|
|
|
|
|
|
|
DEF_TEST(Picture_preserveCullRect, r) {
|
|
|
|
SkPictureRecorder recorder;
|
|
|
|
|
|
|
|
SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4));
|
|
|
|
c->clear(SK_ColorCYAN);
|
|
|
|
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
|
2015-07-14 20:12:25 +00:00
|
|
|
SkDynamicMemoryWStream wstream;
|
|
|
|
picture->serialize(&wstream);
|
|
|
|
|
2016-11-03 18:40:50 +00:00
|
|
|
std::unique_ptr<SkStream> rstream(wstream.detachAsStream());
|
|
|
|
sk_sp<SkPicture> deserializedPicture(SkPicture::MakeFromStream(rstream.get()));
|
2015-07-14 20:12:25 +00:00
|
|
|
|
2016-03-16 17:28:35 +00:00
|
|
|
REPORTER_ASSERT(r, deserializedPicture != nullptr);
|
2015-07-14 20:12:25 +00:00
|
|
|
REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1);
|
|
|
|
REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2);
|
|
|
|
REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3);
|
|
|
|
REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4);
|
|
|
|
}
|
2016-05-13 18:40:07 +00:00
|
|
|
|
2017-04-10 13:50:25 +00:00
|
|
|
|
|
|
|
// If we record bounded ops into a picture with a big cull and calculate the
|
|
|
|
// bounds of those ops, we should trim down the picture cull to the ops' bounds.
|
|
|
|
// If we're not using an SkBBH, we shouldn't change it.
|
|
|
|
DEF_TEST(Picture_UpdatedCull_1, r) {
|
|
|
|
// Testing 1 draw exercises SkMiniPicture.
|
|
|
|
SkRTreeFactory factory;
|
|
|
|
SkPictureRecorder recorder;
|
|
|
|
|
2018-01-08 20:05:02 +00:00
|
|
|
auto canvas = recorder.beginRecording(SkRectPriv::MakeLargest(), &factory);
|
2017-04-10 13:50:25 +00:00
|
|
|
canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
|
|
|
|
auto pic = recorder.finishRecordingAsPicture();
|
|
|
|
REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,20));
|
|
|
|
|
2018-01-08 20:05:02 +00:00
|
|
|
canvas = recorder.beginRecording(SkRectPriv::MakeLargest());
|
2017-04-10 13:50:25 +00:00
|
|
|
canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
|
|
|
|
pic = recorder.finishRecordingAsPicture();
|
2018-01-08 20:05:02 +00:00
|
|
|
REPORTER_ASSERT(r, pic->cullRect() == SkRectPriv::MakeLargest());
|
2017-04-10 13:50:25 +00:00
|
|
|
}
|
|
|
|
DEF_TEST(Picture_UpdatedCull_2, r) {
|
|
|
|
// Testing >1 draw exercises SkBigPicture.
|
|
|
|
SkRTreeFactory factory;
|
|
|
|
SkPictureRecorder recorder;
|
|
|
|
|
2018-01-08 20:05:02 +00:00
|
|
|
auto canvas = recorder.beginRecording(SkRectPriv::MakeLargest(), &factory);
|
2017-04-10 13:50:25 +00:00
|
|
|
canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
|
|
|
|
canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{});
|
|
|
|
auto pic = recorder.finishRecordingAsPicture();
|
|
|
|
REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,40));
|
|
|
|
|
2018-01-08 20:05:02 +00:00
|
|
|
canvas = recorder.beginRecording(SkRectPriv::MakeLargest());
|
2017-04-10 13:50:25 +00:00
|
|
|
canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
|
|
|
|
canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{});
|
|
|
|
pic = recorder.finishRecordingAsPicture();
|
2018-01-08 20:05:02 +00:00
|
|
|
REPORTER_ASSERT(r, pic->cullRect() == SkRectPriv::MakeLargest());
|
2017-04-10 13:50:25 +00:00
|
|
|
}
|
2017-08-14 14:39:28 +00:00
|
|
|
|
2018-01-27 17:30:04 +00:00
|
|
|
DEF_TEST(Picture_RecordsFlush, r) {
|
|
|
|
SkPictureRecorder recorder;
|
|
|
|
|
|
|
|
auto canvas = recorder.beginRecording(SkRect::MakeWH(100,100));
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
|
|
canvas->clear(0);
|
|
|
|
for (int j = 0; j < 10; j++) {
|
|
|
|
canvas->drawRect(SkRect::MakeXYWH(i*10,j*10,10,10), SkPaint());
|
|
|
|
}
|
|
|
|
canvas->flush();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Did we record the flushes?
|
|
|
|
auto pic = recorder.finishRecordingAsPicture();
|
|
|
|
REPORTER_ASSERT(r, pic->approximateOpCount() == 120); // 10 clears, 100 draws, 10 flushes
|
|
|
|
|
|
|
|
// Do we serialize and deserialize flushes?
|
|
|
|
auto skp = pic->serialize();
|
|
|
|
auto back = SkPicture::MakeFromData(skp->data(), skp->size());
|
|
|
|
REPORTER_ASSERT(r, back->approximateOpCount() == pic->approximateOpCount());
|
|
|
|
}
|
2018-01-26 14:49:48 +00:00
|
|
|
|
|
|
|
DEF_TEST(Placeholder, r) {
|
|
|
|
SkRect cull = { 0,0, 10,20 };
|
|
|
|
|
|
|
|
// Each placeholder is unique.
|
|
|
|
sk_sp<SkPicture> p1 = SkPicture::MakePlaceholder(cull),
|
|
|
|
p2 = SkPicture::MakePlaceholder(cull);
|
|
|
|
REPORTER_ASSERT(r, p1->cullRect() == p2->cullRect());
|
|
|
|
REPORTER_ASSERT(r, p1->cullRect() == cull);
|
|
|
|
REPORTER_ASSERT(r, p1->uniqueID() != p2->uniqueID());
|
|
|
|
|
|
|
|
// Placeholders are never unrolled by SkCanvas (while other small pictures may be).
|
|
|
|
SkPictureRecorder recorder;
|
|
|
|
SkCanvas* canvas = recorder.beginRecording(cull);
|
|
|
|
canvas->drawPicture(p1);
|
|
|
|
canvas->drawPicture(p2);
|
|
|
|
sk_sp<SkPicture> pic = recorder.finishRecordingAsPicture();
|
|
|
|
REPORTER_ASSERT(r, pic->approximateOpCount() == 2);
|
|
|
|
}
|
2018-02-01 19:45:50 +00:00
|
|
|
|
|
|
|
DEF_TEST(Picture_empty_serial, reporter) {
|
|
|
|
SkPictureRecorder rec;
|
|
|
|
(void)rec.beginRecording(10, 10);
|
|
|
|
auto pic = rec.finishRecordingAsPicture();
|
|
|
|
REPORTER_ASSERT(reporter, pic);
|
|
|
|
|
|
|
|
auto data = pic->serialize();
|
|
|
|
REPORTER_ASSERT(reporter, data);
|
|
|
|
|
|
|
|
auto pic2 = SkPicture::MakeFromData(data->data(), data->size());
|
|
|
|
REPORTER_ASSERT(reporter, pic2);
|
|
|
|
}
|
|
|
|
|