Path: blob/master/thirdparty/libwebp/sharpyuv/sharpyuv.c
9898 views
// Copyright 2022 Google Inc. All Rights Reserved.1//2// Use of this source code is governed by a BSD-style license3// that can be found in the COPYING file in the root of the source4// tree. An additional intellectual property rights grant can be found5// in the file PATENTS. All contributing project authors may6// be found in the AUTHORS file in the root of the source tree.7// -----------------------------------------------------------------------------8//9// Sharp RGB to YUV conversion.10//11// Author: Skal ([email protected])1213#include "sharpyuv/sharpyuv.h"1415#include <assert.h>16#include <limits.h>17#include <stddef.h>18#include <stdlib.h>19#include <string.h>2021#include "src/webp/types.h"22#include "sharpyuv/sharpyuv_cpu.h"23#include "sharpyuv/sharpyuv_dsp.h"24#include "sharpyuv/sharpyuv_gamma.h"2526//------------------------------------------------------------------------------2728int SharpYuvGetVersion(void) {29return SHARPYUV_VERSION;30}3132//------------------------------------------------------------------------------33// Sharp RGB->YUV conversion3435static const int kNumIterations = 4;3637#define YUV_FIX 16 // fixed-point precision for RGB->YUV38static const int kYuvHalf = 1 << (YUV_FIX - 1);3940// Max bit depth so that intermediate calculations fit in 16 bits.41static const int kMaxBitDepth = 14;4243// Returns the precision shift to use based on the input rgb_bit_depth.44static int GetPrecisionShift(int rgb_bit_depth) {45// Try to add 2 bits of precision if it fits in kMaxBitDepth. Otherwise remove46// bits if needed.47return ((rgb_bit_depth + 2) <= kMaxBitDepth) ? 248: (kMaxBitDepth - rgb_bit_depth);49}5051typedef int16_t fixed_t; // signed type with extra precision for UV52typedef uint16_t fixed_y_t; // unsigned type with extra precision for W5354//------------------------------------------------------------------------------5556static uint8_t clip_8b(fixed_t v) {57return (!(v & ~0xff)) ? (uint8_t)v : (v < 0) ? 0u : 255u;58}5960static uint16_t clip(fixed_t v, int max) {61return (v < 0) ? 0 : (v > max) ? max : (uint16_t)v;62}6364static fixed_y_t clip_bit_depth(int y, int bit_depth) {65const int max = (1 << bit_depth) - 1;66return (!(y & ~max)) ? (fixed_y_t)y : (y < 0) ? 0 : max;67}6869//------------------------------------------------------------------------------7071static int RGBToGray(int64_t r, int64_t g, int64_t b) {72const int64_t luma = 13933 * r + 46871 * g + 4732 * b + kYuvHalf;73return (int)(luma >> YUV_FIX);74}7576static uint32_t ScaleDown(uint16_t a, uint16_t b, uint16_t c, uint16_t d,77int rgb_bit_depth,78SharpYuvTransferFunctionType transfer_type) {79const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);80const uint32_t A = SharpYuvGammaToLinear(a, bit_depth, transfer_type);81const uint32_t B = SharpYuvGammaToLinear(b, bit_depth, transfer_type);82const uint32_t C = SharpYuvGammaToLinear(c, bit_depth, transfer_type);83const uint32_t D = SharpYuvGammaToLinear(d, bit_depth, transfer_type);84return SharpYuvLinearToGamma((A + B + C + D + 2) >> 2, bit_depth,85transfer_type);86}8788static WEBP_INLINE void UpdateW(const fixed_y_t* src, fixed_y_t* dst, int w,89int rgb_bit_depth,90SharpYuvTransferFunctionType transfer_type) {91const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);92int i = 0;93do {94const uint32_t R =95SharpYuvGammaToLinear(src[0 * w + i], bit_depth, transfer_type);96const uint32_t G =97SharpYuvGammaToLinear(src[1 * w + i], bit_depth, transfer_type);98const uint32_t B =99SharpYuvGammaToLinear(src[2 * w + i], bit_depth, transfer_type);100const uint32_t Y = RGBToGray(R, G, B);101dst[i] = (fixed_y_t)SharpYuvLinearToGamma(Y, bit_depth, transfer_type);102} while (++i < w);103}104105static void UpdateChroma(const fixed_y_t* src1, const fixed_y_t* src2,106fixed_t* dst, int uv_w, int rgb_bit_depth,107SharpYuvTransferFunctionType transfer_type) {108int i = 0;109do {110const int r =111ScaleDown(src1[0 * uv_w + 0], src1[0 * uv_w + 1], src2[0 * uv_w + 0],112src2[0 * uv_w + 1], rgb_bit_depth, transfer_type);113const int g =114ScaleDown(src1[2 * uv_w + 0], src1[2 * uv_w + 1], src2[2 * uv_w + 0],115src2[2 * uv_w + 1], rgb_bit_depth, transfer_type);116const int b =117ScaleDown(src1[4 * uv_w + 0], src1[4 * uv_w + 1], src2[4 * uv_w + 0],118src2[4 * uv_w + 1], rgb_bit_depth, transfer_type);119const int W = RGBToGray(r, g, b);120dst[0 * uv_w] = (fixed_t)(r - W);121dst[1 * uv_w] = (fixed_t)(g - W);122dst[2 * uv_w] = (fixed_t)(b - W);123dst += 1;124src1 += 2;125src2 += 2;126} while (++i < uv_w);127}128129static void StoreGray(const fixed_y_t* rgb, fixed_y_t* y, int w) {130int i = 0;131assert(w > 0);132do {133y[i] = RGBToGray(rgb[0 * w + i], rgb[1 * w + i], rgb[2 * w + i]);134} while (++i < w);135}136137//------------------------------------------------------------------------------138139static WEBP_INLINE fixed_y_t Filter2(int A, int B, int W0, int bit_depth) {140const int v0 = (A * 3 + B + 2) >> 2;141return clip_bit_depth(v0 + W0, bit_depth);142}143144//------------------------------------------------------------------------------145146static WEBP_INLINE int Shift(int v, int shift) {147return (shift >= 0) ? (v << shift) : (v >> -shift);148}149150static void ImportOneRow(const uint8_t* const r_ptr,151const uint8_t* const g_ptr,152const uint8_t* const b_ptr,153int rgb_step,154int rgb_bit_depth,155int pic_width,156fixed_y_t* const dst) {157// Convert the rgb_step from a number of bytes to a number of uint8_t or158// uint16_t values depending the bit depth.159const int step = (rgb_bit_depth > 8) ? rgb_step / 2 : rgb_step;160int i = 0;161const int w = (pic_width + 1) & ~1;162do {163const int off = i * step;164const int shift = GetPrecisionShift(rgb_bit_depth);165if (rgb_bit_depth == 8) {166dst[i + 0 * w] = Shift(r_ptr[off], shift);167dst[i + 1 * w] = Shift(g_ptr[off], shift);168dst[i + 2 * w] = Shift(b_ptr[off], shift);169} else {170dst[i + 0 * w] = Shift(((uint16_t*)r_ptr)[off], shift);171dst[i + 1 * w] = Shift(((uint16_t*)g_ptr)[off], shift);172dst[i + 2 * w] = Shift(((uint16_t*)b_ptr)[off], shift);173}174} while (++i < pic_width);175if (pic_width & 1) { // replicate rightmost pixel176dst[pic_width + 0 * w] = dst[pic_width + 0 * w - 1];177dst[pic_width + 1 * w] = dst[pic_width + 1 * w - 1];178dst[pic_width + 2 * w] = dst[pic_width + 2 * w - 1];179}180}181182static void InterpolateTwoRows(const fixed_y_t* const best_y,183const fixed_t* prev_uv,184const fixed_t* cur_uv,185const fixed_t* next_uv,186int w,187fixed_y_t* out1,188fixed_y_t* out2,189int rgb_bit_depth) {190const int uv_w = w >> 1;191const int len = (w - 1) >> 1; // length to filter192int k = 3;193const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);194while (k-- > 0) { // process each R/G/B segments in turn195// special boundary case for i==0196out1[0] = Filter2(cur_uv[0], prev_uv[0], best_y[0], bit_depth);197out2[0] = Filter2(cur_uv[0], next_uv[0], best_y[w], bit_depth);198199SharpYuvFilterRow(cur_uv, prev_uv, len, best_y + 0 + 1, out1 + 1,200bit_depth);201SharpYuvFilterRow(cur_uv, next_uv, len, best_y + w + 1, out2 + 1,202bit_depth);203204// special boundary case for i == w - 1 when w is even205if (!(w & 1)) {206out1[w - 1] = Filter2(cur_uv[uv_w - 1], prev_uv[uv_w - 1],207best_y[w - 1 + 0], bit_depth);208out2[w - 1] = Filter2(cur_uv[uv_w - 1], next_uv[uv_w - 1],209best_y[w - 1 + w], bit_depth);210}211out1 += w;212out2 += w;213prev_uv += uv_w;214cur_uv += uv_w;215next_uv += uv_w;216}217}218219static WEBP_INLINE int RGBToYUVComponent(int r, int g, int b,220const int coeffs[4], int sfix) {221const int srounder = 1 << (YUV_FIX + sfix - 1);222const int luma = coeffs[0] * r + coeffs[1] * g + coeffs[2] * b +223coeffs[3] + srounder;224return (luma >> (YUV_FIX + sfix));225}226227static int ConvertWRGBToYUV(const fixed_y_t* best_y, const fixed_t* best_uv,228uint8_t* y_ptr, int y_stride, uint8_t* u_ptr,229int u_stride, uint8_t* v_ptr, int v_stride,230int rgb_bit_depth,231int yuv_bit_depth, int width, int height,232const SharpYuvConversionMatrix* yuv_matrix) {233int i, j;234const fixed_t* const best_uv_base = best_uv;235const int w = (width + 1) & ~1;236const int h = (height + 1) & ~1;237const int uv_w = w >> 1;238const int uv_h = h >> 1;239const int sfix = GetPrecisionShift(rgb_bit_depth);240const int yuv_max = (1 << yuv_bit_depth) - 1;241242best_uv = best_uv_base;243j = 0;244do {245i = 0;246do {247const int off = (i >> 1);248const int W = best_y[i];249const int r = best_uv[off + 0 * uv_w] + W;250const int g = best_uv[off + 1 * uv_w] + W;251const int b = best_uv[off + 2 * uv_w] + W;252const int y = RGBToYUVComponent(r, g, b, yuv_matrix->rgb_to_y, sfix);253if (yuv_bit_depth <= 8) {254y_ptr[i] = clip_8b(y);255} else {256((uint16_t*)y_ptr)[i] = clip(y, yuv_max);257}258} while (++i < width);259best_y += w;260best_uv += (j & 1) * 3 * uv_w;261y_ptr += y_stride;262} while (++j < height);263264best_uv = best_uv_base;265j = 0;266do {267i = 0;268do {269// Note r, g and b values here are off by W, but a constant offset on all270// 3 components doesn't change the value of u and v with a YCbCr matrix.271const int r = best_uv[i + 0 * uv_w];272const int g = best_uv[i + 1 * uv_w];273const int b = best_uv[i + 2 * uv_w];274const int u = RGBToYUVComponent(r, g, b, yuv_matrix->rgb_to_u, sfix);275const int v = RGBToYUVComponent(r, g, b, yuv_matrix->rgb_to_v, sfix);276if (yuv_bit_depth <= 8) {277u_ptr[i] = clip_8b(u);278v_ptr[i] = clip_8b(v);279} else {280((uint16_t*)u_ptr)[i] = clip(u, yuv_max);281((uint16_t*)v_ptr)[i] = clip(v, yuv_max);282}283} while (++i < uv_w);284best_uv += 3 * uv_w;285u_ptr += u_stride;286v_ptr += v_stride;287} while (++j < uv_h);288return 1;289}290291//------------------------------------------------------------------------------292// Main function293294static void* SafeMalloc(uint64_t nmemb, size_t size) {295const uint64_t total_size = nmemb * (uint64_t)size;296if (total_size != (size_t)total_size) return NULL;297return malloc((size_t)total_size);298}299300#define SAFE_ALLOC(W, H, T) ((T*)SafeMalloc((uint64_t)(W) * (H), sizeof(T)))301302static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr,303const uint8_t* b_ptr, int rgb_step, int rgb_stride,304int rgb_bit_depth, uint8_t* y_ptr, int y_stride,305uint8_t* u_ptr, int u_stride, uint8_t* v_ptr,306int v_stride, int yuv_bit_depth, int width,307int height,308const SharpYuvConversionMatrix* yuv_matrix,309SharpYuvTransferFunctionType transfer_type) {310// we expand the right/bottom border if needed311const int w = (width + 1) & ~1;312const int h = (height + 1) & ~1;313const int uv_w = w >> 1;314const int uv_h = h >> 1;315const int y_bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);316uint64_t prev_diff_y_sum = ~0;317int j, iter;318319// TODO(skal): allocate one big memory chunk. But for now, it's easier320// for valgrind debugging to have several chunks.321fixed_y_t* const tmp_buffer = SAFE_ALLOC(w * 3, 2, fixed_y_t); // scratch322fixed_y_t* const best_y_base = SAFE_ALLOC(w, h, fixed_y_t);323fixed_y_t* const target_y_base = SAFE_ALLOC(w, h, fixed_y_t);324fixed_y_t* const best_rgb_y = SAFE_ALLOC(w, 2, fixed_y_t);325fixed_t* const best_uv_base = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t);326fixed_t* const target_uv_base = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t);327fixed_t* const best_rgb_uv = SAFE_ALLOC(uv_w * 3, 1, fixed_t);328fixed_y_t* best_y = best_y_base;329fixed_y_t* target_y = target_y_base;330fixed_t* best_uv = best_uv_base;331fixed_t* target_uv = target_uv_base;332const uint64_t diff_y_threshold = (uint64_t)(3.0 * w * h);333int ok;334assert(w > 0);335assert(h > 0);336337if (best_y_base == NULL || best_uv_base == NULL ||338target_y_base == NULL || target_uv_base == NULL ||339best_rgb_y == NULL || best_rgb_uv == NULL ||340tmp_buffer == NULL) {341ok = 0;342goto End;343}344345// Import RGB samples to W/RGB representation.346for (j = 0; j < height; j += 2) {347const int is_last_row = (j == height - 1);348fixed_y_t* const src1 = tmp_buffer + 0 * w;349fixed_y_t* const src2 = tmp_buffer + 3 * w;350351// prepare two rows of input352ImportOneRow(r_ptr, g_ptr, b_ptr, rgb_step, rgb_bit_depth, width,353src1);354if (!is_last_row) {355ImportOneRow(r_ptr + rgb_stride, g_ptr + rgb_stride, b_ptr + rgb_stride,356rgb_step, rgb_bit_depth, width, src2);357} else {358memcpy(src2, src1, 3 * w * sizeof(*src2));359}360StoreGray(src1, best_y + 0, w);361StoreGray(src2, best_y + w, w);362363UpdateW(src1, target_y, w, rgb_bit_depth, transfer_type);364UpdateW(src2, target_y + w, w, rgb_bit_depth, transfer_type);365UpdateChroma(src1, src2, target_uv, uv_w, rgb_bit_depth, transfer_type);366memcpy(best_uv, target_uv, 3 * uv_w * sizeof(*best_uv));367best_y += 2 * w;368best_uv += 3 * uv_w;369target_y += 2 * w;370target_uv += 3 * uv_w;371r_ptr += 2 * rgb_stride;372g_ptr += 2 * rgb_stride;373b_ptr += 2 * rgb_stride;374}375376// Iterate and resolve clipping conflicts.377for (iter = 0; iter < kNumIterations; ++iter) {378const fixed_t* cur_uv = best_uv_base;379const fixed_t* prev_uv = best_uv_base;380uint64_t diff_y_sum = 0;381382best_y = best_y_base;383best_uv = best_uv_base;384target_y = target_y_base;385target_uv = target_uv_base;386j = 0;387do {388fixed_y_t* const src1 = tmp_buffer + 0 * w;389fixed_y_t* const src2 = tmp_buffer + 3 * w;390{391const fixed_t* const next_uv = cur_uv + ((j < h - 2) ? 3 * uv_w : 0);392InterpolateTwoRows(best_y, prev_uv, cur_uv, next_uv, w,393src1, src2, rgb_bit_depth);394prev_uv = cur_uv;395cur_uv = next_uv;396}397398UpdateW(src1, best_rgb_y + 0 * w, w, rgb_bit_depth, transfer_type);399UpdateW(src2, best_rgb_y + 1 * w, w, rgb_bit_depth, transfer_type);400UpdateChroma(src1, src2, best_rgb_uv, uv_w, rgb_bit_depth, transfer_type);401402// update two rows of Y and one row of RGB403diff_y_sum +=404SharpYuvUpdateY(target_y, best_rgb_y, best_y, 2 * w, y_bit_depth);405SharpYuvUpdateRGB(target_uv, best_rgb_uv, best_uv, 3 * uv_w);406407best_y += 2 * w;408best_uv += 3 * uv_w;409target_y += 2 * w;410target_uv += 3 * uv_w;411j += 2;412} while (j < h);413// test exit condition414if (iter > 0) {415if (diff_y_sum < diff_y_threshold) break;416if (diff_y_sum > prev_diff_y_sum) break;417}418prev_diff_y_sum = diff_y_sum;419}420421// final reconstruction422ok = ConvertWRGBToYUV(best_y_base, best_uv_base, y_ptr, y_stride, u_ptr,423u_stride, v_ptr, v_stride, rgb_bit_depth, yuv_bit_depth,424width, height, yuv_matrix);425426End:427free(best_y_base);428free(best_uv_base);429free(target_y_base);430free(target_uv_base);431free(best_rgb_y);432free(best_rgb_uv);433free(tmp_buffer);434return ok;435}436437#undef SAFE_ALLOC438439#if defined(WEBP_USE_THREAD) && !defined(_WIN32)440#include <pthread.h> // NOLINT441442#define LOCK_ACCESS \443static pthread_mutex_t sharpyuv_lock = PTHREAD_MUTEX_INITIALIZER; \444if (pthread_mutex_lock(&sharpyuv_lock)) return445#define UNLOCK_ACCESS_AND_RETURN \446do { \447(void)pthread_mutex_unlock(&sharpyuv_lock); \448return; \449} while (0)450#else // !(defined(WEBP_USE_THREAD) && !defined(_WIN32))451#define LOCK_ACCESS do {} while (0)452#define UNLOCK_ACCESS_AND_RETURN return453#endif // defined(WEBP_USE_THREAD) && !defined(_WIN32)454455// Hidden exported init function.456// By default SharpYuvConvert calls it with SharpYuvGetCPUInfo. If needed,457// users can declare it as extern and call it with an alternate VP8CPUInfo458// function.459extern VP8CPUInfo SharpYuvGetCPUInfo;460SHARPYUV_EXTERN void SharpYuvInit(VP8CPUInfo cpu_info_func);461void SharpYuvInit(VP8CPUInfo cpu_info_func) {462static volatile VP8CPUInfo sharpyuv_last_cpuinfo_used =463(VP8CPUInfo)&sharpyuv_last_cpuinfo_used;464LOCK_ACCESS;465// Only update SharpYuvGetCPUInfo when called from external code to avoid a466// race on reading the value in SharpYuvConvert().467if (cpu_info_func != (VP8CPUInfo)&SharpYuvGetCPUInfo) {468SharpYuvGetCPUInfo = cpu_info_func;469}470if (sharpyuv_last_cpuinfo_used == SharpYuvGetCPUInfo) {471UNLOCK_ACCESS_AND_RETURN;472}473474SharpYuvInitDsp();475SharpYuvInitGammaTables();476477sharpyuv_last_cpuinfo_used = SharpYuvGetCPUInfo;478UNLOCK_ACCESS_AND_RETURN;479}480481int SharpYuvConvert(const void* r_ptr, const void* g_ptr, const void* b_ptr,482int rgb_step, int rgb_stride, int rgb_bit_depth,483void* y_ptr, int y_stride, void* u_ptr, int u_stride,484void* v_ptr, int v_stride, int yuv_bit_depth, int width,485int height, const SharpYuvConversionMatrix* yuv_matrix) {486SharpYuvOptions options;487options.yuv_matrix = yuv_matrix;488options.transfer_type = kSharpYuvTransferFunctionSrgb;489return SharpYuvConvertWithOptions(490r_ptr, g_ptr, b_ptr, rgb_step, rgb_stride, rgb_bit_depth, y_ptr, y_stride,491u_ptr, u_stride, v_ptr, v_stride, yuv_bit_depth, width, height, &options);492}493494int SharpYuvOptionsInitInternal(const SharpYuvConversionMatrix* yuv_matrix,495SharpYuvOptions* options, int version) {496const int major = (version >> 24);497const int minor = (version >> 16) & 0xff;498if (options == NULL || yuv_matrix == NULL ||499(major == SHARPYUV_VERSION_MAJOR && major == 0 &&500minor != SHARPYUV_VERSION_MINOR) ||501(major != SHARPYUV_VERSION_MAJOR)) {502return 0;503}504options->yuv_matrix = yuv_matrix;505options->transfer_type = kSharpYuvTransferFunctionSrgb;506return 1;507}508509int SharpYuvConvertWithOptions(const void* r_ptr, const void* g_ptr,510const void* b_ptr, int rgb_step, int rgb_stride,511int rgb_bit_depth, void* y_ptr, int y_stride,512void* u_ptr, int u_stride, void* v_ptr,513int v_stride, int yuv_bit_depth, int width,514int height, const SharpYuvOptions* options) {515const SharpYuvConversionMatrix* yuv_matrix = options->yuv_matrix;516SharpYuvTransferFunctionType transfer_type = options->transfer_type;517SharpYuvConversionMatrix scaled_matrix;518const int rgb_max = (1 << rgb_bit_depth) - 1;519const int rgb_round = 1 << (rgb_bit_depth - 1);520const int yuv_max = (1 << yuv_bit_depth) - 1;521const int sfix = GetPrecisionShift(rgb_bit_depth);522523if (width < 1 || height < 1 || width == INT_MAX || height == INT_MAX ||524r_ptr == NULL || g_ptr == NULL || b_ptr == NULL || y_ptr == NULL ||525u_ptr == NULL || v_ptr == NULL) {526return 0;527}528if (rgb_bit_depth != 8 && rgb_bit_depth != 10 && rgb_bit_depth != 12 &&529rgb_bit_depth != 16) {530return 0;531}532if (yuv_bit_depth != 8 && yuv_bit_depth != 10 && yuv_bit_depth != 12) {533return 0;534}535if (rgb_bit_depth > 8 && (rgb_step % 2 != 0 || rgb_stride % 2 != 0)) {536// Step/stride should be even for uint16_t buffers.537return 0;538}539if (yuv_bit_depth > 8 &&540(y_stride % 2 != 0 || u_stride % 2 != 0 || v_stride % 2 != 0)) {541// Stride should be even for uint16_t buffers.542return 0;543}544// The address of the function pointer is used to avoid a read race.545SharpYuvInit((VP8CPUInfo)&SharpYuvGetCPUInfo);546547// Add scaling factor to go from rgb_bit_depth to yuv_bit_depth, to the548// rgb->yuv conversion matrix.549if (rgb_bit_depth == yuv_bit_depth) {550memcpy(&scaled_matrix, yuv_matrix, sizeof(scaled_matrix));551} else {552int i;553for (i = 0; i < 3; ++i) {554scaled_matrix.rgb_to_y[i] =555(yuv_matrix->rgb_to_y[i] * yuv_max + rgb_round) / rgb_max;556scaled_matrix.rgb_to_u[i] =557(yuv_matrix->rgb_to_u[i] * yuv_max + rgb_round) / rgb_max;558scaled_matrix.rgb_to_v[i] =559(yuv_matrix->rgb_to_v[i] * yuv_max + rgb_round) / rgb_max;560}561}562// Also incorporate precision change scaling.563scaled_matrix.rgb_to_y[3] = Shift(yuv_matrix->rgb_to_y[3], sfix);564scaled_matrix.rgb_to_u[3] = Shift(yuv_matrix->rgb_to_u[3], sfix);565scaled_matrix.rgb_to_v[3] = Shift(yuv_matrix->rgb_to_v[3], sfix);566567return DoSharpArgbToYuv(568(const uint8_t*)r_ptr, (const uint8_t*)g_ptr, (const uint8_t*)b_ptr,569rgb_step, rgb_stride, rgb_bit_depth, (uint8_t*)y_ptr, y_stride,570(uint8_t*)u_ptr, u_stride, (uint8_t*)v_ptr, v_stride, yuv_bit_depth,571width, height, &scaled_matrix, transfer_type);572}573574//------------------------------------------------------------------------------575576577