Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / vcl / source / bitmap / bitmappaint.cxx
blob5c14e1476aca313b6cf752a3d34b429bf4054905
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <tools/poly.hxx>
21 #include <tools/helpers.hxx>
22 #include <vcl/bitmap.hxx>
23 #include <vcl/bitmapaccess.hxx>
24 #include <vcl/alpha.hxx>
26 #include <bitmapwriteaccess.hxx>
27 #include <salbmp.hxx>
28 #include <svdata.hxx>
29 #include <salinst.hxx>
31 #include <algorithm>
32 #include <memory>
34 bool Bitmap::Erase(const Color& rFillColor)
36 if (IsEmpty())
37 return true;
39 BitmapScopedWriteAccess pWriteAcc(*this);
40 bool bRet = false;
42 if (pWriteAcc)
44 const ScanlineFormat nFormat = pWriteAcc->GetScanlineFormat();
45 sal_uInt8 cIndex = 0;
46 bool bFast = false;
48 switch (nFormat)
50 case ScanlineFormat::N1BitMsbPal:
51 case ScanlineFormat::N1BitLsbPal:
53 cIndex = static_cast<sal_uInt8>(pWriteAcc->GetBestPaletteIndex(rFillColor));
54 cIndex = (cIndex ? 255 : 0);
55 bFast = true;
57 break;
59 case ScanlineFormat::N4BitMsnPal:
60 case ScanlineFormat::N4BitLsnPal:
62 cIndex = static_cast<sal_uInt8>(pWriteAcc->GetBestPaletteIndex(rFillColor));
63 cIndex = cIndex | (cIndex << 4);
64 bFast = true;
66 break;
68 case ScanlineFormat::N8BitPal:
70 cIndex = static_cast<sal_uInt8>(pWriteAcc->GetBestPaletteIndex(rFillColor));
71 bFast = true;
73 break;
75 case ScanlineFormat::N24BitTcBgr:
76 case ScanlineFormat::N24BitTcRgb:
78 if (rFillColor.GetRed() == rFillColor.GetGreen()
79 && rFillColor.GetRed() == rFillColor.GetBlue())
81 cIndex = rFillColor.GetRed();
82 bFast = true;
84 else
86 bFast = false;
89 break;
91 default:
92 bFast = false;
93 break;
96 if (bFast)
98 const sal_uLong nBufSize = pWriteAcc->GetScanlineSize() * pWriteAcc->Height();
99 memset(pWriteAcc->GetBuffer(), cIndex, nBufSize);
101 else
103 const tools::Rectangle aRect(Point(), Size(pWriteAcc->Width(), pWriteAcc->Height()));
104 pWriteAcc->SetFillColor(rFillColor);
105 pWriteAcc->FillRect(aRect);
108 bRet = true;
111 return bRet;
114 bool Bitmap::Invert()
116 BitmapScopedWriteAccess pAcc(*this);
117 bool bRet = false;
119 if (pAcc)
121 if (pAcc->HasPalette())
123 BitmapPalette aBmpPal(pAcc->GetPalette());
124 const sal_uInt16 nCount = aBmpPal.GetEntryCount();
126 for (sal_uInt16 i = 0; i < nCount; i++)
128 aBmpPal[i].Invert();
131 pAcc->SetPalette(aBmpPal);
133 else
135 const long nWidth = pAcc->Width();
136 const long nHeight = pAcc->Height();
138 for (long nY = 0; nY < nHeight; nY++)
140 Scanline pScanline = pAcc->GetScanline(nY);
141 for (long nX = 0; nX < nWidth; nX++)
143 BitmapColor aBmpColor = pAcc->GetPixelFromData(pScanline, nX);
144 aBmpColor.Invert();
145 pAcc->SetPixelOnData(pScanline, nX, aBmpColor);
150 mxSalBmp->InvalidateChecksum();
151 pAcc.reset();
152 bRet = true;
155 return bRet;
158 bool Bitmap::Mirror(BmpMirrorFlags nMirrorFlags)
160 bool bHorz(nMirrorFlags & BmpMirrorFlags::Horizontal);
161 bool bVert(nMirrorFlags & BmpMirrorFlags::Vertical);
162 bool bRet = false;
164 if (bHorz && !bVert)
166 BitmapScopedWriteAccess pAcc(*this);
168 if (pAcc)
170 const long nWidth = pAcc->Width();
171 const long nHeight = pAcc->Height();
172 const long nWidth1 = nWidth - 1;
173 const long nWidth_2 = nWidth >> 1;
175 for (long nY = 0; nY < nHeight; nY++)
177 Scanline pScanline = pAcc->GetScanline(nY);
178 for (long nX = 0, nOther = nWidth1; nX < nWidth_2; nX++, nOther--)
180 const BitmapColor aTemp(pAcc->GetPixelFromData(pScanline, nX));
182 pAcc->SetPixelOnData(pScanline, nX, pAcc->GetPixelFromData(pScanline, nOther));
183 pAcc->SetPixelOnData(pScanline, nOther, aTemp);
187 pAcc.reset();
188 bRet = true;
191 else if (bVert && !bHorz)
193 BitmapScopedWriteAccess pAcc(*this);
195 if (pAcc)
197 const long nScanSize = pAcc->GetScanlineSize();
198 std::unique_ptr<sal_uInt8[]> pBuffer(new sal_uInt8[nScanSize]);
199 const long nHeight = pAcc->Height();
200 const long nHeight1 = nHeight - 1;
201 const long nHeight_2 = nHeight >> 1;
203 for (long nY = 0, nOther = nHeight1; nY < nHeight_2; nY++, nOther--)
205 memcpy(pBuffer.get(), pAcc->GetScanline(nY), nScanSize);
206 memcpy(pAcc->GetScanline(nY), pAcc->GetScanline(nOther), nScanSize);
207 memcpy(pAcc->GetScanline(nOther), pBuffer.get(), nScanSize);
210 pAcc.reset();
211 bRet = true;
214 else if (bHorz && bVert)
216 BitmapScopedWriteAccess pAcc(*this);
218 if (pAcc)
220 const long nWidth = pAcc->Width();
221 const long nWidth1 = nWidth - 1;
222 const long nHeight = pAcc->Height();
223 long nHeight_2 = nHeight >> 1;
225 for (long nY = 0, nOtherY = nHeight - 1; nY < nHeight_2; nY++, nOtherY--)
227 Scanline pScanline = pAcc->GetScanline(nY);
228 Scanline pScanlineOther = pAcc->GetScanline(nOtherY);
229 for (long nX = 0, nOtherX = nWidth1; nX < nWidth; nX++, nOtherX--)
231 const BitmapColor aTemp(pAcc->GetPixelFromData(pScanline, nX));
233 pAcc->SetPixelOnData(pScanline, nX,
234 pAcc->GetPixelFromData(pScanlineOther, nOtherX));
235 pAcc->SetPixelOnData(pScanlineOther, nOtherX, aTemp);
239 // if necessary, also mirror the middle line horizontally
240 if (nHeight & 1)
242 Scanline pScanline = pAcc->GetScanline(nHeight_2);
243 for (long nX = 0, nOtherX = nWidth1, nWidth_2 = nWidth >> 1; nX < nWidth_2;
244 nX++, nOtherX--)
246 const BitmapColor aTemp(pAcc->GetPixelFromData(pScanline, nX));
247 pAcc->SetPixelOnData(pScanline, nX, pAcc->GetPixelFromData(pScanline, nOtherX));
248 pAcc->SetPixelOnData(pScanline, nOtherX, aTemp);
252 pAcc.reset();
253 bRet = true;
256 else
257 bRet = true;
259 return bRet;
262 bool Bitmap::Rotate(long nAngle10, const Color& rFillColor)
264 bool bRet = false;
266 nAngle10 %= 3600;
267 nAngle10 = (nAngle10 < 0) ? (3599L + nAngle10) : nAngle10;
269 if (!nAngle10)
270 bRet = true;
271 else if (nAngle10 == 1800)
272 bRet = Mirror(BmpMirrorFlags::Horizontal | BmpMirrorFlags::Vertical);
273 else
275 ScopedReadAccess pReadAcc(*this);
276 Bitmap aRotatedBmp;
278 if (pReadAcc)
280 const Size aSizePix(GetSizePixel());
282 if (nAngle10 == 900 || nAngle10 == 2700)
284 const Size aNewSizePix(aSizePix.Height(), aSizePix.Width());
285 Bitmap aNewBmp(aNewSizePix, GetBitCount(), &pReadAcc->GetPalette());
286 BitmapScopedWriteAccess pWriteAcc(aNewBmp);
288 if (pWriteAcc)
290 const long nWidth = aSizePix.Width();
291 const long nWidth1 = nWidth - 1;
292 const long nHeight = aSizePix.Height();
293 const long nHeight1 = nHeight - 1;
294 const long nNewWidth = aNewSizePix.Width();
295 const long nNewHeight = aNewSizePix.Height();
297 if (nAngle10 == 900)
299 for (long nY = 0, nOtherX = nWidth1; nY < nNewHeight; nY++, nOtherX--)
301 Scanline pScanline = pWriteAcc->GetScanline(nY);
302 for (long nX = 0, nOtherY = 0; nX < nNewWidth; nX++)
304 pWriteAcc->SetPixelOnData(pScanline, nX,
305 pReadAcc->GetPixel(nOtherY++, nOtherX));
309 else if (nAngle10 == 2700)
311 for (long nY = 0, nOtherX = 0; nY < nNewHeight; nY++, nOtherX++)
313 Scanline pScanline = pWriteAcc->GetScanline(nY);
314 for (long nX = 0, nOtherY = nHeight1; nX < nNewWidth; nX++)
316 pWriteAcc->SetPixelOnData(pScanline, nX,
317 pReadAcc->GetPixel(nOtherY--, nOtherX));
322 pWriteAcc.reset();
325 aRotatedBmp = aNewBmp;
327 else
329 Point aTmpPoint;
330 tools::Rectangle aTmpRectangle(aTmpPoint, aSizePix);
331 tools::Polygon aPoly(aTmpRectangle);
332 aPoly.Rotate(aTmpPoint, static_cast<sal_uInt16>(nAngle10));
334 tools::Rectangle aNewBound(aPoly.GetBoundRect());
335 const Size aNewSizePix(aNewBound.GetSize());
336 Bitmap aNewBmp(aNewSizePix, GetBitCount(), &pReadAcc->GetPalette());
337 BitmapScopedWriteAccess pWriteAcc(aNewBmp);
339 if (pWriteAcc)
341 const BitmapColor aFillColor(pWriteAcc->GetBestMatchingColor(rFillColor));
342 const double fCosAngle = cos(nAngle10 * F_PI1800);
343 const double fSinAngle = sin(nAngle10 * F_PI1800);
344 const double fXMin = aNewBound.Left();
345 const double fYMin = aNewBound.Top();
346 const long nWidth = aSizePix.Width();
347 const long nHeight = aSizePix.Height();
348 const long nNewWidth = aNewSizePix.Width();
349 const long nNewHeight = aNewSizePix.Height();
350 long nX;
351 long nY;
352 long nRotX;
353 long nRotY;
354 std::unique_ptr<long[]> pCosX(new long[nNewWidth]);
355 std::unique_ptr<long[]> pSinX(new long[nNewWidth]);
356 std::unique_ptr<long[]> pCosY(new long[nNewHeight]);
357 std::unique_ptr<long[]> pSinY(new long[nNewHeight]);
359 for (nX = 0; nX < nNewWidth; nX++)
361 const double fTmp = (fXMin + nX) * 64.;
363 pCosX[nX] = FRound(fCosAngle * fTmp);
364 pSinX[nX] = FRound(fSinAngle * fTmp);
367 for (nY = 0; nY < nNewHeight; nY++)
369 const double fTmp = (fYMin + nY) * 64.;
371 pCosY[nY] = FRound(fCosAngle * fTmp);
372 pSinY[nY] = FRound(fSinAngle * fTmp);
375 for (nY = 0; nY < nNewHeight; nY++)
377 long nSinY = pSinY[nY];
378 long nCosY = pCosY[nY];
379 Scanline pScanline = pWriteAcc->GetScanline(nY);
381 for (nX = 0; nX < nNewWidth; nX++)
383 nRotX = (pCosX[nX] - nSinY) >> 6;
384 nRotY = (pSinX[nX] + nCosY) >> 6;
386 if ((nRotX > -1) && (nRotX < nWidth) && (nRotY > -1)
387 && (nRotY < nHeight))
389 pWriteAcc->SetPixelOnData(pScanline, nX,
390 pReadAcc->GetPixel(nRotY, nRotX));
392 else
394 pWriteAcc->SetPixelOnData(pScanline, nX, aFillColor);
399 pWriteAcc.reset();
402 aRotatedBmp = aNewBmp;
405 pReadAcc.reset();
408 bRet = !!aRotatedBmp;
409 if (bRet)
410 ReassignWithSize(aRotatedBmp);
413 return bRet;
416 Bitmap Bitmap::CreateMask(const Color& rTransColor, sal_uInt8 nTol) const
418 ScopedReadAccess pReadAcc(const_cast<Bitmap&>(*this));
420 if (!nTol && pReadAcc
421 && (pReadAcc->GetScanlineFormat() == ScanlineFormat::N1BitLsbPal
422 || pReadAcc->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal)
423 && pReadAcc->GetBestMatchingColor(COL_WHITE) == pReadAcc->GetBestMatchingColor(rTransColor))
425 // if we're a 1 bit pixel already, and the transcolor matches the color that would replace it
426 // already, then just return a copy
427 return *this;
430 Bitmap aNewBmp(GetSizePixel(), 1);
431 BitmapScopedWriteAccess pWriteAcc(aNewBmp);
432 bool bRet = false;
434 if (pWriteAcc && pReadAcc)
436 const long nWidth = pReadAcc->Width();
437 const long nHeight = pReadAcc->Height();
438 const BitmapColor aBlack(pWriteAcc->GetBestMatchingColor(COL_BLACK));
439 const BitmapColor aWhite(pWriteAcc->GetBestMatchingColor(COL_WHITE));
441 if (!nTol)
443 const BitmapColor aTest(pReadAcc->GetBestMatchingColor(rTransColor));
445 if (pReadAcc->GetScanlineFormat() == ScanlineFormat::N4BitMsnPal
446 || pReadAcc->GetScanlineFormat() == ScanlineFormat::N4BitLsnPal)
448 // optimized for 4Bit-MSN/LSN source palette
449 const sal_uInt8 cTest = aTest.GetIndex();
450 const long nShiftInit
451 = ((pReadAcc->GetScanlineFormat() == ScanlineFormat::N4BitMsnPal) ? 4 : 0);
453 if (pWriteAcc->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal
454 && aWhite.GetIndex() == 1)
456 // optimized for 1Bit-MSB destination palette
457 for (long nY = 0; nY < nHeight; ++nY)
459 Scanline pSrc = pReadAcc->GetScanline(nY);
460 Scanline pDst = pWriteAcc->GetScanline(nY);
461 for (long nX = 0, nShift = nShiftInit; nX < nWidth; nX++, nShift ^= 4)
463 if (cTest == ((pSrc[nX >> 1] >> nShift) & 0x0f))
464 pDst[nX >> 3] |= 1 << (7 - (nX & 7));
465 else
466 pDst[nX >> 3] &= ~(1 << (7 - (nX & 7)));
470 else
472 for (long nY = 0; nY < nHeight; ++nY)
474 Scanline pSrc = pReadAcc->GetScanline(nY);
475 Scanline pDst = pWriteAcc->GetScanline(nY);
476 for (long nX = 0, nShift = nShiftInit; nX < nWidth; nX++, nShift ^= 4)
478 if (cTest == ((pSrc[nX >> 1] >> nShift) & 0x0f))
479 pWriteAcc->SetPixelOnData(pDst, nX, aWhite);
480 else
481 pWriteAcc->SetPixelOnData(pDst, nX, aBlack);
486 else if (pReadAcc->GetScanlineFormat() == ScanlineFormat::N8BitPal)
488 // optimized for 8Bit source palette
489 const sal_uInt8 cTest = aTest.GetIndex();
491 if (pWriteAcc->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal
492 && aWhite.GetIndex() == 1)
494 // optimized for 1Bit-MSB destination palette
495 for (long nY = 0; nY < nHeight; ++nY)
497 Scanline pSrc = pReadAcc->GetScanline(nY);
498 Scanline pDst = pWriteAcc->GetScanline(nY);
499 for (long nX = 0; nX < nWidth; ++nX)
501 if (cTest == pSrc[nX])
502 pDst[nX >> 3] |= 1 << (7 - (nX & 7));
503 else
504 pDst[nX >> 3] &= ~(1 << (7 - (nX & 7)));
508 else
510 for (long nY = 0; nY < nHeight; ++nY)
512 Scanline pSrc = pReadAcc->GetScanline(nY);
513 Scanline pDst = pWriteAcc->GetScanline(nY);
514 for (long nX = 0; nX < nWidth; ++nX)
516 if (cTest == pSrc[nX])
517 pWriteAcc->SetPixelOnData(pDst, nX, aWhite);
518 else
519 pWriteAcc->SetPixelOnData(pDst, nX, aBlack);
524 else if (pWriteAcc->GetScanlineFormat() == pReadAcc->GetScanlineFormat()
525 && aWhite.GetIndex() == 1
526 && (pReadAcc->GetScanlineFormat() == ScanlineFormat::N1BitLsbPal
527 || pReadAcc->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal))
529 for (long nY = 0; nY < nHeight; ++nY)
531 Scanline pSrc = pReadAcc->GetScanline(nY);
532 Scanline pDst = pWriteAcc->GetScanline(nY);
533 assert(pWriteAcc->GetScanlineSize() == pReadAcc->GetScanlineSize());
534 const long nScanlineSize = pWriteAcc->GetScanlineSize();
535 for (long nX = 0; nX < nScanlineSize; ++nX)
536 pDst[nX] = ~pSrc[nX];
539 else
541 // not optimized
542 for (long nY = 0; nY < nHeight; ++nY)
544 Scanline pScanline = pWriteAcc->GetScanline(nY);
545 Scanline pScanlineRead = pReadAcc->GetScanline(nY);
546 for (long nX = 0; nX < nWidth; ++nX)
548 if (aTest == pReadAcc->GetPixelFromData(pScanlineRead, nX))
549 pWriteAcc->SetPixelOnData(pScanline, nX, aWhite);
550 else
551 pWriteAcc->SetPixelOnData(pScanline, nX, aBlack);
556 else
558 BitmapColor aCol;
559 long nR, nG, nB;
560 const long nMinR = MinMax<long>(rTransColor.GetRed() - nTol, 0, 255);
561 const long nMaxR = MinMax<long>(rTransColor.GetRed() + nTol, 0, 255);
562 const long nMinG = MinMax<long>(rTransColor.GetGreen() - nTol, 0, 255);
563 const long nMaxG = MinMax<long>(rTransColor.GetGreen() + nTol, 0, 255);
564 const long nMinB = MinMax<long>(rTransColor.GetBlue() - nTol, 0, 255);
565 const long nMaxB = MinMax<long>(rTransColor.GetBlue() + nTol, 0, 255);
567 if (pReadAcc->HasPalette())
569 for (long nY = 0; nY < nHeight; nY++)
571 Scanline pScanline = pWriteAcc->GetScanline(nY);
572 Scanline pScanlineRead = pReadAcc->GetScanline(nY);
573 for (long nX = 0; nX < nWidth; nX++)
575 aCol = pReadAcc->GetPaletteColor(
576 pReadAcc->GetIndexFromData(pScanlineRead, nX));
577 nR = aCol.GetRed();
578 nG = aCol.GetGreen();
579 nB = aCol.GetBlue();
581 if (nMinR <= nR && nMaxR >= nR && nMinG <= nG && nMaxG >= nG && nMinB <= nB
582 && nMaxB >= nB)
584 pWriteAcc->SetPixelOnData(pScanline, nX, aWhite);
586 else
588 pWriteAcc->SetPixelOnData(pScanline, nX, aBlack);
593 else
595 for (long nY = 0; nY < nHeight; nY++)
597 Scanline pScanline = pWriteAcc->GetScanline(nY);
598 Scanline pScanlineRead = pReadAcc->GetScanline(nY);
599 for (long nX = 0; nX < nWidth; nX++)
601 aCol = pReadAcc->GetPixelFromData(pScanlineRead, nX);
602 nR = aCol.GetRed();
603 nG = aCol.GetGreen();
604 nB = aCol.GetBlue();
606 if (nMinR <= nR && nMaxR >= nR && nMinG <= nG && nMaxG >= nG && nMinB <= nB
607 && nMaxB >= nB)
609 pWriteAcc->SetPixelOnData(pScanline, nX, aWhite);
611 else
613 pWriteAcc->SetPixelOnData(pScanline, nX, aBlack);
620 bRet = true;
623 pWriteAcc.reset();
624 pReadAcc.reset();
626 if (bRet)
628 aNewBmp.maPrefSize = maPrefSize;
629 aNewBmp.maPrefMapMode = maPrefMapMode;
631 else
632 aNewBmp = Bitmap();
634 return aNewBmp;
637 vcl::Region Bitmap::CreateRegion(const Color& rColor, const tools::Rectangle& rRect) const
639 vcl::Region aRegion;
640 tools::Rectangle aRect(rRect);
641 ScopedReadAccess pReadAcc(const_cast<Bitmap&>(*this));
643 aRect.Intersection(tools::Rectangle(Point(), GetSizePixel()));
644 aRect.Justify();
646 if (pReadAcc)
648 const long nLeft = aRect.Left();
649 const long nTop = aRect.Top();
650 const long nRight = aRect.Right();
651 const long nBottom = aRect.Bottom();
652 const BitmapColor aMatch(pReadAcc->GetBestMatchingColor(rColor));
654 std::vector<long> aLine;
655 long nYStart(nTop);
656 long nY(nTop);
658 for (; nY <= nBottom; nY++)
660 std::vector<long> aNewLine;
661 long nX(nLeft);
662 Scanline pScanlineRead = pReadAcc->GetScanline(nY);
664 for (; nX <= nRight;)
666 while ((nX <= nRight) && (aMatch != pReadAcc->GetPixelFromData(pScanlineRead, nX)))
667 nX++;
669 if (nX <= nRight)
671 aNewLine.push_back(nX);
673 while ((nX <= nRight)
674 && (aMatch == pReadAcc->GetPixelFromData(pScanlineRead, nX)))
676 nX++;
679 aNewLine.push_back(nX - 1);
683 if (aNewLine != aLine)
685 // need to write aLine, it's different from the next line
686 if (!aLine.empty())
688 tools::Rectangle aSubRect;
690 // enter y values and proceed ystart
691 aSubRect.SetTop(nYStart);
692 aSubRect.SetBottom(nY ? nY - 1 : 0);
694 for (size_t a(0); a < aLine.size();)
696 aSubRect.SetLeft(aLine[a++]);
697 aSubRect.SetRight(aLine[a++]);
698 aRegion.Union(aSubRect);
702 // copy line as new line
703 aLine = aNewLine;
704 nYStart = nY;
708 // write last line if used
709 if (!aLine.empty())
711 tools::Rectangle aSubRect;
713 // enter y values
714 aSubRect.SetTop(nYStart);
715 aSubRect.SetBottom(nY ? nY - 1 : 0);
717 for (size_t a(0); a < aLine.size();)
719 aSubRect.SetLeft(aLine[a++]);
720 aSubRect.SetRight(aLine[a++]);
721 aRegion.Union(aSubRect);
725 pReadAcc.reset();
727 else
729 aRegion = aRect;
732 return aRegion;
735 bool Bitmap::Replace(const Bitmap& rMask, const Color& rReplaceColor)
737 ScopedReadAccess pMaskAcc(const_cast<Bitmap&>(rMask));
738 BitmapScopedWriteAccess pAcc(*this);
739 bool bRet = false;
741 if (pMaskAcc && pAcc)
743 const long nWidth = std::min(pMaskAcc->Width(), pAcc->Width());
744 const long nHeight = std::min(pMaskAcc->Height(), pAcc->Height());
745 const BitmapColor aMaskWhite(pMaskAcc->GetBestMatchingColor(COL_WHITE));
746 BitmapColor aReplace;
748 if (pAcc->HasPalette())
750 const sal_uInt16 nActColors = pAcc->GetPaletteEntryCount();
751 const sal_uInt16 nMaxColors = 1 << pAcc->GetBitCount();
753 // default to the nearest color
754 aReplace = pAcc->GetBestMatchingColor(rReplaceColor);
756 // for paletted images without a matching palette entry
757 // look for an unused palette entry (NOTE: expensive!)
758 if (pAcc->GetPaletteColor(aReplace.GetIndex()) != BitmapColor(rReplaceColor))
760 // if the palette has empty entries use the last one
761 if (nActColors < nMaxColors)
763 pAcc->SetPaletteEntryCount(nActColors + 1);
764 pAcc->SetPaletteColor(nActColors, rReplaceColor);
765 aReplace = BitmapColor(static_cast<sal_uInt8>(nActColors));
767 else
769 std::unique_ptr<bool[]> pFlags(new bool[nMaxColors]);
771 // Set all entries to false
772 std::fill(pFlags.get(), pFlags.get() + nMaxColors, false);
774 for (long nY = 0; nY < nHeight; nY++)
776 Scanline pScanline = pAcc->GetScanline(nY);
777 for (long nX = 0; nX < nWidth; nX++)
778 pFlags[pAcc->GetIndexFromData(pScanline, nX)] = true;
781 for (sal_uInt16 i = 0; i < nMaxColors; i++)
783 // Hurray, we do have an unused entry
784 if (!pFlags[i])
786 pAcc->SetPaletteColor(i, rReplaceColor);
787 aReplace = BitmapColor(static_cast<sal_uInt8>(i));
793 else
794 aReplace = rReplaceColor;
796 for (long nY = 0; nY < nHeight; nY++)
798 Scanline pScanline = pAcc->GetScanline(nY);
799 Scanline pScanlineMask = pMaskAcc->GetScanline(nY);
800 for (long nX = 0; nX < nWidth; nX++)
802 if (pMaskAcc->GetPixelFromData(pScanlineMask, nX) == aMaskWhite)
803 pAcc->SetPixelOnData(pScanline, nX, aReplace);
807 bRet = true;
810 return bRet;
813 bool Bitmap::Replace(const AlphaMask& rAlpha, const Color& rMergeColor)
815 Bitmap aNewBmp(GetSizePixel(), 24);
816 ScopedReadAccess pAcc(*this);
817 AlphaMask::ScopedReadAccess pAlphaAcc(const_cast<AlphaMask&>(rAlpha));
818 BitmapScopedWriteAccess pNewAcc(aNewBmp);
819 bool bRet = false;
821 if (pAcc && pAlphaAcc && pNewAcc)
823 BitmapColor aCol;
824 const long nWidth = std::min(pAlphaAcc->Width(), pAcc->Width());
825 const long nHeight = std::min(pAlphaAcc->Height(), pAcc->Height());
827 for (long nY = 0; nY < nHeight; nY++)
829 Scanline pScanline = pNewAcc->GetScanline(nY);
830 Scanline pScanlineAlpha = pAlphaAcc->GetScanline(nY);
831 for (long nX = 0; nX < nWidth; nX++)
833 aCol = pAcc->GetColor(nY, nX);
834 aCol.Merge(rMergeColor, 255 - pAlphaAcc->GetIndexFromData(pScanlineAlpha, nX));
835 pNewAcc->SetPixelOnData(pScanline, nX, aCol);
839 bRet = true;
842 pAcc.reset();
843 pAlphaAcc.reset();
844 pNewAcc.reset();
846 if (bRet)
848 const MapMode aMap(maPrefMapMode);
849 const Size aSize(maPrefSize);
851 *this = aNewBmp;
853 maPrefMapMode = aMap;
854 maPrefSize = aSize;
857 return bRet;
860 bool Bitmap::Replace(const Color& rSearchColor, const Color& rReplaceColor, sal_uInt8 nTol)
862 if (mxSalBmp)
864 // implementation specific replace
865 std::shared_ptr<SalBitmap> xImpBmp(ImplGetSVData()->mpDefInst->CreateSalBitmap());
866 if (xImpBmp->Create(*mxSalBmp) && xImpBmp->Replace(rSearchColor, rReplaceColor, nTol))
868 ImplSetSalBitmap(xImpBmp);
869 maPrefMapMode = MapMode(MapUnit::MapPixel);
870 maPrefSize = xImpBmp->GetSize();
871 return true;
875 // Bitmaps with 1 bit color depth can cause problems if they have other entries than black/white
876 // in their palette
877 if (GetBitCount() == 1)
878 Convert(BmpConversion::N4BitColors);
880 BitmapScopedWriteAccess pAcc(*this);
881 bool bRet = false;
883 if (pAcc)
885 const long nMinR = MinMax<long>(rSearchColor.GetRed() - nTol, 0, 255);
886 const long nMaxR = MinMax<long>(rSearchColor.GetRed() + nTol, 0, 255);
887 const long nMinG = MinMax<long>(rSearchColor.GetGreen() - nTol, 0, 255);
888 const long nMaxG = MinMax<long>(rSearchColor.GetGreen() + nTol, 0, 255);
889 const long nMinB = MinMax<long>(rSearchColor.GetBlue() - nTol, 0, 255);
890 const long nMaxB = MinMax<long>(rSearchColor.GetBlue() + nTol, 0, 255);
892 if (pAcc->HasPalette())
894 for (sal_uInt16 i = 0, nPalCount = pAcc->GetPaletteEntryCount(); i < nPalCount; i++)
896 const BitmapColor& rCol = pAcc->GetPaletteColor(i);
898 if (nMinR <= rCol.GetRed() && nMaxR >= rCol.GetRed() && nMinG <= rCol.GetGreen()
899 && nMaxG >= rCol.GetGreen() && nMinB <= rCol.GetBlue()
900 && nMaxB >= rCol.GetBlue())
902 pAcc->SetPaletteColor(i, rReplaceColor);
906 else
908 BitmapColor aCol;
909 const BitmapColor aReplace(pAcc->GetBestMatchingColor(rReplaceColor));
911 for (long nY = 0, nHeight = pAcc->Height(); nY < nHeight; nY++)
913 Scanline pScanline = pAcc->GetScanline(nY);
914 for (long nX = 0, nWidth = pAcc->Width(); nX < nWidth; nX++)
916 aCol = pAcc->GetPixelFromData(pScanline, nX);
918 if (nMinR <= aCol.GetRed() && nMaxR >= aCol.GetRed() && nMinG <= aCol.GetGreen()
919 && nMaxG >= aCol.GetGreen() && nMinB <= aCol.GetBlue()
920 && nMaxB >= aCol.GetBlue())
922 pAcc->SetPixelOnData(pScanline, nX, aReplace);
928 pAcc.reset();
929 bRet = true;
932 return bRet;
935 bool Bitmap::Replace(const Color* pSearchColors, const Color* pReplaceColors, sal_uLong nColorCount,
936 sal_uInt8 const* pTols)
938 // Bitmaps with 1 bit color depth can cause problems if they have other entries than black/white
939 // in their palette
940 if (GetBitCount() == 1)
941 Convert(BmpConversion::N4BitColors);
943 BitmapScopedWriteAccess pAcc(*this);
944 bool bRet = false;
946 if (pAcc)
948 std::unique_ptr<long[]> pMinR(new long[nColorCount]);
949 std::unique_ptr<long[]> pMaxR(new long[nColorCount]);
950 std::unique_ptr<long[]> pMinG(new long[nColorCount]);
951 std::unique_ptr<long[]> pMaxG(new long[nColorCount]);
952 std::unique_ptr<long[]> pMinB(new long[nColorCount]);
953 std::unique_ptr<long[]> pMaxB(new long[nColorCount]);
955 if (pTols)
957 for (sal_uLong i = 0; i < nColorCount; i++)
959 const Color& rCol = pSearchColors[i];
960 const sal_uInt8 nTol = pTols[i];
962 pMinR[i] = MinMax<long>(rCol.GetRed() - nTol, 0, 255);
963 pMaxR[i] = MinMax<long>(rCol.GetRed() + nTol, 0, 255);
964 pMinG[i] = MinMax<long>(rCol.GetGreen() - nTol, 0, 255);
965 pMaxG[i] = MinMax<long>(rCol.GetGreen() + nTol, 0, 255);
966 pMinB[i] = MinMax<long>(rCol.GetBlue() - nTol, 0, 255);
967 pMaxB[i] = MinMax<long>(rCol.GetBlue() + nTol, 0, 255);
970 else
972 for (sal_uLong i = 0; i < nColorCount; i++)
974 const Color& rCol = pSearchColors[i];
976 pMinR[i] = rCol.GetRed();
977 pMaxR[i] = rCol.GetRed();
978 pMinG[i] = rCol.GetGreen();
979 pMaxG[i] = rCol.GetGreen();
980 pMinB[i] = rCol.GetBlue();
981 pMaxB[i] = rCol.GetBlue();
985 if (pAcc->HasPalette())
987 for (sal_uInt16 nEntry = 0, nPalCount = pAcc->GetPaletteEntryCount();
988 nEntry < nPalCount; nEntry++)
990 const BitmapColor& rCol = pAcc->GetPaletteColor(nEntry);
992 for (sal_uLong i = 0; i < nColorCount; i++)
994 if (pMinR[i] <= rCol.GetRed() && pMaxR[i] >= rCol.GetRed()
995 && pMinG[i] <= rCol.GetGreen() && pMaxG[i] >= rCol.GetGreen()
996 && pMinB[i] <= rCol.GetBlue() && pMaxB[i] >= rCol.GetBlue())
998 pAcc->SetPaletteColor(nEntry, pReplaceColors[i]);
999 break;
1004 else
1006 BitmapColor aCol;
1007 std::unique_ptr<BitmapColor[]> pReplaces(new BitmapColor[nColorCount]);
1009 for (sal_uLong i = 0; i < nColorCount; i++)
1010 pReplaces[i] = pAcc->GetBestMatchingColor(pReplaceColors[i]);
1012 for (long nY = 0, nHeight = pAcc->Height(); nY < nHeight; nY++)
1014 Scanline pScanline = pAcc->GetScanline(nY);
1015 for (long nX = 0, nWidth = pAcc->Width(); nX < nWidth; nX++)
1017 aCol = pAcc->GetPixelFromData(pScanline, nX);
1019 for (sal_uLong i = 0; i < nColorCount; i++)
1021 if (pMinR[i] <= aCol.GetRed() && pMaxR[i] >= aCol.GetRed()
1022 && pMinG[i] <= aCol.GetGreen() && pMaxG[i] >= aCol.GetGreen()
1023 && pMinB[i] <= aCol.GetBlue() && pMaxB[i] >= aCol.GetBlue())
1025 pAcc->SetPixelOnData(pScanline, nX, pReplaces[i]);
1026 break;
1033 pAcc.reset();
1034 bRet = true;
1037 return bRet;
1040 bool Bitmap::CombineSimple(const Bitmap& rMask, BmpCombine eCombine)
1042 ScopedReadAccess pMaskAcc(const_cast<Bitmap&>(rMask));
1043 BitmapScopedWriteAccess pAcc(*this);
1044 bool bRet = false;
1046 if (pMaskAcc && pAcc)
1048 const long nWidth = std::min(pMaskAcc->Width(), pAcc->Width());
1049 const long nHeight = std::min(pMaskAcc->Height(), pAcc->Height());
1050 const Color aColBlack(COL_BLACK);
1051 const BitmapColor aWhite(pAcc->GetBestMatchingColor(COL_WHITE));
1052 const BitmapColor aBlack(pAcc->GetBestMatchingColor(aColBlack));
1053 const BitmapColor aMaskBlack(pMaskAcc->GetBestMatchingColor(aColBlack));
1055 switch (eCombine)
1057 case BmpCombine::And:
1059 for (long nY = 0; nY < nHeight; nY++)
1061 Scanline pScanline = pAcc->GetScanline(nY);
1062 Scanline pScanlineMask = pMaskAcc->GetScanline(nY);
1063 for (long nX = 0; nX < nWidth; nX++)
1065 if (pMaskAcc->GetPixelFromData(pScanlineMask, nX) != aMaskBlack
1066 && pAcc->GetPixelFromData(pScanline, nX) != aBlack)
1068 pAcc->SetPixelOnData(pScanline, nX, aWhite);
1070 else
1072 pAcc->SetPixelOnData(pScanline, nX, aBlack);
1077 break;
1079 case BmpCombine::Or:
1081 for (long nY = 0; nY < nHeight; nY++)
1083 Scanline pScanline = pAcc->GetScanline(nY);
1084 Scanline pScanlineMask = pMaskAcc->GetScanline(nY);
1085 for (long nX = 0; nX < nWidth; nX++)
1087 if (pMaskAcc->GetPixelFromData(pScanlineMask, nX) != aMaskBlack
1088 || pAcc->GetPixelFromData(pScanline, nX) != aBlack)
1090 pAcc->SetPixelOnData(pScanline, nX, aWhite);
1092 else
1094 pAcc->SetPixelOnData(pScanline, nX, aBlack);
1099 break;
1102 bRet = true;
1105 return bRet;
1108 // TODO: Have a look at OutputDevice::ImplDrawAlpha() for some
1109 // optimizations. Might even consolidate the code here and there.
1110 bool Bitmap::Blend(const AlphaMask& rAlpha, const Color& rBackgroundColor)
1112 // Convert to a truecolor bitmap, if we're a paletted one. There's room for tradeoff decision here,
1113 // maybe later for an overload (or a flag)
1114 if (GetBitCount() <= 8)
1115 Convert(BmpConversion::N24Bit);
1117 AlphaMask::ScopedReadAccess pAlphaAcc(const_cast<AlphaMask&>(rAlpha));
1119 BitmapScopedWriteAccess pAcc(*this);
1120 bool bRet = false;
1122 if (pAlphaAcc && pAcc)
1124 const long nWidth = std::min(pAlphaAcc->Width(), pAcc->Width());
1125 const long nHeight = std::min(pAlphaAcc->Height(), pAcc->Height());
1127 for (long nY = 0; nY < nHeight; ++nY)
1129 Scanline pScanline = pAcc->GetScanline(nY);
1130 Scanline pScanlineAlpha = pAlphaAcc->GetScanline(nY);
1131 for (long nX = 0; nX < nWidth; ++nX)
1133 BitmapColor aBmpColor = pAcc->GetPixelFromData(pScanline, nX);
1134 aBmpColor.Merge(rBackgroundColor,
1135 255 - pAlphaAcc->GetIndexFromData(pScanlineAlpha, nX));
1136 pAcc->SetPixelOnData(pScanline, nX, aBmpColor);
1140 bRet = true;
1143 return bRet;
1146 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */