blob: ec9b0e70b29619b83554399ad11620500638cfa4 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2011 Google Inc.
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
vandebo@chromium.org6112c212011-05-13 03:50:38 +000010#include "SkPDFFormXObject.h"
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000011#include "SkPDFGraphicState.h"
vandebo@chromium.orgf71b2102011-04-04 19:46:31 +000012#include "SkPDFUtils.h"
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000013#include "SkStream.h"
vandebo@chromium.org48543272011-02-08 19:28:07 +000014#include "SkTypes.h"
15
vandebo@chromium.org6112c212011-05-13 03:50:38 +000016static const char* blend_mode_from_xfermode(SkXfermode::Mode mode) {
vandebo@chromium.org48543272011-02-08 19:28:07 +000017 switch (mode) {
18 case SkXfermode::kSrcOver_Mode: return "Normal";
19 case SkXfermode::kMultiply_Mode: return "Multiply";
20 case SkXfermode::kScreen_Mode: return "Screen";
21 case SkXfermode::kOverlay_Mode: return "Overlay";
22 case SkXfermode::kDarken_Mode: return "Darken";
23 case SkXfermode::kLighten_Mode: return "Lighten";
24 case SkXfermode::kColorDodge_Mode: return "ColorDodge";
25 case SkXfermode::kColorBurn_Mode: return "ColorBurn";
26 case SkXfermode::kHardLight_Mode: return "HardLight";
27 case SkXfermode::kSoftLight_Mode: return "SoftLight";
28 case SkXfermode::kDifference_Mode: return "Difference";
29 case SkXfermode::kExclusion_Mode: return "Exclusion";
30
vandebo@chromium.org25adce82011-05-09 08:05:01 +000031 // These are handled in SkPDFDevice::setUpContentEntry.
vandebo@chromium.org48543272011-02-08 19:28:07 +000032 case SkXfermode::kClear_Mode:
33 case SkXfermode::kSrc_Mode:
34 case SkXfermode::kDst_Mode:
35 case SkXfermode::kDstOver_Mode:
36 case SkXfermode::kSrcIn_Mode:
37 case SkXfermode::kDstIn_Mode:
38 case SkXfermode::kSrcOut_Mode:
39 case SkXfermode::kDstOut_Mode:
vandebo@chromium.org6112c212011-05-13 03:50:38 +000040 return "Normal";
41
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +000042 // TODO(vandebo): Figure out if we can support more of these modes.
vandebo@chromium.org48543272011-02-08 19:28:07 +000043 case SkXfermode::kSrcATop_Mode:
44 case SkXfermode::kDstATop_Mode:
45 case SkXfermode::kXor_Mode:
46 case SkXfermode::kPlus_Mode:
47 return NULL;
48 }
49 return NULL;
50}
51
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000052SkPDFGraphicState::~SkPDFGraphicState() {
reed@google.comf6c3ebd2011-07-20 17:20:28 +000053 SkAutoMutexAcquire lock(CanonicalPaintsMutex());
vandebo@chromium.org6112c212011-05-13 03:50:38 +000054 if (!fSMask) {
reed@google.comf6c3ebd2011-07-20 17:20:28 +000055 int index = Find(fPaint);
vandebo@chromium.org6112c212011-05-13 03:50:38 +000056 SkASSERT(index >= 0);
vandebo@chromium.org918352f2011-10-30 19:13:26 +000057 SkASSERT(CanonicalPaints()[index].fGraphicState == this);
reed@google.comf6c3ebd2011-07-20 17:20:28 +000058 CanonicalPaints().removeShuffle(index);
vandebo@chromium.org6112c212011-05-13 03:50:38 +000059 }
60 fResources.unrefAll();
61}
62
63void SkPDFGraphicState::getResources(SkTDArray<SkPDFObject*>* resourceList) {
vandebo@chromium.org421d6442011-07-20 17:39:01 +000064 GetResourcesHelper(&fResources, resourceList);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000065}
66
67void SkPDFGraphicState::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
68 bool indirect) {
69 populateDict();
70 SkPDFDict::emitObject(stream, catalog, indirect);
71}
72
vandebo@chromium.org48543272011-02-08 19:28:07 +000073// static
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000074size_t SkPDFGraphicState::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
75 populateDict();
76 return SkPDFDict::getOutputSize(catalog, indirect);
77}
78
79// static
80SkTDArray<SkPDFGraphicState::GSCanonicalEntry>&
reed@google.comf6c3ebd2011-07-20 17:20:28 +000081SkPDFGraphicState::CanonicalPaints() {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000082 // This initialization is only thread safe with gcc.
83 static SkTDArray<SkPDFGraphicState::GSCanonicalEntry> gCanonicalPaints;
84 return gCanonicalPaints;
85}
86
87// static
digit@google.com1771cbf2012-01-26 21:26:40 +000088SkBaseMutex& SkPDFGraphicState::CanonicalPaintsMutex() {
89 // This initialization is only thread safe with gcc or when
90 // POD-style mutex initialization is used.
91 SK_DECLARE_STATIC_MUTEX(gCanonicalPaintsMutex);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000092 return gCanonicalPaintsMutex;
93}
94
95// static
reed@google.comf6c3ebd2011-07-20 17:20:28 +000096SkPDFGraphicState* SkPDFGraphicState::GetGraphicStateForPaint(
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000097 const SkPaint& paint) {
reed@google.comf6c3ebd2011-07-20 17:20:28 +000098 SkAutoMutexAcquire lock(CanonicalPaintsMutex());
99 int index = Find(paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000100 if (index >= 0) {
reed@google.comf6c3ebd2011-07-20 17:20:28 +0000101 CanonicalPaints()[index].fGraphicState->ref();
102 return CanonicalPaints()[index].fGraphicState;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000103 }
104 GSCanonicalEntry newEntry(new SkPDFGraphicState(paint));
reed@google.comf6c3ebd2011-07-20 17:20:28 +0000105 CanonicalPaints().push(newEntry);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000106 return newEntry.fGraphicState;
107}
108
109// static
vandebo@chromium.org19e3c1e2011-05-25 00:41:30 +0000110SkPDFObject* SkPDFGraphicState::GetInvertFunction() {
111 // This assumes that canonicalPaintsMutex is held.
112 static SkPDFStream* invertFunction = NULL;
113 if (!invertFunction) {
114 // Acrobat crashes if we use a type 0 function, kpdf crashes if we use
115 // a type 2 function, so we use a type 4 function.
116 SkRefPtr<SkPDFArray> domainAndRange = new SkPDFArray;
117 domainAndRange->unref(); // SkRefPtr and new both took a reference.
118 domainAndRange->reserve(2);
reed@google.comc789cf12011-07-20 12:14:33 +0000119 domainAndRange->appendInt(0);
120 domainAndRange->appendInt(1);
vandebo@chromium.org19e3c1e2011-05-25 00:41:30 +0000121
122 static const char psInvert[] = "{1 exch sub}";
123 SkRefPtr<SkMemoryStream> psInvertStream =
124 new SkMemoryStream(&psInvert, strlen(psInvert), true);
125 psInvertStream->unref(); // SkRefPtr and new both took a reference.
126
127 invertFunction = new SkPDFStream(psInvertStream.get());
reed@google.comc789cf12011-07-20 12:14:33 +0000128 invertFunction->insertInt("FunctionType", 4);
vandebo@chromium.org19e3c1e2011-05-25 00:41:30 +0000129 invertFunction->insert("Domain", domainAndRange.get());
130 invertFunction->insert("Range", domainAndRange.get());
131 }
132 return invertFunction;
133}
134
135// static
reed@google.comf6c3ebd2011-07-20 17:20:28 +0000136SkPDFGraphicState* SkPDFGraphicState::GetSMaskGraphicState(
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000137 SkPDFFormXObject* sMask, bool invert) {
138 // The practical chances of using the same mask more than once are unlikely
139 // enough that it's not worth canonicalizing.
reed@google.comf6c3ebd2011-07-20 17:20:28 +0000140 SkAutoMutexAcquire lock(CanonicalPaintsMutex());
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000141
142 SkRefPtr<SkPDFDict> sMaskDict = new SkPDFDict("Mask");
143 sMaskDict->unref(); // SkRefPtr and new both took a reference.
reed@google.comc789cf12011-07-20 12:14:33 +0000144 sMaskDict->insertName("S", "Alpha");
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000145 sMaskDict->insert("G", new SkPDFObjRef(sMask))->unref();
146
147 SkPDFGraphicState* result = new SkPDFGraphicState;
148 result->fPopulated = true;
149 result->fSMask = true;
reed@google.comc789cf12011-07-20 12:14:33 +0000150 result->insertName("Type", "ExtGState");
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000151 result->insert("SMask", sMaskDict.get());
152 result->fResources.push(sMask);
153 sMask->ref();
154
155 if (invert) {
vandebo@chromium.org19e3c1e2011-05-25 00:41:30 +0000156 SkPDFObject* invertFunction = GetInvertFunction();
157 result->fResources.push(invertFunction);
158 invertFunction->ref();
159 sMaskDict->insert("TR", new SkPDFObjRef(invertFunction))->unref();
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000160 }
161
162 return result;
163}
164
165// static
reed@google.comf6c3ebd2011-07-20 17:20:28 +0000166SkPDFGraphicState* SkPDFGraphicState::GetNoSMaskGraphicState() {
167 SkAutoMutexAcquire lock(CanonicalPaintsMutex());
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000168 static SkPDFGraphicState* noSMaskGS = NULL;
169 if (!noSMaskGS) {
170 noSMaskGS = new SkPDFGraphicState;
171 noSMaskGS->fPopulated = true;
172 noSMaskGS->fSMask = true;
reed@google.comc789cf12011-07-20 12:14:33 +0000173 noSMaskGS->insertName("Type", "ExtGState");
174 noSMaskGS->insertName("SMask", "None");
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000175 }
176 noSMaskGS->ref();
177 return noSMaskGS;
178}
179
180// static
reed@google.comf6c3ebd2011-07-20 17:20:28 +0000181int SkPDFGraphicState::Find(const SkPaint& paint) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000182 GSCanonicalEntry search(&paint);
reed@google.comf6c3ebd2011-07-20 17:20:28 +0000183 return CanonicalPaints().find(search);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000184}
185
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000186SkPDFGraphicState::SkPDFGraphicState()
187 : fPopulated(false),
188 fSMask(false) {
189}
190
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000191SkPDFGraphicState::SkPDFGraphicState(const SkPaint& paint)
192 : fPaint(paint),
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000193 fPopulated(false),
194 fSMask(false) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000195}
196
197// populateDict and operator== have to stay in sync with each other.
198void SkPDFGraphicState::populateDict() {
199 if (!fPopulated) {
200 fPopulated = true;
reed@google.comc789cf12011-07-20 12:14:33 +0000201 insertName("Type", "ExtGState");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000202
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000203 SkRefPtr<SkPDFScalar> alpha =
vandebo@chromium.org663515b2012-01-05 18:45:27 +0000204 new SkPDFScalar(SkScalarDiv(fPaint.getAlpha(), 0xFF));
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000205 alpha->unref(); // SkRefPtr and new both took a reference.
206 insert("CA", alpha.get());
207 insert("ca", alpha.get());
208
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000209 SK_COMPILE_ASSERT(SkPaint::kButt_Cap == 0, paint_cap_mismatch);
210 SK_COMPILE_ASSERT(SkPaint::kRound_Cap == 1, paint_cap_mismatch);
211 SK_COMPILE_ASSERT(SkPaint::kSquare_Cap == 2, paint_cap_mismatch);
212 SK_COMPILE_ASSERT(SkPaint::kCapCount == 3, paint_cap_mismatch);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000213 SkASSERT(fPaint.getStrokeCap() >= 0 && fPaint.getStrokeCap() <= 2);
reed@google.comc789cf12011-07-20 12:14:33 +0000214 insertInt("LC", fPaint.getStrokeCap());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000215
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000216 SK_COMPILE_ASSERT(SkPaint::kMiter_Join == 0, paint_join_mismatch);
217 SK_COMPILE_ASSERT(SkPaint::kRound_Join == 1, paint_join_mismatch);
218 SK_COMPILE_ASSERT(SkPaint::kBevel_Join == 2, paint_join_mismatch);
219 SK_COMPILE_ASSERT(SkPaint::kJoinCount == 3, paint_join_mismatch);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000220 SkASSERT(fPaint.getStrokeJoin() >= 0 && fPaint.getStrokeJoin() <= 2);
reed@google.comc789cf12011-07-20 12:14:33 +0000221 insertInt("LJ", fPaint.getStrokeJoin());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000222
reed@google.comc789cf12011-07-20 12:14:33 +0000223 insertScalar("LW", fPaint.getStrokeWidth());
224 insertScalar("ML", fPaint.getStrokeMiter());
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000225 insert("SA", new SkPDFBool(true))->unref(); // Auto stroke adjustment.
vandebo@chromium.org48543272011-02-08 19:28:07 +0000226
227 SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode;
vandebo@chromium.orgf71b2102011-04-04 19:46:31 +0000228 // If asMode fails, default to kSrcOver_Mode.
vandebo@chromium.org48543272011-02-08 19:28:07 +0000229 if (fPaint.getXfermode())
230 fPaint.getXfermode()->asMode(&xfermode);
231 // If we don't support the mode, just use kSrcOver_Mode.
232 if (xfermode < 0 || xfermode > SkXfermode::kLastMode ||
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000233 blend_mode_from_xfermode(xfermode) == NULL) {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000234 xfermode = SkXfermode::kSrcOver_Mode;
vandebo@chromium.orgf71b2102011-04-04 19:46:31 +0000235 NOT_IMPLEMENTED("unsupported xfermode", false);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000236 }
reed@google.comc789cf12011-07-20 12:14:33 +0000237 insertName("BM", blend_mode_from_xfermode(xfermode));
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000238 }
239}
240
241// We're only interested in some fields of the SkPaint, so we have a custom
242// operator== function.
243bool SkPDFGraphicState::GSCanonicalEntry::operator==(
244 const SkPDFGraphicState::GSCanonicalEntry& gs) const {
245 const SkPaint* a = fPaint;
246 const SkPaint* b = gs.fPaint;
247 SkASSERT(a != NULL);
248 SkASSERT(b != NULL);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000249
250 if (SkColorGetA(a->getColor()) != SkColorGetA(b->getColor()) ||
251 a->getStrokeCap() != b->getStrokeCap() ||
252 a->getStrokeJoin() != b->getStrokeJoin() ||
253 a->getStrokeWidth() != b->getStrokeWidth() ||
254 a->getStrokeMiter() != b->getStrokeMiter()) {
255 return false;
256 }
257
vandebo@chromium.org48543272011-02-08 19:28:07 +0000258 SkXfermode::Mode aXfermodeName = SkXfermode::kSrcOver_Mode;
vandebo@chromium.org69d4ca32011-05-09 17:34:19 +0000259 SkXfermode* aXfermode = a->getXfermode();
260 if (aXfermode) {
261 aXfermode->asMode(&aXfermodeName);
262 }
263 if (aXfermodeName < 0 || aXfermodeName > SkXfermode::kLastMode ||
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000264 blend_mode_from_xfermode(aXfermodeName) == NULL) {
vandebo@chromium.org69d4ca32011-05-09 17:34:19 +0000265 aXfermodeName = SkXfermode::kSrcOver_Mode;
266 }
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000267 const char* aXfermodeString = blend_mode_from_xfermode(aXfermodeName);
vandebo@chromium.org69d4ca32011-05-09 17:34:19 +0000268 SkASSERT(aXfermodeString != NULL);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000269
vandebo@chromium.org69d4ca32011-05-09 17:34:19 +0000270 SkXfermode::Mode bXfermodeName = SkXfermode::kSrcOver_Mode;
271 SkXfermode* bXfermode = b->getXfermode();
272 if (bXfermode) {
273 bXfermode->asMode(&bXfermodeName);
274 }
275 if (bXfermodeName < 0 || bXfermodeName > SkXfermode::kLastMode ||
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000276 blend_mode_from_xfermode(bXfermodeName) == NULL) {
vandebo@chromium.org69d4ca32011-05-09 17:34:19 +0000277 bXfermodeName = SkXfermode::kSrcOver_Mode;
278 }
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000279 const char* bXfermodeString = blend_mode_from_xfermode(bXfermodeName);
vandebo@chromium.org69d4ca32011-05-09 17:34:19 +0000280 SkASSERT(bXfermodeString != NULL);
281
282 return strcmp(aXfermodeString, bXfermodeString) == 0;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000283}