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))
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
,
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()
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");
68 mSize
= aData
.mPicSize
;
70 if (gfxPlatform::GetPlatform()->IsInGonkEmulator()) {
71 // Emulator does not support HAL_PIXEL_FORMAT_YV12.
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);
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;
93 mTextureClient
= textureClient
;
96 if (graphicBuffer
->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN
,
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,
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
);
118 for (int i
= 0; i
< ySize
.height
; i
++) {
119 memcpy(yChannel
+ i
* yStride
,
120 mData
.mYChannel
+ i
* mData
.mYStride
,
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
);
129 for (int i
= 0; i
< uvSize
.height
; i
++) {
130 memcpy(uChannel
+ i
* uvStride
,
131 mData
.mCbChannel
+ i
* mData
.mCbCrStride
,
133 memcpy(vChannel
+ i
* uvStride
,
134 mData
.mCrChannel
+ i
* mData
.mCbCrStride
,
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
157 * Needed because the Android ColorConverter class assumes that the Y and UV
158 * channels have equal stride.
161 ConvertYVU420SPToRGB565(void *aYData
, uint32_t aYStride
,
162 void *aUData
, void *aVData
, uint32_t aUVStride
,
164 uint32_t aWidth
, uint32_t aHeight
)
166 uint8_t *y
= (uint8_t*)aYData
;
170 if (aUData
< aVData
) {
171 // The color format is YCbCr
173 uv
= (int8_t*)aUData
;
175 // The color format is YCrCb
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
++) {
187 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
);
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
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
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
);
233 NS_WARNING("Couldn't lock graphic buffer using lockYCbCr()");
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
,
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(),
265 aMappedSurface
->mData
,
266 aMappedSurface
->mStride
);
274 ConvertOmxYUVFormatToRGB565(android::sp
<GraphicBuffer
>& aBuffer
,
275 gfx::DataSourceSurface
*aSurface
,
276 gfx::DataSourceSurface::MappedSurface
*aMappedSurface
,
277 const layers::PlanarYCbCrData
& aYcbcrData
,
281 NS_WARNING("Unknown color format");
288 rv
= aBuffer
->lock(android::GraphicBuffer::USAGE_SW_READ_OFTEN
,
289 reinterpret_cast<void **>(&buffer
));
291 NS_WARNING("Couldn't lock graphic buffer");
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,
312 aMappedSurface
->mData
,
317 if (format
== HAL_PIXEL_FORMAT_YCrCb_420_SP
) {
318 uint32_t uvOffset
= height
* width
;
319 ConvertYVU420SPToRGB565(buffer
, width
,
320 buffer
+ uvOffset
+ 1,
323 aMappedSurface
->mData
,
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);
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(),
357 aMappedSurface
->mData
,
358 aMappedSurface
->mStride
);
362 android::ColorConverter
colorConverter((OMX_COLOR_FORMATTYPE
)aOmxFormat
,
363 OMX_COLOR_Format16bitRGB565
);
364 if (!colorConverter
.isValid()) {
365 NS_WARNING("Invalid color conversion");
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 */);
374 NS_WARNING("OMX color conversion failed");
381 TemporaryRef
<gfx::SourceSurface
>
382 GrallocImage::GetAsSourceSurface()
384 if (!mTextureClient
) {
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
)) {
397 gfx::DataSourceSurface::MappedSurface mappedSurface
;
398 if (!surface
->Map(gfx::DataSourceSurface::WRITE
, &mappedSurface
)) {
399 NS_WARNING("Could not map DataSourceSurface");
404 uint32_t omxFormat
= 0;
406 omxFormat
= GrallocImage::GetOmxFormat(graphicBuffer
->getPixelFormat());
408 rv
= ConvertVendorYUVFormatToRGB565(graphicBuffer
, surface
, &mappedSurface
);
412 NS_WARNING("Unknown color format");
419 rv
= ConvertOmxYUVFormatToRGB565(graphicBuffer
, surface
, &mappedSurface
, mData
, omxFormat
);
429 android::sp
<android::GraphicBuffer
>
430 GrallocImage::GetGraphicBuffer() const
432 if (!mTextureClient
) {
435 return mTextureClient
->GetGraphicBuffer();
439 GrallocImage::GetNativeBuffer()
441 if (!mTextureClient
) {
444 android::sp
<android::GraphicBuffer
> graphicBuffer
=
445 mTextureClient
->GetGraphicBuffer();
446 if (!graphicBuffer
.get()) {
449 return graphicBuffer
->getNativeBuffer();
453 GrallocImage::GetTextureClient(CompositableClient
* aClient
)
455 return mTextureClient
;
458 } // namespace layers
459 } // namespace mozilla