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