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:
halcanary 2016-03-13 18:30:24 -07:00 committed by Commit bot
parent 1a9b964084
commit 1437c1eea8
7 changed files with 74 additions and 59 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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);
}

View File

@ -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,
bool invert,
SkPDFSMaskMode sMaskMode) {
sk_sp<SkPDFDict> SkPDFGraphicState::GetSMaskGraphicState(
SkPDFFormXObject* sMask,
bool invert,
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,

View File

@ -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,
bool invert,
SkPDFSMaskMode sMaskMode);
static sk_sp<SkPDFDict> GetSMaskGraphicState(SkPDFFormXObject* sMask,
bool invert,
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);

View File

@ -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(

View File

@ -48,6 +48,8 @@ public:
const SkMatrix& matrix,
const SkIRect& surfaceBBox,
SkScalar rasterScale);
static sk_sp<SkPDFArray> MakeRangeObject();
};
class SkPDFFunctionShader final : public SkPDFDict {