Scale*BilinearUp: Add invariants assertions and simplify loop

In ScaleARGBBilinearUp, ScalePlaneBilinearUp, ScalePlaneBilinearUp_16,
and ScaleUVBilinearUp, dy is strictly < 65536 (dy < 1.0 in 16.16 fixed
point), so yi (y >> 16) advances strictly in unit increments (0 -> 1 ->
2 -> ... -> H - 1).

Because yi transitions across each integer row in sequence:
- src is primed at row 2 and incrementally advanced by 1 row on each
  integer transition while (y + 65536) < max_y.
- When y >= max_y - 65536, src increments stop, leaving src pointing to
  the final row (H - 1).
- Therefore, src is always pointing to row yi + 1 before each buffer
  swap, making the inner 'src = src_ptr + yi * src_stride' recomputation
  and nested 'if (yi != lasty)' checks redundant.

Bug: 540206290, 539110937
Test: LibYUVScaleTest.*
Change-Id: Ic5cc5c601d4771e9df8b9a882df2f7f3e6777b23
Reviewed-on: https://chromium-review.googlesource.com/c/libyuv/libyuv/+/8252878
Commit-Queue: Dale Curtis <dalecurtis@chromium.org>
Reviewed-by: Frank Barchard <fbarchard@google.com>
Reviewed-by: Wan-Teh Chang <wtc@google.com>
This commit is contained in:
Dale Curtis 2026-08-13 19:59:16 +00:00 committed by libyuv-scoped@luci-project-accounts.iam.gserviceaccount.com
parent 2e58f4b971
commit 3f42e3840b
3 changed files with 88 additions and 39 deletions

View File

