text: /*
   * Copyright 2011 Google Inc.
   *
   * Use of this source code is governed by a BSD-style license that can be
   * found in the LICENSE file.
   */
  
  #include "src/pdf/SkPDFGraphicState.h"
  
  #include "include/core/SkData.h"
  #include "include/core/SkPaint.h"
  #include "include/docs/SkPDFDocument.h"
  #include "include/private/SkTo.h"
  #include "src/pdf/SkPDFDocumentPriv.h"
  #include "src/pdf/SkPDFFormXObject.h"
  #include "src/pdf/SkPDFUtils.h"
  
  static const char* as_pdf_blend_mode_name(SkBlendMode mode) {
      const char* name = SkPDFUtils::BlendModeName(mode);
      SkASSERT(name);
      return name;
  }
  
  static int to_stroke_cap(uint8_t cap) {
      // PDF32000.book section 8.4.3.3 "Line Cap Style"
      switch ((SkPaint::Cap)cap) {
          case SkPaint::kButt_Cap:   return 0;
          case SkPaint::kRound_Cap:  return 1;
          case SkPaint::kSquare_Cap: return 2;
          default: SkASSERT(false);  return 0;
      }
  }
  
  static int to_stroke_join(uint8_t join) {
      // PDF32000.book section 8.4.3.4 "Line Join Style"
      switch ((SkPaint::Join)join) {
          case SkPaint::kMiter_Join: return 0;
          case SkPaint::kRound_Join: return 1;
          case SkPaint::kBevel_Join: return 2;
          default: SkASSERT(false);  return 0;
      }
  }
  
  // If a SkXfermode is unsupported in PDF, this function returns
  // SrcOver, otherwise, it returns that Xfermode as a Mode.
  static uint8_t pdf_blend_mode(SkBlendMode mode) {
      if (!SkPDFUtils::BlendModeName(mode)
          || SkBlendMode::kXor  == mode
          || SkBlendMode::kPlus == mode)
      {
          mode = SkBlendMode::kSrcOver;
      }
      return SkToU8((unsigned)mode);
  }
  
  SkPDFIndirectReference SkPDFGraphicState::GetGraphicStateForPaint(SkPDFDocument* doc,
                                                                    const SkPaint& p) {
      SkASSERT(doc);
      const SkBlendMode mode = p.getBlendMode_or(SkBlendMode::kSrcOver);
  
      if (SkPaint::kFill_Style == p.getStyle()) {
          SkPDFFillGraphicState fillKey = {p.getColor4f().fA, pdf_blend_mode(mode)};
          auto& fillMap = doc->fFillGSMap;
          if (SkPDFIndirectReference* statePtr = fillMap.find(fillKey)) {
              return *statePtr;
          }
          SkPDFDict state;
          state.reserve(2);
          state.insertColorComponentF("ca", fillKey.fAlpha);
          state.insertName("BM", as_pdf_blend_mode_name((SkBlendMode)fillKey.fBlendMode));
          SkPDFIndirectReference ref = doc->emit(state);
          fillMap.set(fillKey, ref);
          return ref;
      } else {
          SkPDFStrokeGraphicState strokeKey = {
              p.getStrokeWidth(),
              p.getStrokeMiter(),
              p.getColor4f().fA,
              SkToU8(p.getStrokeCap()),
              SkToU8(p.getStrokeJoin()),
              pdf_blend_mode(mode)
          };
          auto& sMap = doc->fStrokeGSMap;
          if (SkPDFIndirectReference* statePtr = sMap.find(strokeKey)) {
              return *statePtr;
          }
          SkPDFDict state;
          state.reserve(8);
          state.insertColorComponentF("CA", strokeKey.fAlpha);
          state.insertColorComponentF("ca", strokeKey.fAlpha);
          state.insertInt("LC", to_stroke_cap(strokeKey.fStrokeCap));
          state.insertInt("LJ", to_stroke_join(strokeKey.fStrokeJoin));
          state.insertScalar("LW", strokeKey.fStrokeWidth);
          state.insertScalar("ML", strokeKey.fStrokeMiter);
          state.insertBool("SA", true);  // SA = Auto stroke adjustment.
          state.insertName("BM", as_pdf_blend_mode_name((SkBlendMode)strokeKey.fBlendMode));
          SkPDFIndirectReference ref = doc->emit(state);
          sMap.set(strokeKey, ref);
          return ref;
      }
  }
  
  ////////////////////////////////////////////////////////////////////////////////
  
  static SkPDFIndirectReference make_invert_function(SkPDFDocument* doc) {
      // Acrobat crashes if we use a type 0 function, kpdf crashes if we use
      // a type 2 function, so we use a type 4 function.
      static const char psInvert[] = "{1 exch sub}";
      // Do not copy the trailing '\0' into the SkData.
      auto invertFunction = SkData::MakeWithoutCopy(psInvert, strlen(psInvert));
  
      std::unique_ptr<SkPDFDict> dict = SkPDFMakeDict();
      dict->insertInt("FunctionType", 4);
      dict->insertObject("Domain", SkPDFMakeArray(0, 1));
      dict->insertObject("Range", SkPDFMakeArray(0, 1));
      return SkPDFStreamOut(std::move(dict), SkMemoryStream::Make(std::move(invertFunction)), doc);
  }
  
  SkPDFIndirectReference SkPDFGraphicState::GetSMaskGraphicState(SkPDFIndirectReference sMask,
                                                                 bool invert,
                                                                 SkPDFSMaskMode sMaskMode,
                                                                 SkPDFDocument* doc) {
      // The practical chances of using the same mask more than once are unlikely
      // enough that it's not worth canonicalizing.
      auto sMaskDict = SkPDFMakeDict("Mask");
      if (sMaskMode == kAlpha_SMaskMode) {
          sMaskDict->insertName("S", "Alpha");
      } else if (sMaskMode == kLuminosity_SMaskMode) {
          sMaskDict->insertName("S", "Luminosity");
      }
      sMaskDict->insertRef("G", sMask);
      if (invert) {
          // let the doc deduplicate this object.
          if (doc->fInvertFunction == SkPDFIndirectReference()) {
              doc->fInvertFunction = make_invert_function(doc);
          }
          sMaskDict->insertRef("TR", doc->fInvertFunction);
      }
      SkPDFDict result("ExtGState");
      result.insertObject("SMask", std::move(sMaskDict));
      return doc->emit(result);
  }
  