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