// Copyright 2017 PDFium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com #include "core/fxge/dib/cfx_imagestretcher.h" #include #include #include "core/fxge/dib/cfx_dibitmap.h" #include "core/fxge/dib/cfx_dibsource.h" #include "core/fxge/dib/cstretchengine.h" #include "core/fxge/fx_dib.h" #include "third_party/base/ptr_util.h" namespace { const int kMaxProgressiveStretchPixels = 1000000; bool SourceSizeWithinLimit(int width, int height) { return !height || width < kMaxProgressiveStretchPixels / height; } FXDIB_Format GetStretchedFormat(const CFX_DIBSource& src) { FXDIB_Format format = src.GetFormat(); if (format == FXDIB_1bppMask) return FXDIB_8bppMask; if (format == FXDIB_1bppRgb) return FXDIB_8bppRgb; if (format == FXDIB_8bppRgb && src.GetPalette()) return FXDIB_Rgb; return format; } // Returns tuple c, m, y, k std::tuple CmykDecode(const uint32_t cmyk) { return std::make_tuple(FXSYS_GetCValue(cmyk), FXSYS_GetMValue(cmyk), FXSYS_GetYValue(cmyk), FXSYS_GetKValue(cmyk)); } } // namespace CFX_ImageStretcher::CFX_ImageStretcher( IFX_ScanlineComposer* pDest, const CFX_RetainPtr& pSource, int dest_width, int dest_height, const FX_RECT& bitmap_rect, uint32_t flags) : m_pDest(pDest), m_pSource(pSource), m_Flags(flags), m_bFlipX(false), m_bFlipY(false), m_DestWidth(dest_width), m_DestHeight(dest_height), m_ClipRect(bitmap_rect), m_DestFormat(GetStretchedFormat(*pSource)), m_DestBPP(m_DestFormat & 0xff), m_LineIndex(0) {} CFX_ImageStretcher::~CFX_ImageStretcher() {} bool CFX_ImageStretcher::Start() { if (m_DestWidth == 0 || m_DestHeight == 0) return false; if (m_pSource->GetFormat() == FXDIB_1bppRgb && m_pSource->GetPalette()) { FX_ARGB pal[256]; int a0; int r0; int g0; int b0; std::tie(a0, r0, g0, b0) = ArgbDecode(m_pSource->GetPaletteEntry(0)); int a1; int r1; int g1; int b1; std::tie(a1, r1, g1, b1) = ArgbDecode(m_pSource->GetPaletteEntry(1)); for (int i = 0; i < 256; ++i) { int a = a0 + (a1 - a0) * i / 255; int r = r0 + (r1 - r0) * i / 255; int g = g0 + (g1 - g0) * i / 255; int b = b0 + (b1 - b0) * i / 255; pal[i] = ArgbEncode(a, r, g, b); } if (!m_pDest->SetInfo(m_ClipRect.Width(), m_ClipRect.Height(), m_DestFormat, pal)) { return false; } } else if (m_pSource->GetFormat() == FXDIB_1bppCmyk && m_pSource->GetPalette()) { FX_CMYK pal[256]; int c0; int m0; int y0; int k0; std::tie(c0, m0, y0, k0) = CmykDecode(m_pSource->GetPaletteEntry(0)); int c1; int m1; int y1; int k1; std::tie(c1, m1, y1, k1) = CmykDecode(m_pSource->GetPaletteEntry(1)); for (int i = 0; i < 256; ++i) { int c = c0 + (c1 - c0) * i / 255; int m = m0 + (m1 - m0) * i / 255; int y = y0 + (y1 - y0) * i / 255; int k = k0 + (k1 - k0) * i / 255; pal[i] = CmykEncode(c, m, y, k); } if (!m_pDest->SetInfo(m_ClipRect.Width(), m_ClipRect.Height(), m_DestFormat, pal)) { return false; } } else if (!m_pDest->SetInfo(m_ClipRect.Width(), m_ClipRect.Height(), m_DestFormat, nullptr)) { return false; } if (m_Flags & FXDIB_DOWNSAMPLE) return StartQuickStretch(); return StartStretch(); } bool CFX_ImageStretcher::Continue(IFX_Pause* pPause) { if (m_Flags & FXDIB_DOWNSAMPLE) return ContinueQuickStretch(pPause); return ContinueStretch(pPause); } bool CFX_ImageStretcher::StartStretch() { m_pStretchEngine = pdfium::MakeUnique( m_pDest, m_DestFormat, m_DestWidth, m_DestHeight, m_ClipRect, m_pSource, m_Flags); m_pStretchEngine->StartStretchHorz(); if (SourceSizeWithinLimit(m_pSource->GetWidth(), m_pSource->GetHeight())) { m_pStretchEngine->Continue(nullptr); return false; } return true; } bool CFX_ImageStretcher::ContinueStretch(IFX_Pause* pPause) { return m_pStretchEngine && m_pStretchEngine->Continue(pPause); } bool CFX_ImageStretcher::StartQuickStretch() { if (m_DestWidth < 0) { m_bFlipX = true; m_DestWidth = -m_DestWidth; } if (m_DestHeight < 0) { m_bFlipY = true; m_DestHeight = -m_DestHeight; } uint32_t size = m_ClipRect.Width(); if (size && m_DestBPP > static_cast(INT_MAX / size)) return false; size *= m_DestBPP; m_pScanline.reset(FX_Alloc(uint8_t, (size / 8 + 3) / 4 * 4)); if (m_pSource->m_pAlphaMask) m_pMaskScanline.reset(FX_Alloc(uint8_t, (m_ClipRect.Width() + 3) / 4 * 4)); if (SourceSizeWithinLimit(m_pSource->GetWidth(), m_pSource->GetHeight())) { ContinueQuickStretch(nullptr); return false; } return true; } bool CFX_ImageStretcher::ContinueQuickStretch(IFX_Pause* pPause) { if (!m_pScanline) return false; int result_width = m_ClipRect.Width(); int result_height = m_ClipRect.Height(); int src_height = m_pSource->GetHeight(); for (; m_LineIndex < result_height; ++m_LineIndex) { int dest_y; int src_y; if (m_bFlipY) { dest_y = result_height - m_LineIndex - 1; src_y = (m_DestHeight - (dest_y + m_ClipRect.top) - 1) * src_height / m_DestHeight; } else { dest_y = m_LineIndex; src_y = (dest_y + m_ClipRect.top) * src_height / m_DestHeight; } src_y = pdfium::clamp(src_y, 0, src_height - 1); if (m_pSource->SkipToScanline(src_y, pPause)) return true; m_pSource->DownSampleScanline(src_y, m_pScanline.get(), m_DestBPP, m_DestWidth, m_bFlipX, m_ClipRect.left, result_width); if (m_pMaskScanline) { m_pSource->m_pAlphaMask->DownSampleScanline( src_y, m_pMaskScanline.get(), 1, m_DestWidth, m_bFlipX, m_ClipRect.left, result_width); } m_pDest->ComposeScanline(dest_y, m_pScanline.get(), m_pMaskScanline.get()); } return false; }