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