SkPDFGraphicState.cpp revision 28be72b63e457c680c192a34fb9f58e1c693363f
1/*
2 * Copyright (C) 2010 The Android Open Source Project
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
20SkPDFGraphicState::~SkPDFGraphicState() {
21    SkAutoMutexAcquire lock(canonicalPaintsMutex());
22    int index = find(fPaint);
23    SkASSERT(index >= 0);
24    canonicalPaints().removeShuffle(index);
25}
26
27void SkPDFGraphicState::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
28                                   bool indirect) {
29    populateDict();
30    SkPDFDict::emitObject(stream, catalog, indirect);
31}
32
33size_t SkPDFGraphicState::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
34    populateDict();
35    return SkPDFDict::getOutputSize(catalog, indirect);
36}
37
38// static
39SkTDArray<SkPDFGraphicState::GSCanonicalEntry>&
40SkPDFGraphicState::canonicalPaints() {
41    // This initialization is only thread safe with gcc.
42    static SkTDArray<SkPDFGraphicState::GSCanonicalEntry> gCanonicalPaints;
43    return gCanonicalPaints;
44}
45
46// static
47SkMutex& SkPDFGraphicState::canonicalPaintsMutex() {
48    // This initialization is only thread safe with gcc.
49    static SkMutex gCanonicalPaintsMutex;
50    return gCanonicalPaintsMutex;
51}
52
53// static
54SkPDFGraphicState* SkPDFGraphicState::getGraphicStateForPaint(
55        const SkPaint& paint) {
56    SkAutoMutexAcquire lock(canonicalPaintsMutex());
57    int index = find(paint);
58    if (index >= 0) {
59        canonicalPaints()[index].fGraphicState->ref();
60        return canonicalPaints()[index].fGraphicState;
61    }
62    GSCanonicalEntry newEntry(new SkPDFGraphicState(paint));
63    canonicalPaints().push(newEntry);
64    return newEntry.fGraphicState;
65}
66
67// static
68int SkPDFGraphicState::find(const SkPaint& paint) {
69    GSCanonicalEntry search(&paint);
70    return canonicalPaints().find(search);
71}
72
73SkPDFGraphicState::SkPDFGraphicState(const SkPaint& paint)
74    : fPaint(paint),
75      fPopulated(false) {
76}
77
78// populateDict and operator== have to stay in sync with each other.
79void SkPDFGraphicState::populateDict() {
80    if (!fPopulated) {
81        fPopulated = true;
82        SkRefPtr<SkPDFName> typeName = new SkPDFName("ExtGState");
83        typeName->unref();  // SkRefPtr and new both took a reference.
84        insert("Type", typeName.get());
85
86        SkScalar maxAlpha = SkIntToScalar(0xFF);
87        SkRefPtr<SkPDFScalar> alpha =
88            new SkPDFScalar(SkColorGetA(fPaint.getColor())/maxAlpha);
89        alpha->unref();  // SkRefPtr and new both took a reference.
90        insert("CA", alpha.get());
91        insert("ca", alpha.get());
92
93        SkASSERT(SkPaint::kButt_Cap == 0);
94        SkASSERT(SkPaint::kRound_Cap == 1);
95        SkASSERT(SkPaint::kSquare_Cap == 2);
96        SkASSERT(fPaint.getStrokeCap() >= 0 && fPaint.getStrokeCap() <= 2);
97        SkRefPtr<SkPDFInt> strokeCap = new SkPDFInt(fPaint.getStrokeCap());
98        strokeCap->unref();  // SkRefPtr and new both took a reference.
99        insert("LC", strokeCap.get());
100
101        SkASSERT(SkPaint::kMiter_Join == 0);
102        SkASSERT(SkPaint::kRound_Join == 1);
103        SkASSERT(SkPaint::kBevel_Join == 2);
104        SkASSERT(fPaint.getStrokeJoin() >= 0 && fPaint.getStrokeJoin() <= 2);
105        SkRefPtr<SkPDFInt> strokeJoin = new SkPDFInt(fPaint.getStrokeJoin());
106        strokeJoin->unref();  // SkRefPtr and new both took a reference.
107        insert("LJ", strokeJoin.get());
108
109        SkRefPtr<SkPDFScalar> strokeWidth =
110            new SkPDFScalar(fPaint.getStrokeWidth());
111        strokeWidth->unref();  // SkRefPtr and new both took a reference.
112        insert("LW", strokeWidth.get());
113
114        SkRefPtr<SkPDFScalar> strokeMiterLimit = new SkPDFScalar(
115            fPaint.getStrokeMiter());
116        strokeMiterLimit->unref();  // SkRefPtr and new both took a reference.
117        insert("ML", strokeWidth.get());
118
119        // Turn on automatic stroke adjustment.
120        SkRefPtr<SkPDFBool> trueVal = new SkPDFBool(true);
121        trueVal->unref();  // SkRefPtr and new both took a reference.
122        insert("SA", trueVal.get());
123    }
124}
125
126// We're only interested in some fields of the SkPaint, so we have a custom
127// operator== function.
128bool SkPDFGraphicState::GSCanonicalEntry::operator==(
129        const SkPDFGraphicState::GSCanonicalEntry& gs) const {
130    const SkPaint* a = fPaint;
131    const SkPaint* b = gs.fPaint;
132    SkASSERT(a != NULL);
133    SkASSERT(b != NULL);
134    return SkColorGetA(a->getColor()) == SkColorGetA(b->getColor()) &&
135           a->getStrokeCap() == b->getStrokeCap() &&
136           a->getStrokeJoin() == b->getStrokeJoin() &&
137           a->getStrokeWidth() == b->getStrokeWidth() &&
138           a->getStrokeMiter() == b->getStrokeMiter();
139}
140