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