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