@ -1219,6 +1219,11 @@ static int ScalePlaneBilinearUp(int src_width,
const uint8_t* src_ptr,
uint8_t* dst_ptr,
enum FilterMode filtering) {
assert(src_width > 0);
assert(src_height > 0);
assert(dst_width > 0);
assert(dst_height > 0);
int j;
// Initial source x/y coordinate and step values as 16.16 fixed point.
int x = 0;
@ -1234,6 +1239,7 @@ static int ScalePlaneBilinearUp(int src_width,
filtering ? ScaleFilterCols_C : ScaleCols_C;
ScaleSlope(src_width, src_height, dst_width, dst_height, filtering, &x, &y,
&dx, &dy);
assert(dy <= 65536);
src_width = Abs(src_width);
#if defined(HAS_INTERPOLATEROW_AVX2)
@ -1327,22 +1333,27 @@ static int ScalePlaneBilinearUp(int src_width,
src += src_stride;
}
// 2-row rolling buffer:
// rowptr and (rowptr + rowstride) hold the scaled rows for yi and yi + 1.
// Because dy <= 65536 (dy <= 1.0 in 16.16), yi advances in unit steps.
// When yi != lasty:
// 1. Scale the next source row into the older buffer (rowptr).
// 2. Swap buffer pointers (rowptr += rowstride; rowstride = -rowstride;)
// so rowptr points to yi and (rowptr + rowstride) points to yi + 1.
// 3. Advance src by 1 row if row yi + 2 exists ((y + 65536) < max_y),
// otherwise clamp src at (src_height - 1) to avoid reading out of bounds.
for (j = 0; j < dst_height; ++j) {
if (y > max_y) {
y = max_y;
}
yi = y >> 16;
if (yi != lasty) {
if (y > max_y) {
y = max_y;
yi = y >> 16;
src = src_ptr + yi * src_stride;
}
if (yi != lasty) {
ScaleFilterCols(rowptr, src, dst_width, x, dx);
rowptr += rowstride;
rowstride = -rowstride;
lasty = yi;
if ((y + 65536) < max_y) {
src += src_stride;
}
ScaleFilterCols(rowptr, src, dst_width, x, dx);
rowptr += rowstride;
rowstride = -rowstride;
lasty = yi;
if ((y + 65536) < max_y) {
src += src_stride;
}
}
if (filtering == kFilterLinear) {
@ -1705,6 +1716,11 @@ static int ScalePlaneBilinearUp_16(int src_width,
const uint16_t* src_ptr,
uint16_t* dst_ptr,
enum FilterMode filtering) {
assert(src_width > 0);
assert(src_height > 0);
assert(dst_width > 0);
assert(dst_height > 0);
int j;
// Initial source x/y coordinate and step values as 16.16 fixed point.
int x = 0;
@ -1720,6 +1736,7 @@ static int ScalePlaneBilinearUp_16(int src_width,
filtering ? ScaleFilterCols_16_C : ScaleCols_16_C;
ScaleSlope(src_width, src_height, dst_width, dst_height, filtering, &x, &y,
&dx, &dy);
assert(dy <= 65536);
src_width = Abs(src_width);
#if defined(HAS_INTERPOLATEROW_16_SSSE3)
@ -1768,6 +1785,7 @@ static int ScalePlaneBilinearUp_16(int src_width,
}
#endif
}
if (y > max_y) {
y = max_y;
}
@ -1793,22 +1811,27 @@ static int ScalePlaneBilinearUp_16(int src_width,
src += src_stride;
}
// 2-row rolling buffer:
// rowptr and (rowptr + rowstride) hold the scaled rows for yi and yi + 1.
// Because dy <= 65536 (dy <= 1.0 in 16.16), yi advances in unit steps.
// When yi != lasty:
// 1. Scale the next source row into the older buffer (rowptr).
// 2. Swap buffer pointers (rowptr += rowstride; rowstride = -rowstride;)
// so rowptr points to yi and (rowptr + rowstride) points to yi + 1.
// 3. Advance src by 1 row if row yi + 2 exists ((y + 65536) < max_y),
// otherwise clamp src at (src_height - 1) to avoid reading out of bounds.
for (j = 0; j < dst_height; ++j) {
if (y > max_y) {
y = max_y;
}
yi = y >> 16;
if (yi != lasty) {
if (y > max_y) {
y = max_y;
yi = y >> 16;
src = src_ptr + yi * src_stride;
}
if (yi != lasty) {
ScaleFilterCols(rowptr, src, dst_width, x, dx);
rowptr += rowstride;
rowstride = -rowstride;
lasty = yi;
if ((y + 65536) < max_y) {
src += src_stride;
}
ScaleFilterCols(rowptr, src, dst_width, x, dx);
rowptr += rowstride;
rowstride = -rowstride;
lasty = yi;
if ((y + 65536) < max_y) {
src += src_stride;
}
}
if (filtering == kFilterLinear) {

View File

@ -441,6 +441,12 @@ static int ScaleARGBBilinearUp(int src_width,
int y,
int dy,
enum FilterMode filtering) {
assert(src_width > 0);
assert(src_height > 0);
assert(dst_width > 0);
assert(dst_height > 0);
assert(dy <= 65536);
int j;
void (*InterpolateRow)(uint8_t* dst_argb, const uint8_t* src_argb,
ptrdiff_t src_stride, int dst_width,
@ -576,6 +582,15 @@ static int ScaleARGBBilinearUp(int src_width,
src += src_stride;
}
// 2-row rolling buffer:
// rowptr and (rowptr + rowstride) hold the scaled rows for yi and yi + 1.
// Because dy <= 65536 (dy <= 1.0 in 16.16), yi advances in unit steps.
// When yi != lasty:
// 1. Scale the next source row into the older buffer (rowptr).
// 2. Swap buffer pointers (rowptr += rowstride; rowstride = -rowstride;)
// so rowptr points to yi and (rowptr + rowstride) points to yi + 1.
// 3. Advance src by 1 row if row yi + 2 exists ((y64 + 65536) < max_y),
// otherwise clamp src at (src_height - 1) to avoid reading out of bounds.
for (j = 0; j < dst_height; ++j) {
if (y64 > max_y) {
y64 = max_y;

View File

@ -463,6 +463,12 @@ static int ScaleUVBilinearUp(int src_width,
int y,
int dy,
enum FilterMode filtering) {
assert(src_width > 0);
assert(src_height > 0);
assert(dst_width > 0);
assert(dst_height > 0);
assert(dy <= 65536);
int j;
void (*InterpolateRow)(uint8_t* dst_uv, const uint8_t* src_uv,
ptrdiff_t src_stride, int dst_width,
@ -575,22 +581,27 @@ static int ScaleUVBilinearUp(int src_width,
src += src_stride;
}
// 2-row rolling buffer:
// rowptr and (rowptr + rowstride) hold the scaled rows for yi and yi + 1.
// Because dy <= 65536 (dy <= 1.0 in 16.16), yi advances in unit steps.
// When yi != lasty:
// 1. Scale the next source row into the older buffer (rowptr).
// 2. Swap buffer pointers (rowptr += rowstride; rowstride = -rowstride;)
// so rowptr points to yi and (rowptr + rowstride) points to yi + 1.
// 3. Advance src by 1 row if row yi + 2 exists ((y + 65536) < max_y),
// otherwise clamp src at (src_height - 1) to avoid reading out of bounds.
for (j = 0; j < dst_height; ++j) {
if (y > max_y) {
y = max_y;
}
yi = y >> 16;
if (yi != lasty) {
if (y > max_y) {
y = max_y;
yi = y >> 16;
src = src_uv + yi * src_stride;
}
if (yi != lasty) {
ScaleUVFilterCols(rowptr, src, dst_width, x, dx);
rowptr += rowstride;
rowstride = -rowstride;
lasty = yi;
if ((y + 65536) < max_y) {
src += src_stride;
}
ScaleUVFilterCols(rowptr, src, dst_width, x, dx);
rowptr += rowstride;
rowstride = -rowstride;
lasty = yi;
if ((y + 65536) < max_y) {
src += src_stride;
}
}
if (filtering == kFilterLinear) {