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