blob: 0e6e23049ca7a022e2a7e6d81931805942aabef2 [file] [log] [blame]
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +00001/*
vandebo@chromium.org2a22e102011-01-25 21:01:34 +00002 * Copyright (C) 2011 Google Inc.
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +00003 *
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
vandebo@chromium.org6112c212011-05-13 03:50:38 +000017#include "SkPDFFormXObject.h"
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000018#include "SkPDFGraphicState.h"
vandebo@chromium.orgf71b2102011-04-04 19:46:31 +000019#include "SkPDFUtils.h"
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000020#include "SkStream.h"
vandebo@chromium.org48543272011-02-08 19:28:07 +000021#include "SkTypes.h"
22
vandebo@chromium.org6112c212011-05-13 03:50:38 +000023static const char* blend_mode_from_xfermode(SkXfermode::Mode mode) {
vandebo@chromium.org48543272011-02-08 19:28:07 +000024 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
vandebo@chromium.org25adce82011-05-09 08:05:01 +000038 // These are handled in SkPDFDevice::setUpContentEntry.
vandebo@chromium.org48543272011-02-08 19:28:07 +000039 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:
vandebo@chromium.org6112c212011-05-13 03:50:38 +000047 return "Normal";
48
49 // TODO(vandebo) Figure out if we can support more of these modes.
vandebo@chromium.org48543272011-02-08 19:28:07 +000050 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
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000059SkPDFGraphicState::~SkPDFGraphicState() {
reed@google.comf6c3ebd2011-07-20 17:20:28 +000060 SkAutoMutexAcquire lock(CanonicalPaintsMutex());
vandebo@chromium.org6112c212011-05-13 03:50:38 +000061 if (!fSMask) {
reed@google.comf6c3ebd2011-07-20 17:20:28 +000062 int index = Find(fPaint);
vandebo@chromium.org6112c212011-05-13 03:50:38 +000063 SkASSERT(index >= 0);
reed@google.comf6c3ebd2011-07-20 17:20:28 +000064 CanonicalPaints().removeShuffle(index);
vandebo@chromium.org6112c212011-05-13 03:50:38 +000065 }
66 fResources.unrefAll();
67}
68
69void SkPDFGraphicState::getResources(SkTDArray<SkPDFObject*>* resourceList) {
vandebo@chromium.org421d6442011-07-20 17:39:01 +000070 GetResourcesHelper(&fResources, resourceList);
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
reed@google.comf6c3ebd2011-07-20 17:20:28 +000094SkMutex& SkPDFGraphicState::CanonicalPaintsMutex() {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000095 // This initialization is only thread safe with gcc.
96 static SkMutex gCanonicalPaintsMutex;
97 return gCanonicalPaintsMutex;
98}
99
100// static
reed@google.comf6c3ebd2011-07-20 17:20:28 +0000101SkPDFGraphicState* SkPDFGraphicState::GetGraphicStateForPaint(
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000102 const SkPaint& paint) {
reed@google.comf6c3ebd2011-07-20 17:20:28 +0000103 SkAutoMutexAcquire lock(CanonicalPaintsMutex());
104 int index = Find(paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000105 if (index >= 0) {
reed@google.comf6c3ebd2011-07-20 17:20:28 +0000106 CanonicalPaints()[index].fGraphicState->ref();
107 return CanonicalPaints()[index].fGraphicState;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000108 }
109 GSCanonicalEntry newEntry(new SkPDFGraphicState(paint));
reed@google.comf6c3ebd2011-07-20 17:20:28 +0000110 CanonicalPaints().push(newEntry);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000111 return newEntry.fGraphicState;
112}
113
114// static
vandebo@chromium.org19e3c1e2011-05-25 00:41:30 +0000115SkPDFObject* 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 SkRefPtr<SkPDFArray> domainAndRange = new SkPDFArray;
122 domainAndRange->unref(); // SkRefPtr and new both took a reference.
123 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}";
128 SkRefPtr<SkMemoryStream> psInvertStream =
129 new SkMemoryStream(&psInvert, strlen(psInvert), true);
130 psInvertStream->unref(); // SkRefPtr and new both took a reference.
131
132 invertFunction = new SkPDFStream(psInvertStream.get());
reed@google.comc789cf12011-07-20 12:14:33 +0000133 invertFunction->insertInt("FunctionType", 4);
vandebo@chromium.org19e3c1e2011-05-25 00:41:30 +0000134 invertFunction->insert("Domain", domainAndRange.get());
135 invertFunction->insert("Range", domainAndRange.get());
136 }
137 return invertFunction;
138}
139
140// static
reed@google.comf6c3ebd2011-07-20 17:20:28 +0000141SkPDFGraphicState* SkPDFGraphicState::GetSMaskGraphicState(
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000142 SkPDFFormXObject* sMask, bool invert) {
143 // The practical chances of using the same mask more than once are unlikely
144 // enough that it's not worth canonicalizing.
reed@google.comf6c3ebd2011-07-20 17:20:28 +0000145 SkAutoMutexAcquire lock(CanonicalPaintsMutex());
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000146
147 SkRefPtr<SkPDFDict> sMaskDict = new SkPDFDict("Mask");
148 sMaskDict->unref(); // SkRefPtr and new both took a reference.
reed@google.comc789cf12011-07-20 12:14:33 +0000149 sMaskDict->insertName("S", "Alpha");
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000150 sMaskDict->insert("G", new SkPDFObjRef(sMask))->unref();
151
152 SkPDFGraphicState* result = new SkPDFGraphicState;
153 result->fPopulated = true;
154 result->fSMask = true;
reed@google.comc789cf12011-07-20 12:14:33 +0000155 result->insertName("Type", "ExtGState");
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000156 result->insert("SMask", sMaskDict.get());
157 result->fResources.push(sMask);
158 sMask->ref();
159
160 if (invert) {
vandebo@chromium.org19e3c1e2011-05-25 00:41:30 +0000161 SkPDFObject* invertFunction = GetInvertFunction();
162 result->fResources.push(invertFunction);
163 invertFunction->ref();
164 sMaskDict->insert("TR", new SkPDFObjRef(invertFunction))->unref();
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000165 }
166
167 return result;
168}
169
170// static
reed@google.comf6c3ebd2011-07-20 17:20:28 +0000171SkPDFGraphicState* SkPDFGraphicState::GetNoSMaskGraphicState() {
172 SkAutoMutexAcquire lock(CanonicalPaintsMutex());
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000173 static SkPDFGraphicState* noSMaskGS = NULL;
174 if (!noSMaskGS) {
175 noSMaskGS = new SkPDFGraphicState;
176 noSMaskGS->fPopulated = true;
177 noSMaskGS->fSMask = true;
reed@google.comc789cf12011-07-20 12:14:33 +0000178 noSMaskGS->insertName("Type", "ExtGState");
179 noSMaskGS->insertName("SMask", "None");
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000180 }
181 noSMaskGS->ref();
182 return noSMaskGS;
183}
184
185// static
reed@google.comf6c3ebd2011-07-20 17:20:28 +0000186int SkPDFGraphicState::Find(const SkPaint& paint) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000187 GSCanonicalEntry search(&paint);
reed@google.comf6c3ebd2011-07-20 17:20:28 +0000188 return CanonicalPaints().find(search);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000189}
190
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000191SkPDFGraphicState::SkPDFGraphicState()
192 : fPopulated(false),
193 fSMask(false) {
194}
195
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000196SkPDFGraphicState::SkPDFGraphicState(const SkPaint& paint)
197 : fPaint(paint),
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000198 fPopulated(false),
199 fSMask(false) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000200}
201
202// populateDict and operator== have to stay in sync with each other.
203void SkPDFGraphicState::populateDict() {
204 if (!fPopulated) {
205 fPopulated = true;
reed@google.comc789cf12011-07-20 12:14:33 +0000206 insertName("Type", "ExtGState");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000207
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000208 SkRefPtr<SkPDFScalar> alpha =
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000209 new SkPDFScalar(fPaint.getAlpha() * SkScalarInvert(0xFF));
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000210 alpha->unref(); // SkRefPtr and new both took a reference.
211 insert("CA", alpha.get());
212 insert("ca", alpha.get());
213
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000214 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);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000218 SkASSERT(fPaint.getStrokeCap() >= 0 && fPaint.getStrokeCap() <= 2);
reed@google.comc789cf12011-07-20 12:14:33 +0000219 insertInt("LC", fPaint.getStrokeCap());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000220
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000221 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);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000225 SkASSERT(fPaint.getStrokeJoin() >= 0 && fPaint.getStrokeJoin() <= 2);
reed@google.comc789cf12011-07-20 12:14:33 +0000226 insertInt("LJ", fPaint.getStrokeJoin());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000227
reed@google.comc789cf12011-07-20 12:14:33 +0000228 insertScalar("LW", fPaint.getStrokeWidth());
229 insertScalar("ML", fPaint.getStrokeMiter());
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000230 insert("SA", new SkPDFBool(true))->unref(); // Auto stroke adjustment.
vandebo@chromium.org48543272011-02-08 19:28:07 +0000231
232 SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode;
vandebo@chromium.orgf71b2102011-04-04 19:46:31 +0000233 // If asMode fails, default to kSrcOver_Mode.
vandebo@chromium.org48543272011-02-08 19:28:07 +0000234 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 ||
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000238 blend_mode_from_xfermode(xfermode) == NULL) {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000239 xfermode = SkXfermode::kSrcOver_Mode;
vandebo@chromium.orgf71b2102011-04-04 19:46:31 +0000240 NOT_IMPLEMENTED("unsupported xfermode", false);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000241 }
reed@google.comc789cf12011-07-20 12:14:33 +0000242 insertName("BM", blend_mode_from_xfermode(xfermode));
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000243 }
244}
245
246// We're only interested in some fields of the SkPaint, so we have a custom
247// operator== function.
248bool 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);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000254
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
vandebo@chromium.org48543272011-02-08 19:28:07 +0000263 SkXfermode::Mode aXfermodeName = SkXfermode::kSrcOver_Mode;
vandebo@chromium.org69d4ca32011-05-09 17:34:19 +0000264 SkXfermode* aXfermode = a->getXfermode();
265 if (aXfermode) {
266 aXfermode->asMode(&aXfermodeName);
267 }
268 if (aXfermodeName < 0 || aXfermodeName > SkXfermode::kLastMode ||
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000269 blend_mode_from_xfermode(aXfermodeName) == NULL) {
vandebo@chromium.org69d4ca32011-05-09 17:34:19 +0000270 aXfermodeName = SkXfermode::kSrcOver_Mode;
271 }
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000272 const char* aXfermodeString = blend_mode_from_xfermode(aXfermodeName);
vandebo@chromium.org69d4ca32011-05-09 17:34:19 +0000273 SkASSERT(aXfermodeString != NULL);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000274
vandebo@chromium.org69d4ca32011-05-09 17:34:19 +0000275 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 ||
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000281 blend_mode_from_xfermode(bXfermodeName) == NULL) {
vandebo@chromium.org69d4ca32011-05-09 17:34:19 +0000282 bXfermodeName = SkXfermode::kSrcOver_Mode;
283 }
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000284 const char* bXfermodeString = blend_mode_from_xfermode(bXfermodeName);
vandebo@chromium.org69d4ca32011-05-09 17:34:19 +0000285 SkASSERT(bXfermodeString != NULL);
286
287 return strcmp(aXfermodeString, bXfermodeString) == 0;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000288}