From 01a05de1a758730a934e39d5db5695347ce7977d Mon Sep 17 00:00:00 2001 From: Maryla Date: Wed, 6 Apr 2022 15:38:21 +0200 Subject: [PATCH] libsharpyuv: add colorspace utilities Change-Id: I620c8593dc81f39e747de5ed354332adb7278bed --- Android.mk | 1 + CMakeLists.txt | 1 + build.gradle | 1 + makefile.unix | 1 + sharpyuv/Makefile.am | 1 + sharpyuv/sharpyuv.c | 13 +---- sharpyuv/sharpyuv.h | 9 ++-- sharpyuv/sharpyuv_csp.c | 111 ++++++++++++++++++++++++++++++++++++++ sharpyuv/sharpyuv_csp.h | 59 ++++++++++++++++++++ sharpyuv/sharpyuv_dsp.h | 6 +-- src/enc/picture_csp_enc.c | 4 +- 11 files changed, 185 insertions(+), 22 deletions(-) create mode 100644 sharpyuv/sharpyuv_csp.c create mode 100644 sharpyuv/sharpyuv_csp.h diff --git a/Android.mk b/Android.mk index 4ca64baa..06a80f45 100644 --- a/Android.mk +++ b/Android.mk @@ -35,6 +35,7 @@ endif sharpyuv_srcs := \ sharpyuv/sharpyuv.c \ + sharpyuv/sharpyuv_csp.c \ sharpyuv/sharpyuv_dsp.c \ sharpyuv/sharpyuv_neon.$(NEON) \ sharpyuv/sharpyuv_sse2.c \ diff --git a/CMakeLists.txt b/CMakeLists.txt index 1cbc6709..3e3a3e54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -220,6 +220,7 @@ target_include_directories(sharpyuv set_target_properties( sharpyuv PROPERTIES PUBLIC_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/sharpyuv/sharpyuv.h;\ +${CMAKE_CURRENT_SOURCE_DIR}/sharpyuv/sharpyuv_csp.h;\ ${CMAKE_CURRENT_SOURCE_DIR}/src/webp/types.h") if(MSVC) diff --git a/build.gradle b/build.gradle index a8bbc9cb..ed2b1b4c 100644 --- a/build.gradle +++ b/build.gradle @@ -107,6 +107,7 @@ model { source { srcDir "sharpyuv" include "sharpyuv.c" + include "sharpyuv_csp.c" include "sharpyuv_dsp.c" include "sharpyuv_neon.c" include "sharpyuv_sse2.c" diff --git a/makefile.unix b/makefile.unix index 3de0d8ff..2e067a22 100644 --- a/makefile.unix +++ b/makefile.unix @@ -127,6 +127,7 @@ ANIM_UTIL_OBJS = \ SHARPYUV_OBJS = \ sharpyuv/sharpyuv.o \ + sharpyuv/sharpyuv_csp.o \ sharpyuv/sharpyuv_dsp.o \ sharpyuv/sharpyuv_neon.o \ sharpyuv/sharpyuv_sse2.o \ diff --git a/sharpyuv/Makefile.am b/sharpyuv/Makefile.am index fa862e37..8bc6c773 100644 --- a/sharpyuv/Makefile.am +++ b/sharpyuv/Makefile.am @@ -20,6 +20,7 @@ libsharpyuv_neon_la_CPPFLAGS = $(libsharpyuv_la_CPPFLAGS) libsharpyuv_neon_la_CFLAGS = $(AM_CFLAGS) $(NEON_FLAGS) libsharpyuv_la_SOURCES = +libsharpyuv_la_SOURCES += sharpyuv_csp.c sharpyuv_csp.h libsharpyuv_la_SOURCES += sharpyuv_dsp.c sharpyuv_dsp.h libsharpyuv_la_SOURCES += sharpyuv.c sharpyuv.h diff --git a/sharpyuv/sharpyuv.c b/sharpyuv/sharpyuv.c index bdd0c3f3..9189e261 100644 --- a/sharpyuv/sharpyuv.c +++ b/sharpyuv/sharpyuv.c @@ -11,7 +11,7 @@ // // Author: Skal (pascal.massimino@gmail.com) -#include "./sharpyuv.h" +#include "sharpyuv/sharpyuv.h" #include #include @@ -455,17 +455,6 @@ void SharpYuvInit(VP8CPUInfo cpu_info_func) { sharpyuv_last_cpuinfo_used = cpu_info_func; } -// In YUV_FIX fixed point precision. -static const SharpYuvConversionMatrix kWebpYuvMatrix = { - {16839, 33059, 6420, 16 << 16}, - {-9719, -19081, 28800, 128 << 16}, - {28800, -24116, -4684, 128 << 16}, -}; - -const SharpYuvConversionMatrix* SharpYuvGetWebpMatrix(void) { - return &kWebpYuvMatrix; -} - int SharpYuvConvert(const uint8_t* r_ptr, const uint8_t* g_ptr, const uint8_t* b_ptr, int step, int rgb_stride, uint8_t* dst_y, int dst_stride_y, uint8_t* dst_u, diff --git a/sharpyuv/sharpyuv.h b/sharpyuv/sharpyuv.h index 44fc4a63..d0ef379f 100644 --- a/sharpyuv/sharpyuv.h +++ b/sharpyuv/sharpyuv.h @@ -29,18 +29,15 @@ typedef struct { int rgb_to_v[4]; } SharpYuvConversionMatrix; -// Returns the RGB to YUV matrix used by WebP. -const SharpYuvConversionMatrix* SharpYuvGetWebpMatrix(void); - // Converts RGB to YUV420 using a downsampling algorithm that minimizes // artefacts caused by chroma subsampling. // This is slower than standard downsampling (averaging of 4 UV values). // Assumes that the image will be upsampled using a bilinear filter. If nearest // neighbor is used instead, the upsampled image might look worse than with // standard downsampling. -// TODO(maryla): add 10 bits and handling of various colorspaces. Add YUV444 to -// YUV420 conversion. Maybe also add 422 support (it's rarely used in practice, -// especially for images). +// TODO(maryla): add 10 bits support. Add YUV444 to YUV420 conversion. +// Maybe also add 422 support (it's rarely used in practice, especially for +// images). int SharpYuvConvert(const uint8_t* r_ptr, const uint8_t* g_ptr, const uint8_t* b_ptr, int step, int rgb_stride, uint8_t* dst_y, int dst_stride_y, uint8_t* dst_u, diff --git a/sharpyuv/sharpyuv_csp.c b/sharpyuv/sharpyuv_csp.c new file mode 100644 index 00000000..4c644489 --- /dev/null +++ b/sharpyuv/sharpyuv_csp.c @@ -0,0 +1,111 @@ +// Copyright 2022 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Colorspace utilities. + +#include "sharpyuv/sharpyuv_csp.h" + +#include +#include +#include + +static int ToFixed16(float f) { return (int)round(f * (1 << 16)); } + +void SharpYuvComputeConversionMatrix(const SharpYuvColorSpace* yuv_color_space, + SharpYuvConversionMatrix* matrix) { + const float kr = yuv_color_space->kr; + const float kb = yuv_color_space->kb; + const float kg = 1.0f - kr - kb; + const float cr = 0.5f / (1.0f - kb); + const float cb = 0.5f / (1.0f - kr); + + const int shift = yuv_color_space->bits - 8; + + const float denom = (float)((1 << yuv_color_space->bits) - 1); + float scaleY = 1.0f; + float addY = 0.0f; + float scaleU = cr; + float scaleV = cb; + float addUV = (128 << shift); + + assert(yuv_color_space->bits >= 8); + + if (yuv_color_space->range == kSharpYuvRangeLimited) { + scaleY *= (219 << shift) / denom; + scaleU *= (224 << shift) / denom; + scaleV *= (224 << shift) / denom; + addY = (16 << shift); + } + + matrix->rgb_to_y[0] = ToFixed16(kr * scaleY); + matrix->rgb_to_y[1] = ToFixed16(kg * scaleY); + matrix->rgb_to_y[2] = ToFixed16(kb * scaleY); + matrix->rgb_to_y[3] = ToFixed16(addY); + + matrix->rgb_to_u[0] = ToFixed16(-kr * scaleU); + matrix->rgb_to_u[1] = ToFixed16(-kg * scaleU); + matrix->rgb_to_u[2] = ToFixed16((1 - kb) * scaleU); + matrix->rgb_to_u[3] = ToFixed16(addUV); + + matrix->rgb_to_v[0] = ToFixed16((1 - kr) * scaleV); + matrix->rgb_to_v[1] = ToFixed16(-kg * scaleV); + matrix->rgb_to_v[2] = ToFixed16(-kb * scaleV); + matrix->rgb_to_v[3] = ToFixed16(addUV); +} + +// Matrices are in YUV_FIX fixed point precision. +// WebP's matrix, similar but not identical to kRec601LimitedMatrix. +static const SharpYuvConversionMatrix kWebpMatrix = { + {16839, 33059, 6420, 16 << 16}, + {-9719, -19081, 28800, 128 << 16}, + {28800, -24116, -4684, 128 << 16}, +}; +// Kr=0.2990f Kb=0.1140f btits=8 range=kLimited +static const SharpYuvConversionMatrix kRec601LimitedMatrix = { + {16829, 33039, 6416, 16 << 16}, + {-9714, -19071, 28784, 128 << 16}, + {28784, -24103, -4681, 128 << 16}, +}; +// Kr=0.2990f Kb=0.1140f btits=8 range=kFull +static const SharpYuvConversionMatrix kRec601FullMatrix = { + {19595, 38470, 7471, 0}, + {-11058, -21710, 32768, 128 << 16}, + {32768, -27439, -5329, 128 << 16}, +}; +// Kr=0.2126f Kb=0.0722f bits=8 range=kLimited +static const SharpYuvConversionMatrix kRec709LimitedMatrix = { + {11966, 40254, 4064, 16 << 16}, + {-6596, -22189, 28784, 128 << 16}, + {28784, -26145, -2639, 128 << 16}, +}; +// Kr=0.2126f Kb=0.0722f bits=8 range=kFull +static const SharpYuvConversionMatrix kRec709FullMatrix = { + {13933, 46871, 4732, 0}, + {-7509, -25259, 32768, 128 << 16}, + {32768, -29763, -3005, 128 << 16}, +}; + +const SharpYuvConversionMatrix* SharpYuvGetConversionMatrix( + SharpYuvMatrixType matrix_type) { + switch (matrix_type) { + case kSharpYuvMatrixWebp: + return &kWebpMatrix; + case kSharpYuvMarixRec601Limited: + return &kRec601LimitedMatrix; + case kSharpYuvMarixRec601Full: + return &kRec601FullMatrix; + case kSharpYuvMarixRec709Limited: + return &kRec709LimitedMatrix; + case kSharpYuvMarixRec709Full: + return &kRec709FullMatrix; + case kSharpYuvMarixNum: + return NULL; + } + return NULL; +} diff --git a/sharpyuv/sharpyuv_csp.h b/sharpyuv/sharpyuv_csp.h new file mode 100644 index 00000000..37229edb --- /dev/null +++ b/sharpyuv/sharpyuv_csp.h @@ -0,0 +1,59 @@ +// Copyright 2022 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Colorspace utilities. + +#ifndef WEBP_SHARPYUV_SHARPYUV_CSP_H_ +#define WEBP_SHARPYUV_SHARPYUV_CSP_H_ + +#include "sharpyuv/sharpyuv.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Range of YUV values. +typedef enum { + kSharpYuvRangeFull, // YUV values between [0;255] (for 8 bit) + kSharpYuvRangeLimited // Y in [16;235], YUV in [16;240] (for 8 bit) +} SharpYuvRange; + +// Constants that define a YUV color space. +typedef struct { + // Kr and Kb are defined such that: + // Y = Kr * r + Kg * g + Kb * b where Kg = 1 - Kr - Kb. + float kr; + float kb; + int bits; // Only 8 bit is supported by SharpYuvConvert. + SharpYuvRange range; +} SharpYuvColorSpace; + +// Fills in 'matrix' for the given YUVColorSpace. +void SharpYuvComputeConversionMatrix(const SharpYuvColorSpace* yuv_color_space, + SharpYuvConversionMatrix* matrix); + +// Enums for precomputed conversion matrices. +typedef enum { + kSharpYuvMatrixWebp = 0, + kSharpYuvMarixRec601Limited, + kSharpYuvMarixRec601Full, + kSharpYuvMarixRec709Limited, + kSharpYuvMarixRec709Full, + kSharpYuvMarixNum +} SharpYuvMatrixType; + +// Returns a pointer to a matrix for one of the predefined colorspaces. +const SharpYuvConversionMatrix* SharpYuvGetConversionMatrix( + SharpYuvMatrixType matrix_type); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_SHARPYUV_SHARPYUV_CSP_H_ diff --git a/sharpyuv/sharpyuv_dsp.h b/sharpyuv/sharpyuv_dsp.h index 474fbc44..af024e94 100644 --- a/sharpyuv/sharpyuv_dsp.h +++ b/sharpyuv/sharpyuv_dsp.h @@ -9,8 +9,8 @@ // // Speed-critical functions for Sharp YUV. -#ifndef WEBP_SHARPYUV_DSP_H_ -#define WEBP_SHARPYUV_DSP_H_ +#ifndef WEBP_SHARPYUV_SHARPYUV_DSP_H_ +#define WEBP_SHARPYUV_SHARPYUV_DSP_H_ #include @@ -25,4 +25,4 @@ extern void (*SharpYUVFilterRow)(const int16_t* A, const int16_t* B, int len, void SharpYuvInitDsp(VP8CPUInfo cpu_info_func); -#endif // WEBP_SHARPYUV_DSP_H_ +#endif // WEBP_SHARPYUV_SHARPYUV_DSP_H_ diff --git a/src/enc/picture_csp_enc.c b/src/enc/picture_csp_enc.c index 3aec3104..cd36551b 100644 --- a/src/enc/picture_csp_enc.c +++ b/src/enc/picture_csp_enc.c @@ -16,6 +16,7 @@ #include #include "sharpyuv/sharpyuv.h" +#include "sharpyuv/sharpyuv_csp.h" #include "src/enc/vp8i_enc.h" #include "src/utils/random_utils.h" #include "src/utils/utils.h" @@ -202,7 +203,8 @@ static int PreprocessARGB(const uint8_t* r_ptr, const int ok = SharpYuvConvert( r_ptr, g_ptr, b_ptr, step, rgb_stride, picture->y, picture->y_stride, picture->u, picture->uv_stride, picture->v, picture->uv_stride, - picture->width, picture->height, SharpYuvGetWebpMatrix()); + picture->width, picture->height, + SharpYuvGetConversionMatrix(kSharpYuvMatrixWebp)); if (!ok) { return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); }