Update git submodules
[LibreOffice.git] / vcl / skia / salbmp.cxx
blobf82833881470ef5c1f407ccd0eeac34d34566b1b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <skia/salbmp.hxx>
22 #include <o3tl/safeint.hxx>
23 #include <tools/helpers.hxx>
24 #include <boost/smart_ptr/make_shared.hpp>
26 #include <salgdi.hxx>
27 #include <salinst.hxx>
28 #include <scanlinewriter.hxx>
29 #include <svdata.hxx>
30 #include <bitmap/bmpfast.hxx>
31 #include <vcl/BitmapReadAccess.hxx>
33 #include <skia/utils.hxx>
34 #include <skia/zone.hxx>
36 #include <SkBitmap.h>
37 #include <SkCanvas.h>
38 #include <SkImage.h>
39 #include <SkPixelRef.h>
40 #include <SkShader.h>
41 #include <SkSurface.h>
42 #include <SkSwizzle.h>
43 #include <SkColorFilter.h>
44 #include <SkColorMatrix.h>
45 #include <skia_opts.hxx>
47 #ifdef DBG_UTIL
48 #include <fstream>
49 #define CANARY "skia-canary"
50 #endif
52 using namespace SkiaHelper;
54 // As constexpr here, evaluating it directly in code makes Clang warn about unreachable code.
55 constexpr bool kN32_SkColorTypeIsBGRA = (kN32_SkColorType == kBGRA_8888_SkColorType);
57 SkiaSalBitmap::SkiaSalBitmap() {}
59 SkiaSalBitmap::~SkiaSalBitmap() {}
61 SkiaSalBitmap::SkiaSalBitmap(const sk_sp<SkImage>& image)
63 ResetAllData();
64 mImage = image;
65 mPalette = BitmapPalette();
66 #if SKIA_USE_BITMAP32
67 mBitCount = 32;
68 #else
69 mBitCount = 24;
70 #endif
71 mSize = mPixelsSize = Size(image->width(), image->height());
72 ComputeScanlineSize();
73 mReadAccessCount = 0;
74 #ifdef DBG_UTIL
75 mWriteAccessCount = 0;
76 #endif
77 SAL_INFO("vcl.skia.trace", "bitmapfromimage(" << this << ")");
80 bool SkiaSalBitmap::Create(const Size& rSize, vcl::PixelFormat ePixelFormat,
81 const BitmapPalette& rPal)
83 assert(mReadAccessCount == 0);
84 ResetAllData();
85 if (ePixelFormat == vcl::PixelFormat::INVALID)
86 return false;
87 mPalette = rPal;
88 mBitCount = vcl::pixelFormatBitCount(ePixelFormat);
89 mSize = rSize;
90 ResetPendingScaling();
91 if (!ComputeScanlineSize())
93 mBitCount = 0;
94 mSize = mPixelsSize = Size();
95 mScanlineSize = 0;
96 mPalette = BitmapPalette();
97 return false;
99 SAL_INFO("vcl.skia.trace", "create(" << this << ")");
100 return true;
103 bool SkiaSalBitmap::ComputeScanlineSize()
105 int bitScanlineWidth;
106 if (o3tl::checked_multiply<int>(mPixelsSize.Width(), mBitCount, bitScanlineWidth))
108 SAL_WARN("vcl.skia", "checked multiply failed");
109 return false;
111 mScanlineSize = AlignedWidth4Bytes(bitScanlineWidth);
112 return true;
115 void SkiaSalBitmap::CreateBitmapData()
117 assert(!mBuffer);
118 // Make sure code has not missed calling ComputeScanlineSize().
119 assert(mScanlineSize == int(AlignedWidth4Bytes(mPixelsSize.Width() * mBitCount)));
120 // The pixels could be stored in SkBitmap, but Skia only supports 8bit gray, 16bit and 32bit formats
121 // (e.g. 24bpp is actually stored as 32bpp). But some of our code accessing the bitmap assumes that
122 // when it asked for 24bpp, the format really will be 24bpp (e.g. the png loader), so we cannot use
123 // SkBitmap to store the data. And even 8bpp is problematic, since Skia does not support palettes
124 // and a VCL bitmap can change its grayscale status simply by changing the palette.
125 // Moreover creating SkImage from SkBitmap does a data copy unless the bitmap is immutable.
126 // So just always store pixels in our buffer and convert as necessary.
127 if (mScanlineSize == 0 || mPixelsSize.Height() == 0)
128 return;
130 size_t allocate = mScanlineSize * mPixelsSize.Height();
131 #ifdef DBG_UTIL
132 allocate += sizeof(CANARY);
133 #endif
134 mBuffer = boost::make_shared_noinit<sal_uInt8[]>(allocate);
135 #ifdef DBG_UTIL
136 // fill with random garbage
137 sal_uInt8* buffer = mBuffer.get();
138 for (size_t i = 0; i < allocate; i++)
139 buffer[i] = (i & 0xFF);
140 memcpy(buffer + allocate - sizeof(CANARY), CANARY, sizeof(CANARY));
141 #endif
144 bool SkiaSalBitmap::Create(const SalBitmap& rSalBmp)
146 return Create(rSalBmp, vcl::bitDepthToPixelFormat(rSalBmp.GetBitCount()));
149 bool SkiaSalBitmap::Create(const SalBitmap& rSalBmp, SalGraphics* pGraphics)
151 auto ePixelFormat = vcl::PixelFormat::INVALID;
152 if (pGraphics)
153 ePixelFormat = vcl::bitDepthToPixelFormat(pGraphics->GetBitCount());
154 else
155 ePixelFormat = vcl::bitDepthToPixelFormat(rSalBmp.GetBitCount());
157 return Create(rSalBmp, ePixelFormat);
160 bool SkiaSalBitmap::Create(const SalBitmap& rSalBmp, vcl::PixelFormat eNewPixelFormat)
162 assert(mReadAccessCount == 0);
163 assert(&rSalBmp != this);
164 ResetAllData();
165 const SkiaSalBitmap& src = static_cast<const SkiaSalBitmap&>(rSalBmp);
166 mImage = src.mImage;
167 mImageImmutable = src.mImageImmutable;
168 mAlphaImage = src.mAlphaImage;
169 mBuffer = src.mBuffer;
170 mPalette = src.mPalette;
171 mBitCount = src.mBitCount;
172 mSize = src.mSize;
173 mPixelsSize = src.mPixelsSize;
174 mScanlineSize = src.mScanlineSize;
175 mScaleQuality = src.mScaleQuality;
176 mEraseColorSet = src.mEraseColorSet;
177 mEraseColor = src.mEraseColor;
178 if (vcl::pixelFormatBitCount(eNewPixelFormat) != src.GetBitCount())
180 // This appears to be unused(?). Implement this just in case, but be lazy
181 // about it and rely on EnsureBitmapData() doing the conversion from mImage
182 // if needed, even if that may need unnecessary to- and from- SkImage
183 // conversion.
184 ResetToSkImage(GetSkImage());
186 SAL_INFO("vcl.skia.trace", "create(" << this << "): (" << &src << ")");
187 return true;
190 bool SkiaSalBitmap::Create(const css::uno::Reference<css::rendering::XBitmapCanvas>&, Size&, bool)
192 return false;
195 void SkiaSalBitmap::Destroy()
197 SAL_INFO("vcl.skia.trace", "destroy(" << this << ")");
198 #ifdef DBG_UTIL
199 assert(mWriteAccessCount == 0);
200 #endif
201 assert(mReadAccessCount == 0);
202 ResetAllData();
205 Size SkiaSalBitmap::GetSize() const { return mSize; }
207 sal_uInt16 SkiaSalBitmap::GetBitCount() const { return mBitCount; }
209 BitmapBuffer* SkiaSalBitmap::AcquireBuffer(BitmapAccessMode nMode)
211 switch (nMode)
213 case BitmapAccessMode::Write:
214 EnsureBitmapUniqueData();
215 if (!mBuffer)
216 return nullptr;
217 assert(mPixelsSize == mSize);
218 assert(!mEraseColorSet);
219 break;
220 case BitmapAccessMode::Read:
221 EnsureBitmapData();
222 if (!mBuffer)
223 return nullptr;
224 assert(mPixelsSize == mSize);
225 assert(!mEraseColorSet);
226 break;
227 case BitmapAccessMode::Info:
228 // Related tdf#156629 and tdf#156630 force snapshot of alpha mask
229 // On macOS, with Skia/Metal or Skia/Raster with a Retina display
230 // (i.e. 2.0 window scale), the alpha mask gets upscaled in certain
231 // cases.
232 // This bug appears to be caused by pending scaling of an existing
233 // SkImage in the bitmap parameter. So, force the SkiaSalBitmap to
234 // handle its pending scaling.
235 // Note: also handle pending scaling if SAL_FORCE_HIDPI_SCALING is
236 // set otherwise exporting the following animated .png image will
237 // fail:
238 // https://bugs.documentfoundation.org/attachment.cgi?id=188792
239 static const bool bForceHiDPIScaling = getenv("SAL_FORCE_HIDPI_SCALING") != nullptr;
240 if (mImage && !mImageImmutable && mBitCount == 8 && mPalette.IsGreyPalette8Bit()
241 && (mPixelsSize != mSize || bForceHiDPIScaling))
243 ResetToSkImage(GetSkImage());
244 ResetPendingScaling();
245 assert(mPixelsSize == mSize);
247 // When many of the images affected by tdf#156629 and
248 // tdf#156630 are exported to PDF the first time after the
249 // image has been opened and before it has been printed or run
250 // in a slideshow, the alpha mask will unexpectedly be
251 // inverted. Fix that by marking this alpha mask as immutable
252 // so that when Invert() is called on this alpha mask, it will
253 // be a noop. Invert() is a noop after EnsureBitmapData() is
254 // called but we don't want to call that due to performance
255 // so set a flag instead.
256 mImageImmutable = true;
258 break;
260 #ifdef DBG_UTIL
261 // BitmapWriteAccess stores also a copy of the palette and it can
262 // be modified, so concurrent reading of it might result in inconsistencies.
263 assert(mWriteAccessCount == 0 || nMode == BitmapAccessMode::Write);
264 #endif
265 BitmapBuffer* buffer = new BitmapBuffer;
266 buffer->mnWidth = mSize.Width();
267 buffer->mnHeight = mSize.Height();
268 buffer->mnBitCount = mBitCount;
269 buffer->maPalette = mPalette;
270 if (nMode != BitmapAccessMode::Info)
271 buffer->mpBits = mBuffer.get();
272 else
273 buffer->mpBits = nullptr;
274 if (mPixelsSize == mSize)
275 buffer->mnScanlineSize = mScanlineSize;
276 else
278 // The value of mScanlineSize is based on internal mPixelsSize, but the outside
279 // world cares about mSize, the size that the report as the size of the bitmap,
280 // regardless of any internal state. So report scanline size for that size.
281 Size savedPixelsSize = mPixelsSize;
282 mPixelsSize = mSize;
283 ComputeScanlineSize();
284 buffer->mnScanlineSize = mScanlineSize;
285 mPixelsSize = savedPixelsSize;
286 ComputeScanlineSize();
288 switch (mBitCount)
290 case 1:
291 buffer->mnFormat = ScanlineFormat::N1BitMsbPal;
292 break;
293 case 8:
294 buffer->mnFormat = ScanlineFormat::N8BitPal;
295 break;
296 case 24:
297 // Make the RGB/BGR format match the default Skia 32bpp format, to allow
298 // easy conversion later.
299 buffer->mnFormat = kN32_SkColorTypeIsBGRA ? ScanlineFormat::N24BitTcBgr
300 : ScanlineFormat::N24BitTcRgb;
301 break;
302 case 32:
303 buffer->mnFormat = kN32_SkColorTypeIsBGRA ? ScanlineFormat::N32BitTcBgra
304 : ScanlineFormat::N32BitTcRgba;
305 break;
306 default:
307 abort();
309 buffer->mnFormat |= ScanlineFormat::TopDown;
310 // Refcount all read/write accesses, to catch problems with existing accesses while
311 // a bitmap changes, and also to detect when we can free mBuffer if wanted.
312 // Write mode implies also reading. It would be probably a good idea to count even
313 // Info accesses, but VclCanvasBitmap keeps one around pointlessly, causing tdf#150817.
314 if (nMode == BitmapAccessMode::Read || nMode == BitmapAccessMode::Write)
315 ++mReadAccessCount;
316 #ifdef DBG_UTIL
317 if (nMode == BitmapAccessMode::Write)
318 ++mWriteAccessCount;
319 #endif
320 return buffer;
323 void SkiaSalBitmap::ReleaseBuffer(BitmapBuffer* pBuffer, BitmapAccessMode nMode)
325 ReleaseBuffer(pBuffer, nMode, false);
328 void SkiaSalBitmap::ReleaseBuffer(BitmapBuffer* pBuffer, BitmapAccessMode nMode,
329 bool dontChangeToErase)
331 if (nMode == BitmapAccessMode::Write)
333 #ifdef DBG_UTIL
334 assert(mWriteAccessCount > 0);
335 --mWriteAccessCount;
336 #endif
337 mPalette = pBuffer->maPalette;
338 ResetToBuffer();
339 DataChanged();
341 if (nMode == BitmapAccessMode::Read || nMode == BitmapAccessMode::Write)
343 assert(mReadAccessCount > 0);
344 --mReadAccessCount;
346 // Are there any more ground movements underneath us ?
347 assert(pBuffer->mnWidth == mSize.Width());
348 assert(pBuffer->mnHeight == mSize.Height());
349 assert(pBuffer->mnBitCount == mBitCount);
350 assert(pBuffer->mpBits == mBuffer.get() || nMode == BitmapAccessMode::Info);
351 verify();
352 delete pBuffer;
353 if (nMode == BitmapAccessMode::Write && !dontChangeToErase)
355 // This saves memory and is also used by IsFullyOpaqueAsAlpha() to avoid unnecessary
356 // alpha blending.
357 if (IsAllBlack())
359 SAL_INFO("vcl.skia.trace", "releasebuffer(" << this << "): erasing to black");
360 EraseInternal(COL_BLACK);
365 static bool isAllZero(const sal_uInt8* data, size_t size)
366 { // For performance, check in larger data chunks.
367 #ifdef UINT64_MAX
368 const int64_t* d = reinterpret_cast<const int64_t*>(data);
369 #else
370 const int32_t* d = reinterpret_cast<const int32_t*>(data);
371 #endif
372 constexpr size_t step = sizeof(*d) * 8;
373 for (size_t i = 0; i < size / step; ++i)
374 { // Unrolled loop.
375 if (d[0] != 0)
376 return false;
377 if (d[1] != 0)
378 return false;
379 if (d[2] != 0)
380 return false;
381 if (d[3] != 0)
382 return false;
383 if (d[4] != 0)
384 return false;
385 if (d[5] != 0)
386 return false;
387 if (d[6] != 0)
388 return false;
389 if (d[7] != 0)
390 return false;
391 d += 8;
393 for (size_t i = size / step * step; i < size; ++i)
394 if (data[i] != 0)
395 return false;
396 return true;
399 bool SkiaSalBitmap::IsAllBlack() const
401 if (mBitCount % 8 != 0 || (!!mPalette && mPalette[0] != COL_BLACK))
402 return false; // Don't bother.
403 if (mSize.Width() * mBitCount / 8 == mScanlineSize)
404 return isAllZero(mBuffer.get(), mScanlineSize * mSize.Height());
405 for (tools::Long y = 0; y < mSize.Height(); ++y)
406 if (!isAllZero(mBuffer.get() + mScanlineSize * y, mSize.Width() * mBitCount / 8))
407 return false;
408 return true;
411 bool SkiaSalBitmap::GetSystemData(BitmapSystemData&)
413 #ifdef DBG_UTIL
414 assert(mWriteAccessCount == 0);
415 #endif
416 return false;
419 bool SkiaSalBitmap::ScalingSupported() const { return true; }
421 bool SkiaSalBitmap::Scale(const double& rScaleX, const double& rScaleY, BmpScaleFlag nScaleFlag)
423 SkiaZone zone;
424 #ifdef DBG_UTIL
425 assert(mWriteAccessCount == 0);
426 #endif
427 Size newSize(FRound(mSize.Width() * rScaleX), FRound(mSize.Height() * rScaleY));
428 if (mSize == newSize)
429 return true;
431 SAL_INFO("vcl.skia.trace", "scale(" << this << "): " << mSize << "/" << mBitCount << "->"
432 << newSize << ":" << static_cast<int>(nScaleFlag));
434 if (mEraseColorSet)
435 { // Simple.
436 mSize = newSize;
437 ResetPendingScaling();
438 EraseInternal(mEraseColor);
439 return true;
442 if (mBitCount < 24 && !mPalette.IsGreyPalette8Bit())
444 // Scaling can introduce additional colors not present in the original
445 // bitmap (e.g. when smoothing). If the bitmap is indexed (has non-trivial palette),
446 // this would break the bitmap, because the actual scaling is done only somewhen later.
447 // Linear 8bit palette (grey) is ok, since there we use directly the values as colors.
448 SAL_INFO("vcl.skia.trace", "scale(" << this << "): indexed bitmap");
449 return false;
451 // The idea here is that the actual scaling will be delayed until the result
452 // is actually needed. Usually the scaled bitmap will be drawn somewhere,
453 // so delaying will mean the scaling can be done as a part of GetSkImage().
454 // That means it can be GPU-accelerated, while done here directly it would need
455 // to be either done by CPU, or with the CPU->GPU->CPU roundtrip required
456 // by GPU-accelerated scaling.
457 // Pending scaling is detected by 'mSize != mPixelsSize' for mBuffer,
458 // and 'imageSize(mImage) != mSize' for mImage. It is not intended to have 3 different
459 // sizes though, code below keeps only mBuffer or mImage. Note that imageSize(mImage)
460 // may or may not be equal to mPixelsSize, depending on whether mImage is set here
461 // (sizes will be equal) or whether it's set in GetSkImage() (will not be equal).
462 // Pending scaling is considered "done" by the time mBuffer is resized (or created).
463 // Resizing of mImage is somewhat independent of this, since mImage is primarily
464 // considered to be a cached object (although sometimes it's the only data available).
466 // If there is already one scale() pending, use the lowest quality of all requested.
467 switch (nScaleFlag)
469 case BmpScaleFlag::Fast:
470 mScaleQuality = nScaleFlag;
471 break;
472 case BmpScaleFlag::NearestNeighbor:
473 // We handle this the same way as Fast by mapping to Skia's nearest-neighbor,
474 // and it's needed for unittests (mScaling and testTdf132367()).
475 mScaleQuality = nScaleFlag;
476 break;
477 case BmpScaleFlag::Default:
478 if (mScaleQuality == BmpScaleFlag::BestQuality)
479 mScaleQuality = nScaleFlag;
480 break;
481 case BmpScaleFlag::BestQuality:
482 // Best is the maximum, set by default.
483 break;
484 default:
485 SAL_INFO("vcl.skia.trace", "scale(" << this << "): unsupported scale algorithm");
486 return false;
488 mSize = newSize;
489 // If we have both mBuffer and mImage, prefer mImage, since it likely will be drawn later.
490 // We could possibly try to keep the buffer as well, but that would complicate things
491 // with two different data structures to be scaled on-demand, and it's a question
492 // if that'd realistically help with anything.
493 if (mImage)
494 ResetToSkImage(mImage);
495 else
496 ResetToBuffer();
497 DataChanged();
498 // The rest will be handled when the scaled bitmap is actually needed,
499 // such as in EnsureBitmapData() or GetSkImage().
500 return true;
503 bool SkiaSalBitmap::Replace(const Color&, const Color&, sal_uInt8)
505 #ifdef DBG_UTIL
506 assert(mWriteAccessCount == 0);
507 #endif
508 return false;
511 bool SkiaSalBitmap::ConvertToGreyscale()
513 #ifdef DBG_UTIL
514 assert(mWriteAccessCount == 0);
515 #endif
516 // Normally this would need to convert contents of mBuffer for all possible formats,
517 // so just let the VCL algorithm do it.
518 // Avoid the costly SkImage->buffer->SkImage conversion.
519 if (!mBuffer && mImage && !mEraseColorSet)
521 if (mBitCount == 8 && mPalette.IsGreyPalette8Bit())
522 return true;
523 sk_sp<SkSurface> surface
524 = createSkSurface(imageSize(mImage), mImage->imageInfo().alphaType());
525 SkPaint paint;
526 paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
527 // VCL uses different coefficients for conversion to gray than Skia, so use the VCL
528 // values from Bitmap::ImplMakeGreyscales(). Do not use kGray_8_SkColorType,
529 // Skia would use its gray conversion formula.
530 // NOTE: The matrix is 4x5 organized as columns (i.e. each line is a column, not a row).
531 constexpr SkColorMatrix toGray(77 / 256.0, 151 / 256.0, 28 / 256.0, 0, 0, // R column
532 77 / 256.0, 151 / 256.0, 28 / 256.0, 0, 0, // G column
533 77 / 256.0, 151 / 256.0, 28 / 256.0, 0, 0, // B column
534 0, 0, 0, 1, 0); // don't modify alpha
535 paint.setColorFilter(SkColorFilters::Matrix(toGray));
536 surface->getCanvas()->drawImage(mImage, 0, 0, SkSamplingOptions(), &paint);
537 mBitCount = 8;
538 ComputeScanlineSize();
539 mPalette = Bitmap::GetGreyPalette(256);
540 ResetToSkImage(makeCheckedImageSnapshot(surface));
541 DataChanged();
542 SAL_INFO("vcl.skia.trace", "converttogreyscale(" << this << ")");
543 return true;
545 return false;
548 bool SkiaSalBitmap::InterpretAs8Bit()
550 #ifdef DBG_UTIL
551 assert(mWriteAccessCount == 0);
552 #endif
553 if (mBitCount == 8 && mPalette.IsGreyPalette8Bit())
554 return true;
555 if (mEraseColorSet)
557 mBitCount = 8;
558 ComputeScanlineSize();
559 mPalette = Bitmap::GetGreyPalette(256);
560 EraseInternal(mEraseColor);
561 SAL_INFO("vcl.skia.trace", "interpretas8bit(" << this << ") with erase color");
562 return true;
564 // This is usually used by AlphaMask, the point is just to treat
565 // the content as an alpha channel. This is often used
566 // by the horrible separate-alpha-outdev hack, where the bitmap comes
567 // from SkiaSalGraphicsImpl::GetBitmap(), so only mImage is set,
568 // and that is followed by a later call to GetAlphaSkImage().
569 // Avoid the costly SkImage->buffer->SkImage conversion and simply
570 // just treat the SkImage as being for 8bit bitmap. EnsureBitmapData()
571 // will do the conversion if needed, but the normal case will be
572 // GetAlphaSkImage() creating kAlpha_8_SkColorType SkImage from it.
573 if (mImage)
575 mBitCount = 8;
576 ComputeScanlineSize();
577 mPalette = Bitmap::GetGreyPalette(256);
578 ResetToSkImage(mImage); // keep mImage, it will be interpreted as 8bit if needed
579 DataChanged();
580 SAL_INFO("vcl.skia.trace", "interpretas8bit(" << this << ") with image");
581 return true;
583 SAL_INFO("vcl.skia.trace", "interpretas8bit(" << this << ") with pixel data, ignoring");
584 return false;
587 bool SkiaSalBitmap::Erase(const Color& color)
589 #ifdef DBG_UTIL
590 assert(mWriteAccessCount == 0);
591 #endif
592 // Optimized variant, just remember the color and apply it when needed,
593 // which may save having to do format conversions (e.g. GetSkImage()
594 // may directly erase the SkImage).
595 EraseInternal(color);
596 SAL_INFO("vcl.skia.trace", "erase(" << this << ")");
597 return true;
600 void SkiaSalBitmap::EraseInternal(const Color& color)
602 ResetAllData();
603 mEraseColorSet = true;
604 mEraseColor = color;
607 bool SkiaSalBitmap::AlphaBlendWith(const SalBitmap& rSalBmp)
609 #ifdef DBG_UTIL
610 assert(mWriteAccessCount == 0);
611 #endif
612 const SkiaSalBitmap* otherBitmap = dynamic_cast<const SkiaSalBitmap*>(&rSalBmp);
613 if (!otherBitmap)
614 return false;
615 if (mSize != otherBitmap->mSize)
616 return false;
617 // We're called from AlphaMask, which should ensure 8bit.
618 assert(GetBitCount() == 8 && mPalette.IsGreyPalette8Bit());
619 // If neither bitmap have Skia images, then AlphaMask::BlendWith() will be faster,
620 // as it will operate on mBuffer pixel buffers, while for Skia we'd need to convert it.
621 // If one has and one doesn't, do it using Skia, under the assumption that after this
622 // the resulting Skia image will be needed for drawing.
623 if (!(mImage || mEraseColorSet) && !(otherBitmap->mImage || otherBitmap->mEraseColorSet))
624 return false;
625 // This is for AlphaMask, which actually stores the alpha as the pixel values.
626 // I.e. take value of the color channel (one of them, if >8bit, they should be the same).
627 if (mEraseColorSet && otherBitmap->mEraseColorSet)
629 const sal_uInt16 nGrey1 = mEraseColor.GetRed();
630 const sal_uInt16 nGrey2 = otherBitmap->mEraseColor.GetRed();
631 // See comment in AlphaMask::BlendWith for how this calculation was derived
632 const sal_uInt8 nGrey = static_cast<sal_uInt8>(nGrey1 * nGrey2 / 255);
633 mEraseColor = Color(nGrey, nGrey, nGrey);
634 DataChanged();
635 SAL_INFO("vcl.skia.trace",
636 "alphablendwith(" << this << ") : with erase color " << otherBitmap);
637 return true;
639 std::unique_ptr<SkiaSalBitmap> otherBitmapAllocated;
640 if (otherBitmap->GetBitCount() != 8 || !otherBitmap->mPalette.IsGreyPalette8Bit())
641 { // Convert/interpret as 8bit if needed.
642 otherBitmapAllocated = std::make_unique<SkiaSalBitmap>();
643 if (!otherBitmapAllocated->Create(*otherBitmap) || !otherBitmapAllocated->InterpretAs8Bit())
644 return false;
645 otherBitmap = otherBitmapAllocated.get();
647 // This is 8-bit bitmap serving as mask, so the image itself needs no alpha.
648 sk_sp<SkSurface> surface = createSkSurface(mSize, kOpaque_SkAlphaType);
649 SkPaint paint;
650 paint.setBlendMode(SkBlendMode::kSrc); // set as is
651 surface->getCanvas()->drawImage(GetSkImage(), 0, 0, SkSamplingOptions(), &paint);
652 // in the 0..1 range that skia uses, the equation we want is:
653 // r = 1 - ((1 - src) + (1 - dest) - (1 - src) * (1 - dest))
654 // which simplifies to:
655 // r = src * dest
656 // which is SkBlendMode::kModulate
657 paint.setBlendMode(SkBlendMode::kModulate);
658 surface->getCanvas()->drawImage(otherBitmap->GetSkImage(), 0, 0, SkSamplingOptions(), &paint);
659 ResetToSkImage(makeCheckedImageSnapshot(surface));
660 DataChanged();
661 SAL_INFO("vcl.skia.trace", "alphablendwith(" << this << ") : with image " << otherBitmap);
662 return true;
665 bool SkiaSalBitmap::Invert()
667 #ifdef DBG_UTIL
668 assert(mWriteAccessCount == 0);
669 #endif
670 // Normally this would need to convert contents of mBuffer for all possible formats,
671 // so just let the VCL algorithm do it.
672 // Avoid the costly SkImage->buffer->SkImage conversion.
673 if (!mBuffer && mImage && !mImageImmutable && !mEraseColorSet)
675 // This is 8-bit bitmap serving as alpha/transparency/mask, so the image itself needs no alpha.
676 // tdf#156866 use mSize instead of mPixelSize for inverted surface
677 // Commit 5baac4e53128d3c0fc73b9918dc9a9c2777ace08 switched to setting
678 // the surface size to mPixelsSize in an attempt to avoid downscaling
679 // mImage but since it causes tdf#156866, revert back to setting the
680 // surface size to mSize.
681 sk_sp<SkSurface> surface = createSkSurface(mSize, kOpaque_SkAlphaType);
682 surface->getCanvas()->clear(SK_ColorWHITE);
683 SkPaint paint;
684 paint.setBlendMode(SkBlendMode::kDifference);
685 // Drawing the image does not work so create a shader from the image
686 paint.setShader(GetSkShader(SkSamplingOptions()));
687 surface->getCanvas()->drawRect(SkRect::MakeXYWH(0, 0, mSize.Width(), mSize.Height()),
688 paint);
689 ResetToSkImage(makeCheckedImageSnapshot(surface));
690 DataChanged();
692 #ifdef MACOSX
693 // tdf#158014 make image immutable after using Skia to invert
694 // I can't explain why inverting using Skia causes this bug on
695 // macOS but not other platforms. My guess is that Skia on macOS
696 // is sharing some data when different SkiaSalBitmap instances
697 // are created from the same OutputDevice. So, mark this
698 // SkiaSalBitmap instance's image as immutable so that successive
699 // inversions are done with buffered bitmap data instead of Skia.
700 mImageImmutable = true;
701 #endif
703 SAL_INFO("vcl.skia.trace", "invert(" << this << ")");
704 return true;
706 return false;
709 SkBitmap SkiaSalBitmap::GetAsSkBitmap() const
711 #ifdef DBG_UTIL
712 assert(mWriteAccessCount == 0);
713 #endif
714 EnsureBitmapData();
715 assert(mSize == mPixelsSize); // data has already been scaled if needed
716 SkiaZone zone;
717 SkBitmap bitmap;
718 if (mBuffer)
720 if (mBitCount == 32)
722 // Make a copy, the bitmap should be immutable (otherwise converting it
723 // to SkImage will make a copy anyway).
724 const size_t bytes = mPixelsSize.Height() * mScanlineSize;
725 std::unique_ptr<sal_uInt8[]> data(new sal_uInt8[bytes]);
726 memcpy(data.get(), mBuffer.get(), bytes);
727 if (!bitmap.installPixels(
728 SkImageInfo::MakeS32(mPixelsSize.Width(), mPixelsSize.Height(), alphaType()),
729 data.release(), mScanlineSize,
730 [](void* addr, void*) { delete[] static_cast<sal_uInt8*>(addr); }, nullptr))
731 abort();
733 else if (mBitCount == 24)
735 // Convert 24bpp RGB/BGR to 32bpp RGBA/BGRA.
736 std::unique_ptr<uint32_t[]> data(
737 new uint32_t[mPixelsSize.Height() * mPixelsSize.Width()]);
738 uint32_t* dest = data.get();
739 // SkConvertRGBToRGBA() also works as BGR to BGRA (the function extends 3 bytes to 4
740 // by adding 0xFF alpha, so position of B and R doesn't matter).
741 if (mPixelsSize.Width() * 3 == mScanlineSize)
742 SkConvertRGBToRGBA(dest, mBuffer.get(), mPixelsSize.Height() * mPixelsSize.Width());
743 else
745 for (tools::Long y = 0; y < mPixelsSize.Height(); ++y)
747 const sal_uInt8* src = mBuffer.get() + mScanlineSize * y;
748 SkConvertRGBToRGBA(dest, src, mPixelsSize.Width());
749 dest += mPixelsSize.Width();
752 if (!bitmap.installPixels(
753 SkImageInfo::MakeS32(mPixelsSize.Width(), mPixelsSize.Height(),
754 kOpaque_SkAlphaType),
755 data.release(), mPixelsSize.Width() * 4,
756 [](void* addr, void*) { delete[] static_cast<sal_uInt8*>(addr); }, nullptr))
757 abort();
759 else if (mBitCount == 8 && mPalette.IsGreyPalette8Bit())
761 // Convert 8bpp gray to 32bpp RGBA/BGRA.
762 // There's also kGray_8_SkColorType, but it's probably simpler to make
763 // GetAsSkBitmap() always return 32bpp SkBitmap and then assume mImage
764 // is always 32bpp too.
765 std::unique_ptr<uint32_t[]> data(
766 new uint32_t[mPixelsSize.Height() * mPixelsSize.Width()]);
767 uint32_t* dest = data.get();
768 if (mPixelsSize.Width() * 1 == mScanlineSize)
769 SkConvertGrayToRGBA(dest, mBuffer.get(),
770 mPixelsSize.Height() * mPixelsSize.Width());
771 else
773 for (tools::Long y = 0; y < mPixelsSize.Height(); ++y)
775 const sal_uInt8* src = mBuffer.get() + mScanlineSize * y;
776 SkConvertGrayToRGBA(dest, src, mPixelsSize.Width());
777 dest += mPixelsSize.Width();
780 if (!bitmap.installPixels(
781 SkImageInfo::MakeS32(mPixelsSize.Width(), mPixelsSize.Height(),
782 kOpaque_SkAlphaType),
783 data.release(), mPixelsSize.Width() * 4,
784 [](void* addr, void*) { delete[] static_cast<sal_uInt8*>(addr); }, nullptr))
785 abort();
787 else
789 std::unique_ptr<sal_uInt8[]> data = convertDataBitCount(
790 mBuffer.get(), mPixelsSize.Width(), mPixelsSize.Height(), mBitCount, mScanlineSize,
791 mPalette, kN32_SkColorTypeIsBGRA ? BitConvert::BGRA : BitConvert::RGBA);
792 if (!bitmap.installPixels(
793 SkImageInfo::MakeS32(mPixelsSize.Width(), mPixelsSize.Height(),
794 kOpaque_SkAlphaType),
795 data.release(), mPixelsSize.Width() * 4,
796 [](void* addr, void*) { delete[] static_cast<sal_uInt8*>(addr); }, nullptr))
797 abort();
800 bitmap.setImmutable();
801 return bitmap;
804 // If mEraseColor is set, this is the color to use when the bitmap is used as alpha bitmap.
805 // E.g. COL_BLACK actually means fully transparent and COL_WHITE means fully opaque.
806 // This is because the alpha value is set as the color itself, not the alpha of the color.
807 static SkColor fromEraseColorToAlphaImageColor(Color color)
809 return SkColorSetARGB(color.GetBlue(), 0, 0, 0);
812 // SkiaSalBitmap can store data in both the SkImage and our mBuffer, which with large
813 // images can waste quite a lot of memory. Ideally we should store the data in Skia's
814 // SkBitmap, but LO wants us to support data formats that Skia doesn't support.
815 // So try to conserve memory by keeping the data only once in that was the most
816 // recently wanted storage, and drop the other one. Usually the other one won't be needed
817 // for a long time, and especially with raster the conversion is usually fast.
818 // Do this only with raster, to avoid GPU->CPU transfer in GPU mode (exception is 32bit
819 // builds, where memory is more important). Also don't do this with paletted bitmaps,
820 // where EnsureBitmapData() would be expensive.
821 // Ideally SalBitmap should be able to say which bitmap formats it supports
822 // and VCL code should oblige, which would allow reusing the same data.
823 bool SkiaSalBitmap::ConserveMemory() const
825 static bool keepBitmapBuffer = getenv("SAL_SKIA_KEEP_BITMAP_BUFFER") != nullptr;
826 constexpr bool is32Bit = sizeof(void*) == 4;
827 // 16MiB bitmap data at least (set to 0 for easy testing).
828 constexpr tools::Long maxBufferSize = 2000 * 2000 * 4;
829 return !keepBitmapBuffer && (renderMethodToUse() == RenderRaster || is32Bit)
830 && mPixelsSize.Height() * mScanlineSize > maxBufferSize
831 && (mBitCount > 8 || (mBitCount == 8 && mPalette.IsGreyPalette8Bit()));
834 const sk_sp<SkImage>& SkiaSalBitmap::GetSkImage(DirectImage direct) const
836 #ifdef DBG_UTIL
837 assert(mWriteAccessCount == 0);
838 #endif
839 if (direct == DirectImage::Yes)
840 return mImage;
841 if (mEraseColorSet)
843 if (mImage)
845 assert(imageSize(mImage) == mSize);
846 return mImage;
848 SkiaZone zone;
849 sk_sp<SkSurface> surface = createSkSurface(
850 mSize, mEraseColor.IsTransparent() ? kPremul_SkAlphaType : kOpaque_SkAlphaType);
851 assert(surface);
852 surface->getCanvas()->clear(toSkColor(mEraseColor));
853 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
854 thisPtr->mImage = makeCheckedImageSnapshot(surface);
855 SAL_INFO("vcl.skia.trace", "getskimage(" << this << ") from erase color " << mEraseColor);
856 return mImage;
858 if (mPixelsSize != mSize && !mImage && renderMethodToUse() != RenderRaster)
860 // The bitmap has a pending scaling, but no image. This function would below call GetAsSkBitmap(),
861 // which would do CPU-based pixel scaling, and then it would get converted to an image.
862 // Be more efficient, first convert to an image and then the block below will scale on the GPU.
863 SAL_INFO("vcl.skia.trace", "getskimage(" << this << "): shortcut image scaling "
864 << mPixelsSize << "->" << mSize);
865 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
866 Size savedSize = mSize;
867 thisPtr->mSize = mPixelsSize; // block scaling
868 SkiaZone zone;
869 sk_sp<SkImage> image = createSkImage(GetAsSkBitmap());
870 assert(image);
871 thisPtr->mSize = savedSize;
872 thisPtr->ResetToSkImage(image);
874 if (mImage)
876 if (imageSize(mImage) != mSize)
878 assert(!mBuffer); // This code should be only called if only mImage holds data.
879 SkiaZone zone;
880 sk_sp<SkSurface> surface = createSkSurface(mSize, mImage->imageInfo().alphaType());
881 assert(surface);
882 SkPaint paint;
883 paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
884 surface->getCanvas()->drawImageRect(
885 mImage, SkRect::MakeWH(mSize.Width(), mSize.Height()),
886 makeSamplingOptions(mScaleQuality, imageSize(mImage), mSize, 1), &paint);
887 SAL_INFO("vcl.skia.trace", "getskimage(" << this << "): image scaled "
888 << Size(mImage->width(), mImage->height())
889 << "->" << mSize << ":"
890 << static_cast<int>(mScaleQuality));
891 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
892 thisPtr->mImage = makeCheckedImageSnapshot(surface);
894 return mImage;
896 SkiaZone zone;
897 sk_sp<SkImage> image = createSkImage(GetAsSkBitmap());
898 assert(image);
899 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
900 thisPtr->mImage = image;
901 // The data is now stored both in the SkImage and in our mBuffer, so drop the buffer
902 // if conserving memory. It'll be converted back by EnsureBitmapData() if needed.
903 if (ConserveMemory() && mReadAccessCount == 0)
905 SAL_INFO("vcl.skia.trace", "getskimage(" << this << "): dropping buffer");
906 thisPtr->ResetToSkImage(mImage);
908 SAL_INFO("vcl.skia.trace", "getskimage(" << this << ")");
909 return mImage;
912 const sk_sp<SkImage>& SkiaSalBitmap::GetAlphaSkImage(DirectImage direct) const
914 #ifdef DBG_UTIL
915 assert(mWriteAccessCount == 0);
916 #endif
917 if (direct == DirectImage::Yes)
918 return mAlphaImage;
919 if (mEraseColorSet)
921 if (mAlphaImage)
923 assert(imageSize(mAlphaImage) == mSize);
924 return mAlphaImage;
926 SkiaZone zone;
927 sk_sp<SkSurface> surface = createSkSurface(mSize, kAlpha_8_SkColorType);
928 assert(surface);
929 surface->getCanvas()->clear(fromEraseColorToAlphaImageColor(mEraseColor));
930 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
931 thisPtr->mAlphaImage = makeCheckedImageSnapshot(surface);
932 SAL_INFO("vcl.skia.trace",
933 "getalphaskimage(" << this << ") from erase color " << mEraseColor);
934 return mAlphaImage;
936 if (mAlphaImage)
938 if (imageSize(mAlphaImage) == mSize)
939 return mAlphaImage;
941 if (mImage)
943 SkiaZone zone;
944 const bool scaling = imageSize(mImage) != mSize;
945 SkPixmap pixmap;
946 if (mImage->peekPixels(&pixmap))
948 assert(pixmap.colorType() == kN32_SkColorType);
949 // In non-GPU mode, convert 32bit data to 8bit alpha, this is faster than
950 // the SkColorFilter below. Since this is the VCL alpha-vdev alpha, where
951 // all R,G,B are the same and in fact mean alpha, this means we simply take one
952 // 8bit channel from the input, and that's the output.
953 SkBitmap bitmap;
954 if (!bitmap.installPixels(pixmap))
955 abort();
956 SkBitmap alphaBitmap;
957 if (!alphaBitmap.tryAllocPixels(SkImageInfo::MakeA8(bitmap.width(), bitmap.height())))
958 abort();
959 if (int(bitmap.rowBytes()) == bitmap.width() * 4)
961 SkConvertRGBAToR(alphaBitmap.getAddr8(0, 0), bitmap.getAddr32(0, 0),
962 bitmap.width() * bitmap.height());
964 else
966 for (tools::Long y = 0; y < bitmap.height(); ++y)
967 SkConvertRGBAToR(alphaBitmap.getAddr8(0, y), bitmap.getAddr32(0, y),
968 bitmap.width());
970 alphaBitmap.setImmutable();
971 sk_sp<SkImage> alphaImage = createSkImage(alphaBitmap);
972 assert(alphaImage);
973 SAL_INFO("vcl.skia.trace", "getalphaskimage(" << this << ") from raster image");
974 // Don't bother here with ConserveMemory(), mImage -> mAlphaImage conversions should
975 // generally only happen with the separate-alpha-outdev hack, and those bitmaps should
976 // be temporary.
977 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
978 thisPtr->mAlphaImage = alphaImage;
979 // Fix testDelayedScaleAlphaImage unit test
980 // Do not return the alpha mask if it is awaiting pending scaling.
981 // Pending scaling has not yet been done at this point since the
982 // scaling is done in the code following this block.
983 if (!scaling)
984 return mAlphaImage;
986 // Move the R channel value to the alpha channel. This seems to be the only
987 // way to reinterpret data in SkImage as an alpha SkImage without accessing the pixels.
988 // NOTE: The matrix is 4x5 organized as columns (i.e. each line is a column, not a row).
989 constexpr SkColorMatrix redToAlpha(0, 0, 0, 0, 0, // R column
990 0, 0, 0, 0, 0, // G column
991 0, 0, 0, 0, 0, // B column
992 1, 0, 0, 0, 0); // A column
993 SkPaint paint;
994 paint.setColorFilter(SkColorFilters::Matrix(redToAlpha));
995 if (scaling)
996 assert(!mBuffer); // This code should be only called if only mImage holds data.
997 sk_sp<SkSurface> surface = createSkSurface(mSize, kAlpha_8_SkColorType);
998 assert(surface);
999 paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
1000 surface->getCanvas()->drawImageRect(
1001 mImage, SkRect::MakeWH(mSize.Width(), mSize.Height()),
1002 scaling ? makeSamplingOptions(mScaleQuality, imageSize(mImage), mSize, 1)
1003 : SkSamplingOptions(),
1004 &paint);
1005 if (scaling)
1006 SAL_INFO("vcl.skia.trace", "getalphaskimage(" << this << "): image scaled "
1007 << Size(mImage->width(), mImage->height())
1008 << "->" << mSize << ":"
1009 << static_cast<int>(mScaleQuality));
1010 else
1011 SAL_INFO("vcl.skia.trace", "getalphaskimage(" << this << ") from image");
1012 // Don't bother here with ConserveMemory(), mImage -> mAlphaImage conversions should
1013 // generally only happen with the separate-alpha-outdev hack, and those bitmaps should
1014 // be temporary.
1015 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
1016 thisPtr->mAlphaImage = makeCheckedImageSnapshot(surface);
1017 return mAlphaImage;
1019 SkiaZone zone;
1020 EnsureBitmapData();
1021 assert(mSize == mPixelsSize); // data has already been scaled if needed
1022 SkBitmap alphaBitmap;
1023 if (mBuffer && mBitCount <= 8)
1025 assert(mBuffer.get());
1026 verify();
1027 std::unique_ptr<sal_uInt8[]> data
1028 = convertDataBitCount(mBuffer.get(), mSize.Width(), mSize.Height(), mBitCount,
1029 mScanlineSize, mPalette, BitConvert::A8);
1030 if (!alphaBitmap.installPixels(
1031 SkImageInfo::MakeA8(mSize.Width(), mSize.Height()), data.release(), mSize.Width(),
1032 [](void* addr, void*) { delete[] static_cast<sal_uInt8*>(addr); }, nullptr))
1033 abort();
1034 alphaBitmap.setImmutable();
1035 sk_sp<SkImage> image = createSkImage(alphaBitmap);
1036 assert(image);
1037 const_cast<sk_sp<SkImage>&>(mAlphaImage) = image;
1039 else
1041 sk_sp<SkSurface> surface = createSkSurface(mSize, kAlpha_8_SkColorType);
1042 assert(surface);
1043 SkPaint paint;
1044 paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
1045 // Move the R channel value to the alpha channel. This seems to be the only
1046 // way to reinterpret data in SkImage as an alpha SkImage without accessing the pixels.
1047 // NOTE: The matrix is 4x5 organized as columns (i.e. each line is a column, not a row).
1048 constexpr SkColorMatrix redToAlpha(0, 0, 0, 0, 0, // R column
1049 0, 0, 0, 0, 0, // G column
1050 0, 0, 0, 0, 0, // B column
1051 1, 0, 0, 0, 0); // A column
1052 paint.setColorFilter(SkColorFilters::Matrix(redToAlpha));
1053 surface->getCanvas()->drawImage(GetAsSkBitmap().asImage(), 0, 0, SkSamplingOptions(),
1054 &paint);
1055 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
1056 thisPtr->mAlphaImage = makeCheckedImageSnapshot(surface);
1058 // The data is now stored both in the SkImage and in our mBuffer, so drop the buffer
1059 // if conserving memory and the conversion back would be simple (it'll be converted back
1060 // by EnsureBitmapData() if needed).
1061 if (ConserveMemory() && mBitCount == 8 && mPalette.IsGreyPalette8Bit() && mReadAccessCount == 0)
1063 SAL_INFO("vcl.skia.trace", "getalphaskimage(" << this << "): dropping buffer");
1064 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
1065 thisPtr->mBuffer.reset();
1067 SAL_INFO("vcl.skia.trace", "getalphaskimage(" << this << ")");
1068 return mAlphaImage;
1071 void SkiaSalBitmap::TryDirectConvertToAlphaNoScaling()
1073 // This is a bit of a hack. Because of the VCL alpha hack where alpha is stored
1074 // separately, we often convert mImage to mAlphaImage to represent the alpha
1075 // channel. If code finds out that there is mImage but no mAlphaImage,
1076 // this will create it from it, without checking for delayed scaling (i.e.
1077 // it is "direct").
1078 assert(mImage);
1079 assert(!mAlphaImage);
1080 // Set wanted size, trigger conversion.
1081 Size savedSize = mSize;
1082 mSize = imageSize(mImage);
1083 GetAlphaSkImage();
1084 assert(mAlphaImage);
1085 mSize = savedSize;
1088 // If the bitmap is to be erased, SkShader with the color set is more efficient
1089 // than creating an image filled with the color.
1090 bool SkiaSalBitmap::PreferSkShader() const { return mEraseColorSet; }
1092 sk_sp<SkShader> SkiaSalBitmap::GetSkShader(const SkSamplingOptions& samplingOptions,
1093 DirectImage direct) const
1095 if (mEraseColorSet)
1096 return SkShaders::Color(toSkColor(mEraseColor));
1097 return GetSkImage(direct)->makeShader(samplingOptions);
1100 sk_sp<SkShader> SkiaSalBitmap::GetAlphaSkShader(const SkSamplingOptions& samplingOptions,
1101 DirectImage direct) const
1103 if (mEraseColorSet)
1104 return SkShaders::Color(fromEraseColorToAlphaImageColor(mEraseColor));
1105 return GetAlphaSkImage(direct)->makeShader(samplingOptions);
1108 bool SkiaSalBitmap::IsFullyOpaqueAsAlpha() const
1110 if (!mEraseColorSet) // Set from Erase() or ReleaseBuffer().
1111 return false;
1112 // If the erase color is set so that this bitmap used as alpha would
1113 // mean a fully opaque alpha mask (= noop), we can skip using it.
1114 return SkColorGetA(fromEraseColorToAlphaImageColor(mEraseColor)) == 255;
1117 SkAlphaType SkiaSalBitmap::alphaType() const
1119 if (mEraseColorSet)
1120 return mEraseColor.IsTransparent() ? kPremul_SkAlphaType : kOpaque_SkAlphaType;
1121 #if SKIA_USE_BITMAP32
1122 // The bitmap's alpha matters only if SKIA_USE_BITMAP32 is set, otherwise
1123 // the alpha is in a separate bitmap.
1124 if (mBitCount == 32)
1125 return kPremul_SkAlphaType;
1126 #endif
1127 return kOpaque_SkAlphaType;
1130 void SkiaSalBitmap::PerformErase()
1132 if (mPixelsSize.IsEmpty())
1133 return;
1134 BitmapBuffer* bitmapBuffer = AcquireBuffer(BitmapAccessMode::Write);
1135 if (bitmapBuffer == nullptr)
1136 abort();
1137 Color fastColor = mEraseColor;
1138 if (!!mPalette)
1139 fastColor = Color(ColorAlpha, mPalette.GetBestIndex(fastColor));
1140 if (!ImplFastEraseBitmap(*bitmapBuffer, fastColor))
1142 FncSetPixel setPixel = BitmapReadAccess::SetPixelFunction(bitmapBuffer->mnFormat);
1143 assert(bitmapBuffer->mnFormat & ScanlineFormat::TopDown);
1144 // Set first scanline, copy to others.
1145 Scanline scanline = bitmapBuffer->mpBits;
1146 for (tools::Long x = 0; x < bitmapBuffer->mnWidth; ++x)
1147 setPixel(scanline, x, mEraseColor, bitmapBuffer->maColorMask);
1148 for (tools::Long y = 1; y < bitmapBuffer->mnHeight; ++y)
1149 memcpy(scanline + y * bitmapBuffer->mnScanlineSize, scanline,
1150 bitmapBuffer->mnScanlineSize);
1152 ReleaseBuffer(bitmapBuffer, BitmapAccessMode::Write, true);
1155 void SkiaSalBitmap::EnsureBitmapData()
1157 if (mEraseColorSet)
1159 SkiaZone zone;
1160 assert(mPixelsSize == mSize);
1161 assert(!mBuffer);
1162 CreateBitmapData();
1163 // Unset now, so that repeated call will return mBuffer.
1164 mEraseColorSet = false;
1165 PerformErase();
1166 verify();
1167 SAL_INFO("vcl.skia.trace",
1168 "ensurebitmapdata(" << this << ") from erase color " << mEraseColor);
1169 return;
1172 if (mBuffer)
1174 if (mSize == mPixelsSize)
1175 return;
1176 // Pending scaling. Create raster SkImage from the bitmap data
1177 // at the pixel size and then the code below will scale at the correct
1178 // bpp from the image.
1179 SAL_INFO("vcl.skia.trace", "ensurebitmapdata(" << this << "): pixels to be scaled "
1180 << mPixelsSize << "->" << mSize << ":"
1181 << static_cast<int>(mScaleQuality));
1182 Size savedSize = mSize;
1183 mSize = mPixelsSize;
1184 ResetToSkImage(SkImages::RasterFromBitmap(GetAsSkBitmap()));
1185 mSize = savedSize;
1188 // Convert from alpha image, if the conversion is simple.
1189 if (mAlphaImage && imageSize(mAlphaImage) == mSize && mBitCount == 8
1190 && mPalette.IsGreyPalette8Bit())
1192 assert(mAlphaImage->colorType() == kAlpha_8_SkColorType);
1193 SkiaZone zone;
1194 SkBitmap bitmap;
1195 SkPixmap pixmap;
1196 if (mAlphaImage->peekPixels(&pixmap))
1197 bitmap.installPixels(pixmap);
1198 else
1200 if (!bitmap.tryAllocPixels(SkImageInfo::MakeA8(mSize.Width(), mSize.Height())))
1201 abort();
1202 SkCanvas canvas(bitmap);
1203 SkPaint paint;
1204 paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
1205 canvas.drawImage(mAlphaImage, 0, 0, SkSamplingOptions(), &paint);
1206 canvas.flush();
1208 bitmap.setImmutable();
1209 ResetPendingScaling();
1210 CreateBitmapData();
1211 assert(mBuffer != nullptr);
1212 assert(mPixelsSize == mSize);
1213 if (int(bitmap.rowBytes()) == mScanlineSize)
1214 memcpy(mBuffer.get(), bitmap.getPixels(), mSize.Height() * mScanlineSize);
1215 else
1217 for (tools::Long y = 0; y < mSize.Height(); ++y)
1219 const uint8_t* src = static_cast<uint8_t*>(bitmap.getAddr(0, y));
1220 sal_uInt8* dest = mBuffer.get() + mScanlineSize * y;
1221 memcpy(dest, src, mScanlineSize);
1224 verify();
1225 // We've created the bitmap data from mAlphaImage, drop the image if conserving memory,
1226 // it'll be converted back if needed.
1227 if (ConserveMemory())
1229 SAL_INFO("vcl.skia.trace", "ensurebitmapdata(" << this << "): dropping images");
1230 ResetToBuffer();
1232 SAL_INFO("vcl.skia.trace", "ensurebitmapdata(" << this << "): from alpha image");
1233 return;
1236 if (!mImage)
1238 // No data at all, create uninitialized data.
1239 CreateBitmapData();
1240 SAL_INFO("vcl.skia.trace", "ensurebitmapdata(" << this << "): uninitialized");
1241 return;
1243 // Try to fill mBuffer from mImage.
1244 assert(mImage->colorType() == kN32_SkColorType);
1245 SkiaZone zone;
1246 // If the source image has no alpha, then use no alpha (faster to convert), otherwise
1247 // use kUnpremul_SkAlphaType to make Skia convert from premultiplied alpha when reading
1248 // from the SkImage (the alpha will be ignored if converting to bpp<32 formats, but
1249 // the color channels must be unpremultiplied. Unless bpp==32 and SKIA_USE_BITMAP32,
1250 // in which case use kPremul_SkAlphaType, since SKIA_USE_BITMAP32 implies premultiplied alpha.
1251 SkAlphaType alphaType = kUnpremul_SkAlphaType;
1252 if (mImage->imageInfo().alphaType() == kOpaque_SkAlphaType)
1253 alphaType = kOpaque_SkAlphaType;
1254 #if SKIA_USE_BITMAP32
1255 if (mBitCount == 32)
1256 alphaType = kPremul_SkAlphaType;
1257 #endif
1258 SkBitmap bitmap;
1259 SkPixmap pixmap;
1260 if (imageSize(mImage) == mSize && mImage->imageInfo().alphaType() == alphaType
1261 && mImage->peekPixels(&pixmap))
1263 bitmap.installPixels(pixmap);
1265 else
1267 if (!bitmap.tryAllocPixels(SkImageInfo::MakeS32(mSize.Width(), mSize.Height(), alphaType)))
1268 abort();
1269 SkCanvas canvas(bitmap);
1270 SkPaint paint;
1271 paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
1272 if (imageSize(mImage) != mSize) // pending scaling?
1274 canvas.drawImageRect(mImage, SkRect::MakeWH(mSize.getWidth(), mSize.getHeight()),
1275 makeSamplingOptions(mScaleQuality, imageSize(mImage), mSize, 1),
1276 &paint);
1277 SAL_INFO("vcl.skia.trace",
1278 "ensurebitmapdata(" << this << "): image scaled " << imageSize(mImage) << "->"
1279 << mSize << ":" << static_cast<int>(mScaleQuality));
1281 else
1282 canvas.drawImage(mImage, 0, 0, SkSamplingOptions(), &paint);
1283 canvas.flush();
1285 bitmap.setImmutable();
1286 ResetPendingScaling();
1287 CreateBitmapData();
1288 assert(mBuffer != nullptr);
1289 assert(mPixelsSize == mSize);
1290 if (mBitCount == 32)
1292 if (int(bitmap.rowBytes()) == mScanlineSize)
1293 memcpy(mBuffer.get(), bitmap.getPixels(), mSize.Height() * mScanlineSize);
1294 else
1296 for (tools::Long y = 0; y < mSize.Height(); ++y)
1298 const uint8_t* src = static_cast<uint8_t*>(bitmap.getAddr(0, y));
1299 sal_uInt8* dest = mBuffer.get() + mScanlineSize * y;
1300 memcpy(dest, src, mScanlineSize);
1304 else if (mBitCount == 24) // non-paletted
1306 if (int(bitmap.rowBytes()) == mSize.Width() * 4 && mSize.Width() * 3 == mScanlineSize)
1308 SkConvertRGBAToRGB(mBuffer.get(), bitmap.getAddr32(0, 0),
1309 mSize.Height() * mSize.Width());
1311 else
1313 for (tools::Long y = 0; y < mSize.Height(); ++y)
1315 const uint32_t* src = bitmap.getAddr32(0, y);
1316 sal_uInt8* dest = mBuffer.get() + mScanlineSize * y;
1317 SkConvertRGBAToRGB(dest, src, mSize.Width());
1321 else if (mBitCount == 8 && mPalette.IsGreyPalette8Bit())
1322 { // no actual data conversion, use one color channel as the gray value
1323 if (int(bitmap.rowBytes()) == mSize.Width() * 4 && mSize.Width() * 1 == mScanlineSize)
1325 SkConvertRGBAToR(mBuffer.get(), bitmap.getAddr32(0, 0), mSize.Height() * mSize.Width());
1327 else
1329 for (tools::Long y = 0; y < mSize.Height(); ++y)
1331 const uint32_t* src = bitmap.getAddr32(0, y);
1332 sal_uInt8* dest = mBuffer.get() + mScanlineSize * y;
1333 SkConvertRGBAToR(dest, src, mSize.Width());
1337 else
1339 std::unique_ptr<vcl::ScanlineWriter> pWriter
1340 = vcl::ScanlineWriter::Create(mBitCount, mPalette);
1341 for (tools::Long y = 0; y < mSize.Height(); ++y)
1343 const uint8_t* src = static_cast<uint8_t*>(bitmap.getAddr(0, y));
1344 sal_uInt8* dest = mBuffer.get() + mScanlineSize * y;
1345 pWriter->nextLine(dest);
1346 for (tools::Long x = 0; x < mSize.Width(); ++x)
1348 sal_uInt8 r = *src++;
1349 sal_uInt8 g = *src++;
1350 sal_uInt8 b = *src++;
1351 ++src; // skip alpha
1352 pWriter->writeRGB(r, g, b);
1356 verify();
1357 // We've created the bitmap data from mImage, drop the image if conserving memory,
1358 // it'll be converted back if needed.
1359 if (ConserveMemory())
1361 SAL_INFO("vcl.skia.trace", "ensurebitmapdata(" << this << "): dropping images");
1362 ResetToBuffer();
1364 SAL_INFO("vcl.skia.trace", "ensurebitmapdata(" << this << ")");
1367 void SkiaSalBitmap::EnsureBitmapUniqueData()
1369 #ifdef DBG_UTIL
1370 assert(mWriteAccessCount == 0);
1371 #endif
1372 EnsureBitmapData();
1373 assert(mPixelsSize == mSize);
1374 if (mBuffer.use_count() > 1)
1376 sal_uInt32 allocate = mScanlineSize * mSize.Height();
1377 #ifdef DBG_UTIL
1378 assert(memcmp(mBuffer.get() + allocate, CANARY, sizeof(CANARY)) == 0);
1379 allocate += sizeof(CANARY);
1380 #endif
1381 boost::shared_ptr<sal_uInt8[]> newBuffer = boost::make_shared_noinit<sal_uInt8[]>(allocate);
1382 memcpy(newBuffer.get(), mBuffer.get(), allocate);
1383 mBuffer = newBuffer;
1387 void SkiaSalBitmap::ResetToBuffer()
1389 SkiaZone zone;
1390 // This should never be called to drop mImage if that's the only data we have.
1391 assert(mBuffer || !mImage);
1392 mImage.reset();
1393 mImageImmutable = false;
1394 mAlphaImage.reset();
1395 mEraseColorSet = false;
1398 void SkiaSalBitmap::ResetToSkImage(sk_sp<SkImage> image)
1400 assert(mReadAccessCount == 0); // can't reset mBuffer if there's a read access pointing to it
1401 SkiaZone zone;
1402 mBuffer.reset();
1403 // Just to be safe, assume mutability of the image does not change
1404 mImage = image;
1405 mAlphaImage.reset();
1406 mEraseColorSet = false;
1409 void SkiaSalBitmap::ResetAllData()
1411 assert(mReadAccessCount == 0);
1412 SkiaZone zone;
1413 mBuffer.reset();
1414 mImage.reset();
1415 mImageImmutable = false;
1416 mAlphaImage.reset();
1417 mEraseColorSet = false;
1418 mPixelsSize = mSize;
1419 ComputeScanlineSize();
1420 DataChanged();
1423 void SkiaSalBitmap::DataChanged() { InvalidateChecksum(); }
1425 void SkiaSalBitmap::ResetPendingScaling()
1427 if (mPixelsSize == mSize)
1428 return;
1429 SkiaZone zone;
1430 mScaleQuality = BmpScaleFlag::BestQuality;
1431 mPixelsSize = mSize;
1432 ComputeScanlineSize();
1433 // Information about the pending scaling has been discarded, so make sure we do not
1434 // keep around any cached images that would still need scaling.
1435 if (mImage && imageSize(mImage) != mSize)
1437 mImage.reset();
1438 mImageImmutable = false;
1440 if (mAlphaImage && imageSize(mAlphaImage) != mSize)
1441 mAlphaImage.reset();
1444 OString SkiaSalBitmap::GetImageKey(DirectImage direct) const
1446 if (mEraseColorSet)
1448 std::stringstream ss;
1449 ss << std::hex << std::setfill('0') << std::setw(6)
1450 << static_cast<sal_uInt32>(mEraseColor.GetRGBColor()) << std::setw(2)
1451 << static_cast<int>(mEraseColor.GetAlpha());
1452 return OString::Concat("E") + ss.str().c_str();
1454 assert(direct == DirectImage::No || mImage);
1455 sk_sp<SkImage> image = GetSkImage(direct);
1456 // In some cases drawing code may try to draw the same content but using
1457 // different bitmaps (even underlying bitmaps), for example canvas apparently
1458 // copies the same things around in tdf#146095. For pixel-based images
1459 // it should be still cheaper to compute a checksum and avoid re-caching.
1460 if (!image->isTextureBacked())
1461 return OString::Concat("C") + OString::number(getSkImageChecksum(image));
1462 return OString::Concat("I") + OString::number(image->uniqueID());
1465 OString SkiaSalBitmap::GetAlphaImageKey(DirectImage direct) const
1467 if (mEraseColorSet)
1469 std::stringstream ss;
1470 ss << std::hex << std::setfill('0') << std::setw(2)
1471 << static_cast<int>(SkColorGetA(fromEraseColorToAlphaImageColor(mEraseColor)));
1472 return OString::Concat("E") + ss.str().c_str();
1474 assert(direct == DirectImage::No || mAlphaImage);
1475 sk_sp<SkImage> image = GetAlphaSkImage(direct);
1476 if (!image->isTextureBacked())
1477 return OString::Concat("C") + OString::number(getSkImageChecksum(image));
1478 return OString::Concat("I") + OString::number(image->uniqueID());
1481 void SkiaSalBitmap::dump(const char* file) const
1483 // Use a copy, so that debugging doesn't affect this instance.
1484 SkiaSalBitmap copy;
1485 copy.Create(*this);
1486 SkiaHelper::dump(copy.GetSkImage(), file);
1489 #ifdef DBG_UTIL
1490 void SkiaSalBitmap::verify() const
1492 if (!mBuffer)
1493 return;
1494 // Use mPixelsSize, that describes the size of the actual data.
1495 assert(memcmp(mBuffer.get() + mScanlineSize * mPixelsSize.Height(), CANARY, sizeof(CANARY))
1496 == 0);
1498 #endif
1500 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */