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