Convert8To16 takes bits instead of scale.

- NV16/24 FOURCC and document
- Rotate take uint8* for ubsan

Bug: 537818862
Change-Id: I2bc7109abaffe2464e031e33c22296c007077d82
Reviewed-on: https://chromium-review.googlesource.com/c/libyuv/libyuv/+/8339427
Commit-Queue: Frank Barchard <fbarchard@google.com>
Reviewed-by: richard winterton <rrwinterton@gmail.com>
This commit is contained in:
Frank Barchard 2026-09-01 13:39:10 -07:00 committed by libyuv-scoped@luci-project-accounts.iam.gserviceaccount.com
parent addfba3a7c
commit 2fcc1272dd
23 changed files with 677 additions and 119 deletions

View File

@ -1,6 +1,6 @@
Name: libyuv
URL: https://chromium.googlesource.com/libyuv/libyuv/
Version: 1967
Version: 1968
Revision: DEPS
License: BSD-3-Clause
License File: LICENSE

View File

@ -38,13 +38,15 @@ This is how OSX formats map to libyuv
The following is extracted from video_common.h as a complete list of formats supported by libyuv.
enum FourCC {
// 10 Primary YUV formats: 5 planar, 2 biplanar, 2 packed.
// 12 Primary YUV formats: 5 planar, 4 biplanar, 2 packed.
FOURCC_I420 = FOURCC('I', '4', '2', '0'),
FOURCC_I422 = FOURCC('I', '4', '2', '2'),
FOURCC_I444 = FOURCC('I', '4', '4', '4'),
FOURCC_I400 = FOURCC('I', '4', '0', '0'),
FOURCC_NV21 = FOURCC('N', 'V', '2', '1'),
FOURCC_NV12 = FOURCC('N', 'V', '1', '2'),
FOURCC_NV16 = FOURCC('N', 'V', '1', '6'),
FOURCC_NV24 = FOURCC('N', 'V', '2', '4'),
FOURCC_YUY2 = FOURCC('Y', 'U', 'Y', '2'),
FOURCC_UYVY = FOURCC('U', 'Y', 'V', 'Y'),
FOURCC_H010 = FOURCC('H', '0', '1', '0'), // unofficial fourcc. 10 bit lsb

View File

@ -51,12 +51,14 @@ void Convert16To8Plane(const uint16_t* src_y,
int width,
int height);
// Convert 8 bit Y to 10/12/16 bit.
// "bits" accepts bit depths (10, 12, 16) or legacy scale values (1024, 4096, 65536).
LIBYUV_API
void Convert8To16Plane(const uint8_t* src_y,
int src_stride_y,
uint16_t* dst_y,
int dst_stride_y,
int scale, // 1024 for 10 bits
int bits, // 10, 12, 16 bits (or 1024, 4096, 65536 scale)
int width,
int height);

View File

@ -3572,47 +3572,47 @@ void DivideRow_16_Any_NEON(const uint16_t* src_ptr,
void Convert8To16Row_C(const uint8_t* src_y,
uint16_t* dst_y,
int scale,
int bits,
int width);
void Convert8To16Row_SSE2(const uint8_t* src_y,
uint16_t* dst_y,
int scale,
int bits,
int width);
void Convert8To16Row_AVX2(const uint8_t* src_y,
uint16_t* dst_y,
int scale,
int bits,
int width);
void Convert8To16Row_AVX512BW(const uint8_t* src_y,
uint16_t* dst_y,
int scale,
int bits,
int width);
void Convert8To16Row_Any_SSE2(const uint8_t* src_ptr,
uint16_t* dst_ptr,
int scale,
int bits,
int width);
void Convert8To16Row_Any_AVX2(const uint8_t* src_ptr,
uint16_t* dst_ptr,
int scale,
int bits,
int width);
void Convert8To16Row_Any_AVX512BW(const uint8_t* src_ptr,
uint16_t* dst_ptr,
int scale,
int bits,
int width);
void Convert8To16Row_NEON(const uint8_t* src_y,
uint16_t* dst_y,
int scale,
int bits,
int width);
void Convert8To16Row_Any_NEON(const uint8_t* src_y,
uint16_t* dst_y,
int scale,
int bits,
int width);
void Convert8To16Row_SME(const uint8_t* src_y,
uint16_t* dst_y,
int scale,
int bits,
int width);
void Convert8To16Row_RVV(const uint8_t* src_y,
uint16_t* dst_y,
int scale,
int bits,
int width);
void Convert16To8Row_C(const uint16_t* src_y,

View File

@ -11,6 +11,6 @@
#ifndef INCLUDE_LIBYUV_VERSION_H_
#define INCLUDE_LIBYUV_VERSION_H_
#define LIBYUV_VERSION 1968
#define LIBYUV_VERSION 1969
#endif // INCLUDE_LIBYUV_VERSION_H_

View File

@ -53,13 +53,15 @@ extern "C" {
// Secondary formats are converted in 2 steps.
// Auxilliary formats call primary converters.
enum FourCC {
// 10 Primary YUV formats: 5 planar, 2 biplanar, 2 packed.
// 12 Primary YUV formats: 5 planar, 4 biplanar, 2 packed.
FOURCC_I420 = FOURCC('I', '4', '2', '0'),
FOURCC_I422 = FOURCC('I', '4', '2', '2'),
FOURCC_I444 = FOURCC('I', '4', '4', '4'),
FOURCC_I400 = FOURCC('I', '4', '0', '0'),
FOURCC_NV21 = FOURCC('N', 'V', '2', '1'),
FOURCC_NV12 = FOURCC('N', 'V', '1', '2'),
FOURCC_NV16 = FOURCC('N', 'V', '1', '6'),
FOURCC_NV24 = FOURCC('N', 'V', '2', '4'),
FOURCC_YUY2 = FOURCC('Y', 'U', 'Y', '2'),
FOURCC_UYVY = FOURCC('U', 'Y', 'V', 'Y'),
FOURCC_I010 = FOURCC('I', '0', '1', '0'), // bt.601 10 bit 420
@ -158,6 +160,8 @@ enum FourCCBpp {
FOURCC_BPP_I400 = 8,
FOURCC_BPP_NV21 = 12,
FOURCC_BPP_NV12 = 12,
FOURCC_BPP_NV16 = 16,
FOURCC_BPP_NV24 = 24,
FOURCC_BPP_YUY2 = 16,
FOURCC_BPP_UYVY = 16,
FOURCC_BPP_M420 = 12, // deprecated

View File

@ -814,6 +814,43 @@ int ConvertFromI420(const uint8_t* y,
height);
break;
}
case FOURCC_NV16: {
int dst_y_stride = dst_sample_stride ? dst_sample_stride : width;
int dst_uv_stride = dst_sample_stride ? dst_sample_stride : width;
uint8_t* dst_uv = dst_sample + (ptrdiff_t)dst_y_stride * height;
int halfwidth = (width + 1) / 2;
int halfheight = (height + 1) / 2;
CopyPlane(y, y_stride, dst_sample, dst_y_stride, width, height);
MergeUVPlane(u, u_stride, v, v_stride, dst_uv, dst_uv_stride * 2,
halfwidth, halfheight);
if (height > 1) {
MergeUVPlane(u, u_stride, v, v_stride, dst_uv + dst_uv_stride,
dst_uv_stride * 2, halfwidth, height / 2);
}
r = 0;
break;
}
case FOURCC_NV24: {
int dst_y_stride = dst_sample_stride ? dst_sample_stride : width;
int dst_uv_stride = dst_sample_stride ? dst_sample_stride * 2 : width * 2;
uint8_t* dst_uv = dst_sample + (ptrdiff_t)dst_y_stride * height;
align_buffer_64(temp_u, (size_t)width * height);
align_buffer_64(temp_v, (size_t)width * height);
if (!temp_u || !temp_v) {
free_aligned_buffer_64(temp_u);
free_aligned_buffer_64(temp_v);
return 1;
}
r = I420ToI444(y, y_stride, u, u_stride, v, v_stride, dst_sample,
dst_y_stride, temp_u, width, temp_v, width, width, height);
if (r == 0) {
MergeUVPlane(temp_u, width, temp_v, width, dst_uv, dst_uv_stride, width,
height);
}
free_aligned_buffer_64(temp_u);
free_aligned_buffer_64(temp_v);
break;
}
// Triplanar formats
case FOURCC_I420:
case FOURCC_YV12: {

View File

@ -28,6 +28,221 @@ namespace libyuv {
extern "C" {
#endif
static int NV16ToARGB(const uint8_t* src_y,
int src_stride_y,
const uint8_t* src_uv,
int src_stride_uv,
uint8_t* dst_argb,
int dst_stride_argb,
int width,
int height) {
int y;
void (*NV12ToARGBRow)(
const uint8_t* y_buf, const uint8_t* uv_buf, uint8_t* rgb_buf,
const struct YuvConstants* yuvconstants, int width) = NV12ToARGBRow_C;
if (!src_y || !src_uv || !dst_argb || width <= 0 || height == 0 ||
height == INT_MIN) {
return -1;
}
if (height < 0) {
height = -height;
dst_argb = dst_argb + (ptrdiff_t)(height - 1) * dst_stride_argb;
dst_stride_argb = -dst_stride_argb;
}
#if defined(HAS_NV12TOARGBROW_SSSE3)
if (TestCpuFlag(kCpuHasSSSE3)) {
NV12ToARGBRow = NV12ToARGBRow_Any_SSSE3;
if (IS_ALIGNED(width, 8)) {
NV12ToARGBRow = NV12ToARGBRow_SSSE3;
}
}
#endif
#if defined(HAS_NV12TOARGBROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
NV12ToARGBRow = NV12ToARGBRow_Any_AVX2;
if (IS_ALIGNED(width, 16)) {
NV12ToARGBRow = NV12ToARGBRow_AVX2;
}
}
#endif
#if defined(HAS_NV12TOARGBROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
NV12ToARGBRow = NV12ToARGBRow_Any_NEON;
if (IS_ALIGNED(width, 8)) {
NV12ToARGBRow = NV12ToARGBRow_NEON;
}
}
#endif
#if defined(HAS_NV12TOARGBROW_SVE2)
if (TestCpuFlag(kCpuHasSVE2)) {
NV12ToARGBRow = NV12ToARGBRow_SVE2;
}
#endif
#if defined(HAS_NV12TOARGBROW_SME)
if (TestCpuFlag(kCpuHasSME)) {
NV12ToARGBRow = NV12ToARGBRow_SME;
}
#endif
#if defined(HAS_NV12TOARGBROW_LSX)
if (TestCpuFlag(kCpuHasLSX)) {
NV12ToARGBRow = NV12ToARGBRow_Any_LSX;
if (IS_ALIGNED(width, 8)) {
NV12ToARGBRow = NV12ToARGBRow_LSX;
}
}
#endif
#if defined(HAS_NV12TOARGBROW_LASX)
if (TestCpuFlag(kCpuHasLASX)) {
NV12ToARGBRow = NV12ToARGBRow_Any_LASX;
if (IS_ALIGNED(width, 16)) {
NV12ToARGBRow = NV12ToARGBRow_LASX;
}
}
#endif
#if defined(HAS_NV12TOARGBROW_RVV)
if (TestCpuFlag(kCpuHasRVV)) {
NV12ToARGBRow = NV12ToARGBRow_RVV;
}
#endif
for (y = 0; y < height; ++y) {
NV12ToARGBRow(src_y, src_uv, dst_argb, &kYuvI601Constants, width);
dst_argb += dst_stride_argb;
src_y += src_stride_y;
src_uv += src_stride_uv;
}
return 0;
}
static int NV24ToARGB(const uint8_t* src_y,
int src_stride_y,
const uint8_t* src_uv,
int src_stride_uv,
uint8_t* dst_argb,
int dst_stride_argb,
int width,
int height) {
int y;
void (*I444ToARGBRow)(
const uint8_t* y_buf, const uint8_t* u_buf, const uint8_t* v_buf,
uint8_t* rgb_buf, const struct YuvConstants* yuvconstants, int width) =
I444ToARGBRow_C;
void (*SplitUVRow)(const uint8_t* src_uv, uint8_t* dst_u, uint8_t* dst_v,
int width) = SplitUVRow_C;
if (!src_y || !src_uv || !dst_argb || width <= 0 || height == 0 ||
height == INT_MIN) {
return -1;
}
if (height < 0) {
height = -height;
dst_argb = dst_argb + (ptrdiff_t)(height - 1) * dst_stride_argb;
dst_stride_argb = -dst_stride_argb;
}
#if defined(HAS_I444TOARGBROW_SSSE3)
if (TestCpuFlag(kCpuHasSSSE3)) {
I444ToARGBRow = I444ToARGBRow_Any_SSSE3;
if (IS_ALIGNED(width, 8)) {
I444ToARGBRow = I444ToARGBRow_SSSE3;
}
}
#endif
#if defined(HAS_I444TOARGBROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
I444ToARGBRow = I444ToARGBRow_Any_AVX2;
if (IS_ALIGNED(width, 16)) {
I444ToARGBRow = I444ToARGBRow_AVX2;
}
}
#endif
#if defined(HAS_I444TOARGBROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
I444ToARGBRow = I444ToARGBRow_Any_NEON;
if (IS_ALIGNED(width, 8)) {
I444ToARGBRow = I444ToARGBRow_NEON;
}
}
#endif
#if defined(HAS_I444TOARGBROW_SVE2)
if (TestCpuFlag(kCpuHasSVE2)) {
I444ToARGBRow = I444ToARGBRow_SVE2;
}
#endif
#if defined(HAS_I444TOARGBROW_SME)
if (TestCpuFlag(kCpuHasSME)) {
I444ToARGBRow = I444ToARGBRow_SME;
}
#endif
#if defined(HAS_I444TOARGBROW_LSX)
if (TestCpuFlag(kCpuHasLSX)) {
I444ToARGBRow = I444ToARGBRow_Any_LSX;
if (IS_ALIGNED(width, 8)) {
I444ToARGBRow = I444ToARGBRow_LSX;
}
}
#endif
#if defined(HAS_I444TOARGBROW_LASX)
if (TestCpuFlag(kCpuHasLASX)) {
I444ToARGBRow = I444ToARGBRow_Any_LASX;
if (IS_ALIGNED(width, 16)) {
I444ToARGBRow = I444ToARGBRow_LASX;
}
}
#endif
#if defined(HAS_I444TOARGBROW_RVV)
if (TestCpuFlag(kCpuHasRVV)) {
I444ToARGBRow = I444ToARGBRow_RVV;
}
#endif
#if defined(HAS_SPLITUVROW_SSE2)
if (TestCpuFlag(kCpuHasSSE2)) {
SplitUVRow = SplitUVRow_Any_SSE2;
if (IS_ALIGNED(width, 16)) {
SplitUVRow = SplitUVRow_SSE2;
}
}
#endif
#if defined(HAS_SPLITUVROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
SplitUVRow = SplitUVRow_Any_AVX2;
if (IS_ALIGNED(width, 32)) {
SplitUVRow = SplitUVRow_AVX2;
}
}
#endif
#if defined(HAS_SPLITUVROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
SplitUVRow = SplitUVRow_Any_NEON;
if (IS_ALIGNED(width, 16)) {
SplitUVRow = SplitUVRow_NEON;
}
}
#endif
#if defined(HAS_SPLITUVROW_RVV)
if (TestCpuFlag(kCpuHasRVV)) {
SplitUVRow = SplitUVRow_RVV;
}
#endif
align_buffer_64(row_u, width);
align_buffer_64(row_v, width);
if (!row_u || !row_v) {
free_aligned_buffer_64(row_u);
free_aligned_buffer_64(row_v);
return 1;
}
for (y = 0; y < height; ++y) {
SplitUVRow(src_uv, row_u, row_v, width);
I444ToARGBRow(src_y, row_u, row_v, dst_argb, &kYuvI601Constants, width);
dst_argb += dst_stride_argb;
src_y += src_stride_y;
src_uv += src_stride_uv;
}
free_aligned_buffer_64(row_u);
free_aligned_buffer_64(row_v);
return 0;
}
// Convert camera sample to ARGB with cropping, rotation and vertical flip.
// src_width is used for source stride computation
// src_height is used to compute location of planes, and indicate inversion
@ -203,6 +418,23 @@ int ConvertToARGB(const uint8_t* sample,
r = NV21ToARGB(src, src_width, src_uv, aligned_src_width, dst_argb,
dst_stride_argb, crop_width, inv_crop_height);
break;
case FOURCC_NV16:
src = sample + ((ptrdiff_t)src_width * crop_y + crop_x);
src_uv = sample +
aligned_src_width * ((ptrdiff_t)abs_src_height + crop_y) +
(crop_x & ~1);
r = NV16ToARGB(src, src_width, src_uv, aligned_src_width, dst_argb,
dst_stride_argb, crop_width, inv_crop_height);
break;
case FOURCC_NV24:
src = sample + ((ptrdiff_t)src_width * crop_y + crop_x);
src_uv = sample +
(ptrdiff_t)aligned_src_width * abs_src_height +
(ptrdiff_t)aligned_src_width * 2 * crop_y +
(ptrdiff_t)crop_x * 2;
r = NV24ToARGB(src, src_width, src_uv, aligned_src_width * 2, dst_argb,
dst_stride_argb, crop_width, inv_crop_height);
break;
// Triplanar formats
case FOURCC_I420:
case FOURCC_YV12: {

View File

@ -14,7 +14,11 @@
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "libyuv/cpu_id.h"
#include "libyuv/planar_functions.h"
#include "libyuv/row.h"
#include "libyuv/video_common.h"
#ifdef __cplusplus
@ -22,6 +26,158 @@ namespace libyuv {
extern "C" {
#endif
static void HalfRow_C(const uint8_t* src_uv,
ptrdiff_t src_uv_stride,
uint8_t* dst_uv,
int width) {
int x;
for (x = 0; x < width; ++x) {
dst_uv[x] = (src_uv[x] + src_uv[src_uv_stride + x] + 1) >> 1;
}
}
static int NV16ToI420(const uint8_t* src_y,
int src_stride_y,
const uint8_t* src_uv,
int src_stride_uv,
uint8_t* dst_y,
int dst_stride_y,
uint8_t* dst_u,
int dst_stride_u,
uint8_t* dst_v,
int dst_stride_v,
int width,
int height) {
int y;
int halfwidth = (width + 1) >> 1;
int halfheight = (height + 1) >> 1;
void (*SplitUVRow)(const uint8_t* src_uv, uint8_t* dst_u, uint8_t* dst_v,
int width) = SplitUVRow_C;
if ((!src_y && dst_y) || !src_uv || !dst_u || !dst_v || width <= 0 ||
height == 0 || height == INT_MIN) {
return -1;
}
if (height < 0) {
height = -height;
halfheight = (height + 1) >> 1;
src_y = src_y + (ptrdiff_t)(height - 1) * src_stride_y;
src_uv = src_uv + (ptrdiff_t)(height - 1) * src_stride_uv;
src_stride_y = -src_stride_y;
src_stride_uv = -src_stride_uv;
}
if (dst_y) {
CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
}
#if defined(HAS_SPLITUVROW_SSE2)
if (TestCpuFlag(kCpuHasSSE2)) {
SplitUVRow = SplitUVRow_Any_SSE2;
if (IS_ALIGNED(halfwidth, 16)) {
SplitUVRow = SplitUVRow_SSE2;
}
}
#endif
#if defined(HAS_SPLITUVROW_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
SplitUVRow = SplitUVRow_Any_AVX2;
if (IS_ALIGNED(halfwidth, 32)) {
SplitUVRow = SplitUVRow_AVX2;
}
}
#endif
#if defined(HAS_SPLITUVROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
SplitUVRow = SplitUVRow_Any_NEON;
if (IS_ALIGNED(halfwidth, 16)) {
SplitUVRow = SplitUVRow_NEON;
}
}
#endif
align_buffer_64(row_uv, halfwidth * 2);
if (!row_uv) {
return 1;
}
for (y = 0; y < halfheight; ++y) {
if (y * 2 + 1 < height) {
HalfRow_C(src_uv, src_stride_uv, row_uv, halfwidth * 2);
} else {
memcpy(row_uv, src_uv, (size_t)halfwidth * 2);
}
SplitUVRow(row_uv, dst_u, dst_v, halfwidth);
src_uv += (ptrdiff_t)src_stride_uv * 2;
dst_u += dst_stride_u;
dst_v += dst_stride_v;
}
free_aligned_buffer_64(row_uv);
return 0;
}
static int NV24ToI420(const uint8_t* src_y,
int src_stride_y,
const uint8_t* src_uv,
int src_stride_uv,
uint8_t* dst_y,
int dst_stride_y,
uint8_t* dst_u,
int dst_stride_u,
uint8_t* dst_v,
int dst_stride_v,
int width,
int height) {
int x, y;
int halfwidth = (width + 1) >> 1;
int halfheight = (height + 1) >> 1;
if ((!src_y && dst_y) || !src_uv || !dst_u || !dst_v || width <= 0 ||
height == 0 || height == INT_MIN) {
return -1;
}
if (height < 0) {
height = -height;
halfheight = (height + 1) >> 1;
src_y = src_y + (ptrdiff_t)(height - 1) * src_stride_y;
src_uv = src_uv + (ptrdiff_t)(height - 1) * src_stride_uv;
src_stride_y = -src_stride_y;
src_stride_uv = -src_stride_uv;
}
if (dst_y) {
CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
}
align_buffer_64(row_uv, width * 2);
if (!row_uv) {
return 1;
}
for (y = 0; y < halfheight; ++y) {
if (y * 2 + 1 < height) {
HalfRow_C(src_uv, src_stride_uv, row_uv, width * 2);
} else {
memcpy(row_uv, src_uv, (size_t)width * 2);
}
for (x = 0; x < halfwidth - 1; ++x) {
dst_u[x] = (row_uv[x * 4 + 0] + row_uv[x * 4 + 2] + 1) >> 1;
dst_v[x] = (row_uv[x * 4 + 1] + row_uv[x * 4 + 3] + 1) >> 1;
}
if (width & 1) {
dst_u[halfwidth - 1] = row_uv[(halfwidth - 1) * 4 + 0];
dst_v[halfwidth - 1] = row_uv[(halfwidth - 1) * 4 + 1];
} else {
dst_u[halfwidth - 1] =
(row_uv[(halfwidth - 1) * 4 + 0] + row_uv[(halfwidth - 1) * 4 + 2] +
1) >>
1;
dst_v[halfwidth - 1] =
(row_uv[(halfwidth - 1) * 4 + 1] + row_uv[(halfwidth - 1) * 4 + 3] +
1) >>
1;
}
src_uv += (ptrdiff_t)src_stride_uv * 2;
dst_u += dst_stride_u;
dst_v += dst_stride_v;
}
free_aligned_buffer_64(row_uv);
return 0;
}
// Convert camera sample to I420 with cropping, rotation and vertical flip.
// src_width is used for source stride computation
// src_height is used to compute location of planes, and indicate inversion
@ -204,6 +360,24 @@ int ConvertToI420(const uint8_t* sample,
dst_stride_y, dst_v, dst_stride_v, dst_u,
dst_stride_u, crop_width, inv_crop_height, rotation);
break;
case FOURCC_NV16:
src = sample + ((ptrdiff_t)src_width * crop_y + crop_x);
src_uv = sample + ((ptrdiff_t)aligned_src_width * abs_src_height) +
((ptrdiff_t)crop_y * aligned_src_width) +
((crop_x / 2) * 2);
r = NV16ToI420(src, src_width, src_uv, aligned_src_width, dst_y,
dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v,
crop_width, inv_crop_height);
break;
case FOURCC_NV24:
src = sample + ((ptrdiff_t)src_width * crop_y + crop_x);
src_uv = sample + ((ptrdiff_t)aligned_src_width * abs_src_height) +
((ptrdiff_t)crop_y * aligned_src_width * 2) +
(crop_x * 2);
r = NV24ToI420(src, src_width, src_uv, aligned_src_width * 2, dst_y,
dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v,
crop_width, inv_crop_height);
break;
// Triplanar formats
case FOURCC_I420:
case FOURCC_YV12: {

View File

@ -196,16 +196,25 @@ void Convert8To16Plane(const uint8_t* src_y,
int src_stride_y,
uint16_t* dst_y,
int dst_stride_y,
int scale, // 1024 for 10 bits
int bits, // 10, 12, 16 bits (or 1024, 4096, 65536 scale)
int width,
int height) {
int y;
void (*Convert8To16Row)(const uint8_t* src_y, uint16_t* dst_y, int scale,
void (*Convert8To16Row)(const uint8_t* src_y, uint16_t* dst_y, int bits,
int width) = Convert8To16Row_C;
if (width <= 0 || height == 0 || height == INT_MIN) {
return;
}
// Convert legacy scale to bits if needed.
if (bits > 16) {
int b = 0;
while (bits > 1) {
bits >>= 1;
++b;
}
bits = b;
}
// Negative height means invert the image.
if (height < 0) {
height = -height;
@ -264,7 +273,7 @@ void Convert8To16Plane(const uint8_t* src_y,
// Convert plane
for (y = 0; y < height; ++y) {
Convert8To16Row(src_y, dst_y, scale, width);
Convert8To16Row(src_y, dst_y, bits, width);
src_y += src_stride_y;
dst_y += dst_stride_y;
}

View File

@ -145,6 +145,13 @@ void TransposeWxH_16_C(const uint16_t* src,
}
}
static inline void Copy4(uint8_t* dst, const uint8_t* src) {
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
dst[3] = src[3];
}
// Transpose 32 bit values (ARGB)
void Transpose4x4_32_C(const uint8_t* src,
int src_stride,
@ -159,38 +166,22 @@ void Transpose4x4_32_C(const uint8_t* src,
uint8_t* dst3 = dst2 + dst_stride;
int i;
for (i = 0; i < width; i += 4) {
uint32_t p00 = ((uint32_t*)(src))[0];
uint32_t p10 = ((uint32_t*)(src))[1];
uint32_t p20 = ((uint32_t*)(src))[2];
uint32_t p30 = ((uint32_t*)(src))[3];
uint32_t p01 = ((uint32_t*)(src1))[0];
uint32_t p11 = ((uint32_t*)(src1))[1];
uint32_t p21 = ((uint32_t*)(src1))[2];
uint32_t p31 = ((uint32_t*)(src1))[3];
uint32_t p02 = ((uint32_t*)(src2))[0];
uint32_t p12 = ((uint32_t*)(src2))[1];
uint32_t p22 = ((uint32_t*)(src2))[2];
uint32_t p32 = ((uint32_t*)(src2))[3];
uint32_t p03 = ((uint32_t*)(src3))[0];
uint32_t p13 = ((uint32_t*)(src3))[1];
uint32_t p23 = ((uint32_t*)(src3))[2];
uint32_t p33 = ((uint32_t*)(src3))[3];
((uint32_t*)(dst))[0] = p00;
((uint32_t*)(dst))[1] = p01;
((uint32_t*)(dst))[2] = p02;
((uint32_t*)(dst))[3] = p03;
((uint32_t*)(dst1))[0] = p10;
((uint32_t*)(dst1))[1] = p11;
((uint32_t*)(dst1))[2] = p12;
((uint32_t*)(dst1))[3] = p13;
((uint32_t*)(dst2))[0] = p20;
((uint32_t*)(dst2))[1] = p21;
((uint32_t*)(dst2))[2] = p22;
((uint32_t*)(dst2))[3] = p23;
((uint32_t*)(dst3))[0] = p30;
((uint32_t*)(dst3))[1] = p31;
((uint32_t*)(dst3))[2] = p32;
((uint32_t*)(dst3))[3] = p33;
Copy4(dst + 0, src + 0);
Copy4(dst + 4, src1 + 0);
Copy4(dst + 8, src2 + 0);
Copy4(dst + 12, src3 + 0);
Copy4(dst1 + 0, src + 4);
Copy4(dst1 + 4, src1 + 4);
Copy4(dst1 + 8, src2 + 4);
Copy4(dst1 + 12, src3 + 4);
Copy4(dst2 + 0, src + 8);
Copy4(dst2 + 4, src1 + 8);
Copy4(dst2 + 8, src2 + 8);
Copy4(dst2 + 12, src3 + 8);
Copy4(dst3 + 0, src + 12);
Copy4(dst3 + 4, src1 + 12);
Copy4(dst3 + 8, src2 + 12);
Copy4(dst3 + 12, src3 + 12);
src += (ptrdiff_t)src_stride * 4; // advance 4 rows
src1 += (ptrdiff_t)src_stride * 4;
src2 += (ptrdiff_t)src_stride * 4;

View File

@ -1494,7 +1494,7 @@ ANY11T(AB64ToARGBRow_Any_NEON, AB64ToARGBRow_NEON, 8, 4, uint16_t, uint8_t, 7)
// Any 1 to 1 with parameter and shorts. BPP measures in shorts.
#define ANY11C(NAMEANY, ANY_SIMD, SBPP, BPP, STYPE, DTYPE, MASK) \
void NAMEANY(const STYPE* src_ptr, DTYPE* dst_ptr, int scale, int width) { \
void NAMEANY(const STYPE* src_ptr, DTYPE* dst_ptr, int value, int width) { \
SIMD_ALIGNED(STYPE vin[64]); \
static_assert((MASK + 1) * SBPP <= sizeof(vin), "vin buffer too small"); \
SIMD_ALIGNED(DTYPE vout[64]); \
@ -1503,11 +1503,11 @@ ANY11T(AB64ToARGBRow_Any_NEON, AB64ToARGBRow_NEON, 8, 4, uint16_t, uint8_t, 7)
int r = width & MASK; \
int n = width & ~MASK; \
if (n > 0) { \
ANY_SIMD(src_ptr, dst_ptr, scale, n); \
ANY_SIMD(src_ptr, dst_ptr, value, n); \
} \
ptrdiff_t np = n; \
memcpy(vin, src_ptr + np, r * SBPP); \
ANY_SIMD(vin, vout, scale, MASK + 1); \
ANY_SIMD(vin, vout, value, MASK + 1); \
memcpy(dst_ptr + np, vout, r * BPP); \
}

View File

@ -2822,16 +2822,15 @@ void MirrorSplitUVRow_C(const uint8_t* src_uv,
void ARGBMirrorRow_C(const uint8_t* src, uint8_t* dst, int width) {
int x;
const uint32_t* src32 = (const uint32_t*)(src);
uint32_t* dst32 = (uint32_t*)(dst);
src32 += width - 1;
for (x = 0; x < width - 1; x += 2) {
dst32[x] = src32[0];
dst32[x + 1] = src32[-1];
src32 -= 2;
}
if (width & 1) {
dst32[width - 1] = src32[0];
const uint8_t* s = src + (ptrdiff_t)(width - 1) * 4;
uint8_t* d = dst;
for (x = 0; x < width; ++x) {
d[0] = s[0];
d[1] = s[1];
d[2] = s[2];
d[3] = s[3];
s -= 4;
d += 4;
}
}
@ -3241,18 +3240,15 @@ void Convert16To8Row_C(const uint16_t* src_y,
}
}
// Use scale to convert lsb formats to msb, depending how many bits there are:
// 1024 = 10 bits
// 4096 = 12 bits
// 65536 = 16 bits
// Convert 8 bit values to 10, 12 or 16 bits.
void Convert8To16Row_C(const uint8_t* src_y,
uint16_t* dst_y,
int scale,
int bits,
int width) {
int x;
scale *= 0x0101; // replicates the byte.
int shift = 16 - bits;
for (x = 0; x < width; ++x) {
dst_y[x] = (src_y[x] * scale) >> 16;
dst_y[x] = (src_y[x] * 0x0101) >> shift;
}
}

View File

@ -6218,9 +6218,9 @@ void HalfWidthRow_16To8_AVX512BW(const uint16_t* src_uv,
// 65536 = 16 bits
void Convert8To16Row_SSE2(const uint8_t* src_y,
uint16_t* dst_y,
int scale,
int bits,
int width) {
const int shift = __builtin_clz(scale) - 15;
const int shift = 16 - bits;
asm volatile(
"movd %3,%%xmm2 \n"
@ -6249,9 +6249,9 @@ void Convert8To16Row_SSE2(const uint8_t* src_y,
#ifdef HAS_CONVERT8TO16ROW_AVX2
void Convert8To16Row_AVX2(const uint8_t* src_y,
uint16_t* dst_y,
int scale,
int bits,
int width) {
const int shift = __builtin_clz(scale) - 15;
const int shift = 16 - bits;
asm volatile("vmovd %3,%%xmm2 \n"
// 32 pixels per loop.
@ -6279,16 +6279,11 @@ void Convert8To16Row_AVX2(const uint8_t* src_y,
#endif // HAS_CONVERT8TO16ROW_AVX2
#ifdef HAS_CONVERT8TO16ROW_AVX512BW
// Use scale to convert to lsb formats depending how many bits there are:
// 512 = 9 bits
// 1024 = 10 bits
// 4096 = 12 bits
// 65536 = 16 bits
void Convert8To16Row_AVX512BW(const uint8_t* src_y,
uint16_t* dst_y,
int scale,
int bits,
int width) {
const int shift = __builtin_clz(scale) - 15;
const int shift = 16 - bits;
asm volatile(
"vpbroadcastw %4,%%zmm2 \n"
"vmovd %3,%%xmm3 \n"

View File

@ -3835,15 +3835,13 @@ void Convert8To8Row_NEON(const uint8_t* src_y,
// 512 = 9 bits
// 1024 = 10 bits
// 4096 = 12 bits
// 65536 = 16 bits
void Convert8To16Row_NEON(const uint8_t* src_y,
uint16_t* dst_y,
int scale,
int bits,
int width) {
// (src * 0x0101 * scale) >> 16.
// Since scale is a power of two, compute the shift to use to avoid needing
// to widen to int32.
const int shift = 15 - __builtin_clz(scale);
// (src * 0x0101) >> (16 - bits).
// Use negative shift for right shift with vshl.
const int shift = bits - 16;
asm volatile(
"vdup.16 q2, %3 \n"
"1: \n"

View File

@ -5273,12 +5273,11 @@ void Convert8To8Row_NEON(const uint8_t* src_y,
// 65536 = 16 bits
void Convert8To16Row_NEON(const uint8_t* src_y,
uint16_t* dst_y,
int scale,
int bits,
int width) {
// (src * 0x0101 * scale) >> 16.
// Since scale is a power of two, compute the shift to use to avoid needing
// to widen to int32.
const int shift = 15 - __builtin_clz(scale);
// (src * 0x0101) >> (16 - bits).
// Use negative shift for right shift with ushl.
const int shift = bits - 16;
asm volatile(
"dup v2.8h, %w[shift] \n"
"1: \n"

View File

@ -1821,10 +1821,10 @@ void Convert16To8Row_RVV(const uint16_t* src_y,
// 65536 = 16 bits
void Convert8To16Row_RVV(const uint8_t* src_y,
uint16_t* dst_y,
int scale,
int bits,
int width) {
size_t w = (size_t)width;
const int shift = __builtin_clz((int32_t)scale) - 15;
const int shift = 16 - bits;
do {
size_t vl = __riscv_vsetvl_e8m2(w);
vuint8m2_t v_src = __riscv_vle8_v_u8m2(src_y, vl);

View File

@ -1054,12 +1054,10 @@ __arm_locally_streaming void Convert8To8Row_SME(const uint8_t* src_y,
__arm_locally_streaming void Convert8To16Row_SME(const uint8_t* src_y,
uint16_t* dst_y,
int scale,
int bits,
int width) {
// (src * 0x0101 * scale) >> 16.
// Since scale is a power of two, compute the shift to use to avoid needing
// to widen to int32.
const int shift = __builtin_clz(scale) - 15;
// (src * 0x0101) >> (16 - bits).
const int shift = 16 - bits;
uint64_t vl;
asm volatile(

View File

@ -766,23 +766,12 @@ void ARGBToUVMatrixRow_AVX2(const uint8_t* src_argb,
#endif // HAS_ARGBTOUVMATRIXROW_AVX2
#ifdef HAS_CONVERT8TO16ROW_AVX2
// Use scale to convert to lsb formats depending how many bits there are:
// 512 = 9 bits
// 1024 = 10 bits
// 4096 = 12 bits
// 65536 = 16 bits
LIBYUV_TARGET_AVX2
void Convert8To16Row_AVX2(const uint8_t* src_y,
uint16_t* dst_y,
int scale,
int bits,
int width) {
#if defined(_MSC_VER) && !defined(__clang__)
unsigned long index;
_BitScanReverse(&index, (unsigned long)scale);
const int shift = 16 - (int)index;
#else
const int shift = __builtin_clz(scale) - 15;
#endif
const int shift = 16 - bits;
__m128i shift128 = _mm_cvtsi32_si128(shift);
do {
__m256i ymm0 = _mm256_loadu_si256((const __m256i*)src_y);

View File

@ -1680,6 +1680,42 @@ TEST_F(LibYUVConvertTest, RotateWithARGBSource) {
ASSERT_EQ(dst[3], src[1]);
}
TEST_F(LibYUVConvertTest, ConvertToARGB_NV16) {
const int kWidth = 64;
const int kHeight = 48;
const int sample_size = kWidth * kHeight * 2;
align_buffer_page_end(src, sample_size);
align_buffer_page_end(dst, kWidth * kHeight * 4);
MemRandomize(src, sample_size);
memset(dst, 0, kWidth * kHeight * 4);
int r = ConvertToARGB(src, sample_size, dst, kWidth * 4,
0, 0, kWidth, kHeight, kWidth, kHeight,
kRotate0, FOURCC_NV16);
EXPECT_EQ(0, r);
free_aligned_buffer_page_end(src);
free_aligned_buffer_page_end(dst);
}
TEST_F(LibYUVConvertTest, ConvertToARGB_NV24) {
const int kWidth = 64;
const int kHeight = 48;
const int sample_size = kWidth * kHeight * 3;
align_buffer_page_end(src, sample_size);
align_buffer_page_end(dst, kWidth * kHeight * 4);
MemRandomize(src, sample_size);
memset(dst, 0, kWidth * kHeight * 4);
int r = ConvertToARGB(src, sample_size, dst, kWidth * 4,
0, 0, kWidth, kHeight, kWidth, kHeight,
kRotate0, FOURCC_NV24);
EXPECT_EQ(0, r);
free_aligned_buffer_page_end(src);
free_aligned_buffer_page_end(dst);
}
#ifdef HAS_ARGBTOAR30ROW_AVX2
TEST_F(LibYUVConvertTest, ARGBToAR30Row_Opt) {
// ARGBToAR30Row_AVX2 expects a multiple of 8 pixels.

View File

@ -1923,6 +1923,100 @@ TEST_F(LibYUVConvertTest, NV12Crop) {
free_aligned_buffer_page_end(src_y);
}
TEST_F(LibYUVConvertTest, ConvertToI420_NV16) {
const int kWidth = 64;
const int kHeight = 48;
const int sample_size = kWidth * kHeight * 2;
align_buffer_page_end(src, sample_size);
align_buffer_page_end(dst_y, kWidth * kHeight);
align_buffer_page_end(dst_u, (kWidth / 2) * (kHeight / 2));
align_buffer_page_end(dst_v, (kWidth / 2) * (kHeight / 2));
MemRandomize(src, sample_size);
memset(dst_y, 0, kWidth * kHeight);
memset(dst_u, 0, (kWidth / 2) * (kHeight / 2));
memset(dst_v, 0, (kWidth / 2) * (kHeight / 2));
int r = ConvertToI420(src, sample_size, dst_y, kWidth, dst_u, kWidth / 2,
dst_v, kWidth / 2, 0, 0, kWidth, kHeight, kWidth,
kHeight, kRotate0, FOURCC_NV16);
EXPECT_EQ(0, r);
free_aligned_buffer_page_end(src);
free_aligned_buffer_page_end(dst_y);
free_aligned_buffer_page_end(dst_u);
free_aligned_buffer_page_end(dst_v);
}
TEST_F(LibYUVConvertTest, ConvertToI420_NV24) {
const int kWidth = 64;
const int kHeight = 48;
const int sample_size = kWidth * kHeight * 3;
align_buffer_page_end(src, sample_size);
align_buffer_page_end(dst_y, kWidth * kHeight);
align_buffer_page_end(dst_u, (kWidth / 2) * (kHeight / 2));
align_buffer_page_end(dst_v, (kWidth / 2) * (kHeight / 2));
MemRandomize(src, sample_size);
memset(dst_y, 0, kWidth * kHeight);
memset(dst_u, 0, (kWidth / 2) * (kHeight / 2));
memset(dst_v, 0, (kWidth / 2) * (kHeight / 2));
int r = ConvertToI420(src, sample_size, dst_y, kWidth, dst_u, kWidth / 2,
dst_v, kWidth / 2, 0, 0, kWidth, kHeight, kWidth,
kHeight, kRotate0, FOURCC_NV24);
EXPECT_EQ(0, r);
free_aligned_buffer_page_end(src);
free_aligned_buffer_page_end(dst_y);
free_aligned_buffer_page_end(dst_u);
free_aligned_buffer_page_end(dst_v);
}
TEST_F(LibYUVConvertTest, ConvertFromI420_NV16) {
const int kWidth = 64;
const int kHeight = 48;
const int dst_size = kWidth * kHeight * 2;
align_buffer_page_end(src_y, kWidth * kHeight);
align_buffer_page_end(src_u, (kWidth / 2) * (kHeight / 2));
align_buffer_page_end(src_v, (kWidth / 2) * (kHeight / 2));
align_buffer_page_end(dst, dst_size);
MemRandomize(src_y, kWidth * kHeight);
MemRandomize(src_u, (kWidth / 2) * (kHeight / 2));
MemRandomize(src_v, (kWidth / 2) * (kHeight / 2));
memset(dst, 0, dst_size);
int r = ConvertFromI420(src_y, kWidth, src_u, kWidth / 2, src_v, kWidth / 2,
dst, kWidth, kWidth, kHeight, FOURCC_NV16);
EXPECT_EQ(0, r);
free_aligned_buffer_page_end(src_y);
free_aligned_buffer_page_end(src_u);
free_aligned_buffer_page_end(src_v);
free_aligned_buffer_page_end(dst);
}
TEST_F(LibYUVConvertTest, ConvertFromI420_NV24) {
const int kWidth = 64;
const int kHeight = 48;
const int dst_size = kWidth * kHeight * 3;
align_buffer_page_end(src_y, kWidth * kHeight);
align_buffer_page_end(src_u, (kWidth / 2) * (kHeight / 2));
align_buffer_page_end(src_v, (kWidth / 2) * (kHeight / 2));
align_buffer_page_end(dst, dst_size);
MemRandomize(src_y, kWidth * kHeight);
MemRandomize(src_u, (kWidth / 2) * (kHeight / 2));
MemRandomize(src_v, (kWidth / 2) * (kHeight / 2));
memset(dst, 0, dst_size);
int r = ConvertFromI420(src_y, kWidth, src_u, kWidth / 2, src_v, kWidth / 2,
dst, kWidth, kWidth, kHeight, FOURCC_NV24);
EXPECT_EQ(0, r);
free_aligned_buffer_page_end(src_y);
free_aligned_buffer_page_end(src_u);
free_aligned_buffer_page_end(src_v);
free_aligned_buffer_page_end(dst);
}
TEST_F(LibYUVConvertTest, I420CropOddY) {
const int SUBSAMP_X = 2;
const int SUBSAMP_Y = 2;

View File

@ -4159,7 +4159,7 @@ TEST_F(LibYUVPlanarTest, Convert8To16Plane) {
MaskCpuFlags(disable_cpu_flags_);
Convert8To16Plane(src_pixels_y, benchmark_width_,
reinterpret_cast<uint16_t*>(dst_pixels_y_c),
benchmark_width_, 1024, benchmark_width_,
benchmark_width_, 10, benchmark_width_,
benchmark_height_);
MaskCpuFlags(benchmark_cpu_info_);
@ -4193,7 +4193,7 @@ TEST_F(LibYUVPlanarTest, Convert8To16Row_Opt) {
memset(dst_pixels_y_c, 1, kPixels * 2);
Convert8To16Row_C(src_pixels_y, reinterpret_cast<uint16_t*>(dst_pixels_y_c),
1024, kPixels);
10, kPixels);
#if defined(HAS_CONVERT8TO16ROW_AVX512BW)
int has_avx512 = TestCpuFlag(kCpuHasAVX512BW);
@ -4205,20 +4205,20 @@ TEST_F(LibYUVPlanarTest, Convert8To16Row_Opt) {
if (has_avx512) {
Convert8To16Row_AVX512BW(src_pixels_y,
reinterpret_cast<uint16_t*>(dst_pixels_y_opt),
1024, kPixels);
10, kPixels);
} else
#endif
if (has_avx2) {
Convert8To16Row_AVX2(src_pixels_y,
reinterpret_cast<uint16_t*>(dst_pixels_y_opt), 1024,
reinterpret_cast<uint16_t*>(dst_pixels_y_opt), 10,
kPixels);
} else if (has_sse2) {
Convert8To16Row_SSE2(src_pixels_y,
reinterpret_cast<uint16_t*>(dst_pixels_y_opt), 1024,
reinterpret_cast<uint16_t*>(dst_pixels_y_opt), 10,
kPixels);
} else {
Convert8To16Row_C(src_pixels_y,
reinterpret_cast<uint16_t*>(dst_pixels_y_opt), 1024,
reinterpret_cast<uint16_t*>(dst_pixels_y_opt), 10,
kPixels);
}
}

View File

@ -63,6 +63,8 @@ TEST_F(LibYUVBaseTest, TestFourCC) {
ASSERT_TRUE(TestValidFourCC(FOURCC_I400, FOURCC_BPP_I400));
ASSERT_TRUE(TestValidFourCC(FOURCC_NV21, FOURCC_BPP_NV21));
ASSERT_TRUE(TestValidFourCC(FOURCC_NV12, FOURCC_BPP_NV12));
ASSERT_TRUE(TestValidFourCC(FOURCC_NV16, FOURCC_BPP_NV16));
ASSERT_TRUE(TestValidFourCC(FOURCC_NV24, FOURCC_BPP_NV24));
ASSERT_TRUE(TestValidFourCC(FOURCC_YUY2, FOURCC_BPP_YUY2));
ASSERT_TRUE(TestValidFourCC(FOURCC_UYVY, FOURCC_BPP_UYVY));
ASSERT_TRUE(TestValidFourCC(FOURCC_M420, FOURCC_BPP_M420)); // deprecated.