Coverage Report

Created: 2026-04-09 11:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/bitmap/bitmappaint.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 <tools/poly.hxx>
21
#include <tools/helpers.hxx>
22
#include <tools/mapunit.hxx>
23
24
#include <vcl/bitmap.hxx>
25
#include <vcl/alpha.hxx>
26
27
#include <vcl/BitmapWriteAccess.hxx>
28
#include <salbmp.hxx>
29
#include <svdata.hxx>
30
#include <salinst.hxx>
31
32
#include <algorithm>
33
#include <memory>
34
35
static BitmapColor UpdatePaletteForNewColor(BitmapScopedWriteAccess& pAcc,
36
                                            const sal_uInt16 nActColors,
37
                                            const sal_uInt16 nMaxColors, const tools::Long nHeight,
38
                                            const tools::Long nWidth,
39
                                            const BitmapColor& rWantedColor);
40
41
bool Bitmap::Erase(const Color& rFillColor)
42
87.1k
{
43
87.1k
    if (IsEmpty())
44
605
        return true;
45
46
    // implementation specific replace
47
86.5k
    std::shared_ptr<SalBitmap> xImpBmp(GetSalInstance()->CreateSalBitmap());
48
86.5k
    if (xImpBmp->Create(*mxSalBmp) && xImpBmp->Erase(rFillColor))
49
0
    {
50
0
        ImplSetSalBitmap(xImpBmp);
51
0
        maPrefMapMode = MapMode(MapUnit::MapPixel);
52
0
        maPrefSize = xImpBmp->GetSize();
53
0
        return true;
54
0
    }
55
56
86.5k
    BitmapScopedWriteAccess pWriteAcc(*this);
57
86.5k
    bool bRet = false;
58
59
86.5k
    if (pWriteAcc)
60
86.5k
    {
61
86.5k
        pWriteAcc->Erase(rFillColor);
62
86.5k
        bRet = true;
63
86.5k
    }
64
65
86.5k
    return bRet;
66
86.5k
}
67
68
bool Bitmap::Invert()
69
34.5k
{
70
34.5k
    if (!mxSalBmp)
71
4.24k
        return false;
72
73
    // try optimised call, much faster on Skia
74
30.2k
    if (mxSalBmp->Invert())
75
0
    {
76
0
        mxSalBmp->InvalidateChecksum();
77
0
        return true;
78
0
    }
79
80
30.2k
    BitmapScopedWriteAccess pWriteAcc(*this);
81
30.2k
    const tools::Long nWidth = pWriteAcc->Width();
82
30.2k
    const tools::Long nHeight = pWriteAcc->Height();
83
84
30.2k
    if (pWriteAcc->HasPalette())
85
28.2k
    {
86
28.2k
        const sal_uInt16 nActColors = pWriteAcc->GetPaletteEntryCount();
87
88
28.2k
        if (pWriteAcc->GetPalette().IsGreyPalette8Bit())
89
21.9k
        {
90
            // For alpha masks, we need to actually invert the underlying data
91
            // or the optimisations elsewhere do not always work right. If this is a bottleneck,
92
            // probably better to try improving it inside the mxSalBmp->Invert() call above.
93
8.99M
            for (tools::Long nY = 0; nY < nHeight; nY++)
94
8.97M
            {
95
8.97M
                Scanline pScanline = pWriteAcc->GetScanline(nY);
96
80.5M
                for (tools::Long nX = 0; nX < nWidth; nX++)
97
71.6M
                {
98
71.6M
                    BitmapColor aBmpColor = pWriteAcc->GetPixelFromData(pScanline, nX);
99
71.6M
                    aBmpColor.SetIndex(0xff - aBmpColor.GetIndex());
100
71.6M
                    pWriteAcc->SetPixelOnData(pScanline, nX, aBmpColor);
101
71.6M
                }
102
8.97M
            }
103
21.9k
        }
104
6.31k
        else
105
6.31k
        {
106
1.62M
            for (sal_uInt16 i = 0; i < nActColors; ++i)
107
1.61M
            {
108
1.61M
                BitmapColor aBmpColor = pWriteAcc->GetPaletteColor(i);
109
1.61M
                aBmpColor.Invert();
110
1.61M
                pWriteAcc->SetPaletteColor(i, aBmpColor);
111
1.61M
            }
112
6.31k
        }
113
28.2k
    }
114
2.03k
    else
115
2.03k
    {
116
22.1k
        for (tools::Long nY = 0; nY < nHeight; nY++)
117
20.0k
        {
118
20.0k
            Scanline pScanline = pWriteAcc->GetScanline(nY);
119
42.4k
            for (tools::Long nX = 0; nX < nWidth; nX++)
120
22.4k
            {
121
22.4k
                BitmapColor aBmpColor = pWriteAcc->GetPixelFromData(pScanline, nX);
122
22.4k
                aBmpColor.Invert();
123
22.4k
                pWriteAcc->SetPixelOnData(pScanline, nX, aBmpColor);
124
22.4k
            }
125
20.0k
        }
126
2.03k
    }
127
30.2k
    mxSalBmp->InvalidateChecksum();
128
129
30.2k
    return true;
130
30.2k
}
131
132
namespace
133
{
134
// Put each scanline's content horizontally mirrored into the other one.
135
// (optimized version accessing pixel values directly).
136
template <int bitCount>
137
void mirrorScanlines(Scanline scanline1, Scanline scanline2, tools::Long nWidth)
138
0
{
139
0
    constexpr int byteCount = bitCount / 8;
140
0
    Scanline pos1 = scanline1;
141
0
    Scanline pos2 = scanline2 + (nWidth - 1) * byteCount; // last in second scanline
142
0
    sal_uInt8 tmp[byteCount];
143
0
    for (tools::Long i = 0; i < nWidth; ++i)
144
0
    {
145
0
        memcpy(tmp, pos1, byteCount);
146
0
        memcpy(pos1, pos2, byteCount);
147
0
        memcpy(pos2, tmp, byteCount);
148
0
        pos1 += byteCount;
149
0
        pos2 -= byteCount;
150
0
    }
151
0
}
Unexecuted instantiation: bitmappaint.cxx:void (anonymous namespace)::mirrorScanlines<32>(unsigned char*, unsigned char*, long)
Unexecuted instantiation: bitmappaint.cxx:void (anonymous namespace)::mirrorScanlines<24>(unsigned char*, unsigned char*, long)
Unexecuted instantiation: bitmappaint.cxx:void (anonymous namespace)::mirrorScanlines<8>(unsigned char*, unsigned char*, long)
152
}
153
154
bool Bitmap::Mirror(BmpMirrorFlags nMirrorFlags)
155
9.94k
{
156
9.94k
    bool bHorz(nMirrorFlags & BmpMirrorFlags::Horizontal);
157
9.94k
    bool bVert(nMirrorFlags & BmpMirrorFlags::Vertical);
158
9.94k
    bool bRet = false;
159
160
9.94k
    if (bHorz && !bVert)
161
0
    {
162
0
        BitmapScopedWriteAccess pAcc(*this);
163
164
0
        if (pAcc)
165
0
        {
166
0
            const tools::Long nWidth = pAcc->Width();
167
0
            const tools::Long nHeight = pAcc->Height();
168
0
            const tools::Long nWidth1 = nWidth - 1;
169
0
            const tools::Long nWidth_2 = nWidth / 2;
170
0
            const tools::Long nSecondHalf = nWidth - nWidth_2;
171
172
0
            switch (pAcc->GetBitCount())
173
0
            {
174
                // Special-case these, swap the halves of scanlines while mirroring them.
175
0
                case 32:
176
0
                    for (tools::Long nY = 0; nY < nHeight; nY++)
177
0
                        mirrorScanlines<32>(pAcc->GetScanline(nY),
178
0
                                            pAcc->GetScanline(nY) + 4 * nSecondHalf, nWidth_2);
179
0
                    break;
180
0
                case 24:
181
0
                    for (tools::Long nY = 0; nY < nHeight; nY++)
182
0
                        mirrorScanlines<24>(pAcc->GetScanline(nY),
183
0
                                            pAcc->GetScanline(nY) + 3 * nSecondHalf, nWidth_2);
184
0
                    break;
185
0
                case 8:
186
0
                    for (tools::Long nY = 0; nY < nHeight; nY++)
187
0
                        mirrorScanlines<8>(pAcc->GetScanline(nY),
188
0
                                           pAcc->GetScanline(nY) + nSecondHalf, nWidth_2);
189
0
                    break;
190
0
                default:
191
0
                    for (tools::Long nY = 0; nY < nHeight; nY++)
192
0
                    {
193
0
                        Scanline pScanline = pAcc->GetScanline(nY);
194
0
                        for (tools::Long nX = 0, nOther = nWidth1; nX < nWidth_2; nX++, nOther--)
195
0
                        {
196
0
                            const BitmapColor aTemp(pAcc->GetPixelFromData(pScanline, nX));
197
198
0
                            pAcc->SetPixelOnData(pScanline, nX,
199
0
                                                 pAcc->GetPixelFromData(pScanline, nOther));
200
0
                            pAcc->SetPixelOnData(pScanline, nOther, aTemp);
201
0
                        }
202
0
                    }
203
0
            }
204
205
0
            pAcc.reset();
206
0
            bRet = true;
207
0
        }
208
0
    }
209
9.94k
    else if (bVert && !bHorz)
210
9.94k
    {
211
9.94k
        BitmapScopedWriteAccess pAcc(*this);
212
213
9.94k
        if (pAcc)
214
9.94k
        {
215
9.94k
            const tools::Long nScanSize = pAcc->GetScanlineSize();
216
9.94k
            std::unique_ptr<sal_uInt8[]> pBuffer(new sal_uInt8[nScanSize]);
217
9.94k
            const tools::Long nHeight = pAcc->Height();
218
9.94k
            const tools::Long nHeight1 = nHeight - 1;
219
9.94k
            const tools::Long nHeight_2 = nHeight >> 1;
220
221
3.63M
            for (tools::Long nY = 0, nOther = nHeight1; nY < nHeight_2; nY++, nOther--)
222
3.62M
            {
223
3.62M
                memcpy(pBuffer.get(), pAcc->GetScanline(nY), nScanSize);
224
3.62M
                memcpy(pAcc->GetScanline(nY), pAcc->GetScanline(nOther), nScanSize);
225
3.62M
                memcpy(pAcc->GetScanline(nOther), pBuffer.get(), nScanSize);
226
3.62M
            }
227
228
9.94k
            pAcc.reset();
229
9.94k
            bRet = true;
230
9.94k
        }
231
9.94k
    }
232
0
    else if (bHorz && bVert)
233
0
    {
234
0
        BitmapScopedWriteAccess pAcc(*this);
235
236
0
        if (pAcc)
237
0
        {
238
0
            const tools::Long nWidth = pAcc->Width();
239
0
            const tools::Long nWidth1 = nWidth - 1;
240
0
            const tools::Long nHeight = pAcc->Height();
241
0
            tools::Long nHeight_2 = nHeight / 2;
242
0
            const tools::Long nWidth_2 = nWidth / 2;
243
0
            const tools::Long nSecondHalf = nWidth - nWidth_2;
244
245
0
            switch (pAcc->GetBitCount())
246
0
            {
247
0
                case 32:
248
0
                    for (tools::Long nY = 0, nOtherY = nHeight - 1; nY < nHeight_2; nY++, nOtherY--)
249
0
                        mirrorScanlines<32>(pAcc->GetScanline(nY), pAcc->GetScanline(nOtherY),
250
0
                                            nWidth);
251
0
                    if (nHeight & 1)
252
0
                        mirrorScanlines<32>(pAcc->GetScanline(nHeight_2),
253
0
                                            pAcc->GetScanline(nHeight_2) + 4 * nSecondHalf,
254
0
                                            nWidth_2);
255
0
                    break;
256
0
                case 24:
257
0
                    for (tools::Long nY = 0, nOtherY = nHeight - 1; nY < nHeight_2; nY++, nOtherY--)
258
0
                        mirrorScanlines<24>(pAcc->GetScanline(nY), pAcc->GetScanline(nOtherY),
259
0
                                            nWidth);
260
0
                    if (nHeight & 1)
261
0
                        mirrorScanlines<24>(pAcc->GetScanline(nHeight_2),
262
0
                                            pAcc->GetScanline(nHeight_2) + 3 * nSecondHalf,
263
0
                                            nWidth_2);
264
0
                    break;
265
0
                case 8:
266
0
                    for (tools::Long nY = 0, nOtherY = nHeight - 1; nY < nHeight_2; nY++, nOtherY--)
267
0
                        mirrorScanlines<8>(pAcc->GetScanline(nY), pAcc->GetScanline(nOtherY),
268
0
                                           nWidth);
269
0
                    if (nHeight & 1)
270
0
                        mirrorScanlines<8>(pAcc->GetScanline(nHeight_2),
271
0
                                           pAcc->GetScanline(nHeight_2) + nSecondHalf, nWidth_2);
272
0
                    break;
273
0
                default:
274
0
                    for (tools::Long nY = 0, nOtherY = nHeight - 1; nY < nHeight_2; nY++, nOtherY--)
275
0
                    {
276
0
                        Scanline pScanline = pAcc->GetScanline(nY);
277
0
                        Scanline pScanlineOther = pAcc->GetScanline(nOtherY);
278
0
                        for (tools::Long nX = 0, nOtherX = nWidth1; nX < nWidth; nX++, nOtherX--)
279
0
                        {
280
0
                            const BitmapColor aTemp(pAcc->GetPixelFromData(pScanline, nX));
281
282
0
                            pAcc->SetPixelOnData(pScanline, nX,
283
0
                                                 pAcc->GetPixelFromData(pScanlineOther, nOtherX));
284
0
                            pAcc->SetPixelOnData(pScanlineOther, nOtherX, aTemp);
285
0
                        }
286
0
                    }
287
288
                    // if necessary, also mirror the middle line horizontally
289
0
                    if (nHeight & 1)
290
0
                    {
291
0
                        Scanline pScanline = pAcc->GetScanline(nHeight_2);
292
0
                        for (tools::Long nX = 0, nOtherX = nWidth1; nX < nWidth_2; nX++, nOtherX--)
293
0
                        {
294
0
                            const BitmapColor aTemp(pAcc->GetPixelFromData(pScanline, nX));
295
0
                            pAcc->SetPixelOnData(pScanline, nX,
296
0
                                                 pAcc->GetPixelFromData(pScanline, nOtherX));
297
0
                            pAcc->SetPixelOnData(pScanline, nOtherX, aTemp);
298
0
                        }
299
0
                    }
300
0
            }
301
302
0
            pAcc.reset();
303
0
            bRet = true;
304
0
        }
305
0
    }
306
0
    else
307
0
        bRet = true;
308
309
9.94k
    return bRet;
310
9.94k
}
311
312
bool Bitmap::Rotate(Degree10 nAngle10, const Color& rFillColor)
313
12
{
314
12
    nAngle10 %= 3600_deg10;
315
12
    nAngle10 = (nAngle10 < 0_deg10) ? (Degree10(3599) + nAngle10) : nAngle10;
316
317
12
    if (!nAngle10)
318
0
        return true;
319
12
    if (nAngle10 == 1800_deg10)
320
0
        return Mirror(BmpMirrorFlags::Horizontal | BmpMirrorFlags::Vertical);
321
322
12
    BitmapScopedReadAccess pReadAcc(*this);
323
12
    if (!pReadAcc)
324
0
        return false;
325
326
12
    Bitmap aRotatedBmp;
327
12
    bool bRet = false;
328
12
    const Size aSizePix(GetSizePixel());
329
330
12
    if (nAngle10 == 900_deg10 || nAngle10 == 2700_deg10)
331
0
    {
332
0
        const Size aNewSizePix(aSizePix.Height(), aSizePix.Width());
333
0
        Bitmap aNewBmp(aNewSizePix, getPixelFormat(), &pReadAcc->GetPalette());
334
0
        BitmapScopedWriteAccess pWriteAcc(aNewBmp);
335
336
0
        if (pWriteAcc)
337
0
        {
338
0
            const tools::Long nWidth = aSizePix.Width();
339
0
            const tools::Long nWidth1 = nWidth - 1;
340
0
            const tools::Long nHeight = aSizePix.Height();
341
0
            const tools::Long nHeight1 = nHeight - 1;
342
0
            const tools::Long nNewWidth = aNewSizePix.Width();
343
0
            const tools::Long nNewHeight = aNewSizePix.Height();
344
345
0
            if (nAngle10 == 900_deg10)
346
0
            {
347
0
                for (tools::Long nY = 0, nOtherX = nWidth1; nY < nNewHeight; nY++, nOtherX--)
348
0
                {
349
0
                    Scanline pScanline = pWriteAcc->GetScanline(nY);
350
0
                    for (tools::Long nX = 0, nOtherY = 0; nX < nNewWidth; nX++)
351
0
                    {
352
0
                        pWriteAcc->SetPixelOnData(pScanline, nX,
353
0
                                                  pReadAcc->GetPixel(nOtherY++, nOtherX));
354
0
                    }
355
0
                }
356
0
            }
357
0
            else if (nAngle10 == 2700_deg10)
358
0
            {
359
0
                for (tools::Long nY = 0, nOtherX = 0; nY < nNewHeight; nY++, nOtherX++)
360
0
                {
361
0
                    Scanline pScanline = pWriteAcc->GetScanline(nY);
362
0
                    for (tools::Long nX = 0, nOtherY = nHeight1; nX < nNewWidth; nX++)
363
0
                    {
364
0
                        pWriteAcc->SetPixelOnData(pScanline, nX,
365
0
                                                  pReadAcc->GetPixel(nOtherY--, nOtherX));
366
0
                    }
367
0
                }
368
0
            }
369
370
0
            pWriteAcc.reset();
371
0
        }
372
373
0
        aRotatedBmp = std::move(aNewBmp);
374
0
    }
375
12
    else
376
12
    {
377
12
        Point aTmpPoint;
378
12
        tools::Rectangle aTmpRectangle(aTmpPoint, aSizePix);
379
12
        tools::Polygon aPoly(aTmpRectangle);
380
12
        aPoly.Rotate(aTmpPoint, nAngle10);
381
382
12
        tools::Rectangle aNewBound(aPoly.GetBoundRect());
383
12
        const Size aNewSizePix(aNewBound.GetSize());
384
12
        Bitmap aNewBmp(aNewSizePix, getPixelFormat(), &pReadAcc->GetPalette());
385
12
        BitmapScopedWriteAccess pWriteAcc(aNewBmp);
386
387
12
        if (pWriteAcc)
388
12
        {
389
12
            const BitmapColor aFillColor(pWriteAcc->GetBestMatchingColor(rFillColor));
390
12
            const double fCosAngle = cos(toRadians(nAngle10));
391
12
            const double fSinAngle = sin(toRadians(nAngle10));
392
12
            const double fXMin = aNewBound.Left();
393
12
            const double fYMin = aNewBound.Top();
394
12
            const sal_Int32 nWidth = aSizePix.Width();
395
12
            const sal_Int32 nHeight = aSizePix.Height();
396
12
            const sal_Int32 nNewWidth = aNewSizePix.Width();
397
12
            const sal_Int32 nNewHeight = aNewSizePix.Height();
398
            // we store alternating values of cos/sin. We do this instead of
399
            // separate arrays to improve cache hit.
400
12
            std::unique_ptr<sal_Int32[]> pCosSinX(new sal_Int32[nNewWidth * 2]);
401
12
            std::unique_ptr<sal_Int32[]> pCosSinY(new sal_Int32[nNewHeight * 2]);
402
403
10.5k
            for (sal_Int32 nIdx = 0, nX = 0; nX < nNewWidth; nX++)
404
10.4k
            {
405
10.4k
                const double fTmp = (fXMin + nX) * 64;
406
407
10.4k
                pCosSinX[nIdx++] = std::round(fCosAngle * fTmp);
408
10.4k
                pCosSinX[nIdx++] = std::round(fSinAngle * fTmp);
409
10.4k
            }
410
411
8.66k
            for (sal_Int32 nIdx = 0, nY = 0; nY < nNewHeight; nY++)
412
8.64k
            {
413
8.64k
                const double fTmp = (fYMin + nY) * 64;
414
415
8.64k
                pCosSinY[nIdx++] = std::round(fCosAngle * fTmp);
416
8.64k
                pCosSinY[nIdx++] = std::round(fSinAngle * fTmp);
417
8.64k
            }
418
419
8.66k
            for (sal_Int32 nCosSinYIdx = 0, nY = 0; nY < nNewHeight; nY++)
420
8.64k
            {
421
8.64k
                sal_Int32 nCosY = pCosSinY[nCosSinYIdx++];
422
8.64k
                sal_Int32 nSinY = pCosSinY[nCosSinYIdx++];
423
8.64k
                Scanline pScanline = pWriteAcc->GetScanline(nY);
424
425
8.64M
                for (sal_Int32 nCosSinXIdx = 0, nX = 0; nX < nNewWidth; nX++)
426
8.63M
                {
427
8.63M
                    sal_Int32 nRotX = (pCosSinX[nCosSinXIdx++] - nSinY) >> 6;
428
8.63M
                    sal_Int32 nRotY = (pCosSinX[nCosSinXIdx++] + nCosY) >> 6;
429
430
8.63M
                    if ((nRotX > -1) && (nRotX < nWidth) && (nRotY > -1) && (nRotY < nHeight))
431
5.75M
                    {
432
5.75M
                        pWriteAcc->SetPixelOnData(pScanline, nX, pReadAcc->GetPixel(nRotY, nRotX));
433
5.75M
                    }
434
2.88M
                    else
435
2.88M
                    {
436
2.88M
                        pWriteAcc->SetPixelOnData(pScanline, nX, aFillColor);
437
2.88M
                    }
438
8.63M
                }
439
8.64k
            }
440
441
12
            pWriteAcc.reset();
442
12
        }
443
444
12
        aRotatedBmp = std::move(aNewBmp);
445
12
    }
446
447
12
    pReadAcc.reset();
448
449
12
    bRet = !aRotatedBmp.IsEmpty();
450
12
    if (bRet)
451
12
        ReassignWithSize(aRotatedBmp);
452
453
12
    return bRet;
454
12
};
455
456
Bitmap Bitmap::CreateMask(const Color& rTransColor) const
457
0
{
458
0
    BitmapScopedReadAccess pReadAcc(*this);
459
0
    if (!pReadAcc)
460
0
        return Bitmap();
461
462
    // Historically LO used 1bpp masks, but 8bpp masks are much faster,
463
    // better supported by hardware, and the memory savings are not worth
464
    // it anymore.
465
    // TODO: Possibly remove the 1bpp code later.
466
467
0
    auto ePixelFormat = vcl::PixelFormat::N8_BPP;
468
0
    Bitmap aNewBmp(GetSizePixel(), ePixelFormat, &Bitmap::GetGreyPalette(256));
469
0
    BitmapScopedWriteAccess pWriteAcc(aNewBmp);
470
0
    if (!pWriteAcc)
471
0
        return Bitmap();
472
473
0
    const tools::Long nWidth = pReadAcc->Width();
474
0
    const tools::Long nHeight = pReadAcc->Height();
475
0
    const BitmapColor aBlack(pWriteAcc->GetBestMatchingColor(COL_BLACK));
476
0
    const BitmapColor aWhite(pWriteAcc->GetBestMatchingColor(COL_WHITE));
477
478
0
    const BitmapColor aTest(pReadAcc->GetBestMatchingColor(rTransColor));
479
480
0
    if (pReadAcc->GetScanlineFormat() == ScanlineFormat::N8BitPal)
481
0
    {
482
        // optimized for 8Bit source palette
483
0
        const sal_uInt8 cTest = aTest.GetIndex();
484
485
0
        for (tools::Long nY = 0; nY < nHeight; ++nY)
486
0
        {
487
0
            Scanline pSrc = pReadAcc->GetScanline(nY);
488
0
            Scanline pDst = pWriteAcc->GetScanline(nY);
489
0
            for (tools::Long nX = 0; nX < nWidth; ++nX)
490
0
            {
491
0
                if (cTest == pSrc[nX])
492
0
                    pDst[nX] = aWhite.GetIndex();
493
0
                else
494
0
                    pDst[nX] = aBlack.GetIndex();
495
0
            }
496
0
        }
497
0
    }
498
0
    else
499
0
    {
500
        // not optimized
501
0
        for (tools::Long nY = 0; nY < nHeight; ++nY)
502
0
        {
503
0
            Scanline pScanline = pWriteAcc->GetScanline(nY);
504
0
            Scanline pScanlineRead = pReadAcc->GetScanline(nY);
505
0
            for (tools::Long nX = 0; nX < nWidth; ++nX)
506
0
            {
507
0
                if (aTest == pReadAcc->GetPixelFromData(pScanlineRead, nX))
508
0
                    pWriteAcc->SetPixelOnData(pScanline, nX, aWhite);
509
0
                else
510
0
                    pWriteAcc->SetPixelOnData(pScanline, nX, aBlack);
511
0
            }
512
0
        }
513
0
    }
514
515
0
    pWriteAcc.reset();
516
0
    pReadAcc.reset();
517
518
0
    aNewBmp.maPrefSize = maPrefSize;
519
0
    aNewBmp.maPrefMapMode = maPrefMapMode;
520
521
0
    return aNewBmp;
522
0
}
523
524
Bitmap Bitmap::CreateMask(const Color& rTransColor, sal_uInt8 nTol) const
525
0
{
526
0
    if (nTol == 0)
527
0
        return CreateMask(rTransColor);
528
529
0
    BitmapScopedReadAccess pReadAcc(*this);
530
0
    if (!pReadAcc)
531
0
        return Bitmap();
532
533
    // Historically LO used 1bpp masks, but 8bpp masks are much faster,
534
    // better supported by hardware, and the memory savings are not worth
535
    // it anymore.
536
    // TODO: Possibly remove the 1bpp code later.
537
538
0
    auto ePixelFormat = vcl::PixelFormat::N8_BPP;
539
0
    Bitmap aNewBmp(GetSizePixel(), ePixelFormat, &Bitmap::GetGreyPalette(256));
540
0
    BitmapScopedWriteAccess pWriteAcc(aNewBmp);
541
0
    if (!pWriteAcc)
542
0
        return Bitmap();
543
544
0
    const tools::Long nWidth = pReadAcc->Width();
545
0
    const tools::Long nHeight = pReadAcc->Height();
546
0
    const BitmapColor aBlack(pWriteAcc->GetBestMatchingColor(COL_BLACK));
547
0
    const BitmapColor aWhite(pWriteAcc->GetBestMatchingColor(COL_WHITE));
548
549
0
    BitmapColor aCol;
550
0
    tools::Long nR, nG, nB;
551
0
    const tools::Long nMinR = std::clamp<tools::Long>(rTransColor.GetRed() - nTol, 0, 255);
552
0
    const tools::Long nMaxR = std::clamp<tools::Long>(rTransColor.GetRed() + nTol, 0, 255);
553
0
    const tools::Long nMinG = std::clamp<tools::Long>(rTransColor.GetGreen() - nTol, 0, 255);
554
0
    const tools::Long nMaxG = std::clamp<tools::Long>(rTransColor.GetGreen() + nTol, 0, 255);
555
0
    const tools::Long nMinB = std::clamp<tools::Long>(rTransColor.GetBlue() - nTol, 0, 255);
556
0
    const tools::Long nMaxB = std::clamp<tools::Long>(rTransColor.GetBlue() + nTol, 0, 255);
557
558
0
    if (pReadAcc->HasPalette())
559
0
    {
560
0
        for (tools::Long nY = 0; nY < nHeight; nY++)
561
0
        {
562
0
            Scanline pScanline = pWriteAcc->GetScanline(nY);
563
0
            Scanline pScanlineRead = pReadAcc->GetScanline(nY);
564
0
            for (tools::Long nX = 0; nX < nWidth; nX++)
565
0
            {
566
0
                aCol = pReadAcc->GetPaletteColor(pReadAcc->GetIndexFromData(pScanlineRead, nX));
567
0
                nR = aCol.GetRed();
568
0
                nG = aCol.GetGreen();
569
0
                nB = aCol.GetBlue();
570
571
0
                if (nMinR <= nR && nMaxR >= nR && nMinG <= nG && nMaxG >= nG && nMinB <= nB
572
0
                    && nMaxB >= nB)
573
0
                {
574
0
                    pWriteAcc->SetPixelOnData(pScanline, nX, aWhite);
575
0
                }
576
0
                else
577
0
                {
578
0
                    pWriteAcc->SetPixelOnData(pScanline, nX, aBlack);
579
0
                }
580
0
            }
581
0
        }
582
0
    }
583
0
    else
584
0
    {
585
0
        for (tools::Long nY = 0; nY < nHeight; nY++)
586
0
        {
587
0
            Scanline pScanline = pWriteAcc->GetScanline(nY);
588
0
            Scanline pScanlineRead = pReadAcc->GetScanline(nY);
589
0
            for (tools::Long nX = 0; nX < nWidth; nX++)
590
0
            {
591
0
                aCol = pReadAcc->GetPixelFromData(pScanlineRead, nX);
592
0
                nR = aCol.GetRed();
593
0
                nG = aCol.GetGreen();
594
0
                nB = aCol.GetBlue();
595
596
0
                if (nMinR <= nR && nMaxR >= nR && nMinG <= nG && nMaxG >= nG && nMinB <= nB
597
0
                    && nMaxB >= nB)
598
0
                {
599
0
                    pWriteAcc->SetPixelOnData(pScanline, nX, aWhite);
600
0
                }
601
0
                else
602
0
                {
603
0
                    pWriteAcc->SetPixelOnData(pScanline, nX, aBlack);
604
0
                }
605
0
            }
606
0
        }
607
0
    }
608
609
0
    pWriteAcc.reset();
610
0
    pReadAcc.reset();
611
612
0
    aNewBmp.maPrefSize = maPrefSize;
613
0
    aNewBmp.maPrefMapMode = maPrefMapMode;
614
615
0
    return aNewBmp;
616
0
}
617
618
AlphaMask Bitmap::CreateAlphaMask(const Color& rTransColor) const
619
1.71k
{
620
1.71k
    BitmapScopedReadAccess pReadAcc(*this);
621
1.71k
    if (!pReadAcc)
622
0
        return AlphaMask();
623
624
    // Historically LO used 1bpp masks, but 8bpp masks are much faster,
625
    // better supported by hardware, and the memory savings are not worth
626
    // it anymore.
627
628
1.71k
    AlphaMask aNewBmp(GetSizePixel());
629
1.71k
    BitmapScopedWriteAccess pWriteAcc(aNewBmp);
630
1.71k
    if (!pWriteAcc)
631
0
        return AlphaMask();
632
633
1.71k
    const tools::Long nWidth = pReadAcc->Width();
634
1.71k
    const tools::Long nHeight = pReadAcc->Height();
635
1.71k
    const BitmapColor aOpaqueColor(pWriteAcc->GetBestMatchingColor(COL_ALPHA_OPAQUE));
636
1.71k
    const BitmapColor aTransparentColor(pWriteAcc->GetBestMatchingColor(COL_ALPHA_TRANSPARENT));
637
638
1.71k
    const BitmapColor aTest(pReadAcc->GetBestMatchingColor(rTransColor));
639
640
1.71k
    if (pReadAcc->GetScanlineFormat() == ScanlineFormat::N8BitPal)
641
1.01k
    {
642
        // optimized for 8Bit source palette
643
1.01k
        const sal_uInt8 cTest = aTest.GetIndex();
644
645
6.86k
        for (tools::Long nY = 0; nY < nHeight; ++nY)
646
5.84k
        {
647
5.84k
            Scanline pSrc = pReadAcc->GetScanline(nY);
648
5.84k
            Scanline pDst = pWriteAcc->GetScanline(nY);
649
38.0k
            for (tools::Long nX = 0; nX < nWidth; ++nX)
650
32.2k
            {
651
32.2k
                if (cTest == pSrc[nX])
652
16.0k
                    pDst[nX] = aTransparentColor.GetIndex();
653
16.1k
                else
654
16.1k
                    pDst[nX] = aOpaqueColor.GetIndex();
655
32.2k
            }
656
5.84k
        }
657
1.01k
    }
658
702
    else
659
702
    {
660
        // not optimized
661
3.55k
        for (tools::Long nY = 0; nY < nHeight; ++nY)
662
2.85k
        {
663
2.85k
            Scanline pScanline = pWriteAcc->GetScanline(nY);
664
2.85k
            Scanline pScanlineRead = pReadAcc->GetScanline(nY);
665
6.78k
            for (tools::Long nX = 0; nX < nWidth; ++nX)
666
3.93k
            {
667
3.93k
                if (aTest == pReadAcc->GetPixelFromData(pScanlineRead, nX))
668
224
                    pWriteAcc->SetPixelOnData(pScanline, nX, aTransparentColor);
669
3.71k
                else
670
3.71k
                    pWriteAcc->SetPixelOnData(pScanline, nX, aOpaqueColor);
671
3.93k
            }
672
2.85k
        }
673
702
    }
674
675
1.71k
    pWriteAcc.reset();
676
1.71k
    pReadAcc.reset();
677
678
1.71k
    aNewBmp.SetPrefSize(maPrefSize);
679
1.71k
    aNewBmp.SetPrefMapMode(maPrefMapMode);
680
681
1.71k
    return aNewBmp;
682
1.71k
}
683
684
AlphaMask Bitmap::CreateAlphaMask(const Color& rTransColor, sal_uInt8 nTol) const
685
81
{
686
81
    if (nTol == 0)
687
0
        return CreateAlphaMask(rTransColor);
688
689
81
    BitmapScopedReadAccess pReadAcc(*this);
690
81
    if (!pReadAcc)
691
20
        return AlphaMask();
692
693
    // Historically LO used 1bpp masks, but 8bpp masks are much faster,
694
    // better supported by hardware, and the memory savings are not worth
695
    // it anymore.
696
    // TODO: Possibly remove the 1bpp code later.
697
698
61
    AlphaMask aNewBmp(GetSizePixel());
699
61
    BitmapScopedWriteAccess pWriteAcc(aNewBmp);
700
61
    if (!pWriteAcc)
701
0
        return AlphaMask();
702
703
61
    const tools::Long nWidth = pReadAcc->Width();
704
61
    const tools::Long nHeight = pReadAcc->Height();
705
61
    const BitmapColor aOpaqueColor(pWriteAcc->GetBestMatchingColor(COL_ALPHA_OPAQUE));
706
61
    const BitmapColor aTransparentColor(pWriteAcc->GetBestMatchingColor(COL_ALPHA_TRANSPARENT));
707
708
61
    BitmapColor aCol;
709
61
    tools::Long nR, nG, nB;
710
61
    const tools::Long nMinR = std::clamp<tools::Long>(rTransColor.GetRed() - nTol, 0, 255);
711
61
    const tools::Long nMaxR = std::clamp<tools::Long>(rTransColor.GetRed() + nTol, 0, 255);
712
61
    const tools::Long nMinG = std::clamp<tools::Long>(rTransColor.GetGreen() - nTol, 0, 255);
713
61
    const tools::Long nMaxG = std::clamp<tools::Long>(rTransColor.GetGreen() + nTol, 0, 255);
714
61
    const tools::Long nMinB = std::clamp<tools::Long>(rTransColor.GetBlue() - nTol, 0, 255);
715
61
    const tools::Long nMaxB = std::clamp<tools::Long>(rTransColor.GetBlue() + nTol, 0, 255);
716
717
61
    if (pReadAcc->HasPalette())
718
0
    {
719
0
        for (tools::Long nY = 0; nY < nHeight; nY++)
720
0
        {
721
0
            Scanline pScanline = pWriteAcc->GetScanline(nY);
722
0
            Scanline pScanlineRead = pReadAcc->GetScanline(nY);
723
0
            for (tools::Long nX = 0; nX < nWidth; nX++)
724
0
            {
725
0
                aCol = pReadAcc->GetPaletteColor(pReadAcc->GetIndexFromData(pScanlineRead, nX));
726
0
                nR = aCol.GetRed();
727
0
                nG = aCol.GetGreen();
728
0
                nB = aCol.GetBlue();
729
730
0
                if (nMinR <= nR && nMaxR >= nR && nMinG <= nG && nMaxG >= nG && nMinB <= nB
731
0
                    && nMaxB >= nB)
732
0
                {
733
0
                    pWriteAcc->SetPixelOnData(pScanline, nX, aTransparentColor);
734
0
                }
735
0
                else
736
0
                {
737
0
                    pWriteAcc->SetPixelOnData(pScanline, nX, aOpaqueColor);
738
0
                }
739
0
            }
740
0
        }
741
0
    }
742
61
    else
743
61
    {
744
23.6k
        for (tools::Long nY = 0; nY < nHeight; nY++)
745
23.5k
        {
746
23.5k
            Scanline pScanline = pWriteAcc->GetScanline(nY);
747
23.5k
            Scanline pScanlineRead = pReadAcc->GetScanline(nY);
748
12.4M
            for (tools::Long nX = 0; nX < nWidth; nX++)
749
12.4M
            {
750
12.4M
                aCol = pReadAcc->GetPixelFromData(pScanlineRead, nX);
751
12.4M
                nR = aCol.GetRed();
752
12.4M
                nG = aCol.GetGreen();
753
12.4M
                nB = aCol.GetBlue();
754
755
12.4M
                if (nMinR <= nR && nMaxR >= nR && nMinG <= nG && nMaxG >= nG && nMinB <= nB
756
3.62M
                    && nMaxB >= nB)
757
3.62M
                {
758
3.62M
                    pWriteAcc->SetPixelOnData(pScanline, nX, aTransparentColor);
759
3.62M
                }
760
8.83M
                else
761
8.83M
                {
762
8.83M
                    pWriteAcc->SetPixelOnData(pScanline, nX, aOpaqueColor);
763
8.83M
                }
764
12.4M
            }
765
23.5k
        }
766
61
    }
767
768
61
    pWriteAcc.reset();
769
61
    pReadAcc.reset();
770
771
61
    aNewBmp.SetPrefSize(maPrefSize);
772
61
    aNewBmp.SetPrefMapMode(maPrefMapMode);
773
774
61
    return aNewBmp;
775
61
}
776
777
vcl::Region Bitmap::CreateRegion(const Color& rColor, const tools::Rectangle& rRect) const
778
0
{
779
0
    tools::Rectangle aRect(rRect);
780
0
    BitmapScopedReadAccess pReadAcc(*this);
781
782
0
    aRect.Intersection(tools::Rectangle(Point(), GetSizePixel()));
783
0
    aRect.Normalize();
784
785
0
    if (!pReadAcc)
786
0
        return vcl::Region(aRect);
787
788
0
    vcl::Region aRegion;
789
0
    const tools::Long nLeft = aRect.Left();
790
0
    const tools::Long nTop = aRect.Top();
791
0
    const tools::Long nRight = aRect.Right();
792
0
    const tools::Long nBottom = aRect.Bottom();
793
0
    const BitmapColor aMatch(pReadAcc->GetBestMatchingColor(rColor));
794
795
0
    std::vector<tools::Long> aLine;
796
0
    tools::Long nYStart(nTop);
797
0
    tools::Long nY(nTop);
798
799
0
    for (; nY <= nBottom; nY++)
800
0
    {
801
0
        std::vector<tools::Long> aNewLine;
802
0
        tools::Long nX(nLeft);
803
0
        Scanline pScanlineRead = pReadAcc->GetScanline(nY);
804
805
0
        for (; nX <= nRight;)
806
0
        {
807
0
            while ((nX <= nRight) && (aMatch != pReadAcc->GetPixelFromData(pScanlineRead, nX)))
808
0
                nX++;
809
810
0
            if (nX <= nRight)
811
0
            {
812
0
                aNewLine.push_back(nX);
813
814
0
                while ((nX <= nRight) && (aMatch == pReadAcc->GetPixelFromData(pScanlineRead, nX)))
815
0
                {
816
0
                    nX++;
817
0
                }
818
819
0
                aNewLine.push_back(nX - 1);
820
0
            }
821
0
        }
822
823
0
        if (aNewLine != aLine)
824
0
        {
825
            // need to write aLine, it's different from the next line
826
0
            if (!aLine.empty())
827
0
            {
828
0
                tools::Rectangle aSubRect;
829
830
                // enter y values and proceed ystart
831
0
                aSubRect.SetTop(nYStart);
832
0
                aSubRect.SetBottom(nY ? nY - 1 : 0);
833
834
0
                for (size_t a(0); a < aLine.size();)
835
0
                {
836
0
                    aSubRect.SetLeft(aLine[a++]);
837
0
                    aSubRect.SetRight(aLine[a++]);
838
0
                    aRegion.Union(aSubRect);
839
0
                }
840
0
            }
841
842
            // copy line as new line
843
0
            aLine = std::move(aNewLine);
844
0
            nYStart = nY;
845
0
        }
846
0
    }
847
848
    // write last line if used
849
0
    if (!aLine.empty())
850
0
    {
851
0
        tools::Rectangle aSubRect;
852
853
        // enter y values
854
0
        aSubRect.SetTop(nYStart);
855
0
        aSubRect.SetBottom(nY ? nY - 1 : 0);
856
857
0
        for (size_t a(0); a < aLine.size();)
858
0
        {
859
0
            aSubRect.SetLeft(aLine[a++]);
860
0
            aSubRect.SetRight(aLine[a++]);
861
0
            aRegion.Union(aSubRect);
862
0
        }
863
0
    }
864
865
0
    pReadAcc.reset();
866
867
0
    return aRegion;
868
0
}
869
870
bool Bitmap::ReplaceMask(const AlphaMask& rMask, const Color& rReplaceColor)
871
0
{
872
0
    BitmapScopedReadAccess pMaskAcc(rMask);
873
0
    BitmapScopedWriteAccess pAcc(*this);
874
875
0
    if (!pMaskAcc || !pAcc)
876
0
        return false;
877
878
0
    const tools::Long nWidth = std::min(pMaskAcc->Width(), pAcc->Width());
879
0
    const tools::Long nHeight = std::min(pMaskAcc->Height(), pAcc->Height());
880
0
    const BitmapColor aMaskWhite(pMaskAcc->GetBestMatchingColor(COL_WHITE));
881
0
    BitmapColor aReplace;
882
883
0
    if (pAcc->HasPalette())
884
0
    {
885
0
        const sal_uInt16 nActColors = pAcc->GetPaletteEntryCount();
886
0
        const sal_uInt16 nMaxColors = 1 << pAcc->GetBitCount();
887
888
0
        aReplace = UpdatePaletteForNewColor(pAcc, nActColors, nMaxColors, nHeight, nWidth,
889
0
                                            BitmapColor(rReplaceColor));
890
0
    }
891
0
    else
892
0
        aReplace = rReplaceColor;
893
894
0
    for (tools::Long nY = 0; nY < nHeight; nY++)
895
0
    {
896
0
        Scanline pScanline = pAcc->GetScanline(nY);
897
0
        Scanline pScanlineMask = pMaskAcc->GetScanline(nY);
898
0
        for (tools::Long nX = 0; nX < nWidth; nX++)
899
0
        {
900
0
            if (pMaskAcc->GetPixelFromData(pScanlineMask, nX) == aMaskWhite)
901
0
                pAcc->SetPixelOnData(pScanline, nX, aReplace);
902
0
        }
903
0
    }
904
905
0
    return true;
906
0
}
907
908
bool Bitmap::Replace(const AlphaMask& rAlpha, const Color& rMergeColor)
909
0
{
910
0
    Bitmap aNewBmp(GetSizePixel(), vcl::PixelFormat::N24_BPP);
911
0
    BitmapScopedReadAccess pAcc(*this);
912
0
    BitmapScopedReadAccess pAlphaAcc(rAlpha);
913
0
    BitmapScopedWriteAccess pNewAcc(aNewBmp);
914
915
0
    if (!pAcc || !pAlphaAcc || !pNewAcc)
916
0
        return false;
917
918
0
    BitmapColor aCol;
919
0
    const tools::Long nWidth = std::min(pAlphaAcc->Width(), pAcc->Width());
920
0
    const tools::Long nHeight = std::min(pAlphaAcc->Height(), pAcc->Height());
921
922
0
    for (tools::Long nY = 0; nY < nHeight; nY++)
923
0
    {
924
0
        Scanline pScanline = pNewAcc->GetScanline(nY);
925
0
        Scanline pScanlineAlpha = pAlphaAcc->GetScanline(nY);
926
0
        for (tools::Long nX = 0; nX < nWidth; nX++)
927
0
        {
928
0
            aCol = pAcc->GetColor(nY, nX);
929
0
            aCol.Merge(rMergeColor, pAlphaAcc->GetIndexFromData(pScanlineAlpha, nX));
930
0
            pNewAcc->SetPixelOnData(pScanline, nX, aCol);
931
0
        }
932
0
    }
933
934
0
    pAcc.reset();
935
0
    pAlphaAcc.reset();
936
0
    pNewAcc.reset();
937
938
0
    const MapMode aMap(maPrefMapMode);
939
0
    const Size aSize(maPrefSize);
940
941
0
    *this = std::move(aNewBmp);
942
943
0
    maPrefMapMode = aMap;
944
0
    maPrefSize = aSize;
945
946
0
    return true;
947
0
}
948
949
bool Bitmap::Replace(const Color& rSearchColor, const Color& rReplaceColor, sal_uInt8 nTol)
950
0
{
951
0
    BitmapScopedWriteAccess pAcc(*this);
952
0
    if (!pAcc)
953
0
        return false;
954
955
0
    const tools::Long nMinR = std::clamp<tools::Long>(rSearchColor.GetRed() - nTol, 0, 255);
956
0
    const tools::Long nMaxR = std::clamp<tools::Long>(rSearchColor.GetRed() + nTol, 0, 255);
957
0
    const tools::Long nMinG = std::clamp<tools::Long>(rSearchColor.GetGreen() - nTol, 0, 255);
958
0
    const tools::Long nMaxG = std::clamp<tools::Long>(rSearchColor.GetGreen() + nTol, 0, 255);
959
0
    const tools::Long nMinB = std::clamp<tools::Long>(rSearchColor.GetBlue() - nTol, 0, 255);
960
0
    const tools::Long nMaxB = std::clamp<tools::Long>(rSearchColor.GetBlue() + nTol, 0, 255);
961
962
0
    if (pAcc->HasPalette())
963
0
    {
964
0
        for (sal_uInt16 i = 0, nPalCount = pAcc->GetPaletteEntryCount(); i < nPalCount; i++)
965
0
        {
966
0
            const BitmapColor& rCol = pAcc->GetPaletteColor(i);
967
968
0
            if (nMinR <= rCol.GetRed() && nMaxR >= rCol.GetRed() && nMinG <= rCol.GetGreen()
969
0
                && nMaxG >= rCol.GetGreen() && nMinB <= rCol.GetBlue() && nMaxB >= rCol.GetBlue())
970
0
            {
971
0
                pAcc->SetPaletteColor(i, rReplaceColor);
972
0
            }
973
0
        }
974
0
    }
975
0
    else
976
0
    {
977
0
        BitmapColor aCol;
978
0
        const BitmapColor aReplace(pAcc->GetBestMatchingColor(rReplaceColor));
979
980
0
        for (tools::Long nY = 0, nHeight = pAcc->Height(); nY < nHeight; nY++)
981
0
        {
982
0
            Scanline pScanline = pAcc->GetScanline(nY);
983
0
            for (tools::Long nX = 0, nWidth = pAcc->Width(); nX < nWidth; nX++)
984
0
            {
985
0
                aCol = pAcc->GetPixelFromData(pScanline, nX);
986
987
0
                if (nMinR <= aCol.GetRed() && nMaxR >= aCol.GetRed() && nMinG <= aCol.GetGreen()
988
0
                    && nMaxG >= aCol.GetGreen() && nMinB <= aCol.GetBlue()
989
0
                    && nMaxB >= aCol.GetBlue())
990
0
                {
991
0
                    BitmapColor aNewCol = aReplace;
992
0
                    aNewCol.SetAlpha(aCol.GetAlpha());
993
0
                    pAcc->SetPixelOnData(pScanline, nX, aNewCol);
994
0
                }
995
0
            }
996
0
        }
997
0
    }
998
999
0
    pAcc.reset();
1000
1001
0
    return true;
1002
0
}
1003
1004
bool Bitmap::Replace(const Color* pSearchColors, const Color* pReplaceColors, size_t nColorCount,
1005
                     sal_uInt8 const* pTols)
