Bitmap.cpp revision c1c54062f8cc9d47bdea820ae5ab6aef260b4488
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 "Caches.h"
19
20#include <cutils/log.h>
21#include <sys/mman.h>
22#include <cutils/ashmem.h>
23
24namespace android {
25
26static bool computeAllocationSize(const SkBitmap& bitmap, size_t* size) {
27    int32_t rowBytes32 = SkToS32(bitmap.rowBytes());
28    int64_t bigSize = (int64_t)bitmap.height() * rowBytes32;
29    if (rowBytes32 < 0 || !sk_64_isS32(bigSize)) {
30        return false; // allocation will be too large
31    }
32
33    *size = sk_64_asS32(bigSize);
34    return true;
35}
36
37typedef sk_sp<Bitmap> (*AllocPixeRef)(size_t allocSize, const SkImageInfo& info, size_t rowBytes,
38        SkColorTable* ctable);
39
40static sk_sp<Bitmap> allocateBitmap(SkBitmap* bitmap, SkColorTable* ctable, AllocPixeRef alloc) {
41    const SkImageInfo& info = bitmap->info();
42    if (info.colorType() == kUnknown_SkColorType) {
43        LOG_ALWAYS_FATAL("unknown bitmap configuration");
44        return nullptr;
45    }
46
47    size_t size;
48    if (!computeAllocationSize(*bitmap, &size)) {
49        return nullptr;
50    }
51
52    // we must respect the rowBytes value already set on the bitmap instead of
53    // attempting to compute our own.
54    const size_t rowBytes = bitmap->rowBytes();
55    auto wrapper = alloc(size, info, rowBytes, ctable);
56    if (wrapper) {
57        wrapper->getSkBitmap(bitmap);
58        // since we're already allocated, we lockPixels right away
59        // HeapAllocator behaves this way too
60        bitmap->lockPixels();
61    }
62    return wrapper;
63}
64
65sk_sp<Bitmap> Bitmap::allocateHeapBitmap(SkBitmap* bitmap, SkColorTable* ctable) {
66   return allocateBitmap(bitmap, ctable, &Bitmap::allocateHeapBitmap);
67}
68
69sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(SkBitmap* bitmap, SkColorTable* ctable) {
70   return allocateBitmap(bitmap, ctable, &Bitmap::allocateAshmemBitmap);
71}
72
73sk_sp<Bitmap> Bitmap::allocateHeapBitmap(size_t size, const SkImageInfo& info, size_t rowBytes,
74        SkColorTable* ctable) {
75    void* addr = calloc(size, 1);
76    if (!addr) {
77        return nullptr;
78    }
79    return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes, ctable));
80}
81
82sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info,
83        size_t rowBytes, SkColorTable* ctable) {
84    // Create new ashmem region with read/write priv
85    int fd = ashmem_create_region("bitmap", size);
86    if (fd < 0) {
87        return nullptr;
88    }
89
90    void* addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
91    if (addr == MAP_FAILED) {
92        close(fd);
93        return nullptr;
94    }
95
96    if (ashmem_set_prot_region(fd, PROT_READ) < 0) {
97        munmap(addr, size);
98        close(fd);
99        return nullptr;
100    }
101    return sk_sp<Bitmap>(new Bitmap(addr, fd, size, info, rowBytes, ctable));
102}
103
104void Bitmap::reconfigure(const SkImageInfo& newInfo, size_t rowBytes, SkColorTable* ctable) {
105    if (kIndex_8_SkColorType != newInfo.colorType()) {
106        ctable = nullptr;
107    }
108    mRowBytes = rowBytes;
109    if (mColorTable.get() != ctable) {
110        mColorTable.reset(ctable);
111    }
112
113    // Need to validate the alpha type to filter against the color type
114    // to prevent things like a non-opaque RGB565 bitmap
115    SkAlphaType alphaType;
116    LOG_ALWAYS_FATAL_IF(!SkColorTypeValidateAlphaType(
117            newInfo.colorType(), newInfo.alphaType(), &alphaType),
118            "Failed to validate alpha type!");
119
120    // Dirty hack is dirty
121    // TODO: Figure something out here, Skia's current design makes this
122    // really hard to work with. Skia really, really wants immutable objects,
123    // but with the nested-ref-count hackery going on that's just not
124    // feasible without going insane trying to figure it out
125    SkImageInfo* myInfo = const_cast<SkImageInfo*>(&this->info());
126    *myInfo = newInfo;
127    changeAlphaType(alphaType);
128
129    // Docs say to only call this in the ctor, but we're going to call
130    // it anyway even if this isn't always the ctor.
131    // TODO: Fix this too as part of the above TODO
132    setPreLocked(getStorage(), mRowBytes, mColorTable.get());
133}
134
135Bitmap::Bitmap(void* address, size_t size, const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
136            : SkPixelRef(info)
137            , mPixelStorageType(PixelStorageType::Heap) {
138    mPixelStorage.heap.address = address;
139    mPixelStorage.heap.size = size;
140    reconfigure(info, rowBytes, ctable);
141}
142
143Bitmap::Bitmap(void* address, void* context, FreeFunc freeFunc,
144                const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
145            : SkPixelRef(info)
146            , mPixelStorageType(PixelStorageType::External) {
147    mPixelStorage.external.address = address;
148    mPixelStorage.external.context = context;
149    mPixelStorage.external.freeFunc = freeFunc;
150    reconfigure(info, rowBytes, ctable);
151}
152
153Bitmap::Bitmap(void* address, int fd, size_t mappedSize,
154                const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
155            : SkPixelRef(info)
156            , mPixelStorageType(PixelStorageType::Ashmem) {
157    mPixelStorage.ashmem.address = address;
158    mPixelStorage.ashmem.fd = fd;
159    mPixelStorage.ashmem.size = mappedSize;
160    reconfigure(info, rowBytes, ctable);
161}
162
163Bitmap::~Bitmap() {
164    switch (mPixelStorageType) {
165    case PixelStorageType::External:
166        mPixelStorage.external.freeFunc(mPixelStorage.external.address,
167                mPixelStorage.external.context);
168        break;
169    case PixelStorageType::Ashmem:
170        munmap(mPixelStorage.ashmem.address, mPixelStorage.ashmem.size);
171        close(mPixelStorage.ashmem.fd);
172        break;
173    case PixelStorageType::Heap:
174        free(mPixelStorage.heap.address);
175        break;
176    }
177
178    if (android::uirenderer::Caches::hasInstance()) {
179        android::uirenderer::Caches::getInstance().textureCache.releaseTexture(getStableID());
180    }
181}
182
183bool Bitmap::hasHardwareMipMap() const {
184    return mHasHardwareMipMap;
185}
186
187void Bitmap::setHasHardwareMipMap(bool hasMipMap) {
188    mHasHardwareMipMap = hasMipMap;
189}
190
191void* Bitmap::getStorage() const {
192    switch (mPixelStorageType) {
193    case PixelStorageType::External:
194        return mPixelStorage.external.address;
195    case PixelStorageType::Ashmem:
196        return mPixelStorage.ashmem.address;
197    case PixelStorageType::Heap:
198        return mPixelStorage.heap.address;
199    }
200}
201
202bool Bitmap::onNewLockPixels(LockRec* rec) {
203    rec->fPixels = getStorage();
204    rec->fRowBytes = mRowBytes;
205    rec->fColorTable = mColorTable.get();
206    return true;
207}
208
209size_t Bitmap::getAllocatedSizeInBytes() const {
210    return info().getSafeSize(mRowBytes);
211}
212
213int Bitmap::getAshmemFd() const {
214    switch (mPixelStorageType) {
215    case PixelStorageType::Ashmem:
216        return mPixelStorage.ashmem.fd;
217    default:
218        return -1;
219    }
220}
221
222size_t Bitmap::getAllocationByteCount() const {
223    switch (mPixelStorageType) {
224    case PixelStorageType::Heap:
225        return mPixelStorage.heap.size;
226    default:
227        return rowBytes() * height();
228    }
229}
230
231void Bitmap::reconfigure(const SkImageInfo& info) {
232    reconfigure(info, info.minRowBytes(), nullptr);
233}
234
235void Bitmap::setAlphaType(SkAlphaType alphaType) {
236    if (!SkColorTypeValidateAlphaType(info().colorType(), alphaType, &alphaType)) {
237        return;
238    }
239
240    changeAlphaType(alphaType);
241}
242
243void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
244    outBitmap->setInfo(info(), rowBytes());
245    outBitmap->setPixelRef(this);
246    outBitmap->setHasHardwareMipMap(mHasHardwareMipMap);
247}
248
249} // namespace android