/* * Copyright 2011 The LibYuv Project Authors. All rights reserved. * Copyright 2016 Mozilla Foundation * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE 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.
*/
// YUV to RGB conversion and scaling functions were implemented by referencing // scale_argb.cc // // libyuv already has ScaleYUVToARGBBilinearUp(), but its implementation is not // completed yet. Implementations of the functions are based on it. // At first, ScaleYUVToARGBBilinearUp() was implemented by modifying the // libyuv's one. Then all another functions were implemented similarly. // // Function relationship between yuv_convert.cpp and scale_argb.cc are like // the followings // - ScaleYUVToARGBDown2() <-- ScaleARGBDown2() // - ScaleYUVToARGBDownEven() <-- ScaleARGBDownEven() // - ScaleYUVToARGBBilinearDown() <-- ScaleARGBBilinearDown() // - ScaleYUVToARGBBilinearUp() <-- ScaleARGBBilinearUp() and ScaleYUVToARGBBilinearUp() in libyuv // - ScaleYUVToARGBSimple() <-- ScaleARGBSimple() // - ScaleYUVToARGB() <-- ScaleARGB() // Removed some function calls for simplicity. // - YUVToARGBScale() <-- ARGBScale() // // Callings and selections of InterpolateRow() and ScaleARGBFilterCols() were // kept as same as possible. // // The followings changes were done to each scaling functions. // // -[1] Allocate YUV conversion buffer and use it as source buffer of scaling. // Its usage is borrowed from the libyuv's ScaleYUVToARGBBilinearUp(). // -[2] Conversion from YUV to RGB was abstracted as YUVBuferIter. // It is for handling multiple yuv color formats. // -[3] Modified scaling functions as to handle YUV conversion buffer and // use YUVBuferIter. // -[4] Color conversion function selections in YUVBuferIter were borrowed from // I444ToARGBMatrix(), I422ToARGBMatrix() and I420ToARGBMatrix()
struct YUVBuferIter { int src_width; int src_height; int src_stride_y; int src_stride_u; int src_stride_v; const uint8_t* src_y; const uint8_t* src_u; const uint8_t* src_v;
staticvoid YUVBuferIter_MoveToForI420(YUVBuferIter& iter, int y_index) { constint kYShift = 1; // Shift Y by 1 to convert Y plane to UV coordinate. int uv_y_index = y_index >> kYShift;
// ScaleARGB ARGB, 1/2 // This is an optimized version for scaling down a ARGB to 1/2 of // its original size. staticvoid ScaleYUVToARGBDown2(int src_width, int src_height, int dst_width, int dst_height, int src_stride_y, int src_stride_u, int src_stride_v, int dst_stride_argb, const uint8_t* src_y, const uint8_t* src_u, const uint8_t* src_v,
uint8_t* dst_argb, int x, int dx, int y, int dy, enum FilterMode filtering,
uint32_t src_fourcc,
YUVColorSpace yuv_color_space) { int j;
constint dyi = dy >> 16; int lastyi = yi;
YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr); // Prepare next row if necessary if (filtering != kFilterLinear) { if ((yi + dyi) < (src_height - 1)) {
iter.MoveTo(iter, yi + dyi);
YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr + argb_cnv_rowstride);
} else {
argb_cnv_rowstride = 0;
}
}
if (filtering == kFilterLinear) {
argb_cnv_rowstride = 0;
} constint max_yi = src_height - 1; constint max_yi_minus_dyi = max_yi - dyi; for (j = 0; j < dst_height; ++j) { if (yi != lastyi) { if (yi > max_yi) {
yi = max_yi;
} if (yi != lastyi) { if (filtering == kFilterLinear) {
iter.MoveTo(iter, yi);
YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr);
lastyi = yi;
} else { // Prepare current row if (yi == iter.y_index) {
argb_cnv_rowptr = argb_cnv_rowptr + argb_cnv_rowstride;
argb_cnv_rowstride = - argb_cnv_rowstride;
} else {
iter.MoveTo(iter, yi);
argb_cnv_rowptr = argb_cnv_row;
argb_cnv_rowstride = kRowSize;
YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr);
} // Prepare next row if necessary if (iter.y_index < max_yi) { int next_yi = yi < max_yi_minus_dyi ? yi + dyi : max_yi;
iter.MoveTo(iter, next_yi);
YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr + argb_cnv_rowstride);
} else {
argb_cnv_rowstride = 0;
}
lastyi = yi;
}
}
}
ScaleARGBRowDown2(argb_cnv_rowptr + x_offset, argb_cnv_rowstride, dst_argb, dst_width);
dst_argb += dst_stride_argb;
yi += dyi;
}
free_aligned_buffer_64(argb_cnv_row);
}
// ScaleARGB ARGB Even // This is an optimized version for scaling down a ARGB to even // multiple of its original size. staticvoid ScaleYUVToARGBDownEven(int src_width, int src_height, int dst_width, int dst_height, int src_stride_y, int src_stride_u, int src_stride_v, int dst_stride_argb, const uint8_t* src_y, const uint8_t* src_u, const uint8_t* src_v,
uint8_t* dst_argb, int x, int dx, int y, int dy, enum FilterMode filtering,
uint32_t src_fourcc,
YUVColorSpace yuv_color_space) { int j; // Allocate 2 rows of ARGB for source conversion. constint kRowSize = (src_width * 4 + 15) & ~15;
align_buffer_64(argb_cnv_row, kRowSize * 2);
uint8_t* argb_cnv_rowptr = argb_cnv_row; int argb_cnv_rowstride = kRowSize;
int col_step = dx >> 16; void (*ScaleARGBRowDownEven)(const uint8_t* src_argb, ptrdiff_t src_stride, int src_step, uint8_t* dst_argb, int dst_width) =
filtering ? ScaleARGBRowDownEvenBox_C : ScaleARGBRowDownEven_C;
assert(IS_ALIGNED(src_width, 2));
assert(IS_ALIGNED(src_height, 2)); int yi = y >> 16; const ptrdiff_t x_offset = (x >> 16) * 4;
// TODO(fbarchard): Consider not allocating row buffer for kFilterLinear. // Allocate a row of ARGB.
align_buffer_64(row, clip_src_width * 4);
int lastyi = yi;
YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr); // Prepare next row if necessary if (filtering != kFilterLinear) { if ((yi + 1) < src_height) {
iter.MoveToNextRow(iter);
YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr + argb_cnv_rowstride);
} else {
argb_cnv_rowstride = 0;
}
}
constint max_y = (src_height - 1) << 16; constint max_yi = src_height - 1; for (j = 0; j < dst_height; ++j) {
yi = y >> 16; if (yi != lastyi) { if (y > max_y) {
y = max_y;
yi = y >> 16;
} if (yi != lastyi) { if (filtering == kFilterLinear) {
iter.MoveTo(iter, yi);
YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr);
lastyi = yi;
} else { // Prepare current row if (yi == iter.y_index) {
argb_cnv_rowptr = argb_cnv_rowptr + argb_cnv_rowstride;
argb_cnv_rowstride = - argb_cnv_rowstride;
} else {
iter.MoveTo(iter, yi);
argb_cnv_rowptr = argb_cnv_row;
argb_cnv_rowstride = kRowSize;
YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr);
} // Prepare next row if necessary if (iter.y_index < max_yi) {
iter.MoveToNextRow(iter);
YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr + argb_cnv_rowstride);
} else {
argb_cnv_rowstride = 0;
}
lastyi = yi;
}
}
} if (filtering == kFilterLinear) {
ScaleARGBFilterCols(dst_argb, argb_cnv_rowptr + xl_offset, dst_width, x, dx);
} else { int yf = (y >> 8) & 255;
InterpolateRow(row, argb_cnv_rowptr + xl_offset, argb_cnv_rowstride, clip_src_width, yf);
ScaleARGBFilterCols(dst_argb, row, dst_width, x, dx);
}
dst_argb += dst_stride_argb;
y += dy;
}
free_aligned_buffer_64(row);
free_aligned_buffer_64(argb_cnv_row);
}
// Scale YUV to ARGB up with bilinear interpolation. staticvoid ScaleYUVToARGBBilinearUp(int src_width, int src_height, int dst_width, int dst_height, int src_stride_y, int src_stride_u, int src_stride_v, int dst_stride_argb, const uint8_t* src_y, const uint8_t* src_u, const uint8_t* src_v,
uint8_t* dst_argb, int x, int dx, int y, int dy, enum FilterMode filtering,
uint32_t src_fourcc,
YUVColorSpace yuv_color_space) { int j; void (*InterpolateRow)(uint8_t* dst_argb, const uint8_t* src_argb,
ptrdiff_t src_stride, int dst_width, int source_y_fraction) =
InterpolateRow_C; void (*ScaleARGBFilterCols)(uint8_t* dst_argb, const uint8_t* src_argb, int dst_width, int x, int dx) =
filtering ? ScaleARGBFilterCols_C : ScaleARGBCols_C; constint max_y = (src_height - 1) << 16;
// Allocate 1 row of ARGB for source conversion.
align_buffer_64(argb_cnv_row, src_width * 4);
if (filtering == kFilterLinear) {
rowstride = 0;
} // Prepare next row if necessary if (filtering != kFilterLinear) { if ((yi + 1) < src_height) {
iter.MoveToNextRow(iter);
YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_row);
ScaleARGBFilterCols(rowptr + rowstride, argb_cnv_row, dst_width, x, dx);
}else {
rowstride = 0;
}
}
constint max_yi = src_height - 1; for (j = 0; j < dst_height; ++j) {
yi = y >> 16; if (yi != lastyi) { if (y > max_y) {
y = max_y;
yi = y >> 16;
} if (yi != lastyi) { if (filtering == kFilterLinear) {
iter.MoveToNextRow(iter);
YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_row);
ScaleARGBFilterCols(rowptr, argb_cnv_row, dst_width, x, dx);
} else { // Prepare next row if necessary if (yi < max_yi) {
iter.MoveToNextRow(iter);
rowptr += rowstride;
rowstride = -rowstride; // TODO(fbarchard): Convert the clipped region of row.
YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_row);
ScaleARGBFilterCols(rowptr + rowstride, argb_cnv_row, dst_width, x, dx);
} else {
rowstride = 0;
}
}
lastyi = yi;
}
} if (filtering == kFilterLinear) {
InterpolateRow(dst_argb, rowptr, 0, dst_width * 4, 0);
} else { int yf = (y >> 8) & 255;
InterpolateRow(dst_argb, rowptr, rowstride, dst_width * 4, yf);
}
dst_argb += dst_stride_argb;
y += dy;
}
free_aligned_buffer_64(row);
free_aligned_buffer_64(argb_cnv_row);
}
// Scale ARGB to/from any dimensions, without interpolation. // Fixed point math is used for performance: The upper 16 bits // of x and dx is the integer part of the source position and // the lower 16 bits are the fixed decimal part.
staticvoid ScaleYUVToARGBSimple(int src_width, int src_height, int dst_width, int dst_height, int src_stride_y, int src_stride_u, int src_stride_v, int dst_stride_argb, const uint8_t* src_y, const uint8_t* src_u, const uint8_t* src_v,
uint8_t* dst_argb, int x, int dx, int y, int dy,
uint32_t src_fourcc,
YUVColorSpace yuv_color_space) { int j; void (*ScaleARGBCols)(uint8_t* dst_argb, const uint8_t* src_argb, int dst_width, int x, int dx) =
(src_width >= 32768) ? ScaleARGBCols64_C : ScaleARGBCols_C;
// Allocate 1 row of ARGB for source conversion.
align_buffer_64(argb_cnv_row, src_width * 4);
#ifdefined(HAS_SCALEARGBCOLS_SSE2) if (TestCpuFlag(kCpuHasSSE2) && src_width < 32768) {
ScaleARGBCols = ScaleARGBCols_SSE2;
} #endif #ifdefined(HAS_SCALEARGBCOLS_NEON) if (TestCpuFlag(kCpuHasNEON)) {
ScaleARGBCols = ScaleARGBCols_Any_NEON; if (IS_ALIGNED(dst_width, 8)) {
ScaleARGBCols = ScaleARGBCols_NEON;
}
} #endif if (src_width * 2 == dst_width && x < 0x8000) {
ScaleARGBCols = ScaleARGBColsUp2_C; #ifdefined(HAS_SCALEARGBCOLSUP2_SSE2) if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(dst_width, 8)) {
ScaleARGBCols = ScaleARGBColsUp2_SSE2;
} #endif
}
staticvoid ScaleYUVToARGB(const uint8_t* src_y, int src_stride_y, const uint8_t* src_u, int src_stride_u, const uint8_t* src_v, int src_stride_v, int src_width, int src_height,
uint8_t* dst_argb, int dst_stride_argb, int dst_width, int dst_height, enum FilterMode filtering,
uint32_t src_fourcc,
YUVColorSpace yuv_color_space)
{ // Initial source x/y coordinate and step values as 16.16 fixed point. int x = 0; int y = 0; int dx = 0; int dy = 0; // ARGB does not support box filter yet, but allow the user to pass it. // Simplify filtering when possible.
filtering = ScaleFilterReduce(src_width, src_height,
dst_width, dst_height,
filtering);
ScaleSlope(src_width, src_height, dst_width, dst_height, filtering,
&x, &y, &dx, &dy);
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.