• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "SkPDFFormXObject.h"
18 #include "SkPDFGraphicState.h"
19 #include "SkPDFUtils.h"
20 #include "SkStream.h"
21 #include "SkTypes.h"
22 
blend_mode_from_xfermode(SkXfermode::Mode mode)23 static const char* blend_mode_from_xfermode(SkXfermode::Mode mode) {
24     switch (mode) {
25         case SkXfermode::kSrcOver_Mode:    return "Normal";
26         case SkXfermode::kMultiply_Mode:   return "Multiply";
27         case SkXfermode::kScreen_Mode:     return "Screen";
28         case SkXfermode::kOverlay_Mode:    return "Overlay";
29         case SkXfermode::kDarken_Mode:     return "Darken";
30         case SkXfermode::kLighten_Mode:    return "Lighten";
31         case SkXfermode::kColorDodge_Mode: return "ColorDodge";
32         case SkXfermode::kColorBurn_Mode:  return "ColorBurn";
33         case SkXfermode::kHardLight_Mode:  return "HardLight";
34         case SkXfermode::kSoftLight_Mode:  return "SoftLight";
35         case SkXfermode::kDifference_Mode: return "Difference";
36         case SkXfermode::kExclusion_Mode:  return "Exclusion";
37 
38         // These are handled in SkPDFDevice::setUpContentEntry.
39         case SkXfermode::kClear_Mode:
40         case SkXfermode::kSrc_Mode:
41         case SkXfermode::kDst_Mode:
42         case SkXfermode::kDstOver_Mode:
43         case SkXfermode::kSrcIn_Mode:
44         case SkXfermode::kDstIn_Mode:
45         case SkXfermode::kSrcOut_Mode:
46         case SkXfermode::kDstOut_Mode:
47             return "Normal";
48 
49         // TODO(vandebo) Figure out if we can support more of these modes.
50         case SkXfermode::kSrcATop_Mode:
51         case SkXfermode::kDstATop_Mode:
52         case SkXfermode::kXor_Mode:
53         case SkXfermode::kPlus_Mode:
54             return NULL;
55     }
56     return NULL;
57 }
58 
~SkPDFGraphicState()59 SkPDFGraphicState::~SkPDFGraphicState() {
60     SkAutoMutexAcquire lock(canonicalPaintsMutex());
61     if (!fSMask) {
62         int index = find(fPaint);
63         SkASSERT(index >= 0);
64         canonicalPaints().removeShuffle(index);
65     }
66     fResources.unrefAll();
67 }
68 
getResources(SkTDArray<SkPDFObject * > * resourceList)69 void SkPDFGraphicState::getResources(SkTDArray<SkPDFObject*>* resourceList) {
70     resourceList->setReserve(resourceList->count() + fResources.count());
71     for (int i = 0; i < fResources.count(); i++) {
72         resourceList->push(fResources[i]);
73         fResources[i]->ref();
74         fResources[i]->getResources(resourceList);
75     }
76 }
77 
emitObject(SkWStream * stream,SkPDFCatalog * catalog,bool indirect)78 void SkPDFGraphicState::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
79                                    bool indirect) {
80     populateDict();
81     SkPDFDict::emitObject(stream, catalog, indirect);
82 }
83 
84 // static
getOutputSize(SkPDFCatalog * catalog,bool indirect)85 size_t SkPDFGraphicState::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
86     populateDict();
87     return SkPDFDict::getOutputSize(catalog, indirect);
88 }
89 
90 // static
91 SkTDArray<SkPDFGraphicState::GSCanonicalEntry>&
canonicalPaints()92 SkPDFGraphicState::canonicalPaints() {
93     // This initialization is only thread safe with gcc.
94     static SkTDArray<SkPDFGraphicState::GSCanonicalEntry> gCanonicalPaints;
95     return gCanonicalPaints;
96 }
97 
98 // static
canonicalPaintsMutex()99 SkMutex& SkPDFGraphicState::canonicalPaintsMutex() {
100     // This initialization is only thread safe with gcc.
101     static SkMutex gCanonicalPaintsMutex;
102     return gCanonicalPaintsMutex;
103 }
104 
105 // static
getGraphicStateForPaint(const SkPaint & paint)106 SkPDFGraphicState* SkPDFGraphicState::getGraphicStateForPaint(
107         const SkPaint& paint) {
108     SkAutoMutexAcquire lock(canonicalPaintsMutex());
109     int index = find(paint);
110     if (index >= 0) {
111         canonicalPaints()[index].fGraphicState->ref();
112         return canonicalPaints()[index].fGraphicState;
113     }
114     GSCanonicalEntry newEntry(new SkPDFGraphicState(paint));
115     canonicalPaints().push(newEntry);
116     return newEntry.fGraphicState;
117 }
118 
119 // static
GetInvertFunction()120 SkPDFObject* SkPDFGraphicState::GetInvertFunction() {
121     // This assumes that canonicalPaintsMutex is held.
122     static SkPDFStream* invertFunction = NULL;
123     if (!invertFunction) {
124         // Acrobat crashes if we use a type 0 function, kpdf crashes if we use
125         // a type 2 function, so we use a type 4 function.
126         SkRefPtr<SkPDFArray> domainAndRange = new SkPDFArray;
127         domainAndRange->unref();  // SkRefPtr and new both took a reference.
128         domainAndRange->reserve(2);
129         domainAndRange->append(new SkPDFInt(0))->unref();
130         domainAndRange->append(new SkPDFInt(1))->unref();
131 
132         static const char psInvert[] = "{1 exch sub}";
133         SkRefPtr<SkMemoryStream> psInvertStream =
134             new SkMemoryStream(&psInvert, strlen(psInvert), true);
135         psInvertStream->unref();  // SkRefPtr and new both took a reference.
136 
137         invertFunction = new SkPDFStream(psInvertStream.get());
138         invertFunction->insert("FunctionType", new SkPDFInt(4))->unref();
139         invertFunction->insert("Domain", domainAndRange.get());
140         invertFunction->insert("Range", domainAndRange.get());
141     }
142     return invertFunction;
143 }
144 
145 // static
getSMaskGraphicState(SkPDFFormXObject * sMask,bool invert)146 SkPDFGraphicState* SkPDFGraphicState::getSMaskGraphicState(
147         SkPDFFormXObject* sMask, bool invert) {
148     // The practical chances of using the same mask more than once are unlikely
149     // enough that it's not worth canonicalizing.
150     SkAutoMutexAcquire lock(canonicalPaintsMutex());
151 
152     SkRefPtr<SkPDFDict> sMaskDict = new SkPDFDict("Mask");
153     sMaskDict->unref();  // SkRefPtr and new both took a reference.
154     sMaskDict->insert("S", new SkPDFName("Alpha"))->unref();
155     sMaskDict->insert("G", new SkPDFObjRef(sMask))->unref();
156 
157     SkPDFGraphicState* result = new SkPDFGraphicState;
158     result->fPopulated = true;
159     result->fSMask = true;
160     result->insert("Type", new SkPDFName("ExtGState"))->unref();
161     result->insert("SMask", sMaskDict.get());
162     result->fResources.push(sMask);
163     sMask->ref();
164 
165     if (invert) {
166         SkPDFObject* invertFunction = GetInvertFunction();
167         result->fResources.push(invertFunction);
168         invertFunction->ref();
169         sMaskDict->insert("TR", new SkPDFObjRef(invertFunction))->unref();
170     }
171 
172     return result;
173 }
174 
175 // static
getNoSMaskGraphicState()176 SkPDFGraphicState* SkPDFGraphicState::getNoSMaskGraphicState() {
177     SkAutoMutexAcquire lock(canonicalPaintsMutex());
178     static SkPDFGraphicState* noSMaskGS = NULL;
179     if (!noSMaskGS) {
180         noSMaskGS = new SkPDFGraphicState;
181         noSMaskGS->fPopulated = true;
182         noSMaskGS->fSMask = true;
183         noSMaskGS->insert("Type", new SkPDFName("ExtGState"))->unref();
184         noSMaskGS->insert("SMask", new SkPDFName("None"))->unref();
185     }
186     noSMaskGS->ref();
187     return noSMaskGS;
188 }
189 
190 // static
find(const SkPaint & paint)191 int SkPDFGraphicState::find(const SkPaint& paint) {
192     GSCanonicalEntry search(&paint);
193     return canonicalPaints().find(search);
194 }
195 
SkPDFGraphicState()196 SkPDFGraphicState::SkPDFGraphicState()
197     : fPopulated(false),
198       fSMask(false) {
199 }
200 
SkPDFGraphicState(const SkPaint & paint)201 SkPDFGraphicState::SkPDFGraphicState(const SkPaint& paint)
202     : fPaint(paint),
203       fPopulated(false),
204       fSMask(false) {
205 }
206 
207 // populateDict and operator== have to stay in sync with each other.
populateDict()208 void SkPDFGraphicState::populateDict() {
209     if (!fPopulated) {
210         fPopulated = true;
211         insert("Type", new SkPDFName("ExtGState"))->unref();
212 
213         SkRefPtr<SkPDFScalar> alpha =
214             new SkPDFScalar(fPaint.getAlpha() * SkScalarInvert(0xFF));
215         alpha->unref();  // SkRefPtr and new both took a reference.
216         insert("CA", alpha.get());
217         insert("ca", alpha.get());
218 
219         SK_COMPILE_ASSERT(SkPaint::kButt_Cap == 0, paint_cap_mismatch);
220         SK_COMPILE_ASSERT(SkPaint::kRound_Cap == 1, paint_cap_mismatch);
221         SK_COMPILE_ASSERT(SkPaint::kSquare_Cap == 2, paint_cap_mismatch);
222         SK_COMPILE_ASSERT(SkPaint::kCapCount == 3, paint_cap_mismatch);
223         SkASSERT(fPaint.getStrokeCap() >= 0 && fPaint.getStrokeCap() <= 2);
224         insert("LC", new SkPDFInt(fPaint.getStrokeCap()))->unref();
225 
226         SK_COMPILE_ASSERT(SkPaint::kMiter_Join == 0, paint_join_mismatch);
227         SK_COMPILE_ASSERT(SkPaint::kRound_Join == 1, paint_join_mismatch);
228         SK_COMPILE_ASSERT(SkPaint::kBevel_Join == 2, paint_join_mismatch);
229         SK_COMPILE_ASSERT(SkPaint::kJoinCount == 3, paint_join_mismatch);
230         SkASSERT(fPaint.getStrokeJoin() >= 0 && fPaint.getStrokeJoin() <= 2);
231         insert("LJ", new SkPDFInt(fPaint.getStrokeJoin()))->unref();
232 
233         insert("LW", new SkPDFScalar(fPaint.getStrokeWidth()))->unref();
234         insert("ML", new SkPDFScalar(fPaint.getStrokeMiter()))->unref();
235         insert("SA", new SkPDFBool(true))->unref();  // Auto stroke adjustment.
236 
237         SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode;
238         // If asMode fails, default to kSrcOver_Mode.
239         if (fPaint.getXfermode())
240             fPaint.getXfermode()->asMode(&xfermode);
241         // If we don't support the mode, just use kSrcOver_Mode.
242         if (xfermode < 0 || xfermode > SkXfermode::kLastMode ||
243                 blend_mode_from_xfermode(xfermode) == NULL) {
244             xfermode = SkXfermode::kSrcOver_Mode;
245             NOT_IMPLEMENTED("unsupported xfermode", false);
246         }
247         insert("BM",
248                new SkPDFName(blend_mode_from_xfermode(xfermode)))->unref();
249     }
250 }
251 
252 // We're only interested in some fields of the SkPaint, so we have a custom
253 // operator== function.
operator ==(const SkPDFGraphicState::GSCanonicalEntry & gs) const254 bool SkPDFGraphicState::GSCanonicalEntry::operator==(
255         const SkPDFGraphicState::GSCanonicalEntry& gs) const {
256     const SkPaint* a = fPaint;
257     const SkPaint* b = gs.fPaint;
258     SkASSERT(a != NULL);
259     SkASSERT(b != NULL);
260 
261     if (SkColorGetA(a->getColor()) != SkColorGetA(b->getColor()) ||
262            a->getStrokeCap() != b->getStrokeCap() ||
263            a->getStrokeJoin() != b->getStrokeJoin() ||
264            a->getStrokeWidth() != b->getStrokeWidth() ||
265            a->getStrokeMiter() != b->getStrokeMiter()) {
266         return false;
267     }
268 
269     SkXfermode::Mode aXfermodeName = SkXfermode::kSrcOver_Mode;
270     SkXfermode* aXfermode = a->getXfermode();
271     if (aXfermode) {
272         aXfermode->asMode(&aXfermodeName);
273     }
274     if (aXfermodeName < 0 || aXfermodeName > SkXfermode::kLastMode ||
275             blend_mode_from_xfermode(aXfermodeName) == NULL) {
276         aXfermodeName = SkXfermode::kSrcOver_Mode;
277     }
278     const char* aXfermodeString = blend_mode_from_xfermode(aXfermodeName);
279     SkASSERT(aXfermodeString != NULL);
280 
281     SkXfermode::Mode bXfermodeName = SkXfermode::kSrcOver_Mode;
282     SkXfermode* bXfermode = b->getXfermode();
283     if (bXfermode) {
284         bXfermode->asMode(&bXfermodeName);
285     }
286     if (bXfermodeName < 0 || bXfermodeName > SkXfermode::kLastMode ||
287             blend_mode_from_xfermode(bXfermodeName) == NULL) {
288         bXfermodeName = SkXfermode::kSrcOver_Mode;
289     }
290     const char* bXfermodeString = blend_mode_from_xfermode(bXfermodeName);
291     SkASSERT(bXfermodeString != NULL);
292 
293     return strcmp(aXfermodeString, bXfermodeString) == 0;
294 }
295