• Home
  • History
  • Annotate
  • 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_blend_mode(SkBlendMode mode)15 static const char* as_blend_mode(SkBlendMode mode) {
16     switch (mode) {
17         case SkBlendMode::kSrcOver:
18             return "Normal";
19         case SkBlendMode::kMultiply:
20             return "Multiply";
21         case SkBlendMode::kScreen:
22             return "Screen";
23         case SkBlendMode::kOverlay:
24             return "Overlay";
25         case SkBlendMode::kDarken:
26             return "Darken";
27         case SkBlendMode::kLighten:
28             return "Lighten";
29         case SkBlendMode::kColorDodge:
30             return "ColorDodge";
31         case SkBlendMode::kColorBurn:
32             return "ColorBurn";
33         case SkBlendMode::kHardLight:
34             return "HardLight";
35         case SkBlendMode::kSoftLight:
36             return "SoftLight";
37         case SkBlendMode::kDifference:
38             return "Difference";
39         case SkBlendMode::kExclusion:
40             return "Exclusion";
41         case SkBlendMode::kHue:
42             return "Hue";
43         case SkBlendMode::kSaturation:
44             return "Saturation";
45         case SkBlendMode::kColor:
46             return "Color";
47         case SkBlendMode::kLuminosity:
48             return "Luminosity";
49 
50         // These are handled in SkPDFDevice::setUpContentEntry.
51         case SkBlendMode::kClear:
52         case SkBlendMode::kSrc:
53         case SkBlendMode::kDst:
54         case SkBlendMode::kDstOver:
55         case SkBlendMode::kSrcIn:
56         case SkBlendMode::kDstIn:
57         case SkBlendMode::kSrcOut:
58         case SkBlendMode::kDstOut:
59         case SkBlendMode::kSrcATop:
60         case SkBlendMode::kDstATop:
61         case SkBlendMode::kModulate:
62             return "Normal";
63 
64         // TODO(vandebo): Figure out if we can support more of these modes.
65         case SkBlendMode::kXor:
66         case SkBlendMode::kPlus:
67             return nullptr;
68     }
69     return nullptr;
70 }
71 
72 // If a SkXfermode is unsupported in PDF, this function returns
73 // SrcOver, otherwise, it returns that Xfermode as a Mode.
mode_for_pdf(SkBlendMode mode)74 static SkBlendMode mode_for_pdf(SkBlendMode mode) {
75     switch (mode) {
76         case SkBlendMode::kSrcOver:
77         case SkBlendMode::kMultiply:
78         case SkBlendMode::kScreen:
79         case SkBlendMode::kOverlay:
80         case SkBlendMode::kDarken:
81         case SkBlendMode::kLighten:
82         case SkBlendMode::kColorDodge:
83         case SkBlendMode::kColorBurn:
84         case SkBlendMode::kHardLight:
85         case SkBlendMode::kSoftLight:
86         case SkBlendMode::kDifference:
87         case SkBlendMode::kExclusion:
88         case SkBlendMode::kHue:
89         case SkBlendMode::kSaturation:
90         case SkBlendMode::kColor:
91         case SkBlendMode::kLuminosity:
92             // Mode is suppported and handled by pdf graphics state.
93             return mode;
94         default:
95             return SkBlendMode::kSrcOver;  // Default mode.
96     }
97 }
98 
SkPDFGraphicState(const SkPaint & p)99 SkPDFGraphicState::SkPDFGraphicState(const SkPaint& p)
100     : fStrokeWidth(p.getStrokeWidth())
101     , fStrokeMiter(p.getStrokeMiter())
102     , fAlpha(p.getAlpha())
103     , fStrokeCap(SkToU8(p.getStrokeCap()))
104     , fStrokeJoin(SkToU8(p.getStrokeJoin()))
105     , fMode(SkToU8((unsigned)mode_for_pdf(p.getBlendMode()))) {}
106 
107 // static
GetGraphicStateForPaint(SkPDFCanon * canon,const SkPaint & paint)108 SkPDFGraphicState* SkPDFGraphicState::GetGraphicStateForPaint(
109         SkPDFCanon* canon, const SkPaint& paint) {
110     SkASSERT(canon);
111     SkPDFGraphicState key(paint);
112     if (const SkPDFGraphicState* canonGS = canon->findGraphicState(key)) {
113         // The returned SkPDFGraphicState must be made non-const,
114         // since the emitObject() interface is non-const.  But We
115         // promise that there is no way to mutate this object from
116         // here on out.
117         return SkRef(const_cast<SkPDFGraphicState*>(canonGS));
118     }
119     SkPDFGraphicState* pdfGraphicState = new SkPDFGraphicState(paint);
120     canon->addGraphicState(pdfGraphicState);
121     return pdfGraphicState;
122 }
123 
MakeInvertFunction()124 sk_sp<SkPDFStream> SkPDFGraphicState::MakeInvertFunction() {
125     // Acrobat crashes if we use a type 0 function, kpdf crashes if we use
126     // a type 2 function, so we use a type 4 function.
127     auto domainAndRange = sk_make_sp<SkPDFArray>();
128     domainAndRange->reserve(2);
129     domainAndRange->appendInt(0);
130     domainAndRange->appendInt(1);
131 
132     static const char psInvert[] = "{1 exch sub}";
133     // Do not copy the trailing '\0' into the SkData.
134     auto invertFunction = sk_make_sp<SkPDFStream>(
135             SkData::MakeWithoutCopy(psInvert, strlen(psInvert)));
136     invertFunction->dict()->insertInt("FunctionType", 4);
137     invertFunction->dict()->insertObject("Domain", domainAndRange);
138     invertFunction->dict()->insertObject("Range", std::move(domainAndRange));
139     return invertFunction;
140 }
141 
GetSMaskGraphicState(sk_sp<SkPDFObject> sMask,bool invert,SkPDFSMaskMode sMaskMode,SkPDFCanon * canon)142 sk_sp<SkPDFDict> SkPDFGraphicState::GetSMaskGraphicState(
143         sk_sp<SkPDFObject> sMask,
144         bool invert,
145         SkPDFSMaskMode sMaskMode,
146         SkPDFCanon* canon) {
147     // The practical chances of using the same mask more than once are unlikely
148     // enough that it's not worth canonicalizing.
149     auto sMaskDict = sk_make_sp<SkPDFDict>("Mask");
150     if (sMaskMode == kAlpha_SMaskMode) {
151         sMaskDict->insertName("S", "Alpha");
152     } else if (sMaskMode == kLuminosity_SMaskMode) {
153         sMaskDict->insertName("S", "Luminosity");
154     }
155     sMaskDict->insertObjRef("G", std::move(sMask));
156     if (invert) {
157         // Instead of calling SkPDFGraphicState::MakeInvertFunction,
158         // let the canon deduplicate this object.
159         sMaskDict->insertObjRef("TR", canon->makeInvertFunction());
160     }
161 
162     auto result = sk_make_sp<SkPDFDict>("ExtGState");
163     result->insertObject("SMask", std::move(sMaskDict));
164     return result;
165 }
166 
MakeNoSmaskGraphicState()167 sk_sp<SkPDFDict> SkPDFGraphicState::MakeNoSmaskGraphicState() {
168     auto noSMaskGS = sk_make_sp<SkPDFDict>("ExtGState");
169     noSMaskGS->insertName("SMask", "None");
170     return noSMaskGS;
171 }
172 
emitObject(SkWStream * stream,const SkPDFObjNumMap & objNumMap) const173 void SkPDFGraphicState::emitObject(
174         SkWStream* stream,
175         const SkPDFObjNumMap& objNumMap) const {
176     auto dict = sk_make_sp<SkPDFDict>("ExtGState");
177     dict->insertName("Type", "ExtGState");
178 
179     SkScalar alpha = SkIntToScalar(fAlpha) / 0xFF;
180     dict->insertScalar("CA", alpha);
181     dict->insertScalar("ca", alpha);
182 
183     SkPaint::Cap strokeCap = (SkPaint::Cap)fStrokeCap;
184     SkPaint::Join strokeJoin = (SkPaint::Join)fStrokeJoin;
185 
186     static_assert(SkPaint::kButt_Cap == 0, "paint_cap_mismatch");
187     static_assert(SkPaint::kRound_Cap == 1, "paint_cap_mismatch");
188     static_assert(SkPaint::kSquare_Cap == 2, "paint_cap_mismatch");
189     static_assert(SkPaint::kCapCount == 3, "paint_cap_mismatch");
190     SkASSERT(strokeCap >= 0 && strokeCap <= 2);
191     dict->insertInt("LC", strokeCap);
192 
193     static_assert(SkPaint::kMiter_Join == 0, "paint_join_mismatch");
194     static_assert(SkPaint::kRound_Join == 1, "paint_join_mismatch");
195     static_assert(SkPaint::kBevel_Join == 2, "paint_join_mismatch");
196     static_assert(SkPaint::kJoinCount == 3, "paint_join_mismatch");
197     SkASSERT(strokeJoin >= 0 && strokeJoin <= 2);
198     dict->insertInt("LJ", strokeJoin);
199 
200     dict->insertScalar("LW", fStrokeWidth);
201     dict->insertScalar("ML", fStrokeMiter);
202     dict->insertBool("SA", true);  // SA = Auto stroke adjustment.
203     dict->insertName("BM", as_blend_mode((SkBlendMode)fMode));
204     dict->emitObject(stream, objNumMap);
205 }
206