blob: 7a8d026df3b46e071c13e6a1062a09e5e3b2b7a5 [file] [log] [blame]
sergeyv163f8812016-10-07 16:57:29 -07001/*
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 */
sergeyvc1c54062016-10-19 18:47:26 -070016#include "Bitmap.h"
sergeyv163f8812016-10-07 16:57:29 -070017
18#include "Caches.h"
John Recke170fb62018-05-07 08:12:07 -070019#include "HardwareBitmapUploader.h"
John Reck339cf9b2018-07-18 16:32:27 -070020#include "Properties.h"
sergeyv694d4992016-10-27 10:23:13 -070021#include "renderthread/RenderProxy.h"
Romain Guycaaaa662017-03-27 00:40:21 -070022#include "utils/Color.h"
sergeyv163f8812016-10-07 16:57:29 -070023
sergeyv163f8812016-10-07 16:57:29 -070024#include <sys/mman.h>
Mark Salyzyn6f773a02016-09-28 16:15:30 -070025
sergeyv163f8812016-10-07 16:57:29 -070026#include <cutils/ashmem.h>
John Reck1bcacfd2017-11-03 10:12:19 -070027#include <log/log.h>
sergeyv163f8812016-10-07 16:57:29 -070028
sergeyv694d4992016-10-27 10:23:13 -070029#include <binder/IServiceManager.h>
John Reck1bcacfd2017-11-03 10:12:19 -070030#include <private/gui/ComposerService.h>
sergeyv694d4992016-10-27 10:23:13 -070031#include <ui/PixelFormat.h>
32
33#include <SkCanvas.h>
Stan Iliev7bc3bc62017-05-24 13:28:36 -040034#include <SkImagePriv.h>
John Reck1bcacfd2017-11-03 10:12:19 -070035#include <SkToSRGBColorFilter.h>
sergeyv694d4992016-10-27 10:23:13 -070036
Ben Wagnerdbbc74c2018-05-18 17:46:48 -040037#include <limits>
John Reck339cf9b2018-07-18 16:32:27 -070038#include <SkHighContrastFilter.h>
Ben Wagnerdbbc74c2018-05-18 17:46:48 -040039
sergeyv163f8812016-10-07 16:57:29 -070040namespace android {
41
Ben Wagnerdbbc74c2018-05-18 17:46:48 -040042// returns true if rowBytes * height can be represented by a positive int32_t value
43// and places that value in size.
sergeyvfc9999502016-10-17 13:07:38 -070044static bool computeAllocationSize(size_t rowBytes, int height, size_t* size) {
Ben Wagnerdbbc74c2018-05-18 17:46:48 -040045 return 0 <= height && height <= std::numeric_limits<size_t>::max() &&
46 !__builtin_mul_overflow(rowBytes, (size_t)height, size) &&
47 *size <= std::numeric_limits<int32_t>::max();
sergeyvc36bd6c2016-10-11 15:49:16 -070048}
49
John Reck1bcacfd2017-11-03 10:12:19 -070050typedef sk_sp<Bitmap> (*AllocPixelRef)(size_t allocSize, const SkImageInfo& info, size_t rowBytes);
sergeyvc36bd6c2016-10-11 15:49:16 -070051
Leon Scroggins IIIf51a80d2017-07-12 10:46:35 -040052static sk_sp<Bitmap> allocateBitmap(SkBitmap* bitmap, AllocPixelRef alloc) {
sergeyvc36bd6c2016-10-11 15:49:16 -070053 const SkImageInfo& info = bitmap->info();
54 if (info.colorType() == kUnknown_SkColorType) {
55 LOG_ALWAYS_FATAL("unknown bitmap configuration");
56 return nullptr;
57 }
58
59 size_t size;
sergeyvc36bd6c2016-10-11 15:49:16 -070060
61 // we must respect the rowBytes value already set on the bitmap instead of
62 // attempting to compute our own.
63 const size_t rowBytes = bitmap->rowBytes();
sergeyvfc9999502016-10-17 13:07:38 -070064 if (!computeAllocationSize(rowBytes, bitmap->height(), &size)) {
65 return nullptr;
66 }
67
Leon Scroggins IIIf51a80d2017-07-12 10:46:35 -040068 auto wrapper = alloc(size, info, rowBytes);
sergeyvc36bd6c2016-10-11 15:49:16 -070069 if (wrapper) {
70 wrapper->getSkBitmap(bitmap);
sergeyvc36bd6c2016-10-11 15:49:16 -070071 }
72 return wrapper;
73}
74
Leon Scroggins IIIf51a80d2017-07-12 10:46:35 -040075sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(SkBitmap* bitmap) {
John Reck1bcacfd2017-11-03 10:12:19 -070076 return allocateBitmap(bitmap, &Bitmap::allocateAshmemBitmap);
sergeyvc36bd6c2016-10-11 15:49:16 -070077}
78
Leon Scroggins IIIf51a80d2017-07-12 10:46:35 -040079static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) {
sergeyvc36bd6c2016-10-11 15:49:16 -070080 void* addr = calloc(size, 1);
81 if (!addr) {
82 return nullptr;
83 }
Leon Scroggins IIIf51a80d2017-07-12 10:46:35 -040084 return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes));
sergeyvc36bd6c2016-10-11 15:49:16 -070085}
86
sergeyv694d4992016-10-27 10:23:13 -070087sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(SkBitmap& bitmap) {
John Recke170fb62018-05-07 08:12:07 -070088 return uirenderer::HardwareBitmapUploader::allocateHardwareBitmap(bitmap);
sergeyv694d4992016-10-27 10:23:13 -070089}
90
Leon Scroggins IIIf51a80d2017-07-12 10:46:35 -040091sk_sp<Bitmap> Bitmap::allocateHeapBitmap(SkBitmap* bitmap) {
John Reck1bcacfd2017-11-03 10:12:19 -070092 return allocateBitmap(bitmap, &android::allocateHeapBitmap);
sergeyvfc9999502016-10-17 13:07:38 -070093}
94
95sk_sp<Bitmap> Bitmap::allocateHeapBitmap(const SkImageInfo& info) {
96 size_t size;
97 if (!computeAllocationSize(info.minRowBytes(), info.height(), &size)) {
98 LOG_ALWAYS_FATAL("trying to allocate too large bitmap");
99 return nullptr;
100 }
Leon Scroggins IIIf51a80d2017-07-12 10:46:35 -0400101 return android::allocateHeapBitmap(size, info, info.minRowBytes());
sergeyvfc9999502016-10-17 13:07:38 -0700102}
103
John Reck1bcacfd2017-11-03 10:12:19 -0700104sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) {
sergeyvc36bd6c2016-10-11 15:49:16 -0700105 // Create new ashmem region with read/write priv
106 int fd = ashmem_create_region("bitmap", size);
107 if (fd < 0) {
108 return nullptr;
109 }
110
111 void* addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
112 if (addr == MAP_FAILED) {
113 close(fd);
114 return nullptr;
115 }
116
117 if (ashmem_set_prot_region(fd, PROT_READ) < 0) {
118 munmap(addr, size);
119 close(fd);
120 return nullptr;
121 }
Leon Scroggins IIIf51a80d2017-07-12 10:46:35 -0400122 return sk_sp<Bitmap>(new Bitmap(addr, fd, size, info, rowBytes));
sergeyvc36bd6c2016-10-11 15:49:16 -0700123}
124
sergeyvaed7f582016-10-14 16:30:21 -0700125void FreePixelRef(void* addr, void* context) {
John Reck1bcacfd2017-11-03 10:12:19 -0700126 auto pixelRef = (SkPixelRef*)context;
sergeyvaed7f582016-10-14 16:30:21 -0700127 pixelRef->unref();
128}
129
130sk_sp<Bitmap> Bitmap::createFrom(const SkImageInfo& info, SkPixelRef& pixelRef) {
131 pixelRef.ref();
John Reck1bcacfd2017-11-03 10:12:19 -0700132 return sk_sp<Bitmap>(new Bitmap((void*)pixelRef.pixels(), (void*)&pixelRef, FreePixelRef, info,
133 pixelRef.rowBytes()));
sergeyvaed7f582016-10-14 16:30:21 -0700134}
135
sergeyv9a029872016-11-29 10:13:28 -0800136sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer) {
137 PixelFormat format = graphicBuffer->getPixelFormat();
Romain Guy9505a652016-12-14 09:43:50 -0800138 if (!graphicBuffer.get() ||
John Reck1bcacfd2017-11-03 10:12:19 -0700139 (format != PIXEL_FORMAT_RGBA_8888 && format != PIXEL_FORMAT_RGBA_FP16)) {
sergeyv9a029872016-11-29 10:13:28 -0800140 return nullptr;
141 }
142 SkImageInfo info = SkImageInfo::Make(graphicBuffer->getWidth(), graphicBuffer->getHeight(),
John Reck1bcacfd2017-11-03 10:12:19 -0700143 kRGBA_8888_SkColorType, kPremul_SkAlphaType,
144 SkColorSpace::MakeSRGB());
sergeyv9a029872016-11-29 10:13:28 -0800145 return sk_sp<Bitmap>(new Bitmap(graphicBuffer.get(), info));
146}
147
Romain Guy82426562017-04-04 19:38:50 -0700148void Bitmap::setColorSpace(sk_sp<SkColorSpace> colorSpace) {
Matt Sarett489cfff2017-04-21 18:08:40 -0400149 mInfo = mInfo.makeColorSpace(std::move(colorSpace));
150}
151
152static SkImageInfo validateAlpha(const SkImageInfo& info) {
153 // Need to validate the alpha type to filter against the color type
154 // to prevent things like a non-opaque RGB565 bitmap
155 SkAlphaType alphaType;
John Reck1bcacfd2017-11-03 10:12:19 -0700156 LOG_ALWAYS_FATAL_IF(
157 !SkColorTypeValidateAlphaType(info.colorType(), info.alphaType(), &alphaType),
Matt Sarett489cfff2017-04-21 18:08:40 -0400158 "Failed to validate alpha type!");
159 return info.makeAlphaType(alphaType);
Romain Guy82426562017-04-04 19:38:50 -0700160}
161
Leon Scroggins IIIf51a80d2017-07-12 10:46:35 -0400162void Bitmap::reconfigure(const SkImageInfo& newInfo, size_t rowBytes) {
Matt Sarett489cfff2017-04-21 18:08:40 -0400163 mInfo = validateAlpha(newInfo);
sergeyv163f8812016-10-07 16:57:29 -0700164
165 // Dirty hack is dirty
166 // TODO: Figure something out here, Skia's current design makes this
167 // really hard to work with. Skia really, really wants immutable objects,
168 // but with the nested-ref-count hackery going on that's just not
169 // feasible without going insane trying to figure it out
Mike Reed81397c42017-07-18 17:04:16 -0400170 this->android_only_reset(mInfo.width(), mInfo.height(), rowBytes);
sergeyv163f8812016-10-07 16:57:29 -0700171}
172
Leon Scroggins IIIf51a80d2017-07-12 10:46:35 -0400173Bitmap::Bitmap(void* address, size_t size, const SkImageInfo& info, size_t rowBytes)
John Reck1bcacfd2017-11-03 10:12:19 -0700174 : SkPixelRef(info.width(), info.height(), address, rowBytes)
175 , mInfo(validateAlpha(info))
176 , mPixelStorageType(PixelStorageType::Heap) {
sergeyv163f8812016-10-07 16:57:29 -0700177 mPixelStorage.heap.address = address;
178 mPixelStorage.heap.size = size;
sergeyv163f8812016-10-07 16:57:29 -0700179}
180
John Reck1bcacfd2017-11-03 10:12:19 -0700181Bitmap::Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info,
182 size_t rowBytes)
183 : SkPixelRef(info.width(), info.height(), address, rowBytes)
184 , mInfo(validateAlpha(info))
185 , mPixelStorageType(PixelStorageType::External) {
sergeyv163f8812016-10-07 16:57:29 -0700186 mPixelStorage.external.address = address;
187 mPixelStorage.external.context = context;
188 mPixelStorage.external.freeFunc = freeFunc;
sergeyv163f8812016-10-07 16:57:29 -0700189}
190
John Reck1bcacfd2017-11-03 10:12:19 -0700191Bitmap::Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes)
192 : SkPixelRef(info.width(), info.height(), address, rowBytes)
193 , mInfo(validateAlpha(info))
194 , mPixelStorageType(PixelStorageType::Ashmem) {
sergeyv163f8812016-10-07 16:57:29 -0700195 mPixelStorage.ashmem.address = address;
196 mPixelStorage.ashmem.fd = fd;
197 mPixelStorage.ashmem.size = mappedSize;
sergeyv163f8812016-10-07 16:57:29 -0700198}
199
John Reck339cf9b2018-07-18 16:32:27 -0700200Bitmap::Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette palette)
Matt Sarett489cfff2017-04-21 18:08:40 -0400201 : SkPixelRef(info.width(), info.height(), nullptr,
Mike Reed81397c42017-07-18 17:04:16 -0400202 bytesPerPixel(buffer->getPixelFormat()) * buffer->getStride())
Matt Sarett489cfff2017-04-21 18:08:40 -0400203 , mInfo(validateAlpha(info))
John Reck339cf9b2018-07-18 16:32:27 -0700204 , mPixelStorageType(PixelStorageType::Hardware)
205 , mPalette(palette)
206 , mPaletteGenerationId(getGenerationID()) {
sergeyv9a029872016-11-29 10:13:28 -0800207 mPixelStorage.hardware.buffer = buffer;
208 buffer->incStrong(buffer);
John Reck1bcacfd2017-11-03 10:12:19 -0700209 setImmutable(); // HW bitmaps are always immutable
John Reck1072fff2018-04-12 15:20:09 -0700210 mImage = SkImage::MakeFromAHardwareBuffer(reinterpret_cast<AHardwareBuffer*>(buffer),
John Recke170fb62018-05-07 08:12:07 -0700211 mInfo.alphaType(), mInfo.refColorSpace());
sergeyv694d4992016-10-27 10:23:13 -0700212}
sergeyv9a029872016-11-29 10:13:28 -0800213
sergeyvc1c54062016-10-19 18:47:26 -0700214Bitmap::~Bitmap() {
sergeyv163f8812016-10-07 16:57:29 -0700215 switch (mPixelStorageType) {
John Reck1bcacfd2017-11-03 10:12:19 -0700216 case PixelStorageType::External:
217 mPixelStorage.external.freeFunc(mPixelStorage.external.address,
218 mPixelStorage.external.context);
219 break;
220 case PixelStorageType::Ashmem:
221 munmap(mPixelStorage.ashmem.address, mPixelStorage.ashmem.size);
222 close(mPixelStorage.ashmem.fd);
223 break;
224 case PixelStorageType::Heap:
225 free(mPixelStorage.heap.address);
226 break;
227 case PixelStorageType::Hardware:
228 auto buffer = mPixelStorage.hardware.buffer;
229 buffer->decStrong(buffer);
230 mPixelStorage.hardware.buffer = nullptr;
231 break;
sergeyv163f8812016-10-07 16:57:29 -0700232 }
233
John Reck9a814872017-05-22 15:04:21 -0700234 android::uirenderer::renderthread::RenderProxy::onBitmapDestroyed(getStableID());
sergeyv163f8812016-10-07 16:57:29 -0700235}
236
sergeyvc1c54062016-10-19 18:47:26 -0700237bool Bitmap::hasHardwareMipMap() const {
sergeyv163f8812016-10-07 16:57:29 -0700238 return mHasHardwareMipMap;
239}
240
sergeyvc1c54062016-10-19 18:47:26 -0700241void Bitmap::setHasHardwareMipMap(bool hasMipMap) {
sergeyv163f8812016-10-07 16:57:29 -0700242 mHasHardwareMipMap = hasMipMap;
243}
244
sergeyvc1c54062016-10-19 18:47:26 -0700245void* Bitmap::getStorage() const {
sergeyv163f8812016-10-07 16:57:29 -0700246 switch (mPixelStorageType) {
John Reck1bcacfd2017-11-03 10:12:19 -0700247 case PixelStorageType::External:
248 return mPixelStorage.external.address;
249 case PixelStorageType::Ashmem:
250 return mPixelStorage.ashmem.address;
251 case PixelStorageType::Heap:
252 return mPixelStorage.heap.address;
253 case PixelStorageType::Hardware:
254 return nullptr;
sergeyv163f8812016-10-07 16:57:29 -0700255 }
256}
257
sergeyvc1c54062016-10-19 18:47:26 -0700258int Bitmap::getAshmemFd() const {
sergeyv163f8812016-10-07 16:57:29 -0700259 switch (mPixelStorageType) {
John Reck1bcacfd2017-11-03 10:12:19 -0700260 case PixelStorageType::Ashmem:
261 return mPixelStorage.ashmem.fd;
262 default:
263 return -1;
sergeyv163f8812016-10-07 16:57:29 -0700264 }
265}
266
sergeyvc1c54062016-10-19 18:47:26 -0700267size_t Bitmap::getAllocationByteCount() const {
sergeyv163f8812016-10-07 16:57:29 -0700268 switch (mPixelStorageType) {
John Reck1bcacfd2017-11-03 10:12:19 -0700269 case PixelStorageType::Heap:
270 return mPixelStorage.heap.size;
271 default:
272 return rowBytes() * height();
sergeyv163f8812016-10-07 16:57:29 -0700273 }
274}
275
sergeyvc1c54062016-10-19 18:47:26 -0700276void Bitmap::reconfigure(const SkImageInfo& info) {
Leon Scroggins IIIf51a80d2017-07-12 10:46:35 -0400277 reconfigure(info, info.minRowBytes());
sergeyv163f8812016-10-07 16:57:29 -0700278}
279
sergeyvc1c54062016-10-19 18:47:26 -0700280void Bitmap::setAlphaType(SkAlphaType alphaType) {
sergeyv163f8812016-10-07 16:57:29 -0700281 if (!SkColorTypeValidateAlphaType(info().colorType(), alphaType, &alphaType)) {
282 return;
283 }
284
Matt Sarett489cfff2017-04-21 18:08:40 -0400285 mInfo = mInfo.makeAlphaType(alphaType);
sergeyv163f8812016-10-07 16:57:29 -0700286}
287
sergeyvc1c54062016-10-19 18:47:26 -0700288void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
sergeyv59eecb522016-11-17 17:54:57 -0800289 outBitmap->setHasHardwareMipMap(mHasHardwareMipMap);
sergeyv694d4992016-10-27 10:23:13 -0700290 if (isHardware()) {
John Recke170fb62018-05-07 08:12:07 -0700291 outBitmap->allocPixels(SkImageInfo::Make(info().width(), info().height(),
292 info().colorType(), info().alphaType(), nullptr));
sergeyv59eecb522016-11-17 17:54:57 -0800293 uirenderer::renderthread::RenderProxy::copyGraphicBufferInto(graphicBuffer(), outBitmap);
Leon Scroggins III3eadfde2018-04-23 15:52:22 -0400294 if (mInfo.colorSpace()) {
295 sk_sp<SkPixelRef> pixelRef = sk_ref_sp(outBitmap->pixelRef());
296 outBitmap->setInfo(mInfo);
297 outBitmap->setPixelRef(std::move(pixelRef), 0, 0);
298 }
sergeyv694d4992016-10-27 10:23:13 -0700299 return;
300 }
Matt Sarett489cfff2017-04-21 18:08:40 -0400301 outBitmap->setInfo(mInfo, rowBytes());
Mike Reed826deef2017-04-04 15:32:04 -0400302 outBitmap->setPixelRef(sk_ref_sp(this), 0, 0);
sergeyv163f8812016-10-07 16:57:29 -0700303}
304
sergeyvec4a4b12016-10-20 18:39:04 -0700305void Bitmap::getBounds(SkRect* bounds) const {
306 SkASSERT(bounds);
Matt Sarett489cfff2017-04-21 18:08:40 -0400307 bounds->set(0, 0, SkIntToScalar(width()), SkIntToScalar(height()));
sergeyvec4a4b12016-10-20 18:39:04 -0700308}
309
sergeyv694d4992016-10-27 10:23:13 -0700310GraphicBuffer* Bitmap::graphicBuffer() {
311 if (isHardware()) {
312 return mPixelStorage.hardware.buffer;
313 }
314 return nullptr;
315}
316
Derek Sollenbergerfb0c8fc2017-07-26 13:53:27 -0400317sk_sp<SkImage> Bitmap::makeImage(sk_sp<SkColorFilter>* outputColorFilter) {
Stan Iliev7bc3bc62017-05-24 13:28:36 -0400318 sk_sp<SkImage> image = mImage;
319 if (!image) {
John Reck1072fff2018-04-12 15:20:09 -0700320 SkASSERT(!isHardware());
Stan Iliev7bc3bc62017-05-24 13:28:36 -0400321 SkBitmap skiaBitmap;
322 skiaBitmap.setInfo(info(), rowBytes());
323 skiaBitmap.setPixelRef(sk_ref_sp(this), 0, 0);
324 skiaBitmap.setHasHardwareMipMap(mHasHardwareMipMap);
325 // Note we don't cache in this case, because the raster image holds a pointer to this Bitmap
326 // internally and ~Bitmap won't be invoked.
327 // TODO: refactor Bitmap to not derive from SkPixelRef, which would allow caching here.
Derek Sollenbergerfb0c8fc2017-07-26 13:53:27 -0400328 image = SkMakeImageFromRasterBitmap(skiaBitmap, kNever_SkCopyPixelsMode);
329 }
John Reck1072fff2018-04-12 15:20:09 -0700330 if (image->colorSpace() != nullptr && !image->colorSpace()->isSRGB()) {
Derek Sollenbergerfb0c8fc2017-07-26 13:53:27 -0400331 *outputColorFilter = SkToSRGBColorFilter::Make(image->refColorSpace());
Stan Iliev7bc3bc62017-05-24 13:28:36 -0400332 }
John Reck339cf9b2018-07-18 16:32:27 -0700333
334 // TODO: Move this to the canvas (or other?) layer where we have the target lightness
335 // mode and can selectively do the right thing.
336 if (palette() != BitmapPalette::Unknown && uirenderer::Properties::forceDarkMode) {
337 SkHighContrastConfig config;
338 config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness;
339 *outputColorFilter = SkHighContrastFilter::Make(config)->makeComposed(*outputColorFilter);
340 }
Stan Iliev7bc3bc62017-05-24 13:28:36 -0400341 return image;
342}
343
John Reck339cf9b2018-07-18 16:32:27 -0700344class MinMaxAverage {
345public:
346
347 void add(float sample) {
348 if (mCount == 0) {
349 mMin = sample;
350 mMax = sample;
351 } else {
352 mMin = std::min(mMin, sample);
353 mMax = std::max(mMax, sample);
354 }
355 mTotal += sample;
356 mCount++;
357 }
358
359 float average() {
360 return mTotal / mCount;
361 }
362
363 float min() {
364 return mMin;
365 }
366
367 float max() {
368 return mMax;
369 }
370
371 float delta() {
372 return mMax - mMin;
373 }
374
375private:
376 float mMin = 0.0f;
377 float mMax = 0.0f;
378 float mTotal = 0.0f;
379 int mCount = 0;
380};
381
382BitmapPalette Bitmap::computePalette(const SkImageInfo& info, const void* addr, size_t rowBytes) {
383 ATRACE_CALL();
384
385 SkPixmap pixmap{info, addr, rowBytes};
386
387 // TODO: This calculation of converting to HSV & tracking min/max is probably overkill
388 // Experiment with something simpler since we just want to figure out if it's "color-ful"
389 // and then the average perceptual lightness.
390
391 MinMaxAverage hue, saturation, value;
392 int sampledCount = 0;
393
394 // Sample a grid of 100 pixels to get an overall estimation of the colors in play
395 const int x_step = std::max(1, pixmap.width() / 10);
396 const int y_step = std::max(1, pixmap.height() / 10);
397 for (int x = 0; x < pixmap.width(); x += x_step) {
398 for (int y = 0; y < pixmap.height(); y += y_step) {
399 SkColor color = pixmap.getColor(x, y);
400 if (!info.isOpaque() && SkColorGetA(color) < 75) {
401 continue;
402 }
403
404 sampledCount++;
405 float hsv[3];
406 SkColorToHSV(color, hsv);
407 hue.add(hsv[0]);
408 saturation.add(hsv[1]);
409 value.add(hsv[2]);
410 }
411 }
412
413 // TODO: Tune the coverage threshold
414 if (sampledCount < 5) {
415 ALOGV("Not enough samples, only found %d for image sized %dx%d, format = %d, alpha = %d",
416 sampledCount, info.width(), info.height(), (int) info.colorType(), (int) info.alphaType());
417 return BitmapPalette::Unknown;
418 }
419
420 ALOGV("samples = %d, hue [min = %f, max = %f, avg = %f]; saturation [min = %f, max = %f, avg = %f]",
421 sampledCount,
422 hue.min(), hue.max(), hue.average(),
423 saturation.min(), saturation.max(), saturation.average());
424
425 if (hue.delta() <= 20 && saturation.delta() <= .1f) {
426 if (value.average() >= .5f) {
427 return BitmapPalette::Light;
428 } else {
429 return BitmapPalette::Dark;
430 }
431 }
432 return BitmapPalette::Unknown;
433}
434
John Reck1bcacfd2017-11-03 10:12:19 -0700435} // namespace android