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