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 <tools/poly.hxx>
21 #include <tools/helpers.hxx>
23 #include <vcl/bitmap.hxx>
24 #include <vcl/alpha.hxx>
26 #include <vcl/BitmapWriteAccess.hxx>
29 #include <salinst.hxx>
34 static BitmapColor
UpdatePaletteForNewColor(BitmapScopedWriteAccess
& pAcc
,
35 const sal_uInt16 nActColors
,
36 const sal_uInt16 nMaxColors
, const tools::Long nHeight
,
37 const tools::Long nWidth
,
38 const BitmapColor
& rWantedColor
);
40 bool Bitmap::Erase(const Color
& rFillColor
)
45 // implementation specific replace
46 std::shared_ptr
<SalBitmap
> xImpBmp(ImplGetSVData()->mpDefInst
->CreateSalBitmap());
47 if (xImpBmp
->Create(*mxSalBmp
) && xImpBmp
->Erase(rFillColor
))
49 ImplSetSalBitmap(xImpBmp
);
50 maPrefMapMode
= MapMode(MapUnit::MapPixel
);
51 maPrefSize
= xImpBmp
->GetSize();
55 BitmapScopedWriteAccess
pWriteAcc(*this);
60 pWriteAcc
->Erase(rFillColor
);
72 // try optimised call, much faster on Skia
73 if (mxSalBmp
->Invert())
75 mxSalBmp
->InvalidateChecksum();
79 BitmapScopedWriteAccess
pWriteAcc(*this);
80 const tools::Long nWidth
= pWriteAcc
->Width();
81 const tools::Long nHeight
= pWriteAcc
->Height();
83 if (pWriteAcc
->HasPalette())
85 const sal_uInt16 nActColors
= pWriteAcc
->GetPaletteEntryCount();
87 if (pWriteAcc
->GetPalette().IsGreyPalette8Bit())
89 // For alpha masks, we need to actually invert the underlying data
90 // or the optimisations elsewhere do not always work right. If this is a bottleneck,
91 // probably better to try improving it inside the mxSalBmp->Invert() call above.
92 for (tools::Long nY
= 0; nY
< nHeight
; nY
++)
94 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
95 for (tools::Long nX
= 0; nX
< nWidth
; nX
++)
97 BitmapColor aBmpColor
= pWriteAcc
->GetPixelFromData(pScanline
, nX
);
98 aBmpColor
.SetIndex(0xff - aBmpColor
.GetIndex());
99 pWriteAcc
->SetPixelOnData(pScanline
, nX
, aBmpColor
);
105 for (sal_uInt16 i
= 0; i
< nActColors
; ++i
)
107 BitmapColor aBmpColor
= pWriteAcc
->GetPaletteColor(i
);
109 pWriteAcc
->SetPaletteColor(i
, aBmpColor
);
115 for (tools::Long nY
= 0; nY
< nHeight
; nY
++)
117 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
118 for (tools::Long nX
= 0; nX
< nWidth
; nX
++)
120 BitmapColor aBmpColor
= pWriteAcc
->GetPixelFromData(pScanline
, nX
);
122 pWriteAcc
->SetPixelOnData(pScanline
, nX
, aBmpColor
);
126 mxSalBmp
->InvalidateChecksum();
133 // Put each scanline's content horizontally mirrored into the other one.
134 // (optimized version accessing pixel values directly).
135 template <int bitCount
>
136 void mirrorScanlines(Scanline scanline1
, Scanline scanline2
, tools::Long nWidth
)
138 constexpr int byteCount
= bitCount
/ 8;
139 Scanline pos1
= scanline1
;
140 Scanline pos2
= scanline2
+ (nWidth
- 1) * byteCount
; // last in second scanline
141 sal_uInt8 tmp
[byteCount
];
142 for (tools::Long i
= 0; i
< nWidth
; ++i
)
144 memcpy(tmp
, pos1
, byteCount
);
145 memcpy(pos1
, pos2
, byteCount
);
146 memcpy(pos2
, tmp
, byteCount
);
153 bool Bitmap::Mirror(BmpMirrorFlags nMirrorFlags
)
155 bool bHorz(nMirrorFlags
& BmpMirrorFlags::Horizontal
);
156 bool bVert(nMirrorFlags
& BmpMirrorFlags::Vertical
);
161 BitmapScopedWriteAccess
pAcc(*this);
165 const tools::Long nWidth
= pAcc
->Width();
166 const tools::Long nHeight
= pAcc
->Height();
167 const tools::Long nWidth1
= nWidth
- 1;
168 const tools::Long nWidth_2
= nWidth
/ 2;
169 const tools::Long nSecondHalf
= nWidth
- nWidth_2
;
171 switch (pAcc
->GetBitCount())
173 // Special-case these, swap the halves of scanlines while mirroring them.
175 for (tools::Long nY
= 0; nY
< nHeight
; nY
++)
176 mirrorScanlines
<32>(pAcc
->GetScanline(nY
),
177 pAcc
->GetScanline(nY
) + 4 * nSecondHalf
, nWidth_2
);
180 for (tools::Long nY
= 0; nY
< nHeight
; nY
++)
181 mirrorScanlines
<24>(pAcc
->GetScanline(nY
),
182 pAcc
->GetScanline(nY
) + 3 * nSecondHalf
, nWidth_2
);
185 for (tools::Long nY
= 0; nY
< nHeight
; nY
++)
186 mirrorScanlines
<8>(pAcc
->GetScanline(nY
),
187 pAcc
->GetScanline(nY
) + nSecondHalf
, nWidth_2
);
190 for (tools::Long nY
= 0; nY
< nHeight
; nY
++)
192 Scanline pScanline
= pAcc
->GetScanline(nY
);
193 for (tools::Long nX
= 0, nOther
= nWidth1
; nX
< nWidth_2
; nX
++, nOther
--)
195 const BitmapColor
aTemp(pAcc
->GetPixelFromData(pScanline
, nX
));
197 pAcc
->SetPixelOnData(pScanline
, nX
,
198 pAcc
->GetPixelFromData(pScanline
, nOther
));
199 pAcc
->SetPixelOnData(pScanline
, nOther
, aTemp
);
208 else if (bVert
&& !bHorz
)
210 BitmapScopedWriteAccess
pAcc(*this);
214 const tools::Long nScanSize
= pAcc
->GetScanlineSize();
215 std::unique_ptr
<sal_uInt8
[]> pBuffer(new sal_uInt8
[nScanSize
]);
216 const tools::Long nHeight
= pAcc
->Height();
217 const tools::Long nHeight1
= nHeight
- 1;
218 const tools::Long nHeight_2
= nHeight
>> 1;
220 for (tools::Long nY
= 0, nOther
= nHeight1
; nY
< nHeight_2
; nY
++, nOther
--)
222 memcpy(pBuffer
.get(), pAcc
->GetScanline(nY
), nScanSize
);
223 memcpy(pAcc
->GetScanline(nY
), pAcc
->GetScanline(nOther
), nScanSize
);
224 memcpy(pAcc
->GetScanline(nOther
), pBuffer
.get(), nScanSize
);
231 else if (bHorz
&& bVert
)
233 BitmapScopedWriteAccess
pAcc(*this);
237 const tools::Long nWidth
= pAcc
->Width();
238 const tools::Long nWidth1
= nWidth
- 1;
239 const tools::Long nHeight
= pAcc
->Height();
240 tools::Long nHeight_2
= nHeight
/ 2;
241 const tools::Long nWidth_2
= nWidth
/ 2;
242 const tools::Long nSecondHalf
= nWidth
- nWidth_2
;
244 switch (pAcc
->GetBitCount())
247 for (tools::Long nY
= 0, nOtherY
= nHeight
- 1; nY
< nHeight_2
; nY
++, nOtherY
--)
248 mirrorScanlines
<32>(pAcc
->GetScanline(nY
), pAcc
->GetScanline(nOtherY
),
251 mirrorScanlines
<32>(pAcc
->GetScanline(nHeight_2
),
252 pAcc
->GetScanline(nHeight_2
) + 4 * nSecondHalf
,
256 for (tools::Long nY
= 0, nOtherY
= nHeight
- 1; nY
< nHeight_2
; nY
++, nOtherY
--)
257 mirrorScanlines
<24>(pAcc
->GetScanline(nY
), pAcc
->GetScanline(nOtherY
),
260 mirrorScanlines
<24>(pAcc
->GetScanline(nHeight_2
),
261 pAcc
->GetScanline(nHeight_2
) + 3 * nSecondHalf
,
265 for (tools::Long nY
= 0, nOtherY
= nHeight
- 1; nY
< nHeight_2
; nY
++, nOtherY
--)
266 mirrorScanlines
<8>(pAcc
->GetScanline(nY
), pAcc
->GetScanline(nOtherY
),
269 mirrorScanlines
<8>(pAcc
->GetScanline(nHeight_2
),
270 pAcc
->GetScanline(nHeight_2
) + nSecondHalf
, nWidth_2
);
273 for (tools::Long nY
= 0, nOtherY
= nHeight
- 1; nY
< nHeight_2
; nY
++, nOtherY
--)
275 Scanline pScanline
= pAcc
->GetScanline(nY
);
276 Scanline pScanlineOther
= pAcc
->GetScanline(nOtherY
);
277 for (tools::Long nX
= 0, nOtherX
= nWidth1
; nX
< nWidth
; nX
++, nOtherX
--)
279 const BitmapColor
aTemp(pAcc
->GetPixelFromData(pScanline
, nX
));
281 pAcc
->SetPixelOnData(pScanline
, nX
,
282 pAcc
->GetPixelFromData(pScanlineOther
, nOtherX
));
283 pAcc
->SetPixelOnData(pScanlineOther
, nOtherX
, aTemp
);
287 // if necessary, also mirror the middle line horizontally
290 Scanline pScanline
= pAcc
->GetScanline(nHeight_2
);
291 for (tools::Long nX
= 0, nOtherX
= nWidth1
; nX
< nWidth_2
; nX
++, nOtherX
--)
293 const BitmapColor
aTemp(pAcc
->GetPixelFromData(pScanline
, nX
));
294 pAcc
->SetPixelOnData(pScanline
, nX
,
295 pAcc
->GetPixelFromData(pScanline
, nOtherX
));
296 pAcc
->SetPixelOnData(pScanline
, nOtherX
, aTemp
);
311 bool Bitmap::Rotate(Degree10 nAngle10
, const Color
& rFillColor
)
313 nAngle10
%= 3600_deg10
;
314 nAngle10
= (nAngle10
< 0_deg10
) ? (Degree10(3599) + nAngle10
) : nAngle10
;
318 if (nAngle10
== 1800_deg10
)
319 return Mirror(BmpMirrorFlags::Horizontal
| BmpMirrorFlags::Vertical
);
321 BitmapScopedReadAccess
pReadAcc(*this);
327 const Size
aSizePix(GetSizePixel());
329 if (nAngle10
== 900_deg10
|| nAngle10
== 2700_deg10
)
331 const Size
aNewSizePix(aSizePix
.Height(), aSizePix
.Width());
332 Bitmap
aNewBmp(aNewSizePix
, getPixelFormat(), &pReadAcc
->GetPalette());
333 BitmapScopedWriteAccess
pWriteAcc(aNewBmp
);
337 const tools::Long nWidth
= aSizePix
.Width();
338 const tools::Long nWidth1
= nWidth
- 1;
339 const tools::Long nHeight
= aSizePix
.Height();
340 const tools::Long nHeight1
= nHeight
- 1;
341 const tools::Long nNewWidth
= aNewSizePix
.Width();
342 const tools::Long nNewHeight
= aNewSizePix
.Height();
344 if (nAngle10
== 900_deg10
)
346 for (tools::Long nY
= 0, nOtherX
= nWidth1
; nY
< nNewHeight
; nY
++, nOtherX
--)
348 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
349 for (tools::Long nX
= 0, nOtherY
= 0; nX
< nNewWidth
; nX
++)
351 pWriteAcc
->SetPixelOnData(pScanline
, nX
,
352 pReadAcc
->GetPixel(nOtherY
++, nOtherX
));
356 else if (nAngle10
== 2700_deg10
)
358 for (tools::Long nY
= 0, nOtherX
= 0; nY
< nNewHeight
; nY
++, nOtherX
++)
360 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
361 for (tools::Long nX
= 0, nOtherY
= nHeight1
; nX
< nNewWidth
; nX
++)
363 pWriteAcc
->SetPixelOnData(pScanline
, nX
,
364 pReadAcc
->GetPixel(nOtherY
--, nOtherX
));
372 aRotatedBmp
= std::move(aNewBmp
);
377 tools::Rectangle
aTmpRectangle(aTmpPoint
, aSizePix
);
378 tools::Polygon
aPoly(aTmpRectangle
);
379 aPoly
.Rotate(aTmpPoint
, nAngle10
);
381 tools::Rectangle
aNewBound(aPoly
.GetBoundRect());
382 const Size
aNewSizePix(aNewBound
.GetSize());
383 Bitmap
aNewBmp(aNewSizePix
, getPixelFormat(), &pReadAcc
->GetPalette());
384 BitmapScopedWriteAccess
pWriteAcc(aNewBmp
);
388 const BitmapColor
aFillColor(pWriteAcc
->GetBestMatchingColor(rFillColor
));
389 const double fCosAngle
= cos(toRadians(nAngle10
));
390 const double fSinAngle
= sin(toRadians(nAngle10
));
391 const double fXMin
= aNewBound
.Left();
392 const double fYMin
= aNewBound
.Top();
393 const sal_Int32 nWidth
= aSizePix
.Width();
394 const sal_Int32 nHeight
= aSizePix
.Height();
395 const sal_Int32 nNewWidth
= aNewSizePix
.Width();
396 const sal_Int32 nNewHeight
= aNewSizePix
.Height();
397 // we store alternating values of cos/sin. We do this instead of
398 // separate arrays to improve cache hit.
399 std::unique_ptr
<sal_Int32
[]> pCosSinX(new sal_Int32
[nNewWidth
* 2]);
400 std::unique_ptr
<sal_Int32
[]> pCosSinY(new sal_Int32
[nNewHeight
* 2]);
402 for (sal_Int32 nIdx
= 0, nX
= 0; nX
< nNewWidth
; nX
++)
404 const double fTmp
= (fXMin
+ nX
) * 64;
406 pCosSinX
[nIdx
++] = std::round(fCosAngle
* fTmp
);
407 pCosSinX
[nIdx
++] = std::round(fSinAngle
* fTmp
);
410 for (sal_Int32 nIdx
= 0, nY
= 0; nY
< nNewHeight
; nY
++)
412 const double fTmp
= (fYMin
+ nY
) * 64;
414 pCosSinY
[nIdx
++] = std::round(fCosAngle
* fTmp
);
415 pCosSinY
[nIdx
++] = std::round(fSinAngle
* fTmp
);
418 for (sal_Int32 nCosSinYIdx
= 0, nY
= 0; nY
< nNewHeight
; nY
++)
420 sal_Int32 nCosY
= pCosSinY
[nCosSinYIdx
++];
421 sal_Int32 nSinY
= pCosSinY
[nCosSinYIdx
++];
422 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
424 for (sal_Int32 nCosSinXIdx
= 0, nX
= 0; nX
< nNewWidth
; nX
++)
426 sal_Int32 nRotX
= (pCosSinX
[nCosSinXIdx
++] - nSinY
) >> 6;
427 sal_Int32 nRotY
= (pCosSinX
[nCosSinXIdx
++] + nCosY
) >> 6;
429 if ((nRotX
> -1) && (nRotX
< nWidth
) && (nRotY
> -1) && (nRotY
< nHeight
))
431 pWriteAcc
->SetPixelOnData(pScanline
, nX
, pReadAcc
->GetPixel(nRotY
, nRotX
));
435 pWriteAcc
->SetPixelOnData(pScanline
, nX
, aFillColor
);
443 aRotatedBmp
= std::move(aNewBmp
);
448 bRet
= !aRotatedBmp
.IsEmpty();
450 ReassignWithSize(aRotatedBmp
);
455 Bitmap
Bitmap::CreateMask(const Color
& rTransColor
) const
457 BitmapScopedReadAccess
pReadAcc(*this);
461 // Historically LO used 1bpp masks, but 8bpp masks are much faster,
462 // better supported by hardware, and the memory savings are not worth
464 // TODO: Possibly remove the 1bpp code later.
466 if ((pReadAcc
->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal
)
467 && pReadAcc
->GetBestMatchingColor(COL_WHITE
) == pReadAcc
->GetBestMatchingColor(rTransColor
))
469 // if we're a 1 bit pixel already, and the transcolor matches the color that would replace it
470 // already, then just return a copy
474 auto ePixelFormat
= vcl::PixelFormat::N8_BPP
;
475 Bitmap
aNewBmp(GetSizePixel(), ePixelFormat
, &Bitmap::GetGreyPalette(256));
476 BitmapScopedWriteAccess
pWriteAcc(aNewBmp
);
480 const tools::Long nWidth
= pReadAcc
->Width();
481 const tools::Long nHeight
= pReadAcc
->Height();
482 const BitmapColor
aBlack(pWriteAcc
->GetBestMatchingColor(COL_BLACK
));
483 const BitmapColor
aWhite(pWriteAcc
->GetBestMatchingColor(COL_WHITE
));
485 const BitmapColor
aTest(pReadAcc
->GetBestMatchingColor(rTransColor
));
487 if (pWriteAcc
->GetScanlineFormat() == pReadAcc
->GetScanlineFormat() && aWhite
.GetIndex() == 1
488 && (pReadAcc
->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal
))
490 for (tools::Long nY
= 0; nY
< nHeight
; ++nY
)
492 Scanline pSrc
= pReadAcc
->GetScanline(nY
);
493 Scanline pDst
= pWriteAcc
->GetScanline(nY
);
494 assert(pWriteAcc
->GetScanlineSize() == pReadAcc
->GetScanlineSize());
495 const tools::Long nScanlineSize
= pWriteAcc
->GetScanlineSize();
496 for (tools::Long nX
= 0; nX
< nScanlineSize
; ++nX
)
497 pDst
[nX
] = ~pSrc
[nX
];
500 else if (pReadAcc
->GetScanlineFormat() == ScanlineFormat::N8BitPal
)
502 // optimized for 8Bit source palette
503 const sal_uInt8 cTest
= aTest
.GetIndex();
505 for (tools::Long nY
= 0; nY
< nHeight
; ++nY
)
507 Scanline pSrc
= pReadAcc
->GetScanline(nY
);
508 Scanline pDst
= pWriteAcc
->GetScanline(nY
);
509 for (tools::Long nX
= 0; nX
< nWidth
; ++nX
)
511 if (cTest
== pSrc
[nX
])
512 pDst
[nX
] = aWhite
.GetIndex();
514 pDst
[nX
] = aBlack
.GetIndex();
521 for (tools::Long nY
= 0; nY
< nHeight
; ++nY
)
523 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
524 Scanline pScanlineRead
= pReadAcc
->GetScanline(nY
);
525 for (tools::Long nX
= 0; nX
< nWidth
; ++nX
)
527 if (aTest
== pReadAcc
->GetPixelFromData(pScanlineRead
, nX
))
528 pWriteAcc
->SetPixelOnData(pScanline
, nX
, aWhite
);
530 pWriteAcc
->SetPixelOnData(pScanline
, nX
, aBlack
);
538 aNewBmp
.maPrefSize
= maPrefSize
;
539 aNewBmp
.maPrefMapMode
= maPrefMapMode
;
544 Bitmap
Bitmap::CreateMask(const Color
& rTransColor
, sal_uInt8 nTol
) const
547 return CreateMask(rTransColor
);
549 BitmapScopedReadAccess
pReadAcc(*this);
553 // Historically LO used 1bpp masks, but 8bpp masks are much faster,
554 // better supported by hardware, and the memory savings are not worth
556 // TODO: Possibly remove the 1bpp code later.
558 auto ePixelFormat
= vcl::PixelFormat::N8_BPP
;
559 Bitmap
aNewBmp(GetSizePixel(), ePixelFormat
, &Bitmap::GetGreyPalette(256));
560 BitmapScopedWriteAccess
pWriteAcc(aNewBmp
);
564 const tools::Long nWidth
= pReadAcc
->Width();
565 const tools::Long nHeight
= pReadAcc
->Height();
566 const BitmapColor
aBlack(pWriteAcc
->GetBestMatchingColor(COL_BLACK
));
567 const BitmapColor
aWhite(pWriteAcc
->GetBestMatchingColor(COL_WHITE
));
570 tools::Long nR
, nG
, nB
;
571 const tools::Long nMinR
= std::clamp
<tools::Long
>(rTransColor
.GetRed() - nTol
, 0, 255);
572 const tools::Long nMaxR
= std::clamp
<tools::Long
>(rTransColor
.GetRed() + nTol
, 0, 255);
573 const tools::Long nMinG
= std::clamp
<tools::Long
>(rTransColor
.GetGreen() - nTol
, 0, 255);
574 const tools::Long nMaxG
= std::clamp
<tools::Long
>(rTransColor
.GetGreen() + nTol
, 0, 255);
575 const tools::Long nMinB
= std::clamp
<tools::Long
>(rTransColor
.GetBlue() - nTol
, 0, 255);
576 const tools::Long nMaxB
= std::clamp
<tools::Long
>(rTransColor
.GetBlue() + nTol
, 0, 255);
578 if (pReadAcc
->HasPalette())
580 for (tools::Long nY
= 0; nY
< nHeight
; nY
++)
582 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
583 Scanline pScanlineRead
= pReadAcc
->GetScanline(nY
);
584 for (tools::Long nX
= 0; nX
< nWidth
; nX
++)
586 aCol
= pReadAcc
->GetPaletteColor(pReadAcc
->GetIndexFromData(pScanlineRead
, nX
));
588 nG
= aCol
.GetGreen();
591 if (nMinR
<= nR
&& nMaxR
>= nR
&& nMinG
<= nG
&& nMaxG
>= nG
&& nMinB
<= nB
594 pWriteAcc
->SetPixelOnData(pScanline
, nX
, aWhite
);
598 pWriteAcc
->SetPixelOnData(pScanline
, nX
, aBlack
);
605 for (tools::Long nY
= 0; nY
< nHeight
; nY
++)
607 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
608 Scanline pScanlineRead
= pReadAcc
->GetScanline(nY
);
609 for (tools::Long nX
= 0; nX
< nWidth
; nX
++)
611 aCol
= pReadAcc
->GetPixelFromData(pScanlineRead
, nX
);
613 nG
= aCol
.GetGreen();
616 if (nMinR
<= nR
&& nMaxR
>= nR
&& nMinG
<= nG
&& nMaxG
>= nG
&& nMinB
<= nB
619 pWriteAcc
->SetPixelOnData(pScanline
, nX
, aWhite
);
623 pWriteAcc
->SetPixelOnData(pScanline
, nX
, aBlack
);
632 aNewBmp
.maPrefSize
= maPrefSize
;
633 aNewBmp
.maPrefMapMode
= maPrefMapMode
;
638 AlphaMask
Bitmap::CreateAlphaMask(const Color
& rTransColor
) const
640 BitmapScopedReadAccess
pReadAcc(*this);
644 // Historically LO used 1bpp masks, but 8bpp masks are much faster,
645 // better supported by hardware, and the memory savings are not worth
648 if ((pReadAcc
->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal
)
649 && pReadAcc
->GetBestMatchingColor(COL_ALPHA_TRANSPARENT
)
650 == pReadAcc
->GetBestMatchingColor(rTransColor
))
652 // if we're a 1 bit pixel already, and the transcolor matches the color that would replace it
653 // already, then just return a copy
654 return AlphaMask(*this);
657 AlphaMask
aNewBmp(GetSizePixel());
658 BitmapScopedWriteAccess
pWriteAcc(aNewBmp
);
662 const tools::Long nWidth
= pReadAcc
->Width();
663 const tools::Long nHeight
= pReadAcc
->Height();
664 const BitmapColor
aOpaqueColor(pWriteAcc
->GetBestMatchingColor(COL_ALPHA_OPAQUE
));
665 const BitmapColor
aTransparentColor(pWriteAcc
->GetBestMatchingColor(COL_ALPHA_TRANSPARENT
));
667 const BitmapColor
aTest(pReadAcc
->GetBestMatchingColor(rTransColor
));
669 if (pReadAcc
->GetScanlineFormat() == ScanlineFormat::N8BitPal
)
671 // optimized for 8Bit source palette
672 const sal_uInt8 cTest
= aTest
.GetIndex();
674 for (tools::Long nY
= 0; nY
< nHeight
; ++nY
)
676 Scanline pSrc
= pReadAcc
->GetScanline(nY
);
677 Scanline pDst
= pWriteAcc
->GetScanline(nY
);
678 for (tools::Long nX
= 0; nX
< nWidth
; ++nX
)
680 if (cTest
== pSrc
[nX
])
681 pDst
[nX
] = aTransparentColor
.GetIndex();
683 pDst
[nX
] = aOpaqueColor
.GetIndex();
690 for (tools::Long nY
= 0; nY
< nHeight
; ++nY
)
692 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
693 Scanline pScanlineRead
= pReadAcc
->GetScanline(nY
);
694 for (tools::Long nX
= 0; nX
< nWidth
; ++nX
)
696 if (aTest
== pReadAcc
->GetPixelFromData(pScanlineRead
, nX
))
697 pWriteAcc
->SetPixelOnData(pScanline
, nX
, aTransparentColor
);
699 pWriteAcc
->SetPixelOnData(pScanline
, nX
, aOpaqueColor
);
707 aNewBmp
.SetPrefSize(maPrefSize
);
708 aNewBmp
.SetPrefMapMode(maPrefMapMode
);
713 AlphaMask
Bitmap::CreateAlphaMask(const Color
& rTransColor
, sal_uInt8 nTol
) const
716 return CreateAlphaMask(rTransColor
);
718 BitmapScopedReadAccess
pReadAcc(*this);
722 // Historically LO used 1bpp masks, but 8bpp masks are much faster,
723 // better supported by hardware, and the memory savings are not worth
725 // TODO: Possibly remove the 1bpp code later.
727 AlphaMask
aNewBmp(GetSizePixel());
728 BitmapScopedWriteAccess
pWriteAcc(aNewBmp
);
732 const tools::Long nWidth
= pReadAcc
->Width();
733 const tools::Long nHeight
= pReadAcc
->Height();
734 const BitmapColor
aOpaqueColor(pWriteAcc
->GetBestMatchingColor(COL_ALPHA_OPAQUE
));
735 const BitmapColor
aTransparentColor(pWriteAcc
->GetBestMatchingColor(COL_ALPHA_TRANSPARENT
));
738 tools::Long nR
, nG
, nB
;
739 const tools::Long nMinR
= std::clamp
<tools::Long
>(rTransColor
.GetRed() - nTol
, 0, 255);
740 const tools::Long nMaxR
= std::clamp
<tools::Long
>(rTransColor
.GetRed() + nTol
, 0, 255);
741 const tools::Long nMinG
= std::clamp
<tools::Long
>(rTransColor
.GetGreen() - nTol
, 0, 255);
742 const tools::Long nMaxG
= std::clamp
<tools::Long
>(rTransColor
.GetGreen() + nTol
, 0, 255);
743 const tools::Long nMinB
= std::clamp
<tools::Long
>(rTransColor
.GetBlue() - nTol
, 0, 255);
744 const tools::Long nMaxB
= std::clamp
<tools::Long
>(rTransColor
.GetBlue() + nTol
, 0, 255);
746 if (pReadAcc
->HasPalette())
748 for (tools::Long nY
= 0; nY
< nHeight
; nY
++)
750 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
751 Scanline pScanlineRead
= pReadAcc
->GetScanline(nY
);
752 for (tools::Long nX
= 0; nX
< nWidth
; nX
++)
754 aCol
= pReadAcc
->GetPaletteColor(pReadAcc
->GetIndexFromData(pScanlineRead
, nX
));
756 nG
= aCol
.GetGreen();
759 if (nMinR
<= nR
&& nMaxR
>= nR
&& nMinG
<= nG
&& nMaxG
>= nG
&& nMinB
<= nB
762 pWriteAcc
->SetPixelOnData(pScanline
, nX
, aTransparentColor
);
766 pWriteAcc
->SetPixelOnData(pScanline
, nX
, aOpaqueColor
);
773 for (tools::Long nY
= 0; nY
< nHeight
; nY
++)
775 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
776 Scanline pScanlineRead
= pReadAcc
->GetScanline(nY
);
777 for (tools::Long nX
= 0; nX
< nWidth
; nX
++)
779 aCol
= pReadAcc
->GetPixelFromData(pScanlineRead
, nX
);
781 nG
= aCol
.GetGreen();
784 if (nMinR
<= nR
&& nMaxR
>= nR
&& nMinG
<= nG
&& nMaxG
>= nG
&& nMinB
<= nB
787 pWriteAcc
->SetPixelOnData(pScanline
, nX
, aTransparentColor
);
791 pWriteAcc
->SetPixelOnData(pScanline
, nX
, aOpaqueColor
);
800 aNewBmp
.SetPrefSize(maPrefSize
);
801 aNewBmp
.SetPrefMapMode(maPrefMapMode
);
806 vcl::Region
Bitmap::CreateRegion(const Color
& rColor
, const tools::Rectangle
& rRect
) const
808 tools::Rectangle
aRect(rRect
);
809 BitmapScopedReadAccess
pReadAcc(*this);
811 aRect
.Intersection(tools::Rectangle(Point(), GetSizePixel()));
815 return vcl::Region(aRect
);
818 const tools::Long nLeft
= aRect
.Left();
819 const tools::Long nTop
= aRect
.Top();
820 const tools::Long nRight
= aRect
.Right();
821 const tools::Long nBottom
= aRect
.Bottom();
822 const BitmapColor
aMatch(pReadAcc
->GetBestMatchingColor(rColor
));
824 std::vector
<tools::Long
> aLine
;
825 tools::Long
nYStart(nTop
);
826 tools::Long
nY(nTop
);
828 for (; nY
<= nBottom
; nY
++)
830 std::vector
<tools::Long
> aNewLine
;
831 tools::Long
nX(nLeft
);
832 Scanline pScanlineRead
= pReadAcc
->GetScanline(nY
);
834 for (; nX
<= nRight
;)
836 while ((nX
<= nRight
) && (aMatch
!= pReadAcc
->GetPixelFromData(pScanlineRead
, nX
)))
841 aNewLine
.push_back(nX
);
843 while ((nX
<= nRight
) && (aMatch
== pReadAcc
->GetPixelFromData(pScanlineRead
, nX
)))
848 aNewLine
.push_back(nX
- 1);
852 if (aNewLine
!= aLine
)
854 // need to write aLine, it's different from the next line
857 tools::Rectangle aSubRect
;
859 // enter y values and proceed ystart
860 aSubRect
.SetTop(nYStart
);
861 aSubRect
.SetBottom(nY
? nY
- 1 : 0);
863 for (size_t a(0); a
< aLine
.size();)
865 aSubRect
.SetLeft(aLine
[a
++]);
866 aSubRect
.SetRight(aLine
[a
++]);
867 aRegion
.Union(aSubRect
);
871 // copy line as new line
872 aLine
= std::move(aNewLine
);
877 // write last line if used
880 tools::Rectangle aSubRect
;
883 aSubRect
.SetTop(nYStart
);
884 aSubRect
.SetBottom(nY
? nY
- 1 : 0);
886 for (size_t a(0); a
< aLine
.size();)
888 aSubRect
.SetLeft(aLine
[a
++]);
889 aSubRect
.SetRight(aLine
[a
++]);
890 aRegion
.Union(aSubRect
);
899 bool Bitmap::ReplaceMask(const AlphaMask
& rMask
, const Color
& rReplaceColor
)
901 BitmapScopedReadAccess
pMaskAcc(rMask
);
902 BitmapScopedWriteAccess
pAcc(*this);
904 if (!pMaskAcc
|| !pAcc
)
907 const tools::Long nWidth
= std::min(pMaskAcc
->Width(), pAcc
->Width());
908 const tools::Long nHeight
= std::min(pMaskAcc
->Height(), pAcc
->Height());
909 const BitmapColor
aMaskWhite(pMaskAcc
->GetBestMatchingColor(COL_WHITE
));
910 BitmapColor aReplace
;
912 if (pAcc
->HasPalette())
914 const sal_uInt16 nActColors
= pAcc
->GetPaletteEntryCount();
915 const sal_uInt16 nMaxColors
= 1 << pAcc
->GetBitCount();
917 aReplace
= UpdatePaletteForNewColor(pAcc
, nActColors
, nMaxColors
, nHeight
, nWidth
,
918 BitmapColor(rReplaceColor
));
921 aReplace
= rReplaceColor
;
923 for (tools::Long nY
= 0; nY
< nHeight
; nY
++)
925 Scanline pScanline
= pAcc
->GetScanline(nY
);
926 Scanline pScanlineMask
= pMaskAcc
->GetScanline(nY
);
927 for (tools::Long nX
= 0; nX
< nWidth
; nX
++)
929 if (pMaskAcc
->GetPixelFromData(pScanlineMask
, nX
) == aMaskWhite
)
930 pAcc
->SetPixelOnData(pScanline
, nX
, aReplace
);
937 bool Bitmap::Replace(const AlphaMask
& rAlpha
, const Color
& rMergeColor
)
939 Bitmap
aNewBmp(GetSizePixel(), vcl::PixelFormat::N24_BPP
);
940 BitmapScopedReadAccess
pAcc(*this);
941 BitmapScopedReadAccess
pAlphaAcc(rAlpha
);
942 BitmapScopedWriteAccess
pNewAcc(aNewBmp
);
944 if (!pAcc
|| !pAlphaAcc
|| !pNewAcc
)
948 const tools::Long nWidth
= std::min(pAlphaAcc
->Width(), pAcc
->Width());
949 const tools::Long nHeight
= std::min(pAlphaAcc
->Height(), pAcc
->Height());
951 for (tools::Long nY
= 0; nY
< nHeight
; nY
++)
953 Scanline pScanline
= pNewAcc
->GetScanline(nY
);
954 Scanline pScanlineAlpha
= pAlphaAcc
->GetScanline(nY
);
955 for (tools::Long nX
= 0; nX
< nWidth
; nX
++)
957 aCol
= pAcc
->GetColor(nY
, nX
);
958 aCol
.Merge(rMergeColor
, pAlphaAcc
->GetIndexFromData(pScanlineAlpha
, nX
));
959 pNewAcc
->SetPixelOnData(pScanline
, nX
, aCol
);
967 const MapMode
aMap(maPrefMapMode
);
968 const Size
aSize(maPrefSize
);
970 *this = std::move(aNewBmp
);
972 maPrefMapMode
= aMap
;
978 bool Bitmap::Replace(const Color
& rSearchColor
, const Color
& rReplaceColor
, sal_uInt8 nTol
)
982 // implementation specific replace
983 std::shared_ptr
<SalBitmap
> xImpBmp(ImplGetSVData()->mpDefInst
->CreateSalBitmap());
984 if (xImpBmp
->Create(*mxSalBmp
) && xImpBmp
->Replace(rSearchColor
, rReplaceColor
, nTol
))
986 ImplSetSalBitmap(xImpBmp
);
987 maPrefMapMode
= MapMode(MapUnit::MapPixel
);
988 maPrefSize
= xImpBmp
->GetSize();
993 BitmapScopedWriteAccess
pAcc(*this);
997 const tools::Long nMinR
= std::clamp
<tools::Long
>(rSearchColor
.GetRed() - nTol
, 0, 255);
998 const tools::Long nMaxR
= std::clamp
<tools::Long
>(rSearchColor
.GetRed() + nTol
, 0, 255);
999 const tools::Long nMinG
= std::clamp
<tools::Long
>(rSearchColor
.GetGreen() - nTol
, 0, 255);
1000 const tools::Long nMaxG
= std::clamp
<tools::Long
>(rSearchColor
.GetGreen() + nTol
, 0, 255);
1001 const tools::Long nMinB
= std::clamp
<tools::Long
>(rSearchColor
.GetBlue() - nTol
, 0, 255);
1002 const tools::Long nMaxB
= std::clamp
<tools::Long
>(rSearchColor
.GetBlue() + nTol
, 0, 255);
1004 if (pAcc
->HasPalette())
1006 for (sal_uInt16 i
= 0, nPalCount
= pAcc
->GetPaletteEntryCount(); i
< nPalCount
; i
++)
1008 const BitmapColor
& rCol
= pAcc
->GetPaletteColor(i
);
1010 if (nMinR
<= rCol
.GetRed() && nMaxR
>= rCol
.GetRed() && nMinG
<= rCol
.GetGreen()
1011 && nMaxG
>= rCol
.GetGreen() && nMinB
<= rCol
.GetBlue() && nMaxB
>= rCol
.GetBlue())
1013 pAcc
->SetPaletteColor(i
, rReplaceColor
);
1020 const BitmapColor
aReplace(pAcc
->GetBestMatchingColor(rReplaceColor
));
1022 for (tools::Long nY
= 0, nHeight
= pAcc
->Height(); nY
< nHeight
; nY
++)
1024 Scanline pScanline
= pAcc
->GetScanline(nY
);
1025 for (tools::Long nX
= 0, nWidth
= pAcc
->Width(); nX
< nWidth
; nX
++)
1027 aCol
= pAcc
->GetPixelFromData(pScanline
, nX
);
1029 if (nMinR
<= aCol
.GetRed() && nMaxR
>= aCol
.GetRed() && nMinG
<= aCol
.GetGreen()
1030 && nMaxG
>= aCol
.GetGreen() && nMinB
<= aCol
.GetBlue()
1031 && nMaxB
>= aCol
.GetBlue())
1033 pAcc
->SetPixelOnData(pScanline
, nX
, aReplace
);
1044 bool Bitmap::Replace(const Color
* pSearchColors
, const Color
* pReplaceColors
, size_t nColorCount
,
1045 sal_uInt8
const* pTols
)
1047 BitmapScopedWriteAccess
pAcc(*this);
1051 std::vector
<sal_uInt8
> aMinR(nColorCount
);
1052 std::vector
<sal_uInt8
> aMaxR(nColorCount
);
1053 std::vector
<sal_uInt8
> aMinG(nColorCount
);
1054 std::vector
<sal_uInt8
> aMaxG(nColorCount
);
1055 std::vector
<sal_uInt8
> aMinB(nColorCount
);
1056 std::vector
<sal_uInt8
> aMaxB(nColorCount
);
1060 for (size_t i
= 0; i
< nColorCount
; ++i
)
1062 const Color
& rCol
= pSearchColors
[i
];
1063 const sal_uInt8 nTol
= pTols
[i
];
1065 aMinR
[i
] = std::clamp(rCol
.GetRed() - nTol
, 0, 255);
1066 aMaxR
[i
] = std::clamp(rCol
.GetRed() + nTol
, 0, 255);
1067 aMinG
[i
] = std::clamp(rCol
.GetGreen() - nTol
, 0, 255);
1068 aMaxG
[i
] = std::clamp(rCol
.GetGreen() + nTol
, 0, 255);
1069 aMinB
[i
] = std::clamp(rCol
.GetBlue() - nTol
, 0, 255);
1070 aMaxB
[i
] = std::clamp(rCol
.GetBlue() + nTol
, 0, 255);
1075 for (size_t i
= 0; i
< nColorCount
; ++i
)
1077 const Color
& rCol
= pSearchColors
[i
];
1079 aMinR
[i
] = rCol
.GetRed();
1080 aMaxR
[i
] = rCol
.GetRed();
1081 aMinG
[i
] = rCol
.GetGreen();
1082 aMaxG
[i
] = rCol
.GetGreen();
1083 aMinB
[i
] = rCol
.GetBlue();
1084 aMaxB
[i
] = rCol
.GetBlue();
1088 if (pAcc
->HasPalette())
1090 for (sal_uInt16 nEntry
= 0, nPalCount
= pAcc
->GetPaletteEntryCount(); nEntry
< nPalCount
;
1093 const BitmapColor
& rCol
= pAcc
->GetPaletteColor(nEntry
);
1095 for (size_t i
= 0; i
< nColorCount
; ++i
)
1097 if (aMinR
[i
] <= rCol
.GetRed() && aMaxR
[i
] >= rCol
.GetRed()
1098 && aMinG
[i
] <= rCol
.GetGreen() && aMaxG
[i
] >= rCol
.GetGreen()
1099 && aMinB
[i
] <= rCol
.GetBlue() && aMaxB
[i
] >= rCol
.GetBlue())
1101 pAcc
->SetPaletteColor(nEntry
, pReplaceColors
[i
]);
1109 std::vector
<BitmapColor
> aReplaces(nColorCount
);
1111 for (size_t i
= 0; i
< nColorCount
; ++i
)
1112 aReplaces
[i
] = pAcc
->GetBestMatchingColor(pReplaceColors
[i
]);
1114 for (tools::Long nY
= 0, nHeight
= pAcc
->Height(); nY
< nHeight
; nY
++)
1116 Scanline pScanline
= pAcc
->GetScanline(nY
);
1117 for (tools::Long nX
= 0, nWidth
= pAcc
->Width(); nX
< nWidth
; nX
++)
1119 BitmapColor aCol
= pAcc
->GetPixelFromData(pScanline
, nX
);
1121 for (size_t i
= 0; i
< nColorCount
; ++i
)
1123 if (aMinR
[i
] <= aCol
.GetRed() && aMaxR
[i
] >= aCol
.GetRed()
1124 && aMinG
[i
] <= aCol
.GetGreen() && aMaxG
[i
] >= aCol
.GetGreen()
1125 && aMinB
[i
] <= aCol
.GetBlue() && aMaxB
[i
] >= aCol
.GetBlue())
1127 pAcc
->SetPixelOnData(pScanline
, nX
, aReplaces
[i
]);
1140 // TODO: Have a look at OutputDevice::ImplDrawAlpha() for some
1141 // optimizations. Might even consolidate the code here and there.
1142 bool Bitmap::Blend(const AlphaMask
& rAlpha
, const Color
& rBackgroundColor
)
1144 // Convert to a truecolor bitmap, if we're a paletted one. There's room for tradeoff decision here,
1145 // maybe later for an overload (or a flag)
1146 if (vcl::isPalettePixelFormat(getPixelFormat()))
1147 Convert(BmpConversion::N24Bit
);
1149 BitmapScopedReadAccess
pAlphaAcc(rAlpha
);
1151 BitmapScopedWriteAccess
pAcc(*this);
1153 if (!pAlphaAcc
|| !pAcc
)
1156 const tools::Long nWidth
= std::min(pAlphaAcc
->Width(), pAcc
->Width());
1157 const tools::Long nHeight
= std::min(pAlphaAcc
->Height(), pAcc
->Height());
1159 for (tools::Long nY
= 0; nY
< nHeight
; ++nY
)
1161 Scanline pScanline
= pAcc
->GetScanline(nY
);
1162 Scanline pScanlineAlpha
= pAlphaAcc
->GetScanline(nY
);
1163 for (tools::Long nX
= 0; nX
< nWidth
; ++nX
)
1165 BitmapColor aBmpColor
= pAcc
->GetPixelFromData(pScanline
, nX
);
1166 aBmpColor
.Merge(rBackgroundColor
, pAlphaAcc
->GetIndexFromData(pScanlineAlpha
, nX
));
1167 pAcc
->SetPixelOnData(pScanline
, nX
, aBmpColor
);
1174 static BitmapColor
UpdatePaletteForNewColor(BitmapScopedWriteAccess
& pAcc
,
1175 const sal_uInt16 nActColors
,
1176 const sal_uInt16 nMaxColors
, const tools::Long nHeight
,
1177 const tools::Long nWidth
,
1178 const BitmapColor
& rWantedColor
)
1180 // default to the nearest color
1181 sal_uInt16 aReplacePalIndex
= pAcc
->GetMatchingPaletteIndex(rWantedColor
);
1182 if (aReplacePalIndex
!= SAL_MAX_UINT16
)
1183 return BitmapColor(static_cast<sal_uInt8
>(aReplacePalIndex
));
1185 // for paletted images without a matching palette entry
1187 // if the palette has empty entries use the last one
1188 if (nActColors
< nMaxColors
)
1190 pAcc
->SetPaletteEntryCount(nActColors
+ 1);
1191 pAcc
->SetPaletteColor(nActColors
, rWantedColor
);
1192 return BitmapColor(static_cast<sal_uInt8
>(nActColors
));
1195 // look for an unused palette entry (NOTE: expensive!)
1196 std::unique_ptr
<bool[]> pFlags(new bool[nMaxColors
]);
1198 // Set all entries to false
1199 std::fill(pFlags
.get(), pFlags
.get() + nMaxColors
, false);
1201 for (tools::Long nY
= 0; nY
< nHeight
; nY
++)
1203 Scanline pScanline
= pAcc
->GetScanline(nY
);
1204 for (tools::Long nX
= 0; nX
< nWidth
; nX
++)
1205 pFlags
[pAcc
->GetIndexFromData(pScanline
, nX
)] = true;
1208 for (sal_uInt16 i
= 0; i
< nMaxColors
; i
++)
1210 // Hurray, we do have an unused entry
1213 pAcc
->SetPaletteColor(i
, rWantedColor
);
1214 return BitmapColor(static_cast<sal_uInt8
>(i
));
1217 assert(false && "found nothing");
1218 return BitmapColor(0);
1221 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */