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 tools::Long nWidth = pAcc->Width();
86  const tools::Long nHeight = pAcc->Height();
87 
88  for (tools::Long nY = 0; nY < nHeight; nY++)
89  {
90  Scanline pScanline = pAcc->GetScanline(nY);
91  for (tools::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 tools::Long nWidth = pAcc->Width();
121  const tools::Long nHeight = pAcc->Height();
122  const tools::Long nWidth1 = nWidth - 1;
123  const tools::Long nWidth_2 = nWidth >> 1;
124 
125  for (tools::Long nY = 0; nY < nHeight; nY++)
126  {
127  Scanline pScanline = pAcc->GetScanline(nY);
128  for (tools::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 tools::Long nScanSize = pAcc->GetScanlineSize();
148  std::unique_ptr<sal_uInt8[]> pBuffer(new sal_uInt8[nScanSize]);
149  const tools::Long nHeight = pAcc->Height();
150  const tools::Long nHeight1 = nHeight - 1;
151  const tools::Long nHeight_2 = nHeight >> 1;
152 
153  for (tools::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 tools::Long nWidth = pAcc->Width();
171  const tools::Long nWidth1 = nWidth - 1;
172  const tools::Long nHeight = pAcc->Height();
173  tools::Long nHeight_2 = nHeight >> 1;
174 
175  for (tools::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 (tools::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 (tools::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(Degree10 nAngle10, const Color& rFillColor)
213 {
214  bool bRet = false;
215 
216  nAngle10 %= Degree10(3600);
217  nAngle10 = (nAngle10 < Degree10(0)) ? (Degree10(3599) + nAngle10) : nAngle10;
218 
219  if (!nAngle10)
220  bRet = true;
221  else if (nAngle10 == Degree10(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 == Degree10(900) || nAngle10 == Degree10(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 tools::Long nWidth = aSizePix.Width();
241  const tools::Long nWidth1 = nWidth - 1;
242  const tools::Long nHeight = aSizePix.Height();
243  const tools::Long nHeight1 = nHeight - 1;
244  const tools::Long nNewWidth = aNewSizePix.Width();
245  const tools::Long nNewHeight = aNewSizePix.Height();
246 
247  if (nAngle10 == Degree10(900))
248  {
249  for (tools::Long nY = 0, nOtherX = nWidth1; nY < nNewHeight;
250  nY++, nOtherX--)
251  {
252  Scanline pScanline = pWriteAcc->GetScanline(nY);
253  for (tools::Long nX = 0, nOtherY = 0; nX < nNewWidth; nX++)
254  {
255  pWriteAcc->SetPixelOnData(pScanline, nX,
256  pReadAcc->GetPixel(nOtherY++, nOtherX));
257  }
258  }
259  }
260  else if (nAngle10 == Degree10(2700))
261  {
262  for (tools::Long nY = 0, nOtherX = 0; nY < nNewHeight; nY++, nOtherX++)
263  {
264  Scanline pScanline = pWriteAcc->GetScanline(nY);
265  for (tools::Long nX = 0, nOtherY = nHeight1; nX < nNewWidth; nX++)
266  {
267  pWriteAcc->SetPixelOnData(pScanline, nX,
268  pReadAcc->GetPixel(nOtherY--, nOtherX));
269  }
270  }
271  }
272 
273  pWriteAcc.reset();
274  }
275 
276  aRotatedBmp = aNewBmp;
277  }
278  else
279  {
280  Point aTmpPoint;
281  tools::Rectangle aTmpRectangle(aTmpPoint, aSizePix);
282  tools::Polygon aPoly(aTmpRectangle);
283  aPoly.Rotate(aTmpPoint, nAngle10);
284 
285  tools::Rectangle aNewBound(aPoly.GetBoundRect());
286  const Size aNewSizePix(aNewBound.GetSize());
287  Bitmap aNewBmp(aNewSizePix, GetBitCount(), &pReadAcc->GetPalette());
288  BitmapScopedWriteAccess pWriteAcc(aNewBmp);
289 
290  if (pWriteAcc)
291  {
292  const BitmapColor aFillColor(pWriteAcc->GetBestMatchingColor(rFillColor));
293  const double fCosAngle = cos(nAngle10.get() * F_PI1800);
294  const double fSinAngle = sin(nAngle10.get() * F_PI1800);
295  const double fXMin = aNewBound.Left();
296  const double fYMin = aNewBound.Top();
297  const tools::Long nWidth = aSizePix.Width();
298  const tools::Long nHeight = aSizePix.Height();
299  const tools::Long nNewWidth = aNewSizePix.Width();
300  const tools::Long nNewHeight = aNewSizePix.Height();
301  tools::Long nX;
302  tools::Long nY;
303  tools::Long nRotX;
304  tools::Long nRotY;
305  std::unique_ptr<long[]> pCosX(new long[nNewWidth]);
306  std::unique_ptr<long[]> pSinX(new long[nNewWidth]);
307  std::unique_ptr<long[]> pCosY(new long[nNewHeight]);
308  std::unique_ptr<long[]> pSinY(new long[nNewHeight]);
309 
310  for (nX = 0; nX < nNewWidth; nX++)
311  {
312  const double fTmp = (fXMin + nX) * 64.;
313 
314  pCosX[nX] = FRound(fCosAngle * fTmp);
315  pSinX[nX] = FRound(fSinAngle * fTmp);
316  }
317 
318  for (nY = 0; nY < nNewHeight; nY++)
319  {
320  const double fTmp = (fYMin + nY) * 64.;
321 
322  pCosY[nY] = FRound(fCosAngle * fTmp);
323  pSinY[nY] = FRound(fSinAngle * fTmp);
324  }
325 
326  for (nY = 0; nY < nNewHeight; nY++)
327  {
328  tools::Long nSinY = pSinY[nY];
329  tools::Long nCosY = pCosY[nY];
330  Scanline pScanline = pWriteAcc->GetScanline(nY);
331 
332  for (nX = 0; nX < nNewWidth; nX++)
333  {
334  nRotX = (pCosX[nX] - nSinY) >> 6;
335  nRotY = (pSinX[nX] + nCosY) >> 6;
336 
337  if ((nRotX > -1) && (nRotX < nWidth) && (nRotY > -1)
338  && (nRotY < nHeight))
339  {
340  pWriteAcc->SetPixelOnData(pScanline, nX,
341  pReadAcc->GetPixel(nRotY, nRotX));
342  }
343  else
344  {
345  pWriteAcc->SetPixelOnData(pScanline, nX, aFillColor);
346  }
347  }
348  }
349 
350  pWriteAcc.reset();
351  }
352 
353  aRotatedBmp = aNewBmp;
354  }
355 
356  pReadAcc.reset();
357  }
358 
359  bRet = !!aRotatedBmp;
360  if (bRet)
361  ReassignWithSize(aRotatedBmp);
362  }
363 
364  return bRet;
365 };
366 
367 Bitmap Bitmap::CreateMask(const Color& rTransColor, sal_uInt8 nTol) const
368 {
369  ScopedReadAccess pReadAcc(const_cast<Bitmap&>(*this));
370 
371  // Historically LO used 1bpp masks, but 8bpp masks are much faster,
372  // better supported by hardware, and the memory savings are not worth
373  // it anymore.
374  // TODO: Possibly remove the 1bpp code later.
375  constexpr bool use8BitMask = true;
376 
377  if (!nTol && pReadAcc
380  && pReadAcc->GetBestMatchingColor(COL_WHITE) == pReadAcc->GetBestMatchingColor(rTransColor))
381  {
382  // if we're a 1 bit pixel already, and the transcolor matches the color that would replace it
383  // already, then just return a copy
384  return *this;
385  }
386 
387  Bitmap aNewBmp(GetSizePixel(), use8BitMask ? 8 : 1,
388  use8BitMask ? &Bitmap::GetGreyPalette(256) : nullptr);
389  BitmapScopedWriteAccess pWriteAcc(aNewBmp);
390  bool bRet = false;
391 
392  if (pWriteAcc && pReadAcc)
393  {
394  const tools::Long nWidth = pReadAcc->Width();
395  const tools::Long nHeight = pReadAcc->Height();
396  const BitmapColor aBlack(pWriteAcc->GetBestMatchingColor(COL_BLACK));
397  const BitmapColor aWhite(pWriteAcc->GetBestMatchingColor(COL_WHITE));
398 
399  if (!nTol)
400  {
401  const BitmapColor aTest(pReadAcc->GetBestMatchingColor(rTransColor));
402 
403  if (pWriteAcc->GetBitCount() == 1
406  {
407  // optimized for 4Bit-MSN/LSN source palette
408  const sal_uInt8 cTest = aTest.GetIndex();
409  const tools::Long nShiftInit
410  = ((pReadAcc->GetScanlineFormat() == ScanlineFormat::N4BitMsnPal) ? 4 : 0);
411 
413  && aWhite.GetIndex() == 1)
414  {
415  // optimized for 1Bit-MSB destination palette
416  for (tools::Long nY = 0; nY < nHeight; ++nY)
417  {
418  Scanline pSrc = pReadAcc->GetScanline(nY);
419  Scanline pDst = pWriteAcc->GetScanline(nY);
420  for (tools::Long nX = 0, nShift = nShiftInit; nX < nWidth;
421  nX++, nShift ^= 4)
422  {
423  if (cTest == ((pSrc[nX >> 1] >> nShift) & 0x0f))
424  pDst[nX >> 3] |= 1 << (7 - (nX & 7));
425  else
426  pDst[nX >> 3] &= ~(1 << (7 - (nX & 7)));
427  }
428  }
429  }
430  else
431  {
432  for (tools::Long nY = 0; nY < nHeight; ++nY)
433  {
434  Scanline pSrc = pReadAcc->GetScanline(nY);
435  Scanline pDst = pWriteAcc->GetScanline(nY);
436  for (tools::Long nX = 0, nShift = nShiftInit; nX < nWidth;
437  nX++, nShift ^= 4)
438  {
439  if (cTest == ((pSrc[nX >> 1] >> nShift) & 0x0f))
440  pWriteAcc->SetPixelOnData(pDst, nX, aWhite);
441  else
442  pWriteAcc->SetPixelOnData(pDst, nX, aBlack);
443  }
444  }
445  }
446  }
447  else if (pWriteAcc->GetBitCount() == 1
449  {
450  // optimized for 8Bit source palette
451  const sal_uInt8 cTest = aTest.GetIndex();
452 
454  && aWhite.GetIndex() == 1)
455  {
456  // optimized for 1Bit-MSB destination palette
457  for (tools::Long nY = 0; nY < nHeight; ++nY)
458  {
459  Scanline pSrc = pReadAcc->GetScanline(nY);
460  Scanline pDst = pWriteAcc->GetScanline(nY);
461  for (tools::Long nX = 0; nX < nWidth; ++nX)
462  {
463  if (cTest == pSrc[nX])
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 (tools::Long nY = 0; nY < nHeight; ++nY)
473  {
474  Scanline pSrc = pReadAcc->GetScanline(nY);
475  Scanline pDst = pWriteAcc->GetScanline(nY);
476  for (tools::Long nX = 0; nX < nWidth; ++nX)
477  {
478  if (cTest == pSrc[nX])
479  pWriteAcc->SetPixelOnData(pDst, nX, aWhite);
480  else
481  pWriteAcc->SetPixelOnData(pDst, nX, aBlack);
482  }
483  }
484  }
485  }
486  else if (pWriteAcc->GetScanlineFormat() == pReadAcc->GetScanlineFormat()
487  && aWhite.GetIndex() == 1
490  {
491  for (tools::Long nY = 0; nY < nHeight; ++nY)
492  {
493  Scanline pSrc = pReadAcc->GetScanline(nY);
494  Scanline pDst = pWriteAcc->GetScanline(nY);
495  assert(pWriteAcc->GetScanlineSize() == pReadAcc->GetScanlineSize());
496  const tools::Long nScanlineSize = pWriteAcc->GetScanlineSize();
497  for (tools::Long nX = 0; nX < nScanlineSize; ++nX)
498  pDst[nX] = ~pSrc[nX];
499  }
500  }
501  else if (use8BitMask && pWriteAcc->GetBitCount() == 8
503  {
504  // optimized for 8Bit source palette
505  const sal_uInt8 cTest = aTest.GetIndex();
506 
507  for (tools::Long nY = 0; nY < nHeight; ++nY)
508  {
509  Scanline pSrc = pReadAcc->GetScanline(nY);
510  Scanline pDst = pWriteAcc->GetScanline(nY);
511  for (tools::Long nX = 0; nX < nWidth; ++nX)
512  {
513  if (cTest == pSrc[nX])
514  pDst[nX] = aWhite.GetIndex();
515  else
516  pDst[nX] = aBlack.GetIndex();
517  }
518  }
519  }
520  else
521  {
522  // not optimized
523  for (tools::Long nY = 0; nY < nHeight; ++nY)
524  {
525  Scanline pScanline = pWriteAcc->GetScanline(nY);
526  Scanline pScanlineRead = pReadAcc->GetScanline(nY);
527  for (tools::Long nX = 0; nX < nWidth; ++nX)
528  {
529  if (aTest == pReadAcc->GetPixelFromData(pScanlineRead, nX))
530  pWriteAcc->SetPixelOnData(pScanline, nX, aWhite);
531  else
532  pWriteAcc->SetPixelOnData(pScanline, nX, aBlack);
533  }
534  }
535  }
536  }
537  else
538  {
539  BitmapColor aCol;
540  tools::Long nR, nG, nB;
541  const tools::Long nMinR = MinMax<tools::Long>(rTransColor.GetRed() - nTol, 0, 255);
542  const tools::Long nMaxR = MinMax<tools::Long>(rTransColor.GetRed() + nTol, 0, 255);
543  const tools::Long nMinG = MinMax<tools::Long>(rTransColor.GetGreen() - nTol, 0, 255);
544  const tools::Long nMaxG = MinMax<tools::Long>(rTransColor.GetGreen() + nTol, 0, 255);
545  const tools::Long nMinB = MinMax<tools::Long>(rTransColor.GetBlue() - nTol, 0, 255);
546  const tools::Long nMaxB = MinMax<tools::Long>(rTransColor.GetBlue() + nTol, 0, 255);
547 
548  if (pReadAcc->HasPalette())
549  {
550  for (tools::Long nY = 0; nY < nHeight; nY++)
551  {
552  Scanline pScanline = pWriteAcc->GetScanline(nY);
553  Scanline pScanlineRead = pReadAcc->GetScanline(nY);
554  for (tools::Long nX = 0; nX < nWidth; nX++)
555  {
556  aCol = pReadAcc->GetPaletteColor(
557  pReadAcc->GetIndexFromData(pScanlineRead, nX));
558  nR = aCol.GetRed();
559  nG = aCol.GetGreen();
560  nB = aCol.GetBlue();
561 
562  if (nMinR <= nR && nMaxR >= nR && nMinG <= nG && nMaxG >= nG && nMinB <= nB
563  && nMaxB >= nB)
564  {
565  pWriteAcc->SetPixelOnData(pScanline, nX, aWhite);
566  }
567  else
568  {
569  pWriteAcc->SetPixelOnData(pScanline, nX, aBlack);
570  }
571  }
572  }
573  }
574  else
575  {
576  for (tools::Long nY = 0; nY < nHeight; nY++)
577  {
578  Scanline pScanline = pWriteAcc->GetScanline(nY);
579  Scanline pScanlineRead = pReadAcc->GetScanline(nY);
580  for (tools::Long nX = 0; nX < nWidth; nX++)
581  {
582  aCol = pReadAcc->GetPixelFromData(pScanlineRead, nX);
583  nR = aCol.GetRed();
584  nG = aCol.GetGreen();
585  nB = aCol.GetBlue();
586 
587  if (nMinR <= nR && nMaxR >= nR && nMinG <= nG && nMaxG >= nG && nMinB <= nB
588  && nMaxB >= nB)
589  {
590  pWriteAcc->SetPixelOnData(pScanline, nX, aWhite);
591  }
592  else
593  {
594  pWriteAcc->SetPixelOnData(pScanline, nX, aBlack);
595  }
596  }
597  }
598  }
599  }
600 
601  bRet = true;
602  }
603 
604  pWriteAcc.reset();
605  pReadAcc.reset();
606 
607  if (bRet)
608  {
609  aNewBmp.maPrefSize = maPrefSize;
610  aNewBmp.maPrefMapMode = maPrefMapMode;
611  }
612  else
613  aNewBmp = Bitmap();
614 
615  return aNewBmp;
616 }
617 
618 vcl::Region Bitmap::CreateRegion(const Color& rColor, const tools::Rectangle& rRect) const
619 {
620  vcl::Region aRegion;
621  tools::Rectangle aRect(rRect);
622  ScopedReadAccess pReadAcc(const_cast<Bitmap&>(*this));
623 
625  aRect.Justify();
626 
627  if (pReadAcc)
628  {
629  const tools::Long nLeft = aRect.Left();
630  const tools::Long nTop = aRect.Top();
631  const tools::Long nRight = aRect.Right();
632  const tools::Long nBottom = aRect.Bottom();
633  const BitmapColor aMatch(pReadAcc->GetBestMatchingColor(rColor));
634 
635  std::vector<tools::Long> aLine;
636  tools::Long nYStart(nTop);
637  tools::Long nY(nTop);
638 
639  for (; nY <= nBottom; nY++)
640  {
641  std::vector<tools::Long> aNewLine;
642  tools::Long nX(nLeft);
643  Scanline pScanlineRead = pReadAcc->GetScanline(nY);
644 
645  for (; nX <= nRight;)
646  {
647  while ((nX <= nRight) && (aMatch != pReadAcc->GetPixelFromData(pScanlineRead, nX)))
648  nX++;
649 
650  if (nX <= nRight)
651  {
652  aNewLine.push_back(nX);
653 
654  while ((nX <= nRight)
655  && (aMatch == pReadAcc->GetPixelFromData(pScanlineRead, nX)))
656  {
657  nX++;
658  }
659 
660  aNewLine.push_back(nX - 1);
661  }
662  }
663 
664  if (aNewLine != aLine)
665  {
666  // need to write aLine, it's different from the next line
667  if (!aLine.empty())
668  {
669  tools::Rectangle aSubRect;
670 
671  // enter y values and proceed ystart
672  aSubRect.SetTop(nYStart);
673  aSubRect.SetBottom(nY ? nY - 1 : 0);
674 
675  for (size_t a(0); a < aLine.size();)
676  {
677  aSubRect.SetLeft(aLine[a++]);
678  aSubRect.SetRight(aLine[a++]);
679  aRegion.Union(aSubRect);
680  }
681  }
682 
683  // copy line as new line
684  aLine = aNewLine;
685  nYStart = nY;
686  }
687  }
688 
689  // write last line if used
690  if (!aLine.empty())
691  {
692  tools::Rectangle aSubRect;
693 
694  // enter y values
695  aSubRect.SetTop(nYStart);
696  aSubRect.SetBottom(nY ? nY - 1 : 0);
697 
698  for (size_t a(0); a < aLine.size();)
699  {
700  aSubRect.SetLeft(aLine[a++]);
701  aSubRect.SetRight(aLine[a++]);
702  aRegion.Union(aSubRect);
703  }
704  }
705 
706  pReadAcc.reset();
707  }
708  else
709  {
710  aRegion = aRect;
711  }
712 
713  return aRegion;
714 }
715 
716 bool Bitmap::Replace(const Bitmap& rMask, const Color& rReplaceColor)
717 {
718  ScopedReadAccess pMaskAcc(const_cast<Bitmap&>(rMask));
719  BitmapScopedWriteAccess pAcc(*this);
720  bool bRet = false;
721 
722  if (pMaskAcc && pAcc)
723  {
724  const tools::Long nWidth = std::min(pMaskAcc->Width(), pAcc->Width());
725  const tools::Long nHeight = std::min(pMaskAcc->Height(), pAcc->Height());
726  const BitmapColor aMaskWhite(pMaskAcc->GetBestMatchingColor(COL_WHITE));
727  BitmapColor aReplace;
728 
729  if (pAcc->HasPalette())
730  {
731  const sal_uInt16 nActColors = pAcc->GetPaletteEntryCount();
732  const sal_uInt16 nMaxColors = 1 << pAcc->GetBitCount();
733 
734  // default to the nearest color
735  aReplace = pAcc->GetBestMatchingColor(rReplaceColor);
736 
737  // for paletted images without a matching palette entry
738  // look for an unused palette entry (NOTE: expensive!)
739  if (pAcc->GetPaletteColor(aReplace.GetIndex()) != BitmapColor(rReplaceColor))
740  {
741  // if the palette has empty entries use the last one
742  if (nActColors < nMaxColors)
743  {
744  pAcc->SetPaletteEntryCount(nActColors + 1);
745  pAcc->SetPaletteColor(nActColors, rReplaceColor);
746  aReplace = BitmapColor(static_cast<sal_uInt8>(nActColors));
747  }
748  else
749  {
750  std::unique_ptr<bool[]> pFlags(new bool[nMaxColors]);
751 
752  // Set all entries to false
753  std::fill(pFlags.get(), pFlags.get() + nMaxColors, false);
754 
755  for (tools::Long nY = 0; nY < nHeight; nY++)
756  {
757  Scanline pScanline = pAcc->GetScanline(nY);
758  for (tools::Long nX = 0; nX < nWidth; nX++)
759  pFlags[pAcc->GetIndexFromData(pScanline, nX)] = true;
760  }
761 
762  for (sal_uInt16 i = 0; i < nMaxColors; i++)
763  {
764  // Hurray, we do have an unused entry
765  if (!pFlags[i])
766  {
767  pAcc->SetPaletteColor(i, rReplaceColor);
768  aReplace = BitmapColor(static_cast<sal_uInt8>(i));
769  }
770  }
771  }
772  }
773  }
774  else
775  aReplace = rReplaceColor;
776 
777  for (tools::Long nY = 0; nY < nHeight; nY++)
778  {
779  Scanline pScanline = pAcc->GetScanline(nY);
780  Scanline pScanlineMask = pMaskAcc->GetScanline(nY);
781  for (tools::Long nX = 0; nX < nWidth; nX++)
782  {
783  if (pMaskAcc->GetPixelFromData(pScanlineMask, nX) == aMaskWhite)
784  pAcc->SetPixelOnData(pScanline, nX, aReplace);
785  }
786  }
787 
788  bRet = true;
789  }
790 
791  return bRet;
792 }
793 
794 bool Bitmap::Replace(const AlphaMask& rAlpha, const Color& rMergeColor)
795 {
796  Bitmap aNewBmp(GetSizePixel(), 24);
797  ScopedReadAccess pAcc(*this);
798  AlphaMask::ScopedReadAccess pAlphaAcc(const_cast<AlphaMask&>(rAlpha));
799  BitmapScopedWriteAccess pNewAcc(aNewBmp);
800  bool bRet = false;
801 
802  if (pAcc && pAlphaAcc && pNewAcc)
803  {
804  BitmapColor aCol;
805  const tools::Long nWidth = std::min(pAlphaAcc->Width(), pAcc->Width());
806  const tools::Long nHeight = std::min(pAlphaAcc->Height(), pAcc->Height());
807 
808  for (tools::Long nY = 0; nY < nHeight; nY++)
809  {
810  Scanline pScanline = pNewAcc->GetScanline(nY);
811  Scanline pScanlineAlpha = pAlphaAcc->GetScanline(nY);
812  for (tools::Long nX = 0; nX < nWidth; nX++)
813  {
814  aCol = pAcc->GetColor(nY, nX);
815  aCol.Merge(rMergeColor, 255 - pAlphaAcc->GetIndexFromData(pScanlineAlpha, nX));
816  pNewAcc->SetPixelOnData(pScanline, nX, aCol);
817  }
818  }
819 
820  bRet = true;
821  }
822 
823  pAcc.reset();
824  pAlphaAcc.reset();
825  pNewAcc.reset();
826 
827  if (bRet)
828  {
829  const MapMode aMap(maPrefMapMode);
830  const Size aSize(maPrefSize);
831 
832  *this = aNewBmp;
833 
834  maPrefMapMode = aMap;
835  maPrefSize = aSize;
836  }
837 
838  return bRet;
839 }
840 
841 bool Bitmap::Replace(const Color& rSearchColor, const Color& rReplaceColor, sal_uInt8 nTol)
842 {
843  if (mxSalBmp)
844  {
845  // implementation specific replace
846  std::shared_ptr<SalBitmap> xImpBmp(ImplGetSVData()->mpDefInst->CreateSalBitmap());
847  if (xImpBmp->Create(*mxSalBmp) && xImpBmp->Replace(rSearchColor, rReplaceColor, nTol))
848  {
849  ImplSetSalBitmap(xImpBmp);
850  maPrefMapMode = MapMode(MapUnit::MapPixel);
851  maPrefSize = xImpBmp->GetSize();
852  return true;
853  }
854  }
855 
856  // Bitmaps with 1 bit color depth can cause problems if they have other entries than black/white
857  // in their palette
858  if (GetBitCount() == 1)
860 
861  BitmapScopedWriteAccess pAcc(*this);
862  bool bRet = false;
863 
864  if (pAcc)
865  {
866  const tools::Long nMinR = MinMax<tools::Long>(rSearchColor.GetRed() - nTol, 0, 255);
867  const tools::Long nMaxR = MinMax<tools::Long>(rSearchColor.GetRed() + nTol, 0, 255);
868  const tools::Long nMinG = MinMax<tools::Long>(rSearchColor.GetGreen() - nTol, 0, 255);
869  const tools::Long nMaxG = MinMax<tools::Long>(rSearchColor.GetGreen() + nTol, 0, 255);
870  const tools::Long nMinB = MinMax<tools::Long>(rSearchColor.GetBlue() - nTol, 0, 255);
871  const tools::Long nMaxB = MinMax<tools::Long>(rSearchColor.GetBlue() + nTol, 0, 255);
872 
873  if (pAcc->HasPalette())
874  {
875  for (sal_uInt16 i = 0, nPalCount = pAcc->GetPaletteEntryCount(); i < nPalCount; i++)
876  {
877  const BitmapColor& rCol = pAcc->GetPaletteColor(i);
878 
879  if (nMinR <= rCol.GetRed() && nMaxR >= rCol.GetRed() && nMinG <= rCol.GetGreen()
880  && nMaxG >= rCol.GetGreen() && nMinB <= rCol.GetBlue()
881  && nMaxB >= rCol.GetBlue())
882  {
883  pAcc->SetPaletteColor(i, rReplaceColor);
884  }
885  }
886  }
887  else
888  {
889  BitmapColor aCol;
890  const BitmapColor aReplace(pAcc->GetBestMatchingColor(rReplaceColor));
891 
892  for (tools::Long nY = 0, nHeight = pAcc->Height(); nY < nHeight; nY++)
893  {
894  Scanline pScanline = pAcc->GetScanline(nY);
895  for (tools::Long nX = 0, nWidth = pAcc->Width(); nX < nWidth; nX++)
896  {
897  aCol = pAcc->GetPixelFromData(pScanline, nX);
898 
899  if (nMinR <= aCol.GetRed() && nMaxR >= aCol.GetRed() && nMinG <= aCol.GetGreen()
900  && nMaxG >= aCol.GetGreen() && nMinB <= aCol.GetBlue()
901  && nMaxB >= aCol.GetBlue())
902  {
903  pAcc->SetPixelOnData(pScanline, nX, aReplace);
904  }
905  }
906  }
907  }
908 
909  pAcc.reset();
910  bRet = true;
911  }
912 
913  return bRet;
914 }
915 
916 bool Bitmap::Replace(const Color* pSearchColors, const Color* pReplaceColors, sal_uLong nColorCount,
917  sal_uInt8 const* pTols)
918 {
919  // Bitmaps with 1 bit color depth can cause problems if they have other entries than black/white
920  // in their palette
921  if (GetBitCount() == 1)
923 
924  BitmapScopedWriteAccess pAcc(*this);
925  bool bRet = false;
926 
927  if (pAcc)
928  {
929  std::unique_ptr<sal_Int8[]> pMinR(new sal_Int8[nColorCount]);
930  std::unique_ptr<sal_Int8[]> pMaxR(new sal_Int8[nColorCount]);
931  std::unique_ptr<sal_Int8[]> pMinG(new sal_Int8[nColorCount]);
932  std::unique_ptr<sal_Int8[]> pMaxG(new sal_Int8[nColorCount]);
933  std::unique_ptr<sal_Int8[]> pMinB(new sal_Int8[nColorCount]);
934  std::unique_ptr<sal_Int8[]> pMaxB(new sal_Int8[nColorCount]);
935 
936  if (pTols)
937  {
938  for (sal_uLong i = 0; i < nColorCount; i++)
939  {
940  const Color& rCol = pSearchColors[i];
941  const sal_uInt8 nTol = pTols[i];
942 
943  pMinR[i] = std::clamp(rCol.GetRed() - nTol, 0, 255);
944  pMaxR[i] = std::clamp(rCol.GetRed() + nTol, 0, 255);
945  pMinG[i] = std::clamp(rCol.GetGreen() - nTol, 0, 255);
946  pMaxG[i] = std::clamp(rCol.GetGreen() + nTol, 0, 255);
947  pMinB[i] = std::clamp(rCol.GetBlue() - nTol, 0, 255);
948  pMaxB[i] = std::clamp(rCol.GetBlue() + nTol, 0, 255);
949  }
950  }
951  else
952  {
953  for (sal_uLong i = 0; i < nColorCount; i++)
954  {
955  const Color& rCol = pSearchColors[i];
956 
957  pMinR[i] = rCol.GetRed();
958  pMaxR[i] = rCol.GetRed();
959  pMinG[i] = rCol.GetGreen();
960  pMaxG[i] = rCol.GetGreen();
961  pMinB[i] = rCol.GetBlue();
962  pMaxB[i] = rCol.GetBlue();
963  }
964  }
965 
966  if (pAcc->HasPalette())
967  {
968  for (sal_uInt16 nEntry = 0, nPalCount = pAcc->GetPaletteEntryCount();
969  nEntry < nPalCount; nEntry++)
970  {
971  const BitmapColor& rCol = pAcc->GetPaletteColor(nEntry);
972 
973  for (sal_uLong i = 0; i < nColorCount; i++)
974  {
975  if (pMinR[i] <= rCol.GetRed() && pMaxR[i] >= rCol.GetRed()
976  && pMinG[i] <= rCol.GetGreen() && pMaxG[i] >= rCol.GetGreen()
977  && pMinB[i] <= rCol.GetBlue() && pMaxB[i] >= rCol.GetBlue())
978  {
979  pAcc->SetPaletteColor(nEntry, pReplaceColors[i]);
980  break;
981  }
982  }
983  }
984  }
985  else
986  {
987  BitmapColor aCol;
988  std::unique_ptr<BitmapColor[]> pReplaces(new BitmapColor[nColorCount]);
989 
990  for (sal_uLong i = 0; i < nColorCount; i++)
991  pReplaces[i] = pAcc->GetBestMatchingColor(pReplaceColors[i]);
992 
993  for (tools::Long nY = 0, nHeight = pAcc->Height(); nY < nHeight; nY++)
994  {
995  Scanline pScanline = pAcc->GetScanline(nY);
996  for (tools::Long nX = 0, nWidth = pAcc->Width(); nX < nWidth; nX++)
997  {
998  aCol = pAcc->GetPixelFromData(pScanline, nX);
999 
1000  for (sal_uLong i = 0; i < nColorCount; i++)
1001  {
1002  if (pMinR[i] <= aCol.GetRed() && pMaxR[i] >= aCol.GetRed()
1003  && pMinG[i] <= aCol.GetGreen() && pMaxG[i] >= aCol.GetGreen()
1004  && pMinB[i] <= aCol.GetBlue() && pMaxB[i] >= aCol.GetBlue())
1005  {
1006  pAcc->SetPixelOnData(pScanline, nX, pReplaces[i]);
1007  break;
1008  }
1009  }
1010  }
1011  }
1012  }
1013 
1014  pAcc.reset();
1015  bRet = true;
1016  }
1017 
1018  return bRet;
1019 }
1020 
1021 bool Bitmap::CombineSimple(const Bitmap& rMask, BmpCombine eCombine)
1022 {
1023  ScopedReadAccess pMaskAcc(const_cast<Bitmap&>(rMask));
1024  BitmapScopedWriteAccess pAcc(*this);
1025  bool bRet = false;
1026 
1027  if (pMaskAcc && pAcc)
1028  {
1029  const tools::Long nWidth = std::min(pMaskAcc->Width(), pAcc->Width());
1030  const tools::Long nHeight = std::min(pMaskAcc->Height(), pAcc->Height());
1031  const Color aColBlack(COL_BLACK);
1032  const BitmapColor aWhite(pAcc->GetBestMatchingColor(COL_WHITE));
1033  const BitmapColor aBlack(pAcc->GetBestMatchingColor(aColBlack));
1034  const BitmapColor aMaskBlack(pMaskAcc->GetBestMatchingColor(aColBlack));
1035 
1036  switch (eCombine)
1037  {
1038  case BmpCombine::And:
1039  {
1040  for (tools::Long nY = 0; nY < nHeight; nY++)
1041  {
1042  Scanline pScanline = pAcc->GetScanline(nY);
1043  Scanline pScanlineMask = pMaskAcc->GetScanline(nY);
1044  for (tools::Long nX = 0; nX < nWidth; nX++)
1045  {
1046  if (pMaskAcc->GetPixelFromData(pScanlineMask, nX) != aMaskBlack
1047  && pAcc->GetPixelFromData(pScanline, nX) != aBlack)
1048  {
1049  pAcc->SetPixelOnData(pScanline, nX, aWhite);
1050  }
1051  else
1052  {
1053  pAcc->SetPixelOnData(pScanline, nX, aBlack);
1054  }
1055  }
1056  }
1057  }
1058  break;
1059 
1060  case BmpCombine::Or:
1061  {
1062  for (tools::Long nY = 0; nY < nHeight; nY++)
1063  {
1064  Scanline pScanline = pAcc->GetScanline(nY);
1065  Scanline pScanlineMask = pMaskAcc->GetScanline(nY);
1066  for (tools::Long nX = 0; nX < nWidth; nX++)
1067  {
1068  if (pMaskAcc->GetPixelFromData(pScanlineMask, nX) != aMaskBlack
1069  || pAcc->GetPixelFromData(pScanline, nX) != aBlack)
1070  {
1071  pAcc->SetPixelOnData(pScanline, nX, aWhite);
1072  }
1073  else
1074  {
1075  pAcc->SetPixelOnData(pScanline, nX, aBlack);
1076  }
1077  }
1078  }
1079  }
1080  break;
1081  }
1082 
1083  bRet = true;
1084  }
1085 
1086  return bRet;
1087 }
1088 
1089 // TODO: Have a look at OutputDevice::ImplDrawAlpha() for some
1090 // optimizations. Might even consolidate the code here and there.
1091 bool Bitmap::Blend(const AlphaMask& rAlpha, const Color& rBackgroundColor)
1092 {
1093  // Convert to a truecolor bitmap, if we're a paletted one. There's room for tradeoff decision here,
1094  // maybe later for an overload (or a flag)
1095  if (GetBitCount() <= 8)
1097 
1098  AlphaMask::ScopedReadAccess pAlphaAcc(const_cast<AlphaMask&>(rAlpha));
1099 
1100  BitmapScopedWriteAccess pAcc(*this);
1101  bool bRet = false;
1102 
1103  if (pAlphaAcc && pAcc)
1104  {
1105  const tools::Long nWidth = std::min(pAlphaAcc->Width(), pAcc->Width());
1106  const tools::Long nHeight = std::min(pAlphaAcc->Height(), pAcc->Height());
1107 
1108  for (tools::Long nY = 0; nY < nHeight; ++nY)
1109  {
1110  Scanline pScanline = pAcc->GetScanline(nY);
1111  Scanline pScanlineAlpha = pAlphaAcc->GetScanline(nY);
1112  for (tools::Long nX = 0; nX < nWidth; ++nX)
1113  {
1114  BitmapColor aBmpColor = pAcc->GetPixelFromData(pScanline, nX);
1115  aBmpColor.Merge(rBackgroundColor,
1116  255 - pAlphaAcc->GetIndexFromData(pScanlineAlpha, nX));
1117  pAcc->SetPixelOnData(pScanline, nX, aBmpColor);
1118  }
1119  }
1120 
1121  bRet = true;
1122  }
1123 
1124  return bRet;
1125 }
1126 
1127 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_uInt8 GetIndexFromData(const sal_uInt8 *pData, tools::Long nX) const
sal_uInt8 GetIndex() const
Definition: BitmapColor.hxx:61
tools::Long Height() const
sal_uInt8 GetRed() const
tools::Rectangle & Intersection(const tools::Rectangle &rRect)
void Merge(const Color &rMergeColor, sal_uInt8 cTransparency)
signed char sal_Int8
void Union(const tools::Rectangle &rRegion)
Definition: region.cxx:508
sal_uIntPtr sal_uLong
long Long
Size maPrefSize
Definition: bitmap.hxx:543
#define F_PI1800
sal_uInt16 GetBitCount() const
Size GetSizePixel() const
void SetRight(tools::Long v)
void SetPalette(const BitmapPalette &rPalette)
HashMap_OWString_Interface aMap
void SetPixelOnData(sal_uInt8 *pData, tools::Long nX, const BitmapColor &rBitmapColor)
bool CombineSimple(const Bitmap &rMask, BmpCombine eCombine)
Perform boolean operations with another bitmap.
bool Mirror(BmpMirrorFlags nMirrorFlags)
Mirror the bitmap.
tools::Long Left() const
void SetLeft(tools::Long v)
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
Scanline GetScanline(tools::Long nY) const
int nCount
tools::Long Bottom() 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
UNDERLYING_TYPE get() const
sal_uInt32 GetScanlineSize() const
int i
bool HasPalette() const
bool Rotate(Degree10 nAngle10, const Color &rFillColor)
Rotate bitmap by the specified angle.
uno_Any a
ScanlineFormat GetScanlineFormat() const
void Erase(const Color &rColor)
Definition: bmpacc3.cxx:69
tools::Long FRound(double fVal)
BitmapColor GetColor(tools::Long nY, tools::Long nX) const
tools::Long Width() const
tools::Long Width() 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:37
bool Invert()
Perform the Invert operation on every pixel.
Definition: bitmappaint.cxx:64
void SetTop(tools::Long v)
void SetBottom(tools::Long v)
tools::Long Top() const
sal_uInt16 GetPaletteEntryCount() const
sal_uInt8 GetGreen() const
unsigned char sal_uInt8
bool Convert(BmpConversion eConversion)
Convert bitmap format.
Definition: bitmap3.cxx:229
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_WHITE
const BitmapPalette & GetPalette() const
tools::Long Height() const
bool IsEmpty() const
Definition: bitmap.hxx:552
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_BLACK
BitmapColor GetPixelFromData(const sal_uInt8 *pData, tools::Long nX) const
bool Erase(const Color &rFillColor)
Fill the entire bitmap with the given color.
Definition: bitmappaint.cxx:34
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:542
BitmapColor GetPixel(tools::Long nY, tools::Long nX) const
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:541
tools::Long Right() const
BmpCombine
Definition: bitmap.hxx:80
static const BitmapPalette & GetGreyPalette(int nEntries)
SAL_DLLPRIVATE void ImplSetSalBitmap(const std::shared_ptr< SalBitmap > &xImpBmp)