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