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 "SkLazyPtr.h"
10#include "SkPDFCanon.h"
11#include "SkPDFFormXObject.h"
12#include "SkPDFGraphicState.h"
13#include "SkPDFUtils.h"
14#include "SkTypes.h"
15
16static const char* as_blend_mode(SkXfermode::Mode mode) {
17    switch (mode) {
18        case SkXfermode::kSrcOver_Mode:
19            return "Normal";
20        case SkXfermode::kMultiply_Mode:
21            return "Multiply";
22        case SkXfermode::kScreen_Mode:
23            return "Screen";
24        case SkXfermode::kOverlay_Mode:
25            return "Overlay";
26        case SkXfermode::kDarken_Mode:
27            return "Darken";
28        case SkXfermode::kLighten_Mode:
29            return "Lighten";
30        case SkXfermode::kColorDodge_Mode:
31            return "ColorDodge";
32        case SkXfermode::kColorBurn_Mode:
33            return "ColorBurn";
34        case SkXfermode::kHardLight_Mode:
35            return "HardLight";
36        case SkXfermode::kSoftLight_Mode:
37            return "SoftLight";
38        case SkXfermode::kDifference_Mode:
39            return "Difference";
40        case SkXfermode::kExclusion_Mode:
41            return "Exclusion";
42        case SkXfermode::kHue_Mode:
43            return "Hue";
44        case SkXfermode::kSaturation_Mode:
45            return "Saturation";
46        case SkXfermode::kColor_Mode:
47            return "Color";
48        case SkXfermode::kLuminosity_Mode:
49            return "Luminosity";
50
51        // These are handled in SkPDFDevice::setUpContentEntry.
52        case SkXfermode::kClear_Mode:
53        case SkXfermode::kSrc_Mode:
54        case SkXfermode::kDst_Mode:
55        case SkXfermode::kDstOver_Mode:
56        case SkXfermode::kSrcIn_Mode:
57        case SkXfermode::kDstIn_Mode:
58        case SkXfermode::kSrcOut_Mode:
59        case SkXfermode::kDstOut_Mode:
60        case SkXfermode::kSrcATop_Mode:
61        case SkXfermode::kDstATop_Mode:
62        case SkXfermode::kModulate_Mode:
63            return "Normal";
64
65        // TODO(vandebo): Figure out if we can support more of these modes.
66        case SkXfermode::kXor_Mode:
67        case SkXfermode::kPlus_Mode:
68            return NULL;
69    }
70    return NULL;
71}
72
73// If a SkXfermode is unsupported in PDF, this function returns
74// SrcOver, otherwise, it returns that Xfermode as a Mode.
75static SkXfermode::Mode mode_for_pdf(const SkXfermode* xfermode) {
76    SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode;
77    if (xfermode) {
78        xfermode->asMode(&mode);
79    }
80    switch (mode) {
81        case SkXfermode::kSrcOver_Mode:
82        case SkXfermode::kMultiply_Mode:
83        case SkXfermode::kScreen_Mode:
84        case SkXfermode::kOverlay_Mode:
85        case SkXfermode::kDarken_Mode:
86        case SkXfermode::kLighten_Mode:
87        case SkXfermode::kColorDodge_Mode:
88        case SkXfermode::kColorBurn_Mode:
89        case SkXfermode::kHardLight_Mode:
90        case SkXfermode::kSoftLight_Mode:
91        case SkXfermode::kDifference_Mode:
92        case SkXfermode::kExclusion_Mode:
93        case SkXfermode::kHue_Mode:
94        case SkXfermode::kSaturation_Mode:
95        case SkXfermode::kColor_Mode:
96        case SkXfermode::kLuminosity_Mode:
97            // Mode is suppported and handled by pdf graphics state.
98            return mode;
99        default:
100            return SkXfermode::kSrcOver_Mode;  // Default mode.
101    }
102}
103
104SkPDFGraphicState::SkPDFGraphicState(const SkPaint& p)
105    : fStrokeWidth(p.getStrokeWidth())
106    , fStrokeMiter(p.getStrokeMiter())
107    , fAlpha(p.getAlpha())
108    , fStrokeCap(SkToU8(p.getStrokeCap()))
109    , fStrokeJoin(SkToU8(p.getStrokeJoin()))
110    , fMode(SkToU8(mode_for_pdf(p.getXfermode()))) {}
111
112// static
113SkPDFGraphicState* SkPDFGraphicState::GetGraphicStateForPaint(
114        SkPDFCanon* canon, const SkPaint& paint) {
115    SkASSERT(canon);
116    SkPDFGraphicState key(paint);
117    if (const SkPDFGraphicState* canonGS = canon->findGraphicState(key)) {
118        // The returned SkPDFGraphicState must be made non-const,
119        // since the emitObject() interface is non-const.  But We
120        // promise that there is no way to mutate this object from
121        // here on out.
122        return SkRef(const_cast<SkPDFGraphicState*>(canonGS));
123    }
124    SkPDFGraphicState* pdfGraphicState = new SkPDFGraphicState(paint);
125    canon->addGraphicState(pdfGraphicState);
126    return pdfGraphicState;
127}
128
129namespace {
130SkPDFObject* create_invert_function() {
131    // Acrobat crashes if we use a type 0 function, kpdf crashes if we use
132    // a type 2 function, so we use a type 4 function.
133    SkAutoTUnref<SkPDFArray> domainAndRange(new SkPDFArray);
134    domainAndRange->reserve(2);
135    domainAndRange->appendInt(0);
136    domainAndRange->appendInt(1);
137
138    static const char psInvert[] = "{1 exch sub}";
139    // Do not copy the trailing '\0' into the SkData.
140    SkAutoTUnref<SkData> psInvertStream(
141            SkData::NewWithoutCopy(psInvert, strlen(psInvert)));
142
143    SkPDFStream* invertFunction = SkNEW_ARGS(
144            SkPDFStream, (psInvertStream.get()));
145    invertFunction->insertInt("FunctionType", 4);
146    invertFunction->insertObject("Domain", SkRef(domainAndRange.get()));
147    invertFunction->insertObject("Range", domainAndRange.detach());
148    return invertFunction;
149}
150
151template <typename T> void unref(T* ptr) { ptr->unref(); }
152}  // namespace
153
154SK_DECLARE_STATIC_LAZY_PTR(SkPDFObject,
155                           invertFunction,
156                           create_invert_function,
157                           unref<SkPDFObject>);
158
159// static
160SkPDFDict* SkPDFGraphicState::GetSMaskGraphicState(SkPDFFormXObject* sMask,
161                                                   bool invert,
162                                                   SkPDFSMaskMode sMaskMode) {
163    // The practical chances of using the same mask more than once are unlikely
164    // enough that it's not worth canonicalizing.
165    SkAutoTUnref<SkPDFDict> sMaskDict(new SkPDFDict("Mask"));
166    if (sMaskMode == kAlpha_SMaskMode) {
167        sMaskDict->insertName("S", "Alpha");
168    } else if (sMaskMode == kLuminosity_SMaskMode) {
169        sMaskDict->insertName("S", "Luminosity");
170    }
171    sMaskDict->insertObjRef("G", SkRef(sMask));
172    if (invert) {
173        sMaskDict->insertObjRef("TR", SkRef(invertFunction.get()));
174    }
175
176    SkPDFDict* result = new SkPDFDict("ExtGState");
177    result->insertObject("SMask", sMaskDict.detach());
178    return result;
179}
180
181namespace {
182SkPDFDict* create_no_smask_graphic_state() {
183    SkPDFDict* noSMaskGS = new SkPDFDict("ExtGState");
184    noSMaskGS->insertName("SMask", "None");
185    return noSMaskGS;
186}
187} // namespace
188SK_DECLARE_STATIC_LAZY_PTR(SkPDFDict,
189                           noSMaskGraphicState,
190                           create_no_smask_graphic_state,
191                           unref<SkPDFDict>);
192
193// static
194SkPDFDict* SkPDFGraphicState::GetNoSMaskGraphicState() {
195    return SkRef(noSMaskGraphicState.get());
196}
197
198void SkPDFGraphicState::emitObject(SkWStream* stream,
199                                   const SkPDFObjNumMap& objNumMap,
200                                   const SkPDFSubstituteMap& substitutes) {
201    SkAutoTUnref<SkPDFDict> dict(SkNEW_ARGS(SkPDFDict, ("ExtGState")));
202    dict->insertName("Type", "ExtGState");
203
204    SkScalar alpha = SkIntToScalar(fAlpha) / 0xFF;
205    dict->insertScalar("CA", alpha);
206    dict->insertScalar("ca", alpha);
207
208    SkPaint::Cap strokeCap = (SkPaint::Cap)fStrokeCap;
209    SkPaint::Join strokeJoin = (SkPaint::Join)fStrokeJoin;
210    SkXfermode::Mode xferMode = (SkXfermode::Mode)fMode;
211
212    SK_COMPILE_ASSERT(SkPaint::kButt_Cap == 0, paint_cap_mismatch);
213    SK_COMPILE_ASSERT(SkPaint::kRound_Cap == 1, paint_cap_mismatch);
214    SK_COMPILE_ASSERT(SkPaint::kSquare_Cap == 2, paint_cap_mismatch);
215    SK_COMPILE_ASSERT(SkPaint::kCapCount == 3, paint_cap_mismatch);
216    SkASSERT(strokeCap >= 0 && strokeCap <= 2);
217    dict->insertInt("LC", strokeCap);
218
219    SK_COMPILE_ASSERT(SkPaint::kMiter_Join == 0, paint_join_mismatch);
220    SK_COMPILE_ASSERT(SkPaint::kRound_Join == 1, paint_join_mismatch);
221    SK_COMPILE_ASSERT(SkPaint::kBevel_Join == 2, paint_join_mismatch);
222    SK_COMPILE_ASSERT(SkPaint::kJoinCount == 3, paint_join_mismatch);
223    SkASSERT(strokeJoin >= 0 && strokeJoin <= 2);
224    dict->insertInt("LJ", strokeJoin);
225
226    dict->insertScalar("LW", fStrokeWidth);
227    dict->insertScalar("ML", fStrokeMiter);
228    dict->insertBool("SA", true);  // SA = Auto stroke adjustment.
229    dict->insertName("BM", as_blend_mode(xferMode));
230    dict->emitObject(stream, objNumMap, substitutes);
231}
232