Coverage Report

Created: 2026-10-14 09:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/gdi/salmisc.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
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/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
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 .
18
 */
19
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>
27
#include <memory>
28
29
6.75k
#define IMPL_CASE_SET_FORMAT( Format, BitCount )                \
30
6.75k
case( ScanlineFormat::Format ):                                 \
31
6.75k
{                                                               \
32
6.75k
    pFncSetPixel = BitmapReadAccess::SetPixelFor##Format;       \
33
6.75k
    pDstBuffer->mnBitCount = BitCount;                          \
34
6.75k
}                                                               \
35
6.75k
break
36
37
54.0k
#define DOUBLE_SCANLINES()                                                      \
38
54.0k
while( ( nActY < nHeight1 ) && ( pMapY[ nActY + 1 ] == nMapY ) )                \
39
0
{                                                                               \
40
0
    memcpy( pDstScanMap[ nActY + 1 ], pDstScan, rDstBuffer.mnScanlineSize );   \
41
0
    nActY++;                                                                    \
42
0
}
43
44
constexpr int TC_TO_PAL_COLORS = 4096;
45
46
static tools::Long ImplIndexFromColor( const BitmapColor& rCol )
47
0
{
48
0
    return  ( ( static_cast<tools::Long>(rCol.GetBlue()) >> 4) << 8 ) |
49
0
            ( ( static_cast<tools::Long>(rCol.GetGreen()) >> 4 ) << 4 ) |
50
0
            ( static_cast<tools::Long>(rCol.GetRed()) >> 4 );
51
0
}
52
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 )
56
0
{
57
0
    const tools::Long          nHeight1 = rDstBuffer.mnHeight - 1;
58
0
    BitmapPalette       aColMap( rSrcBuffer.maPalette.GetEntryCount() );
59
0
    BitmapColor*        pColMapBuf = aColMap.ImplGetColorBuffer();
60
0
    BitmapColor         aIndex( 0 );
61
62
0
    for( sal_uInt16 i = 0, nSrcCount = aColMap.GetEntryCount(), nDstCount = rDstBuffer.maPalette.GetEntryCount(); i < nSrcCount; i++ )
63
0
    {
64
0
        if( ( i < nDstCount ) && ( rSrcBuffer.maPalette[ i ] == rDstBuffer.maPalette[ i ] ) )
65
0
            aIndex.SetIndex( sal::static_int_cast<sal_uInt8>(i) );
66
0
        else
67
0
            aIndex.SetIndex( sal::static_int_cast<sal_uInt8>(rDstBuffer.maPalette.GetBestIndex( rSrcBuffer.maPalette[ i ] )) );
68
69
0
        pColMapBuf[ i ] = aIndex;
70
0
    }
71
72
0
    for (tools::Long nActY = 0; nActY < rDstBuffer.mnHeight; ++nActY)
73
0
    {
74
0
        tools::Long nMapY = pMapY[nActY];
75
0
        Scanline pSrcScan(pSrcScanMap[nMapY]), pDstScan(pDstScanMap[nActY]);
76
77
0
        for (tools::Long nX = 0; nX < rDstBuffer.mnWidth; ++nX)
78
0
            pFncSetPixel( pDstScan, nX, pColMapBuf[ pFncGetPixel( pSrcScan, pMapX[ nX ] ).GetIndex() ] );
79
80
0
        DOUBLE_SCANLINES();
81
0
    }
82
0
}
83
84
static void ImplPALToTC( const BitmapBuffer& rSrcBuffer, BitmapBuffer const & rDstBuffer,
85
                         FncGetPixel pFncGetPixel, FncSetPixel pFncSetPixel,
86
                         Scanline* pSrcScanMap, Scanline* pDstScanMap, sal_Int32 const * pMapX, const sal_Int32* pMapY )
