Don't implement CPDF_DIB::GetBuffer() (try 2) The first try [1] to just return an empty span in CPDF_DIB::GetBuffer() did not work. This CL goes further and deletes CFX_DIBBase::GetBuffer() altogether. Instead, only implement a non-virtual CFX_DIBBitmap::GetBuffer(). To make this work, get rid of all CFX_DIBBase::GetBuffer() callers: - Switch to calling CFX_DIBBitmap::GetBuffer() when it easy to do so. - Make CFX_DIBBase::RealizeIfNeeded() virtual, and change the default implementation to just call Realize(). - Make RealizeIfNeeded() available for Skia-builds and use it in CreateSkiaImageFromDib(). - Change CreateSkiaImageFromTransformedDib() to only use scanlines. Fix other issues to make sure everything works: - Avoid calling RealizeIfNeeded() on `CPDF_DIB::m_pCachedBitmap`, as that gives the wrong answer, just like CPDF_DIB::GetBuffer(). - Make sure CreateSkiaImageFromDib() does not fail and leak memory. - Give up on avoiding the CachedImage::SkipToScanline() call. - Add a test case for a related bug. [1] https://pdfium-review.googlesource.com/108850 Bug: chromium:1478366,pdfium:2050,pdfium:2051 Change-Id: Ia20d4d257d441dd971ee0a912733f48f29dafad1 Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/111434 Reviewed-by: Nigi <nigi@chromium.org> Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/core/fpdfapi/page/cpdf_dib.cpp b/core/fpdfapi/page/cpdf_dib.cpp index c478d37..657cf16 100644 --- a/core/fpdfapi/page/cpdf_dib.cpp +++ b/core/fpdfapi/page/cpdf_dib.cpp
@@ -1132,11 +1132,6 @@ return true; } -pdfium::span<const uint8_t> CPDF_DIB::GetBuffer() const { - return m_pCachedBitmap ? m_pCachedBitmap->GetBuffer() - : pdfium::span<const uint8_t>(); -} - pdfium::span<const uint8_t> CPDF_DIB::GetScanline(int line) const { if (m_bpc == 0) return pdfium::span<const uint8_t>();
diff --git a/core/fpdfapi/page/cpdf_dib.h b/core/fpdfapi/page/cpdf_dib.h index 5206bf5..1ef656a 100644 --- a/core/fpdfapi/page/cpdf_dib.h +++ b/core/fpdfapi/page/cpdf_dib.h
@@ -45,7 +45,6 @@ CONSTRUCT_VIA_MAKE_RETAIN; // CFX_DIBBase: - pdfium::span<const uint8_t> GetBuffer() const override; pdfium::span<const uint8_t> GetScanline(int line) const override; bool SkipToScanline(int line, PauseIndicatorIface* pPause) const override; size_t GetEstimatedImageMemoryBurden() const override;
diff --git a/core/fpdfapi/page/cpdf_pageimagecache.cpp b/core/fpdfapi/page/cpdf_pageimagecache.cpp index b36f089..30d6aa1 100644 --- a/core/fpdfapi/page/cpdf_pageimagecache.cpp +++ b/core/fpdfapi/page/cpdf_pageimagecache.cpp
@@ -28,7 +28,6 @@ #if defined(_SKIA_SUPPORT_) #include "core/fxcrt/data_vector.h" #include "core/fxge/cfx_defaultrenderdevice.h" -#include "third_party/base/notreached.h" #include "third_party/skia/include/core/SkImage.h" // nogncheck #include "third_party/skia/include/core/SkRefCnt.h" // nogncheck #endif @@ -63,15 +62,6 @@ } } - pdfium::span<const uint8_t> GetBuffer() const override { - // TODO(crbug.com/pdfium/2051): Still needed for - // CGdiDeviceDriver::GDI_StretchDIBits(). `CachedImage` is only used when - // Skia is the default renderer, which should call `RealizeSkImage()` - // instead. Consider removing this or making it `NOTREACHED_NORETURN()` - // by reimplementing its caller when Skia is the default renderer. - return image_->GetBuffer(); - } - pdfium::span<const uint8_t> GetScanline(int line) const override { // TODO(crbug.com/pdfium/2050): Still needed for `Realize()` call in // `CPDF_ImageRenderer`. @@ -79,10 +69,6 @@ } bool SkipToScanline(int line, PauseIndicatorIface* pause) const override { - // TODO(crbug.com/pdfium/2051): `CachedImage` is only used by Skia, which - // should call `RealizeSkImage()` instead. Consider removing this, or at - // least making it `NOTREACHED_NORETURN()`. - NOTREACHED(); return image_->SkipToScanline(line, pause); } @@ -91,6 +77,12 @@ return image_->GetEstimatedImageMemoryBurden(); } +#if BUILDFLAG(IS_WIN) || defined(_SKIA_SUPPORT_) + RetainPtr<const CFX_DIBitmap> RealizeIfNeeded() const override { + return image_->RealizeIfNeeded(); + } +#endif + sk_sp<SkImage> RealizeSkImage() const override { if (!cached_skia_image_) { cached_skia_image_ = image_->RealizeSkImage(); @@ -111,15 +103,7 @@ bool realize_hint) { #if defined(_SKIA_SUPPORT_) if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) { - // TODO(crbug.com/pdfium/2050): Ignore `realize_hint`, as `RealizeSkImage()` - // doesn't benefit from it. The current behavior masks a bug in `CPDF_DIB` - // in which `GetBuffer()` and `GetScanline()` don't give the same answer. - if (realize_hint) { - image = image->Realize(); - if (!image) { - return nullptr; - } - } + // Ignore `realize_hint`, as `RealizeSkImage()` doesn't benefit from it. return pdfium::MakeRetain<CachedImage>(std::move(image)); } #endif // defined(_SKIA_SUPPORT_)
diff --git a/core/fxge/dib/cfx_dibbase.cpp b/core/fxge/dib/cfx_dibbase.cpp index 2a80f89..bf823dc 100644 --- a/core/fxge/dib/cfx_dibbase.cpp +++ b/core/fxge/dib/cfx_dibbase.cpp
@@ -617,10 +617,6 @@ CFX_DIBBase::~CFX_DIBBase() = default; -pdfium::span<const uint8_t> CFX_DIBBase::GetBuffer() const { - return pdfium::span<const uint8_t>(); -} - bool CFX_DIBBase::SkipToScanline(int line, PauseIndicatorIface* pPause) const { return false; } @@ -629,12 +625,9 @@ return GetRequiredPaletteSize() * sizeof(uint32_t); } -#if BUILDFLAG(IS_WIN) -RetainPtr<const CFX_DIBBase> CFX_DIBBase::RealizeIfNeeded() const { - if (GetBuffer().empty()) { - return Realize(); - } - return pdfium::WrapRetain(this); +#if BUILDFLAG(IS_WIN) || defined(_SKIA_SUPPORT_) +RetainPtr<const CFX_DIBitmap> CFX_DIBBase::RealizeIfNeeded() const { + return Realize(); } #endif
diff --git a/core/fxge/dib/cfx_dibbase.h b/core/fxge/dib/cfx_dibbase.h index 6327888..98febad 100644 --- a/core/fxge/dib/cfx_dibbase.h +++ b/core/fxge/dib/cfx_dibbase.h
@@ -41,10 +41,13 @@ static constexpr uint32_t kPaletteSize = 256; - virtual pdfium::span<const uint8_t> GetBuffer() const; virtual pdfium::span<const uint8_t> GetScanline(int line) const = 0; virtual bool SkipToScanline(int line, PauseIndicatorIface* pPause) const; virtual size_t GetEstimatedImageMemoryBurden() const; +#if BUILDFLAG(IS_WIN) || defined(_SKIA_SUPPORT_) + // Calls Realize() if needed. Otherwise, return `this`. + virtual RetainPtr<const CFX_DIBitmap> RealizeIfNeeded() const; +#endif int GetWidth() const { return m_Width; } int GetHeight() const { return m_Height; } @@ -65,11 +68,6 @@ // Copies into internally-owned palette. void SetPalette(pdfium::span<const uint32_t> src_palette); -#if BUILDFLAG(IS_WIN) - // Calls Realize() if needed. Otherwise, return `this`. - RetainPtr<const CFX_DIBBase> RealizeIfNeeded() const; -#endif - RetainPtr<CFX_DIBitmap> Realize() const; RetainPtr<CFX_DIBitmap> ClipTo(const FX_RECT& rect) const; RetainPtr<CFX_DIBitmap> ConvertTo(FXDIB_Format format) const;
diff --git a/core/fxge/dib/cfx_dibitmap.cpp b/core/fxge/dib/cfx_dibitmap.cpp index 6df1eee..cf349c0 100644 --- a/core/fxge/dib/cfx_dibitmap.cpp +++ b/core/fxge/dib/cfx_dibitmap.cpp
@@ -110,6 +110,15 @@ return result; } +#if BUILDFLAG(IS_WIN) || defined(_SKIA_SUPPORT_) +RetainPtr<const CFX_DIBitmap> CFX_DIBitmap::RealizeIfNeeded() const { + if (GetBuffer().empty()) { + return Realize(); + } + return pdfium::WrapRetain(this); +} +#endif + void CFX_DIBitmap::TakeOver(RetainPtr<CFX_DIBitmap>&& pSrcBitmap) { m_pBuffer = std::move(pSrcBitmap->m_pBuffer); m_palette = std::move(pSrcBitmap->m_palette);
diff --git a/core/fxge/dib/cfx_dibitmap.h b/core/fxge/dib/cfx_dibitmap.h index 34f5887..e59644d 100644 --- a/core/fxge/dib/cfx_dibitmap.h +++ b/core/fxge/dib/cfx_dibitmap.h
@@ -34,10 +34,13 @@ bool Copy(const RetainPtr<CFX_DIBBase>& pSrc); // CFX_DIBBase - pdfium::span<const uint8_t> GetBuffer() const override; pdfium::span<const uint8_t> GetScanline(int line) const override; size_t GetEstimatedImageMemoryBurden() const override; +#if BUILDFLAG(IS_WIN) || defined(_SKIA_SUPPORT_) + RetainPtr<const CFX_DIBitmap> RealizeIfNeeded() const override; +#endif + pdfium::span<const uint8_t> GetBuffer() const; pdfium::span<uint8_t> GetWritableBuffer() { pdfium::span<const uint8_t> src = GetBuffer(); return {const_cast<uint8_t*>(src.data()), src.size()};
diff --git a/core/fxge/skia/cfx_dibbase_skia.cpp b/core/fxge/skia/cfx_dibbase_skia.cpp index 417aa93..8606e33 100644 --- a/core/fxge/skia/cfx_dibbase_skia.cpp +++ b/core/fxge/skia/cfx_dibbase_skia.cpp
@@ -33,41 +33,35 @@ namespace { -// Releases `CFX_DIBBase` "leaked" by `CreateSkiaImageFromDib()`. +// Releases `CFX_DIBitmap` "leaked" by `CreateSkiaImageFromDib()`. void ReleaseRetainedHeldBySkImage(const void* /*pixels*/, SkImages::ReleaseContext context) { - RetainPtr<const CFX_DIBBase> retained; - retained.Unleak(reinterpret_cast<const CFX_DIBBase*>(context)); + RetainPtr<const CFX_DIBitmap> realized_bitmap; + realized_bitmap.Unleak(reinterpret_cast<const CFX_DIBitmap*>(context)); } -// Creates an `SkImage` from a `CFX_DIBBase`, sharing the underlying pixels if -// possible. -// -// Note that an `SkImage` must be immutable, so if sharing pixels, they must not -// be modified during the lifetime of the `SkImage`. +// Creates an `SkImage` from a `CFX_DIBBase`. sk_sp<SkImage> CreateSkiaImageFromDib(const CFX_DIBBase* source, SkColorType color_type, SkAlphaType alpha_type) { // Make sure the DIB is backed by a buffer. - RetainPtr<const CFX_DIBBase> retained; - if (source->GetBuffer().empty()) { - retained = source->Realize(); - if (!retained) { - return nullptr; - } - DCHECK(!retained->GetBuffer().empty()); - } else { - retained.Reset(source); + RetainPtr<const CFX_DIBitmap> realized_bitmap = source->RealizeIfNeeded(); + if (!realized_bitmap) { + return nullptr; } + CHECK(!realized_bitmap->GetBuffer().empty()); - // Convert unowned pointer to a retained pointer, then "leak" to `SkImage`. - source = retained.Leak(); - SkImageInfo info = SkImageInfo::Make(source->GetWidth(), source->GetHeight(), + // Transfer ownership of `realized_bitmap` to `bitmap`, which will be freed by + // ReleaseRetainedHeldBySkImage(). + const CFX_DIBitmap* bitmap = realized_bitmap.Leak(); + SkImageInfo info = SkImageInfo::Make(bitmap->GetWidth(), bitmap->GetHeight(), color_type, alpha_type); - return SkImages::RasterFromPixmap( - SkPixmap(info, source->GetBuffer().data(), source->GetPitch()), + auto result = SkImages::RasterFromPixmap( + SkPixmap(info, bitmap->GetBuffer().data(), bitmap->GetPitch()), /*rasterReleaseProc=*/ReleaseRetainedHeldBySkImage, - /*releaseContext=*/const_cast<CFX_DIBBase*>(source)); + /*releaseContext=*/const_cast<CFX_DIBitmap*>(bitmap)); + CHECK(result); // Otherwise, `bitmap` leaks. + return result; } // Releases allocated memory "leaked" by `CreateSkiaImageFromTransformedDib()`. @@ -125,22 +119,6 @@ DCHECK_GE(scanline.size(), min_row_bytes); } -void ValidateBufferSize(pdfium::span<const uint8_t> buffer, - const CFX_DIBBase& source) { -#if DCHECK_IS_ON() - if (source.GetHeight() == 0) { - return; - } - - FX_SAFE_SIZE_T buffer_size = source.GetHeight() - 1; - buffer_size *= source.GetPitch(); - buffer_size += fxge::CalculatePitch8OrDie(source.GetBPP(), /*components=*/1, - source.GetWidth()); - - DCHECK_GE(buffer.size(), buffer_size.ValueOrDie()); -#endif // DCHECK_IS_ON() -} - // Creates an `SkImage` from a `CFX_DIBBase`, transforming the source pixels // using `pixel_transform`. // @@ -167,40 +145,21 @@ return nullptr; } - // Transform source pixels to output pixels. - pdfium::span<const uint8_t> source_buffer = source.GetBuffer(); + // Transform source pixels to output pixels. Iterate by individual scanline. Result* output_cursor = reinterpret_cast<Result*>(output.get()); - if (source_buffer.empty()) { - // No buffer; iterate by individual scanline. - const size_t min_row_bytes = - fxge::CalculatePitch8OrDie(source.GetBPP(), /*components=*/1, width); - DCHECK_LE(min_row_bytes, source.GetPitch()); + const size_t min_row_bytes = + fxge::CalculatePitch8OrDie(source.GetBPP(), /*components=*/1, width); + DCHECK_LE(min_row_bytes, source.GetPitch()); - int line = 0; - for (int row = 0; row < height; ++row) { - pdfium::span<const uint8_t> scanline = source.GetScanline(line++); - ValidateScanlineSize(scanline, min_row_bytes); + int line = 0; + for (int row = 0; row < height; ++row) { + pdfium::span<const uint8_t> scanline = source.GetScanline(line++); + ValidateScanlineSize(scanline, min_row_bytes); - for (int column = 0; column < width; ++column) { - *output_cursor++ = - Traits::Invoke(std::forward<PixelTransform>(pixel_transform), - scanline.data(), column); - } - } - } else { - // Iterate over the entire buffer. - ValidateBufferSize(source_buffer, source); - const size_t row_bytes = source.GetPitch(); - - const uint8_t* next_scanline = source_buffer.data(); - for (int row = 0; row < height; ++row) { - const uint8_t* scanline = next_scanline; - next_scanline += row_bytes; - - for (int column = 0; column < width; ++column) { - *output_cursor++ = Traits::Invoke( - std::forward<PixelTransform>(pixel_transform), scanline, column); - } + for (int column = 0; column < width; ++column) { + *output_cursor++ = + Traits::Invoke(std::forward<PixelTransform>(pixel_transform), + scanline.data(), column); } }
diff --git a/core/fxge/win32/cgdi_device_driver.cpp b/core/fxge/win32/cgdi_device_driver.cpp index e4c2b15..e6e2c21 100644 --- a/core/fxge/win32/cgdi_device_driver.cpp +++ b/core/fxge/win32/cgdi_device_driver.cpp
@@ -394,7 +394,7 @@ int left, int top) { if (m_DeviceType == DeviceType::kPrinter) { - RetainPtr<const CFX_DIBBase> flipped_source = + RetainPtr<const CFX_DIBitmap> flipped_source = source->FlipImage(/*bXFlip=*/false, /*bYFlip=*/true); if (!flipped_source) { return false; @@ -414,7 +414,7 @@ return true; } - RetainPtr<const CFX_DIBBase> realized_source = source->RealizeIfNeeded(); + RetainPtr<const CFX_DIBitmap> realized_source = source->RealizeIfNeeded(); if (!realized_source) { return false; } @@ -445,7 +445,7 @@ SetStretchBltMode(m_hDC, COLORONCOLOR); } - RetainPtr<const CFX_DIBBase> realized_source; + RetainPtr<const CFX_DIBitmap> realized_source; if (m_DeviceType == DeviceType::kPrinter && ((int64_t)source->GetWidth() * source->GetHeight() > (int64_t)abs(dest_width) * abs(dest_height))) { @@ -477,7 +477,7 @@ return false; } - RetainPtr<const CFX_DIBBase> realized_source = source->RealizeIfNeeded(); + RetainPtr<const CFX_DIBitmap> realized_source = source->RealizeIfNeeded(); if (!realized_source) { return false; }
diff --git a/core/fxge/win32/cgdi_plus_ext.cpp b/core/fxge/win32/cgdi_plus_ext.cpp index f6a9181..0cd0a37 100644 --- a/core/fxge/win32/cgdi_plus_ext.cpp +++ b/core/fxge/win32/cgdi_plus_ext.cpp
@@ -223,7 +223,7 @@ return; } - RetainPtr<const CFX_DIBBase> realized_source = source->RealizeIfNeeded(); + RetainPtr<const CFX_DIBitmap> realized_source = source->RealizeIfNeeded(); if (!realized_source) { return; }
diff --git a/testing/resources/pixel/bug_1478366.in b/testing/resources/pixel/bug_1478366.in new file mode 100644 index 0000000..5749b52 --- /dev/null +++ b/testing/resources/pixel/bug_1478366.in
@@ -0,0 +1,56 @@ +{{header}} +{{object 1 0}} << + /Type /Catalog + /Pages 2 0 R +>> +endobj +{{object 2 0}} << + /Type /Pages + /Count 1 + /Kids [3 0 R] +>> +endobj +{{object 3 0}} << + /Type /Page + /Parent 2 0 R + /Contents [4 0 R] + /MediaBox [0 0 200 200] + /Resources << + /XObject << + /X1 5 0 R + >> + >> +>> +endobj +{{object 4 0}} << + {{streamlen}} +>> +stream +q +200 0 0 200 0 0 cm +/X1 Do +Q +endstream +endobj +{{object 5 0}} << + /Type /XObject + /Subtype /Image + % The image needs to have a bitmap size that's at least `kHugeImageSize`. + /Width 4000 + /Height 4000 + /BitsPerComponent 1 + /ColorSpace /DeviceGray + /Filter [/ASCIIHexDecode /JBIG2Decode] + /Mask [255 255] + {{streamlen}} +>> +stream +000000003000010000001300000dea00000353000017110000171151000000000001260001000000 +3500000dea000003530000000000000000020003fffdff02fefefeff7f86530fb6c922cfff7fff7f +ff7fff7fff7fff7fff7fff7fffac +endstream +endobj +{{xref}} +{{trailer}} +{{startxref}} +%%EOF
diff --git a/testing/resources/pixel/bug_1478366_expected.pdf.0.png b/testing/resources/pixel/bug_1478366_expected.pdf.0.png new file mode 100644 index 0000000..23e6296 --- /dev/null +++ b/testing/resources/pixel/bug_1478366_expected.pdf.0.png Binary files differ
diff --git a/testing/resources/pixel/bug_1478366_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1478366_expected_skia.pdf.0.png new file mode 100644 index 0000000..baf8b4b --- /dev/null +++ b/testing/resources/pixel/bug_1478366_expected_skia.pdf.0.png Binary files differ