aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Hal Canary <halcanary@google.com>2017-06-28 16:04:20 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-06-30 16:47:14 +0000
commit80fa7cea93974b0480f35f7a5260ce78ba50420f (patch)
treeefd0f86808985a700b386d94482724538f4f9fba
parenta062258e76e28ef0ec88ef827ae84a90730393cc (diff)
SkPDF: simplify SkPDFGraphicState
- Separate graphic state objects for Stroke and Fill. - SkPDFGraphicState::GetGraphicStateForPaint simplified. - No more SkPDFGraphicState objects.Simplify SkPDFCanon. All PDFs render the same. Most PDFs are slightly smaller, especially those from captured web pages. Change-Id: Id9605c1d7495645da558d5f378ba585cdc201bba Reviewed-on: https://skia-review.googlesource.com/21343 Reviewed-by: Ben Wagner <bungeman@google.com> Commit-Queue: Hal Canary <halcanary@google.com>
-rw-r--r--src/pdf/SkPDFCanon.cpp20
-rw-r--r--src/pdf/SkPDFCanon.h36
-rw-r--r--src/pdf/SkPDFDevice.cpp6
-rw-r--r--src/pdf/SkPDFGraphicState.cpp187
-rw-r--r--src/pdf/SkPDFGraphicState.h58
-rw-r--r--src/pdf/SkPDFTypes.cpp4
-rw-r--r--src/pdf/SkPDFTypes.h3
7 files changed, 120 insertions, 194 deletions
diff --git a/src/pdf/SkPDFCanon.cpp b/src/pdf/SkPDFCanon.cpp
index 5e8a2cb0c2..3ecd474069 100644
--- a/src/pdf/SkPDFCanon.cpp
+++ b/src/pdf/SkPDFCanon.cpp
@@ -12,9 +12,7 @@
////////////////////////////////////////////////////////////////////////////////
-SkPDFCanon::~SkPDFCanon() {
- fGraphicStateRecords.foreach ([](WrapGS w) { w.fPtr->unref(); });
-}
+SkPDFCanon::~SkPDFCanon() {}
////////////////////////////////////////////////////////////////////////////////
@@ -56,19 +54,3 @@ void SkPDFCanon::addImageShader(sk_sp<SkPDFObject> pdfShader,
SkPDFShader::State state) {
fImageShaderRecords.emplace_back(ShaderRec{std::move(state), std::move(pdfShader)});
}
-
-////////////////////////////////////////////////////////////////////////////////
-
-const SkPDFGraphicState* SkPDFCanon::findGraphicState(
- const SkPDFGraphicState& key) const {
- const WrapGS* ptr = fGraphicStateRecords.find(WrapGS(&key));
- return ptr ? ptr->fPtr : nullptr;
-}
-
-void SkPDFCanon::addGraphicState(const SkPDFGraphicState* state) {
- SkASSERT(state);
- WrapGS w(SkRef(state));
- SkASSERT(!fGraphicStateRecords.contains(w));
- fGraphicStateRecords.add(w);
-}
-
diff --git a/src/pdf/SkPDFCanon.h b/src/pdf/SkPDFCanon.h
index 0aac2b5789..d876443c17 100644
--- a/src/pdf/SkPDFCanon.h
+++ b/src/pdf/SkPDFCanon.h
@@ -20,19 +20,6 @@ struct SkAdvancedTypefaceMetrics;
/**
* The SkPDFCanon canonicalizes objects across PDF pages
* (SkPDFDevices) and across draw calls.
- *
- * The PDF backend works correctly if:
- * - There is no more than one SkPDFCanon for each thread.
- * - Every SkPDFDevice is given a pointer to a SkPDFCanon on creation.
- * - All SkPDFDevices in a document share the same SkPDFCanon.
- * The SkPDFDocument class makes this happen by owning a single
- * SkPDFCanon.
- *
- * The addFoo() methods will ref the Foo; the canon's destructor will
- * call foo->unref() on all of these objects.
- *
- * The findFoo() methods do not change the ref count of the Foo
- * objects.
*/
class SkPDFCanon : SkNoncopyable {
public:
@@ -47,15 +34,15 @@ public:
sk_sp<SkPDFObject> findImageShader(const SkPDFShader::State&) const;
void addImageShader(sk_sp<SkPDFObject>, SkPDFShader::State);
- const SkPDFGraphicState* findGraphicState(const SkPDFGraphicState&) const;
- void addGraphicState(const SkPDFGraphicState*);
-
SkTHashMap<SkBitmapKey, sk_sp<SkPDFObject>> fPDFBitmapMap;
SkTHashMap<uint32_t, std::unique_ptr<SkAdvancedTypefaceMetrics>> fTypefaceMetrics;
SkTHashMap<uint32_t, sk_sp<SkPDFDict>> fFontDescriptors;
SkTHashMap<uint64_t, sk_sp<SkPDFFont>> fFontMap;
+ SkTHashMap<SkPDFStrokeGraphicState, sk_sp<SkPDFDict>> fStrokeGSMap;
+ SkTHashMap<SkPDFFillGraphicState, sk_sp<SkPDFDict>> fFillGSMap;
+
sk_sp<SkPixelSerializer> fPixelSerializer;
sk_sp<SkPDFStream> fInvertFunction;
sk_sp<SkPDFDict> fNoSmaskGraphicState;
@@ -69,22 +56,5 @@ private:
SkTArray<ShaderRec> fFunctionShaderRecords;
SkTArray<ShaderRec> fAlphaShaderRecords;
SkTArray<ShaderRec> fImageShaderRecords;
-
- struct WrapGS {
- explicit WrapGS(const SkPDFGraphicState* ptr = nullptr) : fPtr(ptr) {}
- const SkPDFGraphicState* fPtr;
- bool operator==(const WrapGS& rhs) const {
- SkASSERT(fPtr);
- SkASSERT(rhs.fPtr);
- return *fPtr == *rhs.fPtr;
- }
- struct Hash {
- uint32_t operator()(const WrapGS& w) const {
- SkASSERT(w.fPtr);
- return w.fPtr->hash();
- }
- };
- };
- SkTHashSet<WrapGS, WrapGS::Hash> fGraphicStateRecords;
};
#endif // SkPDFCanon_DEFINED
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index 5fc914d3ab..6888f54ca5 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -653,7 +653,9 @@ void SkPDFDevice::drawPoints(SkCanvas::PointMode mode,
SkPaint passedPaint = srcPaint;
remove_color_filter(&passedPaint);
replace_srcmode_on_opaque_paint(&passedPaint);
-
+ if (SkCanvas::kPoints_PointMode != mode) {
+ passedPaint.setStyle(SkPaint::kStroke_Style);
+ }
if (count == 0) {
return;
}
@@ -2188,7 +2190,7 @@ void SkPDFDevice::populateGraphicStateEntryFromPaint(
}
}
- sk_sp<SkPDFGraphicState> newGraphicState;
+ sk_sp<SkPDFDict> newGraphicState;
if (color == paint.getColor()) {
newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument->canon(), paint);
} else {
diff --git a/src/pdf/SkPDFGraphicState.cpp b/src/pdf/SkPDFGraphicState.cpp
index 6b7c502ccc..cab071c747 100644
--- a/src/pdf/SkPDFGraphicState.cpp
+++ b/src/pdf/SkPDFGraphicState.cpp
@@ -12,66 +12,53 @@
#include "SkPDFGraphicState.h"
#include "SkPDFUtils.h"
-static const char* as_blend_mode(SkBlendMode mode) {
+static const char* as_pdf_blend_mode_name(SkBlendMode mode) {
+ // PDF32000.book section 11.3.5 "Blend Mode"
switch (mode) {
- case SkBlendMode::kSrcOver:
- return "Normal";
- case SkBlendMode::kMultiply:
- return "Multiply";
- case SkBlendMode::kScreen:
- return "Screen";
- case SkBlendMode::kOverlay:
- return "Overlay";
- case SkBlendMode::kDarken:
- return "Darken";
- case SkBlendMode::kLighten:
- return "Lighten";
- case SkBlendMode::kColorDodge:
- return "ColorDodge";
- case SkBlendMode::kColorBurn:
- return "ColorBurn";
- case SkBlendMode::kHardLight:
- return "HardLight";
- case SkBlendMode::kSoftLight:
- return "SoftLight";
- case SkBlendMode::kDifference:
- return "Difference";
- case SkBlendMode::kExclusion:
- return "Exclusion";
- case SkBlendMode::kHue:
- return "Hue";
- case SkBlendMode::kSaturation:
- return "Saturation";
- case SkBlendMode::kColor:
- return "Color";
- case SkBlendMode::kLuminosity:
- return "Luminosity";
+ case SkBlendMode::kScreen: return "Screen";
+ case SkBlendMode::kOverlay: return "Overlay";
+ case SkBlendMode::kDarken: return "Darken";
+ case SkBlendMode::kLighten: return "Lighten";
+ case SkBlendMode::kColorDodge: return "ColorDodge";
+ case SkBlendMode::kColorBurn: return "ColorBurn";
+ case SkBlendMode::kHardLight: return "HardLight";
+ case SkBlendMode::kSoftLight: return "SoftLight";
+ case SkBlendMode::kDifference: return "Difference";
+ case SkBlendMode::kExclusion: return "Exclusion";
+ case SkBlendMode::kMultiply: return "Multiply";
+ case SkBlendMode::kHue: return "Hue";
+ case SkBlendMode::kSaturation: return "Saturation";
+ case SkBlendMode::kColor: return "Color";
+ case SkBlendMode::kLuminosity: return "Luminosity";
+ // Other blendmodes are either unsupported or handled in
+ // SkPDFDevice::setUpContentEntry.
+ default: return "Normal";
+ }
+}
- // These are handled in SkPDFDevice::setUpContentEntry.
- case SkBlendMode::kClear:
- case SkBlendMode::kSrc:
- case SkBlendMode::kDst:
- case SkBlendMode::kDstOver:
- case SkBlendMode::kSrcIn:
- case SkBlendMode::kDstIn:
- case SkBlendMode::kSrcOut:
- case SkBlendMode::kDstOut:
- case SkBlendMode::kSrcATop:
- case SkBlendMode::kDstATop:
- case SkBlendMode::kModulate:
- return "Normal";
+static int to_stroke_cap(uint8_t cap) {
+ // PDF32000.book section 8.4.3.3 "Line Cap Style"
+ switch ((SkPaint::Cap)cap) {
+ case SkPaint::kButt_Cap: return 0;
+ case SkPaint::kRound_Cap: return 1;
+ case SkPaint::kSquare_Cap: return 2;
+ default: SkASSERT(false); return 0;
+ }
+}
- // TODO(vandebo): Figure out if we can support more of these modes.
- case SkBlendMode::kXor:
- case SkBlendMode::kPlus:
- return nullptr;
+static int to_stroke_join(uint8_t join) {
+ // PDF32000.book section 8.4.3.4 "Line Join Style"
+ switch ((SkPaint::Join)join) {
+ case SkPaint::kMiter_Join: return 0;
+ case SkPaint::kRound_Join: return 1;
+ case SkPaint::kBevel_Join: return 2;
+ default: SkASSERT(false); return 0;
}
- return nullptr;
}
// If a SkXfermode is unsupported in PDF, this function returns
// SrcOver, otherwise, it returns that Xfermode as a Mode.
-static SkBlendMode mode_for_pdf(SkBlendMode mode) {
+static uint8_t pdf_blend_mode(SkBlendMode mode) {
switch (mode) {
case SkBlendMode::kSrcOver:
case SkBlendMode::kMultiply:
@@ -89,37 +76,53 @@ static SkBlendMode mode_for_pdf(SkBlendMode mode) {
case SkBlendMode::kSaturation:
case SkBlendMode::kColor:
case SkBlendMode::kLuminosity:
- // Mode is suppported and handled by pdf graphics state.
- return mode;
+ return SkToU8((unsigned)mode);
default:
- return SkBlendMode::kSrcOver; // Default mode.
+ return SkToU8((unsigned)SkBlendMode::kSrcOver);
}
}
-SkPDFGraphicState::SkPDFGraphicState(const SkPaint& p)
- : fStrokeWidth(p.getStrokeWidth())
- , fStrokeMiter(p.getStrokeMiter())
- , fAlpha(p.getAlpha())
- , fStrokeCap(SkToU8(p.getStrokeCap()))
- , fStrokeJoin(SkToU8(p.getStrokeJoin()))
- , fMode(SkToU8((unsigned)mode_for_pdf(p.getBlendMode()))) {}
-
-sk_sp<SkPDFGraphicState> SkPDFGraphicState::GetGraphicStateForPaint(SkPDFCanon* canon,
- const SkPaint& paint) {
+sk_sp<SkPDFDict> SkPDFGraphicState::GetGraphicStateForPaint(SkPDFCanon* canon,
+ const SkPaint& p) {
SkASSERT(canon);
- SkPDFGraphicState key(paint);
- if (const SkPDFGraphicState* canonGS = canon->findGraphicState(key)) {
- // The returned SkPDFGraphicState must be made non-const,
- // since the emitObject() interface is non-const. But We
- // promise that there is no way to mutate this object from
- // here on out.
- return sk_sp<SkPDFGraphicState>(SkRef(const_cast<SkPDFGraphicState*>(canonGS)));
+ if (SkPaint::kFill_Style == p.getStyle()) {
+ SkPDFFillGraphicState fillKey = {p.getAlpha(), pdf_blend_mode(p.getBlendMode())};
+ auto& fillMap = canon->fFillGSMap;
+ if (sk_sp<SkPDFDict>* statePtr = fillMap.find(fillKey)) {
+ return *statePtr;
+ }
+ auto state = sk_make_sp<SkPDFDict>();
+ state->reserve(2);
+ state->insertScalar("ca", fillKey.fAlpha / 255.0f);
+ state->insertName("BM", as_pdf_blend_mode_name((SkBlendMode)fillKey.fBlendMode));
+ fillMap.set(fillKey, state);
+ return state;
+ } else {
+ SkPDFStrokeGraphicState strokeKey = {
+ p.getStrokeWidth(), p.getStrokeMiter(),
+ SkToU8(p.getStrokeCap()), SkToU8(p.getStrokeJoin()),
+ p.getAlpha(), pdf_blend_mode(p.getBlendMode())};
+ auto& sMap = canon->fStrokeGSMap;
+ if (sk_sp<SkPDFDict>* statePtr = sMap.find(strokeKey)) {
+ return *statePtr;
+ }
+ auto state = sk_make_sp<SkPDFDict>();
+ state->reserve(8);
+ state->insertScalar("CA", strokeKey.fAlpha / 255.0f);
+ state->insertScalar("ca", strokeKey.fAlpha / 255.0f);
+ 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;
}
- sk_sp<SkPDFGraphicState> pdfGraphicState(new SkPDFGraphicState(paint));
- canon->addGraphicState(pdfGraphicState.get());
- return pdfGraphicState;
}
+////////////////////////////////////////////////////////////////////////////////
+
static sk_sp<SkPDFStream> make_invert_function() {
// 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.
@@ -161,41 +164,7 @@ sk_sp<SkPDFDict> SkPDFGraphicState::GetSMaskGraphicState(
}
sMaskDict->insertObjRef("TR", invertFunction);
}
-
auto result = sk_make_sp<SkPDFDict>("ExtGState");
result->insertObject("SMask", std::move(sMaskDict));
return result;
}
-
-void SkPDFGraphicState::emitObject(
- SkWStream* stream,
- const SkPDFObjNumMap& objNumMap) const {
- auto dict = sk_make_sp<SkPDFDict>("ExtGState");
-
- SkScalar alpha = SkIntToScalar(fAlpha) / 0xFF;
- dict->insertScalar("CA", alpha);
- dict->insertScalar("ca", alpha);
-
- SkPaint::Cap strokeCap = (SkPaint::Cap)fStrokeCap;
- SkPaint::Join strokeJoin = (SkPaint::Join)fStrokeJoin;
-
- static_assert(SkPaint::kButt_Cap == 0, "paint_cap_mismatch");
- static_assert(SkPaint::kRound_Cap == 1, "paint_cap_mismatch");
- static_assert(SkPaint::kSquare_Cap == 2, "paint_cap_mismatch");
- static_assert(SkPaint::kCapCount == 3, "paint_cap_mismatch");
- SkASSERT(strokeCap >= 0 && strokeCap <= 2);
- dict->insertInt("LC", strokeCap);
-
- static_assert(SkPaint::kMiter_Join == 0, "paint_join_mismatch");
- static_assert(SkPaint::kRound_Join == 1, "paint_join_mismatch");
- static_assert(SkPaint::kBevel_Join == 2, "paint_join_mismatch");
- static_assert(SkPaint::kJoinCount == 3, "paint_join_mismatch");
- SkASSERT(strokeJoin >= 0 && strokeJoin <= 2);
- dict->insertInt("LJ", strokeJoin);
-
- dict->insertScalar("LW", fStrokeWidth);
- dict->insertScalar("ML", fStrokeMiter);
- dict->insertBool("SA", true); // SA = Auto stroke adjustment.
- dict->insertName("BM", as_blend_mode((SkBlendMode)fMode));
- dict->emitObject(stream, objNumMap);
-}
diff --git a/src/pdf/SkPDFGraphicState.h b/src/pdf/SkPDFGraphicState.h
index 50b22686ac..4323252bcd 100644
--- a/src/pdf/SkPDFGraphicState.h
+++ b/src/pdf/SkPDFGraphicState.h
@@ -20,24 +20,15 @@ class SkPDFCanon;
be installed. So that a given dictionary is only output to the pdf file
once, we want to canonicalize them.
*/
-class SkPDFGraphicState final : public SkPDFObject {
-
-public:
+namespace SkPDFGraphicState {
enum SkPDFSMaskMode {
kAlpha_SMaskMode,
kLuminosity_SMaskMode
};
- // Override emitObject so that we can populate the dictionary on
- // demand.
- void emitObject(SkWStream* stream,
- const SkPDFObjNumMap& objNumMap) const override;
-
/** Get the graphic state for the passed SkPaint.
- * @param paint The SkPaint to emulate.
*/
- static sk_sp<SkPDFGraphicState> GetGraphicStateForPaint(SkPDFCanon* canon,
- const SkPaint& paint);
+ sk_sp<SkPDFDict> GetGraphicStateForPaint(SkPDFCanon*, const SkPaint&);
/** Make a graphic state that only sets the passed soft mask.
* @param sMask The form xobject to use as a soft mask.
@@ -46,29 +37,34 @@ public:
*
* These are not de-duped.
*/
- static sk_sp<SkPDFDict> GetSMaskGraphicState(sk_sp<SkPDFObject> sMask,
- bool invert,
- SkPDFSMaskMode sMaskMode,
- SkPDFCanon* canon);
-
- static sk_sp<SkPDFStream> MakeInvertFunction();
+ sk_sp<SkPDFDict> GetSMaskGraphicState(sk_sp<SkPDFObject> sMask,
+ bool invert,
+ SkPDFSMaskMode sMaskMode,
+ SkPDFCanon* canon);
- bool operator==(const SkPDFGraphicState& rhs) const {
- return 0 == memcmp(&fStrokeWidth, &rhs.fStrokeWidth, 12);
- }
- uint32_t hash() const { return SkOpts::hash(&fStrokeWidth, 12); }
+ sk_sp<SkPDFStream> MakeInvertFunction();
+}
-private:
- const SkScalar fStrokeWidth;
- const SkScalar fStrokeMiter;
- const uint8_t fAlpha;
- const uint8_t fStrokeCap; // SkPaint::Cap
- const uint8_t fStrokeJoin; // SkPaint::Join
- const uint8_t fMode; // SkBlendMode
-
- SkPDFGraphicState(const SkPaint&);
+SK_BEGIN_REQUIRE_DENSE
+struct SkPDFStrokeGraphicState {
+ SkScalar fStrokeWidth;
+ SkScalar fStrokeMiter;
+ uint8_t fStrokeCap; // SkPaint::Cap
+ uint8_t fStrokeJoin; // SkPaint::Join
+ uint8_t fAlpha;
+ uint8_t fBlendMode;
+ bool operator==(const SkPDFStrokeGraphicState& o) const { return !memcmp(this, &o, sizeof(o)); }
+ bool operator!=(const SkPDFStrokeGraphicState& o) const { return !(*this == o); }
+};
+SK_END_REQUIRE_DENSE
- typedef SkPDFDict INHERITED;
+SK_BEGIN_REQUIRE_DENSE
+struct SkPDFFillGraphicState {
+ uint8_t fAlpha;
+ uint8_t fBlendMode;
+ bool operator==(const SkPDFFillGraphicState& o) const { return !memcmp(this, &o, sizeof(o)); }
+ bool operator!=(const SkPDFFillGraphicState& o) const { return !(*this == o); }
};
+SK_END_REQUIRE_DENSE
#endif
diff --git a/src/pdf/SkPDFTypes.cpp b/src/pdf/SkPDFTypes.cpp
index 614a6eb2f2..c256ff925d 100644
--- a/src/pdf/SkPDFTypes.cpp
+++ b/src/pdf/SkPDFTypes.cpp
@@ -384,6 +384,10 @@ void SkPDFDict::addResources(SkPDFObjNumMap* catalog) const {
int SkPDFDict::size() const { return fRecords.count(); }
+void SkPDFDict::reserve(int n) {
+ fRecords.reserve(n);
+}
+
void SkPDFDict::insertObjRef(const char key[], sk_sp<SkPDFObject> objSp) {
fRecords.emplace_back(Record{SkPDFUnion::Name(key), SkPDFUnion::ObjRef(std::move(objSp))});
}
diff --git a/src/pdf/SkPDFTypes.h b/src/pdf/SkPDFTypes.h
index 06e4858fbe..af5cdaf4f7 100644
--- a/src/pdf/SkPDFTypes.h
+++ b/src/pdf/SkPDFTypes.h
@@ -247,6 +247,9 @@ public:
*/
int size() const;
+ /** Preallocate space for n key-value pairs */
+ void reserve(int n);
+
/** Add the value to the dictionary with the given key.
* @param key The text of the key for this dictionary entry.
* @param value The value for this dictionary entry.