calc: on editing invalidation of view with different zoom is wrong
[LibreOffice.git] / vcl / skia / salbmp.cxx
blob57cea14316c562b3b5fd2c6f0b56b84039dccc31
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 <SkCanvas.h>
37 #include <SkImage.h>
38 #include <SkPixelRef.h>
39 #include <SkSurface.h>
40 #include <SkSwizzle.h>
41 #include <SkColorFilter.h>
42 #include <SkColorMatrix.h>
43 #include <skia_opts.hxx>
45 #ifdef DBG_UTIL
46 #include <fstream>
47 #define CANARY "skia-canary"
48 #endif
50 using namespace SkiaHelper;
52 // As constexpr here, evaluating it directly in code makes Clang warn about unreachable code.
53 constexpr bool kN32_SkColorTypeIsBGRA = (kN32_SkColorType == kBGRA_8888_SkColorType);
55 SkiaSalBitmap::SkiaSalBitmap() {}
57 SkiaSalBitmap::~SkiaSalBitmap() {}
59 SkiaSalBitmap::SkiaSalBitmap(const sk_sp<SkImage>& image)
61 ResetAllData();
62 mImage = image;
63 mPalette = BitmapPalette();
64 #if SKIA_USE_BITMAP32
65 mBitCount = 32;
66 #else
67 mBitCount = 24;
68 #endif
69 mSize = mPixelsSize = Size(image->width(), image->height());
70 ComputeScanlineSize();
71 mReadAccessCount = 0;
72 #ifdef DBG_UTIL
73 mWriteAccessCount = 0;
74 #endif
75 SAL_INFO("vcl.skia.trace", "bitmapfromimage(" << this << ")");
78 bool SkiaSalBitmap::Create(const Size& rSize, vcl::PixelFormat ePixelFormat,
79 const BitmapPalette& rPal)
81 assert(mReadAccessCount == 0);
82 ResetAllData();
83 if (ePixelFormat == vcl::PixelFormat::INVALID)
84 return false;
85 mPalette = rPal;
86 mBitCount = vcl::pixelFormatBitCount(ePixelFormat);
87 mSize = rSize;
88 ResetPendingScaling();
89 if (!ComputeScanlineSize())
91 mBitCount = 0;
92 mSize = mPixelsSize = Size();
93 mScanlineSize = 0;
94 mPalette = BitmapPalette();
95 return false;
97 SAL_INFO("vcl.skia.trace", "create(" << this << ")");
98 return true;
101 bool SkiaSalBitmap::ComputeScanlineSize()
103 int bitScanlineWidth;
104 if (o3tl::checked_multiply<int>(mPixelsSize.Width(), mBitCount, bitScanlineWidth))
106 SAL_WARN("vcl.skia", "checked multiply failed");
107 return false;
109 mScanlineSize = AlignedWidth4Bytes(bitScanlineWidth);
110 return true;
113 void SkiaSalBitmap::CreateBitmapData()
115 assert(!mBuffer);
116 // Make sure code has not missed calling ComputeScanlineSize().
117 assert(mScanlineSize == int(AlignedWidth4Bytes(mPixelsSize.Width() * mBitCount)));
118 // The pixels could be stored in SkBitmap, but Skia only supports 8bit gray, 16bit and 32bit formats
119 // (e.g. 24bpp is actually stored as 32bpp). But some of our code accessing the bitmap assumes that
120 // when it asked for 24bpp, the format really will be 24bpp (e.g. the png loader), so we cannot use
121 // SkBitmap to store the data. And even 8bpp is problematic, since Skia does not support palettes
122 // and a VCL bitmap can change its grayscale status simply by changing the palette.
123 // Moreover creating SkImage from SkBitmap does a data copy unless the bitmap is immutable.
124 // So just always store pixels in our buffer and convert as necessary.
125 if (mScanlineSize == 0 || mPixelsSize.Height() == 0)
126 return;
128 size_t allocate = mScanlineSize * mPixelsSize.Height();
129 #ifdef DBG_UTIL
130 allocate += sizeof(CANARY);
131 #endif
132 mBuffer = boost::make_shared_noinit<sal_uInt8[]>(allocate);
133 #ifdef DBG_UTIL
134 // fill with random garbage
135 sal_uInt8* buffer = mBuffer.get();
136 for (size_t i = 0; i < allocate; i++)
137 buffer[i] = (i & 0xFF);
138 memcpy(buffer + allocate - sizeof(CANARY), CANARY, sizeof(CANARY));
139 #endif
142 bool SkiaSalBitmap::Create(const SalBitmap& rSalBmp)
144 return Create(rSalBmp, vcl::bitDepthToPixelFormat(rSalBmp.GetBitCount()));
147 bool SkiaSalBitmap::Create(const SalBitmap& rSalBmp, SalGraphics* pGraphics)
149 auto ePixelFormat = vcl::PixelFormat::INVALID;
150 if (pGraphics)
151 ePixelFormat = vcl::bitDepthToPixelFormat(pGraphics->GetBitCount());
152 else
153 ePixelFormat = vcl::bitDepthToPixelFormat(rSalBmp.GetBitCount());
155 return Create(rSalBmp, ePixelFormat);
158 bool SkiaSalBitmap::Create(const SalBitmap& rSalBmp, vcl::PixelFormat eNewPixelFormat)
160 assert(mReadAccessCount == 0);
161 assert(&rSalBmp != this);
162 ResetAllData();
163 const SkiaSalBitmap& src = static_cast<const SkiaSalBitmap&>(rSalBmp);
164 mImage = src.mImage;
165 mAlphaImage = src.mAlphaImage;
166 mBuffer = src.mBuffer;
167 mPalette = src.mPalette;
168 mBitCount = src.mBitCount;
169 mSize = src.mSize;
170 mPixelsSize = src.mPixelsSize;
171 mScanlineSize = src.mScanlineSize;
172 mScaleQuality = src.mScaleQuality;
173 mEraseColorSet = src.mEraseColorSet;
174 mEraseColor = src.mEraseColor;
175 if (vcl::pixelFormatBitCount(eNewPixelFormat) != src.GetBitCount())
177 // This appears to be unused(?). Implement this just in case, but be lazy
178 // about it and rely on EnsureBitmapData() doing the conversion from mImage
179 // if needed, even if that may need unnecessary to- and from- SkImage
180 // conversion.
181 ResetToSkImage(GetSkImage());
183 SAL_INFO("vcl.skia.trace", "create(" << this << "): (" << &src << ")");
184 return true;
187 bool SkiaSalBitmap::Create(const css::uno::Reference<css::rendering::XBitmapCanvas>&, Size&, bool)
189 return false;
192 void SkiaSalBitmap::Destroy()
194 SAL_INFO("vcl.skia.trace", "destroy(" << this << ")");
195 #ifdef DBG_UTIL
196 assert(mWriteAccessCount == 0);
197 #endif
198 assert(mReadAccessCount == 0);
199 ResetAllData();
202 Size SkiaSalBitmap::GetSize() const { return mSize; }
204 sal_uInt16 SkiaSalBitmap::GetBitCount() const { return mBitCount; }
206 BitmapBuffer* SkiaSalBitmap::AcquireBuffer(BitmapAccessMode nMode)
208 switch (nMode)
210 case BitmapAccessMode::Write:
211 EnsureBitmapUniqueData();
212 if (!mBuffer)
213 return nullptr;
214 assert(mPixelsSize == mSize);
215 assert(!mEraseColorSet);
216 break;
217 case BitmapAccessMode::Read:
218 EnsureBitmapData();
219 if (!mBuffer)
220 return nullptr;
221 assert(mPixelsSize == mSize);
222 assert(!mEraseColorSet);
223 break;
224 case BitmapAccessMode::Info:
225 break;
227 #ifdef DBG_UTIL
228 // BitmapWriteAccess stores also a copy of the palette and it can
229 // be modified, so concurrent reading of it might result in inconsistencies.
230 assert(mWriteAccessCount == 0 || nMode == BitmapAccessMode::Write);
231 #endif
232 BitmapBuffer* buffer = new BitmapBuffer;
233 buffer->mnWidth = mSize.Width();
234 buffer->mnHeight = mSize.Height();
235 buffer->mnBitCount = mBitCount;
236 buffer->maPalette = mPalette;
237 if (nMode != BitmapAccessMode::Info)
238 buffer->mpBits = mBuffer.get();
239 else
240 buffer->mpBits = nullptr;
241 if (mPixelsSize == mSize)
242 buffer->mnScanlineSize = mScanlineSize;
243 else
245 // The value of mScanlineSize is based on internal mPixelsSize, but the outside
246 // world cares about mSize, the size that the report as the size of the bitmap,
247 // regardless of any internal state. So report scanline size for that size.
248 Size savedPixelsSize = mPixelsSize;
249 mPixelsSize = mSize;
250 ComputeScanlineSize();
251 buffer->mnScanlineSize = mScanlineSize;
252 mPixelsSize = savedPixelsSize;
253 ComputeScanlineSize();
255 switch (mBitCount)
257 case 1:
258 buffer->mnFormat = ScanlineFormat::N1BitMsbPal;
259 break;
260 case 8:
261 buffer->mnFormat = ScanlineFormat::N8BitPal;
262 break;
263 case 24:
264 // Make the RGB/BGR format match the default Skia 32bpp format, to allow
265 // easy conversion later.
266 buffer->mnFormat = kN32_SkColorTypeIsBGRA ? ScanlineFormat::N24BitTcBgr
267 : ScanlineFormat::N24BitTcRgb;
268 break;
269 case 32:
270 buffer->mnFormat = kN32_SkColorTypeIsBGRA ? ScanlineFormat::N32BitTcBgra
271 : ScanlineFormat::N32BitTcRgba;
272 break;
273 default:
274 abort();
276 buffer->mnFormat |= ScanlineFormat::TopDown;
277 // Refcount all read/write accesses, to catch problems with existing accesses while
278 // a bitmap changes, and also to detect when we can free mBuffer if wanted.
279 // Write mode implies also reading. It would be probably a good idea to count even
280 // Info accesses, but VclCanvasBitmap keeps one around pointlessly, causing tdf#150817.
281 if (nMode == BitmapAccessMode::Read || nMode == BitmapAccessMode::Write)
282 ++mReadAccessCount;
283 #ifdef DBG_UTIL
284 if (nMode == BitmapAccessMode::Write)
285 ++mWriteAccessCount;
286 #endif
287 return buffer;
290 void SkiaSalBitmap::ReleaseBuffer(BitmapBuffer* pBuffer, BitmapAccessMode nMode)
292 ReleaseBuffer(pBuffer, nMode, false);
295 void SkiaSalBitmap::ReleaseBuffer(BitmapBuffer* pBuffer, BitmapAccessMode nMode,
296 bool dontChangeToErase)
298 if (nMode == BitmapAccessMode::Write)
300 #ifdef DBG_UTIL
301 assert(mWriteAccessCount > 0);
302 --mWriteAccessCount;
303 #endif
304 mPalette = pBuffer->maPalette;
305 ResetToBuffer();
306 DataChanged();
308 if (nMode == BitmapAccessMode::Read || nMode == BitmapAccessMode::Write)
310 assert(mReadAccessCount > 0);
311 --mReadAccessCount;
313 // Are there any more ground movements underneath us ?
314 assert(pBuffer->mnWidth == mSize.Width());
315 assert(pBuffer->mnHeight == mSize.Height());
316 assert(pBuffer->mnBitCount == mBitCount);
317 assert(pBuffer->mpBits == mBuffer.get() || nMode == BitmapAccessMode::Info);
318 verify();
319 delete pBuffer;
320 if (nMode == BitmapAccessMode::Write && !dontChangeToErase)
322 // This saves memory and is also used by IsFullyOpaqueAsAlpha() to avoid unnecessary
323 // alpha blending.
324 if (IsAllBlack())
326 SAL_INFO("vcl.skia.trace", "releasebuffer(" << this << "): erasing to black");
327 EraseInternal(COL_BLACK);
332 static bool isAllZero(const sal_uInt8* data, size_t size)
333 { // For performance, check in larger data chunks.
334 #ifdef UINT64_MAX
335 const int64_t* d = reinterpret_cast<const int64_t*>(data);
336 #else
337 const int32_t* d = reinterpret_cast<const int32_t*>(data);
338 #endif
339 constexpr size_t step = sizeof(*d) * 8;
340 for (size_t i = 0; i < size / step; ++i)
341 { // Unrolled loop.
342 if (d[0] != 0)
343 return false;
344 if (d[1] != 0)
345 return false;
346 if (d[2] != 0)
347 return false;
348 if (d[3] != 0)
349 return false;
350 if (d[4] != 0)
351 return false;
352 if (d[5] != 0)
353 return false;
354 if (d[6] != 0)
355 return false;
356 if (d[7] != 0)
357 return false;
358 d += 8;
360 for (size_t i = size / step * step; i < size; ++i)
361 if (data[i] != 0)
362 return false;
363 return true;
366 bool SkiaSalBitmap::IsAllBlack() const
368 if (mBitCount % 8 != 0 || (!!mPalette && mPalette[0] != COL_BLACK))
369 return false; // Don't bother.
370 if (mSize.Width() * mBitCount / 8 == mScanlineSize)
371 return isAllZero(mBuffer.get(), mScanlineSize * mSize.Height());
372 for (tools::Long y = 0; y < mSize.Height(); ++y)
373 if (!isAllZero(mBuffer.get() + mScanlineSize * y, mSize.Width() * mBitCount / 8))
374 return false;
375 return true;
378 bool SkiaSalBitmap::GetSystemData(BitmapSystemData&)
380 #ifdef DBG_UTIL
381 assert(mWriteAccessCount == 0);
382 #endif
383 return false;
386 bool SkiaSalBitmap::ScalingSupported() const { return true; }
388 bool SkiaSalBitmap::Scale(const double& rScaleX, const double& rScaleY, BmpScaleFlag nScaleFlag)
390 SkiaZone zone;
391 #ifdef DBG_UTIL
392 assert(mWriteAccessCount == 0);
393 #endif
394 Size newSize(FRound(mSize.Width() * rScaleX), FRound(mSize.Height() * rScaleY));
395 if (mSize == newSize)
396 return true;
398 SAL_INFO("vcl.skia.trace", "scale(" << this << "): " << mSize << "/" << mBitCount << "->"
399 << newSize << ":" << static_cast<int>(nScaleFlag));
401 if (mEraseColorSet)
402 { // Simple.
403 mSize = newSize;
404 ResetPendingScaling();
405 EraseInternal(mEraseColor);
406 return true;
409 if (mBitCount < 24 && !mPalette.IsGreyPalette8Bit())
411 // Scaling can introduce additional colors not present in the original
412 // bitmap (e.g. when smoothing). If the bitmap is indexed (has non-trivial palette),
413 // this would break the bitmap, because the actual scaling is done only somewhen later.
414 // Linear 8bit palette (grey) is ok, since there we use directly the values as colors.
415 SAL_INFO("vcl.skia.trace", "scale(" << this << "): indexed bitmap");
416 return false;
418 // The idea here is that the actual scaling will be delayed until the result
419 // is actually needed. Usually the scaled bitmap will be drawn somewhere,
420 // so delaying will mean the scaling can be done as a part of GetSkImage().
421 // That means it can be GPU-accelerated, while done here directly it would need
422 // to be either done by CPU, or with the CPU->GPU->CPU roundtrip required
423 // by GPU-accelerated scaling.
424 // Pending scaling is detected by 'mSize != mPixelsSize' for mBuffer,
425 // and 'imageSize(mImage) != mSize' for mImage. It is not intended to have 3 different
426 // sizes though, code below keeps only mBuffer or mImage. Note that imageSize(mImage)
427 // may or may not be equal to mPixelsSize, depending on whether mImage is set here
428 // (sizes will be equal) or whether it's set in GetSkImage() (will not be equal).
429 // Pending scaling is considered "done" by the time mBuffer is resized (or created).
430 // Resizing of mImage is somewhat independent of this, since mImage is primarily
431 // considered to be a cached object (although sometimes it's the only data available).
433 // If there is already one scale() pending, use the lowest quality of all requested.
434 switch (nScaleFlag)
436 case BmpScaleFlag::Fast:
437 mScaleQuality = nScaleFlag;
438 break;
439 case BmpScaleFlag::NearestNeighbor:
440 // We handle this the same way as Fast by mapping to Skia's nearest-neighbor,
441 // and it's needed for unittests (mScaling and testTdf132367()).
442 mScaleQuality = nScaleFlag;
443 break;
444 case BmpScaleFlag::Default:
445 if (mScaleQuality == BmpScaleFlag::BestQuality)
446 mScaleQuality = nScaleFlag;
447 break;
448 case BmpScaleFlag::BestQuality:
449 // Best is the maximum, set by default.
450 break;
451 default:
452 SAL_INFO("vcl.skia.trace", "scale(" << this << "): unsupported scale algorithm");
453 return false;
455 mSize = newSize;
456 // If we have both mBuffer and mImage, prefer mImage, since it likely will be drawn later.
457 // We could possibly try to keep the buffer as well, but that would complicate things
458 // with two different data structures to be scaled on-demand, and it's a question
459 // if that'd realistically help with anything.
460 if (mImage)
461 ResetToSkImage(mImage);
462 else
463 ResetToBuffer();
464 DataChanged();
465 // The rest will be handled when the scaled bitmap is actually needed,
466 // such as in EnsureBitmapData() or GetSkImage().
467 return true;
470 bool SkiaSalBitmap::Replace(const Color&, const Color&, sal_uInt8)
472 #ifdef DBG_UTIL
473 assert(mWriteAccessCount == 0);
474 #endif
475 return false;
478 bool SkiaSalBitmap::ConvertToGreyscale()
480 #ifdef DBG_UTIL
481 assert(mWriteAccessCount == 0);
482 #endif
483 // Normally this would need to convert contents of mBuffer for all possible formats,
484 // so just let the VCL algorithm do it.
485 // Avoid the costly SkImage->buffer->SkImage conversion.
486 if (!mBuffer && mImage && !mEraseColorSet)
488 if (mBitCount == 8 && mPalette.IsGreyPalette8Bit())
489 return true;
490 sk_sp<SkSurface> surface
491 = createSkSurface(imageSize(mImage), mImage->imageInfo().alphaType());
492 SkPaint paint;
493 paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
494 // VCL uses different coefficients for conversion to gray than Skia, so use the VCL
495 // values from Bitmap::ImplMakeGreyscales(). Do not use kGray_8_SkColorType,
496 // Skia would use its gray conversion formula.
497 // NOTE: The matrix is 4x5 organized as columns (i.e. each line is a column, not a row).
498 constexpr SkColorMatrix toGray(77 / 256.0, 151 / 256.0, 28 / 256.0, 0, 0, // R column
499 77 / 256.0, 151 / 256.0, 28 / 256.0, 0, 0, // G column
500 77 / 256.0, 151 / 256.0, 28 / 256.0, 0, 0, // B column
501 0, 0, 0, 1, 0); // don't modify alpha
502 paint.setColorFilter(SkColorFilters::Matrix(toGray));
503 surface->getCanvas()->drawImage(mImage, 0, 0, SkSamplingOptions(), &paint);
504 mBitCount = 8;
505 ComputeScanlineSize();
506 mPalette = Bitmap::GetGreyPalette(256);
507 ResetToSkImage(makeCheckedImageSnapshot(surface));
508 DataChanged();
509 SAL_INFO("vcl.skia.trace", "converttogreyscale(" << this << ")");
510 return true;
512 return false;
515 bool SkiaSalBitmap::InterpretAs8Bit()
517 #ifdef DBG_UTIL
518 assert(mWriteAccessCount == 0);
519 #endif
520 if (mBitCount == 8 && mPalette.IsGreyPalette8Bit())
521 return true;
522 if (mEraseColorSet)
524 mBitCount = 8;
525 ComputeScanlineSize();
526 mPalette = Bitmap::GetGreyPalette(256);
527 EraseInternal(mEraseColor);
528 SAL_INFO("vcl.skia.trace", "interpretas8bit(" << this << ") with erase color");
529 return true;
531 // This is usually used by AlphaMask, the point is just to treat
532 // the content as an alpha channel. This is often used
533 // by the horrible separate-alpha-outdev hack, where the bitmap comes
534 // from SkiaSalGraphicsImpl::GetBitmap(), so only mImage is set,
535 // and that is followed by a later call to GetAlphaSkImage().
536 // Avoid the costly SkImage->buffer->SkImage conversion and simply
537 // just treat the SkImage as being for 8bit bitmap. EnsureBitmapData()
538 // will do the conversion if needed, but the normal case will be
539 // GetAlphaSkImage() creating kAlpha_8_SkColorType SkImage from it.
540 if (mImage)
542 mBitCount = 8;
543 ComputeScanlineSize();
544 mPalette = Bitmap::GetGreyPalette(256);
545 ResetToSkImage(mImage); // keep mImage, it will be interpreted as 8bit if needed
546 DataChanged();
547 SAL_INFO("vcl.skia.trace", "interpretas8bit(" << this << ") with image");
548 return true;
550 SAL_INFO("vcl.skia.trace", "interpretas8bit(" << this << ") with pixel data, ignoring");
551 return false;
554 bool SkiaSalBitmap::Erase(const Color& color)
556 #ifdef DBG_UTIL
557 assert(mWriteAccessCount == 0);
558 #endif
559 // Optimized variant, just remember the color and apply it when needed,
560 // which may save having to do format conversions (e.g. GetSkImage()
561 // may directly erase the SkImage).
562 EraseInternal(color);
563 SAL_INFO("vcl.skia.trace", "erase(" << this << ")");
564 return true;
567 void SkiaSalBitmap::EraseInternal(const Color& color)
569 ResetAllData();
570 mEraseColorSet = true;
571 mEraseColor = color;
574 bool SkiaSalBitmap::AlphaBlendWith(const SalBitmap& rSalBmp)
576 #ifdef DBG_UTIL
577 assert(mWriteAccessCount == 0);
578 #endif
579 const SkiaSalBitmap* otherBitmap = dynamic_cast<const SkiaSalBitmap*>(&rSalBmp);
580 if (!otherBitmap)
581 return false;
582 if (mSize != otherBitmap->mSize)
583 return false;
584 // We're called from AlphaMask, which should ensure 8bit.
585 assert(GetBitCount() == 8 && mPalette.IsGreyPalette8Bit());
586 // If neither bitmap have Skia images, then AlphaMask::BlendWith() will be faster,
587 // as it will operate on mBuffer pixel buffers, while for Skia we'd need to convert it.
588 // If one has and one doesn't, do it using Skia, under the assumption that after this
589 // the resulting Skia image will be needed for drawing.
590 if (!(mImage || mEraseColorSet) && !(otherBitmap->mImage || otherBitmap->mEraseColorSet))
591 return false;
592 // This is for AlphaMask, which actually stores the alpha as the pixel values.
593 // I.e. take value of the color channel (one of them, if >8bit, they should be the same).
594 if (mEraseColorSet && otherBitmap->mEraseColorSet)
596 const sal_uInt16 nGrey1 = mEraseColor.GetRed();
597 const sal_uInt16 nGrey2 = otherBitmap->mEraseColor.GetRed();
598 const sal_uInt8 nGrey = static_cast<sal_uInt8>(nGrey1 + nGrey2 - nGrey1 * nGrey2 / 255);
599 mEraseColor = Color(nGrey, nGrey, nGrey);
600 DataChanged();
601 SAL_INFO("vcl.skia.trace",
602 "alphablendwith(" << this << ") : with erase color " << otherBitmap);
603 return true;
605 std::unique_ptr<SkiaSalBitmap> otherBitmapAllocated;
606 if (otherBitmap->GetBitCount() != 8 || !otherBitmap->mPalette.IsGreyPalette8Bit())
607 { // Convert/interpret as 8bit if needed.
608 otherBitmapAllocated = std::make_unique<SkiaSalBitmap>();
609 if (!otherBitmapAllocated->Create(*otherBitmap) || !otherBitmapAllocated->InterpretAs8Bit())
610 return false;
611 otherBitmap = otherBitmapAllocated.get();
613 // This is 8-bit bitmap serving as mask, so the image itself needs no alpha.
614 sk_sp<SkSurface> surface = createSkSurface(mSize, kOpaque_SkAlphaType);
615 SkPaint paint;
616 paint.setBlendMode(SkBlendMode::kSrc); // set as is
617 surface->getCanvas()->drawImage(GetSkImage(), 0, 0, SkSamplingOptions(), &paint);
618 paint.setBlendMode(SkBlendMode::kScreen); // src+dest - src*dest/255 (in 0..1)
619 surface->getCanvas()->drawImage(otherBitmap->GetSkImage(), 0, 0, SkSamplingOptions(), &paint);
620 ResetToSkImage(makeCheckedImageSnapshot(surface));
621 DataChanged();
622 SAL_INFO("vcl.skia.trace", "alphablendwith(" << this << ") : with image " << otherBitmap);
623 return true;
626 SkBitmap SkiaSalBitmap::GetAsSkBitmap() const
628 #ifdef DBG_UTIL
629 assert(mWriteAccessCount == 0);
630 #endif
631 EnsureBitmapData();
632 assert(mSize == mPixelsSize); // data has already been scaled if needed
633 SkiaZone zone;
634 SkBitmap bitmap;
635 if (mBuffer)
637 if (mBitCount == 32)
639 // Make a copy, the bitmap should be immutable (otherwise converting it
640 // to SkImage will make a copy anyway).
641 const size_t bytes = mPixelsSize.Height() * mScanlineSize;
642 std::unique_ptr<sal_uInt8[]> data(new sal_uInt8[bytes]);
643 memcpy(data.get(), mBuffer.get(), bytes);
644 if (!bitmap.installPixels(
645 SkImageInfo::MakeS32(mPixelsSize.Width(), mPixelsSize.Height(), alphaType()),
646 data.release(), mScanlineSize,
647 [](void* addr, void*) { delete[] static_cast<sal_uInt8*>(addr); }, nullptr))
648 abort();
650 else if (mBitCount == 24)
652 // Convert 24bpp RGB/BGR to 32bpp RGBA/BGRA.
653 std::unique_ptr<uint32_t[]> data(
654 new uint32_t[mPixelsSize.Height() * mPixelsSize.Width()]);
655 uint32_t* dest = data.get();
656 // SkConvertRGBToRGBA() also works as BGR to BGRA (the function extends 3 bytes to 4
657 // by adding 0xFF alpha, so position of B and R doesn't matter).
658 if (mPixelsSize.Width() * 3 == mScanlineSize)
659 SkConvertRGBToRGBA(dest, mBuffer.get(), mPixelsSize.Height() * mPixelsSize.Width());
660 else
662 for (tools::Long y = 0; y < mPixelsSize.Height(); ++y)
664 const sal_uInt8* src = mBuffer.get() + mScanlineSize * y;
665 SkConvertRGBToRGBA(dest, src, mPixelsSize.Width());
666 dest += mPixelsSize.Width();
669 if (!bitmap.installPixels(
670 SkImageInfo::MakeS32(mPixelsSize.Width(), mPixelsSize.Height(),
671 kOpaque_SkAlphaType),
672 data.release(), mPixelsSize.Width() * 4,
673 [](void* addr, void*) { delete[] static_cast<sal_uInt8*>(addr); }, nullptr))
674 abort();
676 else if (mBitCount == 8 && mPalette.IsGreyPalette8Bit())
678 // Convert 8bpp gray to 32bpp RGBA/BGRA.
679 // There's also kGray_8_SkColorType, but it's probably simpler to make
680 // GetAsSkBitmap() always return 32bpp SkBitmap and then assume mImage
681 // is always 32bpp too.
682 std::unique_ptr<uint32_t[]> data(
683 new uint32_t[mPixelsSize.Height() * mPixelsSize.Width()]);
684 uint32_t* dest = data.get();
685 if (mPixelsSize.Width() * 1 == mScanlineSize)
686 SkConvertGrayToRGBA(dest, mBuffer.get(),
687 mPixelsSize.Height() * mPixelsSize.Width());
688 else
690 for (tools::Long y = 0; y < mPixelsSize.Height(); ++y)
692 const sal_uInt8* src = mBuffer.get() + mScanlineSize * y;
693 SkConvertGrayToRGBA(dest, src, mPixelsSize.Width());
694 dest += mPixelsSize.Width();
697 if (!bitmap.installPixels(
698 SkImageInfo::MakeS32(mPixelsSize.Width(), mPixelsSize.Height(),
699 kOpaque_SkAlphaType),
700 data.release(), mPixelsSize.Width() * 4,
701 [](void* addr, void*) { delete[] static_cast<sal_uInt8*>(addr); }, nullptr))
702 abort();
704 else
706 std::unique_ptr<sal_uInt8[]> data = convertDataBitCount(
707 mBuffer.get(), mPixelsSize.Width(), mPixelsSize.Height(), mBitCount, mScanlineSize,
708 mPalette, kN32_SkColorTypeIsBGRA ? BitConvert::BGRA : BitConvert::RGBA);
709 if (!bitmap.installPixels(
710 SkImageInfo::MakeS32(mPixelsSize.Width(), mPixelsSize.Height(),
711 kOpaque_SkAlphaType),
712 data.release(), mPixelsSize.Width() * 4,
713 [](void* addr, void*) { delete[] static_cast<sal_uInt8*>(addr); }, nullptr))
714 abort();
717 bitmap.setImmutable();
718 return bitmap;
721 // If mEraseColor is set, this is the color to use when the bitmap is used as alpha bitmap.
722 // E.g. COL_BLACK actually means fully opaque and COL_WHITE means fully transparent.
723 // This is because the alpha value is set as the color itself, not the alpha of the color.
724 // Additionally VCL actually uses transparency and not opacity, so we should use "255 - value",
725 // but we account for this by doing SkBlendMode::kDstOut when using alpha images (which
726 // basically does another "255 - alpha"), so do not do it here.
727 static SkColor fromEraseColorToAlphaImageColor(Color color)
729 return SkColorSetARGB(color.GetBlue(), 0, 0, 0);
732 // SkiaSalBitmap can store data in both the SkImage and our mBuffer, which with large
733 // images can waste quite a lot of memory. Ideally we should store the data in Skia's
734 // SkBitmap, but LO wants us to support data formats that Skia doesn't support.
735 // So try to conserve memory by keeping the data only once in that was the most
736 // recently wanted storage, and drop the other one. Usually the other one won't be needed
737 // for a long time, and especially with raster the conversion is usually fast.
738 // Do this only with raster, to avoid GPU->CPU transfer in GPU mode (exception is 32bit
739 // builds, where memory is more important). Also don't do this with paletted bitmaps,
740 // where EnsureBitmapData() would be expensive.
741 // Ideally SalBitmap should be able to say which bitmap formats it supports
742 // and VCL code should oblige, which would allow reusing the same data.
743 bool SkiaSalBitmap::ConserveMemory() const
745 static bool keepBitmapBuffer = getenv("SAL_SKIA_KEEP_BITMAP_BUFFER") != nullptr;
746 constexpr bool is32Bit = sizeof(void*) == 4;
747 // 16MiB bitmap data at least (set to 0 for easy testing).
748 constexpr tools::Long maxBufferSize = 2000 * 2000 * 4;
749 return !keepBitmapBuffer && (renderMethodToUse() == RenderRaster || is32Bit)
750 && mPixelsSize.Height() * mScanlineSize > maxBufferSize
751 && (mBitCount > 8 || (mBitCount == 8 && mPalette.IsGreyPalette8Bit()));
754 const sk_sp<SkImage>& SkiaSalBitmap::GetSkImage(DirectImage direct) const
756 #ifdef DBG_UTIL
757 assert(mWriteAccessCount == 0);
758 #endif
759 if (direct == DirectImage::Yes)
760 return mImage;
761 if (mEraseColorSet)
763 if (mImage)
765 assert(imageSize(mImage) == mSize);
766 return mImage;
768 SkiaZone zone;
769 sk_sp<SkSurface> surface = createSkSurface(
770 mSize, mEraseColor.IsTransparent() ? kPremul_SkAlphaType : kOpaque_SkAlphaType);
771 assert(surface);
772 surface->getCanvas()->clear(toSkColor(mEraseColor));
773 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
774 thisPtr->mImage = makeCheckedImageSnapshot(surface);
775 SAL_INFO("vcl.skia.trace", "getskimage(" << this << ") from erase color " << mEraseColor);
776 return mImage;
778 if (mPixelsSize != mSize && !mImage && renderMethodToUse() != RenderRaster)
780 // The bitmap has a pending scaling, but no image. This function would below call GetAsSkBitmap(),
781 // which would do CPU-based pixel scaling, and then it would get converted to an image.
782 // Be more efficient, first convert to an image and then the block below will scale on the GPU.
783 SAL_INFO("vcl.skia.trace", "getskimage(" << this << "): shortcut image scaling "
784 << mPixelsSize << "->" << mSize);
785 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
786 Size savedSize = mSize;
787 thisPtr->mSize = mPixelsSize; // block scaling
788 SkiaZone zone;
789 sk_sp<SkImage> image = createSkImage(GetAsSkBitmap());
790 assert(image);
791 thisPtr->mSize = savedSize;
792 thisPtr->ResetToSkImage(image);
794 if (mImage)
796 if (imageSize(mImage) != mSize)
798 assert(!mBuffer); // This code should be only called if only mImage holds data.
799 SkiaZone zone;
800 sk_sp<SkSurface> surface = createSkSurface(mSize, mImage->imageInfo().alphaType());
801 assert(surface);
802 SkPaint paint;
803 paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
804 surface->getCanvas()->drawImageRect(
805 mImage, SkRect::MakeWH(mSize.Width(), mSize.Height()),
806 makeSamplingOptions(mScaleQuality, imageSize(mImage), mSize, 1), &paint);
807 SAL_INFO("vcl.skia.trace", "getskimage(" << this << "): image scaled "
808 << Size(mImage->width(), mImage->height())
809 << "->" << mSize << ":"
810 << static_cast<int>(mScaleQuality));
811 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
812 thisPtr->mImage = makeCheckedImageSnapshot(surface);
814 return mImage;
816 SkiaZone zone;
817 sk_sp<SkImage> image = createSkImage(GetAsSkBitmap());
818 assert(image);
819 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
820 thisPtr->mImage = image;
821 // The data is now stored both in the SkImage and in our mBuffer, so drop the buffer
822 // if conserving memory. It'll be converted back by EnsureBitmapData() if needed.
823 if (ConserveMemory() && mReadAccessCount == 0)
825 SAL_INFO("vcl.skia.trace", "getskimage(" << this << "): dropping buffer");
826 thisPtr->ResetToSkImage(mImage);
828 SAL_INFO("vcl.skia.trace", "getskimage(" << this << ")");
829 return mImage;
832 const sk_sp<SkImage>& SkiaSalBitmap::GetAlphaSkImage(DirectImage direct) const
834 #ifdef DBG_UTIL
835 assert(mWriteAccessCount == 0);
836 #endif
837 if (direct == DirectImage::Yes)
838 return mAlphaImage;
839 if (mEraseColorSet)
841 if (mAlphaImage)
843 assert(imageSize(mAlphaImage) == mSize);
844 return mAlphaImage;
846 SkiaZone zone;
847 sk_sp<SkSurface> surface = createSkSurface(mSize, kAlpha_8_SkColorType);
848 assert(surface);
849 surface->getCanvas()->clear(fromEraseColorToAlphaImageColor(mEraseColor));
850 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
851 thisPtr->mAlphaImage = makeCheckedImageSnapshot(surface);
852 SAL_INFO("vcl.skia.trace",
853 "getalphaskimage(" << this << ") from erase color " << mEraseColor);
854 return mAlphaImage;
856 if (mAlphaImage)
858 if (imageSize(mAlphaImage) == mSize)
859 return mAlphaImage;
861 if (mImage)
863 SkiaZone zone;
864 const bool scaling = imageSize(mImage) != mSize;
865 SkPixmap pixmap;
866 // Note: We cannot do this when 'scaling' because SkCanvas::drawImageRect()
867 // with kAlpha_8_SkColorType as source and destination would act as SkBlendMode::kSrcOver
868 // despite SkBlendMode::kSrc set (https://bugs.chromium.org/p/skia/issues/detail?id=9692).
869 if (mImage->peekPixels(&pixmap) && !scaling)
871 assert(pixmap.colorType() == kN32_SkColorType);
872 // In non-GPU mode, convert 32bit data to 8bit alpha, this is faster than
873 // the SkColorFilter below. Since this is the VCL alpha-vdev alpha, where
874 // all R,G,B are the same and in fact mean alpha, this means we simply take one
875 // 8bit channel from the input, and that's the output.
876 SkBitmap bitmap;
877 if (!bitmap.installPixels(pixmap))
878 abort();
879 SkBitmap alphaBitmap;
880 if (!alphaBitmap.tryAllocPixels(SkImageInfo::MakeA8(bitmap.width(), bitmap.height())))
881 abort();
882 if (int(bitmap.rowBytes()) == bitmap.width() * 4)
884 SkConvertRGBAToR(alphaBitmap.getAddr8(0, 0), bitmap.getAddr32(0, 0),
885 bitmap.width() * bitmap.height());
887 else
889 for (tools::Long y = 0; y < bitmap.height(); ++y)
890 SkConvertRGBAToR(alphaBitmap.getAddr8(0, y), bitmap.getAddr32(0, y),
891 bitmap.width());
893 alphaBitmap.setImmutable();
894 sk_sp<SkImage> alphaImage = createSkImage(alphaBitmap);
895 assert(alphaImage);
896 SAL_INFO("vcl.skia.trace", "getalphaskimage(" << this << ") from raster image");
897 // Don't bother here with ConserveMemory(), mImage -> mAlphaImage conversions should
898 // generally only happen with the separate-alpha-outdev hack, and those bitmaps should
899 // be temporary.
900 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
901 thisPtr->mAlphaImage = alphaImage;
902 return mAlphaImage;
904 // Move the R channel value to the alpha channel. This seems to be the only
905 // way to reinterpret data in SkImage as an alpha SkImage without accessing the pixels.
906 // NOTE: The matrix is 4x5 organized as columns (i.e. each line is a column, not a row).
907 constexpr SkColorMatrix redToAlpha(0, 0, 0, 0, 0, // R column
908 0, 0, 0, 0, 0, // G column
909 0, 0, 0, 0, 0, // B column
910 1, 0, 0, 0, 0); // A column
911 SkPaint paint;
912 paint.setColorFilter(SkColorFilters::Matrix(redToAlpha));
913 if (scaling)
914 assert(!mBuffer); // This code should be only called if only mImage holds data.
915 sk_sp<SkSurface> surface = createSkSurface(mSize, kAlpha_8_SkColorType);
916 assert(surface);
917 paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
918 surface->getCanvas()->drawImageRect(
919 mImage, SkRect::MakeWH(mSize.Width(), mSize.Height()),
920 scaling ? makeSamplingOptions(mScaleQuality, imageSize(mImage), mSize, 1)
921 : SkSamplingOptions(),
922 &paint);
923 if (scaling)
924 SAL_INFO("vcl.skia.trace", "getalphaskimage(" << this << "): image scaled "
925 << Size(mImage->width(), mImage->height())
926 << "->" << mSize << ":"
927 << static_cast<int>(mScaleQuality));
928 else
929 SAL_INFO("vcl.skia.trace", "getalphaskimage(" << this << ") from image");
930 // Don't bother here with ConserveMemory(), mImage -> mAlphaImage conversions should
931 // generally only happen with the separate-alpha-outdev hack, and those bitmaps should
932 // be temporary.
933 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
934 thisPtr->mAlphaImage = makeCheckedImageSnapshot(surface);
935 return mAlphaImage;
937 SkiaZone zone;
938 EnsureBitmapData();
939 assert(mSize == mPixelsSize); // data has already been scaled if needed
940 SkBitmap alphaBitmap;
941 if (mBuffer && mBitCount <= 8)
943 assert(mBuffer.get());
944 verify();
945 std::unique_ptr<sal_uInt8[]> data
946 = convertDataBitCount(mBuffer.get(), mSize.Width(), mSize.Height(), mBitCount,
947 mScanlineSize, mPalette, BitConvert::A8);
948 if (!alphaBitmap.installPixels(
949 SkImageInfo::MakeA8(mSize.Width(), mSize.Height()), data.release(), mSize.Width(),
950 [](void* addr, void*) { delete[] static_cast<sal_uInt8*>(addr); }, nullptr))
951 abort();
952 alphaBitmap.setImmutable();
953 sk_sp<SkImage> image = createSkImage(alphaBitmap);
954 assert(image);
955 const_cast<sk_sp<SkImage>&>(mAlphaImage) = image;
957 else
959 sk_sp<SkSurface> surface = createSkSurface(mSize, kAlpha_8_SkColorType);
960 assert(surface);
961 SkPaint paint;
962 paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
963 // Move the R channel value to the alpha channel. This seems to be the only
964 // way to reinterpret data in SkImage as an alpha SkImage without accessing the pixels.
965 // NOTE: The matrix is 4x5 organized as columns (i.e. each line is a column, not a row).
966 constexpr SkColorMatrix redToAlpha(0, 0, 0, 0, 0, // R column
967 0, 0, 0, 0, 0, // G column
968 0, 0, 0, 0, 0, // B column
969 1, 0, 0, 0, 0); // A column
970 paint.setColorFilter(SkColorFilters::Matrix(redToAlpha));
971 surface->getCanvas()->drawImage(GetAsSkBitmap().asImage(), 0, 0, SkSamplingOptions(),
972 &paint);
973 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
974 thisPtr->mAlphaImage = makeCheckedImageSnapshot(surface);
976 // The data is now stored both in the SkImage and in our mBuffer, so drop the buffer
977 // if conserving memory and the conversion back would be simple (it'll be converted back
978 // by EnsureBitmapData() if needed).
979 if (ConserveMemory() && mBitCount == 8 && mPalette.IsGreyPalette8Bit() && mReadAccessCount == 0)
981 SAL_INFO("vcl.skia.trace", "getalphaskimage(" << this << "): dropping buffer");
982 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
983 thisPtr->mBuffer.reset();
985 SAL_INFO("vcl.skia.trace", "getalphaskimage(" << this << ")");
986 return mAlphaImage;
989 void SkiaSalBitmap::TryDirectConvertToAlphaNoScaling()
991 // This is a bit of a hack. Because of the VCL alpha hack where alpha is stored
992 // separately, we often convert mImage to mAlphaImage to represent the alpha
993 // channel. If code finds out that there is mImage but no mAlphaImage,
994 // this will create it from it, without checking for delayed scaling (i.e.
995 // it is "direct").
996 assert(mImage);
997 assert(!mAlphaImage);
998 // Set wanted size, trigger conversion.
999 Size savedSize = mSize;
1000 mSize = imageSize(mImage);
1001 GetAlphaSkImage();
1002 assert(mAlphaImage);
1003 mSize = savedSize;
1006 // If the bitmap is to be erased, SkShader with the color set is more efficient
1007 // than creating an image filled with the color.
1008 bool SkiaSalBitmap::PreferSkShader() const { return mEraseColorSet; }
1010 sk_sp<SkShader> SkiaSalBitmap::GetSkShader(const SkSamplingOptions& samplingOptions,
1011 DirectImage direct) const
1013 if (mEraseColorSet)
1014 return SkShaders::Color(toSkColor(mEraseColor));
1015 return GetSkImage(direct)->makeShader(samplingOptions);
1018 sk_sp<SkShader> SkiaSalBitmap::GetAlphaSkShader(const SkSamplingOptions& samplingOptions,
1019 DirectImage direct) const
1021 if (mEraseColorSet)
1022 return SkShaders::Color(fromEraseColorToAlphaImageColor(mEraseColor));
1023 return GetAlphaSkImage(direct)->makeShader(samplingOptions);
1026 bool SkiaSalBitmap::IsFullyOpaqueAsAlpha() const
1028 if (!mEraseColorSet) // Set from Erase() or ReleaseBuffer().
1029 return false;
1030 // If the erase color is set so that this bitmap used as alpha would
1031 // mean a fully opaque alpha mask (= noop), we can skip using it.
1032 // Note that for alpha bitmaps we use the VCL "transparency" convention,
1033 // i.e. alpha 0 is opaque.
1034 return SkColorGetA(fromEraseColorToAlphaImageColor(mEraseColor)) == 0;
1037 SkAlphaType SkiaSalBitmap::alphaType() const
1039 if (mEraseColorSet)
1040 return mEraseColor.IsTransparent() ? kPremul_SkAlphaType : kOpaque_SkAlphaType;
1041 #if SKIA_USE_BITMAP32
1042 // The bitmap's alpha matters only if SKIA_USE_BITMAP32 is set, otherwise
1043 // the alpha is in a separate bitmap.
1044 if (mBitCount == 32)
1045 return kPremul_SkAlphaType;
1046 #endif
1047 return kOpaque_SkAlphaType;
1050 void SkiaSalBitmap::PerformErase()
1052 if (mPixelsSize.IsEmpty())
1053 return;
1054 BitmapBuffer* bitmapBuffer = AcquireBuffer(BitmapAccessMode::Write);
1055 if (bitmapBuffer == nullptr)
1056 abort();
1057 Color fastColor = mEraseColor;
1058 if (!!mPalette)
1059 fastColor = Color(ColorTransparency, mPalette.GetBestIndex(fastColor));
1060 if (!ImplFastEraseBitmap(*bitmapBuffer, fastColor))
1062 FncSetPixel setPixel = BitmapReadAccess::SetPixelFunction(bitmapBuffer->mnFormat);
1063 assert(bitmapBuffer->mnFormat & ScanlineFormat::TopDown);
1064 // Set first scanline, copy to others.
1065 Scanline scanline = bitmapBuffer->mpBits;
1066 for (tools::Long x = 0; x < bitmapBuffer->mnWidth; ++x)
1067 setPixel(scanline, x, mEraseColor, bitmapBuffer->maColorMask);
1068 for (tools::Long y = 1; y < bitmapBuffer->mnHeight; ++y)
1069 memcpy(scanline + y * bitmapBuffer->mnScanlineSize, scanline,
1070 bitmapBuffer->mnScanlineSize);
1072 ReleaseBuffer(bitmapBuffer, BitmapAccessMode::Write, true);
1075 void SkiaSalBitmap::EnsureBitmapData()
1077 if (mEraseColorSet)
1079 SkiaZone zone;
1080 assert(mPixelsSize == mSize);
1081 assert(!mBuffer);
1082 CreateBitmapData();
1083 // Unset now, so that repeated call will return mBuffer.
1084 mEraseColorSet = false;
1085 PerformErase();
1086 verify();
1087 SAL_INFO("vcl.skia.trace",
1088 "ensurebitmapdata(" << this << ") from erase color " << mEraseColor);
1089 return;
1092 if (mBuffer)
1094 if (mSize == mPixelsSize)
1095 return;
1096 // Pending scaling. Create raster SkImage from the bitmap data
1097 // at the pixel size and then the code below will scale at the correct
1098 // bpp from the image.
1099 SAL_INFO("vcl.skia.trace", "ensurebitmapdata(" << this << "): pixels to be scaled "
1100 << mPixelsSize << "->" << mSize << ":"
1101 << static_cast<int>(mScaleQuality));
1102 Size savedSize = mSize;
1103 mSize = mPixelsSize;
1104 ResetToSkImage(SkImage::MakeFromBitmap(GetAsSkBitmap()));
1105 mSize = savedSize;
1108 // Convert from alpha image, if the conversion is simple.
1109 if (mAlphaImage && imageSize(mAlphaImage) == mSize && mBitCount == 8
1110 && mPalette.IsGreyPalette8Bit())
1112 assert(mAlphaImage->colorType() == kAlpha_8_SkColorType);
1113 SkiaZone zone;
1114 SkBitmap bitmap;
1115 SkPixmap pixmap;
1116 if (mAlphaImage->peekPixels(&pixmap))
1117 bitmap.installPixels(pixmap);
1118 else
1120 if (!bitmap.tryAllocPixels(SkImageInfo::MakeA8(mSize.Width(), mSize.Height())))
1121 abort();
1122 SkCanvas canvas(bitmap);
1123 SkPaint paint;
1124 paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
1125 canvas.drawImage(mAlphaImage, 0, 0, SkSamplingOptions(), &paint);
1126 canvas.flush();
1128 bitmap.setImmutable();
1129 ResetPendingScaling();
1130 CreateBitmapData();
1131 assert(mBuffer != nullptr);
1132 assert(mPixelsSize == mSize);
1133 if (int(bitmap.rowBytes()) == mScanlineSize)
1134 memcpy(mBuffer.get(), bitmap.getPixels(), mSize.Height() * mScanlineSize);
1135 else
1137 for (tools::Long y = 0; y < mSize.Height(); ++y)
1139 const uint8_t* src = static_cast<uint8_t*>(bitmap.getAddr(0, y));
1140 sal_uInt8* dest = mBuffer.get() + mScanlineSize * y;
1141 memcpy(dest, src, mScanlineSize);
1144 verify();
1145 // We've created the bitmap data from mAlphaImage, drop the image if conserving memory,
1146 // it'll be converted back if needed.
1147 if (ConserveMemory())
1149 SAL_INFO("vcl.skia.trace", "ensurebitmapdata(" << this << "): dropping images");
1150 ResetToBuffer();
1152 SAL_INFO("vcl.skia.trace", "ensurebitmapdata(" << this << "): from alpha image");
1153 return;
1156 if (!mImage)
1158 // No data at all, create uninitialized data.
1159 CreateBitmapData();
1160 SAL_INFO("vcl.skia.trace", "ensurebitmapdata(" << this << "): uninitialized");
1161 return;
1163 // Try to fill mBuffer from mImage.
1164 assert(mImage->colorType() == kN32_SkColorType);
1165 SkiaZone zone;
1166 // If the source image has no alpha, then use no alpha (faster to convert), otherwise
1167 // use kUnpremul_SkAlphaType to make Skia convert from premultiplied alpha when reading
1168 // from the SkImage (the alpha will be ignored if converting to bpp<32 formats, but
1169 // the color channels must be unpremultiplied. Unless bpp==32 and SKIA_USE_BITMAP32,
1170 // in which case use kPremul_SkAlphaType, since SKIA_USE_BITMAP32 implies premultiplied alpha.
1171 SkAlphaType alphaType = kUnpremul_SkAlphaType;
1172 if (mImage->imageInfo().alphaType() == kOpaque_SkAlphaType)
1173 alphaType = kOpaque_SkAlphaType;
1174 #if SKIA_USE_BITMAP32
1175 if (mBitCount == 32)
1176 alphaType = kPremul_SkAlphaType;
1177 #endif
1178 SkBitmap bitmap;
1179 SkPixmap pixmap;
1180 if (imageSize(mImage) == mSize && mImage->imageInfo().alphaType() == alphaType
1181 && mImage->peekPixels(&pixmap))
1183 bitmap.installPixels(pixmap);
1185 else
1187 if (!bitmap.tryAllocPixels(SkImageInfo::MakeS32(mSize.Width(), mSize.Height(), alphaType)))
1188 abort();
1189 SkCanvas canvas(bitmap);
1190 SkPaint paint;
1191 paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
1192 if (imageSize(mImage) != mSize) // pending scaling?
1194 canvas.drawImageRect(mImage, SkRect::MakeWH(mSize.getWidth(), mSize.getHeight()),
1195 makeSamplingOptions(mScaleQuality, imageSize(mImage), mSize, 1),
1196 &paint);
1197 SAL_INFO("vcl.skia.trace",
1198 "ensurebitmapdata(" << this << "): image scaled " << imageSize(mImage) << "->"
1199 << mSize << ":" << static_cast<int>(mScaleQuality));
1201 else
1202 canvas.drawImage(mImage, 0, 0, SkSamplingOptions(), &paint);
1203 canvas.flush();
1205 bitmap.setImmutable();
1206 ResetPendingScaling();
1207 CreateBitmapData();
1208 assert(mBuffer != nullptr);
1209 assert(mPixelsSize == mSize);
1210 if (mBitCount == 32)
1212 if (int(bitmap.rowBytes()) == mScanlineSize)
1213 memcpy(mBuffer.get(), bitmap.getPixels(), mSize.Height() * mScanlineSize);
1214 else
1216 for (tools::Long y = 0; y < mSize.Height(); ++y)
1218 const uint8_t* src = static_cast<uint8_t*>(bitmap.getAddr(0, y));
1219 sal_uInt8* dest = mBuffer.get() + mScanlineSize * y;
1220 memcpy(dest, src, mScanlineSize);
1224 else if (mBitCount == 24) // non-paletted
1226 if (int(bitmap.rowBytes()) == mSize.Width() * 4 && mSize.Width() * 3 == mScanlineSize)
1228 SkConvertRGBAToRGB(mBuffer.get(), bitmap.getAddr32(0, 0),
1229 mSize.Height() * mSize.Width());
1231 else
1233 for (tools::Long y = 0; y < mSize.Height(); ++y)
1235 const uint32_t* src = bitmap.getAddr32(0, y);
1236 sal_uInt8* dest = mBuffer.get() + mScanlineSize * y;
1237 SkConvertRGBAToRGB(dest, src, mSize.Width());
1241 else if (mBitCount == 8 && mPalette.IsGreyPalette8Bit())
1242 { // no actual data conversion, use one color channel as the gray value
1243 if (int(bitmap.rowBytes()) == mSize.Width() * 4 && mSize.Width() * 1 == mScanlineSize)
1245 SkConvertRGBAToR(mBuffer.get(), bitmap.getAddr32(0, 0), mSize.Height() * mSize.Width());
1247 else
1249 for (tools::Long y = 0; y < mSize.Height(); ++y)
1251 const uint32_t* src = bitmap.getAddr32(0, y);
1252 sal_uInt8* dest = mBuffer.get() + mScanlineSize * y;
1253 SkConvertRGBAToR(dest, src, mSize.Width());
1257 else
1259 std::unique_ptr<vcl::ScanlineWriter> pWriter
1260 = vcl::ScanlineWriter::Create(mBitCount, mPalette);
1261 for (tools::Long y = 0; y < mSize.Height(); ++y)
1263 const uint8_t* src = static_cast<uint8_t*>(bitmap.getAddr(0, y));
1264 sal_uInt8* dest = mBuffer.get() + mScanlineSize * y;
1265 pWriter->nextLine(dest);
1266 for (tools::Long x = 0; x < mSize.Width(); ++x)
1268 sal_uInt8 r = *src++;
1269 sal_uInt8 g = *src++;
1270 sal_uInt8 b = *src++;
1271 ++src; // skip alpha
1272 pWriter->writeRGB(r, g, b);
1276 verify();
1277 // We've created the bitmap data from mImage, drop the image if conserving memory,
1278 // it'll be converted back if needed.
1279 if (ConserveMemory())
1281 SAL_INFO("vcl.skia.trace", "ensurebitmapdata(" << this << "): dropping images");
1282 ResetToBuffer();
1284 SAL_INFO("vcl.skia.trace", "ensurebitmapdata(" << this << ")");
1287 void SkiaSalBitmap::EnsureBitmapUniqueData()
1289 #ifdef DBG_UTIL
1290 assert(mWriteAccessCount == 0);
1291 #endif
1292 EnsureBitmapData();
1293 assert(mPixelsSize == mSize);
1294 if (mBuffer.use_count() > 1)
1296 sal_uInt32 allocate = mScanlineSize * mSize.Height();
1297 #ifdef DBG_UTIL
1298 assert(memcmp(mBuffer.get() + allocate, CANARY, sizeof(CANARY)) == 0);
1299 allocate += sizeof(CANARY);
1300 #endif
1301 boost::shared_ptr<sal_uInt8[]> newBuffer = boost::make_shared_noinit<sal_uInt8[]>(allocate);
1302 memcpy(newBuffer.get(), mBuffer.get(), allocate);
1303 mBuffer = newBuffer;
1307 void SkiaSalBitmap::ResetToBuffer()
1309 SkiaZone zone;
1310 // This should never be called to drop mImage if that's the only data we have.
1311 assert(mBuffer || !mImage);
1312 mImage.reset();
1313 mAlphaImage.reset();
1314 mEraseColorSet = false;
1317 void SkiaSalBitmap::ResetToSkImage(sk_sp<SkImage> image)
1319 assert(mReadAccessCount == 0); // can't reset mBuffer if there's a read access pointing to it
1320 SkiaZone zone;
1321 mBuffer.reset();
1322 mImage = image;
1323 mAlphaImage.reset();
1324 mEraseColorSet = false;
1327 void SkiaSalBitmap::ResetAllData()
1329 assert(mReadAccessCount == 0);
1330 SkiaZone zone;
1331 mBuffer.reset();
1332 mImage.reset();
1333 mAlphaImage.reset();
1334 mEraseColorSet = false;
1335 mPixelsSize = mSize;
1336 ComputeScanlineSize();
1337 DataChanged();
1340 void SkiaSalBitmap::DataChanged() { InvalidateChecksum(); }
1342 void SkiaSalBitmap::ResetPendingScaling()
1344 if (mPixelsSize == mSize)
1345 return;
1346 SkiaZone zone;
1347 mScaleQuality = BmpScaleFlag::BestQuality;
1348 mPixelsSize = mSize;
1349 ComputeScanlineSize();
1350 // Information about the pending scaling has been discarded, so make sure we do not
1351 // keep around any cached images that would still need scaling.
1352 if (mImage && imageSize(mImage) != mSize)
1353 mImage.reset();
1354 if (mAlphaImage && imageSize(mAlphaImage) != mSize)
1355 mAlphaImage.reset();
1358 OString SkiaSalBitmap::GetImageKey(DirectImage direct) const
1360 if (mEraseColorSet)
1362 std::stringstream ss;
1363 ss << std::hex << std::setfill('0') << std::setw(6)
1364 << static_cast<sal_uInt32>(mEraseColor.GetRGBColor()) << std::setw(2)
1365 << static_cast<int>(mEraseColor.GetAlpha());
1366 return OString::Concat("E") + ss.str().c_str();
1368 assert(direct == DirectImage::No || mImage);
1369 sk_sp<SkImage> image = GetSkImage(direct);
1370 // In some cases drawing code may try to draw the same content but using
1371 // different bitmaps (even underlying bitmaps), for example canvas apparently
1372 // copies the same things around in tdf#146095. For pixel-based images
1373 // it should be still cheaper to compute a checksum and avoid re-caching.
1374 if (!image->isTextureBacked())
1375 return OString::Concat("C") + OString::number(getSkImageChecksum(image));
1376 return OString::Concat("I") + OString::number(image->uniqueID());
1379 OString SkiaSalBitmap::GetAlphaImageKey(DirectImage direct) const
1381 if (mEraseColorSet)
1383 std::stringstream ss;
1384 ss << std::hex << std::setfill('0') << std::setw(2)
1385 << static_cast<int>(255 - SkColorGetA(fromEraseColorToAlphaImageColor(mEraseColor)));
1386 return OString::Concat("E") + ss.str().c_str();
1388 assert(direct == DirectImage::No || mAlphaImage);
1389 sk_sp<SkImage> image = GetAlphaSkImage(direct);
1390 if (!image->isTextureBacked())
1391 return OString::Concat("C") + OString::number(getSkImageChecksum(image));
1392 return OString::Concat("I") + OString::number(image->uniqueID());
1395 void SkiaSalBitmap::dump(const char* file) const
1397 // Use a copy, so that debugging doesn't affect this instance.
1398 SkiaSalBitmap copy;
1399 copy.Create(*this);
1400 SkiaHelper::dump(copy.GetSkImage(), file);
1403 #ifdef DBG_UTIL
1404 void SkiaSalBitmap::verify() const
1406 if (!mBuffer)
1407 return;
1408 // Use mPixelsSize, that describes the size of the actual data.
1409 assert(memcmp(mBuffer.get() + mScanlineSize * mPixelsSize.Height(), CANARY, sizeof(CANARY))
1410 == 0);
1412 #endif
1414 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */