diff --git a/source/scale.cc b/source/scale.cc index f10ead6cb..7f3f19784 100644 --- a/source/scale.cc +++ b/source/scale.cc @@ -917,6 +917,7 @@ static void ScaleAddCols1_16_C(int dst_width, int boxwidth = MIN1(dx >> 16); int scaleval = 65536 / (boxwidth * boxheight); int i; + x >>= 16; for (i = 0; i < dst_width; ++i) { *dst_ptr++ = SumPixels_16(boxwidth, src_ptr + x) * scaleval >> 16; x += boxwidth; @@ -2125,11 +2126,9 @@ int ScalePlane(const uint8_t* src, int dy = 0; int y = 0; // Check for 1 pixel and avoid FixedDiv overflow. - // TODO(b/573815252): Fix center row calculation when dst_height == 1 and - // src_height == 32768. if (dst_height == 1 && src_height >= 32768) { - dy = FixedDiv(src_height, src_height); - y = CENTERSTART(dy, -32768); + dy = 1 << 16; + y = filtering ? ((src_height << 15) - 32768) : (src_height << 15); } else if (dst_height <= src_height) { // When scaling down, use the center 2 rows to filter. dy = FixedDiv(src_height, dst_height); @@ -2246,11 +2245,9 @@ int ScalePlane_16(const uint16_t* src, int dy = 0; int y = 0; // Check for 1 pixel and avoid FixedDiv overflow. - // TODO(b/573815252): Fix center row calculation when dst_height == 1 and - // src_height == 32768. if (dst_height == 1 && src_height >= 32768) { - dy = FixedDiv(src_height, src_height); - y = CENTERSTART(dy, -32768); + dy = 1 << 16; + y = filtering ? ((src_height << 15) - 32768) : (src_height << 15); } else if (dst_height <= src_height) { // When scaling down, use the center 2 rows to filter. dy = FixedDiv(src_height, dst_height); diff --git a/source/scale_common.cc b/source/scale_common.cc index 1dc311bd7..fe1f0c6df 100644 --- a/source/scale_common.cc +++ b/source/scale_common.cc @@ -1639,31 +1639,41 @@ void ScaleSlope(int src_width, assert(src_height != 0); assert(dst_width > 0); assert(dst_height > 0); - // Check for 1 pixel and avoid FixedDiv overflow. - // TODO(b/573815252): Fix center coordinate calculation when dst == 1 and - // src == 32768. - if (dst_width == 1 && Abs(src_width) >= 32768) { - dst_width = Abs(src_width); - } - if (dst_height == 1 && src_height >= 32768) { - dst_height = src_height; - } + const int abs_src_width = Abs(src_width); if (filtering == kFilterBox) { // Scale step for point sampling duplicates all pixels equally. - *dx = FixedDiv(Abs(src_width), dst_width); - *dy = FixedDiv(src_height, dst_height); - *x = 0; - *y = 0; - } else if (filtering == kFilterBilinear) { - // Scale step for bilinear sampling renders last pixel once for upsample. - if (dst_width <= Abs(src_width)) { - *dx = FixedDiv(Abs(src_width), dst_width); - *x = CENTERSTART(*dx, -32768); // Subtract 0.5 (32768) to center filter. - } else if (src_width > 1 && dst_width > 1) { - *dx = FixedDiv1(Abs(src_width), dst_width); + if (dst_width == 1 && abs_src_width >= 32768) { + // Avoid overflowing 32-bit signed int in FixedDiv(32768, 1) and box + // filter accumulation in row16. + *dx = 1 << 16; + *x = abs_src_width << 15; + } else { + *dx = FixedDiv(abs_src_width, dst_width); *x = 0; } - if (dst_height <= src_height) { + if (dst_height == 1 && src_height >= 32768) { + *dy = 1 << 16; + *y = src_height << 15; + } else { + *dy = FixedDiv(src_height, dst_height); + *y = 0; + } + } else if (filtering == kFilterBilinear) { + // Scale step for bilinear sampling renders last pixel once for upsample. + if (dst_width == 1 && abs_src_width >= 32768) { + *dx = abs_src_width << 15; + *x = (abs_src_width << 15) - 32768; + } else if (dst_width <= abs_src_width) { + *dx = FixedDiv(abs_src_width, dst_width); + *x = CENTERSTART(*dx, -32768); // Subtract 0.5 (32768) to center filter. + } else if (src_width > 1 && dst_width > 1) { + *dx = FixedDiv1(abs_src_width, dst_width); + *x = 0; + } + if (dst_height == 1 && src_height >= 32768) { + *dy = src_height << 15; + *y = (src_height << 15) - 32768; + } else if (dst_height <= src_height) { *dy = FixedDiv(src_height, dst_height); *y = CENTERSTART(*dy, -32768); // Subtract 0.5 (32768) to center filter. } else if (src_height > 1 && dst_height > 1) { @@ -1672,21 +1682,39 @@ void ScaleSlope(int src_width, } } else if (filtering == kFilterLinear) { // Scale step for bilinear sampling renders last pixel once for upsample. - if (dst_width <= Abs(src_width)) { - *dx = FixedDiv(Abs(src_width), dst_width); + if (dst_width == 1 && abs_src_width >= 32768) { + *dx = abs_src_width << 15; + *x = (abs_src_width << 15) - 32768; + } else if (dst_width <= abs_src_width) { + *dx = FixedDiv(abs_src_width, dst_width); *x = CENTERSTART(*dx, -32768); // Subtract 0.5 (32768) to center filter. } else if (src_width > 1 && dst_width > 1) { - *dx = FixedDiv1(Abs(src_width), dst_width); + *dx = FixedDiv1(abs_src_width, dst_width); *x = 0; } - *dy = FixedDiv(src_height, dst_height); - *y = *dy >> 1; + if (dst_height == 1 && src_height >= 32768) { + *dy = src_height << 15; + *y = src_height << 15; + } else { + *dy = FixedDiv(src_height, dst_height); + *y = *dy >> 1; + } } else { // Scale step for point sampling duplicates all pixels equally. - *dx = FixedDiv(Abs(src_width), dst_width); - *dy = FixedDiv(src_height, dst_height); - *x = CENTERSTART(*dx, 0); - *y = CENTERSTART(*dy, 0); + if (dst_width == 1 && abs_src_width >= 32768) { + *dx = abs_src_width << 15; + *x = abs_src_width << 15; + } else { + *dx = FixedDiv(abs_src_width, dst_width); + *x = CENTERSTART(*dx, 0); + } + if (dst_height == 1 && src_height >= 32768) { + *dy = src_height << 15; + *y = src_height << 15; + } else { + *dy = FixedDiv(src_height, dst_height); + *y = CENTERSTART(*dy, 0); + } } // Negative src_width means horizontally mirror. if (src_width < 0) { diff --git a/unit_test/scale_argb_test.cc b/unit_test/scale_argb_test.cc index 532bcb509..8817ae538 100644 --- a/unit_test/scale_argb_test.cc +++ b/unit_test/scale_argb_test.cc @@ -653,6 +653,34 @@ TEST_F(LibYUVScaleTest, ARGBScaleDimensionOverflow) { kFilterNone)); } +TEST_F(LibYUVScaleTest, ARGBScaleOversizeWidthTo1) { + const int kSrcWidth = 32768; + const int kSrcHeight = 2; + uint8_t* src = new uint8_t[kSrcWidth * kSrcHeight * 4]; + memset(src, 0, kSrcWidth * kSrcHeight * 4); + for (int y = 0; y < kSrcHeight; ++y) { + memset(src + (y * kSrcWidth + 16383) * 4, 100, 4); + memset(src + (y * kSrcWidth + 16384) * 4, 200, 4); + } + uint8_t dst[2 * 4] = {}; + + for (int sign : {1, -1}) { + for (int dst_h : {1, 2}) { + for (FilterMode f : + {kFilterNone, kFilterLinear, kFilterBilinear, kFilterBox}) { + EXPECT_EQ(0, ARGBScale(src, kSrcWidth * 4, sign * kSrcWidth, kSrcHeight, + dst, 4, 1, dst_h, f)); + if (f == kFilterNone) { + EXPECT_EQ(200, dst[0]); + } else { + EXPECT_NEAR(150, dst[0], 1); + } + } + } + } + delete[] src; +} + #if defined(__LP64__) || defined(_WIN64) TEST_F(LibYUVScaleTest, ARGBScaleLargeImageOver2GB) { const int kSrcWidth = 4; diff --git a/unit_test/scale_plane_test.cc b/unit_test/scale_plane_test.cc index 544a0f098..ebccac4fe 100644 --- a/unit_test/scale_plane_test.cc +++ b/unit_test/scale_plane_test.cc @@ -811,20 +811,61 @@ TEST_F(LibYUVScaleTest, ScalePlaneOversizeHeight) { const int kSrcHeight = 32768; uint8_t* src = new uint8_t[kSrcWidth * kSrcHeight]; memset(src, 0, kSrcWidth * kSrcHeight); + memset(src + 16383 * kSrcWidth, 100, kSrcWidth); + memset(src + 16384 * kSrcWidth, 200, kSrcWidth); uint8_t dst[4 * 2] = {}; - for (int dst_w : {4, 2}) { + for (int dst_w : {4, 2, 1}) { for (int dst_h : {1, 2}) { for (FilterMode f : {kFilterNone, kFilterLinear, kFilterBilinear, kFilterBox}) { EXPECT_EQ(0, ScalePlane(src, kSrcWidth, kSrcWidth, kSrcHeight, dst, dst_w, dst_w, dst_h, f)); + if (dst_h == 1) { + const uint8_t expected = + (f == kFilterBilinear || (f == kFilterBox && dst_w >= 2)) ? 150 + : 200; + EXPECT_EQ(expected, dst[0]); + } EXPECT_EQ(0, ScalePlane(src, kSrcWidth, kSrcWidth, -kSrcHeight, dst, dst_w, dst_w, dst_h, f)); + if (dst_h == 1) { + const uint8_t expected = + (f == kFilterBilinear || (f == kFilterBox && dst_w >= 2)) ? 150 + : 100; + EXPECT_EQ(expected, dst[0]); + } } } } delete[] src; + + uint16_t* src16 = new uint16_t[kSrcWidth * kSrcHeight]; + memset(src16, 0, kSrcWidth * kSrcHeight * sizeof(uint16_t)); + for (int x = 0; x < kSrcWidth; ++x) { + src16[16383 * kSrcWidth + x] = 1000; + src16[16384 * kSrcWidth + x] = 2000; + } + uint16_t dst16[4] = {}; + for (int dst_w : {4, 2, 1}) { + for (FilterMode f : + {kFilterNone, kFilterLinear, kFilterBilinear, kFilterBox}) { + EXPECT_EQ(0, ScalePlane_16(src16, kSrcWidth, kSrcWidth, kSrcHeight, dst16, + dst_w, dst_w, 1, f)); + const uint16_t expected = + (f == kFilterBilinear || (f == kFilterBox && dst_w >= 2)) ? 1500 + : 2000; + EXPECT_EQ(expected, dst16[0]); + } + } + // Test ScalePlane_16 horizontal 32768 -> 1 box filter (ScaleAddCols1_16_C). + for (int y = 0; y < kSrcWidth; ++y) { + src16[y * kSrcHeight + 16384] = 3000; + } + EXPECT_EQ(0, ScalePlane_16(src16, kSrcHeight, kSrcHeight, kSrcWidth, dst16, 1, + 1, 1, kFilterBox)); + EXPECT_EQ(3000, dst16[0]); + delete[] src16; } TEST_F(LibYUVScaleTest, ScalePlaneBoxMaxSrcHeight) {