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
blend_mode_from_xfermode(SkXfermode::Mode mode)14 static 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
~SkPDFGraphicState()55 SkPDFGraphicState::~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
getResources(const SkTSet<SkPDFObject * > & knownResourceObjects,SkTSet<SkPDFObject * > * newResourceObjects)66 void SkPDFGraphicState::getResources(
67 const SkTSet<SkPDFObject*>& knownResourceObjects,
68 SkTSet<SkPDFObject*>* newResourceObjects) {
69 GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects);
70 }
71
emitObject(SkWStream * stream,SkPDFCatalog * catalog,bool indirect)72 void SkPDFGraphicState::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
73 bool indirect) {
74 populateDict();
75 SkPDFDict::emitObject(stream, catalog, indirect);
76 }
77
78 // static
getOutputSize(SkPDFCatalog * catalog,bool indirect)79 size_t SkPDFGraphicState::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
80 populateDict();
81 return SkPDFDict::getOutputSize(catalog, indirect);
82 }
83
84 // static
CanonicalPaints()85 SkTDArray<SkPDFGraphicState::GSCanonicalEntry>& SkPDFGraphicState::CanonicalPaints() {
86 CanonicalPaintsMutex().assertHeld();
87 static SkTDArray<SkPDFGraphicState::GSCanonicalEntry> gCanonicalPaints;
88 return gCanonicalPaints;
89 }
90
91 // static
CanonicalPaintsMutex()92 SkBaseMutex& SkPDFGraphicState::CanonicalPaintsMutex() {
93 SK_DECLARE_STATIC_MUTEX(gCanonicalPaintsMutex);
94 return gCanonicalPaintsMutex;
95 }
96
97 // static
GetGraphicStateForPaint(const SkPaint & paint)98 SkPDFGraphicState* 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
GetInvertFunction()111 SkPDFObject* 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 SkAutoTUnref<SkMemoryStream> psInvertStream(
125 new SkMemoryStream(&psInvert, strlen(psInvert), true));
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
GetSMaskGraphicState(SkPDFFormXObject * sMask,bool invert,SkPDFSMaskMode sMaskMode)136 SkPDFGraphicState* SkPDFGraphicState::GetSMaskGraphicState(
137 SkPDFFormXObject* sMask, bool invert, SkPDFSMaskMode sMaskMode) {
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 SkAutoTUnref<SkPDFDict> sMaskDict(new SkPDFDict("Mask"));
143 if (sMaskMode == kAlpha_SMaskMode) {
144 sMaskDict->insertName("S", "Alpha");
145 } else if (sMaskMode == kLuminosity_SMaskMode) {
146 sMaskDict->insertName("S", "Luminosity");
147 }
148 sMaskDict->insert("G", new SkPDFObjRef(sMask))->unref();
149
150 SkPDFGraphicState* result = new SkPDFGraphicState;
151 result->fPopulated = true;
152 result->fSMask = true;
153 result->insertName("Type", "ExtGState");
154 result->insert("SMask", sMaskDict.get());
155 result->fResources.push(sMask);
156 sMask->ref();
157
158 if (invert) {
159 SkPDFObject* invertFunction = GetInvertFunction();
160 result->fResources.push(invertFunction);
161 invertFunction->ref();
162 sMaskDict->insert("TR", new SkPDFObjRef(invertFunction))->unref();
163 }
164
165 return result;
166 }
167
168 // static
GetNoSMaskGraphicState()169 SkPDFGraphicState* SkPDFGraphicState::GetNoSMaskGraphicState() {
170 SkAutoMutexAcquire lock(CanonicalPaintsMutex());
171 static SkPDFGraphicState* noSMaskGS = NULL;
172 if (!noSMaskGS) {
173 noSMaskGS = new SkPDFGraphicState;
174 noSMaskGS->fPopulated = true;
175 noSMaskGS->fSMask = true;
176 noSMaskGS->insertName("Type", "ExtGState");
177 noSMaskGS->insertName("SMask", "None");
178 }
179 noSMaskGS->ref();
180 return noSMaskGS;
181 }
182
183 // static
Find(const SkPaint & paint)184 int SkPDFGraphicState::Find(const SkPaint& paint) {
185 CanonicalPaintsMutex().assertHeld();
186 GSCanonicalEntry search(&paint);
187 return CanonicalPaints().find(search);
188 }
189
SkPDFGraphicState()190 SkPDFGraphicState::SkPDFGraphicState()
191 : fPopulated(false),
192 fSMask(false) {
193 }
194
SkPDFGraphicState(const SkPaint & paint)195 SkPDFGraphicState::SkPDFGraphicState(const SkPaint& paint)
196 : fPaint(paint),
197 fPopulated(false),
198 fSMask(false) {
199 }
200
201 // populateDict and operator== have to stay in sync with each other.
populateDict()202 void SkPDFGraphicState::populateDict() {
203 if (!fPopulated) {
204 fPopulated = true;
205 insertName("Type", "ExtGState");
206
207 SkAutoTUnref<SkPDFScalar> alpha(
208 new SkPDFScalar(SkScalarDiv(fPaint.getAlpha(), 0xFF)));
209 insert("CA", alpha.get());
210 insert("ca", alpha.get());
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(fPaint.getStrokeCap() >= 0 && fPaint.getStrokeCap() <= 2);
217 insertInt("LC", fPaint.getStrokeCap());
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(fPaint.getStrokeJoin() >= 0 && fPaint.getStrokeJoin() <= 2);
224 insertInt("LJ", fPaint.getStrokeJoin());
225
226 insertScalar("LW", fPaint.getStrokeWidth());
227 insertScalar("ML", fPaint.getStrokeMiter());
228 insert("SA", new SkPDFBool(true))->unref(); // Auto stroke adjustment.
229
230 SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode;
231 // If asMode fails, default to kSrcOver_Mode.
232 if (fPaint.getXfermode())
233 fPaint.getXfermode()->asMode(&xfermode);
234 // If we don't support the mode, just use kSrcOver_Mode.
235 if (xfermode < 0 || xfermode > SkXfermode::kLastMode ||
236 blend_mode_from_xfermode(xfermode) == NULL) {
237 xfermode = SkXfermode::kSrcOver_Mode;
238 NOT_IMPLEMENTED("unsupported xfermode", false);
239 }
240 insertName("BM", blend_mode_from_xfermode(xfermode));
241 }
242 }
243
244 // We're only interested in some fields of the SkPaint, so we have a custom
245 // operator== function.
operator ==(const SkPDFGraphicState::GSCanonicalEntry & gs) const246 bool SkPDFGraphicState::GSCanonicalEntry::operator==(
247 const SkPDFGraphicState::GSCanonicalEntry& gs) const {
248 const SkPaint* a = fPaint;
249 const SkPaint* b = gs.fPaint;
250 SkASSERT(a != NULL);
251 SkASSERT(b != NULL);
252
253 if (SkColorGetA(a->getColor()) != SkColorGetA(b->getColor()) ||
254 a->getStrokeCap() != b->getStrokeCap() ||
255 a->getStrokeJoin() != b->getStrokeJoin() ||
256 a->getStrokeWidth() != b->getStrokeWidth() ||
257 a->getStrokeMiter() != b->getStrokeMiter()) {
258 return false;
259 }
260
261 SkXfermode::Mode aXfermodeName = SkXfermode::kSrcOver_Mode;
262 SkXfermode* aXfermode = a->getXfermode();
263 if (aXfermode) {
264 aXfermode->asMode(&aXfermodeName);
265 }
266 if (aXfermodeName < 0 || aXfermodeName > SkXfermode::kLastMode ||
267 blend_mode_from_xfermode(aXfermodeName) == NULL) {
268 aXfermodeName = SkXfermode::kSrcOver_Mode;
269 }
270 const char* aXfermodeString = blend_mode_from_xfermode(aXfermodeName);
271 SkASSERT(aXfermodeString != NULL);
272
273 SkXfermode::Mode bXfermodeName = SkXfermode::kSrcOver_Mode;
274 SkXfermode* bXfermode = b->getXfermode();
275 if (bXfermode) {
276 bXfermode->asMode(&bXfermodeName);
277 }
278 if (bXfermodeName < 0 || bXfermodeName > SkXfermode::kLastMode ||
279 blend_mode_from_xfermode(bXfermodeName) == NULL) {
280 bXfermodeName = SkXfermode::kSrcOver_Mode;
281 }
282 const char* bXfermodeString = blend_mode_from_xfermode(bXfermodeName);
283 SkASSERT(bXfermodeString != NULL);
284
285 return strcmp(aXfermodeString, bXfermodeString) == 0;
286 }
287