diff options
author | 2016-09-13 08:09:45 -0700 | |
---|---|---|
committer | 2016-09-13 08:09:45 -0700 | |
commit | 54dc4878b02765efea39e68b218df1e4bfff4b88 (patch) | |
tree | 296347d73c08b8b1d0a0b244ca062434ac48caa1 /src/pipe/SkPipeCanvas.cpp | |
parent | 5f1d0f61ea182829826d9d76cb85346d3e23305d (diff) |
add pipecanvas
BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2201323003
Review-Url: https://codereview.chromium.org/2201323003
Diffstat (limited to 'src/pipe/SkPipeCanvas.cpp')
-rw-r--r-- | src/pipe/SkPipeCanvas.cpp | 1056 |
1 files changed, 1056 insertions, 0 deletions
diff --git a/src/pipe/SkPipeCanvas.cpp b/src/pipe/SkPipeCanvas.cpp new file mode 100644 index 0000000000..062d416537 --- /dev/null +++ b/src/pipe/SkPipeCanvas.cpp @@ -0,0 +1,1056 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkPathEffect.h" +#include "SkColorFilter.h" +#include "SkDrawLooper.h" +#include "SkImageFilter.h" +#include "SkMaskFilter.h" +#include "SkPipeCanvas.h" +#include "SkPipeFormat.h" +#include "SkRasterizer.h" +#include "SkRSXform.h" +#include "SkShader.h" +#include "SkStream.h" +#include "SkTextBlob.h" +#include "SkTypeface.h" + +template <typename T> void write_rrect(T* writer, const SkRRect& rrect) { + char tmp[SkRRect::kSizeInMemory]; + rrect.writeToMemory(tmp); + writer->write(tmp, SkRRect::kSizeInMemory); +} + +template <typename T> void write_pad(T* writer, const void* buffer, size_t len) { + writer->write(buffer, len & ~3); + if (len & 3) { + const char* src = (const char*)buffer + (len & ~3); + len &= 3; + uint32_t tmp = 0; + memcpy(&tmp, src, len); + writer->write(&tmp, 4); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +static uint16_t compute_nondef(const SkPaint& paint, PaintUsage usage) { + // kRespectsStroke_PaintUsage is only valid if other bits are also set + SkASSERT(0 != (usage & ~kRespectsStroke_PaintUsage)); + + const SkScalar kTextSize_Default = 12; + const SkScalar kTextScaleX_Default = 1; + const SkScalar kTextSkewX_Default = 0; + const SkScalar kStrokeWidth_Default = 0; + const SkScalar kStrokeMiter_Default = 4; + const SkColor kColor_Default = SK_ColorBLACK; + + unsigned bits = (paint.getColor() != kColor_Default) ? kColor_NonDef : 0; + + if (usage & kText_PaintUsage) { + bits |= (paint.getTextSize() != kTextSize_Default ? kTextSize_NonDef : 0); + bits |= (paint.getTextScaleX() != kTextScaleX_Default ? kTextScaleX_NonDef : 0); + bits |= (paint.getTextSkewX() != kTextSkewX_Default ? kTextSkewX_NonDef : 0); + bits |= (paint.getTypeface() ? kTypeface_NonDef : 0); + } + + // TODO: kImage_PaintUsage only needs the shader/maskfilter IF its colortype is kAlpha_8 + + if (usage & (kVertices_PaintUsage | kDrawPaint_PaintUsage | kImage_PaintUsage | + kText_PaintUsage | kGeometry_PaintUsage | kTextBlob_PaintUsage)) { + bits |= (paint.getShader() ? kShader_NonDef : 0); + } + + if (usage & (kText_PaintUsage | kGeometry_PaintUsage | kTextBlob_PaintUsage)) { + bits |= (paint.getPathEffect() ? kPathEffect_NonDef : 0); + bits |= (paint.getRasterizer() ? kRasterizer_NonDef : 0); + + if (paint.getStyle() != SkPaint::kFill_Style || (usage & kRespectsStroke_PaintUsage)) { + bits |= (paint.getStrokeWidth() != kStrokeWidth_Default ? kStrokeWidth_NonDef : 0); + bits |= (paint.getStrokeMiter() != kStrokeMiter_Default ? kStrokeMiter_NonDef : 0); + } + } + + if (usage & + (kText_PaintUsage | kGeometry_PaintUsage | kImage_PaintUsage | kTextBlob_PaintUsage)) + { + bits |= (paint.getMaskFilter() ? kMaskFilter_NonDef : 0); + } + + bits |= (paint.getXfermode() ? kXfermode_NonDef : 0); + bits |= (paint.getColorFilter() ? kColorFilter_NonDef : 0); + bits |= (paint.getImageFilter() ? kImageFilter_NonDef : 0); + bits |= (paint.getDrawLooper() ? kDrawLooper_NonDef : 0); + + return SkToU16(bits); +} + +static uint32_t pack_paint_flags(unsigned flags, unsigned hint, unsigned align, + unsigned filter, unsigned style, unsigned caps, unsigned joins, + unsigned encoding) { + SkASSERT(kFlags_BPF + kHint_BPF + kAlign_BPF + kFilter_BPF <= 32); + + ASSERT_FITS_IN(flags, kFlags_BPF); + ASSERT_FITS_IN(filter, kFilter_BPF); + ASSERT_FITS_IN(style, kStyle_BPF); + ASSERT_FITS_IN(caps, kCaps_BPF); + ASSERT_FITS_IN(joins, kJoins_BPF); + ASSERT_FITS_IN(hint, kHint_BPF); + ASSERT_FITS_IN(align, kAlign_BPF); + ASSERT_FITS_IN(encoding, kEncoding_BPF); + + // left-align the fields of "known" size, and right-align the last (flatFlags) so it can easly + // add more bits in the future. + + uint32_t packed = 0; + int shift = 32; + + shift -= kFlags_BPF; packed |= (flags << shift); + shift -= kFilter_BPF; packed |= (filter << shift); + shift -= kStyle_BPF; packed |= (style << shift); + // these are only needed for stroking (geometry or text) + shift -= kCaps_BPF; packed |= (caps << shift); + shift -= kJoins_BPF; packed |= (joins << shift); + // these are only needed for text + shift -= kHint_BPF; packed |= (hint << shift); + shift -= kAlign_BPF; packed |= (align << shift); + shift -= kEncoding_BPF; packed |= (encoding << shift); + + return packed; +} + +#define CHECK_WRITE_SCALAR(writer, nondef, paint, Field) \ + do { if (nondef & (k##Field##_NonDef)) { \ + writer.writeScalar(paint.get##Field()); \ + }} while (0) + +#define CHECK_WRITE_FLATTENABLE(writer, nondef, paint, Field) \ + do { if (nondef & (k##Field##_NonDef)) { \ + SkFlattenable* f = paint.get##Field(); \ + SkASSERT(f != nullptr); \ + writer.writeFlattenable(f); \ + } } while (0) + +/* + * Header: + * paint flags : 32 + * non_def bits : 16 + * xfermode enum : 8 + * pad zeros : 8 + */ +static void write_paint(SkWriteBuffer& writer, const SkPaint& paint, unsigned usage) { + uint32_t packedFlags = pack_paint_flags(paint.getFlags(), paint.getHinting(), + paint.getTextAlign(), paint.getFilterQuality(), + paint.getStyle(), paint.getStrokeCap(), + paint.getStrokeJoin(), paint.getTextEncoding()); + writer.write32(packedFlags); + + unsigned nondef = compute_nondef(paint, (PaintUsage)usage); + SkXfermode::Mode mode; + if (SkXfermode::AsMode(paint.getXfermode(), &mode)) { + nondef &= ~kXfermode_NonDef; // don't need to store a pointer since we have an enum + } else { + SkASSERT(nondef & kXfermode_NonDef); + mode = (SkXfermode::Mode)0; + } + const uint8_t pad = 0; + writer.write32((nondef << 16) | ((unsigned)mode << 8) | pad); + + CHECK_WRITE_SCALAR(writer, nondef, paint, TextSize); + CHECK_WRITE_SCALAR(writer, nondef, paint, TextScaleX); + CHECK_WRITE_SCALAR(writer, nondef, paint, TextSkewX); + CHECK_WRITE_SCALAR(writer, nondef, paint, StrokeWidth); + CHECK_WRITE_SCALAR(writer, nondef, paint, StrokeMiter); + + if (nondef & kColor_NonDef) { + writer.write32(paint.getColor()); + } + if (nondef & kTypeface_NonDef) { + // TODO: explore idea of writing bits indicating "use the prev (or prev N) face" + // e.g. 1-N bits is an index into a ring buffer of typefaces + SkTypeface* tf = paint.getTypeface(); + SkASSERT(tf); + writer.writeTypeface(tf); + } + + CHECK_WRITE_FLATTENABLE(writer, nondef, paint, PathEffect); + CHECK_WRITE_FLATTENABLE(writer, nondef, paint, Shader); + CHECK_WRITE_FLATTENABLE(writer, nondef, paint, Xfermode); + CHECK_WRITE_FLATTENABLE(writer, nondef, paint, MaskFilter); + CHECK_WRITE_FLATTENABLE(writer, nondef, paint, ColorFilter); + CHECK_WRITE_FLATTENABLE(writer, nondef, paint, Rasterizer); + CHECK_WRITE_FLATTENABLE(writer, nondef, paint, ImageFilter); + CHECK_WRITE_FLATTENABLE(writer, nondef, paint, DrawLooper); +} + +class SkPipeWriter : public SkBinaryWriteBuffer { + enum { + N = 1024/4, + }; + uint32_t fStorage[N]; + SkWStream* fStream; + +public: + SkPipeWriter(SkWStream* stream, SkDeduper* deduper) + : SkBinaryWriteBuffer(fStorage, sizeof(fStorage)) + , fStream(stream) + { + this->setDeduper(deduper); + } + + SkPipeWriter(SkPipeCanvas* pc) : SkPipeWriter(pc->fStream, pc->fDeduper) {} + + ~SkPipeWriter() override { + SkASSERT(SkIsAlign4(fStream->bytesWritten())); + this->writeToStream(fStream); + } + + void writePaint(const SkPaint& paint) override { + write_paint(*this, paint, kUnknown_PaintUsage); + } +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +SkPipeCanvas::SkPipeCanvas(const SkRect& cull, SkPipeDeduper* deduper, SkWStream* stream) + : INHERITED(SkScalarCeilToInt(cull.width()), SkScalarCeilToInt(cull.height())) + , fDeduper(deduper) + , fStream(stream) +{} + +SkPipeCanvas::~SkPipeCanvas() {} + +void SkPipeCanvas::willSave() { + fStream->write32(pack_verb(SkPipeVerb::kSave)); + this->INHERITED::willSave(); +} + +SkCanvas::SaveLayerStrategy SkPipeCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) { + SkPipeWriter writer(this); + uint32_t extra = rec.fSaveLayerFlags; + + // remap this wacky flag + if (extra & (1 << 31)/*SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag*/) { + extra &= ~(1 << 31); + extra |= kDontClipToLayer_SaveLayerMask; + } + + if (rec.fBounds) { + extra |= kHasBounds_SaveLayerMask; + } + if (rec.fPaint) { + extra |= kHasPaint_SaveLayerMask; + } + if (rec.fBackdrop) { + extra |= kHasBackdrop_SaveLayerMask; + } + + writer.write32(pack_verb(SkPipeVerb::kSaveLayer, extra)); + if (rec.fBounds) { + writer.writeRect(*rec.fBounds); + } + if (rec.fPaint) { + write_paint(writer, *rec.fPaint, kSaveLayer_PaintUsage); + } + if (rec.fBackdrop) { + writer.writeFlattenable(rec.fBackdrop); + } + return kNoLayer_SaveLayerStrategy; +} + +void SkPipeCanvas::willRestore() { + fStream->write32(pack_verb(SkPipeVerb::kRestore)); + this->INHERITED::willRestore(); +} + +template <typename T> void write_sparse_matrix(T* writer, const SkMatrix& matrix) { + SkMatrix::TypeMask tm = matrix.getType(); + SkScalar tmp[9]; + if (tm & SkMatrix::kPerspective_Mask) { + matrix.get9(tmp); + writer->write(tmp, 9 * sizeof(SkScalar)); + } else if (tm & SkMatrix::kAffine_Mask) { + tmp[0] = matrix[SkMatrix::kMScaleX]; + tmp[1] = matrix[SkMatrix::kMSkewX]; + tmp[2] = matrix[SkMatrix::kMTransX]; + tmp[3] = matrix[SkMatrix::kMScaleY]; + tmp[4] = matrix[SkMatrix::kMSkewY]; + tmp[5] = matrix[SkMatrix::kMTransY]; + writer->write(tmp, 6 * sizeof(SkScalar)); + } else if (tm & SkMatrix::kScale_Mask) { + tmp[0] = matrix[SkMatrix::kMScaleX]; + tmp[1] = matrix[SkMatrix::kMTransX]; + tmp[2] = matrix[SkMatrix::kMScaleY]; + tmp[3] = matrix[SkMatrix::kMTransY]; + writer->write(tmp, 4 * sizeof(SkScalar)); + } else if (tm & SkMatrix::kTranslate_Mask) { + tmp[0] = matrix[SkMatrix::kMTransX]; + tmp[1] = matrix[SkMatrix::kMTransY]; + writer->write(tmp, 2 * sizeof(SkScalar)); + } + // else write nothing for Identity +} + +static void do_concat(SkWStream* stream, const SkMatrix& matrix, bool isSetMatrix) { + unsigned mtype = matrix.getType(); + SkASSERT(0 == (mtype & ~kTypeMask_ConcatMask)); + unsigned extra = mtype; + if (isSetMatrix) { + extra |= kSetMatrix_ConcatMask; + } + if (mtype || isSetMatrix) { + stream->write32(pack_verb(SkPipeVerb::kConcat, extra)); + write_sparse_matrix(stream, matrix); + } +} + +void SkPipeCanvas::didConcat(const SkMatrix& matrix) { + do_concat(fStream, matrix, false); + this->INHERITED::didConcat(matrix); +} + +void SkPipeCanvas::didSetMatrix(const SkMatrix& matrix) { + do_concat(fStream, matrix, true); + this->INHERITED::didSetMatrix(matrix); +} + +void SkPipeCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) { + fStream->write32(pack_verb(SkPipeVerb::kClipRect, ((unsigned)op << 1) | edgeStyle)); + fStream->write(&rect, 4 * sizeof(SkScalar)); + + this->INHERITED::onClipRect(rect, op, edgeStyle); +} + +void SkPipeCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) { + fStream->write32(pack_verb(SkPipeVerb::kClipRRect, ((unsigned)op << 1) | edgeStyle)); + write_rrect(fStream, rrect); + + this->INHERITED::onClipRRect(rrect, op, edgeStyle); +} + +void SkPipeCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) { + SkPipeWriter writer(this); + writer.write32(pack_verb(SkPipeVerb::kClipPath, ((unsigned)op << 1) | edgeStyle)); + writer.writePath(path); + + this->INHERITED::onClipPath(path, op, edgeStyle); +} + +void SkPipeCanvas::onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) { + SkPipeWriter writer(this); + writer.write32(pack_verb(SkPipeVerb::kClipRegion, (unsigned)op << 1)); + writer.writeRegion(deviceRgn); + + this->INHERITED::onClipRegion(deviceRgn, op); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void SkPipeCanvas::onDrawArc(const SkRect& bounds, SkScalar startAngle, SkScalar sweepAngle, + bool useCenter, const SkPaint& paint) { + SkPipeWriter writer(this); + writer.write32(pack_verb(SkPipeVerb::kDrawArc, (int)useCenter)); + writer.writeRect(bounds); + writer.writeScalar(startAngle); + writer.writeScalar(sweepAngle); + write_paint(writer, paint, kGeometry_PaintUsage); +} + +void SkPipeCanvas::onDrawAtlas(const SkImage* image, const SkRSXform xform[], const SkRect rect[], + const SkColor colors[], int count, SkXfermode::Mode mode, + const SkRect* cull, const SkPaint* paint) { + unsigned extra = (unsigned)mode; + SkASSERT(0 == (extra & ~kMode_DrawAtlasMask)); + if (colors) { + extra |= kHasColors_DrawAtlasMask; + } + if (cull) { + extra |= kHasCull_DrawAtlasMask; + } + if (paint) { + extra |= kHasPaint_DrawAtlasMask; + } + + SkPipeWriter writer(this); + writer.write32(pack_verb(SkPipeVerb::kDrawAtlas, extra)); + writer.writeImage(image); + writer.write32(count); + writer.write(xform, count * sizeof(SkRSXform)); + writer.write(rect, count * sizeof(SkRect)); + if (colors) { + writer.write(colors, count * sizeof(SkColor)); + } + if (cull) { + writer.writeRect(*cull); + } + if (paint) { + write_paint(writer, *paint, kImage_PaintUsage); + } +} + +void SkPipeCanvas::onDrawPaint(const SkPaint& paint) { + SkPipeWriter writer(this); + writer.write32(pack_verb(SkPipeVerb::kDrawPaint)); + write_paint(writer, paint, kDrawPaint_PaintUsage); +} + +void SkPipeCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[], + const SkPaint& paint) { + SkPipeWriter writer(this); + writer.write32(pack_verb(SkPipeVerb::kDrawPoints, mode)); + writer.write32(SkToU32(count)); + writer.write(pts, count * sizeof(SkPoint)); + write_paint(writer, paint, kGeometry_PaintUsage | kRespectsStroke_PaintUsage); +} + +void SkPipeCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) { + SkPipeWriter writer(this); + writer.write32(pack_verb(SkPipeVerb::kDrawRect)); + writer.write(&rect, sizeof(SkRect)); + write_paint(writer, paint, kGeometry_PaintUsage); +} + +void SkPipeCanvas::onDrawOval(const SkRect& rect, const SkPaint& paint) { + SkPipeWriter writer(this); + writer.write32(pack_verb(SkPipeVerb::kDrawOval)); + writer.write(&rect, sizeof(SkRect)); + write_paint(writer, paint, kGeometry_PaintUsage); +} + +void SkPipeCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) { + SkPipeWriter writer(this); + writer.write32(pack_verb(SkPipeVerb::kDrawRRect)); + write_rrect(&writer, rrect); + write_paint(writer, paint, kGeometry_PaintUsage); +} + +void SkPipeCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) { + SkPipeWriter writer(this); + writer.write32(pack_verb(SkPipeVerb::kDrawDRRect)); + write_rrect(&writer, outer); + write_rrect(&writer, inner); + write_paint(writer, paint, kGeometry_PaintUsage); +} + +void SkPipeCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { + SkPipeWriter writer(this); + writer.write32(pack_verb(SkPipeVerb::kDrawPath)); + writer.writePath(path); + write_paint(writer, paint, kGeometry_PaintUsage); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +static sk_sp<SkImage> make_from_bitmap(const SkBitmap& bitmap) { + // If we just "make" an image, it will force a CPU copy (if its mutable), only to have + // us then either find it in our cache, or compress and send it. + // + // Better could be to look it up in our cache first, and only create/compress it if we have to. + // + // But for now, just do the dumb thing... + return SkImage::MakeFromBitmap(bitmap); +} + +void SkPipeCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, + const SkPaint* paint) { + sk_sp<SkImage> image = make_from_bitmap(bitmap); + if (image) { + this->onDrawImage(image.get(), x, y, paint); + } +} + +void SkPipeCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst, + const SkPaint* paint, SrcRectConstraint constraint) { + sk_sp<SkImage> image = make_from_bitmap(bitmap); + if (image) { + this->onDrawImageRect(image.get(), src, dst, paint, constraint); + } +} + +void SkPipeCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, + const SkRect& dst, const SkPaint* paint) { + sk_sp<SkImage> image = make_from_bitmap(bitmap); + if (image) { + this->onDrawImageNine(image.get(), center, dst, paint); + } +} + +void SkPipeCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, + const SkRect& dst, const SkPaint* paint) { + sk_sp<SkImage> image = make_from_bitmap(bitmap); + if (image) { + this->onDrawImageLattice(image.get(), lattice, dst, paint); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void SkPipeCanvas::onDrawImage(const SkImage* image, SkScalar left, SkScalar top, + const SkPaint* paint) { + unsigned extra = 0; + if (paint) { + extra |= kHasPaint_DrawImageMask; + } + SkPipeWriter writer(this); + writer.write32(pack_verb(SkPipeVerb::kDrawImage, extra)); + writer.writeImage(image); + writer.writeScalar(left); + writer.writeScalar(top); + if (paint) { + write_paint(writer, *paint, kImage_PaintUsage); + } +} + +void SkPipeCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, + const SkPaint* paint, SrcRectConstraint constraint) { + SkASSERT(0 == ((unsigned)constraint & ~1)); + unsigned extra = (unsigned)constraint; + if (paint) { + extra |= kHasPaint_DrawImageRectMask; + } + if (src) { + extra |= kHasSrcRect_DrawImageRectMask; + } + + SkPipeWriter writer(this); + writer.write32(pack_verb(SkPipeVerb::kDrawImageRect, extra)); + writer.writeImage(image); + if (src) { + writer.write(src, sizeof(*src)); + } + writer.write(&dst, sizeof(dst)); + if (paint) { + write_paint(writer, *paint, kImage_PaintUsage); + } +} + +void SkPipeCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst, + const SkPaint* paint) { + unsigned extra = 0; + if (paint) { + extra |= kHasPaint_DrawImageNineMask; + } + SkPipeWriter writer(this); + writer.write32(pack_verb(SkPipeVerb::kDrawImageNine, extra)); + writer.writeImage(image); + writer.write(¢er, sizeof(center)); + writer.write(&dst, sizeof(dst)); + if (paint) { + write_paint(writer, *paint, kImage_PaintUsage); + } +} + +void SkPipeCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, + const SkRect& dst, const SkPaint* paint) { + unsigned extra = 0; + if (paint) { + extra |= kHasPaint_DrawImageLatticeMask; + } + if (lattice.fFlags) { + extra |= kHasFlags_DrawImageLatticeMask; + } + if (lattice.fXCount >= kCount_DrawImageLatticeMask) { + extra |= kCount_DrawImageLatticeMask << kXCount_DrawImageLatticeShift; + } else { + extra |= lattice.fXCount << kXCount_DrawImageLatticeShift; + } + if (lattice.fYCount >= kCount_DrawImageLatticeMask) { + extra |= kCount_DrawImageLatticeMask << kYCount_DrawImageLatticeShift; + } else { + extra |= lattice.fYCount << kYCount_DrawImageLatticeShift; + } + + SkPipeWriter writer(this); + writer.write32(pack_verb(SkPipeVerb::kDrawImageLattice, extra)); + writer.writeImage(image); + if (lattice.fXCount >= kCount_DrawImageLatticeMask) { + writer.write32(lattice.fXCount); + } + if (lattice.fYCount >= kCount_DrawImageLatticeMask) { + writer.write32(lattice.fYCount); + } + // Often these divs will be small (8 or 16 bits). Consider sniffing that and writing a flag + // so we can store them smaller. + writer.write(lattice.fXDivs, lattice.fXCount * sizeof(int32_t)); + writer.write(lattice.fYDivs, lattice.fYCount * sizeof(int32_t)); + if (lattice.fFlags) { + int32_t count = (lattice.fXCount + 1) * (lattice.fYCount + 1); + SkASSERT(count > 0); + write_pad(&writer, lattice.fFlags, count); + } + writer.write(&dst, sizeof(dst)); + if (paint) { + write_paint(writer, *paint, kImage_PaintUsage); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void SkPipeCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, + const SkPaint& paint) { + SkASSERT(byteLength); + + bool compact = fits_in(byteLength, 24); + + SkPipeWriter writer(this); + writer.write32(pack_verb(SkPipeVerb::kDrawText, compact ? (unsigned)byteLength : 0)); + if (!compact) { + writer.write32(SkToU32(byteLength)); + } + write_pad(&writer, text, byteLength); + writer.writeScalar(x); + writer.writeScalar(y); + write_paint(writer, paint, kText_PaintUsage); +} + +void SkPipeCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[], + const SkPaint& paint) { + SkASSERT(byteLength); + + bool compact = fits_in(byteLength, 24); + + SkPipeWriter writer(this); + writer.write32(pack_verb(SkPipeVerb::kDrawPosText, compact ? (unsigned)byteLength : 0)); + if (!compact) { + writer.write32(SkToU32(byteLength)); + } + write_pad(&writer, text, byteLength); + writer.writePointArray(pos, paint.countText(text, byteLength)); + write_paint(writer, paint, kText_PaintUsage); +} + +void SkPipeCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], + SkScalar constY, const SkPaint& paint) { + SkASSERT(byteLength); + + bool compact = fits_in(byteLength, 24); + + SkPipeWriter writer(this); + writer.write32(pack_verb(SkPipeVerb::kDrawPosTextH, compact ? (unsigned)byteLength : 0)); + if (!compact) { + writer.write32(SkToU32(byteLength)); + } + write_pad(&writer, text, byteLength); + writer.writeScalarArray(xpos, paint.countText(text, byteLength)); + writer.writeScalar(constY); + write_paint(writer, paint, kText_PaintUsage); +} + +void SkPipeCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path, + const SkMatrix* matrix, const SkPaint& paint) { + SkASSERT(byteLength > 0); + + unsigned extra = 0; + if (byteLength <= kTextLength_DrawTextOnPathMask) { + extra |= byteLength; + } // else we will write the length after the packedverb + SkMatrix::TypeMask tm = matrix ? matrix->getType() : SkMatrix::kIdentity_Mask; + extra |= (unsigned)tm << kMatrixType_DrawTextOnPathShift; + + SkPipeWriter writer(this); + writer.write32(pack_verb(SkPipeVerb::kDrawTextOnPath, extra)); + if (byteLength > kTextLength_DrawTextOnPathMask) { + writer.write32(byteLength); + } + write_pad(&writer, text, byteLength); + writer.writePath(path); + if (matrix) { + write_sparse_matrix(&writer, *matrix); + } + write_paint(writer, paint, kText_PaintUsage); +} + +void SkPipeCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[], + const SkRect* cull, const SkPaint& paint) { + SkASSERT(byteLength); + + bool compact = fits_in(byteLength, 23); + unsigned extra = compact ? (byteLength << 1) : 0; + if (cull) { + extra |= 1; + } + + SkPipeWriter writer(this); + writer.write32(pack_verb(SkPipeVerb::kDrawTextRSXform, extra)); + if (!compact) { + writer.write32(SkToU32(byteLength)); + } + write_pad(&writer, text, byteLength); + + int count = paint.countText(text, byteLength); + writer.write32(count); // maybe we can/should store this in extra as well? + writer.write(xform, count * sizeof(SkRSXform)); + if (cull) { + writer.writeRect(*cull); + } + write_paint(writer, paint, kText_PaintUsage); +} + +void SkPipeCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, + const SkPaint &paint) { + SkPipeWriter writer(this); + writer.write32(pack_verb(SkPipeVerb::kDrawTextBlob, 0)); + blob->flatten(writer); + writer.writeScalar(x); + writer.writeScalar(y); + write_paint(writer, paint, kTextBlob_PaintUsage); +} + +void SkPipeCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix, + const SkPaint* paint) { + unsigned extra = fDeduper->findOrDefinePicture(const_cast<SkPicture*>(picture)); + if (matrix) { + extra |= kHasMatrix_DrawPictureExtra; + } + if (paint) { + extra |= kHasPaint_DrawPictureExtra; + } + SkPipeWriter writer(this); + writer.write32(pack_verb(SkPipeVerb::kDrawPicture, extra)); + if (matrix) { + writer.writeMatrix(*matrix); + } + if (paint) { + write_paint(writer, *paint, kSaveLayer_PaintUsage); + } +} + +void SkPipeCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) { + size_t size = region.writeToMemory(nullptr); + unsigned extra = 0; + if (fits_in(size, 24)) { + extra = SkToUInt(size); + } + + SkPipeWriter writer(this); + writer.write32(pack_verb(SkPipeVerb::kDrawRegion, extra)); + if (0 == extra) { + writer.write32(size); + } + SkAutoSMalloc<2048> storage(size); + region.writeToMemory(storage.get()); + write_pad(&writer, storage.get(), size); + write_paint(writer, paint, kGeometry_PaintUsage); +} + +void SkPipeCanvas::onDrawVertices(VertexMode vmode, int vertexCount, + const SkPoint vertices[], const SkPoint texs[], + const SkColor colors[], SkXfermode* xmode, + const uint16_t indices[], int indexCount, + const SkPaint& paint) { + SkASSERT(vertexCount > 0); + + unsigned extra = 0; + if (vertexCount <= kVCount_DrawVerticesMask) { + extra |= vertexCount; + } + extra |= (unsigned)vmode << kVMode_DrawVerticesShift; + + SkXfermode::Mode mode = SkXfermode::kModulate_Mode; + if (xmode && !SkXfermode::AsMode(xmode, &mode)) { + mode = (SkXfermode::Mode)0xFF; // sentinel for read the xfer later + } + extra |= (unsigned)mode << kXMode_DrawVerticesShift; + + if (texs) { + extra |= kHasTex_DrawVerticesMask; + } + if (colors) { + extra |= kHasColors_DrawVerticesMask; + } + if (indexCount > 0) { + extra |= kHasIndices_DrawVerticesMask; + } + + SkPipeWriter writer(this); + writer.write32(pack_verb(SkPipeVerb::kDrawVertices, extra)); + if (vertexCount > kVCount_DrawVerticesMask) { + writer.write32(vertexCount); + } + if (mode == (SkXfermode::Mode)0xFF) { + writer.writeFlattenable(xmode); + } + writer.write(vertices, vertexCount * sizeof(SkPoint)); + if (texs) { + writer.write(texs, vertexCount * sizeof(SkPoint)); + } + if (colors) { + writer.write(colors, vertexCount * sizeof(SkColor)); + } + if (indexCount > 0) { + writer.write32(indexCount); + SkASSERT(SkIsAlign2(indexCount)); + writer.write(indices, indexCount * sizeof(uint16_t)); + } + write_paint(writer, paint, kVertices_PaintUsage); +} + +void SkPipeCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], + const SkPoint texCoords[4], SkXfermode* xfer, + const SkPaint& paint) { + SkPipeWriter writer(this); + unsigned extra = 0; + SkXfermode::Mode mode = SkXfermode::kModulate_Mode; + if (xfer && !xfer->asMode(&mode)) { + mode = (SkXfermode::Mode)kExplicitXfer_DrawPatchExtraValue; + } else { + xfer = nullptr; // signal that we're using the mode enum + } + SkASSERT(0 == (mode & ~kModeEnum_DrawPatchExtraMask)); + extra = (unsigned)mode; + if (colors) { + extra |= kHasColors_DrawPatchExtraMask; + } + if (texCoords) { + extra |= kHasTexture_DrawPatchExtraMask; + } + writer.write32(pack_verb(SkPipeVerb::kDrawPatch, extra)); + writer.write(cubics, sizeof(SkPoint) * 12); + if (colors) { + writer.write(colors, sizeof(SkColor) * 4); + } + if (texCoords) { + writer.write(texCoords, sizeof(SkPoint) * 4); + } + if (xfer) { + xfer->flatten(writer); + } + write_paint(writer, paint, kGeometry_PaintUsage); +} + +void SkPipeCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* data) { + const size_t len = strlen(key) + 1; // must write the trailing 0 + bool compact = fits_in(len, 23); + uint32_t extra = compact ? (unsigned)len : 0; + extra <<= 1; // make room for has_data_sentinel + if (data) { + extra |= 1; + } + + fStream->write32(pack_verb(SkPipeVerb::kDrawAnnotation, extra)); + fStream->write(&rect, sizeof(SkRect)); + if (!compact) { + fStream->write32(SkToU32(len)); + } + write_pad(fStream, key, len); + if (data) { + fStream->write32(SkToU32(data->size())); + write_pad(fStream, data->data(), data->size()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +class A8Serializer : public SkPixelSerializer { +protected: + bool onUseEncodedData(const void* data, size_t len) { + return true; + } + + SkData* onEncode(const SkPixmap& pmap) { + if (kAlpha_8_SkColorType == pmap.colorType()) { + SkDynamicMemoryWStream stream; + stream.write("skiaimgf", 8); + stream.write32(pmap.width()); + stream.write32(pmap.height()); + stream.write16(pmap.colorType()); + stream.write16(pmap.alphaType()); + stream.write32(0); // no colorspace for now + for (int y = 0; y < pmap.height(); ++y) { + stream.write(pmap.addr8(0, y), pmap.width()); + } + return stream.detachAsData().release(); + } + return nullptr; + } +}; + +static bool show_deduper_traffic = false; + +int SkPipeDeduper::findOrDefineImage(SkImage* image) { + int index = fImages.find(image->uniqueID()); + SkASSERT(index >= 0); + if (index) { + if (show_deduper_traffic) { + SkDebugf(" reuseImage(%d)\n", index - 1); + } + return index; + } + + A8Serializer serial; + sk_sp<SkData> data(image->encode(&serial)); + if (!data) { + data.reset(image->encode()); + } + if (data) { + index = fImages.add(image->uniqueID()); + SkASSERT(index > 0); + SkASSERT(fits_in(index, 24)); + fStream->write32(pack_verb(SkPipeVerb::kDefineImage, index)); + + uint32_t len = SkToU32(data->size()); + fStream->write32(SkAlign4(len)); + write_pad(fStream, data->data(), len); + + if (show_deduper_traffic) { + int size = image->width() * image->height() << 2; + SkDebugf(" defineImage(%d) %d -> %d\n", index - 1, size, len); + } + return index; + } + SkDebugf("+++ failed to encode image [%d %d]\n", image->width(), image->height()); + return 0; // failed to encode +} + +int SkPipeDeduper::findOrDefinePicture(SkPicture* picture) { + int index = fPictures.find(picture->uniqueID()); + SkASSERT(index >= 0); + if (index) { + if (show_deduper_traffic) { + SkDebugf(" reusePicture(%d)\n", index - 1); + } + return index; + } + + size_t prevWritten = fStream->bytesWritten(); + unsigned extra = 0; // 0 means we're defining a new picture, non-zero means undef_index + 1 + fStream->write32(pack_verb(SkPipeVerb::kDefinePicture, extra)); + const SkRect cull = picture->cullRect(); + fStream->write(&cull, sizeof(cull)); + picture->playback(fPipeCanvas); + // call fPictures.add *after* we're written the picture, so that any nested pictures will have + // already been defined, and we get the "last" index value. + index = fPictures.add(picture->uniqueID()); + ASSERT_FITS_IN(index, kObjectDefinitionBits); + fStream->write32(pack_verb(SkPipeVerb::kEndPicture, index)); + + SkDebugf(" definePicture(%d) %d\n", + index - 1, SkToU32(fStream->bytesWritten() - prevWritten)); + return index; +} + +static sk_sp<SkData> encode(SkTypeface* tf) { + SkDynamicMemoryWStream stream; + tf->serialize(&stream); + return sk_sp<SkData>(stream.detachAsData()); +} + +int SkPipeDeduper::findOrDefineTypeface(SkTypeface* typeface) { + if (!typeface) { + return 0; // default + } + + int index = fTypefaces.find(typeface->uniqueID()); + SkASSERT(index >= 0); + if (index) { + if (show_deduper_traffic) { + SkDebugf(" reuseTypeface(%d)\n", index - 1); + } + return index; + } + + sk_sp<SkData> data = fTFSerializer ? fTFSerializer->serialize(typeface) : encode(typeface); + if (data) { + index = fTypefaces.add(typeface->uniqueID()); + SkASSERT(index > 0); + SkASSERT(fits_in(index, 24)); + fStream->write32(pack_verb(SkPipeVerb::kDefineTypeface, index)); + + uint32_t len = SkToU32(data->size()); + fStream->write32(SkAlign4(len)); + write_pad(fStream, data->data(), len); + + if (show_deduper_traffic) { + SkDebugf(" defineTypeface(%d) %d\n", index - 1, len); + } + return index; + } + SkDebugf("+++ failed to encode typeface %d\n", typeface->uniqueID()); + return 0; // failed to encode +} + +int SkPipeDeduper::findOrDefineFactory(SkFlattenable* flattenable) { + if (!flattenable) { + return 0; + } + + int index = fFactories.find(flattenable->getFactory()); + SkASSERT(index >= 0); + if (index) { + if (show_deduper_traffic) { + SkDebugf(" reuseFactory(%d)\n", index - 1); + } + return index; + } + + index = fFactories.add(flattenable->getFactory()); + ASSERT_FITS_IN(index, kIndex_DefineFactoryExtraBits); + const char* name = flattenable->getTypeName(); + size_t len = strlen(name); + ASSERT_FITS_IN(len, kNameLength_DefineFactoryExtraBits); + unsigned extra = (index << kNameLength_DefineFactoryExtraBits) | len; + size_t prevWritten = fStream->bytesWritten(); + fStream->write32(pack_verb(SkPipeVerb::kDefineFactory, extra)); + write_pad(fStream, name, len + 1); + if (false) { + SkDebugf(" defineFactory(%d) %d %s\n", + index - 1, SkToU32(fStream->bytesWritten() - prevWritten), name); + } + return index; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +#include "SkPipe.h" + +class SkPipeSerializer::Impl { +public: + SkPipeDeduper fDeduper; + std::unique_ptr<SkPipeCanvas> fCanvas; +}; + +SkPipeSerializer::SkPipeSerializer() : fImpl(new Impl) {} + +SkPipeSerializer::~SkPipeSerializer() { + if (fImpl->fCanvas) { + this->endWrite(); + } +} + +void SkPipeSerializer::setTypefaceSerializer(SkTypefaceSerializer* tfs) { + fImpl->fDeduper.setTypefaceSerializer(tfs); +} + +void SkPipeSerializer::resetCache() { + fImpl->fDeduper.resetCaches(); +} + +void SkPipeSerializer::write(SkPicture* picture, SkWStream* stream) { + stream->write32(kDefinePicture_ExtPipeVerb); + SkRect cull = picture->cullRect(); + stream->write(&cull.fLeft, sizeof(SkRect)); + picture->playback(this->beginWrite(cull, stream)); + this->endWrite(); +} + +void SkPipeSerializer::write(SkImage* image, SkWStream* stream) { + stream->write32(kDefineImage_ExtPipeVerb); + SkPipeWriter writer(stream, &fImpl->fDeduper); + writer.writeImage(image); +} + +SkCanvas* SkPipeSerializer::beginWrite(const SkRect& cull, SkWStream* stream) { + SkASSERT(nullptr == fImpl->fCanvas); + fImpl->fCanvas.reset(new SkPipeCanvas(cull, &fImpl->fDeduper, stream)); + fImpl->fDeduper.setStream(stream); + fImpl->fDeduper.setCanvas(fImpl->fCanvas.get()); + return fImpl->fCanvas.get(); +} + +void SkPipeSerializer::endWrite() { + fImpl->fCanvas->restoreToCount(1); + fImpl->fCanvas.reset(nullptr); +} |