SkPDFGraphicState.cpp revision 48543277728fdf66b993f17421f65fba532a23a2
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 "SkPDFGraphicState.h"
18#include "SkStream.h"
19#include "SkTypes.h"
20
21namespace {
22
23const char* blendModeFromXfermode(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        // TODO(vandebo) Figure out if we can support more of these modes.
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        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
56}
57
58SkPDFGraphicState::~SkPDFGraphicState() {
59    SkAutoMutexAcquire lock(canonicalPaintsMutex());
60    int index = find(fPaint);
61    SkASSERT(index >= 0);
62    canonicalPaints().removeShuffle(index);
63}
64
65void SkPDFGraphicState::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
66                                   bool indirect) {
67    populateDict();
68    SkPDFDict::emitObject(stream, catalog, indirect);
69}
70
71// static
72size_t SkPDFGraphicState::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
73    populateDict();
74    return SkPDFDict::getOutputSize(catalog, indirect);
75}
76
77// static
78SkTDArray<SkPDFGraphicState::GSCanonicalEntry>&
79SkPDFGraphicState::canonicalPaints() {
80    // This initialization is only thread safe with gcc.
81    static SkTDArray<SkPDFGraphicState::GSCanonicalEntry> gCanonicalPaints;
82    return gCanonicalPaints;
83}
84
85// static
86SkMutex& SkPDFGraphicState::canonicalPaintsMutex() {
87    // This initialization is only thread safe with gcc.
88    static SkMutex gCanonicalPaintsMutex;
89    return gCanonicalPaintsMutex;
90}
91
92// static
93SkPDFGraphicState* SkPDFGraphicState::getGraphicStateForPaint(
94        const SkPaint& paint) {
95    SkAutoMutexAcquire lock(canonicalPaintsMutex());
96    int index = find(paint);
97    if (index >= 0) {
98        canonicalPaints()[index].fGraphicState->ref();
99        return canonicalPaints()[index].fGraphicState;
100    }
101    GSCanonicalEntry newEntry(new SkPDFGraphicState(paint));
102    canonicalPaints().push(newEntry);
103    return newEntry.fGraphicState;
104}
105
106// static
107int SkPDFGraphicState::find(const SkPaint& paint) {
108    GSCanonicalEntry search(&paint);
109    return canonicalPaints().find(search);
110}
111
112SkPDFGraphicState::SkPDFGraphicState(const SkPaint& paint)
113    : fPaint(paint),
114      fPopulated(false) {
115}
116
117// populateDict and operator== have to stay in sync with each other.
118void SkPDFGraphicState::populateDict() {
119    if (!fPopulated) {
120        fPopulated = true;
121        insert("Type", new SkPDFName("ExtGState"))->unref();
122
123        SkRefPtr<SkPDFScalar> alpha =
124            new SkPDFScalar(fPaint.getAlpha() * SkScalarInvert(0xFF));
125        alpha->unref();  // SkRefPtr and new both took a reference.
126        insert("CA", alpha.get());
127        insert("ca", alpha.get());
128
129        SK_COMPILE_ASSERT(SkPaint::kButt_Cap == 0, paint_cap_mismatch);
130        SK_COMPILE_ASSERT(SkPaint::kRound_Cap == 1, paint_cap_mismatch);
131        SK_COMPILE_ASSERT(SkPaint::kSquare_Cap == 2, paint_cap_mismatch);
132        SK_COMPILE_ASSERT(SkPaint::kCapCount == 3, paint_cap_mismatch);
133        SkASSERT(fPaint.getStrokeCap() >= 0 && fPaint.getStrokeCap() <= 2);
134        insert("LC", new SkPDFInt(fPaint.getStrokeCap()))->unref();
135
136        SK_COMPILE_ASSERT(SkPaint::kMiter_Join == 0, paint_join_mismatch);
137        SK_COMPILE_ASSERT(SkPaint::kRound_Join == 1, paint_join_mismatch);
138        SK_COMPILE_ASSERT(SkPaint::kBevel_Join == 2, paint_join_mismatch);
139        SK_COMPILE_ASSERT(SkPaint::kJoinCount == 3, paint_join_mismatch);
140        SkASSERT(fPaint.getStrokeJoin() >= 0 && fPaint.getStrokeJoin() <= 2);
141        insert("LJ", new SkPDFInt(fPaint.getStrokeJoin()))->unref();
142
143        insert("LW", new SkPDFScalar(fPaint.getStrokeWidth()))->unref();
144        insert("ML", new SkPDFScalar(fPaint.getStrokeMiter()))->unref();
145        insert("SA", new SkPDFBool(true))->unref();  // Auto stroke adjustment.
146
147        SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode;
148        // If asMode fails return false, default to kSrcOver_Mode.
149        if (fPaint.getXfermode())
150            fPaint.getXfermode()->asMode(&xfermode);
151        // If we don't support the mode, just use kSrcOver_Mode.
152        if (xfermode < 0 || xfermode > SkXfermode::kLastMode ||
153                blendModeFromXfermode(xfermode) == NULL) {
154            fprintf(stderr, "NOT_IMPLEMENTED: xfermode = %d\n", xfermode);
155            xfermode = SkXfermode::kSrcOver_Mode;
156        }
157        insert("BM", new SkPDFName(blendModeFromXfermode(xfermode)))->unref();
158    }
159}
160
161// We're only interested in some fields of the SkPaint, so we have a custom
162// operator== function.
163bool SkPDFGraphicState::GSCanonicalEntry::operator==(
164        const SkPDFGraphicState::GSCanonicalEntry& gs) const {
165    const SkPaint* a = fPaint;
166    const SkPaint* b = gs.fPaint;
167    SkASSERT(a != NULL);
168    SkASSERT(b != NULL);
169
170    if (SkColorGetA(a->getColor()) != SkColorGetA(b->getColor()) ||
171           a->getStrokeCap() != b->getStrokeCap() ||
172           a->getStrokeJoin() != b->getStrokeJoin() ||
173           a->getStrokeWidth() != b->getStrokeWidth() ||
174           a->getStrokeMiter() != b->getStrokeMiter()) {
175        return false;
176    }
177
178    SkXfermode* aXfermode = a->getXfermode();
179    SkXfermode::Mode aXfermodeName = SkXfermode::kSrcOver_Mode;
180    bool aXfermodeKnown = true;
181    if (aXfermode)
182        aXfermodeKnown = aXfermode->asMode(&aXfermodeName);
183    SkXfermode* bXfermode = b->getXfermode();
184    SkXfermode::Mode bXfermodeName = SkXfermode::kSrcOver_Mode;
185    bool bXfermodeKnown = true;
186    if (bXfermode)
187        bXfermodeKnown = bXfermode->asMode(&bXfermodeName);
188
189    if (aXfermodeKnown != bXfermodeKnown)
190        return false;
191    if (!aXfermodeKnown)
192       return aXfermode == bXfermode;
193    return aXfermodeName == bXfermodeName;
194}
195