LibreOffice Module vcl (master)  1
bitmappaint.cxx
Go to the documentation of this file.
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 <vcl/bitmap.hxx>
23 #include <vcl/bitmapaccess.hxx>
24 #include <vcl/alpha.hxx>
25 
26 #include <bitmapwriteaccess.hxx>
27 #include <salbmp.hxx>
28 #include <svdata.hxx>
29 #include <salinst.hxx>
30 
31 #include <algorithm>
32 #include <memory>
33 
34 bool Bitmap::Erase(const Color& rFillColor)
35 {
36  if (IsEmpty())
37  return true;
38 
39  if (mxSalBmp)
40  {
41  // implementation specific replace
42  std::shared_ptr<SalBitmap> xImpBmp(ImplGetSVData()->mpDefInst->CreateSalBitmap());
43  if (xImpBmp->Create(*mxSalBmp) && xImpBmp->Erase(rFillColor))
44  {
45  ImplSetSalBitmap(xImpBmp);
46  maPrefMapMode = MapMode(MapUnit::MapPixel);
47  maPrefSize = xImpBmp->GetSize();
48  return true;
49  }
50  }
51 
52  BitmapScopedWriteAccess pWriteAcc(*this);
53  bool bRet = false;
54 
55  if (pWriteAcc)
56  {
57  pWriteAcc->Erase(rFillColor);
58  bRet = true;
59  }
60 
61  return bRet;
62 }
63 
65 {
66  BitmapScopedWriteAccess pAcc(*this);
67  bool bRet = false;
68 
69  if (pAcc)
70  {
71  if (pAcc->HasPalette())
72  {
73  BitmapPalette aBmpPal(pAcc->GetPalette());
74  const sal_uInt16 nCount = aBmpPal.GetEntryCount();
75 
76  for (sal_uInt16 i = 0; i < nCount; i++)
77  {
78  aBmpPal[i].Invert();
79  }
80 
81  pAcc->SetPalette(aBmpPal);
82  }
83  else
84  {
85  const long nWidth = pAcc->Width();
86  const long nHeight = pAcc->Height();
87 
88  for (long nY = 0; nY < nHeight; nY++)
89  {
90  Scanline pScanline = pAcc->GetScanline(nY);
91  for (long nX = 0; nX < nWidth; nX++)
92  {
93  BitmapColor aBmpColor = pAcc->GetPixelFromData(pScanline, nX);
94  aBmpColor.Invert();
95  pAcc->SetPixelOnData(pScanline, nX, aBmpColor);
96  }
97  }
98  }
99 
100  mxSalBmp->InvalidateChecksum();
101  pAcc.reset();
102  bRet = true;
103  }
104 
105  return bRet;
106 }
107 
108 bool Bitmap::Mirror(BmpMirrorFlags nMirrorFlags)
109 {
110  bool bHorz(nMirrorFlags & BmpMirrorFlags::Horizontal);
111  bool bVert(nMirrorFlags & BmpMirrorFlags::Vertical);
112  bool bRet = false;
113 
114  if (bHorz && !bVert)
115  {
116  BitmapScopedWriteAccess pAcc(*this);
117 
118  if (pAcc)
119  {
120  const long nWidth = pAcc->Width();
121  const long nHeight = pAcc->Height();
122  const long nWidth1 = nWidth - 1;
123  const long nWidth_2 = nWidth >> 1;
124 
125  for (long nY = 0; nY < nHeight; nY++)
126  {
127  Scanline pScanline = pAcc->GetScanline(nY);
128  for (long nX = 0, nOther = nWidth1; nX < nWidth_2; nX++, nOther--)
129  {
130  const BitmapColor aTemp(pAcc->GetPixelFromData(pScanline, nX));
131 
132  pAcc->SetPixelOnData(pScanline, nX, pAcc->GetPixelFromData(pScanline, nOther));
133  pAcc->SetPixelOnData(pScanline, nOther, aTemp);
134  }
135  }
136 
137  pAcc.reset();
138  bRet = true;
139  }
140  }
141  else if (bVert && !bHorz)
142  {
143  BitmapScopedWriteAccess pAcc(*this);
144 
145  if (pAcc)
146  {
147  const long nScanSize = pAcc->GetScanlineSize();
148  std::unique_ptr<sal_uInt8[]> pBuffer(new sal_uInt8[nScanSize]);
149  const long nHeight = pAcc->Height();
150  const long nHeight1 = nHeight - 1;
151  const long nHeight_2 = nHeight >> 1;
152 
153  for (long nY = 0, nOther = nHeight1; nY < nHeight_2; nY++, nOther--)
154  {
155  memcpy(pBuffer.get(), pAcc->GetScanline(nY), nScanSize);
156  memcpy(pAcc->GetScanline(nY), pAcc->GetScanline(nOther), nScanSize);
157  memcpy(pAcc->GetScanline(nOther), pBuffer.get(), nScanSize);
158  }
159 
160  pAcc.reset();
161  bRet = true;
162  }
163  }
164  else if (bHorz && bVert)
165  {
166  BitmapScopedWriteAccess pAcc(*this);
167 
168  if (pAcc)
169  {
170  const long nWidth = pAcc->Width();
171  const long nWidth1 = nWidth - 1;
172  const long nHeight = pAcc->Height();
173  long nHeight_2 = nHeight >> 1;
174 
175  for (long nY = 0, nOtherY = nHeight - 1; nY < nHeight_2; nY++, nOtherY--)
176  {
177  Scanline pScanline = pAcc->GetScanline(nY);
178  Scanline pScanlineOther = pAcc->GetScanline(nOtherY);
179  for (long nX = 0, nOtherX = nWidth1; nX < nWidth; nX++, nOtherX--)
180  {
181  const BitmapColor aTemp(pAcc->GetPixelFromData(pScanline, nX));
182 
183  pAcc->SetPixelOnData(pScanline, nX,
184  pAcc->GetPixelFromData(pScanlineOther, nOtherX));
185  pAcc->SetPixelOnData(pScanlineOther, nOtherX, aTemp);
186  }
187  }
188 
189  // if necessary, also mirror the middle line horizontally
190  if (nHeight & 1)
191  {
192  Scanline pScanline = pAcc->GetScanline(nHeight_2);
193  for (long nX = 0, nOtherX = nWidth1, nWidth_2 = nWidth >> 1; nX < nWidth_2;
194  nX++, nOtherX--)
195  {
196  const BitmapColor aTemp(pAcc->GetPixelFromData(pScanline, nX));
197  pAcc->SetPixelOnData(pScanline, nX, pAcc->GetPixelFromData(pScanline, nOtherX));
198  pAcc->SetPixelOnData(pScanline, nOtherX, aTemp);
199  }
200  }
201 
202  pAcc.reset();
203  bRet = true;
204  }
205  }
206  else
207  bRet = true;
208 
209  return bRet;
210 }
211 
212 bool Bitmap::Rotate(long nAngle10, const Color& rFillColor)
213 {
214  bool bRet = false;
215 
216  nAngle10 %= 3600;
217  nAngle10 = (nAngle10 < 0) ? (3599L + nAngle10) : nAngle10;
218 
219  if (!nAngle10)
220  bRet = true;
221  else if (nAngle10 == 1800)
223  else
224  {
225  ScopedReadAccess pReadAcc(*this);
226  Bitmap aRotatedBmp;
227 
228  if (pReadAcc)
229  {
230  const Size aSizePix(GetSizePixel());
231 
232  if (nAngle10 == 900 || nAngle10 == 2700)
233  {
234  const Size aNewSizePix(aSizePix.Height(), aSizePix.Width());
235  Bitmap aNewBmp(aNewSizePix, GetBitCount(), &pReadAcc->GetPalette());
236  BitmapScopedWriteAccess pWriteAcc(aNewBmp);
237 
238  if (pWriteAcc)
239  {
240  const long nWidth = aSizePix.Width();
241  const long nWidth1 = nWidth - 1;
242  const long nHeight = aSizePix.Height();
243  const long nHeight1 = nHeight - 1;
244  const long nNewWidth = aNewSizePix.Width();
245  const long nNewHeight = aNewSizePix.Height();
246 
247  if (nAngle10 == 900)
248  {
249  for (long nY = 0, nOtherX = nWidth1; nY < nNewHeight; nY++, nOtherX--)
250  {
251  Scanline pScanline = pWriteAcc->GetScanline(nY);
252  for (long nX = 0, nOtherY = 0; nX < nNewWidth; nX++)
253  {
254  pWriteAcc->SetPixelOnData(pScanline, nX,
255  pReadAcc->GetPixel(nOtherY++, nOtherX));
256  }
257  }
258  }
259  else if (nAngle10 == 2700)
260  {
261  for (long nY = 0, nOtherX = 0; nY < nNewHeight; nY++, nOtherX++)
262  {
263  Scanline pScanline = pWriteAcc->GetScanline(nY);
264  for (long nX = 0, nOtherY = nHeight1; nX < nNewWidth; nX++)
265  {
266  pWriteAcc->SetPixelOnData(pScanline, nX,
267  pReadAcc->GetPixel(nOtherY--, nOtherX));
268  }
269  }
270  }
271 
272  pWriteAcc.reset();
273  }
274 
275  aRotatedBmp = aNewBmp;
276  }
277  else
278  {
279  Point aTmpPoint;
280  tools::Rectangle aTmpRectangle(aTmpPoint, aSizePix);
281  tools::Polygon aPoly(aTmpRectangle);
282  aPoly.Rotate(aTmpPoint, static_cast<sal_uInt16>(nAngle10));
283 
284  tools::Rectangle aNewBound(aPoly.GetBoundRect());
285  const Size aNewSizePix(aNewBound.GetSize());
286  Bitmap aNewBmp(aNewSizePix, GetBitCount(), &pReadAcc->GetPalette());
287  BitmapScopedWriteAccess pWriteAcc(aNewBmp);
288 
289  if (pWriteAcc)
290  {
291  const BitmapColor aFillColor(pWriteAcc->GetBestMatchingColor(rFillColor));
292  const double fCosAngle = cos(nAngle10 * F_PI1800);
293  const double fSinAngle = sin(nAngle10 * F_PI1800);
294  const double fXMin = aNewBound.Left();
295  const double fYMin = aNewBound.Top();
296  const long nWidth = aSizePix.Width();
297  const long nHeight = aSizePix.Height();
298  const long nNewWidth = aNewSizePix.Width();
299  const long nNewHeight = aNewSizePix.Height();
300  long nX;
301  long nY;
302  long nRotX;
303  long nRotY;
304  std::unique_ptr<long[]> pCosX(new long[nNewWidth]);
305  std::unique_ptr<long[]> pSinX(new long[nNewWidth]);
306  std::unique_ptr<long[]> pCosY(new long[nNewHeight]);
307  std::unique_ptr<long[]> pSinY(new long[nNewHeight]);
308 
309  for (nX = 0; nX < nNewWidth; nX++)
310  {
311  const double fTmp = (fXMin + nX) * 64.;
312 
313  pCosX[nX] = FRound(fCosAngle * fTmp);
314  pSinX[nX] = FRound(fSinAngle * fTmp);
315  }
316 
317  for (nY = 0; nY < nNewHeight; nY++)
318  {
319  const double fTmp = (fYMin + nY) * 64.;
320 
321  pCosY[nY] = FRound(fCosAngle * fTmp);
322  pSinY[nY] = FRound(fSinAngle * fTmp);
323  }
324 
325  for (nY = 0; nY < nNewHeight; nY++)
326  {
327  long nSinY = pSinY[nY];
328  long nCosY = pCosY[nY];
329  Scanline pScanline = pWriteAcc->GetScanline(nY);
330 
331  for (nX = 0; nX < nNewWidth; nX++)
332  {
333  nRotX = (pCosX[nX] - nSinY) >> 6;
334  nRotY = (pSinX[nX] + nCosY) >> 6;
335 
336  if ((nRotX > -1) && (nRotX < nWidth) && (nRotY > -1)
337  && (nRotY < nHeight))
338  {
339  pWriteAcc->SetPixelOnData(pScanline, nX,
340  pReadAcc->GetPixel(nRotY, nRotX));
341  }
342  else
343  {
344  pWriteAcc->SetPixelOnData(pScanline, nX, aFillColor);
345  }
346  }
347  }
348 
349  pWriteAcc.reset();
350  }
351 
352  aRotatedBmp = aNewBmp;
353  }
354 
355  pReadAcc.reset();
356  }
357 
358  bRet = !!aRotatedBmp;
359  if (bRet)
360  ReassignWithSize(aRotatedBmp);
361  }
362 
363  return bRet;
364 };
365 
366 Bitmap Bitmap::CreateMask(const Color& rTransColor, sal_uInt8 nTol) const
367 {
368  ScopedReadAccess pReadAcc(const_cast<Bitmap&>(*this));
369 
370  if (!nTol && pReadAcc
373  && pReadAcc->GetBestMatchingColor(COL_WHITE) == pReadAcc->GetBestMatchingColor(rTransColor))
374  {
375  // if we're a 1 bit pixel already, and the transcolor matches the color that would replace it
376  // already, then just return a copy
377  return *this;
378  }
379 
380  Bitmap aNewBmp(GetSizePixel(), 1);
381  BitmapScopedWriteAccess pWriteAcc(aNewBmp);
382  bool bRet = false;
383 
384  if (pWriteAcc && pReadAcc)
385  {
386  const long nWidth = pReadAcc->Width();
387  const long nHeight = pReadAcc->Height();
388  const BitmapColor aBlack(pWriteAcc->GetBestMatchingColor(COL_BLACK));
389  const BitmapColor aWhite(pWriteAcc->GetBestMatchingColor(COL_WHITE));
390 
391  if (!nTol)
392  {
393  const BitmapColor aTest(pReadAcc->GetBestMatchingColor(rTransColor));
394 
397  {
398  // optimized for 4Bit-MSN/LSN source palette
399  const sal_uInt8 cTest = aTest.GetIndex();
400  const long nShiftInit
401  = ((pReadAcc->GetScanlineFormat() == ScanlineFormat::N4BitMsnPal) ? 4 : 0);
402 
404  && aWhite.GetIndex() == 1)
405  {
406  // optimized for 1Bit-MSB destination palette
407  for (long nY = 0; nY < nHeight; ++nY)
408  {
409  Scanline pSrc = pReadAcc->GetScanline(nY);
410  Scanline pDst = pWriteAcc->GetScanline(nY);
411  for (long nX = 0, nShift = nShiftInit; nX < nWidth; nX++, nShift ^= 4)
412  {
413  if (cTest == ((pSrc[nX >> 1] >> nShift) & 0x0f))
414  pDst[nX >> 3] |= 1 << (7 - (nX & 7));
415  else
416  pDst[nX >> 3] &= ~(1 << (7 - (nX & 7)));
417  }
418  }
419  }
420  else
421  {
422  for (long nY = 0; nY < nHeight; ++nY)
423  {
424  Scanline pSrc = pReadAcc->GetScanline(nY);
425  Scanline pDst = pWriteAcc->GetScanline(nY);
426  for (long nX = 0, nShift = nShiftInit; nX < nWidth; nX++, nShift ^= 4)
427  {
428  if (cTest == ((pSrc[nX >> 1] >> nShift) & 0x0f))
429  pWriteAcc->SetPixelOnData(pDst, nX, aWhite);
430  else
431  pWriteAcc->SetPixelOnData(pDst, nX, aBlack);
432  }
433  }
434  }
435  }
436  else if (pReadAcc->GetScanlineFormat() == ScanlineFormat::N8BitPal)
437  {
438  // optimized for 8Bit source palette
439  const sal_uInt8 cTest = aTest.GetIndex();
440 
442  && aWhite.GetIndex() == 1)
443  {
444  // optimized for 1Bit-MSB destination palette
445  for (long nY = 0; nY < nHeight; ++nY)
446  {
447  Scanline pSrc = pReadAcc->GetScanline(nY);
448  Scanline pDst = pWriteAcc->GetScanline(nY);
449  for (long nX = 0; nX < nWidth; ++nX)
450  {
451  if (cTest == pSrc[nX])
452  pDst[nX >> 3] |= 1 << (7 - (nX & 7));
453  else
454  pDst[nX >> 3] &= ~(1 << (7 - (nX & 7)));
455  }
456  }
457  }
458  else
459  {
460  for (long nY = 0; nY < nHeight; ++nY)
461  {
462  Scanline pSrc = pReadAcc->GetScanline(nY);
463  Scanline pDst = pWriteAcc->GetScanline(nY);
464  for (long nX = 0; nX < nWidth; ++nX)
465  {
466  if (cTest == pSrc[nX])
467  pWriteAcc->SetPixelOnData(pDst, nX, aWhite);
468  else
469  pWriteAcc->SetPixelOnData(pDst, nX, aBlack);
470  }
471  }
472  }
473  }
474  else if (pWriteAcc->GetScanlineFormat() == pReadAcc->GetScanlineFormat()
475  && aWhite.GetIndex() == 1
478  {
479  for (long nY = 0; nY < nHeight; ++nY)
480  {
481  Scanline pSrc = pReadAcc->GetScanline(nY);
482  Scanline pDst = pWriteAcc->GetScanline(nY);
483  assert(pWriteAcc->GetScanlineSize() == pReadAcc->GetScanlineSize());
484  const long nScanlineSize = pWriteAcc->GetScanlineSize();
485  for (long nX = 0; nX < nScanlineSize; ++nX)
486  pDst[nX] = ~pSrc[nX];
487  }
488  }
489  else
490  {
491  // not optimized
492  for (long nY = 0; nY < nHeight; ++nY)
493  {
494  Scanline pScanline = pWriteAcc->GetScanline(nY);
495  Scanline pScanlineRead = pReadAcc->GetScanline(nY);
496  for (long nX = 0; nX < nWidth; ++nX)
497  {
498  if (aTest == pReadAcc->GetPixelFromData(pScanlineRead, nX))
499  pWriteAcc->SetPixelOnData(pScanline, nX, aWhite);
500  else
501  pWriteAcc->SetPixelOnData(pScanline, nX, aBlack);
502  }
503  }
504  }
505  }
506  else
507  {
508  BitmapColor aCol;
509  long nR, nG, nB;
510  const long nMinR = MinMax<long>(rTransColor.GetRed() - nTol, 0, 255);
511  const long nMaxR = MinMax<long>(rTransColor.GetRed() + nTol, 0, 255);
512  const long nMinG = MinMax<long>(rTransColor.GetGreen() - nTol, 0, 255);
513  const long nMaxG = MinMax<long>(rTransColor.GetGreen() + nTol, 0, 255);
514  const long nMinB = MinMax<long>(rTransColor.GetBlue() - nTol, 0, 255);
515  const long nMaxB = MinMax<long>(rTransColor.GetBlue() + nTol, 0, 255);
516 
517  if (pReadAcc->HasPalette())
518  {
519  for (long nY = 0; nY < nHeight; nY++)
520  {
521  Scanline pScanline = pWriteAcc->GetScanline(nY);
522  Scanline pScanlineRead = pReadAcc->GetScanline(nY);
523  for (long nX = 0; nX < nWidth; nX++)
524  {
525  aCol = pReadAcc->GetPaletteColor(
526  pReadAcc->GetIndexFromData(pScanlineRead, nX));
527  nR = aCol.GetRed();
528  nG = aCol.GetGreen();
529  nB = aCol.GetBlue();
530 
531  if (nMinR <= nR && nMaxR >= nR && nMinG <= nG && nMaxG >= nG && nMinB <= nB
532  && nMaxB >= nB)
533  {
534  pWriteAcc->SetPixelOnData(pScanline, nX, aWhite);
535  }
536  else
537  {
538  pWriteAcc->SetPixelOnData(pScanline, nX, aBlack);
539  }
540  }
541  }
542  }
543  else
544  {
545  for (long nY = 0; nY < nHeight; nY++)
546  {
547  Scanline pScanline = pWriteAcc->GetScanline(nY);
548  Scanline pScanlineRead = pReadAcc->GetScanline(nY);
549  for (long nX = 0; nX < nWidth; nX++)
550  {
551  aCol = pReadAcc->GetPixelFromData(pScanlineRead, nX);
552  nR = aCol.GetRed();
553  nG = aCol.GetGreen();
554  nB = aCol.GetBlue();
555 
556  if (nMinR <= nR && nMaxR >= nR && nMinG <= nG && nMaxG >= nG && nMinB <= nB
557  && nMaxB >= nB)
558  {
559  pWriteAcc->SetPixelOnData(pScanline, nX, aWhite);
560  }
561  else
562  {
563  pWriteAcc->SetPixelOnData(pScanline, nX, aBlack);
564  }
565  }
566  }
567  }
568  }
569 
570  bRet = true;
571  }
572 
573  pWriteAcc.reset();
574  pReadAcc.reset();
575 
576  if (bRet)
577  {
578  aNewBmp.maPrefSize = maPrefSize;
579  aNewBmp.maPrefMapMode = maPrefMapMode;
580  }
581  else
582  aNewBmp = Bitmap();
583 
584  return aNewBmp;
585 }
586 
587 vcl::Region Bitmap::CreateRegion(const Color& rColor, const tools::Rectangle& rRect) const
588 {
589  vcl::Region aRegion;
590  tools::Rectangle aRect(rRect);
591  ScopedReadAccess pReadAcc(const_cast<Bitmap&>(*this));
592 
594  aRect.Justify();
595 
596  if (pReadAcc)
597  {
598  const long nLeft = aRect.Left();
599  const long nTop = aRect.Top();
600  const long nRight = aRect.Right();
601  const long nBottom = aRect.Bottom();
602  const BitmapColor aMatch(pReadAcc->GetBestMatchingColor(rColor));
603 
604  std::vector<long> aLine;
605  long nYStart(nTop);
606  long nY(nTop);
607 
608  for (; nY <= nBottom; nY++)
609  {
610  std::vector<long> aNewLine;
611  long nX(nLeft);
612  Scanline pScanlineRead = pReadAcc->GetScanline(nY);
613 
614  for (; nX <= nRight;)
615  {
616  while ((nX <= nRight) && (aMatch != pReadAcc->GetPixelFromData(pScanlineRead, nX)))
617  nX++;
618 
619  if (nX <= nRight)
620  {
621  aNewLine.push_back(nX);
622 
623  while ((nX <= nRight)
624  && (aMatch == pReadAcc->GetPixelFromData(pScanlineRead, nX)))
625  {
626  nX++;
627  }
628 
629  aNewLine.push_back(nX - 1);
630  }
631  }
632 
633  if (aNewLine != aLine)
634  {
635  // need to write aLine, it's different from the next line
636  if (!aLine.empty())
637  {
638  tools::Rectangle aSubRect;
639 
640  // enter y values and proceed ystart
641  aSubRect.SetTop(nYStart);
642  aSubRect.SetBottom(nY ? nY - 1 : 0);
643 
644  for (size_t a(0); a < aLine.size();)
645  {
646  aSubRect.SetLeft(aLine[a++]);
647  aSubRect.SetRight(aLine[a++]);
648  aRegion.Union(aSubRect);
649  }
650  }
651 
652  // copy line as new line
653  aLine = aNewLine;
654  nYStart = nY;
655  }
656  }
657 
658  // write last line if used
659  if (!aLine.empty())
660  {
661  tools::Rectangle aSubRect;
662 
663  // enter y values
664  aSubRect.SetTop(nYStart);
665  aSubRect.SetBottom(nY ? nY - 1 : 0);
666 
667  for (size_t a(0); a < aLine.size();)
668  {
669  aSubRect.SetLeft(aLine[a++]);
670  aSubRect.SetRight(aLine[a++]);
671  aRegion.Union(aSubRect);
672  }
673  }
674 
675  pReadAcc.reset();
676  }
677  else
678  {
679  aRegion = aRect;
680  }
681 
682  return aRegion;
683 }
684 
685 bool Bitmap::Replace(const Bitmap& rMask, const Color& rReplaceColor)
686 {
687  ScopedReadAccess pMaskAcc(const_cast<Bitmap&>(rMask));
688  BitmapScopedWriteAccess pAcc(*this);
689  bool bRet = false;
690 
691  if (pMaskAcc && pAcc)
692  {
693  const long nWidth = std::min(pMaskAcc->Width(), pAcc->Width());
694  const long nHeight = std::min(pMaskAcc->Height(), pAcc->Height());
695  const BitmapColor aMaskWhite(pMaskAcc->GetBestMatchingColor(COL_WHITE));
696  BitmapColor aReplace;
697 
698  if (pAcc->HasPalette())
699  {
700  const sal_uInt16 nActColors = pAcc->GetPaletteEntryCount();
701  const sal_uInt16 nMaxColors = 1 << pAcc->GetBitCount();
702 
703  // default to the nearest color
704  aReplace = pAcc->GetBestMatchingColor(rReplaceColor);
705 
706  // for paletted images without a matching palette entry
707  // look for an unused palette entry (NOTE: expensive!)
708  if (pAcc->GetPaletteColor(aReplace.GetIndex()) != BitmapColor(rReplaceColor))
709  {
710  // if the palette has empty entries use the last one
711  if (nActColors < nMaxColors)
712  {
713  pAcc->SetPaletteEntryCount(nActColors + 1);
714  pAcc->SetPaletteColor(nActColors, rReplaceColor);
715  aReplace = BitmapColor(static_cast<sal_uInt8>(nActColors));
716  }
717  else
718  {
719  std::unique_ptr<bool[]> pFlags(new bool[nMaxColors]);
720 
721  // Set all entries to false
722  std::fill(pFlags.get(), pFlags.get() + nMaxColors, false);
723 
724  for (long nY = 0; nY < nHeight; nY++)
725  {
726  Scanline pScanline = pAcc->GetScanline(nY);
727  for (long nX = 0; nX < nWidth; nX++)
728  pFlags[pAcc->GetIndexFromData(pScanline, nX)] = true;
729  }
730 
731  for (sal_uInt16 i = 0; i < nMaxColors; i++)
732  {
733  // Hurray, we do have an unused entry
734  if (!pFlags[i])
735  {
736  pAcc->SetPaletteColor(i, rReplaceColor);
737  aReplace = BitmapColor(static_cast<sal_uInt8>(i));
738  }
739  }
740  }
741  }
742  }
743  else
744  aReplace = rReplaceColor;
745 
746  for (long nY = 0; nY < nHeight; nY++)
747  {
748  Scanline pScanline = pAcc->GetScanline(nY);
749  Scanline pScanlineMask = pMaskAcc->GetScanline(nY);
750  for (long nX = 0; nX < nWidth; nX++)
751  {
752  if (pMaskAcc->GetPixelFromData(pScanlineMask, nX) == aMaskWhite)
753  pAcc->SetPixelOnData(pScanline, nX, aReplace);
754  }
755  }
756 
757  bRet = true;
758  }
759 
760  return bRet;
761 }
762 
763 bool Bitmap::Replace(const AlphaMask& rAlpha, const Color& rMergeColor)
764 {
765  Bitmap aNewBmp(GetSizePixel(), 24);
766  ScopedReadAccess pAcc(*this);
767  AlphaMask::ScopedReadAccess pAlphaAcc(const_cast<AlphaMask&>(rAlpha));
768  BitmapScopedWriteAccess pNewAcc(aNewBmp);
769  bool bRet = false;
770 
771  if (pAcc && pAlphaAcc && pNewAcc)
772  {
773  BitmapColor aCol;
774  const long nWidth = std::min(pAlphaAcc->Width(), pAcc->Width());
775  const long nHeight = std::min(pAlphaAcc->Height(), pAcc->Height());
776 
777  for (long nY = 0; nY < nHeight; nY++)
778  {
779  Scanline pScanline = pNewAcc->GetScanline(nY);
780  Scanline pScanlineAlpha = pAlphaAcc->GetScanline(nY);
781  for (long nX = 0; nX < nWidth; nX++)
782  {
783  aCol = pAcc->GetColor(nY, nX);
784  aCol.Merge(rMergeColor, 255 - pAlphaAcc->GetIndexFromData(pScanlineAlpha, nX));
785  pNewAcc->SetPixelOnData(pScanline, nX, aCol);
786  }
787  }
788 
789  bRet = true;
790  }
791 
792  pAcc.reset();
793  pAlphaAcc.reset();
794  pNewAcc.reset();
795 
796  if (bRet)
797  {
798  const MapMode aMap(maPrefMapMode);
799  const Size aSize(maPrefSize);
800 
801  *this = aNewBmp;
802 
803  maPrefMapMode = aMap;
804  maPrefSize = aSize;
805  }
806 
807  return bRet;
808 }
809 
810 bool Bitmap::Replace(const Color& rSearchColor, const Color& rReplaceColor, sal_uInt8 nTol)
811 {
812  if (mxSalBmp)
813  {
814  // implementation specific replace
815  std::shared_ptr<SalBitmap> xImpBmp(ImplGetSVData()->mpDefInst->CreateSalBitmap());
816  if (xImpBmp->Create(*mxSalBmp) && xImpBmp->Replace(rSearchColor, rReplaceColor, nTol))
817  {
818  ImplSetSalBitmap(xImpBmp);
819  maPrefMapMode = MapMode(MapUnit::MapPixel);
820  maPrefSize = xImpBmp->GetSize();
821  return true;
822  }
823  }
824 
825  // Bitmaps with 1 bit color depth can cause problems if they have other entries than black/white
826  // in their palette
827  if (GetBitCount() == 1)
829 
830  BitmapScopedWriteAccess pAcc(*this);
831  bool bRet = false;
832 
833  if (pAcc)
834  {
835  const long nMinR = MinMax<long>(rSearchColor.GetRed() - nTol, 0, 255);
836  const long nMaxR = MinMax<long>(rSearchColor.GetRed() + nTol, 0, 255);
837  const long nMinG = MinMax<long>(rSearchColor.GetGreen() - nTol, 0, 255);
838  const long nMaxG = MinMax<long>(rSearchColor.GetGreen() + nTol, 0, 255);
839  const long nMinB = MinMax<long>(rSearchColor.GetBlue() - nTol, 0, 255);
840  const long nMaxB = MinMax<long>(rSearchColor.GetBlue() + nTol, 0, 255);
841 
842  if (pAcc->HasPalette())
843  {
844  for (sal_uInt16 i = 0, nPalCount = pAcc->GetPaletteEntryCount(); i < nPalCount; i++)
845  {
846  const BitmapColor& rCol = pAcc->GetPaletteColor(i);
847 
848  if (nMinR <= rCol.GetRed() && nMaxR >= rCol.GetRed() && nMinG <= rCol.GetGreen()
849  && nMaxG >= rCol.GetGreen() && nMinB <= rCol.GetBlue()
850  && nMaxB >= rCol.GetBlue())
851  {
852  pAcc->SetPaletteColor(i, rReplaceColor);
853  }
854  }
855  }
856  else
857  {
858  BitmapColor aCol;
859  const BitmapColor aReplace(pAcc->GetBestMatchingColor(rReplaceColor));
860 
861  for (long nY = 0, nHeight = pAcc->Height(); nY < nHeight; nY++)
862  {
863  Scanline pScanline = pAcc->GetScanline(nY);
864  for (long nX = 0, nWidth = pAcc->Width(); nX < nWidth; nX++)
865  {
866  aCol = pAcc->GetPixelFromData(pScanline, nX);
867 
868  if (nMinR <= aCol.GetRed() && nMaxR >= aCol.GetRed() && nMinG <= aCol.GetGreen()
869  && nMaxG >= aCol.GetGreen() && nMinB <= aCol.GetBlue()
870  && nMaxB >= aCol.GetBlue())
871  {
872  pAcc->SetPixelOnData(pScanline, nX, aReplace);
873  }
874  }
875  }
876  }
877 
878  pAcc.reset();
879  bRet = true;
880  }
881 
882  return bRet;
883 }
884 
885 bool Bitmap::Replace(const Color* pSearchColors, const Color* pReplaceColors, sal_uLong nColorCount,
886  sal_uInt8 const* pTols)
887 {
888  // Bitmaps with 1 bit color depth can cause problems if they have other entries than black/white
889  // in their palette
890  if (GetBitCount() == 1)
892 
893  BitmapScopedWriteAccess pAcc(*this);
894  bool bRet = false;
895 
896  if (pAcc)
897  {
898  std::unique_ptr<long[]> pMinR(new long[nColorCount]);
899  std::unique_ptr<long[]> pMaxR(new long[nColorCount]);
900  std::unique_ptr<long[]> pMinG(new long[nColorCount]);
901  std::unique_ptr<long[]> pMaxG(new long[nColorCount]);
902  std::unique_ptr<long[]> pMinB(new long[nColorCount]);
903  std::unique_ptr<long[]> pMaxB(new long[nColorCount]);
904 
905  if (pTols)
906  {
907  for (sal_uLong i = 0; i < nColorCount; i++)
908  {
909  const Color& rCol = pSearchColors[i];
910  const sal_uInt8 nTol = pTols[i];
911 
912  pMinR[i] = MinMax<long>(rCol.GetRed() - nTol, 0, 255);
913  pMaxR[i] = MinMax<long>(rCol.GetRed() + nTol, 0, 255);
914  pMinG[i] = MinMax<long>(rCol.GetGreen() - nTol, 0, 255);
915  pMaxG[i] = MinMax<long>(rCol.GetGreen() + nTol, 0, 255);
916  pMinB[i] = MinMax<long>(rCol.GetBlue() - nTol, 0, 255);
917  pMaxB[i] = MinMax<long>(rCol.GetBlue() + nTol, 0, 255);
918  }
919  }
920  else
921  {
922  for (sal_uLong i = 0; i < nColorCount; i++)
923  {
924  const Color& rCol = pSearchColors[i];
925 
926  pMinR[i] = rCol.GetRed();
927  pMaxR[i] = rCol.GetRed();
928  pMinG[i] = rCol.GetGreen();
929  pMaxG[i] = rCol.GetGreen();
930  pMinB[i] = rCol.GetBlue();
931  pMaxB[i] = rCol.GetBlue();
932  }
933  }
934 
935  if (pAcc->HasPalette())
936  {
937  for (sal_uInt16 nEntry = 0, nPalCount = pAcc->GetPaletteEntryCount();
938  nEntry < nPalCount; nEntry++)
939  {
940  const BitmapColor& rCol = pAcc->GetPaletteColor(nEntry);
941 
942  for (sal_uLong i = 0; i < nColorCount; i++)
943  {
944  if (pMinR[i] <= rCol.GetRed() && pMaxR[i] >= rCol.GetRed()
945  && pMinG[i] <= rCol.GetGreen() && pMaxG[i] >= rCol.GetGreen()
946  && pMinB[i] <= rCol.GetBlue() && pMaxB[i] >= rCol.GetBlue())
947  {
948  pAcc->SetPaletteColor(nEntry, pReplaceColors[i]);
949  break;
950  }
951  }
952  }
953  }
954  else
955  {
956  BitmapColor aCol;
957  std::unique_ptr<BitmapColor[]> pReplaces(new BitmapColor[nColorCount]);
958 
959  for (sal_uLong i = 0; i < nColorCount; i++)
960  pReplaces[i] = pAcc->GetBestMatchingColor(pReplaceColors[i]);
961 
962  for (long nY = 0, nHeight = pAcc->Height(); nY < nHeight; nY++)
963  {
964  Scanline pScanline = pAcc->GetScanline(nY);
965  for (long nX = 0, nWidth = pAcc->Width(); nX < nWidth; nX++)
966  {
967  aCol = pAcc->GetPixelFromData(pScanline, nX);
968 
969  for (sal_uLong i = 0; i < nColorCount; i++)
970  {
971  if (pMinR[i] <= aCol.GetRed() && pMaxR[i] >= aCol.GetRed()
972  && pMinG[i] <= aCol.GetGreen() && pMaxG[i] >= aCol.GetGreen()
973  && pMinB[i] <= aCol.GetBlue() && pMaxB[i] >= aCol.GetBlue())
974  {
975  pAcc->SetPixelOnData(pScanline, nX, pReplaces[i]);
976  break;
977  }
978  }
979  }
980  }
981  }
982 
983  pAcc.reset();
984  bRet = true;
985  }
986 
987  return bRet;
988 }
989 
990 bool Bitmap::CombineSimple(const Bitmap& rMask, BmpCombine eCombine)
991 {
992  ScopedReadAccess pMaskAcc(const_cast<Bitmap&>(rMask));
993  BitmapScopedWriteAccess pAcc(*this);
994  bool bRet = false;
995 
996  if (pMaskAcc && pAcc)
997  {
998  const long nWidth = std::min(pMaskAcc->Width(), pAcc->Width());
999  const long nHeight = std::min(pMaskAcc->Height(), pAcc->Height());
1000  const Color aColBlack(COL_BLACK);
1001  const BitmapColor aWhite(pAcc->GetBestMatchingColor(COL_WHITE));
1002  const BitmapColor aBlack(pAcc->GetBestMatchingColor(aColBlack));
1003  const BitmapColor aMaskBlack(pMaskAcc->GetBestMatchingColor(aColBlack));
1004 
1005  switch (eCombine)
1006  {
1007  case BmpCombine::And:
1008  {
1009  for (long nY = 0; nY < nHeight; nY++)
1010  {
1011  Scanline pScanline = pAcc->GetScanline(nY);
1012  Scanline pScanlineMask = pMaskAcc->GetScanline(nY);
1013  for (long nX = 0; nX < nWidth; nX++)
1014  {
1015  if (pMaskAcc->GetPixelFromData(pScanlineMask, nX) != aMaskBlack
1016  && pAcc->GetPixelFromData(pScanline, nX) != aBlack)
1017  {
1018  pAcc->SetPixelOnData(pScanline, nX, aWhite);
1019  }
1020  else
1021  {
1022  pAcc->SetPixelOnData(pScanline, nX, aBlack);
1023  }
1024  }
1025  }
1026  }
1027  break;
1028 
1029  case BmpCombine::Or:
1030  {
1031  for (long nY = 0; nY < nHeight; nY++)
1032  {
1033  Scanline pScanline = pAcc->GetScanline(nY);
1034  Scanline pScanlineMask = pMaskAcc->GetScanline(nY);
1035  for (long nX = 0; nX < nWidth; nX++)
1036  {
1037  if (pMaskAcc->GetPixelFromData(pScanlineMask, nX) != aMaskBlack
1038  || pAcc->GetPixelFromData(pScanline, nX) != aBlack)
1039  {
1040  pAcc->SetPixelOnData(pScanline, nX, aWhite);
1041  }
1042  else
1043  {
1044  pAcc->SetPixelOnData(pScanline, nX, aBlack);
1045  }
1046  }
1047  }
1048  }
1049  break;
1050  }
1051 
1052  bRet = true;
1053  }
1054 
1055  return bRet;
1056 }
1057 
1058 // TODO: Have a look at OutputDevice::ImplDrawAlpha() for some
1059 // optimizations. Might even consolidate the code here and there.
1060 bool Bitmap::Blend(const AlphaMask& rAlpha, const Color& rBackgroundColor)
1061 {
1062  // Convert to a truecolor bitmap, if we're a paletted one. There's room for tradeoff decision here,
1063  // maybe later for an overload (or a flag)
1064  if (GetBitCount() <= 8)
1066 
1067  AlphaMask::ScopedReadAccess pAlphaAcc(const_cast<AlphaMask&>(rAlpha));
1068 
1069  BitmapScopedWriteAccess pAcc(*this);
1070  bool bRet = false;
1071 
1072  if (pAlphaAcc && pAcc)
1073  {
1074  const long nWidth = std::min(pAlphaAcc->Width(), pAcc->Width());
1075  const long nHeight = std::min(pAlphaAcc->Height(), pAcc->Height());
1076 
1077  for (long nY = 0; nY < nHeight; ++nY)
1078  {
1079  Scanline pScanline = pAcc->GetScanline(nY);
1080  Scanline pScanlineAlpha = pAlphaAcc->GetScanline(nY);
1081  for (long nX = 0; nX < nWidth; ++nX)
1082  {
1083  BitmapColor aBmpColor = pAcc->GetPixelFromData(pScanline, nX);
1084  aBmpColor.Merge(rBackgroundColor,
1085  255 - pAlphaAcc->GetIndexFromData(pScanlineAlpha, nX));
1086  pAcc->SetPixelOnData(pScanline, nX, aBmpColor);
1087  }
1088  }
1089 
1090  bRet = true;
1091  }
1092 
1093  return bRet;
1094 }
1095 
1096 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
long Width() const
sal_uInt8 GetIndex() const
Definition: BitmapColor.hxx:61
constexpr::Color COL_BLACK(0x00, 0x00, 0x00)
sal_uInt8 GetRed() const
tools::Rectangle & Intersection(const tools::Rectangle &rRect)
long FRound(double fVal)
Scanline GetScanline(long nY) const
void Merge(const Color &rMergeColor, sal_uInt8 cTransparency)
long Height() const
void Union(const tools::Rectangle &rRegion)
Definition: region.cxx:508
sal_uIntPtr sal_uLong
Size maPrefSize
Definition: bitmap.hxx:542
#define F_PI1800
sal_uInt16 GetBitCount() const
Size GetSizePixel() const
long Width() const
void SetPalette(const BitmapPalette &rPalette)
HashMap_OWString_Interface aMap
long Right() const
bool CombineSimple(const Bitmap &rMask, BmpCombine eCombine)
Perform boolean operations with another bitmap.
bool Mirror(BmpMirrorFlags nMirrorFlags)
Mirror the bitmap.
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
int nCount
void SetPixelOnData(sal_uInt8 *pData, long nX, const BitmapColor &rBitmapColor)
long Top() const
bool Blend(const AlphaMask &rAlpha, const Color &rBackgroundColor)
Alpha-blend the given bitmap against a specified uniform background color.
sal_uInt8 GetBlue() const
void Rotate(const Point &rCenter, double fSin, double fCos)
void Invert()
ImplSVData * ImplGetSVData()
Definition: svdata.cxx:76
sal_uInt8 * Scanline
Definition: Scanline.hxx:25
void SetTop(long v)
sal_uInt32 GetScanlineSize() const
int i
bool HasPalette() const
uno_Any a
ScanlineFormat GetScanlineFormat() const
void Erase(const Color &rColor)
Definition: bmpacc3.cxx:68
void SetRight(long v)
long Bottom() const
vcl::Region CreateRegion(const Color &rColor, const tools::Rectangle &rRect) const
Create region of similar colors in a given rectangle.
BmpMirrorFlags
Definition: bitmap.hxx:36
bool Invert()
Perform the Invert operation on every pixel.
Definition: bitmappaint.cxx:64
BitmapColor GetPixel(long nY, long nX) const
sal_uInt16 GetPaletteEntryCount() const
sal_uInt8 GetGreen() const
bool Rotate(long nAngle10, const Color &rFillColor)
Rotate bitmap by the specified angle.
long Height() const
unsigned char sal_uInt8
bool Convert(BmpConversion eConversion)
Convert bitmap format.
Definition: bitmap3.cxx:229
BitmapColor GetColor(long nY, long nX) const
const BitmapPalette & GetPalette() const
void SetBottom(long v)
bool IsEmpty() const
Definition: bitmap.hxx:551
constexpr::Color COL_WHITE(0xFF, 0xFF, 0xFF)
long Left() const
BitmapColor GetPixelFromData(const sal_uInt8 *pData, long nX) const
bool Erase(const Color &rFillColor)
Fill the entire bitmap with the given color.
Definition: bitmappaint.cxx:34
void SetLeft(long v)
const BitmapColor & GetPaletteColor(sal_uInt16 nColor) const
tools::Rectangle GetBoundRect() const
sal_uInt16 GetBitCount() const
SAL_DLLPRIVATE void ReassignWithSize(const Bitmap &rBitmap)
ReassignWithSize and recalculate bitmap.
bool Replace(const Bitmap &rMask, const Color &rReplaceColor)
Replace all pixel where the given mask is on with the specified color.
MapMode maPrefMapMode
Definition: bitmap.hxx:541
BitmapColor GetBestMatchingColor(const BitmapColor &rBitmapColor)
Bitmap CreateMask(const Color &rTransColor, sal_uInt8 nTol=0) const
Create on-off mask from bitmap.
void SetPaletteEntryCount(sal_uInt16 nCount)
void SetPaletteColor(sal_uInt16 nColor, const BitmapColor &rBitmapColor)
std::shared_ptr< SalBitmap > mxSalBmp
Definition: bitmap.hxx:540
BmpCombine
Definition: bitmap.hxx:79
SAL_DLLPRIVATE void ImplSetSalBitmap(const std::shared_ptr< SalBitmap > &xImpBmp)
sal_uInt8 GetIndexFromData(const sal_uInt8 *pData, long nX) const