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