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