diff options
author | caryclark <caryclark@google.com> | 2016-06-02 08:59:20 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-06-02 08:59:20 -0700 |
commit | 36e258b475702bdb1a95a88fcebd78b51069c532 (patch) | |
tree | 6752f85aa4944cbc59b7e05decfd1e891ad22ed1 | |
parent | 2235b7b52e2cedea9b5d4822de9548994362ca96 (diff) | |
download | pdfium-36e258b475702bdb1a95a88fcebd78b51069c532.tar.xz |
The PDFium source in core/fxge/dib implements a bit-blitting backend.
This code has several disadvantages over a more modern graphics engine:
- no SIMD support
- no GPU support
- limited quality
Further, calling this code locks in the perceived resolution, so that
the output cannot be scaled without additional loss.
By directing all bitmap drawing through
CFX_SkiaDeviceDriver::StartDIBits, Skia can handle all appropriate
bitmap optimizations.
To that end, SetDIBits and StretchDIBits now call StartDIBits.
Other changes:
Skia's bitmaps are premultiplied. PDF contains bitmaps that are
unpremultiplied. PDFium appears to use premultiplied bitmaps sometimes,
and unpremultiplied bitmaps elsewhere. Add a debug check for
unpremultiplied bits in Skia's driver, and add a utility to premultiply
PDFium's bitmaps' bits.
PDFium supports a 24 bit RGB bitmap padded to a 32 bit word. Set the
high byte so that Skia can treat this as an ARGB bitmap.
Defer the application of the alpha value to the draw call rather than
calling MultiplyAlpha where possible.
Allow the destination bitmap to be alpha 8 or argb 32.
Review-Url: https://codereview.chromium.org/2025043002
-rw-r--r-- | core/fpdfapi/fpdf_render/fpdf_render.cpp | 9 | ||||
-rw-r--r-- | core/fpdfapi/fpdf_render/fpdf_render_image.cpp | 11 | ||||
-rw-r--r-- | core/fxge/dib/fx_dib_main.cpp | 5 | ||||
-rw-r--r-- | core/fxge/include/fx_ge.h | 1 | ||||
-rw-r--r-- | core/fxge/skia/fx_skia_device.cpp | 144 | ||||
-rw-r--r-- | core/fxge/skia/fx_skia_device.h | 9 |
6 files changed, 128 insertions, 51 deletions
diff --git a/core/fpdfapi/fpdf_render/fpdf_render.cpp b/core/fpdfapi/fpdf_render/fpdf_render.cpp index a3dbbdb3f2..6ac78ecd43 100644 --- a/core/fpdfapi/fpdf_render/fpdf_render.cpp +++ b/core/fpdfapi/fpdf_render/fpdf_render.cpp @@ -815,14 +815,19 @@ FX_BOOL CPDF_RenderStatus::ProcessTransparency(const CPDF_PageObject* pPageObj, bitmap->MultiplyAlpha(pTextMask.get()); pTextMask.reset(); } + int32_t blitAlpha = 255; if (Transparency & PDFTRANS_GROUP && group_alpha != 1.0f) { - bitmap->MultiplyAlpha((int32_t)(group_alpha * 255)); + blitAlpha = (int32_t)(group_alpha * 255); +#ifndef _SKIA_SUPPORT_ + bitmap->MultiplyAlpha(blitAlpha); + blitAlpha = 255; +#endif } Transparency = m_Transparency; if (pPageObj->IsForm()) { Transparency |= PDFTRANS_GROUP; } - CompositeDIBitmap(bitmap, rect.left, rect.top, 0, 255, blend_type, + CompositeDIBitmap(bitmap, rect.left, rect.top, 0, blitAlpha, blend_type, Transparency); return TRUE; } diff --git a/core/fpdfapi/fpdf_render/fpdf_render_image.cpp b/core/fpdfapi/fpdf_render/fpdf_render_image.cpp index d3fbb7990c..acfb20314a 100644 --- a/core/fpdfapi/fpdf_render/fpdf_render_image.cpp +++ b/core/fpdfapi/fpdf_render/fpdf_render_image.cpp @@ -48,7 +48,15 @@ void CPDF_RenderStatus::CompositeDIBitmap(CFX_DIBitmap* pDIBitmap, if (blend_mode == FXDIB_BLEND_NORMAL) { if (!pDIBitmap->IsAlphaMask()) { if (bitmap_alpha < 255) { +#ifdef _SKIA_SUPPORT_ + void* dummy; + CFX_Matrix m(pDIBitmap->GetWidth(), 0, 0, -pDIBitmap->GetHeight(), left, + top + pDIBitmap->GetHeight()); + m_pDevice->StartDIBits(pDIBitmap, bitmap_alpha, 0, &m, 0, dummy); + return; +#else pDIBitmap->MultiplyAlpha(bitmap_alpha); +#endif } if (m_pDevice->SetDIBits(pDIBitmap, left, top)) { return; @@ -693,6 +701,9 @@ FX_BOOL CPDF_ImageRenderer::DrawMaskedImage() { } bitmap_device2.GetBitmap()->ConvertFormat(FXDIB_8bppMask); bitmap_device1.GetBitmap()->MultiplyAlpha(bitmap_device2.GetBitmap()); +#ifdef _SKIA_SUPPORT_ + bitmap_device1.PreMultiply(); // convert unpremultiplied to premultiplied +#endif if (m_BitmapAlpha < 255) { bitmap_device1.GetBitmap()->MultiplyAlpha(m_BitmapAlpha); } diff --git a/core/fxge/dib/fx_dib_main.cpp b/core/fxge/dib/fx_dib_main.cpp index 85df71e11d..896551401a 100644 --- a/core/fxge/dib/fx_dib_main.cpp +++ b/core/fxge/dib/fx_dib_main.cpp @@ -344,6 +344,11 @@ void CFX_DIBitmap::Clear(uint32_t color) { case FXDIB_Rgb32: case FXDIB_Argb: { color = IsCmykImage() ? FXCMYK_TODIB(color) : FXARGB_TODIB(color); +#ifdef _SKIA_SUPPORT_ + if (FXDIB_Rgb32 == GetFormat() && !IsCmykImage()) { + color |= 0xFF000000; + } +#endif for (int i = 0; i < m_Width; i++) { ((uint32_t*)m_pBuffer)[i] = color; } diff --git a/core/fxge/include/fx_ge.h b/core/fxge/include/fx_ge.h index 6a3113d070..8111e46d13 100644 --- a/core/fxge/include/fx_ge.h +++ b/core/fxge/include/fx_ge.h @@ -412,6 +412,7 @@ class CFX_FxgeDevice : public CFX_RenderDevice { #ifdef _SKIA_SUPPORT_ bool AttachRecorder(SkPictureRecorder* recorder); SkPictureRecorder* CreateRecorder(int size_x, int size_y); + void PreMultiply(); #endif protected: diff --git a/core/fxge/skia/fx_skia_device.cpp b/core/fxge/skia/fx_skia_device.cpp index 452472e40c..86a8269b79 100644 --- a/core/fxge/skia/fx_skia_device.cpp +++ b/core/fxge/skia/fx_skia_device.cpp @@ -81,6 +81,45 @@ void DebugDrawSkiaClipPath(SkCanvas* canvas, const SkPath& path) {} #undef SHOW_SKIA_PATH #undef DRAW_SKIA_CLIP +static void DebugVerifyBitmapIsPreMultiplied(void* buffer, + int width, + int height) { +#ifdef SK_DEBUG + // verify that input is really premultiplied + for (int y = 0; y < height; ++y) { + const uint32_t* srcRow = static_cast<const uint32_t*>(buffer) + y * width; + for (int x = 0; x < width; ++x) { + uint8_t a = SkGetPackedA32(srcRow[x]); + uint8_t r = SkGetPackedR32(srcRow[x]); + uint8_t g = SkGetPackedG32(srcRow[x]); + uint8_t b = SkGetPackedB32(srcRow[x]); + SkA32Assert(a); + SkASSERT(r <= a); + SkASSERT(g <= a); + SkASSERT(b <= a); + } + } +#endif +} + +static void DebugValidate(const CFX_DIBitmap* bitmap, + const CFX_DIBitmap* device) { + if (bitmap) { + SkASSERT(bitmap->GetBPP() == 8 || bitmap->GetBPP() == 32); + if (bitmap->GetBPP() == 32) { + DebugVerifyBitmapIsPreMultiplied(bitmap->GetBuffer(), bitmap->GetWidth(), + bitmap->GetHeight()); + } + } + if (device) { + SkASSERT(device->GetBPP() == 8 || device->GetBPP() == 32); + if (device->GetBPP() == 32) { + DebugVerifyBitmapIsPreMultiplied(device->GetBuffer(), device->GetWidth(), + device->GetHeight()); + } + } +} + SkPath BuildPath(const CFX_PathData* pPathData) { SkPath skPath; const CFX_PathData* pFPath = pPathData; @@ -509,9 +548,11 @@ CFX_SkiaDeviceDriver::CFX_SkiaDeviceDriver(CFX_DIBitmap* pBitmap, m_bRgbByteOrder(bRgbByteOrder), m_bGroupKnockout(bGroupKnockout) { SkBitmap skBitmap; - SkImageInfo imageInfo = - SkImageInfo::Make(pBitmap->GetWidth(), pBitmap->GetHeight(), - kN32_SkColorType, kOpaque_SkAlphaType); + SkASSERT(pBitmap->GetBPP() == 8 || pBitmap->GetBPP() == 32); + SkImageInfo imageInfo = SkImageInfo::Make( + pBitmap->GetWidth(), pBitmap->GetHeight(), + pBitmap->GetBPP() == 8 ? kAlpha_8_SkColorType : kN32_SkColorType, + kOpaque_SkAlphaType); skBitmap.installPixels(imageInfo, pBitmap->GetBuffer(), pBitmap->GetPitch(), nullptr, /* to do : set color table */ nullptr, nullptr); @@ -553,7 +594,7 @@ FX_BOOL CFX_SkiaDeviceDriver::DrawDeviceText(int nChars, uint32_t color, int alpha_flag, void* pIccTransform) { - CFX_TypeFace* typeface = pCache->GetDeviceCache(pFont); + sk_sp<SkTypeface> typeface(SkSafeRef(pCache->GetDeviceCache(pFont))); SkPaint paint; paint.setAntiAlias(true); paint.setColor(color); @@ -932,15 +973,12 @@ FX_BOOL CFX_SkiaDeviceDriver::SetDIBits(const CFX_DIBSource* pBitmap, void* pIccTransform) { if (!m_pBitmap || !m_pBitmap->GetBuffer()) return TRUE; - if (pBitmap->IsAlphaMask()) { - return m_pBitmap->CompositeMask( - left, top, pSrcRect->Width(), pSrcRect->Height(), pBitmap, argb, - pSrcRect->left, pSrcRect->top, blend_type, nullptr, m_bRgbByteOrder, - alpha_flag, pIccTransform); - } - return m_pBitmap->CompositeBitmap( - left, top, pSrcRect->Width(), pSrcRect->Height(), pBitmap, pSrcRect->left, - pSrcRect->top, blend_type, nullptr, m_bRgbByteOrder, pIccTransform); + + CFX_Matrix m(pBitmap->GetWidth(), 0, 0, -pBitmap->GetHeight(), left, + top + pBitmap->GetHeight()); + void* dummy; + return this->StartDIBits(pBitmap, 0xFF, argb, &m, 0, dummy, alpha_flag, + pIccTransform, blend_type); } FX_BOOL CFX_SkiaDeviceDriver::StretchDIBits(const CFX_DIBSource* pSource, @@ -956,27 +994,19 @@ FX_BOOL CFX_SkiaDeviceDriver::StretchDIBits(const CFX_DIBSource* pSource, int blend_type) { if (!m_pBitmap->GetBuffer()) return TRUE; - if (dest_width == pSource->GetWidth() && - dest_height == pSource->GetHeight()) { - FX_RECT rect(0, 0, dest_width, dest_height); - return SetDIBits(pSource, argb, &rect, dest_left, dest_top, blend_type, - alpha_flag, pIccTransform); - } - FX_RECT dest_rect(dest_left, dest_top, dest_left + dest_width, - dest_top + dest_height); - dest_rect.Normalize(); - FX_RECT dest_clip = dest_rect; - dest_clip.Intersect(*pClipRect); - CFX_BitmapComposer composer; - composer.Compose(m_pBitmap, nullptr, 255, argb, dest_clip, FALSE, FALSE, - FALSE, m_bRgbByteOrder, alpha_flag, pIccTransform, - blend_type); - dest_clip.Offset(-dest_rect.left, -dest_rect.top); - CFX_ImageStretcher stretcher(&composer, pSource, dest_width, dest_height, - dest_clip, flags); - if (stretcher.Start()) - stretcher.Continue(nullptr); - return TRUE; + CFX_Matrix m(dest_width, 0, 0, -dest_height, dest_left, + dest_top + dest_height); + + m_pCanvas->save(); + SkRect skClipRect = SkRect::MakeLTRB(pClipRect->left, pClipRect->bottom, + pClipRect->right, pClipRect->top); + m_pCanvas->clipRect(skClipRect); + void* dummy; + FX_BOOL result = this->StartDIBits(pSource, 0xFF, argb, &m, 0, dummy, + alpha_flag, pIccTransform, blend_type); + m_pCanvas->restore(); + + return result; } FX_BOOL CFX_SkiaDeviceDriver::StartDIBits(const CFX_DIBSource* pSource, @@ -988,9 +1018,12 @@ FX_BOOL CFX_SkiaDeviceDriver::StartDIBits(const CFX_DIBSource* pSource, int alpha_flag, void* pIccTransform, 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(); std::unique_ptr<uint8_t, FxFreeDeleter> dst8Storage; @@ -1025,23 +1058,27 @@ FX_BOOL CFX_SkiaDeviceDriver::StartDIBits(const CFX_DIBSource* pSource, 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) + 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; + DebugVerifyBitmapIsPreMultiplied(buffer, width, height); break; default: + SkASSERT(0); // TODO(caryclark) ensure that all cases are covered colorType = SkColorType::kUnknown_SkColorType; } - SkImageInfo imageInfo = SkImageInfo::Make( - width, height, colorType, - pSource->IsAlphaMask() ? kPremul_SkAlphaType : kOpaque_SkAlphaType); + SkImageInfo imageInfo = + SkImageInfo::Make(width, height, colorType, alphaType); SkBitmap skBitmap; skBitmap.installPixels(imageInfo, buffer, rowBytes, ct, nullptr, nullptr); m_pCanvas->save(); @@ -1063,19 +1100,28 @@ FX_BOOL CFX_SkiaDeviceDriver::StartDIBits(const CFX_DIBSource* pSource, m_pCanvas->restore(); if (ct) ct->unref(); + DebugValidate(m_pBitmap, m_pOriDevice); return TRUE; } -FX_BOOL CFX_SkiaDeviceDriver::ContinueDIBits(void* pHandle, IFX_Pause* pPause) { - if (!m_pBitmap->GetBuffer()) - return TRUE; - return ((CFX_ImageRenderer*)pHandle)->Continue(pPause); -} - -void CFX_SkiaDeviceDriver::CancelDIBits(void* pHandle) { - if (!m_pBitmap->GetBuffer()) +void CFX_SkiaDeviceDriver::PreMultiply() { + void* buffer = m_pBitmap->GetBuffer(); + if (!buffer) return; - delete (CFX_ImageRenderer*)pHandle; + if (m_pBitmap->GetBPP() != 32) { + return; + } + int height = m_pBitmap->GetHeight(); + int width = m_pBitmap->GetWidth(); + int rowBytes = m_pBitmap->GetPitch(); + SkImageInfo unpremultipliedInfo = + SkImageInfo::Make(width, height, kN32_SkColorType, kUnpremul_SkAlphaType); + SkPixmap unpremultiplied(unpremultipliedInfo, buffer, rowBytes); + SkImageInfo premultipliedInfo = + SkImageInfo::Make(width, height, kN32_SkColorType, kPremul_SkAlphaType); + SkPixmap premultiplied(premultipliedInfo, buffer, rowBytes); + unpremultiplied.readPixels(premultiplied); + DebugVerifyBitmapIsPreMultiplied(buffer, width, height); } CFX_FxgeDevice::CFX_FxgeDevice() { @@ -1129,4 +1175,8 @@ CFX_FxgeDevice::~CFX_FxgeDevice() { delete GetBitmap(); } +void CFX_FxgeDevice::PreMultiply() { + (static_cast<CFX_SkiaDeviceDriver*>(this->GetDeviceDriver()))->PreMultiply(); +} + #endif diff --git a/core/fxge/skia/fx_skia_device.h b/core/fxge/skia/fx_skia_device.h index dd3ba420e8..085977b66e 100644 --- a/core/fxge/skia/fx_skia_device.h +++ b/core/fxge/skia/fx_skia_device.h @@ -114,8 +114,12 @@ class CFX_SkiaDeviceDriver : public IFX_RenderDeviceDriver { int alpha_flag = 0, void* pIccTransform = NULL, int blend_type = FXDIB_BLEND_NORMAL) override; - FX_BOOL ContinueDIBits(void* handle, IFX_Pause* pPause) override; - void CancelDIBits(void* handle) override; + + FX_BOOL ContinueDIBits(void* handle, IFX_Pause* pPause) override { + return FALSE; + } + + void CancelDIBits(void* handle) override {} FX_BOOL DrawDeviceText(int nChars, const FXTEXT_CHARPOS* pCharPos, @@ -138,6 +142,7 @@ class CFX_SkiaDeviceDriver : public IFX_RenderDeviceDriver { const CFX_GraphStateData* pGraphState, const SkMatrix& matrix); SkPictureRecorder* GetRecorder() const { return m_pRecorder; } + void PreMultiply(); private: CFX_DIBitmap* m_pBitmap; |