1006
0
{
1007
0
    BitmapScopedWriteAccess pAcc(*this);
1008
0
    if (!pAcc)
1009
0
        return false;
1010
1011
0
    std::vector<sal_uInt8> aMinR(nColorCount);
1012
0
    std::vector<sal_uInt8> aMaxR(nColorCount);
1013
0
    std::vector<sal_uInt8> aMinG(nColorCount);
1014
0
    std::vector<sal_uInt8> aMaxG(nColorCount);
1015
0
    std::vector<sal_uInt8> aMinB(nColorCount);
1016
0
    std::vector<sal_uInt8> aMaxB(nColorCount);
1017
1018
0
    if (pTols)
1019
0
    {
1020
0
        for (size_t i = 0; i < nColorCount; ++i)
1021
0
        {
1022
0
            const Color& rCol = pSearchColors[i];
1023
0
            const sal_uInt8 nTol = pTols[i];
1024
1025
0
            aMinR[i] = std::clamp(rCol.GetRed() - nTol, 0, 255);
1026
0
            aMaxR[i] = std::clamp(rCol.GetRed() + nTol, 0, 255);
1027
0
            aMinG[i] = std::clamp(rCol.GetGreen() - nTol, 0, 255);
1028
0
            aMaxG[i] = std::clamp(rCol.GetGreen() + nTol, 0, 255);
1029
0
            aMinB[i] = std::clamp(rCol.GetBlue() - nTol, 0, 255);
1030
0
            aMaxB[i] = std::clamp(rCol.GetBlue() + nTol, 0, 255);
1031
0
        }
1032
0
    }
1033
0
    else
1034
0
    {
1035
0
        for (size_t i = 0; i < nColorCount; ++i)
1036
0
        {
1037
0
            const Color& rCol = pSearchColors[i];
1038
1039
0
            aMinR[i] = rCol.GetRed();
1040
0
            aMaxR[i] = rCol.GetRed();
1041
0
            aMinG[i] = rCol.GetGreen();
1042
0
            aMaxG[i] = rCol.GetGreen();
1043
0
            aMinB[i] = rCol.GetBlue();
1044
0
            aMaxB[i] = rCol.GetBlue();
1045
0
        }
1046
0
    }
1047
1048
0
    if (pAcc->HasPalette())
1049
0
    {
1050
0
        for (sal_uInt16 nEntry = 0, nPalCount = pAcc->GetPaletteEntryCount(); nEntry < nPalCount;
1051
0
             nEntry++)
1052
0
        {
1053
0
            const BitmapColor& rCol = pAcc->GetPaletteColor(nEntry);
1054
1055
0
            for (size_t i = 0; i < nColorCount; ++i)
1056
0
            {
1057
0
                if (aMinR[i] <= rCol.GetRed() && aMaxR[i] >= rCol.GetRed()
1058
0
                    && aMinG[i] <= rCol.GetGreen() && aMaxG[i] >= rCol.GetGreen()
1059
0
                    && aMinB[i] <= rCol.GetBlue() && aMaxB[i] >= rCol.GetBlue())
1060
0
                {
1061
0
                    pAcc->SetPaletteColor(nEntry, pReplaceColors[i]);
1062
0
                    break;
1063
0
                }
1064
0
            }
1065
0
        }
1066
0
    }
1067
0
    else
1068
0
    {
1069
0
        std::vector<BitmapColor> aReplaces(nColorCount);
1070
1071
0
        for (size_t i = 0; i < nColorCount; ++i)
1072
0
            aReplaces[i] = pAcc->GetBestMatchingColor(pReplaceColors[i]);
1073
1074
0
        for (tools::Long nY = 0, nHeight = pAcc->Height(); nY < nHeight; nY++)
1075
0
        {
1076
0
            Scanline pScanline = pAcc->GetScanline(nY);
1077
0
            for (tools::Long nX = 0, nWidth = pAcc->Width(); nX < nWidth; nX++)
1078
0
            {
1079
0
                BitmapColor aCol = pAcc->GetPixelFromData(pScanline, nX);
1080
1081
0
                for (size_t i = 0; i < nColorCount; ++i)
1082
0
                {
1083
0
                    if (aMinR[i] <= aCol.GetRed() && aMaxR[i] >= aCol.GetRed()
1084
0
                        && aMinG[i] <= aCol.GetGreen() && aMaxG[i] >= aCol.GetGreen()
1085
0
                        && aMinB[i] <= aCol.GetBlue() && aMaxB[i] >= aCol.GetBlue())
1086
0
                    {
1087
0
                        BitmapColor aNewCol = aReplaces[i];
1088
0
                        aNewCol.SetAlpha(aCol.GetAlpha());
1089
0
                        pAcc->SetPixelOnData(pScanline, nX, aNewCol);
1090
0
                        break;
1091
0
                    }
1092
0
                }
1093
0
            }
1094
0
        }
1095
0
    }
1096
1097
0
    pAcc.reset();
1098
1099
0
    return true;
1100
0
}
1101
1102
// TODO: Have a look at OutputDevice::ImplDrawAlpha() for some
1103
// optimizations. Might even consolidate the code here and there.
1104
bool Bitmap::Blend(const AlphaMask& rAlpha, const Color& rBackgroundColor)
1105
0
{
1106
    // Convert to a truecolor bitmap, if we're a paletted one. There's room for tradeoff decision here,
1107
    // maybe later for an overload (or a flag)
1108
0
    if (vcl::isPalettePixelFormat(getPixelFormat()))
1109
0
        Convert(BmpConversion::N24Bit);
1110
1111
0
    BitmapScopedReadAccess pAlphaAcc(rAlpha);
1112
1113
0
    BitmapScopedWriteAccess pAcc(*this);
1114
1115
0
    if (!pAlphaAcc || !pAcc)
1116
0
        return false;
1117
1118
0
    const tools::Long nWidth = std::min(pAlphaAcc->Width(), pAcc->Width());
1119
0
    const tools::Long nHeight = std::min(pAlphaAcc->Height(), pAcc->Height());
1120
1121
0
    for (tools::Long nY = 0; nY < nHeight; ++nY)
1122
0
    {
1123
0
        Scanline pScanline = pAcc->GetScanline(nY);
1124
0
        Scanline pScanlineAlpha = pAlphaAcc->GetScanline(nY);
1125
0
        for (tools::Long nX = 0; nX < nWidth; ++nX)
1126
0
        {
1127
0
            BitmapColor aBmpColor = pAcc->GetPixelFromData(pScanline, nX);
1128
0
            aBmpColor.Merge(rBackgroundColor, pAlphaAcc->GetIndexFromData(pScanlineAlpha, nX));
1129
0
            pAcc->SetPixelOnData(pScanline, nX, aBmpColor);
1130
0
        }
1131
0
    }
1132
1133
0
    return true;
1134
0
}
1135
1136
static BitmapColor UpdatePaletteForNewColor(BitmapScopedWriteAccess& pAcc,
1137
                                            const sal_uInt16 nActColors,
1138
                                            const sal_uInt16 nMaxColors, const tools::Long nHeight,
1139
                                            const tools::Long nWidth,
1140
                                            const BitmapColor& rWantedColor)
