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