bump product version to 6.4.0.3
[LibreOffice.git] / vcl / source / gdi / salmisc.cxx
blob29ddddaef4547de38c8831bc73e1d78f06d3bea6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/bitmapaccess.hxx>
21 #include <vcl/salgtype.hxx>
22 #include <bmpfast.hxx>
23 #include <o3tl/safeint.hxx>
24 #include <osl/diagnose.h>
25 #include <sal/log.hxx>
26 #include <tools/helpers.hxx>
27 #include <memory>
29 #define IMPL_CASE_GET_FORMAT( Format ) \
30 case( ScanlineFormat::Format ): \
31 pFncGetPixel = BitmapReadAccess::GetPixelFor##Format; \
32 break
34 #define IMPL_CASE_SET_FORMAT( Format, BitCount ) \
35 case( ScanlineFormat::Format ): \
36 { \
37 pFncSetPixel = BitmapReadAccess::SetPixelFor##Format; \
38 pDstBuffer->mnBitCount = BitCount; \
39 } \
40 break
42 #define DOUBLE_SCANLINES() \
43 while( ( nActY < nHeight1 ) && ( pMapY[ nActY + 1 ] == nMapY ) ) \
44 { \
45 memcpy( pDstScanMap[ nActY + 1 ], pDstScan, rDstBuffer.mnScanlineSize ); \
46 nActY++; \
49 #define TC_TO_PAL_COLORS 4096
51 static long ImplIndexFromColor( const BitmapColor& rCol )
53 #if TC_TO_PAL_COLORS == 4096
55 return( ( ( static_cast<long>(rCol.GetBlue()) >> 4) << 8 ) |
56 ( ( static_cast<long>(rCol.GetGreen()) >> 4 ) << 4 ) |
57 ( static_cast<long>(rCol.GetRed()) >> 4 ) );
59 #elif TC_TO_PAL_COLORS == 32768
61 return( ( ( (long) rCol.GetBlue() >> 3) << 10 ) |
62 ( ( (long) rCol.GetGreen() >> 3 ) << 5 ) |
63 ( (long) rCol.GetRed() >> 3 ) );
65 #endif
68 static void ImplPALToPAL( const BitmapBuffer& rSrcBuffer, BitmapBuffer& rDstBuffer,
69 FncGetPixel pFncGetPixel, FncSetPixel pFncSetPixel,
70 Scanline* pSrcScanMap, Scanline* pDstScanMap, long const * pMapX, const long* pMapY )
72 const long nHeight1 = rDstBuffer.mnHeight - 1;
73 const ColorMask& rSrcMask = rSrcBuffer.maColorMask;
74 const ColorMask& rDstMask = rDstBuffer.maColorMask;
75 BitmapPalette aColMap( rSrcBuffer.maPalette.GetEntryCount() );
76 BitmapColor* pColMapBuf = aColMap.ImplGetColorBuffer();
77 BitmapColor aIndex( 0 );
79 for( sal_uInt16 i = 0, nSrcCount = aColMap.GetEntryCount(), nDstCount = rDstBuffer.maPalette.GetEntryCount(); i < nSrcCount; i++ )
81 if( ( i < nDstCount ) && ( rSrcBuffer.maPalette[ i ] == rDstBuffer.maPalette[ i ] ) )
82 aIndex.SetIndex( sal::static_int_cast<sal_uInt8>(i) );
83 else
84 aIndex.SetIndex( sal::static_int_cast<sal_uInt8>(rDstBuffer.maPalette.GetBestIndex( rSrcBuffer.maPalette[ i ] )) );
86 pColMapBuf[ i ] = aIndex;
89 for (long nActY = 0; nActY < rDstBuffer.mnHeight; ++nActY)
91 long nMapY = pMapY[nActY];
92 Scanline pSrcScan(pSrcScanMap[nMapY]), pDstScan(pDstScanMap[nActY]);
94 for (long nX = 0; nX < rDstBuffer.mnWidth; ++nX)
95 pFncSetPixel( pDstScan, nX, pColMapBuf[ pFncGetPixel( pSrcScan, pMapX[ nX ], rSrcMask ).GetIndex() ], rDstMask );
97 DOUBLE_SCANLINES();
101 static void ImplPALToTC( const BitmapBuffer& rSrcBuffer, BitmapBuffer const & rDstBuffer,
102 FncGetPixel pFncGetPixel, FncSetPixel pFncSetPixel,
103 Scanline* pSrcScanMap, Scanline* pDstScanMap, long const * pMapX, const long* pMapY )
105 const long nHeight1 = rDstBuffer.mnHeight - 1;
106 const ColorMask& rSrcMask = rSrcBuffer.maColorMask;
107 const ColorMask& rDstMask = rDstBuffer.maColorMask;
108 const BitmapColor* pColBuf = rSrcBuffer.maPalette.ImplGetColorBuffer();
110 if( RemoveScanline( rSrcBuffer.mnFormat ) == ScanlineFormat::N1BitMsbPal )
112 const BitmapColor aCol0( pColBuf[ 0 ] );
113 const BitmapColor aCol1( pColBuf[ 1 ] );
114 long nMapX;
116 for (long nActY = 0; nActY < rDstBuffer.mnHeight; ++nActY)
118 long nMapY = pMapY[nActY];
119 Scanline pSrcScan(pSrcScanMap[nMapY]), pDstScan(pDstScanMap[nActY]);
121 for (long nX = 0; nX < rDstBuffer.mnWidth;)
123 nMapX = pMapX[ nX ];
124 pFncSetPixel( pDstScan, nX++,
125 pSrcScan[ nMapX >> 3 ] & ( 1 << ( 7 - ( nMapX & 7 ) ) ) ? aCol1 : aCol0,
126 rDstMask );
129 DOUBLE_SCANLINES();
132 else if( RemoveScanline( rSrcBuffer.mnFormat ) == ScanlineFormat::N4BitMsnPal )
134 long nMapX;
136 for (long nActY = 0; nActY < rDstBuffer.mnHeight; ++nActY)
138 long nMapY = pMapY[nActY];
139 Scanline pSrcScan(pSrcScanMap[nMapY]), pDstScan(pDstScanMap[nActY]);
141 for (long nX = 0; nX < rDstBuffer.mnWidth;)
143 nMapX = pMapX[ nX ];
144 pFncSetPixel( pDstScan, nX++,
145 pColBuf[ ( pSrcScan[ nMapX >> 1 ] >> ( nMapX & 1 ? 0 : 4 ) ) & 0x0f ],
146 rDstMask );
149 DOUBLE_SCANLINES();
152 else if( RemoveScanline( rSrcBuffer.mnFormat ) == ScanlineFormat::N8BitPal )
154 for (long nActY = 0; nActY < rDstBuffer.mnHeight; ++nActY)
156 long nMapY = pMapY[nActY];
157 Scanline pSrcScan(pSrcScanMap[nMapY]), pDstScan(pDstScanMap[nActY]);
159 for (long nX = 0; nX < rDstBuffer.mnWidth; ++nX)
160 pFncSetPixel( pDstScan, nX, pColBuf[ pSrcScan[ pMapX[ nX ] ] ], rDstMask );
162 DOUBLE_SCANLINES();
165 else
167 for (long nActY = 0; nActY < rDstBuffer.mnHeight; ++nActY)
169 long nMapY = pMapY[nActY];
170 Scanline pSrcScan(pSrcScanMap[nMapY]), pDstScan(pDstScanMap[nActY]);
172 for (long nX = 0; nX < rDstBuffer.mnWidth; ++nX)
173 pFncSetPixel( pDstScan, nX, pColBuf[ pFncGetPixel( pSrcScan, pMapX[ nX ], rSrcMask ).GetIndex() ], rDstMask );
175 DOUBLE_SCANLINES();
180 static void ImplTCToTC( const BitmapBuffer& rSrcBuffer, BitmapBuffer const & rDstBuffer,
181 FncGetPixel pFncGetPixel, FncSetPixel pFncSetPixel,
182 Scanline* pSrcScanMap, Scanline* pDstScanMap, long const * pMapX, const long* pMapY )
184 const long nHeight1 = rDstBuffer.mnHeight - 1;
185 const ColorMask& rSrcMask = rSrcBuffer.maColorMask;
186 const ColorMask& rDstMask = rDstBuffer.maColorMask;
188 if( RemoveScanline( rSrcBuffer.mnFormat ) == ScanlineFormat::N24BitTcBgr )
190 BitmapColor aCol;
191 sal_uInt8* pPixel = nullptr;
193 for (long nActY = 0; nActY < rDstBuffer.mnHeight; ++nActY)
195 long nMapY = pMapY[nActY];
196 Scanline pSrcScan(pSrcScanMap[nMapY]), pDstScan(pDstScanMap[nActY]);
198 for (long nX = 0; nX < rDstBuffer.mnWidth; ++nX)
200 pPixel = pSrcScan + pMapX[ nX ] * 3;
201 aCol.SetBlue( *pPixel++ );
202 aCol.SetGreen( *pPixel++ );
203 aCol.SetRed( *pPixel );
204 pFncSetPixel( pDstScan, nX, aCol, rDstMask );
207 DOUBLE_SCANLINES()
210 else
212 for (long nActY = 0; nActY < rDstBuffer.mnHeight; ++nActY)
214 long nMapY = pMapY[nActY];
215 Scanline pSrcScan(pSrcScanMap[nMapY]), pDstScan(pDstScanMap[nActY]);
217 for (long nX = 0; nX < rDstBuffer.mnWidth; ++nX)
218 pFncSetPixel( pDstScan, nX, pFncGetPixel( pSrcScan, pMapX[ nX ], rSrcMask ), rDstMask );
220 DOUBLE_SCANLINES();
225 static void ImplTCToPAL( const BitmapBuffer& rSrcBuffer, BitmapBuffer const & rDstBuffer,
226 FncGetPixel pFncGetPixel, FncSetPixel pFncSetPixel,
227 Scanline* pSrcScanMap, Scanline* pDstScanMap, long const * pMapX, const long* pMapY )
229 const long nHeight1 = rDstBuffer.mnHeight- 1;
230 const ColorMask& rSrcMask = rSrcBuffer.maColorMask;
231 const ColorMask& rDstMask = rDstBuffer.maColorMask;
232 BitmapPalette aColMap( rSrcBuffer.maPalette.GetEntryCount() );
233 std::unique_ptr<sal_uInt8[]> pColToPalMap(new sal_uInt8[ TC_TO_PAL_COLORS ]);
234 BitmapColor aIndex( 0 );
236 for( long nR = 0; nR < 16; nR++ )
238 for( long nG = 0; nG < 16; nG++ )
240 for( long nB = 0; nB < 16; nB++ )
242 BitmapColor aCol( sal::static_int_cast<sal_uInt8>(nR << 4),
243 sal::static_int_cast<sal_uInt8>(nG << 4),
244 sal::static_int_cast<sal_uInt8>(nB << 4) );
245 pColToPalMap[ ImplIndexFromColor( aCol ) ] = static_cast<sal_uInt8>(rDstBuffer.maPalette.GetBestIndex( aCol ));
250 for (long nActY = 0; nActY < rDstBuffer.mnHeight; ++nActY)
252 long nMapY = pMapY[nActY];
253 Scanline pSrcScan(pSrcScanMap[nMapY]), pDstScan(pDstScanMap[nActY]);
255 for (long nX = 0; nX < rDstBuffer.mnWidth; ++nX)
257 aIndex.SetIndex( pColToPalMap[ ImplIndexFromColor( pFncGetPixel( pSrcScan, pMapX[ nX ], rSrcMask ) ) ] );
258 pFncSetPixel( pDstScan, nX, aIndex, rDstMask );
261 DOUBLE_SCANLINES();
265 std::unique_ptr<BitmapBuffer> StretchAndConvert(
266 const BitmapBuffer& rSrcBuffer, const SalTwoRect& rTwoRect,
267 ScanlineFormat nDstBitmapFormat, const BitmapPalette* pDstPal, const ColorMask* pDstMask )
269 FncGetPixel pFncGetPixel;
270 FncSetPixel pFncSetPixel;
271 std::unique_ptr<BitmapBuffer> pDstBuffer(new BitmapBuffer);
273 // set function for getting pixels
274 switch( RemoveScanline( rSrcBuffer.mnFormat ) )
276 IMPL_CASE_GET_FORMAT( N1BitMsbPal );
277 IMPL_CASE_GET_FORMAT( N1BitLsbPal );
278 IMPL_CASE_GET_FORMAT( N4BitMsnPal );
279 IMPL_CASE_GET_FORMAT( N4BitLsnPal );
280 IMPL_CASE_GET_FORMAT( N8BitPal );
281 IMPL_CASE_GET_FORMAT( N8BitTcMask );
282 IMPL_CASE_GET_FORMAT( N24BitTcBgr );
283 IMPL_CASE_GET_FORMAT( N24BitTcRgb );
284 IMPL_CASE_GET_FORMAT( N32BitTcAbgr );
285 IMPL_CASE_GET_FORMAT( N32BitTcArgb );
286 IMPL_CASE_GET_FORMAT( N32BitTcBgra );
287 IMPL_CASE_GET_FORMAT( N32BitTcRgba );
288 IMPL_CASE_GET_FORMAT( N32BitTcMask );
290 default:
291 // should never come here
292 // initialize pFncGetPixel to something valid that is
293 // least likely to crash
294 pFncGetPixel = BitmapReadAccess::GetPixelForN1BitMsbPal;
295 OSL_FAIL( "unknown read format" );
296 break;
299 // set function for setting pixels
300 const ScanlineFormat nDstScanlineFormat = RemoveScanline( nDstBitmapFormat );
301 switch( nDstScanlineFormat )
303 IMPL_CASE_SET_FORMAT( N1BitMsbPal, 1 );
304 IMPL_CASE_SET_FORMAT( N1BitLsbPal, 1 );
305 IMPL_CASE_SET_FORMAT( N4BitMsnPal, 1 );
306 IMPL_CASE_SET_FORMAT( N4BitLsnPal, 4 );
307 IMPL_CASE_SET_FORMAT( N8BitPal, 8 );
308 IMPL_CASE_SET_FORMAT( N8BitTcMask, 8 );
309 IMPL_CASE_SET_FORMAT( N24BitTcBgr, 24 );
310 IMPL_CASE_SET_FORMAT( N24BitTcRgb, 24 );
311 IMPL_CASE_SET_FORMAT( N32BitTcAbgr, 32 );
312 IMPL_CASE_SET_FORMAT( N32BitTcArgb, 32 );
313 IMPL_CASE_SET_FORMAT( N32BitTcBgra, 32 );
314 IMPL_CASE_SET_FORMAT( N32BitTcRgba, 32 );
315 IMPL_CASE_SET_FORMAT( N32BitTcMask, 32 );
317 default:
318 // should never come here
319 // initialize pFncSetPixel to something valid that is
320 // least likely to crash
321 pFncSetPixel = BitmapReadAccess::SetPixelForN1BitMsbPal;
322 pDstBuffer->mnBitCount = 1;
323 OSL_FAIL( "unknown write format" );
324 break;
327 // fill destination buffer
328 pDstBuffer->mnFormat = nDstBitmapFormat;
329 pDstBuffer->mnWidth = rTwoRect.mnDestWidth;
330 pDstBuffer->mnHeight = rTwoRect.mnDestHeight;
331 long nScanlineBase;
332 bool bFail = o3tl::checked_multiply<long>(pDstBuffer->mnBitCount, pDstBuffer->mnWidth, nScanlineBase);
333 if (bFail)
335 SAL_WARN("vcl.gdi", "checked multiply failed");
336 pDstBuffer->mpBits = nullptr;
337 return nullptr;
339 pDstBuffer->mnScanlineSize = AlignedWidth4Bytes(nScanlineBase);
340 if (pDstBuffer->mnScanlineSize < nScanlineBase/8)
342 SAL_WARN("vcl.gdi", "scanline calculation wraparound");
343 pDstBuffer->mpBits = nullptr;
344 return nullptr;
348 pDstBuffer->mpBits = new sal_uInt8[ pDstBuffer->mnScanlineSize * pDstBuffer->mnHeight ];
350 catch( const std::bad_alloc& )
352 // memory exception, clean up
353 pDstBuffer->mpBits = nullptr;
354 return nullptr;
357 // do we need a destination palette or color mask?
358 if( ( nDstScanlineFormat == ScanlineFormat::N1BitMsbPal ) ||
359 ( nDstScanlineFormat == ScanlineFormat::N1BitLsbPal ) ||
360 ( nDstScanlineFormat == ScanlineFormat::N4BitMsnPal ) ||
361 ( nDstScanlineFormat == ScanlineFormat::N4BitLsnPal ) ||
362 ( nDstScanlineFormat == ScanlineFormat::N8BitPal ) )
364 assert(pDstPal && "destination buffer requires palette");
365 if (!pDstPal)
367 return nullptr;
369 pDstBuffer->maPalette = *pDstPal;
371 else if( ( nDstScanlineFormat == ScanlineFormat::N8BitTcMask ) ||
372 ( nDstScanlineFormat == ScanlineFormat::N32BitTcMask ) )
374 assert(pDstMask && "destination buffer requires color mask");
375 if (!pDstMask)
377 return nullptr;
379 pDstBuffer->maColorMask = *pDstMask;
382 // short circuit the most important conversions
383 bool bFastConvert = ImplFastBitmapConversion( *pDstBuffer, rSrcBuffer, rTwoRect );
384 if( bFastConvert )
385 return pDstBuffer;
387 std::unique_ptr<Scanline[]> pSrcScan;
388 std::unique_ptr<Scanline[]> pDstScan;
389 std::unique_ptr<long[]> pMapX;
390 std::unique_ptr<long[]> pMapY;
394 pSrcScan.reset(new Scanline[rSrcBuffer.mnHeight]);
395 pDstScan.reset(new Scanline[pDstBuffer->mnHeight]);
396 pMapX.reset(new long[pDstBuffer->mnWidth]);
397 pMapY.reset(new long[pDstBuffer->mnHeight]);
399 catch( const std::bad_alloc& )
401 // memory exception, clean up
402 // remark: the buffer ptr causing the exception
403 // is still NULL here
404 return nullptr;
407 // horizontal mapping table
408 if( (pDstBuffer->mnWidth != rTwoRect.mnSrcWidth) && (pDstBuffer->mnWidth != 0) )
410 const double fFactorX = static_cast<double>(rTwoRect.mnSrcWidth) / pDstBuffer->mnWidth;
412 for (long i = 0; i < pDstBuffer->mnWidth; ++i)
413 pMapX[ i ] = rTwoRect.mnSrcX + static_cast<int>( i * fFactorX );
415 else
417 for (long i = 0, nTmp = rTwoRect.mnSrcX ; i < pDstBuffer->mnWidth; ++i)
418 pMapX[ i ] = nTmp++;
421 // vertical mapping table
422 if( (pDstBuffer->mnHeight != rTwoRect.mnSrcHeight) && (pDstBuffer->mnHeight != 0) )
424 const double fFactorY = static_cast<double>(rTwoRect.mnSrcHeight) / pDstBuffer->mnHeight;
426 for (long i = 0; i < pDstBuffer->mnHeight; ++i)
427 pMapY[ i ] = rTwoRect.mnSrcY + static_cast<int>( i * fFactorY );
429 else
431 for (long i = 0, nTmp = rTwoRect.mnSrcY; i < pDstBuffer->mnHeight; ++i)
432 pMapY[ i ] = nTmp++;
435 // source scanline buffer
436 Scanline pTmpScan;
437 long nOffset;
438 if( rSrcBuffer.mnFormat & ScanlineFormat::TopDown )
440 pTmpScan = rSrcBuffer.mpBits;
441 nOffset = rSrcBuffer.mnScanlineSize;
443 else
445 pTmpScan = rSrcBuffer.mpBits + ( rSrcBuffer.mnHeight - 1 ) * rSrcBuffer.mnScanlineSize;
446 nOffset = -rSrcBuffer.mnScanlineSize;
449 for (long i = 0; i < rSrcBuffer.mnHeight; i++, pTmpScan += nOffset)
450 pSrcScan[ i ] = pTmpScan;
452 // destination scanline buffer
453 if( pDstBuffer->mnFormat & ScanlineFormat::TopDown )
455 pTmpScan = pDstBuffer->mpBits;
456 nOffset = pDstBuffer->mnScanlineSize;
458 else
460 pTmpScan = pDstBuffer->mpBits + ( pDstBuffer->mnHeight - 1 ) * pDstBuffer->mnScanlineSize;
461 nOffset = -pDstBuffer->mnScanlineSize;
464 for (long i = 0; i < pDstBuffer->mnHeight; i++, pTmpScan += nOffset)
465 pDstScan[ i ] = pTmpScan;
467 // do buffer scaling and conversion
468 if( rSrcBuffer.mnBitCount <= 8 && pDstBuffer->mnBitCount <= 8 )
470 ImplPALToPAL( rSrcBuffer, *pDstBuffer, pFncGetPixel, pFncSetPixel,
471 pSrcScan.get(), pDstScan.get(), pMapX.get(), pMapY.get() );
473 else if( rSrcBuffer.mnBitCount <= 8 && pDstBuffer->mnBitCount > 8 )
475 ImplPALToTC( rSrcBuffer, *pDstBuffer, pFncGetPixel, pFncSetPixel,
476 pSrcScan.get(), pDstScan.get(), pMapX.get(), pMapY.get() );
478 else if( rSrcBuffer.mnBitCount > 8 && pDstBuffer->mnBitCount > 8 )
480 ImplTCToTC( rSrcBuffer, *pDstBuffer, pFncGetPixel, pFncSetPixel,
481 pSrcScan.get(), pDstScan.get(), pMapX.get(), pMapY.get() );
483 else
485 ImplTCToPAL( rSrcBuffer, *pDstBuffer, pFncGetPixel, pFncSetPixel,
486 pSrcScan.get(), pDstScan.get(), pMapX.get(), pMapY.get() );
489 return pDstBuffer;
492 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */