1// Copyright 2017 PDFium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com 6 7#include "core/fxge/dib/cfx_imagestretcher.h" 8 9#include <climits> 10#include <tuple> 11 12#include "core/fxge/dib/cfx_dibitmap.h" 13#include "core/fxge/dib/cfx_dibsource.h" 14#include "core/fxge/dib/cstretchengine.h" 15#include "core/fxge/fx_dib.h" 16#include "third_party/base/ptr_util.h" 17 18namespace { 19 20const int kMaxProgressiveStretchPixels = 1000000; 21 22bool SourceSizeWithinLimit(int width, int height) { 23 return !height || width < kMaxProgressiveStretchPixels / height; 24} 25 26FXDIB_Format GetStretchedFormat(const CFX_DIBSource& src) { 27 FXDIB_Format format = src.GetFormat(); 28 if (format == FXDIB_1bppMask) 29 return FXDIB_8bppMask; 30 if (format == FXDIB_1bppRgb) 31 return FXDIB_8bppRgb; 32 if (format == FXDIB_8bppRgb && src.GetPalette()) 33 return FXDIB_Rgb; 34 return format; 35} 36 37// Returns tuple c, m, y, k 38std::tuple<int, int, int, int> CmykDecode(const uint32_t cmyk) { 39 return std::make_tuple(FXSYS_GetCValue(cmyk), FXSYS_GetMValue(cmyk), 40 FXSYS_GetYValue(cmyk), FXSYS_GetKValue(cmyk)); 41} 42 43} // namespace 44 45CFX_ImageStretcher::CFX_ImageStretcher(IFX_ScanlineComposer* pDest, 46 const RetainPtr<CFX_DIBSource>& pSource, 47 int dest_width, 48 int dest_height, 49 const FX_RECT& bitmap_rect, 50 uint32_t flags) 51 : m_pDest(pDest), 52 m_pSource(pSource), 53 m_Flags(flags), 54 m_bFlipX(false), 55 m_bFlipY(false), 56 m_DestWidth(dest_width), 57 m_DestHeight(dest_height), 58 m_ClipRect(bitmap_rect), 59 m_DestFormat(GetStretchedFormat(*pSource)), 60 m_DestBPP(m_DestFormat & 0xff), 61 m_LineIndex(0) {} 62 63CFX_ImageStretcher::~CFX_ImageStretcher() {} 64 65bool CFX_ImageStretcher::Start() { 66 if (m_DestWidth == 0 || m_DestHeight == 0) 67 return false; 68 69 if (m_pSource->GetFormat() == FXDIB_1bppRgb && m_pSource->GetPalette()) { 70 FX_ARGB pal[256]; 71 int a0; 72 int r0; 73 int g0; 74 int b0; 75 std::tie(a0, r0, g0, b0) = ArgbDecode(m_pSource->GetPaletteArgb(0)); 76 int a1; 77 int r1; 78 int g1; 79 int b1; 80 std::tie(a1, r1, g1, b1) = ArgbDecode(m_pSource->GetPaletteArgb(1)); 81 for (int i = 0; i < 256; ++i) { 82 int a = a0 + (a1 - a0) * i / 255; 83 int r = r0 + (r1 - r0) * i / 255; 84 int g = g0 + (g1 - g0) * i / 255; 85 int b = b0 + (b1 - b0) * i / 255; 86 pal[i] = ArgbEncode(a, r, g, b); 87 } 88 if (!m_pDest->SetInfo(m_ClipRect.Width(), m_ClipRect.Height(), m_DestFormat, 89 pal)) { 90 return false; 91 } 92 } else if (m_pSource->GetFormat() == FXDIB_1bppCmyk && 93 m_pSource->GetPalette()) { 94 FX_CMYK pal[256]; 95 int c0; 96 int m0; 97 int y0; 98 int k0; 99 std::tie(c0, m0, y0, k0) = CmykDecode(m_pSource->GetPaletteArgb(0)); 100 int c1; 101 int m1; 102 int y1; 103 int k1; 104 std::tie(c1, m1, y1, k1) = CmykDecode(m_pSource->GetPaletteArgb(1)); 105 for (int i = 0; i < 256; ++i) { 106 int c = c0 + (c1 - c0) * i / 255; 107 int m = m0 + (m1 - m0) * i / 255; 108 int y = y0 + (y1 - y0) * i / 255; 109 int k = k0 + (k1 - k0) * i / 255; 110 pal[i] = CmykEncode(c, m, y, k); 111 } 112 if (!m_pDest->SetInfo(m_ClipRect.Width(), m_ClipRect.Height(), m_DestFormat, 113 pal)) { 114 return false; 115 } 116 } else if (!m_pDest->SetInfo(m_ClipRect.Width(), m_ClipRect.Height(), 117 m_DestFormat, nullptr)) { 118 return false; 119 } 120 121 if (m_Flags & FXDIB_DOWNSAMPLE) 122 return StartQuickStretch(); 123 124 return StartStretch(); 125} 126 127bool CFX_ImageStretcher::Continue(IFX_PauseIndicator* pPause) { 128 if (m_Flags & FXDIB_DOWNSAMPLE) 129 return ContinueQuickStretch(pPause); 130 return ContinueStretch(pPause); 131} 132 133bool CFX_ImageStretcher::StartStretch() { 134 m_pStretchEngine = pdfium::MakeUnique<CStretchEngine>( 135 m_pDest.Get(), m_DestFormat, m_DestWidth, m_DestHeight, m_ClipRect, 136 m_pSource, m_Flags); 137 m_pStretchEngine->StartStretchHorz(); 138 if (SourceSizeWithinLimit(m_pSource->GetWidth(), m_pSource->GetHeight())) { 139 m_pStretchEngine->Continue(nullptr); 140 return false; 141 } 142 return true; 143} 144 145bool CFX_ImageStretcher::ContinueStretch(IFX_PauseIndicator* pPause) { 146 return m_pStretchEngine && m_pStretchEngine->Continue(pPause); 147} 148 149bool CFX_ImageStretcher::StartQuickStretch() { 150 if (m_DestWidth < 0) { 151 m_bFlipX = true; 152 m_DestWidth = -m_DestWidth; 153 } 154 if (m_DestHeight < 0) { 155 m_bFlipY = true; 156 m_DestHeight = -m_DestHeight; 157 } 158 uint32_t size = m_ClipRect.Width(); 159 if (size && m_DestBPP > static_cast<int>(INT_MAX / size)) 160 return false; 161 162 size *= m_DestBPP; 163 m_pScanline.reset(FX_Alloc(uint8_t, (size / 8 + 3) / 4 * 4)); 164 if (m_pSource->m_pAlphaMask) 165 m_pMaskScanline.reset(FX_Alloc(uint8_t, (m_ClipRect.Width() + 3) / 4 * 4)); 166 167 if (SourceSizeWithinLimit(m_pSource->GetWidth(), m_pSource->GetHeight())) { 168 ContinueQuickStretch(nullptr); 169 return false; 170 } 171 return true; 172} 173 174bool CFX_ImageStretcher::ContinueQuickStretch(IFX_PauseIndicator* pPause) { 175 if (!m_pScanline) 176 return false; 177 178 int result_width = m_ClipRect.Width(); 179 int result_height = m_ClipRect.Height(); 180 int src_height = m_pSource->GetHeight(); 181 for (; m_LineIndex < result_height; ++m_LineIndex) { 182 int dest_y; 183 int src_y; 184 if (m_bFlipY) { 185 dest_y = result_height - m_LineIndex - 1; 186 src_y = (m_DestHeight - (dest_y + m_ClipRect.top) - 1) * src_height / 187 m_DestHeight; 188 } else { 189 dest_y = m_LineIndex; 190 src_y = (dest_y + m_ClipRect.top) * src_height / m_DestHeight; 191 } 192 src_y = pdfium::clamp(src_y, 0, src_height - 1); 193 194 if (m_pSource->SkipToScanline(src_y, pPause)) 195 return true; 196 197 m_pSource->DownSampleScanline(src_y, m_pScanline.get(), m_DestBPP, 198 m_DestWidth, m_bFlipX, m_ClipRect.left, 199 result_width); 200 if (m_pMaskScanline) { 201 m_pSource->m_pAlphaMask->DownSampleScanline( 202 src_y, m_pMaskScanline.get(), 1, m_DestWidth, m_bFlipX, 203 m_ClipRect.left, result_width); 204 } 205 m_pDest->ComposeScanline(dest_y, m_pScanline.get(), m_pMaskScanline.get()); 206 } 207 return false; 208} 209