michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "GrallocImages.h" michael@0: #include // for size_t michael@0: #include // for int8_t, uint8_t, uint32_t, etc michael@0: #include "nsDebug.h" // for NS_WARNING, NS_PRECONDITION michael@0: #include "mozilla/layers/ImageBridgeChild.h" michael@0: #include "mozilla/layers/GrallocTextureClient.h" michael@0: #include "gfx2DGlue.h" michael@0: #include "YCbCrUtils.h" // for YCbCr conversions michael@0: michael@0: #include michael@0: #include michael@0: michael@0: michael@0: using namespace mozilla::ipc; michael@0: using namespace android; michael@0: michael@0: #define ALIGN(x, align) ((x + align - 1) & ~(align - 1)) michael@0: michael@0: namespace mozilla { michael@0: namespace layers { michael@0: michael@0: uint32_t GrallocImage::sColorIdMap[] = { michael@0: HAL_PIXEL_FORMAT_YCbCr_420_P, OMX_COLOR_FormatYUV420Planar, michael@0: HAL_PIXEL_FORMAT_YCbCr_422_P, OMX_COLOR_FormatYUV422Planar, michael@0: HAL_PIXEL_FORMAT_YCbCr_420_SP, OMX_COLOR_FormatYUV420SemiPlanar, michael@0: HAL_PIXEL_FORMAT_YCrCb_420_SP, -1, michael@0: HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO, -1, michael@0: HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED, HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED, michael@0: HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS, HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS, michael@0: HAL_PIXEL_FORMAT_YV12, OMX_COLOR_FormatYUV420Planar, michael@0: 0, 0 michael@0: }; michael@0: michael@0: struct GraphicBufferAutoUnlock { michael@0: android::sp mGraphicBuffer; michael@0: michael@0: GraphicBufferAutoUnlock(android::sp& aGraphicBuffer) michael@0: : mGraphicBuffer(aGraphicBuffer) { } michael@0: michael@0: ~GraphicBufferAutoUnlock() { mGraphicBuffer->unlock(); } michael@0: }; michael@0: michael@0: GrallocImage::GrallocImage() michael@0: : PlanarYCbCrImage(nullptr) michael@0: { michael@0: mFormat = ImageFormat::GRALLOC_PLANAR_YCBCR; michael@0: } michael@0: michael@0: GrallocImage::~GrallocImage() michael@0: { michael@0: } michael@0: michael@0: void michael@0: GrallocImage::SetData(const Data& aData) michael@0: { michael@0: MOZ_ASSERT(!mTextureClient, "TextureClient is already set"); michael@0: NS_PRECONDITION(aData.mYSize.width % 2 == 0, "Image should have even width"); michael@0: NS_PRECONDITION(aData.mYSize.height % 2 == 0, "Image should have even height"); michael@0: NS_PRECONDITION(aData.mYStride % 16 == 0, "Image should have stride of multiple of 16 pixels"); michael@0: michael@0: mData = aData; michael@0: mSize = aData.mPicSize; michael@0: michael@0: if (gfxPlatform::GetPlatform()->IsInGonkEmulator()) { michael@0: // Emulator does not support HAL_PIXEL_FORMAT_YV12. michael@0: return; michael@0: } michael@0: michael@0: RefPtr textureClient = michael@0: new GrallocTextureClientOGL(ImageBridgeChild::GetSingleton(), michael@0: gfx::SurfaceFormat::UNKNOWN, michael@0: gfx::BackendType::NONE); michael@0: bool result = michael@0: textureClient->AllocateGralloc(mData.mYSize, michael@0: HAL_PIXEL_FORMAT_YV12, michael@0: GraphicBuffer::USAGE_SW_READ_OFTEN | michael@0: GraphicBuffer::USAGE_SW_WRITE_OFTEN | michael@0: GraphicBuffer::USAGE_HW_TEXTURE); michael@0: sp graphicBuffer = textureClient->GetGraphicBuffer(); michael@0: if (!result || !graphicBuffer.get()) { michael@0: mTextureClient = nullptr; michael@0: return; michael@0: } michael@0: michael@0: mTextureClient = textureClient; michael@0: michael@0: void* vaddr; michael@0: if (graphicBuffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, michael@0: &vaddr) != OK) { michael@0: return; michael@0: } michael@0: michael@0: uint8_t* yChannel = static_cast(vaddr); michael@0: gfx::IntSize ySize = aData.mYSize; michael@0: int32_t yStride = graphicBuffer->getStride(); michael@0: michael@0: uint8_t* vChannel = yChannel + (yStride * ySize.height); michael@0: gfx::IntSize uvSize = gfx::IntSize(ySize.width / 2, michael@0: ySize.height / 2); michael@0: // Align to 16 bytes boundary michael@0: int32_t uvStride = ((yStride / 2) + 15) & ~0x0F; michael@0: uint8_t* uChannel = vChannel + (uvStride * uvSize.height); michael@0: michael@0: // Memory outside of the image width may not writable. If the stride michael@0: // equals to the image width then we can use only one copy. michael@0: if (yStride == mData.mYStride && michael@0: yStride == ySize.width) { michael@0: memcpy(yChannel, mData.mYChannel, yStride * ySize.height); michael@0: } else { michael@0: for (int i = 0; i < ySize.height; i++) { michael@0: memcpy(yChannel + i * yStride, michael@0: mData.mYChannel + i * mData.mYStride, michael@0: ySize.width); michael@0: } michael@0: } michael@0: if (uvStride == mData.mCbCrStride && michael@0: uvStride == uvSize.width) { michael@0: memcpy(uChannel, mData.mCbChannel, uvStride * uvSize.height); michael@0: memcpy(vChannel, mData.mCrChannel, uvStride * uvSize.height); michael@0: } else { michael@0: for (int i = 0; i < uvSize.height; i++) { michael@0: memcpy(uChannel + i * uvStride, michael@0: mData.mCbChannel + i * mData.mCbCrStride, michael@0: uvSize.width); michael@0: memcpy(vChannel + i * uvStride, michael@0: mData.mCrChannel + i * mData.mCbCrStride, michael@0: uvSize.width); michael@0: } michael@0: } michael@0: graphicBuffer->unlock(); michael@0: } michael@0: michael@0: void GrallocImage::SetData(const GrallocData& aData) michael@0: { michael@0: mTextureClient = static_cast(aData.mGraphicBuffer.get()); michael@0: mSize = aData.mPicSize; michael@0: } michael@0: michael@0: /** michael@0: * Converts YVU420 semi planar frames to RGB565, possibly taking different michael@0: * stride values. michael@0: * Needed because the Android ColorConverter class assumes that the Y and UV michael@0: * channels have equal stride. michael@0: */ michael@0: static void michael@0: ConvertYVU420SPToRGB565(void *aYData, uint32_t aYStride, michael@0: void *aUVData, uint32_t aUVStride, michael@0: void *aOut, michael@0: uint32_t aWidth, uint32_t aHeight) michael@0: { michael@0: uint8_t *y = (uint8_t*)aYData; michael@0: int8_t *uv = (int8_t*)aUVData; michael@0: michael@0: uint16_t *rgb = (uint16_t*)aOut; michael@0: michael@0: for (size_t i = 0; i < aHeight; i++) { michael@0: for (size_t j = 0; j < aWidth; j++) { michael@0: int8_t d = uv[j | 1] - 128; michael@0: int8_t e = uv[j & ~1] - 128; michael@0: michael@0: // Constants taken from https://en.wikipedia.org/wiki/YUV michael@0: int32_t r = (298 * y[j] + 409 * e + 128) >> 11; michael@0: int32_t g = (298 * y[j] - 100 * d - 208 * e + 128) >> 10; michael@0: int32_t b = (298 * y[j] + 516 * d + 128) >> 11; michael@0: michael@0: r = r > 0x1f ? 0x1f : r < 0 ? 0 : r; michael@0: g = g > 0x3f ? 0x3f : g < 0 ? 0 : g; michael@0: b = b > 0x1f ? 0x1f : b < 0 ? 0 : b; michael@0: michael@0: *rgb++ = (uint16_t)(r << 11 | g << 5 | b); michael@0: } michael@0: michael@0: y += aYStride; michael@0: if (i % 2) { michael@0: uv += aUVStride; michael@0: } michael@0: } michael@0: } michael@0: michael@0: TemporaryRef michael@0: GrallocImage::GetAsSourceSurface() michael@0: { michael@0: if (!mTextureClient) { michael@0: return nullptr; michael@0: } michael@0: android::sp graphicBuffer = michael@0: mTextureClient->GetGraphicBuffer(); michael@0: michael@0: void *buffer; michael@0: int32_t rv = michael@0: graphicBuffer->lock(android::GraphicBuffer::USAGE_SW_READ_OFTEN, &buffer); michael@0: michael@0: if (rv) { michael@0: NS_WARNING("Couldn't lock graphic buffer"); michael@0: return nullptr; michael@0: } michael@0: michael@0: GraphicBufferAutoUnlock unlock(graphicBuffer); michael@0: michael@0: uint32_t format = graphicBuffer->getPixelFormat(); michael@0: uint32_t omxFormat = 0; michael@0: michael@0: for (int i = 0; sColorIdMap[i]; i += 2) { michael@0: if (sColorIdMap[i] == format) { michael@0: omxFormat = sColorIdMap[i + 1]; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (!omxFormat) { michael@0: NS_WARNING("Unknown color format"); michael@0: return nullptr; michael@0: } michael@0: michael@0: RefPtr surface michael@0: = gfx::Factory::CreateDataSourceSurface(GetSize(), gfx::SurfaceFormat::R5G6B5); michael@0: michael@0: uint32_t width = GetSize().width; michael@0: uint32_t height = GetSize().height; michael@0: michael@0: gfx::DataSourceSurface::MappedSurface mappedSurface; michael@0: if (!surface->Map(gfx::DataSourceSurface::WRITE, &mappedSurface)) { michael@0: NS_WARNING("Could not map DataSourceSurface"); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO) { michael@0: // The Adreno hardware decoder aligns image dimensions to a multiple of 32, michael@0: // so we have to account for that here michael@0: uint32_t alignedWidth = ALIGN(width, 32); michael@0: uint32_t alignedHeight = ALIGN(height, 32); michael@0: uint32_t uvOffset = ALIGN(alignedHeight * alignedWidth, 4096); michael@0: uint32_t uvStride = 2 * ALIGN(width / 2, 32); michael@0: uint8_t* buffer_as_bytes = static_cast(buffer); michael@0: ConvertYVU420SPToRGB565(buffer, alignedWidth, michael@0: buffer_as_bytes + uvOffset, uvStride, michael@0: mappedSurface.mData, michael@0: width, height); michael@0: michael@0: surface->Unmap(); michael@0: return surface; michael@0: } michael@0: michael@0: if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP) { michael@0: uint32_t uvOffset = height * width; michael@0: ConvertYVU420SPToRGB565(buffer, width, michael@0: buffer + uvOffset, width, michael@0: mappedSurface.mData, michael@0: width, height); michael@0: michael@0: surface->Unmap(); michael@0: return surface; michael@0: } michael@0: michael@0: if (format == HAL_PIXEL_FORMAT_YV12) { michael@0: gfx::ConvertYCbCrToRGB(mData, michael@0: surface->GetFormat(), michael@0: mSize, michael@0: surface->GetData(), michael@0: surface->Stride()); michael@0: surface->Unmap(); michael@0: return surface; michael@0: } michael@0: michael@0: android::ColorConverter colorConverter((OMX_COLOR_FORMATTYPE)omxFormat, michael@0: OMX_COLOR_Format16bitRGB565); michael@0: michael@0: if (!colorConverter.isValid()) { michael@0: NS_WARNING("Invalid color conversion"); michael@0: surface->Unmap(); michael@0: return nullptr; michael@0: } michael@0: michael@0: rv = colorConverter.convert(buffer, width, height, michael@0: 0, 0, width - 1, height - 1 /* source crop */, michael@0: mappedSurface.mData, width, height, michael@0: 0, 0, width - 1, height - 1 /* dest crop */); michael@0: michael@0: surface->Unmap(); michael@0: michael@0: if (rv) { michael@0: NS_WARNING("OMX color conversion failed"); michael@0: return nullptr; michael@0: } michael@0: michael@0: return surface; michael@0: } michael@0: michael@0: android::sp michael@0: GrallocImage::GetGraphicBuffer() const michael@0: { michael@0: if (!mTextureClient) { michael@0: return nullptr; michael@0: } michael@0: return mTextureClient->GetGraphicBuffer(); michael@0: } michael@0: michael@0: void* michael@0: GrallocImage::GetNativeBuffer() michael@0: { michael@0: if (!mTextureClient) { michael@0: return nullptr; michael@0: } michael@0: android::sp graphicBuffer = michael@0: mTextureClient->GetGraphicBuffer(); michael@0: if (!graphicBuffer.get()) { michael@0: return nullptr; michael@0: } michael@0: return graphicBuffer->getNativeBuffer(); michael@0: } michael@0: michael@0: TextureClient* michael@0: GrallocImage::GetTextureClient(CompositableClient* aClient) michael@0: { michael@0: return mTextureClient; michael@0: } michael@0: michael@0: } // namespace layers michael@0: } // namespace mozilla