87
6.75k
{
88
6.75k
    const tools::Long          nHeight1 = rDstBuffer.mnHeight - 1;
89
6.75k
    const BitmapColor*  pColBuf = rSrcBuffer.maPalette.ImplGetColorBuffer();
90
91
6.75k
    if (rSrcBuffer.meFormat == ScanlineFormat::N8BitPal)
92
6.75k
    {
93
60.7k
        for (tools::Long nActY = 0; nActY < rDstBuffer.mnHeight; ++nActY)
94
54.0k
        {
95
54.0k
            tools::Long nMapY = pMapY[nActY];
96
54.0k
            Scanline pSrcScan(pSrcScanMap[nMapY]), pDstScan(pDstScanMap[nActY]);
97
98
486k
            for (tools::Long nX = 0; nX < rDstBuffer.mnWidth; ++nX)
99
432k
                pFncSetPixel( pDstScan, nX, pColBuf[ pSrcScan[ pMapX[ nX ] ] ] );
100
101
54.0k
            DOUBLE_SCANLINES();
102
54.0k
        }
103
6.75k
    }
104
0
    else
105
0
    {
106
0
        for (tools::Long nActY = 0; nActY < rDstBuffer.mnHeight; ++nActY)
107
0
        {
108
0
            tools::Long nMapY = pMapY[nActY];
109
0
            Scanline pSrcScan(pSrcScanMap[nMapY]), pDstScan(pDstScanMap[nActY]);
110
111
0
            for (tools::Long nX = 0; nX < rDstBuffer.mnWidth; ++nX)
112
0
                pFncSetPixel( pDstScan, nX, pColBuf[ pFncGetPixel( pSrcScan, pMapX[ nX ] ).GetIndex() ] );
113
114
0
            DOUBLE_SCANLINES();
115
0
        }
116
0
    }
117
6.75k
}
118
119
static void ImplTCToTC( const BitmapBuffer& rSrcBuffer, BitmapBuffer const & rDstBuffer,
120
                        FncGetPixel pFncGetPixel, FncSetPixel pFncSetPixel,
121
                        Scanline* pSrcScanMap, Scanline* pDstScanMap, sal_Int32 const * pMapX, const sal_Int32* pMapY )
122
0
{
123
0
    const tools::Long          nHeight1 = rDstBuffer.mnHeight - 1;
124
125
0
    if (rSrcBuffer.meFormat == ScanlineFormat::N24BitTcBgr)
126
0
    {
127
0
        BitmapColor aCol;
128
0
        sal_uInt8* pPixel = nullptr;
129
130
0
        for (tools::Long nActY = 0; nActY < rDstBuffer.mnHeight; ++nActY)
131
0
        {
132
0
            tools::Long nMapY = pMapY[nActY];
133
0
            Scanline pSrcScan(pSrcScanMap[nMapY]), pDstScan(pDstScanMap[nActY]);
134
135
0
            for (tools::Long nX = 0; nX < rDstBuffer.mnWidth; ++nX)
136
0
            {
137
0
                pPixel = pSrcScan + pMapX[ nX ] * 3;
138
0
                aCol.SetBlue( *pPixel++ );
139
0
                aCol.SetGreen( *pPixel++ );
140
0
                aCol.SetRed( *pPixel );
141
0
                pFncSetPixel( pDstScan, nX, aCol );
142
0
            }
143
144
0
            DOUBLE_SCANLINES()
145
0
        }
146
0
    }
147
0
    else
148
0
    {
149
0
        for (tools::Long nActY = 0; nActY < rDstBuffer.mnHeight; ++nActY)
150
0
        {
151
0
            tools::Long nMapY = pMapY[nActY];
152
0
            Scanline pSrcScan(pSrcScanMap[nMapY]), pDstScan(pDstScanMap[nActY]);
153
154
0
            for (tools::Long nX = 0; nX < rDstBuffer.mnWidth; ++nX)
155
0
                pFncSetPixel( pDstScan, nX, pFncGetPixel( pSrcScan, pMapX[ nX ] ) );
156
157
0
            DOUBLE_SCANLINES();
158
0
        }
159
0
    }
160
0
}
161
162
static void ImplTCToPAL( BitmapBuffer const & rDstBuffer,
163
                         FncGetPixel pFncGetPixel, FncSetPixel pFncSetPixel,
164
                         Scanline* pSrcScanMap, Scanline* pDstScanMap, sal_Int32 const * pMapX, const sal_Int32* pMapY )
