xref: /aosp_15_r20/frameworks/base/libs/hwui/hwui/Bitmap.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "Bitmap.h"
17 
18 #include <android-base/file.h>
19 #include "HardwareBitmapUploader.h"
20 #include "Properties.h"
21 #ifdef __ANDROID__  // Layoutlib does not support render thread
22 #include <private/android/AHardwareBufferHelpers.h>
23 #include <ui/GraphicBuffer.h>
24 #include <ui/GraphicBufferMapper.h>
25 
26 #include "renderthread/RenderProxy.h"
27 #endif
28 #include "utils/Color.h"
29 #include <utils/Trace.h>
30 
31 #ifndef _WIN32
32 #include <sys/mman.h>
33 #endif
34 
35 #include <cutils/ashmem.h>
36 #include <log/log.h>
37 
38 #ifndef _WIN32
39 #include <binder/IServiceManager.h>
40 #endif
41 
42 #include <Gainmap.h>
43 #include <SkCanvas.h>
44 #include <SkColor.h>
45 #include <SkEncodedImageFormat.h>
46 #include <SkHighContrastFilter.h>
47 #include <SkImage.h>
48 #include <SkImageAndroid.h>
49 #include <SkImagePriv.h>
50 #include <SkJpegEncoder.h>
51 #include <SkJpegGainmapEncoder.h>
52 #include <SkPixmap.h>
53 #include <SkPngEncoder.h>
54 #include <SkRect.h>
55 #include <SkStream.h>
56 #include <SkWebpEncoder.h>
57 
58 #include <atomic>
59 #include <format>
60 #include <limits>
61 
62 #ifdef __ANDROID__
63 #include <com_android_graphics_hwui_flags.h>
64 namespace hwui_flags = com::android::graphics::hwui::flags;
65 #else
66 namespace hwui_flags {
bitmap_ashmem_long_name()67 constexpr bool bitmap_ashmem_long_name() { return false; }
68 }
69 #endif
70 
71 namespace android {
72 
73 #ifdef __ANDROID__
AHardwareBuffer_getAllocationSize(AHardwareBuffer * aHardwareBuffer)74 static uint64_t AHardwareBuffer_getAllocationSize(AHardwareBuffer* aHardwareBuffer) {
75     GraphicBuffer* buffer = AHardwareBuffer_to_GraphicBuffer(aHardwareBuffer);
76     auto& mapper = GraphicBufferMapper::get();
77     uint64_t size = 0;
78     auto err = mapper.getAllocationSize(buffer->handle, &size);
79     if (err == OK) {
80         if (size > 0) {
81             return size;
82         } else {
83             ALOGW("Mapper returned size = 0 for buffer format: 0x%x size: %d x %d", buffer->format,
84                   buffer->width, buffer->height);
85             // Fall-through to estimate
86         }
87     }
88 
89     // Estimation time!
90     // Stride could be = 0 if it's ill-defined (eg, compressed buffer), in which case we use the
91     // width of the buffer instead
92     size = std::max(buffer->width, buffer->stride) * buffer->height;
93     // Require bpp to be at least 1. This is too low for many formats, but it's better than 0
94     // Also while we could make increasingly better estimates, the reality is that mapper@4
95     // should be common enough at this point that we won't ever hit this anyway
96     size *= std::max(1u, bytesPerPixel(buffer->format));
97     return size;
98 }
99 #endif
100 
101 // generate an ID for this Bitmap, id is a 64-bit integer of 3 parts:
102 //   0000xxxxxx - the lower 6 decimal digits is a monotonically increasing number
103 //   000x000000 - the 7th decimal digit is the storage type (see PixelStorageType)
104 //   xxx0000000 - the 8th decimal digit and above is the current pid
105 //
106 //   e.g. 43231000076 - means this bitmap is the 76th bitmap created, has the
107 //   storage type of 'Heap', and is created in a process with pid 4323.
108 //
109 //   NOTE:
110 //   1) the monotonic number could increase beyond 1000,000 and wrap around, which
111 //   only happens when more than 1,000,000 bitmaps have been created over time.
112 //   This could result in two IDs being the same despite being really rare.
113 //   2) the IDs are intentionally represented in decimal to make it easier to
114 //   reason and associate with numbers shown in heap dump (mostly in decimal)
115 //   and PIDs shown in different tools (mostly in decimal as well).
getId(PixelStorageType type)116 uint64_t Bitmap::getId(PixelStorageType type) {
117     static std::atomic<uint64_t> idCounter{0};
118     return (idCounter.fetch_add(1) % 1000000)
119         + static_cast<uint64_t>(type) * 1000000
120         + static_cast<uint64_t>(getpid()) * 10000000;
121 }
122 
computeAllocationSize(size_t rowBytes,int height,size_t * size)123 bool Bitmap::computeAllocationSize(size_t rowBytes, int height, size_t* size) {
124     return 0 <= height && height <= std::numeric_limits<size_t>::max() &&
125            !__builtin_mul_overflow(rowBytes, (size_t)height, size) &&
126            *size <= std::numeric_limits<int32_t>::max();
127 }
128 
129 typedef sk_sp<Bitmap> (*AllocPixelRef)(size_t allocSize, const SkImageInfo& info, size_t rowBytes);
130 
allocateBitmap(SkBitmap * bitmap,AllocPixelRef alloc)131 static sk_sp<Bitmap> allocateBitmap(SkBitmap* bitmap, AllocPixelRef alloc) {
132     const SkImageInfo& info = bitmap->info();
133     if (info.colorType() == kUnknown_SkColorType) {
134         LOG_ALWAYS_FATAL("unknown bitmap configuration");
135         return nullptr;
136     }
137 
138     size_t size;
139 
140     // we must respect the rowBytes value already set on the bitmap instead of
141     // attempting to compute our own.
142     const size_t rowBytes = bitmap->rowBytes();
143     if (!Bitmap::computeAllocationSize(rowBytes, bitmap->height(), &size)) {
144         return nullptr;
145     }
146 
147     auto wrapper = alloc(size, info, rowBytes);
148     if (wrapper) {
149         wrapper->getSkBitmap(bitmap);
150     }
151     return wrapper;
152 }
153 
getAshmemId(const char * tag,uint64_t bitmapId,int width,int height,size_t size)154 std::string Bitmap::getAshmemId(const char* tag, uint64_t bitmapId,
155                                 int width, int height, size_t size) {
156     if (!hwui_flags::bitmap_ashmem_long_name()) {
157         return "bitmap";
158     }
159     static std::string sCmdline = [] {
160         std::string temp;
161         android::base::ReadFileToString("/proc/self/cmdline", &temp);
162         return temp;
163     }();
164     return std::format("bitmap/{}-id_{}-{}x{}-size_{}-{}",
165                        tag, bitmapId, width, height, size, sCmdline);
166 }
167 
allocateAshmemBitmap(SkBitmap * bitmap)168 sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(SkBitmap* bitmap) {
169     return allocateBitmap(bitmap, &Bitmap::allocateAshmemBitmap);
170 }
171 
allocateAshmemBitmap(size_t size,const SkImageInfo & info,size_t rowBytes)172 sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) {
173 #ifdef __ANDROID__
174     // Create new ashmem region with read/write priv
175     uint64_t id = getId(PixelStorageType::Ashmem);
176     auto ashmemId = getAshmemId("allocate", id, info.width(), info.height(), size);
177     int fd = ashmem_create_region(ashmemId.c_str(), size);
178     if (fd < 0) {
179         return nullptr;
180     }
181 
182     void* addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
183     if (addr == MAP_FAILED) {
184         close(fd);
185         return nullptr;
186     }
187 
188     if (ashmem_set_prot_region(fd, PROT_READ) < 0) {
189         munmap(addr, size);
190         close(fd);
191         return nullptr;
192     }
193     return sk_sp<Bitmap>(new Bitmap(addr, fd, size, info, rowBytes, id));
194 #else
195     return Bitmap::allocateHeapBitmap(size, info, rowBytes);
196 #endif
197 }
198 
allocateHardwareBitmap(const SkBitmap & bitmap)199 sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(const SkBitmap& bitmap) {
200 #ifdef __ANDROID__  // Layoutlib does not support hardware acceleration
201     return uirenderer::HardwareBitmapUploader::allocateHardwareBitmap(bitmap);
202 #else
203     return Bitmap::allocateHeapBitmap(bitmap.info());
204 #endif
205 }
206 
allocateHeapBitmap(SkBitmap * bitmap)207 sk_sp<Bitmap> Bitmap::allocateHeapBitmap(SkBitmap* bitmap) {
208     return allocateBitmap(bitmap, &Bitmap::allocateHeapBitmap);
209 }
210 
allocateHeapBitmap(const SkImageInfo & info)211 sk_sp<Bitmap> Bitmap::allocateHeapBitmap(const SkImageInfo& info) {
212     size_t size;
213     if (!computeAllocationSize(info.minRowBytes(), info.height(), &size)) {
214         LOG_ALWAYS_FATAL("trying to allocate too large bitmap");
215         return nullptr;
216     }
217     return allocateHeapBitmap(size, info, info.minRowBytes());
218 }
219 
allocateHeapBitmap(size_t size,const SkImageInfo & info,size_t rowBytes)220 sk_sp<Bitmap> Bitmap::allocateHeapBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) {
221     void* addr = calloc(size, 1);
222     if (!addr) {
223         return nullptr;
224     }
225     return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes));
226 }
227 
createFrom(const SkImageInfo & info,SkPixelRef & pixelRef)228 sk_sp<Bitmap> Bitmap::createFrom(const SkImageInfo& info, SkPixelRef& pixelRef) {
229     return sk_sp<Bitmap>(new Bitmap(pixelRef, info));
230 }
231 
232 
233 #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
createFrom(AHardwareBuffer * hardwareBuffer,sk_sp<SkColorSpace> colorSpace,BitmapPalette palette)234 sk_sp<Bitmap> Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, sk_sp<SkColorSpace> colorSpace,
235                                  BitmapPalette palette) {
236     AHardwareBuffer_Desc bufferDesc;
237     AHardwareBuffer_describe(hardwareBuffer, &bufferDesc);
238     SkImageInfo info = uirenderer::BufferDescriptionToImageInfo(bufferDesc, colorSpace);
239     return createFrom(hardwareBuffer, info, bufferDesc, palette);
240 }
241 
createFrom(AHardwareBuffer * hardwareBuffer,SkColorType colorType,sk_sp<SkColorSpace> colorSpace,SkAlphaType alphaType,BitmapPalette palette)242 sk_sp<Bitmap> Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, SkColorType colorType,
243                                  sk_sp<SkColorSpace> colorSpace, SkAlphaType alphaType,
244                                  BitmapPalette palette) {
245     AHardwareBuffer_Desc bufferDesc;
246     AHardwareBuffer_describe(hardwareBuffer, &bufferDesc);
247     SkImageInfo info = SkImageInfo::Make(bufferDesc.width, bufferDesc.height,
248                                          colorType, alphaType, colorSpace);
249     return createFrom(hardwareBuffer, info, bufferDesc, palette);
250 }
251 
createFrom(AHardwareBuffer * hardwareBuffer,const SkImageInfo & info,const AHardwareBuffer_Desc & bufferDesc,BitmapPalette palette)252 sk_sp<Bitmap> Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, const SkImageInfo& info,
253                                  const AHardwareBuffer_Desc& bufferDesc, BitmapPalette palette) {
254     // If the stride is 0 we have to use the width as an approximation (eg, compressed buffer)
255     const auto bufferStride = bufferDesc.stride > 0 ? bufferDesc.stride : bufferDesc.width;
256     const size_t rowBytes = info.bytesPerPixel() * bufferStride;
257     return sk_sp<Bitmap>(new Bitmap(hardwareBuffer, info, rowBytes, palette));
258 }
259 #endif
260 
createFrom(const SkImageInfo & info,size_t rowBytes,int fd,void * addr,size_t size,bool readOnly)261 sk_sp<Bitmap> Bitmap::createFrom(const SkImageInfo& info, size_t rowBytes, int fd, void* addr,
262                                  size_t size, bool readOnly) {
263 #ifdef _WIN32 // ashmem not implemented on Windows
264      return nullptr;
265 #else
266     if (info.colorType() == kUnknown_SkColorType) {
267         LOG_ALWAYS_FATAL("unknown bitmap configuration");
268         return nullptr;
269     }
270 
271     if (!addr) {
272         // Map existing ashmem region if not already mapped.
273         int flags = readOnly ? (PROT_READ) : (PROT_READ | PROT_WRITE);
274         size = ashmem_get_size_region(fd);
275         addr = mmap(NULL, size, flags, MAP_SHARED, fd, 0);
276         if (addr == MAP_FAILED) {
277             return nullptr;
278         }
279     }
280 
281     sk_sp<Bitmap> bitmap(new Bitmap(addr, fd, size, info, rowBytes));
282     if (readOnly) {
283         bitmap->setImmutable();
284     }
285     return bitmap;
286 #endif
287 }
288 
setColorSpace(sk_sp<SkColorSpace> colorSpace)289 void Bitmap::setColorSpace(sk_sp<SkColorSpace> colorSpace) {
290     mInfo = mInfo.makeColorSpace(std::move(colorSpace));
291 }
292 
validateAlpha(const SkImageInfo & info)293 static SkImageInfo validateAlpha(const SkImageInfo& info) {
294     // Need to validate the alpha type to filter against the color type
295     // to prevent things like a non-opaque RGB565 bitmap
296     SkAlphaType alphaType;
297     LOG_ALWAYS_FATAL_IF(
298             !SkColorTypeValidateAlphaType(info.colorType(), info.alphaType(), &alphaType),
299             "Failed to validate alpha type!");
300     return info.makeAlphaType(alphaType);
301 }
302 
reconfigure(const SkImageInfo & newInfo,size_t rowBytes)303 void Bitmap::reconfigure(const SkImageInfo& newInfo, size_t rowBytes) {
304     mInfo = validateAlpha(newInfo);
305 
306     // TODO: Skia intends for SkPixelRef to be immutable, but this method
307     // modifies it. Find another way to support reusing the same pixel memory.
308     this->android_only_reset(mInfo.width(), mInfo.height(), rowBytes);
309 }
310 
Bitmap(void * address,size_t size,const SkImageInfo & info,size_t rowBytes)311 Bitmap::Bitmap(void* address, size_t size, const SkImageInfo& info, size_t rowBytes)
312         : SkPixelRef(info.width(), info.height(), address, rowBytes)
313         , mInfo(validateAlpha(info))
314         , mPixelStorageType(PixelStorageType::Heap)
315         , mId(getId(mPixelStorageType)) {
316     mPixelStorage.heap.address = address;
317     mPixelStorage.heap.size = size;
318     traceBitmapCreate();
319 }
320 
Bitmap(SkPixelRef & pixelRef,const SkImageInfo & info)321 Bitmap::Bitmap(SkPixelRef& pixelRef, const SkImageInfo& info)
322         : SkPixelRef(info.width(), info.height(), pixelRef.pixels(), pixelRef.rowBytes())
323         , mInfo(validateAlpha(info))
324         , mPixelStorageType(PixelStorageType::WrappedPixelRef)
325         , mId(getId(mPixelStorageType)) {
326     pixelRef.ref();
327     mPixelStorage.wrapped.pixelRef = &pixelRef;
328     traceBitmapCreate();
329 }
330 
Bitmap(void * address,int fd,size_t mappedSize,const SkImageInfo & info,size_t rowBytes,uint64_t id)331 Bitmap::Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info,
332                size_t rowBytes, uint64_t id)
333         : SkPixelRef(info.width(), info.height(), address, rowBytes)
334         , mInfo(validateAlpha(info))
335         , mPixelStorageType(PixelStorageType::Ashmem)
336         , mId(id != INVALID_BITMAP_ID ? id : getId(mPixelStorageType)) {
337     mPixelStorage.ashmem.address = address;
338     mPixelStorage.ashmem.fd = fd;
339     mPixelStorage.ashmem.size = mappedSize;
340     traceBitmapCreate();
341 }
342 
343 #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
Bitmap(AHardwareBuffer * buffer,const SkImageInfo & info,size_t rowBytes,BitmapPalette palette)344 Bitmap::Bitmap(AHardwareBuffer* buffer, const SkImageInfo& info, size_t rowBytes,
345                BitmapPalette palette)
346         : SkPixelRef(info.width(), info.height(), nullptr, rowBytes)
347         , mInfo(validateAlpha(info))
348         , mPixelStorageType(PixelStorageType::Hardware)
349         , mPalette(palette)
350         , mPaletteGenerationId(getGenerationID())
351         , mId(getId(mPixelStorageType)) {
352     mPixelStorage.hardware.buffer = buffer;
353     mPixelStorage.hardware.size = AHardwareBuffer_getAllocationSize(buffer);
354     AHardwareBuffer_acquire(buffer);
355     setImmutable();  // HW bitmaps are always immutable
356     mImage = SkImages::DeferredFromAHardwareBuffer(buffer, mInfo.alphaType(),
357                                                    mInfo.refColorSpace());
358     traceBitmapCreate();
359 }
360 #endif
361 
~Bitmap()362 Bitmap::~Bitmap() {
363     traceBitmapDelete();
364     switch (mPixelStorageType) {
365         case PixelStorageType::WrappedPixelRef:
366             mPixelStorage.wrapped.pixelRef->unref();
367             break;
368         case PixelStorageType::Ashmem:
369 #ifndef _WIN32 // ashmem not implemented on Windows
370             munmap(mPixelStorage.ashmem.address, mPixelStorage.ashmem.size);
371 #endif
372             close(mPixelStorage.ashmem.fd);
373             break;
374         case PixelStorageType::Heap:
375             free(mPixelStorage.heap.address);
376 #ifdef __ANDROID__
377             mallopt(M_PURGE, 0);
378 #endif
379             break;
380         case PixelStorageType::Hardware:
381 #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
382             auto buffer = mPixelStorage.hardware.buffer;
383             AHardwareBuffer_release(buffer);
384             mPixelStorage.hardware.buffer = nullptr;
385 #endif
386             break;
387     }
388 }
389 
hasHardwareMipMap() const390 bool Bitmap::hasHardwareMipMap() const {
391     return mHasHardwareMipMap;
392 }
393 
setHasHardwareMipMap(bool hasMipMap)394 void Bitmap::setHasHardwareMipMap(bool hasMipMap) {
395     mHasHardwareMipMap = hasMipMap;
396 }
397 
getAshmemFd() const398 int Bitmap::getAshmemFd() const {
399     switch (mPixelStorageType) {
400         case PixelStorageType::Ashmem:
401             return mPixelStorage.ashmem.fd;
402         default:
403             return -1;
404     }
405 }
406 
getAllocationByteCount() const407 size_t Bitmap::getAllocationByteCount() const {
408     switch (mPixelStorageType) {
409         case PixelStorageType::Heap:
410             return mPixelStorage.heap.size;
411         case PixelStorageType::Ashmem:
412             return mPixelStorage.ashmem.size;
413 #ifdef __ANDROID__
414         case PixelStorageType::Hardware:
415             return mPixelStorage.hardware.size;
416 #endif
417         default:
418             return rowBytes() * height();
419     }
420 }
421 
reconfigure(const SkImageInfo & info)422 void Bitmap::reconfigure(const SkImageInfo& info) {
423     reconfigure(info, info.minRowBytes());
424 }
425 
setAlphaType(SkAlphaType alphaType)426 void Bitmap::setAlphaType(SkAlphaType alphaType) {
427     if (!SkColorTypeValidateAlphaType(info().colorType(), alphaType, &alphaType)) {
428         return;
429     }
430 
431     mInfo = mInfo.makeAlphaType(alphaType);
432 }
433 
getSkBitmap(SkBitmap * outBitmap)434 void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
435 #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
436     if (isHardware()) {
437         outBitmap->allocPixels(mInfo);
438         uirenderer::renderthread::RenderProxy::copyHWBitmapInto(this, outBitmap);
439         return;
440     }
441 #endif
442     outBitmap->setInfo(mInfo, rowBytes());
443     outBitmap->setPixelRef(sk_ref_sp(this), 0, 0);
444 }
445 
getBounds(SkRect * bounds) const446 void Bitmap::getBounds(SkRect* bounds) const {
447     SkASSERT(bounds);
448     bounds->setIWH(width(), height());
449 }
450 
451 #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
hardwareBuffer()452 AHardwareBuffer* Bitmap::hardwareBuffer() {
453     if (isHardware()) {
454         return mPixelStorage.hardware.buffer;
455     }
456     return nullptr;
457 }
458 #endif
459 
makeImage()460 sk_sp<SkImage> Bitmap::makeImage() {
461     sk_sp<SkImage> image = mImage;
462     if (!image) {
463         SkASSERT(!isHardware());
464         SkBitmap skiaBitmap;
465         skiaBitmap.setInfo(info(), rowBytes());
466         skiaBitmap.setPixelRef(sk_ref_sp(this), 0, 0);
467         // Note we don't cache in this case, because the raster image holds a pointer to this Bitmap
468         // internally and ~Bitmap won't be invoked.
469         // TODO: refactor Bitmap to not derive from SkPixelRef, which would allow caching here.
470 #ifdef __ANDROID__
471         // pinnable images are only supported with the Ganesh GPU backend compiled in.
472         image = SkImages::PinnableRasterFromBitmap(skiaBitmap);
473 #else
474         image = SkMakeImageFromRasterBitmap(skiaBitmap, kNever_SkCopyPixelsMode);
475 #endif
476     }
477     return image;
478 }
479 
480 class MinMaxAverage {
481 public:
add(float sample)482     void add(float sample) {
483         if (mCount == 0) {
484             mMin = sample;
485             mMax = sample;
486         } else {
487             mMin = std::min(mMin, sample);
488             mMax = std::max(mMax, sample);
489         }
490         mTotal += sample;
491         mCount++;
492     }
493 
average()494     float average() { return mTotal / mCount; }
495 
min()496     float min() { return mMin; }
497 
max()498     float max() { return mMax; }
499 
delta()500     float delta() { return mMax - mMin; }
501 
502 private:
503     float mMin = 0.0f;
504     float mMax = 0.0f;
505     float mTotal = 0.0f;
506     int mCount = 0;
507 };
508 
computePalette(const SkImageInfo & info,const void * addr,size_t rowBytes)509 BitmapPalette Bitmap::computePalette(const SkImageInfo& info, const void* addr, size_t rowBytes) {
510     ATRACE_CALL();
511 
512     SkPixmap pixmap{info, addr, rowBytes};
513 
514     // TODO: This calculation of converting to HSV & tracking min/max is probably overkill
515     // Experiment with something simpler since we just want to figure out if it's "color-ful"
516     // and then the average perceptual lightness.
517 
518     MinMaxAverage hue, saturation, value;
519     int sampledCount = 0;
520 
521     // Sample a grid of 100 pixels to get an overall estimation of the colors in play
522     const int x_step = std::max(1, pixmap.width() / 10);
523     const int y_step = std::max(1, pixmap.height() / 10);
524     for (int x = 0; x < pixmap.width(); x += x_step) {
525         for (int y = 0; y < pixmap.height(); y += y_step) {
526             SkColor color = pixmap.getColor(x, y);
527             if (!info.isOpaque() && SkColorGetA(color) < 75) {
528                 continue;
529             }
530 
531             sampledCount++;
532             float hsv[3];
533             SkColorToHSV(color, hsv);
534             hue.add(hsv[0]);
535             saturation.add(hsv[1]);
536             value.add(hsv[2]);
537         }
538     }
539 
540     // TODO: Tune the coverage threshold
541     if (sampledCount < 5) {
542         ALOGV("Not enough samples, only found %d for image sized %dx%d, format = %d, alpha = %d",
543               sampledCount, info.width(), info.height(), (int)info.colorType(),
544               (int)info.alphaType());
545         return BitmapPalette::Unknown;
546     }
547 
548     ALOGV("samples = %d, hue [min = %f, max = %f, avg = %f]; saturation [min = %f, max = %f, avg = "
549           "%f]",
550           sampledCount, hue.min(), hue.max(), hue.average(), saturation.min(), saturation.max(),
551           saturation.average());
552 
553     if (hue.delta() <= 20 && saturation.delta() <= .1f) {
554         if (value.average() >= .5f) {
555             return BitmapPalette::Light;
556         } else {
557             return BitmapPalette::Dark;
558         }
559     }
560     return BitmapPalette::Unknown;
561 }
562 
compress(JavaCompressFormat format,int32_t quality,SkWStream * stream)563 bool Bitmap::compress(JavaCompressFormat format, int32_t quality, SkWStream* stream) {
564 #ifdef __ANDROID__  // TODO: This isn't built for host for some reason?
565     if (hasGainmap() && format == JavaCompressFormat::Jpeg) {
566         SkBitmap baseBitmap = getSkBitmap();
567         SkBitmap gainmapBitmap = gainmap()->bitmap->getSkBitmap();
568         if (gainmapBitmap.colorType() == SkColorType::kAlpha_8_SkColorType) {
569             SkBitmap greyGainmap;
570             auto greyInfo = gainmapBitmap.info().makeColorType(SkColorType::kGray_8_SkColorType);
571             greyGainmap.setInfo(greyInfo, gainmapBitmap.rowBytes());
572             greyGainmap.setPixelRef(sk_ref_sp(gainmapBitmap.pixelRef()), 0, 0);
573             gainmapBitmap = std::move(greyGainmap);
574         }
575         SkJpegEncoder::Options options{.fQuality = quality};
576         return SkJpegGainmapEncoder::EncodeHDRGM(stream, baseBitmap.pixmap(), options,
577                                                  gainmapBitmap.pixmap(), options, gainmap()->info);
578     }
579 #endif
580 
581     SkBitmap skbitmap;
582     getSkBitmap(&skbitmap);
583     return compress(skbitmap, format, quality, stream);
584 }
585 
compress(const SkBitmap & bitmap,JavaCompressFormat format,int32_t quality,SkWStream * stream)586 bool Bitmap::compress(const SkBitmap& bitmap, JavaCompressFormat format,
587                       int32_t quality, SkWStream* stream) {
588     if (bitmap.colorType() == kAlpha_8_SkColorType) {
589         // None of the JavaCompressFormats have a sensible way to compress an
590         // ALPHA_8 Bitmap. SkPngEncoder will compress one, but it uses a non-
591         // standard format that most decoders do not understand, so this is
592         // likely not useful.
593         return false;
594     }
595 
596     switch (format) {
597         case JavaCompressFormat::Jpeg: {
598             SkJpegEncoder::Options options;
599             options.fQuality = quality;
600             return SkJpegEncoder::Encode(stream, bitmap.pixmap(), options);
601         }
602         case JavaCompressFormat::Png:
603             return SkPngEncoder::Encode(stream, bitmap.pixmap(), {});
604         case JavaCompressFormat::Webp: {
605             SkWebpEncoder::Options options;
606             if (quality >= 100) {
607                 options.fCompression = SkWebpEncoder::Compression::kLossless;
608                 options.fQuality = 75; // This is effort to compress
609             } else {
610                 options.fCompression = SkWebpEncoder::Compression::kLossy;
611                 options.fQuality = quality;
612             }
613             return SkWebpEncoder::Encode(stream, bitmap.pixmap(), options);
614         }
615         case JavaCompressFormat::WebpLossy:
616         case JavaCompressFormat::WebpLossless: {
617             SkWebpEncoder::Options options;
618             options.fQuality = quality;
619             options.fCompression = format == JavaCompressFormat::WebpLossy ?
620                     SkWebpEncoder::Compression::kLossy : SkWebpEncoder::Compression::kLossless;
621             return SkWebpEncoder::Encode(stream, bitmap.pixmap(), options);
622         }
623     }
624 }
625 
gainmap() const626 sp<uirenderer::Gainmap> Bitmap::gainmap() const {
627     LOG_ALWAYS_FATAL_IF(!hasGainmap(), "Bitmap doesn't have a gainmap");
628     return mGainmap;
629 }
630 
setGainmap(sp<uirenderer::Gainmap> && gainmap)631 void Bitmap::setGainmap(sp<uirenderer::Gainmap>&& gainmap) {
632     mGainmap = std::move(gainmap);
633 }
634 
635 std::mutex Bitmap::mLock{};
636 
637 size_t Bitmap::mTotalBitmapBytes = 0;
638 size_t Bitmap::mTotalBitmapCount = 0;
639 
traceBitmapCreate()640 void Bitmap::traceBitmapCreate() {
641     size_t bytes = getAllocationByteCount();
642     std::lock_guard lock{mLock};
643     mTotalBitmapBytes += bytes;
644     mTotalBitmapCount++;
645     if (ATRACE_ENABLED()) {
646         ATRACE_INT64("Bitmap Memory", mTotalBitmapBytes);
647         ATRACE_INT64("Bitmap Count", mTotalBitmapCount);
648     }
649 }
650 
traceBitmapDelete()651 void Bitmap::traceBitmapDelete() {
652     size_t bytes = getAllocationByteCount();
653     std::lock_guard lock{mLock};
654     mTotalBitmapBytes -= getAllocationByteCount();
655     mTotalBitmapCount--;
656     if (ATRACE_ENABLED()) {
657         ATRACE_INT64("Bitmap Memory", mTotalBitmapBytes);
658         ATRACE_INT64("Bitmap Count", mTotalBitmapCount);
659     }
660 }
661 
662 }  // namespace android
663