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