165
0
{
166
0
    const tools::Long          nHeight1 = rDstBuffer.mnHeight- 1;
167
0
    std::unique_ptr<sal_uInt8[]> pColToPalMap(new sal_uInt8[ TC_TO_PAL_COLORS ]);
168
0
    BitmapColor         aIndex( 0 );
169
170
0
    for( tools::Long nR = 0; nR < 16; nR++ )
171
0
    {
172
0
        for( tools::Long nG = 0; nG < 16; nG++ )
173
0
        {
174
0
            for( tools::Long nB = 0; nB < 16; nB++ )
175
0
            {
176
0
                BitmapColor aCol( sal::static_int_cast<sal_uInt8>(nR << 4),
177
0
                                  sal::static_int_cast<sal_uInt8>(nG << 4),
178
0
                                  sal::static_int_cast<sal_uInt8>(nB << 4) );
179
0
                pColToPalMap[ ImplIndexFromColor( aCol ) ] = static_cast<sal_uInt8>(rDstBuffer.maPalette.GetBestIndex( aCol ));
180
0
            }
181
0
        }
182
0
    }
183
184
0
    for (tools::Long nActY = 0; nActY < rDstBuffer.mnHeight; ++nActY)
185
0
    {
186
0
        tools::Long nMapY = pMapY[nActY];
187
0
        Scanline pSrcScan(pSrcScanMap[nMapY]), pDstScan(pDstScanMap[nActY]);
188
189
0
        for (tools::Long nX = 0; nX < rDstBuffer.mnWidth; ++nX)
190
0
        {
191
0
            aIndex.SetIndex( pColToPalMap[ ImplIndexFromColor( pFncGetPixel( pSrcScan, pMapX[ nX ] ) ) ] );
192
0
            pFncSetPixel( pDstScan, nX, aIndex );
193
0
        }
194
195
0
        DOUBLE_SCANLINES();
196
0
    }
197
0
}
198
199
std::optional<BitmapBuffer> StretchAndConvert(
200
    const BitmapBuffer& rSrcBuffer, const SalTwoRect& rTwoRect,
201
    ScanlineFormat nDstBitmapFormat, std::optional<BitmapPalette> pDstPal )