1141
0
{
1142
    // default to the nearest color
1143
0
    sal_uInt16 aReplacePalIndex = pAcc->GetMatchingPaletteIndex(rWantedColor);
1144
0
    if (aReplacePalIndex != SAL_MAX_UINT16)
1145
0
        return BitmapColor(static_cast<sal_uInt8>(aReplacePalIndex));
1146
1147
    // for paletted images without a matching palette entry
1148
1149
    // if the palette has empty entries use the last one
1150
0
    if (nActColors < nMaxColors)
1151
0
    {
1152
0
        pAcc->SetPaletteEntryCount(nActColors + 1);
1153
0
        pAcc->SetPaletteColor(nActColors, rWantedColor);
1154
0
        return BitmapColor(static_cast<sal_uInt8>(nActColors));
1155
0
    }
1156
1157
    // look for an unused palette entry (NOTE: expensive!)
1158
0
    std::unique_ptr<bool[]> pFlags(new bool[nMaxColors]);
1159
1160
    // Set all entries to false
1161
0
    std::fill(pFlags.get(), pFlags.get() + nMaxColors, false);
1162
1163
0
    for (tools::Long nY = 0; nY < nHeight; nY++)
1164
0
    {
1165
0
        Scanline pScanline = pAcc->GetScanline(nY);
1166
0
        for (tools::Long nX = 0; nX < nWidth; nX++)
1167
0
            pFlags[pAcc->GetIndexFromData(pScanline, nX)] = true;
1168
0
    }
1169
1170
0
    for (sal_uInt16 i = 0; i < nMaxColors; i++)
1171
0
    {
1172
        // Hurray, we do have an unused entry
1173
0
        if (!pFlags[i])
1174
0
        {
1175
0
            pAcc->SetPaletteColor(i, rWantedColor);
1176
0
            return BitmapColor(static_cast<sal_uInt8>(i));
1177
0
        }
1178
0
    }
1179
0
    assert(false && "found nothing");
1180
0
    return BitmapColor(0);
1181
0
}
1182
1183
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */