1 /*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "ColorConverter"
19 #include <android-base/macros.h>
20 #include <utils/Log.h>
21
22 #include <media/stagefright/foundation/ADebug.h>
23 #include <media/stagefright/foundation/ALooper.h>
24 #include <media/stagefright/foundation/ColorUtils.h>
25 #include <media/stagefright/ColorConverter.h>
26 #include <media/stagefright/MediaErrors.h>
27
28 #include "libyuv/convert_from.h"
29 #include "libyuv/convert_argb.h"
30 #include "libyuv/planar_functions.h"
31 #include "libyuv/video_common.h"
32 #include <functional>
33 #include <sys/time.h>
34
35 #define USE_LIBYUV
36 #define PERF_PROFILING 0
37
38
39 #if defined(__aarch64__) || defined(__ARM_NEON__)
40 #define USE_NEON_Y410 1
41 #else
42 #define USE_NEON_Y410 0
43 #endif
44
45 #if USE_NEON_Y410
46 #include <arm_neon.h>
47 #endif
48
49 namespace android {
50
isRGB(OMX_COLOR_FORMATTYPE colorFormat)51 static bool isRGB(OMX_COLOR_FORMATTYPE colorFormat) {
52 return colorFormat == OMX_COLOR_Format16bitRGB565
53 || colorFormat == OMX_COLOR_Format32BitRGBA8888
54 || colorFormat == OMX_COLOR_Format32bitBGRA8888;
55 }
56
isBt709()57 bool ColorConverter::ColorSpace::isBt709() {
58 return (mStandard == ColorUtils::kColorStandardBT709);
59 }
60
61
isJpeg()62 bool ColorConverter::ColorSpace::isJpeg() {
63 return ((mStandard == ColorUtils::kColorStandardBT601_625)
64 || (mStandard == ColorUtils::kColorStandardBT601_525))
65 && (mRange == ColorUtils::kColorRangeFull);
66 }
67
ColorConverter(OMX_COLOR_FORMATTYPE from,OMX_COLOR_FORMATTYPE to)68 ColorConverter::ColorConverter(
69 OMX_COLOR_FORMATTYPE from, OMX_COLOR_FORMATTYPE to)
70 : mSrcFormat(from),
71 mDstFormat(to),
72 mSrcColorSpace({0, 0, 0}),
73 mClip(NULL) {
74 }
75
~ColorConverter()76 ColorConverter::~ColorConverter() {
77 delete[] mClip;
78 mClip = NULL;
79 }
80
isValid() const81 bool ColorConverter::isValid() const {
82 switch (mSrcFormat) {
83 case OMX_COLOR_FormatYUV420Planar16:
84 if (mDstFormat == OMX_COLOR_FormatYUV444Y410) {
85 return true;
86 }
87 FALLTHROUGH_INTENDED;
88 case OMX_COLOR_FormatYUV420Planar:
89 return mDstFormat == OMX_COLOR_Format16bitRGB565
90 || mDstFormat == OMX_COLOR_Format32BitRGBA8888
91 || mDstFormat == OMX_COLOR_Format32bitBGRA8888;
92
93 case OMX_COLOR_FormatCbYCrY:
94 case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
95 case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar:
96 return mDstFormat == OMX_COLOR_Format16bitRGB565;
97
98 case OMX_COLOR_FormatYUV420SemiPlanar:
99 #ifdef USE_LIBYUV
100 return mDstFormat == OMX_COLOR_Format16bitRGB565
101 || mDstFormat == OMX_COLOR_Format32BitRGBA8888;
102 #else
103 return mDstFormat == OMX_COLOR_Format16bitRGB565;
104 #endif
105
106 default:
107 return false;
108 }
109 }
110
isDstRGB() const111 bool ColorConverter::isDstRGB() const {
112 return isRGB(mDstFormat);
113 }
114
setSrcColorSpace(uint32_t standard,uint32_t range,uint32_t transfer)115 void ColorConverter::setSrcColorSpace(
116 uint32_t standard, uint32_t range, uint32_t transfer) {
117 if (isRGB(mSrcFormat)) {
118 ALOGW("Can't set color space on RGB source");
119 return;
120 }
121 mSrcColorSpace.mStandard = standard;
122 mSrcColorSpace.mRange = range;
123 mSrcColorSpace.mTransfer = transfer;
124 }
125
126 /*
127 * If stride is non-zero, client's stride will be used. For planar
128 * or semi-planar YUV formats, stride must be even numbers.
129 * If stride is zero, it will be calculated based on width and bpp
130 * of the format, assuming no padding on the right edge.
131 */
BitmapParams(void * bits,size_t width,size_t height,size_t stride,size_t cropLeft,size_t cropTop,size_t cropRight,size_t cropBottom,OMX_COLOR_FORMATTYPE colorFromat)132 ColorConverter::BitmapParams::BitmapParams(
133 void *bits,
134 size_t width, size_t height, size_t stride,
135 size_t cropLeft, size_t cropTop,
136 size_t cropRight, size_t cropBottom,
137 OMX_COLOR_FORMATTYPE colorFromat)
138 : mBits(bits),
139 mColorFormat(colorFromat),
140 mWidth(width),
141 mHeight(height),
142 mCropLeft(cropLeft),
143 mCropTop(cropTop),
144 mCropRight(cropRight),
145 mCropBottom(cropBottom) {
146 switch(mColorFormat) {
147 case OMX_COLOR_Format16bitRGB565:
148 case OMX_COLOR_FormatYUV420Planar16:
149 case OMX_COLOR_FormatCbYCrY:
150 mBpp = 2;
151 mStride = 2 * mWidth;
152 break;
153
154 case OMX_COLOR_Format32bitBGRA8888:
155 case OMX_COLOR_Format32BitRGBA8888:
156 case OMX_COLOR_FormatYUV444Y410:
157 mBpp = 4;
158 mStride = 4 * mWidth;
159 break;
160
161 case OMX_COLOR_FormatYUV420Planar:
162 case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
163 case OMX_COLOR_FormatYUV420SemiPlanar:
164 case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar:
165 mBpp = 1;
166 mStride = mWidth;
167 break;
168
169 default:
170 ALOGE("Unsupported color format %d", mColorFormat);
171 mBpp = 1;
172 mStride = mWidth;
173 break;
174 }
175 // use client's stride if it's specified.
176 if (stride != 0) {
177 mStride = stride;
178 }
179 }
180
cropWidth() const181 size_t ColorConverter::BitmapParams::cropWidth() const {
182 return mCropRight - mCropLeft + 1;
183 }
184
cropHeight() const185 size_t ColorConverter::BitmapParams::cropHeight() const {
186 return mCropBottom - mCropTop + 1;
187 }
188
convert(const void * srcBits,size_t srcWidth,size_t srcHeight,size_t srcStride,size_t srcCropLeft,size_t srcCropTop,size_t srcCropRight,size_t srcCropBottom,void * dstBits,size_t dstWidth,size_t dstHeight,size_t dstStride,size_t dstCropLeft,size_t dstCropTop,size_t dstCropRight,size_t dstCropBottom)189 status_t ColorConverter::convert(
190 const void *srcBits,
191 size_t srcWidth, size_t srcHeight, size_t srcStride,
192 size_t srcCropLeft, size_t srcCropTop,
193 size_t srcCropRight, size_t srcCropBottom,
194 void *dstBits,
195 size_t dstWidth, size_t dstHeight, size_t dstStride,
196 size_t dstCropLeft, size_t dstCropTop,
197 size_t dstCropRight, size_t dstCropBottom) {
198 BitmapParams src(
199 const_cast<void *>(srcBits),
200 srcWidth, srcHeight, srcStride,
201 srcCropLeft, srcCropTop, srcCropRight, srcCropBottom, mSrcFormat);
202
203 BitmapParams dst(
204 dstBits,
205 dstWidth, dstHeight, dstStride,
206 dstCropLeft, dstCropTop, dstCropRight, dstCropBottom, mDstFormat);
207
208 if (!((src.mCropLeft & 1) == 0
209 && src.cropWidth() == dst.cropWidth()
210 && src.cropHeight() == dst.cropHeight())) {
211 return ERROR_UNSUPPORTED;
212 }
213
214 status_t err;
215
216 switch (mSrcFormat) {
217 case OMX_COLOR_FormatYUV420Planar:
218 #ifdef USE_LIBYUV
219 err = convertYUV420PlanarUseLibYUV(src, dst);
220 #else
221 err = convertYUV420Planar(src, dst);
222 #endif
223 break;
224
225 case OMX_COLOR_FormatYUV420Planar16:
226 {
227 #if PERF_PROFILING
228 int64_t startTimeUs = ALooper::GetNowUs();
229 #endif
230 err = convertYUV420Planar16(src, dst);
231 #if PERF_PROFILING
232 int64_t endTimeUs = ALooper::GetNowUs();
233 ALOGD("convertYUV420Planar16 took %lld us", (long long) (endTimeUs - startTimeUs));
234 #endif
235 break;
236 }
237
238 case OMX_COLOR_FormatCbYCrY:
239 err = convertCbYCrY(src, dst);
240 break;
241
242 case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
243 err = convertQCOMYUV420SemiPlanar(src, dst);
244 break;
245
246 case OMX_COLOR_FormatYUV420SemiPlanar:
247 #ifdef USE_LIBYUV
248 err = convertYUV420SemiPlanarUseLibYUV(src, dst);
249 #else
250 err = convertYUV420SemiPlanar(src, dst);
251 #endif
252 break;
253
254 case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar:
255 err = convertTIYUV420PackedSemiPlanar(src, dst);
256 break;
257
258 default:
259 {
260 CHECK(!"Should not be here. Unknown color conversion.");
261 break;
262 }
263 }
264
265 return err;
266 }
267
convertCbYCrY(const BitmapParams & src,const BitmapParams & dst)268 status_t ColorConverter::convertCbYCrY(
269 const BitmapParams &src, const BitmapParams &dst) {
270 // XXX Untested
271
272 uint8_t *kAdjustedClip = initClip();
273
274 uint16_t *dst_ptr = (uint16_t *)dst.mBits
275 + dst.mCropTop * dst.mWidth + dst.mCropLeft;
276
277 const uint8_t *src_ptr = (const uint8_t *)src.mBits
278 + (src.mCropTop * dst.mWidth + src.mCropLeft) * 2;
279
280 for (size_t y = 0; y < src.cropHeight(); ++y) {
281 for (size_t x = 0; x < src.cropWidth(); x += 2) {
282 signed y1 = (signed)src_ptr[2 * x + 1] - 16;
283 signed y2 = (signed)src_ptr[2 * x + 3] - 16;
284 signed u = (signed)src_ptr[2 * x] - 128;
285 signed v = (signed)src_ptr[2 * x + 2] - 128;
286
287 signed u_b = u * 517;
288 signed u_g = -u * 100;
289 signed v_g = -v * 208;
290 signed v_r = v * 409;
291
292 signed tmp1 = y1 * 298;
293 signed b1 = (tmp1 + u_b) / 256;
294 signed g1 = (tmp1 + v_g + u_g) / 256;
295 signed r1 = (tmp1 + v_r) / 256;
296
297 signed tmp2 = y2 * 298;
298 signed b2 = (tmp2 + u_b) / 256;
299 signed g2 = (tmp2 + v_g + u_g) / 256;
300 signed r2 = (tmp2 + v_r) / 256;
301
302 uint32_t rgb1 =
303 ((kAdjustedClip[r1] >> 3) << 11)
304 | ((kAdjustedClip[g1] >> 2) << 5)
305 | (kAdjustedClip[b1] >> 3);
306
307 uint32_t rgb2 =
308 ((kAdjustedClip[r2] >> 3) << 11)
309 | ((kAdjustedClip[g2] >> 2) << 5)
310 | (kAdjustedClip[b2] >> 3);
311
312 if (x + 1 < src.cropWidth()) {
313 *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1;
314 } else {
315 dst_ptr[x] = rgb1;
316 }
317 }
318
319 src_ptr += src.mWidth * 2;
320 dst_ptr += dst.mWidth;
321 }
322
323 return OK;
324 }
325
326 #define DECLARE_YUV2RGBFUNC(func, rgb) int (*func)( \
327 const uint8*, int, const uint8*, int, \
328 const uint8*, int, uint8*, int, int, int) \
329 = mSrcColorSpace.isBt709() ? libyuv::H420To##rgb \
330 : mSrcColorSpace.isJpeg() ? libyuv::J420To##rgb \
331 : libyuv::I420To##rgb
332
convertYUV420PlanarUseLibYUV(const BitmapParams & src,const BitmapParams & dst)333 status_t ColorConverter::convertYUV420PlanarUseLibYUV(
334 const BitmapParams &src, const BitmapParams &dst) {
335 uint8_t *dst_ptr = (uint8_t *)dst.mBits
336 + dst.mCropTop * dst.mStride + dst.mCropLeft * dst.mBpp;
337
338 const uint8_t *src_y =
339 (const uint8_t *)src.mBits + src.mCropTop * src.mStride + src.mCropLeft;
340
341 const uint8_t *src_u =
342 (const uint8_t *)src.mBits + src.mStride * src.mHeight
343 + (src.mCropTop / 2) * (src.mStride / 2) + (src.mCropLeft / 2);
344
345 const uint8_t *src_v =
346 src_u + (src.mStride / 2) * (src.mHeight / 2);
347
348 switch (mDstFormat) {
349 case OMX_COLOR_Format16bitRGB565:
350 {
351 DECLARE_YUV2RGBFUNC(func, RGB565);
352 (*func)(src_y, src.mStride, src_u, src.mStride / 2, src_v, src.mStride / 2,
353 (uint8 *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight());
354 break;
355 }
356
357 case OMX_COLOR_Format32BitRGBA8888:
358 {
359 DECLARE_YUV2RGBFUNC(func, ABGR);
360 (*func)(src_y, src.mStride, src_u, src.mStride / 2, src_v, src.mStride / 2,
361 (uint8 *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight());
362 break;
363 }
364
365 case OMX_COLOR_Format32bitBGRA8888:
366 {
367 DECLARE_YUV2RGBFUNC(func, ARGB);
368 (*func)(src_y, src.mStride, src_u, src.mStride / 2, src_v, src.mStride / 2,
369 (uint8 *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight());
370 break;
371 }
372
373 default:
374 return ERROR_UNSUPPORTED;
375 }
376
377 return OK;
378 }
379
convertYUV420SemiPlanarUseLibYUV(const BitmapParams & src,const BitmapParams & dst)380 status_t ColorConverter::convertYUV420SemiPlanarUseLibYUV(
381 const BitmapParams &src, const BitmapParams &dst) {
382 uint8_t *dst_ptr = (uint8_t *)dst.mBits
383 + dst.mCropTop * dst.mStride + dst.mCropLeft * dst.mBpp;
384
385 const uint8_t *src_y =
386 (const uint8_t *)src.mBits + src.mCropTop * src.mStride + src.mCropLeft;
387
388 const uint8_t *src_u =
389 (const uint8_t *)src.mBits + src.mStride * src.mHeight
390 + (src.mCropTop / 2) * src.mStride + src.mCropLeft;
391
392 switch (mDstFormat) {
393 case OMX_COLOR_Format16bitRGB565:
394 libyuv::NV12ToRGB565(src_y, src.mStride, src_u, src.mStride, (uint8 *)dst_ptr,
395 dst.mStride, src.cropWidth(), src.cropHeight());
396 break;
397
398 case OMX_COLOR_Format32bitBGRA8888:
399 libyuv::NV12ToARGB(src_y, src.mStride, src_u, src.mStride, (uint8 *)dst_ptr,
400 dst.mStride, src.cropWidth(), src.cropHeight());
401 break;
402
403 case OMX_COLOR_Format32BitRGBA8888:
404 libyuv::NV12ToABGR(src_y, src.mStride, src_u, src.mStride, (uint8 *)dst_ptr,
405 dst.mStride, src.cropWidth(), src.cropHeight());
406 break;
407
408 default:
409 return ERROR_UNSUPPORTED;
410 }
411
412 return OK;
413 }
414
415 std::function<void (void *, void *, void *, size_t,
416 signed *, signed *, signed *, signed *)>
getReadFromSrc(OMX_COLOR_FORMATTYPE srcFormat)417 getReadFromSrc(OMX_COLOR_FORMATTYPE srcFormat) {
418 switch(srcFormat) {
419 case OMX_COLOR_FormatYUV420Planar:
420 return [](void *src_y, void *src_u, void *src_v, size_t x,
421 signed *y1, signed *y2, signed *u, signed *v) {
422 *y1 = ((uint8_t*)src_y)[x] - 16;
423 *y2 = ((uint8_t*)src_y)[x + 1] - 16;
424 *u = ((uint8_t*)src_u)[x / 2] - 128;
425 *v = ((uint8_t*)src_v)[x / 2] - 128;
426 };
427 case OMX_COLOR_FormatYUV420Planar16:
428 return [](void *src_y, void *src_u, void *src_v, size_t x,
429 signed *y1, signed *y2, signed *u, signed *v) {
430 *y1 = (signed)(((uint16_t*)src_y)[x] >> 2) - 16;
431 *y2 = (signed)(((uint16_t*)src_y)[x + 1] >> 2) - 16;
432 *u = (signed)(((uint16_t*)src_u)[x / 2] >> 2) - 128;
433 *v = (signed)(((uint16_t*)src_v)[x / 2] >> 2) - 128;
434 };
435 default:
436 TRESPASS();
437 }
438 return nullptr;
439 }
440
441 std::function<void (void *, bool, signed, signed, signed, signed, signed, signed)>
getWriteToDst(OMX_COLOR_FORMATTYPE dstFormat,uint8_t * kAdjustedClip)442 getWriteToDst(OMX_COLOR_FORMATTYPE dstFormat, uint8_t *kAdjustedClip) {
443 switch (dstFormat) {
444 case OMX_COLOR_Format16bitRGB565:
445 {
446 return [kAdjustedClip](void *dst_ptr, bool uncropped,
447 signed r1, signed g1, signed b1,
448 signed r2, signed g2, signed b2) {
449 uint32_t rgb1 =
450 ((kAdjustedClip[r1] >> 3) << 11)
451 | ((kAdjustedClip[g1] >> 2) << 5)
452 | (kAdjustedClip[b1] >> 3);
453
454 if (uncropped) {
455 uint32_t rgb2 =
456 ((kAdjustedClip[r2] >> 3) << 11)
457 | ((kAdjustedClip[g2] >> 2) << 5)
458 | (kAdjustedClip[b2] >> 3);
459
460 *(uint32_t *)dst_ptr = (rgb2 << 16) | rgb1;
461 } else {
462 *(uint16_t *)dst_ptr = rgb1;
463 }
464 };
465 }
466 case OMX_COLOR_Format32BitRGBA8888:
467 {
468 return [kAdjustedClip](void *dst_ptr, bool uncropped,
469 signed r1, signed g1, signed b1,
470 signed r2, signed g2, signed b2) {
471 ((uint32_t *)dst_ptr)[0] =
472 (kAdjustedClip[r1])
473 | (kAdjustedClip[g1] << 8)
474 | (kAdjustedClip[b1] << 16)
475 | (0xFF << 24);
476
477 if (uncropped) {
478 ((uint32_t *)dst_ptr)[1] =
479 (kAdjustedClip[r2])
480 | (kAdjustedClip[g2] << 8)
481 | (kAdjustedClip[b2] << 16)
482 | (0xFF << 24);
483 }
484 };
485 }
486 case OMX_COLOR_Format32bitBGRA8888:
487 {
488 return [kAdjustedClip](void *dst_ptr, bool uncropped,
489 signed r1, signed g1, signed b1,
490 signed r2, signed g2, signed b2) {
491 ((uint32_t *)dst_ptr)[0] =
492 (kAdjustedClip[b1])
493 | (kAdjustedClip[g1] << 8)
494 | (kAdjustedClip[r1] << 16)
495 | (0xFF << 24);
496
497 if (uncropped) {
498 ((uint32_t *)dst_ptr)[1] =
499 (kAdjustedClip[b2])
500 | (kAdjustedClip[g2] << 8)
501 | (kAdjustedClip[r2] << 16)
502 | (0xFF << 24);
503 }
504 };
505 }
506 default:
507 TRESPASS();
508 }
509 return nullptr;
510 }
511
convertYUV420Planar(const BitmapParams & src,const BitmapParams & dst)512 status_t ColorConverter::convertYUV420Planar(
513 const BitmapParams &src, const BitmapParams &dst) {
514 uint8_t *kAdjustedClip = initClip();
515
516 auto readFromSrc = getReadFromSrc(mSrcFormat);
517 auto writeToDst = getWriteToDst(mDstFormat, kAdjustedClip);
518
519 uint8_t *dst_ptr = (uint8_t *)dst.mBits
520 + dst.mCropTop * dst.mStride + dst.mCropLeft * dst.mBpp;
521
522 uint8_t *src_y = (uint8_t *)src.mBits
523 + src.mCropTop * src.mStride + src.mCropLeft * src.mBpp;
524
525 uint8_t *src_u = (uint8_t *)src.mBits + src.mStride * src.mHeight
526 + (src.mCropTop / 2) * (src.mStride / 2) + src.mCropLeft / 2 * src.mBpp;
527
528 uint8_t *src_v = src_u + (src.mStride / 2) * (src.mHeight / 2);
529
530 for (size_t y = 0; y < src.cropHeight(); ++y) {
531 for (size_t x = 0; x < src.cropWidth(); x += 2) {
532 // B = 1.164 * (Y - 16) + 2.018 * (U - 128)
533 // G = 1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128)
534 // R = 1.164 * (Y - 16) + 1.596 * (V - 128)
535
536 // B = 298/256 * (Y - 16) + 517/256 * (U - 128)
537 // G = .................. - 208/256 * (V - 128) - 100/256 * (U - 128)
538 // R = .................. + 409/256 * (V - 128)
539
540 // min_B = (298 * (- 16) + 517 * (- 128)) / 256 = -277
541 // min_G = (298 * (- 16) - 208 * (255 - 128) - 100 * (255 - 128)) / 256 = -172
542 // min_R = (298 * (- 16) + 409 * (- 128)) / 256 = -223
543
544 // max_B = (298 * (255 - 16) + 517 * (255 - 128)) / 256 = 534
545 // max_G = (298 * (255 - 16) - 208 * (- 128) - 100 * (- 128)) / 256 = 432
546 // max_R = (298 * (255 - 16) + 409 * (255 - 128)) / 256 = 481
547
548 // clip range -278 .. 535
549
550 signed y1, y2, u, v;
551 readFromSrc(src_y, src_u, src_v, x, &y1, &y2, &u, &v);
552
553 signed u_b = u * 517;
554 signed u_g = -u * 100;
555 signed v_g = -v * 208;
556 signed v_r = v * 409;
557
558 signed tmp1 = y1 * 298;
559 signed b1 = (tmp1 + u_b) / 256;
560 signed g1 = (tmp1 + v_g + u_g) / 256;
561 signed r1 = (tmp1 + v_r) / 256;
562
563 signed tmp2 = y2 * 298;
564 signed b2 = (tmp2 + u_b) / 256;
565 signed g2 = (tmp2 + v_g + u_g) / 256;
566 signed r2 = (tmp2 + v_r) / 256;
567
568 bool uncropped = x + 1 < src.cropWidth();
569 writeToDst(dst_ptr + x * dst.mBpp, uncropped, r1, g1, b1, r2, g2, b2);
570 }
571
572 src_y += src.mStride;
573
574 if (y & 1) {
575 src_u += src.mStride / 2;
576 src_v += src.mStride / 2;
577 }
578
579 dst_ptr += dst.mStride;
580 }
581
582 return OK;
583 }
584
convertYUV420Planar16(const BitmapParams & src,const BitmapParams & dst)585 status_t ColorConverter::convertYUV420Planar16(
586 const BitmapParams &src, const BitmapParams &dst) {
587 if (mDstFormat == OMX_COLOR_FormatYUV444Y410) {
588 return convertYUV420Planar16ToY410(src, dst);
589 }
590
591 return convertYUV420Planar(src, dst);
592 }
593
594 /*
595 * Pack 10-bit YUV into RGBA_1010102.
596 *
597 * Media sends 10-bit YUV in a RGBA_1010102 format buffer. SF will handle
598 * the conversion to RGB using RenderEngine fallback.
599 *
600 * We do not perform a YUV->RGB conversion here, however the conversion with
601 * BT2020 to Full range is below for reference:
602 *
603 * B = 1.168 *(Y - 64) + 2.148 *(U - 512)
604 * G = 1.168 *(Y - 64) - 0.652 *(V - 512) - 0.188 *(U - 512)
605 * R = 1.168 *(Y - 64) + 1.683 *(V - 512)
606 *
607 * B = 1196/1024 *(Y - 64) + 2200/1024 *(U - 512)
608 * G = .................... - 668/1024 *(V - 512) - 192/1024 *(U - 512)
609 * R = .................... + 1723/1024 *(V - 512)
610 *
611 * min_B = (1196 *(- 64) + 2200 *(- 512)) / 1024 = -1175
612 * min_G = (1196 *(- 64) - 668 *(1023 - 512) - 192 *(1023 - 512)) / 1024 = -504
613 * min_R = (1196 *(- 64) + 1723 *(- 512)) / 1024 = -937
614 *
615 * max_B = (1196 *(1023 - 64) + 2200 *(1023 - 512)) / 1024 = 2218
616 * max_G = (1196 *(1023 - 64) - 668 *(- 512) - 192 *(- 512)) / 1024 = 1551
617 * max_R = (1196 *(1023 - 64) + 1723 *(1023 - 512)) / 1024 = 1980
618 *
619 * clip range -1175 .. 2218
620 *
621 */
622
623 #if !USE_NEON_Y410
624
convertYUV420Planar16ToY410(const BitmapParams & src,const BitmapParams & dst)625 status_t ColorConverter::convertYUV420Planar16ToY410(
626 const BitmapParams &src, const BitmapParams &dst) {
627 uint8_t *dst_ptr = (uint8_t *)dst.mBits
628 + dst.mCropTop * dst.mStride + dst.mCropLeft * dst.mBpp;
629
630 const uint8_t *src_y =
631 (const uint8_t *)src.mBits + src.mCropTop * src.mStride + src.mCropLeft * src.mBpp;
632
633 const uint8_t *src_u =
634 (const uint8_t *)src.mBits + src.mStride * src.mHeight
635 + (src.mCropTop / 2) * (src.mStride / 2) + (src.mCropLeft / 2) * src.mBpp;
636
637 const uint8_t *src_v =
638 src_u + (src.mStride / 2) * (src.mHeight / 2);
639
640 // Converting two lines at a time, slightly faster
641 for (size_t y = 0; y < src.cropHeight(); y += 2) {
642 uint32_t *dst_top = (uint32_t *) dst_ptr;
643 uint32_t *dst_bot = (uint32_t *) (dst_ptr + dst.mStride);
644 uint16_t *ptr_ytop = (uint16_t*) src_y;
645 uint16_t *ptr_ybot = (uint16_t*) (src_y + src.mStride);
646 uint16_t *ptr_u = (uint16_t*) src_u;
647 uint16_t *ptr_v = (uint16_t*) src_v;
648
649 uint32_t u01, v01, y01, y23, y45, y67, uv0, uv1;
650 size_t x = 0;
651 for (; x < src.cropWidth() - 3; x += 4) {
652 u01 = *((uint32_t*)ptr_u); ptr_u += 2;
653 v01 = *((uint32_t*)ptr_v); ptr_v += 2;
654
655 y01 = *((uint32_t*)ptr_ytop); ptr_ytop += 2;
656 y23 = *((uint32_t*)ptr_ytop); ptr_ytop += 2;
657 y45 = *((uint32_t*)ptr_ybot); ptr_ybot += 2;
658 y67 = *((uint32_t*)ptr_ybot); ptr_ybot += 2;
659
660 uv0 = (u01 & 0x3FF) | ((v01 & 0x3FF) << 20);
661 uv1 = (u01 >> 16) | ((v01 >> 16) << 20);
662
663 *dst_top++ = ((y01 & 0x3FF) << 10) | uv0;
664 *dst_top++ = ((y01 >> 16) << 10) | uv0;
665 *dst_top++ = ((y23 & 0x3FF) << 10) | uv1;
666 *dst_top++ = ((y23 >> 16) << 10) | uv1;
667
668 *dst_bot++ = ((y45 & 0x3FF) << 10) | uv0;
669 *dst_bot++ = ((y45 >> 16) << 10) | uv0;
670 *dst_bot++ = ((y67 & 0x3FF) << 10) | uv1;
671 *dst_bot++ = ((y67 >> 16) << 10) | uv1;
672 }
673
674 // There should be at most 2 more pixels to process. Note that we don't
675 // need to consider odd case as the buffer is always aligned to even.
676 if (x < src.cropWidth()) {
677 u01 = *ptr_u;
678 v01 = *ptr_v;
679 y01 = *((uint32_t*)ptr_ytop);
680 y45 = *((uint32_t*)ptr_ybot);
681 uv0 = (u01 & 0x3FF) | ((v01 & 0x3FF) << 20);
682 *dst_top++ = ((y01 & 0x3FF) << 10) | uv0;
683 *dst_top++ = ((y01 >> 16) << 10) | uv0;
684 *dst_bot++ = ((y45 & 0x3FF) << 10) | uv0;
685 *dst_bot++ = ((y45 >> 16) << 10) | uv0;
686 }
687
688 src_y += src.mStride * 2;
689 src_u += src.mStride / 2;
690 src_v += src.mStride / 2;
691 dst_ptr += dst.mStride * 2;
692 }
693
694 return OK;
695 }
696
697 #else
698
convertYUV420Planar16ToY410(const BitmapParams & src,const BitmapParams & dst)699 status_t ColorConverter::convertYUV420Planar16ToY410(
700 const BitmapParams &src, const BitmapParams &dst) {
701 uint8_t *out = (uint8_t *)dst.mBits
702 + dst.mCropTop * dst.mStride + dst.mCropLeft * dst.mBpp;
703
704 const uint8_t *src_y =
705 (const uint8_t *)src.mBits + src.mCropTop * src.mStride + src.mCropLeft * src.mBpp;
706
707 const uint8_t *src_u =
708 (const uint8_t *)src.mBits + src.mStride * src.mHeight
709 + (src.mCropTop / 2) * (src.mStride / 2) + (src.mCropLeft / 2) * src.mBpp;
710
711 const uint8_t *src_v =
712 src_u + (src.mStride / 2) * (src.mHeight / 2);
713
714 for (size_t y = 0; y < src.cropHeight(); y++) {
715 uint16_t *ptr_y = (uint16_t*) src_y;
716 uint16_t *ptr_u = (uint16_t*) src_u;
717 uint16_t *ptr_v = (uint16_t*) src_v;
718 uint32_t *ptr_out = (uint32_t *) out;
719
720 // Process 16-pixel at a time.
721 uint32_t *ptr_limit = ptr_out + (src.cropWidth() & ~15);
722 while (ptr_out < ptr_limit) {
723 uint16x4_t u0123 = vld1_u16(ptr_u); ptr_u += 4;
724 uint16x4_t u4567 = vld1_u16(ptr_u); ptr_u += 4;
725 uint16x4_t v0123 = vld1_u16(ptr_v); ptr_v += 4;
726 uint16x4_t v4567 = vld1_u16(ptr_v); ptr_v += 4;
727 uint16x4_t y0123 = vld1_u16(ptr_y); ptr_y += 4;
728 uint16x4_t y4567 = vld1_u16(ptr_y); ptr_y += 4;
729 uint16x4_t y89ab = vld1_u16(ptr_y); ptr_y += 4;
730 uint16x4_t ycdef = vld1_u16(ptr_y); ptr_y += 4;
731
732 uint32x2_t uvtempl;
733 uint32x4_t uvtempq;
734
735 uvtempq = vaddw_u16(vshll_n_u16(v0123, 20), u0123);
736
737 uvtempl = vget_low_u32(uvtempq);
738 uint32x4_t uv0011 = vreinterpretq_u32_u64(
739 vaddw_u32(vshll_n_u32(uvtempl, 32), uvtempl));
740
741 uvtempl = vget_high_u32(uvtempq);
742 uint32x4_t uv2233 = vreinterpretq_u32_u64(
743 vaddw_u32(vshll_n_u32(uvtempl, 32), uvtempl));
744
745 uvtempq = vaddw_u16(vshll_n_u16(v4567, 20), u4567);
746
747 uvtempl = vget_low_u32(uvtempq);
748 uint32x4_t uv4455 = vreinterpretq_u32_u64(
749 vaddw_u32(vshll_n_u32(uvtempl, 32), uvtempl));
750
751 uvtempl = vget_high_u32(uvtempq);
752 uint32x4_t uv6677 = vreinterpretq_u32_u64(
753 vaddw_u32(vshll_n_u32(uvtempl, 32), uvtempl));
754
755 uint32x4_t dsttemp;
756
757 dsttemp = vorrq_u32(uv0011, vshll_n_u16(y0123, 10));
758 vst1q_u32(ptr_out, dsttemp); ptr_out += 4;
759
760 dsttemp = vorrq_u32(uv2233, vshll_n_u16(y4567, 10));
761 vst1q_u32(ptr_out, dsttemp); ptr_out += 4;
762
763 dsttemp = vorrq_u32(uv4455, vshll_n_u16(y89ab, 10));
764 vst1q_u32(ptr_out, dsttemp); ptr_out += 4;
765
766 dsttemp = vorrq_u32(uv6677, vshll_n_u16(ycdef, 10));
767 vst1q_u32(ptr_out, dsttemp); ptr_out += 4;
768 }
769
770 src_y += src.mStride;
771 if (y & 1) {
772 src_u += src.mStride / 2;
773 src_v += src.mStride / 2;
774 }
775 out += dst.mStride;
776 }
777
778 // Process the left-overs out-of-loop, 2-pixel at a time. Note that we don't
779 // need to consider odd case as the buffer is always aligned to even.
780 if (src.cropWidth() & 15) {
781 size_t xstart = (src.cropWidth() & ~15);
782
783 uint8_t *out = (uint8_t *)dst.mBits + dst.mCropTop * dst.mStride
784 + (dst.mCropLeft + xstart) * dst.mBpp;
785
786 const uint8_t *src_y = (const uint8_t *)src.mBits + src.mCropTop * src.mStride
787 + (src.mCropLeft + xstart) * src.mBpp;
788
789 const uint8_t *src_u = (const uint8_t *)src.mBits + src.mStride * src.mHeight
790 + (src.mCropTop / 2) * (src.mStride / 2)
791 + ((src.mCropLeft + xstart) / 2) * src.mBpp;
792
793 const uint8_t *src_v = src_u + (src.mStride / 2) * (src.mHeight / 2);
794
795 for (size_t y = 0; y < src.cropHeight(); y++) {
796 uint16_t *ptr_y = (uint16_t*) src_y;
797 uint16_t *ptr_u = (uint16_t*) src_u;
798 uint16_t *ptr_v = (uint16_t*) src_v;
799 uint32_t *ptr_out = (uint32_t *) out;
800 for (size_t x = xstart; x < src.cropWidth(); x += 2) {
801 uint16_t u = *ptr_u++;
802 uint16_t v = *ptr_v++;
803 uint32_t y01 = *((uint32_t*)ptr_y); ptr_y += 2;
804 uint32_t uv = u | (((uint32_t)v) << 20);
805 *ptr_out++ = ((y01 & 0x3FF) << 10) | uv;
806 *ptr_out++ = ((y01 >> 16) << 10) | uv;
807 }
808 src_y += src.mStride;
809 if (y & 1) {
810 src_u += src.mStride / 2;
811 src_v += src.mStride / 2;
812 }
813 out += dst.mStride;
814 }
815 }
816
817 return OK;
818 }
819
820 #endif // USE_NEON_Y410
821
convertQCOMYUV420SemiPlanar(const BitmapParams & src,const BitmapParams & dst)822 status_t ColorConverter::convertQCOMYUV420SemiPlanar(
823 const BitmapParams &src, const BitmapParams &dst) {
824 uint8_t *kAdjustedClip = initClip();
825
826 uint16_t *dst_ptr = (uint16_t *)dst.mBits
827 + dst.mCropTop * dst.mWidth + dst.mCropLeft;
828
829 const uint8_t *src_y =
830 (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
831
832 const uint8_t *src_u =
833 (const uint8_t *)src_y + src.mWidth * src.mHeight
834 + src.mCropTop * src.mWidth + src.mCropLeft;
835
836 for (size_t y = 0; y < src.cropHeight(); ++y) {
837 for (size_t x = 0; x < src.cropWidth(); x += 2) {
838 signed y1 = (signed)src_y[x] - 16;
839 signed y2 = (signed)src_y[x + 1] - 16;
840
841 signed u = (signed)src_u[x & ~1] - 128;
842 signed v = (signed)src_u[(x & ~1) + 1] - 128;
843
844 signed u_b = u * 517;
845 signed u_g = -u * 100;
846 signed v_g = -v * 208;
847 signed v_r = v * 409;
848
849 signed tmp1 = y1 * 298;
850 signed b1 = (tmp1 + u_b) / 256;
851 signed g1 = (tmp1 + v_g + u_g) / 256;
852 signed r1 = (tmp1 + v_r) / 256;
853
854 signed tmp2 = y2 * 298;
855 signed b2 = (tmp2 + u_b) / 256;
856 signed g2 = (tmp2 + v_g + u_g) / 256;
857 signed r2 = (tmp2 + v_r) / 256;
858
859 uint32_t rgb1 =
860 ((kAdjustedClip[b1] >> 3) << 11)
861 | ((kAdjustedClip[g1] >> 2) << 5)
862 | (kAdjustedClip[r1] >> 3);
863
864 uint32_t rgb2 =
865 ((kAdjustedClip[b2] >> 3) << 11)
866 | ((kAdjustedClip[g2] >> 2) << 5)
867 | (kAdjustedClip[r2] >> 3);
868
869 if (x + 1 < src.cropWidth()) {
870 *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1;
871 } else {
872 dst_ptr[x] = rgb1;
873 }
874 }
875
876 src_y += src.mWidth;
877
878 if (y & 1) {
879 src_u += src.mWidth;
880 }
881
882 dst_ptr += dst.mWidth;
883 }
884
885 return OK;
886 }
887
convertYUV420SemiPlanar(const BitmapParams & src,const BitmapParams & dst)888 status_t ColorConverter::convertYUV420SemiPlanar(
889 const BitmapParams &src, const BitmapParams &dst) {
890 // XXX Untested
891
892 uint8_t *kAdjustedClip = initClip();
893
894 uint16_t *dst_ptr = (uint16_t *)((uint8_t *)
895 dst.mBits + dst.mCropTop * dst.mStride + dst.mCropLeft * dst.mBpp);
896
897 const uint8_t *src_y =
898 (const uint8_t *)src.mBits + src.mCropTop * src.mStride + src.mCropLeft;
899
900 const uint8_t *src_u =
901 (const uint8_t *)src.mBits + src.mHeight * src.mStride +
902 (src.mCropTop / 2) * src.mStride + src.mCropLeft;
903
904 for (size_t y = 0; y < src.cropHeight(); ++y) {
905 for (size_t x = 0; x < src.cropWidth(); x += 2) {
906 signed y1 = (signed)src_y[x] - 16;
907 signed y2 = (signed)src_y[x + 1] - 16;
908
909 signed v = (signed)src_u[x & ~1] - 128;
910 signed u = (signed)src_u[(x & ~1) + 1] - 128;
911
912 signed u_b = u * 517;
913 signed u_g = -u * 100;
914 signed v_g = -v * 208;
915 signed v_r = v * 409;
916
917 signed tmp1 = y1 * 298;
918 signed b1 = (tmp1 + u_b) / 256;
919 signed g1 = (tmp1 + v_g + u_g) / 256;
920 signed r1 = (tmp1 + v_r) / 256;
921
922 signed tmp2 = y2 * 298;
923 signed b2 = (tmp2 + u_b) / 256;
924 signed g2 = (tmp2 + v_g + u_g) / 256;
925 signed r2 = (tmp2 + v_r) / 256;
926
927 uint32_t rgb1 =
928 ((kAdjustedClip[b1] >> 3) << 11)
929 | ((kAdjustedClip[g1] >> 2) << 5)
930 | (kAdjustedClip[r1] >> 3);
931
932 uint32_t rgb2 =
933 ((kAdjustedClip[b2] >> 3) << 11)
934 | ((kAdjustedClip[g2] >> 2) << 5)
935 | (kAdjustedClip[r2] >> 3);
936
937 if (x + 1 < src.cropWidth()) {
938 *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1;
939 } else {
940 dst_ptr[x] = rgb1;
941 }
942 }
943
944 src_y += src.mStride;
945
946 if (y & 1) {
947 src_u += src.mStride;
948 }
949
950 dst_ptr = (uint16_t*)((uint8_t*)dst_ptr + dst.mStride);
951 }
952
953 return OK;
954 }
955
convertTIYUV420PackedSemiPlanar(const BitmapParams & src,const BitmapParams & dst)956 status_t ColorConverter::convertTIYUV420PackedSemiPlanar(
957 const BitmapParams &src, const BitmapParams &dst) {
958 uint8_t *kAdjustedClip = initClip();
959
960 uint16_t *dst_ptr = (uint16_t *)dst.mBits
961 + dst.mCropTop * dst.mWidth + dst.mCropLeft;
962
963 const uint8_t *src_y =
964 (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
965
966 const uint8_t *src_u =
967 (const uint8_t *)src_y + src.mWidth * (src.mHeight - src.mCropTop / 2);
968
969 for (size_t y = 0; y < src.cropHeight(); ++y) {
970 for (size_t x = 0; x < src.cropWidth(); x += 2) {
971 signed y1 = (signed)src_y[x] - 16;
972 signed y2 = (signed)src_y[x + 1] - 16;
973
974 signed u = (signed)src_u[x & ~1] - 128;
975 signed v = (signed)src_u[(x & ~1) + 1] - 128;
976
977 signed u_b = u * 517;
978 signed u_g = -u * 100;
979 signed v_g = -v * 208;
980 signed v_r = v * 409;
981
982 signed tmp1 = y1 * 298;
983 signed b1 = (tmp1 + u_b) / 256;
984 signed g1 = (tmp1 + v_g + u_g) / 256;
985 signed r1 = (tmp1 + v_r) / 256;
986
987 signed tmp2 = y2 * 298;
988 signed b2 = (tmp2 + u_b) / 256;
989 signed g2 = (tmp2 + v_g + u_g) / 256;
990 signed r2 = (tmp2 + v_r) / 256;
991
992 uint32_t rgb1 =
993 ((kAdjustedClip[r1] >> 3) << 11)
994 | ((kAdjustedClip[g1] >> 2) << 5)
995 | (kAdjustedClip[b1] >> 3);
996
997 uint32_t rgb2 =
998 ((kAdjustedClip[r2] >> 3) << 11)
999 | ((kAdjustedClip[g2] >> 2) << 5)
1000 | (kAdjustedClip[b2] >> 3);
1001
1002 if (x + 1 < src.cropWidth()) {
1003 *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1;
1004 } else {
1005 dst_ptr[x] = rgb1;
1006 }
1007 }
1008
1009 src_y += src.mWidth;
1010
1011 if (y & 1) {
1012 src_u += src.mWidth;
1013 }
1014
1015 dst_ptr += dst.mWidth;
1016 }
1017
1018 return OK;
1019 }
1020
initClip()1021 uint8_t *ColorConverter::initClip() {
1022 static const signed kClipMin = -278;
1023 static const signed kClipMax = 535;
1024
1025 if (mClip == NULL) {
1026 mClip = new uint8_t[kClipMax - kClipMin + 1];
1027
1028 for (signed i = kClipMin; i <= kClipMax; ++i) {
1029 mClip[i - kClipMin] = (i < 0) ? 0 : (i > 255) ? 255 : (uint8_t)i;
1030 }
1031 }
1032
1033 return &mClip[-kClipMin];
1034 }
1035
1036 } // namespace android
1037