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