|
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 |
|
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 |
|
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 |
|
66 void SkPDFGraphicState::getResources( |
|
67 const SkTSet<SkPDFObject*>& knownResourceObjects, |
|
68 SkTSet<SkPDFObject*>* newResourceObjects) { |
|
69 GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects); |
|
70 } |
|
71 |
|
72 void SkPDFGraphicState::emitObject(SkWStream* stream, SkPDFCatalog* catalog, |
|
73 bool indirect) { |
|
74 populateDict(); |
|
75 SkPDFDict::emitObject(stream, catalog, indirect); |
|
76 } |
|
77 |
|
78 // static |
|
79 size_t SkPDFGraphicState::getOutputSize(SkPDFCatalog* catalog, bool indirect) { |
|
80 populateDict(); |
|
81 return SkPDFDict::getOutputSize(catalog, indirect); |
|
82 } |
|
83 |
|
84 // static |
|
85 SkTDArray<SkPDFGraphicState::GSCanonicalEntry>& |
|
86 SkPDFGraphicState::CanonicalPaints() { |
|
87 // This initialization is only thread safe with gcc. |
|
88 static SkTDArray<SkPDFGraphicState::GSCanonicalEntry> gCanonicalPaints; |
|
89 return gCanonicalPaints; |
|
90 } |
|
91 |
|
92 // static |
|
93 SkBaseMutex& 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 |
|
101 SkPDFGraphicState* 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 |
|
115 SkPDFObject* 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 |
|
139 SkPDFGraphicState* 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 |
|
172 SkPDFGraphicState* 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 |
|
187 int SkPDFGraphicState::Find(const SkPaint& paint) { |
|
188 GSCanonicalEntry search(&paint); |
|
189 return CanonicalPaints().find(search); |
|
190 } |
|
191 |
|
192 SkPDFGraphicState::SkPDFGraphicState() |
|
193 : fPopulated(false), |
|
194 fSMask(false) { |
|
195 } |
|
196 |
|
197 SkPDFGraphicState::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. |
|
204 void 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. |
|
248 bool 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 } |