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