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 "HardwareBitmapUploader.h"
19 #include "Properties.h"
20 #ifdef __ANDROID__ // Layoutlib does not support render thread
21 #include "renderthread/RenderProxy.h"
22 #endif
23 #include "utils/Color.h"
24 #include <utils/Trace.h>
25
26 #ifndef _WIN32
27 #include <sys/mman.h>
28 #endif
29
30 #include <cutils/ashmem.h>
31 #include <log/log.h>
32
33 #ifndef _WIN32
34 #include <binder/IServiceManager.h>
35 #endif
36 #include <ui/PixelFormat.h>
37
38 #include <SkCanvas.h>
39 #include <SkImagePriv.h>
40 #include <SkWebpEncoder.h>
41 #include <SkHighContrastFilter.h>
42 #include <limits>
43
44 namespace android {
45
computeAllocationSize(size_t rowBytes,int height,size_t * size)46 bool Bitmap::computeAllocationSize(size_t rowBytes, int height, size_t* size) {
47 return 0 <= height && height <= std::numeric_limits<size_t>::max() &&
48 !__builtin_mul_overflow(rowBytes, (size_t)height, size) &&
49 *size <= std::numeric_limits<int32_t>::max();
50 }
51
52 typedef sk_sp<Bitmap> (*AllocPixelRef)(size_t allocSize, const SkImageInfo& info, size_t rowBytes);
53
allocateBitmap(SkBitmap * bitmap,AllocPixelRef alloc)54 static sk_sp<Bitmap> allocateBitmap(SkBitmap* bitmap, AllocPixelRef alloc) {
55 const SkImageInfo& info = bitmap->info();
56 if (info.colorType() == kUnknown_SkColorType) {
57 LOG_ALWAYS_FATAL("unknown bitmap configuration");
58 return nullptr;
59 }
60
61 size_t size;
62
63 // we must respect the rowBytes value already set on the bitmap instead of
64 // attempting to compute our own.
65 const size_t rowBytes = bitmap->rowBytes();
66 if (!Bitmap::computeAllocationSize(rowBytes, bitmap->height(), &size)) {
67 return nullptr;
68 }
69
70 auto wrapper = alloc(size, info, rowBytes);
71 if (wrapper) {
72 wrapper->getSkBitmap(bitmap);
73 }
74 return wrapper;
75 }
76
allocateAshmemBitmap(SkBitmap * bitmap)77 sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(SkBitmap* bitmap) {
78 return allocateBitmap(bitmap, &Bitmap::allocateAshmemBitmap);
79 }
80
allocateAshmemBitmap(size_t size,const SkImageInfo & info,size_t rowBytes)81 sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) {
82 #ifdef __ANDROID__
83 // Create new ashmem region with read/write priv
84 int fd = ashmem_create_region("bitmap", size);
85 if (fd < 0) {
86 return nullptr;
87 }
88
89 void* addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
90 if (addr == MAP_FAILED) {
91 close(fd);
92 return nullptr;
93 }
94
95 if (ashmem_set_prot_region(fd, PROT_READ) < 0) {
96 munmap(addr, size);
97 close(fd);
98 return nullptr;
99 }
100 return sk_sp<Bitmap>(new Bitmap(addr, fd, size, info, rowBytes));
101 #else
102 return Bitmap::allocateHeapBitmap(size, info, rowBytes);
103 #endif
104 }
105
allocateHardwareBitmap(const SkBitmap & bitmap)106 sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(const SkBitmap& bitmap) {
107 #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
108 return uirenderer::HardwareBitmapUploader::allocateHardwareBitmap(bitmap);
109 #else
110 return Bitmap::allocateHeapBitmap(bitmap.info());
111 #endif
112 }
113
allocateHeapBitmap(SkBitmap * bitmap)114 sk_sp<Bitmap> Bitmap::allocateHeapBitmap(SkBitmap* bitmap) {
115 return allocateBitmap(bitmap, &Bitmap::allocateHeapBitmap);
116 }
117
allocateHeapBitmap(const SkImageInfo & info)118 sk_sp<Bitmap> Bitmap::allocateHeapBitmap(const SkImageInfo& info) {
119 size_t size;
120 if (!computeAllocationSize(info.minRowBytes(), info.height(), &size)) {
121 LOG_ALWAYS_FATAL("trying to allocate too large bitmap");
122 return nullptr;
123 }
124 return allocateHeapBitmap(size, info, info.minRowBytes());
125 }
126
allocateHeapBitmap(size_t size,const SkImageInfo & info,size_t rowBytes)127 sk_sp<Bitmap> Bitmap::allocateHeapBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) {
128 void* addr = calloc(size, 1);
129 if (!addr) {
130 return nullptr;
131 }
132 return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes));
133 }
134
FreePixelRef(void * addr,void * context)135 void FreePixelRef(void* addr, void* context) {
136 auto pixelRef = (SkPixelRef*)context;
137 pixelRef->unref();
138 }
139
createFrom(const SkImageInfo & info,SkPixelRef & pixelRef)140 sk_sp<Bitmap> Bitmap::createFrom(const SkImageInfo& info, SkPixelRef& pixelRef) {
141 pixelRef.ref();
142 return sk_sp<Bitmap>(new Bitmap((void*)pixelRef.pixels(), (void*)&pixelRef, FreePixelRef, info,
143 pixelRef.rowBytes()));
144 }
145
146
147 #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
createFrom(AHardwareBuffer * hardwareBuffer,sk_sp<SkColorSpace> colorSpace,BitmapPalette palette)148 sk_sp<Bitmap> Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, sk_sp<SkColorSpace> colorSpace,
149 BitmapPalette palette) {
150 AHardwareBuffer_Desc bufferDesc;
151 AHardwareBuffer_describe(hardwareBuffer, &bufferDesc);
152 SkImageInfo info = uirenderer::BufferDescriptionToImageInfo(bufferDesc, colorSpace);
153 return createFrom(hardwareBuffer, info, bufferDesc, palette);
154 }
155
createFrom(AHardwareBuffer * hardwareBuffer,SkColorType colorType,sk_sp<SkColorSpace> colorSpace,SkAlphaType alphaType,BitmapPalette palette)156 sk_sp<Bitmap> Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, SkColorType colorType,
157 sk_sp<SkColorSpace> colorSpace, SkAlphaType alphaType,
158 BitmapPalette palette) {
159 AHardwareBuffer_Desc bufferDesc;
160 AHardwareBuffer_describe(hardwareBuffer, &bufferDesc);
161 SkImageInfo info = SkImageInfo::Make(bufferDesc.width, bufferDesc.height,
162 colorType, alphaType, colorSpace);
163 return createFrom(hardwareBuffer, info, bufferDesc, palette);
164 }
165
createFrom(AHardwareBuffer * hardwareBuffer,const SkImageInfo & info,const AHardwareBuffer_Desc & bufferDesc,BitmapPalette palette)166 sk_sp<Bitmap> Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, const SkImageInfo& info,
167 const AHardwareBuffer_Desc& bufferDesc, BitmapPalette palette) {
168 // If the stride is 0 we have to use the width as an approximation (eg, compressed buffer)
169 const auto bufferStride = bufferDesc.stride > 0 ? bufferDesc.stride : bufferDesc.width;
170 const size_t rowBytes = info.bytesPerPixel() * bufferStride;
171 return sk_sp<Bitmap>(new Bitmap(hardwareBuffer, info, rowBytes, palette));
172 }
173 #endif
174
createFrom(const SkImageInfo & info,size_t rowBytes,int fd,void * addr,size_t size,bool readOnly)175 sk_sp<Bitmap> Bitmap::createFrom(const SkImageInfo& info, size_t rowBytes, int fd, void* addr,
176 size_t size, bool readOnly) {
177 #ifdef _WIN32 // ashmem not implemented on Windows
178 return nullptr;
179 #else
180 if (info.colorType() == kUnknown_SkColorType) {
181 LOG_ALWAYS_FATAL("unknown bitmap configuration");
182 return nullptr;
183 }
184
185 if (!addr) {
186 // Map existing ashmem region if not already mapped.
187 int flags = readOnly ? (PROT_READ) : (PROT_READ | PROT_WRITE);
188 size = ashmem_get_size_region(fd);
189 addr = mmap(NULL, size, flags, MAP_SHARED, fd, 0);
190 if (addr == MAP_FAILED) {
191 return nullptr;
192 }
193 }
194
195 sk_sp<Bitmap> bitmap(new Bitmap(addr, fd, size, info, rowBytes));
196 if (readOnly) {
197 bitmap->setImmutable();
198 }
199 return bitmap;
200 #endif
201 }
202
setColorSpace(sk_sp<SkColorSpace> colorSpace)203 void Bitmap::setColorSpace(sk_sp<SkColorSpace> colorSpace) {
204 mInfo = mInfo.makeColorSpace(std::move(colorSpace));
205 }
206
validateAlpha(const SkImageInfo & info)207 static SkImageInfo validateAlpha(const SkImageInfo& info) {
208 // Need to validate the alpha type to filter against the color type
209 // to prevent things like a non-opaque RGB565 bitmap
210 SkAlphaType alphaType;
211 LOG_ALWAYS_FATAL_IF(
212 !SkColorTypeValidateAlphaType(info.colorType(), info.alphaType(), &alphaType),
213 "Failed to validate alpha type!");
214 return info.makeAlphaType(alphaType);
215 }
216
reconfigure(const SkImageInfo & newInfo,size_t rowBytes)217 void Bitmap::reconfigure(const SkImageInfo& newInfo, size_t rowBytes) {
218 mInfo = validateAlpha(newInfo);
219
220 // Dirty hack is dirty
221 // TODO: Figure something out here, Skia's current design makes this
222 // really hard to work with. Skia really, really wants immutable objects,
223 // but with the nested-ref-count hackery going on that's just not
224 // feasible without going insane trying to figure it out
225 this->android_only_reset(mInfo.width(), mInfo.height(), rowBytes);
226 }
227
Bitmap(void * address,size_t size,const SkImageInfo & info,size_t rowBytes)228 Bitmap::Bitmap(void* address, size_t size, const SkImageInfo& info, size_t rowBytes)
229 : SkPixelRef(info.width(), info.height(), address, rowBytes)
230 , mInfo(validateAlpha(info))
231 , mPixelStorageType(PixelStorageType::Heap) {
232 mPixelStorage.heap.address = address;
233 mPixelStorage.heap.size = size;
234 }
235
Bitmap(void * address,void * context,FreeFunc freeFunc,const SkImageInfo & info,size_t rowBytes)236 Bitmap::Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info,
237 size_t rowBytes)
238 : SkPixelRef(info.width(), info.height(), address, rowBytes)
239 , mInfo(validateAlpha(info))
240 , mPixelStorageType(PixelStorageType::External) {
241 mPixelStorage.external.address = address;
242 mPixelStorage.external.context = context;
243 mPixelStorage.external.freeFunc = freeFunc;
244 }
245
Bitmap(void * address,int fd,size_t mappedSize,const SkImageInfo & info,size_t rowBytes)246 Bitmap::Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes)
247 : SkPixelRef(info.width(), info.height(), address, rowBytes)
248 , mInfo(validateAlpha(info))
249 , mPixelStorageType(PixelStorageType::Ashmem) {
250 mPixelStorage.ashmem.address = address;
251 mPixelStorage.ashmem.fd = fd;
252 mPixelStorage.ashmem.size = mappedSize;
253 }
254
255 #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
Bitmap(AHardwareBuffer * buffer,const SkImageInfo & info,size_t rowBytes,BitmapPalette palette)256 Bitmap::Bitmap(AHardwareBuffer* buffer, const SkImageInfo& info, size_t rowBytes,
257 BitmapPalette palette)
258 : SkPixelRef(info.width(), info.height(), nullptr, rowBytes)
259 , mInfo(validateAlpha(info))
260 , mPixelStorageType(PixelStorageType::Hardware)
261 , mPalette(palette)
262 , mPaletteGenerationId(getGenerationID()) {
263 mPixelStorage.hardware.buffer = buffer;
264 AHardwareBuffer_acquire(buffer);
265 setImmutable(); // HW bitmaps are always immutable
266 mImage = SkImage::MakeFromAHardwareBuffer(buffer, mInfo.alphaType(), mInfo.refColorSpace());
267 }
268 #endif
269
~Bitmap()270 Bitmap::~Bitmap() {
271 switch (mPixelStorageType) {
272 case PixelStorageType::External:
273 mPixelStorage.external.freeFunc(mPixelStorage.external.address,
274 mPixelStorage.external.context);
275 break;
276 case PixelStorageType::Ashmem:
277 #ifndef _WIN32 // ashmem not implemented on Windows
278 munmap(mPixelStorage.ashmem.address, mPixelStorage.ashmem.size);
279 #endif
280 close(mPixelStorage.ashmem.fd);
281 break;
282 case PixelStorageType::Heap:
283 free(mPixelStorage.heap.address);
284 #ifdef __ANDROID__
285 mallopt(M_PURGE, 0);
286 #endif
287 break;
288 case PixelStorageType::Hardware:
289 #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
290 auto buffer = mPixelStorage.hardware.buffer;
291 AHardwareBuffer_release(buffer);
292 mPixelStorage.hardware.buffer = nullptr;
293 #endif
294 break;
295 }
296 }
297
hasHardwareMipMap() const298 bool Bitmap::hasHardwareMipMap() const {
299 return mHasHardwareMipMap;
300 }
301
setHasHardwareMipMap(bool hasMipMap)302 void Bitmap::setHasHardwareMipMap(bool hasMipMap) {
303 mHasHardwareMipMap = hasMipMap;
304 }
305
getStorage() const306 void* Bitmap::getStorage() const {
307 switch (mPixelStorageType) {
308 case PixelStorageType::External:
309 return mPixelStorage.external.address;
310 case PixelStorageType::Ashmem:
311 return mPixelStorage.ashmem.address;
312 case PixelStorageType::Heap:
313 return mPixelStorage.heap.address;
314 case PixelStorageType::Hardware:
315 return nullptr;
316 }
317 }
318
getAshmemFd() const319 int Bitmap::getAshmemFd() const {
320 switch (mPixelStorageType) {
321 case PixelStorageType::Ashmem:
322 return mPixelStorage.ashmem.fd;
323 default:
324 return -1;
325 }
326 }
327
getAllocationByteCount() const328 size_t Bitmap::getAllocationByteCount() const {
329 switch (mPixelStorageType) {
330 case PixelStorageType::Heap:
331 return mPixelStorage.heap.size;
332 case PixelStorageType::Ashmem:
333 return mPixelStorage.ashmem.size;
334 default:
335 return rowBytes() * height();
336 }
337 }
338
reconfigure(const SkImageInfo & info)339 void Bitmap::reconfigure(const SkImageInfo& info) {
340 reconfigure(info, info.minRowBytes());
341 }
342
setAlphaType(SkAlphaType alphaType)343 void Bitmap::setAlphaType(SkAlphaType alphaType) {
344 if (!SkColorTypeValidateAlphaType(info().colorType(), alphaType, &alphaType)) {
345 return;
346 }
347
348 mInfo = mInfo.makeAlphaType(alphaType);
349 }
350
getSkBitmap(SkBitmap * outBitmap)351 void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
352 #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
353 if (isHardware()) {
354 outBitmap->allocPixels(mInfo);
355 uirenderer::renderthread::RenderProxy::copyHWBitmapInto(this, outBitmap);
356 return;
357 }
358 #endif
359 outBitmap->setInfo(mInfo, rowBytes());
360 outBitmap->setPixelRef(sk_ref_sp(this), 0, 0);
361 }
362
getBounds(SkRect * bounds) const363 void Bitmap::getBounds(SkRect* bounds) const {
364 SkASSERT(bounds);
365 bounds->setIWH(width(), height());
366 }
367
368 #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
hardwareBuffer()369 AHardwareBuffer* Bitmap::hardwareBuffer() {
370 if (isHardware()) {
371 return mPixelStorage.hardware.buffer;
372 }
373 return nullptr;
374 }
375 #endif
376
makeImage()377 sk_sp<SkImage> Bitmap::makeImage() {
378 sk_sp<SkImage> image = mImage;
379 if (!image) {
380 SkASSERT(!isHardware());
381 SkBitmap skiaBitmap;
382 skiaBitmap.setInfo(info(), rowBytes());
383 skiaBitmap.setPixelRef(sk_ref_sp(this), 0, 0);
384 // Note we don't cache in this case, because the raster image holds a pointer to this Bitmap
385 // internally and ~Bitmap won't be invoked.
386 // TODO: refactor Bitmap to not derive from SkPixelRef, which would allow caching here.
387 image = SkMakeImageFromRasterBitmap(skiaBitmap, kNever_SkCopyPixelsMode);
388 }
389 return image;
390 }
391
392 class MinMaxAverage {
393 public:
add(float sample)394 void add(float sample) {
395 if (mCount == 0) {
396 mMin = sample;
397 mMax = sample;
398 } else {
399 mMin = std::min(mMin, sample);
400 mMax = std::max(mMax, sample);
401 }
402 mTotal += sample;
403 mCount++;
404 }
405
average()406 float average() { return mTotal / mCount; }
407
min()408 float min() { return mMin; }
409
max()410 float max() { return mMax; }
411
delta()412 float delta() { return mMax - mMin; }
413
414 private:
415 float mMin = 0.0f;
416 float mMax = 0.0f;
417 float mTotal = 0.0f;
418 int mCount = 0;
419 };
420
computePalette(const SkImageInfo & info,const void * addr,size_t rowBytes)421 BitmapPalette Bitmap::computePalette(const SkImageInfo& info, const void* addr, size_t rowBytes) {
422 ATRACE_CALL();
423
424 SkPixmap pixmap{info, addr, rowBytes};
425
426 // TODO: This calculation of converting to HSV & tracking min/max is probably overkill
427 // Experiment with something simpler since we just want to figure out if it's "color-ful"
428 // and then the average perceptual lightness.
429
430 MinMaxAverage hue, saturation, value;
431 int sampledCount = 0;
432
433 // Sample a grid of 100 pixels to get an overall estimation of the colors in play
434 const int x_step = std::max(1, pixmap.width() / 10);
435 const int y_step = std::max(1, pixmap.height() / 10);
436 for (int x = 0; x < pixmap.width(); x += x_step) {
437 for (int y = 0; y < pixmap.height(); y += y_step) {
438 SkColor color = pixmap.getColor(x, y);
439 if (!info.isOpaque() && SkColorGetA(color) < 75) {
440 continue;
441 }
442
443 sampledCount++;
444 float hsv[3];
445 SkColorToHSV(color, hsv);
446 hue.add(hsv[0]);
447 saturation.add(hsv[1]);
448 value.add(hsv[2]);
449 }
450 }
451
452 // TODO: Tune the coverage threshold
453 if (sampledCount < 5) {
454 ALOGV("Not enough samples, only found %d for image sized %dx%d, format = %d, alpha = %d",
455 sampledCount, info.width(), info.height(), (int)info.colorType(),
456 (int)info.alphaType());
457 return BitmapPalette::Unknown;
458 }
459
460 ALOGV("samples = %d, hue [min = %f, max = %f, avg = %f]; saturation [min = %f, max = %f, avg = "
461 "%f]",
462 sampledCount, hue.min(), hue.max(), hue.average(), saturation.min(), saturation.max(),
463 saturation.average());
464
465 if (hue.delta() <= 20 && saturation.delta() <= .1f) {
466 if (value.average() >= .5f) {
467 return BitmapPalette::Light;
468 } else {
469 return BitmapPalette::Dark;
470 }
471 }
472 return BitmapPalette::Unknown;
473 }
474
compress(JavaCompressFormat format,int32_t quality,SkWStream * stream)475 bool Bitmap::compress(JavaCompressFormat format, int32_t quality, SkWStream* stream) {
476 SkBitmap skbitmap;
477 getSkBitmap(&skbitmap);
478 return compress(skbitmap, format, quality, stream);
479 }
480
compress(const SkBitmap & bitmap,JavaCompressFormat format,int32_t quality,SkWStream * stream)481 bool Bitmap::compress(const SkBitmap& bitmap, JavaCompressFormat format,
482 int32_t quality, SkWStream* stream) {
483 if (bitmap.colorType() == kAlpha_8_SkColorType) {
484 // None of the JavaCompressFormats have a sensible way to compress an
485 // ALPHA_8 Bitmap. SkPngEncoder will compress one, but it uses a non-
486 // standard format that most decoders do not understand, so this is
487 // likely not useful.
488 return false;
489 }
490
491 SkEncodedImageFormat fm;
492 switch (format) {
493 case JavaCompressFormat::Jpeg:
494 fm = SkEncodedImageFormat::kJPEG;
495 break;
496 case JavaCompressFormat::Png:
497 fm = SkEncodedImageFormat::kPNG;
498 break;
499 case JavaCompressFormat::Webp:
500 fm = SkEncodedImageFormat::kWEBP;
501 break;
502 case JavaCompressFormat::WebpLossy:
503 case JavaCompressFormat::WebpLossless: {
504 SkWebpEncoder::Options options;
505 options.fQuality = quality;
506 options.fCompression = format == JavaCompressFormat::WebpLossy ?
507 SkWebpEncoder::Compression::kLossy : SkWebpEncoder::Compression::kLossless;
508 return SkWebpEncoder::Encode(stream, bitmap.pixmap(), options);
509 }
510 }
511
512 return SkEncodeImage(stream, bitmap, fm, quality);
513 }
514 } // namespace android
515