1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/layers/GrallocImages.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,325 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "GrallocImages.h" 1.11 +#include <stddef.h> // for size_t 1.12 +#include <stdint.h> // for int8_t, uint8_t, uint32_t, etc 1.13 +#include "nsDebug.h" // for NS_WARNING, NS_PRECONDITION 1.14 +#include "mozilla/layers/ImageBridgeChild.h" 1.15 +#include "mozilla/layers/GrallocTextureClient.h" 1.16 +#include "gfx2DGlue.h" 1.17 +#include "YCbCrUtils.h" // for YCbCr conversions 1.18 + 1.19 +#include <ColorConverter.h> 1.20 +#include <OMX_IVCommon.h> 1.21 + 1.22 + 1.23 +using namespace mozilla::ipc; 1.24 +using namespace android; 1.25 + 1.26 +#define ALIGN(x, align) ((x + align - 1) & ~(align - 1)) 1.27 + 1.28 +namespace mozilla { 1.29 +namespace layers { 1.30 + 1.31 +uint32_t GrallocImage::sColorIdMap[] = { 1.32 + HAL_PIXEL_FORMAT_YCbCr_420_P, OMX_COLOR_FormatYUV420Planar, 1.33 + HAL_PIXEL_FORMAT_YCbCr_422_P, OMX_COLOR_FormatYUV422Planar, 1.34 + HAL_PIXEL_FORMAT_YCbCr_420_SP, OMX_COLOR_FormatYUV420SemiPlanar, 1.35 + HAL_PIXEL_FORMAT_YCrCb_420_SP, -1, 1.36 + HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO, -1, 1.37 + HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED, HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED, 1.38 + HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS, HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS, 1.39 + HAL_PIXEL_FORMAT_YV12, OMX_COLOR_FormatYUV420Planar, 1.40 + 0, 0 1.41 +}; 1.42 + 1.43 +struct GraphicBufferAutoUnlock { 1.44 + android::sp<GraphicBuffer> mGraphicBuffer; 1.45 + 1.46 + GraphicBufferAutoUnlock(android::sp<GraphicBuffer>& aGraphicBuffer) 1.47 + : mGraphicBuffer(aGraphicBuffer) { } 1.48 + 1.49 + ~GraphicBufferAutoUnlock() { mGraphicBuffer->unlock(); } 1.50 +}; 1.51 + 1.52 +GrallocImage::GrallocImage() 1.53 + : PlanarYCbCrImage(nullptr) 1.54 +{ 1.55 + mFormat = ImageFormat::GRALLOC_PLANAR_YCBCR; 1.56 +} 1.57 + 1.58 +GrallocImage::~GrallocImage() 1.59 +{ 1.60 +} 1.61 + 1.62 +void 1.63 +GrallocImage::SetData(const Data& aData) 1.64 +{ 1.65 + MOZ_ASSERT(!mTextureClient, "TextureClient is already set"); 1.66 + NS_PRECONDITION(aData.mYSize.width % 2 == 0, "Image should have even width"); 1.67 + NS_PRECONDITION(aData.mYSize.height % 2 == 0, "Image should have even height"); 1.68 + NS_PRECONDITION(aData.mYStride % 16 == 0, "Image should have stride of multiple of 16 pixels"); 1.69 + 1.70 + mData = aData; 1.71 + mSize = aData.mPicSize; 1.72 + 1.73 + if (gfxPlatform::GetPlatform()->IsInGonkEmulator()) { 1.74 + // Emulator does not support HAL_PIXEL_FORMAT_YV12. 1.75 + return; 1.76 + } 1.77 + 1.78 + RefPtr<GrallocTextureClientOGL> textureClient = 1.79 + new GrallocTextureClientOGL(ImageBridgeChild::GetSingleton(), 1.80 + gfx::SurfaceFormat::UNKNOWN, 1.81 + gfx::BackendType::NONE); 1.82 + bool result = 1.83 + textureClient->AllocateGralloc(mData.mYSize, 1.84 + HAL_PIXEL_FORMAT_YV12, 1.85 + GraphicBuffer::USAGE_SW_READ_OFTEN | 1.86 + GraphicBuffer::USAGE_SW_WRITE_OFTEN | 1.87 + GraphicBuffer::USAGE_HW_TEXTURE); 1.88 + sp<GraphicBuffer> graphicBuffer = textureClient->GetGraphicBuffer(); 1.89 + if (!result || !graphicBuffer.get()) { 1.90 + mTextureClient = nullptr; 1.91 + return; 1.92 + } 1.93 + 1.94 + mTextureClient = textureClient; 1.95 + 1.96 + void* vaddr; 1.97 + if (graphicBuffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, 1.98 + &vaddr) != OK) { 1.99 + return; 1.100 + } 1.101 + 1.102 + uint8_t* yChannel = static_cast<uint8_t*>(vaddr); 1.103 + gfx::IntSize ySize = aData.mYSize; 1.104 + int32_t yStride = graphicBuffer->getStride(); 1.105 + 1.106 + uint8_t* vChannel = yChannel + (yStride * ySize.height); 1.107 + gfx::IntSize uvSize = gfx::IntSize(ySize.width / 2, 1.108 + ySize.height / 2); 1.109 + // Align to 16 bytes boundary 1.110 + int32_t uvStride = ((yStride / 2) + 15) & ~0x0F; 1.111 + uint8_t* uChannel = vChannel + (uvStride * uvSize.height); 1.112 + 1.113 + // Memory outside of the image width may not writable. If the stride 1.114 + // equals to the image width then we can use only one copy. 1.115 + if (yStride == mData.mYStride && 1.116 + yStride == ySize.width) { 1.117 + memcpy(yChannel, mData.mYChannel, yStride * ySize.height); 1.118 + } else { 1.119 + for (int i = 0; i < ySize.height; i++) { 1.120 + memcpy(yChannel + i * yStride, 1.121 + mData.mYChannel + i * mData.mYStride, 1.122 + ySize.width); 1.123 + } 1.124 + } 1.125 + if (uvStride == mData.mCbCrStride && 1.126 + uvStride == uvSize.width) { 1.127 + memcpy(uChannel, mData.mCbChannel, uvStride * uvSize.height); 1.128 + memcpy(vChannel, mData.mCrChannel, uvStride * uvSize.height); 1.129 + } else { 1.130 + for (int i = 0; i < uvSize.height; i++) { 1.131 + memcpy(uChannel + i * uvStride, 1.132 + mData.mCbChannel + i * mData.mCbCrStride, 1.133 + uvSize.width); 1.134 + memcpy(vChannel + i * uvStride, 1.135 + mData.mCrChannel + i * mData.mCbCrStride, 1.136 + uvSize.width); 1.137 + } 1.138 + } 1.139 + graphicBuffer->unlock(); 1.140 +} 1.141 + 1.142 +void GrallocImage::SetData(const GrallocData& aData) 1.143 +{ 1.144 + mTextureClient = static_cast<GrallocTextureClientOGL*>(aData.mGraphicBuffer.get()); 1.145 + mSize = aData.mPicSize; 1.146 +} 1.147 + 1.148 +/** 1.149 + * Converts YVU420 semi planar frames to RGB565, possibly taking different 1.150 + * stride values. 1.151 + * Needed because the Android ColorConverter class assumes that the Y and UV 1.152 + * channels have equal stride. 1.153 + */ 1.154 +static void 1.155 +ConvertYVU420SPToRGB565(void *aYData, uint32_t aYStride, 1.156 + void *aUVData, uint32_t aUVStride, 1.157 + void *aOut, 1.158 + uint32_t aWidth, uint32_t aHeight) 1.159 +{ 1.160 + uint8_t *y = (uint8_t*)aYData; 1.161 + int8_t *uv = (int8_t*)aUVData; 1.162 + 1.163 + uint16_t *rgb = (uint16_t*)aOut; 1.164 + 1.165 + for (size_t i = 0; i < aHeight; i++) { 1.166 + for (size_t j = 0; j < aWidth; j++) { 1.167 + int8_t d = uv[j | 1] - 128; 1.168 + int8_t e = uv[j & ~1] - 128; 1.169 + 1.170 + // Constants taken from https://en.wikipedia.org/wiki/YUV 1.171 + int32_t r = (298 * y[j] + 409 * e + 128) >> 11; 1.172 + int32_t g = (298 * y[j] - 100 * d - 208 * e + 128) >> 10; 1.173 + int32_t b = (298 * y[j] + 516 * d + 128) >> 11; 1.174 + 1.175 + r = r > 0x1f ? 0x1f : r < 0 ? 0 : r; 1.176 + g = g > 0x3f ? 0x3f : g < 0 ? 0 : g; 1.177 + b = b > 0x1f ? 0x1f : b < 0 ? 0 : b; 1.178 + 1.179 + *rgb++ = (uint16_t)(r << 11 | g << 5 | b); 1.180 + } 1.181 + 1.182 + y += aYStride; 1.183 + if (i % 2) { 1.184 + uv += aUVStride; 1.185 + } 1.186 + } 1.187 +} 1.188 + 1.189 +TemporaryRef<gfx::SourceSurface> 1.190 +GrallocImage::GetAsSourceSurface() 1.191 +{ 1.192 + if (!mTextureClient) { 1.193 + return nullptr; 1.194 + } 1.195 + android::sp<GraphicBuffer> graphicBuffer = 1.196 + mTextureClient->GetGraphicBuffer(); 1.197 + 1.198 + void *buffer; 1.199 + int32_t rv = 1.200 + graphicBuffer->lock(android::GraphicBuffer::USAGE_SW_READ_OFTEN, &buffer); 1.201 + 1.202 + if (rv) { 1.203 + NS_WARNING("Couldn't lock graphic buffer"); 1.204 + return nullptr; 1.205 + } 1.206 + 1.207 + GraphicBufferAutoUnlock unlock(graphicBuffer); 1.208 + 1.209 + uint32_t format = graphicBuffer->getPixelFormat(); 1.210 + uint32_t omxFormat = 0; 1.211 + 1.212 + for (int i = 0; sColorIdMap[i]; i += 2) { 1.213 + if (sColorIdMap[i] == format) { 1.214 + omxFormat = sColorIdMap[i + 1]; 1.215 + break; 1.216 + } 1.217 + } 1.218 + 1.219 + if (!omxFormat) { 1.220 + NS_WARNING("Unknown color format"); 1.221 + return nullptr; 1.222 + } 1.223 + 1.224 + RefPtr<gfx::DataSourceSurface> surface 1.225 + = gfx::Factory::CreateDataSourceSurface(GetSize(), gfx::SurfaceFormat::R5G6B5); 1.226 + 1.227 + uint32_t width = GetSize().width; 1.228 + uint32_t height = GetSize().height; 1.229 + 1.230 + gfx::DataSourceSurface::MappedSurface mappedSurface; 1.231 + if (!surface->Map(gfx::DataSourceSurface::WRITE, &mappedSurface)) { 1.232 + NS_WARNING("Could not map DataSourceSurface"); 1.233 + return nullptr; 1.234 + } 1.235 + 1.236 + if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO) { 1.237 + // The Adreno hardware decoder aligns image dimensions to a multiple of 32, 1.238 + // so we have to account for that here 1.239 + uint32_t alignedWidth = ALIGN(width, 32); 1.240 + uint32_t alignedHeight = ALIGN(height, 32); 1.241 + uint32_t uvOffset = ALIGN(alignedHeight * alignedWidth, 4096); 1.242 + uint32_t uvStride = 2 * ALIGN(width / 2, 32); 1.243 + uint8_t* buffer_as_bytes = static_cast<uint8_t*>(buffer); 1.244 + ConvertYVU420SPToRGB565(buffer, alignedWidth, 1.245 + buffer_as_bytes + uvOffset, uvStride, 1.246 + mappedSurface.mData, 1.247 + width, height); 1.248 + 1.249 + surface->Unmap(); 1.250 + return surface; 1.251 + } 1.252 + 1.253 + if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP) { 1.254 + uint32_t uvOffset = height * width; 1.255 + ConvertYVU420SPToRGB565(buffer, width, 1.256 + buffer + uvOffset, width, 1.257 + mappedSurface.mData, 1.258 + width, height); 1.259 + 1.260 + surface->Unmap(); 1.261 + return surface; 1.262 + } 1.263 + 1.264 + if (format == HAL_PIXEL_FORMAT_YV12) { 1.265 + gfx::ConvertYCbCrToRGB(mData, 1.266 + surface->GetFormat(), 1.267 + mSize, 1.268 + surface->GetData(), 1.269 + surface->Stride()); 1.270 + surface->Unmap(); 1.271 + return surface; 1.272 + } 1.273 + 1.274 + android::ColorConverter colorConverter((OMX_COLOR_FORMATTYPE)omxFormat, 1.275 + OMX_COLOR_Format16bitRGB565); 1.276 + 1.277 + if (!colorConverter.isValid()) { 1.278 + NS_WARNING("Invalid color conversion"); 1.279 + surface->Unmap(); 1.280 + return nullptr; 1.281 + } 1.282 + 1.283 + rv = colorConverter.convert(buffer, width, height, 1.284 + 0, 0, width - 1, height - 1 /* source crop */, 1.285 + mappedSurface.mData, width, height, 1.286 + 0, 0, width - 1, height - 1 /* dest crop */); 1.287 + 1.288 + surface->Unmap(); 1.289 + 1.290 + if (rv) { 1.291 + NS_WARNING("OMX color conversion failed"); 1.292 + return nullptr; 1.293 + } 1.294 + 1.295 + return surface; 1.296 +} 1.297 + 1.298 +android::sp<android::GraphicBuffer> 1.299 +GrallocImage::GetGraphicBuffer() const 1.300 +{ 1.301 + if (!mTextureClient) { 1.302 + return nullptr; 1.303 + } 1.304 + return mTextureClient->GetGraphicBuffer(); 1.305 +} 1.306 + 1.307 +void* 1.308 +GrallocImage::GetNativeBuffer() 1.309 +{ 1.310 + if (!mTextureClient) { 1.311 + return nullptr; 1.312 + } 1.313 + android::sp<android::GraphicBuffer> graphicBuffer = 1.314 + mTextureClient->GetGraphicBuffer(); 1.315 + if (!graphicBuffer.get()) { 1.316 + return nullptr; 1.317 + } 1.318 + return graphicBuffer->getNativeBuffer(); 1.319 +} 1.320 + 1.321 +TextureClient* 1.322 +GrallocImage::GetTextureClient(CompositableClient* aClient) 1.323 +{ 1.324 + return mTextureClient; 1.325 +} 1.326 + 1.327 +} // namespace layers 1.328 +} // namespace mozilla