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
, sal_Int32
const * pMapX
, const sal_Int32
* 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
, sal_Int32
const * pMapX
, const sal_Int32
* 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 (rSrcBuffer
.meFormat
== 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 (rSrcBuffer
.meFormat
== 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
, sal_Int32
const * pMapX
, const sal_Int32
* pMapY
)
149 const tools::Long nHeight1
= rDstBuffer
.mnHeight
- 1;
150 const ColorMask
& rSrcMask
= rSrcBuffer
.maColorMask
;
151 const ColorMask
& rDstMask
= rDstBuffer
.maColorMask
;
153 if (rSrcBuffer
.meFormat
== 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
, sal_Int32
const * pMapX
, const sal_Int32
* 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::optional
<BitmapBuffer
> StretchAndConvert(
230 const BitmapBuffer
& rSrcBuffer
, const SalTwoRect
& rTwoRect
,
231 ScanlineFormat nDstBitmapFormat
, std::optional
<BitmapPalette
> pDstPal
, const ColorMask
* pDstMask
)
233 FncGetPixel pFncGetPixel
;
234 FncSetPixel pFncSetPixel
;
235 std::optional
<BitmapBuffer
> pDstBuffer(std::in_place
);
238 pDstBuffer
->meDirection
= rSrcBuffer
.meDirection
;
239 // set function for getting pixels
240 pFncGetPixel
= BitmapReadAccess::GetPixelFunction(rSrcBuffer
.meFormat
);
243 // should never come here
244 // initialize pFncGetPixel to something valid that is
245 // least likely to crash
246 pFncGetPixel
= BitmapReadAccess::GetPixelForN1BitMsbPal
;
247 OSL_FAIL( "unknown read format" );
250 // set function for setting pixels
251 switch (nDstBitmapFormat
)
253 IMPL_CASE_SET_FORMAT( N1BitMsbPal
, 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
->meFormat
= 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 (nDstBitmapFormat
== ScanlineFormat::N1BitMsbPal
||
305 nDstBitmapFormat
== ScanlineFormat::N8BitPal
)
307 assert(pDstPal
&& "destination buffer requires palette");
312 pDstBuffer
->maPalette
= *pDstPal
;
314 else if (nDstBitmapFormat
== ScanlineFormat::N32BitTcMask
)
316 assert(pDstMask
&& "destination buffer requires color mask");
321 pDstBuffer
->maColorMask
= *pDstMask
;
324 // short circuit the most important conversions
325 bool bFastConvert
= ImplFastBitmapConversion( *pDstBuffer
, rSrcBuffer
, rTwoRect
);
329 std::unique_ptr
<Scanline
[]> pSrcScan
;
330 std::unique_ptr
<Scanline
[]> pDstScan
;
331 std::unique_ptr
<sal_Int32
[]> pMapX
;
332 std::unique_ptr
<sal_Int32
[]> pMapY
;
336 pSrcScan
.reset(new Scanline
[rSrcBuffer
.mnHeight
]);
337 pDstScan
.reset(new Scanline
[pDstBuffer
->mnHeight
]);
338 pMapX
.reset(new sal_Int32
[pDstBuffer
->mnWidth
]);
339 pMapY
.reset(new sal_Int32
[pDstBuffer
->mnHeight
]);
341 catch( const std::bad_alloc
& )
343 // memory exception, clean up
344 // remark: the buffer ptr causing the exception
345 // is still NULL here
349 // horizontal mapping table
350 if( (pDstBuffer
->mnWidth
!= rTwoRect
.mnSrcWidth
) && (pDstBuffer
->mnWidth
!= 0) )
352 const double fFactorX
= static_cast<double>(rTwoRect
.mnSrcWidth
) / pDstBuffer
->mnWidth
;
354 for (tools::Long i
= 0; i
< pDstBuffer
->mnWidth
; ++i
)
355 pMapX
[ i
] = rTwoRect
.mnSrcX
+ static_cast<int>( i
* fFactorX
);
359 for (tools::Long i
= 0, nTmp
= rTwoRect
.mnSrcX
; i
< pDstBuffer
->mnWidth
; ++i
)
363 // vertical mapping table
364 if( (pDstBuffer
->mnHeight
!= rTwoRect
.mnSrcHeight
) && (pDstBuffer
->mnHeight
!= 0) )
366 const double fFactorY
= static_cast<double>(rTwoRect
.mnSrcHeight
) / pDstBuffer
->mnHeight
;
368 for (tools::Long i
= 0; i
< pDstBuffer
->mnHeight
; ++i
)
369 pMapY
[ i
] = rTwoRect
.mnSrcY
+ static_cast<int>( i
* fFactorY
);
373 for (tools::Long i
= 0, nTmp
= rTwoRect
.mnSrcY
; i
< pDstBuffer
->mnHeight
; ++i
)
377 // source scanline buffer
380 if (rSrcBuffer
.meDirection
== ScanlineDirection::TopDown
)
382 pTmpScan
= rSrcBuffer
.mpBits
;
383 nOffset
= rSrcBuffer
.mnScanlineSize
;
387 pTmpScan
= rSrcBuffer
.mpBits
+ ( rSrcBuffer
.mnHeight
- 1 ) * rSrcBuffer
.mnScanlineSize
;
388 nOffset
= -rSrcBuffer
.mnScanlineSize
;
391 for (tools::Long i
= 0; i
< rSrcBuffer
.mnHeight
; i
++, pTmpScan
+= nOffset
)
392 pSrcScan
[ i
] = pTmpScan
;
394 // destination scanline buffer
395 if (pDstBuffer
->meDirection
== ScanlineDirection::TopDown
)
397 pTmpScan
= pDstBuffer
->mpBits
;
398 nOffset
= pDstBuffer
->mnScanlineSize
;
402 pTmpScan
= pDstBuffer
->mpBits
+ ( pDstBuffer
->mnHeight
- 1 ) * pDstBuffer
->mnScanlineSize
;
403 nOffset
= -pDstBuffer
->mnScanlineSize
;
406 for (tools::Long i
= 0; i
< pDstBuffer
->mnHeight
; i
++, pTmpScan
+= nOffset
)
407 pDstScan
[ i
] = pTmpScan
;
409 // do buffer scaling and conversion
410 if( rSrcBuffer
.mnBitCount
<= 8 && pDstBuffer
->mnBitCount
<= 8 )
412 ImplPALToPAL( rSrcBuffer
, *pDstBuffer
, pFncGetPixel
, pFncSetPixel
,
413 pSrcScan
.get(), pDstScan
.get(), pMapX
.get(), pMapY
.get() );
415 else if( rSrcBuffer
.mnBitCount
<= 8 && pDstBuffer
->mnBitCount
> 8 )
417 ImplPALToTC( rSrcBuffer
, *pDstBuffer
, pFncGetPixel
, pFncSetPixel
,
418 pSrcScan
.get(), pDstScan
.get(), pMapX
.get(), pMapY
.get() );
420 else if( rSrcBuffer
.mnBitCount
> 8 && pDstBuffer
->mnBitCount
> 8 )
422 ImplTCToTC( rSrcBuffer
, *pDstBuffer
, pFncGetPixel
, pFncSetPixel
,
423 pSrcScan
.get(), pDstScan
.get(), pMapX
.get(), pMapY
.get() );
427 ImplTCToPAL( rSrcBuffer
, *pDstBuffer
, pFncGetPixel
, pFncSetPixel
,
428 pSrcScan
.get(), pDstScan
.get(), pMapX
.get(), pMapY
.get() );
434 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */