SkPDF: remove all globally references SkPDFObjects
Move these singletons into SkPDFCanon (there is still a single object per document; output PDF size will not change). Motivation: After this change, all indirectly-referenced SkPDFObjects are serialized exactly once. The next CL will add a memory saving feature: a method to purge an object's resources immediately after serialization. After that, further changes wil allow some objects to be serialized *before* SkDocument::close(), leading to potentially very large memory savings. BUG=skia:5087 GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1788263002 Review URL: https://codereview.chromium.org/1788263002
This commit is contained in:
parent
1a9b964084
commit
1437c1eea8
@ -149,3 +149,25 @@ const SkImage* SkPDFCanon::bitmapToImage(const SkBitmap& bm) {
|
||||
bm.copyTo(&n32bitmap, kN32_SkColorType);
|
||||
return *fBitmapToImageMap.set(key, SkImage::NewFromBitmap(n32bitmap));
|
||||
}
|
||||
|
||||
sk_sp<SkPDFStream> SkPDFCanon::makeInvertFunction() {
|
||||
if (fInvertFunction) {
|
||||
return fInvertFunction;
|
||||
}
|
||||
fInvertFunction = SkPDFGraphicState::MakeInvertFunction();
|
||||
return fInvertFunction;
|
||||
}
|
||||
sk_sp<SkPDFDict> SkPDFCanon::makeNoSmaskGraphicState() {
|
||||
if (fNoSmaskGraphicState) {
|
||||
return fNoSmaskGraphicState;
|
||||
}
|
||||
fNoSmaskGraphicState = SkPDFGraphicState::MakeNoSmaskGraphicState();
|
||||
return fNoSmaskGraphicState;
|
||||
}
|
||||
sk_sp<SkPDFArray> SkPDFCanon::makeRangeObject() {
|
||||
if (fRangeObject) {
|
||||
return fRangeObject;
|
||||
}
|
||||
fRangeObject = SkPDFShader::MakeRangeObject();
|
||||
return fRangeObject;
|
||||
}
|
||||
|
@ -84,6 +84,10 @@ public:
|
||||
SkPixelSerializer* getPixelSerializer() const { return fPixelSerializer.get(); }
|
||||
void setPixelSerializer(SkPixelSerializer* ps) { fPixelSerializer.reset(ps); }
|
||||
|
||||
sk_sp<SkPDFStream> makeInvertFunction();
|
||||
sk_sp<SkPDFDict> makeNoSmaskGraphicState();
|
||||
sk_sp<SkPDFArray> makeRangeObject();
|
||||
|
||||
private:
|
||||
struct FontRec {
|
||||
SkPDFFont* fFont;
|
||||
@ -119,5 +123,8 @@ private:
|
||||
SkTHashMap<uint32_t /*ImageUniqueID*/, SkPDFObject*> fPDFBitmapMap;
|
||||
|
||||
sk_sp<SkPixelSerializer> fPixelSerializer;
|
||||
sk_sp<SkPDFStream> fInvertFunction;
|
||||
sk_sp<SkPDFDict> fNoSmaskGraphicState;
|
||||
sk_sp<SkPDFArray> fRangeObject;
|
||||
};
|
||||
#endif // SkPDFCanon_DEFINED
|
||||
|
@ -1742,8 +1742,8 @@ void SkPDFDevice::drawFormXObjectWithMask(int xObjectIndex,
|
||||
return;
|
||||
}
|
||||
|
||||
sk_sp<SkPDFObject> sMaskGS(SkPDFGraphicState::GetSMaskGraphicState(
|
||||
mask, invertClip, SkPDFGraphicState::kAlpha_SMaskMode));
|
||||
auto sMaskGS = SkPDFGraphicState::GetSMaskGraphicState(
|
||||
mask, invertClip, SkPDFGraphicState::kAlpha_SMaskMode, fCanon);
|
||||
|
||||
SkMatrix identity;
|
||||
identity.reset();
|
||||
@ -1757,7 +1757,10 @@ void SkPDFDevice::drawFormXObjectWithMask(int xObjectIndex,
|
||||
&content.entry()->fContent);
|
||||
SkPDFUtils::DrawFormXObject(xObjectIndex, &content.entry()->fContent);
|
||||
|
||||
sMaskGS.reset(SkPDFGraphicState::GetNoSMaskGraphicState());
|
||||
// Call makeNoSmaskGraphicState() instead of
|
||||
// SkPDFGraphicState::MakeNoSmaskGraphicState so that the canon
|
||||
// can deduplicate.
|
||||
sMaskGS = fCanon->makeNoSmaskGraphicState();
|
||||
SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
|
||||
&content.entry()->fContent);
|
||||
}
|
||||
|
@ -6,12 +6,10 @@
|
||||
*/
|
||||
|
||||
#include "SkData.h"
|
||||
#include "SkOncePtr.h"
|
||||
#include "SkPDFCanon.h"
|
||||
#include "SkPDFFormXObject.h"
|
||||
#include "SkPDFGraphicState.h"
|
||||
#include "SkPDFUtils.h"
|
||||
#include "SkTypes.h"
|
||||
|
||||
static const char* as_blend_mode(SkXfermode::Mode mode) {
|
||||
switch (mode) {
|
||||
@ -126,7 +124,7 @@ SkPDFGraphicState* SkPDFGraphicState::GetGraphicStateForPaint(
|
||||
return pdfGraphicState;
|
||||
}
|
||||
|
||||
static SkPDFStream* create_invert_function() {
|
||||
sk_sp<SkPDFStream> SkPDFGraphicState::MakeInvertFunction() {
|
||||
// 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 = sk_make_sp<SkPDFArray>();
|
||||
@ -143,20 +141,14 @@ static SkPDFStream* create_invert_function() {
|
||||
invertFunction->insertInt("FunctionType", 4);
|
||||
invertFunction->insertObject("Domain", domainAndRange);
|
||||
invertFunction->insertObject("Range", std::move(domainAndRange));
|
||||
return invertFunction.release();
|
||||
return invertFunction;
|
||||
}
|
||||
|
||||
SK_DECLARE_STATIC_ONCE_PTR(SkPDFStream, invertFunction);
|
||||
|
||||
static sk_sp<SkPDFStream> make_invert_function() {
|
||||
return sk_sp<SkPDFStream>(
|
||||
SkRef(invertFunction.get(create_invert_function)));
|
||||
}
|
||||
|
||||
// static
|
||||
SkPDFDict* SkPDFGraphicState::GetSMaskGraphicState(SkPDFFormXObject* sMask,
|
||||
sk_sp<SkPDFDict> SkPDFGraphicState::GetSMaskGraphicState(
|
||||
SkPDFFormXObject* sMask,
|
||||
bool invert,
|
||||
SkPDFSMaskMode sMaskMode) {
|
||||
SkPDFSMaskMode sMaskMode,
|
||||
SkPDFCanon* canon) {
|
||||
// The practical chances of using the same mask more than once are unlikely
|
||||
// enough that it's not worth canonicalizing.
|
||||
auto sMaskDict = sk_make_sp<SkPDFDict>("Mask");
|
||||
@ -167,25 +159,21 @@ SkPDFDict* SkPDFGraphicState::GetSMaskGraphicState(SkPDFFormXObject* sMask,
|
||||
}
|
||||
sMaskDict->insertObjRef("G", sk_ref_sp(sMask));
|
||||
if (invert) {
|
||||
sMaskDict->insertObjRef("TR", make_invert_function());
|
||||
// Instead of calling SkPDFGraphicState::MakeInvertFunction,
|
||||
// let the canon deduplicate this object.
|
||||
sMaskDict->insertObjRef("TR", canon->makeInvertFunction());
|
||||
}
|
||||
|
||||
auto result = sk_make_sp<SkPDFDict>("ExtGState");
|
||||
result->insertObject("SMask", std::move(sMaskDict));
|
||||
return result.release();
|
||||
return result;
|
||||
}
|
||||
|
||||
static SkPDFDict* create_no_smask_graphic_state() {
|
||||
SkPDFDict* noSMaskGS = new SkPDFDict("ExtGState");
|
||||
sk_sp<SkPDFDict> SkPDFGraphicState::MakeNoSmaskGraphicState() {
|
||||
auto noSMaskGS = sk_make_sp<SkPDFDict>("ExtGState");
|
||||
noSMaskGS->insertName("SMask", "None");
|
||||
return noSMaskGS;
|
||||
}
|
||||
SK_DECLARE_STATIC_ONCE_PTR(SkPDFDict, noSMaskGraphicState);
|
||||
|
||||
// static
|
||||
SkPDFDict* SkPDFGraphicState::GetNoSMaskGraphicState() {
|
||||
return SkRef(noSMaskGraphicState.get(create_no_smask_graphic_state));
|
||||
}
|
||||
|
||||
void SkPDFGraphicState::emitObject(
|
||||
SkWStream* stream,
|
||||
|
@ -11,7 +11,7 @@
|
||||
#define SkPDFGraphicState_DEFINED
|
||||
|
||||
#include "SkPaint.h"
|
||||
#include "SkPDFTypes.h"
|
||||
#include "SkPDFStream.h"
|
||||
#include "SkChecksum.h"
|
||||
|
||||
class SkPDFCanon;
|
||||
@ -46,28 +46,21 @@ public:
|
||||
static SkPDFGraphicState* GetGraphicStateForPaint(SkPDFCanon* canon,
|
||||
const SkPaint& paint);
|
||||
|
||||
/** Make a graphic state that only sets the passed soft mask. The
|
||||
* reference count of the object is incremented and it is the caller's
|
||||
* responsibility to unreference it when done.
|
||||
/** Make a graphic state that only sets the passed soft mask.
|
||||
* @param sMask The form xobject to use as a soft mask.
|
||||
* @param invert Indicates if the alpha of the sMask should be inverted.
|
||||
* @param sMaskMode Whether to use alpha or luminosity for the sMask.
|
||||
*
|
||||
* These are not de-duped.
|
||||
*/
|
||||
static SkPDFDict* GetSMaskGraphicState(SkPDFFormXObject* sMask,
|
||||
static sk_sp<SkPDFDict> GetSMaskGraphicState(SkPDFFormXObject* sMask,
|
||||
bool invert,
|
||||
SkPDFSMaskMode sMaskMode);
|
||||
SkPDFSMaskMode sMaskMode,
|
||||
SkPDFCanon* canon);
|
||||
|
||||
/** Get a graphic state that only unsets the soft mask. The reference
|
||||
* count of the object is incremented and it is the caller's responsibility
|
||||
* to unreference it when done. This is needed to accommodate the weak
|
||||
* reference pattern used when the returned object is new and has no
|
||||
* other references.
|
||||
*
|
||||
* The returned object is a singleton.
|
||||
*/
|
||||
static SkPDFDict* GetNoSMaskGraphicState();
|
||||
/** Make a graphic state that only unsets the soft mask. */
|
||||
static sk_sp<SkPDFDict> MakeNoSmaskGraphicState();
|
||||
static sk_sp<SkPDFStream> MakeInvertFunction();
|
||||
|
||||
bool operator==(const SkPDFGraphicState& rhs) const {
|
||||
return 0 == memcmp(&fStrokeWidth, &rhs.fStrokeWidth, 12);
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include "SkPDFShader.h"
|
||||
|
||||
#include "SkData.h"
|
||||
#include "SkOncePtr.h"
|
||||
#include "SkPDFCanon.h"
|
||||
#include "SkPDFDevice.h"
|
||||
#include "SkPDFFormXObject.h"
|
||||
@ -20,7 +19,6 @@
|
||||
#include "SkScalar.h"
|
||||
#include "SkStream.h"
|
||||
#include "SkTemplates.h"
|
||||
#include "SkTypes.h"
|
||||
|
||||
static bool inverse_transform_bbox(const SkMatrix& matrix, SkRect* bbox) {
|
||||
SkMatrix inverse;
|
||||
@ -580,7 +578,7 @@ static SkStream* create_pattern_fill_content(int gsIndex, SkRect& bounds) {
|
||||
* Creates a ExtGState with the SMask set to the luminosityShader in
|
||||
* luminosity mode. The shader pattern extends to the bbox.
|
||||
*/
|
||||
static SkPDFObject* create_smask_graphic_state(
|
||||
static sk_sp<SkPDFObject> create_smask_graphic_state(
|
||||
SkPDFCanon* canon, SkScalar dpi, const SkPDFShader::State& state) {
|
||||
SkRect bbox;
|
||||
bbox.set(state.fBBox);
|
||||
@ -600,7 +598,7 @@ static SkPDFObject* create_smask_graphic_state(
|
||||
|
||||
return SkPDFGraphicState::GetSMaskGraphicState(
|
||||
alphaMask.get(), false,
|
||||
SkPDFGraphicState::kLuminosity_SMaskMode);
|
||||
SkPDFGraphicState::kLuminosity_SMaskMode, canon);
|
||||
}
|
||||
|
||||
SkPDFAlphaFunctionShader* SkPDFAlphaFunctionShader::Create(
|
||||
@ -621,8 +619,7 @@ SkPDFAlphaFunctionShader* SkPDFAlphaFunctionShader::Create(
|
||||
|
||||
// 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(canon, dpi, state));
|
||||
auto alphaGs = create_smask_graphic_state(canon, dpi, state);
|
||||
|
||||
SkPDFAlphaFunctionShader* alphaFunctionShader =
|
||||
new SkPDFAlphaFunctionShader(autoState->detach());
|
||||
@ -678,8 +675,8 @@ static bool split_perspective(const SkMatrix in, SkMatrix* affine,
|
||||
return true;
|
||||
}
|
||||
|
||||
static SkPDFObject* create_range_object() {
|
||||
SkPDFArray* range = new SkPDFArray;
|
||||
sk_sp<SkPDFArray> SkPDFShader::MakeRangeObject() {
|
||||
auto range = sk_make_sp<SkPDFArray>();
|
||||
range->reserve(6);
|
||||
range->appendInt(0);
|
||||
range->appendInt(1);
|
||||
@ -689,16 +686,16 @@ static SkPDFObject* create_range_object() {
|
||||
range->appendInt(1);
|
||||
return range;
|
||||
}
|
||||
SK_DECLARE_STATIC_ONCE_PTR(SkPDFObject, rangeObject);
|
||||
|
||||
static SkPDFStream* make_ps_function(const SkString& psCode,
|
||||
SkPDFArray* domain) {
|
||||
static sk_sp<SkPDFStream> make_ps_function(const SkString& psCode,
|
||||
SkPDFArray* domain,
|
||||
sk_sp<SkPDFObject> range) {
|
||||
SkAutoDataUnref funcData(
|
||||
SkData::NewWithCopy(psCode.c_str(), psCode.size()));
|
||||
SkPDFStream* result = new SkPDFStream(funcData.get());
|
||||
auto result = sk_make_sp<SkPDFStream>(funcData.get());
|
||||
result->insertInt("FunctionType", 4);
|
||||
result->insertObject("Domain", sk_ref_sp(domain));
|
||||
result->insertObject("Range", sk_ref_sp(rangeObject.get(create_range_object)));
|
||||
result->insertObject("Range", std::move(range));
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -804,8 +801,11 @@ SkPDFFunctionShader* SkPDFFunctionShader::Create(
|
||||
pdfShader->insertName("ColorSpace", "DeviceRGB");
|
||||
pdfShader->insertObject("Domain", sk_ref_sp(domain.get()));
|
||||
|
||||
sk_sp<SkPDFStream> function(
|
||||
make_ps_function(functionCode, domain.get()));
|
||||
// Call canon->makeRangeObject() instead of
|
||||
// SkPDFShader::MakeRangeObject() so that the canon can
|
||||
// deduplicate.
|
||||
auto function = make_ps_function(functionCode, domain.get(),
|
||||
canon->makeRangeObject());
|
||||
pdfShader->insertObjRef("Function", std::move(function));
|
||||
|
||||
sk_sp<SkPDFFunctionShader> pdfFunctionShader(
|
||||
|
@ -48,6 +48,8 @@ public:
|
||||
const SkMatrix& matrix,
|
||||
const SkIRect& surfaceBBox,
|
||||
SkScalar rasterScale);
|
||||
|
||||
static sk_sp<SkPDFArray> MakeRangeObject();
|
||||
};
|
||||
|
||||
class SkPDFFunctionShader final : public SkPDFDict {
|
||||
|
Loading…
Reference in New Issue
Block a user