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