Home | History | Annotate | Download | only in colorconversion
      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 namespace android {
     26 
     27 ColorConverter::ColorConverter(
     28         OMX_COLOR_FORMATTYPE from, OMX_COLOR_FORMATTYPE to)
     29     : mSrcFormat(from),
     30       mDstFormat(to),
     31       mClip(NULL) {
     32 }
     33 
     34 ColorConverter::~ColorConverter() {
     35     delete[] mClip;
     36     mClip = NULL;
     37 }
     38 
     39 bool 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 
     57 ColorConverter::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 
     71 size_t ColorConverter::BitmapParams::cropWidth() const {
     72     return mCropRight - mCropLeft + 1;
     73 }
     74 
     75 size_t ColorConverter::BitmapParams::cropHeight() const {
     76     return mCropBottom - mCropTop + 1;
     77 }
     78 
     79 status_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 
    135 status_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 
    199 status_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 
    293 status_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 
    365 status_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 
    439 status_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 
    509 uint8_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