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