SkPDFGraphicState.cpp revision d1c53aae59ee44377be8bc0cc15e54d46aa530ce
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 "SkPDFFormXObject.h" 9#include "SkPDFGraphicState.h" 10#include "SkPDFUtils.h" 11#include "SkStream.h" 12#include "SkTypes.h" 13 14static const char* blend_mode_from_xfermode(SkXfermode::Mode mode) { 15 switch (mode) { 16 case SkXfermode::kSrcOver_Mode: return "Normal"; 17 case SkXfermode::kModulate_Mode: return "Multiply"; 18 case SkXfermode::kScreen_Mode: return "Screen"; 19 case SkXfermode::kOverlay_Mode: return "Overlay"; 20 case SkXfermode::kDarken_Mode: return "Darken"; 21 case SkXfermode::kLighten_Mode: return "Lighten"; 22 case SkXfermode::kColorDodge_Mode: return "ColorDodge"; 23 case SkXfermode::kColorBurn_Mode: return "ColorBurn"; 24 case SkXfermode::kHardLight_Mode: return "HardLight"; 25 case SkXfermode::kSoftLight_Mode: return "SoftLight"; 26 case SkXfermode::kDifference_Mode: return "Difference"; 27 case SkXfermode::kExclusion_Mode: return "Exclusion"; 28 29 // These are handled in SkPDFDevice::setUpContentEntry. 30 case SkXfermode::kClear_Mode: 31 case SkXfermode::kSrc_Mode: 32 case SkXfermode::kDst_Mode: 33 case SkXfermode::kDstOver_Mode: 34 case SkXfermode::kSrcIn_Mode: 35 case SkXfermode::kDstIn_Mode: 36 case SkXfermode::kSrcOut_Mode: 37 case SkXfermode::kDstOut_Mode: 38 return "Normal"; 39 40 // TODO(vandebo): Figure out if we can support more of these modes. 41 case SkXfermode::kSrcATop_Mode: 42 case SkXfermode::kDstATop_Mode: 43 case SkXfermode::kXor_Mode: 44 case SkXfermode::kPlus_Mode: 45 case SkXfermode::kMultiply_Mode: 46 return NULL; 47 } 48 return NULL; 49} 50 51SkPDFGraphicState::~SkPDFGraphicState() { 52 SkAutoMutexAcquire lock(CanonicalPaintsMutex()); 53 if (!fSMask) { 54 int index = Find(fPaint); 55 SkASSERT(index >= 0); 56 SkASSERT(CanonicalPaints()[index].fGraphicState == this); 57 CanonicalPaints().removeShuffle(index); 58 } 59 fResources.unrefAll(); 60} 61 62void SkPDFGraphicState::getResources(SkTDArray<SkPDFObject*>* resourceList) { 63 GetResourcesHelper(&fResources, resourceList); 64} 65 66void SkPDFGraphicState::emitObject(SkWStream* stream, SkPDFCatalog* catalog, 67 bool indirect) { 68 populateDict(); 69 SkPDFDict::emitObject(stream, catalog, indirect); 70} 71 72// static 73size_t SkPDFGraphicState::getOutputSize(SkPDFCatalog* catalog, bool indirect) { 74 populateDict(); 75 return SkPDFDict::getOutputSize(catalog, indirect); 76} 77 78// static 79SkTDArray<SkPDFGraphicState::GSCanonicalEntry>& 80SkPDFGraphicState::CanonicalPaints() { 81 // This initialization is only thread safe with gcc. 82 static SkTDArray<SkPDFGraphicState::GSCanonicalEntry> gCanonicalPaints; 83 return gCanonicalPaints; 84} 85 86// static 87SkBaseMutex& SkPDFGraphicState::CanonicalPaintsMutex() { 88 // This initialization is only thread safe with gcc or when 89 // POD-style mutex initialization is used. 90 SK_DECLARE_STATIC_MUTEX(gCanonicalPaintsMutex); 91 return gCanonicalPaintsMutex; 92} 93 94// static 95SkPDFGraphicState* SkPDFGraphicState::GetGraphicStateForPaint( 96 const SkPaint& paint) { 97 SkAutoMutexAcquire lock(CanonicalPaintsMutex()); 98 int index = Find(paint); 99 if (index >= 0) { 100 CanonicalPaints()[index].fGraphicState->ref(); 101 return CanonicalPaints()[index].fGraphicState; 102 } 103 GSCanonicalEntry newEntry(new SkPDFGraphicState(paint)); 104 CanonicalPaints().push(newEntry); 105 return newEntry.fGraphicState; 106} 107 108// static 109SkPDFObject* SkPDFGraphicState::GetInvertFunction() { 110 // This assumes that canonicalPaintsMutex is held. 111 static SkPDFStream* invertFunction = NULL; 112 if (!invertFunction) { 113 // Acrobat crashes if we use a type 0 function, kpdf crashes if we use 114 // a type 2 function, so we use a type 4 function. 115 SkAutoTUnref<SkPDFArray> domainAndRange(new SkPDFArray); 116 domainAndRange->reserve(2); 117 domainAndRange->appendInt(0); 118 domainAndRange->appendInt(1); 119 120 static const char psInvert[] = "{1 exch sub}"; 121 SkAutoTUnref<SkMemoryStream> psInvertStream( 122 new SkMemoryStream(&psInvert, strlen(psInvert), true)); 123 124 invertFunction = new SkPDFStream(psInvertStream.get()); 125 invertFunction->insertInt("FunctionType", 4); 126 invertFunction->insert("Domain", domainAndRange.get()); 127 invertFunction->insert("Range", domainAndRange.get()); 128 } 129 return invertFunction; 130} 131 132// static 133SkPDFGraphicState* SkPDFGraphicState::GetSMaskGraphicState( 134 SkPDFFormXObject* sMask, bool invert) { 135 // The practical chances of using the same mask more than once are unlikely 136 // enough that it's not worth canonicalizing. 137 SkAutoMutexAcquire lock(CanonicalPaintsMutex()); 138 139 SkAutoTUnref<SkPDFDict> sMaskDict(new SkPDFDict("Mask")); 140 sMaskDict->insertName("S", "Alpha"); 141 sMaskDict->insert("G", new SkPDFObjRef(sMask))->unref(); 142 143 SkPDFGraphicState* result = new SkPDFGraphicState; 144 result->fPopulated = true; 145 result->fSMask = true; 146 result->insertName("Type", "ExtGState"); 147 result->insert("SMask", sMaskDict.get()); 148 result->fResources.push(sMask); 149 sMask->ref(); 150 151 if (invert) { 152 SkPDFObject* invertFunction = GetInvertFunction(); 153 result->fResources.push(invertFunction); 154 invertFunction->ref(); 155 sMaskDict->insert("TR", new SkPDFObjRef(invertFunction))->unref(); 156 } 157 158 return result; 159} 160 161// static 162SkPDFGraphicState* SkPDFGraphicState::GetNoSMaskGraphicState() { 163 SkAutoMutexAcquire lock(CanonicalPaintsMutex()); 164 static SkPDFGraphicState* noSMaskGS = NULL; 165 if (!noSMaskGS) { 166 noSMaskGS = new SkPDFGraphicState; 167 noSMaskGS->fPopulated = true; 168 noSMaskGS->fSMask = true; 169 noSMaskGS->insertName("Type", "ExtGState"); 170 noSMaskGS->insertName("SMask", "None"); 171 } 172 noSMaskGS->ref(); 173 return noSMaskGS; 174} 175 176// static 177int SkPDFGraphicState::Find(const SkPaint& paint) { 178 GSCanonicalEntry search(&paint); 179 return CanonicalPaints().find(search); 180} 181 182SkPDFGraphicState::SkPDFGraphicState() 183 : fPopulated(false), 184 fSMask(false) { 185} 186 187SkPDFGraphicState::SkPDFGraphicState(const SkPaint& paint) 188 : fPaint(paint), 189 fPopulated(false), 190 fSMask(false) { 191} 192 193// populateDict and operator== have to stay in sync with each other. 194void SkPDFGraphicState::populateDict() { 195 if (!fPopulated) { 196 fPopulated = true; 197 insertName("Type", "ExtGState"); 198 199 SkAutoTUnref<SkPDFScalar> alpha( 200 new SkPDFScalar(SkScalarDiv(fPaint.getAlpha(), 0xFF))); 201 insert("CA", alpha.get()); 202 insert("ca", alpha.get()); 203 204 SK_COMPILE_ASSERT(SkPaint::kButt_Cap == 0, paint_cap_mismatch); 205 SK_COMPILE_ASSERT(SkPaint::kRound_Cap == 1, paint_cap_mismatch); 206 SK_COMPILE_ASSERT(SkPaint::kSquare_Cap == 2, paint_cap_mismatch); 207 SK_COMPILE_ASSERT(SkPaint::kCapCount == 3, paint_cap_mismatch); 208 SkASSERT(fPaint.getStrokeCap() >= 0 && fPaint.getStrokeCap() <= 2); 209 insertInt("LC", fPaint.getStrokeCap()); 210 211 SK_COMPILE_ASSERT(SkPaint::kMiter_Join == 0, paint_join_mismatch); 212 SK_COMPILE_ASSERT(SkPaint::kRound_Join == 1, paint_join_mismatch); 213 SK_COMPILE_ASSERT(SkPaint::kBevel_Join == 2, paint_join_mismatch); 214 SK_COMPILE_ASSERT(SkPaint::kJoinCount == 3, paint_join_mismatch); 215 SkASSERT(fPaint.getStrokeJoin() >= 0 && fPaint.getStrokeJoin() <= 2); 216 insertInt("LJ", fPaint.getStrokeJoin()); 217 218 insertScalar("LW", fPaint.getStrokeWidth()); 219 insertScalar("ML", fPaint.getStrokeMiter()); 220 insert("SA", new SkPDFBool(true))->unref(); // Auto stroke adjustment. 221 222 SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode; 223 // If asMode fails, default to kSrcOver_Mode. 224 if (fPaint.getXfermode()) 225 fPaint.getXfermode()->asMode(&xfermode); 226 // If we don't support the mode, just use kSrcOver_Mode. 227 if (xfermode < 0 || xfermode > SkXfermode::kLastMode || 228 blend_mode_from_xfermode(xfermode) == NULL) { 229 xfermode = SkXfermode::kSrcOver_Mode; 230 NOT_IMPLEMENTED("unsupported xfermode", false); 231 } 232 insertName("BM", blend_mode_from_xfermode(xfermode)); 233 } 234} 235 236// We're only interested in some fields of the SkPaint, so we have a custom 237// operator== function. 238bool SkPDFGraphicState::GSCanonicalEntry::operator==( 239 const SkPDFGraphicState::GSCanonicalEntry& gs) const { 240 const SkPaint* a = fPaint; 241 const SkPaint* b = gs.fPaint; 242 SkASSERT(a != NULL); 243 SkASSERT(b != NULL); 244 245 if (SkColorGetA(a->getColor()) != SkColorGetA(b->getColor()) || 246 a->getStrokeCap() != b->getStrokeCap() || 247 a->getStrokeJoin() != b->getStrokeJoin() || 248 a->getStrokeWidth() != b->getStrokeWidth() || 249 a->getStrokeMiter() != b->getStrokeMiter()) { 250 return false; 251 } 252 253 SkXfermode::Mode aXfermodeName = SkXfermode::kSrcOver_Mode; 254 SkXfermode* aXfermode = a->getXfermode(); 255 if (aXfermode) { 256 aXfermode->asMode(&aXfermodeName); 257 } 258 if (aXfermodeName < 0 || aXfermodeName > SkXfermode::kLastMode || 259 blend_mode_from_xfermode(aXfermodeName) == NULL) { 260 aXfermodeName = SkXfermode::kSrcOver_Mode; 261 } 262 const char* aXfermodeString = blend_mode_from_xfermode(aXfermodeName); 263 SkASSERT(aXfermodeString != NULL); 264 265 SkXfermode::Mode bXfermodeName = SkXfermode::kSrcOver_Mode; 266 SkXfermode* bXfermode = b->getXfermode(); 267 if (bXfermode) { 268 bXfermode->asMode(&bXfermodeName); 269 } 270 if (bXfermodeName < 0 || bXfermodeName > SkXfermode::kLastMode || 271 blend_mode_from_xfermode(bXfermodeName) == NULL) { 272 bXfermodeName = SkXfermode::kSrcOver_Mode; 273 } 274 const char* bXfermodeString = blend_mode_from_xfermode(bXfermodeName); 275 SkASSERT(bXfermodeString != NULL); 276 277 return strcmp(aXfermodeString, bXfermodeString) == 0; 278} 279