nss: upgrade to release 3.73
[LibreOffice.git] / vcl / skia / salbmp.cxx
blobe29f84b90b5356f4bcd28832a06b2abfce4d709c
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 <bmpfast.hxx>
31 #include <vcl/bitmapaccess.hxx>
33 #include <SkCanvas.h>
34 #include <SkImage.h>
35 #include <SkPixelRef.h>
36 #include <SkSurface.h>
37 #include <SkSwizzle.h>
38 #include <SkColorFilter.h>
39 #include <SkColorMatrix.h>
40 #include <skia_opts.hxx>
42 #include <skia/utils.hxx>
43 #include <skia/zone.hxx>
45 #ifdef DBG_UTIL
46 #include <fstream>
47 #define CANARY "skia-canary"
48 #endif
50 // As constexpr here, evaluating it directly in code makes Clang warn about unreachable code.
51 constexpr bool kN32_SkColorTypeIsBGRA = (kN32_SkColorType == kBGRA_8888_SkColorType);
53 SkiaSalBitmap::SkiaSalBitmap() {}
55 SkiaSalBitmap::~SkiaSalBitmap() {}
57 static bool isValidBitCount(sal_uInt16 nBitCount)
59 return (nBitCount == 1) || (nBitCount == 4) || (nBitCount == 8) || (nBitCount == 24)
60 || (nBitCount == 32);
63 SkiaSalBitmap::SkiaSalBitmap(const sk_sp<SkImage>& image)
65 ResetAllData();
66 mImage = image;
67 mPalette = BitmapPalette();
68 mBitCount = 32;
69 mSize = mPixelsSize = Size(image->width(), image->height());
70 ComputeScanlineSize();
71 #ifdef DBG_UTIL
72 mWriteAccessCount = 0;
73 #endif
74 SAL_INFO("vcl.skia.trace", "bitmapfromimage(" << this << ")");
77 bool SkiaSalBitmap::Create(const Size& rSize, sal_uInt16 nBitCount, const BitmapPalette& rPal)
79 ResetAllData();
80 if (!isValidBitCount(nBitCount))
81 return false;
82 mPalette = rPal;
83 mBitCount = nBitCount;
84 mSize = mPixelsSize = rSize;
85 #ifdef DBG_UTIL
86 mWriteAccessCount = 0;
87 #endif
88 if (!ComputeScanlineSize())
90 mBitCount = 0;
91 mSize = mPixelsSize = Size();
92 mScanlineSize = 0;
93 mPalette = BitmapPalette();
94 return false;
96 SAL_INFO("vcl.skia.trace", "create(" << this << ")");
97 return true;
100 bool SkiaSalBitmap::ComputeScanlineSize()
102 int bitScanlineWidth;
103 if (o3tl::checked_multiply<int>(mPixelsSize.Width(), mBitCount, bitScanlineWidth))
105 SAL_WARN("vcl.skia", "checked multiply failed");
106 return false;
108 mScanlineSize = AlignedWidth4Bytes(bitScanlineWidth);
109 return true;
112 void SkiaSalBitmap::CreateBitmapData()
114 assert(!mBuffer);
115 // Make sure code has not missed calling ComputeScanlineSize().
116 assert(mScanlineSize == int(AlignedWidth4Bytes(mPixelsSize.Width() * mBitCount)));
117 // The pixels could be stored in SkBitmap, but Skia only supports 8bit gray, 16bit and 32bit formats
118 // (e.g. 24bpp is actually stored as 32bpp). But some of our code accessing the bitmap assumes that
119 // when it asked for 24bpp, the format really will be 24bpp (e.g. the png loader), so we cannot use
120 // SkBitmap to store the data. And even 8bpp is problematic, since Skia does not support palettes
121 // and a VCL bitmap can change its grayscale status simply by changing the palette.
122 // Moreover creating SkImage from SkBitmap does a data copy unless the bitmap is immutable.
123 // So just always store pixels in our buffer and convert as necessary.
124 if (mScanlineSize == 0 || mPixelsSize.Height() == 0)
125 return;
127 size_t allocate = mScanlineSize * mPixelsSize.Height();
128 #ifdef DBG_UTIL
129 allocate += sizeof(CANARY);
130 #endif
131 mBuffer = boost::make_shared_noinit<sal_uInt8[]>(allocate);
132 #ifdef DBG_UTIL
133 // fill with random garbage
134 sal_uInt8* buffer = mBuffer.get();
135 for (size_t i = 0; i < allocate; i++)
136 buffer[i] = (i & 0xFF);
137 memcpy(buffer + allocate - sizeof(CANARY), CANARY, sizeof(CANARY));
138 #endif
141 bool SkiaSalBitmap::Create(const SalBitmap& rSalBmp)
143 return Create(rSalBmp, rSalBmp.GetBitCount());
146 bool SkiaSalBitmap::Create(const SalBitmap& rSalBmp, SalGraphics* pGraphics)
148 return Create(rSalBmp, pGraphics ? pGraphics->GetBitCount() : rSalBmp.GetBitCount());
151 bool SkiaSalBitmap::Create(const SalBitmap& rSalBmp, sal_uInt16 nNewBitCount)
153 const SkiaSalBitmap& src = static_cast<const SkiaSalBitmap&>(rSalBmp);
154 mImage = src.mImage;
155 mAlphaImage = src.mAlphaImage;
156 mBuffer = src.mBuffer;
157 mPalette = src.mPalette;
158 mBitCount = src.mBitCount;
159 mSize = src.mSize;
160 mPixelsSize = src.mPixelsSize;
161 mScanlineSize = src.mScanlineSize;
162 mScaleQuality = src.mScaleQuality;
163 mEraseColorSet = src.mEraseColorSet;
164 mEraseColor = src.mEraseColor;
165 #ifdef DBG_UTIL
166 mWriteAccessCount = 0;
167 #endif
168 if (nNewBitCount != src.GetBitCount())
170 // This appears to be unused(?). Implement this just in case, but be lazy
171 // about it and rely on EnsureBitmapData() doing the conversion from mImage
172 // if needed, even if that may need unnecessary to- and from- SkImage
173 // conversion.
174 ResetToSkImage(GetSkImage());
176 SAL_INFO("vcl.skia.trace", "create(" << this << "): (" << &src << ")");
177 return true;
180 bool SkiaSalBitmap::Create(const css::uno::Reference<css::rendering::XBitmapCanvas>&, Size&, bool)
182 return false;
185 void SkiaSalBitmap::Destroy()
187 SAL_INFO("vcl.skia.trace", "destroy(" << this << ")");
188 #ifdef DBG_UTIL
189 assert(mWriteAccessCount == 0);
190 #endif
191 ResetAllData();
194 Size SkiaSalBitmap::GetSize() const { return mSize; }
196 sal_uInt16 SkiaSalBitmap::GetBitCount() const { return mBitCount; }
198 BitmapBuffer* SkiaSalBitmap::AcquireBuffer(BitmapAccessMode nMode)
200 switch (nMode)
202 case BitmapAccessMode::Write:
203 EnsureBitmapUniqueData();
204 if (!mBuffer)
205 return nullptr;
206 assert(mPixelsSize == mSize);
207 assert(!mEraseColorSet);
208 break;
209 case BitmapAccessMode::Read:
210 EnsureBitmapData();
211 if (!mBuffer)
212 return nullptr;
213 assert(mPixelsSize == mSize);
214 assert(!mEraseColorSet);
215 break;
216 case BitmapAccessMode::Info:
217 break;
219 #ifdef DBG_UTIL
220 // BitmapWriteAccess stores also a copy of the palette and it can
221 // be modified, so concurrent reading of it might result in inconsistencies.
222 assert(mWriteAccessCount == 0 || nMode == BitmapAccessMode::Write);
223 #endif
224 BitmapBuffer* buffer = new BitmapBuffer;
225 buffer->mnWidth = mSize.Width();
226 buffer->mnHeight = mSize.Height();
227 buffer->mnBitCount = mBitCount;
228 buffer->maPalette = mPalette;
229 buffer->mpBits = mBuffer.get();
230 if (mPixelsSize == mSize)
231 buffer->mnScanlineSize = mScanlineSize;
232 else
234 // The value of mScanlineSize is based on internal mPixelsSize, but the outside
235 // world cares about mSize, the size that the report as the size of the bitmap,
236 // regardless of any internal state. So report scanline size for that size.
237 Size savedPixelsSize = mPixelsSize;
238 mPixelsSize = mSize;
239 ComputeScanlineSize();
240 buffer->mnScanlineSize = mScanlineSize;
241 mPixelsSize = savedPixelsSize;
242 ComputeScanlineSize();
244 switch (mBitCount)
246 case 1:
247 buffer->mnFormat = ScanlineFormat::N1BitMsbPal;
248 break;
249 case 4:
250 buffer->mnFormat = ScanlineFormat::N4BitMsnPal;
251 break;
252 case 8:
253 buffer->mnFormat = ScanlineFormat::N8BitPal;
254 break;
255 case 24:
256 // Make the RGB/BGR format match the default Skia 32bpp format, to allow
257 // easy conversion later.
258 buffer->mnFormat = kN32_SkColorTypeIsBGRA ? ScanlineFormat::N24BitTcBgr
259 : ScanlineFormat::N24BitTcRgb;
260 break;
261 case 32:
262 buffer->mnFormat = kN32_SkColorTypeIsBGRA ? ScanlineFormat::N32BitTcBgra
263 : ScanlineFormat::N32BitTcRgba;
264 break;
265 default:
266 abort();
268 buffer->mnFormat |= ScanlineFormat::TopDown;
269 #ifdef DBG_UTIL
270 if (nMode == BitmapAccessMode::Write)
271 ++mWriteAccessCount;
272 #endif
273 return buffer;
276 void SkiaSalBitmap::ReleaseBuffer(BitmapBuffer* pBuffer, BitmapAccessMode nMode)
278 if (nMode == BitmapAccessMode::Write)
280 #ifdef DBG_UTIL
281 assert(mWriteAccessCount > 0);
282 --mWriteAccessCount;
283 #endif
284 mPalette = pBuffer->maPalette;
285 ResetToBuffer();
286 InvalidateChecksum();
288 // Are there any more ground movements underneath us ?
289 assert(pBuffer->mnWidth == mSize.Width());
290 assert(pBuffer->mnHeight == mSize.Height());
291 assert(pBuffer->mnBitCount == mBitCount);
292 assert(pBuffer->mpBits == mBuffer.get());
293 verify();
294 delete pBuffer;
297 bool SkiaSalBitmap::GetSystemData(BitmapSystemData&)
299 #ifdef DBG_UTIL
300 assert(mWriteAccessCount == 0);
301 #endif
302 return false;
305 bool SkiaSalBitmap::ScalingSupported() const { return true; }
307 bool SkiaSalBitmap::Scale(const double& rScaleX, const double& rScaleY, BmpScaleFlag nScaleFlag)
309 SkiaZone zone;
310 #ifdef DBG_UTIL
311 assert(mWriteAccessCount == 0);
312 #endif
313 Size newSize(FRound(mSize.Width() * rScaleX), FRound(mSize.Height() * rScaleY));
314 if (mSize == newSize)
315 return true;
317 SAL_INFO("vcl.skia.trace", "scale(" << this << "): " << mSize << "/" << mBitCount << "->"
318 << newSize << ":" << static_cast<int>(nScaleFlag));
320 if (mEraseColorSet)
321 { // Simple.
322 mSize = mPixelsSize = newSize;
323 ComputeScanlineSize();
324 EraseInternal(mEraseColor);
325 return true;
328 // The idea here is that the actual scaling will be delayed until the result
329 // is actually needed. Usually the scaled bitmap will be drawn somewhere,
330 // so delaying will mean the scaling can be done as a part of GetSkImage().
331 // That means it can be GPU-accelerated, while done here directly it would need
332 // to be either done by CPU, or with the CPU->GPU->CPU roundtrip required
333 // by GPU-accelerated scaling.
334 // Pending scaling is detected by 'mSize != mPixelsSize'.
335 SkFilterQuality currentQuality;
336 switch (nScaleFlag)
338 case BmpScaleFlag::Fast:
339 currentQuality = kNone_SkFilterQuality;
340 break;
341 case BmpScaleFlag::Default:
342 currentQuality = kMedium_SkFilterQuality;
343 break;
344 case BmpScaleFlag::BestQuality:
345 currentQuality = kHigh_SkFilterQuality;
346 break;
347 default:
348 SAL_INFO("vcl.skia.trace", "scale(" << this << "): unsupported scale algorithm");
349 return false;
351 if (mBitCount < 24 && !mPalette.IsGreyPalette8Bit())
353 // Scaling can introduce additional colors not present in the original
354 // bitmap (e.g. when smoothing). If the bitmap is indexed (has non-trivial palette),
355 // this would break the bitmap, because the actual scaling is done only somewhen later.
356 // Linear 8bit palette (grey) is ok, since there we use directly the values as colors.
357 SAL_INFO("vcl.skia.trace", "scale(" << this << "): indexed bitmap");
358 return false;
360 // if there is already one scale() pending, use the lowest quality of all requested
361 static_assert(kMedium_SkFilterQuality < kHigh_SkFilterQuality);
362 mScaleQuality = std::min(mScaleQuality, currentQuality);
363 // scaling will be actually done on-demand when needed, the need will be recognized
364 // by mSize != mPixelsSize
365 mSize = newSize;
366 // Do not reset cached data if mImage is possibly the only data we have.
367 if (mImage)
368 ResetToSkImage(mImage);
369 else
370 ResetToBuffer();
371 // The rest will be handled when the scaled bitmap is actually needed,
372 // such as in EnsureBitmapData() or GetSkImage().
373 return true;
376 bool SkiaSalBitmap::Replace(const Color&, const Color&, sal_uInt8)
378 #ifdef DBG_UTIL
379 assert(mWriteAccessCount == 0);
380 #endif
381 return false;
384 bool SkiaSalBitmap::ConvertToGreyscale()
386 #ifdef DBG_UTIL
387 assert(mWriteAccessCount == 0);
388 #endif
389 // Normally this would need to convert contents of mBuffer for all possible formats,
390 // so just let the VCL algorithm do it.
391 // Avoid the costly SkImage->buffer->SkImage conversion.
392 if (!mBuffer && mImage)
394 if (mBitCount == 8 && mPalette.IsGreyPalette8Bit())
395 return true;
396 sk_sp<SkSurface> surface
397 = SkiaHelper::createSkSurface(mPixelsSize, mImage->imageInfo().alphaType());
398 SkPaint paint;
399 paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
400 // VCL uses different coefficients for conversion to gray than Skia, so use the VCL
401 // values from Bitmap::ImplMakeGreyscales(). Do not use kGray_8_SkColorType,
402 // Skia would use its gray conversion formula.
403 // NOTE: The matrix is 4x5 organized as columns (i.e. each line is a column, not a row).
404 constexpr SkColorMatrix toGray(77 / 256.0, 151 / 256.0, 28 / 256.0, 0, 0, // R column
405 77 / 256.0, 151 / 256.0, 28 / 256.0, 0, 0, // G column
406 77 / 256.0, 151 / 256.0, 28 / 256.0, 0, 0, // B column
407 0, 0, 0, 1, 0); // don't modify alpha
408 paint.setColorFilter(SkColorFilters::Matrix(toGray));
409 surface->getCanvas()->drawImage(mImage, 0, 0, &paint);
410 mBitCount = 8;
411 ComputeScanlineSize();
412 mPalette = Bitmap::GetGreyPalette(256);
413 ResetToSkImage(SkiaHelper::makeCheckedImageSnapshot(surface));
414 SAL_INFO("vcl.skia.trace", "converttogreyscale(" << this << ")");
415 return true;
417 return false;
420 bool SkiaSalBitmap::InterpretAs8Bit()
422 #ifdef DBG_UTIL
423 assert(mWriteAccessCount == 0);
424 #endif
425 if (mBitCount == 8 && mPalette.IsGreyPalette8Bit())
426 return true;
427 if (mEraseColorSet)
429 mBitCount = 8;
430 ComputeScanlineSize();
431 mPalette = Bitmap::GetGreyPalette(256);
432 EraseInternal(mEraseColor);
433 SAL_INFO("vcl.skia.trace", "interpretas8bit(" << this << ") with erase color");
434 return true;
436 // This is usually used by AlphaMask, the point is just to treat
437 // the content as an alpha channel. This is often used
438 // by the horrible separate-alpha-outdev hack, where the bitmap comes
439 // from SkiaSalGraphicsImpl::GetBitmap(), so only mImage is set,
440 // and that is followed by a later call to GetAlphaSkImage().
441 // Avoid the costly SkImage->buffer->SkImage conversion and simply
442 // just treat the SkImage as being for 8bit bitmap. EnsureBitmapData()
443 // will do the conversion if needed, but the normal case will be
444 // GetAlphaSkImage() creating kAlpha_8_SkColorType SkImage from it.
445 if (mImage)
447 mBitCount = 8;
448 ComputeScanlineSize();
449 mPalette = Bitmap::GetGreyPalette(256);
450 ResetToSkImage(mImage); // keep mImage, it will be interpreted as 8bit if needed
451 SAL_INFO("vcl.skia.trace", "interpretas8bit(" << this << ") with image");
452 return true;
454 SAL_INFO("vcl.skia.trace", "interpretas8bit(" << this << ") with pixel data, ignoring");
455 return false;
458 bool SkiaSalBitmap::Erase(const Color& color)
460 // Optimized variant, just remember the color and apply it when needed,
461 // which may save having to do format conversions (e.g. GetSkImage()
462 // may directly erase the SkImage).
463 EraseInternal(color);
464 SAL_INFO("vcl.skia.trace", "erase(" << this << ")");
465 return true;
468 void SkiaSalBitmap::EraseInternal(const Color& color)
470 ResetAllData();
471 mEraseColorSet = true;
472 mEraseColor = color;
475 bool SkiaSalBitmap::AlphaBlendWith(const SalBitmap& rSalBmp)
477 const SkiaSalBitmap* otherBitmap = dynamic_cast<const SkiaSalBitmap*>(&rSalBmp);
478 if (!otherBitmap)
479 return false;
480 if (mSize != otherBitmap->mSize)
481 return false;
482 // We're called from AlphaMask, which should ensure 8bit.
483 assert(GetBitCount() == 8 && mPalette.IsGreyPalette8Bit());
484 // If neither bitmap have Skia images, then AlphaMask::BlendWith() will be faster,
485 // as it will operate on mBuffer pixel buffers, while for Skia we'd need to convert it.
486 // If one has and one doesn't, do it using Skia, under the assumption that after this
487 // the resulting Skia image will be needed for drawing.
488 if (!(mImage || mEraseColorSet) && !(otherBitmap->mImage || otherBitmap->mEraseColorSet))
489 return false;
490 // This is for AlphaMask, which actually stores the alpha as the pixel values.
491 // I.e. take value of the color channel (one of them, if >8bit, they should be the same).
492 if (mEraseColorSet && otherBitmap->mEraseColorSet)
494 const sal_uInt16 nGrey1 = mEraseColor.GetRed();
495 const sal_uInt16 nGrey2 = otherBitmap->mEraseColor.GetRed();
496 const sal_uInt8 nGrey = static_cast<sal_uInt8>(nGrey1 + nGrey2 - nGrey1 * nGrey2 / 255);
497 mEraseColor = Color(nGrey, nGrey, nGrey);
498 SAL_INFO("vcl.skia.trace",
499 "alphablendwith(" << this << ") : with erase color " << otherBitmap);
500 return true;
502 std::unique_ptr<SkiaSalBitmap> otherBitmapAllocated;
503 if (otherBitmap->GetBitCount() != 8 || !otherBitmap->mPalette.IsGreyPalette8Bit())
504 { // Convert/interpret as 8bit if needed.
505 otherBitmapAllocated = std::make_unique<SkiaSalBitmap>();
506 if (!otherBitmapAllocated->Create(*otherBitmap) || !otherBitmapAllocated->InterpretAs8Bit())
507 return false;
508 otherBitmap = otherBitmapAllocated.get();
510 sk_sp<SkSurface> surface = SkiaHelper::createSkSurface(mSize);
511 SkPaint paint;
512 paint.setBlendMode(SkBlendMode::kSrc); // set as is
513 surface->getCanvas()->drawImage(GetSkImage(), 0, 0, &paint);
514 paint.setBlendMode(SkBlendMode::kScreen); // src+dest - src*dest/255 (in 0..1)
515 surface->getCanvas()->drawImage(otherBitmap->GetSkImage(), 0, 0, &paint);
516 ResetToSkImage(SkiaHelper::makeCheckedImageSnapshot(surface));
517 SAL_INFO("vcl.skia.trace", "alphablendwith(" << this << ") : with image " << otherBitmap);
518 return true;
521 SkBitmap SkiaSalBitmap::GetAsSkBitmap() const
523 #ifdef DBG_UTIL
524 assert(mWriteAccessCount == 0);
525 #endif
526 EnsureBitmapData();
527 assert(mSize == mPixelsSize); // data has already been scaled if needed
528 SkiaZone zone;
529 SkBitmap bitmap;
530 if (mBuffer)
532 if (mBitCount == 32)
534 // Make a copy, the bitmap should be immutable (otherwise converting it
535 // to SkImage will make a copy anyway).
536 const size_t bytes = mPixelsSize.Height() * mScanlineSize;
537 std::unique_ptr<sal_uInt8[]> data(new sal_uInt8[bytes]);
538 memcpy(data.get(), mBuffer.get(), bytes);
539 if (!bitmap.installPixels(
540 SkImageInfo::MakeS32(mPixelsSize.Width(), mPixelsSize.Height(), alphaType()),
541 data.release(), mScanlineSize,
542 [](void* addr, void*) { delete[] static_cast<sal_uInt8*>(addr); }, nullptr))
543 abort();
544 bitmap.setImmutable();
546 else if (mBitCount == 24)
548 // Convert 24bpp RGB/BGR to 32bpp RGBA/BGRA.
549 std::unique_ptr<uint32_t[]> data(
550 new uint32_t[mPixelsSize.Height() * mPixelsSize.Width()]);
551 uint32_t* dest = data.get();
552 // SkConvertRGBToRGBA() also works as BGR to BGRA (the function extends 3 bytes to 4
553 // by adding 0xFF alpha, so position of B and R doesn't matter).
554 if (mPixelsSize.Width() * 3 == mScanlineSize)
555 SkConvertRGBToRGBA(dest, mBuffer.get(), mPixelsSize.Height() * mPixelsSize.Width());
556 else
558 for (tools::Long y = 0; y < mPixelsSize.Height(); ++y)
560 const sal_uInt8* src = mBuffer.get() + mScanlineSize * y;
561 SkConvertRGBToRGBA(dest, src, mPixelsSize.Width());
562 dest += mPixelsSize.Width();
565 if (!bitmap.installPixels(
566 SkImageInfo::MakeS32(mPixelsSize.Width(), mPixelsSize.Height(),
567 kOpaque_SkAlphaType),
568 data.release(), mPixelsSize.Width() * 4,
569 [](void* addr, void*) { delete[] static_cast<sal_uInt8*>(addr); }, nullptr))
570 abort();
571 bitmap.setImmutable();
573 else if (mBitCount == 8 && mPalette.IsGreyPalette8Bit())
575 // Convert 8bpp gray to 32bpp RGBA/BGRA.
576 // There's also kGray_8_SkColorType, but it's probably simpler to make
577 // GetAsSkBitmap() always return 32bpp SkBitmap and then assume mImage
578 // is always 32bpp too.
579 std::unique_ptr<uint32_t[]> data(
580 new uint32_t[mPixelsSize.Height() * mPixelsSize.Width()]);
581 uint32_t* dest = data.get();
582 if (mPixelsSize.Width() * 1 == mScanlineSize)
583 SkConvertGrayToRGBA(dest, mBuffer.get(),
584 mPixelsSize.Height() * mPixelsSize.Width());
585 else
587 for (tools::Long y = 0; y < mPixelsSize.Height(); ++y)
589 const sal_uInt8* src = mBuffer.get() + mScanlineSize * y;
590 SkConvertGrayToRGBA(dest, src, mPixelsSize.Width());
591 dest += mPixelsSize.Width();
594 if (!bitmap.installPixels(
595 SkImageInfo::MakeS32(mPixelsSize.Width(), mPixelsSize.Height(),
596 kOpaque_SkAlphaType),
597 data.release(), mPixelsSize.Width() * 4,
598 [](void* addr, void*) { delete[] static_cast<sal_uInt8*>(addr); }, nullptr))
599 abort();
600 bitmap.setImmutable();
602 else
604 std::unique_ptr<sal_uInt8[]> data = convertDataBitCount(
605 mBuffer.get(), mPixelsSize.Width(), mPixelsSize.Height(), mBitCount, mScanlineSize,
606 mPalette, kN32_SkColorTypeIsBGRA ? BitConvert::BGRA : BitConvert::RGBA);
607 if (!bitmap.installPixels(
608 SkImageInfo::MakeS32(mPixelsSize.Width(), mPixelsSize.Height(),
609 kOpaque_SkAlphaType),
610 data.release(), mPixelsSize.Width() * 4,
611 [](void* addr, void*) { delete[] static_cast<sal_uInt8*>(addr); }, nullptr))
612 abort();
613 bitmap.setImmutable();
616 return bitmap;
619 static SkColor toSkColor(Color color)
621 return SkColorSetARGB(255 - color.GetTransparency(), color.GetRed(), color.GetGreen(),
622 color.GetBlue());
625 // If mEraseColor is set, this is the color to use when the bitmap is used as alpha bitmap.
626 // E.g. COL_BLACK actually means fully opaque and COL_WHITE means fully transparent.
627 // This is because the alpha value is set as the color itself, not the alpha of the color.
628 // Additionally VCL actually uses transparency and not opacity, so we should use "255 - value",
629 // but we account for this by doing SkBlendMode::kDstOut when using alpha images (which
630 // basically does another "255 - alpha"), so do not do it here.
631 static SkColor fromEraseColorToAlphaImageColor(Color color)
633 return SkColorSetARGB(color.GetBlue(), 0, 0, 0);
636 // SkiaSalBitmap can store data in both the SkImage and our mBuffer, which with large
637 // images can waste quite a lot of memory. Ideally we should store the data in Skia's
638 // SkBitmap, but LO wants us to support data formats that Skia doesn't support.
639 // So try to conserve memory by keeping the data only once in that was the most
640 // recently wanted storage, and drop the other one. Usually the other one won't be needed
641 // for a long time, and especially with raster the conversion is usually fast.
642 // Do this only with raster, to avoid GPU->CPU transfer in GPU mode (exception is 32bit
643 // builds, where memory is more important). Also don't do this with paletted bitmaps,
644 // where EnsureBitmapData() would be expensive.
645 // Ideally SalBitmap should be able to say which bitmap formats it supports
646 // and VCL code should oblige, which would allow reusing the same data.
647 bool SkiaSalBitmap::ConserveMemory() const
649 static bool keepBitmapBuffer = getenv("SAL_SKIA_KEEP_BITMAP_BUFFER") != nullptr;
650 constexpr bool is32Bit = sizeof(void*) == 4;
651 // 16MiB bitmap data at least (set to 0 for easy testing).
652 constexpr tools::Long maxBufferSize = 2000 * 2000 * 4;
653 return !keepBitmapBuffer
654 && (SkiaHelper::renderMethodToUse() == SkiaHelper::RenderRaster || is32Bit)
655 && mPixelsSize.Height() * mScanlineSize > maxBufferSize
656 && (mBitCount > 8 || (mBitCount == 8 && mPalette.IsGreyPalette8Bit()));
659 const sk_sp<SkImage>& SkiaSalBitmap::GetSkImage() const
661 #ifdef DBG_UTIL
662 assert(mWriteAccessCount == 0);
663 #endif
664 if (mEraseColorSet)
666 SkiaZone zone;
667 sk_sp<SkSurface> surface = SkiaHelper::createSkSurface(
668 mSize, mEraseColor.GetTransparency() != 0 ? kPremul_SkAlphaType : kOpaque_SkAlphaType);
669 assert(surface);
670 surface->getCanvas()->clear(toSkColor(mEraseColor));
671 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
672 thisPtr->mImage = SkiaHelper::makeCheckedImageSnapshot(surface);
673 SAL_INFO("vcl.skia.trace", "getskimage(" << this << ") from erase color " << mEraseColor);
674 return mImage;
676 if (mPixelsSize != mSize && !mImage
677 && SkiaHelper::renderMethodToUse() != SkiaHelper::RenderRaster)
679 // The bitmap has a pending scaling, but no image. This function would below call GetAsSkBitmap(),
680 // which would do CPU-based pixel scaling, and then it would get converted to an image.
681 // Be more efficient, first convert to an image and then the block below will scale on the GPU.
682 SAL_INFO("vcl.skia.trace", "getskimage(" << this << "): shortcut image scaling "
683 << mPixelsSize << "->" << mSize);
684 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
685 Size savedSize = mSize;
686 thisPtr->mSize = mPixelsSize; // block scaling
687 SkiaZone zone;
688 sk_sp<SkImage> image = SkiaHelper::createSkImage(GetAsSkBitmap());
689 assert(image);
690 thisPtr->mSize = savedSize;
691 thisPtr->ResetToSkImage(image);
693 if (mImage)
695 if (mImage->width() != mSize.Width() || mImage->height() != mSize.Height())
697 assert(!mBuffer); // This code should be only called if only mImage holds data.
698 SkiaZone zone;
699 sk_sp<SkSurface> surface
700 = SkiaHelper::createSkSurface(mSize, mImage->imageInfo().alphaType());
701 assert(surface);
702 SkPaint paint;
703 paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
704 paint.setFilterQuality(mScaleQuality);
705 surface->getCanvas()->drawImageRect(
706 mImage, SkRect::MakeWH(mImage->width(), mImage->height()),
707 SkRect::MakeWH(mSize.Width(), mSize.Height()), &paint);
708 SAL_INFO("vcl.skia.trace", "getskimage(" << this << "): image scaled "
709 << Size(mImage->width(), mImage->height())
710 << "->" << mSize << ":"
711 << static_cast<int>(mScaleQuality));
712 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
713 thisPtr->mImage = SkiaHelper::makeCheckedImageSnapshot(surface);
715 return mImage;
717 SkiaZone zone;
718 sk_sp<SkImage> image = SkiaHelper::createSkImage(GetAsSkBitmap());
719 assert(image);
720 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
721 thisPtr->mImage = image;
722 // The data is now stored both in the SkImage and in our mBuffer, so drop the buffer
723 // if conserving memory. It'll be converted back by EnsureBitmapData() if needed.
724 if (ConserveMemory())
726 SAL_INFO("vcl.skia.trace", "getskimage(" << this << "): dropping buffer");
727 thisPtr->ResetToSkImage(mImage);
729 SAL_INFO("vcl.skia.trace", "getskimage(" << this << ")");
730 return mImage;
733 const sk_sp<SkImage>& SkiaSalBitmap::GetAlphaSkImage() const
735 #ifdef DBG_UTIL
736 assert(mWriteAccessCount == 0);
737 #endif
738 if (mEraseColorSet)
740 SkiaZone zone;
741 sk_sp<SkSurface> surface = SkiaHelper::createSkSurface(mSize, kAlpha_8_SkColorType);
742 assert(surface);
743 surface->getCanvas()->clear(fromEraseColorToAlphaImageColor(mEraseColor));
744 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
745 thisPtr->mAlphaImage = SkiaHelper::makeCheckedImageSnapshot(surface);
746 SAL_INFO("vcl.skia.trace",
747 "getalphaskimage(" << this << ") from erase color " << mEraseColor);
748 return mAlphaImage;
750 if (mAlphaImage)
752 assert(mSize == mPixelsSize); // data has already been scaled if needed
753 return mAlphaImage;
755 if (mImage)
757 SkiaZone zone;
758 sk_sp<SkSurface> surface = SkiaHelper::createSkSurface(mSize, kAlpha_8_SkColorType);
759 assert(surface);
760 SkPaint paint;
761 paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
762 // Move the R channel value to the alpha channel. This seems to be the only
763 // way to reinterpret data in SkImage as an alpha SkImage without accessing the pixels.
764 // NOTE: The matrix is 4x5 organized as columns (i.e. each line is a column, not a row).
765 constexpr SkColorMatrix redToAlpha(0, 0, 0, 0, 0, // R column
766 0, 0, 0, 0, 0, // G column
767 0, 0, 0, 0, 0, // B column
768 1, 0, 0, 0, 0); // A column
769 paint.setColorFilter(SkColorFilters::Matrix(redToAlpha));
770 bool scaling = mImage->width() != mSize.Width() || mImage->height() != mSize.Height();
771 if (scaling)
773 assert(!mBuffer); // This code should be only called if only mImage holds data.
774 paint.setFilterQuality(mScaleQuality);
776 surface->getCanvas()->drawImageRect(mImage,
777 SkRect::MakeWH(mImage->width(), mImage->height()),
778 SkRect::MakeWH(mSize.Width(), mSize.Height()), &paint);
779 if (scaling)
780 SAL_INFO("vcl.skia.trace", "getalphaskimage(" << this << "): image scaled "
781 << Size(mImage->width(), mImage->height())
782 << "->" << mSize << ":"
783 << static_cast<int>(mScaleQuality));
784 else
785 SAL_INFO("vcl.skia.trace", "getalphaskimage(" << this << ") from image");
786 // Don't bother here with ConserveMemory(), mImage -> mAlphaImage conversions should
787 // generally only happen with the separate-alpha-outdev hack, and those bitmaps should
788 // be temporary.
789 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
790 thisPtr->mAlphaImage = SkiaHelper::makeCheckedImageSnapshot(surface);
791 return mAlphaImage;
793 SkiaZone zone;
794 EnsureBitmapData();
795 assert(mSize == mPixelsSize); // data has already been scaled if needed
796 SkBitmap alphaBitmap;
797 if (mBuffer && mBitCount <= 8)
799 assert(mBuffer.get());
800 verify();
801 std::unique_ptr<sal_uInt8[]> data
802 = convertDataBitCount(mBuffer.get(), mSize.Width(), mSize.Height(), mBitCount,
803 mScanlineSize, mPalette, BitConvert::A8);
804 if (!alphaBitmap.installPixels(
805 SkImageInfo::MakeA8(mSize.Width(), mSize.Height()), data.release(), mSize.Width(),
806 [](void* addr, void*) { delete[] static_cast<sal_uInt8*>(addr); }, nullptr))
807 abort();
808 alphaBitmap.setImmutable();
809 sk_sp<SkImage> image = SkiaHelper::createSkImage(alphaBitmap);
810 assert(image);
811 const_cast<sk_sp<SkImage>&>(mAlphaImage) = image;
813 else
815 sk_sp<SkSurface> surface = SkiaHelper::createSkSurface(mSize, kAlpha_8_SkColorType);
816 assert(surface);
817 SkPaint paint;
818 paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
819 // Move the R channel value to the alpha channel. This seems to be the only
820 // way to reinterpret data in SkImage as an alpha SkImage without accessing the pixels.
821 // NOTE: The matrix is 4x5 organized as columns (i.e. each line is a column, not a row).
822 constexpr SkColorMatrix redToAlpha(0, 0, 0, 0, 0, // R column
823 0, 0, 0, 0, 0, // G column
824 0, 0, 0, 0, 0, // B column
825 1, 0, 0, 0, 0); // A column
826 paint.setColorFilter(SkColorFilters::Matrix(redToAlpha));
827 surface->getCanvas()->drawBitmap(GetAsSkBitmap(), 0, 0, &paint);
828 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
829 thisPtr->mAlphaImage = SkiaHelper::makeCheckedImageSnapshot(surface);
831 // The data is now stored both in the SkImage and in our mBuffer, so drop the buffer
832 // if conserving memory and the conversion back would be simple (it'll be converted back
833 // by EnsureBitmapData() if needed).
834 if (ConserveMemory() && mBitCount == 8 && mPalette.IsGreyPalette8Bit())
836 SAL_INFO("vcl.skia.trace", "getalphaskimage(" << this << "): dropping buffer");
837 SkiaSalBitmap* thisPtr = const_cast<SkiaSalBitmap*>(this);
838 thisPtr->mBuffer.reset();
840 SAL_INFO("vcl.skia.trace", "getalphaskimage(" << this << ")");
841 return mAlphaImage;
844 // If the bitmap is to be erased, SkShader with the color set is more efficient
845 // than creating an image filled with the color.
846 bool SkiaSalBitmap::PreferSkShader() const { return mEraseColorSet; }
848 sk_sp<SkShader> SkiaSalBitmap::GetSkShader() const
850 if (mEraseColorSet)
851 return SkShaders::Color(toSkColor(mEraseColor));
852 return GetSkImage()->makeShader();
855 sk_sp<SkShader> SkiaSalBitmap::GetAlphaSkShader() const
857 if (mEraseColorSet)
858 return SkShaders::Color(fromEraseColorToAlphaImageColor(mEraseColor));
859 return GetAlphaSkImage()->makeShader();
862 bool SkiaSalBitmap::IsFullyOpaqueAsAlpha() const
864 if (!mEraseColorSet)
865 return false; // don't bother figuring it out from the pixels
866 // If the erase color is set so that this bitmap used as alpha would
867 // mean a fully opaque alpha mask (= noop), we can skip using it.
868 // Note that for alpha bitmaps we use the VCL "transparency" convention,
869 // i.e. alpha 0 is opaque.
870 return SkColorGetA(fromEraseColorToAlphaImageColor(mEraseColor)) == 0;
873 SkAlphaType SkiaSalBitmap::alphaType() const
875 if (mEraseColorSet)
876 return mEraseColor.GetTransparency() != 0 ? kPremul_SkAlphaType : kOpaque_SkAlphaType;
877 #if SKIA_USE_BITMAP32
878 // The bitmap's alpha matters only if SKIA_USE_BITMAP32 is set, otherwise
879 // the alpha is in a separate bitmap.
880 if (mBitCount == 32)
881 return kPremul_SkAlphaType;
882 #endif
883 return kOpaque_SkAlphaType;
886 void SkiaSalBitmap::PerformErase()
888 if (mPixelsSize.IsEmpty())
889 return;
890 BitmapBuffer* bitmapBuffer = AcquireBuffer(BitmapAccessMode::Write);
891 if (bitmapBuffer == nullptr)
892 abort();
893 Color fastColor = mEraseColor;
894 if (!!mPalette)
895 fastColor = mPalette.GetBestIndex(fastColor);
896 if (!ImplFastEraseBitmap(*bitmapBuffer, fastColor))
898 FncSetPixel setPixel = BitmapReadAccess::SetPixelFunction(bitmapBuffer->mnFormat);
899 assert(bitmapBuffer->mnFormat & ScanlineFormat::TopDown);
900 // Set first scanline, copy to others.
901 Scanline scanline = bitmapBuffer->mpBits;
902 for (tools::Long x = 0; x < bitmapBuffer->mnWidth; ++x)
903 setPixel(scanline, x, mEraseColor, bitmapBuffer->maColorMask);
904 for (tools::Long y = 1; y < bitmapBuffer->mnHeight; ++y)
905 memcpy(scanline + y * bitmapBuffer->mnScanlineSize, scanline,
906 bitmapBuffer->mnScanlineSize);
908 ReleaseBuffer(bitmapBuffer, BitmapAccessMode::Write);
911 void SkiaSalBitmap::EnsureBitmapData()
913 if (mEraseColorSet)
915 SkiaZone zone;
916 if (mPixelsSize != mSize)
918 mPixelsSize = mSize;
919 ComputeScanlineSize();
920 mBuffer.reset();
922 mScaleQuality = kHigh_SkFilterQuality;
923 if (!mBuffer)
924 CreateBitmapData();
925 // Unset now, so that repeated call will return mBuffer.
926 mEraseColorSet = false;
927 PerformErase();
928 verify();
929 SAL_INFO("vcl.skia.trace",
930 "ensurebitmapdata(" << this << ") from erase color " << mEraseColor);
931 return;
934 if (mBuffer)
936 if (mSize == mPixelsSize)
937 return;
938 // Pending scaling. Create raster SkImage from the bitmap data
939 // at the pixel size and then the code below will scale at the correct
940 // bpp from the image.
941 SAL_INFO("vcl.skia.trace", "ensurebitmapdata(" << this << "): pixels to be scaled "
942 << mPixelsSize << "->" << mSize << ":"
943 << static_cast<int>(mScaleQuality));
944 Size savedSize = mSize;
945 mSize = mPixelsSize;
946 ResetToSkImage(SkImage::MakeFromBitmap(GetAsSkBitmap()));
947 mSize = savedSize;
950 // Convert from alpha image, if the conversion is simple.
951 if (mAlphaImage && mSize == mPixelsSize && mBitCount == 8 && mPalette.IsGreyPalette8Bit())
953 assert(mAlphaImage->colorType() == kAlpha_8_SkColorType);
954 SkiaZone zone;
955 SkBitmap bitmap;
956 if (!bitmap.tryAllocPixels(SkImageInfo::MakeA8(mSize.Width(), mSize.Height())))
957 abort();
958 SkCanvas canvas(bitmap);
959 SkPaint paint;
960 paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
961 canvas.drawImage(mAlphaImage, 0, 0, &paint);
962 canvas.flush();
963 bitmap.setImmutable();
964 CreateBitmapData();
965 assert(mBuffer != nullptr);
966 assert(mPixelsSize == mSize);
967 if (int(bitmap.rowBytes()) == mScanlineSize)
968 memcpy(mBuffer.get(), bitmap.getPixels(), mSize.Height() * mScanlineSize);
969 else
971 for (tools::Long y = 0; y < mSize.Height(); ++y)
973 const uint8_t* src = static_cast<uint8_t*>(bitmap.getAddr(0, y));
974 sal_uInt8* dest = mBuffer.get() + mScanlineSize * y;
975 memcpy(dest, src, mScanlineSize);
978 verify();
979 // We've created the bitmap data from mAlphaImage, drop the image if conserving memory,
980 // it'll be converted back if needed.
981 if (ConserveMemory())
983 SAL_INFO("vcl.skia.trace", "ensurebitmapdata(" << this << "): dropping images");
984 ResetToBuffer();
986 SAL_INFO("vcl.skia.trace", "ensurebitmapdata(" << this << "): from alpha image");
987 return;
990 if (!mImage)
992 // No data at all, create uninitialized data.
993 CreateBitmapData();
994 SAL_INFO("vcl.skia.trace", "ensurebitmapdata(" << this << "): uninitialized");
995 return;
997 // Try to fill mBuffer from mImage.
998 assert(mImage->colorType() == kN32_SkColorType);
999 SkiaZone zone;
1000 // If the source image has no alpha, then use no alpha (faster to convert), otherwise
1001 // use kUnpremul_SkAlphaType to make Skia convert from premultiplied alpha when reading
1002 // from the SkImage (the alpha will be ignored if converting to bpp<32 formats, but
1003 // the color channels must be unpremultiplied. Unless bpp==32 and SKIA_USE_BITMAP32,
1004 // in which case use kPremul_SkAlphaType, since SKIA_USE_BITMAP32 implies premultiplied alpha.
1005 SkAlphaType alphaType = kUnpremul_SkAlphaType;
1006 if (mImage->imageInfo().alphaType() == kOpaque_SkAlphaType)
1007 alphaType = kOpaque_SkAlphaType;
1008 #if SKIA_USE_BITMAP32
1009 if (mBitCount == 32)
1010 alphaType = kPremul_SkAlphaType;
1011 #endif
1012 SkBitmap bitmap;
1013 if (!bitmap.tryAllocPixels(SkImageInfo::MakeS32(mSize.Width(), mSize.Height(), alphaType)))
1014 abort();
1015 SkCanvas canvas(bitmap);
1016 SkPaint paint;
1017 paint.setBlendMode(SkBlendMode::kSrc); // set as is, including alpha
1018 if (mSize != mPixelsSize) // pending scaling?
1020 paint.setFilterQuality(mScaleQuality);
1021 canvas.drawImageRect(mImage,
1022 SkRect::MakeWH(mPixelsSize.getWidth(), mPixelsSize.getHeight()),
1023 SkRect::MakeWH(mSize.getWidth(), mSize.getHeight()), &paint);
1024 SAL_INFO("vcl.skia.trace", "ensurebitmapdata(" << this << "): image scaled " << mPixelsSize
1025 << "->" << mSize << ":"
1026 << static_cast<int>(mScaleQuality));
1027 mPixelsSize = mSize;
1028 ComputeScanlineSize();
1029 mScaleQuality = kHigh_SkFilterQuality;
1030 // Information about the pending scaling has been discarded, so make sure we do not
1031 // keep around any cached images that would still need scaling.
1032 ResetCachedDataBySize();
1034 else
1035 canvas.drawImage(mImage, 0, 0, &paint);
1036 canvas.flush();
1037 bitmap.setImmutable();
1038 CreateBitmapData();
1039 assert(mBuffer != nullptr);
1040 assert(mPixelsSize == mSize);
1041 if (mBitCount == 32)
1043 if (int(bitmap.rowBytes()) == mScanlineSize)
1044 memcpy(mBuffer.get(), bitmap.getPixels(), mSize.Height() * mScanlineSize);
1045 else
1047 for (tools::Long y = 0; y < mSize.Height(); ++y)
1049 const uint8_t* src = static_cast<uint8_t*>(bitmap.getAddr(0, y));
1050 sal_uInt8* dest = mBuffer.get() + mScanlineSize * y;
1051 memcpy(dest, src, mScanlineSize);
1055 else if (mBitCount == 24) // non-paletted
1057 if (int(bitmap.rowBytes()) == mSize.Width() * 4 && mSize.Width() * 3 == mScanlineSize)
1059 SkConvertRGBAToRGB(mBuffer.get(), bitmap.getAddr32(0, 0),
1060 mSize.Height() * mSize.Width());
1062 else
1064 for (tools::Long y = 0; y < mSize.Height(); ++y)
1066 const uint32_t* src = bitmap.getAddr32(0, y);
1067 sal_uInt8* dest = mBuffer.get() + mScanlineSize * y;
1068 SkConvertRGBAToRGB(dest, src, mSize.Width());
1072 else if (mBitCount == 8 && mPalette.IsGreyPalette8Bit())
1073 { // no actual data conversion, use one color channel as the gray value
1074 if (int(bitmap.rowBytes()) == mSize.Width() * 4 && mSize.Width() * 1 == mScanlineSize)
1076 SkConvertRGBAToGrayFast(mBuffer.get(), bitmap.getAddr32(0, 0),
1077 mSize.Height() * mSize.Width());
1079 else
1081 for (tools::Long y = 0; y < mSize.Height(); ++y)
1083 const uint32_t* src = bitmap.getAddr32(0, y);
1084 sal_uInt8* dest = mBuffer.get() + mScanlineSize * y;
1085 SkConvertRGBAToGrayFast(dest, src, mSize.Width());
1089 else
1091 std::unique_ptr<vcl::ScanlineWriter> pWriter
1092 = vcl::ScanlineWriter::Create(mBitCount, mPalette);
1093 for (tools::Long y = 0; y < mSize.Height(); ++y)
1095 const uint8_t* src = static_cast<uint8_t*>(bitmap.getAddr(0, y));
1096 sal_uInt8* dest = mBuffer.get() + mScanlineSize * y;
1097 pWriter->nextLine(dest);
1098 for (tools::Long x = 0; x < mSize.Width(); ++x)
1100 sal_uInt8 r = *src++;
1101 sal_uInt8 g = *src++;
1102 sal_uInt8 b = *src++;
1103 ++src; // skip alpha
1104 pWriter->writeRGB(r, g, b);
1108 verify();
1109 // We've created the bitmap data from mImage, drop the image if conserving memory,
1110 // it'll be converted back if needed.
1111 if (ConserveMemory())
1113 SAL_INFO("vcl.skia.trace", "ensurebitmapdata(" << this << "): dropping images");
1114 ResetToBuffer();
1116 SAL_INFO("vcl.skia.trace", "ensurebitmapdata(" << this << ")");
1119 void SkiaSalBitmap::EnsureBitmapUniqueData()
1121 EnsureBitmapData();
1122 assert(mPixelsSize == mSize);
1123 if (mBuffer.use_count() > 1)
1125 sal_uInt32 allocate = mScanlineSize * mSize.Height();
1126 #ifdef DBG_UTIL
1127 assert(memcmp(mBuffer.get() + allocate, CANARY, sizeof(CANARY)) == 0);
1128 allocate += sizeof(CANARY);
1129 #endif
1130 boost::shared_ptr<sal_uInt8[]> newBuffer = boost::make_shared_noinit<sal_uInt8[]>(allocate);
1131 memcpy(newBuffer.get(), mBuffer.get(), allocate);
1132 mBuffer = newBuffer;
1136 void SkiaSalBitmap::ResetToBuffer()
1138 SkiaZone zone;
1139 // This should never be called to drop mImage if that's the only data we have.
1140 assert(mBuffer || !mImage);
1141 mImage.reset();
1142 mAlphaImage.reset();
1143 mEraseColorSet = false;
1146 void SkiaSalBitmap::ResetToSkImage(sk_sp<SkImage> image)
1148 SkiaZone zone;
1149 mBuffer.reset();
1150 mImage = image;
1151 mAlphaImage.reset();
1152 mEraseColorSet = false;
1155 void SkiaSalBitmap::ResetAllData()
1157 SkiaZone zone;
1158 mBuffer.reset();
1159 mImage.reset();
1160 mAlphaImage.reset();
1161 mEraseColorSet = false;
1164 void SkiaSalBitmap::ResetCachedDataBySize()
1166 SkiaZone zone;
1167 assert(mSize == mPixelsSize);
1168 assert(!mEraseColorSet);
1169 if (mImage && (mImage->width() != mSize.getWidth() || mImage->height() != mSize.getHeight()))
1170 mImage.reset();
1171 if (mAlphaImage
1172 && (mAlphaImage->width() != mSize.getWidth() || mAlphaImage->height() != mSize.getHeight()))
1173 mAlphaImage.reset();
1176 OString SkiaSalBitmap::GetImageKey() const
1178 if (mEraseColorSet)
1180 std::stringstream ss;
1181 ss << std::hex << std::setfill('0') << std::setw(2) << (255 - mEraseColor.GetTransparency())
1182 << std::setw(6) << sal_uInt32(mEraseColor.GetRGBColor());
1183 return OStringLiteral("E") + ss.str().c_str();
1185 return OStringLiteral("I") + OString::number(GetSkImage()->uniqueID());
1188 OString SkiaSalBitmap::GetAlphaImageKey() const
1190 if (mEraseColorSet)
1192 std::stringstream ss;
1193 ss << std::hex << std::setfill('0') << std::setw(2)
1194 << (255 - SkColorGetA(fromEraseColorToAlphaImageColor(mEraseColor)));
1195 return OStringLiteral("E") + ss.str().c_str();
1197 return OStringLiteral("I") + OString::number(GetAlphaSkImage()->uniqueID());
1200 #ifdef DBG_UTIL
1201 void SkiaSalBitmap::dump(const char* file) const
1203 // Use a copy, so that debugging doesn't affect this instance.
1204 SkiaSalBitmap copy;
1205 copy.Create(*this);
1206 SkiaHelper::dump(copy.GetSkImage(), file);
1209 void SkiaSalBitmap::verify() const
1211 if (!mBuffer)
1212 return;
1213 // Use mPixelsSize, that describes the size of the actual data.
1214 assert(memcmp(mBuffer.get() + mScanlineSize * mPixelsSize.Height(), CANARY, sizeof(CANARY))
1215 == 0);
1218 #endif
1220 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */