1/* 2 * Copyright 2013 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "include/core/SkBitmap.h" 9#include "include/core/SkColor.h" 10#include "include/core/SkImageInfo.h" 11#include "include/core/SkMallocPixelRef.h" 12#include "include/core/SkPixelRef.h" 13#include "include/core/SkPixmap.h" 14#include "include/core/SkRefCnt.h" 15#include "include/core/SkScalar.h" 16#include "include/core/SkTypes.h" 17#include "include/private/SkFloatingPoint.h" 18#include "include/utils/SkRandom.h" 19#include "tests/Test.h" 20#include "tools/ToolUtils.h" 21 22#include <initializer_list> 23 24static void test_peekpixels(skiatest::Reporter* reporter) { 25 const SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10); 26 27 SkPixmap pmap; 28 SkBitmap bm; 29 30 // empty should return false 31 REPORTER_ASSERT(reporter, !bm.peekPixels(nullptr)); 32 REPORTER_ASSERT(reporter, !bm.peekPixels(&pmap)); 33 34 // no pixels should return false 35 bm.setInfo(SkImageInfo::MakeN32Premul(10, 10)); 36 REPORTER_ASSERT(reporter, !bm.peekPixels(nullptr)); 37 REPORTER_ASSERT(reporter, !bm.peekPixels(&pmap)); 38 39 // real pixels should return true 40 bm.allocPixels(info); 41 REPORTER_ASSERT(reporter, bm.peekPixels(nullptr)); 42 REPORTER_ASSERT(reporter, bm.peekPixels(&pmap)); 43 REPORTER_ASSERT(reporter, pmap.info() == bm.info()); 44 REPORTER_ASSERT(reporter, pmap.addr() == bm.getPixels()); 45 REPORTER_ASSERT(reporter, pmap.rowBytes() == bm.rowBytes()); 46} 47 48// https://code.google.com/p/chromium/issues/detail?id=446164 49static void test_bigalloc(skiatest::Reporter* reporter) { 50 const int width = 0x40000001; 51 const int height = 0x00000096; 52 const SkImageInfo info = SkImageInfo::MakeN32Premul(width, height); 53 54 SkBitmap bm; 55 REPORTER_ASSERT(reporter, !bm.tryAllocPixels(info)); 56 57 sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeAllocate(info, info.minRowBytes()); 58 REPORTER_ASSERT(reporter, !pr); 59} 60 61static void test_allocpixels(skiatest::Reporter* reporter) { 62 const int width = 10; 63 const int height = 10; 64 const SkImageInfo info = SkImageInfo::MakeN32Premul(width, height); 65 const size_t explicitRowBytes = info.minRowBytes() + 24; 66 67 SkBitmap bm; 68 bm.setInfo(info); 69 REPORTER_ASSERT(reporter, info.minRowBytes() == bm.rowBytes()); 70 bm.allocPixels(); 71 REPORTER_ASSERT(reporter, info.minRowBytes() == bm.rowBytes()); 72 bm.reset(); 73 bm.allocPixels(info); 74 REPORTER_ASSERT(reporter, info.minRowBytes() == bm.rowBytes()); 75 76 bm.setInfo(info, explicitRowBytes); 77 REPORTER_ASSERT(reporter, explicitRowBytes == bm.rowBytes()); 78 bm.allocPixels(); 79 REPORTER_ASSERT(reporter, explicitRowBytes == bm.rowBytes()); 80 bm.reset(); 81 bm.allocPixels(info, explicitRowBytes); 82 REPORTER_ASSERT(reporter, explicitRowBytes == bm.rowBytes()); 83 84 bm.reset(); 85 bm.setInfo(info, 0); 86 REPORTER_ASSERT(reporter, info.minRowBytes() == bm.rowBytes()); 87 bm.reset(); 88 bm.allocPixels(info, 0); 89 REPORTER_ASSERT(reporter, info.minRowBytes() == bm.rowBytes()); 90 91 bm.reset(); 92 bool success = bm.setInfo(info, info.minRowBytes() - 1); // invalid for 32bit 93 REPORTER_ASSERT(reporter, !success); 94 REPORTER_ASSERT(reporter, bm.isNull()); 95 96 for (SkColorType ct : { 97 kAlpha_8_SkColorType, 98 kRGB_565_SkColorType, 99 kARGB_4444_SkColorType, 100 kRGBA_8888_SkColorType, 101 kBGRA_8888_SkColorType, 102 kRGB_888x_SkColorType, 103 kRGBA_1010102_SkColorType, 104 kRGB_101010x_SkColorType, 105 kGray_8_SkColorType, 106 kRGBA_F16Norm_SkColorType, 107 kRGBA_F16_SkColorType, 108 kRGBA_F32_SkColorType, 109 kR8G8_unorm_SkColorType, 110 kA16_unorm_SkColorType, 111 kR16G16_unorm_SkColorType, 112 kA16_float_SkColorType, 113 kR16G16_float_SkColorType, 114 kR16G16B16A16_unorm_SkColorType, 115 }) { 116 SkImageInfo imageInfo = info.makeColorType(ct); 117 for (int rowBytesPadding = 1; rowBytesPadding <= 17; rowBytesPadding++) { 118 bm.reset(); 119 success = bm.setInfo(imageInfo, imageInfo.minRowBytes() + rowBytesPadding); 120 if (rowBytesPadding % imageInfo.bytesPerPixel() == 0) { 121 REPORTER_ASSERT(reporter, success); 122 success = bm.tryAllocPixels(); 123 REPORTER_ASSERT(reporter, success); 124 } else { 125 // Not pixel aligned. 126 REPORTER_ASSERT(reporter, !success); 127 REPORTER_ASSERT(reporter, bm.isNull()); 128 } 129 } 130 } 131} 132 133static void test_bigwidth(skiatest::Reporter* reporter) { 134 SkBitmap bm; 135 int width = 1 << 29; // *4 will be the high-bit of 32bit int 136 137 SkImageInfo info = SkImageInfo::MakeA8(width, 1); 138 REPORTER_ASSERT(reporter, bm.setInfo(info)); 139 REPORTER_ASSERT(reporter, bm.setInfo(info.makeColorType(kRGB_565_SkColorType))); 140 141 // for a 4-byte config, this width will compute a rowbytes of 0x80000000, 142 // which does not fit in a int32_t. setConfig should detect this, and fail. 143 144 // TODO: perhaps skia can relax this, and only require that rowBytes fit 145 // in a uint32_t (or larger), but for now this is the constraint. 146 147 REPORTER_ASSERT(reporter, !bm.setInfo(info.makeColorType(kN32_SkColorType))); 148} 149 150DEF_TEST(Bitmap, reporter) { 151 // Zero-sized bitmaps are allowed 152 for (int width = 0; width < 2; ++width) { 153 for (int height = 0; height < 2; ++height) { 154 SkBitmap bm; 155 bool setConf = bm.setInfo(SkImageInfo::MakeN32Premul(width, height)); 156 REPORTER_ASSERT(reporter, setConf); 157 if (setConf) { 158 bm.allocPixels(); 159 } 160 REPORTER_ASSERT(reporter, SkToBool(width & height) != bm.empty()); 161 } 162 } 163 164 test_bigwidth(reporter); 165 test_allocpixels(reporter); 166 test_bigalloc(reporter); 167 test_peekpixels(reporter); 168} 169 170/** 171 * This test checks that getColor works for both swizzles. 172 */ 173DEF_TEST(Bitmap_getColor_Swizzle, r) { 174 SkBitmap source; 175 source.allocN32Pixels(1,1); 176 source.eraseColor(SK_ColorRED); 177 SkColorType colorTypes[] = { 178 kRGBA_8888_SkColorType, 179 kBGRA_8888_SkColorType, 180 }; 181 for (SkColorType ct : colorTypes) { 182 SkBitmap copy; 183 if (!ToolUtils::copy_to(©, ct, source)) { 184 ERRORF(r, "SkBitmap::copy failed %d", (int)ct); 185 continue; 186 } 187 REPORTER_ASSERT(r, source.getColor(0, 0) == copy.getColor(0, 0)); 188 } 189} 190 191static void test_erasecolor_premul(skiatest::Reporter* reporter, SkColorType ct, SkColor input, 192 SkColor expected) { 193 SkBitmap bm; 194 bm.allocPixels(SkImageInfo::Make(1, 1, ct, kPremul_SkAlphaType)); 195 bm.eraseColor(input); 196 INFOF(reporter, "expected: %x actual: %x\n", expected, bm.getColor(0, 0)); 197 REPORTER_ASSERT(reporter, bm.getColor(0, 0) == expected); 198} 199 200/** 201 * This test checks that eraseColor premultiplies the color correctly. 202 */ 203DEF_TEST(Bitmap_eraseColor_Premul, r) { 204 SkColor color = 0x80FF0080; 205 test_erasecolor_premul(r, kAlpha_8_SkColorType, color, 0x80000000); 206 test_erasecolor_premul(r, kRGB_565_SkColorType, color, 0xFF840042); 207 test_erasecolor_premul(r, kARGB_4444_SkColorType, color, 0x88FF0080); 208 test_erasecolor_premul(r, kRGBA_8888_SkColorType, color, color); 209 test_erasecolor_premul(r, kBGRA_8888_SkColorType, color, color); 210} 211 212// Test that SkBitmap::ComputeOpaque() is correct for various colortypes. 213DEF_TEST(Bitmap_compute_is_opaque, r) { 214 215 for (int i = 1; i <= kLastEnum_SkColorType; ++i) { 216 SkColorType ct = (SkColorType) i; 217 SkBitmap bm; 218 SkAlphaType at = SkColorTypeIsAlwaysOpaque(ct) ? kOpaque_SkAlphaType : kPremul_SkAlphaType; 219 bm.allocPixels(SkImageInfo::Make(13, 17, ct, at)); 220 bm.eraseColor(SkColorSetARGB(255, 10, 20, 30)); 221 REPORTER_ASSERT(r, SkBitmap::ComputeIsOpaque(bm)); 222 223 bm.eraseColor(SkColorSetARGB(128, 255, 255, 255)); 224 bool isOpaque = SkBitmap::ComputeIsOpaque(bm); 225 bool shouldBeOpaque = (at == kOpaque_SkAlphaType); 226 REPORTER_ASSERT(r, isOpaque == shouldBeOpaque); 227 } 228} 229 230// Test that erase+getColor round trips with RGBA_F16 pixels. 231DEF_TEST(Bitmap_erase_f16_erase_getColor, r) { 232 SkRandom random; 233 SkPixmap pm; 234 SkBitmap bm; 235 bm.allocPixels(SkImageInfo::Make(1, 1, kRGBA_F16_SkColorType, kPremul_SkAlphaType)); 236 REPORTER_ASSERT(r, bm.peekPixels(&pm)); 237 for (unsigned i = 0; i < 0x100; ++i) { 238 // Test all possible values of blue component. 239 SkColor color1 = (SkColor)((random.nextU() & 0xFFFFFF00) | i); 240 // Test all possible values of alpha component. 241 SkColor color2 = (SkColor)((random.nextU() & 0x00FFFFFF) | (i << 24)); 242 for (SkColor color : {color1, color2}) { 243 pm.erase(color); 244 if (SkColorGetA(color) != 0) { 245 REPORTER_ASSERT(r, color == pm.getColor(0, 0)); 246 } else { 247 REPORTER_ASSERT(r, 0 == SkColorGetA(pm.getColor(0, 0))); 248 } 249 } 250 } 251} 252 253// Verify that SkBitmap::erase erases in SRGB, regardless of the SkColorSpace of the 254// SkBitmap. 255DEF_TEST(Bitmap_erase_srgb, r) { 256 SkBitmap bm; 257 // Use a color spin from SRGB. 258 bm.allocPixels(SkImageInfo::Make(1, 1, kN32_SkColorType, kPremul_SkAlphaType, 259 SkColorSpace::MakeSRGB()->makeColorSpin())); 260 // RED will be converted into the spun color space. 261 bm.eraseColor(SK_ColorRED); 262 // getColor doesn't take the color space into account, so the returned color 263 // is different due to the color spin. 264 REPORTER_ASSERT(r, bm.getColor(0, 0) == SK_ColorBLUE); 265} 266 267// Make sure that the bitmap remains valid when pixelref is removed. 268DEF_TEST(Bitmap_clear_pixelref_keep_info, r) { 269 SkBitmap bm; 270 bm.allocPixels(SkImageInfo::MakeN32Premul(100,100)); 271 bm.setPixelRef(nullptr, 0, 0); 272 SkDEBUGCODE(bm.validate();) 273} 274 275// At the time of writing, SkBitmap::erase() works when the color is zero for all formats, 276// but some formats failed when the color is non-zero! 277DEF_TEST(Bitmap_erase, r) { 278 SkColorType colorTypes[] = { 279 kRGB_565_SkColorType, 280 kARGB_4444_SkColorType, 281 kRGB_888x_SkColorType, 282 kRGBA_8888_SkColorType, 283 kBGRA_8888_SkColorType, 284 kRGB_101010x_SkColorType, 285 kRGBA_1010102_SkColorType, 286 }; 287 288 for (SkColorType ct : colorTypes) { 289 SkImageInfo info = SkImageInfo::Make(1,1, (SkColorType)ct, kPremul_SkAlphaType); 290 291 SkBitmap bm; 292 bm.allocPixels(info); 293 294 bm.eraseColor(0x00000000); 295 if (SkColorTypeIsAlwaysOpaque(ct)) { 296 REPORTER_ASSERT(r, bm.getColor(0,0) == 0xff000000); 297 } else { 298 REPORTER_ASSERT(r, bm.getColor(0,0) == 0x00000000); 299 } 300 301 bm.eraseColor(0xaabbccdd); 302 REPORTER_ASSERT(r, bm.getColor(0,0) != 0xff000000); 303 REPORTER_ASSERT(r, bm.getColor(0,0) != 0x00000000); 304 } 305} 306 307static void check_alphas(skiatest::Reporter* reporter, const SkBitmap& bm, 308 bool (*pred)(float expected, float actual), SkColorType ct) { 309 SkASSERT(bm.width() == 16); 310 SkASSERT(bm.height() == 16); 311 312 int alpha = 0; 313 for (int y = 0; y < 16; ++y) { 314 for (int x = 0; x < 16; ++x) { 315 float expected = alpha / 255.0f; 316 float actual = bm.getAlphaf(x, y); 317 if (!pred(expected, actual)) { 318 ERRORF(reporter, "%s: got %g, want %g\n", 319 ToolUtils::colortype_name(ct), actual, expected); 320 } 321 alpha += 1; 322 } 323 } 324} 325 326static bool unit_compare(float expected, float actual, float tol = 1.0f/(1<<12)) { 327 SkASSERT(expected >= 0 && expected <= 1); 328 SkASSERT( actual >= 0 && actual <= 1); 329 if (expected == 0 || expected == 1) { 330 return actual == expected; 331 } else { 332 return SkScalarNearlyEqual(expected, actual, tol); 333 } 334} 335 336static float unit_discretize(float value, float scale) { 337 SkASSERT(value >= 0 && value <= 1); 338 if (value == 1) { 339 return 1; 340 } else { 341 return sk_float_floor(value * scale + 0.5f) / scale; 342 } 343} 344 345DEF_TEST(getalphaf, reporter) { 346 SkImageInfo info = SkImageInfo::MakeN32Premul(16, 16); 347 SkBitmap bm; 348 bm.allocPixels(info); 349 350 int alpha = 0; 351 for (int y = 0; y < 16; ++y) { 352 for (int x = 0; x < 16; ++x) { 353 *bm.getAddr32(x, y) = alpha++ << 24; 354 } 355 } 356 357 auto nearly = [](float expected, float actual) -> bool { 358 return unit_compare(expected, actual); 359 }; 360 auto nearly4bit = [](float expected, float actual) -> bool { 361 expected = unit_discretize(expected, 15); 362 return unit_compare(expected, actual); 363 }; 364 auto nearly2bit = [](float expected, float actual) -> bool { 365 expected = unit_discretize(expected, 3); 366 return unit_compare(expected, actual); 367 }; 368 auto opaque = [](float expected, float actual) -> bool { 369 return actual == 1.0f; 370 }; 371 372 auto nearly_half = [](float expected, float actual) -> bool { 373 return unit_compare(expected, actual, 1.0f/(1<<10)); 374 }; 375 376 const struct { 377 SkColorType fColorType; 378 bool (*fPred)(float, float); 379 } recs[] = { 380 { kRGB_565_SkColorType, opaque }, 381 { kGray_8_SkColorType, opaque }, 382 { kR8G8_unorm_SkColorType, opaque }, 383 { kR16G16_unorm_SkColorType, opaque }, 384 { kR16G16_float_SkColorType, opaque }, 385 { kRGB_888x_SkColorType, opaque }, 386 { kRGB_101010x_SkColorType, opaque }, 387 388 { kAlpha_8_SkColorType, nearly }, 389 { kA16_unorm_SkColorType, nearly }, 390 { kA16_float_SkColorType, nearly_half }, 391 { kRGBA_8888_SkColorType, nearly }, 392 { kBGRA_8888_SkColorType, nearly }, 393 { kR16G16B16A16_unorm_SkColorType, nearly }, 394 { kRGBA_F16_SkColorType, nearly_half }, 395 { kRGBA_F32_SkColorType, nearly }, 396 397 { kRGBA_1010102_SkColorType, nearly2bit }, 398 399 { kARGB_4444_SkColorType, nearly4bit }, 400 }; 401 402 for (const auto& rec : recs) { 403 SkBitmap tmp; 404 tmp.allocPixels(bm.info().makeColorType(rec.fColorType)); 405 if (bm.readPixels(tmp.pixmap())) { 406 check_alphas(reporter, tmp, rec.fPred, rec.fColorType); 407 } else { 408 SkDebugf("can't readpixels\n"); 409 } 410 } 411} 412 413/* computeByteSize() is documented to return 0 if height is zero, but does not 414 * special-case width==0, so computeByteSize() can return non-zero for that 415 * (since it is defined to return (height-1)*rb + ... 416 * 417 * Test that allocPixels() respects this, and allocates a buffer as large as 418 * computeByteSize()... even though the bitmap is logicallly empty. 419 */ 420DEF_TEST(bitmap_zerowidth_crbug_1103827, reporter) { 421 const size_t big_rb = 1 << 16; 422 423 struct { 424 int width, height; 425 size_t rowbytes, expected_size; 426 } rec[] = { 427 { 2, 0, big_rb, 0 }, // zero-height means zero-size 428 { 0, 2, big_rb, big_rb }, // zero-width is computed normally 429 }; 430 431 for (const auto& r : rec) { 432 auto info = SkImageInfo::Make(r.width, r.height, 433 kRGBA_8888_SkColorType, kPremul_SkAlphaType); 434 size_t size = info.computeByteSize(r.rowbytes); 435 REPORTER_ASSERT(reporter, size == r.expected_size); 436 437 SkBitmap bm; 438 bm.setInfo(info, r.rowbytes); 439 REPORTER_ASSERT(reporter, size == bm.computeByteSize()); 440 441 // Be sure we can actually write to that much memory. If the bitmap underallocated 442 // the buffer, this should trash memory and crash (we hope). 443 bm.allocPixels(); 444 sk_bzero(bm.getPixels(), size); 445 } 446} 447