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);
|
bm.copyTo(&n32bitmap, kN32_SkColorType);
|
||||||
return *fBitmapToImageMap.set(key, SkImage::NewFromBitmap(n32bitmap));
|
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(); }
|
SkPixelSerializer* getPixelSerializer() const { return fPixelSerializer.get(); }
|
||||||
void setPixelSerializer(SkPixelSerializer* ps) { fPixelSerializer.reset(ps); }
|
void setPixelSerializer(SkPixelSerializer* ps) { fPixelSerializer.reset(ps); }
|
||||||
|
|
||||||
|
sk_sp<SkPDFStream> makeInvertFunction();
|
||||||
|
sk_sp<SkPDFDict> makeNoSmaskGraphicState();
|
||||||
|
sk_sp<SkPDFArray> makeRangeObject();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct FontRec {
|
struct FontRec {
|
||||||
SkPDFFont* fFont;
|
SkPDFFont* fFont;
|
||||||
@ -119,5 +123,8 @@ private:
|
|||||||
SkTHashMap<uint32_t /*ImageUniqueID*/, SkPDFObject*> fPDFBitmapMap;
|
SkTHashMap<uint32_t /*ImageUniqueID*/, SkPDFObject*> fPDFBitmapMap;
|
||||||
|
|
||||||
sk_sp<SkPixelSerializer> fPixelSerializer;
|
sk_sp<SkPixelSerializer> fPixelSerializer;
|
||||||
|
sk_sp<SkPDFStream> fInvertFunction;
|
||||||
|
sk_sp<SkPDFDict> fNoSmaskGraphicState;
|
||||||
|
sk_sp<SkPDFArray> fRangeObject;
|
||||||
};
|
};
|
||||||
#endif // SkPDFCanon_DEFINED
|
#endif // SkPDFCanon_DEFINED
|
||||||
|
@ -1742,8 +1742,8 @@ void SkPDFDevice::drawFormXObjectWithMask(int xObjectIndex,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sk_sp<SkPDFObject> sMaskGS(SkPDFGraphicState::GetSMaskGraphicState(
|
auto sMaskGS = SkPDFGraphicState::GetSMaskGraphicState(
|
||||||
mask, invertClip, SkPDFGraphicState::kAlpha_SMaskMode));
|
mask, invertClip, SkPDFGraphicState::kAlpha_SMaskMode, fCanon);
|
||||||
|
|
||||||
SkMatrix identity;
|
SkMatrix identity;
|
||||||
identity.reset();
|
identity.reset();
|
||||||
@ -1757,7 +1757,10 @@ void SkPDFDevice::drawFormXObjectWithMask(int xObjectIndex,
|
|||||||
&content.entry()->fContent);
|
&content.entry()->fContent);
|
||||||
SkPDFUtils::DrawFormXObject(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()),
|
SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
|
||||||
&content.entry()->fContent);
|
&content.entry()->fContent);
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "SkData.h"
|
#include "SkData.h"
|
||||||
#include "SkOncePtr.h"
|
|
||||||
#include "SkPDFCanon.h"
|
#include "SkPDFCanon.h"
|
||||||
#include "SkPDFFormXObject.h"
|
#include "SkPDFFormXObject.h"
|
||||||
#include "SkPDFGraphicState.h"
|
#include "SkPDFGraphicState.h"
|
||||||
#include "SkPDFUtils.h"
|
#include "SkPDFUtils.h"
|
||||||
#include "SkTypes.h"
|
|
||||||
|
|
||||||
static const char* as_blend_mode(SkXfermode::Mode mode) {
|
static const char* as_blend_mode(SkXfermode::Mode mode) {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
@ -126,7 +124,7 @@ SkPDFGraphicState* SkPDFGraphicState::GetGraphicStateForPaint(
|
|||||||
return pdfGraphicState;
|
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
|
// 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.
|
// a type 2 function, so we use a type 4 function.
|
||||||
auto domainAndRange = sk_make_sp<SkPDFArray>();
|
auto domainAndRange = sk_make_sp<SkPDFArray>();
|
||||||
@ -143,20 +141,14 @@ static SkPDFStream* create_invert_function() {
|
|||||||
invertFunction->insertInt("FunctionType", 4);
|
invertFunction->insertInt("FunctionType", 4);
|
||||||
invertFunction->insertObject("Domain", domainAndRange);
|
invertFunction->insertObject("Domain", domainAndRange);
|
||||||
invertFunction->insertObject("Range", std::move(domainAndRange));
|
invertFunction->insertObject("Range", std::move(domainAndRange));
|
||||||
return invertFunction.release();
|
return invertFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
SK_DECLARE_STATIC_ONCE_PTR(SkPDFStream, invertFunction);
|
sk_sp<SkPDFDict> SkPDFGraphicState::GetSMaskGraphicState(
|
||||||
|
SkPDFFormXObject* sMask,
|
||||||
static sk_sp<SkPDFStream> make_invert_function() {
|
|
||||||
return sk_sp<SkPDFStream>(
|
|
||||||
SkRef(invertFunction.get(create_invert_function)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
SkPDFDict* SkPDFGraphicState::GetSMaskGraphicState(SkPDFFormXObject* sMask,
|
|
||||||
bool invert,
|
bool invert,
|
||||||
SkPDFSMaskMode sMaskMode) {
|
SkPDFSMaskMode sMaskMode,
|
||||||
|
SkPDFCanon* canon) {
|
||||||
// The practical chances of using the same mask more than once are unlikely
|
// The practical chances of using the same mask more than once are unlikely
|
||||||
// enough that it's not worth canonicalizing.
|
// enough that it's not worth canonicalizing.
|
||||||
auto sMaskDict = sk_make_sp<SkPDFDict>("Mask");
|
auto sMaskDict = sk_make_sp<SkPDFDict>("Mask");
|
||||||
@ -167,25 +159,21 @@ SkPDFDict* SkPDFGraphicState::GetSMaskGraphicState(SkPDFFormXObject* sMask,
|
|||||||
}
|
}
|
||||||
sMaskDict->insertObjRef("G", sk_ref_sp(sMask));
|
sMaskDict->insertObjRef("G", sk_ref_sp(sMask));
|
||||||
if (invert) {
|
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");
|
auto result = sk_make_sp<SkPDFDict>("ExtGState");
|
||||||
result->insertObject("SMask", std::move(sMaskDict));
|
result->insertObject("SMask", std::move(sMaskDict));
|
||||||
return result.release();
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SkPDFDict* create_no_smask_graphic_state() {
|
sk_sp<SkPDFDict> SkPDFGraphicState::MakeNoSmaskGraphicState() {
|
||||||
SkPDFDict* noSMaskGS = new SkPDFDict("ExtGState");
|
auto noSMaskGS = sk_make_sp<SkPDFDict>("ExtGState");
|
||||||
noSMaskGS->insertName("SMask", "None");
|
noSMaskGS->insertName("SMask", "None");
|
||||||
return noSMaskGS;
|
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(
|
void SkPDFGraphicState::emitObject(
|
||||||
SkWStream* stream,
|
SkWStream* stream,
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
#define SkPDFGraphicState_DEFINED
|
#define SkPDFGraphicState_DEFINED
|
||||||
|
|
||||||
#include "SkPaint.h"
|
#include "SkPaint.h"
|
||||||
#include "SkPDFTypes.h"
|
#include "SkPDFStream.h"
|
||||||
#include "SkChecksum.h"
|
#include "SkChecksum.h"
|
||||||
|
|
||||||
class SkPDFCanon;
|
class SkPDFCanon;
|
||||||
@ -46,28 +46,21 @@ public:
|
|||||||
static SkPDFGraphicState* GetGraphicStateForPaint(SkPDFCanon* canon,
|
static SkPDFGraphicState* GetGraphicStateForPaint(SkPDFCanon* canon,
|
||||||
const SkPaint& paint);
|
const SkPaint& paint);
|
||||||
|
|
||||||
/** Make a graphic state that only sets the passed soft mask. The
|
/** Make a graphic state that only sets the passed soft mask.
|
||||||
* reference count of the object is incremented and it is the caller's
|
|
||||||
* responsibility to unreference it when done.
|
|
||||||
* @param sMask The form xobject to use as a 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 invert Indicates if the alpha of the sMask should be inverted.
|
||||||
* @param sMaskMode Whether to use alpha or luminosity for the sMask.
|
* @param sMaskMode Whether to use alpha or luminosity for the sMask.
|
||||||
*
|
*
|
||||||
* These are not de-duped.
|
* These are not de-duped.
|
||||||
*/
|
*/
|
||||||
static SkPDFDict* GetSMaskGraphicState(SkPDFFormXObject* sMask,
|
static sk_sp<SkPDFDict> GetSMaskGraphicState(SkPDFFormXObject* sMask,
|
||||||
bool invert,
|
bool invert,
|
||||||
SkPDFSMaskMode sMaskMode);
|
SkPDFSMaskMode sMaskMode,
|
||||||
|
SkPDFCanon* canon);
|
||||||
|
|
||||||
/** Get a graphic state that only unsets the soft mask. The reference
|
/** Make a graphic state that only unsets the soft mask. */
|
||||||
* count of the object is incremented and it is the caller's responsibility
|
static sk_sp<SkPDFDict> MakeNoSmaskGraphicState();
|
||||||
* to unreference it when done. This is needed to accommodate the weak
|
static sk_sp<SkPDFStream> MakeInvertFunction();
|
||||||
* reference pattern used when the returned object is new and has no
|
|
||||||
* other references.
|
|
||||||
*
|
|
||||||
* The returned object is a singleton.
|
|
||||||
*/
|
|
||||||
static SkPDFDict* GetNoSMaskGraphicState();
|
|
||||||
|
|
||||||
bool operator==(const SkPDFGraphicState& rhs) const {
|
bool operator==(const SkPDFGraphicState& rhs) const {
|
||||||
return 0 == memcmp(&fStrokeWidth, &rhs.fStrokeWidth, 12);
|
return 0 == memcmp(&fStrokeWidth, &rhs.fStrokeWidth, 12);
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
#include "SkPDFShader.h"
|
#include "SkPDFShader.h"
|
||||||
|
|
||||||
#include "SkData.h"
|
#include "SkData.h"
|
||||||
#include "SkOncePtr.h"
|
|
||||||
#include "SkPDFCanon.h"
|
#include "SkPDFCanon.h"
|
||||||
#include "SkPDFDevice.h"
|
#include "SkPDFDevice.h"
|
||||||
#include "SkPDFFormXObject.h"
|
#include "SkPDFFormXObject.h"
|
||||||
@ -20,7 +19,6 @@
|
|||||||
#include "SkScalar.h"
|
#include "SkScalar.h"
|
||||||
#include "SkStream.h"
|
#include "SkStream.h"
|
||||||
#include "SkTemplates.h"
|
#include "SkTemplates.h"
|
||||||
#include "SkTypes.h"
|
|
||||||
|
|
||||||
static bool inverse_transform_bbox(const SkMatrix& matrix, SkRect* bbox) {
|
static bool inverse_transform_bbox(const SkMatrix& matrix, SkRect* bbox) {
|
||||||
SkMatrix inverse;
|
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
|
* Creates a ExtGState with the SMask set to the luminosityShader in
|
||||||
* luminosity mode. The shader pattern extends to the bbox.
|
* 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) {
|
SkPDFCanon* canon, SkScalar dpi, const SkPDFShader::State& state) {
|
||||||
SkRect bbox;
|
SkRect bbox;
|
||||||
bbox.set(state.fBBox);
|
bbox.set(state.fBBox);
|
||||||
@ -600,7 +598,7 @@ static SkPDFObject* create_smask_graphic_state(
|
|||||||
|
|
||||||
return SkPDFGraphicState::GetSMaskGraphicState(
|
return SkPDFGraphicState::GetSMaskGraphicState(
|
||||||
alphaMask.get(), false,
|
alphaMask.get(), false,
|
||||||
SkPDFGraphicState::kLuminosity_SMaskMode);
|
SkPDFGraphicState::kLuminosity_SMaskMode, canon);
|
||||||
}
|
}
|
||||||
|
|
||||||
SkPDFAlphaFunctionShader* SkPDFAlphaFunctionShader::Create(
|
SkPDFAlphaFunctionShader* SkPDFAlphaFunctionShader::Create(
|
||||||
@ -621,8 +619,7 @@ SkPDFAlphaFunctionShader* SkPDFAlphaFunctionShader::Create(
|
|||||||
|
|
||||||
// Create resource dict with alpha graphics state as G0 and
|
// Create resource dict with alpha graphics state as G0 and
|
||||||
// pattern shader as P0, then write content stream.
|
// pattern shader as P0, then write content stream.
|
||||||
sk_sp<SkPDFObject> alphaGs(
|
auto alphaGs = create_smask_graphic_state(canon, dpi, state);
|
||||||
create_smask_graphic_state(canon, dpi, state));
|
|
||||||
|
|
||||||
SkPDFAlphaFunctionShader* alphaFunctionShader =
|
SkPDFAlphaFunctionShader* alphaFunctionShader =
|
||||||
new SkPDFAlphaFunctionShader(autoState->detach());
|
new SkPDFAlphaFunctionShader(autoState->detach());
|
||||||
@ -678,8 +675,8 @@ static bool split_perspective(const SkMatrix in, SkMatrix* affine,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SkPDFObject* create_range_object() {
|
sk_sp<SkPDFArray> SkPDFShader::MakeRangeObject() {
|
||||||
SkPDFArray* range = new SkPDFArray;
|
auto range = sk_make_sp<SkPDFArray>();
|
||||||
range->reserve(6);
|
range->reserve(6);
|
||||||
range->appendInt(0);
|
range->appendInt(0);
|
||||||
range->appendInt(1);
|
range->appendInt(1);
|
||||||
@ -689,16 +686,16 @@ static SkPDFObject* create_range_object() {
|
|||||||
range->appendInt(1);
|
range->appendInt(1);
|
||||||
return range;
|
return range;
|
||||||
}
|
}
|
||||||
SK_DECLARE_STATIC_ONCE_PTR(SkPDFObject, rangeObject);
|
|
||||||
|
|
||||||
static SkPDFStream* make_ps_function(const SkString& psCode,
|
static sk_sp<SkPDFStream> make_ps_function(const SkString& psCode,
|
||||||
SkPDFArray* domain) {
|
SkPDFArray* domain,
|
||||||
|
sk_sp<SkPDFObject> range) {
|
||||||
SkAutoDataUnref funcData(
|
SkAutoDataUnref funcData(
|
||||||
SkData::NewWithCopy(psCode.c_str(), psCode.size()));
|
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->insertInt("FunctionType", 4);
|
||||||
result->insertObject("Domain", sk_ref_sp(domain));
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -804,8 +801,11 @@ SkPDFFunctionShader* SkPDFFunctionShader::Create(
|
|||||||
pdfShader->insertName("ColorSpace", "DeviceRGB");
|
pdfShader->insertName("ColorSpace", "DeviceRGB");
|
||||||
pdfShader->insertObject("Domain", sk_ref_sp(domain.get()));
|
pdfShader->insertObject("Domain", sk_ref_sp(domain.get()));
|
||||||
|
|
||||||
sk_sp<SkPDFStream> function(
|
// Call canon->makeRangeObject() instead of
|
||||||
make_ps_function(functionCode, domain.get()));
|
// SkPDFShader::MakeRangeObject() so that the canon can
|
||||||
|
// deduplicate.
|
||||||
|
auto function = make_ps_function(functionCode, domain.get(),
|
||||||
|
canon->makeRangeObject());
|
||||||
pdfShader->insertObjRef("Function", std::move(function));
|
pdfShader->insertObjRef("Function", std::move(function));
|
||||||
|
|
||||||
sk_sp<SkPDFFunctionShader> pdfFunctionShader(
|
sk_sp<SkPDFFunctionShader> pdfFunctionShader(
|
||||||
|
@ -48,6 +48,8 @@ public:
|
|||||||
const SkMatrix& matrix,
|
const SkMatrix& matrix,
|
||||||
const SkIRect& surfaceBBox,
|
const SkIRect& surfaceBBox,
|
||||||
SkScalar rasterScale);
|
SkScalar rasterScale);
|
||||||
|
|
||||||
|
static sk_sp<SkPDFArray> MakeRangeObject();
|
||||||
};
|
};
|
||||||
|
|
||||||
class SkPDFFunctionShader final : public SkPDFDict {
|
class SkPDFFunctionShader final : public SkPDFDict {
|
||||||
|
Loading…
Reference in New Issue
Block a user