// 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. // ----------------------------------------------------------------------------- // // Sharp RGB to YUV conversion. // // Author: Skal (pascal.massimino@gmail.com)
// Max bit depth so that intermediate calculations fit in 16 bits. staticconstint kMaxBitDepth = 14;
// Returns the precision shift to use based on the input rgb_bit_depth. staticint GetPrecisionShift(int rgb_bit_depth) { // Try to add 2 bits of precision if it fits in kMaxBitDepth. Otherwise remove // bits if needed. return ((rgb_bit_depth + 2) <= kMaxBitDepth) ? 2
: (kMaxBitDepth - rgb_bit_depth);
}
typedef int16_t fixed_t; // signed type with extra precision for UV typedef uint16_t fixed_y_t; // unsigned type with extra precision for W
staticvoid StoreGray(const fixed_y_t* rgb, fixed_y_t* y, int w) { int i = 0;
assert(w > 0); do {
y[i] = RGBToGray(rgb[0 * w + i], rgb[1 * w + i], rgb[2 * w + i]);
} while (++i < w);
}
static WEBP_INLINE fixed_y_t Filter2(int A, int B, int W0, int bit_depth) { constint v0 = (A * 3 + B + 2) >> 2; return clip_bit_depth(v0 + W0, bit_depth);
}
// special boundary case for i == w - 1 when w is even if (!(w & 1)) {
out1[w - 1] = Filter2(cur_uv[uv_w - 1], prev_uv[uv_w - 1],
best_y[w - 1 + 0], bit_depth);
out2[w - 1] = Filter2(cur_uv[uv_w - 1], next_uv[uv_w - 1],
best_y[w - 1 + w], bit_depth);
}
out1 += w;
out2 += w;
prev_uv += uv_w;
cur_uv += uv_w;
next_uv += uv_w;
}
}
static WEBP_INLINE int RGBToYUVComponent(int r, int g, int b, constint coeffs[4], int sfix) { constint srounder = 1 << (YUV_FIX + sfix - 1); constint luma = coeffs[0] * r + coeffs[1] * g + coeffs[2] * b +
coeffs[3] + srounder; return (luma >> (YUV_FIX + sfix));
}
staticint ConvertWRGBToYUV(const fixed_y_t* best_y, const fixed_t* best_uv,
uint8_t* y_ptr, int y_stride, uint8_t* u_ptr, int u_stride, uint8_t* v_ptr, int v_stride, int rgb_bit_depth, int yuv_bit_depth, int width, int height, const SharpYuvConversionMatrix* yuv_matrix) { int i, j; const fixed_t* const best_uv_base = best_uv; constint w = (width + 1) & ~1; constint h = (height + 1) & ~1; constint uv_w = w >> 1; constint uv_h = h >> 1; constint sfix = GetPrecisionShift(rgb_bit_depth); constint yuv_max = (1 << yuv_bit_depth) - 1;
best_uv = best_uv_base;
j = 0; do {
i = 0; do { constint off = (i >> 1); constint W = best_y[i]; constint r = best_uv[off + 0 * uv_w] + W; constint g = best_uv[off + 1 * uv_w] + W; constint b = best_uv[off + 2 * uv_w] + W; constint y = RGBToYUVComponent(r, g, b, yuv_matrix->rgb_to_y, sfix); if (yuv_bit_depth <= 8) {
y_ptr[i] = clip_8b(y);
} else {
((uint16_t*)y_ptr)[i] = clip(y, yuv_max);
}
} while (++i < width);
best_y += w;
best_uv += (j & 1) * 3 * uv_w;
y_ptr += y_stride;
} while (++j < height);
best_uv = best_uv_base;
j = 0; do {
i = 0; do { // Note r, g and b values here are off by W, but a constant offset on all // 3 components doesn't change the value of u and v with a YCbCr matrix. constint r = best_uv[i + 0 * uv_w]; constint g = best_uv[i + 1 * uv_w]; constint b = best_uv[i + 2 * uv_w]; constint u = RGBToYUVComponent(r, g, b, yuv_matrix->rgb_to_u, sfix); constint v = RGBToYUVComponent(r, g, b, yuv_matrix->rgb_to_v, sfix); if (yuv_bit_depth <= 8) {
u_ptr[i] = clip_8b(u);
v_ptr[i] = clip_8b(v);
} else {
((uint16_t*)u_ptr)[i] = clip(u, yuv_max);
((uint16_t*)v_ptr)[i] = clip(v, yuv_max);
}
} while (++i < uv_w);
best_uv += 3 * uv_w;
u_ptr += u_stride;
v_ptr += v_stride;
} while (++j < uv_h); return 1;
}
//------------------------------------------------------------------------------ // Main function
// update two rows of Y and one row of RGB
diff_y_sum +=
SharpYuvUpdateY(target_y, best_rgb_y, best_y, 2 * w, y_bit_depth);
SharpYuvUpdateRGB(target_uv, best_rgb_uv, best_uv, 3 * uv_w);
#define LOCK_ACCESS \ static pthread_mutex_t sharpyuv_lock = PTHREAD_MUTEX_INITIALIZER; \ if (pthread_mutex_lock(&sharpyuv_lock)) return #define UNLOCK_ACCESS_AND_RETURN \ do { \
(void)pthread_mutex_unlock(&sharpyuv_lock); \ return; \
} while (0) #else// !(defined(WEBP_USE_THREAD) && !defined(_WIN32)) #define LOCK_ACCESS do {} while (0) #define UNLOCK_ACCESS_AND_RETURN return #endif// defined(WEBP_USE_THREAD) && !defined(_WIN32)
// Hidden exported init function. // By default SharpYuvConvert calls it with SharpYuvGetCPUInfo. If needed, // users can declare it as extern and call it with an alternate VP8CPUInfo // function. extern VP8CPUInfo SharpYuvGetCPUInfo;
SHARPYUV_EXTERN void SharpYuvInit(VP8CPUInfo cpu_info_func); void SharpYuvInit(VP8CPUInfo cpu_info_func) { staticvolatile VP8CPUInfo sharpyuv_last_cpuinfo_used =
(VP8CPUInfo)&sharpyuv_last_cpuinfo_used;
LOCK_ACCESS; // Only update SharpYuvGetCPUInfo when called from external code to avoid a // race on reading the value in SharpYuvConvert(). if (cpu_info_func != (VP8CPUInfo)&SharpYuvGetCPUInfo) {
SharpYuvGetCPUInfo = cpu_info_func;
} if (sharpyuv_last_cpuinfo_used == SharpYuvGetCPUInfo) {
UNLOCK_ACCESS_AND_RETURN;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.