blob: 730dc29c69096601833fa2163f25ea0321e029be [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
17#include "SkPDFGraphicState.h"
vandebo@chromium.orgf71b2102011-04-04 19:46:31 +000018#include "SkPDFUtils.h"
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000019#include "SkStream.h"
vandebo@chromium.org48543272011-02-08 19:28:07 +000020#include "SkTypes.h"
21
22namespace {
23
24const char* blendModeFromXfermode(SkXfermode::Mode mode) {
25 switch (mode) {
26 case SkXfermode::kSrcOver_Mode: return "Normal";
27 case SkXfermode::kMultiply_Mode: return "Multiply";
28 case SkXfermode::kScreen_Mode: return "Screen";
29 case SkXfermode::kOverlay_Mode: return "Overlay";
30 case SkXfermode::kDarken_Mode: return "Darken";
31 case SkXfermode::kLighten_Mode: return "Lighten";
32 case SkXfermode::kColorDodge_Mode: return "ColorDodge";
33 case SkXfermode::kColorBurn_Mode: return "ColorBurn";
34 case SkXfermode::kHardLight_Mode: return "HardLight";
35 case SkXfermode::kSoftLight_Mode: return "SoftLight";
36 case SkXfermode::kDifference_Mode: return "Difference";
37 case SkXfermode::kExclusion_Mode: return "Exclusion";
38
vandebo@chromium.org25adce82011-05-09 08:05:01 +000039 // These are handled in SkPDFDevice::setUpContentEntry.
vandebo@chromium.org48543272011-02-08 19:28:07 +000040 case SkXfermode::kClear_Mode:
41 case SkXfermode::kSrc_Mode:
42 case SkXfermode::kDst_Mode:
43 case SkXfermode::kDstOver_Mode:
vandebo@chromium.org25adce82011-05-09 08:05:01 +000044 return "Normal";
45
46 // TODO(vandebo) Figure out if we can support more of these modes.
vandebo@chromium.org48543272011-02-08 19:28:07 +000047 case SkXfermode::kSrcIn_Mode:
48 case SkXfermode::kDstIn_Mode:
49 case SkXfermode::kSrcOut_Mode:
50 case SkXfermode::kDstOut_Mode:
51 case SkXfermode::kSrcATop_Mode:
52 case SkXfermode::kDstATop_Mode:
53 case SkXfermode::kXor_Mode:
54 case SkXfermode::kPlus_Mode:
55 return NULL;
56 }
57 return NULL;
58}
59
60}
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000061
62SkPDFGraphicState::~SkPDFGraphicState() {
63 SkAutoMutexAcquire lock(canonicalPaintsMutex());
64 int index = find(fPaint);
65 SkASSERT(index >= 0);
66 canonicalPaints().removeShuffle(index);
67}
68
69void SkPDFGraphicState::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
70 bool indirect) {
71 populateDict();
72 SkPDFDict::emitObject(stream, catalog, indirect);
73}
74
vandebo@chromium.org48543272011-02-08 19:28:07 +000075// static
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000076size_t SkPDFGraphicState::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
77 populateDict();
78 return SkPDFDict::getOutputSize(catalog, indirect);
79}
80
81// static
82SkTDArray<SkPDFGraphicState::GSCanonicalEntry>&
83SkPDFGraphicState::canonicalPaints() {
84 // This initialization is only thread safe with gcc.
85 static SkTDArray<SkPDFGraphicState::GSCanonicalEntry> gCanonicalPaints;
86 return gCanonicalPaints;
87}
88
89// static
90SkMutex& SkPDFGraphicState::canonicalPaintsMutex() {
91 // This initialization is only thread safe with gcc.
92 static SkMutex gCanonicalPaintsMutex;
93 return gCanonicalPaintsMutex;
94}
95
96// static
97SkPDFGraphicState* SkPDFGraphicState::getGraphicStateForPaint(
98 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
111int SkPDFGraphicState::find(const SkPaint& paint) {
112 GSCanonicalEntry search(&paint);
113 return canonicalPaints().find(search);
114}
115
116SkPDFGraphicState::SkPDFGraphicState(const SkPaint& paint)
117 : fPaint(paint),
118 fPopulated(false) {
119}
120
121// populateDict and operator== have to stay in sync with each other.
122void SkPDFGraphicState::populateDict() {
123 if (!fPopulated) {
124 fPopulated = true;
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000125 insert("Type", new SkPDFName("ExtGState"))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000126
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000127 SkRefPtr<SkPDFScalar> alpha =
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000128 new SkPDFScalar(fPaint.getAlpha() * SkScalarInvert(0xFF));
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000129 alpha->unref(); // SkRefPtr and new both took a reference.
130 insert("CA", alpha.get());
131 insert("ca", alpha.get());
132
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000133 SK_COMPILE_ASSERT(SkPaint::kButt_Cap == 0, paint_cap_mismatch);
134 SK_COMPILE_ASSERT(SkPaint::kRound_Cap == 1, paint_cap_mismatch);
135 SK_COMPILE_ASSERT(SkPaint::kSquare_Cap == 2, paint_cap_mismatch);
136 SK_COMPILE_ASSERT(SkPaint::kCapCount == 3, paint_cap_mismatch);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000137 SkASSERT(fPaint.getStrokeCap() >= 0 && fPaint.getStrokeCap() <= 2);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000138 insert("LC", new SkPDFInt(fPaint.getStrokeCap()))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000139
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000140 SK_COMPILE_ASSERT(SkPaint::kMiter_Join == 0, paint_join_mismatch);
141 SK_COMPILE_ASSERT(SkPaint::kRound_Join == 1, paint_join_mismatch);
142 SK_COMPILE_ASSERT(SkPaint::kBevel_Join == 2, paint_join_mismatch);
143 SK_COMPILE_ASSERT(SkPaint::kJoinCount == 3, paint_join_mismatch);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000144 SkASSERT(fPaint.getStrokeJoin() >= 0 && fPaint.getStrokeJoin() <= 2);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000145 insert("LJ", new SkPDFInt(fPaint.getStrokeJoin()))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000146
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000147 insert("LW", new SkPDFScalar(fPaint.getStrokeWidth()))->unref();
148 insert("ML", new SkPDFScalar(fPaint.getStrokeMiter()))->unref();
149 insert("SA", new SkPDFBool(true))->unref(); // Auto stroke adjustment.
vandebo@chromium.org48543272011-02-08 19:28:07 +0000150
151 SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode;
vandebo@chromium.orgf71b2102011-04-04 19:46:31 +0000152 // If asMode fails, default to kSrcOver_Mode.
vandebo@chromium.org48543272011-02-08 19:28:07 +0000153 if (fPaint.getXfermode())
154 fPaint.getXfermode()->asMode(&xfermode);
155 // If we don't support the mode, just use kSrcOver_Mode.
156 if (xfermode < 0 || xfermode > SkXfermode::kLastMode ||
157 blendModeFromXfermode(xfermode) == NULL) {
vandebo@chromium.org48543272011-02-08 19:28:07 +0000158 xfermode = SkXfermode::kSrcOver_Mode;
vandebo@chromium.orgf71b2102011-04-04 19:46:31 +0000159 NOT_IMPLEMENTED("unsupported xfermode", false);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000160 }
161 insert("BM", new SkPDFName(blendModeFromXfermode(xfermode)))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000162 }
163}
164
165// We're only interested in some fields of the SkPaint, so we have a custom
166// operator== function.
167bool SkPDFGraphicState::GSCanonicalEntry::operator==(
168 const SkPDFGraphicState::GSCanonicalEntry& gs) const {
169 const SkPaint* a = fPaint;
170 const SkPaint* b = gs.fPaint;
171 SkASSERT(a != NULL);
172 SkASSERT(b != NULL);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000173
174 if (SkColorGetA(a->getColor()) != SkColorGetA(b->getColor()) ||
175 a->getStrokeCap() != b->getStrokeCap() ||
176 a->getStrokeJoin() != b->getStrokeJoin() ||
177 a->getStrokeWidth() != b->getStrokeWidth() ||
178 a->getStrokeMiter() != b->getStrokeMiter()) {
179 return false;
180 }
181
182 SkXfermode* aXfermode = a->getXfermode();
183 SkXfermode::Mode aXfermodeName = SkXfermode::kSrcOver_Mode;
184 bool aXfermodeKnown = true;
185 if (aXfermode)
186 aXfermodeKnown = aXfermode->asMode(&aXfermodeName);
187 SkXfermode* bXfermode = b->getXfermode();
188 SkXfermode::Mode bXfermodeName = SkXfermode::kSrcOver_Mode;
189 bool bXfermodeKnown = true;
190 if (bXfermode)
191 bXfermodeKnown = bXfermode->asMode(&bXfermodeName);
192
193 if (aXfermodeKnown != bXfermodeKnown)
194 return false;
195 if (!aXfermodeKnown)
196 return aXfermode == bXfermode;
197 return aXfermodeName == bXfermodeName;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000198}