Strip old backend recording down to essentials
Feature-wise, this removes: 1) BBH support; 2) peephole optimizations; 3) record-time text op specializations; 4) the guarantee that SkPaints are flattened. This deletes the optimizations GM, which only exists to test the peepholes of the old backend. SkRecord optimizations are unit tested, and if that ever fails we can think about adding another GM like this, but they're different enough we'd want to start from scratch anyway. We need to keep the code that plays back the specialized text ops around for a while for compatibility with existing .SKPs that have those ops recorded. BUG=skia: CQ_EXTRA_TRYBOTS=tryserver.skia:Canary-Chrome-Ubuntu13.10-Ninja-x86_64-ToT-Trybot R=robertphillips@google.com, reed@google.com, mtklein@google.com Author: mtklein@chromium.org Review URL: https://codereview.chromium.org/617953002
This commit is contained in:
parent
afbf2d6273
commit
46616af01b
@ -1,420 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "gm.h"
|
||||
#include "SkDebugCanvas.h"
|
||||
#include "SkPictureFlat.h"
|
||||
#include "SkPictureRecorder.h"
|
||||
|
||||
#define WARN(msg) \
|
||||
SkDebugf("%s:%d: %s\n", __FILE__, __LINE__, msg);
|
||||
|
||||
// Do the commands in 'input' match the supplied pattern? Note: this is a pretty
|
||||
// heavy-weight operation since we are drawing the picture into a debug canvas
|
||||
// to extract the commands.
|
||||
static bool check_pattern(SkPicture& input, const SkTDArray<DrawType> &pattern) {
|
||||
SkDebugCanvas debugCanvas(SkScalarCeilToInt(input.cullRect().width()),
|
||||
SkScalarCeilToInt(input.cullRect().height()));
|
||||
input.playback(&debugCanvas);
|
||||
|
||||
if (pattern.count() != debugCanvas.getSize()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < pattern.count(); ++i) {
|
||||
if (pattern[i] != debugCanvas.getDrawCommandAt(i)->getType()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// construct the pattern removed by the SkPictureRecord::remove_save_layer1
|
||||
// optimization, i.e.:
|
||||
// SAVE_LAYER
|
||||
// DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
|
||||
// RESTORE
|
||||
//
|
||||
// saveLayerHasPaint - control if the saveLayer has a paint (the optimization
|
||||
// takes a different path if this is false)
|
||||
// dbmr2rHasPaint - control if the dbmr2r has a paint (the optimization
|
||||
// takes a different path if this is false)
|
||||
// colorsMatch - control if the saveLayer and dbmr2r paint colors
|
||||
// match (the optimization will fail if they do not)
|
||||
static SkPicture* create_save_layer_opt_1(SkTDArray<DrawType>* preOptPattern,
|
||||
SkTDArray<DrawType>* postOptPattern,
|
||||
const SkBitmap& checkerBoard,
|
||||
bool saveLayerHasPaint,
|
||||
bool dbmr2rHasPaint,
|
||||
bool colorsMatch) {
|
||||
// Create the pattern that should trigger the optimization
|
||||
preOptPattern->setCount(5);
|
||||
(*preOptPattern)[0] = SAVE;
|
||||
(*preOptPattern)[1] = SAVE_LAYER;
|
||||
(*preOptPattern)[2] = DRAW_BITMAP_RECT_TO_RECT;
|
||||
(*preOptPattern)[3] = RESTORE;
|
||||
(*preOptPattern)[4] = RESTORE;
|
||||
|
||||
if (colorsMatch) {
|
||||
// Create the pattern that should appear after the optimization
|
||||
postOptPattern->setCount(5);
|
||||
(*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
|
||||
(*postOptPattern)[1] = SAVE;
|
||||
(*postOptPattern)[2] = DRAW_BITMAP_RECT_TO_RECT;
|
||||
(*postOptPattern)[3] = RESTORE;
|
||||
(*postOptPattern)[4] = RESTORE;
|
||||
} else {
|
||||
// Create the pattern that appears if the optimization doesn't fire
|
||||
postOptPattern->setCount(7);
|
||||
(*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
|
||||
(*postOptPattern)[1] = SAVE;
|
||||
(*postOptPattern)[2] = SAVE_LAYER;
|
||||
(*postOptPattern)[3] = DRAW_BITMAP_RECT_TO_RECT;
|
||||
(*postOptPattern)[4] = RESTORE;
|
||||
(*postOptPattern)[5] = RESTORE;
|
||||
(*postOptPattern)[6] = RESTORE;
|
||||
}
|
||||
|
||||
SkPictureRecorder recorder;
|
||||
|
||||
SkCanvas* canvas = recorder.DEPRECATED_beginRecording(100, 100, NULL, 0);
|
||||
// have to disable the optimizations while generating the picture
|
||||
recorder.internalOnly_EnableOpts(false);
|
||||
|
||||
SkPaint saveLayerPaint;
|
||||
saveLayerPaint.setColor(0xCC000000);
|
||||
|
||||
// saveLayer's 'bounds' parameter must be NULL for this optimization
|
||||
if (saveLayerHasPaint) {
|
||||
canvas->saveLayer(NULL, &saveLayerPaint);
|
||||
} else {
|
||||
canvas->saveLayer(NULL, NULL);
|
||||
}
|
||||
|
||||
SkRect rect = { 10, 10, 90, 90 };
|
||||
|
||||
// The dbmr2r's paint must be opaque
|
||||
SkPaint dbmr2rPaint;
|
||||
if (colorsMatch) {
|
||||
dbmr2rPaint.setColor(0xFF000000);
|
||||
} else {
|
||||
dbmr2rPaint.setColor(0xFFFF0000);
|
||||
}
|
||||
|
||||
if (dbmr2rHasPaint) {
|
||||
canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, &dbmr2rPaint);
|
||||
} else {
|
||||
canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, NULL);
|
||||
}
|
||||
canvas->restore();
|
||||
|
||||
return recorder.endRecording();
|
||||
}
|
||||
|
||||
// straight-ahead version that is seen in the skps
|
||||
static SkPicture* create_save_layer_opt_1_v1(SkTDArray<DrawType>* preOptPattern,
|
||||
SkTDArray<DrawType>* postOptPattern,
|
||||
const SkBitmap& checkerBoard) {
|
||||
return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
|
||||
true, // saveLayer has a paint
|
||||
true, // dbmr2r has a paint
|
||||
true); // and the colors match
|
||||
}
|
||||
|
||||
// alternate version that should still succeed
|
||||
static SkPicture* create_save_layer_opt_1_v2(SkTDArray<DrawType>* preOptPattern,
|
||||
SkTDArray<DrawType>* postOptPattern,
|
||||
const SkBitmap& checkerBoard) {
|
||||
return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
|
||||
false, // saveLayer doesn't have a paint!
|
||||
true, // dbmr2r has a paint
|
||||
true); // color matching not really applicable
|
||||
}
|
||||
|
||||
// alternate version that should still succeed
|
||||
static SkPicture* create_save_layer_opt_1_v3(SkTDArray<DrawType>* preOptPattern,
|
||||
SkTDArray<DrawType>* postOptPattern,
|
||||
const SkBitmap& checkerBoard) {
|
||||
return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
|
||||
true, // saveLayer has a paint
|
||||
false, // dbmr2r doesn't have a paint!
|
||||
true); // color matching not really applicable
|
||||
}
|
||||
|
||||
// version in which the optimization fails b.c. the colors don't match
|
||||
static SkPicture* create_save_layer_opt_1_v4(SkTDArray<DrawType>* preOptPattern,
|
||||
SkTDArray<DrawType>* postOptPattern,
|
||||
const SkBitmap& checkerBoard) {
|
||||
return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard,
|
||||
true, // saveLayer has a paint
|
||||
true, // dbmr2r has a paint
|
||||
false); // and the colors don't match!
|
||||
}
|
||||
|
||||
// construct the pattern removed by the SkPictureRecord::remove_save_layer2
|
||||
// optimization, i.e.:
|
||||
// SAVE_LAYER (with NULL == bounds)
|
||||
// SAVE
|
||||
// CLIP_RECT
|
||||
// DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
|
||||
// RESTORE
|
||||
// RESTORE
|
||||
//
|
||||
// saveLayerHasPaint - control if the saveLayer has a paint (the optimization
|
||||
// takes a different path if this is false)
|
||||
// dbmr2rHasPaint - control if the dbmr2r has a paint (the optimization
|
||||
// takes a different path if this is false)
|
||||
// colorsMatch - control if the saveLayer and dbmr2r paint colors
|
||||
// match (the optimization will fail if they do not)
|
||||
static SkPicture* create_save_layer_opt_2(SkTDArray<DrawType>* preOptPattern,
|
||||
SkTDArray<DrawType>* postOptPattern,
|
||||
const SkBitmap& checkerBoard,
|
||||
bool saveLayerHasPaint,
|
||||
bool dbmr2rHasPaint,
|
||||
bool colorsMatch) {
|
||||
// Create the pattern that should trigger the optimization
|
||||
preOptPattern->setCount(8);
|
||||
(*preOptPattern)[0] = SAVE;
|
||||
(*preOptPattern)[1] = SAVE_LAYER;
|
||||
(*preOptPattern)[2] = SAVE;
|
||||
(*preOptPattern)[3] = CLIP_RECT;
|
||||
(*preOptPattern)[4] = DRAW_BITMAP_RECT_TO_RECT;
|
||||
(*preOptPattern)[5] = RESTORE;
|
||||
(*preOptPattern)[6] = RESTORE;
|
||||
(*preOptPattern)[7] = RESTORE;
|
||||
|
||||
if (colorsMatch) {
|
||||
// Create the pattern that should appear after the optimization
|
||||
postOptPattern->setCount(8);
|
||||
(*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
|
||||
(*postOptPattern)[1] = SAVE;
|
||||
(*postOptPattern)[2] = SAVE;
|
||||
(*postOptPattern)[3] = CLIP_RECT;
|
||||
(*postOptPattern)[4] = DRAW_BITMAP_RECT_TO_RECT;
|
||||
(*postOptPattern)[5] = RESTORE;
|
||||
(*postOptPattern)[6] = RESTORE;
|
||||
(*postOptPattern)[7] = RESTORE;
|
||||
} else {
|
||||
// Create the pattern that appears if the optimization doesn't fire
|
||||
postOptPattern->setCount(10);
|
||||
(*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
|
||||
(*postOptPattern)[1] = SAVE;
|
||||
(*postOptPattern)[2] = SAVE_LAYER;
|
||||
(*postOptPattern)[3] = SAVE;
|
||||
(*postOptPattern)[4] = CLIP_RECT;
|
||||
(*postOptPattern)[5] = DRAW_BITMAP_RECT_TO_RECT;
|
||||
(*postOptPattern)[6] = RESTORE;
|
||||
(*postOptPattern)[7] = RESTORE;
|
||||
(*postOptPattern)[8] = RESTORE;
|
||||
(*postOptPattern)[9] = RESTORE;
|
||||
}
|
||||
|
||||
SkPictureRecorder recorder;
|
||||
|
||||
SkCanvas* canvas = recorder.DEPRECATED_beginRecording(100, 100, NULL, 0);
|
||||
// have to disable the optimizations while generating the picture
|
||||
recorder.internalOnly_EnableOpts(false);
|
||||
|
||||
SkPaint saveLayerPaint;
|
||||
saveLayerPaint.setColor(0xCC000000);
|
||||
|
||||
// saveLayer's 'bounds' parameter must be NULL for this optimization
|
||||
if (saveLayerHasPaint) {
|
||||
canvas->saveLayer(NULL, &saveLayerPaint);
|
||||
} else {
|
||||
canvas->saveLayer(NULL, NULL);
|
||||
}
|
||||
|
||||
canvas->save();
|
||||
|
||||
SkRect rect = { 10, 10, 90, 90 };
|
||||
canvas->clipRect(rect);
|
||||
|
||||
// The dbmr2r's paint must be opaque
|
||||
SkPaint dbmr2rPaint;
|
||||
if (colorsMatch) {
|
||||
dbmr2rPaint.setColor(0xFF000000);
|
||||
} else {
|
||||
dbmr2rPaint.setColor(0xFFFF0000);
|
||||
}
|
||||
|
||||
if (dbmr2rHasPaint) {
|
||||
canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, &dbmr2rPaint);
|
||||
} else {
|
||||
canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, NULL);
|
||||
}
|
||||
canvas->restore();
|
||||
canvas->restore();
|
||||
|
||||
return recorder.endRecording();
|
||||
}
|
||||
|
||||
// straight-ahead version that is seen in the skps
|
||||
static SkPicture* create_save_layer_opt_2_v1(SkTDArray<DrawType>* preOptPattern,
|
||||
SkTDArray<DrawType>* postOptPattern,
|
||||
const SkBitmap& checkerBoard) {
|
||||
return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
|
||||
true, // saveLayer has a paint
|
||||
true, // dbmr2r has a paint
|
||||
true); // and the colors match
|
||||
}
|
||||
|
||||
// alternate version that should still succeed
|
||||
static SkPicture* create_save_layer_opt_2_v2(SkTDArray<DrawType>* preOptPattern,
|
||||
SkTDArray<DrawType>* postOptPattern,
|
||||
const SkBitmap& checkerBoard) {
|
||||
return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
|
||||
false, // saveLayer doesn't have a paint!
|
||||
true, // dbmr2r has a paint
|
||||
true); // color matching not really applicable
|
||||
}
|
||||
|
||||
// alternate version that should still succeed
|
||||
static SkPicture* create_save_layer_opt_2_v3(SkTDArray<DrawType>* preOptPattern,
|
||||
SkTDArray<DrawType>* postOptPattern,
|
||||
const SkBitmap& checkerBoard) {
|
||||
return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
|
||||
true, // saveLayer has a paint
|
||||
false, // dbmr2r doesn't have a paint!
|
||||
true); // color matching not really applicable
|
||||
}
|
||||
|
||||
// version in which the optimization fails b.c. the colors don't match
|
||||
static SkPicture* create_save_layer_opt_2_v4(SkTDArray<DrawType>* preOptPattern,
|
||||
SkTDArray<DrawType>* postOptPattern,
|
||||
const SkBitmap& checkerBoard) {
|
||||
return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard,
|
||||
true, // saveLayer has a paint
|
||||
true, // dbmr2r has a paint
|
||||
false); // and the colors don't match!
|
||||
}
|
||||
|
||||
// As our .skp optimizations get folded into the captured skps our code will
|
||||
// no longer be locally exercised. This GM manually constructs the patterns
|
||||
// our optimizations will remove to test them. It acts as both a GM and a unit
|
||||
// test
|
||||
class OptimizationsGM : public skiagm::GM {
|
||||
public:
|
||||
OptimizationsGM() {
|
||||
this->makeCheckerboard();
|
||||
}
|
||||
|
||||
static const int kWidth = 800;
|
||||
static const int kHeight = 800;
|
||||
|
||||
protected:
|
||||
uint32_t onGetFlags() const SK_OVERRIDE {
|
||||
// One optimization changes the color drawn slightly in a 565 target.
|
||||
// We've decided it's innocuous, so we disable this GM when targeting 565.
|
||||
// Revisit this if we get finer-grained control: it'd be nice to keep drawing directly.
|
||||
// For more, see skia:1994.
|
||||
return skiagm::GM::kSkip565_Flag;
|
||||
}
|
||||
|
||||
SkString onShortName() {
|
||||
return SkString("optimizations");
|
||||
}
|
||||
|
||||
SkISize onISize() { return SkISize::Make(kWidth, kHeight); }
|
||||
|
||||
typedef SkPicture* (*PFCreateOpt)(SkTDArray<DrawType> *preOptPattern,
|
||||
SkTDArray<DrawType> *postOptPattern,
|
||||
const SkBitmap& checkerBoard);
|
||||
|
||||
virtual void onDraw(SkCanvas* canvas) {
|
||||
|
||||
PFCreateOpt gOpts[] = {
|
||||
create_save_layer_opt_1_v1,
|
||||
create_save_layer_opt_1_v2,
|
||||
create_save_layer_opt_1_v3,
|
||||
create_save_layer_opt_1_v4,
|
||||
create_save_layer_opt_2_v1,
|
||||
create_save_layer_opt_2_v2,
|
||||
create_save_layer_opt_2_v3,
|
||||
create_save_layer_opt_2_v4,
|
||||
};
|
||||
|
||||
SkTDArray<DrawType> prePattern, postPattern;
|
||||
SkScalar xPos = 0, yPos = 0;
|
||||
|
||||
for (size_t i = 0; i < SK_ARRAY_COUNT(gOpts); ++i) {
|
||||
SkAutoTUnref<SkPicture> pre((*gOpts[i])(&prePattern, &postPattern, fCheckerboard));
|
||||
|
||||
if (!(check_pattern(*pre, prePattern))) {
|
||||
WARN("Pre optimization pattern mismatch");
|
||||
SkASSERT(0);
|
||||
}
|
||||
|
||||
canvas->save();
|
||||
canvas->translate(xPos, yPos);
|
||||
pre->playback(canvas);
|
||||
xPos += pre->cullRect().width();
|
||||
canvas->restore();
|
||||
|
||||
// re-render the 'pre' picture and thus 'apply' the optimization
|
||||
SkPictureRecorder recorder;
|
||||
|
||||
SkCanvas* recordCanvas =
|
||||
recorder.DEPRECATED_beginRecording(pre->cullRect().width(),
|
||||
pre->cullRect().height(),
|
||||
NULL, 0);
|
||||
|
||||
pre->playback(recordCanvas);
|
||||
|
||||
SkAutoTUnref<SkPicture> post(recorder.endRecording());
|
||||
|
||||
if (!(check_pattern(*post, postPattern))) {
|
||||
WARN("Post optimization pattern mismatch");
|
||||
SkASSERT(0);
|
||||
}
|
||||
|
||||
canvas->save();
|
||||
canvas->translate(xPos, yPos);
|
||||
post->playback(canvas);
|
||||
xPos += post->cullRect().width();
|
||||
canvas->restore();
|
||||
|
||||
if (xPos >= kWidth) {
|
||||
// start a new line
|
||||
xPos = 0;
|
||||
yPos += post->cullRect().height();
|
||||
}
|
||||
|
||||
// TODO: we could also render the pre and post pictures to bitmaps
|
||||
// and manually compare them in this method
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void makeCheckerboard() {
|
||||
static const unsigned int kCheckerboardWidth = 16;
|
||||
static const unsigned int kCheckerboardHeight = 16;
|
||||
|
||||
fCheckerboard.allocN32Pixels(kCheckerboardWidth, kCheckerboardHeight);
|
||||
for (unsigned int y = 0; y < kCheckerboardHeight; y += 2) {
|
||||
SkPMColor* scanline = fCheckerboard.getAddr32(0, y);
|
||||
for (unsigned int x = 0; x < kCheckerboardWidth; x += 2) {
|
||||
*scanline++ = 0xFFFFFFFF;
|
||||
*scanline++ = 0xFF000000;
|
||||
}
|
||||
scanline = fCheckerboard.getAddr32(0, y + 1);
|
||||
for (unsigned int x = 0; x < kCheckerboardWidth; x += 2) {
|
||||
*scanline++ = 0xFF000000;
|
||||
*scanline++ = 0xFFFFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SkBitmap fCheckerboard;
|
||||
|
||||
typedef skiagm::GM INHERITED;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DEF_GM( return new OptimizationsGM; )
|
@ -14,10 +14,6 @@
|
||||
'<(skia_src_path)/core/SkAntiRun.h',
|
||||
'<(skia_src_path)/core/SkBBHFactory.cpp',
|
||||
'<(skia_src_path)/core/SkBBoxHierarchy.h',
|
||||
'<(skia_src_path)/core/SkBBoxRecord.cpp',
|
||||
'<(skia_src_path)/core/SkBBoxRecord.h',
|
||||
'<(skia_src_path)/core/SkBBoxHierarchyRecord.cpp',
|
||||
'<(skia_src_path)/core/SkBBoxHierarchyRecord.h',
|
||||
'<(skia_src_path)/core/SkBitmap.cpp',
|
||||
'<(skia_src_path)/core/SkBitmapCache.cpp',
|
||||
'<(skia_src_path)/core/SkBitmapDevice.cpp',
|
||||
@ -141,8 +137,6 @@
|
||||
'<(skia_src_path)/core/SkPictureRecorder.cpp',
|
||||
'<(skia_src_path)/core/SkPictureShader.cpp',
|
||||
'<(skia_src_path)/core/SkPictureShader.h',
|
||||
'<(skia_src_path)/core/SkPictureStateTree.cpp',
|
||||
'<(skia_src_path)/core/SkPictureStateTree.h',
|
||||
'<(skia_src_path)/core/SkPixelRef.cpp',
|
||||
'<(skia_src_path)/core/SkPoint.cpp',
|
||||
'<(skia_src_path)/core/SkProcSpriteBlitter.cpp',
|
||||
|
@ -127,7 +127,6 @@
|
||||
'../gm/ninepatchstretch.cpp',
|
||||
'../gm/nonclosedpaths.cpp',
|
||||
'../gm/offsetimagefilter.cpp',
|
||||
'../gm/optimizations.cpp',
|
||||
'../gm/ovals.cpp',
|
||||
'../gm/patch.cpp',
|
||||
'../gm/patchgrid.cpp',
|
||||
|
@ -155,7 +155,6 @@
|
||||
'../tests/PathTest.cpp',
|
||||
'../tests/PathUtilsTest.cpp',
|
||||
'../tests/PictureShaderTest.cpp',
|
||||
'../tests/PictureStateTreeTest.cpp',
|
||||
'../tests/PictureTest.cpp',
|
||||
'../tests/PixelRefTest.cpp',
|
||||
'../tests/PointTest.cpp',
|
||||
|
@ -253,8 +253,7 @@ private:
|
||||
|
||||
mutable uint32_t fUniqueID;
|
||||
|
||||
// TODO: make SkPictureData const when clone method goes away
|
||||
SkAutoTDelete<SkPictureData> fData;
|
||||
SkAutoTDelete<const SkPictureData> fData;
|
||||
const SkScalar fCullWidth;
|
||||
const SkScalar fCullHeight;
|
||||
mutable SkAutoTUnref<const AccelData> fAccelData;
|
||||
@ -270,30 +269,14 @@ private:
|
||||
|
||||
SkPicture(SkScalar width, SkScalar height, const SkPictureRecord& record, bool deepCopyOps);
|
||||
|
||||
// An OperationList encapsulates a set of operation offsets into the picture byte
|
||||
// stream along with the CTMs needed for those operation.
|
||||
class OperationList : ::SkNoncopyable {
|
||||
public:
|
||||
// The following three entry points should only be accessed if
|
||||
// 'valid' returns true.
|
||||
int numOps() const { return fOps.count(); }
|
||||
// The offset in the picture of the operation to execute.
|
||||
uint32_t offset(int index) const;
|
||||
// The CTM that must be installed for the operation to behave correctly
|
||||
const SkMatrix& matrix(int index) const;
|
||||
|
||||
SkTDArray<void*> fOps;
|
||||
};
|
||||
|
||||
void createHeader(SkPictInfo* info) const;
|
||||
static bool IsValidPictInfo(const SkPictInfo& info);
|
||||
|
||||
friend class SkPictureData; // to access OperationList
|
||||
friend class SkPictureRecorder; // just for SkPicture-based constructor
|
||||
friend class SkGpuDevice; // for fData access
|
||||
friend class GrLayerHoister; // access to fRecord
|
||||
friend class CollectLayers; // access to fRecord
|
||||
friend class SkPicturePlayback; // to get fData & OperationList
|
||||
friend class SkPicturePlayback; // to get fData
|
||||
friend void GrRecordReplaceDraw(const SkPicture* picture,
|
||||
SkCanvas* canvas,
|
||||
const GrReplacements* replacements,
|
||||
|
@ -72,14 +72,6 @@ public:
|
||||
*/
|
||||
SkPicture* endRecording();
|
||||
|
||||
/** Enable/disable all the picture recording optimizations (i.e.,
|
||||
those in SkPictureRecord). It is mainly intended for testing the
|
||||
existing optimizations (i.e., to actually have the pattern
|
||||
appear in an .skp we have to disable the optimization). Call right
|
||||
after 'beginRecording'.
|
||||
*/
|
||||
void internalOnly_EnableOpts(bool enableOpts);
|
||||
|
||||
private:
|
||||
void reset();
|
||||
|
||||
|
@ -1,132 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkBBoxHierarchyRecord.h"
|
||||
#include "SkPictureStateTree.h"
|
||||
|
||||
SkBBoxHierarchyRecord::SkBBoxHierarchyRecord(const SkISize& size,
|
||||
uint32_t recordFlags,
|
||||
SkBBoxHierarchy* h)
|
||||
: INHERITED(size, recordFlags) {
|
||||
fStateTree = SkNEW(SkPictureStateTree);
|
||||
fBoundingHierarchy = h;
|
||||
fBoundingHierarchy->ref();
|
||||
fBoundingHierarchy->setClient(this);
|
||||
}
|
||||
|
||||
void SkBBoxHierarchyRecord::handleBBox(const SkRect& bounds) {
|
||||
SkPictureStateTree::Draw* draw = fStateTree->appendDraw(this->writeStream().bytesWritten());
|
||||
fBoundingHierarchy->insert(draw, bounds, true);
|
||||
}
|
||||
|
||||
void SkBBoxHierarchyRecord::willSave() {
|
||||
fStateTree->appendSave();
|
||||
this->INHERITED::willSave();
|
||||
}
|
||||
|
||||
SkCanvas::SaveLayerStrategy SkBBoxHierarchyRecord::willSaveLayer(const SkRect* bounds,
|
||||
const SkPaint* paint,
|
||||
SaveFlags flags) {
|
||||
// For now, assume all filters affect transparent black.
|
||||
// FIXME: This could be made less conservative as an optimization.
|
||||
bool paintAffectsTransparentBlack = paint &&
|
||||
((paint->getImageFilter()) ||
|
||||
(paint->getColorFilter()));
|
||||
bool needToHandleBBox = paintAffectsTransparentBlack;
|
||||
if (!needToHandleBBox && paint) {
|
||||
// Unusual Xfermodes require us to process a saved layer
|
||||
// even with operations outisde the clip.
|
||||
// For example, DstIn is used by masking layers.
|
||||
// https://code.google.com/p/skia/issues/detail?id=1291
|
||||
SkXfermode* xfermode = paint->getXfermode();
|
||||
SkXfermode::Mode mode;
|
||||
// SrcOver is the common case with a NULL xfermode, so we should
|
||||
// make that the fast path and bypass the mode extraction and test.
|
||||
if (xfermode && xfermode->asMode(&mode)) {
|
||||
switch (mode) {
|
||||
case SkXfermode::kClear_Mode:
|
||||
case SkXfermode::kSrc_Mode:
|
||||
case SkXfermode::kSrcIn_Mode:
|
||||
case SkXfermode::kDstIn_Mode:
|
||||
case SkXfermode::kSrcOut_Mode:
|
||||
case SkXfermode::kDstATop_Mode:
|
||||
case SkXfermode::kModulate_Mode:
|
||||
needToHandleBBox = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SkRect drawBounds;
|
||||
if (needToHandleBBox) {
|
||||
SkIRect deviceBounds;
|
||||
this->getClipDeviceBounds(&deviceBounds);
|
||||
drawBounds.set(deviceBounds);
|
||||
}
|
||||
fStateTree->appendSaveLayer(this->writeStream().bytesWritten());
|
||||
SkCanvas::SaveLayerStrategy strategy = this->INHERITED::willSaveLayer(bounds, paint, flags);
|
||||
if (needToHandleBBox) {
|
||||
this->handleBBox(drawBounds);
|
||||
this->addNoOp();
|
||||
}
|
||||
return strategy;
|
||||
}
|
||||
|
||||
void SkBBoxHierarchyRecord::willRestore() {
|
||||
fStateTree->appendRestore();
|
||||
this->INHERITED::willRestore();
|
||||
}
|
||||
|
||||
void SkBBoxHierarchyRecord::didConcat(const SkMatrix& matrix) {
|
||||
fStateTree->appendTransform(getTotalMatrix());
|
||||
INHERITED::didConcat(matrix);
|
||||
}
|
||||
|
||||
void SkBBoxHierarchyRecord::didSetMatrix(const SkMatrix& matrix) {
|
||||
fStateTree->appendTransform(getTotalMatrix());
|
||||
INHERITED::didSetMatrix(matrix);
|
||||
}
|
||||
|
||||
void SkBBoxHierarchyRecord::onClipRect(const SkRect& rect,
|
||||
SkRegion::Op op,
|
||||
ClipEdgeStyle edgeStyle) {
|
||||
fStateTree->appendClip(this->writeStream().bytesWritten());
|
||||
this->INHERITED::onClipRect(rect, op, edgeStyle);
|
||||
}
|
||||
|
||||
void SkBBoxHierarchyRecord::onClipRegion(const SkRegion& region,
|
||||
SkRegion::Op op) {
|
||||
fStateTree->appendClip(this->writeStream().bytesWritten());
|
||||
this->INHERITED::onClipRegion(region, op);
|
||||
}
|
||||
|
||||
void SkBBoxHierarchyRecord::onClipPath(const SkPath& path,
|
||||
SkRegion::Op op,
|
||||
ClipEdgeStyle edgeStyle) {
|
||||
fStateTree->appendClip(this->writeStream().bytesWritten());
|
||||
this->INHERITED::onClipPath(path, op, edgeStyle);
|
||||
}
|
||||
|
||||
void SkBBoxHierarchyRecord::onClipRRect(const SkRRect& rrect,
|
||||
SkRegion::Op op,
|
||||
ClipEdgeStyle edgeStyle) {
|
||||
fStateTree->appendClip(this->writeStream().bytesWritten());
|
||||
this->INHERITED::onClipRRect(rrect, op, edgeStyle);
|
||||
}
|
||||
|
||||
bool SkBBoxHierarchyRecord::shouldRewind(void* data) {
|
||||
// SkBBoxHierarchy::rewindInserts is called by SkPicture after the
|
||||
// SkPicture has rewound its command stream. To match that rewind in the
|
||||
// BBH, we rewind all draws that reference commands that were recorded
|
||||
// past the point to which the SkPicture has rewound, which is given by
|
||||
// writeStream().bytesWritten().
|
||||
SkPictureStateTree::Draw* draw = static_cast<SkPictureStateTree::Draw*>(data);
|
||||
return draw->fOffset >= writeStream().bytesWritten();
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkRTreeCanvas_DEFINED
|
||||
#define SkRTreeCanvas_DEFINED
|
||||
|
||||
#include "SkBBoxHierarchy.h"
|
||||
#include "SkBBoxRecord.h"
|
||||
|
||||
/**
|
||||
* This records bounding box information into an SkBBoxHierarchy, and clip/transform information
|
||||
* into an SkPictureStateTree to allow for efficient culling and correct playback of draws.
|
||||
*/
|
||||
class SkBBoxHierarchyRecord : public SkBBoxRecord, public SkBBoxHierarchyClient {
|
||||
public:
|
||||
/** This will take a ref of h */
|
||||
SkBBoxHierarchyRecord(const SkISize& size, uint32_t recordFlags, SkBBoxHierarchy* h);
|
||||
virtual ~SkBBoxHierarchyRecord() { };
|
||||
|
||||
virtual void handleBBox(const SkRect& bounds) SK_OVERRIDE;
|
||||
|
||||
// Implementation of the SkBBoxHierarchyClient interface
|
||||
virtual bool shouldRewind(void* data) SK_OVERRIDE;
|
||||
|
||||
protected:
|
||||
virtual void willSave() SK_OVERRIDE;
|
||||
virtual SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) SK_OVERRIDE;
|
||||
virtual void willRestore() SK_OVERRIDE;
|
||||
|
||||
virtual void didConcat(const SkMatrix&) SK_OVERRIDE;
|
||||
virtual void didSetMatrix(const SkMatrix&) SK_OVERRIDE;
|
||||
|
||||
virtual void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
|
||||
virtual void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
|
||||
virtual void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
|
||||
virtual void onClipRegion(const SkRegion&, SkRegion::Op) SK_OVERRIDE;
|
||||
|
||||
private:
|
||||
typedef SkBBoxRecord INHERITED;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,380 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkBBoxRecord.h"
|
||||
#include "SkPatchUtils.h"
|
||||
|
||||
#include "SkTextBlob.h"
|
||||
|
||||
SkBBoxRecord::~SkBBoxRecord() {
|
||||
fSaveStack.deleteAll();
|
||||
}
|
||||
|
||||
void SkBBoxRecord::drawOval(const SkRect& rect, const SkPaint& paint) {
|
||||
if (this->transformBounds(rect, &paint)) {
|
||||
INHERITED::drawOval(rect, paint);
|
||||
}
|
||||
}
|
||||
|
||||
void SkBBoxRecord::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
|
||||
if (this->transformBounds(rrect.rect(), &paint)) {
|
||||
INHERITED::drawRRect(rrect, paint);
|
||||
}
|
||||
}
|
||||
|
||||
void SkBBoxRecord::drawRect(const SkRect& rect, const SkPaint& paint) {
|
||||
if (this->transformBounds(rect, &paint)) {
|
||||
INHERITED::drawRect(rect, paint);
|
||||
}
|
||||
}
|
||||
|
||||
void SkBBoxRecord::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
|
||||
const SkPaint& paint) {
|
||||
if (this->transformBounds(outer.rect(), &paint)) {
|
||||
this->INHERITED::onDrawDRRect(outer, inner, paint);
|
||||
}
|
||||
}
|
||||
|
||||
void SkBBoxRecord::drawPath(const SkPath& path, const SkPaint& paint) {
|
||||
if (path.isInverseFillType()) {
|
||||
// If path is inverse filled, use the current clip bounds as the
|
||||
// path's device-space bounding box.
|
||||
SkIRect clipBounds;
|
||||
if (this->getClipDeviceBounds(&clipBounds)) {
|
||||
this->handleBBox(SkRect::Make(clipBounds));
|
||||
INHERITED::drawPath(path, paint);
|
||||
}
|
||||
} else if (this->transformBounds(path.getBounds(), &paint)) {
|
||||
INHERITED::drawPath(path, paint);
|
||||
}
|
||||
}
|
||||
|
||||
void SkBBoxRecord::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
|
||||
const SkPaint& paint) {
|
||||
SkRect bbox;
|
||||
bbox.set(pts, SkToInt(count));
|
||||
// Small min width value, just to ensure hairline point bounding boxes aren't empty.
|
||||
// Even though we know hairline primitives are drawn one pixel wide, we do not use a
|
||||
// minimum of 1 because the playback scale factor is unknown at record time. Later
|
||||
// outsets will take care of adding additional padding for antialiasing and rounding out
|
||||
// to integer device coordinates, guaranteeing that the rasterized pixels will be included
|
||||
// in the computed bounds.
|
||||
// Note: The device coordinate outset in SkBBoxHierarchyRecord::handleBBox is currently
|
||||
// done in the recording coordinate space, which is wrong.
|
||||
// http://code.google.com/p/skia/issues/detail?id=1021
|
||||
static const SkScalar kMinWidth = 0.01f;
|
||||
SkScalar halfStrokeWidth = SkMaxScalar(paint.getStrokeWidth(), kMinWidth) / 2;
|
||||
bbox.outset(halfStrokeWidth, halfStrokeWidth);
|
||||
if (this->transformBounds(bbox, &paint)) {
|
||||
INHERITED::drawPoints(mode, count, pts, paint);
|
||||
}
|
||||
}
|
||||
|
||||
void SkBBoxRecord::drawPaint(const SkPaint& paint) {
|
||||
SkRect bbox;
|
||||
if (this->getClipBounds(&bbox)) {
|
||||
if (this->transformBounds(bbox, &paint)) {
|
||||
INHERITED::drawPaint(paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SkBBoxRecord::clear(SkColor color) {
|
||||
SkISize size = this->getDeviceSize();
|
||||
SkRect bbox = {0, 0, SkIntToScalar(size.width()), SkIntToScalar(size.height())};
|
||||
this->handleBBox(bbox);
|
||||
INHERITED::clear(color);
|
||||
}
|
||||
|
||||
void SkBBoxRecord::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
|
||||
const SkPaint& paint) {
|
||||
SkRect bbox;
|
||||
paint.measureText(text, byteLength, &bbox);
|
||||
SkPaint::FontMetrics metrics;
|
||||
paint.getFontMetrics(&metrics);
|
||||
|
||||
// Vertical and aligned text need to be offset
|
||||
if (paint.isVerticalText()) {
|
||||
SkScalar h = bbox.fBottom - bbox.fTop;
|
||||
if (paint.getTextAlign() == SkPaint::kCenter_Align) {
|
||||
bbox.fTop -= h / 2;
|
||||
bbox.fBottom -= h / 2;
|
||||
}
|
||||
// Pad top and bottom with max extents from FontMetrics
|
||||
bbox.fBottom += metrics.fBottom;
|
||||
bbox.fTop += metrics.fTop;
|
||||
} else {
|
||||
SkScalar w = bbox.fRight - bbox.fLeft;
|
||||
if (paint.getTextAlign() == SkPaint::kCenter_Align) {
|
||||
bbox.fLeft -= w / 2;
|
||||
bbox.fRight -= w / 2;
|
||||
} else if (paint.getTextAlign() == SkPaint::kRight_Align) {
|
||||
bbox.fLeft -= w;
|
||||
bbox.fRight -= w;
|
||||
}
|
||||
// Set vertical bounds to max extents from font metrics
|
||||
bbox.fTop = metrics.fTop;
|
||||
bbox.fBottom = metrics.fBottom;
|
||||
}
|
||||
|
||||
// Pad horizontal bounds on each side by half of max vertical extents (this is sort of
|
||||
// arbitrary, but seems to produce reasonable results, if there were a way of getting max
|
||||
// glyph X-extents to pad by, that may be better here, but FontMetrics fXMin and fXMax seem
|
||||
// incorrect on most platforms (too small in Linux, never even set in Windows).
|
||||
SkScalar pad = (metrics.fBottom - metrics.fTop) / 2;
|
||||
bbox.fLeft -= pad;
|
||||
bbox.fRight += pad;
|
||||
|
||||
bbox.fLeft += x;
|
||||
bbox.fRight += x;
|
||||
bbox.fTop += y;
|
||||
bbox.fBottom += y;
|
||||
if (this->transformBounds(bbox, &paint)) {
|
||||
INHERITED::onDrawText(text, byteLength, x, y, paint);
|
||||
}
|
||||
}
|
||||
|
||||
void SkBBoxRecord::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
|
||||
const SkPaint* paint) {
|
||||
SkRect bbox = {left, top, left + bitmap.width(), top + bitmap.height()};
|
||||
if (this->transformBounds(bbox, paint)) {
|
||||
INHERITED::drawBitmap(bitmap, left, top, paint);
|
||||
}
|
||||
}
|
||||
|
||||
void SkBBoxRecord::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
|
||||
const SkRect& dst, const SkPaint* paint,
|
||||
DrawBitmapRectFlags flags) {
|
||||
if (this->transformBounds(dst, paint)) {
|
||||
INHERITED::drawBitmapRectToRect(bitmap, src, dst, paint, flags);
|
||||
}
|
||||
}
|
||||
|
||||
void SkBBoxRecord::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& mat,
|
||||
const SkPaint* paint) {
|
||||
SkMatrix m = mat;
|
||||
SkRect bbox = {0, 0, SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height())};
|
||||
m.mapRect(&bbox);
|
||||
if (this->transformBounds(bbox, paint)) {
|
||||
INHERITED::drawBitmapMatrix(bitmap, mat, paint);
|
||||
}
|
||||
}
|
||||
|
||||
void SkBBoxRecord::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
|
||||
const SkRect& dst, const SkPaint* paint) {
|
||||
if (this->transformBounds(dst, paint)) {
|
||||
INHERITED::drawBitmapNine(bitmap, center, dst, paint);
|
||||
}
|
||||
}
|
||||
|
||||
// Hack to work-around https://code.google.com/p/chromium/issues/detail?id=373785
|
||||
// This logic assums that 'pad' is enough to add to the left and right to account for
|
||||
// big glyphs. For the font in question (a logo font) the glyphs is much wider than just
|
||||
// the pointsize (approx 3x wider).
|
||||
// As a temp work-around, we scale-up pad.
|
||||
// A more correct fix might be to add fontmetrics.fMaxX, but we don't have that value in hand
|
||||
// at the moment, and (possibly) the value in the font may not be accurate (but who knows).
|
||||
//
|
||||
static SkScalar hack_373785_amend_pad(SkScalar pad) {
|
||||
return pad * 4;
|
||||
}
|
||||
|
||||
void SkBBoxRecord::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
|
||||
const SkPaint& paint) {
|
||||
SkRect bbox;
|
||||
bbox.set(pos, paint.countText(text, byteLength));
|
||||
SkPaint::FontMetrics metrics;
|
||||
paint.getFontMetrics(&metrics);
|
||||
bbox.fTop += metrics.fTop;
|
||||
bbox.fBottom += metrics.fBottom;
|
||||
|
||||
// pad on left and right by half of max vertical glyph extents
|
||||
SkScalar pad = (metrics.fTop - metrics.fBottom) / 2;
|
||||
pad = hack_373785_amend_pad(pad);
|
||||
bbox.fLeft += pad;
|
||||
bbox.fRight -= pad;
|
||||
|
||||
if (this->transformBounds(bbox, &paint)) {
|
||||
INHERITED::onDrawPosText(text, byteLength, pos, paint);
|
||||
}
|
||||
}
|
||||
|
||||
void SkBBoxRecord::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
|
||||
SkScalar constY, const SkPaint& paint) {
|
||||
size_t numChars = paint.countText(text, byteLength);
|
||||
if (numChars == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const SkFlatData* flatPaintData = this->getFlatPaintData(paint);
|
||||
WriteTopBot(paint, *flatPaintData);
|
||||
|
||||
SkScalar top = flatPaintData->topBot()[0];
|
||||
SkScalar bottom = flatPaintData->topBot()[1];
|
||||
SkScalar pad = top - bottom;
|
||||
|
||||
SkRect bbox;
|
||||
bbox.fLeft = SK_ScalarMax;
|
||||
bbox.fRight = SK_ScalarMin;
|
||||
|
||||
for (size_t i = 0; i < numChars; ++i) {
|
||||
if (xpos[i] < bbox.fLeft) {
|
||||
bbox.fLeft = xpos[i];
|
||||
}
|
||||
if (xpos[i] > bbox.fRight) {
|
||||
bbox.fRight = xpos[i];
|
||||
}
|
||||
}
|
||||
|
||||
// pad horizontally by max glyph height
|
||||
pad = hack_373785_amend_pad(pad);
|
||||
bbox.fLeft += pad;
|
||||
bbox.fRight -= pad;
|
||||
|
||||
bbox.fTop = top + constY;
|
||||
bbox.fBottom = bottom + constY;
|
||||
|
||||
if (!this->transformBounds(bbox, &paint)) {
|
||||
return;
|
||||
}
|
||||
// This is the equivalent of calling:
|
||||
// INHERITED::drawPosTextH(text, byteLength, xpos, constY, paint);
|
||||
// but we filled our flat paint beforehand so that we could get font metrics.
|
||||
drawPosTextHImpl(text, byteLength, xpos, constY, paint, flatPaintData);
|
||||
}
|
||||
|
||||
void SkBBoxRecord::drawSprite(const SkBitmap& bitmap, int left, int top,
|
||||
const SkPaint* paint) {
|
||||
SkRect bbox;
|
||||
bbox.set(SkIRect::MakeXYWH(left, top, bitmap.width(), bitmap.height()));
|
||||
this->handleBBox(bbox); // directly call handleBBox, matrix is ignored
|
||||
INHERITED::drawSprite(bitmap, left, top, paint);
|
||||
}
|
||||
|
||||
void SkBBoxRecord::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
|
||||
const SkMatrix* matrix, const SkPaint& paint) {
|
||||
SkRect bbox = path.getBounds();
|
||||
SkPaint::FontMetrics metrics;
|
||||
paint.getFontMetrics(&metrics);
|
||||
|
||||
// pad out all sides by the max glyph height above baseline
|
||||
SkScalar pad = metrics.fTop;
|
||||
bbox.fLeft += pad;
|
||||
bbox.fRight -= pad;
|
||||
bbox.fTop += pad;
|
||||
bbox.fBottom -= pad;
|
||||
|
||||
if (this->transformBounds(bbox, &paint)) {
|
||||
INHERITED::onDrawTextOnPath(text, byteLength, path, matrix, paint);
|
||||
}
|
||||
}
|
||||
|
||||
void SkBBoxRecord::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
|
||||
const SkPaint& paint) {
|
||||
SkRect bbox = blob->bounds();
|
||||
bbox.offset(x, y);
|
||||
// FIXME: implement implicit blob bounds!
|
||||
if (bbox.isEmpty()) {
|
||||
this->getClipBounds(&bbox);
|
||||
}
|
||||
|
||||
if (this->transformBounds(bbox, &paint)) {
|
||||
INHERITED::onDrawTextBlob(blob, x, y, paint);
|
||||
}
|
||||
}
|
||||
|
||||
void SkBBoxRecord::drawVertices(VertexMode mode, int vertexCount,
|
||||
const SkPoint vertices[], const SkPoint texs[],
|
||||
const SkColor colors[], SkXfermode* xfer,
|
||||
const uint16_t indices[], int indexCount,
|
||||
const SkPaint& paint) {
|
||||
SkRect bbox;
|
||||
bbox.set(vertices, vertexCount);
|
||||
if (this->transformBounds(bbox, &paint)) {
|
||||
INHERITED::drawVertices(mode, vertexCount, vertices, texs,
|
||||
colors, xfer, indices, indexCount, paint);
|
||||
}
|
||||
}
|
||||
|
||||
void SkBBoxRecord::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
|
||||
const SkPoint texCoords[4], SkXfermode* xmode,
|
||||
const SkPaint& paint) {
|
||||
SkRect bbox;
|
||||
bbox.set(cubics, SkPatchUtils::kNumCtrlPts);
|
||||
if (this->transformBounds(bbox, &paint)) {
|
||||
INHERITED::onDrawPatch(cubics, colors, texCoords, xmode, paint);
|
||||
}
|
||||
}
|
||||
|
||||
void SkBBoxRecord::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
|
||||
const SkPaint* paint) {
|
||||
SkRect bounds = picture->cullRect();
|
||||
// todo: wonder if we should allow passing an optional matrix to transformBounds so we don't
|
||||
// end up transforming the rect twice.
|
||||
if (matrix) {
|
||||
matrix->mapRect(&bounds);
|
||||
}
|
||||
if (this->transformBounds(bounds, paint)) {
|
||||
this->INHERITED::onDrawPicture(picture, matrix, paint);
|
||||
}
|
||||
}
|
||||
|
||||
void SkBBoxRecord::willSave() {
|
||||
fSaveStack.push(NULL);
|
||||
this->INHERITED::willSave();
|
||||
}
|
||||
|
||||
SkCanvas::SaveLayerStrategy SkBBoxRecord::willSaveLayer(const SkRect* bounds,
|
||||
const SkPaint* paint,
|
||||
SaveFlags flags) {
|
||||
// Image filters can affect the effective bounds of primitives drawn inside saveLayer().
|
||||
// Copy the paint so we can compute the modified bounds in transformBounds().
|
||||
fSaveStack.push(paint && paint->getImageFilter() ? new SkPaint(*paint) : NULL);
|
||||
return this->INHERITED::willSaveLayer(bounds, paint, flags);
|
||||
}
|
||||
|
||||
void SkBBoxRecord::willRestore() {
|
||||
delete fSaveStack.top();
|
||||
fSaveStack.pop();
|
||||
this->INHERITED::willRestore();
|
||||
}
|
||||
|
||||
bool SkBBoxRecord::transformBounds(const SkRect& bounds, const SkPaint* paint) {
|
||||
SkRect outBounds = bounds;
|
||||
outBounds.sort();
|
||||
|
||||
if (paint) {
|
||||
// account for stroking, path effects, shadows, etc
|
||||
if (paint->canComputeFastBounds()) {
|
||||
SkRect temp;
|
||||
outBounds = paint->computeFastBounds(outBounds, &temp);
|
||||
} else {
|
||||
// set bounds to current clip
|
||||
if (!this->getClipBounds(&outBounds)) {
|
||||
// current clip is empty
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = fSaveStack.count() - 1; i >= 0; --i) {
|
||||
const SkPaint* paint = fSaveStack.getAt(i);
|
||||
if (paint && paint->canComputeFastBounds()) {
|
||||
SkRect temp;
|
||||
outBounds = paint->computeFastBounds(outBounds, &temp);
|
||||
}
|
||||
}
|
||||
|
||||
if (!outBounds.isEmpty() && !this->quickReject(outBounds)) {
|
||||
this->getTotalMatrix().mapRect(&outBounds);
|
||||
this->handleBBox(outBounds);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkBBoxRecord_DEFINED
|
||||
#define SkBBoxRecord_DEFINED
|
||||
|
||||
#include "SkPictureRecord.h"
|
||||
#include "SkTDArray.h"
|
||||
|
||||
/**
|
||||
* This is an abstract SkPictureRecord subclass that intercepts draw calls and computes an
|
||||
* axis-aligned bounding box for each draw that it sees, subclasses implement handleBBox()
|
||||
* which will be called every time we get a new bounding box.
|
||||
*/
|
||||
class SkBBoxRecord : public SkPictureRecord {
|
||||
public:
|
||||
|
||||
SkBBoxRecord(const SkISize& size, uint32_t recordFlags)
|
||||
: INHERITED(size, recordFlags) {
|
||||
}
|
||||
virtual ~SkBBoxRecord();
|
||||
|
||||
/**
|
||||
* This is called each time we get a bounding box, it will be axis-aligned,
|
||||
* in device coordinates, and expanded to include stroking, shadows, etc.
|
||||
*/
|
||||
virtual void handleBBox(const SkRect& bbox) = 0;
|
||||
|
||||
virtual void drawOval(const SkRect& rect, const SkPaint& paint) SK_OVERRIDE;
|
||||
virtual void drawRRect(const SkRRect& rrect, const SkPaint& paint) SK_OVERRIDE;
|
||||
virtual void drawRect(const SkRect& rect, const SkPaint& paint) SK_OVERRIDE;
|
||||
virtual void drawPath(const SkPath& path, const SkPaint& paint) SK_OVERRIDE;
|
||||
virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[],
|
||||
const SkPaint& paint) SK_OVERRIDE;
|
||||
virtual void drawPaint(const SkPaint& paint) SK_OVERRIDE;
|
||||
virtual void clear(SkColor) SK_OVERRIDE;
|
||||
virtual void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
|
||||
const SkPaint* paint = NULL) SK_OVERRIDE;
|
||||
virtual void drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
|
||||
const SkRect& dst, const SkPaint* paint,
|
||||
DrawBitmapRectFlags flags) SK_OVERRIDE;
|
||||
virtual void drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& mat,
|
||||
const SkPaint* paint) SK_OVERRIDE;
|
||||
virtual void drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
|
||||
const SkRect& dst, const SkPaint* paint) SK_OVERRIDE;
|
||||
virtual void drawSprite(const SkBitmap& bitmap, int left, int top,
|
||||
const SkPaint* paint) SK_OVERRIDE;
|
||||
virtual void drawVertices(VertexMode mode, int vertexCount,
|
||||
const SkPoint vertices[], const SkPoint texs[],
|
||||
const SkColor colors[], SkXfermode* xfer,
|
||||
const uint16_t indices[], int indexCount,
|
||||
const SkPaint& paint) SK_OVERRIDE;
|
||||
|
||||
protected:
|
||||
virtual void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) SK_OVERRIDE;
|
||||
virtual void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
|
||||
const SkPaint&) SK_OVERRIDE;
|
||||
virtual void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
|
||||
const SkPaint&) SK_OVERRIDE;
|
||||
virtual void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
|
||||
SkScalar constY, const SkPaint&) SK_OVERRIDE;
|
||||
virtual void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
|
||||
const SkMatrix* matrix, const SkPaint&) SK_OVERRIDE;
|
||||
virtual void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
|
||||
const SkPaint& paint) SK_OVERRIDE;
|
||||
virtual void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
|
||||
const SkPoint texCoords[4], SkXfermode* xmode,
|
||||
const SkPaint& paint) SK_OVERRIDE;
|
||||
virtual void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) SK_OVERRIDE;
|
||||
virtual void willSave() SK_OVERRIDE;
|
||||
virtual SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) SK_OVERRIDE;
|
||||
virtual void willRestore() SK_OVERRIDE;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Takes a bounding box in current canvas view space, accounts for stroking and effects, and
|
||||
* computes an axis-aligned bounding box in device coordinates, then passes it to handleBBox()
|
||||
* returns false if the draw is completely clipped out, and may safely be ignored.
|
||||
**/
|
||||
bool transformBounds(const SkRect& bounds, const SkPaint* paint);
|
||||
|
||||
/**
|
||||
* Paints from currently-active saveLayers that need to be applied to bounding boxes of all
|
||||
* primitives drawn inside them. We own these pointers.
|
||||
**/
|
||||
SkTDArray<const SkPaint*> fSaveStack;
|
||||
|
||||
typedef SkPictureRecord INHERITED;
|
||||
};
|
||||
|
||||
#endif
|
@ -12,7 +12,6 @@
|
||||
#include "SkPicturePlayback.h"
|
||||
#include "SkPictureRecord.h"
|
||||
#include "SkPictureRecorder.h"
|
||||
#include "SkPictureStateTree.h"
|
||||
|
||||
#include "SkBitmapDevice.h"
|
||||
#include "SkCanvas.h"
|
||||
@ -32,7 +31,6 @@
|
||||
#include "SkReader32.h"
|
||||
#include "SkWriter32.h"
|
||||
#include "SkRTree.h"
|
||||
#include "SkBBoxHierarchyRecord.h"
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
#include "GrContext.h"
|
||||
@ -307,32 +305,21 @@ SkPicture::AccelData::Domain SkPicture::AccelData::GenerateDomain() {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
uint32_t SkPicture::OperationList::offset(int index) const {
|
||||
SkASSERT(index < fOps.count());
|
||||
return ((SkPictureStateTree::Draw*)fOps[index])->fOffset;
|
||||
}
|
||||
|
||||
const SkMatrix& SkPicture::OperationList::matrix(int index) const {
|
||||
SkASSERT(index < fOps.count());
|
||||
return *((SkPictureStateTree::Draw*)fOps[index])->fMatrix;
|
||||
}
|
||||
|
||||
// fRecord OK
|
||||
void SkPicture::playback(SkCanvas* canvas, SkDrawPictureCallback* callback) const {
|
||||
SkASSERT(canvas);
|
||||
SkASSERT(fData.get() || fRecord.get());
|
||||
|
||||
// If the query contains the whole picture, don't bother with the BBH.
|
||||
SkRect clipBounds = { 0, 0, 0, 0 };
|
||||
(void)canvas->getClipBounds(&clipBounds);
|
||||
const bool useBBH = !clipBounds.contains(this->cullRect());
|
||||
|
||||
if (fData.get()) {
|
||||
SkPicturePlayback playback(this);
|
||||
playback.setUseBBH(useBBH);
|
||||
playback.draw(canvas, callback);
|
||||
}
|
||||
if (fRecord.get()) {
|
||||
// If the query contains the whole picture, don't bother with the BBH.
|
||||
SkRect clipBounds = { 0, 0, 0, 0 };
|
||||
(void)canvas->getClipBounds(&clipBounds);
|
||||
const bool useBBH = !clipBounds.contains(this->cullRect());
|
||||
|
||||
SkRecordDraw(*fRecord, canvas, useBBH ? fBBH.get() : NULL, callback);
|
||||
}
|
||||
}
|
||||
|
@ -46,17 +46,8 @@ SkPictureData::SkPictureData(const SkPictureRecord& record,
|
||||
|
||||
fOpData = record.opData(deepCopyOps);
|
||||
|
||||
fBoundingHierarchy = record.fBoundingHierarchy;
|
||||
fStateTree = record.fStateTree;
|
||||
|
||||
SkSafeRef(fBoundingHierarchy);
|
||||
SkSafeRef(fStateTree);
|
||||
fContentInfo.set(record.fContentInfo);
|
||||
|
||||
if (fBoundingHierarchy) {
|
||||
fBoundingHierarchy->flushDeferredInserts();
|
||||
}
|
||||
|
||||
// copy over the refcnt dictionary to our reader
|
||||
record.fFlattenableHeap.setupPlaybacks();
|
||||
|
||||
@ -98,8 +89,6 @@ void SkPictureData::init() {
|
||||
fTextBlobCount = 0;
|
||||
fOpData = NULL;
|
||||
fFactoryPlayback = NULL;
|
||||
fBoundingHierarchy = NULL;
|
||||
fStateTree = NULL;
|
||||
}
|
||||
|
||||
SkPictureData::~SkPictureData() {
|
||||
@ -107,8 +96,6 @@ SkPictureData::~SkPictureData() {
|
||||
|
||||
SkSafeUnref(fBitmaps);
|
||||
SkSafeUnref(fPaints);
|
||||
SkSafeUnref(fBoundingHierarchy);
|
||||
SkSafeUnref(fStateTree);
|
||||
|
||||
for (int i = 0; i < fPictureCount; i++) {
|
||||
fPictureRefs[i]->unref();
|
||||
@ -578,16 +565,6 @@ bool SkPictureData::parseBuffer(SkReadBuffer& buffer) {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const SkPicture::OperationList* SkPictureData::getActiveOps(const SkRect& query) const {
|
||||
if (NULL == fStateTree || NULL == fBoundingHierarchy) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SkPicture::OperationList* activeOps = SkNEW(SkPicture::OperationList);
|
||||
fBoundingHierarchy->search(query, &(activeOps->fOps));
|
||||
return activeOps;
|
||||
}
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
bool SkPictureData::suitableForGpuRasterization(GrContext* context, const char **reason,
|
||||
int sampleCount) const {
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include "SkPicture.h"
|
||||
#include "SkPictureContentInfo.h"
|
||||
#include "SkPictureFlat.h"
|
||||
#include "SkPictureStateTree.h"
|
||||
|
||||
class SkData;
|
||||
class SkPictureRecord;
|
||||
@ -24,7 +23,6 @@ class SkBBoxHierarchy;
|
||||
class SkMatrix;
|
||||
class SkPaint;
|
||||
class SkPath;
|
||||
class SkPictureStateTree;
|
||||
class SkReadBuffer;
|
||||
class SkTextBlob;
|
||||
|
||||
@ -67,8 +65,6 @@ public:
|
||||
|
||||
virtual ~SkPictureData();
|
||||
|
||||
const SkPicture::OperationList* getActiveOps(const SkRect& queryRect) const;
|
||||
|
||||
void serialize(SkWStream*, SkPicture::EncodeBitmap) const;
|
||||
void flatten(SkWriteBuffer&) const;
|
||||
|
||||
@ -123,14 +119,6 @@ public:
|
||||
return fTextBlobRefs[index - 1];
|
||||
}
|
||||
|
||||
void initIterator(SkPictureStateTree::Iterator* iter,
|
||||
const SkTDArray<void*>& draws,
|
||||
SkCanvas* canvas) const {
|
||||
if (fStateTree) {
|
||||
fStateTree->initIterator(iter, draws, canvas);
|
||||
}
|
||||
}
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
/**
|
||||
* sampleCount is the number of samples-per-pixel or zero if non-MSAA.
|
||||
@ -175,9 +163,6 @@ private:
|
||||
const SkTextBlob** fTextBlobRefs;
|
||||
int fTextBlobCount;
|
||||
|
||||
SkBBoxHierarchy* fBoundingHierarchy;
|
||||
SkPictureStateTree* fStateTree;
|
||||
|
||||
SkPictureContentInfo fContentInfo;
|
||||
|
||||
SkTypefacePlayback fTFPlayback;
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include "SkPictureData.h"
|
||||
#include "SkPicturePlayback.h"
|
||||
#include "SkPictureRecord.h"
|
||||
#include "SkPictureStateTree.h"
|
||||
#include "SkReader32.h"
|
||||
#include "SkTextBlob.h"
|
||||
#include "SkTDArray.h"
|
||||
@ -66,84 +65,12 @@ static SkBitmap shallow_copy(const SkBitmap& bitmap) {
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
const SkPicture::OperationList* SkPicturePlayback::getActiveOps(const SkCanvas* canvas) {
|
||||
|
||||
if (fUseBBH) {
|
||||
SkRect clipBounds;
|
||||
if (canvas->getClipBounds(&clipBounds)) {
|
||||
return fPictureData->getActiveOps(clipBounds);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Initialize the state tree iterator. Return false if there is nothing left to draw.
|
||||
bool SkPicturePlayback::initIterator(SkPictureStateTree::Iterator* iter,
|
||||
SkCanvas* canvas,
|
||||
const SkPicture::OperationList *activeOpsList) {
|
||||
|
||||
if (activeOpsList) {
|
||||
if (0 == activeOpsList->numOps()) {
|
||||
return false; // nothing to draw
|
||||
}
|
||||
|
||||
fPictureData->initIterator(iter, activeOpsList->fOps, canvas);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// If 'iter' is valid use it to skip forward through the picture.
|
||||
void SkPicturePlayback::StepIterator(SkPictureStateTree::Iterator* iter, SkReader32* reader) {
|
||||
if (iter->isValid()) {
|
||||
uint32_t skipTo = iter->nextDraw();
|
||||
if (SkPictureStateTree::Iterator::kDrawComplete == skipTo) {
|
||||
reader->setOffset(reader->size()); // skip to end
|
||||
} else {
|
||||
reader->setOffset(skipTo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the iterator and state tree to catch up with the skipped ops.
|
||||
void SkPicturePlayback::SkipIterTo(SkPictureStateTree::Iterator* iter,
|
||||
SkReader32* reader,
|
||||
uint32_t skipTo) {
|
||||
SkASSERT(skipTo <= reader->size());
|
||||
SkASSERT(reader->offset() <= skipTo); // should only be skipping forward
|
||||
|
||||
if (iter->isValid()) {
|
||||
// If using a bounding box hierarchy, advance the state tree
|
||||
// iterator until at or after skipTo
|
||||
uint32_t adjustedSkipTo;
|
||||
do {
|
||||
adjustedSkipTo = iter->nextDraw();
|
||||
} while (adjustedSkipTo < skipTo);
|
||||
skipTo = adjustedSkipTo;
|
||||
}
|
||||
if (SkPictureStateTree::Iterator::kDrawComplete == skipTo) {
|
||||
reader->setOffset(reader->size()); // skip to end
|
||||
} else {
|
||||
reader->setOffset(skipTo);
|
||||
}
|
||||
}
|
||||
|
||||
void SkPicturePlayback::draw(SkCanvas* canvas, SkDrawPictureCallback* callback) {
|
||||
AutoResetOpID aroi(this);
|
||||
SkASSERT(0 == fCurOffset);
|
||||
|
||||
SkAutoTDelete<const SkPicture::OperationList> activeOpsList(this->getActiveOps(canvas));
|
||||
SkPictureStateTree::Iterator it;
|
||||
|
||||
if (!this->initIterator(&it, canvas, activeOpsList.get())) {
|
||||
return; // nothing to draw
|
||||
}
|
||||
|
||||
SkReader32 reader(fPictureData->opData()->bytes(), fPictureData->opData()->size());
|
||||
|
||||
StepIterator(&it, &reader);
|
||||
|
||||
// Record this, so we can concat w/ it if we encounter a setMatrix()
|
||||
SkMatrix initialMatrix = canvas->getTotalMatrix();
|
||||
|
||||
@ -157,15 +84,8 @@ void SkPicturePlayback::draw(SkCanvas* canvas, SkDrawPictureCallback* callback)
|
||||
fCurOffset = reader.offset();
|
||||
uint32_t size;
|
||||
DrawType op = ReadOpAndSize(&reader, &size);
|
||||
if (NOOP == op) {
|
||||
// NOOPs are to be ignored - do not propagate them any further
|
||||
SkipIterTo(&it, &reader, fCurOffset + size);
|
||||
continue;
|
||||
}
|
||||
|
||||
this->handleOp(&reader, op, size, canvas, initialMatrix);
|
||||
|
||||
StepIterator(&it, &reader);
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,6 +95,10 @@ void SkPicturePlayback::handleOp(SkReader32* reader,
|
||||
SkCanvas* canvas,
|
||||
const SkMatrix& initialMatrix) {
|
||||
switch (op) {
|
||||
case NOOP: {
|
||||
SkASSERT(size >= 4);
|
||||
reader->skip(size - 4);
|
||||
} break;
|
||||
case CLIP_PATH: {
|
||||
const SkPath& path = fPictureData->getPath(reader);
|
||||
uint32_t packed = reader->readInt();
|
||||
@ -518,7 +442,7 @@ void SkPicturePlayback::handleOp(SkReader32* reader,
|
||||
canvas->translate(dx, dy);
|
||||
} break;
|
||||
default:
|
||||
SkASSERT(0);
|
||||
SkASSERTF(false, "Unknown draw type: %d", op);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
#define SkPicturePlayback_DEFINED
|
||||
|
||||
#include "SkPictureFlat.h" // for DrawType
|
||||
#include "SkPictureStateTree.h"
|
||||
|
||||
class SkBitmap;
|
||||
class SkCanvas;
|
||||
@ -18,14 +17,11 @@ class SkPaint;
|
||||
class SkPictureData;
|
||||
|
||||
// The basic picture playback class replays the provided picture into a canvas.
|
||||
// If the picture was generated with a BBH it is used to accelerate drawing
|
||||
// unless disabled via setUseBBH.
|
||||
class SkPicturePlayback : SkNoncopyable {
|
||||
public:
|
||||
SkPicturePlayback(const SkPicture* picture)
|
||||
: fPictureData(picture->fData.get())
|
||||
, fCurOffset(0)
|
||||
, fUseBBH(true) {
|
||||
, fCurOffset(0) {
|
||||
}
|
||||
virtual ~SkPicturePlayback() { }
|
||||
|
||||
@ -37,31 +33,18 @@ public:
|
||||
size_t curOpID() const { return fCurOffset; }
|
||||
void resetOpID() { fCurOffset = 0; }
|
||||
|
||||
// TODO: remove setUseBBH after cleaning up GrGatherCanvas
|
||||
void setUseBBH(bool useBBH) { fUseBBH = useBBH; }
|
||||
|
||||
protected:
|
||||
const SkPictureData* fPictureData;
|
||||
|
||||
// The offset of the current operation when within the draw method
|
||||
size_t fCurOffset;
|
||||
|
||||
bool fUseBBH;
|
||||
|
||||
void handleOp(SkReader32* reader,
|
||||
DrawType op,
|
||||
uint32_t size,
|
||||
SkCanvas* canvas,
|
||||
const SkMatrix& initialMatrix);
|
||||
|
||||
const SkPicture::OperationList* getActiveOps(const SkCanvas* canvas);
|
||||
bool initIterator(SkPictureStateTree::Iterator* iter,
|
||||
SkCanvas* canvas,
|
||||
const SkPicture::OperationList *activeOpsList);
|
||||
static void StepIterator(SkPictureStateTree::Iterator* iter, SkReader32* reader);
|
||||
static void SkipIterTo(SkPictureStateTree::Iterator* iter,
|
||||
SkReader32* reader, uint32_t skipTo);
|
||||
|
||||
static DrawType ReadOpAndSize(SkReader32* reader, uint32_t* size);
|
||||
|
||||
class AutoResetOpID {
|
||||
|
@ -6,10 +6,8 @@
|
||||
*/
|
||||
|
||||
#include "SkPictureRecord.h"
|
||||
#include "SkBBoxHierarchy.h"
|
||||
#include "SkDevice.h"
|
||||
#include "SkPatchUtils.h"
|
||||
#include "SkPictureStateTree.h"
|
||||
#include "SkPixelRef.h"
|
||||
#include "SkRRect.h"
|
||||
#include "SkTextBlob.h"
|
||||
@ -17,15 +15,6 @@
|
||||
|
||||
#define HEAP_BLOCK_SIZE 4096
|
||||
|
||||
// If SK_RECORD_LITERAL_PICTURES is defined, record our inputs as literally as possible.
|
||||
// Otherwise, we can be clever and record faster equivalents. kBeClever is normally true.
|
||||
static const bool kBeClever =
|
||||
#ifdef SK_RECORD_LITERAL_PICTURES
|
||||
false;
|
||||
#else
|
||||
true;
|
||||
#endif
|
||||
|
||||
enum {
|
||||
// just need a value that save or getSaveCount would never return
|
||||
kNoInitialSave = -1,
|
||||
@ -40,12 +29,9 @@ static const uint32_t kSaveLayerWithBoundsSize = 4 * kUInt32Size + sizeof(SkRect
|
||||
|
||||
SkPictureRecord::SkPictureRecord(const SkISize& dimensions, uint32_t flags)
|
||||
: INHERITED(dimensions.width(), dimensions.height())
|
||||
, fBoundingHierarchy(NULL)
|
||||
, fStateTree(NULL)
|
||||
, fFlattenableHeap(HEAP_BLOCK_SIZE)
|
||||
, fPaints(&fFlattenableHeap)
|
||||
, fRecordFlags(flags)
|
||||
, fOptsEnabled(kBeClever) {
|
||||
, fRecordFlags(flags) {
|
||||
|
||||
fBitmapHeap = SkNEW(SkBitmapHeap);
|
||||
fFlattenableHeap.setBitmapStorage(fBitmapHeap);
|
||||
@ -56,8 +42,6 @@ SkPictureRecord::SkPictureRecord(const SkISize& dimensions, uint32_t flags)
|
||||
|
||||
SkPictureRecord::~SkPictureRecord() {
|
||||
SkSafeUnref(fBitmapHeap);
|
||||
SkSafeUnref(fBoundingHierarchy);
|
||||
SkSafeUnref(fStateTree);
|
||||
fFlattenableHeap.setBitmapStorage(NULL);
|
||||
fPictureRefs.unrefAll();
|
||||
fTextBlobRefs.unrefAll();
|
||||
@ -211,15 +195,7 @@ bool SkPictureRecord::isDrawingToLayer() const {
|
||||
return fFirstSavedLayerIndex != kNoSavedLayerIndex;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the op code from 'offset' in 'writer'.
|
||||
*/
|
||||
#ifdef SK_DEBUG
|
||||
static DrawType peek_op(SkWriter32* writer, size_t offset) {
|
||||
return (DrawType)(writer->readTAt<uint32_t>(offset) >> 24);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Read the op code from 'offset' in 'writer' and extract the size too.
|
||||
*/
|
||||
@ -234,346 +210,7 @@ static DrawType peek_op_and_size(SkWriter32* writer, size_t offset, uint32_t* si
|
||||
}
|
||||
return (DrawType) op;
|
||||
}
|
||||
|
||||
// Is the supplied paint simply a color?
|
||||
static bool is_simple(const SkPaint& p) {
|
||||
intptr_t orAccum = (intptr_t)p.getPathEffect() |
|
||||
(intptr_t)p.getShader() |
|
||||
(intptr_t)p.getXfermode() |
|
||||
(intptr_t)p.getMaskFilter() |
|
||||
(intptr_t)p.getColorFilter() |
|
||||
(intptr_t)p.getRasterizer() |
|
||||
(intptr_t)p.getLooper() |
|
||||
(intptr_t)p.getImageFilter();
|
||||
return 0 == orAccum;
|
||||
}
|
||||
|
||||
// CommandInfos are fed to the 'match' method and filled in with command
|
||||
// information.
|
||||
struct CommandInfo {
|
||||
DrawType fActualOp;
|
||||
uint32_t fOffset;
|
||||
uint32_t fSize;
|
||||
};
|
||||
|
||||
/*
|
||||
* Attempt to match the provided pattern of commands starting at 'offset'
|
||||
* in the byte stream and stopping at the end of the stream. Upon success,
|
||||
* return true with all the pattern information filled out in the result
|
||||
* array (i.e., actual ops, offsets and sizes).
|
||||
* Note this method skips any NOOPs seen in the stream
|
||||
*/
|
||||
static bool match(SkWriter32* writer, uint32_t offset,
|
||||
int* pattern, CommandInfo* result, int numCommands) {
|
||||
SkASSERT(offset < writer->bytesWritten());
|
||||
|
||||
uint32_t curOffset = offset;
|
||||
uint32_t curSize = 0;
|
||||
int numMatched;
|
||||
for (numMatched = 0; numMatched < numCommands && curOffset < writer->bytesWritten(); ++numMatched) {
|
||||
DrawType op = peek_op_and_size(writer, curOffset, &curSize);
|
||||
while (NOOP == op) {
|
||||
curOffset += curSize;
|
||||
if (curOffset >= writer->bytesWritten()) {
|
||||
return false;
|
||||
}
|
||||
op = peek_op_and_size(writer, curOffset, &curSize);
|
||||
}
|
||||
|
||||
if (kDRAW_BITMAP_FLAVOR == pattern[numMatched]) {
|
||||
if (DRAW_BITMAP != op && DRAW_BITMAP_MATRIX != op &&
|
||||
DRAW_BITMAP_NINE != op && DRAW_BITMAP_RECT_TO_RECT != op) {
|
||||
return false;
|
||||
}
|
||||
} else if (op != pattern[numMatched]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
result[numMatched].fActualOp = op;
|
||||
result[numMatched].fOffset = curOffset;
|
||||
result[numMatched].fSize = curSize;
|
||||
|
||||
curOffset += curSize;
|
||||
}
|
||||
|
||||
if (numMatched != numCommands) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (curOffset < writer->bytesWritten()) {
|
||||
// Something else between the last command and the end of the stream
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// temporarily here to make code review easier
|
||||
static bool merge_savelayer_paint_into_drawbitmp(SkWriter32* writer,
|
||||
SkPaintDictionary* paintDict,
|
||||
const CommandInfo& saveLayerInfo,
|
||||
const CommandInfo& dbmInfo);
|
||||
|
||||
/*
|
||||
* Restore has just been called (but not recorded), look back at the
|
||||
* matching save* and see if we are in the configuration:
|
||||
* SAVE_LAYER
|
||||
* DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
|
||||
* RESTORE
|
||||
* where the saveLayer's color can be moved into the drawBitmap*'s paint
|
||||
*/
|
||||
static bool remove_save_layer1(SkWriter32* writer, int32_t offset,
|
||||
SkPaintDictionary* paintDict) {
|
||||
// back up to the save block
|
||||
// TODO: add a stack to track save*/restore offsets rather than searching backwards
|
||||
while (offset > 0) {
|
||||
offset = writer->readTAt<uint32_t>(offset);
|
||||
}
|
||||
|
||||
int pattern[] = { SAVE_LAYER, kDRAW_BITMAP_FLAVOR, /* RESTORE */ };
|
||||
CommandInfo result[SK_ARRAY_COUNT(pattern)];
|
||||
|
||||
if (!match(writer, -offset, pattern, result, SK_ARRAY_COUNT(pattern))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (kSaveLayerWithBoundsSize == result[0].fSize) {
|
||||
// The saveLayer's bound can offset where the dbm is drawn
|
||||
return false;
|
||||
}
|
||||
|
||||
return merge_savelayer_paint_into_drawbitmp(writer, paintDict,
|
||||
result[0], result[1]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the command code located at 'offset' to a NOOP. Leave the size
|
||||
* field alone so the NOOP can be skipped later.
|
||||
*/
|
||||
static void convert_command_to_noop(SkWriter32* writer, uint32_t offset) {
|
||||
uint32_t command = writer->readTAt<uint32_t>(offset);
|
||||
writer->overwriteTAt(offset, (command & MASK_24) | (NOOP << 24));
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to merge the saveLayer's paint into the drawBitmap*'s paint.
|
||||
* Return true on success; false otherwise.
|
||||
*/
|
||||
static bool merge_savelayer_paint_into_drawbitmp(SkWriter32* writer,
|
||||
SkPaintDictionary* paintDict,
|
||||
const CommandInfo& saveLayerInfo,
|
||||
const CommandInfo& dbmInfo) {
|
||||
SkASSERT(SAVE_LAYER == saveLayerInfo.fActualOp);
|
||||
SkASSERT(DRAW_BITMAP == dbmInfo.fActualOp ||
|
||||
DRAW_BITMAP_MATRIX == dbmInfo.fActualOp ||
|
||||
DRAW_BITMAP_NINE == dbmInfo.fActualOp ||
|
||||
DRAW_BITMAP_RECT_TO_RECT == dbmInfo.fActualOp);
|
||||
|
||||
size_t dbmPaintOffset = getPaintOffset(dbmInfo.fActualOp, dbmInfo.fSize);
|
||||
size_t slPaintOffset = getPaintOffset(SAVE_LAYER, saveLayerInfo.fSize);
|
||||
|
||||
// we have a match, now we need to get the paints involved
|
||||
uint32_t dbmPaintId = writer->readTAt<uint32_t>(dbmInfo.fOffset + dbmPaintOffset);
|
||||
uint32_t saveLayerPaintId = writer->readTAt<uint32_t>(saveLayerInfo.fOffset + slPaintOffset);
|
||||
|
||||
if (0 == saveLayerPaintId) {
|
||||
// In this case the saveLayer/restore isn't needed at all - just kill the saveLayer
|
||||
// and signal the caller (by returning true) to not add the RESTORE op
|
||||
convert_command_to_noop(writer, saveLayerInfo.fOffset);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (0 == dbmPaintId) {
|
||||
// In this case just make the DBM* use the saveLayer's paint, kill the saveLayer
|
||||
// and signal the caller (by returning true) to not add the RESTORE op
|
||||
convert_command_to_noop(writer, saveLayerInfo.fOffset);
|
||||
writer->overwriteTAt(dbmInfo.fOffset + dbmPaintOffset, saveLayerPaintId);
|
||||
return true;
|
||||
}
|
||||
|
||||
SkAutoTDelete<SkPaint> saveLayerPaint(paintDict->unflatten(saveLayerPaintId));
|
||||
if (NULL == saveLayerPaint.get() || !is_simple(*saveLayerPaint)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// For this optimization we only fold the saveLayer and drawBitmapRect
|
||||
// together if the saveLayer's draw is simple (i.e., no fancy effects) and
|
||||
// and the only difference in the colors is that the saveLayer's can have
|
||||
// an alpha while the drawBitmapRect's is opaque.
|
||||
// TODO: it should be possible to fold them together even if they both
|
||||
// have different non-255 alphas
|
||||
SkColor layerColor = saveLayerPaint->getColor() | 0xFF000000; // force opaque
|
||||
|
||||
SkAutoTDelete<SkPaint> dbmPaint(paintDict->unflatten(dbmPaintId));
|
||||
if (NULL == dbmPaint.get() || dbmPaint->getColor() != layerColor || !is_simple(*dbmPaint)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkColor newColor = SkColorSetA(dbmPaint->getColor(),
|
||||
SkColorGetA(saveLayerPaint->getColor()));
|
||||
dbmPaint->setColor(newColor);
|
||||
|
||||
const SkFlatData* data = paintDict->findAndReturnFlat(*dbmPaint);
|
||||
if (NULL == data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// kill the saveLayer and alter the DBMR2R's paint to be the modified one
|
||||
convert_command_to_noop(writer, saveLayerInfo.fOffset);
|
||||
writer->overwriteTAt(dbmInfo.fOffset + dbmPaintOffset, data->index());
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Restore has just been called (but not recorded), look back at the
|
||||
* matching save* and see if we are in the configuration:
|
||||
* SAVE_LAYER (with NULL == bounds)
|
||||
* SAVE
|
||||
* CLIP_RECT
|
||||
* DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
|
||||
* RESTORE
|
||||
* RESTORE
|
||||
* where the saveLayer's color can be moved into the drawBitmap*'s paint
|
||||
*/
|
||||
static bool remove_save_layer2(SkWriter32* writer, int32_t offset,
|
||||
SkPaintDictionary* paintDict) {
|
||||
// back up to the save block
|
||||
// TODO: add a stack to track save*/restore offsets rather than searching backwards
|
||||
while (offset > 0) {
|
||||
offset = writer->readTAt<uint32_t>(offset);
|
||||
}
|
||||
|
||||
int pattern[] = { SAVE_LAYER, SAVE, CLIP_RECT, kDRAW_BITMAP_FLAVOR, RESTORE, /* RESTORE */ };
|
||||
CommandInfo result[SK_ARRAY_COUNT(pattern)];
|
||||
|
||||
if (!match(writer, -offset, pattern, result, SK_ARRAY_COUNT(pattern))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (kSaveLayerWithBoundsSize == result[0].fSize) {
|
||||
// The saveLayer's bound can offset where the dbm is drawn
|
||||
return false;
|
||||
}
|
||||
|
||||
return merge_savelayer_paint_into_drawbitmp(writer, paintDict,
|
||||
result[0], result[3]);
|
||||
}
|
||||
|
||||
static bool is_drawing_op(DrawType op) {
|
||||
|
||||
// FIXME: yuck. convert to a lookup table?
|
||||
return (op > CONCAT && op < ROTATE)
|
||||
|| DRAW_DRRECT == op
|
||||
|| DRAW_PATCH == op
|
||||
|| DRAW_PICTURE_MATRIX_PAINT == op
|
||||
|| DRAW_TEXT_BLOB == op;
|
||||
}
|
||||
|
||||
/*
|
||||
* Restore has just been called (but not recorded), so look back at the
|
||||
* matching save(), and see if we can eliminate the pair of them, due to no
|
||||
* intervening matrix/clip calls.
|
||||
*
|
||||
* If so, update the writer and return true, in which case we won't even record
|
||||
* the restore() call. If we still need the restore(), return false.
|
||||
*/
|
||||
static bool collapse_save_clip_restore(SkWriter32* writer, int32_t offset,
|
||||
SkPaintDictionary* paintDict) {
|
||||
int32_t restoreOffset = (int32_t)writer->bytesWritten();
|
||||
|
||||
// back up to the save block
|
||||
while (offset > 0) {
|
||||
offset = writer->readTAt<uint32_t>(offset);
|
||||
}
|
||||
|
||||
// now offset points to a save
|
||||
offset = -offset;
|
||||
uint32_t opSize;
|
||||
DrawType op = peek_op_and_size(writer, offset, &opSize);
|
||||
if (SAVE_LAYER == op) {
|
||||
// not ready to cull these out yet (mrr)
|
||||
return false;
|
||||
}
|
||||
SkASSERT(SAVE == op);
|
||||
SkASSERT(kSaveSize == opSize);
|
||||
|
||||
// Walk forward until we get back to either a draw-verb (abort) or we hit
|
||||
// our restore (success).
|
||||
int32_t saveOffset = offset;
|
||||
|
||||
offset += opSize;
|
||||
while (offset < restoreOffset) {
|
||||
op = peek_op_and_size(writer, offset, &opSize);
|
||||
if (is_drawing_op(op) || (SAVE_LAYER == op)) {
|
||||
// drawing verb, abort
|
||||
return false;
|
||||
}
|
||||
offset += opSize;
|
||||
}
|
||||
|
||||
writer->rewindToOffset(saveOffset);
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*PictureRecordOptProc)(SkWriter32* writer, int32_t offset,
|
||||
SkPaintDictionary* paintDict);
|
||||
enum PictureRecordOptType {
|
||||
kRewind_OptType, // Optimization rewinds the command stream
|
||||
kCollapseSaveLayer_OptType, // Optimization eliminates a save/restore pair
|
||||
};
|
||||
|
||||
enum PictureRecordOptFlags {
|
||||
kSkipIfBBoxHierarchy_Flag = 0x1, // Optimization should be skipped if the
|
||||
// SkPicture has a bounding box hierarchy.
|
||||
kRescindLastSave_Flag = 0x2,
|
||||
kRescindLastSaveLayer_Flag = 0x4,
|
||||
};
|
||||
|
||||
struct PictureRecordOpt {
|
||||
PictureRecordOptProc fProc;
|
||||
PictureRecordOptType fType;
|
||||
unsigned fFlags;
|
||||
};
|
||||
/*
|
||||
* A list of the optimizations that are tried upon seeing a restore
|
||||
* TODO: add a real API for such optimizations
|
||||
* Add the ability to fire optimizations on any op (not just RESTORE)
|
||||
*/
|
||||
static const PictureRecordOpt gPictureRecordOpts[] = {
|
||||
// 'collapse_save_clip_restore' is skipped if there is a BBoxHierarchy
|
||||
// because it is redundant with the state traversal optimization in
|
||||
// SkPictureStateTree, and applying the optimization introduces significant
|
||||
// record time overhead because it requires rewinding contents that were
|
||||
// recorded into the BBoxHierarchy.
|
||||
{ collapse_save_clip_restore, kRewind_OptType,
|
||||
kSkipIfBBoxHierarchy_Flag|kRescindLastSave_Flag },
|
||||
{ remove_save_layer1, kCollapseSaveLayer_OptType, kRescindLastSaveLayer_Flag },
|
||||
{ remove_save_layer2, kCollapseSaveLayer_OptType, kRescindLastSaveLayer_Flag }
|
||||
};
|
||||
|
||||
// This is called after an optimization has been applied to the command stream
|
||||
// in order to adjust the contents and state of the bounding box hierarchy and
|
||||
// state tree to reflect the optimization.
|
||||
static void apply_optimization_to_bbh(PictureRecordOptType opt, SkPictureStateTree* stateTree,
|
||||
SkBBoxHierarchy* boundingHierarchy) {
|
||||
switch (opt) {
|
||||
case kCollapseSaveLayer_OptType:
|
||||
if (stateTree) {
|
||||
stateTree->saveCollapsed();
|
||||
}
|
||||
break;
|
||||
case kRewind_OptType:
|
||||
if (boundingHierarchy) {
|
||||
boundingHierarchy->rewindInserts();
|
||||
}
|
||||
// Note: No need to touch the state tree for this to work correctly.
|
||||
// Unused branches do not burden the playback, and pruning the tree
|
||||
// would be O(N^2), so it is best to leave it alone.
|
||||
break;
|
||||
default:
|
||||
SkASSERT(0);
|
||||
}
|
||||
}
|
||||
#endif//SK_DEBUG
|
||||
|
||||
void SkPictureRecord::willRestore() {
|
||||
// FIXME: SkDeferredCanvas needs to be refactored to respect
|
||||
@ -592,31 +229,7 @@ void SkPictureRecord::willRestore() {
|
||||
fFirstSavedLayerIndex = kNoSavedLayerIndex;
|
||||
}
|
||||
|
||||
size_t opt = 0;
|
||||
if (fOptsEnabled) {
|
||||
for (opt = 0; opt < SK_ARRAY_COUNT(gPictureRecordOpts); ++opt) {
|
||||
if (0 != (gPictureRecordOpts[opt].fFlags & kSkipIfBBoxHierarchy_Flag)
|
||||
&& fBoundingHierarchy) {
|
||||
continue;
|
||||
}
|
||||
if ((*gPictureRecordOpts[opt].fProc)(&fWriter, fRestoreOffsetStack.top(), &fPaints)) {
|
||||
// Some optimization fired so don't add the RESTORE
|
||||
apply_optimization_to_bbh(gPictureRecordOpts[opt].fType,
|
||||
fStateTree, fBoundingHierarchy);
|
||||
if (gPictureRecordOpts[opt].fFlags & kRescindLastSave_Flag) {
|
||||
fContentInfo.rescindLastSave();
|
||||
} else if (gPictureRecordOpts[opt].fFlags & kRescindLastSaveLayer_Flag) {
|
||||
fContentInfo.rescindLastSaveLayer();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!fOptsEnabled || SK_ARRAY_COUNT(gPictureRecordOpts) == opt) {
|
||||
// No optimization fired so add the RESTORE
|
||||
this->recordRestore();
|
||||
}
|
||||
this->recordRestore();
|
||||
|
||||
fRestoreOffsetStack.pop();
|
||||
|
||||
@ -905,19 +518,13 @@ void SkPictureRecord::drawRect(const SkRect& rect, const SkPaint& paint) {
|
||||
}
|
||||
|
||||
void SkPictureRecord::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
|
||||
if (rrect.isRect() && kBeClever) {
|
||||
this->SkPictureRecord::drawRect(rrect.getBounds(), paint);
|
||||
} else if (rrect.isOval() && kBeClever) {
|
||||
this->SkPictureRecord::drawOval(rrect.getBounds(), paint);
|
||||
} else {
|
||||
// op + paint index + rrect
|
||||
size_t size = 2 * kUInt32Size + SkRRect::kSizeInMemory;
|
||||
size_t initialOffset = this->addDraw(DRAW_RRECT, &size);
|
||||
SkASSERT(initialOffset+getPaintOffset(DRAW_RRECT, size) == fWriter.bytesWritten());
|
||||
this->addPaint(paint);
|
||||
this->addRRect(rrect);
|
||||
this->validate(initialOffset, size);
|
||||
}
|
||||
// op + paint index + rrect
|
||||
size_t size = 2 * kUInt32Size + SkRRect::kSizeInMemory;
|
||||
size_t initialOffset = this->addDraw(DRAW_RRECT, &size);
|
||||
SkASSERT(initialOffset+getPaintOffset(DRAW_RRECT, size) == fWriter.bytesWritten());
|
||||
this->addPaint(paint);
|
||||
this->addRRect(rrect);
|
||||
this->validate(initialOffset, size);
|
||||
}
|
||||
|
||||
void SkPictureRecord::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
|
||||
@ -946,10 +553,6 @@ void SkPictureRecord::drawPath(const SkPath& path, const SkPaint& paint) {
|
||||
|
||||
void SkPictureRecord::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
|
||||
const SkPaint* paint = NULL) {
|
||||
if (bitmap.drawsNothing() && kBeClever) {
|
||||
return;
|
||||
}
|
||||
|
||||
// op + paint index + bitmap index + left + top
|
||||
size_t size = 3 * kUInt32Size + 2 * sizeof(SkScalar);
|
||||
size_t initialOffset = this->addDraw(DRAW_BITMAP, &size);
|
||||
@ -964,10 +567,6 @@ void SkPictureRecord::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar
|
||||
void SkPictureRecord::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
|
||||
const SkRect& dst, const SkPaint* paint,
|
||||
DrawBitmapRectFlags flags) {
|
||||
if (bitmap.drawsNothing() && kBeClever) {
|
||||
return;
|
||||
}
|
||||
|
||||
// id + paint index + bitmap index + bool for 'src' + flags
|
||||
size_t size = 5 * kUInt32Size;
|
||||
if (src) {
|
||||
@ -988,10 +587,6 @@ void SkPictureRecord::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect*
|
||||
|
||||
void SkPictureRecord::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
|
||||
const SkPaint* paint) {
|
||||
if (bitmap.drawsNothing() && kBeClever) {
|
||||
return;
|
||||
}
|
||||
|
||||
// id + paint index + bitmap index + matrix
|
||||
size_t size = 3 * kUInt32Size + matrix.writeToMemory(NULL);
|
||||
size_t initialOffset = this->addDraw(DRAW_BITMAP_MATRIX, &size);
|
||||
@ -1004,10 +599,6 @@ void SkPictureRecord::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m
|
||||
|
||||
void SkPictureRecord::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
|
||||
const SkRect& dst, const SkPaint* paint) {
|
||||
if (bitmap.drawsNothing() && kBeClever) {
|
||||
return;
|
||||
}
|
||||
|
||||
// op + paint index + bitmap id + center + dst rect
|
||||
size_t size = 3 * kUInt32Size + sizeof(center) + sizeof(dst);
|
||||
size_t initialOffset = this->addDraw(DRAW_BITMAP_NINE, &size);
|
||||
@ -1021,10 +612,6 @@ void SkPictureRecord::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& cent
|
||||
|
||||
void SkPictureRecord::drawSprite(const SkBitmap& bitmap, int left, int top,
|
||||
const SkPaint* paint = NULL) {
|
||||
if (bitmap.drawsNothing() && kBeClever) {
|
||||
return;
|
||||
}
|
||||
|
||||
// op + paint index + bitmap index + left + top
|
||||
size_t size = 5 * kUInt32Size;
|
||||
size_t initialOffset = this->addDraw(DRAW_SPRITE, &size);
|
||||
@ -1036,160 +623,52 @@ void SkPictureRecord::drawSprite(const SkBitmap& bitmap, int left, int top,
|
||||
this->validate(initialOffset, size);
|
||||
}
|
||||
|
||||
void SkPictureRecord::ComputeFontMetricsTopBottom(const SkPaint& paint, SkScalar topbot[2]) {
|
||||
SkPaint::FontMetrics metrics;
|
||||
paint.getFontMetrics(&metrics);
|
||||
SkRect bounds;
|
||||
// construct a rect so we can see any adjustments from the paint.
|
||||
// we use 0,1 for left,right, just so the rect isn't empty
|
||||
bounds.set(0, metrics.fTop, SK_Scalar1, metrics.fBottom);
|
||||
(void)paint.computeFastBounds(bounds, &bounds);
|
||||
topbot[0] = bounds.fTop;
|
||||
topbot[1] = bounds.fBottom;
|
||||
}
|
||||
|
||||
void SkPictureRecord::addFontMetricsTopBottom(const SkPaint& paint, const SkFlatData& flat,
|
||||
SkScalar minY, SkScalar maxY) {
|
||||
WriteTopBot(paint, flat);
|
||||
this->addScalar(flat.topBot()[0] + minY);
|
||||
this->addScalar(flat.topBot()[1] + maxY);
|
||||
}
|
||||
|
||||
void SkPictureRecord::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
|
||||
const SkPaint& paint) {
|
||||
bool fast = !paint.isVerticalText() && paint.canComputeFastBounds() && kBeClever;
|
||||
|
||||
// op + paint index + length + 'length' worth of chars + x + y
|
||||
size_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 2 * sizeof(SkScalar);
|
||||
if (fast) {
|
||||
size += 2 * sizeof(SkScalar); // + top & bottom
|
||||
}
|
||||
|
||||
DrawType op = fast ? DRAW_TEXT_TOP_BOTTOM : DRAW_TEXT;
|
||||
DrawType op = DRAW_TEXT;
|
||||
size_t initialOffset = this->addDraw(op, &size);
|
||||
SkASSERT(initialOffset+getPaintOffset(op, size) == fWriter.bytesWritten());
|
||||
const SkFlatData* flatPaintData = addPaint(paint);
|
||||
SkASSERT(flatPaintData);
|
||||
this->addPaint(paint);
|
||||
this->addText(text, byteLength);
|
||||
this->addScalar(x);
|
||||
this->addScalar(y);
|
||||
if (fast) {
|
||||
this->addFontMetricsTopBottom(paint, *flatPaintData, y, y);
|
||||
}
|
||||
this->validate(initialOffset, size);
|
||||
}
|
||||
|
||||
void SkPictureRecord::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
|
||||
const SkPaint& paint) {
|
||||
int points = paint.countText(text, byteLength);
|
||||
if (0 == points)
|
||||
return;
|
||||
|
||||
bool canUseDrawH = true;
|
||||
SkScalar minY = pos[0].fY;
|
||||
SkScalar maxY = pos[0].fY;
|
||||
// check if the caller really should have used drawPosTextH()
|
||||
{
|
||||
const SkScalar firstY = pos[0].fY;
|
||||
for (int index = 1; index < points; index++) {
|
||||
if (pos[index].fY != firstY) {
|
||||
canUseDrawH = false;
|
||||
if (pos[index].fY < minY) {
|
||||
minY = pos[index].fY;
|
||||
} else if (pos[index].fY > maxY) {
|
||||
maxY = pos[index].fY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// op + paint index + length + 'length' worth of data + num points + x&y point data
|
||||
size_t size = 3 * kUInt32Size + SkAlign4(byteLength) + kUInt32Size + points * sizeof(SkPoint);
|
||||
|
||||
bool fastBounds = !paint.isVerticalText() && paint.canComputeFastBounds() && kBeClever;
|
||||
bool fast = canUseDrawH && fastBounds && kBeClever;
|
||||
DrawType op = DRAW_POS_TEXT;
|
||||
|
||||
// op + paint index + length + 'length' worth of data + num points
|
||||
size_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 1 * kUInt32Size;
|
||||
if (canUseDrawH) {
|
||||
if (fast) {
|
||||
size += 2 * sizeof(SkScalar); // + top & bottom
|
||||
}
|
||||
// + y-pos + actual x-point data
|
||||
size += sizeof(SkScalar) + points * sizeof(SkScalar);
|
||||
} else {
|
||||
// + x&y point data
|
||||
size += points * sizeof(SkPoint);
|
||||
if (fastBounds) {
|
||||
size += 2 * sizeof(SkScalar); // + top & bottom
|
||||
}
|
||||
}
|
||||
|
||||
DrawType op;
|
||||
if (fast) {
|
||||
op = DRAW_POS_TEXT_H_TOP_BOTTOM;
|
||||
} else if (canUseDrawH) {
|
||||
op = DRAW_POS_TEXT_H;
|
||||
} else if (fastBounds) {
|
||||
op = DRAW_POS_TEXT_TOP_BOTTOM;
|
||||
} else {
|
||||
op = DRAW_POS_TEXT;
|
||||
}
|
||||
size_t initialOffset = this->addDraw(op, &size);
|
||||
SkASSERT(initialOffset+getPaintOffset(op, size) == fWriter.bytesWritten());
|
||||
const SkFlatData* flatPaintData = this->addPaint(paint);
|
||||
SkASSERT(flatPaintData);
|
||||
this->addPaint(paint);
|
||||
this->addText(text, byteLength);
|
||||
this->addInt(points);
|
||||
|
||||
if (canUseDrawH) {
|
||||
if (fast) {
|
||||
this->addFontMetricsTopBottom(paint, *flatPaintData, pos[0].fY, pos[0].fY);
|
||||
}
|
||||
this->addScalar(pos[0].fY);
|
||||
SkScalar* xptr = (SkScalar*)fWriter.reserve(points * sizeof(SkScalar));
|
||||
for (int index = 0; index < points; index++)
|
||||
*xptr++ = pos[index].fX;
|
||||
} else {
|
||||
fWriter.writeMul4(pos, points * sizeof(SkPoint));
|
||||
if (fastBounds) {
|
||||
this->addFontMetricsTopBottom(paint, *flatPaintData, minY, maxY);
|
||||
}
|
||||
}
|
||||
fWriter.writeMul4(pos, points * sizeof(SkPoint));
|
||||
this->validate(initialOffset, size);
|
||||
}
|
||||
|
||||
void SkPictureRecord::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
|
||||
SkScalar constY, const SkPaint& paint) {
|
||||
const SkFlatData* flatPaintData = this->getFlatPaintData(paint);
|
||||
this->drawPosTextHImpl(text, byteLength, xpos, constY, paint, flatPaintData);
|
||||
}
|
||||
|
||||
void SkPictureRecord::drawPosTextHImpl(const void* text, size_t byteLength,
|
||||
const SkScalar xpos[], SkScalar constY,
|
||||
const SkPaint& paint, const SkFlatData* flatPaintData) {
|
||||
int points = paint.countText(text, byteLength);
|
||||
if (0 == points && kBeClever) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool fast = !paint.isVerticalText() && paint.canComputeFastBounds() && kBeClever;
|
||||
|
||||
// op + paint index + length + 'length' worth of data + num points
|
||||
size_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 1 * kUInt32Size;
|
||||
if (fast) {
|
||||
size += 2 * sizeof(SkScalar); // + top & bottom
|
||||
}
|
||||
// + y + the actual points
|
||||
size += 1 * kUInt32Size + points * sizeof(SkScalar);
|
||||
size_t initialOffset = this->addDraw(fast ? DRAW_POS_TEXT_H_TOP_BOTTOM : DRAW_POS_TEXT_H,
|
||||
&size);
|
||||
SkASSERT(flatPaintData);
|
||||
this->addFlatPaint(flatPaintData);
|
||||
|
||||
size_t initialOffset = this->addDraw(DRAW_POS_TEXT_H, &size);
|
||||
this->addPaint(paint);
|
||||
this->addText(text, byteLength);
|
||||
this->addInt(points);
|
||||
|
||||
if (fast) {
|
||||
this->addFontMetricsTopBottom(paint, *flatPaintData, constY, constY);
|
||||
}
|
||||
this->addScalar(constY);
|
||||
fWriter.writeMul4(xpos, points * sizeof(SkScalar));
|
||||
this->validate(initialOffset, size);
|
||||
@ -1409,14 +888,6 @@ void SkPictureRecord::onPopCull() {
|
||||
uint32_t cullSkipOffset = fCullOffsetStack.top();
|
||||
fCullOffsetStack.pop();
|
||||
|
||||
// Collapse empty push/pop pairs.
|
||||
if ((size_t)(cullSkipOffset + kUInt32Size) == fWriter.bytesWritten() && kBeClever) {
|
||||
SkASSERT(fWriter.bytesWritten() >= kPushCullOpSize);
|
||||
SkASSERT(PUSH_CULL == peek_op(&fWriter, fWriter.bytesWritten() - kPushCullOpSize));
|
||||
fWriter.rewindToOffset(fWriter.bytesWritten() - kPushCullOpSize);
|
||||
return;
|
||||
}
|
||||
|
||||
// op only
|
||||
size_t size = kUInt32Size;
|
||||
size_t initialOffset = this->addDraw(POP_CULL, &size);
|
||||
@ -1447,21 +918,16 @@ void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
|
||||
fWriter.writeMatrix(matrix);
|
||||
}
|
||||
|
||||
const SkFlatData* SkPictureRecord::getFlatPaintData(const SkPaint& paint) {
|
||||
return fPaints.findAndReturnFlat(paint);
|
||||
}
|
||||
|
||||
const SkFlatData* SkPictureRecord::addPaintPtr(const SkPaint* paint) {
|
||||
void SkPictureRecord::addPaintPtr(const SkPaint* paint) {
|
||||
fContentInfo.onAddPaintPtr(paint);
|
||||
|
||||
const SkFlatData* data = paint ? getFlatPaintData(*paint) : NULL;
|
||||
this->addFlatPaint(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
void SkPictureRecord::addFlatPaint(const SkFlatData* flatPaint) {
|
||||
int index = flatPaint ? flatPaint->index() : 0;
|
||||
this->addInt(index);
|
||||
if (paint) {
|
||||
const SkFlatData* flat = fPaints.findAndReturnFlat(*paint);
|
||||
SkASSERT(flat && flat->index() != 0);
|
||||
this->addInt(flat->index());
|
||||
} else {
|
||||
this->addInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
int SkPictureRecord::addPathToHeap(const SkPath& path) {
|
||||
|
@ -17,9 +17,6 @@
|
||||
#include "SkTemplates.h"
|
||||
#include "SkWriter32.h"
|
||||
|
||||
class SkBBoxHierarchy;
|
||||
class SkPictureStateTree;
|
||||
|
||||
// These macros help with packing and unpacking a single byte value and
|
||||
// a 3 byte value into/out of a uint32_t
|
||||
#define MASK_24 0x00FFFFFF
|
||||
@ -64,9 +61,6 @@ public:
|
||||
virtual void endCommentGroup() SK_OVERRIDE;
|
||||
virtual bool isDrawingToLayer() const SK_OVERRIDE;
|
||||
|
||||
void addFontMetricsTopBottom(const SkPaint& paint, const SkFlatData&,
|
||||
SkScalar minY, SkScalar maxY);
|
||||
|
||||
const SkTDArray<const SkPicture* >& getPictureRefs() const {
|
||||
return fPictureRefs;
|
||||
}
|
||||
@ -108,10 +102,6 @@ public:
|
||||
void beginRecording();
|
||||
void endRecording();
|
||||
|
||||
void internalOnly_EnableOpts(bool optsEnabled) {
|
||||
fOptsEnabled = optsEnabled;
|
||||
}
|
||||
|
||||
protected:
|
||||
void addNoOp();
|
||||
|
||||
@ -170,9 +160,8 @@ private:
|
||||
// The command at 'offset' in the skp uses the specified bitmap
|
||||
int addBitmap(const SkBitmap& bitmap);
|
||||
void addMatrix(const SkMatrix& matrix);
|
||||
const SkFlatData* addPaint(const SkPaint& paint) { return this->addPaintPtr(&paint); }
|
||||
const SkFlatData* addPaintPtr(const SkPaint* paint);
|
||||
void addFlatPaint(const SkFlatData* flatPaint);
|
||||
void addPaint(const SkPaint& paint) { this->addPaintPtr(&paint); }
|
||||
void addPaintPtr(const SkPaint* paint);
|
||||
void addPatch(const SkPoint cubics[12]);
|
||||
void addPath(const SkPath& path);
|
||||
void addPicture(const SkPicture* picture);
|
||||
@ -232,27 +221,6 @@ protected:
|
||||
|
||||
virtual void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) SK_OVERRIDE;
|
||||
|
||||
// Return fontmetrics.fTop,fBottom in topbot[0,1], after they have been
|
||||
// tweaked by paint.computeFastBounds().
|
||||
static void ComputeFontMetricsTopBottom(const SkPaint& paint, SkScalar topbot[2]);
|
||||
|
||||
// Make sure that flat has fTopBot written.
|
||||
static void WriteTopBot(const SkPaint& paint, const SkFlatData& flat) {
|
||||
if (!flat.isTopBotWritten()) {
|
||||
ComputeFontMetricsTopBottom(paint, flat.writableTopBot());
|
||||
SkASSERT(flat.isTopBotWritten());
|
||||
}
|
||||
}
|
||||
// Will return a cached version when possible.
|
||||
const SkFlatData* getFlatPaintData(const SkPaint& paint);
|
||||
/**
|
||||
* SkBBoxRecord::drawPosTextH gets a flat paint and uses it,
|
||||
* then it calls this, using the extra parameter, to avoid duplication.
|
||||
*/
|
||||
void drawPosTextHImpl(const void* text, size_t byteLength,
|
||||
const SkScalar xpos[], SkScalar constY,
|
||||
const SkPaint& paint, const SkFlatData* flatPaintData);
|
||||
|
||||
int addPathToHeap(const SkPath& path); // does not write to ops stream
|
||||
|
||||
// These entry points allow the writing of matrices, clips, saves &
|
||||
@ -269,11 +237,6 @@ protected:
|
||||
void recordSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags);
|
||||
void recordRestore(bool fillInSkips = true);
|
||||
|
||||
// These are set to NULL in our constructor, but may be changed by
|
||||
// subclasses, in which case they will be SkSafeUnref'd in our destructor.
|
||||
SkBBoxHierarchy* fBoundingHierarchy;
|
||||
SkPictureStateTree* fStateTree;
|
||||
|
||||
// Allocated in the constructor and managed by this class.
|
||||
SkBitmapHeap* fBitmapHeap;
|
||||
|
||||
@ -292,7 +255,6 @@ private:
|
||||
SkTDArray<const SkTextBlob*> fTextBlobRefs;
|
||||
|
||||
uint32_t fRecordFlags;
|
||||
bool fOptsEnabled;
|
||||
int fInitialSaveCount;
|
||||
|
||||
friend class SkPictureData; // for SkPictureData's SkPictureRecord-based constructor
|
||||
|
@ -5,7 +5,6 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkBBoxHierarchyRecord.h"
|
||||
#include "SkPictureRecord.h"
|
||||
#include "SkPictureRecorder.h"
|
||||
#include "SkRecord.h"
|
||||
@ -26,20 +25,11 @@ SkCanvas* SkPictureRecorder::beginRecording(SkScalar width, SkScalar height,
|
||||
SkCanvas* SkPictureRecorder::DEPRECATED_beginRecording(SkScalar width, SkScalar height,
|
||||
SkBBHFactory* bbhFactory /* = NULL */,
|
||||
uint32_t recordFlags /* = 0 */) {
|
||||
SkASSERT(!bbhFactory); // No longer suppported with this backend.
|
||||
|
||||
fCullWidth = width;
|
||||
fCullHeight = height;
|
||||
|
||||
const SkISize size = SkISize::Make(width, height);
|
||||
|
||||
if (bbhFactory) {
|
||||
// We don't need to hold a ref on the BBH ourselves, but might as well for
|
||||
// consistency with EXPERIMENTAL_beginRecording(), which does need to.
|
||||
fBBH.reset((*bbhFactory)(width, height));
|
||||
SkASSERT(fBBH.get());
|
||||
fPictureRecord.reset(SkNEW_ARGS(SkBBoxHierarchyRecord, (size, recordFlags, fBBH.get())));
|
||||
} else {
|
||||
fPictureRecord.reset(SkNEW_ARGS(SkPictureRecord, (size, recordFlags)));
|
||||
}
|
||||
fPictureRecord.reset(SkNEW_ARGS(SkPictureRecord, (SkISize::Make(width, height), recordFlags)));
|
||||
|
||||
fPictureRecord->beginRecording();
|
||||
return this->getRecordingCanvas();
|
||||
@ -85,12 +75,6 @@ SkPicture* SkPictureRecorder::endRecording() {
|
||||
return picture;
|
||||
}
|
||||
|
||||
void SkPictureRecorder::internalOnly_EnableOpts(bool enableOpts) {
|
||||
if (fPictureRecord.get()) {
|
||||
fPictureRecord->internalOnly_EnableOpts(enableOpts);
|
||||
}
|
||||
}
|
||||
|
||||
void SkPictureRecorder::partialReplay(SkCanvas* canvas) const {
|
||||
if (NULL == canvas) {
|
||||
return;
|
||||
|
@ -1,218 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkPictureStateTree.h"
|
||||
#include "SkCanvas.h"
|
||||
|
||||
SkPictureStateTree::SkPictureStateTree()
|
||||
: fAlloc(2048)
|
||||
, fLastRestoredNode(NULL)
|
||||
, fStateStack(sizeof(Draw), 16) {
|
||||
fRootMatrix.reset();
|
||||
fRoot.fParent = NULL;
|
||||
fRoot.fMatrix = &fRootMatrix;
|
||||
fRoot.fFlags = Node::kSave_Flag;
|
||||
fRoot.fOffset = 0;
|
||||
fRoot.fLevel = 0;
|
||||
fCurrentState.fNode = &fRoot;
|
||||
fCurrentState.fMatrix = &fRootMatrix;
|
||||
*static_cast<Draw*>(fStateStack.push_back()) = fCurrentState;
|
||||
}
|
||||
|
||||
SkPictureStateTree::~SkPictureStateTree() {
|
||||
}
|
||||
|
||||
SkPictureStateTree::Draw* SkPictureStateTree::appendDraw(size_t offset) {
|
||||
Draw* draw = static_cast<Draw*>(fAlloc.allocThrow(sizeof(Draw)));
|
||||
*draw = fCurrentState;
|
||||
draw->fOffset = SkToU32(offset);
|
||||
return draw;
|
||||
}
|
||||
|
||||
void SkPictureStateTree::appendSave() {
|
||||
*static_cast<Draw*>(fStateStack.push_back()) = fCurrentState;
|
||||
fCurrentState.fNode->fFlags |= Node::kSave_Flag;
|
||||
}
|
||||
|
||||
void SkPictureStateTree::appendSaveLayer(size_t offset) {
|
||||
*static_cast<Draw*>(fStateStack.push_back()) = fCurrentState;
|
||||
this->appendNode(offset);
|
||||
fCurrentState.fNode->fFlags |= Node::kSaveLayer_Flag;
|
||||
}
|
||||
|
||||
void SkPictureStateTree::saveCollapsed() {
|
||||
SkASSERT(fLastRestoredNode);
|
||||
SkASSERT(SkToBool(fLastRestoredNode->fFlags & \
|
||||
(Node::kSaveLayer_Flag | Node::kSave_Flag)));
|
||||
SkASSERT(fLastRestoredNode->fParent == fCurrentState.fNode);
|
||||
// The structure of the tree is not modified here. We just turn off
|
||||
// the save or saveLayer flag to prevent the iterator from making state
|
||||
// changing calls on the playback canvas when traversing a save or
|
||||
// saveLayerNode node.
|
||||
fLastRestoredNode->fFlags = 0;
|
||||
}
|
||||
|
||||
void SkPictureStateTree::appendRestore() {
|
||||
fLastRestoredNode = fCurrentState.fNode;
|
||||
fCurrentState = *static_cast<Draw*>(fStateStack.back());
|
||||
fStateStack.pop_back();
|
||||
}
|
||||
|
||||
void SkPictureStateTree::appendTransform(const SkMatrix& trans) {
|
||||
SkMatrix* m = static_cast<SkMatrix*>(fAlloc.allocThrow(sizeof(SkMatrix)));
|
||||
*m = trans;
|
||||
fCurrentState.fMatrix = m;
|
||||
}
|
||||
|
||||
void SkPictureStateTree::appendClip(size_t offset) {
|
||||
this->appendNode(offset);
|
||||
}
|
||||
|
||||
void SkPictureStateTree::initIterator(SkPictureStateTree::Iterator* iter,
|
||||
const SkTDArray<void*>& draws,
|
||||
SkCanvas* canvas) {
|
||||
iter->init(draws, canvas, &fRoot);
|
||||
}
|
||||
|
||||
void SkPictureStateTree::appendNode(size_t offset) {
|
||||
Node* n = static_cast<Node*>(fAlloc.allocThrow(sizeof(Node)));
|
||||
n->fOffset = SkToU32(offset);
|
||||
n->fFlags = 0;
|
||||
n->fParent = fCurrentState.fNode;
|
||||
n->fLevel = fCurrentState.fNode->fLevel + 1;
|
||||
n->fMatrix = fCurrentState.fMatrix;
|
||||
fCurrentState.fNode = n;
|
||||
}
|
||||
|
||||
void SkPictureStateTree::Iterator::init(const SkTDArray<void*>& draws, SkCanvas* canvas, Node* root) {
|
||||
SkASSERT(!fValid);
|
||||
fDraws = &draws;
|
||||
fCanvas = canvas;
|
||||
fCurrentNode = root;
|
||||
fPlaybackMatrix = canvas->getTotalMatrix();
|
||||
fCurrentMatrix = NULL;
|
||||
fPlaybackIndex = 0;
|
||||
fSave = false;
|
||||
fValid = true;
|
||||
}
|
||||
|
||||
void SkPictureStateTree::Iterator::setCurrentMatrix(const SkMatrix* matrix) {
|
||||
SkASSERT(matrix);
|
||||
|
||||
if (matrix == fCurrentMatrix) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The matrix is in recording space, but we also inherit
|
||||
// a playback matrix from out target canvas.
|
||||
SkMatrix m = *matrix;
|
||||
m.postConcat(fPlaybackMatrix);
|
||||
fCanvas->setMatrix(m);
|
||||
fCurrentMatrix = matrix;
|
||||
}
|
||||
|
||||
uint32_t SkPictureStateTree::Iterator::finish() {
|
||||
if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) {
|
||||
fCanvas->restore();
|
||||
}
|
||||
|
||||
for (fCurrentNode = fCurrentNode->fParent; fCurrentNode;
|
||||
fCurrentNode = fCurrentNode->fParent) {
|
||||
// Note: we call restore() twice when both flags are set.
|
||||
if (fCurrentNode->fFlags & Node::kSave_Flag) {
|
||||
fCanvas->restore();
|
||||
}
|
||||
if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) {
|
||||
fCanvas->restore();
|
||||
}
|
||||
}
|
||||
|
||||
fCanvas->setMatrix(fPlaybackMatrix);
|
||||
fCurrentMatrix = NULL;
|
||||
return kDrawComplete;
|
||||
}
|
||||
|
||||
uint32_t SkPictureStateTree::Iterator::nextDraw() {
|
||||
SkASSERT(this->isValid());
|
||||
if (fPlaybackIndex >= fDraws->count()) {
|
||||
return this->finish();
|
||||
}
|
||||
|
||||
Draw* draw = static_cast<Draw*>((*fDraws)[fPlaybackIndex]);
|
||||
Node* targetNode = draw->fNode;
|
||||
|
||||
if (fSave) {
|
||||
fCanvas->save();
|
||||
fSave = false;
|
||||
}
|
||||
|
||||
if (fCurrentNode != targetNode) {
|
||||
// If we're not at the target and we don't have a list of nodes to get there, we need to
|
||||
// figure out the path from our current node, to the target
|
||||
if (fNodes.count() == 0) {
|
||||
// Trace back up to a common ancestor, restoring to get our current state to match that
|
||||
// of the ancestor, and saving a list of nodes whose state we need to apply to get to
|
||||
// the target (we can restore up to the ancestor immediately, but we'll need to return
|
||||
// an offset for each node on the way down to the target, to apply the desired clips and
|
||||
// saveLayers, so it may take several draw() calls before the next draw actually occurs)
|
||||
Node* tmp = fCurrentNode;
|
||||
Node* ancestor = targetNode;
|
||||
while (tmp != ancestor) {
|
||||
uint16_t currentLevel = tmp->fLevel;
|
||||
uint16_t targetLevel = ancestor->fLevel;
|
||||
if (currentLevel >= targetLevel) {
|
||||
if (tmp != fCurrentNode && tmp->fFlags & Node::kSave_Flag) {
|
||||
fCanvas->restore();
|
||||
// restore() may change the matrix, so we need to reapply.
|
||||
fCurrentMatrix = NULL;
|
||||
}
|
||||
if (tmp->fFlags & Node::kSaveLayer_Flag) {
|
||||
fCanvas->restore();
|
||||
// restore() may change the matrix, so we need to reapply.
|
||||
fCurrentMatrix = NULL;
|
||||
}
|
||||
tmp = tmp->fParent;
|
||||
}
|
||||
if (currentLevel <= targetLevel) {
|
||||
fNodes.push(ancestor);
|
||||
ancestor = ancestor->fParent;
|
||||
}
|
||||
}
|
||||
|
||||
if (ancestor->fFlags & Node::kSave_Flag) {
|
||||
if (fCurrentNode != ancestor) {
|
||||
fCanvas->restore();
|
||||
// restore() may change the matrix, so we need to reapply.
|
||||
fCurrentMatrix = NULL;
|
||||
}
|
||||
if (targetNode != ancestor) {
|
||||
fCanvas->save();
|
||||
}
|
||||
}
|
||||
fCurrentNode = ancestor;
|
||||
}
|
||||
|
||||
// If we're not at the target node yet, we'll need to return an offset to make the caller
|
||||
// apply the next clip or saveLayer.
|
||||
if (fCurrentNode != targetNode) {
|
||||
uint32_t offset = fNodes.top()->fOffset;
|
||||
fCurrentNode = fNodes.top();
|
||||
fSave = fCurrentNode != targetNode && fCurrentNode->fFlags & Node::kSave_Flag;
|
||||
fNodes.pop();
|
||||
this->setCurrentMatrix(fCurrentNode->fMatrix);
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
|
||||
// If we got this far, the clip/saveLayer state is all set, so we can proceed to set the matrix
|
||||
// for the draw, and return its offset.
|
||||
this->setCurrentMatrix(draw->fMatrix);
|
||||
|
||||
++fPlaybackIndex;
|
||||
return draw->fOffset;
|
||||
}
|
@ -1,162 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkPictureStateTree_DEFINED
|
||||
#define SkPictureStateTree_DEFINED
|
||||
|
||||
#include "SkTDArray.h"
|
||||
#include "SkChunkAlloc.h"
|
||||
#include "SkDeque.h"
|
||||
#include "SkMatrix.h"
|
||||
#include "SkRefCnt.h"
|
||||
|
||||
class SkCanvas;
|
||||
|
||||
/**
|
||||
* Provides an interface that, given a sequence of draws into an SkPicture with corresponding
|
||||
* offsets, allows for playback of an arbitrary subset of the draws (note that Z-order is only
|
||||
* guaranteed if the draws are explicitly sorted).
|
||||
*/
|
||||
class SkPictureStateTree : public SkRefCnt {
|
||||
private:
|
||||
struct Node;
|
||||
public:
|
||||
SK_DECLARE_INST_COUNT(SkPictureStateTree)
|
||||
|
||||
/**
|
||||
* A draw call, stores offset into command buffer, a pointer to the matrix, and a pointer to
|
||||
* the node in the tree that corresponds to its clip/layer state
|
||||
*/
|
||||
struct Draw {
|
||||
SkMatrix* fMatrix;
|
||||
Node* fNode;
|
||||
uint32_t fOffset;
|
||||
bool operator<(const Draw& other) const { return fOffset < other.fOffset; }
|
||||
};
|
||||
|
||||
class Iterator;
|
||||
|
||||
SkPictureStateTree();
|
||||
~SkPictureStateTree();
|
||||
|
||||
/**
|
||||
* Creates and returns a struct representing a draw at the given offset.
|
||||
*/
|
||||
Draw* appendDraw(size_t offset);
|
||||
|
||||
/**
|
||||
* Given a list of draws, and a canvas, initialize an iterator that produces the correct
|
||||
* sequence of offsets into the command buffer to carry out those calls with correct
|
||||
* matrix/clip state. This handles saves/restores, and does all necessary matrix setup.
|
||||
*/
|
||||
void initIterator(SkPictureStateTree::Iterator* iter,
|
||||
const SkTDArray<void*>& draws,
|
||||
SkCanvas* canvas);
|
||||
|
||||
void appendSave();
|
||||
void appendSaveLayer(size_t offset);
|
||||
void appendRestore();
|
||||
void appendTransform(const SkMatrix& trans);
|
||||
void appendClip(size_t offset);
|
||||
|
||||
/**
|
||||
* Call this immediately after an appendRestore call that is associated
|
||||
* a save or saveLayer that was removed from the command stream
|
||||
* due to a command pattern optimization in SkPicture.
|
||||
*/
|
||||
void saveCollapsed();
|
||||
|
||||
/**
|
||||
* Playback helper
|
||||
*/
|
||||
class Iterator {
|
||||
public:
|
||||
/** Returns the next op offset needed to create the drawing state
|
||||
required by the queued up draw operation or the offset of the queued
|
||||
up draw operation itself. In the latter case, the next draw operation
|
||||
will move into the queued up slot.
|
||||
It retuns kDrawComplete when done.
|
||||
TODO: this might be better named nextOp
|
||||
*/
|
||||
uint32_t nextDraw();
|
||||
static const uint32_t kDrawComplete = SK_MaxU32;
|
||||
Iterator() : fValid(false) { }
|
||||
bool isValid() const { return fValid; }
|
||||
|
||||
private:
|
||||
void init(const SkTDArray<void*>& draws, SkCanvas* canvas, Node* root);
|
||||
|
||||
void setCurrentMatrix(const SkMatrix*);
|
||||
|
||||
// The draws this iterator is associated with
|
||||
const SkTDArray<void*>* fDraws;
|
||||
|
||||
// canvas this is playing into (so we can insert saves/restores as necessary)
|
||||
SkCanvas* fCanvas;
|
||||
|
||||
// current state node
|
||||
Node* fCurrentNode;
|
||||
|
||||
// List of nodes whose state we need to apply to reach TargetNode
|
||||
SkTDArray<Node*> fNodes;
|
||||
|
||||
// The matrix of the canvas we're playing back into
|
||||
SkMatrix fPlaybackMatrix;
|
||||
|
||||
// Cache of current matrix, so we can avoid redundantly setting it
|
||||
const SkMatrix* fCurrentMatrix;
|
||||
|
||||
// current position in the array of draws
|
||||
int fPlaybackIndex;
|
||||
// Whether or not we need to do a save next iteration
|
||||
bool fSave;
|
||||
|
||||
// Whether or not this is a valid iterator (the default public constructor sets this false)
|
||||
bool fValid;
|
||||
|
||||
uint32_t finish();
|
||||
|
||||
friend class SkPictureStateTree;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
void appendNode(size_t offset);
|
||||
|
||||
SkChunkAlloc fAlloc;
|
||||
// Needed by saveCollapsed() because nodes do not currently store
|
||||
// references to their children. If they did, we could just retrieve the
|
||||
// last added child.
|
||||
Node* fLastRestoredNode;
|
||||
|
||||
// The currently active state
|
||||
Draw fCurrentState;
|
||||
// A stack of states for tracking save/restores
|
||||
SkDeque fStateStack;
|
||||
|
||||
// Represents a notable piece of state that requires an offset into the command buffer,
|
||||
// corresponding to a clip/saveLayer/etc call, to apply.
|
||||
struct Node {
|
||||
Node* fParent;
|
||||
uint32_t fOffset;
|
||||
uint16_t fLevel;
|
||||
uint16_t fFlags;
|
||||
SkMatrix* fMatrix;
|
||||
enum Flags {
|
||||
kSave_Flag = 0x1,
|
||||
kSaveLayer_Flag = 0x2
|
||||
};
|
||||
};
|
||||
|
||||
Node fRoot;
|
||||
SkMatrix fRootMatrix;
|
||||
|
||||
typedef SkRefCnt INHERITED;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,123 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkBBHFactory.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkPictureRecorder.h"
|
||||
#include "SkPictureStateTree.h"
|
||||
#include "Test.h"
|
||||
|
||||
static SkPicture* draw_scene(SkBBHFactory* bbhFactory) {
|
||||
SkPictureRecorder recorder;
|
||||
SkCanvas* canvas = recorder.beginRecording(200, 200, bbhFactory, 0);
|
||||
|
||||
SkPaint p1, p2;
|
||||
p1.setStyle(SkPaint::kFill_Style);
|
||||
p1.setARGB(0x80, 0, 0xff, 0);
|
||||
p2.setStyle(SkPaint::kFill_Style);
|
||||
p2.setARGB(0x80, 0xff, 0, 0);
|
||||
|
||||
canvas->drawColor(SK_ColorWHITE);
|
||||
|
||||
// This is intended to exercise some tricky SkPictureStateTree code paths when
|
||||
// played back with various clips:
|
||||
//
|
||||
// * cleanup/rewind when the last draw is not at the root of the state tree.
|
||||
// * state nodes with both kSave_Flag & kSaveLayer_Flag set.
|
||||
// * state tree transitions which implicitly reset the matrix via. restore().
|
||||
|
||||
canvas->save();
|
||||
canvas->translate(10, 10);
|
||||
|
||||
canvas->drawRect(SkRect::MakeWH(100, 50), p1);
|
||||
canvas->drawRect(SkRect::MakeWH(50, 100), p2);
|
||||
|
||||
SkRect layerBounds = SkRect::MakeXYWH(0, 0, 90, 90);
|
||||
canvas->saveLayer(&layerBounds, NULL);
|
||||
canvas->save();
|
||||
canvas->clipRect(layerBounds);
|
||||
|
||||
canvas->save();
|
||||
canvas->clipRect(SkRect::MakeWH(25, 25));
|
||||
canvas->rotate(90);
|
||||
canvas->drawRect(SkRect::MakeWH(100, 50), p1);
|
||||
canvas->restore();
|
||||
|
||||
canvas->save();
|
||||
canvas->clipRect(SkRect::MakeWH(25, 25));
|
||||
canvas->save();
|
||||
canvas->rotate(90);
|
||||
canvas->drawRect(SkRect::MakeWH(50, 100), p2);
|
||||
canvas->restore();
|
||||
canvas->drawRect(SkRect::MakeWH(100, 50), p1);
|
||||
canvas->restore();
|
||||
canvas->drawRect(SkRect::MakeXYWH(99, 99, 1, 1), p1);
|
||||
canvas->restore();
|
||||
canvas->restore();
|
||||
|
||||
canvas->restore();
|
||||
|
||||
return recorder.endRecording();
|
||||
}
|
||||
|
||||
static void check_bms(skiatest::Reporter* reporter, const SkBitmap& bm1, const SkBitmap& bm2) {
|
||||
SkASSERT(bm1.getSize() == bm2.getSize());
|
||||
REPORTER_ASSERT(reporter, 0 == memcmp(bm1.getAddr(0, 0), bm2.getAddr(0, 0), bm1.getSize()));
|
||||
}
|
||||
|
||||
static void test_reference_picture(skiatest::Reporter* reporter) {
|
||||
SkRTreeFactory bbhFactory;
|
||||
|
||||
SkAutoTUnref<SkPicture> bbhPicture(draw_scene(&bbhFactory));
|
||||
SkAutoTUnref<SkPicture> referencePicture(draw_scene(NULL));
|
||||
|
||||
SkBitmap referenceBitmap;
|
||||
referenceBitmap.allocN32Pixels(100, 100);
|
||||
SkCanvas referenceCanvas(referenceBitmap);
|
||||
|
||||
SkBitmap bbhBitmap;
|
||||
bbhBitmap.allocN32Pixels(100, 100);
|
||||
SkCanvas bbhCanvas(bbhBitmap);
|
||||
|
||||
referenceCanvas.drawColor(SK_ColorTRANSPARENT);
|
||||
referenceCanvas.drawPicture(referencePicture.get());
|
||||
bbhCanvas.drawColor(SK_ColorTRANSPARENT);
|
||||
bbhCanvas.drawPicture(bbhPicture.get());
|
||||
REPORTER_ASSERT(reporter,
|
||||
referenceCanvas.getSaveCount() == bbhCanvas.getSaveCount());
|
||||
REPORTER_ASSERT(reporter,
|
||||
referenceCanvas.getTotalMatrix() == bbhCanvas.getTotalMatrix());
|
||||
check_bms(reporter, referenceBitmap, bbhBitmap);
|
||||
|
||||
referenceCanvas.drawColor(SK_ColorTRANSPARENT);
|
||||
referenceCanvas.clipRect(SkRect::MakeWH(50, 50));
|
||||
referenceCanvas.drawPicture(referencePicture.get());
|
||||
bbhCanvas.drawColor(SK_ColorTRANSPARENT);
|
||||
bbhCanvas.clipRect(SkRect::MakeWH(50, 50));
|
||||
bbhCanvas.drawPicture(bbhPicture.get());
|
||||
REPORTER_ASSERT(reporter,
|
||||
referenceCanvas.getSaveCount() == bbhCanvas.getSaveCount());
|
||||
REPORTER_ASSERT(reporter,
|
||||
referenceCanvas.getTotalMatrix() == bbhCanvas.getTotalMatrix());
|
||||
check_bms(reporter, referenceBitmap, bbhBitmap);
|
||||
|
||||
referenceCanvas.drawColor(SK_ColorTRANSPARENT);
|
||||
referenceCanvas.clipRect(SkRect::MakeWH(10, 10));
|
||||
referenceCanvas.drawPicture(referencePicture.get());
|
||||
bbhCanvas.drawColor(SK_ColorTRANSPARENT);
|
||||
bbhCanvas.clipRect(SkRect::MakeWH(10, 10));
|
||||
bbhCanvas.drawPicture(bbhPicture.get());
|
||||
REPORTER_ASSERT(reporter,
|
||||
referenceCanvas.getSaveCount() == bbhCanvas.getSaveCount());
|
||||
REPORTER_ASSERT(reporter,
|
||||
referenceCanvas.getTotalMatrix() == bbhCanvas.getTotalMatrix());
|
||||
check_bms(reporter, referenceBitmap, bbhBitmap);
|
||||
}
|
||||
|
||||
DEF_TEST(PictureStateTree, reporter) {
|
||||
test_reference_picture(reporter);
|
||||
}
|
@ -20,20 +20,17 @@
|
||||
// This arose from http://crbug.com/401593 which has
|
||||
// https://code.google.com/p/skia/issues/detail?id=1291 as its root cause.
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
class Drawer {
|
||||
public:
|
||||
explicit Drawer()
|
||||
: fImageInfo(SkImageInfo::MakeN32Premul(200,100))
|
||||
{
|
||||
fCircleBM.allocPixels( SkImageInfo::MakeN32Premul(100,100) );
|
||||
explicit Drawer() : fImageInfo(SkImageInfo::MakeN32Premul(200, 100)) {
|
||||
fCircleBM.allocPixels(SkImageInfo::MakeN32Premul(100, 100));
|
||||
SkCanvas canvas(fCircleBM);
|
||||
canvas.clear(0xffffffff);
|
||||
SkPaint circlePaint;
|
||||
circlePaint.setColor(0xff000000);
|
||||
canvas.drawCircle(50,50,50,circlePaint);
|
||||
canvas.drawCircle(50, 50, 50, circlePaint);
|
||||
}
|
||||
|
||||
const SkImageInfo& imageInfo() const { return fImageInfo; }
|
||||
@ -48,15 +45,16 @@ class Drawer {
|
||||
SkPaint layerPaint;
|
||||
layerPaint.setColor(0xff000000);
|
||||
layerPaint.setXfermodeMode(mode);
|
||||
SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fImageInfo.width()),SkIntToScalar(fImageInfo.height())));
|
||||
SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fImageInfo.width()),
|
||||
SkIntToScalar(fImageInfo.height())));
|
||||
|
||||
canvas->clipRect(clipRect);
|
||||
canvas->clear(0xff000000);
|
||||
|
||||
canvas->saveLayer(NULL,&blackPaint);
|
||||
canvas->drawRect(canvasRect,greenPaint);
|
||||
canvas->saveLayer(NULL,&layerPaint);
|
||||
canvas->drawBitmapRect(fCircleBM,SkRect::MakeXYWH(20,20,60,60),&blackPaint);
|
||||
canvas->saveLayer(NULL, &blackPaint);
|
||||
canvas->drawRect(canvasRect, greenPaint);
|
||||
canvas->saveLayer(NULL, &layerPaint);
|
||||
canvas->drawBitmapRect(fCircleBM, SkRect::MakeXYWH(20,20,60,60), &blackPaint);
|
||||
canvas->restore();
|
||||
canvas->restore();
|
||||
}
|
||||
@ -69,7 +67,6 @@ class Drawer {
|
||||
class RecordingStrategy {
|
||||
public:
|
||||
virtual ~RecordingStrategy() {}
|
||||
virtual void init(const SkImageInfo&) = 0;
|
||||
virtual const SkBitmap& recordAndReplay(const Drawer& drawer,
|
||||
const SkRect& intoClip,
|
||||
SkXfermode::Mode) = 0;
|
||||
@ -78,9 +75,7 @@ class RecordingStrategy {
|
||||
class BitmapBackedCanvasStrategy : public RecordingStrategy {
|
||||
// This version just draws into a bitmap-backed canvas.
|
||||
public:
|
||||
BitmapBackedCanvasStrategy() {}
|
||||
|
||||
virtual void init(const SkImageInfo& imageInfo) {
|
||||
BitmapBackedCanvasStrategy(const SkImageInfo& imageInfo) {
|
||||
fBitmap.allocPixels(imageInfo);
|
||||
}
|
||||
|
||||
@ -99,18 +94,15 @@ class BitmapBackedCanvasStrategy : public RecordingStrategy {
|
||||
SkBitmap fBitmap;
|
||||
};
|
||||
|
||||
class DeprecatedRecorderStrategy : public RecordingStrategy {
|
||||
// This version draws the entire scene into an SkPictureRecorder,
|
||||
// using the deprecated recording backend.
|
||||
class PictureStrategy : public RecordingStrategy {
|
||||
// This version draws the entire scene into an SkPictureRecorder.
|
||||
// Then it then replays the scene through a clip rectangle.
|
||||
// This backend proved to be buggy.
|
||||
public:
|
||||
DeprecatedRecorderStrategy() {}
|
||||
|
||||
virtual void init(const SkImageInfo& imageInfo) {
|
||||
PictureStrategy(const SkImageInfo& imageInfo) {
|
||||
fBitmap.allocPixels(imageInfo);
|
||||
fWidth = imageInfo.width();
|
||||
fHeight= imageInfo.height();
|
||||
fWidth = imageInfo.width();
|
||||
fHeight = imageInfo.height();
|
||||
}
|
||||
|
||||
virtual const SkBitmap& recordAndReplay(const Drawer& drawer,
|
||||
@ -120,47 +112,9 @@ class DeprecatedRecorderStrategy : public RecordingStrategy {
|
||||
SkTileGridFactory factory(tileGridInfo);
|
||||
SkPictureRecorder recorder;
|
||||
SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fWidth),SkIntToScalar(fHeight)));
|
||||
SkCanvas* canvas = recorder.DEPRECATED_beginRecording( SkIntToScalar(fWidth), SkIntToScalar(fHeight), &factory);
|
||||
drawer.draw(canvas, canvasRect, mode);
|
||||
SkAutoTDelete<SkPicture> picture(recorder.endRecording());
|
||||
|
||||
SkCanvas replayCanvas(fBitmap);
|
||||
replayCanvas.clear(0xffffffff);
|
||||
replayCanvas.clipRect(intoClip);
|
||||
picture->playback(&replayCanvas);
|
||||
|
||||
return fBitmap;
|
||||
}
|
||||
|
||||
private:
|
||||
SkBitmap fBitmap;
|
||||
int fWidth;
|
||||
int fHeight;
|
||||
};
|
||||
|
||||
class NewRecordingStrategy : public RecordingStrategy {
|
||||
// This version draws the entire scene into an SkPictureRecorder,
|
||||
// using the new recording backend.
|
||||
// Then it then replays the scene through a clip rectangle.
|
||||
// This backend proved to be buggy.
|
||||
public:
|
||||
NewRecordingStrategy() {}
|
||||
|
||||
virtual void init(const SkImageInfo& imageInfo) {
|
||||
fBitmap.allocPixels(imageInfo);
|
||||
fWidth = imageInfo.width();
|
||||
fHeight= imageInfo.height();
|
||||
}
|
||||
|
||||
virtual const SkBitmap& recordAndReplay(const Drawer& drawer,
|
||||
const SkRect& intoClip,
|
||||
SkXfermode::Mode mode) {
|
||||
SkTileGridFactory::TileGridInfo tileGridInfo = { {100,100}, {0,0}, {0,0} };
|
||||
SkTileGridFactory factory(tileGridInfo);
|
||||
SkPictureRecorder recorder;
|
||||
SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fWidth),SkIntToScalar(fHeight)));
|
||||
SkCanvas* canvas = recorder.EXPERIMENTAL_beginRecording( SkIntToScalar(fWidth), SkIntToScalar(fHeight), &factory);
|
||||
|
||||
SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(fWidth),
|
||||
SkIntToScalar(fHeight),
|
||||
&factory);
|
||||
drawer.draw(canvas, canvasRect, mode);
|
||||
SkAutoTDelete<SkPicture> picture(recorder.endRecording());
|
||||
|
||||
@ -177,64 +131,45 @@ class NewRecordingStrategy : public RecordingStrategy {
|
||||
int fHeight;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
DEF_TEST(SkRecordingAccuracyXfermode, reporter) {
|
||||
#define FINEGRAIN 0
|
||||
|
||||
const Drawer drawer;
|
||||
|
||||
BitmapBackedCanvasStrategy golden; // This is the expected result.
|
||||
DeprecatedRecorderStrategy deprecatedRecording;
|
||||
NewRecordingStrategy newRecording;
|
||||
|
||||
golden.init(drawer.imageInfo());
|
||||
deprecatedRecording.init(drawer.imageInfo());
|
||||
newRecording.init(drawer.imageInfo());
|
||||
BitmapBackedCanvasStrategy golden(drawer.imageInfo());
|
||||
PictureStrategy picture(drawer.imageInfo());
|
||||
|
||||
#if !FINEGRAIN
|
||||
unsigned numErrors = 0;
|
||||
SkString errors;
|
||||
#endif
|
||||
|
||||
for (int iMode = 0; iMode < int(SkXfermode::kLastMode) ; iMode++ ) {
|
||||
const SkRect& clip = SkRect::MakeXYWH(100,0,100,100);
|
||||
for (int iMode = 0; iMode < int(SkXfermode::kLastMode); iMode++) {
|
||||
const SkRect& clip = SkRect::MakeXYWH(100, 0, 100, 100);
|
||||
SkXfermode::Mode mode = SkXfermode::Mode(iMode);
|
||||
|
||||
const SkBitmap& goldenBM = golden.recordAndReplay(drawer, clip, mode);
|
||||
const SkBitmap& deprecatedBM = deprecatedRecording.recordAndReplay(drawer, clip, mode);
|
||||
const SkBitmap& newRecordingBM = newRecording.recordAndReplay(drawer, clip, mode);
|
||||
const SkBitmap& pictureBM = picture.recordAndReplay(drawer, clip, mode);
|
||||
|
||||
size_t pixelsSize = goldenBM.getSize();
|
||||
REPORTER_ASSERT( reporter, pixelsSize == deprecatedBM.getSize() );
|
||||
REPORTER_ASSERT( reporter, pixelsSize == newRecordingBM.getSize() );
|
||||
REPORTER_ASSERT(reporter, pixelsSize == pictureBM.getSize());
|
||||
|
||||
// The pixel arrays should match.
|
||||
#if FINEGRAIN
|
||||
REPORTER_ASSERT_MESSAGE( reporter,
|
||||
0==memcmp( goldenBM.getPixels(), deprecatedBM.getPixels(), pixelsSize ),
|
||||
"Tiled bitmap is wrong");
|
||||
REPORTER_ASSERT_MESSAGE( reporter,
|
||||
0==memcmp( goldenBM.getPixels(), recordingBM.getPixels(), pixelsSize ),
|
||||
"SkRecorder bitmap is wrong");
|
||||
REPORTER_ASSERT(reporter,
|
||||
0 == memcmp(goldenBM.getPixels(), pictureBM.getPixels(), pixelsSize));
|
||||
#else
|
||||
if ( memcmp( goldenBM.getPixels(), deprecatedBM.getPixels(), pixelsSize ) ) {
|
||||
if (memcmp(goldenBM.getPixels(), pictureBM.getPixels(), pixelsSize)) {
|
||||
numErrors++;
|
||||
SkString str;
|
||||
str.printf("For SkXfermode %d %s: Deprecated recorder bitmap is wrong\n", iMode, SkXfermode::ModeName(mode));
|
||||
errors.append(str);
|
||||
}
|
||||
if ( memcmp( goldenBM.getPixels(), newRecordingBM.getPixels(), pixelsSize ) ) {
|
||||
numErrors++;
|
||||
SkString str;
|
||||
str.printf("For SkXfermode %d %s: SkPictureRecorder bitmap is wrong\n", iMode, SkXfermode::ModeName(mode));
|
||||
errors.append(str);
|
||||
errors.appendf("For SkXfermode %d %s: SkPictureRecorder bitmap is wrong\n",
|
||||
iMode, SkXfermode::ModeName(mode));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !FINEGRAIN
|
||||
REPORTER_ASSERT_MESSAGE( reporter, 0==numErrors, errors.c_str() );
|
||||
REPORTER_ASSERT_MESSAGE(reporter, 0 == numErrors, errors.c_str());
|
||||
#endif
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user