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