/src/skia/src/pdf/SkPDFGraphicState.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2011 Google Inc. |
3 | | * |
4 | | * Use of this source code is governed by a BSD-style license that can be |
5 | | * found in the LICENSE file. |
6 | | */ |
7 | | |
8 | | #include "src/pdf/SkPDFGraphicState.h" |
9 | | |
10 | | #include "include/core/SkData.h" |
11 | | #include "include/core/SkPaint.h" |
12 | | #include "include/docs/SkPDFDocument.h" |
13 | | #include "include/private/SkTo.h" |
14 | | #include "src/pdf/SkPDFDocumentPriv.h" |
15 | | #include "src/pdf/SkPDFFormXObject.h" |
16 | | #include "src/pdf/SkPDFUtils.h" |
17 | | |
18 | 0 | static const char* as_pdf_blend_mode_name(SkBlendMode mode) { |
19 | 0 | const char* name = SkPDFUtils::BlendModeName(mode); |
20 | 0 | SkASSERT(name); |
21 | 0 | return name; |
22 | 0 | } |
23 | | |
24 | 0 | static int to_stroke_cap(uint8_t cap) { |
25 | | // PDF32000.book section 8.4.3.3 "Line Cap Style" |
26 | 0 | switch ((SkPaint::Cap)cap) { |
27 | 0 | case SkPaint::kButt_Cap: return 0; |
28 | 0 | case SkPaint::kRound_Cap: return 1; |
29 | 0 | case SkPaint::kSquare_Cap: return 2; |
30 | 0 | default: SkASSERT(false); return 0; |
31 | 0 | } |
32 | 0 | } |
33 | | |
34 | 0 | static int to_stroke_join(uint8_t join) { |
35 | | // PDF32000.book section 8.4.3.4 "Line Join Style" |
36 | 0 | switch ((SkPaint::Join)join) { |
37 | 0 | case SkPaint::kMiter_Join: return 0; |
38 | 0 | case SkPaint::kRound_Join: return 1; |
39 | 0 | case SkPaint::kBevel_Join: return 2; |
40 | 0 | default: SkASSERT(false); return 0; |
41 | 0 | } |
42 | 0 | } |
43 | | |
44 | | // If a SkXfermode is unsupported in PDF, this function returns |
45 | | // SrcOver, otherwise, it returns that Xfermode as a Mode. |
46 | 0 | static uint8_t pdf_blend_mode(SkBlendMode mode) { |
47 | 0 | if (!SkPDFUtils::BlendModeName(mode) |
48 | 0 | || SkBlendMode::kXor == mode |
49 | 0 | || SkBlendMode::kPlus == mode) |
50 | 0 | { |
51 | 0 | mode = SkBlendMode::kSrcOver; |
52 | 0 | } |
53 | 0 | return SkToU8((unsigned)mode); |
54 | 0 | } |
55 | | |
56 | | SkPDFIndirectReference SkPDFGraphicState::GetGraphicStateForPaint(SkPDFDocument* doc, |
57 | 0 | const SkPaint& p) { |
58 | 0 | SkASSERT(doc); |
59 | 0 | const SkBlendMode mode = p.getBlendMode_or(SkBlendMode::kSrcOver); |
60 | |
|
61 | 0 | if (SkPaint::kFill_Style == p.getStyle()) { |
62 | 0 | SkPDFFillGraphicState fillKey = {p.getColor4f().fA, pdf_blend_mode(mode)}; |
63 | 0 | auto& fillMap = doc->fFillGSMap; |
64 | 0 | if (SkPDFIndirectReference* statePtr = fillMap.find(fillKey)) { |
65 | 0 | return *statePtr; |
66 | 0 | } |
67 | 0 | SkPDFDict state; |
68 | 0 | state.reserve(2); |
69 | 0 | state.insertColorComponentF("ca", fillKey.fAlpha); |
70 | 0 | state.insertName("BM", as_pdf_blend_mode_name((SkBlendMode)fillKey.fBlendMode)); |
71 | 0 | SkPDFIndirectReference ref = doc->emit(state); |
72 | 0 | fillMap.set(fillKey, ref); |
73 | 0 | return ref; |
74 | 0 | } else { |
75 | 0 | SkPDFStrokeGraphicState strokeKey = { |
76 | 0 | p.getStrokeWidth(), |
77 | 0 | p.getStrokeMiter(), |
78 | 0 | p.getColor4f().fA, |
79 | 0 | SkToU8(p.getStrokeCap()), |
80 | 0 | SkToU8(p.getStrokeJoin()), |
81 | 0 | pdf_blend_mode(mode) |
82 | 0 | }; |
83 | 0 | auto& sMap = doc->fStrokeGSMap; |
84 | 0 | if (SkPDFIndirectReference* statePtr = sMap.find(strokeKey)) { |
85 | 0 | return *statePtr; |
86 | 0 | } |
87 | 0 | SkPDFDict state; |
88 | 0 | state.reserve(8); |
89 | 0 | state.insertColorComponentF("CA", strokeKey.fAlpha); |
90 | 0 | state.insertColorComponentF("ca", strokeKey.fAlpha); |
91 | 0 | state.insertInt("LC", to_stroke_cap(strokeKey.fStrokeCap)); |
92 | 0 | state.insertInt("LJ", to_stroke_join(strokeKey.fStrokeJoin)); |
93 | 0 | state.insertScalar("LW", strokeKey.fStrokeWidth); |
94 | 0 | state.insertScalar("ML", strokeKey.fStrokeMiter); |
95 | 0 | state.insertBool("SA", true); // SA = Auto stroke adjustment. |
96 | 0 | state.insertName("BM", as_pdf_blend_mode_name((SkBlendMode)strokeKey.fBlendMode)); |
97 | 0 | SkPDFIndirectReference ref = doc->emit(state); |
98 | 0 | sMap.set(strokeKey, ref); |
99 | 0 | return ref; |
100 | 0 | } |
101 | 0 | } |
102 | | |
103 | | //////////////////////////////////////////////////////////////////////////////// |
104 | | |
105 | 0 | static SkPDFIndirectReference make_invert_function(SkPDFDocument* doc) { |
106 | | // Acrobat crashes if we use a type 0 function, kpdf crashes if we use |
107 | | // a type 2 function, so we use a type 4 function. |
108 | 0 | static const char psInvert[] = "{1 exch sub}"; |
109 | | // Do not copy the trailing '\0' into the SkData. |
110 | 0 | auto invertFunction = SkData::MakeWithoutCopy(psInvert, strlen(psInvert)); |
111 | |
|
112 | 0 | std::unique_ptr<SkPDFDict> dict = SkPDFMakeDict(); |
113 | 0 | dict->insertInt("FunctionType", 4); |
114 | 0 | dict->insertObject("Domain", SkPDFMakeArray(0, 1)); |
115 | 0 | dict->insertObject("Range", SkPDFMakeArray(0, 1)); |
116 | 0 | return SkPDFStreamOut(std::move(dict), SkMemoryStream::Make(std::move(invertFunction)), doc); |
117 | 0 | } |
118 | | |
119 | | SkPDFIndirectReference SkPDFGraphicState::GetSMaskGraphicState(SkPDFIndirectReference sMask, |
120 | | bool invert, |
121 | | SkPDFSMaskMode sMaskMode, |
122 | 0 | SkPDFDocument* doc) { |
123 | | // The practical chances of using the same mask more than once are unlikely |
124 | | // enough that it's not worth canonicalizing. |
125 | 0 | auto sMaskDict = SkPDFMakeDict("Mask"); |
126 | 0 | if (sMaskMode == kAlpha_SMaskMode) { |
127 | 0 | sMaskDict->insertName("S", "Alpha"); |
128 | 0 | } else if (sMaskMode == kLuminosity_SMaskMode) { |
129 | 0 | sMaskDict->insertName("S", "Luminosity"); |
130 | 0 | } |
131 | 0 | sMaskDict->insertRef("G", sMask); |
132 | 0 | if (invert) { |
133 | | // let the doc deduplicate this object. |
134 | 0 | if (doc->fInvertFunction == SkPDFIndirectReference()) { |
135 | 0 | doc->fInvertFunction = make_invert_function(doc); |
136 | 0 | } |
137 | 0 | sMaskDict->insertRef("TR", doc->fInvertFunction); |
138 | 0 | } |
139 | 0 | SkPDFDict result("ExtGState"); |
140 | 0 | result.insertObject("SMask", std::move(sMaskDict)); |
141 | 0 | return doc->emit(result); |
142 | 0 | } |