blob: 43d22f308eb930ecc3f04464f1743367548563d3 [file] [log] [blame]
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2011 Google Inc.
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +00006 */
7
vandebo@chromium.org6112c212011-05-13 03:50:38 +00008#include "SkPDFFormXObject.h"
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +00009#include "SkPDFGraphicState.h"
vandebo@chromium.orgf71b2102011-04-04 19:46:31 +000010#include "SkPDFUtils.h"
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000011#include "SkStream.h"
vandebo@chromium.org48543272011-02-08 19:28:07 +000012#include "SkTypes.h"
13
vandebo@chromium.org6112c212011-05-13 03:50:38 +000014static const char* blend_mode_from_xfermode(SkXfermode::Mode mode) {
vandebo@chromium.org48543272011-02-08 19:28:07 +000015 switch (mode) {
16 case SkXfermode::kSrcOver_Mode: return "Normal";
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +000017 // kModulate is not really like multipy but similar most of the time.
skia.committer@gmail.com64334352013-03-06 07:01:46 +000018 case SkXfermode::kModulate_Mode:
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +000019 case SkXfermode::kMultiply_Mode: return "Multiply";
vandebo@chromium.org48543272011-02-08 19:28:07 +000020 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";
commit-bot@chromium.orgb24f8932013-03-05 16:23:59 +000030 case SkXfermode::kHue_Mode: return "Hue";
31 case SkXfermode::kSaturation_Mode: return "Saturation";
32 case SkXfermode::kColor_Mode: return "Color";
33 case SkXfermode::kLuminosity_Mode: return "Luminosity";
vandebo@chromium.org48543272011-02-08 19:28:07 +000034
vandebo@chromium.org25adce82011-05-09 08:05:01 +000035 // These are handled in SkPDFDevice::setUpContentEntry.
vandebo@chromium.org48543272011-02-08 19:28:07 +000036 case SkXfermode::kClear_Mode:
37 case SkXfermode::kSrc_Mode:
38 case SkXfermode::kDst_Mode:
39 case SkXfermode::kDstOver_Mode:
40 case SkXfermode::kSrcIn_Mode:
41 case SkXfermode::kDstIn_Mode:
42 case SkXfermode::kSrcOut_Mode:
43 case SkXfermode::kDstOut_Mode:
vandebo@chromium.org6112c212011-05-13 03:50:38 +000044 return "Normal";
45
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +000046 // TODO(vandebo): Figure out if we can support more of these modes.
vandebo@chromium.org48543272011-02-08 19:28:07 +000047 case SkXfermode::kSrcATop_Mode:
48 case SkXfermode::kDstATop_Mode:
49 case SkXfermode::kXor_Mode:
50 case SkXfermode::kPlus_Mode:
51 return NULL;
52 }
53 return NULL;
54}
55
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000056SkPDFGraphicState::~SkPDFGraphicState() {
reed@google.comf6c3ebd2011-07-20 17:20:28 +000057 SkAutoMutexAcquire lock(CanonicalPaintsMutex());
vandebo@chromium.org6112c212011-05-13 03:50:38 +000058 if (!fSMask) {
reed@google.comf6c3ebd2011-07-20 17:20:28 +000059 int index = Find(fPaint);
vandebo@chromium.org6112c212011-05-13 03:50:38 +000060 SkASSERT(index >= 0);
vandebo@chromium.org918352f2011-10-30 19:13:26 +000061 SkASSERT(CanonicalPaints()[index].fGraphicState == this);
reed@google.comf6c3ebd2011-07-20 17:20:28 +000062 CanonicalPaints().removeShuffle(index);
vandebo@chromium.org6112c212011-05-13 03:50:38 +000063 }
64 fResources.unrefAll();
65}
66
edisonn@google.com6addb192013-04-02 15:33:08 +000067void SkPDFGraphicState::getResources(
68 const SkTSet<SkPDFObject*>& knownResourceObjects,
69 SkTSet<SkPDFObject*>* newResourceObjects) {
70 GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000071}
72
73void SkPDFGraphicState::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
74 bool indirect) {
75 populateDict();
76 SkPDFDict::emitObject(stream, catalog, indirect);
77}
78
vandebo@chromium.org48543272011-02-08 19:28:07 +000079// static
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000080size_t SkPDFGraphicState::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
81 populateDict();
82 return SkPDFDict::getOutputSize(catalog, indirect);
83}
84
85// static
86SkTDArray<SkPDFGraphicState::GSCanonicalEntry>&
reed@google.comf6c3ebd2011-07-20 17:20:28 +000087SkPDFGraphicState::CanonicalPaints() {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000088 // This initialization is only thread safe with gcc.
89 static SkTDArray<SkPDFGraphicState::GSCanonicalEntry> gCanonicalPaints;
90 return gCanonicalPaints;
91}
92
93// static
digit@google.com1771cbf2012-01-26 21:26:40 +000094SkBaseMutex& SkPDFGraphicState::CanonicalPaintsMutex() {
95 // This initialization is only thread safe with gcc or when
96 // POD-style mutex initialization is used.
97 SK_DECLARE_STATIC_MUTEX(gCanonicalPaintsMutex);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000098 return gCanonicalPaintsMutex;
99}
100
101// static
reed@google.comf6c3ebd2011-07-20 17:20:28 +0000102SkPDFGraphicState* SkPDFGraphicState::GetGraphicStateForPaint(
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000103 const SkPaint& paint) {
reed@google.comf6c3ebd2011-07-20 17:20:28 +0000104 SkAutoMutexAcquire lock(CanonicalPaintsMutex());
105 int index = Find(paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000106 if (index >= 0) {
reed@google.comf6c3ebd2011-07-20 17:20:28 +0000107 CanonicalPaints()[index].fGraphicState->ref();
108 return CanonicalPaints()[index].fGraphicState;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000109 }
110 GSCanonicalEntry newEntry(new SkPDFGraphicState(paint));
reed@google.comf6c3ebd2011-07-20 17:20:28 +0000111 CanonicalPaints().push(newEntry);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000112 return newEntry.fGraphicState;
113}
114
115// static
vandebo@chromium.org19e3c1e2011-05-25 00:41:30 +0000116SkPDFObject* SkPDFGraphicState::GetInvertFunction() {
117 // This assumes that canonicalPaintsMutex is held.
118 static SkPDFStream* invertFunction = NULL;
119 if (!invertFunction) {
120 // Acrobat crashes if we use a type 0 function, kpdf crashes if we use
121 // a type 2 function, so we use a type 4 function.
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000122 SkAutoTUnref<SkPDFArray> domainAndRange(new SkPDFArray);
vandebo@chromium.org19e3c1e2011-05-25 00:41:30 +0000123 domainAndRange->reserve(2);
reed@google.comc789cf12011-07-20 12:14:33 +0000124 domainAndRange->appendInt(0);
125 domainAndRange->appendInt(1);
vandebo@chromium.org19e3c1e2011-05-25 00:41:30 +0000126
127 static const char psInvert[] = "{1 exch sub}";
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000128 SkAutoTUnref<SkMemoryStream> psInvertStream(
129 new SkMemoryStream(&psInvert, strlen(psInvert), true));
vandebo@chromium.org19e3c1e2011-05-25 00:41:30 +0000130
131 invertFunction = new SkPDFStream(psInvertStream.get());
reed@google.comc789cf12011-07-20 12:14:33 +0000132 invertFunction->insertInt("FunctionType", 4);
vandebo@chromium.org19e3c1e2011-05-25 00:41:30 +0000133 invertFunction->insert("Domain", domainAndRange.get());
134 invertFunction->insert("Range", domainAndRange.get());
135 }
136 return invertFunction;
137}
138
139// static
reed@google.comf6c3ebd2011-07-20 17:20:28 +0000140SkPDFGraphicState* SkPDFGraphicState::GetSMaskGraphicState(
commit-bot@chromium.org93a2e212013-07-23 23:16:03 +0000141 SkPDFFormXObject* sMask, bool invert, SkPDFSMaskMode sMaskMode) {
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000142 // The practical chances of using the same mask more than once are unlikely
143 // enough that it's not worth canonicalizing.
reed@google.comf6c3ebd2011-07-20 17:20:28 +0000144 SkAutoMutexAcquire lock(CanonicalPaintsMutex());
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000145
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000146 SkAutoTUnref<SkPDFDict> sMaskDict(new SkPDFDict("Mask"));
commit-bot@chromium.org93a2e212013-07-23 23:16:03 +0000147 if (sMaskMode == kAlpha_SMaskMode) {
148 sMaskDict->insertName("S", "Alpha");
149 } else if (sMaskMode == kLuminosity_SMaskMode) {
150 sMaskDict->insertName("S", "Luminosity");
151 }
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000152 sMaskDict->insert("G", new SkPDFObjRef(sMask))->unref();
153
154 SkPDFGraphicState* result = new SkPDFGraphicState;
155 result->fPopulated = true;
156 result->fSMask = true;
reed@google.comc789cf12011-07-20 12:14:33 +0000157 result->insertName("Type", "ExtGState");
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000158 result->insert("SMask", sMaskDict.get());
159 result->fResources.push(sMask);
160 sMask->ref();
161
162 if (invert) {
vandebo@chromium.org19e3c1e2011-05-25 00:41:30 +0000163 SkPDFObject* invertFunction = GetInvertFunction();
164 result->fResources.push(invertFunction);
165 invertFunction->ref();
166 sMaskDict->insert("TR", new SkPDFObjRef(invertFunction))->unref();
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000167 }
168
169 return result;
170}
171
172// static
reed@google.comf6c3ebd2011-07-20 17:20:28 +0000173SkPDFGraphicState* SkPDFGraphicState::GetNoSMaskGraphicState() {
174 SkAutoMutexAcquire lock(CanonicalPaintsMutex());
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000175 static SkPDFGraphicState* noSMaskGS = NULL;
176 if (!noSMaskGS) {
177 noSMaskGS = new SkPDFGraphicState;
178 noSMaskGS->fPopulated = true;
179 noSMaskGS->fSMask = true;
reed@google.comc789cf12011-07-20 12:14:33 +0000180 noSMaskGS->insertName("Type", "ExtGState");
181 noSMaskGS->insertName("SMask", "None");
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000182 }
183 noSMaskGS->ref();
184 return noSMaskGS;
185}
186
187// static
reed@google.comf6c3ebd2011-07-20 17:20:28 +0000188int SkPDFGraphicState::Find(const SkPaint& paint) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000189 GSCanonicalEntry search(&paint);
reed@google.comf6c3ebd2011-07-20 17:20:28 +0000190 return CanonicalPaints().find(search);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000191}
192
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000193SkPDFGraphicState::SkPDFGraphicState()
194 : fPopulated(false),
195 fSMask(false) {
196}
197
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000198SkPDFGraphicState::SkPDFGraphicState(const SkPaint& paint)
199 : fPaint(paint),
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000200 fPopulated(false),
201 fSMask(false) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000202}
203
204// populateDict and operator== have to stay in sync with each other.
205void SkPDFGraphicState::populateDict() {
206 if (!fPopulated) {
207 fPopulated = true;
reed@google.comc789cf12011-07-20 12:14:33 +0000208 insertName("Type", "ExtGState");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000209
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000210 SkAutoTUnref<SkPDFScalar> alpha(
211 new SkPDFScalar(SkScalarDiv(fPaint.getAlpha(), 0xFF)));
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000212 insert("CA", alpha.get());
213 insert("ca", alpha.get());
214
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000215 SK_COMPILE_ASSERT(SkPaint::kButt_Cap == 0, paint_cap_mismatch);
216 SK_COMPILE_ASSERT(SkPaint::kRound_Cap == 1, paint_cap_mismatch);
217 SK_COMPILE_ASSERT(SkPaint::kSquare_Cap == 2, paint_cap_mismatch);
218 SK_COMPILE_ASSERT(SkPaint::kCapCount == 3, paint_cap_mismatch);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000219 SkASSERT(fPaint.getStrokeCap() >= 0 && fPaint.getStrokeCap() <= 2);
reed@google.comc789cf12011-07-20 12:14:33 +0000220 insertInt("LC", fPaint.getStrokeCap());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000221
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000222 SK_COMPILE_ASSERT(SkPaint::kMiter_Join == 0, paint_join_mismatch);
223 SK_COMPILE_ASSERT(SkPaint::kRound_Join == 1, paint_join_mismatch);
224 SK_COMPILE_ASSERT(SkPaint::kBevel_Join == 2, paint_join_mismatch);
225 SK_COMPILE_ASSERT(SkPaint::kJoinCount == 3, paint_join_mismatch);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000226 SkASSERT(fPaint.getStrokeJoin() >= 0 && fPaint.getStrokeJoin() <= 2);
reed@google.comc789cf12011-07-20 12:14:33 +0000227 insertInt("LJ", fPaint.getStrokeJoin());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000228
reed@google.comc789cf12011-07-20 12:14:33 +0000229 insertScalar("LW", fPaint.getStrokeWidth());
230 insertScalar("ML", fPaint.getStrokeMiter());
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000231 insert("SA", new SkPDFBool(true))->unref(); // Auto stroke adjustment.
vandebo@chromium.org48543272011-02-08 19:28:07 +0000232
233 SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode;
vandebo@chromium.orgf71b2102011-04-04 19:46:31 +0000234 // If asMode fails, default to kSrcOver_Mode.
vandebo@chromium.org48543272011-02-08 19:28:07 +0000235 if (fPaint.getXfermode())
236 fPaint.getXfermode()->asMode(&xfermode);
237 // If we don't support the mode, just use kSrcOver_Mode.
238 if (xfermode < 0 || xfermode > SkXfermode::kLastMode ||
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000239 blend_mode_from_xfermode(xfermode) == NULL) {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000240 xfermode = SkXfermode::kSrcOver_Mode;
vandebo@chromium.orgf71b2102011-04-04 19:46:31 +0000241 NOT_IMPLEMENTED("unsupported xfermode", false);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000242 }
reed@google.comc789cf12011-07-20 12:14:33 +0000243 insertName("BM", blend_mode_from_xfermode(xfermode));
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000244 }
245}
246
247// We're only interested in some fields of the SkPaint, so we have a custom
248// operator== function.
249bool SkPDFGraphicState::GSCanonicalEntry::operator==(
250 const SkPDFGraphicState::GSCanonicalEntry& gs) const {
251 const SkPaint* a = fPaint;
252 const SkPaint* b = gs.fPaint;
253 SkASSERT(a != NULL);
254 SkASSERT(b != NULL);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000255
256 if (SkColorGetA(a->getColor()) != SkColorGetA(b->getColor()) ||
257 a->getStrokeCap() != b->getStrokeCap() ||
258 a->getStrokeJoin() != b->getStrokeJoin() ||
259 a->getStrokeWidth() != b->getStrokeWidth() ||
260 a->getStrokeMiter() != b->getStrokeMiter()) {
261 return false;
262 }
263
vandebo@chromium.org48543272011-02-08 19:28:07 +0000264 SkXfermode::Mode aXfermodeName = SkXfermode::kSrcOver_Mode;
vandebo@chromium.org69d4ca32011-05-09 17:34:19 +0000265 SkXfermode* aXfermode = a->getXfermode();
266 if (aXfermode) {
267 aXfermode->asMode(&aXfermodeName);
268 }
269 if (aXfermodeName < 0 || aXfermodeName > SkXfermode::kLastMode ||
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000270 blend_mode_from_xfermode(aXfermodeName) == NULL) {
vandebo@chromium.org69d4ca32011-05-09 17:34:19 +0000271 aXfermodeName = SkXfermode::kSrcOver_Mode;
272 }
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000273 const char* aXfermodeString = blend_mode_from_xfermode(aXfermodeName);
vandebo@chromium.org69d4ca32011-05-09 17:34:19 +0000274 SkASSERT(aXfermodeString != NULL);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000275
vandebo@chromium.org69d4ca32011-05-09 17:34:19 +0000276 SkXfermode::Mode bXfermodeName = SkXfermode::kSrcOver_Mode;
277 SkXfermode* bXfermode = b->getXfermode();
278 if (bXfermode) {
279 bXfermode->asMode(&bXfermodeName);
280 }
281 if (bXfermodeName < 0 || bXfermodeName > SkXfermode::kLastMode ||
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000282 blend_mode_from_xfermode(bXfermodeName) == NULL) {
vandebo@chromium.org69d4ca32011-05-09 17:34:19 +0000283 bXfermodeName = SkXfermode::kSrcOver_Mode;
284 }
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000285 const char* bXfermodeString = blend_mode_from_xfermode(bXfermodeName);
vandebo@chromium.org69d4ca32011-05-09 17:34:19 +0000286 SkASSERT(bXfermodeString != NULL);
287
288 return strcmp(aXfermodeString, bXfermodeString) == 0;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000289}