/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //#define LOG_NDEBUG 0 #define LOG_TAG "ColorConverter" #include #include #include #include namespace android { ColorConverter::ColorConverter( OMX_COLOR_FORMATTYPE from, OMX_COLOR_FORMATTYPE to) : mSrcFormat(from), mDstFormat(to), mClip(NULL) { } ColorConverter::~ColorConverter() { delete[] mClip; mClip = NULL; } bool ColorConverter::isValid() const { if (mDstFormat != OMX_COLOR_Format16bitRGB565) { return false; } switch (mSrcFormat) { case OMX_COLOR_FormatYUV420Planar: case OMX_COLOR_FormatCbYCrY: case OMX_QCOM_COLOR_FormatYVU420SemiPlanar: case OMX_COLOR_FormatYUV420SemiPlanar: case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar: return true; default: return false; } } ColorConverter::BitmapParams::BitmapParams( void *bits, size_t width, size_t height, size_t cropLeft, size_t cropTop, size_t cropRight, size_t cropBottom) : mBits(bits), mWidth(width), mHeight(height), mCropLeft(cropLeft), mCropTop(cropTop), mCropRight(cropRight), mCropBottom(cropBottom) { } size_t ColorConverter::BitmapParams::cropWidth() const { return mCropRight - mCropLeft + 1; } size_t ColorConverter::BitmapParams::cropHeight() const { return mCropBottom - mCropTop + 1; } status_t ColorConverter::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) { if (mDstFormat != OMX_COLOR_Format16bitRGB565) { return ERROR_UNSUPPORTED; } BitmapParams src( const_cast(srcBits), srcWidth, srcHeight, srcCropLeft, srcCropTop, srcCropRight, srcCropBottom); BitmapParams dst( dstBits, dstWidth, dstHeight, dstCropLeft, dstCropTop, dstCropRight, dstCropBottom); status_t err; switch (mSrcFormat) { case OMX_COLOR_FormatYUV420Planar: err = convertYUV420Planar(src, dst); break; case OMX_COLOR_FormatCbYCrY: err = convertCbYCrY(src, dst); break; case OMX_QCOM_COLOR_FormatYVU420SemiPlanar: err = convertQCOMYUV420SemiPlanar(src, dst); break; case OMX_COLOR_FormatYUV420SemiPlanar: err = convertYUV420SemiPlanar(src, dst); break; case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar: err = convertTIYUV420PackedSemiPlanar(src, dst); break; default: { CHECK(!"Should not be here. Unknown color conversion."); break; } } return err; } status_t ColorConverter::convertCbYCrY( const BitmapParams &src, const BitmapParams &dst) { // XXX Untested uint8_t *kAdjustedClip = initClip(); if (!((src.mCropLeft & 1) == 0 && src.cropWidth() == dst.cropWidth() && src.cropHeight() == dst.cropHeight())) { return ERROR_UNSUPPORTED; } uint16_t *dst_ptr = (uint16_t *)dst.mBits + dst.mCropTop * dst.mWidth + dst.mCropLeft; const uint8_t *src_ptr = (const uint8_t *)src.mBits + (src.mCropTop * dst.mWidth + src.mCropLeft) * 2; for (size_t y = 0; y < src.cropHeight(); ++y) { for (size_t x = 0; x < src.cropWidth(); x += 2) { signed y1 = (signed)src_ptr[2 * x + 1] - 16; signed y2 = (signed)src_ptr[2 * x + 3] - 16; signed u = (signed)src_ptr[2 * x] - 128; signed v = (signed)src_ptr[2 * x + 2] - 128; signed u_b = u * 517; signed u_g = -u * 100; signed v_g = -v * 208; signed v_r = v * 409; signed tmp1 = y1 * 298; signed b1 = (tmp1 + u_b) / 256; signed g1 = (tmp1 + v_g + u_g) / 256; signed r1 = (tmp1 + v_r) / 256; signed tmp2 = y2 * 298; signed b2 = (tmp2 + u_b) / 256; signed g2 = (tmp2 + v_g + u_g) / 256; signed r2 = (tmp2 + v_r) / 256; uint32_t rgb1 = ((kAdjustedClip[r1] >> 3) << 11) | ((kAdjustedClip[g1] >> 2) << 5) | (kAdjustedClip[b1] >> 3); uint32_t rgb2 = ((kAdjustedClip[r2] >> 3) << 11) | ((kAdjustedClip[g2] >> 2) << 5) | (kAdjustedClip[b2] >> 3); if (x + 1 < src.cropWidth()) { *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1; } else { dst_ptr[x] = rgb1; } } src_ptr += src.mWidth * 2; dst_ptr += dst.mWidth; } return OK; } status_t ColorConverter::convertYUV420Planar( const BitmapParams &src, const BitmapParams &dst) { if (!((src.mCropLeft & 1) == 0 && src.cropWidth() == dst.cropWidth() && src.cropHeight() == dst.cropHeight())) { return ERROR_UNSUPPORTED; } uint8_t *kAdjustedClip = initClip(); uint16_t *dst_ptr = (uint16_t *)dst.mBits + dst.mCropTop * dst.mWidth + dst.mCropLeft; const uint8_t *src_y = (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft; const uint8_t *src_u = (const uint8_t *)src_y + src.mWidth * src.mHeight + src.mCropTop * (src.mWidth / 2) + src.mCropLeft / 2; const uint8_t *src_v = src_u + (src.mWidth / 2) * (src.mHeight / 2); for (size_t y = 0; y < src.cropHeight(); ++y) { for (size_t x = 0; x < src.cropWidth(); x += 2) { // B = 1.164 * (Y - 16) + 2.018 * (U - 128) // G = 1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128) // R = 1.164 * (Y - 16) + 1.596 * (V - 128) // B = 298/256 * (Y - 16) + 517/256 * (U - 128) // G = .................. - 208/256 * (V - 128) - 100/256 * (U - 128) // R = .................. + 409/256 * (V - 128) // min_B = (298 * (- 16) + 517 * (- 128)) / 256 = -277 // min_G = (298 * (- 16) - 208 * (255 - 128) - 100 * (255 - 128)) / 256 = -172 // min_R = (298 * (- 16) + 409 * (- 128)) / 256 = -223 // max_B = (298 * (255 - 16) + 517 * (255 - 128)) / 256 = 534 // max_G = (298 * (255 - 16) - 208 * (- 128) - 100 * (- 128)) / 256 = 432 // max_R = (298 * (255 - 16) + 409 * (255 - 128)) / 256 = 481 // clip range -278 .. 535 signed y1 = (signed)src_y[x] - 16; signed y2 = (signed)src_y[x + 1] - 16; signed u = (signed)src_u[x / 2] - 128; signed v = (signed)src_v[x / 2] - 128; signed u_b = u * 517; signed u_g = -u * 100; signed v_g = -v * 208; signed v_r = v * 409; signed tmp1 = y1 * 298; signed b1 = (tmp1 + u_b) / 256; signed g1 = (tmp1 + v_g + u_g) / 256; signed r1 = (tmp1 + v_r) / 256; signed tmp2 = y2 * 298; signed b2 = (tmp2 + u_b) / 256; signed g2 = (tmp2 + v_g + u_g) / 256; signed r2 = (tmp2 + v_r) / 256; uint32_t rgb1 = ((kAdjustedClip[r1] >> 3) << 11) | ((kAdjustedClip[g1] >> 2) << 5) | (kAdjustedClip[b1] >> 3); uint32_t rgb2 = ((kAdjustedClip[r2] >> 3) << 11) | ((kAdjustedClip[g2] >> 2) << 5) | (kAdjustedClip[b2] >> 3); if (x + 1 < src.cropWidth()) { *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1; } else { dst_ptr[x] = rgb1; } } src_y += src.mWidth; if (y & 1) { src_u += src.mWidth / 2; src_v += src.mWidth / 2; } dst_ptr += dst.mWidth; } return OK; } status_t ColorConverter::convertQCOMYUV420SemiPlanar( const BitmapParams &src, const BitmapParams &dst) { uint8_t *kAdjustedClip = initClip(); if (!((src.mCropLeft & 1) == 0 && src.cropWidth() == dst.cropWidth() && src.cropHeight() == dst.cropHeight())) { return ERROR_UNSUPPORTED; } uint16_t *dst_ptr = (uint16_t *)dst.mBits + dst.mCropTop * dst.mWidth + dst.mCropLeft; const uint8_t *src_y = (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft; const uint8_t *src_u = (const uint8_t *)src_y + src.mWidth * src.mHeight + src.mCropTop * src.mWidth + src.mCropLeft; for (size_t y = 0; y < src.cropHeight(); ++y) { for (size_t x = 0; x < src.cropWidth(); x += 2) { signed y1 = (signed)src_y[x] - 16; signed y2 = (signed)src_y[x + 1] - 16; signed u = (signed)src_u[x & ~1] - 128; signed v = (signed)src_u[(x & ~1) + 1] - 128; signed u_b = u * 517; signed u_g = -u * 100; signed v_g = -v * 208; signed v_r = v * 409; signed tmp1 = y1 * 298; signed b1 = (tmp1 + u_b) / 256; signed g1 = (tmp1 + v_g + u_g) / 256; signed r1 = (tmp1 + v_r) / 256; signed tmp2 = y2 * 298; signed b2 = (tmp2 + u_b) / 256; signed g2 = (tmp2 + v_g + u_g) / 256; signed r2 = (tmp2 + v_r) / 256; uint32_t rgb1 = ((kAdjustedClip[b1] >> 3) << 11) | ((kAdjustedClip[g1] >> 2) << 5) | (kAdjustedClip[r1] >> 3); uint32_t rgb2 = ((kAdjustedClip[b2] >> 3) << 11) | ((kAdjustedClip[g2] >> 2) << 5) | (kAdjustedClip[r2] >> 3); if (x + 1 < src.cropWidth()) { *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1; } else { dst_ptr[x] = rgb1; } } src_y += src.mWidth; if (y & 1) { src_u += src.mWidth; } dst_ptr += dst.mWidth; } return OK; } status_t ColorConverter::convertYUV420SemiPlanar( const BitmapParams &src, const BitmapParams &dst) { // XXX Untested uint8_t *kAdjustedClip = initClip(); if (!((src.mCropLeft & 1) == 0 && src.cropWidth() == dst.cropWidth() && src.cropHeight() == dst.cropHeight())) { return ERROR_UNSUPPORTED; } uint16_t *dst_ptr = (uint16_t *)dst.mBits + dst.mCropTop * dst.mWidth + dst.mCropLeft; const uint8_t *src_y = (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft; const uint8_t *src_u = (const uint8_t *)src_y + src.mWidth * src.mHeight + src.mCropTop * src.mWidth + src.mCropLeft; for (size_t y = 0; y < src.cropHeight(); ++y) { for (size_t x = 0; x < src.cropWidth(); x += 2) { signed y1 = (signed)src_y[x] - 16; signed y2 = (signed)src_y[x + 1] - 16; signed v = (signed)src_u[x & ~1] - 128; signed u = (signed)src_u[(x & ~1) + 1] - 128; signed u_b = u * 517; signed u_g = -u * 100; signed v_g = -v * 208; signed v_r = v * 409; signed tmp1 = y1 * 298; signed b1 = (tmp1 + u_b) / 256; signed g1 = (tmp1 + v_g + u_g) / 256; signed r1 = (tmp1 + v_r) / 256; signed tmp2 = y2 * 298; signed b2 = (tmp2 + u_b) / 256; signed g2 = (tmp2 + v_g + u_g) / 256; signed r2 = (tmp2 + v_r) / 256; uint32_t rgb1 = ((kAdjustedClip[b1] >> 3) << 11) | ((kAdjustedClip[g1] >> 2) << 5) | (kAdjustedClip[r1] >> 3); uint32_t rgb2 = ((kAdjustedClip[b2] >> 3) << 11) | ((kAdjustedClip[g2] >> 2) << 5) | (kAdjustedClip[r2] >> 3); if (x + 1 < src.cropWidth()) { *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1; } else { dst_ptr[x] = rgb1; } } src_y += src.mWidth; if (y & 1) { src_u += src.mWidth; } dst_ptr += dst.mWidth; } return OK; } status_t ColorConverter::convertTIYUV420PackedSemiPlanar( const BitmapParams &src, const BitmapParams &dst) { uint8_t *kAdjustedClip = initClip(); if (!((src.mCropLeft & 1) == 0 && src.cropWidth() == dst.cropWidth() && src.cropHeight() == dst.cropHeight())) { return ERROR_UNSUPPORTED; } uint16_t *dst_ptr = (uint16_t *)dst.mBits + dst.mCropTop * dst.mWidth + dst.mCropLeft; const uint8_t *src_y = (const uint8_t *)src.mBits; const uint8_t *src_u = (const uint8_t *)src_y + src.mWidth * (src.mHeight - src.mCropTop / 2); for (size_t y = 0; y < src.cropHeight(); ++y) { for (size_t x = 0; x < src.cropWidth(); x += 2) { signed y1 = (signed)src_y[x] - 16; signed y2 = (signed)src_y[x + 1] - 16; signed u = (signed)src_u[x & ~1] - 128; signed v = (signed)src_u[(x & ~1) + 1] - 128; signed u_b = u * 517; signed u_g = -u * 100; signed v_g = -v * 208; signed v_r = v * 409; signed tmp1 = y1 * 298; signed b1 = (tmp1 + u_b) / 256; signed g1 = (tmp1 + v_g + u_g) / 256; signed r1 = (tmp1 + v_r) / 256; signed tmp2 = y2 * 298; signed b2 = (tmp2 + u_b) / 256; signed g2 = (tmp2 + v_g + u_g) / 256; signed r2 = (tmp2 + v_r) / 256; uint32_t rgb1 = ((kAdjustedClip[r1] >> 3) << 11) | ((kAdjustedClip[g1] >> 2) << 5) | (kAdjustedClip[b1] >> 3); uint32_t rgb2 = ((kAdjustedClip[r2] >> 3) << 11) | ((kAdjustedClip[g2] >> 2) << 5) | (kAdjustedClip[b2] >> 3); if (x + 1 < src.cropWidth()) { *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1; } else { dst_ptr[x] = rgb1; } } src_y += src.mWidth; if (y & 1) { src_u += src.mWidth; } dst_ptr += dst.mWidth; } return OK; } uint8_t *ColorConverter::initClip() { static const signed kClipMin = -278; static const signed kClipMax = 535; if (mClip == NULL) { mClip = new uint8_t[kClipMax - kClipMin + 1]; for (signed i = kClipMin; i <= kClipMax; ++i) { mClip[i - kClipMin] = (i < 0) ? 0 : (i > 255) ? 255 : (uint8_t)i; } } return &mClip[-kClipMin]; } } // namespace android