/*
* Copyright 2011 Google Inc .
*
* Use of this source code is governed by a BSD - style license that can be
* found in the LICENSE file .
*/
#include "src/core/SkPictureRecord.h"
#include "include/core/SkBlendMode.h"
#include "include/core/SkImageFilter.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkPoint3.h"
#include "include/core/SkRRect.h"
#include "include/core/SkRSXform.h"
#include "include/core/SkRect.h"
#include "include/core/SkRegion.h"
#include "include/core/SkShader.h"
#include "include/core/SkSurface.h"
#include "include/core/SkTextBlob.h"
#include "include/core/SkTileMode.h"
#include "include/private/base/SkPoint_impl.h"
#include "include/private/base/SkTo.h"
#include "include/private/chromium/Slug.h"
#include "src/core/SkCanvasPriv.h"
#include "src/core/SkDrawShadowInfo.h"
#include "src/core/SkMatrixPriv.h"
#include "src/core/SkSamplingPriv.h"
#include "src/utils/SkPatchUtils.h"
#include <utility>
class SkSurfaceProps;
enum class SkClipOp;
struct SkISize;
struct SkImageInfo;
using namespace skia_private;
enum {
// just need a value that save or getSaveCount would never return
kNoInitialSave = -1 ,
};
// A lot of basic types get stored as a uint32_t: bools, ints, paint indices, etc.
static int const kUInt32Size = 4 ;
SkPictureRecord::SkPictureRecord(const SkIRect& dimensions, uint32_t flags)
: SkCanvasVirtualEnforcer<SkCanvas>(dimensions)
, fRecordFlags(flags)
, fInitialSaveCount(kNoInitialSave) {
}
SkPictureRecord::SkPictureRecord(const SkISize& dimensions, uint32_t flags)
: SkPictureRecord(SkIRect::MakeSize(dimensions), flags) {}
void SkPictureRecord::willSave() {
// record the offset to us, making it non-positive to distinguish a save
// from a clip entry.
fRestoreOffsetStack.push_back(-(int32_t)fWriter.bytesWritten());
this->recordSave();
this->SkCanvasVirtualEnforcer<SkCanvas>::willSave();
}
void SkPictureRecord::recordSave() {
// op only
size_t size = sizeof (kUInt32Size);
size_t initialOffset = this->addDraw(SAVE, &size);
this->validate(initialOffset, size);
}
SkCanvas::SaveLayerStrategy SkPictureRecord::getSaveLayerStrategy(const SaveLayerRec& rec) {
// record the offset to us, making it non-positive to distinguish a save
// from a clip entry.
fRestoreOffsetStack.push_back(-(int32_t)fWriter.bytesWritten());
this->recordSaveLayer(rec);
(void )this->SkCanvasVirtualEnforcer<SkCanvas>::getSaveLayerStrategy(rec);
/* No need for a (potentially very big) layer which we don't actually need
at this time ( and may not be able to afford since during record our
clip starts out the size of the picture , which is often much larger
than the size of the actual device we ' ll use during playback ) .
*/
return kNoLayer_SaveLayerStrategy;
}
bool SkPictureRecord::onDoSaveBehind(const SkRect* subset) {
fRestoreOffsetStack.push_back(-(int32_t)fWriter.bytesWritten());
size_t size = sizeof (kUInt32Size) + sizeof (uint32_t); // op + flags
uint32_t flags = 0 ;
if (subset) {
flags |= SAVEBEHIND_HAS_SUBSET;
size += sizeof (*subset);
}
size_t initialOffset = this->addDraw(SAVE_BEHIND, &size);
this->addInt(flags);
if (subset) {
this->addRect(*subset);
}
this->validate(initialOffset, size);
return false ;
}
void SkPictureRecord::recordSaveLayer(const SaveLayerRec& rec) {
// op + flatflags
size_t size = 2 * kUInt32Size;
uint32_t flatFlags = 0 ;
uint32_t filterCount = SkToU32(rec.fFilters.size());
if (rec.fBounds) {
flatFlags |= SAVELAYERREC_HAS_BOUNDS;
size += sizeof (*rec.fBounds);
}
if (rec.fPaint) {
flatFlags |= SAVELAYERREC_HAS_PAINT;
size += sizeof (uint32_t); // index
}
if (rec.fBackdrop) {
flatFlags |= SAVELAYERREC_HAS_BACKDROP;
size += sizeof (uint32_t); // (paint) index
}
if (rec.fSaveLayerFlags) {
flatFlags |= SAVELAYERREC_HAS_FLAGS;
size += sizeof (uint32_t);
}
if (SkCanvasPriv::GetBackdropScaleFactor(rec) != 1 .f) {
flatFlags |= SAVELAYERREC_HAS_BACKDROP_SCALE;
size += sizeof (SkScalar);
}
if (filterCount) {
flatFlags |= SAVELAYERREC_HAS_MULTIPLE_FILTERS;
size += sizeof (uint32_t); // count
size += sizeof (uint32_t) * filterCount; // N (paint) indices
}
if (rec.fBackdropTileMode != SkTileMode::kClamp) {
flatFlags |= SAVELAYERREC_HAS_BACKDROP_TILEMODE;
size += sizeof (uint32_t); // SkTileMode
}
const size_t initialOffset = this->addDraw(SAVE_LAYER_SAVELAYERREC, &size);
this->addInt(flatFlags);
if (flatFlags & SAVELAYERREC_HAS_BOUNDS) {
this->addRect(*rec.fBounds);
}
if (flatFlags & SAVELAYERREC_HAS_PAINT) {
this->addPaintPtr(rec.fPaint);
}
if (flatFlags & SAVELAYERREC_HAS_BACKDROP) {
// overkill, but we didn't already track single flattenables, so using a paint for that
SkPaint paint;
paint.setImageFilter(sk_ref_sp(const_cast <SkImageFilter*>(rec.fBackdrop)));
this->addPaint(paint);
}
if (flatFlags & SAVELAYERREC_HAS_FLAGS) {
this->addInt(rec.fSaveLayerFlags);
}
if (flatFlags & SAVELAYERREC_HAS_BACKDROP_SCALE) {
this->addScalar(SkCanvasPriv::GetBackdropScaleFactor(rec));
}
if (flatFlags & SAVELAYERREC_HAS_MULTIPLE_FILTERS) {
this->addInt(filterCount);
for (uint32_t i = 0 ; i < filterCount; ++i) {
// overkill to store a paint, oh well.
SkPaint paint;
paint.setImageFilter(rec.fFilters[i]);
this->addPaint(paint);
}
}
if (rec.fBackdropTileMode != SkTileMode::kClamp) {
this->addInt((int ) rec.fBackdropTileMode);
}
this->validate(initialOffset, size);
}
#ifdef SK_DEBUG
/*
* Read the op code from ' offset ' in ' writer ' and extract the size too .
*/
static DrawType peek_op_and_size(SkWriter32* writer, size_t offset, uint32_t* size) {
uint32_t peek = writer->readTAt<uint32_t>(offset);
uint32_t op;
UNPACK_8_24(peek, op, *size);
if (MASK_24 == *size) {
// size required its own slot right after the op code
*size = writer->readTAt<uint32_t>(offset + kUInt32Size);
}
return (DrawType) op;
}
#endif //SK_DEBUG
void SkPictureRecord::willRestore() {
#if 0
SkASSERT(fRestoreOffsetStack.count() > 1 );
#endif
// check for underflow
if (fRestoreOffsetStack.empty()) {
return ;
}
this->recordRestore();
fRestoreOffsetStack.pop_back();
this->SkCanvasVirtualEnforcer<SkCanvas>::willRestore();
}
void SkPictureRecord::recordRestore(bool fillInSkips) {
if (fillInSkips) {
this->fillRestoreOffsetPlaceholdersForCurrentStackLevel((uint32_t)fWriter.bytesWritten());
}
size_t size = 1 * kUInt32Size; // RESTORE consists solely of 1 op code
size_t initialOffset = this->addDraw(RESTORE, &size);
this->validate(initialOffset, size);
}
void SkPictureRecord::recordTranslate(const SkMatrix& m) {
SkASSERT(SkMatrix::kTranslate_Mask == m.getType());
// op + dx + dy
size_t size = 1 * kUInt32Size + 2 * sizeof (SkScalar);
size_t initialOffset = this->addDraw(TRANSLATE, &size);
this->addScalar(m.getTranslateX());
this->addScalar(m.getTranslateY());
this->validate(initialOffset, size);
}
void SkPictureRecord::recordScale(const SkMatrix& m) {
SkASSERT(SkMatrix::kScale_Mask == m.getType());
// op + sx + sy
size_t size = 1 * kUInt32Size + 2 * sizeof (SkScalar);
size_t initialOffset = this->addDraw(SCALE, &size);
this->addScalar(m.getScaleX());
this->addScalar(m.getScaleY());
this->validate(initialOffset, size);
}
void SkPictureRecord::didConcat44(const SkM44& m) {
this->validate(fWriter.bytesWritten(), 0 );
// op + matrix
size_t size = kUInt32Size + 16 * sizeof (SkScalar);
size_t initialOffset = this->addDraw(CONCAT44, &size);
fWriter.write(SkMatrixPriv::M44ColMajor(m), 16 * sizeof (SkScalar));
this->validate(initialOffset, size);
this->SkCanvasVirtualEnforcer<SkCanvas>::didConcat44(m);
}
void SkPictureRecord::didSetM44(const SkM44& m) {
this->validate(fWriter.bytesWritten(), 0 );
// op + matrix
size_t size = kUInt32Size + 16 * sizeof (SkScalar);
size_t initialOffset = this->addDraw(SET_M44, &size);
fWriter.write(SkMatrixPriv::M44ColMajor(m), 16 * sizeof (SkScalar));
this->validate(initialOffset, size);
this->SkCanvasVirtualEnforcer<SkCanvas>::didSetM44(m);
}
void SkPictureRecord::didScale(SkScalar x, SkScalar y) {
this->didConcat44(SkM44::Scale(x, y));
}
void SkPictureRecord::didTranslate(SkScalar x, SkScalar y) {
this->didConcat44(SkM44::Translate(x, y));
}
void SkPictureRecord::recordConcat(const SkMatrix& matrix) {
this->validate(fWriter.bytesWritten(), 0 );
// op + matrix
size_t size = kUInt32Size + SkMatrixPriv::WriteToMemory(matrix, nullptr);
size_t initialOffset = this->addDraw(CONCAT, &size);
this->addMatrix(matrix);
this->validate(initialOffset, size);
}
void SkPictureRecord::fillRestoreOffsetPlaceholdersForCurrentStackLevel(uint32_t restoreOffset) {
int32_t offset = fRestoreOffsetStack.back();
while (offset > 0 ) {
uint32_t peek = fWriter.readTAt<uint32_t>(offset);
fWriter.overwriteTAt(offset, restoreOffset);
offset = peek;
}
#ifdef SK_DEBUG
// offset of 0 has been disabled, so we skip it
if (offset > 0 ) {
// assert that the final offset value points to a save verb
uint32_t opSize;
DrawType drawOp = peek_op_and_size(&fWriter, -offset, &opSize);
SkASSERT(SAVE == drawOp || SAVE_LAYER_SAVELAYERREC == drawOp);
}
#endif
}
void SkPictureRecord::beginRecording() {
// we have to call this *after* our constructor, to ensure that it gets
// recorded. This is balanced by restoreToCount() call from endRecording,
// which in-turn calls our overridden restore(), so those get recorded too.
fInitialSaveCount = this->save();
}
void SkPictureRecord::endRecording() {
SkASSERT(kNoInitialSave != fInitialSaveCount);
this->restoreToCount(fInitialSaveCount);
}
size_t SkPictureRecord::recordRestoreOffsetPlaceholder() {
if (fRestoreOffsetStack.empty()) {
return -1 ;
}
// The RestoreOffset field is initially filled with a placeholder
// value that points to the offset of the previous RestoreOffset
// in the current stack level, thus forming a linked list so that
// the restore offsets can be filled in when the corresponding
// restore command is recorded.
int32_t prevOffset = fRestoreOffsetStack.back();
size_t offset = fWriter.bytesWritten();
this->addInt(prevOffset);
fRestoreOffsetStack.back() = SkToU32(offset);
return offset;
}
void SkPictureRecord::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
this->recordClipRect(rect, op, kSoft_ClipEdgeStyle == edgeStyle);
this->SkCanvasVirtualEnforcer<SkCanvas>::onClipRect(rect, op, edgeStyle);
}
size_t SkPictureRecord::recordClipRect(const SkRect& rect, SkClipOp op, bool doAA) {
// id + rect + clip params
size_t size = 1 * kUInt32Size + sizeof (rect) + 1 * kUInt32Size;
// recordRestoreOffsetPlaceholder doesn't always write an offset
if (!fRestoreOffsetStack.empty()) {
// + restore offset
size += kUInt32Size;
}
size_t initialOffset = this->addDraw(CLIP_RECT, &size);
this->addRect(rect);
this->addInt(ClipParams_pack(op, doAA));
size_t offset = this->recordRestoreOffsetPlaceholder();
this->validate(initialOffset, size);
return offset;
}
void SkPictureRecord::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
this->recordClipRRect(rrect, op, kSoft_ClipEdgeStyle == edgeStyle);
this->SkCanvasVirtualEnforcer<SkCanvas>::onClipRRect(rrect, op, edgeStyle);
}
size_t SkPictureRecord::recordClipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
// op + rrect + clip params
size_t size = 1 * kUInt32Size + SkRRect::kSizeInMemory + 1 * kUInt32Size;
// recordRestoreOffsetPlaceholder doesn't always write an offset
if (!fRestoreOffsetStack.empty()) {
// + restore offset
size += kUInt32Size;
}
size_t initialOffset = this->addDraw(CLIP_RRECT, &size);
this->addRRect(rrect);
this->addInt(ClipParams_pack(op, doAA));
size_t offset = recordRestoreOffsetPlaceholder();
this->validate(initialOffset, size);
return offset;
}
void SkPictureRecord::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
int pathID = this->addPathToHeap(path);
this->recordClipPath(pathID, op, kSoft_ClipEdgeStyle == edgeStyle);
this->SkCanvasVirtualEnforcer<SkCanvas>::onClipPath(path, op, edgeStyle);
}
size_t SkPictureRecord::recordClipPath(int pathID, SkClipOp op, bool doAA) {
// op + path index + clip params
size_t size = 3 * kUInt32Size;
// recordRestoreOffsetPlaceholder doesn't always write an offset
if (!fRestoreOffsetStack.empty()) {
// + restore offset
size += kUInt32Size;
}
size_t initialOffset = this->addDraw(CLIP_PATH, &size);
this->addInt(pathID);
this->addInt(ClipParams_pack(op, doAA));
size_t offset = recordRestoreOffsetPlaceholder();
this->validate(initialOffset, size);
return offset;
}
void SkPictureRecord::onClipShader(sk_sp<SkShader> cs, SkClipOp op) {
// Overkill to store a whole paint, but we don't have an existing structure to just store
// shaders. If size becomes an issue in the future, we can optimize this.
SkPaint paint;
paint.setShader(cs);
// op + paint index + clipop
size_t size = 3 * kUInt32Size;
size_t initialOffset = this->addDraw(CLIP_SHADER_IN_PAINT, &size);
this->addPaint(paint);
this->addInt((int )op);
this->validate(initialOffset, size);
this->SkCanvasVirtualEnforcer<SkCanvas>::onClipShader(std::move(cs), op);
}
void SkPictureRecord::onClipRegion(const SkRegion& region, SkClipOp op) {
this->recordClipRegion(region, op);
this->SkCanvasVirtualEnforcer<SkCanvas>::onClipRegion(region, op);
}
size_t SkPictureRecord::recordClipRegion(const SkRegion& region, SkClipOp op) {
// op + clip params + region
size_t size = 2 * kUInt32Size + region.writeToMemory(nullptr);
// recordRestoreOffsetPlaceholder doesn't always write an offset
if (!fRestoreOffsetStack.empty()) {
// + restore offset
size += kUInt32Size;
}
size_t initialOffset = this->addDraw(CLIP_REGION, &size);
this->addRegion(region);
this->addInt(ClipParams_pack(op, false ));
size_t offset = this->recordRestoreOffsetPlaceholder();
this->validate(initialOffset, size);
return offset;
}
void SkPictureRecord::onResetClip() {
if (!fRestoreOffsetStack.empty()) {
// Run back through any previous clip ops, and mark their offset to
// be 0, disabling their ability to trigger a jump-to-restore, otherwise
// they could hide this expansion of the clip.
this->fillRestoreOffsetPlaceholdersForCurrentStackLevel(0 );
}
size_t size = sizeof (kUInt32Size);
size_t initialOffset = this->addDraw(RESET_CLIP, &size);
this->validate(initialOffset, size);
this->SkCanvasVirtualEnforcer<SkCanvas>::onResetClip();
}
void SkPictureRecord::onDrawPaint(const SkPaint& paint) {
// op + paint index
size_t size = 2 * kUInt32Size;
size_t initialOffset = this->addDraw(DRAW_PAINT, &size);
this->addPaint(paint);
this->validate(initialOffset, size);
}
void SkPictureRecord::onDrawBehind(const SkPaint& paint) {
// logically the same as drawPaint, but with a diff enum
// op + paint index
size_t size = 2 * kUInt32Size;
size_t initialOffset = this->addDraw(DRAW_BEHIND_PAINT, &size);
this->addPaint(paint);
this->validate(initialOffset, size);
}
void SkPictureRecord::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
const SkPaint& paint) {
// op + paint index + mode + count + point data
size_t size = 4 * kUInt32Size + count * sizeof (SkPoint);
size_t initialOffset = this->addDraw(DRAW_POINTS, &size);
this->addPaint(paint);
this->addInt(mode);
this->addInt(SkToInt(count));
fWriter.writeMul4(pts, count * sizeof (SkPoint));
this->validate(initialOffset, size);
}
void SkPictureRecord::onDrawOval(const SkRect& oval, const SkPaint& paint) {
// op + paint index + rect
size_t size = 2 * kUInt32Size + sizeof (oval);
size_t initialOffset = this->addDraw(DRAW_OVAL, &size);
this->addPaint(paint);
this->addRect(oval);
this->validate(initialOffset, size);
}
void SkPictureRecord::onDrawArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
bool useCenter, const SkPaint& paint) {
// op + paint index + rect + start + sweep + bool (as int)
size_t size = 2 * kUInt32Size + sizeof (oval) + sizeof (startAngle) + sizeof (sweepAngle) +
sizeof (int );
size_t initialOffset = this->addDraw(DRAW_ARC, &size);
this->addPaint(paint);
this->addRect(oval);
this->addScalar(startAngle);
this->addScalar(sweepAngle);
this->addInt(useCenter);
this->validate(initialOffset, size);
}
void SkPictureRecord::onDrawRect(const SkRect& rect, const SkPaint& paint) {
// op + paint index + rect
size_t size = 2 * kUInt32Size + sizeof (rect);
size_t initialOffset = this->addDraw(DRAW_RECT, &size);
this->addPaint(paint);
this->addRect(rect);
this->validate(initialOffset, size);
}
void SkPictureRecord::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
// op + paint index + region
size_t regionBytes = region.writeToMemory(nullptr);
size_t size = 2 * kUInt32Size + regionBytes;
size_t initialOffset = this->addDraw(DRAW_REGION, &size);
this->addPaint(paint);
fWriter.writeRegion(region);
this->validate(initialOffset, size);
}
void SkPictureRecord::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
// op + paint index + rrect
size_t size = 2 * kUInt32Size + SkRRect::kSizeInMemory;
size_t initialOffset = this->addDraw(DRAW_RRECT, &size);
this->addPaint(paint);
this->addRRect(rrect);
this->validate(initialOffset, size);
}
void SkPictureRecord::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
const SkPaint& paint) {
// op + paint index + rrects
size_t size = 2 * kUInt32Size + SkRRect::kSizeInMemory * 2 ;
size_t initialOffset = this->addDraw(DRAW_DRRECT, &size);
this->addPaint(paint);
this->addRRect(outer);
this->addRRect(inner);
this->validate(initialOffset, size);
}
void SkPictureRecord::onDrawPath(const SkPath& path, const SkPaint& paint) {
// op + paint index + path index
size_t size = 3 * kUInt32Size;
size_t initialOffset = this->addDraw(DRAW_PATH, &size);
this->addPaint(paint);
this->addPath(path);
this->validate(initialOffset, size);
}
void SkPictureRecord::onDrawImage2(const SkImage* image, SkScalar x, SkScalar y,
const SkSamplingOptions& sampling, const SkPaint* paint) {
// op + paint_index + image_index + x + y
size_t size = 3 * kUInt32Size + 2 * sizeof (SkScalar) + SkSamplingPriv::FlatSize(sampling);
size_t initialOffset = this->addDraw(DRAW_IMAGE2, &size);
this->addPaintPtr(paint);
this->addImage(image);
this->addScalar(x);
this->addScalar(y);
this->addSampling(sampling);
this->validate(initialOffset, size);
}
void SkPictureRecord::onDrawImageRect2(const SkImage* image, const SkRect& src, const SkRect& dst,
const SkSamplingOptions& sampling, const SkPaint* paint,
SrcRectConstraint constraint) {
// id + paint_index + image_index + constraint
size_t size = 3 * kUInt32Size + 2 * sizeof (dst) + SkSamplingPriv::FlatSize(sampling) +
kUInt32Size;
size_t initialOffset = this->addDraw(DRAW_IMAGE_RECT2, &size);
this->addPaintPtr(paint);
this->addImage(image);
this->addRect(src);
this->addRect(dst);
this->addSampling(sampling);
this->addInt(constraint);
this->validate(initialOffset, size);
}
void SkPictureRecord::onDrawImageLattice2(const SkImage* image, const Lattice& lattice,
const SkRect& dst, SkFilterMode filter,
const SkPaint* paint) {
size_t latticeSize = SkCanvasPriv::WriteLattice(nullptr, lattice);
// op + paint index + image index + lattice + dst rect
size_t size = 3 * kUInt32Size + latticeSize + sizeof (dst) + sizeof (uint32_t); // filter
size_t initialOffset = this->addDraw(DRAW_IMAGE_LATTICE2, &size);
this->addPaintPtr(paint);
this->addImage(image);
(void )SkCanvasPriv::WriteLattice(fWriter.reservePad(latticeSize), lattice);
this->addRect(dst);
this->addInt(static_cast<uint32_t>(filter));
this->validate(initialOffset, size);
}
void SkPictureRecord::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
const SkPaint& paint) {
// op + paint index + blob index + x/y
size_t size = 3 * kUInt32Size + 2 * sizeof (SkScalar);
size_t initialOffset = this->addDraw(DRAW_TEXT_BLOB, &size);
this->addPaint(paint);
this->addTextBlob(blob);
this->addScalar(x);
this->addScalar(y);
this->validate(initialOffset, size);
}
void SkPictureRecord::onDrawSlug(const sktext::gpu::Slug* slug, const SkPaint& paint) {
// op + paint index + slug id
size_t size = 3 * kUInt32Size;
size_t initialOffset = this->addDraw(DRAW_SLUG, &size);
this->addPaint(paint);
this->addSlug(slug);
this->validate(initialOffset, size);
}
void SkPictureRecord::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
const SkPaint* paint) {
// op + picture index
size_t size = 2 * kUInt32Size;
size_t initialOffset;
if (nullptr == matrix && nullptr == paint) {
initialOffset = this->addDraw(DRAW_PICTURE, &size);
this->addPicture(picture);
} else {
const SkMatrix& m = matrix ? *matrix : SkMatrix::I();
size += SkMatrixPriv::WriteToMemory(m, nullptr) + kUInt32Size; // matrix + paint
initialOffset = this->addDraw(DRAW_PICTURE_MATRIX_PAINT, &size);
this->addPaintPtr(paint);
this->addMatrix(m);
this->addPicture(picture);
}
this->validate(initialOffset, size);
}
void SkPictureRecord::onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) {
// op + drawable index
size_t size = 2 * kUInt32Size;
size_t initialOffset;
if (nullptr == matrix) {
initialOffset = this->addDraw(DRAW_DRAWABLE, &size);
this->addDrawable(drawable);
} else {
size += SkMatrixPriv::WriteToMemory(*matrix, nullptr); // matrix
initialOffset = this->addDraw(DRAW_DRAWABLE_MATRIX, &size);
this->addMatrix(*matrix);
this->addDrawable(drawable);
}
this->validate(initialOffset, size);
}
void SkPictureRecord::onDrawVerticesObject(const SkVertices* vertices,
SkBlendMode mode, const SkPaint& paint) {
// op + paint index + vertices index + zero_bones + mode
size_t size = 5 * kUInt32Size;
size_t initialOffset = this->addDraw(DRAW_VERTICES_OBJECT, &size);
this->addPaint(paint);
this->addVertices(vertices);
this->addInt(0 ); // legacy bone count
this->addInt(static_cast<uint32_t>(mode));
this->validate(initialOffset, size);
}
void SkPictureRecord::onDrawPatch(const SkPoint cubics[12 ], const SkColor colors[4 ],
const SkPoint texCoords[4 ], SkBlendMode bmode,
const SkPaint& paint) {
// op + paint index + patch 12 control points + flag + patch 4 colors + 4 texture coordinates
size_t size = 2 * kUInt32Size + SkPatchUtils::kNumCtrlPts * sizeof (SkPoint) + kUInt32Size;
uint32_t flag = 0 ;
if (colors) {
flag |= DRAW_VERTICES_HAS_COLORS;
size += SkPatchUtils::kNumCorners * sizeof (SkColor);
}
if (texCoords) {
flag |= DRAW_VERTICES_HAS_TEXS;
size += SkPatchUtils::kNumCorners * sizeof (SkPoint);
}
if (SkBlendMode::kModulate != bmode) {
flag |= DRAW_VERTICES_HAS_XFER;
size += kUInt32Size;
}
size_t initialOffset = this->addDraw(DRAW_PATCH, &size);
this->addPaint(paint);
this->addPatch(cubics);
this->addInt(flag);
// write optional parameters
if (colors) {
fWriter.write(colors, SkPatchUtils::kNumCorners * sizeof (SkColor));
}
if (texCoords) {
fWriter.write(texCoords, SkPatchUtils::kNumCorners * sizeof (SkPoint));
}
if (flag & DRAW_VERTICES_HAS_XFER) {
this->addInt((int )bmode);
}
this->validate(initialOffset, size);
}
void SkPictureRecord::onDrawAtlas2(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
const SkColor colors[], int count, SkBlendMode mode,
const SkSamplingOptions& sampling, const SkRect* cull,
const SkPaint* paint) {
// [op + paint-index + atlas-index + flags + count] + [xform] + [tex] + [*colors + mode] + cull
size_t size = 5 * kUInt32Size + count * sizeof (SkRSXform) + count * sizeof (SkRect);
size += SkSamplingPriv::FlatSize(sampling);
uint32_t flags = 0 ;
if (colors) {
flags |= DRAW_ATLAS_HAS_COLORS;
size += count * sizeof (SkColor);
size += sizeof (uint32_t); // xfermode::mode
}
if (cull) {
flags |= DRAW_ATLAS_HAS_CULL;
size += sizeof (SkRect);
}
flags |= DRAW_ATLAS_HAS_SAMPLING;
size_t initialOffset = this->addDraw(DRAW_ATLAS, &size);
this->addPaintPtr(paint);
this->addImage(atlas);
this->addInt(flags);
this->addInt(count);
fWriter.write(xform, count * sizeof (SkRSXform));
fWriter.write(tex, count * sizeof (SkRect));
// write optional parameters
if (colors) {
fWriter.write(colors, count * sizeof (SkColor));
this->addInt((int )mode);
}
if (cull) {
fWriter.write(cull, sizeof (SkRect));
}
this->addSampling(sampling);
this->validate(initialOffset, size);
}
void SkPictureRecord::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
// op + path index + zParams + lightPos + lightRadius + spot/ambient alphas + color + flags
size_t size = 2 * kUInt32Size + 2 * sizeof (SkPoint3) + 1 * sizeof (SkScalar) + 3 * kUInt32Size;
size_t initialOffset = this->addDraw(DRAW_SHADOW_REC, &size);
this->addPath(path);
fWriter.writePoint3(rec.fZPlaneParams);
fWriter.writePoint3(rec.fLightPos);
fWriter.writeScalar(rec.fLightRadius);
fWriter.write32(rec.fAmbientColor);
fWriter.write32(rec.fSpotColor);
fWriter.write32(rec.fFlags);
this->validate(initialOffset, size);
}
void SkPictureRecord::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
size_t keyLen = SkWriter32::WriteStringSize(key);
size_t valueLen = SkWriter32::WriteDataSize(value);
size_t size = 4 + sizeof (SkRect) + keyLen + valueLen;
size_t initialOffset = this->addDraw(DRAW_ANNOTATION, &size);
this->addRect(rect);
fWriter.writeString(key);
fWriter.writeData(value);
this->validate(initialOffset, size);
}
void SkPictureRecord::onDrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4 ],
SkCanvas::QuadAAFlags aa, const SkColor4f& color,
SkBlendMode mode) {
// op + rect + aa flags + color + mode + hasClip(as int) + clipCount*points
size_t size = 4 * kUInt32Size + sizeof (SkColor4f) + sizeof (rect) +
(clip ? 4 : 0 ) * sizeof (SkPoint);
size_t initialOffset = this->addDraw(DRAW_EDGEAA_QUAD, &size);
this->addRect(rect);
this->addInt((int ) aa);
fWriter.write(&color, sizeof (SkColor4f));
this->addInt((int ) mode);
this->addInt(clip != nullptr);
if (clip) {
this->addPoints(clip, 4 );
}
this->validate(initialOffset, size);
}
void SkPictureRecord::onDrawEdgeAAImageSet2(const SkCanvas::ImageSetEntry set[], int count,
const SkPoint dstClips[],
const SkMatrix preViewMatrices[],
const SkSamplingOptions& sampling,
const SkPaint* paint,
SkCanvas::SrcRectConstraint constraint) {
static constexpr size_t kMatrixSize = 9 * sizeof (SkScalar); // *not* sizeof(SkMatrix)
// op + count + paint + constraint + (image index, src rect, dst rect, alpha, aa flags,
// hasClip(int), matrixIndex) * cnt + totalClipCount + dstClips + totalMatrixCount + matrices
int totalDstClipCount, totalMatrixCount;
SkCanvasPriv::GetDstClipAndMatrixCounts(set, count, &totalDstClipCount, &totalMatrixCount);
size_t size = 6 * kUInt32Size + sizeof (SkPoint) * totalDstClipCount +
kMatrixSize * totalMatrixCount +
(4 * kUInt32Size + 2 * sizeof (SkRect) + sizeof (SkScalar)) * count +
SkSamplingPriv::FlatSize(sampling);
size_t initialOffset = this->addDraw(DRAW_EDGEAA_IMAGE_SET2, &size);
this->addInt(count);
this->addPaintPtr(paint);
this->addSampling(sampling);
this->addInt((int ) constraint);
for (int i = 0 ; i < count; ++i) {
this->addImage(set[i].fImage.get());
this->addRect(set[i].fSrcRect);
this->addRect(set[i].fDstRect);
this->addInt(set[i].fMatrixIndex);
this->addScalar(set[i].fAlpha);
this->addInt((int )set[i].fAAFlags);
this->addInt(set[i].fHasClip);
}
this->addInt(totalDstClipCount);
this->addPoints(dstClips, totalDstClipCount);
this->addInt(totalMatrixCount);
for (int i = 0 ; i < totalMatrixCount; ++i) {
this->addMatrix(preViewMatrices[i]);
}
this->validate(initialOffset, size);
}
///////////////////////////////////////////////////////////////////////////////
// De-duping helper.
template <typename T>
static bool equals(T* a, T* b) { return a->uniqueID() == b->uniqueID(); }
template <>
bool equals(SkDrawable* a, SkDrawable* b) {
// SkDrawable's generationID is not a stable unique identifier.
return a == b;
}
template <typename T>
static int find_or_append(TArray<sk_sp<T>>& array, T* obj) {
for (int i = 0 ; i < array.size(); i++) {
if (equals(array[i].get(), obj)) {
return i;
}
}
array.push_back(sk_ref_sp(obj));
return array.size() - 1 ;
}
sk_sp<SkSurface> SkPictureRecord::onNewSurface(const SkImageInfo& info, const SkSurfaceProps&) {
return nullptr;
}
void SkPictureRecord::addImage(const SkImage* image) {
// convention for images is 0-based index
this->addInt(find_or_append(fImages, image));
}
void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
fWriter.writeMatrix(matrix);
}
void SkPictureRecord::addPaintPtr(const SkPaint* paint) {
if (paint) {
fPaints.push_back(*paint);
this->addInt(fPaints.size());
} else {
this->addInt(0 );
}
}
int SkPictureRecord::addPathToHeap(const SkPath& path) {
if (int * n = fPaths.find(path)) {
return *n;
}
int n = fPaths.count() + 1 ; // 0 is reserved for null / error.
fPaths.set(path, n);
return n;
}
void SkPictureRecord::addPath(const SkPath& path) {
this->addInt(this->addPathToHeap(path));
}
void SkPictureRecord::addPatch(const SkPoint cubics[12 ]) {
fWriter.write(cubics, SkPatchUtils::kNumCtrlPts * sizeof (SkPoint));
}
void SkPictureRecord::addPicture(const SkPicture* picture) {
// follow the convention of recording a 1-based index
this->addInt(find_or_append(fPictures, picture) + 1 );
}
void SkPictureRecord::addDrawable(SkDrawable* drawable) {
// follow the convention of recording a 1-based index
this->addInt(find_or_append(fDrawables, drawable) + 1 );
}
void SkPictureRecord::addPoint(const SkPoint& point) {
fWriter.writePoint(point);
}
void SkPictureRecord::addPoints(const SkPoint pts[], int count) {
fWriter.writeMul4(pts, count * sizeof (SkPoint));
}
void SkPictureRecord::addNoOp() {
size_t size = kUInt32Size; // op
this->addDraw(NOOP, &size);
}
void SkPictureRecord::addRect(const SkRect& rect) {
fWriter.writeRect(rect);
}
void SkPictureRecord::addRectPtr(const SkRect* rect) {
if (fWriter.writeBool(rect != nullptr)) {
fWriter.writeRect(*rect);
}
}
void SkPictureRecord::addIRect(const SkIRect& rect) {
fWriter.write(&rect, sizeof (rect));
}
void SkPictureRecord::addIRectPtr(const SkIRect* rect) {
if (fWriter.writeBool(rect != nullptr)) {
*(SkIRect*)fWriter.reserve(sizeof (SkIRect)) = *rect;
}
}
void SkPictureRecord::addRRect(const SkRRect& rrect) {
fWriter.writeRRect(rrect);
}
void SkPictureRecord::addRegion(const SkRegion& region) {
fWriter.writeRegion(region);
}
void SkPictureRecord::addSampling(const SkSamplingOptions& sampling) {
fWriter.writeSampling(sampling);
}
void SkPictureRecord::addText(const void * text, size_t byteLength) {
addInt(SkToInt(byteLength));
fWriter.writePad(text, byteLength);
}
void SkPictureRecord::addTextBlob(const SkTextBlob* blob) {
// follow the convention of recording a 1-based index
this->addInt(find_or_append(fTextBlobs, blob) + 1 );
}
void SkPictureRecord::addSlug(const sktext::gpu::Slug* slug) {
// follow the convention of recording a 1-based index
this->addInt(find_or_append(fSlugs, slug) + 1 );
}
void SkPictureRecord::addVertices(const SkVertices* vertices) {
// follow the convention of recording a 1-based index
this->addInt(find_or_append(fVertices, vertices) + 1 );
}
Messung V0.5 in Prozent C=92 H=94 G=92
¤ Dauer der Verarbeitung: 0.19 Sekunden
(vorverarbeitet am 2026-08-26)
¤
*© Formatika GbR, Deutschland