• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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 SK_DECLARE_STATIC_MUTEX(gCanonicalPaintsMutex);
92 // static
CanonicalPaintsMutex()93 SkBaseMutex& SkPDFGraphicState::CanonicalPaintsMutex() {
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         // 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
GetSMaskGraphicState(SkPDFFormXObject * sMask,bool invert,SkPDFSMaskMode sMaskMode)137 SkPDFGraphicState* 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
GetNoSMaskGraphicState()170 SkPDFGraphicState* 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
Find(const SkPaint & paint)185 int SkPDFGraphicState::Find(const SkPaint& paint) {
186     CanonicalPaintsMutex().assertHeld();
187     GSCanonicalEntry search(&paint);
188     return CanonicalPaints().find(search);
189 }
190 
SkPDFGraphicState()191 SkPDFGraphicState::SkPDFGraphicState()
192     : fPopulated(false),
193       fSMask(false) {
194 }
195 
SkPDFGraphicState(const SkPaint & paint)196 SkPDFGraphicState::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.
populateDict()203 void 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.
operator ==(const SkPDFGraphicState::GSCanonicalEntry & gs) const247 bool 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