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 "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         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>&
     86 SkPDFGraphicState::CanonicalPaints() {
     87     // This initialization is only thread safe with gcc.
     88     static SkTDArray<SkPDFGraphicState::GSCanonicalEntry> gCanonicalPaints;
     89     return gCanonicalPaints;
     90 }
     91 
     92 // static
     93 SkBaseMutex& SkPDFGraphicState::CanonicalPaintsMutex() {
     94     // This initialization is only thread safe with gcc or when
     95     // POD-style mutex initialization is used.
     96     SK_DECLARE_STATIC_MUTEX(gCanonicalPaintsMutex);
     97     return gCanonicalPaintsMutex;
     98 }
     99 
    100 // static
    101 SkPDFGraphicState* SkPDFGraphicState::GetGraphicStateForPaint(
    102         const SkPaint& paint) {
    103     SkAutoMutexAcquire lock(CanonicalPaintsMutex());
    104     int index = Find(paint);
    105     if (index >= 0) {
    106         CanonicalPaints()[index].fGraphicState->ref();
    107         return CanonicalPaints()[index].fGraphicState;
    108     }
    109     GSCanonicalEntry newEntry(new SkPDFGraphicState(paint));
    110     CanonicalPaints().push(newEntry);
    111     return newEntry.fGraphicState;
    112 }
    113 
    114 // static
    115 SkPDFObject* SkPDFGraphicState::GetInvertFunction() {
    116     // This assumes that canonicalPaintsMutex is held.
    117     static SkPDFStream* invertFunction = NULL;
    118     if (!invertFunction) {
    119         // Acrobat crashes if we use a type 0 function, kpdf crashes if we use
    120         // a type 2 function, so we use a type 4 function.
    121         SkAutoTUnref<SkPDFArray> domainAndRange(new SkPDFArray);
    122         domainAndRange->reserve(2);
    123         domainAndRange->appendInt(0);
    124         domainAndRange->appendInt(1);
    125 
    126         static const char psInvert[] = "{1 exch sub}";
    127         SkAutoTUnref<SkMemoryStream> psInvertStream(
    128             new SkMemoryStream(&psInvert, strlen(psInvert), true));
    129 
    130         invertFunction = new SkPDFStream(psInvertStream.get());
    131         invertFunction->insertInt("FunctionType", 4);
    132         invertFunction->insert("Domain", domainAndRange.get());
    133         invertFunction->insert("Range", domainAndRange.get());
    134     }
    135     return invertFunction;
    136 }
    137 
    138 // static
    139 SkPDFGraphicState* SkPDFGraphicState::GetSMaskGraphicState(
    140         SkPDFFormXObject* sMask, bool invert, SkPDFSMaskMode sMaskMode) {
    141     // The practical chances of using the same mask more than once are unlikely
    142     // enough that it's not worth canonicalizing.
    143     SkAutoMutexAcquire lock(CanonicalPaintsMutex());
    144 
    145     SkAutoTUnref<SkPDFDict> sMaskDict(new SkPDFDict("Mask"));
    146     if (sMaskMode == kAlpha_SMaskMode) {
    147         sMaskDict->insertName("S", "Alpha");
    148     } else if (sMaskMode == kLuminosity_SMaskMode) {
    149         sMaskDict->insertName("S", "Luminosity");
    150     }
    151     sMaskDict->insert("G", new SkPDFObjRef(sMask))->unref();
    152 
    153     SkPDFGraphicState* result = new SkPDFGraphicState;
    154     result->fPopulated = true;
    155     result->fSMask = true;
    156     result->insertName("Type", "ExtGState");
    157     result->insert("SMask", sMaskDict.get());
    158     result->fResources.push(sMask);
    159     sMask->ref();
    160 
    161     if (invert) {
    162         SkPDFObject* invertFunction = GetInvertFunction();
    163         result->fResources.push(invertFunction);
    164         invertFunction->ref();
    165         sMaskDict->insert("TR", new SkPDFObjRef(invertFunction))->unref();
    166     }
    167 
    168     return result;
    169 }
    170 
    171 // static
    172 SkPDFGraphicState* SkPDFGraphicState::GetNoSMaskGraphicState() {
    173     SkAutoMutexAcquire lock(CanonicalPaintsMutex());
    174     static SkPDFGraphicState* noSMaskGS = NULL;
    175     if (!noSMaskGS) {
    176         noSMaskGS = new SkPDFGraphicState;
    177         noSMaskGS->fPopulated = true;
    178         noSMaskGS->fSMask = true;
    179         noSMaskGS->insertName("Type", "ExtGState");
    180         noSMaskGS->insertName("SMask", "None");
    181     }
    182     noSMaskGS->ref();
    183     return noSMaskGS;
    184 }
    185 
    186 // static
    187 int SkPDFGraphicState::Find(const SkPaint& paint) {
    188     GSCanonicalEntry search(&paint);
    189     return CanonicalPaints().find(search);
    190 }
    191 
    192 SkPDFGraphicState::SkPDFGraphicState()
    193     : fPopulated(false),
    194       fSMask(false) {
    195 }
    196 
    197 SkPDFGraphicState::SkPDFGraphicState(const SkPaint& paint)
    198     : fPaint(paint),
    199       fPopulated(false),
    200       fSMask(false) {
    201 }
    202 
    203 // populateDict and operator== have to stay in sync with each other.
    204 void SkPDFGraphicState::populateDict() {
    205     if (!fPopulated) {
    206         fPopulated = true;
    207         insertName("Type", "ExtGState");
    208 
    209         SkAutoTUnref<SkPDFScalar> alpha(
    210             new SkPDFScalar(SkScalarDiv(fPaint.getAlpha(), 0xFF)));
    211         insert("CA", alpha.get());
    212         insert("ca", alpha.get());
    213 
    214         SK_COMPILE_ASSERT(SkPaint::kButt_Cap == 0, paint_cap_mismatch);
    215         SK_COMPILE_ASSERT(SkPaint::kRound_Cap == 1, paint_cap_mismatch);
    216         SK_COMPILE_ASSERT(SkPaint::kSquare_Cap == 2, paint_cap_mismatch);
    217         SK_COMPILE_ASSERT(SkPaint::kCapCount == 3, paint_cap_mismatch);
    218         SkASSERT(fPaint.getStrokeCap() >= 0 && fPaint.getStrokeCap() <= 2);
    219         insertInt("LC", fPaint.getStrokeCap());
    220 
    221         SK_COMPILE_ASSERT(SkPaint::kMiter_Join == 0, paint_join_mismatch);
    222         SK_COMPILE_ASSERT(SkPaint::kRound_Join == 1, paint_join_mismatch);
    223         SK_COMPILE_ASSERT(SkPaint::kBevel_Join == 2, paint_join_mismatch);
    224         SK_COMPILE_ASSERT(SkPaint::kJoinCount == 3, paint_join_mismatch);
    225         SkASSERT(fPaint.getStrokeJoin() >= 0 && fPaint.getStrokeJoin() <= 2);
    226         insertInt("LJ", fPaint.getStrokeJoin());
    227 
    228         insertScalar("LW", fPaint.getStrokeWidth());
    229         insertScalar("ML", fPaint.getStrokeMiter());
    230         insert("SA", new SkPDFBool(true))->unref();  // Auto stroke adjustment.
    231 
    232         SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode;
    233         // If asMode fails, default to kSrcOver_Mode.
    234         if (fPaint.getXfermode())
    235             fPaint.getXfermode()->asMode(&xfermode);
    236         // If we don't support the mode, just use kSrcOver_Mode.
    237         if (xfermode < 0 || xfermode > SkXfermode::kLastMode ||
    238                 blend_mode_from_xfermode(xfermode) == NULL) {
    239             xfermode = SkXfermode::kSrcOver_Mode;
    240             NOT_IMPLEMENTED("unsupported xfermode", false);
    241         }
    242         insertName("BM", blend_mode_from_xfermode(xfermode));
    243     }
    244 }
    245 
    246 // We're only interested in some fields of the SkPaint, so we have a custom
    247 // operator== function.
    248 bool SkPDFGraphicState::GSCanonicalEntry::operator==(
    249         const SkPDFGraphicState::GSCanonicalEntry& gs) const {
    250     const SkPaint* a = fPaint;
    251     const SkPaint* b = gs.fPaint;
    252     SkASSERT(a != NULL);
    253     SkASSERT(b != NULL);
    254 
    255     if (SkColorGetA(a->getColor()) != SkColorGetA(b->getColor()) ||
    256            a->getStrokeCap() != b->getStrokeCap() ||
    257            a->getStrokeJoin() != b->getStrokeJoin() ||
    258            a->getStrokeWidth() != b->getStrokeWidth() ||
    259            a->getStrokeMiter() != b->getStrokeMiter()) {
    260         return false;
    261     }
    262 
    263     SkXfermode::Mode aXfermodeName = SkXfermode::kSrcOver_Mode;
    264     SkXfermode* aXfermode = a->getXfermode();
    265     if (aXfermode) {
    266         aXfermode->asMode(&aXfermodeName);
    267     }
    268     if (aXfermodeName < 0 || aXfermodeName > SkXfermode::kLastMode ||
    269             blend_mode_from_xfermode(aXfermodeName) == NULL) {
    270         aXfermodeName = SkXfermode::kSrcOver_Mode;
    271     }
    272     const char* aXfermodeString = blend_mode_from_xfermode(aXfermodeName);
    273     SkASSERT(aXfermodeString != NULL);
    274 
    275     SkXfermode::Mode bXfermodeName = SkXfermode::kSrcOver_Mode;
    276     SkXfermode* bXfermode = b->getXfermode();
    277     if (bXfermode) {
    278         bXfermode->asMode(&bXfermodeName);
    279     }
    280     if (bXfermodeName < 0 || bXfermodeName > SkXfermode::kLastMode ||
    281             blend_mode_from_xfermode(bXfermodeName) == NULL) {
    282         bXfermodeName = SkXfermode::kSrcOver_Mode;
    283     }
    284     const char* bXfermodeString = blend_mode_from_xfermode(bXfermodeName);
    285     SkASSERT(bXfermodeString != NULL);
    286 
    287     return strcmp(aXfermodeString, bXfermodeString) == 0;
    288 }
    289