gfx/layers/GrallocImages.cpp

changeset 0
6474c204b198
     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

mercurial