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/ColorConverter.h>
23#include <media/stagefright/MediaErrors.h>
24
25#include "libyuv/convert_from.h"
26
27#define USE_LIBYUV
28
29namespace android {
30
31ColorConverter::ColorConverter(
32        OMX_COLOR_FORMATTYPE from, OMX_COLOR_FORMATTYPE to)
33    : mSrcFormat(from),
34      mDstFormat(to),
35      mClip(NULL) {
36}
37
38ColorConverter::~ColorConverter() {
39    delete[] mClip;
40    mClip = NULL;
41}
42
43bool ColorConverter::isValid() const {
44    if (mDstFormat != OMX_COLOR_Format16bitRGB565) {
45        return false;
46    }
47
48    switch (mSrcFormat) {
49        case OMX_COLOR_FormatYUV420Planar:
50        case OMX_COLOR_FormatCbYCrY:
51        case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
52        case OMX_COLOR_FormatYUV420SemiPlanar:
53        case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar:
54            return true;
55
56        default:
57            return false;
58    }
59}
60
61ColorConverter::BitmapParams::BitmapParams(
62        void *bits,
63        size_t width, size_t height,
64        size_t cropLeft, size_t cropTop,
65        size_t cropRight, size_t cropBottom)
66    : mBits(bits),
67      mWidth(width),
68      mHeight(height),
69      mCropLeft(cropLeft),
70      mCropTop(cropTop),
71      mCropRight(cropRight),
72      mCropBottom(cropBottom) {
73}
74
75size_t ColorConverter::BitmapParams::cropWidth() const {
76    return mCropRight - mCropLeft + 1;
77}
78
79size_t ColorConverter::BitmapParams::cropHeight() const {
80    return mCropBottom - mCropTop + 1;
81}
82
83status_t ColorConverter::convert(
84        const void *srcBits,
85        size_t srcWidth, size_t srcHeight,
86        size_t srcCropLeft, size_t srcCropTop,
87        size_t srcCropRight, size_t srcCropBottom,
88        void *dstBits,
89        size_t dstWidth, size_t dstHeight,
90        size_t dstCropLeft, size_t dstCropTop,
91        size_t dstCropRight, size_t dstCropBottom) {
92    if (mDstFormat != OMX_COLOR_Format16bitRGB565) {
93        return ERROR_UNSUPPORTED;
94    }
95
96    BitmapParams src(
97            const_cast<void *>(srcBits),
98            srcWidth, srcHeight,
99            srcCropLeft, srcCropTop, srcCropRight, srcCropBottom);
100
101    BitmapParams dst(
102            dstBits,
103            dstWidth, dstHeight,
104            dstCropLeft, dstCropTop, dstCropRight, dstCropBottom);
105
106    status_t err;
107
108    switch (mSrcFormat) {
109        case OMX_COLOR_FormatYUV420Planar:
110#ifdef USE_LIBYUV
111            err = convertYUV420PlanarUseLibYUV(src, dst);
112#else
113            err = convertYUV420Planar(src, dst);
114#endif
115            break;
116
117        case OMX_COLOR_FormatCbYCrY:
118            err = convertCbYCrY(src, dst);
119            break;
120
121        case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
122            err = convertQCOMYUV420SemiPlanar(src, dst);
123            break;
124
125        case OMX_COLOR_FormatYUV420SemiPlanar:
126            err = convertYUV420SemiPlanar(src, dst);
127            break;
128
129        case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar:
130            err = convertTIYUV420PackedSemiPlanar(src, dst);
131            break;
132
133        default:
134        {
135            CHECK(!"Should not be here. Unknown color conversion.");
136            break;
137        }
138    }
139
140    return err;
141}
142
143status_t ColorConverter::convertCbYCrY(
144        const BitmapParams &src, const BitmapParams &dst) {
145    // XXX Untested
146
147    uint8_t *kAdjustedClip = initClip();
148
149    if (!((src.mCropLeft & 1) == 0
150        && src.cropWidth() == dst.cropWidth()
151        && src.cropHeight() == dst.cropHeight())) {
152        return ERROR_UNSUPPORTED;
153    }
154
155    uint16_t *dst_ptr = (uint16_t *)dst.mBits
156        + dst.mCropTop * dst.mWidth + dst.mCropLeft;
157
158    const uint8_t *src_ptr = (const uint8_t *)src.mBits
159        + (src.mCropTop * dst.mWidth + src.mCropLeft) * 2;
160
161    for (size_t y = 0; y < src.cropHeight(); ++y) {
162        for (size_t x = 0; x < src.cropWidth(); x += 2) {
163            signed y1 = (signed)src_ptr[2 * x + 1] - 16;
164            signed y2 = (signed)src_ptr[2 * x + 3] - 16;
165            signed u = (signed)src_ptr[2 * x] - 128;
166            signed v = (signed)src_ptr[2 * x + 2] - 128;
167
168            signed u_b = u * 517;
169            signed u_g = -u * 100;
170            signed v_g = -v * 208;
171            signed v_r = v * 409;
172
173            signed tmp1 = y1 * 298;
174            signed b1 = (tmp1 + u_b) / 256;
175            signed g1 = (tmp1 + v_g + u_g) / 256;
176            signed r1 = (tmp1 + v_r) / 256;
177
178            signed tmp2 = y2 * 298;
179            signed b2 = (tmp2 + u_b) / 256;
180            signed g2 = (tmp2 + v_g + u_g) / 256;
181            signed r2 = (tmp2 + v_r) / 256;
182
183            uint32_t rgb1 =
184                ((kAdjustedClip[r1] >> 3) << 11)
185                | ((kAdjustedClip[g1] >> 2) << 5)
186                | (kAdjustedClip[b1] >> 3);
187
188            uint32_t rgb2 =
189                ((kAdjustedClip[r2] >> 3) << 11)
190                | ((kAdjustedClip[g2] >> 2) << 5)
191                | (kAdjustedClip[b2] >> 3);
192
193            if (x + 1 < src.cropWidth()) {
194                *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1;
195            } else {
196                dst_ptr[x] = rgb1;
197            }
198        }
199
200        src_ptr += src.mWidth * 2;
201        dst_ptr += dst.mWidth;
202    }
203
204    return OK;
205}
206
207status_t ColorConverter::convertYUV420PlanarUseLibYUV(
208        const BitmapParams &src, const BitmapParams &dst) {
209    if (!((src.mCropLeft & 1) == 0
210            && src.cropWidth() == dst.cropWidth()
211            && src.cropHeight() == dst.cropHeight())) {
212        return ERROR_UNSUPPORTED;
213    }
214
215    uint16_t *dst_ptr = (uint16_t *)dst.mBits
216        + dst.mCropTop * dst.mWidth + dst.mCropLeft;
217
218    const uint8_t *src_y =
219        (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
220
221    const uint8_t *src_u =
222        (const uint8_t *)src_y + src.mWidth * src.mHeight
223        + src.mCropTop * (src.mWidth / 2) + src.mCropLeft / 2;
224
225    const uint8_t *src_v =
226        src_u + (src.mWidth / 2) * (src.mHeight / 2);
227
228
229    libyuv::I420ToRGB565(src_y, src.mWidth, src_u, src.mWidth / 2, src_v, src.mWidth / 2,
230            (uint8 *)dst_ptr, dst.mWidth * 2, dst.mWidth, dst.mHeight);
231
232    return OK;
233}
234
235status_t ColorConverter::convertYUV420Planar(
236        const BitmapParams &src, const BitmapParams &dst) {
237    if (!((src.mCropLeft & 1) == 0
238            && src.cropWidth() == dst.cropWidth()
239            && src.cropHeight() == dst.cropHeight())) {
240        return ERROR_UNSUPPORTED;
241    }
242
243    uint8_t *kAdjustedClip = initClip();
244
245    uint16_t *dst_ptr = (uint16_t *)dst.mBits
246        + dst.mCropTop * dst.mWidth + dst.mCropLeft;
247
248    const uint8_t *src_y =
249        (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
250
251    const uint8_t *src_u =
252        (const uint8_t *)src_y + src.mWidth * src.mHeight
253        + src.mCropTop * (src.mWidth / 2) + src.mCropLeft / 2;
254
255    const uint8_t *src_v =
256        src_u + (src.mWidth / 2) * (src.mHeight / 2);
257
258    for (size_t y = 0; y < src.cropHeight(); ++y) {
259        for (size_t x = 0; x < src.cropWidth(); x += 2) {
260            // B = 1.164 * (Y - 16) + 2.018 * (U - 128)
261            // G = 1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128)
262            // R = 1.164 * (Y - 16) + 1.596 * (V - 128)
263
264            // B = 298/256 * (Y - 16) + 517/256 * (U - 128)
265            // G = .................. - 208/256 * (V - 128) - 100/256 * (U - 128)
266            // R = .................. + 409/256 * (V - 128)
267
268            // min_B = (298 * (- 16) + 517 * (- 128)) / 256 = -277
269            // min_G = (298 * (- 16) - 208 * (255 - 128) - 100 * (255 - 128)) / 256 = -172
270            // min_R = (298 * (- 16) + 409 * (- 128)) / 256 = -223
271
272            // max_B = (298 * (255 - 16) + 517 * (255 - 128)) / 256 = 534
273            // max_G = (298 * (255 - 16) - 208 * (- 128) - 100 * (- 128)) / 256 = 432
274            // max_R = (298 * (255 - 16) + 409 * (255 - 128)) / 256 = 481
275
276            // clip range -278 .. 535
277
278            signed y1 = (signed)src_y[x] - 16;
279            signed y2 = (signed)src_y[x + 1] - 16;
280
281            signed u = (signed)src_u[x / 2] - 128;
282            signed v = (signed)src_v[x / 2] - 128;
283
284            signed u_b = u * 517;
285            signed u_g = -u * 100;
286            signed v_g = -v * 208;
287            signed v_r = v * 409;
288
289            signed tmp1 = y1 * 298;
290            signed b1 = (tmp1 + u_b) / 256;
291            signed g1 = (tmp1 + v_g + u_g) / 256;
292            signed r1 = (tmp1 + v_r) / 256;
293
294            signed tmp2 = y2 * 298;
295            signed b2 = (tmp2 + u_b) / 256;
296            signed g2 = (tmp2 + v_g + u_g) / 256;
297            signed r2 = (tmp2 + v_r) / 256;
298
299            uint32_t rgb1 =
300                ((kAdjustedClip[r1] >> 3) << 11)
301                | ((kAdjustedClip[g1] >> 2) << 5)
302                | (kAdjustedClip[b1] >> 3);
303
304            uint32_t rgb2 =
305                ((kAdjustedClip[r2] >> 3) << 11)
306                | ((kAdjustedClip[g2] >> 2) << 5)
307                | (kAdjustedClip[b2] >> 3);
308
309            if (x + 1 < src.cropWidth()) {
310                *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1;
311            } else {
312                dst_ptr[x] = rgb1;
313            }
314        }
315
316        src_y += src.mWidth;
317
318        if (y & 1) {
319            src_u += src.mWidth / 2;
320            src_v += src.mWidth / 2;
321        }
322
323        dst_ptr += dst.mWidth;
324    }
325
326    return OK;
327}
328
329status_t ColorConverter::convertQCOMYUV420SemiPlanar(
330        const BitmapParams &src, const BitmapParams &dst) {
331    uint8_t *kAdjustedClip = initClip();
332
333    if (!((src.mCropLeft & 1) == 0
334            && src.cropWidth() == dst.cropWidth()
335            && src.cropHeight() == dst.cropHeight())) {
336        return ERROR_UNSUPPORTED;
337    }
338
339    uint16_t *dst_ptr = (uint16_t *)dst.mBits
340        + dst.mCropTop * dst.mWidth + dst.mCropLeft;
341
342    const uint8_t *src_y =
343        (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
344
345    const uint8_t *src_u =
346        (const uint8_t *)src_y + src.mWidth * src.mHeight
347        + src.mCropTop * src.mWidth + src.mCropLeft;
348
349    for (size_t y = 0; y < src.cropHeight(); ++y) {
350        for (size_t x = 0; x < src.cropWidth(); x += 2) {
351            signed y1 = (signed)src_y[x] - 16;
352            signed y2 = (signed)src_y[x + 1] - 16;
353
354            signed u = (signed)src_u[x & ~1] - 128;
355            signed v = (signed)src_u[(x & ~1) + 1] - 128;
356
357            signed u_b = u * 517;
358            signed u_g = -u * 100;
359            signed v_g = -v * 208;
360            signed v_r = v * 409;
361
362            signed tmp1 = y1 * 298;
363            signed b1 = (tmp1 + u_b) / 256;
364            signed g1 = (tmp1 + v_g + u_g) / 256;
365            signed r1 = (tmp1 + v_r) / 256;
366
367            signed tmp2 = y2 * 298;
368            signed b2 = (tmp2 + u_b) / 256;
369            signed g2 = (tmp2 + v_g + u_g) / 256;
370            signed r2 = (tmp2 + v_r) / 256;
371
372            uint32_t rgb1 =
373                ((kAdjustedClip[b1] >> 3) << 11)
374                | ((kAdjustedClip[g1] >> 2) << 5)
375                | (kAdjustedClip[r1] >> 3);
376
377            uint32_t rgb2 =
378                ((kAdjustedClip[b2] >> 3) << 11)
379                | ((kAdjustedClip[g2] >> 2) << 5)
380                | (kAdjustedClip[r2] >> 3);
381
382            if (x + 1 < src.cropWidth()) {
383                *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1;
384            } else {
385                dst_ptr[x] = rgb1;
386            }
387        }
388
389        src_y += src.mWidth;
390
391        if (y & 1) {
392            src_u += src.mWidth;
393        }
394
395        dst_ptr += dst.mWidth;
396    }
397
398    return OK;
399}
400
401status_t ColorConverter::convertYUV420SemiPlanar(
402        const BitmapParams &src, const BitmapParams &dst) {
403    // XXX Untested
404
405    uint8_t *kAdjustedClip = initClip();
406
407    if (!((src.mCropLeft & 1) == 0
408            && src.cropWidth() == dst.cropWidth()
409            && src.cropHeight() == dst.cropHeight())) {
410        return ERROR_UNSUPPORTED;
411    }
412
413    uint16_t *dst_ptr = (uint16_t *)dst.mBits
414        + dst.mCropTop * dst.mWidth + dst.mCropLeft;
415
416    const uint8_t *src_y =
417        (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
418
419    const uint8_t *src_u =
420        (const uint8_t *)src_y + src.mWidth * src.mHeight
421        + src.mCropTop * src.mWidth + src.mCropLeft;
422
423    for (size_t y = 0; y < src.cropHeight(); ++y) {
424        for (size_t x = 0; x < src.cropWidth(); x += 2) {
425            signed y1 = (signed)src_y[x] - 16;
426            signed y2 = (signed)src_y[x + 1] - 16;
427
428            signed v = (signed)src_u[x & ~1] - 128;
429            signed u = (signed)src_u[(x & ~1) + 1] - 128;
430
431            signed u_b = u * 517;
432            signed u_g = -u * 100;
433            signed v_g = -v * 208;
434            signed v_r = v * 409;
435
436            signed tmp1 = y1 * 298;
437            signed b1 = (tmp1 + u_b) / 256;
438            signed g1 = (tmp1 + v_g + u_g) / 256;
439            signed r1 = (tmp1 + v_r) / 256;
440
441            signed tmp2 = y2 * 298;
442            signed b2 = (tmp2 + u_b) / 256;
443            signed g2 = (tmp2 + v_g + u_g) / 256;
444            signed r2 = (tmp2 + v_r) / 256;
445
446            uint32_t rgb1 =
447                ((kAdjustedClip[b1] >> 3) << 11)
448                | ((kAdjustedClip[g1] >> 2) << 5)
449                | (kAdjustedClip[r1] >> 3);
450
451            uint32_t rgb2 =
452                ((kAdjustedClip[b2] >> 3) << 11)
453                | ((kAdjustedClip[g2] >> 2) << 5)
454                | (kAdjustedClip[r2] >> 3);
455
456            if (x + 1 < src.cropWidth()) {
457                *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1;
458            } else {
459                dst_ptr[x] = rgb1;
460            }
461        }
462
463        src_y += src.mWidth;
464
465        if (y & 1) {
466            src_u += src.mWidth;
467        }
468
469        dst_ptr += dst.mWidth;
470    }
471
472    return OK;
473}
474
475status_t ColorConverter::convertTIYUV420PackedSemiPlanar(
476        const BitmapParams &src, const BitmapParams &dst) {
477    uint8_t *kAdjustedClip = initClip();
478
479    if (!((src.mCropLeft & 1) == 0
480            && src.cropWidth() == dst.cropWidth()
481            && src.cropHeight() == dst.cropHeight())) {
482        return ERROR_UNSUPPORTED;
483    }
484
485    uint16_t *dst_ptr = (uint16_t *)dst.mBits
486        + dst.mCropTop * dst.mWidth + dst.mCropLeft;
487
488    const uint8_t *src_y = (const uint8_t *)src.mBits;
489
490    const uint8_t *src_u =
491        (const uint8_t *)src_y + src.mWidth * (src.mHeight - src.mCropTop / 2);
492
493    for (size_t y = 0; y < src.cropHeight(); ++y) {
494        for (size_t x = 0; x < src.cropWidth(); x += 2) {
495            signed y1 = (signed)src_y[x] - 16;
496            signed y2 = (signed)src_y[x + 1] - 16;
497
498            signed u = (signed)src_u[x & ~1] - 128;
499            signed v = (signed)src_u[(x & ~1) + 1] - 128;
500
501            signed u_b = u * 517;
502            signed u_g = -u * 100;
503            signed v_g = -v * 208;
504            signed v_r = v * 409;
505
506            signed tmp1 = y1 * 298;
507            signed b1 = (tmp1 + u_b) / 256;
508            signed g1 = (tmp1 + v_g + u_g) / 256;
509            signed r1 = (tmp1 + v_r) / 256;
510
511            signed tmp2 = y2 * 298;
512            signed b2 = (tmp2 + u_b) / 256;
513            signed g2 = (tmp2 + v_g + u_g) / 256;
514            signed r2 = (tmp2 + v_r) / 256;
515
516            uint32_t rgb1 =
517                ((kAdjustedClip[r1] >> 3) << 11)
518                | ((kAdjustedClip[g1] >> 2) << 5)
519                | (kAdjustedClip[b1] >> 3);
520
521            uint32_t rgb2 =
522                ((kAdjustedClip[r2] >> 3) << 11)
523                | ((kAdjustedClip[g2] >> 2) << 5)
524                | (kAdjustedClip[b2] >> 3);
525
526            if (x + 1 < src.cropWidth()) {
527                *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1;
528            } else {
529                dst_ptr[x] = rgb1;
530            }
531        }
532
533        src_y += src.mWidth;
534
535        if (y & 1) {
536            src_u += src.mWidth;
537        }
538
539        dst_ptr += dst.mWidth;
540    }
541
542    return OK;
543}
544
545uint8_t *ColorConverter::initClip() {
546    static const signed kClipMin = -278;
547    static const signed kClipMax = 535;
548
549    if (mClip == NULL) {
550        mClip = new uint8_t[kClipMax - kClipMin + 1];
551
552        for (signed i = kClipMin; i <= kClipMax; ++i) {
553            mClip[i - kClipMin] = (i < 0) ? 0 : (i > 255) ? 255 : (uint8_t)i;
554        }
555    }
556
557    return &mClip[-kClipMin];
558}
559
560}  // namespace android
561