Home | History | Annotate | Download | only in pdf
      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 
     14 static 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 
     55 SkPDFGraphicState::~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 
     66 void SkPDFGraphicState::getResources(
     67         const SkTSet<SkPDFObject*>& knownResourceObjects,
     68         SkTSet<SkPDFObject*>* newResourceObjects) {
     69     GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects);
     70 }
     71 
     72 void SkPDFGraphicState::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
     73                                    bool indirect) {
     74     populateDict();
     75     SkPDFDict::emitObject(stream, catalog, indirect);
     76 }
     77 
     78 // static
     79 size_t SkPDFGraphicState::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
     80     populateDict();
     81     return SkPDFDict::getOutputSize(catalog, indirect);
     82 }
     83 
     84 // static
     85 SkTDArray<SkPDFGraphicState::GSCanonicalEntry>& SkPDFGraphicState::CanonicalPaints() {
     86     CanonicalPaintsMutex().assertHeld();
     87     static SkTDArray<SkPDFGraphicState::GSCanonicalEntry> gCanonicalPaints;
     88     return gCanonicalPaints;
     89 }
     90 
     91 SK_DECLARE_STATIC_MUTEX(gCanonicalPaintsMutex);
     92 // static
     93 SkBaseMutex& SkPDFGraphicState::CanonicalPaintsMutex() {
     94     return gCanonicalPaintsMutex;
     95 }
     96 
     97 // static
     98 SkPDFGraphicState* 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
    111 SkPDFObject* 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
    137 SkPDFGraphicState* 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
    170 SkPDFGraphicState* 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
    185 int SkPDFGraphicState::Find(const SkPaint& paint) {
    186     CanonicalPaintsMutex().assertHeld();
    187     GSCanonicalEntry search(&paint);
    188     return CanonicalPaints().find(search);
    189 }
    190 
    191 SkPDFGraphicState::SkPDFGraphicState()
    192     : fPopulated(false),
    193       fSMask(false) {
    194 }
    195 
    196 SkPDFGraphicState::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.
    203 void 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.
    247 bool 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