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