|
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/. */ |
|
6 |
|
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 |
|
15 |
|
16 #include <ColorConverter.h> |
|
17 #include <OMX_IVCommon.h> |
|
18 |
|
19 |
|
20 using namespace mozilla::ipc; |
|
21 using namespace android; |
|
22 |
|
23 #define ALIGN(x, align) ((x + align - 1) & ~(align - 1)) |
|
24 |
|
25 namespace mozilla { |
|
26 namespace layers { |
|
27 |
|
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 }; |
|
39 |
|
40 struct GraphicBufferAutoUnlock { |
|
41 android::sp<GraphicBuffer> mGraphicBuffer; |
|
42 |
|
43 GraphicBufferAutoUnlock(android::sp<GraphicBuffer>& aGraphicBuffer) |
|
44 : mGraphicBuffer(aGraphicBuffer) { } |
|
45 |
|
46 ~GraphicBufferAutoUnlock() { mGraphicBuffer->unlock(); } |
|
47 }; |
|
48 |
|
49 GrallocImage::GrallocImage() |
|
50 : PlanarYCbCrImage(nullptr) |
|
51 { |
|
52 mFormat = ImageFormat::GRALLOC_PLANAR_YCBCR; |
|
53 } |
|
54 |
|
55 GrallocImage::~GrallocImage() |
|
56 { |
|
57 } |
|
58 |
|
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"); |
|
66 |
|
67 mData = aData; |
|
68 mSize = aData.mPicSize; |
|
69 |
|
70 if (gfxPlatform::GetPlatform()->IsInGonkEmulator()) { |
|
71 // Emulator does not support HAL_PIXEL_FORMAT_YV12. |
|
72 return; |
|
73 } |
|
74 |
|
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 } |
|
90 |
|
91 mTextureClient = textureClient; |
|
92 |
|
93 void* vaddr; |
|
94 if (graphicBuffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, |
|
95 &vaddr) != OK) { |
|
96 return; |
|
97 } |
|
98 |
|
99 uint8_t* yChannel = static_cast<uint8_t*>(vaddr); |
|
100 gfx::IntSize ySize = aData.mYSize; |
|
101 int32_t yStride = graphicBuffer->getStride(); |
|
102 |
|
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); |
|
109 |
|
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 } |
|
138 |
|
139 void GrallocImage::SetData(const GrallocData& aData) |
|
140 { |
|
141 mTextureClient = static_cast<GrallocTextureClientOGL*>(aData.mGraphicBuffer.get()); |
|
142 mSize = aData.mPicSize; |
|
143 } |
|
144 |
|
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; |
|
159 |
|
160 uint16_t *rgb = (uint16_t*)aOut; |
|
161 |
|
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; |
|
166 |
|
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; |
|
171 |
|
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; |
|
175 |
|
176 *rgb++ = (uint16_t)(r << 11 | g << 5 | b); |
|
177 } |
|
178 |
|
179 y += aYStride; |
|
180 if (i % 2) { |
|
181 uv += aUVStride; |
|
182 } |
|
183 } |
|
184 } |
|
185 |
|
186 TemporaryRef<gfx::SourceSurface> |
|
187 GrallocImage::GetAsSourceSurface() |
|
188 { |
|
189 if (!mTextureClient) { |
|
190 return nullptr; |
|
191 } |
|
192 android::sp<GraphicBuffer> graphicBuffer = |
|
193 mTextureClient->GetGraphicBuffer(); |
|
194 |
|
195 void *buffer; |
|
196 int32_t rv = |
|
197 graphicBuffer->lock(android::GraphicBuffer::USAGE_SW_READ_OFTEN, &buffer); |
|
198 |
|
199 if (rv) { |
|
200 NS_WARNING("Couldn't lock graphic buffer"); |
|
201 return nullptr; |
|
202 } |
|
203 |
|
204 GraphicBufferAutoUnlock unlock(graphicBuffer); |
|
205 |
|
206 uint32_t format = graphicBuffer->getPixelFormat(); |
|
207 uint32_t omxFormat = 0; |
|
208 |
|
209 for (int i = 0; sColorIdMap[i]; i += 2) { |
|
210 if (sColorIdMap[i] == format) { |
|
211 omxFormat = sColorIdMap[i + 1]; |
|
212 break; |
|
213 } |
|
214 } |
|
215 |
|
216 if (!omxFormat) { |
|
217 NS_WARNING("Unknown color format"); |
|
218 return nullptr; |
|
219 } |
|
220 |
|
221 RefPtr<gfx::DataSourceSurface> surface |
|
222 = gfx::Factory::CreateDataSourceSurface(GetSize(), gfx::SurfaceFormat::R5G6B5); |
|
223 |
|
224 uint32_t width = GetSize().width; |
|
225 uint32_t height = GetSize().height; |
|
226 |
|
227 gfx::DataSourceSurface::MappedSurface mappedSurface; |
|
228 if (!surface->Map(gfx::DataSourceSurface::WRITE, &mappedSurface)) { |
|
229 NS_WARNING("Could not map DataSourceSurface"); |
|
230 return nullptr; |
|
231 } |
|
232 |
|
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); |
|
245 |
|
246 surface->Unmap(); |
|
247 return surface; |
|
248 } |
|
249 |
|
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); |
|
256 |
|
257 surface->Unmap(); |
|
258 return surface; |
|
259 } |
|
260 |
|
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 } |
|
270 |
|
271 android::ColorConverter colorConverter((OMX_COLOR_FORMATTYPE)omxFormat, |
|
272 OMX_COLOR_Format16bitRGB565); |
|
273 |
|
274 if (!colorConverter.isValid()) { |
|
275 NS_WARNING("Invalid color conversion"); |
|
276 surface->Unmap(); |
|
277 return nullptr; |
|
278 } |
|
279 |
|
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 */); |
|
284 |
|
285 surface->Unmap(); |
|
286 |
|
287 if (rv) { |
|
288 NS_WARNING("OMX color conversion failed"); |
|
289 return nullptr; |
|
290 } |
|
291 |
|
292 return surface; |
|
293 } |
|
294 |
|
295 android::sp<android::GraphicBuffer> |
|
296 GrallocImage::GetGraphicBuffer() const |
|
297 { |
|
298 if (!mTextureClient) { |
|
299 return nullptr; |
|
300 } |
|
301 return mTextureClient->GetGraphicBuffer(); |
|
302 } |
|
303 |
|
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 } |
|
317 |
|
318 TextureClient* |
|
319 GrallocImage::GetTextureClient(CompositableClient* aClient) |
|
320 { |
|
321 return mTextureClient; |
|
322 } |
|
323 |
|
324 } // namespace layers |
|
325 } // namespace mozilla |