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 "SkOncePtr.h" 10#include "SkPDFCanon.h" 11#include "SkPDFFormXObject.h" 12#include "SkPDFGraphicState.h" 13#include "SkPDFUtils.h" 14#include "SkTypes.h" 15 16static const char* as_blend_mode(SkXfermode::Mode mode) { 17 switch (mode) { 18 case SkXfermode::kSrcOver_Mode: 19 return "Normal"; 20 case SkXfermode::kMultiply_Mode: 21 return "Multiply"; 22 case SkXfermode::kScreen_Mode: 23 return "Screen"; 24 case SkXfermode::kOverlay_Mode: 25 return "Overlay"; 26 case SkXfermode::kDarken_Mode: 27 return "Darken"; 28 case SkXfermode::kLighten_Mode: 29 return "Lighten"; 30 case SkXfermode::kColorDodge_Mode: 31 return "ColorDodge"; 32 case SkXfermode::kColorBurn_Mode: 33 return "ColorBurn"; 34 case SkXfermode::kHardLight_Mode: 35 return "HardLight"; 36 case SkXfermode::kSoftLight_Mode: 37 return "SoftLight"; 38 case SkXfermode::kDifference_Mode: 39 return "Difference"; 40 case SkXfermode::kExclusion_Mode: 41 return "Exclusion"; 42 case SkXfermode::kHue_Mode: 43 return "Hue"; 44 case SkXfermode::kSaturation_Mode: 45 return "Saturation"; 46 case SkXfermode::kColor_Mode: 47 return "Color"; 48 case SkXfermode::kLuminosity_Mode: 49 return "Luminosity"; 50 51 // These are handled in SkPDFDevice::setUpContentEntry. 52 case SkXfermode::kClear_Mode: 53 case SkXfermode::kSrc_Mode: 54 case SkXfermode::kDst_Mode: 55 case SkXfermode::kDstOver_Mode: 56 case SkXfermode::kSrcIn_Mode: 57 case SkXfermode::kDstIn_Mode: 58 case SkXfermode::kSrcOut_Mode: 59 case SkXfermode::kDstOut_Mode: 60 case SkXfermode::kSrcATop_Mode: 61 case SkXfermode::kDstATop_Mode: 62 case SkXfermode::kModulate_Mode: 63 return "Normal"; 64 65 // TODO(vandebo): Figure out if we can support more of these modes. 66 case SkXfermode::kXor_Mode: 67 case SkXfermode::kPlus_Mode: 68 return nullptr; 69 } 70 return nullptr; 71} 72 73// If a SkXfermode is unsupported in PDF, this function returns 74// SrcOver, otherwise, it returns that Xfermode as a Mode. 75static SkXfermode::Mode mode_for_pdf(const SkXfermode* xfermode) { 76 SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode; 77 if (xfermode) { 78 xfermode->asMode(&mode); 79 } 80 switch (mode) { 81 case SkXfermode::kSrcOver_Mode: 82 case SkXfermode::kMultiply_Mode: 83 case SkXfermode::kScreen_Mode: 84 case SkXfermode::kOverlay_Mode: 85 case SkXfermode::kDarken_Mode: 86 case SkXfermode::kLighten_Mode: 87 case SkXfermode::kColorDodge_Mode: 88 case SkXfermode::kColorBurn_Mode: 89 case SkXfermode::kHardLight_Mode: 90 case SkXfermode::kSoftLight_Mode: 91 case SkXfermode::kDifference_Mode: 92 case SkXfermode::kExclusion_Mode: 93 case SkXfermode::kHue_Mode: 94 case SkXfermode::kSaturation_Mode: 95 case SkXfermode::kColor_Mode: 96 case SkXfermode::kLuminosity_Mode: 97 // Mode is suppported and handled by pdf graphics state. 98 return mode; 99 default: 100 return SkXfermode::kSrcOver_Mode; // Default mode. 101 } 102} 103 104SkPDFGraphicState::SkPDFGraphicState(const SkPaint& p) 105 : fStrokeWidth(p.getStrokeWidth()) 106 , fStrokeMiter(p.getStrokeMiter()) 107 , fAlpha(p.getAlpha()) 108 , fStrokeCap(SkToU8(p.getStrokeCap())) 109 , fStrokeJoin(SkToU8(p.getStrokeJoin())) 110 , fMode(SkToU8(mode_for_pdf(p.getXfermode()))) {} 111 112// static 113SkPDFGraphicState* SkPDFGraphicState::GetGraphicStateForPaint( 114 SkPDFCanon* canon, const SkPaint& paint) { 115 SkASSERT(canon); 116 SkPDFGraphicState key(paint); 117 if (const SkPDFGraphicState* canonGS = canon->findGraphicState(key)) { 118 // The returned SkPDFGraphicState must be made non-const, 119 // since the emitObject() interface is non-const. But We 120 // promise that there is no way to mutate this object from 121 // here on out. 122 return SkRef(const_cast<SkPDFGraphicState*>(canonGS)); 123 } 124 SkPDFGraphicState* pdfGraphicState = new SkPDFGraphicState(paint); 125 canon->addGraphicState(pdfGraphicState); 126 return pdfGraphicState; 127} 128 129static SkPDFObject* create_invert_function() { 130 // Acrobat crashes if we use a type 0 function, kpdf crashes if we use 131 // a type 2 function, so we use a type 4 function. 132 SkAutoTUnref<SkPDFArray> domainAndRange(new SkPDFArray); 133 domainAndRange->reserve(2); 134 domainAndRange->appendInt(0); 135 domainAndRange->appendInt(1); 136 137 static const char psInvert[] = "{1 exch sub}"; 138 // Do not copy the trailing '\0' into the SkData. 139 SkAutoTUnref<SkData> psInvertStream( 140 SkData::NewWithoutCopy(psInvert, strlen(psInvert))); 141 142 SkPDFStream* invertFunction = new SkPDFStream(psInvertStream.get()); 143 invertFunction->insertInt("FunctionType", 4); 144 invertFunction->insertObject("Domain", SkRef(domainAndRange.get())); 145 invertFunction->insertObject("Range", domainAndRange.detach()); 146 return invertFunction; 147} 148 149SK_DECLARE_STATIC_ONCE_PTR(SkPDFObject, invertFunction); 150 151// static 152SkPDFDict* SkPDFGraphicState::GetSMaskGraphicState(SkPDFFormXObject* sMask, 153 bool invert, 154 SkPDFSMaskMode sMaskMode) { 155 // The practical chances of using the same mask more than once are unlikely 156 // enough that it's not worth canonicalizing. 157 SkAutoTUnref<SkPDFDict> sMaskDict(new SkPDFDict("Mask")); 158 if (sMaskMode == kAlpha_SMaskMode) { 159 sMaskDict->insertName("S", "Alpha"); 160 } else if (sMaskMode == kLuminosity_SMaskMode) { 161 sMaskDict->insertName("S", "Luminosity"); 162 } 163 sMaskDict->insertObjRef("G", SkRef(sMask)); 164 if (invert) { 165 sMaskDict->insertObjRef("TR", SkRef(invertFunction.get(create_invert_function))); 166 } 167 168 SkPDFDict* result = new SkPDFDict("ExtGState"); 169 result->insertObject("SMask", sMaskDict.detach()); 170 return result; 171} 172 173static SkPDFDict* create_no_smask_graphic_state() { 174 SkPDFDict* noSMaskGS = new SkPDFDict("ExtGState"); 175 noSMaskGS->insertName("SMask", "None"); 176 return noSMaskGS; 177} 178SK_DECLARE_STATIC_ONCE_PTR(SkPDFDict, noSMaskGraphicState); 179 180// static 181SkPDFDict* SkPDFGraphicState::GetNoSMaskGraphicState() { 182 return SkRef(noSMaskGraphicState.get(create_no_smask_graphic_state)); 183} 184 185void SkPDFGraphicState::emitObject( 186 SkWStream* stream, 187 const SkPDFObjNumMap& objNumMap, 188 const SkPDFSubstituteMap& substitutes) const { 189 SkAutoTUnref<SkPDFDict> dict(new SkPDFDict("ExtGState")); 190 dict->insertName("Type", "ExtGState"); 191 192 SkScalar alpha = SkIntToScalar(fAlpha) / 0xFF; 193 dict->insertScalar("CA", alpha); 194 dict->insertScalar("ca", alpha); 195 196 SkPaint::Cap strokeCap = (SkPaint::Cap)fStrokeCap; 197 SkPaint::Join strokeJoin = (SkPaint::Join)fStrokeJoin; 198 SkXfermode::Mode xferMode = (SkXfermode::Mode)fMode; 199 200 static_assert(SkPaint::kButt_Cap == 0, "paint_cap_mismatch"); 201 static_assert(SkPaint::kRound_Cap == 1, "paint_cap_mismatch"); 202 static_assert(SkPaint::kSquare_Cap == 2, "paint_cap_mismatch"); 203 static_assert(SkPaint::kCapCount == 3, "paint_cap_mismatch"); 204 SkASSERT(strokeCap >= 0 && strokeCap <= 2); 205 dict->insertInt("LC", strokeCap); 206 207 static_assert(SkPaint::kMiter_Join == 0, "paint_join_mismatch"); 208 static_assert(SkPaint::kRound_Join == 1, "paint_join_mismatch"); 209 static_assert(SkPaint::kBevel_Join == 2, "paint_join_mismatch"); 210 static_assert(SkPaint::kJoinCount == 3, "paint_join_mismatch"); 211 SkASSERT(strokeJoin >= 0 && strokeJoin <= 2); 212 dict->insertInt("LJ", strokeJoin); 213 214 dict->insertScalar("LW", fStrokeWidth); 215 dict->insertScalar("ML", fStrokeMiter); 216 dict->insertBool("SA", true); // SA = Auto stroke adjustment. 217 dict->insertName("BM", as_blend_mode(xferMode)); 218 dict->emitObject(stream, objNumMap, substitutes); 219} 220