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  // Historically LO used 1bpp masks, but 8bpp masks are much faster,
371  // better supported by hardware, and the memory savings are not worth
372  // it anymore.
373  // TODO: Possibly remove the 1bpp code later.
374  constexpr bool use8BitMask = true;
375 
376  if (!nTol && pReadAcc
379  && pReadAcc->GetBestMatchingColor(COL_WHITE) == pReadAcc->GetBestMatchingColor(rTransColor))
380  {
381  // if we're a 1 bit pixel already, and the transcolor matches the color that would replace it
382  // already, then just return a copy
383  return *this;
384  }
385 
386  Bitmap aNewBmp(GetSizePixel(), use8BitMask ? 8 : 1,
387  use8BitMask ? &Bitmap::GetGreyPalette(256) : nullptr);
388  BitmapScopedWriteAccess pWriteAcc(aNewBmp);
389  bool bRet = false;
390 
391  if (pWriteAcc && pReadAcc)
392  {
393  const long nWidth = pReadAcc->Width();
394  const long nHeight = pReadAcc->Height();
395  const BitmapColor aBlack(pWriteAcc->GetBestMatchingColor(COL_BLACK));
396  const BitmapColor aWhite(pWriteAcc->GetBestMatchingColor(COL_WHITE));
397 
398  if (!nTol)
399  {
400  const BitmapColor aTest(pReadAcc->GetBestMatchingColor(rTransColor));
401 
402  if (pWriteAcc->GetBitCount() == 1
405  {
406  // optimized for 4Bit-MSN/LSN source palette
407  const sal_uInt8 cTest = aTest.GetIndex();
408  const long nShiftInit
409  = ((pReadAcc->GetScanlineFormat() == ScanlineFormat::N4BitMsnPal) ? 4 : 0);
410 
412  && aWhite.GetIndex() == 1)
413  {
414  // optimized for 1Bit-MSB destination palette
415  for (long nY = 0; nY < nHeight; ++nY)
416  {
417  Scanline pSrc = pReadAcc->GetScanline(nY);
418  Scanline pDst = pWriteAcc->GetScanline(nY);
419  for (long nX = 0, nShift = nShiftInit; nX < nWidth; nX++, nShift ^= 4)
420  {
421  if (cTest == ((pSrc[nX >> 1] >> nShift) & 0x0f))
422  pDst[nX >> 3] |= 1 << (7 - (nX & 7));
423  else
424  pDst[nX >> 3] &= ~(1 << (7 - (nX & 7)));
425  }
426  }
427  }
428  else
429  {
430  for (long nY = 0; nY < nHeight; ++nY)
431  {
432  Scanline pSrc = pReadAcc->GetScanline(nY);
433  Scanline pDst = pWriteAcc->GetScanline(nY);
434  for (long nX = 0, nShift = nShiftInit; nX < nWidth; nX++, nShift ^= 4)
435  {
436  if (cTest == ((pSrc[nX >> 1] >> nShift) & 0x0f))
437  pWriteAcc->SetPixelOnData(pDst, nX, aWhite);
438  else
439  pWriteAcc->SetPixelOnData(pDst, nX, aBlack);
440  }
441  }
442  }
443  }
444  else if (pWriteAcc->GetBitCount() == 1
446  {
447  // optimized for 8Bit source palette
448  const sal_uInt8 cTest = aTest.GetIndex();
449 
451  && aWhite.GetIndex() == 1)
452  {
453  // optimized for 1Bit-MSB destination palette
454  for (long nY = 0; nY < nHeight; ++nY)
455  {
456  Scanline pSrc = pReadAcc->GetScanline(nY);
457  Scanline pDst = pWriteAcc->GetScanline(nY);
458  for (long nX = 0; nX < nWidth; ++nX)
459  {
460  if (cTest == pSrc[nX])
461  pDst[nX >> 3] |= 1 << (7 - (nX & 7));
462  else
463  pDst[nX >> 3] &= ~(1 << (7 - (nX & 7)));
464  }
465  }
466  }
467  else
468  {
469  for (long nY = 0; nY < nHeight; ++nY)
470  {
471  Scanline pSrc = pReadAcc->GetScanline(nY);
472  Scanline pDst = pWriteAcc->GetScanline(nY);
473  for (long nX = 0; nX < nWidth; ++nX)
474  {
475  if (cTest == pSrc[nX])
476  pWriteAcc->SetPixelOnData(pDst, nX, aWhite);
477  else
478  pWriteAcc->SetPixelOnData(pDst, nX, aBlack);
479  }
480  }
481  }
482  }
483  else if (pWriteAcc->GetScanlineFormat() == pReadAcc->GetScanlineFormat()
484  && aWhite.GetIndex() == 1
487  {
488  for (long nY = 0; nY < nHeight; ++nY)
489  {
490  Scanline pSrc = pReadAcc->GetScanline(nY);
491  Scanline pDst = pWriteAcc->GetScanline(nY);
492  assert(pWriteAcc->GetScanlineSize() == pReadAcc->GetScanlineSize());
493  const long nScanlineSize = pWriteAcc->GetScanlineSize();
494  for (long nX = 0; nX < nScanlineSize; ++nX)
495  pDst[nX] = ~pSrc[nX];
496  }
497  }
498  else if (use8BitMask && pWriteAcc->GetBitCount() == 8
500  {
501  // optimized for 8Bit source palette
502  const sal_uInt8 cTest = aTest.GetIndex();
503 
504  for (long nY = 0; nY < nHeight; ++nY)
505  {
506  Scanline pSrc = pReadAcc->GetScanline(nY);
507  Scanline pDst = pWriteAcc->GetScanline(nY);
508  for (long nX = 0; nX < nWidth; ++nX)
509  {
510  if (cTest == pSrc[nX])
511  pDst[nX] = aWhite.GetIndex();
512  else
513  pDst[nX] = aBlack.GetIndex();
514  }
515  }
516  }
517  else
518  {
519  // not optimized
520  for (long nY = 0; nY < nHeight; ++nY)
521  {
522  Scanline pScanline = pWriteAcc->GetScanline(nY);
523  Scanline pScanlineRead = pReadAcc->GetScanline(nY);
524  for (long nX = 0; nX < nWidth; ++nX)
525  {
526  if (aTest == pReadAcc->GetPixelFromData(pScanlineRead, nX))
527  pWriteAcc->SetPixelOnData(pScanline, nX, aWhite);
528  else
529  pWriteAcc->SetPixelOnData(pScanline, nX, aBlack);
530  }
531  }
532  }
533  }
534  else
535  {
536  BitmapColor aCol;
537  long nR, nG, nB;
538  const long nMinR = MinMax<long>(rTransColor.GetRed() - nTol, 0, 255);
539  const long nMaxR = MinMax<long>(rTransColor.GetRed() + nTol, 0, 255);
540  const long nMinG = MinMax<long>(rTransColor.GetGreen() - nTol, 0, 255);
541  const long nMaxG = MinMax<long>(rTransColor.GetGreen() + nTol, 0, 255);
542  const long nMinB = MinMax<long>(rTransColor.GetBlue() - nTol, 0, 255);
543  const long nMaxB = MinMax<long>(rTransColor.GetBlue() + nTol, 0, 255);
544 
545  if (pReadAcc->HasPalette())
546  {
547  for (long nY = 0; nY < nHeight; nY++)
548  {
549  Scanline pScanline = pWriteAcc->GetScanline(nY);
550  Scanline pScanlineRead = pReadAcc->GetScanline(nY);
551  for (long nX = 0; nX < nWidth; nX++)
552  {
553  aCol = pReadAcc->GetPaletteColor(
554  pReadAcc->GetIndexFromData(pScanlineRead, nX));
555  nR = aCol.GetRed();
556  nG = aCol.GetGreen();
557  nB = aCol.GetBlue();
558 
559  if (nMinR <= nR && nMaxR >= nR && nMinG <= nG && nMaxG >= nG && nMinB <= nB
560  && nMaxB >= nB)
561  {
562  pWriteAcc->SetPixelOnData(pScanline, nX, aWhite);
563  }
564  else
565  {
566  pWriteAcc->SetPixelOnData(pScanline, nX, aBlack);
567  }
568  }
569  }
570  }
571  else
572  {
573  for (long nY = 0; nY < nHeight; nY++)
574  {
575  Scanline pScanline = pWriteAcc->GetScanline(nY);
576  Scanline pScanlineRead = pReadAcc->GetScanline(nY);
577  for (long nX = 0; nX < nWidth; nX++)
578  {
579  aCol = pReadAcc->GetPixelFromData(pScanlineRead, nX);
580  nR = aCol.GetRed();
581  nG = aCol.GetGreen();
582  nB = aCol.GetBlue();
583 
584  if (nMinR <= nR && nMaxR >= nR && nMinG <= nG && nMaxG >= nG && nMinB <= nB
585  && nMaxB >= nB)
586  {
587  pWriteAcc->SetPixelOnData(pScanline, nX, aWhite);
588  }
589  else
590  {
591  pWriteAcc->SetPixelOnData(pScanline, nX, aBlack);
592  }
593  }
594  }
595  }
596  }
597 
598  bRet = true;
599  }
600 
601  pWriteAcc.reset();
602  pReadAcc.reset();
603 
604  if (bRet)
605  {
606  aNewBmp.maPrefSize = maPrefSize;
607  aNewBmp.maPrefMapMode = maPrefMapMode;
608  }
609  else
610  aNewBmp = Bitmap();
611 
612  return aNewBmp;
613 }
614 
615 vcl::Region Bitmap::CreateRegion(const Color& rColor, const tools::Rectangle& rRect) const
616 {
617  vcl::Region aRegion;
618  tools::Rectangle aRect(rRect);
619  ScopedReadAccess pReadAcc(const_cast<Bitmap&>(*this));
620 
622  aRect.Justify();
623 
624  if (pReadAcc)
625  {
626  const long nLeft = aRect.Left();
627  const long nTop = aRect.Top();
628  const long nRight = aRect.Right();
629  const long nBottom = aRect.Bottom();
630  const BitmapColor aMatch(pReadAcc->GetBestMatchingColor(rColor));
631 
632  std::vector<long> aLine;
633  long nYStart(nTop);
634  long nY(nTop);
635 
636  for (; nY <= nBottom; nY++)
637  {
638  std::vector<long> aNewLine;
639  long nX(nLeft);
640  Scanline pScanlineRead = pReadAcc->GetScanline(nY);
641 
642  for (; nX <= nRight;)
643  {
644  while ((nX <= nRight) && (aMatch != pReadAcc->GetPixelFromData(pScanlineRead, nX)))
645  nX++;
646 
647  if (nX <= nRight)
648  {
649  aNewLine.push_back(nX);
650 
651  while ((nX <= nRight)
652  && (aMatch == pReadAcc->GetPixelFromData(pScanlineRead, nX)))
653  {
654  nX++;
655  }
656 
657  aNewLine.push_back(nX - 1);
658  }
659  }
660 
661  if (aNewLine != aLine)
662  {
663  // need to write aLine, it's different from the next line
664  if (!aLine.empty())
665  {
666  tools::Rectangle aSubRect;
667 
668  // enter y values and proceed ystart
669  aSubRect.SetTop(nYStart);
670  aSubRect.SetBottom(nY ? nY - 1 : 0);
671 
672  for (size_t a(0); a < aLine.size();)
673  {
674  aSubRect.SetLeft(aLine[a++]);
675  aSubRect.SetRight(aLine[a++]);
676  aRegion.Union(aSubRect);
677  }
678  }
679 
680  // copy line as new line
681  aLine = aNewLine;
682  nYStart = nY;
683  }
684  }
685 
686  // write last line if used
687  if (!aLine.empty())
688  {
689  tools::Rectangle aSubRect;
690 
691  // enter y values
692  aSubRect.SetTop(nYStart);
693  aSubRect.SetBottom(nY ? nY - 1 : 0);
694 
695  for (size_t a(0); a < aLine.size();)
696  {
697  aSubRect.SetLeft(aLine[a++]);
698  aSubRect.SetRight(aLine[a++]);
699  aRegion.Union(aSubRect);
700  }
701  }
702 
703  pReadAcc.reset();
704  }
705  else
706  {
707  aRegion = aRect;
708  }
709 
710  return aRegion;
711 }
712 
713 bool Bitmap::Replace(const Bitmap& rMask, const Color& rReplaceColor)
714 {
715  ScopedReadAccess pMaskAcc(const_cast<Bitmap&>(rMask));
716  BitmapScopedWriteAccess pAcc(*this);
717  bool bRet = false;
718 
719  if (pMaskAcc && pAcc)
720  {
721  const long nWidth = std::min(pMaskAcc->Width(), pAcc->Width());
722  const long nHeight = std::min(pMaskAcc->Height(), pAcc->Height());
723  const BitmapColor aMaskWhite(pMaskAcc->GetBestMatchingColor(COL_WHITE));
724  BitmapColor aReplace;
725 
726  if (pAcc->HasPalette())
727  {
728  const sal_uInt16 nActColors = pAcc->GetPaletteEntryCount();
729  const sal_uInt16 nMaxColors = 1 << pAcc->GetBitCount();
730 
731  // default to the nearest color
732  aReplace = pAcc->GetBestMatchingColor(rReplaceColor);
733 
734  // for paletted images without a matching palette entry
735  // look for an unused palette entry (NOTE: expensive!)
736  if (pAcc->GetPaletteColor(aReplace.GetIndex()) != BitmapColor(rReplaceColor))
737  {
738  // if the palette has empty entries use the last one
739  if (nActColors < nMaxColors)
740  {
741  pAcc->SetPaletteEntryCount(nActColors + 1);
742  pAcc->SetPaletteColor(nActColors, rReplaceColor);
743  aReplace = BitmapColor(static_cast<sal_uInt8>(nActColors));
744  }
745  else
746  {
747  std::unique_ptr<bool[]> pFlags(new bool[nMaxColors]);
748 
749  // Set all entries to false
750  std::fill(pFlags.get(), pFlags.get() + nMaxColors, false);
751 
752  for (long nY = 0; nY < nHeight; nY++)
753  {
754  Scanline pScanline = pAcc->GetScanline(nY);
755  for (long nX = 0; nX < nWidth; nX++)
756  pFlags[pAcc->GetIndexFromData(pScanline, nX)] = true;
757  }
758 
759  for (sal_uInt16 i = 0; i < nMaxColors; i++)
760  {
761  // Hurray, we do have an unused entry
762  if (!pFlags[i])
763  {
764  pAcc->SetPaletteColor(i, rReplaceColor);
765  aReplace = BitmapColor(static_cast<sal_uInt8>(i));
766  }
767  }
768  }
769  }
770  }
771  else
772  aReplace = rReplaceColor;
773 
774  for (long nY = 0; nY < nHeight; nY++)
775  {
776  Scanline pScanline = pAcc->GetScanline(nY);
777  Scanline pScanlineMask = pMaskAcc->GetScanline(nY);
778  for (long nX = 0; nX < nWidth; nX++)
779  {
780  if (pMaskAcc->GetPixelFromData(pScanlineMask, nX) == aMaskWhite)
781  pAcc->SetPixelOnData(pScanline, nX, aReplace);
782  }
783  }
784 
785  bRet = true;
786  }
787 
788  return bRet;
789 }
790 
791 bool Bitmap::Replace(const AlphaMask& rAlpha, const Color& rMergeColor)
792 {
793  Bitmap aNewBmp(GetSizePixel(), 24);
794  ScopedReadAccess pAcc(*this);
795  AlphaMask::ScopedReadAccess pAlphaAcc(const_cast<AlphaMask&>(rAlpha));
796  BitmapScopedWriteAccess pNewAcc(aNewBmp);
797  bool bRet = false;
798 
799  if (pAcc && pAlphaAcc && pNewAcc)
800  {
801  BitmapColor aCol;
802  const long nWidth = std::min(pAlphaAcc->Width(), pAcc->Width());
803  const long nHeight = std::min(pAlphaAcc->Height(), pAcc->Height());
804 
805  for (long nY = 0; nY < nHeight; nY++)
806  {
807  Scanline pScanline = pNewAcc->GetScanline(nY);
808  Scanline pScanlineAlpha = pAlphaAcc->GetScanline(nY);
809  for (long nX = 0; nX < nWidth; nX++)
810  {
811  aCol = pAcc->GetColor(nY, nX);
812  aCol.Merge(rMergeColor, 255 - pAlphaAcc->GetIndexFromData(pScanlineAlpha, nX));
813  pNewAcc->SetPixelOnData(pScanline, nX, aCol);
814  }
815  }
816 
817  bRet = true;
818  }
819 
820  pAcc.reset();
821  pAlphaAcc.reset();
822  pNewAcc.reset();
823 
824  if (bRet)
825  {
826  const MapMode aMap(maPrefMapMode);
827  const Size aSize(maPrefSize);
828 
829  *this = aNewBmp;
830 
831  maPrefMapMode = aMap;
832  maPrefSize = aSize;
833  }
834 
835  return bRet;
836 }
837 
838 bool Bitmap::Replace(const Color& rSearchColor, const Color& rReplaceColor, sal_uInt8 nTol)
839 {
840  if (mxSalBmp)
841  {
842  // implementation specific replace
843  std::shared_ptr<SalBitmap> xImpBmp(ImplGetSVData()->mpDefInst->CreateSalBitmap());
844  if (xImpBmp->Create(*mxSalBmp) && xImpBmp->Replace(rSearchColor, rReplaceColor, nTol))
845  {
846  ImplSetSalBitmap(xImpBmp);
847  maPrefMapMode = MapMode(MapUnit::MapPixel);
848  maPrefSize = xImpBmp->GetSize();
849  return true;
850  }
851  }
852 
853  // Bitmaps with 1 bit color depth can cause problems if they have other entries than black/white
854  // in their palette
855  if (GetBitCount() == 1)
857 
858  BitmapScopedWriteAccess pAcc(*this);
859  bool bRet = false;
860 
861  if (pAcc)
862  {
863  const long nMinR = MinMax<long>(rSearchColor.GetRed() - nTol, 0, 255);
864  const long nMaxR = MinMax<long>(rSearchColor.GetRed() + nTol, 0, 255);
865  const long nMinG = MinMax<long>(rSearchColor.GetGreen() - nTol, 0, 255);
866  const long nMaxG = MinMax<long>(rSearchColor.GetGreen() + nTol, 0, 255);
867  const long nMinB = MinMax<long>(rSearchColor.GetBlue() - nTol, 0, 255);
868  const long nMaxB = MinMax<long>(rSearchColor.GetBlue() + nTol, 0, 255);
869 
870  if (pAcc->HasPalette())
871  {
872  for (sal_uInt16 i = 0, nPalCount = pAcc->GetPaletteEntryCount(); i < nPalCount; i++)
873  {
874  const BitmapColor& rCol = pAcc->GetPaletteColor(i);
875 
876  if (nMinR <= rCol.GetRed() && nMaxR >= rCol.GetRed() && nMinG <= rCol.GetGreen()
877  && nMaxG >= rCol.GetGreen() && nMinB <= rCol.GetBlue()
878  && nMaxB >= rCol.GetBlue())
879  {
880  pAcc->SetPaletteColor(i, rReplaceColor);
881  }
882  }
883  }
884  else
885  {
886  BitmapColor aCol;
887  const BitmapColor aReplace(pAcc->GetBestMatchingColor(rReplaceColor));
888 
889  for (long nY = 0, nHeight = pAcc->Height(); nY < nHeight; nY++)
890  {
891  Scanline pScanline = pAcc->GetScanline(nY);
892  for (long nX = 0, nWidth = pAcc->Width(); nX < nWidth; nX++)
893  {
894  aCol = pAcc->GetPixelFromData(pScanline, nX);
895 
896  if (nMinR <= aCol.GetRed() && nMaxR >= aCol.GetRed() && nMinG <= aCol.GetGreen()
897  && nMaxG >= aCol.GetGreen() && nMinB <= aCol.GetBlue()
898  && nMaxB >= aCol.GetBlue())
899  {
900  pAcc->SetPixelOnData(pScanline, nX, aReplace);
901  }
902  }
903  }
904  }
905 
906  pAcc.reset();
907  bRet = true;
908  }
909 
910  return bRet;
911 }
912 
913 bool Bitmap::Replace(const Color* pSearchColors, const Color* pReplaceColors, sal_uLong nColorCount,
914  sal_uInt8 const* pTols)
915 {
916  // Bitmaps with 1 bit color depth can cause problems if they have other entries than black/white
917  // in their palette
918  if (GetBitCount() == 1)
920 
921  BitmapScopedWriteAccess pAcc(*this);
922  bool bRet = false;
923 
924  if (pAcc)
925  {
926  std::unique_ptr<long[]> pMinR(new long[nColorCount]);
927  std::unique_ptr<long[]> pMaxR(new long[nColorCount]);
928  std::unique_ptr<long[]> pMinG(new long[nColorCount]);
929  std::unique_ptr<long[]> pMaxG(new long[nColorCount]);
930  std::unique_ptr<long[]> pMinB(new long[nColorCount]);
931  std::unique_ptr<long[]> pMaxB(new long[nColorCount]);
932 
933  if (pTols)
934  {
935  for (sal_uLong i = 0; i < nColorCount; i++)
936  {
937  const Color& rCol = pSearchColors[i];
938  const sal_uInt8 nTol = pTols[i];
939 
940  pMinR[i] = MinMax<long>(rCol.GetRed() - nTol, 0, 255);
941  pMaxR[i] = MinMax<long>(rCol.GetRed() + nTol, 0, 255);
942  pMinG[i] = MinMax<long>(rCol.GetGreen() - nTol, 0, 255);
943  pMaxG[i] = MinMax<long>(rCol.GetGreen() + nTol, 0, 255);
944  pMinB[i] = MinMax<long>(rCol.GetBlue() - nTol, 0, 255);
945  pMaxB[i] = MinMax<long>(rCol.GetBlue() + nTol, 0, 255);
946  }
947  }
948  else
949  {
950  for (sal_uLong i = 0; i < nColorCount; i++)
951  {
952  const Color& rCol = pSearchColors[i];
953 
954  pMinR[i] = rCol.GetRed();
955  pMaxR[i] = rCol.GetRed();
956  pMinG[i] = rCol.GetGreen();
957  pMaxG[i] = rCol.GetGreen();
958  pMinB[i] = rCol.GetBlue();
959  pMaxB[i] = rCol.GetBlue();
960  }
961  }
962 
963  if (pAcc->HasPalette())
964  {
965  for (sal_uInt16 nEntry = 0, nPalCount = pAcc->GetPaletteEntryCount();
966  nEntry < nPalCount; nEntry++)
967  {
968  const BitmapColor& rCol = pAcc->GetPaletteColor(nEntry);
969 
970  for (sal_uLong i = 0; i < nColorCount; i++)
971  {
972  if (pMinR[i] <= rCol.GetRed() && pMaxR[i] >= rCol.GetRed()
973  && pMinG[i] <= rCol.GetGreen() && pMaxG[i] >= rCol.GetGreen()
974  && pMinB[i] <= rCol.GetBlue() && pMaxB[i] >= rCol.GetBlue())
975  {
976  pAcc->SetPaletteColor(nEntry, pReplaceColors[i]);
977  break;
978  }
979  }
980  }
981  }
982  else
983  {
984  BitmapColor aCol;
985  std::unique_ptr<BitmapColor[]> pReplaces(new BitmapColor[nColorCount]);
986 
987  for (sal_uLong i = 0; i < nColorCount; i++)
988  pReplaces[i] = pAcc->GetBestMatchingColor(pReplaceColors[i]);
989 
990  for (long nY = 0, nHeight = pAcc->Height(); nY < nHeight; nY++)
991  {
992  Scanline pScanline = pAcc->GetScanline(nY);
993  for (long nX = 0, nWidth = pAcc->Width(); nX < nWidth; nX++)
994  {
995  aCol = pAcc->GetPixelFromData(pScanline, nX);
996 
997  for (sal_uLong i = 0; i < nColorCount; i++)
998  {
999  if (pMinR[i] <= aCol.GetRed() && pMaxR[i] >= aCol.GetRed()
1000  && pMinG[i] <= aCol.GetGreen() && pMaxG[i] >= aCol.GetGreen()
1001  && pMinB[i] <= aCol.GetBlue() && pMaxB[i] >= aCol.GetBlue())
1002  {
1003  pAcc->SetPixelOnData(pScanline, nX, pReplaces[i]);
1004  break;
1005  }
1006  }
1007  }
1008  }
1009  }
1010 
1011  pAcc.reset();
1012  bRet = true;
1013  }
1014 
1015  return bRet;
1016 }
1017 
1018 bool Bitmap::CombineSimple(const Bitmap& rMask, BmpCombine eCombine)
1019 {
1020  ScopedReadAccess pMaskAcc(const_cast<Bitmap&>(rMask));
1021  BitmapScopedWriteAccess pAcc(*this);
1022  bool bRet = false;
1023 
1024  if (pMaskAcc && pAcc)
1025  {
1026  const long nWidth = std::min(pMaskAcc->Width(), pAcc->Width());
1027  const long nHeight = std::min(pMaskAcc->Height(), pAcc->Height());
1028  const Color aColBlack(COL_BLACK);
1029  const BitmapColor aWhite(pAcc->GetBestMatchingColor(COL_WHITE));
1030  const BitmapColor aBlack(pAcc->GetBestMatchingColor(aColBlack));
1031  const BitmapColor aMaskBlack(pMaskAcc->GetBestMatchingColor(aColBlack));
1032 
1033  switch (eCombine)
1034  {
1035  case BmpCombine::And:
1036  {
1037  for (long nY = 0; nY < nHeight; nY++)
1038  {
1039  Scanline pScanline = pAcc->GetScanline(nY);
1040  Scanline pScanlineMask = pMaskAcc->GetScanline(nY);
1041  for (long nX = 0; nX < nWidth; nX++)
1042  {
1043  if (pMaskAcc->GetPixelFromData(pScanlineMask, nX) != aMaskBlack
1044  && pAcc->GetPixelFromData(pScanline, nX) != aBlack)
1045  {
1046  pAcc->SetPixelOnData(pScanline, nX, aWhite);
1047  }
1048  else
1049  {
1050  pAcc->SetPixelOnData(pScanline, nX, aBlack);
1051  }
1052  }
1053  }
1054  }
1055  break;
1056 
1057  case BmpCombine::Or:
1058  {
1059  for (long nY = 0; nY < nHeight; nY++)
1060  {
1061  Scanline pScanline = pAcc->GetScanline(nY);
1062  Scanline pScanlineMask = pMaskAcc->GetScanline(nY);
1063  for (long nX = 0; nX < nWidth; nX++)
1064  {
1065  if (pMaskAcc->GetPixelFromData(pScanlineMask, nX) != aMaskBlack
1066  || pAcc->GetPixelFromData(pScanline, nX) != aBlack)
1067  {
1068  pAcc->SetPixelOnData(pScanline, nX, aWhite);
1069  }
1070  else
1071  {
1072  pAcc->SetPixelOnData(pScanline, nX, aBlack);
1073  }
1074  }
1075  }
1076  }
1077  break;
1078  }
1079 
1080  bRet = true;
1081  }
1082 
1083  return bRet;
1084 }
1085 
1086 // TODO: Have a look at OutputDevice::ImplDrawAlpha() for some
1087 // optimizations. Might even consolidate the code here and there.
1088 bool Bitmap::Blend(const AlphaMask& rAlpha, const Color& rBackgroundColor)
1089 {
1090  // Convert to a truecolor bitmap, if we're a paletted one. There's room for tradeoff decision here,
1091  // maybe later for an overload (or a flag)
1092  if (GetBitCount() <= 8)
1094 
1095  AlphaMask::ScopedReadAccess pAlphaAcc(const_cast<AlphaMask&>(rAlpha));
1096 
1097  BitmapScopedWriteAccess pAcc(*this);
1098  bool bRet = false;
1099 
1100  if (pAlphaAcc && pAcc)
1101  {
1102  const long nWidth = std::min(pAlphaAcc->Width(), pAcc->Width());
1103  const long nHeight = std::min(pAlphaAcc->Height(), pAcc->Height());
1104 
1105  for (long nY = 0; nY < nHeight; ++nY)
1106  {
1107  Scanline pScanline = pAcc->GetScanline(nY);
1108  Scanline pScanlineAlpha = pAlphaAcc->GetScanline(nY);
1109  for (long nX = 0; nX < nWidth; ++nX)
1110  {
1111  BitmapColor aBmpColor = pAcc->GetPixelFromData(pScanline, nX);
1112  aBmpColor.Merge(rBackgroundColor,
1113  255 - pAlphaAcc->GetIndexFromData(pScanlineAlpha, nX));
1114  pAcc->SetPixelOnData(pScanline, nX, aBmpColor);
1115  }
1116  }
1117 
1118  bRet = true;
1119  }
1120 
1121  return bRet;
1122 }
1123 
1124 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
long Width() const
sal_uInt8 GetIndex() const
Definition: BitmapColor.hxx:61
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 ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_WHITE
const BitmapPalette & GetPalette() const
void SetBottom(long v)
bool IsEmpty() const
Definition: bitmap.hxx:551
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_BLACK
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
static const BitmapPalette & GetGreyPalette(int nEntries)
SAL_DLLPRIVATE void ImplSetSalBitmap(const std::shared_ptr< SalBitmap > &xImpBmp)
sal_uInt8 GetIndexFromData(const sal_uInt8 *pData, long nX) const