SkPDFGraphicState.cpp revision e6ea244717feda4265b7062a0462267a0d9e1753
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 "SkLazyPtr.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 NULL; 69 } 70 return NULL; 71} 72 73static bool equivalent(const SkPaint& a, const SkPaint& b) { 74 // We're only interested in some fields of the SkPaint, so we have 75 // a custom equality function. 76 if (SkColorGetA(a.getColor()) != SkColorGetA(b.getColor()) || 77 a.getStrokeCap() != b.getStrokeCap() || 78 a.getStrokeJoin() != b.getStrokeJoin() || 79 a.getStrokeWidth() != b.getStrokeWidth() || 80 a.getStrokeMiter() != b.getStrokeMiter()) { 81 return false; 82 } 83 84 SkXfermode::Mode aXfermodeName = SkXfermode::kSrcOver_Mode; 85 SkXfermode* aXfermode = a.getXfermode(); 86 if (aXfermode) { 87 aXfermode->asMode(&aXfermodeName); 88 } 89 if (aXfermodeName < 0 || aXfermodeName > SkXfermode::kLastMode || 90 as_blend_mode(aXfermodeName) == NULL) { 91 aXfermodeName = SkXfermode::kSrcOver_Mode; 92 } 93 const char* aXfermodeString = as_blend_mode(aXfermodeName); 94 SkASSERT(aXfermodeString != NULL); 95 96 SkXfermode::Mode bXfermodeName = SkXfermode::kSrcOver_Mode; 97 SkXfermode* bXfermode = b.getXfermode(); 98 if (bXfermode) { 99 bXfermode->asMode(&bXfermodeName); 100 } 101 if (bXfermodeName < 0 || bXfermodeName > SkXfermode::kLastMode || 102 as_blend_mode(bXfermodeName) == NULL) { 103 bXfermodeName = SkXfermode::kSrcOver_Mode; 104 } 105 const char* bXfermodeString = as_blend_mode(bXfermodeName); 106 SkASSERT(bXfermodeString != NULL); 107 108 return strcmp(aXfermodeString, bXfermodeString) == 0; 109} 110 111bool SkPDFGraphicState::equals(const SkPaint& paint) const { 112 return equivalent(paint, fPaint); 113} 114 115SkPDFGraphicState::~SkPDFGraphicState() { 116 SkAutoMutexAcquire lock(SkPDFCanon::GetPaintMutex()); 117 if (!fSMask) { 118 SkPDFCanon::GetCanon().removeGraphicState(this); 119 } 120 fResources.unrefAll(); 121} 122 123void SkPDFGraphicState::getResources( 124 const SkTSet<SkPDFObject*>& knownResourceObjects, 125 SkTSet<SkPDFObject*>* newResourceObjects) { 126 GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects); 127} 128 129void SkPDFGraphicState::emitObject(SkWStream* stream, SkPDFCatalog* catalog) { 130 populateDict(); 131 SkPDFDict::emitObject(stream, catalog); 132} 133 134// static 135SkPDFGraphicState* SkPDFGraphicState::GetGraphicStateForPaint( 136 const SkPaint& paint) { 137 SkAutoMutexAcquire lock(SkPDFCanon::GetPaintMutex()); 138 SkPDFGraphicState* pdfGraphicState = 139 SkPDFCanon::GetCanon().findGraphicState(paint); 140 if (pdfGraphicState) { 141 return SkRef(pdfGraphicState); 142 } 143 pdfGraphicState = new SkPDFGraphicState(paint); 144 SkPDFCanon::GetCanon().addGraphicState(pdfGraphicState); 145 return pdfGraphicState; 146} 147 148namespace { 149SkPDFObject* create_invert_function() { 150 // Acrobat crashes if we use a type 0 function, kpdf crashes if we use 151 // a type 2 function, so we use a type 4 function. 152 SkAutoTUnref<SkPDFArray> domainAndRange(new SkPDFArray); 153 domainAndRange->reserve(2); 154 domainAndRange->appendInt(0); 155 domainAndRange->appendInt(1); 156 157 static const char psInvert[] = "{1 exch sub}"; 158 // Do not copy the trailing '\0' into the SkData. 159 SkAutoTUnref<SkData> psInvertStream( 160 SkData::NewWithoutCopy(psInvert, strlen(psInvert))); 161 162 SkPDFStream* invertFunction = SkNEW_ARGS( 163 SkPDFStream, (psInvertStream.get())); 164 invertFunction->insertInt("FunctionType", 4); 165 invertFunction->insert("Domain", domainAndRange.get()); 166 invertFunction->insert("Range", domainAndRange.get()); 167 return invertFunction; 168} 169 170template <typename T> void unref(T* ptr) { ptr->unref(); } 171} // namespace 172 173SK_DECLARE_STATIC_LAZY_PTR(SkPDFObject, invertFunction, 174 create_invert_function, unref<SkPDFObject>); 175 176// static 177SkPDFObject* SkPDFGraphicState::GetInvertFunction() { 178 return invertFunction.get(); 179} 180 181// static 182SkPDFGraphicState* SkPDFGraphicState::GetSMaskGraphicState( 183 SkPDFFormXObject* sMask, bool invert, SkPDFSMaskMode sMaskMode) { 184 // The practical chances of using the same mask more than once are unlikely 185 // enough that it's not worth canonicalizing. 186 SkAutoTUnref<SkPDFDict> sMaskDict(new SkPDFDict("Mask")); 187 if (sMaskMode == kAlpha_SMaskMode) { 188 sMaskDict->insertName("S", "Alpha"); 189 } else if (sMaskMode == kLuminosity_SMaskMode) { 190 sMaskDict->insertName("S", "Luminosity"); 191 } 192 sMaskDict->insert("G", new SkPDFObjRef(sMask))->unref(); 193 194 SkPDFGraphicState* result = new SkPDFGraphicState; 195 result->fPopulated = true; 196 result->fSMask = true; 197 result->insertName("Type", "ExtGState"); 198 result->insert("SMask", sMaskDict.get()); 199 result->fResources.push(sMask); 200 sMask->ref(); 201 202 if (invert) { 203 SkPDFObject* invertFunction = GetInvertFunction(); 204 result->fResources.push(invertFunction); 205 invertFunction->ref(); 206 sMaskDict->insert("TR", new SkPDFObjRef(invertFunction))->unref(); 207 } 208 209 return result; 210} 211 212SkPDFGraphicState* SkPDFGraphicState::CreateNoSMaskGraphicState() { 213 SkPDFGraphicState* noSMaskGS = SkNEW(SkPDFGraphicState); 214 noSMaskGS->fPopulated = true; 215 noSMaskGS->fSMask = true; 216 noSMaskGS->insertName("Type", "ExtGState"); 217 noSMaskGS->insertName("SMask", "None"); 218 return noSMaskGS; 219} 220 221SK_DECLARE_STATIC_LAZY_PTR( 222 SkPDFGraphicState, noSMaskGraphicState, 223 SkPDFGraphicState::CreateNoSMaskGraphicState, unref<SkPDFGraphicState>); 224 225// static 226SkPDFGraphicState* SkPDFGraphicState::GetNoSMaskGraphicState() { 227 return SkRef(noSMaskGraphicState.get()); 228} 229 230SkPDFGraphicState::SkPDFGraphicState() 231 : fPopulated(false), 232 fSMask(false) { 233} 234 235SkPDFGraphicState::SkPDFGraphicState(const SkPaint& paint) 236 : fPaint(paint), 237 fPopulated(false), 238 fSMask(false) { 239} 240 241// populateDict and operator== have to stay in sync with each other. 242void SkPDFGraphicState::populateDict() { 243 if (!fPopulated) { 244 fPopulated = true; 245 insertName("Type", "ExtGState"); 246 247 SkAutoTUnref<SkPDFScalar> alpha( 248 new SkPDFScalar(SkScalarDiv(fPaint.getAlpha(), 0xFF))); 249 insert("CA", alpha.get()); 250 insert("ca", alpha.get()); 251 252 SK_COMPILE_ASSERT(SkPaint::kButt_Cap == 0, paint_cap_mismatch); 253 SK_COMPILE_ASSERT(SkPaint::kRound_Cap == 1, paint_cap_mismatch); 254 SK_COMPILE_ASSERT(SkPaint::kSquare_Cap == 2, paint_cap_mismatch); 255 SK_COMPILE_ASSERT(SkPaint::kCapCount == 3, paint_cap_mismatch); 256 SkASSERT(fPaint.getStrokeCap() >= 0 && fPaint.getStrokeCap() <= 2); 257 insertInt("LC", fPaint.getStrokeCap()); 258 259 SK_COMPILE_ASSERT(SkPaint::kMiter_Join == 0, paint_join_mismatch); 260 SK_COMPILE_ASSERT(SkPaint::kRound_Join == 1, paint_join_mismatch); 261 SK_COMPILE_ASSERT(SkPaint::kBevel_Join == 2, paint_join_mismatch); 262 SK_COMPILE_ASSERT(SkPaint::kJoinCount == 3, paint_join_mismatch); 263 SkASSERT(fPaint.getStrokeJoin() >= 0 && fPaint.getStrokeJoin() <= 2); 264 insertInt("LJ", fPaint.getStrokeJoin()); 265 266 insertScalar("LW", fPaint.getStrokeWidth()); 267 insertScalar("ML", fPaint.getStrokeMiter()); 268 insert("SA", new SkPDFBool(true))->unref(); // Auto stroke adjustment. 269 270 SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode; 271 // If asMode fails, default to kSrcOver_Mode. 272 if (fPaint.getXfermode()) 273 fPaint.getXfermode()->asMode(&xfermode); 274 // If we don't support the mode, just use kSrcOver_Mode. 275 if (xfermode < 0 || xfermode > SkXfermode::kLastMode || 276 as_blend_mode(xfermode) == NULL) { 277 xfermode = SkXfermode::kSrcOver_Mode; 278 NOT_IMPLEMENTED("unsupported xfermode", false); 279 } 280 insertName("BM", as_blend_mode(xfermode)); 281 } 282} 283