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