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