Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "ui/gfx/content_analysis.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <cmath> | |
| 9 #include <cstdlib> | |
| 10 #include <fstream> | |
| 11 #include <iostream> | |
| 12 #include <limits> | |
| 13 #include <numeric> | |
| 14 #include <vector> | |
| 15 | |
| 16 #include "base/file_util.h" | |
| 17 #include "base/files/file_path.h" | |
| 18 #include "base/memory/scoped_ptr.h" | |
| 19 #include "testing/gtest/include/gtest/gtest.h" | |
| 20 #include "third_party/skia/include/core/SkBitmap.h" | |
| 21 #include "third_party/skia/include/core/SkColor.h" | |
| 22 #include "ui/gfx/canvas.h" | |
| 23 #include "ui/gfx/color_analysis.h" | |
| 24 #include "ui/gfx/color_utils.h" | |
| 25 #include "ui/gfx/image/image.h" | |
| 26 #include "ui/gfx/rect.h" | |
| 27 #include "ui/gfx/size.h" | |
| 28 | |
| 29 namespace { | |
| 30 | |
| 31 #ifndef M_PI | |
| 32 #define M_PI 3.14159265358979323846 | |
| 33 #endif | |
| 34 | |
| 35 void DumpBitmapToFile(const SkBitmap& bitmap) { | |
|
Alexei Svitkine (slow)
2013/04/09 18:02:23
If you're not using these in your tests, please re
motek.
2013/04/11 16:18:38
Done.
| |
| 36 SkAutoLockPixels bitmap_lock(bitmap); | |
| 37 scoped_refptr<base::RefCountedMemory> png_bytes = | |
| 38 gfx::Image::CreateFrom1xBitmap(bitmap).As1xPNGBytes(); | |
| 39 base::FilePath file_path; | |
| 40 if (file_util::CreateTemporaryFile(&file_path)) { | |
| 41 file_util::WriteFile(file_path, | |
| 42 reinterpret_cast<const char*>(png_bytes->front()), | |
| 43 png_bytes->size()); | |
| 44 DLOG(INFO) << "Image content writen out to " << file_path.value(); | |
| 45 } else { | |
| 46 NOTREACHED(); | |
| 47 } | |
| 48 } | |
| 49 | |
| 50 void DumpBitmapBytesToFile(const SkBitmap& bitmap) { | |
| 51 SkAutoLockPixels bitmap_lock(bitmap); | |
| 52 base::FilePath file_path; | |
| 53 if (file_util::CreateTemporaryFile(&file_path)) { | |
| 54 file_util::WriteFile(file_path, | |
| 55 static_cast<const char*>(bitmap.getPixels()), | |
| 56 bitmap.getSafeSize()); | |
| 57 DLOG(INFO) << "Image content writen out to " << file_path.value(); | |
| 58 } else { | |
| 59 NOTREACHED(); | |
| 60 } | |
| 61 } | |
| 62 | |
| 63 template<class InputIterator> | |
| 64 void DumpVectorToFile(InputIterator first, InputIterator last) { | |
| 65 base::FilePath file_path; | |
| 66 if (file_util::CreateTemporaryFile(&file_path)) { | |
| 67 std::ofstream outfile; | |
| 68 outfile.open(file_path.value().c_str()); | |
| 69 for (; first < last; ++first) { | |
| 70 outfile << *first; | |
| 71 outfile << "\n"; | |
| 72 } | |
| 73 outfile.close(); | |
| 74 DLOG(INFO) << "Vector content writen out to " << file_path.value(); | |
| 75 } | |
| 76 } | |
| 77 | |
| 78 unsigned long ImagePixelSum(const SkBitmap& bitmap, const gfx::Rect& rect) { | |
| 79 // Get the sum of pixel values in the rectangle. Applicable only to | |
| 80 // monochrome bitmaps. | |
| 81 DCHECK_EQ(SkBitmap::kA8_Config, bitmap.config()); | |
| 82 unsigned long total = 0; | |
| 83 for (int r = rect.y(); r < rect.bottom(); ++r) { | |
| 84 const uint8* row_data = static_cast<const uint8*>( | |
| 85 bitmap.getPixels()) + r * bitmap.rowBytes(); | |
| 86 for (int c = rect.x(); c < rect.right(); ++c) | |
| 87 total += row_data[c]; | |
| 88 } | |
| 89 | |
| 90 return total; | |
| 91 } | |
| 92 | |
| 93 bool CompareImageFragments(const SkBitmap& bitmap_left, | |
| 94 const SkBitmap& bitmap_right, | |
| 95 const gfx::Size& comparison_area, | |
| 96 const gfx::Point& origin_left, | |
| 97 const gfx::Point& origin_right) { | |
| 98 SkAutoLockPixels left_lock(bitmap_left); | |
| 99 SkAutoLockPixels right_lock(bitmap_right); | |
| 100 for (int r = 0; r < comparison_area.height(); ++r) { | |
| 101 for (int c = 0; c < comparison_area.width(); ++c) { | |
| 102 SkColor clr_left = bitmap_left.getColor(origin_left.x() + c, | |
| 103 origin_left.y() + r); | |
| 104 SkColor clr_right = bitmap_right.getColor(origin_right.x() + c, | |
| 105 origin_right.y() + r); | |
| 106 if (clr_left != clr_right) | |
| 107 return false; | |
| 108 } | |
| 109 } | |
| 110 | |
| 111 return true; | |
| 112 } | |
| 113 | |
| 114 } | |
|
Alexei Svitkine (slow)
2013/04/09 18:02:23
Add " // namespace"
motek.
2013/04/11 16:18:38
Done.
| |
| 115 | |
| 116 class ContentAnalysisTest : public testing::Test { | |
| 117 }; | |
| 118 | |
| 119 TEST_F(ContentAnalysisTest, ApplyGradientMagnitudeOnImpulse) { | |
| 120 gfx::Canvas canvas(gfx::Size(800, 600), ui::SCALE_FACTOR_100P, true); | |
| 121 | |
| 122 // The image consists of vertical non-overlapping stripes 100 pixels wide. | |
| 123 canvas.FillRect(gfx::Rect(0, 0, 800, 600), SkColorSetARGB(0, 10, 10, 10)); | |
| 124 canvas.FillRect(gfx::Rect(400, 300, 1, 1), SkColorSetRGB(255, 255, 255)); | |
| 125 | |
| 126 SkBitmap source = | |
| 127 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false); | |
| 128 | |
| 129 SkBitmap reduced_color; | |
| 130 reduced_color.setConfig( | |
| 131 SkBitmap::kA8_Config, source.width(), source.height()); | |
| 132 reduced_color.allocPixels(); | |
| 133 | |
| 134 gfx::Vector3dF transform(0.299f, 0.587f, 0.114f); | |
| 135 EXPECT_TRUE(color_utils::ApplyColorReduction( | |
| 136 source, transform, true, &reduced_color)); | |
| 137 | |
| 138 float sigma = 2.5f; | |
| 139 color_utils::ApplyGaussianGradientMagnitudeFilter(&reduced_color, sigma); | |
| 140 | |
| 141 // Expect everything to be within 8 * sigma. | |
| 142 int tail_length = static_cast<int>(8.0f * sigma + 0.5f); | |
| 143 gfx::Rect echo_rect(399 - tail_length, 299 - tail_length, | |
| 144 2 * tail_length + 1, 2 * tail_length + 1); | |
| 145 unsigned long data_sum = ImagePixelSum(reduced_color, echo_rect); | |
| 146 unsigned long all_sum = ImagePixelSum(reduced_color, gfx::Rect(800, 600)); | |
| 147 EXPECT_GT(data_sum, 0U); | |
| 148 EXPECT_EQ(data_sum, all_sum); | |
| 149 | |
| 150 sigma = 5.0f; | |
| 151 color_utils::ApplyGaussianGradientMagnitudeFilter(&reduced_color, sigma); | |
| 152 | |
| 153 // Expect everything to be within 8 * sigma. | |
| 154 tail_length = static_cast<int>(8.0f * sigma + 0.5f); | |
| 155 echo_rect = gfx::Rect(399 - tail_length, 299 - tail_length, | |
| 156 2 * tail_length + 1, 2 * tail_length + 1); | |
| 157 data_sum = ImagePixelSum(reduced_color, echo_rect); | |
| 158 all_sum = ImagePixelSum(reduced_color, gfx::Rect(800, 600)); | |
| 159 EXPECT_GT(data_sum, 0U); | |
| 160 EXPECT_EQ(data_sum, all_sum); | |
| 161 } | |
| 162 | |
| 163 TEST_F(ContentAnalysisTest, ApplyGradientMagnitudeOnFrame) { | |
| 164 gfx::Canvas canvas(gfx::Size(800, 600), ui::SCALE_FACTOR_100P, true); | |
| 165 | |
| 166 // The image consists of a single white block in the centre. | |
| 167 gfx::Rect draw_rect(300, 200, 200, 200); | |
| 168 canvas.FillRect(gfx::Rect(0, 0, 800, 600), SkColorSetARGB(0, 0, 0, 0)); | |
| 169 canvas.DrawRect(draw_rect, SkColorSetRGB(255, 255, 255)); | |
| 170 | |
| 171 SkBitmap source = | |
| 172 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false); | |
| 173 | |
| 174 SkBitmap reduced_color; | |
| 175 reduced_color.setConfig( | |
| 176 SkBitmap::kA8_Config, source.width(), source.height()); | |
| 177 reduced_color.allocPixels(); | |
| 178 | |
| 179 gfx::Vector3dF transform(0.299f, 0.587f, 0.114f); | |
| 180 EXPECT_TRUE(color_utils::ApplyColorReduction( | |
| 181 source, transform, true, &reduced_color)); | |
| 182 | |
| 183 float sigma = 2.5f; | |
| 184 color_utils::ApplyGaussianGradientMagnitudeFilter(&reduced_color, sigma); | |
| 185 | |
| 186 int tail_length = static_cast<int>(8.0f * sigma + 0.5f); | |
| 187 gfx::Rect outer_rect(draw_rect.x() - tail_length, | |
| 188 draw_rect.y() - tail_length, | |
| 189 draw_rect.width() + 2 * tail_length, | |
| 190 draw_rect.height() + 2 * tail_length); | |
| 191 gfx::Rect inner_rect(draw_rect.x() + tail_length, | |
| 192 draw_rect.y() + tail_length, | |
| 193 draw_rect.width() - 2 * tail_length, | |
| 194 draw_rect.height() - 2 * tail_length); | |
| 195 unsigned long data_sum = ImagePixelSum(reduced_color, outer_rect); | |
| 196 unsigned long all_sum = ImagePixelSum(reduced_color, gfx::Rect(800, 600)); | |
| 197 EXPECT_GT(data_sum, 0U); | |
| 198 EXPECT_EQ(data_sum, all_sum); | |
| 199 EXPECT_EQ(ImagePixelSum(reduced_color, inner_rect), 0U); | |
| 200 } | |
| 201 | |
| 202 TEST_F(ContentAnalysisTest, ExtractImageProfileInformation) { | |
| 203 gfx::Canvas canvas(gfx::Size(800, 600), ui::SCALE_FACTOR_100P, true); | |
| 204 | |
| 205 // The image consists of a white frame drawn in the centre. | |
| 206 gfx::Rect draw_rect(100, 100, 200, 100); | |
| 207 gfx::Rect image_rect(0, 0, 800, 600); | |
| 208 canvas.FillRect(image_rect, SkColorSetARGB(0, 0, 0, 0)); | |
| 209 canvas.DrawRect(draw_rect, SkColorSetRGB(255, 255, 255)); | |
| 210 | |
| 211 SkBitmap source = | |
| 212 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false); | |
| 213 SkBitmap reduced_color; | |
| 214 reduced_color.setConfig( | |
| 215 SkBitmap::kA8_Config, source.width(), source.height()); | |
| 216 reduced_color.allocPixels(); | |
| 217 | |
| 218 gfx::Vector3dF transform(1, 0, 0); | |
| 219 EXPECT_TRUE(color_utils::ApplyColorReduction( | |
| 220 source, transform, true, &reduced_color)); | |
| 221 std::vector<float> column_profile; | |
| 222 std::vector<float> row_profile; | |
| 223 color_utils::ExtractImageProfileInformation(reduced_color, | |
| 224 image_rect, | |
| 225 gfx::Size(), | |
| 226 false, | |
| 227 &row_profile, | |
| 228 &column_profile); | |
| 229 EXPECT_EQ(0, std::accumulate(column_profile.begin(), | |
| 230 column_profile.begin() + draw_rect.x() - 1, | |
| 231 0)); | |
| 232 EXPECT_EQ(column_profile[draw_rect.x()], 255U * (draw_rect.height() + 1)); | |
| 233 EXPECT_EQ(2 * 255 * (draw_rect.width() - 2), | |
| 234 std::accumulate(column_profile.begin() + draw_rect.x() + 1, | |
| 235 column_profile.begin() + draw_rect.right() - 1, | |
| 236 0)); | |
| 237 | |
| 238 EXPECT_EQ(0, std::accumulate(row_profile.begin(), | |
| 239 row_profile.begin() + draw_rect.y() - 1, | |
| 240 0)); | |
| 241 EXPECT_EQ(row_profile[draw_rect.y()], 255U * (draw_rect.width() + 1)); | |
| 242 EXPECT_EQ(2 * 255 * (draw_rect.height() - 2), | |
| 243 std::accumulate(row_profile.begin() + draw_rect.y() + 1, | |
| 244 row_profile.begin() + draw_rect.bottom() - 1, | |
| 245 0)); | |
| 246 | |
| 247 gfx::Rect test_rect(150, 80, 400, 100); | |
| 248 color_utils::ExtractImageProfileInformation(reduced_color, | |
| 249 test_rect, | |
| 250 gfx::Size(), | |
| 251 false, | |
| 252 &row_profile, | |
| 253 &column_profile); | |
| 254 | |
| 255 // Some overlap with the drawn rectagle. If you work it out on a piece of | |
| 256 // paper, sums should be as follows. | |
| 257 EXPECT_EQ(255 * (test_rect.bottom() - draw_rect.y()) + | |
| 258 255 * (draw_rect.right() - test_rect.x()), | |
| 259 std::accumulate(row_profile.begin(), row_profile.end(), 0)); | |
| 260 EXPECT_EQ(255 * (test_rect.bottom() - draw_rect.y()) + | |
| 261 255 * (draw_rect.right() - test_rect.x()), | |
| 262 std::accumulate(column_profile.begin(), column_profile.end(), 0)); | |
| 263 } | |
| 264 | |
| 265 TEST_F(ContentAnalysisTest, ExtractImageProfileInformationWithClosing) { | |
| 266 gfx::Canvas canvas(gfx::Size(800, 600), ui::SCALE_FACTOR_100P, true); | |
| 267 | |
| 268 // The image consists of a two white frames drawn side by side, with a | |
| 269 // single-pixel vertical gap in between. | |
| 270 gfx::Rect image_rect(0, 0, 800, 600); | |
| 271 canvas.FillRect(image_rect, SkColorSetARGB(0, 0, 0, 0)); | |
| 272 canvas.DrawRect(gfx::Rect(300, 250, 99, 100), SkColorSetRGB(255, 255, 255)); | |
| 273 canvas.DrawRect(gfx::Rect(401, 250, 99, 100), SkColorSetRGB(255, 255, 255)); | |
| 274 | |
| 275 SkBitmap source = | |
| 276 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false); | |
| 277 SkBitmap reduced_color; | |
| 278 reduced_color.setConfig( | |
| 279 SkBitmap::kA8_Config, source.width(), source.height()); | |
| 280 reduced_color.allocPixels(); | |
| 281 | |
| 282 gfx::Vector3dF transform(1, 0, 0); | |
| 283 EXPECT_TRUE(color_utils::ApplyColorReduction( | |
| 284 source, transform, true, &reduced_color)); | |
| 285 std::vector<float> column_profile; | |
| 286 std::vector<float> row_profile; | |
| 287 | |
| 288 color_utils::ExtractImageProfileInformation(reduced_color, | |
| 289 image_rect, | |
| 290 gfx::Size(), | |
| 291 true, | |
| 292 &row_profile, | |
| 293 &column_profile); | |
| 294 // Column profiles should have two spikes in the middle, with a single | |
| 295 // 0-valued value between them. | |
| 296 EXPECT_GT(column_profile[398], 0.0f); | |
| 297 EXPECT_GT(column_profile[399], column_profile[398]); | |
| 298 EXPECT_GT(column_profile[402], 0.0f); | |
| 299 EXPECT_GT(column_profile[401], column_profile[402]); | |
| 300 EXPECT_EQ(column_profile[401], column_profile[399]); | |
| 301 EXPECT_EQ(column_profile[402], column_profile[398]); | |
| 302 EXPECT_EQ(column_profile[400], 0.0f); | |
| 303 EXPECT_EQ(column_profile[299], 0.0f); | |
| 304 EXPECT_EQ(column_profile[502], 0.0f); | |
| 305 | |
| 306 // Now the same with closing applied. The space in the middle will be closed. | |
| 307 color_utils::ExtractImageProfileInformation(reduced_color, | |
| 308 image_rect, | |
| 309 gfx::Size(200, 100), | |
| 310 true, | |
| 311 &row_profile, | |
| 312 &column_profile); | |
| 313 EXPECT_GT(column_profile[398], 0); | |
| 314 EXPECT_GT(column_profile[400], 0); | |
| 315 EXPECT_GT(column_profile[402], 0); | |
| 316 EXPECT_EQ(column_profile[299], 0); | |
| 317 EXPECT_EQ(column_profile[502], 0); | |
| 318 EXPECT_EQ(column_profile[399], column_profile[401]); | |
| 319 EXPECT_EQ(column_profile[398], column_profile[402]); | |
| 320 } | |
| 321 | |
| 322 TEST_F(ContentAnalysisTest, AutoSegmentPeaks) { | |
| 323 std::vector<float> profile_info; | |
| 324 | |
| 325 EXPECT_EQ(color_utils::AutoSegmentPeaks(profile_info), | |
| 326 std::numeric_limits<float>::max()); | |
| 327 profile_info.resize(1000, 1.0f); | |
| 328 EXPECT_EQ(color_utils::AutoSegmentPeaks(profile_info), 1.0f); | |
| 329 std::srand(42); | |
| 330 std::generate(profile_info.begin(), profile_info.end(), std::rand); | |
| 331 float threshold = color_utils::AutoSegmentPeaks(profile_info); | |
| 332 EXPECT_GT(threshold, 0); // Not much to expect. | |
| 333 | |
| 334 // There should be roughly 50% above and below the threshold. | |
| 335 // Random is not really random thanks to srand, so we can sort-of compare. | |
| 336 int above_count = std::count_if( | |
| 337 profile_info.begin(), | |
| 338 profile_info.end(), | |
| 339 std::bind2nd(std::greater<float>(), threshold)); | |
| 340 EXPECT_GT(above_count, 475); // Not much to expect. | |
| 341 EXPECT_LT(above_count, 525); | |
| 342 | |
| 343 for (unsigned i = 0; i < profile_info.size(); ++i) { | |
| 344 float y = std::sin(M_PI * i / 250.0f); | |
| 345 profile_info[i] = y > 0 ? y : 0; | |
| 346 } | |
| 347 threshold = color_utils::AutoSegmentPeaks(profile_info); | |
| 348 | |
| 349 above_count = std::count_if( | |
| 350 profile_info.begin(), | |
| 351 profile_info.end(), | |
| 352 std::bind2nd(std::greater<float>(), threshold)); | |
| 353 EXPECT_LT(above_count, 500); // Negative y expected to fall below threshold. | |
| 354 | |
| 355 // Expect two peaks around between 0 and 250 and 500 and 750. | |
| 356 std::vector<bool> thresholded_values(profile_info.size(), false); | |
| 357 std::transform(profile_info.begin(), | |
| 358 profile_info.end(), | |
| 359 thresholded_values.begin(), | |
| 360 std::bind2nd(std::greater<float>(), threshold)); | |
| 361 EXPECT_TRUE(thresholded_values[125]); | |
| 362 EXPECT_TRUE(thresholded_values[625]); | |
| 363 int transitions = 0; | |
| 364 for (unsigned i = 1; i < thresholded_values.size(); ++i) { | |
| 365 if (thresholded_values[i] != thresholded_values[i-1]) | |
| 366 transitions++; | |
| 367 } | |
| 368 EXPECT_EQ(transitions, 4); // We have two contiguous peaks. Good going! | |
| 369 } | |
| 370 | |
| 371 TEST_F(ContentAnalysisTest, ComputeDecimatedImage) { | |
| 372 gfx::Size image_size(1600, 1200); | |
| 373 gfx::Canvas canvas(image_size, ui::SCALE_FACTOR_100P, true); | |
| 374 | |
| 375 // Make some content we will later want to keep. | |
| 376 canvas.FillRect(gfx::Rect(100, 200, 100, 100), SkColorSetARGB(0, 125, 0, 0)); | |
| 377 canvas.FillRect(gfx::Rect(300, 200, 100, 100), SkColorSetARGB(0, 0, 200, 0)); | |
| 378 canvas.FillRect(gfx::Rect(500, 200, 100, 100), SkColorSetARGB(0, 0, 0, 225)); | |
| 379 canvas.FillRect(gfx::Rect(100, 400, 600, 100), | |
| 380 SkColorSetARGB(0, 125, 200, 225)); | |
| 381 | |
| 382 std::vector<bool> rows(image_size.height(), false); | |
| 383 std::fill_n(rows.begin() + 200, 100, true); | |
| 384 std::fill_n(rows.begin() + 400, 100, true); | |
| 385 | |
| 386 std::vector<bool> columns(image_size.width(), false); | |
| 387 std::fill_n(columns.begin() + 100, 100, true); | |
| 388 std::fill_n(columns.begin() + 300, 100, true); | |
| 389 std::fill_n(columns.begin() + 500, 100, true); | |
| 390 | |
| 391 SkBitmap source = | |
| 392 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false); | |
| 393 scoped_ptr<SkBitmap> result( | |
| 394 color_utils::ComputeDecimatedImage(source, rows, columns)); | |
| 395 EXPECT_FALSE(result.get() == NULL); | |
| 396 EXPECT_EQ(300, result->width()); | |
| 397 EXPECT_EQ(200, result->height()); | |
| 398 | |
| 399 // The call should have removed all empty spaces. | |
| 400 ASSERT_TRUE(CompareImageFragments(source, | |
| 401 *result, | |
| 402 gfx::Size(100, 100), | |
| 403 gfx::Point(100, 200), | |
| 404 gfx::Point(0, 0))); | |
| 405 ASSERT_TRUE(CompareImageFragments(source, | |
| 406 *result, | |
| 407 gfx::Size(100, 100), | |
| 408 gfx::Point(300, 200), | |
| 409 gfx::Point(100, 0))); | |
| 410 ASSERT_TRUE(CompareImageFragments(source, | |
| 411 *result, | |
| 412 gfx::Size(100, 100), | |
| 413 gfx::Point(500, 200), | |
| 414 gfx::Point(200, 0))); | |
| 415 ASSERT_TRUE(CompareImageFragments(source, | |
| 416 *result, | |
| 417 gfx::Size(100, 100), | |
| 418 gfx::Point(100, 400), | |
| 419 gfx::Point(0, 0))); | |
| 420 } | |
| 421 | |
| 422 TEST_F(ContentAnalysisTest, CreateRetargettedThumbnailImage) { | |
| 423 gfx::Size image_size(1200, 1300); | |
| 424 gfx::Canvas canvas(image_size, ui::SCALE_FACTOR_100P, true); | |
| 425 | |
| 426 // The following will create a 'fake image' consisting of color blocks placed | |
| 427 // on a neutral background. The entire layout is supposed to mimic a | |
| 428 // screenshot of a web page. | |
| 429 // The tested function is supposed to locate the interesing areas in the | |
| 430 // middle. | |
| 431 const int margin_horizontal = 60; | |
| 432 const int margin_vertical = 20; | |
| 433 canvas.FillRect(gfx::Rect(image_size), SkColorSetRGB(200, 210, 210)); | |
| 434 const gfx::Rect header_rect(margin_horizontal, | |
| 435 margin_vertical, | |
| 436 image_size.width() - 2 * margin_horizontal, | |
| 437 100); | |
| 438 const gfx::Rect footer_rect(margin_horizontal, | |
| 439 image_size.height() - margin_vertical - 100, | |
| 440 image_size.width() - 2 * margin_horizontal, | |
| 441 100); | |
| 442 const gfx::Rect body_rect(margin_horizontal, | |
| 443 header_rect.bottom() + margin_vertical, | |
| 444 image_size.width() - 2 * margin_horizontal, | |
| 445 footer_rect.y() - header_rect.bottom() - | |
| 446 2 * margin_vertical); | |
| 447 canvas.FillRect(header_rect, SkColorSetRGB(200, 40, 10)); | |
| 448 canvas.FillRect(footer_rect, SkColorSetRGB(10, 40, 180)); | |
| 449 canvas.FillRect(body_rect, SkColorSetRGB(150, 180, 40)); | |
| 450 | |
| 451 // 'Fine print' at the bottom. | |
| 452 const int fine_print = 8; | |
| 453 const SkColor print_color = SkColorSetRGB(45, 30, 30); | |
| 454 for (int y = footer_rect.y() + fine_print; | |
| 455 y < footer_rect.bottom() - fine_print; | |
| 456 y += 2 * fine_print) { | |
| 457 for (int x = footer_rect.x() + fine_print; | |
| 458 x < footer_rect.right() - fine_print; | |
| 459 x += 2 * fine_print) { | |
| 460 canvas.DrawRect(gfx::Rect(x, y, fine_print, fine_print), print_color); | |
| 461 } | |
| 462 } | |
| 463 | |
| 464 // Blocky content at the top. | |
| 465 const int block_size = header_rect.height() - margin_vertical; | |
| 466 for (int x = header_rect.x() + margin_horizontal; | |
| 467 x < header_rect.right() - block_size; | |
| 468 x += block_size + margin_horizontal) { | |
| 469 const int half_block = block_size / 2 - 5; | |
| 470 const SkColor block_color = SkColorSetRGB(255, 255, 255); | |
| 471 const int y = header_rect.y() + margin_vertical / 2; | |
| 472 int second_col = x + half_block + 10; | |
| 473 int second_row = y + half_block + 10; | |
| 474 canvas.FillRect(gfx::Rect(x, y, half_block, block_size), block_color); | |
| 475 canvas.FillRect(gfx::Rect(second_col, y, half_block, half_block), | |
| 476 block_color); | |
| 477 canvas.FillRect(gfx::Rect(second_col, second_row, half_block, half_block), | |
| 478 block_color); | |
| 479 } | |
| 480 | |
| 481 // Now the main body. Mostly text with some 'pictures'. | |
| 482 for (int y = body_rect.y() + fine_print; | |
| 483 y < body_rect.bottom() - fine_print; | |
| 484 y += 2 * fine_print) { | |
| 485 for (int x = body_rect.x() + fine_print; | |
| 486 x < body_rect.right() - fine_print; | |
| 487 x += 2 * fine_print) { | |
| 488 canvas.DrawRect(gfx::Rect(x, y, fine_print, fine_print), print_color); | |
| 489 } | |
| 490 } | |
| 491 | |
| 492 for (int line = 0; line < 3; ++line) { | |
| 493 int alignment = line % 2; | |
| 494 const int y = body_rect.y() + | |
| 495 body_rect.height() / 3 * line + margin_vertical; | |
| 496 const int x = body_rect.x() + | |
| 497 alignment * body_rect.width() / 2 + margin_vertical; | |
| 498 gfx::Rect pict_rect(x, y, | |
| 499 body_rect.width() / 2 - 2 * margin_vertical, | |
| 500 body_rect.height() / 3 - 2 * margin_vertical); | |
| 501 canvas.FillRect(pict_rect, SkColorSetRGB(255, 255, 255)); | |
| 502 canvas.DrawRect(pict_rect, SkColorSetRGB(0, 0, 0)); | |
| 503 } | |
| 504 | |
| 505 SkBitmap source = | |
| 506 skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(false); | |
| 507 | |
| 508 scoped_ptr<SkBitmap> result(color_utils::CreateRetargettedThumbnailImage( | |
| 509 source, gfx::Size(424, 264), 2.5)); | |
| 510 EXPECT_FALSE(result.get() == NULL); | |
| 511 | |
| 512 // Given the nature of computation We can't really assert much here about the | |
| 513 // image itself. We know it should have been computed, should be smaller than | |
| 514 // the original and it must not be zero. | |
| 515 EXPECT_LT(result->width(), image_size.width()); | |
| 516 EXPECT_LT(result->height(), image_size.height()); | |
| 517 | |
| 518 int histogram[256]; | |
| 519 color_utils::BuildLumaHistogram(*result, histogram); | |
| 520 int non_zero_color_count = std::count_if( | |
| 521 histogram, histogram + 256, std::bind2nd(std::greater<int>(), 0)); | |
| 522 EXPECT_GT(non_zero_color_count, 4); | |
| 523 } | |
| OLD | NEW |