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         // 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