Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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
38 };
40 struct GraphicBufferAutoUnlock {
41 android::sp<GraphicBuffer> mGraphicBuffer;
43 GraphicBufferAutoUnlock(android::sp<GraphicBuffer>& aGraphicBuffer)
44 : mGraphicBuffer(aGraphicBuffer) { }
46 ~GraphicBufferAutoUnlock() { mGraphicBuffer->unlock(); }
47 };
49 GrallocImage::GrallocImage()
50 : PlanarYCbCrImage(nullptr)
51 {
52 mFormat = ImageFormat::GRALLOC_PLANAR_YCBCR;
53 }
55 GrallocImage::~GrallocImage()
56 {
57 }
59 void
60 GrallocImage::SetData(const Data& aData)
61 {
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;
73 }
75 RefPtr<GrallocTextureClientOGL> textureClient =
76 new GrallocTextureClientOGL(ImageBridgeChild::GetSingleton(),
77 gfx::SurfaceFormat::UNKNOWN,
78 gfx::BackendType::NONE);
79 bool result =
80 textureClient->AllocateGralloc(mData.mYSize,
81 HAL_PIXEL_FORMAT_YV12,
82 GraphicBuffer::USAGE_SW_READ_OFTEN |
83 GraphicBuffer::USAGE_SW_WRITE_OFTEN |
84 GraphicBuffer::USAGE_HW_TEXTURE);
85 sp<GraphicBuffer> graphicBuffer = textureClient->GetGraphicBuffer();
86 if (!result || !graphicBuffer.get()) {
87 mTextureClient = nullptr;
88 return;
89 }
91 mTextureClient = textureClient;
93 void* vaddr;
94 if (graphicBuffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN,
95 &vaddr) != OK) {
96 return;
97 }
99 uint8_t* yChannel = static_cast<uint8_t*>(vaddr);
100 gfx::IntSize ySize = aData.mYSize;
101 int32_t yStride = graphicBuffer->getStride();
103 uint8_t* vChannel = yChannel + (yStride * ySize.height);
104 gfx::IntSize uvSize = gfx::IntSize(ySize.width / 2,
105 ySize.height / 2);
106 // Align to 16 bytes boundary
107 int32_t uvStride = ((yStride / 2) + 15) & ~0x0F;
108 uint8_t* uChannel = vChannel + (uvStride * uvSize.height);
110 // Memory outside of the image width may not writable. If the stride
111 // equals to the image width then we can use only one copy.
112 if (yStride == mData.mYStride &&
113 yStride == ySize.width) {
114 memcpy(yChannel, mData.mYChannel, yStride * ySize.height);
115 } else {
116 for (int i = 0; i < ySize.height; i++) {
117 memcpy(yChannel + i * yStride,
118 mData.mYChannel + i * mData.mYStride,
119 ySize.width);
120 }
121 }
122 if (uvStride == mData.mCbCrStride &&
123 uvStride == uvSize.width) {
124 memcpy(uChannel, mData.mCbChannel, uvStride * uvSize.height);
125 memcpy(vChannel, mData.mCrChannel, uvStride * uvSize.height);
126 } else {
127 for (int i = 0; i < uvSize.height; i++) {
128 memcpy(uChannel + i * uvStride,
129 mData.mCbChannel + i * mData.mCbCrStride,
130 uvSize.width);
131 memcpy(vChannel + i * uvStride,
132 mData.mCrChannel + i * mData.mCbCrStride,
133 uvSize.width);
134 }
135 }
136 graphicBuffer->unlock();
137 }
139 void GrallocImage::SetData(const GrallocData& aData)
140 {
141 mTextureClient = static_cast<GrallocTextureClientOGL*>(aData.mGraphicBuffer.get());
142 mSize = aData.mPicSize;
143 }
145 /**
146 * Converts YVU420 semi planar frames to RGB565, possibly taking different
147 * stride values.
148 * Needed because the Android ColorConverter class assumes that the Y and UV
149 * channels have equal stride.
150 */
151 static void
152 ConvertYVU420SPToRGB565(void *aYData, uint32_t aYStride,
153 void *aUVData, uint32_t aUVStride,
154 void *aOut,
155 uint32_t aWidth, uint32_t aHeight)
156 {
157 uint8_t *y = (uint8_t*)aYData;
158 int8_t *uv = (int8_t*)aUVData;
160 uint16_t *rgb = (uint16_t*)aOut;
162 for (size_t i = 0; i < aHeight; i++) {
163 for (size_t j = 0; j < aWidth; j++) {
164 int8_t d = uv[j | 1] - 128;
165 int8_t e = uv[j & ~1] - 128;
167 // Constants taken from https://en.wikipedia.org/wiki/YUV
168 int32_t r = (298 * y[j] + 409 * e + 128) >> 11;
169 int32_t g = (298 * y[j] - 100 * d - 208 * e + 128) >> 10;
170 int32_t b = (298 * y[j] + 516 * d + 128) >> 11;
172 r = r > 0x1f ? 0x1f : r < 0 ? 0 : r;
173 g = g > 0x3f ? 0x3f : g < 0 ? 0 : g;
174 b = b > 0x1f ? 0x1f : b < 0 ? 0 : b;
176 *rgb++ = (uint16_t)(r << 11 | g << 5 | b);
177 }
179 y += aYStride;
180 if (i % 2) {
181 uv += aUVStride;
182 }
183 }
184 }
186 TemporaryRef<gfx::SourceSurface>
187 GrallocImage::GetAsSourceSurface()
188 {
189 if (!mTextureClient) {
190 return nullptr;
191 }
192 android::sp<GraphicBuffer> graphicBuffer =
193 mTextureClient->GetGraphicBuffer();
195 void *buffer;
196 int32_t rv =
197 graphicBuffer->lock(android::GraphicBuffer::USAGE_SW_READ_OFTEN, &buffer);
199 if (rv) {
200 NS_WARNING("Couldn't lock graphic buffer");
201 return nullptr;
202 }
204 GraphicBufferAutoUnlock unlock(graphicBuffer);
206 uint32_t format = graphicBuffer->getPixelFormat();
207 uint32_t omxFormat = 0;
209 for (int i = 0; sColorIdMap[i]; i += 2) {
210 if (sColorIdMap[i] == format) {
211 omxFormat = sColorIdMap[i + 1];
212 break;
213 }
214 }
216 if (!omxFormat) {
217 NS_WARNING("Unknown color format");
218 return nullptr;
219 }
221 RefPtr<gfx::DataSourceSurface> surface
222 = gfx::Factory::CreateDataSourceSurface(GetSize(), gfx::SurfaceFormat::R5G6B5);
224 uint32_t width = GetSize().width;
225 uint32_t height = GetSize().height;
227 gfx::DataSourceSurface::MappedSurface mappedSurface;
228 if (!surface->Map(gfx::DataSourceSurface::WRITE, &mappedSurface)) {
229 NS_WARNING("Could not map DataSourceSurface");
230 return nullptr;
231 }
233 if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO) {
234 // The Adreno hardware decoder aligns image dimensions to a multiple of 32,
235 // so we have to account for that here
236 uint32_t alignedWidth = ALIGN(width, 32);
237 uint32_t alignedHeight = ALIGN(height, 32);
238 uint32_t uvOffset = ALIGN(alignedHeight * alignedWidth, 4096);
239 uint32_t uvStride = 2 * ALIGN(width / 2, 32);
240 uint8_t* buffer_as_bytes = static_cast<uint8_t*>(buffer);
241 ConvertYVU420SPToRGB565(buffer, alignedWidth,
242 buffer_as_bytes + uvOffset, uvStride,
243 mappedSurface.mData,
244 width, height);
246 surface->Unmap();
247 return surface;
248 }
250 if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP) {
251 uint32_t uvOffset = height * width;
252 ConvertYVU420SPToRGB565(buffer, width,
253 buffer + uvOffset, width,
254 mappedSurface.mData,
255 width, height);
257 surface->Unmap();
258 return surface;
259 }
261 if (format == HAL_PIXEL_FORMAT_YV12) {
262 gfx::ConvertYCbCrToRGB(mData,
263 surface->GetFormat(),
264 mSize,
265 surface->GetData(),
266 surface->Stride());
267 surface->Unmap();
268 return surface;
269 }
271 android::ColorConverter colorConverter((OMX_COLOR_FORMATTYPE)omxFormat,
272 OMX_COLOR_Format16bitRGB565);
274 if (!colorConverter.isValid()) {
275 NS_WARNING("Invalid color conversion");
276 surface->Unmap();
277 return nullptr;
278 }
280 rv = colorConverter.convert(buffer, width, height,
281 0, 0, width - 1, height - 1 /* source crop */,
282 mappedSurface.mData, width, height,
283 0, 0, width - 1, height - 1 /* dest crop */);
285 surface->Unmap();
287 if (rv) {
288 NS_WARNING("OMX color conversion failed");
289 return nullptr;
290 }
292 return surface;
293 }
295 android::sp<android::GraphicBuffer>
296 GrallocImage::GetGraphicBuffer() const
297 {
298 if (!mTextureClient) {
299 return nullptr;
300 }
301 return mTextureClient->GetGraphicBuffer();
302 }
304 void*
305 GrallocImage::GetNativeBuffer()
306 {
307 if (!mTextureClient) {
308 return nullptr;
309 }
310 android::sp<android::GraphicBuffer> graphicBuffer =
311 mTextureClient->GetGraphicBuffer();
312 if (!graphicBuffer.get()) {
313 return nullptr;
314 }
315 return graphicBuffer->getNativeBuffer();
316 }
318 TextureClient*
319 GrallocImage::GetTextureClient(CompositableClient* aClient)
320 {
321 return mTextureClient;
322 }
324 } // namespace layers
325 } // namespace mozilla