SkPDFGraphicState.cpp revision 918352f86e939fe042495aad4f5ff33c4ecec9ed
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::kMultiply_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
88SkMutex& SkPDFGraphicState::CanonicalPaintsMutex() {
89    // This initialization is only thread safe with gcc.
90    static SkMutex 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        SkRefPtr<SkPDFArray> domainAndRange = new SkPDFArray;
116        domainAndRange->unref();  // SkRefPtr and new both took a reference.
117        domainAndRange->reserve(2);
118        domainAndRange->appendInt(0);
119        domainAndRange->appendInt(1);
120
121        static const char psInvert[] = "{1 exch sub}";
122        SkRefPtr<SkMemoryStream> psInvertStream =
123            new SkMemoryStream(&psInvert, strlen(psInvert), true);
124        psInvertStream->unref();  // SkRefPtr and new both took a reference.
125
126        invertFunction = new SkPDFStream(psInvertStream.get());
127        invertFunction->insertInt("FunctionType", 4);
128        invertFunction->insert("Domain", domainAndRange.get());
129        invertFunction->insert("Range", domainAndRange.get());
130    }
131    return invertFunction;
132}
133
134// static
135SkPDFGraphicState* SkPDFGraphicState::GetSMaskGraphicState(
136        SkPDFFormXObject* sMask, bool invert) {
137    // The practical chances of using the same mask more than once are unlikely
138    // enough that it's not worth canonicalizing.
139    SkAutoMutexAcquire lock(CanonicalPaintsMutex());
140
141    SkRefPtr<SkPDFDict> sMaskDict = new SkPDFDict("Mask");
142    sMaskDict->unref();  // SkRefPtr and new both took a reference.
143    sMaskDict->insertName("S", "Alpha");
144    sMaskDict->insert("G", new SkPDFObjRef(sMask))->unref();
145
146    SkPDFGraphicState* result = new SkPDFGraphicState;
147    result->fPopulated = true;
148    result->fSMask = true;
149    result->insertName("Type", "ExtGState");
150    result->insert("SMask", sMaskDict.get());
151    result->fResources.push(sMask);
152    sMask->ref();
153
154    if (invert) {
155        SkPDFObject* invertFunction = GetInvertFunction();
156        result->fResources.push(invertFunction);
157        invertFunction->ref();
158        sMaskDict->insert("TR", new SkPDFObjRef(invertFunction))->unref();
159    }
160
161    return result;
162}
163
164// static
165SkPDFGraphicState* SkPDFGraphicState::GetNoSMaskGraphicState() {
166    SkAutoMutexAcquire lock(CanonicalPaintsMutex());
167    static SkPDFGraphicState* noSMaskGS = NULL;
168    if (!noSMaskGS) {
169        noSMaskGS = new SkPDFGraphicState;
170        noSMaskGS->fPopulated = true;
171        noSMaskGS->fSMask = true;
172        noSMaskGS->insertName("Type", "ExtGState");
173        noSMaskGS->insertName("SMask", "None");
174    }
175    noSMaskGS->ref();
176    return noSMaskGS;
177}
178
179// static
180int SkPDFGraphicState::Find(const SkPaint& paint) {
181    GSCanonicalEntry search(&paint);
182    return CanonicalPaints().find(search);
183}
184
185SkPDFGraphicState::SkPDFGraphicState()
186    : fPopulated(false),
187      fSMask(false) {
188}
189
190SkPDFGraphicState::SkPDFGraphicState(const SkPaint& paint)
191    : fPaint(paint),
192      fPopulated(false),
193      fSMask(false) {
194}
195
196// populateDict and operator== have to stay in sync with each other.
197void SkPDFGraphicState::populateDict() {
198    if (!fPopulated) {
199        fPopulated = true;
200        insertName("Type", "ExtGState");
201
202        SkRefPtr<SkPDFScalar> alpha =
203            new SkPDFScalar(fPaint.getAlpha() * SkScalarInvert(0xFF));
204        alpha->unref();  // SkRefPtr and new both took a reference.
205        insert("CA", alpha.get());
206        insert("ca", alpha.get());
207
208        SK_COMPILE_ASSERT(SkPaint::kButt_Cap == 0, paint_cap_mismatch);
209        SK_COMPILE_ASSERT(SkPaint::kRound_Cap == 1, paint_cap_mismatch);
210        SK_COMPILE_ASSERT(SkPaint::kSquare_Cap == 2, paint_cap_mismatch);
211        SK_COMPILE_ASSERT(SkPaint::kCapCount == 3, paint_cap_mismatch);
212        SkASSERT(fPaint.getStrokeCap() >= 0 && fPaint.getStrokeCap() <= 2);
213        insertInt("LC", fPaint.getStrokeCap());
214
215        SK_COMPILE_ASSERT(SkPaint::kMiter_Join == 0, paint_join_mismatch);
216        SK_COMPILE_ASSERT(SkPaint::kRound_Join == 1, paint_join_mismatch);
217        SK_COMPILE_ASSERT(SkPaint::kBevel_Join == 2, paint_join_mismatch);
218        SK_COMPILE_ASSERT(SkPaint::kJoinCount == 3, paint_join_mismatch);
219        SkASSERT(fPaint.getStrokeJoin() >= 0 && fPaint.getStrokeJoin() <= 2);
220        insertInt("LJ", fPaint.getStrokeJoin());
221
222        insertScalar("LW", fPaint.getStrokeWidth());
223        insertScalar("ML", fPaint.getStrokeMiter());
224        insert("SA", new SkPDFBool(true))->unref();  // Auto stroke adjustment.
225
226        SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode;
227        // If asMode fails, default to kSrcOver_Mode.
228        if (fPaint.getXfermode())
229            fPaint.getXfermode()->asMode(&xfermode);
230        // If we don't support the mode, just use kSrcOver_Mode.
231        if (xfermode < 0 || xfermode > SkXfermode::kLastMode ||
232                blend_mode_from_xfermode(xfermode) == NULL) {
233            xfermode = SkXfermode::kSrcOver_Mode;
234            NOT_IMPLEMENTED("unsupported xfermode", false);
235        }
236        insertName("BM", blend_mode_from_xfermode(xfermode));
237    }
238}
239
240// We're only interested in some fields of the SkPaint, so we have a custom
241// operator== function.
242bool SkPDFGraphicState::GSCanonicalEntry::operator==(
243        const SkPDFGraphicState::GSCanonicalEntry& gs) const {
244    const SkPaint* a = fPaint;
245    const SkPaint* b = gs.fPaint;
246    SkASSERT(a != NULL);
247    SkASSERT(b != NULL);
248
249    if (SkColorGetA(a->getColor()) != SkColorGetA(b->getColor()) ||
250           a->getStrokeCap() != b->getStrokeCap() ||
251           a->getStrokeJoin() != b->getStrokeJoin() ||
252           a->getStrokeWidth() != b->getStrokeWidth() ||
253           a->getStrokeMiter() != b->getStrokeMiter()) {
254        return false;
255    }
256
257    SkXfermode::Mode aXfermodeName = SkXfermode::kSrcOver_Mode;
258    SkXfermode* aXfermode = a->getXfermode();
259    if (aXfermode) {
260        aXfermode->asMode(&aXfermodeName);
261    }
262    if (aXfermodeName < 0 || aXfermodeName > SkXfermode::kLastMode ||
263            blend_mode_from_xfermode(aXfermodeName) == NULL) {
264        aXfermodeName = SkXfermode::kSrcOver_Mode;
265    }
266    const char* aXfermodeString = blend_mode_from_xfermode(aXfermodeName);
267    SkASSERT(aXfermodeString != NULL);
268
269    SkXfermode::Mode bXfermodeName = SkXfermode::kSrcOver_Mode;
270    SkXfermode* bXfermode = b->getXfermode();
271    if (bXfermode) {
272        bXfermode->asMode(&bXfermodeName);
273    }
274    if (bXfermodeName < 0 || bXfermodeName > SkXfermode::kLastMode ||
275            blend_mode_from_xfermode(bXfermodeName) == NULL) {
276        bXfermodeName = SkXfermode::kSrcOver_Mode;
277    }
278    const char* bXfermodeString = blend_mode_from_xfermode(bXfermodeName);
279    SkASSERT(bXfermodeString != NULL);
280
281    return strcmp(aXfermodeString, bXfermodeString) == 0;
282}
283