text: /*
   * Copyright 2012 Google Inc.
   *
   * Use of this source code is governed by a BSD-style license that can be
   * found in the LICENSE file.
   */
  
  #include "src/gpu/GrSWMaskHelper.h"
  
  #include "include/gpu/GrRecordingContext.h"
  #include "src/core/SkMatrixProvider.h"
  #include "src/gpu/GrBitmapTextureMaker.h"
  #include "src/gpu/GrCaps.h"
  #include "src/gpu/GrProxyProvider.h"
  #include "src/gpu/GrRecordingContextPriv.h"
  #include "src/gpu/GrSurfaceContext.h"
  #include "src/gpu/GrTextureProxy.h"
  #include "src/gpu/geometry/GrStyledShape.h"
  
  /*
   * Convert a boolean operation into a transfer mode code
   */
  static SkBlendMode op_to_mode(SkRegion::Op op) {
  
      static const SkBlendMode modeMap[] = {
          SkBlendMode::kDstOut,   // kDifference_Op
          SkBlendMode::kModulate, // kIntersect_Op
          SkBlendMode::kSrcOver,  // kUnion_Op
          SkBlendMode::kXor,      // kXOR_Op
          SkBlendMode::kClear,    // kReverseDifference_Op
          SkBlendMode::kSrc,      // kReplace_Op
      };
  
      return modeMap[op];
  }
  
  static SkPaint get_paint(SkRegion::Op op, GrAA aa, uint8_t alpha) {
      SkPaint paint;
      paint.setBlendMode(op_to_mode(op));
      paint.setAntiAlias(GrAA::kYes == aa);
      // SkPaint's color is unpremul so this will produce alpha in every channel.
      paint.setColor(SkColorSetARGB(alpha, 255, 255, 255));
      return paint;
  }
  
  /**
   * Draw a single rect element of the clip stack into the accumulation bitmap
   */
  void GrSWMaskHelper::drawRect(const SkRect& rect, const SkMatrix& matrix, SkRegion::Op op, GrAA aa,
                                uint8_t alpha) {
      SkMatrix translatedMatrix = matrix;
      translatedMatrix.postTranslate(fTranslate.fX, fTranslate.fY);
      SkSimpleMatrixProvider matrixProvider(translatedMatrix);
      fDraw.fMatrixProvider = &matrixProvider;
  
      fDraw.drawRect(rect, get_paint(op, aa, alpha));
  }
  
  void GrSWMaskHelper::drawRRect(const SkRRect& rrect, const SkMatrix& matrix, SkRegion::Op op,
                                 GrAA aa, uint8_t alpha) {
      SkMatrix translatedMatrix = matrix;
      translatedMatrix.postTranslate(fTranslate.fX, fTranslate.fY);
      SkSimpleMatrixProvider matrixProvider(translatedMatrix);
      fDraw.fMatrixProvider = &matrixProvider;
  
      fDraw.drawRRect(rrect, get_paint(op, aa, alpha));
  }
  
  /**
   * Draw a single path element of the clip stack into the accumulation bitmap
   */
  void GrSWMaskHelper::drawShape(const GrStyledShape& shape, const SkMatrix& matrix, SkRegion::Op op,
                                 GrAA aa, uint8_t alpha) {
      SkPaint paint = get_paint(op, aa, alpha);
      paint.setPathEffect(shape.style().refPathEffect());
      shape.style().strokeRec().applyToPaint(&paint);
  
      SkMatrix translatedMatrix = matrix;
      translatedMatrix.postTranslate(fTranslate.fX, fTranslate.fY);
      SkSimpleMatrixProvider matrixProvider(translatedMatrix);
      fDraw.fMatrixProvider = &matrixProvider;
  
      SkPath path;
      shape.asPath(&path);
      if (SkRegion::kReplace_Op == op && 0xFF == alpha) {
          SkASSERT(0xFF == paint.getAlpha());
          fDraw.drawPathCoverage(path, paint);
      } else {
          fDraw.drawPath(path, paint);
      }
  }
  
  void GrSWMaskHelper::drawShape(const GrShape& shape, const SkMatrix& matrix, SkRegion::Op op,
                                 GrAA aa, uint8_t alpha) {
      SkPaint paint = get_paint(op, aa, alpha);
  
      SkMatrix translatedMatrix = matrix;
      translatedMatrix.postTranslate(fTranslate.fX, fTranslate.fY);
      SkSimpleMatrixProvider matrixProvider(translatedMatrix);
      fDraw.fMatrixProvider = &matrixProvider;
  
      if (shape.inverted()) {
          if (shape.isEmpty() || shape.isLine() || shape.isPoint()) {
              // These shapes are empty for simple fills, so when inverted, cover everything
              fDraw.drawPaint(paint);
              return;
          }
          // Else fall through to the draw method using asPath(), which will toggle fill type properly
      } else if (shape.isEmpty() || shape.isLine() || shape.isPoint()) {
          // Do nothing, these shapes do not cover any pixels for simple fills
          return;
      } else if (shape.isRect()) {
          fDraw.drawRect(shape.rect(), paint);
          return;
      } else if (shape.isRRect()) {
          fDraw.drawRRect(shape.rrect(), paint);
          return;
      }
  
      // A complex, or inverse-filled shape, so go through drawPath.
      SkPath path;
      shape.asPath(&path);
      if (SkRegion::kReplace_Op == op && 0xFF == alpha) {
          SkASSERT(0xFF == paint.getAlpha());
          fDraw.drawPathCoverage(path, paint);
      } else {
          fDraw.drawPath(path, paint);
      }
  }
  
  bool GrSWMaskHelper::init(const SkIRect& resultBounds) {
      // We will need to translate draws so the bound's UL corner is at the origin
      fTranslate = {-SkIntToScalar(resultBounds.fLeft), -SkIntToScalar(resultBounds.fTop)};
      SkIRect bounds = SkIRect::MakeWH(resultBounds.width(), resultBounds.height());
  
      const SkImageInfo bmImageInfo = SkImageInfo::MakeA8(bounds.width(), bounds.height());
      if (!fPixels->tryAlloc(bmImageInfo)) {
          return false;
      }
      fPixels->erase(0);
  
      fDraw.fDst      = *fPixels;
      fRasterClip.setRect(bounds);
      fDraw.fRC       = &fRasterClip;
      return true;
  }
  
  GrSurfaceProxyView GrSWMaskHelper::toTextureView(GrRecordingContext* context, SkBackingFit fit) {
      SkImageInfo ii = SkImageInfo::MakeA8(fPixels->width(), fPixels->height());
      size_t rowBytes = fPixels->rowBytes();
  
      SkBitmap bitmap;
      SkAssertResult(bitmap.installPixels(ii, fPixels->detachPixels(), rowBytes,
                                          [](void* addr, void* context) { sk_free(addr); },
                                          nullptr));
      bitmap.setImmutable();
  
      GrBitmapTextureMaker maker(context, bitmap, fit);
      return maker.view(GrMipmapped::kNo);
  }
  