SkPDF: refactor streams, experimental threading
All PDF Streams are immediatly serialized, and are never long-lived
in memory.
if EXPERIMENTAL fExecutor is set on the Document, streams are
compressed in parallel.
Results for PDFBigDocBench:
without patch 1807885.01 μs
with patch without executor 1802808.35 μs
with patch with executor 246313.72 μs
SkPDFStreamOut() function replaces SkPDFStream classes.
Page resources are all serialized early.
Several Document-level objects are serialzied early.
SkUUID introduced as top-level object.
Many {insert|append}ObjRef() converted to memory efficient
{insert|append}Ref().
Bug: skia:8630
Change-Id: Ic336917d0c8b9ac1c2423b43bfe9b49a3533fbff
Reviewed-on: https://skia-review.googlesource.com/c/176588
Auto-Submit: Hal Canary <halcanary@google.com>
Reviewed-by: Herb Derby <herb@google.com>
Commit-Queue: Hal Canary <halcanary@google.com>
diff --git a/bench/PDFBench.cpp b/bench/PDFBench.cpp
index f612357..5eae967 100644
--- a/bench/PDFBench.cpp
+++ b/bench/PDFBench.cpp
@@ -10,6 +10,7 @@
#include "Resources.h"
#include "SkAutoPixmapStorage.h"
#include "SkData.h"
+#include "SkExecutor.h"
#include "SkFloatToDecimal.h"
#include "SkGradientShader.h"
#include "SkImage.h"
@@ -79,20 +80,6 @@
#include "SkPDFUtils.h"
namespace {
-static void test_pdf_object_serialization(const sk_sp<SkPDFObject> object) {
- // SkDebugWStream wStream;
- SkNullWStream wStream;
- SkPDFObjNumMap objNumMap;
- objNumMap.addObjectRecursively(object.get());
- for (size_t i = 0; i < objNumMap.objects().size(); ++i) {
- SkPDFObject* object = objNumMap.objects()[i].get();
- wStream.writeDecAsText(i + 1);
- wStream.writeText(" 0 obj\n");
- object->emitObject(&wStream);
- wStream.writeText("\nendobj\n");
- }
-}
-
class PDFImageBench : public Benchmark {
public:
PDFImageBench() {}
@@ -184,11 +171,11 @@
SkASSERT(fAsset);
if (!fAsset) { return; }
while (loops-- > 0) {
- sk_sp<SkPDFObject> object =
- sk_make_sp<SkPDFSharedStream>(
- std::unique_ptr<SkStreamAsset>(fAsset->duplicate()));
- test_pdf_object_serialization(object);
- }
+ SkNullWStream wStream;
+ SkPDFDocument doc(&wStream, SkPDF::Metadata());
+ doc.beginPage(256, 256);
+ (void)SkPDFStreamOut(nullptr, fAsset->duplicate(), &doc, true);
+ }
}
private:
@@ -229,8 +216,9 @@
while (loops-- > 0) {
SkNullWStream nullStream;
SkPDFDocument doc(&nullStream, SkPDF::Metadata());
- sk_sp<SkPDFObject> shader = SkPDFMakeShader(&doc, fShader.get(), SkMatrix::I(),
- {0, 0, 400, 400}, SK_ColorBLACK);
+ doc.beginPage(256, 256);
+ (void) SkPDFMakeShader(&doc, fShader.get(), SkMatrix::I(),
+ {0, 0, 400, 400}, SK_ColorBLACK);
}
}
};
@@ -262,5 +250,125 @@
DEF_BENCH(return new PDFShaderBench;)
DEF_BENCH(return new WritePDFTextBenchmark;)
+#if 0
+#include "SkExecutor.h"
+namespace {
+void big_pdf_test(const SkBitmap& background) {
+ static const char* kText[] = {
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do",
+ "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad",
+ "minim veniam, quis nostrud exercitation ullamco laboris nisi ut",
+ "aliquip ex ea commodo consequat. Duis aute irure dolor in",
+ "reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla",
+ "pariatur. Excepteur sint occaecat cupidatat non proident, sunt in",
+ "culpa qui officia deserunt mollit anim id est laborum.",
+ "",
+ "Sed ut perspiciatis, unde omnis iste natus error sit voluptatem",
+ "accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae",
+ "ab illo inventore veritatis et quasi architecto beatae vitae dicta",
+ "sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit,",
+ "aspernatur aut odit aut fugit, sed quia consequuntur magni dolores",
+ "eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est,",
+ "qui dolorem ipsum, quia dolor sit amet consectetur adipiscing velit,",
+ "sed quia non numquam do eius modi tempora incididunt, ut labore et",
+ "dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam,",
+ "quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi",
+ "ut aliquid ex ea commodi consequatur? Quis autem vel eum iure",
+ "reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae",
+ "consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla",
+ "pariatur?",
+ "",
+ "At vero eos et accusamus et iusto odio dignissimos ducimus, qui",
+ "blanditiis praesentium voluptatum deleniti atque corrupti, quos",
+ "dolores et quas molestias excepturi sint, obcaecati cupiditate non",
+ "provident, similique sunt in culpa, qui officia deserunt mollitia",
+ "animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis",
+ "est et expedita distinctio. Nam libero tempore, cum soluta nobis est",
+ "eligendi optio, cumque nihil impedit, quo minus id, quod maxime",
+ "placeat, facere possimus, omnis voluptas assumenda est, omnis dolor",
+ "repellendus. Temporibus autem quibusdam et aut officiis debitis aut",
+ "rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint",
+ "et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente",
+ "delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut",
+ "perferendis doloribus asperiores repellat",
+ "",
+ "Sed ut perspiciatis, unde omnis iste natus error sit voluptatem",
+ "accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae",
+ "ab illo inventore veritatis et quasi architecto beatae vitae dicta",
+ "sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit,",
+ "aspernatur aut odit aut fugit, sed quia consequuntur magni dolores",
+ "eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est,",
+ "qui dolorem ipsum, quia dolor sit amet consectetur adipiscing velit,",
+ "sed quia non numquam do eius modi tempora incididunt, ut labore et",
+ "dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam,",
+ "quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi",
+ "ut aliquid ex ea commodi consequatur? Quis autem vel eum iure",
+ "reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae",
+ "consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla",
+ "pariatur?",
+ "",
+ };
+ //SkFILEWStream wStream("/tmp/big_pdf.pdf");
+ SkNullWStream wStream;
+ SkPDF::Metadata metadata;
+ //std::unique_ptr<SkExecutor> executor = SkExecutor::MakeFIFOThreadPool();
+ //metadata.fExecutor = executor.get();
+ sk_sp<SkDocument> doc = SkPDF::MakeDocument(&wStream, metadata);
+
+ SkCanvas* canvas = nullptr;
+ float x = 36;
+ float y = 36;
+ constexpr size_t kLineCount = SK_ARRAY_COUNT(kText);
+ constexpr int kLoopCount = 200;
+ SkPaint paint;
+ paint.setTextEncoding(kUTF8_SkTextEncoding);
+ for (int loop = 0; loop < kLoopCount; ++loop) {
+ for (size_t line = 0; line < kLineCount; ++line) {
+ y += paint.getFontSpacing();
+ if (!canvas || y > 792 - 36) {
+ y = 36 + paint.getFontSpacing();
+ canvas = doc->beginPage(612, 792);
+ background.notifyPixelsChanged();
+ canvas->drawBitmap(background, 0, 0);
+ }
+ canvas->drawText(kText[line], strlen(kText[line]), x, y, paint);
+ }
+ }
+}
+
+SkBitmap make_background() {
+ SkBitmap background;
+ SkBitmap bitmap;
+ bitmap.allocN32Pixels(32, 32);
+ bitmap.eraseColor(SK_ColorWHITE);
+ SkCanvas tmp(bitmap);
+ SkPaint gray;
+ gray.setColor(SkColorSetARGB(0xFF, 0xEE, 0xEE, 0xEE));
+ tmp.drawRect({0,0,16,16}, gray);
+ tmp.drawRect({16,16,32,32}, gray);
+ SkPaint shader;
+ shader.setShader(
+ SkShader::MakeBitmapShader(
+ bitmap, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode));
+ background.allocN32Pixels(612, 792);
+ SkCanvas tmp2(background);
+ tmp2.drawPaint(shader);
+ return background;
+}
+
+struct PDFBigDocBench : public Benchmark {
+ SkBitmap fBackground;
+ void onDelayedSetup() override { fBackground = make_background(); }
+ const char* onGetName() override { return "PDFBigDocBench"; }
+ bool isSuitableFor(Backend backend) override {
+ return backend == kNonRendering_Backend;
+ }
+ void onDraw(int loops, SkCanvas*) override {
+ while (loops-- > 0) { big_pdf_test(fBackground); }
+ }
+};
+} // namespace
+DEF_BENCH(return new PDFBigDocBench;)
#endif
+#endif // SK_SUPPORT_PDF
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index cd9f099..e8726b4 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -1638,6 +1638,10 @@
metadata.fCreator = "Skia/DM";
metadata.fRasterDPI = fRasterDpi;
metadata.fPDFA = fPDFA;
+#if 0
+ std::unique_ptr<SkExecutor> executor = SkExecutor::MakeFIFOThreadPool();
+ metadata.fExecutor = executor.get();
+#endif
sk_sp<SkDocument> doc = SkPDF::MakeDocument(dst, metadata);
if (!doc) {
return "SkPDF::MakeDocument() returned nullptr";
diff --git a/include/docs/SkPDFDocument.h b/include/docs/SkPDFDocument.h
index 4623c7a..ad0007c 100644
--- a/include/docs/SkPDFDocument.h
+++ b/include/docs/SkPDFDocument.h
@@ -9,6 +9,8 @@
#include "SkString.h"
#include "SkTime.h"
+class SkExecutor;
+
namespace SkPDF {
/** Table 333 in PDF 32000-1:2008
@@ -141,6 +143,15 @@
* should retain ownership.
*/
const StructureElementNode* fStructureElementTreeRoot = nullptr;
+
+ /** Executor to handle threaded work within PDF Backend. If this is nullptr,
+ then all work will be done serially on the main thread. To have worker
+ threads assist with various tasks, set this to a valid SkExecutor
+ instance. Currently used for executing Deflate algorithm in parallel.
+
+ Experimental.
+ */
+ SkExecutor* fExecutor = nullptr;
};
/** Associate a node ID with subsequent drawing commands in an
diff --git a/src/pdf/SkPDFBitmap.cpp b/src/pdf/SkPDFBitmap.cpp
index a5754ef..0a6e27c 100644
--- a/src/pdf/SkPDFBitmap.cpp
+++ b/src/pdf/SkPDFBitmap.cpp
@@ -10,6 +10,7 @@
#include "SkColorData.h"
#include "SkData.h"
#include "SkDeflate.h"
+#include "SkExecutor.h"
#include "SkImage.h"
#include "SkImageInfoPriv.h"
#include "SkJpegInfo.h"
@@ -120,12 +121,13 @@
return ref;
}
-static SkPDFIndirectReference do_deflated_image(const SkPixmap& pm,
- SkPDFDocument* doc,
- bool isOpaque) {
+static void do_deflated_image(const SkPixmap& pm,
+ SkPDFDocument* doc,
+ bool isOpaque,
+ SkPDFIndirectReference ref) {
SkPDFIndirectReference sMask;
if (!isOpaque) {
- sMask = doc->reserve();
+ sMask = doc->reserveRef();
}
SkDynamicMemoryWStream buffer;
SkDeflateWStream deflateWStream(&buffer);
@@ -167,7 +169,6 @@
deflateWStream.write(byteBuffer, dst - byteBuffer);
}
deflateWStream.finalize();
- SkPDFIndirectReference ref = doc->reserve();
SkWStream* stream = doc->beginObject(ref);
emit_dict(stream, pm.info().dimensions(), colorSpace,
sMask.fValue != -1 ? &sMask : nullptr,
@@ -177,11 +178,10 @@
if (!isOpaque) {
do_deflated_alpha(pm, doc, sMask);
}
- return ref;
}
static bool do_jpeg(const SkData& data, SkPDFDocument* doc, SkISize size,
- SkPDFIndirectReference* result) {
+ SkPDFIndirectReference ref) {
SkISize jpegSize;
SkEncodedInfo::Color jpegColorType;
SkEncodedOrigin exifOrientation;
@@ -199,10 +199,8 @@
#ifdef SK_PDF_IMAGE_STATS
gJpegImageObjects.fetch_add(1);
#endif
- SkPDFIndirectReference ref = doc->reserve();
- *result = ref;
- SkWStream* stream = doc->beginObject(ref);
+ SkWStream* stream = doc->beginObject(ref);
SkPDFDict pdfDict("XObject");
pdfDict.insertName("Subtype", "Image");
pdfDict.insertInt("Width", jpegSize.width());
@@ -247,26 +245,44 @@
return bm;
}
-SkPDFIndirectReference SkPDFSerializeImage(const SkImage* img,
- SkPDFDocument* doc,
- int encodingQuality) {
- SkPDFIndirectReference result;
+void serialize_image(const SkImage* img,
+ int encodingQuality,
+ SkPDFDocument* doc,
+ SkPDFIndirectReference ref) {
SkASSERT(img);
SkASSERT(doc);
SkASSERT(encodingQuality >= 0);
SkISize dimensions = img->dimensions();
sk_sp<SkData> data = img->refEncodedData();
- if (data && do_jpeg(*data, doc, dimensions, &result)) {
- return result;
+ if (data && do_jpeg(*data, doc, dimensions, ref)) {
+ return;
}
SkBitmap bm = to_pixels(img);
SkPixmap pm = bm.pixmap();
bool isOpaque = pm.isOpaque() || pm.computeIsOpaque();
if (encodingQuality <= 100 && isOpaque) {
sk_sp<SkData> data = img->encodeToData(SkEncodedImageFormat::kJPEG, encodingQuality);
- if (data && do_jpeg(*data, doc, dimensions, &result)) {
- return result;
+ if (data && do_jpeg(*data, doc, dimensions, ref)) {
+ return;
}
}
- return do_deflated_image(pm, doc, isOpaque);
+ do_deflated_image(pm, doc, isOpaque, ref);
+}
+
+SkPDFIndirectReference SkPDFSerializeImage(const SkImage* img,
+ SkPDFDocument* doc,
+ int encodingQuality) {
+ SkASSERT(img);
+ SkASSERT(doc);
+ SkPDFIndirectReference ref = doc->reserveRef();
+ if (SkExecutor* executor = doc->executor()) {
+ SkRef(img);
+ executor->add([img, encodingQuality, doc, ref]() {
+ serialize_image(img, encodingQuality, doc, ref);
+ SkSafeUnref(img);
+ });
+ return ref;
+ }
+ serialize_image(img, encodingQuality, doc, ref);
+ return ref;
}
diff --git a/src/pdf/SkPDFCanon.h b/src/pdf/SkPDFCanon.h
index b4782c2..1d93711 100644
--- a/src/pdf/SkPDFCanon.h
+++ b/src/pdf/SkPDFCanon.h
@@ -34,11 +34,11 @@
SkPDFCanon& operator=(SkPDFCanon&&);
SkPDFCanon& operator=(const SkPDFCanon&) = delete;
- SkTHashMap<SkPDFImageShaderKey, sk_sp<SkPDFObject>> fImageShaderMap;
+ SkTHashMap<SkPDFImageShaderKey, SkPDFIndirectReference> fImageShaderMap;
SkPDFGradientShader::HashMap fGradientPatternMap;
- SkTHashMap<SkBitmapKey, sk_sp<SkPDFObject>> fPDFBitmapMap;
+ SkTHashMap<SkBitmapKey, SkPDFIndirectReference> fPDFBitmapMap;
SkTHashMap<uint32_t, std::unique_ptr<SkAdvancedTypefaceMetrics>> fTypefaceMetrics;
SkTHashMap<uint32_t, std::vector<SkString>> fType1GlyphNames;
@@ -47,12 +47,11 @@
SkTHashMap<uint32_t, SkPDFIndirectReference> fType3FontDescriptors;
SkTHashMap<uint64_t, SkPDFFont> fFontMap;
- SkTHashMap<SkPDFStrokeGraphicState, sk_sp<SkPDFDict>> fStrokeGSMap;
- SkTHashMap<SkPDFFillGraphicState, sk_sp<SkPDFDict>> fFillGSMap;
+ SkTHashMap<SkPDFStrokeGraphicState, SkPDFIndirectReference> fStrokeGSMap;
+ SkTHashMap<SkPDFFillGraphicState, SkPDFIndirectReference> fFillGSMap;
- sk_sp<SkPDFStream> fInvertFunction;
- sk_sp<SkPDFDict> fNoSmaskGraphicState;
- sk_sp<SkPDFArray> fRangeObject;
+ SkPDFIndirectReference fInvertFunction;
+ SkPDFIndirectReference fNoSmaskGraphicState;
};
#endif // SkPDFCanon_DEFINED
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index 7475009..95d65d4 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -107,6 +107,11 @@
return SkImage::MakeFromBitmap(greyBitmap);
}
+static int add_resource(SkTHashSet<SkPDFIndirectReference>& resources, SkPDFIndirectReference ref) {
+ resources.add(ref);
+ return ref.fValue;
+}
+
static void draw_points(SkCanvas::PointMode mode,
size_t count,
const SkPoint* points,
@@ -429,7 +434,7 @@
if (shape->isEmpty()) {
shape = nullptr;
}
- fDevice->finishContentEntry(fClipStack, fBlendMode, std::move(fDstFormXObject), shape);
+ fDevice->finishContentEntry(fClipStack, fBlendMode, fDstFormXObject, shape);
}
}
@@ -474,7 +479,7 @@
SkPDFDevice* fDevice = nullptr;
SkDynamicMemoryWStream* fContentStream = nullptr;
SkBlendMode fBlendMode;
- sk_sp<SkPDFObject> fDstFormXObject;
+ SkPDFIndirectReference fDstFormXObject;
SkPath fShape;
const SkClipStack* fClipStack;
};
@@ -497,9 +502,9 @@
fLinkToURLs = std::vector<RectWithData>();
fLinkToDestinations = std::vector<RectWithData>();
fNamedDestinations = std::vector<NamedDestination>();
- fGraphicStateResources = std::vector<sk_sp<SkPDFObject>>();
- fXObjectResources = std::vector<sk_sp<SkPDFObject>>();
- fShaderResources = std::vector<sk_sp<SkPDFObject>>();
+ fGraphicStateResources.reset();
+ fXObjectResources.reset();
+ fShaderResources.reset();
fFontResources.reset();
fContent.reset();
fActiveStackState = GraphicStackState();
@@ -797,23 +802,24 @@
return index;
}
-void SkPDFDevice::setGraphicState(sk_sp<SkPDFObject> gs, SkDynamicMemoryWStream* content) {
- SkPDFUtils::ApplyGraphicState(find_or_add(&fGraphicStateResources, std::move(gs)), content);
+void SkPDFDevice::setGraphicState(SkPDFIndirectReference gs, SkDynamicMemoryWStream* content) {
+ SkPDFUtils::ApplyGraphicState(add_resource(fGraphicStateResources, gs), content);
}
void SkPDFDevice::addSMaskGraphicState(sk_sp<SkPDFDevice> maskDevice,
SkDynamicMemoryWStream* contentStream) {
this->setGraphicState(SkPDFGraphicState::GetSMaskGraphicState(
maskDevice->makeFormXObjectFromDevice(true), false,
- SkPDFGraphicState::kLuminosity_SMaskMode, this->getCanon()), contentStream);
+ SkPDFGraphicState::kLuminosity_SMaskMode, fDocument), contentStream);
}
void SkPDFDevice::clearMaskOnGraphicState(SkDynamicMemoryWStream* contentStream) {
// The no-softmask graphic state is used to "turn off" the mask for later draw calls.
- sk_sp<SkPDFDict>& noSMaskGS = this->getCanon()->fNoSmaskGraphicState;
+ SkPDFIndirectReference& noSMaskGS = this->getCanon()->fNoSmaskGraphicState;
if (!noSMaskGS) {
- noSMaskGS = sk_make_sp<SkPDFDict>("ExtGState");
- noSMaskGS->insertName("SMask", "None");
+ SkPDFDict tmp("ExtGState");
+ tmp.insertName("SMask", "None");
+ noSMaskGS = fDocument->emit(tmp);
}
this->setGraphicState(noSMaskGS, contentStream);
}
@@ -1240,12 +1246,10 @@
// Not yet specified font or need to switch font.
font = SkPDFFont::GetFontResource(fDocument, glyphCache.get(), typeface, gid);
SkASSERT(font); // All preconditions for SkPDFFont::GetFontResource are met.
- SkPDFIndirectReference ref = font->indirectReference();
- fFontResources.add(ref);
-
glyphPositioner.flush();
glyphPositioner.setWideChars(font->multiByteGlyphs());
- SkPDFWriteResourceName(out, SkPDFResourceType::kFont, ref.fValue);
+ SkPDFWriteResourceName(out, SkPDFResourceType::kFont,
+ add_resource(fFontResources, font->indirectReference()));
out->writeText(" ");
SkPDFUtils::AppendScalar(textSize, out);
out->writeText(" Tf\n");
@@ -1275,9 +1279,10 @@
// TODO: implement drawVertices
}
-void SkPDFDevice::drawFormXObject(sk_sp<SkPDFObject> xObject, SkDynamicMemoryWStream* content) {
+void SkPDFDevice::drawFormXObject(SkPDFIndirectReference xObject, SkDynamicMemoryWStream* content) {
+ SkASSERT(xObject);
SkPDFWriteResourceName(content, SkPDFResourceType::kXObject,
- find_or_add(&fXObjectResources, std::move(xObject)));
+ add_resource(fXObjectResources, xObject));
content->writeText(" Do\n");
}
@@ -1335,18 +1340,20 @@
return SkSurface::MakeRaster(info, &props);
}
+static std::vector<SkPDFIndirectReference> sort(const SkTHashSet<SkPDFIndirectReference>& src) {
+ std::vector<SkPDFIndirectReference> dst;
+ dst.reserve(src.count());
+ src.foreach([&dst](SkPDFIndirectReference ref) { dst.push_back(ref); } );
+ std::sort(dst.begin(), dst.end(),
+ [](SkPDFIndirectReference a, SkPDFIndirectReference b) { return a.fValue < b.fValue; });
+ return dst;
+}
sk_sp<SkPDFDict> SkPDFDevice::makeResourceDict() {
- std::vector<SkPDFIndirectReference> fonts;
- fonts.reserve(fFontResources.count());
- fFontResources.foreach([&fonts](SkPDFIndirectReference ref) { fonts.push_back(ref); } );
- fFontResources.reset();
- std::sort(fonts.begin(), fonts.end(),
- [](SkPDFIndirectReference a, SkPDFIndirectReference b) { return a.fValue < b.fValue; });
- return SkPDFMakeResourceDict(std::move(fGraphicStateResources),
- std::move(fShaderResources),
- std::move(fXObjectResources),
- std::move(fonts));
+ return SkPDFMakeResourceDict(sort(fGraphicStateResources),
+ sort(fShaderResources),
+ sort(fXObjectResources),
+ sort(fFontResources));
}
std::unique_ptr<SkStreamAsset> SkPDFDevice::content() {
@@ -1446,13 +1453,13 @@
for (const RectWithData& rectWithURL : fLinkToURLs) {
SkRect r;
fInitialTransform.mapRect(&r, rectWithURL.rect);
- array->appendObjRef(create_link_to_url(rectWithURL.data.get(), r));
+ array->appendRef(fDocument->emit(*create_link_to_url(rectWithURL.data.get(), r)));
}
for (const RectWithData& linkToDestination : fLinkToDestinations) {
SkRect r;
fInitialTransform.mapRect(&r, linkToDestination.rect);
- array->appendObjRef(
- create_link_named_dest(linkToDestination.data.get(), r));
+ array->appendRef(
+ fDocument->emit(*create_link_named_dest(linkToDestination.data.get(), r)));
}
return array;
}
@@ -1461,6 +1468,7 @@
for (const NamedDestination& dest : fNamedDestinations) {
auto pdfDest = sk_make_sp<SkPDFArray>();
pdfDest->reserve(5);
+ // TODO(halcanry) reserve an IndirectReference for each page with beginPage()
pdfDest->appendObjRef(sk_ref_sp(page));
pdfDest->appendName("XYZ");
SkPoint p = fInitialTransform.mapXY(dest.point.x(), dest.point.y());
@@ -1472,7 +1480,7 @@
}
}
-sk_sp<SkPDFObject> SkPDFDevice::makeFormXObjectFromDevice(bool alpha) {
+SkPDFIndirectReference SkPDFDevice::makeFormXObjectFromDevice(bool alpha) {
SkMatrix inverseTransform = SkMatrix::I();
if (!fInitialTransform.isIdentity()) {
if (!fInitialTransform.invert(&inverseTransform)) {
@@ -1482,8 +1490,8 @@
}
const char* colorSpace = alpha ? "DeviceGray" : nullptr;
- sk_sp<SkPDFObject> xobject =
- SkPDFMakeFormXObject(this->content(),
+ SkPDFIndirectReference xobject =
+ SkPDFMakeFormXObject(fDocument, this->content(),
SkPDFMakeArray(0, 0, this->width(), this->height()),
this->makeResourceDict(), inverseTransform, colorSpace);
// We always draw the form xobjects that we create back into the device, so
@@ -1493,10 +1501,11 @@
return xobject;
}
-void SkPDFDevice::drawFormXObjectWithMask(sk_sp<SkPDFObject> xObject,
- sk_sp<SkPDFObject> mask,
+void SkPDFDevice::drawFormXObjectWithMask(SkPDFIndirectReference xObject,
+ SkPDFIndirectReference sMask,
SkBlendMode mode,
bool invertClip) {
+ SkASSERT(sMask);
SkPaint paint;
paint.setBlendMode(mode);
ScopedContentEntry content(this, nullptr, SkMatrix::I(), paint);
@@ -1504,9 +1513,9 @@
return;
}
this->setGraphicState(SkPDFGraphicState::GetSMaskGraphicState(
- std::move(mask), invertClip, SkPDFGraphicState::kAlpha_SMaskMode,
- fDocument->canon()), content.stream());
- this->drawFormXObject(std::move(xObject), content.stream());
+ sMask, invertClip, SkPDFGraphicState::kAlpha_SMaskMode,
+ fDocument), content.stream());
+ this->drawFormXObject(xObject, content.stream());
this->clearMaskOnGraphicState(content.stream());
}
@@ -1519,8 +1528,8 @@
const SkMatrix& matrix,
const SkPaint& paint,
bool hasText,
- sk_sp<SkPDFObject>* dst) {
- *dst = nullptr;
+ SkPDFIndirectReference* dst) {
+ SkASSERT(!*dst);
SkBlendMode blendMode = paint.getBlendMode();
// Dst xfer mode doesn't draw source at all.
@@ -1570,7 +1579,7 @@
void SkPDFDevice::finishContentEntry(const SkClipStack* clipStack,
SkBlendMode blendMode,
- sk_sp<SkPDFObject> dst,
+ SkPDFIndirectReference dst,
SkPath* shape) {
SkASSERT(blendMode != SkBlendMode::kDst);
if (treat_as_regular_pdf_blend_mode(blendMode)) {
@@ -1618,7 +1627,7 @@
SkPaint stockPaint;
- sk_sp<SkPDFObject> srcFormXObject;
+ SkPDFIndirectReference srcFormXObject;
if (this->isContentEmpty()) {
// If nothing was drawn and there's no shape, then the draw was a
// no-op, but dst needs to be restored for that to be true.
@@ -1628,7 +1637,7 @@
if (shape == nullptr || blendMode == SkBlendMode::kDstOut ||
blendMode == SkBlendMode::kSrcATop) {
ScopedContentEntry content(this, nullptr, SkMatrix::I(), stockPaint);
- this->drawFormXObject(std::move(dst), content.stream());
+ this->drawFormXObject(dst, content.stream());
return;
} else {
blendMode = SkBlendMode::kClear;
@@ -1691,8 +1700,8 @@
if (blendMode == SkBlendMode::kSrcIn ||
blendMode == SkBlendMode::kSrcOut ||
blendMode == SkBlendMode::kSrcATop) {
- this->drawFormXObjectWithMask(std::move(srcFormXObject), std::move(dst),
- SkBlendMode::kSrcOver, blendMode == SkBlendMode::kSrcOut);
+ this->drawFormXObjectWithMask(srcFormXObject, dst, SkBlendMode::kSrcOver,
+ blendMode == SkBlendMode::kSrcOut);
return;
} else {
SkBlendMode mode = SkBlendMode::kSrcOver;
@@ -1700,8 +1709,7 @@
this->drawFormXObjectWithMask(srcFormXObject, dst, SkBlendMode::kSrcOver, false);
mode = SkBlendMode::kMultiply;
}
- this->drawFormXObjectWithMask(std::move(dst), std::move(srcFormXObject), mode,
- blendMode == SkBlendMode::kDstOut);
+ this->drawFormXObjectWithMask(dst, srcFormXObject, mode, blendMode == SkBlendMode::kDstOut);
return;
}
}
@@ -1728,7 +1736,6 @@
entry->fShaderIndex = -1;
// PDF treats a shader as a color, so we only set one or the other.
- sk_sp<SkPDFObject> pdfShader;
SkShader* shader = paint.getShader();
if (shader) {
if (SkShader::kColor_GradientType == shader->asAGradient(nullptr)) {
@@ -1759,24 +1766,25 @@
SkIRect bounds;
clipStackBounds.roundOut(&bounds);
- pdfShader = SkPDFMakeShader(fDocument, shader, transform, bounds, paint.getColor());
+ SkPDFIndirectReference pdfShader
+ = SkPDFMakeShader(fDocument, shader, transform, bounds, paint.getColor());
if (pdfShader) {
// pdfShader has been canonicalized so we can directly compare pointers.
- entry->fShaderIndex = find_or_add(&fShaderResources, std::move(pdfShader));
+ entry->fShaderIndex = add_resource(fShaderResources, pdfShader);
}
}
}
- sk_sp<SkPDFDict> newGraphicState;
+ SkPDFIndirectReference newGraphicState;
if (color == paint.getColor4f()) {
- newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument->canon(), paint);
+ newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument, paint);
} else {
SkPaint newPaint = paint;
newPaint.setColor4f(color, nullptr);
- newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument->canon(), newPaint);
+ newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument, newPaint);
}
- entry->fGraphicStateIndex = find_or_add(&fGraphicStateResources, std::move(newGraphicState));
+ entry->fGraphicStateIndex = add_resource(fGraphicStateResources, std::move(newGraphicState));
if (hasText) {
entry->fTextScaleX = paint.getTextScaleX();
@@ -1814,15 +1822,6 @@
is_integer(r.bottom());
}
-namespace {
-// This struct will go away when fIndirectReference goes away.
-struct PDFObj final : public SkPDFObject {
- PDFObj(SkPDFIndirectReference ref) { fIndirectReference = ref; }
- // emitObject() is never called since the Object already has a indirect ref.
- void emitObject(SkWStream*) const override { SK_ABORT("DO NOT REACH HERE"); }
-};
-} // namespace
-
void SkPDFDevice::internalDrawImageRect(SkKeyedImage imageSubset,
const SkRect* src,
const SkRect& dst,
@@ -2046,18 +2045,17 @@
}
SkBitmapKey key = imageSubset.key();
- sk_sp<SkPDFObject>* pdfimagePtr = fDocument->canon()->fPDFBitmapMap.find(key);
- sk_sp<SkPDFObject> pdfimage = pdfimagePtr ? *pdfimagePtr : nullptr;
- if (!pdfimage) {
+ SkPDFIndirectReference* pdfimagePtr = fDocument->canon()->fPDFBitmapMap.find(key);
+ SkPDFIndirectReference pdfimage = pdfimagePtr ? *pdfimagePtr : SkPDFIndirectReference();
+ if (!pdfimagePtr) {
SkASSERT(imageSubset);
- auto ref = SkPDFSerializeImage(imageSubset.image().get(), fDocument,
+ pdfimage = SkPDFSerializeImage(imageSubset.image().get(), fDocument,
fDocument->metadata().fEncodingQuality);
- SkASSERT(ref.fValue > 0);
- pdfimage = sk_make_sp<PDFObj>(ref);
SkASSERT((key != SkBitmapKey{{0, 0, 0, 0}, 0}));
fDocument->canon()->fPDFBitmapMap.set(key, pdfimage);
}
- this->drawFormXObject(std::move(pdfimage), content.stream());
+ SkASSERT(pdfimage != SkPDFIndirectReference());
+ this->drawFormXObject(pdfimage, content.stream());
}
///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/pdf/SkPDFDevice.h b/src/pdf/SkPDFDevice.h
index 86dc4f5..c709d59 100644
--- a/src/pdf/SkPDFDevice.h
+++ b/src/pdf/SkPDFDevice.h
@@ -33,7 +33,6 @@
class SkPDFDict;
class SkPDFFont;
class SkPDFObject;
-class SkPDFStream;
class SkRRect;
struct SkPDFIndirectReference;
@@ -167,9 +166,9 @@
std::vector<RectWithData> fLinkToDestinations;
std::vector<NamedDestination> fNamedDestinations;
- std::vector<sk_sp<SkPDFObject>> fGraphicStateResources;
- std::vector<sk_sp<SkPDFObject>> fXObjectResources;
- std::vector<sk_sp<SkPDFObject>> fShaderResources;
+ SkTHashSet<SkPDFIndirectReference> fGraphicStateResources;
+ SkTHashSet<SkPDFIndirectReference> fXObjectResources;
+ SkTHashSet<SkPDFIndirectReference> fShaderResources;
SkTHashSet<SkPDFIndirectReference> fFontResources;
int fNodeId;
@@ -199,10 +198,10 @@
SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override;
// Set alpha to true if making a transparency group form x-objects.
- sk_sp<SkPDFObject> makeFormXObjectFromDevice(bool alpha = false);
+ SkPDFIndirectReference makeFormXObjectFromDevice(bool alpha = false);
- void drawFormXObjectWithMask(sk_sp<SkPDFObject> xObject,
- sk_sp<SkPDFObject> mask,
+ void drawFormXObjectWithMask(SkPDFIndirectReference xObject,
+ SkPDFIndirectReference sMask,
SkBlendMode,
bool invertClip);
@@ -211,11 +210,11 @@
// setUpContentEntry and finishContentEntry can be used directly, but
// the preferred method is to use the ScopedContentEntry helper class.
SkDynamicMemoryWStream* setUpContentEntry(const SkClipStack* clipStack,
- const SkMatrix& matrix,
- const SkPaint& paint,
- bool hasText,
- sk_sp<SkPDFObject>* dst);
- void finishContentEntry(const SkClipStack*, SkBlendMode, sk_sp<SkPDFObject> dst, SkPath* shape);
+ const SkMatrix& matrix,
+ const SkPaint& paint,
+ bool hasText,
+ SkPDFIndirectReference* dst);
+ void finishContentEntry(const SkClipStack*, SkBlendMode, SkPDFIndirectReference, SkPath*);
bool isContentEmpty();
void populateGraphicStateEntryFromPaint(const SkMatrix& matrix,
@@ -248,8 +247,8 @@
void addSMaskGraphicState(sk_sp<SkPDFDevice> maskDevice, SkDynamicMemoryWStream*);
void clearMaskOnGraphicState(SkDynamicMemoryWStream*);
- void setGraphicState(sk_sp<SkPDFObject>, SkDynamicMemoryWStream*);
- void drawFormXObject(sk_sp<SkPDFObject>, SkDynamicMemoryWStream*);
+ void setGraphicState(SkPDFIndirectReference gs, SkDynamicMemoryWStream*);
+ void drawFormXObject(SkPDFIndirectReference xObject, SkDynamicMemoryWStream*);
bool hasEmptyClip() const { return this->cs().isEmpty(this->bounds()); }
diff --git a/src/pdf/SkPDFDocument.cpp b/src/pdf/SkPDFDocument.cpp
index d2bb292..2bf48ee 100644
--- a/src/pdf/SkPDFDocument.cpp
+++ b/src/pdf/SkPDFDocument.cpp
@@ -47,10 +47,6 @@
SkPDFObjectSerializer::~SkPDFObjectSerializer() = default;
-SkPDFObjectSerializer::SkPDFObjectSerializer(SkPDFObjectSerializer&&) = default;
-
-SkPDFObjectSerializer& SkPDFObjectSerializer::operator=(SkPDFObjectSerializer&&) = default;
-
#define SKPDF_MAGIC "\xD3\xEB\xE9\xE1"
#ifndef SK_BUILD_FOR_WIN
@@ -59,16 +55,13 @@
static_assert((SKPDF_MAGIC[2] & 0x7F) == "Skia"[2], "");
static_assert((SKPDF_MAGIC[3] & 0x7F) == "Skia"[3], "");
#endif
-void SkPDFObjectSerializer::serializeHeader(SkWStream* wStream,
- const SkPDF::Metadata& md) {
+void SkPDFObjectSerializer::serializeHeader(SkWStream* wStream) {
fBaseOffset = wStream->bytesWritten();
static const char kHeader[] = "%PDF-1.4\n%" SKPDF_MAGIC "\n";
wStream->writeText(kHeader);
// The PDF spec recommends including a comment with four
// bytes, all with their high bits set. "\xD3\xEB\xE9\xE1" is
// "Skia" with the high bits set.
- fInfoDict = SkPDFMetadata::MakeDocumentInformationDict(md);
- this->serializeObject(fInfoDict, wStream);
}
#undef SKPDF_MAGIC
@@ -86,8 +79,7 @@
void SkPDFObjectSerializer::serializeObject(const sk_sp<SkPDFObject>& object,
SkWStream* wStream) {
- SkPDFObjNumMap objNumMap;
- objNumMap.fNextObjectNumber = fNextObjectNumber;
+ SkPDFObjNumMap objNumMap(this);
objNumMap.addObjectRecursively(object.get());
for (const sk_sp<SkPDFObject>& object : objNumMap.fObjects) {
@@ -96,18 +88,18 @@
this->endObject(wStream);
object->drop();
}
- fNextObjectNumber = objNumMap.fNextObjectNumber; // save for later.
}
// Xref table and footer
void SkPDFObjectSerializer::serializeFooter(SkWStream* wStream,
- const sk_sp<SkPDFObject> docCatalog,
- sk_sp<SkPDFObject> id) {
+ SkPDFIndirectReference infoDict,
+ SkPDFIndirectReference docCatalog,
+ SkUUID uuid) {
int xRefFileOffset = this->offset(wStream).fValue;
// Include the special zeroth object in the count.
- int objCount = SkToS32(fNextObjectNumber);
+ int objCount = SkToS32(fNextObjectNumber.load());
wStream->writeText("xref\n0 ");
wStream->writeDecAsText(objCount);
wStream->writeText("\n0000000000 65535 f \n");
@@ -118,12 +110,12 @@
}
SkPDFDict trailerDict;
trailerDict.insertInt("Size", objCount);
- SkASSERT(docCatalog);
- trailerDict.insertObjRef("Root", docCatalog);
- SkASSERT(fInfoDict);
- trailerDict.insertObjRef("Info", std::move(fInfoDict));
- if (id) {
- trailerDict.insertObject("ID", std::move(id));
+ SkASSERT(docCatalog != SkPDFIndirectReference());
+ trailerDict.insertRef("Root", docCatalog);
+ SkASSERT(infoDict != SkPDFIndirectReference());
+ trailerDict.insertRef("Info", infoDict);
+ if (SkUUID() != uuid) {
+ trailerDict.insertObject("ID", SkPDFMetadata::MakePdfId(uuid, uuid));
}
wStream->writeText("trailer\n");
trailerDict.emitObject(wStream);
@@ -216,6 +208,7 @@
if (fMetadata.fStructureElementTreeRoot) {
fTagRoot = recursiveBuildTagTree(*fMetadata.fStructureElementTreeRoot, nullptr);
}
+ fExecutor = metadata.fExecutor;
}
SkPDFDocument::~SkPDFDocument() {
@@ -225,28 +218,34 @@
SkPDFIndirectReference SkPDFDocument::serialize(const sk_sp<SkPDFObject>& object) {
SkASSERT(object);
- fObjectSerializer.serializeObject(object, this->getStream());
+ if (object->fIndirectReference == SkPDFIndirectReference()) {
+ SkAutoMutexAcquire autoMutexAcquire(fMutex);
+ fObjectSerializer.serializeObject(object, this->getStream());
+ }
+ SkASSERT(object->fIndirectReference != SkPDFIndirectReference());
return object->fIndirectReference;
}
-SkPDFIndirectReference SkPDFDocument::emit(const SkPDFObject& object){
- SkPDFIndirectReference ref = this->reserve();
+SkPDFIndirectReference SkPDFDocument::emit(const SkPDFObject& object, SkPDFIndirectReference ref){
object.emitObject(this->beginObject(ref));
this->endObject();
return ref;
}
-SkPDFIndirectReference SkPDFDocument::reserve() {
+SkPDFIndirectReference SkPDFDocument::reserveRef() {
++fOutstandingRefs;
- return SkPDFIndirectReference{fObjectSerializer.fNextObjectNumber++};
+ return fObjectSerializer.reserve();
};
SkWStream* SkPDFDocument::beginObject(SkPDFIndirectReference ref) {
- --fOutstandingRefs;
+ fMutex.acquire();
return fObjectSerializer.beginObject(ref, this->getStream());
};
+
void SkPDFDocument::endObject() {
fObjectSerializer.endObject(this->getStream());
+ fMutex.release();
+ fSemaphore.signal();
};
static SkSize operator*(SkISize u, SkScalar s) { return SkSize{u.width() * s, u.height() * s}; }
@@ -256,18 +255,22 @@
SkASSERT(fCanvas.imageInfo().dimensions().isZero());
if (fPages.empty()) {
// if this is the first page if the document.
- fObjectSerializer.serializeHeader(this->getStream(), fMetadata);
+ {
+ SkAutoMutexAcquire autoMutexAcquire(fMutex);
+ fObjectSerializer.serializeHeader(this->getStream());
+ }
+
+ fInfoDict = this->serialize(SkPDFMetadata::MakeDocumentInformationDict(fMetadata));
fDests = sk_make_sp<SkPDFDict>();
if (fMetadata.fPDFA) {
- SkPDFMetadata::UUID uuid = SkPDFMetadata::CreateUUID(fMetadata);
+ fUUID = SkPDFMetadata::CreateUUID(fMetadata);
// We use the same UUID for Document ID and Instance ID since this
// is the first revision of this document (and Skia does not
// support revising existing PDF documents).
// If we are not in PDF/A mode, don't use a UUID since testing
// works best with reproducible outputs.
- fID = SkPDFMetadata::MakePdfId(uuid, uuid);
- fXMP = SkPDFMetadata::MakeXMPObject(fMetadata, uuid, uuid);
- fObjectSerializer.serializeObject(fXMP, this->getStream());
+ fXMP = this->serialize(
+ SkPDFMetadata::MakeXMPObject(fMetadata, fUUID, fUUID));
}
}
// By scaling the page at the device level, we will create bitmap layer
@@ -295,7 +298,7 @@
auto page = sk_make_sp<SkPDFDict>("Page");
SkSize mediaSize = fPageDevice->imageInfo().dimensions() * fInverseRasterScale;
- auto contentObject = sk_make_sp<SkPDFStream>(fPageDevice->content());
+ std::unique_ptr<SkStreamAsset> pageContent = fPageDevice->content();
auto resourceDict = fPageDevice->makeResourceDict();
auto annotations = fPageDevice->getAnnotations();
fPageDevice->appendDestinations(fDests.get(), page.get());
@@ -307,8 +310,7 @@
if (annotations) {
page->insertObject("Annots", std::move(annotations));
}
- this->serialize(contentObject);
- page->insertObjRef("Contents", std::move(contentObject));
+ page->insertRef("Contents", SkPDFStreamOut(nullptr, std::move(pageContent), this));
// The StructParents unique identifier for each page is just its
// 0-based page index.
page->insertInt("StructParents", static_cast<int>(fPages.size()));
@@ -320,14 +322,14 @@
}
void SkPDFDocument::reset() {
- fObjectSerializer = SkPDFObjectSerializer();
+ reset_object(&fObjectSerializer);
fCanon = SkPDFCanon();
reset_object(&fCanvas);
fPages = std::vector<sk_sp<SkPDFDict>>();
fDests = nullptr;
fPageDevice = nullptr;
- fID = nullptr;
- fXMP = nullptr;
+ fUUID = SkUUID();
+ fXMP = SkPDFIndirectReference();
fMetadata = SkPDF::Metadata();
fRasterScale = 1;
fInverseRasterScale = 1;
@@ -447,14 +449,14 @@
return SkData::MakeWithoutCopy(kProfile, kProfileLength);
}
-static sk_sp<SkPDFStream> make_srgb_color_profile() {
- sk_sp<SkPDFStream> stream = sk_make_sp<SkPDFStream>(SkSrgbIcm());
- stream->dict()->insertInt("N", 3);
- stream->dict()->insertObject("Range", SkPDFMakeArray(0, 1, 0, 1, 0, 1));
- return stream;
+static SkPDFIndirectReference make_srgb_color_profile(SkPDFDocument* doc) {
+ sk_sp<SkPDFDict> dict = sk_make_sp<SkPDFDict>();
+ dict->insertInt("N", 3);
+ dict->insertObject("Range", SkPDFMakeArray(0, 1, 0, 1, 0, 1));
+ return SkPDFStreamOut(std::move(dict), SkMemoryStream::Make(SkSrgbIcm()), doc, true);
}
-static sk_sp<SkPDFArray> make_srgb_output_intents() {
+static sk_sp<SkPDFArray> make_srgb_output_intents(SkPDFDocument* doc) {
// sRGB is specified by HTML, CSS, and SVG.
auto outputIntent = sk_make_sp<SkPDFDict>("OutputIntent");
outputIntent->insertName("S", "GTS_PDFA1");
@@ -462,8 +464,7 @@
outputIntent->insertString("OutputConditionIdentifier",
"Custom");
outputIntent->insertString("Info","sRGB IEC61966-2.1");
- outputIntent->insertObjRef("DestOutputProfile",
- make_srgb_color_profile());
+ outputIntent->insertRef("DestOutputProfile", make_srgb_color_profile(doc));
auto intentArray = sk_make_sp<SkPDFArray>();
intentArray->appendObject(std::move(outputIntent));
return intentArray;
@@ -533,11 +534,11 @@
}
auto docCatalog = sk_make_sp<SkPDFDict>("Catalog");
if (fMetadata.fPDFA) {
- SkASSERT(fXMP);
- docCatalog->insertObjRef("Metadata", fXMP);
+ SkASSERT(fXMP != SkPDFIndirectReference());
+ docCatalog->insertRef("Metadata", fXMP);
// Don't specify OutputIntents if we are not in PDF/A mode since
// no one has ever asked for this feature.
- docCatalog->insertObject("OutputIntents", make_srgb_output_intents());
+ docCatalog->insertObject("OutputIntents", make_srgb_output_intents(this));
}
sk_sp<SkPDFDict> pageTree = generate_page_tree(fPages);
@@ -593,15 +594,23 @@
}
}
}
- fObjectSerializer.serializeObject(docCatalog, this->getStream());
+ auto docCatalogRef = this->serialize(docCatalog);
for (const SkPDFFont* f : get_fonts(fCanon)) {
f->emitSubset(this);
}
- SkASSERT(fOutstandingRefs == 0);
- fObjectSerializer.serializeFooter(this->getStream(), docCatalog, fID);
+ while (fOutstandingRefs > 0) {
+ fSemaphore.wait();
+ --fOutstandingRefs;
+ }
+
+ {
+ SkAutoMutexAcquire autoMutexAcquire(fMutex);
+ fObjectSerializer.serializeFooter(this->getStream(), fInfoDict, docCatalogRef, fUUID);
+ }
this->reset();
}
+
///////////////////////////////////////////////////////////////////////////////
void SkPDF::SetNodeId(SkCanvas* canvas, int nodeID) {
diff --git a/src/pdf/SkPDFDocumentPriv.h b/src/pdf/SkPDFDocumentPriv.h
index c23e026..27810dd 100644
--- a/src/pdf/SkPDFDocumentPriv.h
+++ b/src/pdf/SkPDFDocumentPriv.h
@@ -8,13 +8,17 @@
#define SkPDFDocumentPriv_DEFINED
#include "SkCanvas.h"
-#include "SkPDFDocument.h"
#include "SkPDFCanon.h"
+#include "SkPDFDocument.h"
#include "SkPDFFont.h"
#include "SkPDFMetadata.h"
+#include "SkUUID.h"
+
+#include <atomic>
class SkPDFDevice;
class SkPDFTag;
+class SkExecutor;
const char* SkPDFGetNodeIdKey();
@@ -31,23 +35,26 @@
// Logically part of SkPDFDocument (like SkPDFCanon), but separate to
// keep similar functionality together.
struct SkPDFObjectSerializer {
- int fNextObjectNumber = 1;
+ std::atomic<int> fNextObjectNumber = {1};
+ SkPDFIndirectReference reserve() { return SkPDFIndirectReference{fNextObjectNumber++}; }
SkPDFOffsetMap fOffsets;
- sk_sp<SkPDFObject> fInfoDict;
size_t fBaseOffset = SIZE_MAX;
SkPDFObjectSerializer();
~SkPDFObjectSerializer();
- SkPDFObjectSerializer(SkPDFObjectSerializer&&);
- SkPDFObjectSerializer& operator=(SkPDFObjectSerializer&&);
+ SkPDFObjectSerializer(SkPDFObjectSerializer&&) = delete;
+ SkPDFObjectSerializer& operator=(SkPDFObjectSerializer&&) = delete;
SkPDFObjectSerializer(const SkPDFObjectSerializer&) = delete;
SkPDFObjectSerializer& operator=(const SkPDFObjectSerializer&) = delete;
SkWStream* beginObject(SkPDFIndirectReference, SkWStream*);
void endObject(SkWStream*);
- void serializeHeader(SkWStream*, const SkPDF::Metadata&);
+ void serializeHeader(SkWStream*);
void serializeObject(const sk_sp<SkPDFObject>&, SkWStream*);
- void serializeFooter(SkWStream*, const sk_sp<SkPDFObject>, sk_sp<SkPDFObject>);
+ void serializeFooter(SkWStream*,
+ SkPDFIndirectReference infoDict,
+ SkPDFIndirectReference docCatalog,
+ SkUUID uuid);
SkPDFFileOffset offset(SkWStream*);
};
@@ -73,7 +80,8 @@
after calling serialize, since those changes will be too late.
*/
SkPDFIndirectReference serialize(const sk_sp<SkPDFObject>&);
- SkPDFIndirectReference emit(const SkPDFObject&);
+ SkPDFIndirectReference emit(const SkPDFObject&, SkPDFIndirectReference);
+ SkPDFIndirectReference emit(const SkPDFObject& o) { return this->emit(o, this->reserveRef()); }
SkPDFCanon* canon() { return &fCanon; }
const SkPDF::Metadata& metadata() const { return fMetadata; }
@@ -81,10 +89,12 @@
// Returns -1 if no mark ID.
int getMarkIdForNodeId(int nodeId);
- SkPDFIndirectReference reserve();
+ SkPDFIndirectReference reserveRef();
SkWStream* beginObject(SkPDFIndirectReference);
void endObject();
+ SkExecutor* executor() const { return fExecutor; }
+
private:
sk_sp<SkPDFTag> recursiveBuildTagTree(const SkPDF::StructureElementNode& node,
sk_sp<SkPDFTag> parent);
@@ -95,11 +105,13 @@
std::vector<sk_sp<SkPDFDict>> fPages;
sk_sp<SkPDFDict> fDests;
sk_sp<SkPDFDevice> fPageDevice;
- sk_sp<SkPDFObject> fID;
- sk_sp<SkPDFObject> fXMP;
+ SkUUID fUUID;
+ SkPDFIndirectReference fInfoDict;
+ SkPDFIndirectReference fXMP;
SkPDF::Metadata fMetadata;
SkScalar fRasterScale = 1;
SkScalar fInverseRasterScale = 1;
+ SkExecutor* fExecutor = nullptr;
// For tagged PDFs.
@@ -110,7 +122,9 @@
// A mapping from node ID to tag for fast lookup.
SkTHashMap<int, sk_sp<SkPDFTag>> fNodeIdToTag;
- int fOutstandingRefs = 0;
+ std::atomic<int> fOutstandingRefs = {0};
+ SkMutex fMutex;
+ SkSemaphore fSemaphore;
void reset();
};
diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp
index 83615f2..9be890b 100644
--- a/src/pdf/SkPDFFont.cpp
+++ b/src/pdf/SkPDFFont.cpp
@@ -225,7 +225,7 @@
firstNonZeroGlyph = subsetCode;
lastGlyph = SkToU16(SkTMin<int>((int)lastGlyph, 254 + (int)subsetCode));
}
- auto ref = doc->reserve();
+ auto ref = doc->reserveRef();
return canon->fFontMap.set(
fontID, SkPDFFont(std::move(typeface), firstNonZeroGlyph, lastGlyph, type, ref));
}
@@ -321,11 +321,13 @@
stream_to_data(std::move(fontAsset)), font.glyphUsage(),
metrics.fFontName.c_str(), ttcIndex);
if (subsetFontData) {
- size_t len = subsetFontData->size();
- sk_sp<SkPDFStream> subsetStream = sk_make_sp<SkPDFStream>(
- std::move(subsetFontData));
- subsetStream->dict()->insertInt("Length1", SkToInt(len));
- descriptor->insertRef("FontFile2", doc->serialize(subsetStream));
+ sk_sp<SkPDFDict> tmp = sk_make_sp<SkPDFDict>();
+ tmp->insertInt("Length1", SkToInt(subsetFontData->size()));
+ descriptor->insertRef(
+ "FontFile2",
+ SkPDFStreamOut(std::move(tmp),
+ SkMemoryStream::Make(std::move(subsetFontData)),
+ doc, true));
break;
}
// If subsetting fails, fall back to original font data.
@@ -335,15 +337,19 @@
if (!fontAsset || fontAsset->getLength() == 0) { break; }
}
#endif // SK_PDF_SUBSET_SUPPORTED
- auto fontStream = sk_make_sp<SkPDFSharedStream>(std::move(fontAsset));
- fontStream->dict()->insertInt("Length1", fontSize);
- descriptor->insertRef("FontFile2", doc->serialize(fontStream));
+ sk_sp<SkPDFDict> tmp = sk_make_sp<SkPDFDict>();
+ tmp->insertInt("Length1", fontSize);
+ descriptor->insertRef("FontFile2",
+ SkPDFStreamOut(std::move(tmp), std::move(fontAsset),
+ doc, true));
break;
}
case SkAdvancedTypefaceMetrics::kType1CID_Font: {
- auto fontStream = sk_make_sp<SkPDFSharedStream>(std::move(fontAsset));
- fontStream->dict()->insertName("Subtype", "CIDFontType0C");
- descriptor->insertRef("FontFile3", doc->serialize(fontStream));
+ sk_sp<SkPDFDict> tmp = sk_make_sp<SkPDFDict>();
+ tmp->insertName("Subtype", "CIDFontType0C");
+ descriptor->insertRef("FontFile3",
+ SkPDFStreamOut(std::move(tmp), std::move(fontAsset),
+ doc, true));
break;
}
default:
@@ -398,17 +404,15 @@
const std::vector<SkUnichar>& glyphToUnicode =
SkPDFFont::GetUnicodeMap(font.typeface(), canon);
SkASSERT(SkToSizeT(font.typeface()->countGlyphs()) == glyphToUnicode.size());
- fontDict.insertRef("ToUnicode",
- doc->serialize(
- SkPDFMakeToUnicodeCmap(glyphToUnicode.data(),
- &font.glyphUsage(),
- font.multiByteGlyphs(),
- font.firstGlyphID(),
- font.lastGlyphID())));
+ std::unique_ptr<SkStreamAsset> toUnicode =
+ SkPDFMakeToUnicodeCmap(glyphToUnicode.data(),
+ &font.glyphUsage(),
+ font.multiByteGlyphs(),
+ font.firstGlyphID(),
+ font.lastGlyphID());
+ fontDict.insertRef("ToUnicode", SkPDFStreamOut(nullptr, std::move(toUnicode), doc));
- SkWStream* stream = doc->beginObject(font.indirectReference());
- fontDict.emitObject(stream);
- doc->endObject();
+ doc->emit(fontDict, font.indirectReference());
}
///////////////////////////////////////////////////////////////////////////////
@@ -431,11 +435,13 @@
sk_sp<SkData> fontData = SkPDFConvertType1FontStream(std::move(rawFontData),
&header, &data, &trailer);
if (fontData) {
- SkPDFStream fontStream(std::move(fontData));
- fontStream.dict()->insertInt("Length1", header);
- fontStream.dict()->insertInt("Length2", data);
- fontStream.dict()->insertInt("Length3", trailer);
- descriptor.insertRef("FontFile", doc->emit(fontStream));
+ sk_sp<SkPDFDict> dict = sk_make_sp<SkPDFDict>();
+ dict->insertInt("Length1", header);
+ dict->insertInt("Length2", data);
+ dict->insertInt("Length3", trailer);
+ auto fontStream = SkMemoryStream::Make(std::move(fontData));
+ descriptor.insertRef("FontFile", SkPDFStreamOut(std::move(dict),
+ std::move(fontStream), doc, true));
}
}
}
@@ -514,9 +520,7 @@
encoding->insertObject("Differences", std::move(encDiffs));
font.insertObject("Encoding", std::move(encoding));
- SkWStream* stream = doc->beginObject(pdfFont.indirectReference());
- font.emitObject(stream);
- doc->endObject();
+ doc->emit(font, pdfFont.indirectReference());
}
void SkPDFFont::GetType1GlyphNames(const SkTypeface& face, SkString* dst) {
@@ -717,7 +721,8 @@
content.writeText(" Do\n");
}
}
- charProcs->insertRef(characterName, doc->emit(SkPDFStream(content.detachAsStream())));
+ charProcs->insertRef(characterName, SkPDFStreamOut(nullptr,
+ content.detachAsStream(), doc));
}
encDiffs->appendName(std::move(characterName));
widthArray->appendScalar(advance);
@@ -750,20 +755,18 @@
const std::vector<SkUnichar>& glyphToUnicode = SkPDFFont::GetUnicodeMap(typeface, canon);
SkASSERT(glyphToUnicode.size() == SkToSizeT(typeface->countGlyphs()));
- sk_sp<SkPDFStream> toUnicodeCmap = SkPDFMakeToUnicodeCmap(glyphToUnicode.data(),
- &subset,
- false,
- firstGlyphID,
- lastGlyphID);
- font.insertRef("ToUnicode", doc->serialize(toUnicodeCmap));
+ auto toUnicodeCmap = SkPDFMakeToUnicodeCmap(glyphToUnicode.data(),
+ &subset,
+ false,
+ firstGlyphID,
+ lastGlyphID);
+ font.insertRef("ToUnicode", SkPDFStreamOut(nullptr, std::move(toUnicodeCmap), doc));
font.insertRef("FontDescriptor", type3_descriptor(doc, typeface, cache.get()));
font.insertObject("Widths", std::move(widthArray));
font.insertObject("Encoding", std::move(encoding));
font.insertObject("CharProcs", std::move(charProcs));
- SkWStream* stream = doc->beginObject(pdfFont.indirectReference());
- font.emitObject(stream);
- doc->endObject();
+ doc->emit(font, pdfFont.indirectReference());
}
diff --git a/src/pdf/SkPDFFormXObject.cpp b/src/pdf/SkPDFFormXObject.cpp
index 99146a2..7dfa562 100644
--- a/src/pdf/SkPDFFormXObject.cpp
+++ b/src/pdf/SkPDFFormXObject.cpp
@@ -9,20 +9,20 @@
#include "SkPDFFormXObject.h"
#include "SkPDFUtils.h"
-sk_sp<SkPDFObject> SkPDFMakeFormXObject(std::unique_ptr<SkStreamAsset> content,
- sk_sp<SkPDFArray> mediaBox,
- sk_sp<SkPDFDict> resourceDict,
- const SkMatrix& inverseTransform,
- const char* colorSpace) {
- auto form = sk_make_sp<SkPDFStream>(std::move(content));
- form->dict()->insertName("Type", "XObject");
- form->dict()->insertName("Subtype", "Form");
+SkPDFIndirectReference SkPDFMakeFormXObject(SkPDFDocument* doc,
+ std::unique_ptr<SkStreamAsset> content,
+ sk_sp<SkPDFArray> mediaBox,
+ sk_sp<SkPDFDict> resourceDict,
+ const SkMatrix& inverseTransform,
+ const char* colorSpace) {
+ sk_sp<SkPDFDict> dict = sk_make_sp<SkPDFDict>();
+ dict->insertName("Type", "XObject");
+ dict->insertName("Subtype", "Form");
if (!inverseTransform.isIdentity()) {
- sk_sp<SkPDFObject> mat(SkPDFUtils::MatrixToArray(inverseTransform));
- form->dict()->insertObject("Matrix", std::move(mat));
+ dict->insertObject("Matrix", SkPDFUtils::MatrixToArray(inverseTransform));
}
- form->dict()->insertObject("Resources", std::move(resourceDict));
- form->dict()->insertObject("BBox", std::move(mediaBox));
+ dict->insertObject("Resources", std::move(resourceDict));
+ dict->insertObject("BBox", std::move(mediaBox));
// Right now FormXObject is only used for saveLayer, which implies
// isolated blending. Do this conditionally if that changes.
@@ -34,6 +34,6 @@
group->insertName("CS", colorSpace);
}
group->insertBool("I", true); // Isolated.
- form->dict()->insertObject("Group", std::move(group));
- return std::move(form);
+ dict->insertObject("Group", std::move(group));
+ return SkPDFStreamOut(std::move(dict), std::move(content), doc);
}
diff --git a/src/pdf/SkPDFFormXObject.h b/src/pdf/SkPDFFormXObject.h
index e62b69c..5fa52c9 100644
--- a/src/pdf/SkPDFFormXObject.h
+++ b/src/pdf/SkPDFFormXObject.h
@@ -12,14 +12,17 @@
#include "SkPDFDevice.h"
#include "SkPDFTypes.h"
+class SkPDFDocument;
+
/** A form XObject is a self contained description of a graphics
object. A form XObject is a page object with slightly different
syntax, that can be drawn into a page content stream, just like a
bitmap XObject can be drawn into a page content stream.
*/
-sk_sp<SkPDFObject> SkPDFMakeFormXObject(std::unique_ptr<SkStreamAsset> content,
- sk_sp<SkPDFArray> mediaBox,
- sk_sp<SkPDFDict> resourceDict,
- const SkMatrix& inverseTransform,
- const char* colorSpace);
+SkPDFIndirectReference SkPDFMakeFormXObject(SkPDFDocument* doc,
+ std::unique_ptr<SkStreamAsset> content,
+ sk_sp<SkPDFArray> mediaBox,
+ sk_sp<SkPDFDict> resourceDict,
+ const SkMatrix& inverseTransform,
+ const char* colorSpace);
#endif
diff --git a/src/pdf/SkPDFGradientShader.cpp b/src/pdf/SkPDFGradientShader.cpp
index d61b5f1..7bebb3c 100644
--- a/src/pdf/SkPDFGradientShader.cpp
+++ b/src/pdf/SkPDFGradientShader.cpp
@@ -13,6 +13,7 @@
#include "SkPDFFormXObject.h"
#include "SkPDFGraphicState.h"
#include "SkPDFResourceDict.h"
+#include "SkPDFTypes.h"
#include "SkPDFUtils.h"
static uint32_t hash(const SkShader::GradientInfo& v) {
@@ -573,20 +574,19 @@
return true;
}
-static sk_sp<SkPDFStream> make_ps_function(
- std::unique_ptr<SkStreamAsset> psCode,
- sk_sp<SkPDFArray> domain,
- sk_sp<SkPDFObject> range) {
- auto result = sk_make_sp<SkPDFStream>(std::move(psCode));
- result->dict()->insertInt("FunctionType", 4);
- result->dict()->insertObject("Domain", std::move(domain));
- result->dict()->insertObject("Range", std::move(range));
- return result;
+static SkPDFIndirectReference make_ps_function(std::unique_ptr<SkStreamAsset> psCode,
+ sk_sp<SkPDFArray> domain,
+ sk_sp<SkPDFObject> range,
+ SkPDFDocument* doc) {
+ sk_sp<SkPDFDict> dict = sk_make_sp<SkPDFDict>();
+ dict->insertInt("FunctionType", 4);
+ dict->insertObject("Domain", std::move(domain));
+ dict->insertObject("Range", std::move(range));
+ return SkPDFStreamOut(std::move(dict), std::move(psCode), doc);
}
-
-static sk_sp<SkPDFDict> make_function_shader(SkPDFCanon* canon,
- const SkPDFGradientShader::Key& state) {
+static SkPDFIndirectReference make_function_shader(SkPDFDocument* doc,
+ const SkPDFGradientShader::Key& state) {
SkPoint transformPoints[2];
const SkShader::GradientInfo& info = state.fInfo;
SkMatrix finalMatrix = state.fCanvasTransform;
@@ -670,7 +670,7 @@
case SkShader::kColor_GradientType:
case SkShader::kNone_GradientType:
default:
- return nullptr;
+ return SkPDFIndirectReference();
}
// Move any scaling (assuming a unit gradient) or translation
@@ -691,14 +691,14 @@
if (finalMatrix.hasPerspective()) {
if (!split_perspective(finalMatrix,
&finalMatrix, &perspectiveInverseOnly)) {
- return nullptr;
+ return SkPDFIndirectReference();
}
}
SkRect bbox;
bbox.set(state.fBBox);
if (!SkPDFUtils::InverseTransformBBox(finalMatrix, &bbox)) {
- return nullptr;
+ return SkPDFIndirectReference();
}
SkDynamicMemoryWStream functionCode;
@@ -707,7 +707,7 @@
if (state.fType == SkShader::kConical_GradientType) {
SkMatrix inverseMapperMatrix;
if (!mapperMatrix.invert(&inverseMapperMatrix)) {
- return nullptr;
+ return SkPDFIndirectReference();
}
inverseMapperMatrix.mapPoints(infoCopy.fPoint, 2);
infoCopy.fRadius[0] = inverseMapperMatrix.mapRadius(info.fRadius[0]);
@@ -735,13 +735,10 @@
bbox.bottom());
pdfShader->insertObject("Domain", domain);
- sk_sp<SkPDFArray>& rangeObject = canon->fRangeObject;
- if (!rangeObject) {
- rangeObject = SkPDFMakeArray(0, 1, 0, 1, 0, 1);
- }
- pdfShader->insertObjRef("Function",
- make_ps_function(functionCode.detachAsStream(), std::move(domain),
- rangeObject));
+ sk_sp<SkPDFArray> rangeObject = SkPDFMakeArray(0, 1, 0, 1, 0, 1);
+ pdfShader->insertRef("Function",
+ make_ps_function(functionCode.detachAsStream(), std::move(domain),
+ std::move(rangeObject), doc));
}
pdfShader->insertInt("ShadingType", shadingType);
@@ -752,38 +749,40 @@
pdfFunctionShader->insertObject("Matrix", SkPDFUtils::MatrixToArray(finalMatrix));
pdfFunctionShader->insertObject("Shading", std::move(pdfShader));
- return pdfFunctionShader;
+ return doc->serialize(pdfFunctionShader);
}
-static sk_sp<SkPDFObject> find_pdf_shader(SkPDFDocument* doc,
- SkPDFGradientShader::Key key,
- bool keyHasAlpha);
+static SkPDFIndirectReference find_pdf_shader(SkPDFDocument* doc,
+ SkPDFGradientShader::Key key,
+ bool keyHasAlpha);
-static sk_sp<SkPDFDict> get_gradient_resource_dict(sk_sp<SkPDFObject> functionShader,
- sk_sp<SkPDFObject> gState) {
- std::vector<sk_sp<SkPDFObject>> patternShaders;
- if (functionShader) {
- patternShaders.push_back(std::move(functionShader));
+static sk_sp<SkPDFDict> get_gradient_resource_dict(SkPDFIndirectReference functionShader,
+ SkPDFIndirectReference gState) {
+ std::vector<SkPDFIndirectReference> patternShaders;
+ if (functionShader != SkPDFIndirectReference()) {
+ patternShaders.push_back(functionShader);
}
- std::vector<sk_sp<SkPDFObject>> graphicStates;
- if (gState) {
- graphicStates.push_back(std::move(gState));
+ std::vector<SkPDFIndirectReference> graphicStates;
+ if (gState != SkPDFIndirectReference()) {
+ graphicStates.push_back(gState);
}
return SkPDFMakeResourceDict(std::move(graphicStates),
std::move(patternShaders),
- std::vector<sk_sp<SkPDFObject>>(),
+ std::vector<SkPDFIndirectReference>(),
std::vector<SkPDFIndirectReference>());
}
// Creates a content stream which fills the pattern P0 across bounds.
// @param gsIndex A graphics state resource index to apply, or <0 if no
// graphics state to apply.
-static std::unique_ptr<SkStreamAsset> create_pattern_fill_content(int gsIndex, SkRect& bounds) {
+static std::unique_ptr<SkStreamAsset> create_pattern_fill_content(int gsIndex,
+ int patternIndex,
+ SkRect& bounds) {
SkDynamicMemoryWStream content;
if (gsIndex >= 0) {
SkPDFUtils::ApplyGraphicState(gsIndex, &content);
}
- SkPDFUtils::ApplyPattern(0, &content);
+ SkPDFUtils::ApplyPattern(patternIndex, &content);
SkPDFUtils::AppendRectangle(bounds, &content);
SkPDFUtils::PaintPath(SkPaint::kFill_Style, SkPath::kEvenOdd_FillType, &content);
return content.detachAsStream();
@@ -818,7 +817,7 @@
return clone;
}
-static sk_sp<SkPDFObject> create_smask_graphic_state(SkPDFDocument* doc,
+static SkPDFIndirectReference create_smask_graphic_state(SkPDFDocument* doc,
const SkPDFGradientShader::Key& state) {
SkASSERT(state.fType != SkShader::kNone_GradientType);
SkPDFGradientShader::Key luminosityState = clone_key(state);
@@ -829,21 +828,23 @@
luminosityState.fHash = hash(luminosityState);
SkASSERT(!gradient_has_alpha(luminosityState));
- sk_sp<SkPDFObject> luminosityShader = find_pdf_shader(doc, std::move(luminosityState), false);
- sk_sp<SkPDFDict> resources = get_gradient_resource_dict(std::move(luminosityShader), nullptr);
+ SkPDFIndirectReference luminosityShader = find_pdf_shader(doc, std::move(luminosityState), false);
+ sk_sp<SkPDFDict> resources = get_gradient_resource_dict(luminosityShader,
+ SkPDFIndirectReference());
SkRect bbox = SkRect::Make(state.fBBox);
- sk_sp<SkPDFObject> alphaMask = SkPDFMakeFormXObject(create_pattern_fill_content(-1, bbox),
- SkPDFUtils::RectToArray(bbox),
- std::move(resources),
- SkMatrix::I(),
- "DeviceRGB");
+ SkPDFIndirectReference alphaMask =
+ SkPDFMakeFormXObject(doc,
+ create_pattern_fill_content(-1, luminosityShader.fValue, bbox),
+ SkPDFUtils::RectToArray(bbox),
+ std::move(resources),
+ SkMatrix::I(),
+ "DeviceRGB");
return SkPDFGraphicState::GetSMaskGraphicState(
- std::move(alphaMask), false,
- SkPDFGraphicState::kLuminosity_SMaskMode, doc->canon());
+ alphaMask, false, SkPDFGraphicState::kLuminosity_SMaskMode, doc);
}
-static sk_sp<SkPDFStream> make_alpha_function_shader(SkPDFDocument* doc,
- const SkPDFGradientShader::Key& state) {
+static SkPDFIndirectReference make_alpha_function_shader(SkPDFDocument* doc,
+ const SkPDFGradientShader::Key& state) {
SkASSERT(state.fType != SkShader::kNone_GradientType);
SkPDFGradientShader::Key opaqueState = clone_key(state);
for (int i = 0; i < opaqueState.fInfo.fColorCount; i++) {
@@ -853,24 +854,22 @@
SkASSERT(!gradient_has_alpha(opaqueState));
SkRect bbox = SkRect::Make(state.fBBox);
- sk_sp<SkPDFObject> colorShader = find_pdf_shader(doc, std::move(opaqueState), false);
+ SkPDFIndirectReference colorShader = find_pdf_shader(doc, std::move(opaqueState), false);
if (!colorShader) {
- return nullptr;
+ return SkPDFIndirectReference();
}
-
// Create resource dict with alpha graphics state as G0 and
// pattern shader as P0, then write content stream.
- sk_sp<SkPDFObject> alphaGs = create_smask_graphic_state(doc, state);
+ SkPDFIndirectReference alphaGsRef = create_smask_graphic_state(doc, state);
- sk_sp<SkPDFDict> resourceDict =
- get_gradient_resource_dict(std::move(colorShader), std::move(alphaGs));
+ sk_sp<SkPDFDict> resourceDict = get_gradient_resource_dict(colorShader, alphaGsRef);
- std::unique_ptr<SkStreamAsset> colorStream(create_pattern_fill_content(0, bbox));
- auto alphaFunctionShader = sk_make_sp<SkPDFStream>(std::move(colorStream));
-
- SkPDFUtils::PopulateTilingPatternDict(alphaFunctionShader->dict(), bbox,
+ std::unique_ptr<SkStreamAsset> colorStream =
+ create_pattern_fill_content(alphaGsRef.fValue, colorShader.fValue, bbox);
+ sk_sp<SkPDFDict> alphaFunctionShader = sk_make_sp<SkPDFDict>();
+ SkPDFUtils::PopulateTilingPatternDict(alphaFunctionShader.get(), bbox,
std::move(resourceDict), SkMatrix::I());
- return alphaFunctionShader;
+ return SkPDFStreamOut(std::move(alphaFunctionShader), std::move(colorStream), doc);
}
static SkPDFGradientShader::Key make_key(const SkShader* shader,
@@ -896,25 +895,25 @@
return key;
}
-static sk_sp<SkPDFObject> find_pdf_shader(SkPDFDocument* doc,
- SkPDFGradientShader::Key key,
- bool keyHasAlpha) {
+static SkPDFIndirectReference find_pdf_shader(SkPDFDocument* doc,
+ SkPDFGradientShader::Key key,
+ bool keyHasAlpha) {
SkASSERT(gradient_has_alpha(key) == keyHasAlpha);
- SkPDFCanon* canon = doc->canon();
- if (sk_sp<SkPDFObject>* ptr = canon->fGradientPatternMap.find(key)) {
+ auto& gradientPatternMap = doc->canon()->fGradientPatternMap;
+ if (SkPDFIndirectReference* ptr = gradientPatternMap.find(key)) {
return *ptr;
}
- sk_sp<SkPDFObject> pdfShader;
+ SkPDFIndirectReference pdfShader;
if (keyHasAlpha) {
pdfShader = make_alpha_function_shader(doc, key);
} else {
- pdfShader = make_function_shader(canon, key);
+ pdfShader = make_function_shader(doc, key);
}
- canon->fGradientPatternMap.set(std::move(key), pdfShader);
+ gradientPatternMap.set(std::move(key), pdfShader);
return pdfShader;
}
-sk_sp<SkPDFObject> SkPDFGradientShader::Make(SkPDFDocument* doc,
+SkPDFIndirectReference SkPDFGradientShader::Make(SkPDFDocument* doc,
SkShader* shader,
const SkMatrix& canvasTransform,
const SkIRect& bbox) {
diff --git a/src/pdf/SkPDFGradientShader.h b/src/pdf/SkPDFGradientShader.h
index 0cc059c..7050e09 100644
--- a/src/pdf/SkPDFGradientShader.h
+++ b/src/pdf/SkPDFGradientShader.h
@@ -17,10 +17,10 @@
namespace SkPDFGradientShader {
-sk_sp<SkPDFObject> Make(SkPDFDocument* doc,
- SkShader* shader,
- const SkMatrix& matrix,
- const SkIRect& surfaceBBox);
+SkPDFIndirectReference Make(SkPDFDocument* doc,
+ SkShader* shader,
+ const SkMatrix& matrix,
+ const SkIRect& surfaceBBox);
struct Key {
SkShader::GradientType fType;
@@ -37,7 +37,7 @@
uint32_t operator()(const Key& k) const { return k.fHash; }
};
-using HashMap = SkTHashMap<Key, sk_sp<SkPDFObject>, KeyHash>;
+using HashMap = SkTHashMap<Key, SkPDFIndirectReference, KeyHash>;
inline bool operator==(const SkShader::GradientInfo& u, const SkShader::GradientInfo& v) {
return u.fColorCount == v.fColorCount
diff --git a/src/pdf/SkPDFGraphicState.cpp b/src/pdf/SkPDFGraphicState.cpp
index b9a5eb3..7bf4a92 100644
--- a/src/pdf/SkPDFGraphicState.cpp
+++ b/src/pdf/SkPDFGraphicState.cpp
@@ -9,6 +9,7 @@
#include "SkData.h"
#include "SkPDFCanon.h"
+#include "SkPDFDocumentPriv.h"
#include "SkPDFFormXObject.h"
#include "SkPDFUtils.h"
#include "SkPaint.h"
@@ -52,21 +53,23 @@
return SkToU8((unsigned)mode);
}
-sk_sp<SkPDFDict> SkPDFGraphicState::GetGraphicStateForPaint(SkPDFCanon* canon,
- const SkPaint& p) {
+SkPDFIndirectReference SkPDFGraphicState::GetGraphicStateForPaint(SkPDFDocument* doc,
+ const SkPaint& p) {
+ SkPDFCanon* canon = doc->canon();
SkASSERT(canon);
if (SkPaint::kFill_Style == p.getStyle()) {
SkPDFFillGraphicState fillKey = {p.getColor4f().fA, pdf_blend_mode(p.getBlendMode())};
auto& fillMap = canon->fFillGSMap;
- if (sk_sp<SkPDFDict>* statePtr = fillMap.find(fillKey)) {
+ if (SkPDFIndirectReference* statePtr = fillMap.find(fillKey)) {
return *statePtr;
}
- auto state = sk_make_sp<SkPDFDict>();
- state->reserve(2);
- state->insertColorComponentF("ca", fillKey.fAlpha);
- state->insertName("BM", as_pdf_blend_mode_name((SkBlendMode)fillKey.fBlendMode));
- fillMap.set(fillKey, state);
- return state;
+ SkPDFDict state;
+ state.reserve(2);
+ state.insertColorComponentF("ca", fillKey.fAlpha);
+ state.insertName("BM", as_pdf_blend_mode_name((SkBlendMode)fillKey.fBlendMode));
+ SkPDFIndirectReference ref = doc->emit(state);
+ fillMap.set(fillKey, ref);
+ return ref;
} else {
SkPDFStrokeGraphicState strokeKey = {
p.getStrokeWidth(),
@@ -77,65 +80,63 @@
pdf_blend_mode(p.getBlendMode())
};
auto& sMap = canon->fStrokeGSMap;
- if (sk_sp<SkPDFDict>* statePtr = sMap.find(strokeKey)) {
+ if (SkPDFIndirectReference* statePtr = sMap.find(strokeKey)) {
return *statePtr;
}
- auto state = sk_make_sp<SkPDFDict>();
- state->reserve(8);
- state->insertColorComponentF("CA", strokeKey.fAlpha);
- state->insertColorComponentF("ca", strokeKey.fAlpha);
- state->insertInt("LC", to_stroke_cap(strokeKey.fStrokeCap));
- state->insertInt("LJ", to_stroke_join(strokeKey.fStrokeJoin));
- state->insertScalar("LW", strokeKey.fStrokeWidth);
- state->insertScalar("ML", strokeKey.fStrokeMiter);
- state->insertBool("SA", true); // SA = Auto stroke adjustment.
- state->insertName("BM", as_pdf_blend_mode_name((SkBlendMode)strokeKey.fBlendMode));
- sMap.set(strokeKey, state);
- return state;
+ SkPDFDict state;
+ state.reserve(8);
+ state.insertColorComponentF("CA", strokeKey.fAlpha);
+ state.insertColorComponentF("ca", strokeKey.fAlpha);
+ state.insertInt("LC", to_stroke_cap(strokeKey.fStrokeCap));
+ state.insertInt("LJ", to_stroke_join(strokeKey.fStrokeJoin));
+ state.insertScalar("LW", strokeKey.fStrokeWidth);
+ state.insertScalar("ML", strokeKey.fStrokeMiter);
+ state.insertBool("SA", true); // SA = Auto stroke adjustment.
+ state.insertName("BM", as_pdf_blend_mode_name((SkBlendMode)strokeKey.fBlendMode));
+ SkPDFIndirectReference ref = doc->emit(state);
+ sMap.set(strokeKey, ref);
+ return ref;
}
}
////////////////////////////////////////////////////////////////////////////////
-static sk_sp<SkPDFStream> make_invert_function() {
+static SkPDFIndirectReference make_invert_function(SkPDFDocument* doc) {
// Acrobat crashes if we use a type 0 function, kpdf crashes if we use
// a type 2 function, so we use a type 4 function.
- auto domainAndRange = SkPDFMakeArray(0, 1);
-
static const char psInvert[] = "{1 exch sub}";
// Do not copy the trailing '\0' into the SkData.
- auto invertFunction = sk_make_sp<SkPDFStream>(
- SkData::MakeWithoutCopy(psInvert, strlen(psInvert)));
- invertFunction->dict()->insertInt("FunctionType", 4);
- invertFunction->dict()->insertObject("Domain", domainAndRange);
- invertFunction->dict()->insertObject("Range", std::move(domainAndRange));
- return invertFunction;
+ auto invertFunction = SkData::MakeWithoutCopy(psInvert, strlen(psInvert));
+
+ sk_sp<SkPDFDict> dict = sk_make_sp<SkPDFDict>();
+ dict->insertInt("FunctionType", 4);
+ dict->insertObject("Domain", SkPDFMakeArray(0, 1));
+ dict->insertObject("Range", SkPDFMakeArray(0, 1));
+ return SkPDFStreamOut(std::move(dict), SkMemoryStream::Make(std::move(invertFunction)), doc);
}
-sk_sp<SkPDFDict> SkPDFGraphicState::GetSMaskGraphicState(
- sk_sp<SkPDFObject> sMask,
- bool invert,
- SkPDFSMaskMode sMaskMode,
- SkPDFCanon* canon) {
+SkPDFIndirectReference SkPDFGraphicState::GetSMaskGraphicState(SkPDFIndirectReference sMask,
+ bool invert,
+ SkPDFSMaskMode sMaskMode,
+ SkPDFDocument* doc) {
// The practical chances of using the same mask more than once are unlikely
// enough that it's not worth canonicalizing.
+ SkPDFCanon* canon = doc->canon();
auto sMaskDict = sk_make_sp<SkPDFDict>("Mask");
if (sMaskMode == kAlpha_SMaskMode) {
sMaskDict->insertName("S", "Alpha");
} else if (sMaskMode == kLuminosity_SMaskMode) {
sMaskDict->insertName("S", "Luminosity");
}
- sMaskDict->insertObjRef("G", std::move(sMask));
+ sMaskDict->insertRef("G", sMask);
if (invert) {
- // Instead of calling SkPDFGraphicState::MakeInvertFunction,
// let the canon deduplicate this object.
- sk_sp<SkPDFStream>& invertFunction = canon->fInvertFunction;
- if (!invertFunction) {
- invertFunction = make_invert_function();
+ if (canon->fInvertFunction == SkPDFIndirectReference()) {
+ canon->fInvertFunction = make_invert_function(doc);
}
- sMaskDict->insertObjRef("TR", invertFunction);
+ sMaskDict->insertRef("TR", canon->fInvertFunction);
}
- auto result = sk_make_sp<SkPDFDict>("ExtGState");
- result->insertObject("SMask", std::move(sMaskDict));
- return result;
+ SkPDFDict result("ExtGState");
+ result.insertObject("SMask", std::move(sMaskDict));
+ return doc->emit(result);
}
diff --git a/src/pdf/SkPDFGraphicState.h b/src/pdf/SkPDFGraphicState.h
index 86f4eab..9c22e17 100644
--- a/src/pdf/SkPDFGraphicState.h
+++ b/src/pdf/SkPDFGraphicState.h
@@ -29,7 +29,7 @@
/** Get the graphic state for the passed SkPaint.
*/
- sk_sp<SkPDFDict> GetGraphicStateForPaint(SkPDFCanon*, const SkPaint&);
+ SkPDFIndirectReference GetGraphicStateForPaint(SkPDFDocument*, const SkPaint&);
/** Make a graphic state that only sets the passed soft mask.
* @param sMask The form xobject to use as a soft mask.
@@ -38,12 +38,10 @@
*
* These are not de-duped.
*/
- sk_sp<SkPDFDict> GetSMaskGraphicState(sk_sp<SkPDFObject> sMask,
- bool invert,
- SkPDFSMaskMode sMaskMode,
- SkPDFCanon* canon);
-
- sk_sp<SkPDFStream> MakeInvertFunction();
+ SkPDFIndirectReference GetSMaskGraphicState(SkPDFIndirectReference sMask,
+ bool invert,
+ SkPDFSMaskMode sMaskMode,
+ SkPDFDocument* doc);
}
SK_BEGIN_REQUIRE_DENSE
diff --git a/src/pdf/SkPDFMakeToUnicodeCmap.cpp b/src/pdf/SkPDFMakeToUnicodeCmap.cpp
index a329547..73cdc1d 100644
--- a/src/pdf/SkPDFMakeToUnicodeCmap.cpp
+++ b/src/pdf/SkPDFMakeToUnicodeCmap.cpp
@@ -205,7 +205,7 @@
append_bfrange_section(bfrangeEntries, multiByteGlyphs, cmap);
}
-sk_sp<SkPDFStream> SkPDFMakeToUnicodeCmap(
+std::unique_ptr<SkStreamAsset> SkPDFMakeToUnicodeCmap(
const SkUnichar* glyphToUnicode,
const SkPDFGlyphUse* subset,
bool multiByteGlyphs,
@@ -216,6 +216,5 @@
SkPDFAppendCmapSections(glyphToUnicode, subset, &cmap, multiByteGlyphs,
firstGlyphID, lastGlyphID);
append_cmap_footer(&cmap);
- return sk_make_sp<SkPDFStream>(
- std::unique_ptr<SkStreamAsset>(cmap.detachAsStream()));
+ return cmap.detachAsStream();
}
diff --git a/src/pdf/SkPDFMakeToUnicodeCmap.h b/src/pdf/SkPDFMakeToUnicodeCmap.h
index f52862f..6614c01 100644
--- a/src/pdf/SkPDFMakeToUnicodeCmap.h
+++ b/src/pdf/SkPDFMakeToUnicodeCmap.h
@@ -10,7 +10,7 @@
#include "SkPDFFont.h"
#include "SkStream.h"
-sk_sp<SkPDFStream> SkPDFMakeToUnicodeCmap(
+std::unique_ptr<SkStreamAsset> SkPDFMakeToUnicodeCmap(
const SkUnichar* glyphToUnicode,
const SkPDFGlyphUse* subset,
bool multiByteGlyphs,
diff --git a/src/pdf/SkPDFMetadata.cpp b/src/pdf/SkPDFMetadata.cpp
index 06d7471..b18df79 100644
--- a/src/pdf/SkPDFMetadata.cpp
+++ b/src/pdf/SkPDFMetadata.cpp
@@ -150,8 +150,7 @@
return std::move(dict);
}
-SkPDFMetadata::UUID SkPDFMetadata::CreateUUID(
- const SkPDF::Metadata& metadata) {
+SkUUID SkPDFMetadata::CreateUUID(const SkPDF::Metadata& metadata) {
// The main requirement is for the UUID to be unique; the exact
// format of the data that will be hashed is not important.
SkMD5 md5;
@@ -177,22 +176,22 @@
// See RFC 4122, page 6-7.
digest.data[6] = (digest.data[6] & 0x0F) | 0x30;
digest.data[8] = (digest.data[6] & 0x3F) | 0x80;
- static_assert(sizeof(digest) == sizeof(UUID), "uuid_size");
- SkPDFMetadata::UUID uuid;
+ static_assert(sizeof(digest) == sizeof(SkUUID), "uuid_size");
+ SkUUID uuid;
memcpy(&uuid, &digest, sizeof(digest));
return uuid;
}
-sk_sp<SkPDFObject> SkPDFMetadata::MakePdfId(const UUID& doc,
- const UUID& instance) {
+sk_sp<SkPDFObject> SkPDFMetadata::MakePdfId(const SkUUID& doc,
+ const SkUUID& instance) {
// /ID [ <81b14aafa313db63dbd6f981e49f94f4>
// <81b14aafa313db63dbd6f981e49f94f4> ]
auto array = sk_make_sp<SkPDFArray>();
- static_assert(sizeof(SkPDFMetadata::UUID) == 16, "uuid_size");
+ static_assert(sizeof(SkUUID) == 16, "uuid_size");
array->appendString(
- SkString(reinterpret_cast<const char*>(&doc), sizeof(UUID)));
+ SkString(reinterpret_cast<const char*>(&doc), sizeof(SkUUID)));
array->appendString(
- SkString(reinterpret_cast<const char*>(&instance), sizeof(UUID)));
+ SkString(reinterpret_cast<const char*>(&instance), sizeof(SkUUID)));
return std::move(array);
}
@@ -208,7 +207,7 @@
}
}
-static SkString uuid_to_string(const SkPDFMetadata::UUID& uuid) {
+static SkString uuid_to_string(const SkUUID& uuid) {
// 8-4-4-4-12
char buffer[36]; // [32 + 4]
char* ptr = buffer;
@@ -306,8 +305,8 @@
sk_sp<SkPDFObject> SkPDFMetadata::MakeXMPObject(
const SkPDF::Metadata& metadata,
- const UUID& doc,
- const UUID& instance) {
+ const SkUUID& doc,
+ const SkUUID& instance) {
static const char templateString[] =
"<?xpacket begin=\"\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n"
"<x:xmpmeta xmlns:x=\"adobe:ns:meta/\"\n"
diff --git a/src/pdf/SkPDFMetadata.h b/src/pdf/SkPDFMetadata.h
index 314b57e..70b96e0 100644
--- a/src/pdf/SkPDFMetadata.h
+++ b/src/pdf/SkPDFMetadata.h
@@ -9,22 +9,19 @@
#define SkPDFMetadata_DEFINED
#include "SkPDFDocument.h"
+#include "SkUUID.h"
class SkPDFObject;
namespace SkPDFMetadata {
sk_sp<SkPDFObject> MakeDocumentInformationDict(const SkPDF::Metadata&);
-struct UUID {
- uint8_t fData[16];
-};
+SkUUID CreateUUID(const SkPDF::Metadata&);
-UUID CreateUUID(const SkPDF::Metadata&);
-
-sk_sp<SkPDFObject> MakePdfId(const UUID& doc, const UUID& instance);
+sk_sp<SkPDFObject> MakePdfId(const SkUUID& doc, const SkUUID& instance);
sk_sp<SkPDFObject> MakeXMPObject(const SkPDF::Metadata&,
- const UUID& doc,
- const UUID& instance);
+ const SkUUID& doc,
+ const SkUUID& instance);
}
#endif // SkPDFMetadata_DEFINED
diff --git a/src/pdf/SkPDFResourceDict.cpp b/src/pdf/SkPDFResourceDict.cpp
index fbd5492..545d801 100644
--- a/src/pdf/SkPDFResourceDict.cpp
+++ b/src/pdf/SkPDFResourceDict.cpp
@@ -59,18 +59,6 @@
return SkString(buffer, (size_t)(end - buffer));
}
-static void add_subdict(std::vector<sk_sp<SkPDFObject>> resourceList,
- SkPDFResourceType type,
- SkPDFDict* dst) {
- if (!resourceList.empty()) {
- auto resources = sk_make_sp<SkPDFDict>();
- for (size_t i = 0; i < resourceList.size(); i++) {
- resources->insertObjRef(resource(type, SkToInt(i)), std::move(resourceList[i]));
- }
- dst->insertObject(resource_name(type), std::move(resources));
- }
-}
-
static void add_subdict(const std::vector<SkPDFIndirectReference>& resourceList,
SkPDFResourceType type,
SkPDFDict* dst) {
@@ -83,14 +71,14 @@
}
}
-sk_sp<SkPDFDict> SkPDFMakeResourceDict(std::vector<sk_sp<SkPDFObject>> graphicStateResources,
- std::vector<sk_sp<SkPDFObject>> shaderResources,
- std::vector<sk_sp<SkPDFObject>> xObjectResources,
+sk_sp<SkPDFDict> SkPDFMakeResourceDict(std::vector<SkPDFIndirectReference> graphicStateResources,
+ std::vector<SkPDFIndirectReference> shaderResources,
+ std::vector<SkPDFIndirectReference> xObjectResources,
std::vector<SkPDFIndirectReference> fontResources) {
auto dict = sk_make_sp<SkPDFDict>();
- add_subdict(std::move(graphicStateResources), SkPDFResourceType::kExtGState, dict.get());
- add_subdict(std::move(shaderResources), SkPDFResourceType::kPattern, dict.get());
- add_subdict(std::move(xObjectResources), SkPDFResourceType::kXObject, dict.get());
- add_subdict(fontResources, SkPDFResourceType::kFont, dict.get());
+ add_subdict(graphicStateResources, SkPDFResourceType::kExtGState, dict.get());
+ add_subdict(shaderResources, SkPDFResourceType::kPattern, dict.get());
+ add_subdict(xObjectResources, SkPDFResourceType::kXObject, dict.get());
+ add_subdict(fontResources, SkPDFResourceType::kFont, dict.get());
return dict;
}
diff --git a/src/pdf/SkPDFResourceDict.h b/src/pdf/SkPDFResourceDict.h
index 40a17f9..a278b0c 100644
--- a/src/pdf/SkPDFResourceDict.h
+++ b/src/pdf/SkPDFResourceDict.h
@@ -32,9 +32,9 @@
*
* Any arguments can be nullptr.
*/
-sk_sp<SkPDFDict> SkPDFMakeResourceDict(std::vector<sk_sp<SkPDFObject>> graphicStateResources,
- std::vector<sk_sp<SkPDFObject>> shaderResources,
- std::vector<sk_sp<SkPDFObject>> xObjectResources,
+sk_sp<SkPDFDict> SkPDFMakeResourceDict(std::vector<SkPDFIndirectReference> graphicStateResources,
+ std::vector<SkPDFIndirectReference> shaderResources,
+ std::vector<SkPDFIndirectReference> xObjectResources,
std::vector<SkPDFIndirectReference> fontResources);
/**
diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp
index 7448d86..168805b 100644
--- a/src/pdf/SkPDFShader.cpp
+++ b/src/pdf/SkPDFShader.cpp
@@ -37,9 +37,9 @@
canvas->drawBitmap(bm, 0, 0, &paint);
}
-static sk_sp<SkPDFStream> make_image_shader(SkPDFDocument* doc,
- const SkPDFImageShaderKey& key,
- SkImage* image) {
+static SkPDFIndirectReference make_image_shader(SkPDFDocument* doc,
+ const SkPDFImageShaderKey& key,
+ SkImage* image) {
SkASSERT(image);
// The image shader pattern cell will be drawn into a separate device
@@ -52,7 +52,7 @@
finalMatrix.preConcat(key.fShaderTransform);
SkRect deviceBounds = SkRect::Make(key.fBBox);
if (!SkPDFUtils::InverseTransformBBox(finalMatrix, &deviceBounds)) {
- return nullptr;
+ return SkPDFIndirectReference();
}
SkRect bitmapBounds = SkRect::Make(image->bounds());
@@ -244,22 +244,23 @@
}
}
- auto imageShader = sk_make_sp<SkPDFStream>(patternDevice->content());
+ auto imageShader = patternDevice->content();
sk_sp<SkPDFDict> resourceDict = patternDevice->makeResourceDict();
- SkPDFUtils::PopulateTilingPatternDict(imageShader->dict(), patternBBox,
+ sk_sp<SkPDFDict> dict = sk_make_sp<SkPDFDict>();
+ SkPDFUtils::PopulateTilingPatternDict(dict.get(), patternBBox,
std::move(resourceDict), finalMatrix);
- return imageShader;
+ return SkPDFStreamOut(std::move(dict), std::move(imageShader), doc);
}
// Generic fallback for unsupported shaders:
// * allocate a surfaceBBox-sized bitmap
// * shade the whole area
// * use the result as a bitmap shader
-static sk_sp<SkPDFObject> make_fallback_shader(SkPDFDocument* doc,
- SkShader* shader,
- const SkMatrix& canvasTransform,
- const SkIRect& surfaceBBox,
- SkColor paintColor) {
+static SkPDFIndirectReference make_fallback_shader(SkPDFDocument* doc,
+ SkShader* shader,
+ const SkMatrix& canvasTransform,
+ const SkIRect& surfaceBBox,
+ SkColor paintColor) {
// TODO(vandebo) This drops SKComposeShader on the floor. We could
// handle compose shader by pulling things up to a layer, drawing with
// the first shader, applying the xfer mode and drawing again with the
@@ -280,7 +281,7 @@
// MakeImageShader's behavior).
SkRect shaderRect = SkRect::Make(surfaceBBox);
if (!SkPDFUtils::InverseTransformBBox(canvasTransform, &shaderRect)) {
- return nullptr;
+ return SkPDFIndirectReference();
}
// Clamp the bitmap size to about 1M pixels
static const SkScalar kMaxBitmapArea = 1024 * 1024;
@@ -324,18 +325,18 @@
return paintColor & SK_ColorBLACK;
}
-sk_sp<SkPDFObject> SkPDFMakeShader(SkPDFDocument* doc,
- SkShader* shader,
- const SkMatrix& canvasTransform,
- const SkIRect& surfaceBBox,
- SkColor paintColor) {
+SkPDFIndirectReference SkPDFMakeShader(SkPDFDocument* doc,
+ SkShader* shader,
+ const SkMatrix& canvasTransform,
+ const SkIRect& surfaceBBox,
+ SkColor paintColor) {
SkASSERT(shader);
SkASSERT(doc);
if (SkShader::kNone_GradientType != shader->asAGradient(nullptr)) {
return SkPDFGradientShader::Make(doc, shader, canvasTransform, surfaceBBox);
}
if (surfaceBBox.isEmpty()) {
- return nullptr;
+ return SkPDFIndirectReference();
}
SkBitmap image;
SkPDFImageShaderKey key = {
@@ -350,11 +351,11 @@
if (SkImage* skimg = shader->isAImage(&key.fShaderTransform, key.fImageTileModes)) {
key.fBitmapKey = SkBitmapKeyFromImage(skimg);
SkPDFCanon* canon = doc->canon();
- sk_sp<SkPDFObject>* shaderPtr = canon->fImageShaderMap.find(key);
+ SkPDFIndirectReference* shaderPtr = canon->fImageShaderMap.find(key);
if (shaderPtr) {
return *shaderPtr;
}
- sk_sp<SkPDFObject> pdfShader = make_image_shader(doc, key, skimg);
+ SkPDFIndirectReference pdfShader = make_image_shader(doc, key, skimg);
canon->fImageShaderMap.set(std::move(key), pdfShader);
return pdfShader;
}
diff --git a/src/pdf/SkPDFShader.h b/src/pdf/SkPDFShader.h
index b003e34..2a150c3 100644
--- a/src/pdf/SkPDFShader.h
+++ b/src/pdf/SkPDFShader.h
@@ -38,11 +38,11 @@
* @param paintColor Color+Alpha of the paint. Color is usually ignored,
* unless it is a alpha shader.
*/
-sk_sp<SkPDFObject> SkPDFMakeShader(SkPDFDocument* doc,
- SkShader* shader,
- const SkMatrix& ctm,
- const SkIRect& surfaceBBox,
- SkColor paintColor);
+SkPDFIndirectReference SkPDFMakeShader(SkPDFDocument* doc,
+ SkShader* shader,
+ const SkMatrix& ctm,
+ const SkIRect& surfaceBBox,
+ SkColor paintColor);
SK_BEGIN_REQUIRE_DENSE
struct SkPDFImageShaderKey {
diff --git a/src/pdf/SkPDFTypes.cpp b/src/pdf/SkPDFTypes.cpp
index 4d9a71f..f210fc9 100644
--- a/src/pdf/SkPDFTypes.cpp
+++ b/src/pdf/SkPDFTypes.cpp
@@ -9,7 +9,9 @@
#include "SkData.h"
#include "SkDeflate.h"
+#include "SkExecutor.h"
#include "SkMakeUnique.h"
+#include "SkPDFDocumentPriv.h"
#include "SkPDFUtils.h"
#include "SkStream.h"
#include "SkStreamPriv.h"
@@ -203,6 +205,7 @@
fObject->emitObject(stream);
return;
case Type::kRef:
+ SkASSERT(fIntValue >= 0);
stream->writeDecAsText(fIntValue);
stream->writeText(" 0 R"); // Generation number is always 0.
return;
@@ -496,143 +499,71 @@
////////////////////////////////////////////////////////////////////////////////
-SkPDFSharedStream::SkPDFSharedStream(std::unique_ptr<SkStreamAsset> data)
- : fAsset(std::move(data)) {
- SkASSERT(fAsset);
-}
-
-SkPDFSharedStream::~SkPDFSharedStream() { this->drop(); }
-
-void SkPDFSharedStream::drop() {
- fAsset = nullptr;
- fDict.drop();
-}
-
-#ifdef SK_PDF_LESS_COMPRESSION
-void SkPDFSharedStream::emitObject(SkWStream* stream) const {
- SkASSERT(fAsset);
- std::unique_ptr<SkStreamAsset> dup(fAsset->duplicate());
- SkASSERT(dup && dup->hasLength());
- size_t length = dup->getLength();
- stream->writeText("<<");
- fDict.emitAll(stream);
- stream->writeText("\n");
- SkPDFUnion::Name("Length").emitObject(stream);
- stream->writeText(" ");
- SkPDFUnion::Int(length).emitObject(stream);
- stream->writeText("\n>>stream\n");
- SkStreamCopy(stream, dup.get());
- stream->writeText("\nendstream");
-}
-#else
-void SkPDFSharedStream::emitObject(SkWStream* stream) const {
- SkASSERT(fAsset);
- SkDynamicMemoryWStream buffer;
- SkDeflateWStream deflateWStream(&buffer);
- // Since emitObject is const, this function doesn't change the dictionary.
- std::unique_ptr<SkStreamAsset> dup(fAsset->duplicate()); // Cheap copy
- SkASSERT(dup);
- SkStreamCopy(&deflateWStream, dup.get());
- deflateWStream.finalize();
- size_t length = buffer.bytesWritten();
- stream->writeText("<<");
- fDict.emitAll(stream);
- stream->writeText("\n");
- SkPDFUnion::Name("Length").emitObject(stream);
- stream->writeText(" ");
- SkPDFUnion::Int(length).emitObject(stream);
- stream->writeText("\n");
- SkPDFUnion::Name("Filter").emitObject(stream);
- stream->writeText(" ");
- SkPDFUnion::Name("FlateDecode").emitObject(stream);
- stream->writeText(">>");
- stream->writeText(" stream\n");
- buffer.writeToAndReset(stream);
- stream->writeText("\nendstream");
-}
-#endif
-
-void SkPDFSharedStream::addResources(
- SkPDFObjNumMap* catalog) const {
- SkASSERT(fAsset);
- fDict.addResources(catalog);
-}
-
-
-////////////////////////////////////////////////////////////////////////////////
-
-SkPDFStream::SkPDFStream(sk_sp<SkData> data) {
- this->setData(skstd::make_unique<SkMemoryStream>(std::move(data)));
-}
-
-SkPDFStream::SkPDFStream(std::unique_ptr<SkStreamAsset> stream) {
- this->setData(std::move(stream));
-}
-
-SkPDFStream::SkPDFStream() {}
-
-SkPDFStream::~SkPDFStream() {}
-
-void SkPDFStream::addResources(SkPDFObjNumMap* catalog) const {
- SkASSERT(fCompressedData);
- fDict.addResources(catalog);
-}
-
-void SkPDFStream::drop() {
- fCompressedData.reset(nullptr);
- fDict.drop();
-}
-
-void SkPDFStream::emitObject(SkWStream* stream) const {
- SkASSERT(fCompressedData);
- fDict.emitObject(stream);
- // duplicate (a cheap operation) preserves const on fCompressedData.
- std::unique_ptr<SkStreamAsset> dup(fCompressedData->duplicate());
- SkASSERT(dup);
- SkASSERT(dup->hasLength());
- stream->writeText(" stream\n");
- stream->writeStream(dup.get(), dup->getLength());
- stream->writeText("\nendstream");
-}
-
-void SkPDFStream::setData(std::unique_ptr<SkStreamAsset> stream) {
- SkASSERT(!fCompressedData); // Only call this function once.
- SkASSERT(stream);
+static void serialize_stream(const SkPDFDict* origDict,
+ SkStreamAsset* stream,
+ bool deflate,
+ SkPDFDocument* doc,
+ SkPDFIndirectReference ref) {
// Code assumes that the stream starts at the beginning.
+ SkASSERT(stream && stream->hasLength());
- #ifdef SK_PDF_LESS_COMPRESSION
- fCompressedData = std::move(stream);
- SkASSERT(fCompressedData && fCompressedData->hasLength());
- fDict.insertInt("Length", fCompressedData->getLength());
- #else
-
- SkASSERT(stream->hasLength());
- SkDynamicMemoryWStream compressedData;
- SkDeflateWStream deflateWStream(&compressedData);
- if (stream->getLength() > 0) {
- SkStreamCopy(&deflateWStream, stream.get());
+ std::unique_ptr<SkStreamAsset> tmp;
+ SkPDFDict dict;
+ static const size_t kMinimumSavings = strlen("/Filter_/FlateDecode_");
+ if (deflate && stream->getLength() > kMinimumSavings) {
+ SkDynamicMemoryWStream compressedData;
+ SkDeflateWStream deflateWStream(&compressedData);
+ SkStreamCopy(&deflateWStream, stream);
+ deflateWStream.finalize();
+ if (stream->getLength() > compressedData.bytesWritten() + kMinimumSavings) {
+ tmp = compressedData.detachAsStream();
+ stream = tmp.get();
+ dict.insertName("Filter", "FlateDecode");
+ } else {
+ SkAssertResult(stream->rewind());
+ }
}
- deflateWStream.finalize();
- size_t compressedLength = compressedData.bytesWritten();
- size_t originalLength = stream->getLength();
+ dict.insertInt("Length", stream->getLength());
- if (originalLength <= compressedLength + strlen("/Filter_/FlateDecode_")) {
- SkAssertResult(stream->rewind());
- fCompressedData = std::move(stream);
- fDict.insertInt("Length", originalLength);
- return;
+ SkWStream* dst = doc->beginObject(ref);
+ dst->writeText("<<");
+ if (origDict) {
+ origDict->emitAll(dst);
}
- fCompressedData = compressedData.detachAsStream();
- fDict.insertName("Filter", "FlateDecode");
- fDict.insertInt("Length", compressedLength);
- #endif
+ dict.emitAll(dst);
+ dst->writeText(">> stream\n");
+ dst->writeStream(stream, stream->getLength());
+ dst->writeText("\nendstream");
+ doc->endObject();
+}
+
+SkPDFIndirectReference SkPDFStreamOut(sk_sp<SkPDFDict> dict,
+ std::unique_ptr<SkStreamAsset> content,
+ SkPDFDocument* doc,
+ bool deflate) {
+ SkPDFIndirectReference ref = doc->reserveRef();
+ if (SkExecutor* executor = doc->executor()) {
+ SkPDFDict* dictPtr = dict.release();
+ SkStreamAsset* contentPtr = content.release();
+ // Pass ownership of both pointers into a std::function, which should
+ // only be executed once.
+ executor->add([dictPtr, contentPtr, deflate, doc, ref]() {
+ serialize_stream(dictPtr, contentPtr, deflate, doc, ref);
+ SkSafeUnref(dictPtr);
+ delete contentPtr;
+ });
+ return ref;
+ }
+ serialize_stream(dict.get(), content.get(), deflate, doc, ref);
+ return ref;
}
////////////////////////////////////////////////////////////////////////////////
void SkPDFObjNumMap::addObjectRecursively(SkPDFObject* obj) {
+ SkASSERT(fIndirectReferenceSource != nullptr);
if (obj && obj->fIndirectReference.fValue == -1) {
- obj->fIndirectReference.fValue = fNextObjectNumber++;
+ obj->fIndirectReference = fIndirectReferenceSource->reserve();
fObjects.emplace_back(sk_ref_sp(obj));
obj->addResources(this);
}
diff --git a/src/pdf/SkPDFTypes.h b/src/pdf/SkPDFTypes.h
index 935d1b7..3dd5bfc 100644
--- a/src/pdf/SkPDFTypes.h
+++ b/src/pdf/SkPDFTypes.h
@@ -27,6 +27,7 @@
class SkStreamAsset;
class SkString;
class SkWStream;
+struct SkPDFObjectSerializer;
#ifdef SK_PDF_IMAGE_STATS
#include <atomic>
@@ -34,6 +35,7 @@
struct SkPDFIndirectReference {
int fValue = -1;
+ explicit operator bool() { return fValue != -1; }
};
inline static bool operator==(SkPDFIndirectReference u, SkPDFIndirectReference v) {
@@ -44,6 +46,7 @@
return u.fValue != v.fValue;
}
+
/** \class SkPDFObject
A PDF Object is the base class for primitive elements in a PDF file. A
@@ -352,67 +355,16 @@
SkDEBUGCODE(bool fDumped;)
};
-/** \class SkPDFSharedStream
+#ifdef SK_PDF_LESS_COMPRESSION
+ static constexpr bool kSkPDFDefaultDoDeflate = false;
+#else
+ static constexpr bool kSkPDFDefaultDoDeflate = true;
+#endif
- This class takes an asset and assumes that it is backed by
- long-lived shared data (for example, an open file
- descriptor). That is: no memory savings can be made by holding on
- to a compressed version instead.
- */
-class SkPDFSharedStream final : public SkPDFObject {
-public:
- SkPDFSharedStream(std::unique_ptr<SkStreamAsset> data);
- ~SkPDFSharedStream() override;
- SkPDFDict* dict() { return &fDict; }
- void emitObject(SkWStream*) const override;
- void addResources(SkPDFObjNumMap*) const override;
- void drop() override;
-
-private:
- std::unique_ptr<SkStreamAsset> fAsset;
- SkPDFDict fDict;
- typedef SkPDFObject INHERITED;
-};
-
-/** \class SkPDFStream
-
- This class takes an asset and assumes that it is the only owner of
- the asset's data. It immediately compresses the asset to save
- memory.
- */
-
-class SkPDFStream final : public SkPDFObject {
-
-public:
- /** Create a PDF stream. A Length entry is automatically added to the
- * stream dictionary.
- * @param data The data part of the stream.
- * @param stream The data part of the stream. */
- explicit SkPDFStream(sk_sp<SkData> data);
- explicit SkPDFStream(std::unique_ptr<SkStreamAsset> stream);
- ~SkPDFStream() override;
-
- SkPDFDict* dict() { return &fDict; }
-
- // The SkPDFObject interface.
- void emitObject(SkWStream* stream) const override;
- void addResources(SkPDFObjNumMap*) const final;
- void drop() override;
-
-protected:
- /* Create a PDF stream with no data. The setData method must be called to
- * set the data. */
- SkPDFStream();
-
- /** Only call this function once. */
- void setData(std::unique_ptr<SkStreamAsset> stream);
-
-private:
- std::unique_ptr<SkStreamAsset> fCompressedData;
- SkPDFDict fDict;
-
- typedef SkPDFDict INHERITED;
-};
+SkPDFIndirectReference SkPDFStreamOut(sk_sp<SkPDFDict> dict,
+ std::unique_ptr<SkStreamAsset> stream,
+ SkPDFDocument* doc,
+ bool deflate = kSkPDFDefaultDoDeflate);
////////////////////////////////////////////////////////////////////////////////
@@ -423,6 +375,8 @@
*/
class SkPDFObjNumMap : SkNoncopyable {
public:
+ SkPDFObjNumMap(SkPDFObjectSerializer* s) : fIndirectReferenceSource(s) {}
+
/** Add the passed object to the catalog, as well as all its dependencies.
* @param obj The object to add. If nullptr, this is a noop.
*/
@@ -438,8 +392,8 @@
private:
friend struct SkPDFObjectSerializer;
+ SkPDFObjectSerializer* fIndirectReferenceSource;
std::vector<sk_sp<SkPDFObject>> fObjects;
- int fNextObjectNumber = 1;
};
////////////////////////////////////////////////////////////////////////////////
diff --git a/src/pdf/SkUUID.h b/src/pdf/SkUUID.h
new file mode 100644
index 0000000..3d81865
--- /dev/null
+++ b/src/pdf/SkUUID.h
@@ -0,0 +1,18 @@
+// Copyright 2018 Google LLC.
+// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+#ifndef SkUUID_DEFINED
+#define SkUUID_DEFINED
+
+#include <cstdint>
+#include <cstring>
+
+struct SkUUID {
+ uint8_t fData[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+};
+
+static inline bool operator==(const SkUUID& u, const SkUUID& v) {
+ return 0 == memcmp(u.fData, v.fData, sizeof(u.fData));
+}
+static inline bool operator!=(const SkUUID& u, const SkUUID& v) { return !(u == v); }
+
+#endif // SkUUID_DEFINED
diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp
index cbb706d..efb2ab2 100644
--- a/tests/PDFPrimitivesTest.cpp
+++ b/tests/PDFPrimitivesTest.cpp
@@ -23,6 +23,7 @@
#include "SkPDFCanon.h"
#include "SkPDFDevice.h"
#include "SkPDFDocument.h"
+#include "SkPDFDocumentPriv.h"
#include "SkPDFFont.h"
#include "SkPDFTypes.h"
#include "SkPDFUtils.h"
@@ -77,51 +78,11 @@
assert_eq(reporter, result, string);
}
-static void TestPDFStream(skiatest::Reporter* reporter) {
- char streamBytes[] = "Test\nFoo\tBar";
- auto streamData = skstd::make_unique<SkMemoryStream>(
- streamBytes, strlen(streamBytes), true);
- auto stream = sk_make_sp<SkPDFStream>(std::move(streamData));
- assert_emit_eq(reporter,
- *stream,
- "<</Length 12>> stream\nTest\nFoo\tBar\nendstream");
- stream->dict()->insertInt("Attribute", 42);
- assert_emit_eq(reporter,
- *stream,
- "<</Length 12\n/Attribute 42>> stream\n"
- "Test\nFoo\tBar\nendstream");
-
- {
- char streamBytes2[] = "This is a longer string, so that compression "
- "can do something with it. With shorter strings, "
- "the short circuit logic cuts in and we end up "
- "with an uncompressed string.";
- auto stream = sk_make_sp<SkPDFStream>(
- SkData::MakeWithCopy(streamBytes2, strlen(streamBytes2)));
-
- SkDynamicMemoryWStream compressedByteStream;
- SkDeflateWStream deflateWStream(&compressedByteStream);
- deflateWStream.write(streamBytes2, strlen(streamBytes2));
- deflateWStream.finalize();
-
- SkDynamicMemoryWStream expected;
- expected.writeText("<</Filter /FlateDecode\n/Length 116>> stream\n");
- compressedByteStream.writeToStream(&expected);
- compressedByteStream.reset();
- expected.writeText("\nendstream");
- sk_sp<SkData> expectedResultData2(expected.detachAsData());
- SkString result = emit_to_string(*stream);
- #ifndef SK_PDF_LESS_COMPRESSION
- assert_eql(reporter,
- result,
- (const char*)expectedResultData2->data(),
- expectedResultData2->size());
- #endif
- }
-}
-
static void TestObjectNumberMap(skiatest::Reporter* reporter) {
- SkPDFObjNumMap objNumMap;
+ SkPDFObjectSerializer pdfObjectSerializer;
+ SkNullWStream nullstream;
+ pdfObjectSerializer.serializeHeader(&nullstream);
+ SkPDFObjNumMap objNumMap(&pdfObjectSerializer);
sk_sp<SkPDFArray> a1(new SkPDFArray);
sk_sp<SkPDFArray> a2(new SkPDFArray);
sk_sp<SkPDFArray> a3(new SkPDFArray);
@@ -145,7 +106,10 @@
sk_sp<SkPDFArray> a2(new SkPDFArray);
a2->appendObjRef(a1);
- SkPDFObjNumMap catalog;
+ SkPDFObjectSerializer pdfObjectSerializer;
+ SkNullWStream nullstream;
+ pdfObjectSerializer.serializeHeader(&nullstream);
+ SkPDFObjNumMap catalog(&pdfObjectSerializer);
catalog.addObjectRecursively(a1.get());
REPORTER_ASSERT(reporter, catalog.getObjectNumber(a1.get()) == 1);
@@ -264,7 +228,10 @@
"(Another String) [-1]]");
sk_sp<SkPDFArray> referencedArray(new SkPDFArray);
- SkPDFObjNumMap catalog;
+ SkPDFObjectSerializer pdfObjectSerializer;
+ SkNullWStream nullstream;
+ pdfObjectSerializer.serializeHeader(&nullstream);
+ SkPDFObjNumMap catalog(&pdfObjectSerializer);
catalog.addObjectRecursively(referencedArray.get());
REPORTER_ASSERT(reporter, catalog.getObjectNumber(
referencedArray.get()) == 1);
@@ -328,8 +295,12 @@
assert_emit_eq(reporter, *dict, "<</Type /DType>>");
sk_sp<SkPDFArray> referencedArray(new SkPDFArray);
- SkPDFObjNumMap catalog;
+ SkPDFObjectSerializer pdfObjectSerializer;
+ SkNullWStream nullstream;
+ pdfObjectSerializer.serializeHeader(&nullstream);
+ SkPDFObjNumMap catalog(&pdfObjectSerializer);
catalog.addObjectRecursively(referencedArray.get());
+
REPORTER_ASSERT(reporter, catalog.getObjectNumber(
referencedArray.get()) == 1);
dict->insertObjRef("n1", std::move(referencedArray));
@@ -341,7 +312,6 @@
TestPDFUnion(reporter);
TestPDFArray(reporter);
TestPDFDict(reporter);
- TestPDFStream(reporter);
TestObjectNumberMap(reporter);
TestObjectRef(reporter);
test_issue1083();