1/* 2 * Copyright 2012 The LibYuv Project Authors. All rights reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11#include "libyuv/convert_from.h" 12 13#include "libyuv/basic_types.h" 14#include "libyuv/convert.h" // For I420Copy 15#include "libyuv/cpu_id.h" 16#include "libyuv/planar_functions.h" 17#include "libyuv/rotate.h" 18#include "libyuv/scale.h" // For ScalePlane() 19#include "libyuv/video_common.h" 20#include "libyuv/row.h" 21 22#ifdef __cplusplus 23namespace libyuv { 24extern "C" { 25#endif 26 27#define SUBSAMPLE(v, a, s) (v < 0) ? (-((-v + a) >> s)) : ((v + a) >> s) 28static __inline int Abs(int v) { 29 return v >= 0 ? v : -v; 30} 31 32// I420 To any I4xx YUV format with mirroring. 33static int I420ToI4xx(const uint8* src_y, int src_stride_y, 34 const uint8* src_u, int src_stride_u, 35 const uint8* src_v, int src_stride_v, 36 uint8* dst_y, int dst_stride_y, 37 uint8* dst_u, int dst_stride_u, 38 uint8* dst_v, int dst_stride_v, 39 int src_y_width, int src_y_height, 40 int dst_uv_width, int dst_uv_height) { 41 const int dst_y_width = Abs(src_y_width); 42 const int dst_y_height = Abs(src_y_height); 43 const int src_uv_width = SUBSAMPLE(src_y_width, 1, 1); 44 const int src_uv_height = SUBSAMPLE(src_y_height, 1, 1); 45 if (src_y_width == 0 || src_y_height == 0 || 46 dst_uv_width <= 0 || dst_uv_height <= 0) { 47 return -1; 48 } 49 ScalePlane(src_y, src_stride_y, src_y_width, src_y_height, 50 dst_y, dst_stride_y, dst_y_width, dst_y_height, 51 kFilterBilinear); 52 ScalePlane(src_u, src_stride_u, src_uv_width, src_uv_height, 53 dst_u, dst_stride_u, dst_uv_width, dst_uv_height, 54 kFilterBilinear); 55 ScalePlane(src_v, src_stride_v, src_uv_width, src_uv_height, 56 dst_v, dst_stride_v, dst_uv_width, dst_uv_height, 57 kFilterBilinear); 58 return 0; 59} 60 61// 420 chroma is 1/2 width, 1/2 height 62// 422 chroma is 1/2 width, 1x height 63LIBYUV_API 64int I420ToI422(const uint8* src_y, int src_stride_y, 65 const uint8* src_u, int src_stride_u, 66 const uint8* src_v, int src_stride_v, 67 uint8* dst_y, int dst_stride_y, 68 uint8* dst_u, int dst_stride_u, 69 uint8* dst_v, int dst_stride_v, 70 int width, int height) { 71 const int dst_uv_width = (Abs(width) + 1) >> 1; 72 const int dst_uv_height = Abs(height); 73 return I420ToI4xx(src_y, src_stride_y, 74 src_u, src_stride_u, 75 src_v, src_stride_v, 76 dst_y, dst_stride_y, 77 dst_u, dst_stride_u, 78 dst_v, dst_stride_v, 79 width, height, 80 dst_uv_width, dst_uv_height); 81} 82 83// 420 chroma is 1/2 width, 1/2 height 84// 444 chroma is 1x width, 1x height 85LIBYUV_API 86int I420ToI444(const uint8* src_y, int src_stride_y, 87 const uint8* src_u, int src_stride_u, 88 const uint8* src_v, int src_stride_v, 89 uint8* dst_y, int dst_stride_y, 90 uint8* dst_u, int dst_stride_u, 91 uint8* dst_v, int dst_stride_v, 92 int width, int height) { 93 const int dst_uv_width = Abs(width); 94 const int dst_uv_height = Abs(height); 95 return I420ToI4xx(src_y, src_stride_y, 96 src_u, src_stride_u, 97 src_v, src_stride_v, 98 dst_y, dst_stride_y, 99 dst_u, dst_stride_u, 100 dst_v, dst_stride_v, 101 width, height, 102 dst_uv_width, dst_uv_height); 103} 104 105// 420 chroma is 1/2 width, 1/2 height 106// 411 chroma is 1/4 width, 1x height 107LIBYUV_API 108int I420ToI411(const uint8* src_y, int src_stride_y, 109 const uint8* src_u, int src_stride_u, 110 const uint8* src_v, int src_stride_v, 111 uint8* dst_y, int dst_stride_y, 112 uint8* dst_u, int dst_stride_u, 113 uint8* dst_v, int dst_stride_v, 114 int width, int height) { 115 const int dst_uv_width = (Abs(width) + 3) >> 2; 116 const int dst_uv_height = Abs(height); 117 return I420ToI4xx(src_y, src_stride_y, 118 src_u, src_stride_u, 119 src_v, src_stride_v, 120 dst_y, dst_stride_y, 121 dst_u, dst_stride_u, 122 dst_v, dst_stride_v, 123 width, height, 124 dst_uv_width, dst_uv_height); 125} 126 127// Copy to I400. Source can be I420,422,444,400,NV12,NV21 128LIBYUV_API 129int I400Copy(const uint8* src_y, int src_stride_y, 130 uint8* dst_y, int dst_stride_y, 131 int width, int height) { 132 if (!src_y || !dst_y || 133 width <= 0 || height == 0) { 134 return -1; 135 } 136 // Negative height means invert the image. 137 if (height < 0) { 138 height = -height; 139 src_y = src_y + (height - 1) * src_stride_y; 140 src_stride_y = -src_stride_y; 141 } 142 CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height); 143 return 0; 144} 145 146LIBYUV_API 147int I422ToYUY2(const uint8* src_y, int src_stride_y, 148 const uint8* src_u, int src_stride_u, 149 const uint8* src_v, int src_stride_v, 150 uint8* dst_yuy2, int dst_stride_yuy2, 151 int width, int height) { 152 int y; 153 void (*I422ToYUY2Row)(const uint8* src_y, const uint8* src_u, 154 const uint8* src_v, uint8* dst_yuy2, int width) = 155 I422ToYUY2Row_C; 156 if (!src_y || !src_u || !src_v || !dst_yuy2 || 157 width <= 0 || height == 0) { 158 return -1; 159 } 160 // Negative height means invert the image. 161 if (height < 0) { 162 height = -height; 163 dst_yuy2 = dst_yuy2 + (height - 1) * dst_stride_yuy2; 164 dst_stride_yuy2 = -dst_stride_yuy2; 165 } 166 // Coalesce rows. 167 if (src_stride_y == width && 168 src_stride_u * 2 == width && 169 src_stride_v * 2 == width && 170 dst_stride_yuy2 == width * 2) { 171 width *= height; 172 height = 1; 173 src_stride_y = src_stride_u = src_stride_v = dst_stride_yuy2 = 0; 174 } 175#if defined(HAS_I422TOYUY2ROW_SSE2) 176 if (TestCpuFlag(kCpuHasSSE2)) { 177 I422ToYUY2Row = I422ToYUY2Row_Any_SSE2; 178 if (IS_ALIGNED(width, 16)) { 179 I422ToYUY2Row = I422ToYUY2Row_SSE2; 180 } 181 } 182#endif 183#if defined(HAS_I422TOYUY2ROW_NEON) 184 if (TestCpuFlag(kCpuHasNEON)) { 185 I422ToYUY2Row = I422ToYUY2Row_Any_NEON; 186 if (IS_ALIGNED(width, 16)) { 187 I422ToYUY2Row = I422ToYUY2Row_NEON; 188 } 189 } 190#endif 191 192 for (y = 0; y < height; ++y) { 193 I422ToYUY2Row(src_y, src_u, src_v, dst_yuy2, width); 194 src_y += src_stride_y; 195 src_u += src_stride_u; 196 src_v += src_stride_v; 197 dst_yuy2 += dst_stride_yuy2; 198 } 199 return 0; 200} 201 202LIBYUV_API 203int I420ToYUY2(const uint8* src_y, int src_stride_y, 204 const uint8* src_u, int src_stride_u, 205 const uint8* src_v, int src_stride_v, 206 uint8* dst_yuy2, int dst_stride_yuy2, 207 int width, int height) { 208 int y; 209 void (*I422ToYUY2Row)(const uint8* src_y, const uint8* src_u, 210 const uint8* src_v, uint8* dst_yuy2, int width) = 211 I422ToYUY2Row_C; 212 if (!src_y || !src_u || !src_v || !dst_yuy2 || 213 width <= 0 || height == 0) { 214 return -1; 215 } 216 // Negative height means invert the image. 217 if (height < 0) { 218 height = -height; 219 dst_yuy2 = dst_yuy2 + (height - 1) * dst_stride_yuy2; 220 dst_stride_yuy2 = -dst_stride_yuy2; 221 } 222#if defined(HAS_I422TOYUY2ROW_SSE2) 223 if (TestCpuFlag(kCpuHasSSE2)) { 224 I422ToYUY2Row = I422ToYUY2Row_Any_SSE2; 225 if (IS_ALIGNED(width, 16)) { 226 I422ToYUY2Row = I422ToYUY2Row_SSE2; 227 } 228 } 229#endif 230#if defined(HAS_I422TOYUY2ROW_NEON) 231 if (TestCpuFlag(kCpuHasNEON)) { 232 I422ToYUY2Row = I422ToYUY2Row_Any_NEON; 233 if (IS_ALIGNED(width, 16)) { 234 I422ToYUY2Row = I422ToYUY2Row_NEON; 235 } 236 } 237#endif 238 239 for (y = 0; y < height - 1; y += 2) { 240 I422ToYUY2Row(src_y, src_u, src_v, dst_yuy2, width); 241 I422ToYUY2Row(src_y + src_stride_y, src_u, src_v, 242 dst_yuy2 + dst_stride_yuy2, width); 243 src_y += src_stride_y * 2; 244 src_u += src_stride_u; 245 src_v += src_stride_v; 246 dst_yuy2 += dst_stride_yuy2 * 2; 247 } 248 if (height & 1) { 249 I422ToYUY2Row(src_y, src_u, src_v, dst_yuy2, width); 250 } 251 return 0; 252} 253 254LIBYUV_API 255int I422ToUYVY(const uint8* src_y, int src_stride_y, 256 const uint8* src_u, int src_stride_u, 257 const uint8* src_v, int src_stride_v, 258 uint8* dst_uyvy, int dst_stride_uyvy, 259 int width, int height) { 260 int y; 261 void (*I422ToUYVYRow)(const uint8* src_y, const uint8* src_u, 262 const uint8* src_v, uint8* dst_uyvy, int width) = 263 I422ToUYVYRow_C; 264 if (!src_y || !src_u || !src_v || !dst_uyvy || 265 width <= 0 || height == 0) { 266 return -1; 267 } 268 // Negative height means invert the image. 269 if (height < 0) { 270 height = -height; 271 dst_uyvy = dst_uyvy + (height - 1) * dst_stride_uyvy; 272 dst_stride_uyvy = -dst_stride_uyvy; 273 } 274 // Coalesce rows. 275 if (src_stride_y == width && 276 src_stride_u * 2 == width && 277 src_stride_v * 2 == width && 278 dst_stride_uyvy == width * 2) { 279 width *= height; 280 height = 1; 281 src_stride_y = src_stride_u = src_stride_v = dst_stride_uyvy = 0; 282 } 283#if defined(HAS_I422TOUYVYROW_SSE2) 284 if (TestCpuFlag(kCpuHasSSE2)) { 285 I422ToUYVYRow = I422ToUYVYRow_Any_SSE2; 286 if (IS_ALIGNED(width, 16)) { 287 I422ToUYVYRow = I422ToUYVYRow_SSE2; 288 } 289 } 290#endif 291#if defined(HAS_I422TOUYVYROW_NEON) 292 if (TestCpuFlag(kCpuHasNEON)) { 293 I422ToUYVYRow = I422ToUYVYRow_Any_NEON; 294 if (IS_ALIGNED(width, 16)) { 295 I422ToUYVYRow = I422ToUYVYRow_NEON; 296 } 297 } 298#endif 299 300 for (y = 0; y < height; ++y) { 301 I422ToUYVYRow(src_y, src_u, src_v, dst_uyvy, width); 302 src_y += src_stride_y; 303 src_u += src_stride_u; 304 src_v += src_stride_v; 305 dst_uyvy += dst_stride_uyvy; 306 } 307 return 0; 308} 309 310LIBYUV_API 311int I420ToUYVY(const uint8* src_y, int src_stride_y, 312 const uint8* src_u, int src_stride_u, 313 const uint8* src_v, int src_stride_v, 314 uint8* dst_uyvy, int dst_stride_uyvy, 315 int width, int height) { 316 int y; 317 void (*I422ToUYVYRow)(const uint8* src_y, const uint8* src_u, 318 const uint8* src_v, uint8* dst_uyvy, int width) = 319 I422ToUYVYRow_C; 320 if (!src_y || !src_u || !src_v || !dst_uyvy || 321 width <= 0 || height == 0) { 322 return -1; 323 } 324 // Negative height means invert the image. 325 if (height < 0) { 326 height = -height; 327 dst_uyvy = dst_uyvy + (height - 1) * dst_stride_uyvy; 328 dst_stride_uyvy = -dst_stride_uyvy; 329 } 330#if defined(HAS_I422TOUYVYROW_SSE2) 331 if (TestCpuFlag(kCpuHasSSE2)) { 332 I422ToUYVYRow = I422ToUYVYRow_Any_SSE2; 333 if (IS_ALIGNED(width, 16)) { 334 I422ToUYVYRow = I422ToUYVYRow_SSE2; 335 } 336 } 337#endif 338#if defined(HAS_I422TOUYVYROW_NEON) 339 if (TestCpuFlag(kCpuHasNEON)) { 340 I422ToUYVYRow = I422ToUYVYRow_Any_NEON; 341 if (IS_ALIGNED(width, 16)) { 342 I422ToUYVYRow = I422ToUYVYRow_NEON; 343 } 344 } 345#endif 346 347 for (y = 0; y < height - 1; y += 2) { 348 I422ToUYVYRow(src_y, src_u, src_v, dst_uyvy, width); 349 I422ToUYVYRow(src_y + src_stride_y, src_u, src_v, 350 dst_uyvy + dst_stride_uyvy, width); 351 src_y += src_stride_y * 2; 352 src_u += src_stride_u; 353 src_v += src_stride_v; 354 dst_uyvy += dst_stride_uyvy * 2; 355 } 356 if (height & 1) { 357 I422ToUYVYRow(src_y, src_u, src_v, dst_uyvy, width); 358 } 359 return 0; 360} 361 362LIBYUV_API 363int I420ToNV12(const uint8* src_y, int src_stride_y, 364 const uint8* src_u, int src_stride_u, 365 const uint8* src_v, int src_stride_v, 366 uint8* dst_y, int dst_stride_y, 367 uint8* dst_uv, int dst_stride_uv, 368 int width, int height) { 369 int y; 370 void (*MergeUVRow_)(const uint8* src_u, const uint8* src_v, uint8* dst_uv, 371 int width) = MergeUVRow_C; 372 // Coalesce rows. 373 int halfwidth = (width + 1) >> 1; 374 int halfheight = (height + 1) >> 1; 375 if (!src_y || !src_u || !src_v || !dst_y || !dst_uv || 376 width <= 0 || height == 0) { 377 return -1; 378 } 379 // Negative height means invert the image. 380 if (height < 0) { 381 height = -height; 382 halfheight = (height + 1) >> 1; 383 dst_y = dst_y + (height - 1) * dst_stride_y; 384 dst_uv = dst_uv + (halfheight - 1) * dst_stride_uv; 385 dst_stride_y = -dst_stride_y; 386 dst_stride_uv = -dst_stride_uv; 387 } 388 if (src_stride_y == width && 389 dst_stride_y == width) { 390 width *= height; 391 height = 1; 392 src_stride_y = dst_stride_y = 0; 393 } 394 // Coalesce rows. 395 if (src_stride_u == halfwidth && 396 src_stride_v == halfwidth && 397 dst_stride_uv == halfwidth * 2) { 398 halfwidth *= halfheight; 399 halfheight = 1; 400 src_stride_u = src_stride_v = dst_stride_uv = 0; 401 } 402#if defined(HAS_MERGEUVROW_SSE2) 403 if (TestCpuFlag(kCpuHasSSE2)) { 404 MergeUVRow_ = MergeUVRow_Any_SSE2; 405 if (IS_ALIGNED(halfwidth, 16)) { 406 MergeUVRow_ = MergeUVRow_SSE2; 407 } 408 } 409#endif 410#if defined(HAS_MERGEUVROW_AVX2) 411 if (TestCpuFlag(kCpuHasAVX2)) { 412 MergeUVRow_ = MergeUVRow_Any_AVX2; 413 if (IS_ALIGNED(halfwidth, 32)) { 414 MergeUVRow_ = MergeUVRow_AVX2; 415 } 416 } 417#endif 418#if defined(HAS_MERGEUVROW_NEON) 419 if (TestCpuFlag(kCpuHasNEON)) { 420 MergeUVRow_ = MergeUVRow_Any_NEON; 421 if (IS_ALIGNED(halfwidth, 16)) { 422 MergeUVRow_ = MergeUVRow_NEON; 423 } 424 } 425#endif 426 427 CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height); 428 for (y = 0; y < halfheight; ++y) { 429 // Merge a row of U and V into a row of UV. 430 MergeUVRow_(src_u, src_v, dst_uv, halfwidth); 431 src_u += src_stride_u; 432 src_v += src_stride_v; 433 dst_uv += dst_stride_uv; 434 } 435 return 0; 436} 437 438LIBYUV_API 439int I420ToNV21(const uint8* src_y, int src_stride_y, 440 const uint8* src_u, int src_stride_u, 441 const uint8* src_v, int src_stride_v, 442 uint8* dst_y, int dst_stride_y, 443 uint8* dst_vu, int dst_stride_vu, 444 int width, int height) { 445 return I420ToNV12(src_y, src_stride_y, 446 src_v, src_stride_v, 447 src_u, src_stride_u, 448 dst_y, dst_stride_y, 449 dst_vu, dst_stride_vu, 450 width, height); 451} 452 453// Convert I422 to RGBA with matrix 454static int I420ToRGBAMatrix(const uint8* src_y, int src_stride_y, 455 const uint8* src_u, int src_stride_u, 456 const uint8* src_v, int src_stride_v, 457 uint8* dst_rgba, int dst_stride_rgba, 458 const struct YuvConstants* yuvconstants, 459 int width, int height) { 460 int y; 461 void (*I422ToRGBARow)(const uint8* y_buf, 462 const uint8* u_buf, 463 const uint8* v_buf, 464 uint8* rgb_buf, 465 const struct YuvConstants* yuvconstants, 466 int width) = I422ToRGBARow_C; 467 if (!src_y || !src_u || !src_v || !dst_rgba || 468 width <= 0 || height == 0) { 469 return -1; 470 } 471 // Negative height means invert the image. 472 if (height < 0) { 473 height = -height; 474 dst_rgba = dst_rgba + (height - 1) * dst_stride_rgba; 475 dst_stride_rgba = -dst_stride_rgba; 476 } 477#if defined(HAS_I422TORGBAROW_SSSE3) 478 if (TestCpuFlag(kCpuHasSSSE3)) { 479 I422ToRGBARow = I422ToRGBARow_Any_SSSE3; 480 if (IS_ALIGNED(width, 8)) { 481 I422ToRGBARow = I422ToRGBARow_SSSE3; 482 } 483 } 484#endif 485#if defined(HAS_I422TORGBAROW_AVX2) 486 if (TestCpuFlag(kCpuHasAVX2)) { 487 I422ToRGBARow = I422ToRGBARow_Any_AVX2; 488 if (IS_ALIGNED(width, 16)) { 489 I422ToRGBARow = I422ToRGBARow_AVX2; 490 } 491 } 492#endif 493#if defined(HAS_I422TORGBAROW_NEON) 494 if (TestCpuFlag(kCpuHasNEON)) { 495 I422ToRGBARow = I422ToRGBARow_Any_NEON; 496 if (IS_ALIGNED(width, 8)) { 497 I422ToRGBARow = I422ToRGBARow_NEON; 498 } 499 } 500#endif 501#if defined(HAS_I422TORGBAROW_DSPR2) 502 if (TestCpuFlag(kCpuHasDSPR2) && IS_ALIGNED(width, 4) && 503 IS_ALIGNED(src_y, 4) && IS_ALIGNED(src_stride_y, 4) && 504 IS_ALIGNED(src_u, 2) && IS_ALIGNED(src_stride_u, 2) && 505 IS_ALIGNED(src_v, 2) && IS_ALIGNED(src_stride_v, 2) && 506 IS_ALIGNED(dst_rgba, 4) && IS_ALIGNED(dst_stride_rgba, 4)) { 507 I422ToRGBARow = I422ToRGBARow_DSPR2; 508 } 509#endif 510 511 for (y = 0; y < height; ++y) { 512 I422ToRGBARow(src_y, src_u, src_v, dst_rgba, yuvconstants, width); 513 dst_rgba += dst_stride_rgba; 514 src_y += src_stride_y; 515 if (y & 1) { 516 src_u += src_stride_u; 517 src_v += src_stride_v; 518 } 519 } 520 return 0; 521} 522 523// Convert I420 to RGBA. 524LIBYUV_API 525int I420ToRGBA(const uint8* src_y, int src_stride_y, 526 const uint8* src_u, int src_stride_u, 527 const uint8* src_v, int src_stride_v, 528 uint8* dst_rgba, int dst_stride_rgba, 529 int width, int height) { 530 return I420ToRGBAMatrix(src_y, src_stride_y, 531 src_u, src_stride_u, 532 src_v, src_stride_v, 533 dst_rgba, dst_stride_rgba, 534 &kYuvI601Constants, 535 width, height); 536} 537 538// Convert I420 to BGRA. 539LIBYUV_API 540int I420ToBGRA(const uint8* src_y, int src_stride_y, 541 const uint8* src_u, int src_stride_u, 542 const uint8* src_v, int src_stride_v, 543 uint8* dst_bgra, int dst_stride_bgra, 544 int width, int height) { 545 return I420ToRGBAMatrix(src_y, src_stride_y, 546 src_v, src_stride_v, // Swap U and V 547 src_u, src_stride_u, 548 dst_bgra, dst_stride_bgra, 549 &kYvuI601Constants, // Use Yvu matrix 550 width, height); 551} 552 553// Convert I420 to RGB24 with matrix 554static int I420ToRGB24Matrix(const uint8* src_y, int src_stride_y, 555 const uint8* src_u, int src_stride_u, 556 const uint8* src_v, int src_stride_v, 557 uint8* dst_rgb24, int dst_stride_rgb24, 558 const struct YuvConstants* yuvconstants, 559 int width, int height) { 560 int y; 561 void (*I422ToRGB24Row)(const uint8* y_buf, 562 const uint8* u_buf, 563 const uint8* v_buf, 564 uint8* rgb_buf, 565 const struct YuvConstants* yuvconstants, 566 int width) = I422ToRGB24Row_C; 567 if (!src_y || !src_u || !src_v || !dst_rgb24 || 568 width <= 0 || height == 0) { 569 return -1; 570 } 571 // Negative height means invert the image. 572 if (height < 0) { 573 height = -height; 574 dst_rgb24 = dst_rgb24 + (height - 1) * dst_stride_rgb24; 575 dst_stride_rgb24 = -dst_stride_rgb24; 576 } 577#if defined(HAS_I422TORGB24ROW_SSSE3) 578 if (TestCpuFlag(kCpuHasSSSE3)) { 579 I422ToRGB24Row = I422ToRGB24Row_Any_SSSE3; 580 if (IS_ALIGNED(width, 8)) { 581 I422ToRGB24Row = I422ToRGB24Row_SSSE3; 582 } 583 } 584#endif 585#if defined(HAS_I422TORGB24ROW_AVX2) 586 if (TestCpuFlag(kCpuHasAVX2)) { 587 I422ToRGB24Row = I422ToRGB24Row_Any_AVX2; 588 if (IS_ALIGNED(width, 16)) { 589 I422ToRGB24Row = I422ToRGB24Row_AVX2; 590 } 591 } 592#endif 593#if defined(HAS_I422TORGB24ROW_NEON) 594 if (TestCpuFlag(kCpuHasNEON)) { 595 I422ToRGB24Row = I422ToRGB24Row_Any_NEON; 596 if (IS_ALIGNED(width, 8)) { 597 I422ToRGB24Row = I422ToRGB24Row_NEON; 598 } 599 } 600#endif 601 602 for (y = 0; y < height; ++y) { 603 I422ToRGB24Row(src_y, src_u, src_v, dst_rgb24, yuvconstants, width); 604 dst_rgb24 += dst_stride_rgb24; 605 src_y += src_stride_y; 606 if (y & 1) { 607 src_u += src_stride_u; 608 src_v += src_stride_v; 609 } 610 } 611 return 0; 612} 613 614// Convert I420 to RGB24. 615LIBYUV_API 616int I420ToRGB24(const uint8* src_y, int src_stride_y, 617 const uint8* src_u, int src_stride_u, 618 const uint8* src_v, int src_stride_v, 619 uint8* dst_rgb24, int dst_stride_rgb24, 620 int width, int height) { 621 return I420ToRGB24Matrix(src_y, src_stride_y, 622 src_u, src_stride_u, 623 src_v, src_stride_v, 624 dst_rgb24, dst_stride_rgb24, 625 &kYuvI601Constants, 626 width, height); 627} 628 629// Convert I420 to RAW. 630LIBYUV_API 631int I420ToRAW(const uint8* src_y, int src_stride_y, 632 const uint8* src_u, int src_stride_u, 633 const uint8* src_v, int src_stride_v, 634 uint8* dst_raw, int dst_stride_raw, 635 int width, int height) { 636 return I420ToRGB24Matrix(src_y, src_stride_y, 637 src_v, src_stride_v, // Swap U and V 638 src_u, src_stride_u, 639 dst_raw, dst_stride_raw, 640 &kYvuI601Constants, // Use Yvu matrix 641 width, height); 642} 643 644// Convert I420 to ARGB1555. 645LIBYUV_API 646int I420ToARGB1555(const uint8* src_y, int src_stride_y, 647 const uint8* src_u, int src_stride_u, 648 const uint8* src_v, int src_stride_v, 649 uint8* dst_argb1555, int dst_stride_argb1555, 650 int width, int height) { 651 int y; 652 void (*I422ToARGB1555Row)(const uint8* y_buf, 653 const uint8* u_buf, 654 const uint8* v_buf, 655 uint8* rgb_buf, 656 const struct YuvConstants* yuvconstants, 657 int width) = I422ToARGB1555Row_C; 658 if (!src_y || !src_u || !src_v || !dst_argb1555 || 659 width <= 0 || height == 0) { 660 return -1; 661 } 662 // Negative height means invert the image. 663 if (height < 0) { 664 height = -height; 665 dst_argb1555 = dst_argb1555 + (height - 1) * dst_stride_argb1555; 666 dst_stride_argb1555 = -dst_stride_argb1555; 667 } 668#if defined(HAS_I422TOARGB1555ROW_SSSE3) 669 if (TestCpuFlag(kCpuHasSSSE3)) { 670 I422ToARGB1555Row = I422ToARGB1555Row_Any_SSSE3; 671 if (IS_ALIGNED(width, 8)) { 672 I422ToARGB1555Row = I422ToARGB1555Row_SSSE3; 673 } 674 } 675#endif 676#if defined(HAS_I422TOARGB1555ROW_AVX2) 677 if (TestCpuFlag(kCpuHasAVX2)) { 678 I422ToARGB1555Row = I422ToARGB1555Row_Any_AVX2; 679 if (IS_ALIGNED(width, 16)) { 680 I422ToARGB1555Row = I422ToARGB1555Row_AVX2; 681 } 682 } 683#endif 684#if defined(HAS_I422TOARGB1555ROW_NEON) 685 if (TestCpuFlag(kCpuHasNEON)) { 686 I422ToARGB1555Row = I422ToARGB1555Row_Any_NEON; 687 if (IS_ALIGNED(width, 8)) { 688 I422ToARGB1555Row = I422ToARGB1555Row_NEON; 689 } 690 } 691#endif 692 693 for (y = 0; y < height; ++y) { 694 I422ToARGB1555Row(src_y, src_u, src_v, dst_argb1555, &kYuvI601Constants, 695 width); 696 dst_argb1555 += dst_stride_argb1555; 697 src_y += src_stride_y; 698 if (y & 1) { 699 src_u += src_stride_u; 700 src_v += src_stride_v; 701 } 702 } 703 return 0; 704} 705 706 707// Convert I420 to ARGB4444. 708LIBYUV_API 709int I420ToARGB4444(const uint8* src_y, int src_stride_y, 710 const uint8* src_u, int src_stride_u, 711 const uint8* src_v, int src_stride_v, 712 uint8* dst_argb4444, int dst_stride_argb4444, 713 int width, int height) { 714 int y; 715 void (*I422ToARGB4444Row)(const uint8* y_buf, 716 const uint8* u_buf, 717 const uint8* v_buf, 718 uint8* rgb_buf, 719 const struct YuvConstants* yuvconstants, 720 int width) = I422ToARGB4444Row_C; 721 if (!src_y || !src_u || !src_v || !dst_argb4444 || 722 width <= 0 || height == 0) { 723 return -1; 724 } 725 // Negative height means invert the image. 726 if (height < 0) { 727 height = -height; 728 dst_argb4444 = dst_argb4444 + (height - 1) * dst_stride_argb4444; 729 dst_stride_argb4444 = -dst_stride_argb4444; 730 } 731#if defined(HAS_I422TOARGB4444ROW_SSSE3) 732 if (TestCpuFlag(kCpuHasSSSE3)) { 733 I422ToARGB4444Row = I422ToARGB4444Row_Any_SSSE3; 734 if (IS_ALIGNED(width, 8)) { 735 I422ToARGB4444Row = I422ToARGB4444Row_SSSE3; 736 } 737 } 738#endif 739#if defined(HAS_I422TOARGB4444ROW_AVX2) 740 if (TestCpuFlag(kCpuHasAVX2)) { 741 I422ToARGB4444Row = I422ToARGB4444Row_Any_AVX2; 742 if (IS_ALIGNED(width, 16)) { 743 I422ToARGB4444Row = I422ToARGB4444Row_AVX2; 744 } 745 } 746#endif 747#if defined(HAS_I422TOARGB4444ROW_NEON) 748 if (TestCpuFlag(kCpuHasNEON)) { 749 I422ToARGB4444Row = I422ToARGB4444Row_Any_NEON; 750 if (IS_ALIGNED(width, 8)) { 751 I422ToARGB4444Row = I422ToARGB4444Row_NEON; 752 } 753 } 754#endif 755 756 for (y = 0; y < height; ++y) { 757 I422ToARGB4444Row(src_y, src_u, src_v, dst_argb4444, &kYuvI601Constants, 758 width); 759 dst_argb4444 += dst_stride_argb4444; 760 src_y += src_stride_y; 761 if (y & 1) { 762 src_u += src_stride_u; 763 src_v += src_stride_v; 764 } 765 } 766 return 0; 767} 768 769// Convert I420 to RGB565. 770LIBYUV_API 771int I420ToRGB565(const uint8* src_y, int src_stride_y, 772 const uint8* src_u, int src_stride_u, 773 const uint8* src_v, int src_stride_v, 774 uint8* dst_rgb565, int dst_stride_rgb565, 775 int width, int height) { 776 int y; 777 void (*I422ToRGB565Row)(const uint8* y_buf, 778 const uint8* u_buf, 779 const uint8* v_buf, 780 uint8* rgb_buf, 781 const struct YuvConstants* yuvconstants, 782 int width) = I422ToRGB565Row_C; 783 if (!src_y || !src_u || !src_v || !dst_rgb565 || 784 width <= 0 || height == 0) { 785 return -1; 786 } 787 // Negative height means invert the image. 788 if (height < 0) { 789 height = -height; 790 dst_rgb565 = dst_rgb565 + (height - 1) * dst_stride_rgb565; 791 dst_stride_rgb565 = -dst_stride_rgb565; 792 } 793#if defined(HAS_I422TORGB565ROW_SSSE3) 794 if (TestCpuFlag(kCpuHasSSSE3)) { 795 I422ToRGB565Row = I422ToRGB565Row_Any_SSSE3; 796 if (IS_ALIGNED(width, 8)) { 797 I422ToRGB565Row = I422ToRGB565Row_SSSE3; 798 } 799 } 800#endif 801#if defined(HAS_I422TORGB565ROW_AVX2) 802 if (TestCpuFlag(kCpuHasAVX2)) { 803 I422ToRGB565Row = I422ToRGB565Row_Any_AVX2; 804 if (IS_ALIGNED(width, 16)) { 805 I422ToRGB565Row = I422ToRGB565Row_AVX2; 806 } 807 } 808#endif 809#if defined(HAS_I422TORGB565ROW_NEON) 810 if (TestCpuFlag(kCpuHasNEON)) { 811 I422ToRGB565Row = I422ToRGB565Row_Any_NEON; 812 if (IS_ALIGNED(width, 8)) { 813 I422ToRGB565Row = I422ToRGB565Row_NEON; 814 } 815 } 816#endif 817 818 for (y = 0; y < height; ++y) { 819 I422ToRGB565Row(src_y, src_u, src_v, dst_rgb565, &kYuvI601Constants, width); 820 dst_rgb565 += dst_stride_rgb565; 821 src_y += src_stride_y; 822 if (y & 1) { 823 src_u += src_stride_u; 824 src_v += src_stride_v; 825 } 826 } 827 return 0; 828} 829 830// Ordered 8x8 dither for 888 to 565. Values from 0 to 7. 831static const uint8 kDither565_4x4[16] = { 832 0, 4, 1, 5, 833 6, 2, 7, 3, 834 1, 5, 0, 4, 835 7, 3, 6, 2, 836}; 837 838// Convert I420 to RGB565 with dithering. 839LIBYUV_API 840int I420ToRGB565Dither(const uint8* src_y, int src_stride_y, 841 const uint8* src_u, int src_stride_u, 842 const uint8* src_v, int src_stride_v, 843 uint8* dst_rgb565, int dst_stride_rgb565, 844 const uint8* dither4x4, int width, int height) { 845 int y; 846 void (*I422ToARGBRow)(const uint8* y_buf, 847 const uint8* u_buf, 848 const uint8* v_buf, 849 uint8* rgb_buf, 850 const struct YuvConstants* yuvconstants, 851 int width) = I422ToARGBRow_C; 852 void (*ARGBToRGB565DitherRow)(const uint8* src_argb, uint8* dst_rgb, 853 const uint32 dither4, int width) = ARGBToRGB565DitherRow_C; 854 if (!src_y || !src_u || !src_v || !dst_rgb565 || 855 width <= 0 || height == 0) { 856 return -1; 857 } 858 // Negative height means invert the image. 859 if (height < 0) { 860 height = -height; 861 dst_rgb565 = dst_rgb565 + (height - 1) * dst_stride_rgb565; 862 dst_stride_rgb565 = -dst_stride_rgb565; 863 } 864 if (!dither4x4) { 865 dither4x4 = kDither565_4x4; 866 } 867#if defined(HAS_I422TOARGBROW_SSSE3) 868 if (TestCpuFlag(kCpuHasSSSE3)) { 869 I422ToARGBRow = I422ToARGBRow_Any_SSSE3; 870 if (IS_ALIGNED(width, 8)) { 871 I422ToARGBRow = I422ToARGBRow_SSSE3; 872 } 873 } 874#endif 875#if defined(HAS_I422TOARGBROW_AVX2) 876 if (TestCpuFlag(kCpuHasAVX2)) { 877 I422ToARGBRow = I422ToARGBRow_Any_AVX2; 878 if (IS_ALIGNED(width, 16)) { 879 I422ToARGBRow = I422ToARGBRow_AVX2; 880 } 881 } 882#endif 883#if defined(HAS_I422TOARGBROW_NEON) 884 if (TestCpuFlag(kCpuHasNEON)) { 885 I422ToARGBRow = I422ToARGBRow_Any_NEON; 886 if (IS_ALIGNED(width, 8)) { 887 I422ToARGBRow = I422ToARGBRow_NEON; 888 } 889 } 890#endif 891#if defined(HAS_I422TOARGBROW_DSPR2) 892 if (TestCpuFlag(kCpuHasDSPR2) && IS_ALIGNED(width, 4) && 893 IS_ALIGNED(src_y, 4) && IS_ALIGNED(src_stride_y, 4) && 894 IS_ALIGNED(src_u, 2) && IS_ALIGNED(src_stride_u, 2) && 895 IS_ALIGNED(src_v, 2) && IS_ALIGNED(src_stride_v, 2)) { 896 I422ToARGBRow = I422ToARGBRow_DSPR2; 897 } 898#endif 899#if defined(HAS_ARGBTORGB565DITHERROW_SSE2) 900 if (TestCpuFlag(kCpuHasSSE2)) { 901 ARGBToRGB565DitherRow = ARGBToRGB565DitherRow_Any_SSE2; 902 if (IS_ALIGNED(width, 4)) { 903 ARGBToRGB565DitherRow = ARGBToRGB565DitherRow_SSE2; 904 } 905 } 906#endif 907#if defined(HAS_ARGBTORGB565DITHERROW_AVX2) 908 if (TestCpuFlag(kCpuHasAVX2)) { 909 ARGBToRGB565DitherRow = ARGBToRGB565DitherRow_Any_AVX2; 910 if (IS_ALIGNED(width, 8)) { 911 ARGBToRGB565DitherRow = ARGBToRGB565DitherRow_AVX2; 912 } 913 } 914#endif 915#if defined(HAS_ARGBTORGB565DITHERROW_NEON) 916 if (TestCpuFlag(kCpuHasNEON)) { 917 ARGBToRGB565DitherRow = ARGBToRGB565DitherRow_Any_NEON; 918 if (IS_ALIGNED(width, 8)) { 919 ARGBToRGB565DitherRow = ARGBToRGB565DitherRow_NEON; 920 } 921 } 922#endif 923 { 924 // Allocate a row of argb. 925 align_buffer_64(row_argb, width * 4); 926 for (y = 0; y < height; ++y) { 927 I422ToARGBRow(src_y, src_u, src_v, row_argb, &kYuvI601Constants, width); 928 ARGBToRGB565DitherRow(row_argb, dst_rgb565, 929 *(uint32*)(dither4x4 + ((y & 3) << 2)), width); 930 dst_rgb565 += dst_stride_rgb565; 931 src_y += src_stride_y; 932 if (y & 1) { 933 src_u += src_stride_u; 934 src_v += src_stride_v; 935 } 936 } 937 free_aligned_buffer_64(row_argb); 938 } 939 return 0; 940} 941 942// Convert I420 to specified format 943LIBYUV_API 944int ConvertFromI420(const uint8* y, int y_stride, 945 const uint8* u, int u_stride, 946 const uint8* v, int v_stride, 947 uint8* dst_sample, int dst_sample_stride, 948 int width, int height, 949 uint32 fourcc) { 950 uint32 format = CanonicalFourCC(fourcc); 951 int r = 0; 952 if (!y || !u|| !v || !dst_sample || 953 width <= 0 || height == 0) { 954 return -1; 955 } 956 switch (format) { 957 // Single plane formats 958 case FOURCC_YUY2: 959 r = I420ToYUY2(y, y_stride, 960 u, u_stride, 961 v, v_stride, 962 dst_sample, 963 dst_sample_stride ? dst_sample_stride : width * 2, 964 width, height); 965 break; 966 case FOURCC_UYVY: 967 r = I420ToUYVY(y, y_stride, 968 u, u_stride, 969 v, v_stride, 970 dst_sample, 971 dst_sample_stride ? dst_sample_stride : width * 2, 972 width, height); 973 break; 974 case FOURCC_RGBP: 975 r = I420ToRGB565(y, y_stride, 976 u, u_stride, 977 v, v_stride, 978 dst_sample, 979 dst_sample_stride ? dst_sample_stride : width * 2, 980 width, height); 981 break; 982 case FOURCC_RGBO: 983 r = I420ToARGB1555(y, y_stride, 984 u, u_stride, 985 v, v_stride, 986 dst_sample, 987 dst_sample_stride ? dst_sample_stride : width * 2, 988 width, height); 989 break; 990 case FOURCC_R444: 991 r = I420ToARGB4444(y, y_stride, 992 u, u_stride, 993 v, v_stride, 994 dst_sample, 995 dst_sample_stride ? dst_sample_stride : width * 2, 996 width, height); 997 break; 998 case FOURCC_24BG: 999 r = I420ToRGB24(y, y_stride, 1000 u, u_stride, 1001 v, v_stride, 1002 dst_sample, 1003 dst_sample_stride ? dst_sample_stride : width * 3, 1004 width, height); 1005 break; 1006 case FOURCC_RAW: 1007 r = I420ToRAW(y, y_stride, 1008 u, u_stride, 1009 v, v_stride, 1010 dst_sample, 1011 dst_sample_stride ? dst_sample_stride : width * 3, 1012 width, height); 1013 break; 1014 case FOURCC_ARGB: 1015 r = I420ToARGB(y, y_stride, 1016 u, u_stride, 1017 v, v_stride, 1018 dst_sample, 1019 dst_sample_stride ? dst_sample_stride : width * 4, 1020 width, height); 1021 break; 1022 case FOURCC_BGRA: 1023 r = I420ToBGRA(y, y_stride, 1024 u, u_stride, 1025 v, v_stride, 1026 dst_sample, 1027 dst_sample_stride ? dst_sample_stride : width * 4, 1028 width, height); 1029 break; 1030 case FOURCC_ABGR: 1031 r = I420ToABGR(y, y_stride, 1032 u, u_stride, 1033 v, v_stride, 1034 dst_sample, 1035 dst_sample_stride ? dst_sample_stride : width * 4, 1036 width, height); 1037 break; 1038 case FOURCC_RGBA: 1039 r = I420ToRGBA(y, y_stride, 1040 u, u_stride, 1041 v, v_stride, 1042 dst_sample, 1043 dst_sample_stride ? dst_sample_stride : width * 4, 1044 width, height); 1045 break; 1046 case FOURCC_I400: 1047 r = I400Copy(y, y_stride, 1048 dst_sample, 1049 dst_sample_stride ? dst_sample_stride : width, 1050 width, height); 1051 break; 1052 case FOURCC_NV12: { 1053 uint8* dst_uv = dst_sample + width * height; 1054 r = I420ToNV12(y, y_stride, 1055 u, u_stride, 1056 v, v_stride, 1057 dst_sample, 1058 dst_sample_stride ? dst_sample_stride : width, 1059 dst_uv, 1060 dst_sample_stride ? dst_sample_stride : width, 1061 width, height); 1062 break; 1063 } 1064 case FOURCC_NV21: { 1065 uint8* dst_vu = dst_sample + width * height; 1066 r = I420ToNV21(y, y_stride, 1067 u, u_stride, 1068 v, v_stride, 1069 dst_sample, 1070 dst_sample_stride ? dst_sample_stride : width, 1071 dst_vu, 1072 dst_sample_stride ? dst_sample_stride : width, 1073 width, height); 1074 break; 1075 } 1076 // TODO(fbarchard): Add M420. 1077 // Triplanar formats 1078 // TODO(fbarchard): halfstride instead of halfwidth 1079 case FOURCC_I420: 1080 case FOURCC_YV12: { 1081 int halfwidth = (width + 1) / 2; 1082 int halfheight = (height + 1) / 2; 1083 uint8* dst_u; 1084 uint8* dst_v; 1085 if (format == FOURCC_YV12) { 1086 dst_v = dst_sample + width * height; 1087 dst_u = dst_v + halfwidth * halfheight; 1088 } else { 1089 dst_u = dst_sample + width * height; 1090 dst_v = dst_u + halfwidth * halfheight; 1091 } 1092 r = I420Copy(y, y_stride, 1093 u, u_stride, 1094 v, v_stride, 1095 dst_sample, width, 1096 dst_u, halfwidth, 1097 dst_v, halfwidth, 1098 width, height); 1099 break; 1100 } 1101 case FOURCC_I422: 1102 case FOURCC_YV16: { 1103 int halfwidth = (width + 1) / 2; 1104 uint8* dst_u; 1105 uint8* dst_v; 1106 if (format == FOURCC_YV16) { 1107 dst_v = dst_sample + width * height; 1108 dst_u = dst_v + halfwidth * height; 1109 } else { 1110 dst_u = dst_sample + width * height; 1111 dst_v = dst_u + halfwidth * height; 1112 } 1113 r = I420ToI422(y, y_stride, 1114 u, u_stride, 1115 v, v_stride, 1116 dst_sample, width, 1117 dst_u, halfwidth, 1118 dst_v, halfwidth, 1119 width, height); 1120 break; 1121 } 1122 case FOURCC_I444: 1123 case FOURCC_YV24: { 1124 uint8* dst_u; 1125 uint8* dst_v; 1126 if (format == FOURCC_YV24) { 1127 dst_v = dst_sample + width * height; 1128 dst_u = dst_v + width * height; 1129 } else { 1130 dst_u = dst_sample + width * height; 1131 dst_v = dst_u + width * height; 1132 } 1133 r = I420ToI444(y, y_stride, 1134 u, u_stride, 1135 v, v_stride, 1136 dst_sample, width, 1137 dst_u, width, 1138 dst_v, width, 1139 width, height); 1140 break; 1141 } 1142 case FOURCC_I411: { 1143 int quarterwidth = (width + 3) / 4; 1144 uint8* dst_u = dst_sample + width * height; 1145 uint8* dst_v = dst_u + quarterwidth * height; 1146 r = I420ToI411(y, y_stride, 1147 u, u_stride, 1148 v, v_stride, 1149 dst_sample, width, 1150 dst_u, quarterwidth, 1151 dst_v, quarterwidth, 1152 width, height); 1153 break; 1154 } 1155 1156 // Formats not supported - MJPG, biplanar, some rgb formats. 1157 default: 1158 return -1; // unknown fourcc - return failure code. 1159 } 1160 return r; 1161} 1162 1163#ifdef __cplusplus 1164} // extern "C" 1165} // namespace libyuv 1166#endif 1167