• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "SkData.h"
9 #include "SkPaint.h"
10 #include "SkPDFCanon.h"
11 #include "SkPDFFormXObject.h"
12 #include "SkPDFGraphicState.h"
13 #include "SkPDFUtils.h"
14 
as_pdf_blend_mode_name(SkBlendMode mode)15 static const char* as_pdf_blend_mode_name(SkBlendMode mode) {
16     // PDF32000.book section 11.3.5 "Blend Mode"
17     switch (mode) {
18         case SkBlendMode::kScreen:      return "Screen";
19         case SkBlendMode::kOverlay:     return "Overlay";
20         case SkBlendMode::kDarken:      return "Darken";
21         case SkBlendMode::kLighten:     return "Lighten";
22         case SkBlendMode::kColorDodge:  return "ColorDodge";
23         case SkBlendMode::kColorBurn:   return "ColorBurn";
24         case SkBlendMode::kHardLight:   return "HardLight";
25         case SkBlendMode::kSoftLight:   return "SoftLight";
26         case SkBlendMode::kDifference:  return "Difference";
27         case SkBlendMode::kExclusion:   return "Exclusion";
28         case SkBlendMode::kMultiply:    return "Multiply";
29         case SkBlendMode::kHue:         return "Hue";
30         case SkBlendMode::kSaturation:  return "Saturation";
31         case SkBlendMode::kColor:       return "Color";
32         case SkBlendMode::kLuminosity:  return "Luminosity";
33         // Other blendmodes are either unsupported or handled in
34         // SkPDFDevice::setUpContentEntry.
35         default:                        return "Normal";
36     }
37 }
38 
to_stroke_cap(uint8_t cap)39 static int to_stroke_cap(uint8_t cap) {
40     // PDF32000.book section 8.4.3.3 "Line Cap Style"
41     switch ((SkPaint::Cap)cap) {
42         case SkPaint::kButt_Cap:   return 0;
43         case SkPaint::kRound_Cap:  return 1;
44         case SkPaint::kSquare_Cap: return 2;
45         default: SkASSERT(false);  return 0;
46     }
47 }
48 
to_stroke_join(uint8_t join)49 static int to_stroke_join(uint8_t join) {
50     // PDF32000.book section 8.4.3.4 "Line Join Style"
51     switch ((SkPaint::Join)join) {
52         case SkPaint::kMiter_Join: return 0;
53         case SkPaint::kRound_Join: return 1;
54         case SkPaint::kBevel_Join: return 2;
55         default: SkASSERT(false);  return 0;
56     }
57 }
58 
59 // If a SkXfermode is unsupported in PDF, this function returns
60 // SrcOver, otherwise, it returns that Xfermode as a Mode.
pdf_blend_mode(SkBlendMode mode)61 static uint8_t pdf_blend_mode(SkBlendMode mode) {
62     switch (mode) {
63         case SkBlendMode::kSrcOver:
64         case SkBlendMode::kMultiply:
65         case SkBlendMode::kScreen:
66         case SkBlendMode::kOverlay:
67         case SkBlendMode::kDarken:
68         case SkBlendMode::kLighten:
69         case SkBlendMode::kColorDodge:
70         case SkBlendMode::kColorBurn:
71         case SkBlendMode::kHardLight:
72         case SkBlendMode::kSoftLight:
73         case SkBlendMode::kDifference:
74         case SkBlendMode::kExclusion:
75         case SkBlendMode::kHue:
76         case SkBlendMode::kSaturation:
77         case SkBlendMode::kColor:
78         case SkBlendMode::kLuminosity:
79             return SkToU8((unsigned)mode);
80         default:
81             return SkToU8((unsigned)SkBlendMode::kSrcOver);
82     }
83 }
84 
GetGraphicStateForPaint(SkPDFCanon * canon,const SkPaint & p)85 sk_sp<SkPDFDict> SkPDFGraphicState::GetGraphicStateForPaint(SkPDFCanon* canon,
86                                                             const SkPaint& p) {
87     SkASSERT(canon);
88     if (SkPaint::kFill_Style == p.getStyle()) {
89         SkPDFFillGraphicState fillKey = {p.getAlpha(), pdf_blend_mode(p.getBlendMode())};
90         auto& fillMap = canon->fFillGSMap;
91         if (sk_sp<SkPDFDict>* statePtr = fillMap.find(fillKey)) {
92             return *statePtr;
93         }
94         auto state = sk_make_sp<SkPDFDict>();
95         state->reserve(2);
96         state->insertScalar("ca", fillKey.fAlpha / 255.0f);
97         state->insertName("BM", as_pdf_blend_mode_name((SkBlendMode)fillKey.fBlendMode));
98         fillMap.set(fillKey, state);
99         return state;
100     } else {
101         SkPDFStrokeGraphicState strokeKey = {
102             p.getStrokeWidth(), p.getStrokeMiter(),
103             SkToU8(p.getStrokeCap()), SkToU8(p.getStrokeJoin()),
104             p.getAlpha(), pdf_blend_mode(p.getBlendMode())};
105         auto& sMap = canon->fStrokeGSMap;
106         if (sk_sp<SkPDFDict>* statePtr = sMap.find(strokeKey)) {
107             return *statePtr;
108         }
109         auto state = sk_make_sp<SkPDFDict>();
110         state->reserve(8);
111         state->insertScalar("CA", strokeKey.fAlpha / 255.0f);
112         state->insertScalar("ca", strokeKey.fAlpha / 255.0f);
113         state->insertInt("LC", to_stroke_cap(strokeKey.fStrokeCap));
114         state->insertInt("LJ", to_stroke_join(strokeKey.fStrokeJoin));
115         state->insertScalar("LW", strokeKey.fStrokeWidth);
116         state->insertScalar("ML", strokeKey.fStrokeMiter);
117         state->insertBool("SA", true);  // SA = Auto stroke adjustment.
118         state->insertName("BM", as_pdf_blend_mode_name((SkBlendMode)strokeKey.fBlendMode));
119         sMap.set(strokeKey, state);
120         return state;
121     }
122 }
123 
124 ////////////////////////////////////////////////////////////////////////////////
125 
make_invert_function()126 static sk_sp<SkPDFStream> make_invert_function() {
127     // Acrobat crashes if we use a type 0 function, kpdf crashes if we use
128     // a type 2 function, so we use a type 4 function.
129     auto domainAndRange = sk_make_sp<SkPDFArray>();
130     domainAndRange->reserve(2);
131     domainAndRange->appendInt(0);
132     domainAndRange->appendInt(1);
133 
134     static const char psInvert[] = "{1 exch sub}";
135     // Do not copy the trailing '\0' into the SkData.
136     auto invertFunction = sk_make_sp<SkPDFStream>(
137             SkData::MakeWithoutCopy(psInvert, strlen(psInvert)));
138     invertFunction->dict()->insertInt("FunctionType", 4);
139     invertFunction->dict()->insertObject("Domain", domainAndRange);
140     invertFunction->dict()->insertObject("Range", std::move(domainAndRange));
141     return invertFunction;
142 }
143 
GetSMaskGraphicState(sk_sp<SkPDFObject> sMask,bool invert,SkPDFSMaskMode sMaskMode,SkPDFCanon * canon)144 sk_sp<SkPDFDict> SkPDFGraphicState::GetSMaskGraphicState(
145         sk_sp<SkPDFObject> sMask,
146         bool invert,
147         SkPDFSMaskMode sMaskMode,
148         SkPDFCanon* canon) {
149     // The practical chances of using the same mask more than once are unlikely
150     // enough that it's not worth canonicalizing.
151     auto sMaskDict = sk_make_sp<SkPDFDict>("Mask");
152     if (sMaskMode == kAlpha_SMaskMode) {
153         sMaskDict->insertName("S", "Alpha");
154     } else if (sMaskMode == kLuminosity_SMaskMode) {
155         sMaskDict->insertName("S", "Luminosity");
156     }
157     sMaskDict->insertObjRef("G", std::move(sMask));
158     if (invert) {
159         // Instead of calling SkPDFGraphicState::MakeInvertFunction,
160         // let the canon deduplicate this object.
161         sk_sp<SkPDFStream>& invertFunction = canon->fInvertFunction;
162         if (!invertFunction) {
163             invertFunction = make_invert_function();
164         }
165         sMaskDict->insertObjRef("TR", invertFunction);
166     }
167     auto result = sk_make_sp<SkPDFDict>("ExtGState");
168     result->insertObject("SMask", std::move(sMaskDict));
169     return result;
170 }
171