Bumping manifests a=b2g-bump
[gecko.git] / gfx / layers / GrallocImages.cpp
blob4889341f8153c71dc683c7a89653c6baab9d9e8c
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "GrallocImages.h"
8 #include <stddef.h> // for size_t
9 #include <stdint.h> // for int8_t, uint8_t, uint32_t, etc
10 #include "nsDebug.h" // for NS_WARNING, NS_PRECONDITION
11 #include "mozilla/layers/ImageBridgeChild.h"
12 #include "mozilla/layers/GrallocTextureClient.h"
13 #include "gfx2DGlue.h"
14 #include "YCbCrUtils.h" // for YCbCr conversions
16 #include <ColorConverter.h>
17 #include <OMX_IVCommon.h>
20 using namespace mozilla::ipc;
21 using namespace android;
23 #define ALIGN(x, align) ((x + align - 1) & ~(align - 1))
25 namespace mozilla {
26 namespace layers {
28 uint32_t GrallocImage::sColorIdMap[] = {
29 HAL_PIXEL_FORMAT_YCbCr_420_P, OMX_COLOR_FormatYUV420Planar,
30 HAL_PIXEL_FORMAT_YCbCr_422_P, OMX_COLOR_FormatYUV422Planar,
31 HAL_PIXEL_FORMAT_YCbCr_420_SP, OMX_COLOR_FormatYUV420SemiPlanar,
32 HAL_PIXEL_FORMAT_YCrCb_420_SP, -1,
33 HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO, -1,
34 HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED, HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED,
35 HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS, HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS,
36 HAL_PIXEL_FORMAT_YV12, OMX_COLOR_FormatYUV420Planar,
37 0, 0
40 struct GraphicBufferAutoUnlock {
41 android::sp<GraphicBuffer> mGraphicBuffer;
43 GraphicBufferAutoUnlock(android::sp<GraphicBuffer>& aGraphicBuffer)
44 : mGraphicBuffer(aGraphicBuffer) { }
46 ~GraphicBufferAutoUnlock() { mGraphicBuffer->unlock(); }
49 GrallocImage::GrallocImage()
50 : PlanarYCbCrImage(nullptr)
52 mFormat = ImageFormat::GRALLOC_PLANAR_YCBCR;
55 GrallocImage::~GrallocImage()
59 void
60 GrallocImage::SetData(const Data& aData)
62 MOZ_ASSERT(!mTextureClient, "TextureClient is already set");
63 NS_PRECONDITION(aData.mYSize.width % 2 == 0, "Image should have even width");
64 NS_PRECONDITION(aData.mYSize.height % 2 == 0, "Image should have even height");
65 NS_PRECONDITION(aData.mYStride % 16 == 0, "Image should have stride of multiple of 16 pixels");
67 mData = aData;
68 mSize = aData.mPicSize;
70 if (gfxPlatform::GetPlatform()->IsInGonkEmulator()) {
71 // Emulator does not support HAL_PIXEL_FORMAT_YV12.
72 return;
75 RefPtr<GrallocTextureClientOGL> textureClient =
76 new GrallocTextureClientOGL(ImageBridgeChild::GetSingleton(),
77 gfx::SurfaceFormat::UNKNOWN,
78 gfx::BackendType::NONE);
79 // GrallocImages are all YUV and don't support alpha.
80 textureClient->SetIsOpaque(true);
81 bool result =
82 textureClient->AllocateGralloc(mData.mYSize,
83 HAL_PIXEL_FORMAT_YV12,
84 GraphicBuffer::USAGE_SW_READ_OFTEN |
85 GraphicBuffer::USAGE_SW_WRITE_OFTEN |
86 GraphicBuffer::USAGE_HW_TEXTURE);
87 sp<GraphicBuffer> graphicBuffer = textureClient->GetGraphicBuffer();
88 if (!result || !graphicBuffer.get()) {
89 mTextureClient = nullptr;
90 return;
93 mTextureClient = textureClient;
95 void* vaddr;
96 if (graphicBuffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN,
97 &vaddr) != OK) {
98 return;
101 uint8_t* yChannel = static_cast<uint8_t*>(vaddr);
102 gfx::IntSize ySize = aData.mYSize;
103 int32_t yStride = graphicBuffer->getStride();
105 uint8_t* vChannel = yChannel + (yStride * ySize.height);
106 gfx::IntSize uvSize = gfx::IntSize(ySize.width / 2,
107 ySize.height / 2);
108 // Align to 16 bytes boundary
109 int32_t uvStride = ((yStride / 2) + 15) & ~0x0F;
110 uint8_t* uChannel = vChannel + (uvStride * uvSize.height);
112 // Memory outside of the image width may not writable. If the stride
113 // equals to the image width then we can use only one copy.
114 if (yStride == mData.mYStride &&
115 yStride == ySize.width) {
116 memcpy(yChannel, mData.mYChannel, yStride * ySize.height);
117 } else {
118 for (int i = 0; i < ySize.height; i++) {
119 memcpy(yChannel + i * yStride,
120 mData.mYChannel + i * mData.mYStride,
121 ySize.width);
124 if (uvStride == mData.mCbCrStride &&
125 uvStride == uvSize.width) {
126 memcpy(uChannel, mData.mCbChannel, uvStride * uvSize.height);
127 memcpy(vChannel, mData.mCrChannel, uvStride * uvSize.height);
128 } else {
129 for (int i = 0; i < uvSize.height; i++) {
130 memcpy(uChannel + i * uvStride,
131 mData.mCbChannel + i * mData.mCbCrStride,
132 uvSize.width);
133 memcpy(vChannel + i * uvStride,
134 mData.mCrChannel + i * mData.mCbCrStride,
135 uvSize.width);
138 graphicBuffer->unlock();
139 // Initialze the channels' addresses.
140 // Do not cache the addresses when gralloc buffer is not locked.
141 // gralloc hal could map gralloc buffer only when the buffer is locked,
142 // though some gralloc hals implementation maps it when it is allocated.
143 mData.mYChannel = nullptr;
144 mData.mCrChannel = nullptr;
145 mData.mCbChannel = nullptr;
148 void GrallocImage::SetData(const GrallocData& aData)
150 mTextureClient = static_cast<GrallocTextureClientOGL*>(aData.mGraphicBuffer.get());
151 mSize = aData.mPicSize;
155 * Converts YVU420 semi planar frames to RGB565, possibly taking different
156 * stride values.
157 * Needed because the Android ColorConverter class assumes that the Y and UV
158 * channels have equal stride.
160 static void
161 ConvertYVU420SPToRGB565(void *aYData, uint32_t aYStride,
162 void *aUData, void *aVData, uint32_t aUVStride,
163 void *aOut,
164 uint32_t aWidth, uint32_t aHeight)
166 uint8_t *y = (uint8_t*)aYData;
167 bool isCbCr;
168 int8_t *uv;
170 if (aUData < aVData) {
171 // The color format is YCbCr
172 isCbCr = true;
173 uv = (int8_t*)aUData;
174 } else {
175 // The color format is YCrCb
176 isCbCr = false;
177 uv = (int8_t*)aVData;
180 uint16_t *rgb = (uint16_t*)aOut;
182 for (size_t i = 0; i < aHeight; i++) {
183 for (size_t j = 0; j < aWidth; j++) {
184 int8_t d, e;
186 if (isCbCr) {
187 d = uv[j & ~1] - 128;
188 e = uv[j | 1] - 128;
189 } else {
190 d = uv[j | 1] - 128;
191 e = uv[j & ~1] - 128;
194 // Constants taken from https://en.wikipedia.org/wiki/YUV
195 int32_t r = (298 * y[j] + 409 * e + 128) >> 11;
196 int32_t g = (298 * y[j] - 100 * d - 208 * e + 128) >> 10;
197 int32_t b = (298 * y[j] + 516 * d + 128) >> 11;
199 r = r > 0x1f ? 0x1f : r < 0 ? 0 : r;
200 g = g > 0x3f ? 0x3f : g < 0 ? 0 : g;
201 b = b > 0x1f ? 0x1f : b < 0 ? 0 : b;
203 *rgb++ = (uint16_t)(r << 11 | g << 5 | b);
206 y += aYStride;
207 if (i % 2) {
208 uv += aUVStride;
214 * Converts the format of vendor-specific YVU420(planar and semi-planar)
215 * with the help of GraphicBuffer::lockYCbCr. In this way, we can convert
216 * the YUV color format without awaring actual definition/enumeration
217 * of vendor formats.
219 static status_t
220 ConvertVendorYUVFormatToRGB565(android::sp<GraphicBuffer>& aBuffer,
221 gfx::DataSourceSurface *aSurface,
222 gfx::DataSourceSurface::MappedSurface *aMappedSurface)
224 status_t rv = BAD_VALUE;
226 #if ANDROID_VERSION >= 18
227 android_ycbcr ycbcr;
229 // Check if the vendor provides explicit addresses of Y/Cb/Cr buffer from lockYCbCr
230 rv = aBuffer->lockYCbCr(android::GraphicBuffer::USAGE_SW_READ_OFTEN, &ycbcr);
232 if (rv != OK) {
233 NS_WARNING("Couldn't lock graphic buffer using lockYCbCr()");
234 return rv;
237 GraphicBufferAutoUnlock unlock(aBuffer);
239 uint32_t width = aSurface->GetSize().width;
240 uint32_t height = aSurface->GetSize().height;
242 if (ycbcr.chroma_step == 2) {
243 // From the system/core/include/system/graphics.h
244 // @chroma_step is the distance in bytes from one chroma pixel value to
245 // the next. This is 2 bytes for semiplanar (because chroma values are
246 // interleaved and each chroma value is one byte) and 1 for planar.
247 ConvertYVU420SPToRGB565(ycbcr.y, ycbcr.ystride,
248 ycbcr.cb, ycbcr.cr, ycbcr.cstride,
249 aMappedSurface->mData,
250 width, height);
251 } else {
252 layers::PlanarYCbCrData ycbcrData;
253 ycbcrData.mYChannel = static_cast<uint8_t*>(ycbcr.y);
254 ycbcrData.mYStride = ycbcr.ystride;
255 ycbcrData.mYSize = aSurface->GetSize();
256 ycbcrData.mCbChannel = static_cast<uint8_t*>(ycbcr.cb);
257 ycbcrData.mCrChannel = static_cast<uint8_t*>(ycbcr.cr);
258 ycbcrData.mCbCrStride = ycbcr.cstride;
259 ycbcrData.mCbCrSize = aSurface->GetSize() / 2;
260 ycbcrData.mPicSize = aSurface->GetSize();
262 gfx::ConvertYCbCrToRGB(ycbcrData,
263 aSurface->GetFormat(),
264 aSurface->GetSize(),
265 aMappedSurface->mData,
266 aMappedSurface->mStride);
268 #endif
270 return rv;
273 static status_t
274 ConvertOmxYUVFormatToRGB565(android::sp<GraphicBuffer>& aBuffer,
275 gfx::DataSourceSurface *aSurface,
276 gfx::DataSourceSurface::MappedSurface *aMappedSurface,
277 const layers::PlanarYCbCrData& aYcbcrData,
278 int aOmxFormat)
280 if (!aOmxFormat) {
281 NS_WARNING("Unknown color format");
282 return BAD_VALUE;
285 status_t rv;
286 uint8_t *buffer;
288 rv = aBuffer->lock(android::GraphicBuffer::USAGE_SW_READ_OFTEN,
289 reinterpret_cast<void **>(&buffer));
290 if (rv != OK) {
291 NS_WARNING("Couldn't lock graphic buffer");
292 return BAD_VALUE;
295 GraphicBufferAutoUnlock unlock(aBuffer);
297 uint32_t format = aBuffer->getPixelFormat();
298 uint32_t width = aSurface->GetSize().width;
299 uint32_t height = aSurface->GetSize().height;
301 if (format == GrallocImage::HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO) {
302 // The Adreno hardware decoder aligns image dimensions to a multiple of 32,
303 // so we have to account for that here
304 uint32_t alignedWidth = ALIGN(width, 32);
305 uint32_t alignedHeight = ALIGN(height, 32);
306 uint32_t uvOffset = ALIGN(alignedHeight * alignedWidth, 4096);
307 uint32_t uvStride = 2 * ALIGN(width / 2, 32);
308 ConvertYVU420SPToRGB565(buffer, alignedWidth,
309 buffer + uvOffset + 1,
310 buffer + uvOffset,
311 uvStride,
312 aMappedSurface->mData,
313 width, height);
314 return OK;
317 if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP) {
318 uint32_t uvOffset = height * width;
319 ConvertYVU420SPToRGB565(buffer, width,
320 buffer + uvOffset + 1,
321 buffer + uvOffset,
322 width,
323 aMappedSurface->mData,
324 width, height);
325 return OK;
328 if (format == HAL_PIXEL_FORMAT_YV12) {
329 // Depend on platforms, it is possible for HW decoder to output YV12 format.
330 // It means the mData won't be configured during the SetData API because the
331 // yuv data has already stored in GraphicBuffer. Here we try to confgiure the
332 // mData if it doesn't contain valid configuration.
333 layers::PlanarYCbCrData ycbcrData = aYcbcrData;
334 if (!ycbcrData.mYChannel) {
335 ycbcrData.mYChannel = buffer;
336 ycbcrData.mYSkip = 0;
337 ycbcrData.mYStride = aBuffer->getStride();
338 ycbcrData.mYSize = aSurface->GetSize();
339 ycbcrData.mCbSkip = 0;
340 ycbcrData.mCbCrSize = aSurface->GetSize() / 2;
341 ycbcrData.mPicSize = aSurface->GetSize();
342 ycbcrData.mCrChannel = buffer + ycbcrData.mYStride * aBuffer->getHeight();
343 ycbcrData.mCrSkip = 0;
344 // Align to 16 bytes boundary
345 ycbcrData.mCbCrStride = ALIGN(ycbcrData.mYStride / 2, 16);
346 ycbcrData.mCbChannel = ycbcrData.mCrChannel + (ycbcrData.mCbCrStride * aBuffer->getHeight() / 2);
347 } else {
348 // Update channels' address.
349 // Gralloc buffer could map gralloc buffer only when the buffer is locked.
350 ycbcrData.mYChannel = buffer;
351 ycbcrData.mCrChannel = buffer + ycbcrData.mYStride * aBuffer->getHeight();
352 ycbcrData.mCbChannel = ycbcrData.mCrChannel + (ycbcrData.mCbCrStride * aBuffer->getHeight() / 2);
354 gfx::ConvertYCbCrToRGB(ycbcrData,
355 aSurface->GetFormat(),
356 aSurface->GetSize(),
357 aMappedSurface->mData,
358 aMappedSurface->mStride);
359 return OK;
362 android::ColorConverter colorConverter((OMX_COLOR_FORMATTYPE)aOmxFormat,
363 OMX_COLOR_Format16bitRGB565);
364 if (!colorConverter.isValid()) {
365 NS_WARNING("Invalid color conversion");
366 return BAD_VALUE;
369 rv = colorConverter.convert(buffer, width, height,
370 0, 0, width - 1, height - 1 /* source crop */,
371 aMappedSurface->mData, width, height,
372 0, 0, width - 1, height - 1 /* dest crop */);
373 if (rv) {
374 NS_WARNING("OMX color conversion failed");
375 return BAD_VALUE;
378 return OK;
381 TemporaryRef<gfx::SourceSurface>
382 GrallocImage::GetAsSourceSurface()
384 if (!mTextureClient) {
385 return nullptr;
388 android::sp<GraphicBuffer> graphicBuffer =
389 mTextureClient->GetGraphicBuffer();
391 RefPtr<gfx::DataSourceSurface> surface =
392 gfx::Factory::CreateDataSourceSurface(GetSize(), gfx::SurfaceFormat::R5G6B5);
393 if (NS_WARN_IF(!surface)) {
394 return nullptr;
397 gfx::DataSourceSurface::MappedSurface mappedSurface;
398 if (!surface->Map(gfx::DataSourceSurface::WRITE, &mappedSurface)) {
399 NS_WARNING("Could not map DataSourceSurface");
400 return nullptr;
403 int32_t rv;
404 uint32_t omxFormat = 0;
406 omxFormat = GrallocImage::GetOmxFormat(graphicBuffer->getPixelFormat());
407 if (!omxFormat) {
408 rv = ConvertVendorYUVFormatToRGB565(graphicBuffer, surface, &mappedSurface);
409 surface->Unmap();
411 if (rv != OK) {
412 NS_WARNING("Unknown color format");
413 return nullptr;
416 return surface;
419 rv = ConvertOmxYUVFormatToRGB565(graphicBuffer, surface, &mappedSurface, mData, omxFormat);
420 surface->Unmap();
422 if (rv != OK) {
423 return nullptr;
426 return surface;
429 android::sp<android::GraphicBuffer>
430 GrallocImage::GetGraphicBuffer() const
432 if (!mTextureClient) {
433 return nullptr;
435 return mTextureClient->GetGraphicBuffer();
438 void*
439 GrallocImage::GetNativeBuffer()
441 if (!mTextureClient) {
442 return nullptr;
444 android::sp<android::GraphicBuffer> graphicBuffer =
445 mTextureClient->GetGraphicBuffer();
446 if (!graphicBuffer.get()) {
447 return nullptr;
449 return graphicBuffer->getNativeBuffer();
452 TextureClient*
453 GrallocImage::GetTextureClient(CompositableClient* aClient)
455 return mTextureClient;
458 } // namespace layers
459 } // namespace mozilla