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  BitmapScopedWriteAccess pWriteAcc(*this);
40  bool bRet = false;
41 
42  if (pWriteAcc)
43  {
44  const ScanlineFormat nFormat = pWriteAcc->GetScanlineFormat();
45  sal_uInt8 cIndex = 0;
46  bool bFast = false;
47 
48  switch (nFormat)
49  {
52  {
53  cIndex = static_cast<sal_uInt8>(pWriteAcc->GetBestPaletteIndex(rFillColor));
54  cIndex = (cIndex ? 255 : 0);
55  bFast = true;
56  }
57  break;
58 
61  {
62  cIndex = static_cast<sal_uInt8>(pWriteAcc->GetBestPaletteIndex(rFillColor));
63  cIndex = cIndex | (cIndex << 4);
64  bFast = true;
65  }
66  break;
67 
69  {
70  cIndex = static_cast<sal_uInt8>(pWriteAcc->GetBestPaletteIndex(rFillColor));
71  bFast = true;
72  }
73  break;
74 
77  {
78  if (rFillColor.GetRed() == rFillColor.GetGreen()
79  && rFillColor.GetRed() == rFillColor.GetBlue())
80  {
81  cIndex = rFillColor.GetRed();
82  bFast = true;
83  }
84  else
85  {
86  bFast = false;
87  }
88  }
89  break;
90 
91  default:
92  bFast = false;
93  break;
94  }
95 
96  if (bFast)
97  {
98  const sal_uLong nBufSize = pWriteAcc->GetScanlineSize() * pWriteAcc->Height();
99  memset(pWriteAcc->GetBuffer(), cIndex, nBufSize);
100  }
101  else
102  {
103  const tools::Rectangle aRect(Point(), Size(pWriteAcc->Width(), pWriteAcc->Height()));
104  pWriteAcc->SetFillColor(rFillColor);
105  pWriteAcc->FillRect(aRect);
106  }
107 
108  bRet = true;
109  }
110 
111  return bRet;
112 }
113 
115 {
116  BitmapScopedWriteAccess pAcc(*this);
117  bool bRet = false;
118 
119  if (pAcc)
120  {
121  if (pAcc->HasPalette())
122  {
123  BitmapPalette aBmpPal(pAcc->GetPalette());
124  const sal_uInt16 nCount = aBmpPal.GetEntryCount();
125 
126  for (sal_uInt16 i = 0; i < nCount; i++)
127  {
128  aBmpPal[i].Invert();
129  }
130 
131  pAcc->SetPalette(aBmpPal);
132  }
133  else
134  {
135  const long nWidth = pAcc->Width();
136  const long nHeight = pAcc->Height();
137 
138  for (long nY = 0; nY < nHeight; nY++)
139  {
140  Scanline pScanline = pAcc->GetScanline(nY);
141  for (long nX = 0; nX < nWidth; nX++)
142  {
143  BitmapColor aBmpColor = pAcc->GetPixelFromData(pScanline, nX);
144  aBmpColor.Invert();
145  pAcc->SetPixelOnData(pScanline, nX, aBmpColor);
146  }
147  }
148  }
149 
150  mxSalBmp->InvalidateChecksum();
151  pAcc.reset();
152  bRet = true;
153  }
154 
155  return bRet;
156 }
157 
158 bool Bitmap::Mirror(BmpMirrorFlags nMirrorFlags)
159 {
160  bool bHorz(nMirrorFlags & BmpMirrorFlags::Horizontal);
161  bool bVert(nMirrorFlags & BmpMirrorFlags::Vertical);
162  bool bRet = false;
163 
164  if (bHorz && !bVert)
165  {
166  BitmapScopedWriteAccess pAcc(*this);
167 
168  if (pAcc)
169  {
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;
174 
175  for (long nY = 0; nY < nHeight; nY++)
176  {
177  Scanline pScanline = pAcc->GetScanline(nY);
178  for (long nX = 0, nOther = nWidth1; nX < nWidth_2; nX++, nOther--)
179  {
180  const BitmapColor aTemp(pAcc->GetPixelFromData(pScanline, nX));
181 
182  pAcc->SetPixelOnData(pScanline, nX, pAcc->GetPixelFromData(pScanline, nOther));
183  pAcc->SetPixelOnData(pScanline, nOther, aTemp);
184  }
185  }
186 
187  pAcc.reset();
188  bRet = true;
189  }
190  }
191  else if (bVert && !bHorz)
192  {
193  BitmapScopedWriteAccess pAcc(*this);
194 
195  if (pAcc)
196  {
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;
202 
203  for (long nY = 0, nOther = nHeight1; nY < nHeight_2; nY++, nOther--)
204  {
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);
208  }
209 
210  pAcc.reset();
211  bRet = true;
212  }
213  }
214  else if (bHorz && bVert)
215  {
216  BitmapScopedWriteAccess pAcc(*this);
217 
218  if (pAcc)
219  {
220  const long nWidth = pAcc->Width();
221  const long nWidth1 = nWidth - 1;
222  const long nHeight = pAcc->Height();
223  long nHeight_2 = nHeight >> 1;
224 
225  for (long nY = 0, nOtherY = nHeight - 1; nY < nHeight_2; nY++, nOtherY--)
226  {
227  Scanline pScanline = pAcc->GetScanline(nY);
228  Scanline pScanlineOther = pAcc->GetScanline(nOtherY);
229  for (long nX = 0, nOtherX = nWidth1; nX < nWidth; nX++, nOtherX--)
230  {
231  const BitmapColor aTemp(pAcc->GetPixelFromData(pScanline, nX));
232 
233  pAcc->SetPixelOnData(pScanline, nX,
234  pAcc->GetPixelFromData(pScanlineOther, nOtherX));
235  pAcc->SetPixelOnData(pScanlineOther, nOtherX, aTemp);
236  }
237  }
238 
239  // if necessary, also mirror the middle line horizontally
240  if (nHeight & 1)
241  {
242  Scanline pScanline = pAcc->GetScanline(nHeight_2);
243  for (long nX = 0, nOtherX = nWidth1, nWidth_2 = nWidth >> 1; nX < nWidth_2;
244  nX++, nOtherX--)
245  {
246  const BitmapColor aTemp(pAcc->GetPixelFromData(pScanline, nX));
247  pAcc->SetPixelOnData(pScanline, nX, pAcc->GetPixelFromData(pScanline, nOtherX));
248  pAcc->SetPixelOnData(pScanline, nOtherX, aTemp);
249  }
250  }
251 
252  pAcc.reset();
253  bRet = true;
254  }
255  }
256  else
257  bRet = true;
258 
259  return bRet;
260 }
261 
262 bool Bitmap::Rotate(long nAngle10, const Color& rFillColor)
263 {
264  bool bRet = false;
265 
266  nAngle10 %= 3600;
267  nAngle10 = (nAngle10 < 0) ? (3599L + nAngle10) : nAngle10;
268 
269  if (!nAngle10)
270  bRet = true;
271  else if (nAngle10 == 1800)
273  else
274  {
275  ScopedReadAccess pReadAcc(*this);
276  Bitmap aRotatedBmp;
277 
278  if (pReadAcc)
279  {
280  const Size aSizePix(GetSizePixel());
281 
282  if (nAngle10 == 900 || nAngle10 == 2700)
283  {
284  const Size aNewSizePix(aSizePix.Height(), aSizePix.Width());
285  Bitmap aNewBmp(aNewSizePix, GetBitCount(), &pReadAcc->GetPalette());
286  BitmapScopedWriteAccess pWriteAcc(aNewBmp);
287 
288  if (pWriteAcc)
289  {
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();
296 
297  if (nAngle10 == 900)
298  {
299  for (long nY = 0, nOtherX = nWidth1; nY < nNewHeight; nY++, nOtherX--)
300  {
301  Scanline pScanline = pWriteAcc->GetScanline(nY);
302  for (long nX = 0, nOtherY = 0; nX < nNewWidth; nX++)
303  {
304  pWriteAcc->SetPixelOnData(pScanline, nX,
305  pReadAcc->GetPixel(nOtherY++, nOtherX));
306  }
307  }
308  }
309  else if (nAngle10 == 2700)
310  {
311  for (long nY = 0, nOtherX = 0; nY < nNewHeight; nY++, nOtherX++)
312  {
313  Scanline pScanline = pWriteAcc->GetScanline(nY);
314  for (long nX = 0, nOtherY = nHeight1; nX < nNewWidth; nX++)
315  {
316  pWriteAcc->SetPixelOnData(pScanline, nX,
317  pReadAcc->GetPixel(nOtherY--, nOtherX));
318  }
319  }
320  }
321 
322  pWriteAcc.reset();
323  }
324 
325  aRotatedBmp = aNewBmp;
326  }
327  else
328  {
329  Point aTmpPoint;
330  tools::Rectangle aTmpRectangle(aTmpPoint, aSizePix);
331  tools::Polygon aPoly(aTmpRectangle);
332  aPoly.Rotate(aTmpPoint, static_cast<sal_uInt16>(nAngle10));
333 
334  tools::Rectangle aNewBound(aPoly.GetBoundRect());
335  const Size aNewSizePix(aNewBound.GetSize());
336  Bitmap aNewBmp(aNewSizePix, GetBitCount(), &pReadAcc->GetPalette());
337  BitmapScopedWriteAccess pWriteAcc(aNewBmp);
338 
339  if (pWriteAcc)
340  {
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]);
358 
359  for (nX = 0; nX < nNewWidth; nX++)
360  {
361  const double fTmp = (fXMin + nX) * 64.;
362 
363  pCosX[nX] = FRound(fCosAngle * fTmp);
364  pSinX[nX] = FRound(fSinAngle * fTmp);
365  }
366 
367  for (nY = 0; nY < nNewHeight; nY++)
368  {
369  const double fTmp = (fYMin + nY) * 64.;
370 
371  pCosY[nY] = FRound(fCosAngle * fTmp);
372  pSinY[nY] = FRound(fSinAngle * fTmp);
373  }
374 
375  for (nY = 0; nY < nNewHeight; nY++)
376  {
377  long nSinY = pSinY[nY];
378  long nCosY = pCosY[nY];
379  Scanline pScanline = pWriteAcc->GetScanline(nY);
380 
381  for (nX = 0; nX < nNewWidth; nX++)
382  {
383  nRotX = (pCosX[nX] - nSinY) >> 6;
384  nRotY = (pSinX[nX] + nCosY) >> 6;
385 
386  if ((nRotX > -1) && (nRotX < nWidth) && (nRotY > -1)
387  && (nRotY < nHeight))
388  {
389  pWriteAcc->SetPixelOnData(pScanline, nX,
390  pReadAcc->GetPixel(nRotY, nRotX));
391  }
392  else
393  {
394  pWriteAcc->SetPixelOnData(pScanline, nX, aFillColor);
395  }
396  }
397  }
398 
399  pWriteAcc.reset();
400  }
401 
402  aRotatedBmp = aNewBmp;
403  }
404 
405  pReadAcc.reset();
406  }
407 
408  bRet = !!aRotatedBmp;
409  if (bRet)
410  ReassignWithSize(aRotatedBmp);
411  }
412 
413  return bRet;
414 };
415 
416 Bitmap Bitmap::CreateMask(const Color& rTransColor, sal_uInt8 nTol) const
417 {
418  ScopedReadAccess pReadAcc(const_cast<Bitmap&>(*this));
419 
420  if (!nTol && pReadAcc
423  && pReadAcc->GetBestMatchingColor(COL_WHITE) == pReadAcc->GetBestMatchingColor(rTransColor))
424  {
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;
428  }
429 
430  Bitmap aNewBmp(GetSizePixel(), 1);
431  BitmapScopedWriteAccess pWriteAcc(aNewBmp);
432  bool bRet = false;
433 
434  if (pWriteAcc && pReadAcc)
435  {
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));
440 
441  if (!nTol)
442  {
443  const BitmapColor aTest(pReadAcc->GetBestMatchingColor(rTransColor));
444 
447  {
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);
452 
454  && aWhite.GetIndex() == 1)
455  {
456  // optimized for 1Bit-MSB destination palette
457  for (long nY = 0; nY < nHeight; ++nY)
458  {
459  Scanline pSrc = pReadAcc->GetScanline(nY);
460  Scanline pDst = pWriteAcc->GetScanline(nY);
461  for (long nX = 0, nShift = nShiftInit; nX < nWidth; nX++, nShift ^= 4)
462  {
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)));
467  }
468  }
469  }
470  else
471  {
472  for (long nY = 0; nY < nHeight; ++nY)
473  {
474  Scanline pSrc = pReadAcc->GetScanline(nY);
475  Scanline pDst = pWriteAcc->GetScanline(nY);
476  for (long nX = 0, nShift = nShiftInit; nX < nWidth; nX++, nShift ^= 4)
477  {
478  if (cTest == ((pSrc[nX >> 1] >> nShift) & 0x0f))
479  pWriteAcc->SetPixelOnData(pDst, nX, aWhite);
480  else
481  pWriteAcc->SetPixelOnData(pDst, nX, aBlack);
482  }
483  }
484  }
485  }
486  else if (pReadAcc->GetScanlineFormat() == ScanlineFormat::N8BitPal)
487  {
488  // optimized for 8Bit source palette
489  const sal_uInt8 cTest = aTest.GetIndex();
490 
492  && aWhite.GetIndex() == 1)
493  {
494  // optimized for 1Bit-MSB destination palette
495  for (long nY = 0; nY < nHeight; ++nY)
496  {
497  Scanline pSrc = pReadAcc->GetScanline(nY);
498  Scanline pDst = pWriteAcc->GetScanline(nY);
499  for (long nX = 0; nX < nWidth; ++nX)
500  {
501  if (cTest == pSrc[nX])
502  pDst[nX >> 3] |= 1 << (7 - (nX & 7));
503  else
504  pDst[nX >> 3] &= ~(1 << (7 - (nX & 7)));
505  }
506  }
507  }
508  else
509  {
510  for (long nY = 0; nY < nHeight; ++nY)
511  {
512  Scanline pSrc = pReadAcc->GetScanline(nY);
513  Scanline pDst = pWriteAcc->GetScanline(nY);
514  for (long nX = 0; nX < nWidth; ++nX)
515  {
516  if (cTest == pSrc[nX])
517  pWriteAcc->SetPixelOnData(pDst, nX, aWhite);
518  else
519  pWriteAcc->SetPixelOnData(pDst, nX, aBlack);
520  }
521  }
522  }
523  }
524  else if (pWriteAcc->GetScanlineFormat() == pReadAcc->GetScanlineFormat()
525  && aWhite.GetIndex() == 1
528  {
529  for (long nY = 0; nY < nHeight; ++nY)
530  {
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];
537  }
538  }
539  else
540  {
541  // not optimized
542  for (long nY = 0; nY < nHeight; ++nY)
543  {
544  Scanline pScanline = pWriteAcc->GetScanline(nY);
545  Scanline pScanlineRead = pReadAcc->GetScanline(nY);
546  for (long nX = 0; nX < nWidth; ++nX)
547  {
548  if (aTest == pReadAcc->GetPixelFromData(pScanlineRead, nX))
549  pWriteAcc->SetPixelOnData(pScanline, nX, aWhite);
550  else
551  pWriteAcc->SetPixelOnData(pScanline, nX, aBlack);
552  }
553  }
554  }
555  }
556  else
557  {
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);
566 
567  if (pReadAcc->HasPalette())
568  {
569  for (long nY = 0; nY < nHeight; nY++)
570  {
571  Scanline pScanline = pWriteAcc->GetScanline(nY);
572  Scanline pScanlineRead = pReadAcc->GetScanline(nY);
573  for (long nX = 0; nX < nWidth; nX++)
574  {
575  aCol = pReadAcc->GetPaletteColor(
576  pReadAcc->GetIndexFromData(pScanlineRead, nX));
577  nR = aCol.GetRed();
578  nG = aCol.GetGreen();
579  nB = aCol.GetBlue();
580 
581  if (nMinR <= nR && nMaxR >= nR && nMinG <= nG && nMaxG >= nG && nMinB <= nB
582  && nMaxB >= nB)
583  {
584  pWriteAcc->SetPixelOnData(pScanline, nX, aWhite);
585  }
586  else
587  {
588  pWriteAcc->SetPixelOnData(pScanline, nX, aBlack);
589  }
590  }
591  }
592  }
593  else
594  {
595  for (long nY = 0; nY < nHeight; nY++)
596  {
597  Scanline pScanline = pWriteAcc->GetScanline(nY);
598  Scanline pScanlineRead = pReadAcc->GetScanline(nY);
599  for (long nX = 0; nX < nWidth; nX++)
600  {
601  aCol = pReadAcc->GetPixelFromData(pScanlineRead, nX);
602  nR = aCol.GetRed();
603  nG = aCol.GetGreen();
604  nB = aCol.GetBlue();
605 
606  if (nMinR <= nR && nMaxR >= nR && nMinG <= nG && nMaxG >= nG && nMinB <= nB
607  && nMaxB >= nB)
608  {
609  pWriteAcc->SetPixelOnData(pScanline, nX, aWhite);
610  }
611  else
612  {
613  pWriteAcc->SetPixelOnData(pScanline, nX, aBlack);
614  }
615  }
616  }
617  }
618  }
619 
620  bRet = true;
621  }
622 
623  pWriteAcc.reset();
624  pReadAcc.reset();
625 
626  if (bRet)
627  {
628  aNewBmp.maPrefSize = maPrefSize;
629  aNewBmp.maPrefMapMode = maPrefMapMode;
630  }
631  else
632  aNewBmp = Bitmap();
633 
634  return aNewBmp;
635 }
636 
637 vcl::Region Bitmap::CreateRegion(const Color& rColor, const tools::Rectangle& rRect) const
638 {
639  vcl::Region aRegion;
640  tools::Rectangle aRect(rRect);
641  ScopedReadAccess pReadAcc(const_cast<Bitmap&>(*this));
642 
644  aRect.Justify();
645 
646  if (pReadAcc)
647  {
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));
653 
654  std::vector<long> aLine;
655  long nYStart(nTop);
656  long nY(nTop);
657 
658  for (; nY <= nBottom; nY++)
659  {
660  std::vector<long> aNewLine;
661  long nX(nLeft);
662  Scanline pScanlineRead = pReadAcc->GetScanline(nY);
663 
664  for (; nX <= nRight;)
665  {
666  while ((nX <= nRight) && (aMatch != pReadAcc->GetPixelFromData(pScanlineRead, nX)))
667  nX++;
668 
669  if (nX <= nRight)
670  {
671  aNewLine.push_back(nX);
672 
673  while ((nX <= nRight)
674  && (aMatch == pReadAcc->GetPixelFromData(pScanlineRead, nX)))
675  {
676  nX++;
677  }
678 
679  aNewLine.push_back(nX - 1);
680  }
681  }
682 
683  if (aNewLine != aLine)
684  {
685  // need to write aLine, it's different from the next line
686  if (!aLine.empty())
687  {
688  tools::Rectangle aSubRect;
689 
690  // enter y values and proceed ystart
691  aSubRect.SetTop(nYStart);
692  aSubRect.SetBottom(nY ? nY - 1 : 0);
693 
694  for (size_t a(0); a < aLine.size();)
695  {
696  aSubRect.SetLeft(aLine[a++]);
697  aSubRect.SetRight(aLine[a++]);
698  aRegion.Union(aSubRect);
699  }
700  }
701 
702  // copy line as new line
703  aLine = aNewLine;
704  nYStart = nY;
705  }
706  }
707 
708  // write last line if used
709  if (!aLine.empty())
710  {
711  tools::Rectangle aSubRect;
712 
713  // enter y values
714  aSubRect.SetTop(nYStart);
715  aSubRect.SetBottom(nY ? nY - 1 : 0);
716 
717  for (size_t a(0); a < aLine.size();)
718  {
719  aSubRect.SetLeft(aLine[a++]);
720  aSubRect.SetRight(aLine[a++]);
721  aRegion.Union(aSubRect);
722  }
723  }
724 
725  pReadAcc.reset();
726  }
727  else
728  {
729  aRegion = aRect;
730  }
731 
732  return aRegion;
733 }
734 
735 bool Bitmap::Replace(const Bitmap& rMask, const Color& rReplaceColor)
736 {
737  ScopedReadAccess pMaskAcc(const_cast<Bitmap&>(rMask));
738  BitmapScopedWriteAccess pAcc(*this);
739  bool bRet = false;
740 
741  if (pMaskAcc && pAcc)
742  {
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;
747 
748  if (pAcc->HasPalette())
749  {
750  const sal_uInt16 nActColors = pAcc->GetPaletteEntryCount();
751  const sal_uInt16 nMaxColors = 1 << pAcc->GetBitCount();
752 
753  // default to the nearest color
754  aReplace = pAcc->GetBestMatchingColor(rReplaceColor);
755 
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))
759  {
760  // if the palette has empty entries use the last one
761  if (nActColors < nMaxColors)
762  {
763  pAcc->SetPaletteEntryCount(nActColors + 1);
764  pAcc->SetPaletteColor(nActColors, rReplaceColor);
765  aReplace = BitmapColor(static_cast<sal_uInt8>(nActColors));
766  }
767  else
768  {
769  std::unique_ptr<bool[]> pFlags(new bool[nMaxColors]);
770 
771  // Set all entries to false
772  std::fill(pFlags.get(), pFlags.get() + nMaxColors, false);
773 
774  for (long nY = 0; nY < nHeight; nY++)
775  {
776  Scanline pScanline = pAcc->GetScanline(nY);
777  for (long nX = 0; nX < nWidth; nX++)
778  pFlags[pAcc->GetIndexFromData(pScanline, nX)] = true;
779  }
780 
781  for (sal_uInt16 i = 0; i < nMaxColors; i++)
782  {
783  // Hurray, we do have an unused entry
784  if (!pFlags[i])
785  {
786  pAcc->SetPaletteColor(i, rReplaceColor);
787  aReplace = BitmapColor(static_cast<sal_uInt8>(i));
788  }
789  }
790  }
791  }
792  }
793  else
794  aReplace = rReplaceColor;
795 
796  for (long nY = 0; nY < nHeight; nY++)
797  {
798  Scanline pScanline = pAcc->GetScanline(nY);
799  Scanline pScanlineMask = pMaskAcc->GetScanline(nY);
800  for (long nX = 0; nX < nWidth; nX++)
801  {
802  if (pMaskAcc->GetPixelFromData(pScanlineMask, nX) == aMaskWhite)
803  pAcc->SetPixelOnData(pScanline, nX, aReplace);
804  }
805  }
806 
807  bRet = true;
808  }
809 
810  return bRet;
811 }
812 
813 bool Bitmap::Replace(const AlphaMask& rAlpha, const Color& rMergeColor)
814 {
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;
820 
821  if (pAcc && pAlphaAcc && pNewAcc)
822  {
823  BitmapColor aCol;
824  const long nWidth = std::min(pAlphaAcc->Width(), pAcc->Width());
825  const long nHeight = std::min(pAlphaAcc->Height(), pAcc->Height());
826 
827  for (long nY = 0; nY < nHeight; nY++)
828  {
829  Scanline pScanline = pNewAcc->GetScanline(nY);
830  Scanline pScanlineAlpha = pAlphaAcc->GetScanline(nY);
831  for (long nX = 0; nX < nWidth; nX++)
832  {
833  aCol = pAcc->GetColor(nY, nX);
834  aCol.Merge(rMergeColor, 255 - pAlphaAcc->GetIndexFromData(pScanlineAlpha, nX));
835  pNewAcc->SetPixelOnData(pScanline, nX, aCol);
836  }
837  }
838 
839  bRet = true;
840  }
841 
842  pAcc.reset();
843  pAlphaAcc.reset();
844  pNewAcc.reset();
845 
846  if (bRet)
847  {
848  const MapMode aMap(maPrefMapMode);
849  const Size aSize(maPrefSize);
850 
851  *this = aNewBmp;
852 
853  maPrefMapMode = aMap;
854  maPrefSize = aSize;
855  }
856 
857  return bRet;
858 }
859 
860 bool Bitmap::Replace(const Color& rSearchColor, const Color& rReplaceColor, sal_uInt8 nTol)
861 {
862  if (mxSalBmp)
863  {
864  // implementation specific replace
865  std::shared_ptr<SalBitmap> xImpBmp(ImplGetSVData()->mpDefInst->CreateSalBitmap());
866  if (xImpBmp->Create(*mxSalBmp) && xImpBmp->Replace(rSearchColor, rReplaceColor, nTol))
867  {
868  ImplSetSalBitmap(xImpBmp);
869  maPrefMapMode = MapMode(MapUnit::MapPixel);
870  maPrefSize = xImpBmp->GetSize();
871  return true;
872  }
873  }
874 
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)
879 
880  BitmapScopedWriteAccess pAcc(*this);
881  bool bRet = false;
882 
883  if (pAcc)
884  {
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);
891 
892  if (pAcc->HasPalette())
893  {
894  for (sal_uInt16 i = 0, nPalCount = pAcc->GetPaletteEntryCount(); i < nPalCount; i++)
895  {
896  const BitmapColor& rCol = pAcc->GetPaletteColor(i);
897 
898  if (nMinR <= rCol.GetRed() && nMaxR >= rCol.GetRed() && nMinG <= rCol.GetGreen()
899  && nMaxG >= rCol.GetGreen() && nMinB <= rCol.GetBlue()
900  && nMaxB >= rCol.GetBlue())
901  {
902  pAcc->SetPaletteColor(i, rReplaceColor);
903  }
904  }
905  }
906  else
907  {
908  BitmapColor aCol;
909  const BitmapColor aReplace(pAcc->GetBestMatchingColor(rReplaceColor));
910 
911  for (long nY = 0, nHeight = pAcc->Height(); nY < nHeight; nY++)
912  {
913  Scanline pScanline = pAcc->GetScanline(nY);
914  for (long nX = 0, nWidth = pAcc->Width(); nX < nWidth; nX++)
915  {
916  aCol = pAcc->GetPixelFromData(pScanline, nX);
917 
918  if (nMinR <= aCol.GetRed() && nMaxR >= aCol.GetRed() && nMinG <= aCol.GetGreen()
919  && nMaxG >= aCol.GetGreen() && nMinB <= aCol.GetBlue()
920  && nMaxB >= aCol.GetBlue())
921  {
922  pAcc->SetPixelOnData(pScanline, nX, aReplace);
923  }
924  }
925  }
926  }
927 
928  pAcc.reset();
929  bRet = true;
930  }
931 
932  return bRet;
933 }
934 
935 bool Bitmap::Replace(const Color* pSearchColors, const Color* pReplaceColors, sal_uLong nColorCount,
936  sal_uInt8 const* pTols)
937 {
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)
942 
943  BitmapScopedWriteAccess pAcc(*this);
944  bool bRet = false;
945 
946  if (pAcc)
947  {
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]);
954 
955  if (pTols)
956  {
957  for (sal_uLong i = 0; i < nColorCount; i++)
958  {
959  const Color& rCol = pSearchColors[i];
960  const sal_uInt8 nTol = pTols[i];
961 
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);
968  }
969  }
970  else
971  {
972  for (sal_uLong i = 0; i < nColorCount; i++)
973  {
974  const Color& rCol = pSearchColors[i];
975 
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();
982  }
983  }
984 
985  if (pAcc->HasPalette())
986  {
987  for (sal_uInt16 nEntry = 0, nPalCount = pAcc->GetPaletteEntryCount();
988  nEntry < nPalCount; nEntry++)
989  {
990  const BitmapColor& rCol = pAcc->GetPaletteColor(nEntry);
991 
992  for (sal_uLong i = 0; i < nColorCount; i++)
993  {
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())
997  {
998  pAcc->SetPaletteColor(nEntry, pReplaceColors[i]);
999  break;
1000  }
1001  }
1002  }
1003  }
1004  else
1005  {
1006  BitmapColor aCol;
1007  std::unique_ptr<BitmapColor[]> pReplaces(new BitmapColor[nColorCount]);
1008 
1009  for (sal_uLong i = 0; i < nColorCount; i++)
1010  pReplaces[i] = pAcc->GetBestMatchingColor(pReplaceColors[i]);
1011 
1012  for (long nY = 0, nHeight = pAcc->Height(); nY < nHeight; nY++)
1013  {
1014  Scanline pScanline = pAcc->GetScanline(nY);
1015  for (long nX = 0, nWidth = pAcc->Width(); nX < nWidth; nX++)
1016  {
1017  aCol = pAcc->GetPixelFromData(pScanline, nX);
1018 
1019  for (sal_uLong i = 0; i < nColorCount; i++)
1020  {
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())
1024  {
1025  pAcc->SetPixelOnData(pScanline, nX, pReplaces[i]);
1026  break;
1027  }
1028  }
1029  }
1030  }
1031  }
1032 
1033  pAcc.reset();
1034  bRet = true;
1035  }
1036 
1037  return bRet;
1038 }
1039 
1040 bool Bitmap::CombineSimple(const Bitmap& rMask, BmpCombine eCombine)
1041 {
1042  ScopedReadAccess pMaskAcc(const_cast<Bitmap&>(rMask));
1043  BitmapScopedWriteAccess pAcc(*this);
1044  bool bRet = false;
1045 
1046  if (pMaskAcc && pAcc)
1047  {
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  BitmapColor aPixel;
1052  BitmapColor aMaskPixel;
1053  const BitmapColor aWhite(pAcc->GetBestMatchingColor(COL_WHITE));
1054  const BitmapColor aBlack(pAcc->GetBestMatchingColor(aColBlack));
1055  const BitmapColor aMaskBlack(pMaskAcc->GetBestMatchingColor(aColBlack));
1056 
1057  switch (eCombine)
1058  {
1059  case BmpCombine::And:
1060  {
1061  for (long nY = 0; nY < nHeight; nY++)
1062  {
1063  Scanline pScanline = pAcc->GetScanline(nY);
1064  Scanline pScanlineMask = pMaskAcc->GetScanline(nY);
1065  for (long nX = 0; nX < nWidth; nX++)
1066  {
1067  if (pMaskAcc->GetPixelFromData(pScanlineMask, nX) != aMaskBlack
1068  && pAcc->GetPixelFromData(pScanline, nX) != aBlack)
1069  {
1070  pAcc->SetPixelOnData(pScanline, nX, aWhite);
1071  }
1072  else
1073  {
1074  pAcc->SetPixelOnData(pScanline, nX, aBlack);
1075  }
1076  }
1077  }
1078  }
1079  break;
1080 
1081  case BmpCombine::Or:
1082  {
1083  for (long nY = 0; nY < nHeight; nY++)
1084  {
1085  Scanline pScanline = pAcc->GetScanline(nY);
1086  Scanline pScanlineMask = pMaskAcc->GetScanline(nY);
1087  for (long nX = 0; nX < nWidth; nX++)
1088  {
1089  if (pMaskAcc->GetPixelFromData(pScanlineMask, nX) != aMaskBlack
1090  || pAcc->GetPixelFromData(pScanline, nX) != aBlack)
1091  {
1092  pAcc->SetPixelOnData(pScanline, nX, aWhite);
1093  }
1094  else
1095  {
1096  pAcc->SetPixelOnData(pScanline, nX, aBlack);
1097  }
1098  }
1099  }
1100  }
1101  break;
1102  }
1103 
1104  bRet = true;
1105  }
1106 
1107  return bRet;
1108 }
1109 
1110 // TODO: Have a look at OutputDevice::ImplDrawAlpha() for some
1111 // optimizations. Might even consolidate the code here and there.
1112 bool Bitmap::Blend(const AlphaMask& rAlpha, const Color& rBackgroundColor)
1113 {
1114  // Convert to a truecolor bitmap, if we're a paletted one. There's room for tradeoff decision here,
1115  // maybe later for an overload (or a flag)
1116  if (GetBitCount() <= 8)
1118 
1119  AlphaMask::ScopedReadAccess pAlphaAcc(const_cast<AlphaMask&>(rAlpha));
1120 
1121  BitmapScopedWriteAccess pAcc(*this);
1122  bool bRet = false;
1123 
1124  if (pAlphaAcc && pAcc)
1125  {
1126  const long nWidth = std::min(pAlphaAcc->Width(), pAcc->Width());
1127  const long nHeight = std::min(pAlphaAcc->Height(), pAcc->Height());
1128 
1129  for (long nY = 0; nY < nHeight; ++nY)
1130  {
1131  Scanline pScanline = pAcc->GetScanline(nY);
1132  Scanline pScanlineAlpha = pAlphaAcc->GetScanline(nY);
1133  for (long nX = 0; nX < nWidth; ++nX)
1134  {
1135  BitmapColor aBmpColor = pAcc->GetPixelFromData(pScanline, nX);
1136  aBmpColor.Merge(rBackgroundColor,
1137  255 - pAlphaAcc->GetIndexFromData(pScanlineAlpha, nX));
1138  pAcc->SetPixelOnData(pScanline, nX, aBmpColor);
1139  }
1140  }
1141 
1142  bRet = true;
1143  }
1144 
1145  return bRet;
1146 }
1147 
1148 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
long Width() const
Scanline GetBuffer() 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
sal_uInt16 GetBestPaletteIndex(const BitmapColor &rBitmapColor) const
Definition: bmpacc.cxx:81
Size GetSizePixel() const
long Width() const
void SetPalette(const BitmapPalette &rPalette)
ScanlineFormat
Definition: Scanline.hxx:28
void FillRect(const tools::Rectangle &rRect)
Definition: bmpacc3.cxx:236
long Right() const
bool CombineSimple(const Bitmap &rMask, BmpCombine eCombine)
Perform boolean operations with another bitmap.
bool Mirror(BmpMirrorFlags nMirrorFlags)
Mirror the bitmap.
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:66
sal_uInt16 GetEntryCount() const
sal_uInt8 * Scanline
Definition: Scanline.hxx:25
void SetTop(long v)
sal_uInt32 GetScanlineSize() const
bool HasPalette() const
ScanlineFormat GetScanlineFormat() const
int i
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.
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:226
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
void SetFillColor()
Definition: bmpacc3.cxx:44
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:78
SAL_DLLPRIVATE void ImplSetSalBitmap(const std::shared_ptr< SalBitmap > &xImpBmp)
sal_uInt8 GetIndexFromData(const sal_uInt8 *pData, long nX) const