1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <vcl/BitmapReadAccess.hxx>
21 #include <vcl/salgtype.hxx>
22 #include <bitmap/bmpfast.hxx>
23 #include <o3tl/safeint.hxx>
24 #include <osl/diagnose.h>
25 #include <sal/log.hxx>
26 #include <tools/helpers.hxx>
29 #define IMPL_CASE_SET_FORMAT( Format, BitCount ) \
30 case( ScanlineFormat::Format ): \
32 pFncSetPixel = BitmapReadAccess::SetPixelFor##Format; \
33 pDstBuffer->mnBitCount = BitCount; \
37 #define DOUBLE_SCANLINES() \
38 while( ( nActY < nHeight1 ) && ( pMapY[ nActY + 1 ] == nMapY ) ) \
40 memcpy( pDstScanMap[ nActY + 1 ], pDstScan, rDstBuffer.mnScanlineSize ); \
44 constexpr int TC_TO_PAL_COLORS
= 4096;
46 static tools::Long
ImplIndexFromColor( const BitmapColor
& rCol
)
48 return ( ( static_cast<tools::Long
>(rCol
.GetBlue()) >> 4) << 8 ) |
49 ( ( static_cast<tools::Long
>(rCol
.GetGreen()) >> 4 ) << 4 ) |
50 ( static_cast<tools::Long
>(rCol
.GetRed()) >> 4 );
53 static void ImplPALToPAL( const BitmapBuffer
& rSrcBuffer
, BitmapBuffer
& rDstBuffer
,
54 FncGetPixel pFncGetPixel
, FncSetPixel pFncSetPixel
,
55 Scanline
* pSrcScanMap
, Scanline
* pDstScanMap
, tools::Long
const * pMapX
, const tools::Long
* pMapY
)
57 const tools::Long nHeight1
= rDstBuffer
.mnHeight
- 1;
58 const ColorMask
& rSrcMask
= rSrcBuffer
.maColorMask
;
59 const ColorMask
& rDstMask
= rDstBuffer
.maColorMask
;
60 BitmapPalette
aColMap( rSrcBuffer
.maPalette
.GetEntryCount() );
61 BitmapColor
* pColMapBuf
= aColMap
.ImplGetColorBuffer();
62 BitmapColor
aIndex( 0 );
64 for( sal_uInt16 i
= 0, nSrcCount
= aColMap
.GetEntryCount(), nDstCount
= rDstBuffer
.maPalette
.GetEntryCount(); i
< nSrcCount
; i
++ )
66 if( ( i
< nDstCount
) && ( rSrcBuffer
.maPalette
[ i
] == rDstBuffer
.maPalette
[ i
] ) )
67 aIndex
.SetIndex( sal::static_int_cast
<sal_uInt8
>(i
) );
69 aIndex
.SetIndex( sal::static_int_cast
<sal_uInt8
>(rDstBuffer
.maPalette
.GetBestIndex( rSrcBuffer
.maPalette
[ i
] )) );
71 pColMapBuf
[ i
] = aIndex
;
74 for (tools::Long nActY
= 0; nActY
< rDstBuffer
.mnHeight
; ++nActY
)
76 tools::Long nMapY
= pMapY
[nActY
];
77 Scanline
pSrcScan(pSrcScanMap
[nMapY
]), pDstScan(pDstScanMap
[nActY
]);
79 for (tools::Long nX
= 0; nX
< rDstBuffer
.mnWidth
; ++nX
)
80 pFncSetPixel( pDstScan
, nX
, pColMapBuf
[ pFncGetPixel( pSrcScan
, pMapX
[ nX
], rSrcMask
).GetIndex() ], rDstMask
);
86 static void ImplPALToTC( const BitmapBuffer
& rSrcBuffer
, BitmapBuffer
const & rDstBuffer
,
87 FncGetPixel pFncGetPixel
, FncSetPixel pFncSetPixel
,
88 Scanline
* pSrcScanMap
, Scanline
* pDstScanMap
, tools::Long
const * pMapX
, const tools::Long
* pMapY
)
90 const tools::Long nHeight1
= rDstBuffer
.mnHeight
- 1;
91 const ColorMask
& rSrcMask
= rSrcBuffer
.maColorMask
;
92 const ColorMask
& rDstMask
= rDstBuffer
.maColorMask
;
93 const BitmapColor
* pColBuf
= rSrcBuffer
.maPalette
.ImplGetColorBuffer();
95 if( RemoveScanline( rSrcBuffer
.mnFormat
) == ScanlineFormat::N1BitMsbPal
)
97 const BitmapColor
aCol0( pColBuf
[ 0 ] );
98 const BitmapColor
aCol1( pColBuf
[ 1 ] );
101 for (tools::Long nActY
= 0; nActY
< rDstBuffer
.mnHeight
; ++nActY
)
103 tools::Long nMapY
= pMapY
[nActY
];
104 Scanline
pSrcScan(pSrcScanMap
[nMapY
]), pDstScan(pDstScanMap
[nActY
]);
106 for (tools::Long nX
= 0; nX
< rDstBuffer
.mnWidth
;)
109 pFncSetPixel( pDstScan
, nX
++,
110 pSrcScan
[ nMapX
>> 3 ] & ( 1 << ( 7 - ( nMapX
& 7 ) ) ) ? aCol1
: aCol0
,
117 else if( RemoveScanline( rSrcBuffer
.mnFormat
) == ScanlineFormat::N8BitPal
)
119 for (tools::Long nActY
= 0; nActY
< rDstBuffer
.mnHeight
; ++nActY
)
121 tools::Long nMapY
= pMapY
[nActY
];
122 Scanline
pSrcScan(pSrcScanMap
[nMapY
]), pDstScan(pDstScanMap
[nActY
]);
124 for (tools::Long nX
= 0; nX
< rDstBuffer
.mnWidth
; ++nX
)
125 pFncSetPixel( pDstScan
, nX
, pColBuf
[ pSrcScan
[ pMapX
[ nX
] ] ], rDstMask
);
132 for (tools::Long nActY
= 0; nActY
< rDstBuffer
.mnHeight
; ++nActY
)
134 tools::Long nMapY
= pMapY
[nActY
];
135 Scanline
pSrcScan(pSrcScanMap
[nMapY
]), pDstScan(pDstScanMap
[nActY
]);
137 for (tools::Long nX
= 0; nX
< rDstBuffer
.mnWidth
; ++nX
)
138 pFncSetPixel( pDstScan
, nX
, pColBuf
[ pFncGetPixel( pSrcScan
, pMapX
[ nX
], rSrcMask
).GetIndex() ], rDstMask
);
145 static void ImplTCToTC( const BitmapBuffer
& rSrcBuffer
, BitmapBuffer
const & rDstBuffer
,
146 FncGetPixel pFncGetPixel
, FncSetPixel pFncSetPixel
,
147 Scanline
* pSrcScanMap
, Scanline
* pDstScanMap
, tools::Long
const * pMapX
, const tools::Long
* pMapY
)
149 const tools::Long nHeight1
= rDstBuffer
.mnHeight
- 1;
150 const ColorMask
& rSrcMask
= rSrcBuffer
.maColorMask
;
151 const ColorMask
& rDstMask
= rDstBuffer
.maColorMask
;
153 if( RemoveScanline( rSrcBuffer
.mnFormat
) == ScanlineFormat::N24BitTcBgr
)
156 sal_uInt8
* pPixel
= nullptr;
158 for (tools::Long nActY
= 0; nActY
< rDstBuffer
.mnHeight
; ++nActY
)
160 tools::Long nMapY
= pMapY
[nActY
];
161 Scanline
pSrcScan(pSrcScanMap
[nMapY
]), pDstScan(pDstScanMap
[nActY
]);
163 for (tools::Long nX
= 0; nX
< rDstBuffer
.mnWidth
; ++nX
)
165 pPixel
= pSrcScan
+ pMapX
[ nX
] * 3;
166 aCol
.SetBlue( *pPixel
++ );
167 aCol
.SetGreen( *pPixel
++ );
168 aCol
.SetRed( *pPixel
);
169 pFncSetPixel( pDstScan
, nX
, aCol
, rDstMask
);
177 for (tools::Long nActY
= 0; nActY
< rDstBuffer
.mnHeight
; ++nActY
)
179 tools::Long nMapY
= pMapY
[nActY
];
180 Scanline
pSrcScan(pSrcScanMap
[nMapY
]), pDstScan(pDstScanMap
[nActY
]);
182 for (tools::Long nX
= 0; nX
< rDstBuffer
.mnWidth
; ++nX
)
183 pFncSetPixel( pDstScan
, nX
, pFncGetPixel( pSrcScan
, pMapX
[ nX
], rSrcMask
), rDstMask
);
190 static void ImplTCToPAL( const BitmapBuffer
& rSrcBuffer
, BitmapBuffer
const & rDstBuffer
,
191 FncGetPixel pFncGetPixel
, FncSetPixel pFncSetPixel
,
192 Scanline
* pSrcScanMap
, Scanline
* pDstScanMap
, tools::Long
const * pMapX
, const tools::Long
* pMapY
)
194 const tools::Long nHeight1
= rDstBuffer
.mnHeight
- 1;
195 const ColorMask
& rSrcMask
= rSrcBuffer
.maColorMask
;
196 const ColorMask
& rDstMask
= rDstBuffer
.maColorMask
;
197 std::unique_ptr
<sal_uInt8
[]> pColToPalMap(new sal_uInt8
[ TC_TO_PAL_COLORS
]);
198 BitmapColor
aIndex( 0 );
200 for( tools::Long nR
= 0; nR
< 16; nR
++ )
202 for( tools::Long nG
= 0; nG
< 16; nG
++ )
204 for( tools::Long nB
= 0; nB
< 16; nB
++ )
206 BitmapColor
aCol( sal::static_int_cast
<sal_uInt8
>(nR
<< 4),
207 sal::static_int_cast
<sal_uInt8
>(nG
<< 4),
208 sal::static_int_cast
<sal_uInt8
>(nB
<< 4) );
209 pColToPalMap
[ ImplIndexFromColor( aCol
) ] = static_cast<sal_uInt8
>(rDstBuffer
.maPalette
.GetBestIndex( aCol
));
214 for (tools::Long nActY
= 0; nActY
< rDstBuffer
.mnHeight
; ++nActY
)
216 tools::Long nMapY
= pMapY
[nActY
];
217 Scanline
pSrcScan(pSrcScanMap
[nMapY
]), pDstScan(pDstScanMap
[nActY
]);
219 for (tools::Long nX
= 0; nX
< rDstBuffer
.mnWidth
; ++nX
)
221 aIndex
.SetIndex( pColToPalMap
[ ImplIndexFromColor( pFncGetPixel( pSrcScan
, pMapX
[ nX
], rSrcMask
) ) ] );
222 pFncSetPixel( pDstScan
, nX
, aIndex
, rDstMask
);
229 std::unique_ptr
<BitmapBuffer
> StretchAndConvert(
230 const BitmapBuffer
& rSrcBuffer
, const SalTwoRect
& rTwoRect
,
231 ScanlineFormat nDstBitmapFormat
, const BitmapPalette
* pDstPal
, const ColorMask
* pDstMask
)
233 FncGetPixel pFncGetPixel
;
234 FncSetPixel pFncSetPixel
;
235 std::unique_ptr
<BitmapBuffer
> pDstBuffer(new BitmapBuffer
);
237 // set function for getting pixels
238 pFncGetPixel
= BitmapReadAccess::GetPixelFunction( rSrcBuffer
.mnFormat
);
241 // should never come here
242 // initialize pFncGetPixel to something valid that is
243 // least likely to crash
244 pFncGetPixel
= BitmapReadAccess::GetPixelForN1BitMsbPal
;
245 OSL_FAIL( "unknown read format" );
248 // set function for setting pixels
249 const ScanlineFormat nDstScanlineFormat
= RemoveScanline( nDstBitmapFormat
);
250 switch( nDstScanlineFormat
)
252 IMPL_CASE_SET_FORMAT( N1BitMsbPal
, 1 );
253 IMPL_CASE_SET_FORMAT( N1BitLsbPal
, 1 );
254 IMPL_CASE_SET_FORMAT( N8BitPal
, 8 );
255 IMPL_CASE_SET_FORMAT( N24BitTcBgr
, 24 );
256 IMPL_CASE_SET_FORMAT( N24BitTcRgb
, 24 );
257 IMPL_CASE_SET_FORMAT( N32BitTcAbgr
, 32 );
258 IMPL_CASE_SET_FORMAT( N32BitTcArgb
, 32 );
259 IMPL_CASE_SET_FORMAT( N32BitTcBgra
, 32 );
260 IMPL_CASE_SET_FORMAT( N32BitTcRgba
, 32 );
261 IMPL_CASE_SET_FORMAT( N32BitTcMask
, 32 );
264 // should never come here
265 // initialize pFncSetPixel to something valid that is
266 // least likely to crash
267 pFncSetPixel
= BitmapReadAccess::SetPixelForN1BitMsbPal
;
268 pDstBuffer
->mnBitCount
= 1;
269 OSL_FAIL( "unknown write format" );
273 // fill destination buffer
274 pDstBuffer
->mnFormat
= nDstBitmapFormat
;
275 pDstBuffer
->mnWidth
= rTwoRect
.mnDestWidth
;
276 pDstBuffer
->mnHeight
= rTwoRect
.mnDestHeight
;
277 tools::Long nScanlineBase
;
278 bool bFail
= o3tl::checked_multiply
<tools::Long
>(pDstBuffer
->mnBitCount
, pDstBuffer
->mnWidth
, nScanlineBase
);
281 SAL_WARN("vcl.gdi", "checked multiply failed");
282 pDstBuffer
->mpBits
= nullptr;
285 pDstBuffer
->mnScanlineSize
= AlignedWidth4Bytes(nScanlineBase
);
286 if (pDstBuffer
->mnScanlineSize
< nScanlineBase
/8)
288 SAL_WARN("vcl.gdi", "scanline calculation wraparound");
289 pDstBuffer
->mpBits
= nullptr;
294 pDstBuffer
->mpBits
= new sal_uInt8
[ pDstBuffer
->mnScanlineSize
* pDstBuffer
->mnHeight
];
296 catch( const std::bad_alloc
& )
298 // memory exception, clean up
299 pDstBuffer
->mpBits
= nullptr;
303 // do we need a destination palette or color mask?
304 if( ( nDstScanlineFormat
== ScanlineFormat::N1BitMsbPal
) ||
305 ( nDstScanlineFormat
== ScanlineFormat::N1BitLsbPal
) ||
306 ( nDstScanlineFormat
== ScanlineFormat::N8BitPal
) )
308 assert(pDstPal
&& "destination buffer requires palette");
313 pDstBuffer
->maPalette
= *pDstPal
;
315 else if(nDstScanlineFormat
== ScanlineFormat::N32BitTcMask
)
317 assert(pDstMask
&& "destination buffer requires color mask");
322 pDstBuffer
->maColorMask
= *pDstMask
;
325 // short circuit the most important conversions
326 bool bFastConvert
= ImplFastBitmapConversion( *pDstBuffer
, rSrcBuffer
, rTwoRect
);
330 std::unique_ptr
<Scanline
[]> pSrcScan
;
331 std::unique_ptr
<Scanline
[]> pDstScan
;
332 std::unique_ptr
<tools::Long
[]> pMapX
;
333 std::unique_ptr
<tools::Long
[]> pMapY
;
337 pSrcScan
.reset(new Scanline
[rSrcBuffer
.mnHeight
]);
338 pDstScan
.reset(new Scanline
[pDstBuffer
->mnHeight
]);
339 pMapX
.reset(new tools::Long
[pDstBuffer
->mnWidth
]);
340 pMapY
.reset(new tools::Long
[pDstBuffer
->mnHeight
]);
342 catch( const std::bad_alloc
& )
344 // memory exception, clean up
345 // remark: the buffer ptr causing the exception
346 // is still NULL here
350 // horizontal mapping table
351 if( (pDstBuffer
->mnWidth
!= rTwoRect
.mnSrcWidth
) && (pDstBuffer
->mnWidth
!= 0) )
353 const double fFactorX
= static_cast<double>(rTwoRect
.mnSrcWidth
) / pDstBuffer
->mnWidth
;
355 for (tools::Long i
= 0; i
< pDstBuffer
->mnWidth
; ++i
)
356 pMapX
[ i
] = rTwoRect
.mnSrcX
+ static_cast<int>( i
* fFactorX
);
360 for (tools::Long i
= 0, nTmp
= rTwoRect
.mnSrcX
; i
< pDstBuffer
->mnWidth
; ++i
)
364 // vertical mapping table
365 if( (pDstBuffer
->mnHeight
!= rTwoRect
.mnSrcHeight
) && (pDstBuffer
->mnHeight
!= 0) )
367 const double fFactorY
= static_cast<double>(rTwoRect
.mnSrcHeight
) / pDstBuffer
->mnHeight
;
369 for (tools::Long i
= 0; i
< pDstBuffer
->mnHeight
; ++i
)
370 pMapY
[ i
] = rTwoRect
.mnSrcY
+ static_cast<int>( i
* fFactorY
);
374 for (tools::Long i
= 0, nTmp
= rTwoRect
.mnSrcY
; i
< pDstBuffer
->mnHeight
; ++i
)
378 // source scanline buffer
381 if( rSrcBuffer
.mnFormat
& ScanlineFormat::TopDown
)
383 pTmpScan
= rSrcBuffer
.mpBits
;
384 nOffset
= rSrcBuffer
.mnScanlineSize
;
388 pTmpScan
= rSrcBuffer
.mpBits
+ ( rSrcBuffer
.mnHeight
- 1 ) * rSrcBuffer
.mnScanlineSize
;
389 nOffset
= -rSrcBuffer
.mnScanlineSize
;
392 for (tools::Long i
= 0; i
< rSrcBuffer
.mnHeight
; i
++, pTmpScan
+= nOffset
)
393 pSrcScan
[ i
] = pTmpScan
;
395 // destination scanline buffer
396 if( pDstBuffer
->mnFormat
& ScanlineFormat::TopDown
)
398 pTmpScan
= pDstBuffer
->mpBits
;
399 nOffset
= pDstBuffer
->mnScanlineSize
;
403 pTmpScan
= pDstBuffer
->mpBits
+ ( pDstBuffer
->mnHeight
- 1 ) * pDstBuffer
->mnScanlineSize
;
404 nOffset
= -pDstBuffer
->mnScanlineSize
;
407 for (tools::Long i
= 0; i
< pDstBuffer
->mnHeight
; i
++, pTmpScan
+= nOffset
)
408 pDstScan
[ i
] = pTmpScan
;
410 // do buffer scaling and conversion
411 if( rSrcBuffer
.mnBitCount
<= 8 && pDstBuffer
->mnBitCount
<= 8 )
413 ImplPALToPAL( rSrcBuffer
, *pDstBuffer
, pFncGetPixel
, pFncSetPixel
,
414 pSrcScan
.get(), pDstScan
.get(), pMapX
.get(), pMapY
.get() );
416 else if( rSrcBuffer
.mnBitCount
<= 8 && pDstBuffer
->mnBitCount
> 8 )
418 ImplPALToTC( rSrcBuffer
, *pDstBuffer
, pFncGetPixel
, pFncSetPixel
,
419 pSrcScan
.get(), pDstScan
.get(), pMapX
.get(), pMapY
.get() );
421 else if( rSrcBuffer
.mnBitCount
> 8 && pDstBuffer
->mnBitCount
> 8 )
423 ImplTCToTC( rSrcBuffer
, *pDstBuffer
, pFncGetPixel
, pFncSetPixel
,
424 pSrcScan
.get(), pDstScan
.get(), pMapX
.get(), pMapY
.get() );
428 ImplTCToPAL( rSrcBuffer
, *pDstBuffer
, pFncGetPixel
, pFncSetPixel
,
429 pSrcScan
.get(), pDstScan
.get(), pMapX
.get(), pMapY
.get() );
435 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */