Commit 23b702de authored by James Cook's avatar James Cook Committed by Commit Bot

Fix K-Means average color computation for narrow and short images

The color computation tries to inset its samples by 1 pixel on each
edge, but that doesn't work for 1 pixel wide images. Skip the inset
if the image is too narrow or too short.

See bug for details.

Bug: 970343
Test: Added to gfx_unittests
Change-Id: If558a8bbacb27d9011da93868ad3b6f0e72f8754
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1644299
Commit-Queue: James Cook <jamescook@chromium.org>
Reviewed-by: default avatarMichael Wasserman <msw@chromium.org>
Cr-Commit-Position: refs/heads/master@{#666314}
parent 77eef456
...@@ -30,7 +30,7 @@ namespace color_utils { ...@@ -30,7 +30,7 @@ namespace color_utils {
namespace { namespace {
// RGBA KMean Constants // RGBA KMean Constants
const uint32_t kNumberOfClusters = 4; const int kNumberOfClusters = 4;
const int kNumberOfIterations = 50; const int kNumberOfIterations = 50;
const HSL kDefaultLowerHSLBound = {-1, -1, 0.15}; const HSL kDefaultLowerHSLBound = {-1, -1, 0.15};
...@@ -413,12 +413,13 @@ int GridSampler::GetSample(int width, int height) { ...@@ -413,12 +413,13 @@ int GridSampler::GetSample(int width, int height) {
// .......... // ..........
// .3.7...... // .3.7......
// .......... // ..........
const int kPadX = 1; // But don't inset if the image is too narrow or too short.
const int kPadY = 1; const int kInsetX = (width > 2 ? 1 : 0);
int x = kPadX + const int kInsetY = (height > 2 ? 1 : 0);
(calls_ / kNumberOfClusters) * ((width - 2 * kPadX) / kNumberOfClusters); int x = kInsetX + (calls_ / kNumberOfClusters) *
int y = kPadY + ((width - 2 * kInsetX) / kNumberOfClusters);
(calls_ % kNumberOfClusters) * ((height - 2 * kPadY) / kNumberOfClusters); int y = kInsetY + (calls_ % kNumberOfClusters) *
((height - 2 * kInsetY) / kNumberOfClusters);
int index = x + (y * width); int index = x + (y * width);
++calls_; ++calls_;
return index % (width * height); return index % (width * height);
...@@ -468,7 +469,7 @@ SkColor CalculateKMeanColorOfBuffer(uint8_t* decoded_data, ...@@ -468,7 +469,7 @@ SkColor CalculateKMeanColorOfBuffer(uint8_t* decoded_data,
SkColor color = kDefaultBgColor; SkColor color = kDefaultBgColor;
if (img_width > 0 && img_height > 0) { if (img_width > 0 && img_height > 0) {
std::vector<KMeanCluster> clusters; std::vector<KMeanCluster> clusters;
clusters.resize(kNumberOfClusters, KMeanCluster()); clusters.resize(static_cast<size_t>(kNumberOfClusters), KMeanCluster());
// Pick a starting point for each cluster // Pick a starting point for each cluster
auto new_cluster = clusters.begin(); auto new_cluster = clusters.begin();
......
...@@ -323,6 +323,40 @@ TEST_F(ColorAnalysisTest, CalculateKMeanColorOfBitmap) { ...@@ -323,6 +323,40 @@ TEST_F(ColorAnalysisTest, CalculateKMeanColorOfBitmap) {
EXPECT_TRUE(ChannelApproximatelyEqual(200, SkColorGetB(color))); EXPECT_TRUE(ChannelApproximatelyEqual(200, SkColorGetB(color)));
} }
// Regression test for heap-buffer-underlow. https://crbug.com/970343
TEST_F(ColorAnalysisTest, CalculateKMeanColorOfSmallImage) {
SkBitmap bitmap;
// Create a 1x41 bitmap, so it is not wide enough to have 1 pixel of padding
// on both sides.
bitmap.allocN32Pixels(1, 41);
bitmap.eraseARGB(255, 100, 150, 200);
SkColor color = CalculateKMeanColorOfBitmap(bitmap);
EXPECT_EQ(255u, SkColorGetA(color));
EXPECT_TRUE(ChannelApproximatelyEqual(100, SkColorGetR(color)));
EXPECT_TRUE(ChannelApproximatelyEqual(150, SkColorGetG(color)));
EXPECT_TRUE(ChannelApproximatelyEqual(200, SkColorGetB(color)));
// Test a wide but narrow bitmap.
bitmap.allocN32Pixels(41, 1);
bitmap.eraseARGB(255, 100, 150, 200);
color = CalculateKMeanColorOfBitmap(bitmap);
EXPECT_EQ(255u, SkColorGetA(color));
EXPECT_TRUE(ChannelApproximatelyEqual(100, SkColorGetR(color)));
EXPECT_TRUE(ChannelApproximatelyEqual(150, SkColorGetG(color)));
EXPECT_TRUE(ChannelApproximatelyEqual(200, SkColorGetB(color)));
// Test a tiny bitmap.
bitmap.allocN32Pixels(1, 1);
bitmap.eraseARGB(255, 100, 150, 200);
color = CalculateKMeanColorOfBitmap(bitmap);
EXPECT_EQ(255u, SkColorGetA(color));
EXPECT_TRUE(ChannelApproximatelyEqual(100, SkColorGetR(color)));
EXPECT_TRUE(ChannelApproximatelyEqual(150, SkColorGetG(color)));
EXPECT_TRUE(ChannelApproximatelyEqual(200, SkColorGetB(color)));
}
TEST_F(ColorAnalysisTest, ComputeColorCovarianceTrivial) { TEST_F(ColorAnalysisTest, ComputeColorCovarianceTrivial) {
SkBitmap bitmap; SkBitmap bitmap;
bitmap.setInfo(SkImageInfo::MakeN32Premul(100, 200)); bitmap.setInfo(SkImageInfo::MakeN32Premul(100, 200));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment