SkPDFGraphicState.cpp revision e6ea244717feda4265b7062a0462267a0d9e1753
1/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkData.h"
9#include "SkLazyPtr.h"
10#include "SkPDFCanon.h"
11#include "SkPDFFormXObject.h"
12#include "SkPDFGraphicState.h"
13#include "SkPDFUtils.h"
14#include "SkTypes.h"
15
16static const char* as_blend_mode(SkXfermode::Mode mode) {
17    switch (mode) {
18        case SkXfermode::kSrcOver_Mode:
19            return "Normal";
20        case SkXfermode::kMultiply_Mode:
21            return "Multiply";
22        case SkXfermode::kScreen_Mode:
23            return "Screen";
24        case SkXfermode::kOverlay_Mode:
25            return "Overlay";
26        case SkXfermode::kDarken_Mode:
27            return "Darken";
28        case SkXfermode::kLighten_Mode:
29            return "Lighten";
30        case SkXfermode::kColorDodge_Mode:
31            return "ColorDodge";
32        case SkXfermode::kColorBurn_Mode:
33            return "ColorBurn";
34        case SkXfermode::kHardLight_Mode:
35            return "HardLight";
36        case SkXfermode::kSoftLight_Mode:
37            return "SoftLight";
38        case SkXfermode::kDifference_Mode:
39            return "Difference";
40        case SkXfermode::kExclusion_Mode:
41            return "Exclusion";
42        case SkXfermode::kHue_Mode:
43            return "Hue";
44        case SkXfermode::kSaturation_Mode:
45            return "Saturation";
46        case SkXfermode::kColor_Mode:
47            return "Color";
48        case SkXfermode::kLuminosity_Mode:
49            return "Luminosity";
50
51        // These are handled in SkPDFDevice::setUpContentEntry.
52        case SkXfermode::kClear_Mode:
53        case SkXfermode::kSrc_Mode:
54        case SkXfermode::kDst_Mode:
55        case SkXfermode::kDstOver_Mode:
56        case SkXfermode::kSrcIn_Mode:
57        case SkXfermode::kDstIn_Mode:
58        case SkXfermode::kSrcOut_Mode:
59        case SkXfermode::kDstOut_Mode:
60        case SkXfermode::kSrcATop_Mode:
61        case SkXfermode::kDstATop_Mode:
62        case SkXfermode::kModulate_Mode:
63            return "Normal";
64
65        // TODO(vandebo): Figure out if we can support more of these modes.
66        case SkXfermode::kXor_Mode:
67        case SkXfermode::kPlus_Mode:
68            return NULL;
69    }
70    return NULL;
71}
72
73static bool equivalent(const SkPaint& a, const SkPaint& b) {
74    // We're only interested in some fields of the SkPaint, so we have
75    // a custom equality function.
76    if (SkColorGetA(a.getColor()) != SkColorGetA(b.getColor()) ||
77        a.getStrokeCap() != b.getStrokeCap() ||
78        a.getStrokeJoin() != b.getStrokeJoin() ||
79        a.getStrokeWidth() != b.getStrokeWidth() ||
80        a.getStrokeMiter() != b.getStrokeMiter()) {
81        return false;
82    }
83
84    SkXfermode::Mode aXfermodeName = SkXfermode::kSrcOver_Mode;
85    SkXfermode* aXfermode = a.getXfermode();
86    if (aXfermode) {
87        aXfermode->asMode(&aXfermodeName);
88    }
89    if (aXfermodeName < 0 || aXfermodeName > SkXfermode::kLastMode ||
90        as_blend_mode(aXfermodeName) == NULL) {
91        aXfermodeName = SkXfermode::kSrcOver_Mode;
92    }
93    const char* aXfermodeString = as_blend_mode(aXfermodeName);
94    SkASSERT(aXfermodeString != NULL);
95
96    SkXfermode::Mode bXfermodeName = SkXfermode::kSrcOver_Mode;
97    SkXfermode* bXfermode = b.getXfermode();
98    if (bXfermode) {
99        bXfermode->asMode(&bXfermodeName);
100    }
101    if (bXfermodeName < 0 || bXfermodeName > SkXfermode::kLastMode ||
102        as_blend_mode(bXfermodeName) == NULL) {
103        bXfermodeName = SkXfermode::kSrcOver_Mode;
104    }
105    const char* bXfermodeString = as_blend_mode(bXfermodeName);
106    SkASSERT(bXfermodeString != NULL);
107
108    return strcmp(aXfermodeString, bXfermodeString) == 0;
109}
110
111bool SkPDFGraphicState::equals(const SkPaint& paint) const {
112    return equivalent(paint, fPaint);
113}
114
115SkPDFGraphicState::~SkPDFGraphicState() {
116    SkAutoMutexAcquire lock(SkPDFCanon::GetPaintMutex());
117    if (!fSMask) {
118        SkPDFCanon::GetCanon().removeGraphicState(this);
119    }
120    fResources.unrefAll();
121}
122
123void SkPDFGraphicState::getResources(
124        const SkTSet<SkPDFObject*>& knownResourceObjects,
125        SkTSet<SkPDFObject*>* newResourceObjects) {
126    GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects);
127}
128
129void SkPDFGraphicState::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
130    populateDict();
131    SkPDFDict::emitObject(stream, catalog);
132}
133
134// static
135SkPDFGraphicState* SkPDFGraphicState::GetGraphicStateForPaint(
136        const SkPaint& paint) {
137    SkAutoMutexAcquire lock(SkPDFCanon::GetPaintMutex());
138    SkPDFGraphicState* pdfGraphicState =
139            SkPDFCanon::GetCanon().findGraphicState(paint);
140    if (pdfGraphicState) {
141        return SkRef(pdfGraphicState);
142    }
143    pdfGraphicState = new SkPDFGraphicState(paint);
144    SkPDFCanon::GetCanon().addGraphicState(pdfGraphicState);
145    return pdfGraphicState;
146}
147
148namespace {
149SkPDFObject* create_invert_function() {
150    // Acrobat crashes if we use a type 0 function, kpdf crashes if we use
151    // a type 2 function, so we use a type 4 function.
152    SkAutoTUnref<SkPDFArray> domainAndRange(new SkPDFArray);
153    domainAndRange->reserve(2);
154    domainAndRange->appendInt(0);
155    domainAndRange->appendInt(1);
156
157    static const char psInvert[] = "{1 exch sub}";
158    // Do not copy the trailing '\0' into the SkData.
159    SkAutoTUnref<SkData> psInvertStream(
160            SkData::NewWithoutCopy(psInvert, strlen(psInvert)));
161
162    SkPDFStream* invertFunction = SkNEW_ARGS(
163            SkPDFStream, (psInvertStream.get()));
164    invertFunction->insertInt("FunctionType", 4);
165    invertFunction->insert("Domain", domainAndRange.get());
166    invertFunction->insert("Range", domainAndRange.get());
167    return invertFunction;
168}
169
170template <typename T> void unref(T* ptr) { ptr->unref(); }
171}  // namespace
172
173SK_DECLARE_STATIC_LAZY_PTR(SkPDFObject, invertFunction,
174                           create_invert_function, unref<SkPDFObject>);
175
176// static
177SkPDFObject* SkPDFGraphicState::GetInvertFunction() {
178    return invertFunction.get();
179}
180
181// static
182SkPDFGraphicState* SkPDFGraphicState::GetSMaskGraphicState(
183        SkPDFFormXObject* sMask, bool invert, SkPDFSMaskMode sMaskMode) {
184    // The practical chances of using the same mask more than once are unlikely
185    // enough that it's not worth canonicalizing.
186    SkAutoTUnref<SkPDFDict> sMaskDict(new SkPDFDict("Mask"));
187    if (sMaskMode == kAlpha_SMaskMode) {
188        sMaskDict->insertName("S", "Alpha");
189    } else if (sMaskMode == kLuminosity_SMaskMode) {
190        sMaskDict->insertName("S", "Luminosity");
191    }
192    sMaskDict->insert("G", new SkPDFObjRef(sMask))->unref();
193
194    SkPDFGraphicState* result = new SkPDFGraphicState;
195    result->fPopulated = true;
196    result->fSMask = true;
197    result->insertName("Type", "ExtGState");
198    result->insert("SMask", sMaskDict.get());
199    result->fResources.push(sMask);
200    sMask->ref();
201
202    if (invert) {
203        SkPDFObject* invertFunction = GetInvertFunction();
204        result->fResources.push(invertFunction);
205        invertFunction->ref();
206        sMaskDict->insert("TR", new SkPDFObjRef(invertFunction))->unref();
207    }
208
209    return result;
210}
211
212SkPDFGraphicState* SkPDFGraphicState::CreateNoSMaskGraphicState() {
213    SkPDFGraphicState* noSMaskGS = SkNEW(SkPDFGraphicState);
214    noSMaskGS->fPopulated = true;
215    noSMaskGS->fSMask = true;
216    noSMaskGS->insertName("Type", "ExtGState");
217    noSMaskGS->insertName("SMask", "None");
218    return noSMaskGS;
219}
220
221SK_DECLARE_STATIC_LAZY_PTR(
222        SkPDFGraphicState, noSMaskGraphicState,
223        SkPDFGraphicState::CreateNoSMaskGraphicState, unref<SkPDFGraphicState>);
224
225// static
226SkPDFGraphicState* SkPDFGraphicState::GetNoSMaskGraphicState() {
227    return SkRef(noSMaskGraphicState.get());
228}
229
230SkPDFGraphicState::SkPDFGraphicState()
231    : fPopulated(false),
232      fSMask(false) {
233}
234
235SkPDFGraphicState::SkPDFGraphicState(const SkPaint& paint)
236    : fPaint(paint),
237      fPopulated(false),
238      fSMask(false) {
239}
240
241// populateDict and operator== have to stay in sync with each other.
242void SkPDFGraphicState::populateDict() {
243    if (!fPopulated) {
244        fPopulated = true;
245        insertName("Type", "ExtGState");
246
247        SkAutoTUnref<SkPDFScalar> alpha(
248            new SkPDFScalar(SkScalarDiv(fPaint.getAlpha(), 0xFF)));
249        insert("CA", alpha.get());
250        insert("ca", alpha.get());
251
252        SK_COMPILE_ASSERT(SkPaint::kButt_Cap == 0, paint_cap_mismatch);
253        SK_COMPILE_ASSERT(SkPaint::kRound_Cap == 1, paint_cap_mismatch);
254        SK_COMPILE_ASSERT(SkPaint::kSquare_Cap == 2, paint_cap_mismatch);
255        SK_COMPILE_ASSERT(SkPaint::kCapCount == 3, paint_cap_mismatch);
256        SkASSERT(fPaint.getStrokeCap() >= 0 && fPaint.getStrokeCap() <= 2);
257        insertInt("LC", fPaint.getStrokeCap());
258
259        SK_COMPILE_ASSERT(SkPaint::kMiter_Join == 0, paint_join_mismatch);
260        SK_COMPILE_ASSERT(SkPaint::kRound_Join == 1, paint_join_mismatch);
261        SK_COMPILE_ASSERT(SkPaint::kBevel_Join == 2, paint_join_mismatch);
262        SK_COMPILE_ASSERT(SkPaint::kJoinCount == 3, paint_join_mismatch);
263        SkASSERT(fPaint.getStrokeJoin() >= 0 && fPaint.getStrokeJoin() <= 2);
264        insertInt("LJ", fPaint.getStrokeJoin());
265
266        insertScalar("LW", fPaint.getStrokeWidth());
267        insertScalar("ML", fPaint.getStrokeMiter());
268        insert("SA", new SkPDFBool(true))->unref();  // Auto stroke adjustment.
269
270        SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode;
271        // If asMode fails, default to kSrcOver_Mode.
272        if (fPaint.getXfermode())
273            fPaint.getXfermode()->asMode(&xfermode);
274        // If we don't support the mode, just use kSrcOver_Mode.
275        if (xfermode < 0 || xfermode > SkXfermode::kLastMode ||
276            as_blend_mode(xfermode) == NULL) {
277            xfermode = SkXfermode::kSrcOver_Mode;
278            NOT_IMPLEMENTED("unsupported xfermode", false);
279        }
280        insertName("BM", as_blend_mode(xfermode));
281    }
282}
283