202
6.75k
{
203
6.75k
    FncGetPixel     pFncGetPixel;
204
6.75k
    FncSetPixel     pFncSetPixel;
205
6.75k
    std::optional<BitmapBuffer> pDstBuffer(std::in_place);
206
207
208
6.75k
    pDstBuffer->meDirection = rSrcBuffer.meDirection;
209
    // set function for getting pixels
210
6.75k
    pFncGetPixel = BitmapReadAccess::GetPixelFunction(rSrcBuffer.meFormat);
211
6.75k
    if( !pFncGetPixel )
212
0
    {
213
        // should never come here
214
        // initialize pFncGetPixel to something valid that is
215
        // least likely to crash
216
0
        pFncGetPixel = BitmapReadAccess::GetPixelForN8BitPal;
217
0
        OSL_FAIL( "unknown read format" );
218
0
    }
219
220
    // set function for setting pixels
221
6.75k
    switch (nDstBitmapFormat)
222
6.75k
    {
223
0
        IMPL_CASE_SET_FORMAT( N8BitPal, 8 );
224
0
        IMPL_CASE_SET_FORMAT( N24BitTcBgr, 24 );
225
0
        IMPL_CASE_SET_FORMAT( N24BitTcRgb, 24 );
226
0
        IMPL_CASE_SET_FORMAT( N32BitTcAbgr, 32 );
227
0
        IMPL_CASE_SET_FORMAT( N32BitTcXbgr, 32 );
228
0
        IMPL_CASE_SET_FORMAT( N32BitTcArgb, 32 );
229
0
        IMPL_CASE_SET_FORMAT( N32BitTcXrgb, 32 );
230
6.75k
        IMPL_CASE_SET_FORMAT( N32BitTcBgra, 32 );
231
0
        IMPL_CASE_SET_FORMAT( N32BitTcBgrx, 32 );
232
0
        IMPL_CASE_SET_FORMAT( N32BitTcRgba, 32 );
233
0
        IMPL_CASE_SET_FORMAT( N32BitTcRgbx, 32 );
234
235
0
        default:
236
            // should never come here
237
            // initialize pFncSetPixel to something valid that is
238
            // least likely to crash
239
0
            pFncSetPixel = BitmapReadAccess::SetPixelForN8BitPal;
240
0
            pDstBuffer->mnBitCount = 8;
241
0
            assert(false && "unknown write format" );
242
0
        break;
243
6.75k
    }
244
245
    // fill destination buffer
246
6.75k
    pDstBuffer->meFormat = nDstBitmapFormat;
247
6.75k
    pDstBuffer->mnWidth = rTwoRect.mnDestWidth;
248
6.75k
    pDstBuffer->mnHeight = rTwoRect.mnDestHeight;
249
6.75k
    tools::Long nScanlineBase;
250
6.75k
    bool bFail = o3tl::checked_multiply<tools::Long>(pDstBuffer->mnBitCount, pDstBuffer->mnWidth, nScanlineBase);
251
6.75k
    if (bFail)
252
0
    {
253
0
        SAL_WARN("vcl.gdi", "checked multiply failed");
254
0
        pDstBuffer->mpBits = nullptr;
255
0
        return std::nullopt;
256
0
    }
257
6.75k
    pDstBuffer->mnScanlineSize = AlignedWidth4Bytes(nScanlineBase);
258
6.75k
    if (pDstBuffer->mnScanlineSize < nScanlineBase/8)
259
0
    {
260
0
        SAL_WARN("vcl.gdi", "scanline calculation wraparound");
261
0
        pDstBuffer->mpBits = nullptr;
262
0
        return std::nullopt;
263
0
    }
264
6.75k
    try
265
6.75k
    {
266
6.75k
        pDstBuffer->mpBits = new sal_uInt8[ pDstBuffer->mnScanlineSize * pDstBuffer->mnHeight ];
267
6.75k
    }
268
6.75k
    catch( const std::bad_alloc& )
269
6.75k
    {
270
        // memory exception, clean up
271
0
        pDstBuffer->mpBits = nullptr;
272
0
        return std::nullopt;
273
0
    }
274
275
    // do we need a destination palette or color mask?
276
6.75k
    if (nDstBitmapFormat == ScanlineFormat::N8BitPal)
277
0
    {
278
0
        assert(pDstPal && "destination buffer requires palette");
279
0
        if (!pDstPal)
280
0
        {
281
0
            return std::nullopt;
282
0
        }
283
0
        pDstBuffer->maPalette = *pDstPal;
284
0
    }
285
286
    // short circuit the most important conversions
287
6.75k
    bool bFastConvert = ImplFastBitmapConversion( *pDstBuffer, rSrcBuffer, rTwoRect );
288
6.75k
    if( bFastConvert )
289
0
        return pDstBuffer;
290
291
6.75k
    std::unique_ptr<Scanline[]> pSrcScan;
292
6.75k
    std::unique_ptr<Scanline[]> pDstScan;
293
6.75k
    std::unique_ptr<sal_Int32[]> pMapX;
294
6.75k
    std::unique_ptr<sal_Int32[]> pMapY;
295
296
6.75k
    try
297
6.75k
    {
298
6.75k
        pSrcScan.reset(new Scanline[rSrcBuffer.mnHeight]);
299
6.75k
        pDstScan.reset(new Scanline[pDstBuffer->mnHeight]);
300
6.75k
        pMapX.reset(new sal_Int32[pDstBuffer->mnWidth]);
301
6.75k
        pMapY.reset(new sal_Int32[pDstBuffer->mnHeight]);
302
6.75k
    }
303
6.75k
    catch( const std::bad_alloc& )
304
6.75k
    {
305
        // memory exception, clean up
306
        // remark: the buffer ptr causing the exception
307
        // is still NULL here
308
0
        return std::nullopt;
309
0
    }
310
311
    // horizontal mapping table
312
6.75k
    if( (pDstBuffer->mnWidth != rTwoRect.mnSrcWidth) && (pDstBuffer->mnWidth != 0) )
313
0
    {
314
0
        const double fFactorX = static_cast<double>(rTwoRect.mnSrcWidth) / pDstBuffer->mnWidth;
315
316
0
        for (tools::Long i = 0; i < pDstBuffer->mnWidth; ++i)
317
0
            pMapX[ i ] = rTwoRect.mnSrcX + static_cast<int>( i * fFactorX );
318
0
    }
319
6.75k
    else
320
6.75k
    {
321
60.7k
        for (tools::Long i = 0, nTmp = rTwoRect.mnSrcX ; i < pDstBuffer->mnWidth; ++i)
322
54.0k
            pMapX[ i ] = nTmp++;
323
6.75k
    }
324
325
    // vertical mapping table
326
6.75k
    if( (pDstBuffer->mnHeight != rTwoRect.mnSrcHeight) && (pDstBuffer->mnHeight != 0) )
327
0
    {
328
0
        const double fFactorY = static_cast<double>(rTwoRect.mnSrcHeight) / pDstBuffer->mnHeight;
329
330
0
        for (tools::Long i = 0; i < pDstBuffer->mnHeight; ++i)
331
0
            pMapY[ i ] = rTwoRect.mnSrcY + static_cast<int>( i * fFactorY );
332
0
    }
333
6.75k
    else
334
6.75k
    {
335
60.7k
        for (tools::Long i = 0, nTmp = rTwoRect.mnSrcY; i < pDstBuffer->mnHeight; ++i)
336
54.0k
            pMapY[ i ] = nTmp++;
337
6.75k
    }
338
339
    // source scanline buffer
340
6.75k
    Scanline pTmpScan;
341
6.75k
    tools::Long nOffset;
342
6.75k
    if (rSrcBuffer.meDirection == ScanlineDirection::TopDown)
343
6.75k
    {
344
6.75k
        pTmpScan = rSrcBuffer.mpBits;
345
6.75k
        nOffset = rSrcBuffer.mnScanlineSize;
346
6.75k
    }
347
0
    else
348
0
    {
349
0
        pTmpScan = rSrcBuffer.mpBits + ( rSrcBuffer.mnHeight - 1 ) * rSrcBuffer.mnScanlineSize;
350
0
        nOffset = -rSrcBuffer.mnScanlineSize;
351
0
    }
352
353
60.7k
    for (tools::Long i = 0; i < rSrcBuffer.mnHeight; i++, pTmpScan += nOffset)
354
54.0k
        pSrcScan[ i ] = pTmpScan;
355
356
    // destination scanline buffer
357
6.75k
    if (pDstBuffer->meDirection == ScanlineDirection::TopDown)
358
6.75k
    {
359
6.75k
        pTmpScan = pDstBuffer->mpBits;
360
6.75k
        nOffset = pDstBuffer->mnScanlineSize;
361
6.75k
    }
362
0
    else
363
0
    {
364
0
        pTmpScan = pDstBuffer->mpBits + ( pDstBuffer->mnHeight - 1 ) * pDstBuffer->mnScanlineSize;
365
0
        nOffset = -pDstBuffer->mnScanlineSize;
366
0
    }
367
368
60.7k
    for (tools::Long i = 0; i < pDstBuffer->mnHeight; i++, pTmpScan += nOffset)
369
54.0k
        pDstScan[ i ] = pTmpScan;
370
371
    // do buffer scaling and conversion
372
6.75k
    if( rSrcBuffer.mnBitCount <= 8 && pDstBuffer->mnBitCount <= 8 )
373
0
    {
374
0
        ImplPALToPAL( rSrcBuffer, *pDstBuffer, pFncGetPixel, pFncSetPixel,
375
0
                      pSrcScan.get(), pDstScan.get(), pMapX.get(), pMapY.get() );
376
0
    }
377
6.75k
    else if( rSrcBuffer.mnBitCount <= 8 && pDstBuffer->mnBitCount > 8 )
378
6.75k
    {
379
6.75k
        ImplPALToTC( rSrcBuffer, *pDstBuffer, pFncGetPixel, pFncSetPixel,
380
6.75k
                     pSrcScan.get(), pDstScan.get(), pMapX.get(), pMapY.get() );
381
6.75k
    }
382
0
    else if( rSrcBuffer.mnBitCount > 8 && pDstBuffer->mnBitCount > 8 )
383
0
    {
384
0
        ImplTCToTC( rSrcBuffer, *pDstBuffer, pFncGetPixel, pFncSetPixel,
385
0
                    pSrcScan.get(), pDstScan.get(), pMapX.get(), pMapY.get() );
386
0
    }
387
0
    else
388
0
    {
389
0
        ImplTCToPAL( *pDstBuffer, pFncGetPixel, pFncSetPixel,
390
0
                     pSrcScan.get(), pDstScan.get(), pMapX.get(), pMapY.get() );
391
0
    }
392
393
6.75k
    return pDstBuffer;
394
6.75k
}
395
396
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */