add native draw bitmap with alpha mask

PDFs draw bitmaps with alpha by supplying an
image alpha mask along side the image color data.

The alpha mask may not be the same scale as the
image data. Skia doesn't have a direct mapping
for this draw call, but can come close by using
the internal SkCompositeShader class.

This scales the alpha mask to match the size of
the image data, then puts both in the composite
shader to draw.

There are more things to do:

- Allow Alpha8 or Gray8 to draw scaled to
  a matching device (blit doesn't work today)

Temporary code marked with
a TODO implements this functionality.

- Refactor PDFium to allow SkComposeShader's
  components to take a matrix.

The latter goal will defer drawing so that PDFium
doesn't resize the mask, discarding resolution,
before Skia has a chance to record it.

Additionally fix drawing text with a negative
font size.

R=reed@google.com,dsinclair@chromium.org,herb@chromium.org

Review-Url: https://codereview.chromium.org/2182763002
diff --git a/core/fpdfapi/fpdf_render/fpdf_render_image.cpp b/core/fpdfapi/fpdf_render/fpdf_render_image.cpp
index c93600f..8d638f6 100644
--- a/core/fpdfapi/fpdf_render/fpdf_render_image.cpp
+++ b/core/fpdfapi/fpdf_render/fpdf_render_image.cpp
@@ -646,6 +646,7 @@
       bitmap_device1.GetBitmap(), rect.left, rect.top, m_BlendType);
   return FALSE;
 }
+
 FX_BOOL CPDF_ImageRenderer::DrawMaskedImage() {
   if (m_pRenderStatus->m_bPrint &&
       !(m_pRenderStatus->m_pDevice->GetRenderCaps() & FXRC_BLEND_MODE)) {
@@ -665,7 +666,11 @@
   if (!bitmap_device1.Create(width, height, FXDIB_Rgb32, nullptr))
     return TRUE;
 
+#if defined _SKIA_SUPPORT_
+  bitmap_device1.Clear(0xffffff);
+#else
   bitmap_device1.GetBitmap()->Clear(0xffffff);
+#endif
   {
     CPDF_RenderStatus bitmap_render;
     bitmap_render.Initialize(m_pRenderStatus->m_pContext, &bitmap_device1,
@@ -682,7 +687,11 @@
     if (!bitmap_device2.Create(width, height, FXDIB_8bppRgb, nullptr))
       return TRUE;
 
+#if defined _SKIA_SUPPORT_
+    bitmap_device2.Clear(0);
+#else
     bitmap_device2.GetBitmap()->Clear(0);
+#endif
     CPDF_RenderStatus bitmap_render;
     bitmap_render.Initialize(m_pRenderStatus->m_pContext, &bitmap_device2,
                              nullptr, nullptr, nullptr, nullptr, nullptr, 0,
@@ -731,17 +740,21 @@
         }
       }
     }
+#ifdef _SKIA_SUPPORT_
+    m_pRenderStatus->m_pDevice->SetBitsWithMask(
+        bitmap_device1.GetBitmap(), bitmap_device2.GetBitmap(), rect.left,
+        rect.top, m_BitmapAlpha, m_BlendType);
+  }
+#else
     bitmap_device2.GetBitmap()->ConvertFormat(FXDIB_8bppMask);
     bitmap_device1.GetBitmap()->MultiplyAlpha(bitmap_device2.GetBitmap());
-#ifdef _SKIA_SUPPORT_
-    CFX_SkiaDeviceDriver::PreMultiply(bitmap_device1.GetBitmap());
-#endif
     if (m_BitmapAlpha < 255) {
       bitmap_device1.GetBitmap()->MultiplyAlpha(m_BitmapAlpha);
     }
   }
   m_pRenderStatus->m_pDevice->SetDIBitsWithBlend(
       bitmap_device1.GetBitmap(), rect.left, rect.top, m_BlendType);
+#endif  //  _SKIA_SUPPORT_
   return FALSE;
 }
 
@@ -756,7 +769,8 @@
   }
 #ifdef _SKIA_SUPPORT_
   CFX_DIBitmap* premultiplied = m_pDIBSource->Clone();
-  CFX_SkiaDeviceDriver::PreMultiply(premultiplied);
+  if (m_pDIBSource->HasAlpha())
+    CFX_SkiaDeviceDriver::PreMultiply(premultiplied);
   if (m_pRenderStatus->m_pDevice->StartDIBitsWithBlend(
           premultiplied, m_BitmapAlpha, m_FillArgb, &m_ImageMatrix, m_Flags,
           m_DeviceHandle, m_BlendType)) {
diff --git a/core/fxge/ge/cfx_renderdevice.cpp b/core/fxge/ge/cfx_renderdevice.cpp
index f04f4ba..53ee039 100644
--- a/core/fxge/ge/cfx_renderdevice.cpp
+++ b/core/fxge/ge/cfx_renderdevice.cpp
@@ -811,10 +811,19 @@
 }
 
 #ifdef _SKIA_SUPPORT_
-
 void CFX_RenderDevice::DebugVerifyBitmapIsPreMultiplied() const {
   SkASSERT(0);
 }
+
+bool CFX_RenderDevice::SetBitsWithMask(const CFX_DIBSource* pBitmap,
+                                       const CFX_DIBSource* pMask,
+                                       int left,
+                                       int top,
+                                       int bitmap_alpha,
+                                       int blend_type) {
+  return m_pDeviceDriver->SetBitsWithMask(pBitmap, pMask, left, top,
+                                          bitmap_alpha, blend_type);
+}
 #endif
 
 FX_BOOL CFX_RenderDevice::DrawNormalText(int nChars,
diff --git a/core/fxge/ifx_renderdevicedriver.cpp b/core/fxge/ifx_renderdevicedriver.cpp
index 9b7f985..25c3a4a 100644
--- a/core/fxge/ifx_renderdevicedriver.cpp
+++ b/core/fxge/ifx_renderdevicedriver.cpp
@@ -92,3 +92,12 @@
                                             FX_BOOL bAlphaMode) {
   return false;
 }
+
+bool IFX_RenderDeviceDriver::SetBitsWithMask(const CFX_DIBSource* pBitmap,
+                                             const CFX_DIBSource* pMask,
+                                             int left,
+                                             int top,
+                                             int bitmap_alpha,
+                                             int blend_type) {
+  return false;
+}
diff --git a/core/fxge/include/cfx_fxgedevice.h b/core/fxge/include/cfx_fxgedevice.h
index a5dba37..7f34f1c 100644
--- a/core/fxge/include/cfx_fxgedevice.h
+++ b/core/fxge/include/cfx_fxgedevice.h
@@ -28,8 +28,15 @@
 
 #ifdef _SKIA_SUPPORT_
   bool AttachRecorder(SkPictureRecorder* recorder);
+  void Clear(uint32_t color);
   SkPictureRecorder* CreateRecorder(int size_x, int size_y);
   void DebugVerifyBitmapIsPreMultiplied() const override;
+  bool SetBitsWithMask(const CFX_DIBSource* pBitmap,
+                       const CFX_DIBSource* pMask,
+                       int left,
+                       int top,
+                       int bitmap_alpha,
+                       int blend_type) override;
 #endif
 
  private:
diff --git a/core/fxge/include/cfx_renderdevice.h b/core/fxge/include/cfx_renderdevice.h
index fafa626..390d39b 100644
--- a/core/fxge/include/cfx_renderdevice.h
+++ b/core/fxge/include/cfx_renderdevice.h
@@ -253,6 +253,12 @@
 
 #ifdef _SKIA_SUPPORT_
   virtual void DebugVerifyBitmapIsPreMultiplied() const;
+  virtual bool SetBitsWithMask(const CFX_DIBSource* pBitmap,
+                               const CFX_DIBSource* pMask,
+                               int left,
+                               int top,
+                               int bitmap_alpha,
+                               int blend_type);
   void Flush();
 #endif
 
diff --git a/core/fxge/include/ifx_renderdevicedriver.h b/core/fxge/include/ifx_renderdevicedriver.h
index 572469b..8b20cf0 100644
--- a/core/fxge/include/ifx_renderdevicedriver.h
+++ b/core/fxge/include/ifx_renderdevicedriver.h
@@ -99,6 +99,12 @@
                               const FX_RECT& clip_rect,
                               int alpha,
                               FX_BOOL bAlphaMode);
+  virtual bool SetBitsWithMask(const CFX_DIBSource* pBitmap,
+                               const CFX_DIBSource* pMask,
+                               int left,
+                               int top,
+                               int bitmap_alpha,
+                               int blend_type);
 };
 
 #endif  // CORE_FXGE_INCLUDE_IFX_RENDERDEVICEDRIVER_H_
diff --git a/core/fxge/skia/fx_skia_device.cpp b/core/fxge/skia/fx_skia_device.cpp
index 49c4639..3effa0a 100644
--- a/core/fxge/skia/fx_skia_device.cpp
+++ b/core/fxge/skia/fx_skia_device.cpp
@@ -24,9 +24,11 @@
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkColorFilter.h"
 #include "third_party/skia/include/core/SkColorPriv.h"
+#include "third_party/skia/include/core/SkMaskFilter.h"
 #include "third_party/skia/include/core/SkPaint.h"
 #include "third_party/skia/include/core/SkPath.h"
 #include "third_party/skia/include/core/SkPictureRecorder.h"
+#include "third_party/skia/include/core/SkShader.h"
 #include "third_party/skia/include/core/SkStream.h"
 #include "third_party/skia/include/core/SkTypeface.h"
 #include "third_party/skia/include/effects/SkDashPathEffect.h"
@@ -140,9 +142,10 @@
 }
 
 // use when pdf's y-axis points up insead of down
-SkMatrix ToFlippedSkMatrix(const CFX_Matrix& m) {
+SkMatrix ToFlippedSkMatrix(const CFX_Matrix& m, SkScalar flip) {
   SkMatrix skMatrix;
-  skMatrix.setAll(m.a, -m.c, m.e, m.b, -m.d, m.f, 0, 0, 1);
+  skMatrix.setAll(m.a * flip, -m.c * flip, m.e, m.b * flip, -m.d * flip, m.f, 0,
+                  0, 1);
   return skMatrix;
 }
 
@@ -367,6 +370,107 @@
   clip->lineTo(IntersectSides(rectPts[maxBounds], slope, startEdgePt));
 }
 
+void SetBitmapMatrix(const CFX_Matrix* pMatrix,
+                     int width,
+                     int height,
+                     SkMatrix* skMatrix) {
+  const CFX_Matrix& m = *pMatrix;
+  skMatrix->setAll(m.a / width, -m.c / height, m.c + m.e, m.b / width,
+                   -m.d / height, m.d + m.f, 0, 0, 1);
+}
+
+void SetBitmapPaint(bool isAlphaMask,
+                    uint32_t argb,
+                    int bitmap_alpha,
+                    int blend_type,
+                    SkPaint* paint) {
+  paint->setAntiAlias(true);
+  if (isAlphaMask) {
+    paint->setColorFilter(
+        SkColorFilter::MakeModeFilter(argb, SkXfermode::kSrc_Mode));
+  }
+  // paint->setFilterQuality(kHigh_SkFilterQuality);
+  paint->setXfermodeMode(GetSkiaBlendMode(blend_type));
+  paint->setAlpha(bitmap_alpha);
+}
+
+bool Upsample(const CFX_DIBSource* pSource,
+              std::unique_ptr<uint8_t, FxFreeDeleter>& dst8Storage,
+              std::unique_ptr<uint32_t, FxFreeDeleter>& dst32Storage,
+              SkColorTable** ctPtr,
+              SkBitmap* skBitmap,
+              int* widthPtr,
+              int* heightPtr,
+              bool forceAlpha) {
+  void* buffer = pSource->GetBuffer();
+  if (!buffer)
+    return false;
+  SkColorType colorType = forceAlpha || pSource->IsAlphaMask()
+                              ? SkColorType::kAlpha_8_SkColorType
+                              : SkColorType::kGray_8_SkColorType;
+  SkAlphaType alphaType =
+      pSource->IsAlphaMask() ? kPremul_SkAlphaType : kOpaque_SkAlphaType;
+  int width = pSource->GetWidth();
+  int height = pSource->GetHeight();
+  int rowBytes = pSource->GetPitch();
+  switch (pSource->GetBPP()) {
+    case 1: {
+      dst8Storage.reset(FX_Alloc2D(uint8_t, width, height));
+      uint8_t* dst8Pixels = dst8Storage.get();
+      for (int y = 0; y < height; ++y) {
+        const uint8_t* srcRow =
+            static_cast<const uint8_t*>(buffer) + y * rowBytes;
+        uint8_t* dstRow = dst8Pixels + y * width;
+        for (int x = 0; x < width; ++x)
+          dstRow[x] = srcRow[x >> 3] & (1 << (~x & 0x07)) ? 0xFF : 0x00;
+      }
+      buffer = dst8Storage.get();
+      rowBytes = width;
+      break;
+    }
+    case 8:
+      if (pSource->GetPalette()) {
+        *ctPtr =
+            new SkColorTable(pSource->GetPalette(), pSource->GetPaletteSize());
+        colorType = SkColorType::kIndex_8_SkColorType;
+      }
+      break;
+    case 24: {
+      dst32Storage.reset(FX_Alloc2D(uint32_t, width, height));
+      uint32_t* dst32Pixels = dst32Storage.get();
+      for (int y = 0; y < height; ++y) {
+        const uint8_t* srcRow =
+            static_cast<const uint8_t*>(buffer) + y * rowBytes;
+        uint32_t* dstRow = dst32Pixels + y * width;
+        for (int x = 0; x < width; ++x) {
+          dstRow[x] = SkPackARGB32(0xFF, srcRow[x * 3 + 2], srcRow[x * 3 + 1],
+                                   srcRow[x * 3 + 0]);
+        }
+      }
+      buffer = dst32Storage.get();
+      rowBytes = width * sizeof(uint32_t);
+      colorType = SkColorType::kN32_SkColorType;
+      alphaType = kOpaque_SkAlphaType;
+      break;
+    }
+    case 32:
+      colorType = SkColorType::kN32_SkColorType;
+      alphaType = kPremul_SkAlphaType;
+      pSource->DebugVerifyBitmapIsPreMultiplied(buffer);
+      break;
+    default:
+      SkASSERT(0);  // TODO(caryclark) ensure that all cases are covered
+      colorType = SkColorType::kUnknown_SkColorType;
+  }
+  SkImageInfo imageInfo =
+      SkImageInfo::Make(width, height, colorType, alphaType);
+  skBitmap->installPixels(imageInfo, buffer, rowBytes, *ctPtr, nullptr,
+                          nullptr);
+  *widthPtr = width;
+  *heightPtr = height;
+  return true;
+}
+
 }  // namespace
 
 // Encapsulate the state used for successive text and path draws so that
@@ -500,22 +604,24 @@
     int count = m_positions.count();
     m_positions.setCount(nChars + count);
     m_glyphs.setCount(nChars + count);
+    SkScalar flip = m_fontSize < 0 ? -1 : 1;
     for (int index = 0; index < nChars; ++index) {
       const FXTEXT_CHARPOS& cp = pCharPos[index];
-      m_positions[index + count] = {cp.m_OriginX, cp.m_OriginY};
+      m_positions[index + count] = {cp.m_OriginX * flip, cp.m_OriginY * flip};
       m_glyphs[index + count] = (uint16_t)cp.m_GlyphIndex;
     }
     SkPoint delta;
     if (MatrixOffset(pMatrix, &delta)) {
       for (int index = 0; index < nChars; ++index)
-        m_positions[index + count].offset(delta.fX, -delta.fY);
+        m_positions[index + count].offset(delta.fX * flip, -delta.fY * flip);
     }
     m_drawText = true;
     return true;
   }
 
   void FlushText(CFX_SkiaDeviceDriver* pDriver) {
-    SkMatrix skMatrix = ToFlippedSkMatrix(m_drawMatrix);
+    SkScalar flip = m_fontSize < 0 ? -1 : 1;
+    SkMatrix skMatrix = ToFlippedSkMatrix(m_drawMatrix, flip);
     SkPaint skPaint;
     skPaint.setAntiAlias(true);
     skPaint.setColor(m_fillColor);
@@ -919,7 +1025,8 @@
   paint.setTextSize(font_size);
   paint.setSubpixelText(true);
   m_pCanvas->save();
-  SkMatrix skMatrix = ToFlippedSkMatrix(*pObject2Device);
+  SkScalar flip = font_size < 0 ? -1 : 1;
+  SkMatrix skMatrix = ToFlippedSkMatrix(*pObject2Device, flip);
   m_pCanvas->concat(skMatrix);
   SkTDArray<SkPoint> positions;
   positions.setCount(nChars);
@@ -927,7 +1034,7 @@
   glyphs.setCount(nChars);
   for (int index = 0; index < nChars; ++index) {
     const FXTEXT_CHARPOS& cp = pCharPos[index];
-    positions[index] = {cp.m_OriginX, cp.m_OriginY};
+    positions[index] = {cp.m_OriginX * flip, cp.m_OriginY * flip};
     glyphs[index] = (uint16_t)cp.m_GlyphIndex;
   }
   m_pCanvas->drawPosText(glyphs.begin(), nChars * 2, positions.begin(), paint);
@@ -1339,86 +1446,39 @@
                                           void*& handle,
                                           int blend_type) {
   DebugValidate(m_pBitmap, m_pOriDevice);
-  SkColorType colorType = pSource->IsAlphaMask()
-                              ? SkColorType::kAlpha_8_SkColorType
-                              : SkColorType::kGray_8_SkColorType;
-  SkAlphaType alphaType =
-      pSource->IsAlphaMask() ? kPremul_SkAlphaType : kOpaque_SkAlphaType;
   SkColorTable* ct = nullptr;
-  void* buffer = pSource->GetBuffer();
-  if (!buffer)
-    return FALSE;
   std::unique_ptr<uint8_t, FxFreeDeleter> dst8Storage;
   std::unique_ptr<uint32_t, FxFreeDeleter> dst32Storage;
-  int width = pSource->GetWidth();
-  int height = pSource->GetHeight();
-  int rowBytes = pSource->GetPitch();
-  switch (pSource->GetBPP()) {
-    case 1: {
-      dst8Storage.reset(FX_Alloc2D(uint8_t, width, height));
-      uint8_t* dst8Pixels = dst8Storage.get();
-      for (int y = 0; y < height; ++y) {
-        const uint8_t* srcRow =
-            static_cast<const uint8_t*>(buffer) + y * rowBytes;
-        uint8_t* dstRow = dst8Pixels + y * width;
-        for (int x = 0; x < width; ++x)
-          dstRow[x] = srcRow[x >> 3] & (1 << (~x & 0x07)) ? 0xFF : 0x00;
-      }
-      buffer = dst8Storage.get();
-      rowBytes = width;
-    } break;
-    case 8:
-      if (pSource->GetPalette()) {
-        ct = new SkColorTable(pSource->GetPalette(), pSource->GetPaletteSize());
-        colorType = SkColorType::kIndex_8_SkColorType;
-      }
-      break;
-    case 24: {
-      dst32Storage.reset(FX_Alloc2D(uint32_t, width, height));
-      uint32_t* dst32Pixels = dst32Storage.get();
-      for (int y = 0; y < height; ++y) {
-        const uint8_t* srcRow =
-            static_cast<const uint8_t*>(buffer) + y * rowBytes;
-        uint32_t* dstRow = dst32Pixels + y * width;
-        for (int x = 0; x < width; ++x) {
-          dstRow[x] = SkPackARGB32(0xFF, srcRow[x * 3 + 2], srcRow[x * 3 + 1],
-                                   srcRow[x * 3 + 0]);
-        }
-      }
-      buffer = dst32Storage.get();
-      rowBytes = width * sizeof(uint32_t);
-      colorType = SkColorType::kN32_SkColorType;
-      alphaType = kOpaque_SkAlphaType;
-    } break;
-    case 32:
-      colorType = SkColorType::kN32_SkColorType;
-      alphaType = kPremul_SkAlphaType;
-      pSource->DebugVerifyBitmapIsPreMultiplied(buffer);
-      break;
-    default:
-      SkASSERT(0);  // TODO(caryclark) ensure that all cases are covered
-      colorType = SkColorType::kUnknown_SkColorType;
-  }
-  SkImageInfo imageInfo =
-      SkImageInfo::Make(width, height, colorType, alphaType);
   SkBitmap skBitmap;
-  skBitmap.installPixels(imageInfo, buffer, rowBytes, ct, nullptr, nullptr);
+  int width, height;
+  if (!Upsample(pSource, dst8Storage, dst32Storage, &ct, &skBitmap, &width,
+                &height, false)) {
+    return FALSE;
+  }
   m_pCanvas->save();
   SkMatrix skMatrix;
-  const CFX_Matrix& m = *pMatrix;
-  skMatrix.setAll(m.a / width, -m.c / height, m.c + m.e, m.b / width,
-                  -m.d / height, m.d + m.f, 0, 0, 1);
+  SetBitmapMatrix(pMatrix, width, height, &skMatrix);
   m_pCanvas->concat(skMatrix);
   SkPaint paint;
-  paint.setAntiAlias(true);
-  if (pSource->IsAlphaMask()) {
-    paint.setColorFilter(
-        SkColorFilter::MakeModeFilter(argb, SkXfermode::kSrc_Mode));
+  SetBitmapPaint(pSource->IsAlphaMask(), argb, bitmap_alpha, blend_type,
+                 &paint);
+  // TODO(caryclark) Once Skia supports 8 bit src to 8 bit dst remove this
+  if (m_pBitmap->GetBPP() == 8 && pSource->GetBPP() == 8) {
+    SkMatrix inv;
+    SkAssertResult(skMatrix.invert(&inv));
+    for (int y = 0; y < m_pBitmap->GetHeight(); ++y) {
+      for (int x = 0; x < m_pBitmap->GetWidth(); ++x) {
+        SkPoint src = {x + 0.5f, y + 0.5f};
+        inv.mapPoints(&src, 1);
+        // TODO(caryclark) Why does the matrix map require clamping?
+        src.fX = SkTMax(0.5f, SkTMin(src.fX, width - 0.5f));
+        src.fY = SkTMax(0.5f, SkTMin(src.fY, height - 0.5f));
+        m_pBitmap->SetPixel(x, y, skBitmap.getColor(src.fX, src.fY));
+      }
+    }
+  } else {
+    m_pCanvas->drawBitmap(skBitmap, 0, 0, &paint);
   }
-  // paint.setFilterQuality(kHigh_SkFilterQuality);
-  paint.setXfermodeMode(GetSkiaBlendMode(blend_type));
-  paint.setAlpha(bitmap_alpha);
-  m_pCanvas->drawBitmap(skBitmap, 0, 0, &paint);
   m_pCanvas->restore();
   if (ct)
     ct->unref();
@@ -1450,6 +1510,68 @@
   pDIBitmap->DebugVerifyBitmapIsPreMultiplied();
 }
 
+bool CFX_SkiaDeviceDriver::DrawBitsWithMask(const CFX_DIBSource* pSource,
+                                            const CFX_DIBSource* pMask,
+                                            int bitmap_alpha,
+                                            const CFX_Matrix* pMatrix,
+                                            int blend_type) {
+  DebugValidate(m_pBitmap, m_pOriDevice);
+  SkColorTable* srcCt = nullptr;
+  SkColorTable* maskCt = nullptr;
+  std::unique_ptr<uint8_t, FxFreeDeleter> src8Storage, mask8Storage;
+  std::unique_ptr<uint32_t, FxFreeDeleter> src32Storage, mask32Storage;
+  SkBitmap skBitmap, skMask;
+  int srcWidth, srcHeight, maskWidth, maskHeight;
+  if (!Upsample(pSource, src8Storage, src32Storage, &srcCt, &skBitmap,
+                &srcWidth, &srcHeight, false)) {
+    return false;
+  }
+  if (!Upsample(pMask, mask8Storage, mask32Storage, &maskCt, &skMask,
+                &maskWidth, &maskHeight, true)) {
+    return false;
+  }
+  m_pCanvas->save();
+  SkMatrix skMatrix;
+  SetBitmapMatrix(pMatrix, srcWidth, srcHeight, &skMatrix);
+  m_pCanvas->concat(skMatrix);
+  SkPaint paint;
+  SetBitmapPaint(pSource->IsAlphaMask(), 0xFFFFFFFF, bitmap_alpha, blend_type,
+                 &paint);
+  sk_sp<SkImage> skSrc = SkImage::MakeFromBitmap(skBitmap);
+  sk_sp<SkShader> skSrcShader =
+      skSrc->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
+  sk_sp<SkImage> skMaskImage = SkImage::MakeFromBitmap(skMask);
+  sk_sp<SkShader> skMaskShader = skMaskImage->makeShader(
+      SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
+  sk_sp<SkXfermode> dstInMode = SkXfermode::Make(SkXfermode::kSrcIn_Mode);
+  paint.setShader(
+      SkShader::MakeComposeShader(skMaskShader, skSrcShader, dstInMode));
+  SkRect r = {0, 0, SkIntToScalar(srcWidth), SkIntToScalar(srcHeight)};
+  m_pCanvas->drawRect(r, paint);
+  m_pCanvas->restore();
+  if (srcCt)
+    srcCt->unref();
+  DebugValidate(m_pBitmap, m_pOriDevice);
+  return true;
+}
+
+bool CFX_SkiaDeviceDriver::SetBitsWithMask(const CFX_DIBSource* pBitmap,
+                                           const CFX_DIBSource* pMask,
+                                           int dest_left,
+                                           int dest_top,
+                                           int bitmap_alpha,
+                                           int blend_type) {
+  if (!m_pBitmap || !m_pBitmap->GetBuffer())
+    return true;
+  CFX_Matrix m(pBitmap->GetWidth(), 0, 0, -pBitmap->GetHeight(), dest_left,
+               dest_top + pBitmap->GetHeight());
+  return DrawBitsWithMask(pBitmap, pMask, bitmap_alpha, &m, blend_type);
+}
+
+void CFX_SkiaDeviceDriver::Clear(uint32_t color) {
+  m_pCanvas->clear(color);
+}
+
 void CFX_SkiaDeviceDriver::Dump() const {
 #ifdef SK_DEBUG
   if (m_pCache)
@@ -1466,6 +1588,12 @@
   m_bOwnedBitmap = FALSE;
 }
 
+void CFX_FxgeDevice::Clear(uint32_t color) {
+  CFX_SkiaDeviceDriver* skDriver =
+      static_cast<CFX_SkiaDeviceDriver*>(GetDeviceDriver());
+  skDriver->Clear(color);
+}
+
 SkPictureRecorder* CFX_FxgeDevice::CreateRecorder(int size_x, int size_y) {
   CFX_SkiaDeviceDriver* skDriver = new CFX_SkiaDeviceDriver(size_x, size_y);
   SetDeviceDriver(WrapUnique(skDriver));
@@ -1523,6 +1651,20 @@
 #endif
 }
 
+bool CFX_FxgeDevice::SetBitsWithMask(const CFX_DIBSource* pBitmap,
+                                     const CFX_DIBSource* pMask,
+                                     int left,
+                                     int top,
+                                     int bitmap_alpha,
+                                     int blend_type) {
+  CFX_SkiaDeviceDriver* skDriver =
+      static_cast<CFX_SkiaDeviceDriver*>(GetDeviceDriver());
+  if (skDriver)
+    return skDriver->SetBitsWithMask(pBitmap, pMask, left, top, bitmap_alpha,
+                                     blend_type);
+  return false;
+}
+
 void CFX_DIBSource::DebugVerifyBitmapIsPreMultiplied(void* opt) const {
 #ifdef SK_DEBUG
   SkASSERT(32 == GetBPP());
diff --git a/core/fxge/skia/fx_skia_device.h b/core/fxge/skia/fx_skia_device.h
index 661cf2e..6e112f5 100644
--- a/core/fxge/skia/fx_skia_device.h
+++ b/core/fxge/skia/fx_skia_device.h
@@ -84,6 +84,12 @@
                     int dest_left,
                     int dest_top,
                     int blend_type) override;
+  bool SetBitsWithMask(const CFX_DIBSource* pBitmap,
+                       const CFX_DIBSource* pMask,
+                       int dest_left,
+                       int dest_top,
+                       int bitmap_alpha,
+                       int blend_type) override;
   FX_BOOL StretchDIBits(const CFX_DIBSource* pBitmap,
                         uint32_t color,
                         int dest_left,
@@ -106,6 +112,12 @@
 
   void CancelDIBits(void* handle) override {}
 
+  bool DrawBitsWithMask(const CFX_DIBSource* pBitmap,
+                        const CFX_DIBSource* pMask,
+                        int bitmap_alpha,
+                        const CFX_Matrix* pMatrix,
+                        int blend_type);
+
   FX_BOOL DrawDeviceText(int nChars,
                          const FXTEXT_CHARPOS* pCharPos,
                          CFX_Font* pFont,
@@ -125,6 +137,7 @@
   void PaintStroke(SkPaint* spaint,
                    const CFX_GraphStateData* pGraphState,
                    const SkMatrix& matrix);
+  void Clear(uint32_t color);
   void Flush();
   SkPictureRecorder* GetRecorder() const { return m_pRecorder; }
   static void PreMultiply(CFX_DIBitmap* pDIBitmap);