1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
27 #include <salinst.hxx>
28 #include <scanlinewriter.hxx>
30 #include <bitmap/bmpfast.hxx>
31 #include <vcl/BitmapReadAccess.hxx>
33 #include <skia/utils.hxx>
34 #include <skia/zone.hxx>
39 #include <SkPixelRef.h>
41 #include <SkSurface.h>
42 #include <SkSwizzle.h>
43 #include <SkColorFilter.h>
44 #include <SkColorMatrix.h>
45 #include <skia_opts.hxx>
49 #define CANARY "skia-canary"
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
)
65 mPalette
= BitmapPalette();
71 mSize
= mPixelsSize
= Size(image
->width(), image
->height());
72 ComputeScanlineSize();
75 mWriteAccessCount
= 0;
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);
85 if (ePixelFormat
== vcl::PixelFormat::INVALID
)
88 mBitCount
= vcl::pixelFormatBitCount(ePixelFormat
);
90 ResetPendingScaling();
91 if (!ComputeScanlineSize())
94 mSize
= mPixelsSize
= Size();
96 mPalette
= BitmapPalette();
99 SAL_INFO("vcl.skia.trace", "create(" << this << ")");
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");
111 mScanlineSize
= AlignedWidth4Bytes(bitScanlineWidth
);
115 void SkiaSalBitmap::CreateBitmapData()
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)
130 size_t allocate
= mScanlineSize
* mPixelsSize
.Height();
132 allocate
+= sizeof(CANARY
);
134 mBuffer
= boost::make_shared_noinit
<sal_uInt8
[]>(allocate
);
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
));
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
;
153 ePixelFormat
= vcl::bitDepthToPixelFormat(pGraphics
->GetBitCount());
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);
165 const SkiaSalBitmap
& src
= static_cast<const SkiaSalBitmap
&>(rSalBmp
);
167 mImageImmutable
= src
.mImageImmutable
;
168 mAlphaImage
= src
.mAlphaImage
;
169 mBuffer
= src
.mBuffer
;
170 mPalette
= src
.mPalette
;
171 mBitCount
= src
.mBitCount
;
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
184 ResetToSkImage(GetSkImage());
186 SAL_INFO("vcl.skia.trace", "create(" << this << "): (" << &src
<< ")");
190 bool SkiaSalBitmap::Create(const css::uno::Reference
<css::rendering::XBitmapCanvas
>&, Size
&, bool)
195 void SkiaSalBitmap::Destroy()
197 SAL_INFO("vcl.skia.trace", "destroy(" << this << ")");
199 assert(mWriteAccessCount
== 0);
201 assert(mReadAccessCount
== 0);
205 Size
SkiaSalBitmap::GetSize() const { return mSize
; }
207 sal_uInt16
SkiaSalBitmap::GetBitCount() const { return mBitCount
; }
209 BitmapBuffer
* SkiaSalBitmap::AcquireBuffer(BitmapAccessMode nMode
)
213 case BitmapAccessMode::Write
:
214 EnsureBitmapUniqueData();
217 assert(mPixelsSize
== mSize
);
218 assert(!mEraseColorSet
);
220 case BitmapAccessMode::Read
:
224 assert(mPixelsSize
== mSize
);
225 assert(!mEraseColorSet
);
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
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
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;
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
);
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();
273 buffer
->mpBits
= nullptr;
274 if (mPixelsSize
== mSize
)
275 buffer
->mnScanlineSize
= mScanlineSize
;
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
;
283 ComputeScanlineSize();
284 buffer
->mnScanlineSize
= mScanlineSize
;
285 mPixelsSize
= savedPixelsSize
;
286 ComputeScanlineSize();
291 buffer
->mnFormat
= ScanlineFormat::N1BitMsbPal
;
294 buffer
->mnFormat
= ScanlineFormat::N8BitPal
;
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
;
303 buffer
->mnFormat
= kN32_SkColorTypeIsBGRA
? ScanlineFormat::N32BitTcBgra
304 : ScanlineFormat::N32BitTcRgba
;
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
)
317 if (nMode
== BitmapAccessMode::Write
)
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
)
334 assert(mWriteAccessCount
> 0);
337 mPalette
= pBuffer
->maPalette
;
341 if (nMode
== BitmapAccessMode::Read
|| nMode
== BitmapAccessMode::Write
)
343 assert(mReadAccessCount
> 0);
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
);
353 if (nMode
== BitmapAccessMode::Write
&& !dontChangeToErase
)
355 // This saves memory and is also used by IsFullyOpaqueAsAlpha() to avoid unnecessary
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.
368 const int64_t* d
= reinterpret_cast<const int64_t*>(data
);
370 const int32_t* d
= reinterpret_cast<const int32_t*>(data
);
372 constexpr size_t step
= sizeof(*d
) * 8;
373 for (size_t i
= 0; i
< size
/ step
; ++i
)
393 for (size_t i
= size
/ step
* step
; i
< size
; ++i
)
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))
411 bool SkiaSalBitmap::GetSystemData(BitmapSystemData
&)
414 assert(mWriteAccessCount
== 0);
419 bool SkiaSalBitmap::ScalingSupported() const { return true; }
421 bool SkiaSalBitmap::Scale(const double& rScaleX
, const double& rScaleY
, BmpScaleFlag nScaleFlag
)
425 assert(mWriteAccessCount
== 0);
427 Size
newSize(FRound(mSize
.Width() * rScaleX
), FRound(mSize
.Height() * rScaleY
));
428 if (mSize
== newSize
)
431 SAL_INFO("vcl.skia.trace", "scale(" << this << "): " << mSize
<< "/" << mBitCount
<< "->"
432 << newSize
<< ":" << static_cast<int>(nScaleFlag
));
437 ResetPendingScaling();
438 EraseInternal(mEraseColor
);
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");
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.
469 case BmpScaleFlag::Fast
:
470 mScaleQuality
= nScaleFlag
;
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
;
477 case BmpScaleFlag::Default
:
478 if (mScaleQuality
== BmpScaleFlag::BestQuality
)
479 mScaleQuality
= nScaleFlag
;
481 case BmpScaleFlag::BestQuality
:
482 // Best is the maximum, set by default.
485 SAL_INFO("vcl.skia.trace", "scale(" << this << "): unsupported scale algorithm");
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.
494 ResetToSkImage(mImage
);
498 // The rest will be handled when the scaled bitmap is actually needed,
499 // such as in EnsureBitmapData() or GetSkImage().
503 bool SkiaSalBitmap::Replace(const Color
&, const Color
&, sal_uInt8
)
506 assert(mWriteAccessCount
== 0);
511 bool SkiaSalBitmap::ConvertToGreyscale()
514 assert(mWriteAccessCount
== 0);
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())
523 sk_sp
<SkSurface
> surface
524 = createSkSurface(imageSize(mImage
), mImage
->imageInfo().alphaType());
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
);
538 ComputeScanlineSize();
539 mPalette
= Bitmap::GetGreyPalette(256);
540 ResetToSkImage(makeCheckedImageSnapshot(surface
));
542 SAL_INFO("vcl.skia.trace", "converttogreyscale(" << this << ")");
548 bool SkiaSalBitmap::InterpretAs8Bit()
551 assert(mWriteAccessCount
== 0);
553 if (mBitCount
== 8 && mPalette
.IsGreyPalette8Bit())
558 ComputeScanlineSize();
559 mPalette
= Bitmap::GetGreyPalette(256);
560 EraseInternal(mEraseColor
);
561 SAL_INFO("vcl.skia.trace", "interpretas8bit(" << this << ") with erase color");
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.
576 ComputeScanlineSize();
577 mPalette
= Bitmap::GetGreyPalette(256);
578 ResetToSkImage(mImage
); // keep mImage, it will be interpreted as 8bit if needed
580 SAL_INFO("vcl.skia.trace", "interpretas8bit(" << this << ") with image");
583 SAL_INFO("vcl.skia.trace", "interpretas8bit(" << this << ") with pixel data, ignoring");
587 bool SkiaSalBitmap::Erase(const Color
& color
)
590 assert(mWriteAccessCount
== 0);
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 << ")");
600 void SkiaSalBitmap::EraseInternal(const Color
& color
)
603 mEraseColorSet
= true;
607 bool SkiaSalBitmap::AlphaBlendWith(const SalBitmap
& rSalBmp
)
610 assert(mWriteAccessCount
== 0);
612 const SkiaSalBitmap
* otherBitmap
= dynamic_cast<const SkiaSalBitmap
*>(&rSalBmp
);
615 if (mSize
!= otherBitmap
->mSize
)
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
))
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
);
635 SAL_INFO("vcl.skia.trace",
636 "alphablendwith(" << this << ") : with erase color " << otherBitmap
);
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())
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
);
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:
656 // which is SkBlendMode::kModulate
657 paint
.setBlendMode(SkBlendMode::kModulate
);
658 surface
->getCanvas()->drawImage(otherBitmap
->GetSkImage(), 0, 0, SkSamplingOptions(), &paint
);
659 ResetToSkImage(makeCheckedImageSnapshot(surface
));
661 SAL_INFO("vcl.skia.trace", "alphablendwith(" << this << ") : with image " << otherBitmap
);
665 bool SkiaSalBitmap::Invert()
668 assert(mWriteAccessCount
== 0);
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
);
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()),
689 ResetToSkImage(makeCheckedImageSnapshot(surface
));
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;
703 SAL_INFO("vcl.skia.trace", "invert(" << this << ")");
709 SkBitmap
SkiaSalBitmap::GetAsSkBitmap() const
712 assert(mWriteAccessCount
== 0);
715 assert(mSize
== mPixelsSize
); // data has already been scaled if needed
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))
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());
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))
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());
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))
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))
800 bitmap
.setImmutable();
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
837 assert(mWriteAccessCount
== 0);
839 if (direct
== DirectImage::Yes
)
845 assert(imageSize(mImage
) == mSize
);
849 sk_sp
<SkSurface
> surface
= createSkSurface(
850 mSize
, mEraseColor
.IsTransparent() ? kPremul_SkAlphaType
: kOpaque_SkAlphaType
);
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
);
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
869 sk_sp
<SkImage
> image
= createSkImage(GetAsSkBitmap());
871 thisPtr
->mSize
= savedSize
;
872 thisPtr
->ResetToSkImage(image
);
876 if (imageSize(mImage
) != mSize
)
878 assert(!mBuffer
); // This code should be only called if only mImage holds data.
880 sk_sp
<SkSurface
> surface
= createSkSurface(mSize
, mImage
->imageInfo().alphaType());
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
);
897 sk_sp
<SkImage
> image
= createSkImage(GetAsSkBitmap());
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 << ")");
912 const sk_sp
<SkImage
>& SkiaSalBitmap::GetAlphaSkImage(DirectImage direct
) const
915 assert(mWriteAccessCount
== 0);
917 if (direct
== DirectImage::Yes
)
923 assert(imageSize(mAlphaImage
) == mSize
);
927 sk_sp
<SkSurface
> surface
= createSkSurface(mSize
, kAlpha_8_SkColorType
);
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
);
938 if (imageSize(mAlphaImage
) == mSize
)
944 const bool scaling
= imageSize(mImage
) != mSize
;
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.
954 if (!bitmap
.installPixels(pixmap
))
956 SkBitmap alphaBitmap
;
957 if (!alphaBitmap
.tryAllocPixels(SkImageInfo::MakeA8(bitmap
.width(), bitmap
.height())))
959 if (int(bitmap
.rowBytes()) == bitmap
.width() * 4)
961 SkConvertRGBAToR(alphaBitmap
.getAddr8(0, 0), bitmap
.getAddr32(0, 0),
962 bitmap
.width() * bitmap
.height());
966 for (tools::Long y
= 0; y
< bitmap
.height(); ++y
)
967 SkConvertRGBAToR(alphaBitmap
.getAddr8(0, y
), bitmap
.getAddr32(0, y
),
970 alphaBitmap
.setImmutable();
971 sk_sp
<SkImage
> alphaImage
= createSkImage(alphaBitmap
);
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
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.
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
994 paint
.setColorFilter(SkColorFilters::Matrix(redToAlpha
));
996 assert(!mBuffer
); // This code should be only called if only mImage holds data.
997 sk_sp
<SkSurface
> surface
= createSkSurface(mSize
, kAlpha_8_SkColorType
);
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(),
1006 SAL_INFO("vcl.skia.trace", "getalphaskimage(" << this << "): image scaled "
1007 << Size(mImage
->width(), mImage
->height())
1008 << "->" << mSize
<< ":"
1009 << static_cast<int>(mScaleQuality
));
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
1015 SkiaSalBitmap
* thisPtr
= const_cast<SkiaSalBitmap
*>(this);
1016 thisPtr
->mAlphaImage
= makeCheckedImageSnapshot(surface
);
1021 assert(mSize
== mPixelsSize
); // data has already been scaled if needed
1022 SkBitmap alphaBitmap
;
1023 if (mBuffer
&& mBitCount
<= 8)
1025 assert(mBuffer
.get());
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))
1034 alphaBitmap
.setImmutable();
1035 sk_sp
<SkImage
> image
= createSkImage(alphaBitmap
);
1037 const_cast<sk_sp
<SkImage
>&>(mAlphaImage
) = image
;
1041 sk_sp
<SkSurface
> surface
= createSkSurface(mSize
, kAlpha_8_SkColorType
);
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(),
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 << ")");
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.
1079 assert(!mAlphaImage
);
1080 // Set wanted size, trigger conversion.
1081 Size savedSize
= mSize
;
1082 mSize
= imageSize(mImage
);
1084 assert(mAlphaImage
);
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
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
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().
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
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
;
1127 return kOpaque_SkAlphaType
;
1130 void SkiaSalBitmap::PerformErase()
1132 if (mPixelsSize
.IsEmpty())
1134 BitmapBuffer
* bitmapBuffer
= AcquireBuffer(BitmapAccessMode::Write
);
1135 if (bitmapBuffer
== nullptr)
1137 Color fastColor
= mEraseColor
;
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()
1160 assert(mPixelsSize
== mSize
);
1163 // Unset now, so that repeated call will return mBuffer.
1164 mEraseColorSet
= false;
1167 SAL_INFO("vcl.skia.trace",
1168 "ensurebitmapdata(" << this << ") from erase color " << mEraseColor
);
1174 if (mSize
== mPixelsSize
)
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()));
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
);
1196 if (mAlphaImage
->peekPixels(&pixmap
))
1197 bitmap
.installPixels(pixmap
);
1200 if (!bitmap
.tryAllocPixels(SkImageInfo::MakeA8(mSize
.Width(), mSize
.Height())))
1202 SkCanvas
canvas(bitmap
);
1204 paint
.setBlendMode(SkBlendMode::kSrc
); // set as is, including alpha
1205 canvas
.drawImage(mAlphaImage
, 0, 0, SkSamplingOptions(), &paint
);
1208 bitmap
.setImmutable();
1209 ResetPendingScaling();
1211 assert(mBuffer
!= nullptr);
1212 assert(mPixelsSize
== mSize
);
1213 if (int(bitmap
.rowBytes()) == mScanlineSize
)
1214 memcpy(mBuffer
.get(), bitmap
.getPixels(), mSize
.Height() * mScanlineSize
);
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
);
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");
1232 SAL_INFO("vcl.skia.trace", "ensurebitmapdata(" << this << "): from alpha image");
1238 // No data at all, create uninitialized data.
1240 SAL_INFO("vcl.skia.trace", "ensurebitmapdata(" << this << "): uninitialized");
1243 // Try to fill mBuffer from mImage.
1244 assert(mImage
->colorType() == kN32_SkColorType
);
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
;
1260 if (imageSize(mImage
) == mSize
&& mImage
->imageInfo().alphaType() == alphaType
1261 && mImage
->peekPixels(&pixmap
))
1263 bitmap
.installPixels(pixmap
);
1267 if (!bitmap
.tryAllocPixels(SkImageInfo::MakeS32(mSize
.Width(), mSize
.Height(), alphaType
)))
1269 SkCanvas
canvas(bitmap
);
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),
1277 SAL_INFO("vcl.skia.trace",
1278 "ensurebitmapdata(" << this << "): image scaled " << imageSize(mImage
) << "->"
1279 << mSize
<< ":" << static_cast<int>(mScaleQuality
));
1282 canvas
.drawImage(mImage
, 0, 0, SkSamplingOptions(), &paint
);
1285 bitmap
.setImmutable();
1286 ResetPendingScaling();
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
);
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());
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());
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());
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
);
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");
1364 SAL_INFO("vcl.skia.trace", "ensurebitmapdata(" << this << ")");
1367 void SkiaSalBitmap::EnsureBitmapUniqueData()
1370 assert(mWriteAccessCount
== 0);
1373 assert(mPixelsSize
== mSize
);
1374 if (mBuffer
.use_count() > 1)
1376 sal_uInt32 allocate
= mScanlineSize
* mSize
.Height();
1378 assert(memcmp(mBuffer
.get() + allocate
, CANARY
, sizeof(CANARY
)) == 0);
1379 allocate
+= sizeof(CANARY
);
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()
1390 // This should never be called to drop mImage if that's the only data we have.
1391 assert(mBuffer
|| !mImage
);
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
1403 // Just to be safe, assume mutability of the image does not change
1405 mAlphaImage
.reset();
1406 mEraseColorSet
= false;
1409 void SkiaSalBitmap::ResetAllData()
1411 assert(mReadAccessCount
== 0);
1415 mImageImmutable
= false;
1416 mAlphaImage
.reset();
1417 mEraseColorSet
= false;
1418 mPixelsSize
= mSize
;
1419 ComputeScanlineSize();
1423 void SkiaSalBitmap::DataChanged() { InvalidateChecksum(); }
1425 void SkiaSalBitmap::ResetPendingScaling()
1427 if (mPixelsSize
== mSize
)
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
)
1438 mImageImmutable
= false;
1440 if (mAlphaImage
&& imageSize(mAlphaImage
) != mSize
)
1441 mAlphaImage
.reset();
1444 OString
SkiaSalBitmap::GetImageKey(DirectImage direct
) const
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
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.
1486 SkiaHelper::dump(copy
.GetSkImage(), file
);
1490 void SkiaSalBitmap::verify() const
1494 // Use mPixelsSize, that describes the size of the actual data.
1495 assert(memcmp(mBuffer
.get() + mScanlineSize
* mPixelsSize
.Height(), CANARY
, sizeof(CANARY
))
1500 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */