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