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/ColorConverter.h>
     22 #include <media/stagefright/MediaDebug.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     uint32_t *dst_ptr = (uint32_t *)dst.mBits
    148         + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
    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             dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
    186         }
    187 
    188         src_ptr += src.mWidth * 2;
    189         dst_ptr += dst.mWidth / 2;
    190     }
    191 
    192     return OK;
    193 }
    194 
    195 status_t ColorConverter::convertYUV420Planar(
    196         const BitmapParams &src, const BitmapParams &dst) {
    197     if (!((src.mCropLeft & 1) == 0
    198             && src.cropWidth() == dst.cropWidth()
    199             && src.cropHeight() == dst.cropHeight())) {
    200         return ERROR_UNSUPPORTED;
    201     }
    202 
    203     uint8_t *kAdjustedClip = initClip();
    204 
    205     uint16_t *dst_ptr = (uint16_t *)dst.mBits
    206         + dst.mCropTop * dst.mWidth + dst.mCropLeft;
    207 
    208     const uint8_t *src_y =
    209         (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
    210 
    211     const uint8_t *src_u =
    212         (const uint8_t *)src_y + src.mWidth * src.mHeight
    213         + src.mCropTop * (src.mWidth / 2) + src.mCropLeft / 2;
    214 
    215     const uint8_t *src_v =
    216         src_u + (src.mWidth / 2) * (src.mHeight / 2);
    217 
    218     for (size_t y = 0; y < src.cropHeight(); ++y) {
    219         for (size_t x = 0; x < src.cropWidth(); x += 2) {
    220             // B = 1.164 * (Y - 16) + 2.018 * (U - 128)
    221             // G = 1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128)
    222             // R = 1.164 * (Y - 16) + 1.596 * (V - 128)
    223 
    224             // B = 298/256 * (Y - 16) + 517/256 * (U - 128)
    225             // G = .................. - 208/256 * (V - 128) - 100/256 * (U - 128)
    226             // R = .................. + 409/256 * (V - 128)
    227 
    228             // min_B = (298 * (- 16) + 517 * (- 128)) / 256 = -277
    229             // min_G = (298 * (- 16) - 208 * (255 - 128) - 100 * (255 - 128)) / 256 = -172
    230             // min_R = (298 * (- 16) + 409 * (- 128)) / 256 = -223
    231 
    232             // max_B = (298 * (255 - 16) + 517 * (255 - 128)) / 256 = 534
    233             // max_G = (298 * (255 - 16) - 208 * (- 128) - 100 * (- 128)) / 256 = 432
    234             // max_R = (298 * (255 - 16) + 409 * (255 - 128)) / 256 = 481
    235 
    236             // clip range -278 .. 535
    237 
    238             signed y1 = (signed)src_y[x] - 16;
    239             signed y2 = (signed)src_y[x + 1] - 16;
    240 
    241             signed u = (signed)src_u[x / 2] - 128;
    242             signed v = (signed)src_v[x / 2] - 128;
    243 
    244             signed u_b = u * 517;
    245             signed u_g = -u * 100;
    246             signed v_g = -v * 208;
    247             signed v_r = v * 409;
    248 
    249             signed tmp1 = y1 * 298;
    250             signed b1 = (tmp1 + u_b) / 256;
    251             signed g1 = (tmp1 + v_g + u_g) / 256;
    252             signed r1 = (tmp1 + v_r) / 256;
    253 
    254             signed tmp2 = y2 * 298;
    255             signed b2 = (tmp2 + u_b) / 256;
    256             signed g2 = (tmp2 + v_g + u_g) / 256;
    257             signed r2 = (tmp2 + v_r) / 256;
    258 
    259             uint32_t rgb1 =
    260                 ((kAdjustedClip[r1] >> 3) << 11)
    261                 | ((kAdjustedClip[g1] >> 2) << 5)
    262                 | (kAdjustedClip[b1] >> 3);
    263 
    264             uint32_t rgb2 =
    265                 ((kAdjustedClip[r2] >> 3) << 11)
    266                 | ((kAdjustedClip[g2] >> 2) << 5)
    267                 | (kAdjustedClip[b2] >> 3);
    268 
    269             if (x + 1 < src.cropWidth()) {
    270                 *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1;
    271             } else {
    272                 dst_ptr[x] = rgb1;
    273             }
    274         }
    275 
    276         src_y += src.mWidth;
    277 
    278         if (y & 1) {
    279             src_u += src.mWidth / 2;
    280             src_v += src.mWidth / 2;
    281         }
    282 
    283         dst_ptr += dst.mWidth;
    284     }
    285 
    286     return OK;
    287 }
    288 
    289 status_t ColorConverter::convertQCOMYUV420SemiPlanar(
    290         const BitmapParams &src, const BitmapParams &dst) {
    291     uint8_t *kAdjustedClip = initClip();
    292 
    293     if (!((dst.mWidth & 3) == 0
    294             && (src.mCropLeft & 1) == 0
    295             && src.cropWidth() == dst.cropWidth()
    296             && src.cropHeight() == dst.cropHeight())) {
    297         return ERROR_UNSUPPORTED;
    298     }
    299 
    300     uint32_t *dst_ptr = (uint32_t *)dst.mBits
    301         + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
    302 
    303     const uint8_t *src_y =
    304         (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
    305 
    306     const uint8_t *src_u =
    307         (const uint8_t *)src_y + src.mWidth * src.mHeight
    308         + src.mCropTop * src.mWidth + src.mCropLeft;
    309 
    310     for (size_t y = 0; y < src.cropHeight(); ++y) {
    311         for (size_t x = 0; x < src.cropWidth(); x += 2) {
    312             signed y1 = (signed)src_y[x] - 16;
    313             signed y2 = (signed)src_y[x + 1] - 16;
    314 
    315             signed u = (signed)src_u[x & ~1] - 128;
    316             signed v = (signed)src_u[(x & ~1) + 1] - 128;
    317 
    318             signed u_b = u * 517;
    319             signed u_g = -u * 100;
    320             signed v_g = -v * 208;
    321             signed v_r = v * 409;
    322 
    323             signed tmp1 = y1 * 298;
    324             signed b1 = (tmp1 + u_b) / 256;
    325             signed g1 = (tmp1 + v_g + u_g) / 256;
    326             signed r1 = (tmp1 + v_r) / 256;
    327 
    328             signed tmp2 = y2 * 298;
    329             signed b2 = (tmp2 + u_b) / 256;
    330             signed g2 = (tmp2 + v_g + u_g) / 256;
    331             signed r2 = (tmp2 + v_r) / 256;
    332 
    333             uint32_t rgb1 =
    334                 ((kAdjustedClip[b1] >> 3) << 11)
    335                 | ((kAdjustedClip[g1] >> 2) << 5)
    336                 | (kAdjustedClip[r1] >> 3);
    337 
    338             uint32_t rgb2 =
    339                 ((kAdjustedClip[b2] >> 3) << 11)
    340                 | ((kAdjustedClip[g2] >> 2) << 5)
    341                 | (kAdjustedClip[r2] >> 3);
    342 
    343             dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
    344         }
    345 
    346         src_y += src.mWidth;
    347 
    348         if (y & 1) {
    349             src_u += src.mWidth;
    350         }
    351 
    352         dst_ptr += dst.mWidth / 2;
    353     }
    354 
    355     return OK;
    356 }
    357 
    358 status_t ColorConverter::convertYUV420SemiPlanar(
    359         const BitmapParams &src, const BitmapParams &dst) {
    360     // XXX Untested
    361 
    362     uint8_t *kAdjustedClip = initClip();
    363 
    364     if (!((dst.mWidth & 3) == 0
    365             && (src.mCropLeft & 1) == 0
    366             && src.cropWidth() == dst.cropWidth()
    367             && src.cropHeight() == dst.cropHeight())) {
    368         return ERROR_UNSUPPORTED;
    369     }
    370 
    371     uint32_t *dst_ptr = (uint32_t *)dst.mBits
    372         + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
    373 
    374     const uint8_t *src_y =
    375         (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
    376 
    377     const uint8_t *src_u =
    378         (const uint8_t *)src_y + src.mWidth * src.mHeight
    379         + src.mCropTop * src.mWidth + src.mCropLeft;
    380 
    381     for (size_t y = 0; y < src.cropHeight(); ++y) {
    382         for (size_t x = 0; x < src.cropWidth(); x += 2) {
    383             signed y1 = (signed)src_y[x] - 16;
    384             signed y2 = (signed)src_y[x + 1] - 16;
    385 
    386             signed v = (signed)src_u[x & ~1] - 128;
    387             signed u = (signed)src_u[(x & ~1) + 1] - 128;
    388 
    389             signed u_b = u * 517;
    390             signed u_g = -u * 100;
    391             signed v_g = -v * 208;
    392             signed v_r = v * 409;
    393 
    394             signed tmp1 = y1 * 298;
    395             signed b1 = (tmp1 + u_b) / 256;
    396             signed g1 = (tmp1 + v_g + u_g) / 256;
    397             signed r1 = (tmp1 + v_r) / 256;
    398 
    399             signed tmp2 = y2 * 298;
    400             signed b2 = (tmp2 + u_b) / 256;
    401             signed g2 = (tmp2 + v_g + u_g) / 256;
    402             signed r2 = (tmp2 + v_r) / 256;
    403 
    404             uint32_t rgb1 =
    405                 ((kAdjustedClip[b1] >> 3) << 11)
    406                 | ((kAdjustedClip[g1] >> 2) << 5)
    407                 | (kAdjustedClip[r1] >> 3);
    408 
    409             uint32_t rgb2 =
    410                 ((kAdjustedClip[b2] >> 3) << 11)
    411                 | ((kAdjustedClip[g2] >> 2) << 5)
    412                 | (kAdjustedClip[r2] >> 3);
    413 
    414             dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
    415         }
    416 
    417         src_y += src.mWidth;
    418 
    419         if (y & 1) {
    420             src_u += src.mWidth;
    421         }
    422 
    423         dst_ptr += dst.mWidth / 2;
    424     }
    425 
    426     return OK;
    427 }
    428 
    429 status_t ColorConverter::convertTIYUV420PackedSemiPlanar(
    430         const BitmapParams &src, const BitmapParams &dst) {
    431     uint8_t *kAdjustedClip = initClip();
    432 
    433     if (!((dst.mWidth & 3) == 0
    434             && (src.mCropLeft & 1) == 0
    435             && src.cropWidth() == dst.cropWidth()
    436             && src.cropHeight() == dst.cropHeight())) {
    437         return ERROR_UNSUPPORTED;
    438     }
    439 
    440     uint32_t *dst_ptr = (uint32_t *)dst.mBits
    441         + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
    442 
    443     const uint8_t *src_y = (const uint8_t *)src.mBits;
    444 
    445     const uint8_t *src_u =
    446         (const uint8_t *)src_y + src.mWidth * (src.mHeight - src.mCropTop / 2);
    447 
    448     for (size_t y = 0; y < src.cropHeight(); ++y) {
    449         for (size_t x = 0; x < src.cropWidth(); x += 2) {
    450             signed y1 = (signed)src_y[x] - 16;
    451             signed y2 = (signed)src_y[x + 1] - 16;
    452 
    453             signed u = (signed)src_u[x & ~1] - 128;
    454             signed v = (signed)src_u[(x & ~1) + 1] - 128;
    455 
    456             signed u_b = u * 517;
    457             signed u_g = -u * 100;
    458             signed v_g = -v * 208;
    459             signed v_r = v * 409;
    460 
    461             signed tmp1 = y1 * 298;
    462             signed b1 = (tmp1 + u_b) / 256;
    463             signed g1 = (tmp1 + v_g + u_g) / 256;
    464             signed r1 = (tmp1 + v_r) / 256;
    465 
    466             signed tmp2 = y2 * 298;
    467             signed b2 = (tmp2 + u_b) / 256;
    468             signed g2 = (tmp2 + v_g + u_g) / 256;
    469             signed r2 = (tmp2 + v_r) / 256;
    470 
    471             uint32_t rgb1 =
    472                 ((kAdjustedClip[r1] >> 3) << 11)
    473                 | ((kAdjustedClip[g1] >> 2) << 5)
    474                 | (kAdjustedClip[b1] >> 3);
    475 
    476             uint32_t rgb2 =
    477                 ((kAdjustedClip[r2] >> 3) << 11)
    478                 | ((kAdjustedClip[g2] >> 2) << 5)
    479                 | (kAdjustedClip[b2] >> 3);
    480 
    481             dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
    482         }
    483 
    484         src_y += src.mWidth;
    485 
    486         if (y & 1) {
    487             src_u += src.mWidth;
    488         }
    489 
    490         dst_ptr += dst.mWidth / 2;
    491     }
    492 
    493     return OK;
    494 }
    495 
    496 uint8_t *ColorConverter::initClip() {
    497     static const signed kClipMin = -278;
    498     static const signed kClipMax = 535;
    499 
    500     if (mClip == NULL) {
    501         mClip = new uint8_t[kClipMax - kClipMin + 1];
    502 
    503         for (signed i = kClipMin; i <= kClipMax; ++i) {
    504             mClip[i - kClipMin] = (i < 0) ? 0 : (i > 255) ? 255 : (uint8_t)i;
    505         }
    506     }
    507 
    508     return &mClip[-kClipMin];
    509 }
    510 
    511 }  // namespace android
    512