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