Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright 2013 Google Inc. | 2 * Copyright 2013 Google Inc. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 #include "SkMipMap.h" | 8 #include "SkMipMap.h" |
| 9 #include "SkBitmap.h" | 9 #include "SkBitmap.h" |
| 10 #include "SkColorPriv.h" | 10 #include "SkColorPriv.h" |
| 11 | 11 |
| 12 #ifdef SK_SUPPORT_LEGACY_MIPLEVEL_BUILDER | |
| 13 | |
| 12 static void downsample32_nocheck(void* dst, int, int, const void* srcPtr, const SkPixmap& srcPM) { | 14 static void downsample32_nocheck(void* dst, int, int, const void* srcPtr, const SkPixmap& srcPM) { |
| 13 const uint32_t* p = static_cast<const uint32_t*>(srcPtr); | 15 const uint32_t* p = static_cast<const uint32_t*>(srcPtr); |
| 14 const uint32_t* baseP = p; | 16 const uint32_t* baseP = p; |
| 15 uint32_t c, ag, rb; | 17 uint32_t c, ag, rb; |
| 16 | 18 |
| 17 c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF; | 19 c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF; |
| 18 p += 1; | 20 p += 1; |
| 19 | 21 |
| 20 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF; | 22 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF; |
| 21 | 23 |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 125 p = baseP; | 127 p = baseP; |
| 126 if (y < srcPM.height() - 1) { | 128 if (y < srcPM.height() - 1) { |
| 127 p += srcPM.rowBytes() >> 1; | 129 p += srcPM.rowBytes() >> 1; |
| 128 } | 130 } |
| 129 c += expand4444(*p); | 131 c += expand4444(*p); |
| 130 if (x < srcPM.width() - 1) { | 132 if (x < srcPM.width() - 1) { |
| 131 p += 1; | 133 p += 1; |
| 132 } | 134 } |
| 133 c += expand4444(*p); | 135 c += expand4444(*p); |
| 134 | 136 |
| 135 *((uint16_t*)dst) = (uint16_t)collaps4444(c >> 2); | 137 *((uint16_t*)dst) = (uint16_t)collaps4444(c >> 2); |
| 136 } | 138 } |
| 137 | 139 |
| 138 static void downsample8_nocheck(void* dst, int, int, const void* srcPtr, const S kPixmap& srcPM) { | 140 static void downsample8_nocheck(void* dst, int, int, const void* srcPtr, const S kPixmap& srcPM) { |
| 139 const size_t rb = srcPM.rowBytes(); | 141 const size_t rb = srcPM.rowBytes(); |
| 140 const uint8_t* p = static_cast<const uint8_t*>(srcPtr); | 142 const uint8_t* p = static_cast<const uint8_t*>(srcPtr); |
| 141 *(uint8_t*)dst = (p[0] + p[1] + p[rb] + p[rb + 1]) >> 2; | 143 *(uint8_t*)dst = (p[0] + p[1] + p[rb] + p[rb + 1]) >> 2; |
| 142 } | 144 } |
| 143 | 145 |
| 144 static void downsample8_check(void* dst, int x, int y, const void* srcPtr, const SkPixmap& srcPM) { | 146 static void downsample8_check(void* dst, int x, int y, const void* srcPtr, const SkPixmap& srcPM) { |
| 145 const uint8_t* p = static_cast<const uint8_t*>(srcPtr); | 147 const uint8_t* p = static_cast<const uint8_t*>(srcPtr); |
| (...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 307 for (int x = 0; x < width; x++) { | 309 for (int x = 0; x < width; x++) { |
| 308 proc_check(dstPtr, x, heightEven, srcPtr, srcPM); | 310 proc_check(dstPtr, x, heightEven, srcPtr, srcPM); |
| 309 srcPtr = (char*)srcPtr + pixelSize * 2; | 311 srcPtr = (char*)srcPtr + pixelSize * 2; |
| 310 dstPtr = (char*)dstPtr + pixelSize; | 312 dstPtr = (char*)dstPtr + pixelSize; |
| 311 } | 313 } |
| 312 } | 314 } |
| 313 srcPM = dstPM; | 315 srcPM = dstPM; |
| 314 addr += height * rowBytes; | 316 addr += height * rowBytes; |
| 315 } | 317 } |
| 316 SkASSERT(addr == baseAddr + size); | 318 SkASSERT(addr == baseAddr + size); |
| 317 | 319 |
| 318 return mipmap; | 320 return mipmap; |
| 319 } | 321 } |
| 320 | 322 |
| 323 #else // new technique that handles odd dimensions better | |
| 324 | |
| 325 // | |
| 326 // ColorTypeFilter is the "Type" we pass to some downsample template functions. | |
| 327 // It controls how we expand a pixel into a large type, with space between each component, | |
| 328 // so we can then perform our simple filter (either box or triangle) and store t he intermediates | |
| 329 // in the expanded type. | |
| 330 // | |
| 331 | |
| 332 struct ColorTypeFilter_8888 { | |
| 333 typedef uint32_t Type; | |
| 334 static uint64_t Expand(uint32_t x) { | |
| 335 return (x & 0xFF00FF) | ((uint64_t)(x & 0xFF00FF00) << 24); | |
| 336 } | |
| 337 static uint32_t Compact(uint64_t x) { | |
| 338 return (uint32_t)((x & 0xFF00FF) | ((x >> 24) & 0xFF00FF00)); | |
| 339 } | |
| 340 }; | |
| 341 | |
| 342 struct ColorTypeFilter_565 { | |
| 343 typedef uint16_t Type; | |
| 344 static uint32_t Expand(uint16_t x) { | |
| 345 return (x & ~SK_G16_MASK_IN_PLACE) | ((x & SK_G16_MASK_IN_PLACE) << 16); | |
| 346 } | |
| 347 static uint16_t Compact(uint32_t x) { | |
| 348 return (x & ~SK_G16_MASK_IN_PLACE) | ((x >> 16) & SK_G16_MASK_IN_PLACE); | |
| 349 } | |
| 350 }; | |
| 351 | |
| 352 struct ColorTypeFilter_4444 { | |
| 353 typedef uint16_t Type; | |
| 354 static uint32_t Expand(uint16_t x) { | |
| 355 return (x & 0xF0F) | ((x & ~0xF0F) << 12); | |
| 356 } | |
| 357 static uint16_t Compact(uint32_t x) { | |
| 358 return (x & 0xF0F) | ((x >> 12) & ~0xF0F); | |
| 359 } | |
| 360 }; | |
| 361 | |
| 362 struct ColorTypeFilter_8 { | |
| 363 typedef uint8_t Type; | |
| 364 static unsigned Expand(unsigned x) { | |
| 365 return x; | |
| 366 } | |
| 367 static uint8_t Compact(unsigned x) { | |
| 368 return (uint8_t)x; | |
| 369 } | |
| 370 }; | |
| 371 | |
| 372 template <typename T> T add_121(T a, T b, T c) { | |
| 373 return a + b + b + c; | |
| 374 } | |
| 375 | |
| 376 // | |
| 377 // To produce each mip level, we need to filter down by 1/2 (e.g. 100x100 -> 50 ,50) | |
| 378 // If the starting dimension is odd, we floor the size of the lower level (e.g. 101 -> 50) | |
| 379 // In those (odd) cases, we use a triangle filter, with 1-pixel overlap between samplings, | |
| 380 // else for even cases, we just use a 2x box filter. | |
| 381 // | |
| 382 // This produces 4 possible filters: 2x2 2x3 3x2 3x3 where WxH indicates the nu mber of src pixels | |
| 383 // we need to sample in each dimension to produce 1 dst pixel. | |
| 384 // | |
| 385 | |
| 386 template <typename F> void downsample_2x2(void* dst, const void* src, size_t src RB) { | |
| 387 auto p0 = static_cast<const typename F::Type*>(src); | |
| 388 auto p1 = (const typename F::Type*)((const char*)p0 + srcRB); | |
| 389 | |
| 390 auto c00 = F::Expand(p0[0]); | |
| 391 auto c10 = F::Expand(p0[1]); | |
|
mtklein
2016/01/15 20:11:22
Is it just me or would these read more easily as
reed1
2016/01/15 20:58:16
Done.
| |
| 392 auto c01 = F::Expand(p1[0]); | |
| 393 auto c11 = F::Expand(p1[1]); | |
| 394 | |
| 395 auto c = c00 + c10 + c01 + c11; | |
| 396 *(typename F::Type*)dst = F::Compact(c >> 2); | |
| 397 } | |
| 398 | |
| 399 template <typename F> void downsample_3x2(void* dst, const void* src, size_t src RB) { | |
| 400 auto p0 = static_cast<const typename F::Type*>(src); | |
| 401 auto p1 = (const typename F::Type*)((const char*)p0 + srcRB); | |
| 402 | |
| 403 auto c00 = F::Expand(p0[0]); | |
| 404 auto c10 = F::Expand(p0[1]); | |
| 405 auto c20 = F::Expand(p0[2]); | |
| 406 auto c01 = F::Expand(p1[0]); | |
| 407 auto c11 = F::Expand(p1[1]); | |
| 408 auto c21 = F::Expand(p1[2]); | |
| 409 | |
| 410 auto c = add_121(c00, c10, c20) + add_121(c01, c11, c21); | |
| 411 *(typename F::Type*)dst = F::Compact(c >> 3); | |
| 412 } | |
| 413 | |
| 414 template <typename F> void downsample_2x3(void* dst, const void* src, size_t src RB) { | |
| 415 auto p0 = static_cast<const typename F::Type*>(src); | |
| 416 auto p1 = (const typename F::Type*)((const char*)p0 + srcRB); | |
| 417 auto p2 = (const typename F::Type*)((const char*)p1 + srcRB); | |
| 418 | |
| 419 auto c00 = F::Expand(p0[0]); | |
| 420 auto c10 = F::Expand(p0[1]); | |
| 421 auto c01 = F::Expand(p1[0]); | |
| 422 auto c11 = F::Expand(p1[1]); | |
| 423 auto c02 = F::Expand(p2[0]); | |
| 424 auto c12 = F::Expand(p2[1]); | |
| 425 auto c = add_121(c00, c01, c02) + add_121(c10, c11, c12); | |
| 426 *(uint32_t*)dst = F::Compact(c >> 3); | |
|
mtklein
2016/01/15 20:11:22
typename F::Type*
reed1
2016/01/15 20:58:16
Done.
| |
| 427 } | |
| 428 | |
| 429 template <typename F> void downsample_3x3(void* dst, const void* src, size_t src RB) { | |
| 430 auto p0 = static_cast<const typename F::Type*>(src); | |
| 431 auto p1 = (const typename F::Type*)((const char*)p0 + srcRB); | |
| 432 auto p2 = (const typename F::Type*)((const char*)p1 + srcRB); | |
| 433 | |
| 434 auto c00 = F::Expand(p0[0]); | |
| 435 auto c10 = F::Expand(p0[1]); | |
| 436 auto c20 = F::Expand(p0[2]); | |
| 437 auto c01 = F::Expand(p1[0]); | |
| 438 auto c11 = F::Expand(p1[1]); | |
| 439 auto c21 = F::Expand(p1[2]); | |
| 440 auto c02 = F::Expand(p2[0]); | |
| 441 auto c12 = F::Expand(p2[1]); | |
| 442 auto c22 = F::Expand(p2[2]); | |
| 443 | |
| 444 auto c = add_121(c00, c10, c20) + (add_121(c01, c11, c21) << 1) + add_121(c0 2, c12, c22); | |
| 445 *(typename F::Type*)dst = F::Compact(c >> 4); | |
| 446 } | |
| 447 | |
| 448 //////////////////////////////////////////////////////////////////////////////// /////////////////// | |
| 449 | |
| 450 size_t SkMipMap::AllocLevelsSize(int levelCount, size_t pixelSize) { | |
| 451 if (levelCount < 0) { | |
| 452 return 0; | |
| 453 } | |
| 454 int64_t size = sk_64_mul(levelCount + 1, sizeof(Level)) + pixelSize; | |
| 455 if (!sk_64_isS32(size)) { | |
| 456 return 0; | |
| 457 } | |
| 458 return sk_64_asS32(size); | |
| 459 } | |
| 460 | |
| 461 typedef void SkDownSampleProc(void*, const void* srcPtr, size_t srcRB); | |
| 462 | |
| 463 SkMipMap* SkMipMap::Build(const SkBitmap& src, SkDiscardableFactoryProc fact) { | |
| 464 SkDownSampleProc* proc2x2 = nullptr; | |
|
mtklein
2016/01/15 20:11:22
Can you just move your typedef inside the function
reed1
2016/01/15 20:58:16
Done.
| |
| 465 SkDownSampleProc* proc2x3 = nullptr; | |
| 466 SkDownSampleProc* proc3x2 = nullptr; | |
| 467 SkDownSampleProc* proc3x3 = nullptr; | |
| 468 | |
| 469 const SkColorType ct = src.colorType(); | |
| 470 const SkAlphaType at = src.alphaType(); | |
| 471 switch (ct) { | |
| 472 case kRGBA_8888_SkColorType: | |
| 473 case kBGRA_8888_SkColorType: | |
| 474 proc2x2 = downsample_2x2<ColorTypeFilter_8888>; | |
|
mtklein
2016/01/15 20:11:22
This may seem anal, but I think this section of co
reed1
2016/01/15 20:58:16
Hmmm, not sure about this one. I guess its ok eith
| |
| 475 proc2x3 = downsample_2x3<ColorTypeFilter_8888>; | |
| 476 proc3x2 = downsample_3x2<ColorTypeFilter_8888>; | |
| 477 proc3x3 = downsample_3x3<ColorTypeFilter_8888>; | |
| 478 break; | |
| 479 case kRGB_565_SkColorType: | |
| 480 proc2x2 = downsample_2x2<ColorTypeFilter_565>; | |
| 481 proc2x3 = downsample_2x3<ColorTypeFilter_565>; | |
| 482 proc3x2 = downsample_3x2<ColorTypeFilter_565>; | |
| 483 proc3x3 = downsample_3x3<ColorTypeFilter_565>; | |
| 484 break; | |
| 485 case kARGB_4444_SkColorType: | |
| 486 proc2x2 = downsample_2x2<ColorTypeFilter_4444>; | |
| 487 proc2x3 = downsample_2x3<ColorTypeFilter_4444>; | |
| 488 proc3x2 = downsample_3x2<ColorTypeFilter_4444>; | |
| 489 proc3x3 = downsample_3x3<ColorTypeFilter_4444>; | |
| 490 break; | |
| 491 case kAlpha_8_SkColorType: | |
| 492 case kGray_8_SkColorType: | |
| 493 proc2x2 = downsample_2x2<ColorTypeFilter_8>; | |
| 494 proc2x3 = downsample_2x3<ColorTypeFilter_8>; | |
| 495 proc3x2 = downsample_3x2<ColorTypeFilter_8>; | |
| 496 proc3x3 = downsample_3x3<ColorTypeFilter_8>; | |
| 497 break; | |
| 498 default: | |
| 499 // TODO: We could build miplevels for kIndex8 if the levels were in 8888. | |
| 500 // Means using more ram, but the quality would be fine. | |
| 501 return nullptr; | |
| 502 } | |
| 503 | |
| 504 // whip through our loop to compute the exact size needed | |
| 505 size_t size = 0; | |
| 506 int countLevels = 0; | |
| 507 { | |
| 508 int width = src.width(); | |
| 509 int height = src.height(); | |
| 510 for (;;) { | |
| 511 width >>= 1; | |
| 512 height >>= 1; | |
| 513 if (0 == width || 0 == height) { | |
| 514 break; | |
| 515 } | |
| 516 size += SkColorTypeMinRowBytes(ct, width) * height; | |
| 517 countLevels += 1; | |
| 518 } | |
| 519 } | |
| 520 if (0 == countLevels) { | |
| 521 return nullptr; | |
| 522 } | |
| 523 | |
| 524 size_t storageSize = SkMipMap::AllocLevelsSize(countLevels, size); | |
| 525 if (0 == storageSize) { | |
| 526 return nullptr; | |
| 527 } | |
| 528 | |
| 529 SkAutoPixmapUnlock srcUnlocker; | |
| 530 if (!src.requestLock(&srcUnlocker)) { | |
| 531 return nullptr; | |
| 532 } | |
| 533 const SkPixmap& srcPixmap = srcUnlocker.pixmap(); | |
| 534 // Try to catch where we might have returned nullptr for src crbug.com/49281 8 | |
| 535 if (nullptr == srcPixmap.addr()) { | |
| 536 sk_throw(); | |
| 537 } | |
| 538 | |
| 539 SkMipMap* mipmap; | |
| 540 if (fact) { | |
| 541 SkDiscardableMemory* dm = fact(storageSize); | |
| 542 if (nullptr == dm) { | |
| 543 return nullptr; | |
| 544 } | |
| 545 mipmap = new SkMipMap(storageSize, dm); | |
| 546 } else { | |
| 547 mipmap = new SkMipMap(sk_malloc_throw(storageSize), storageSize); | |
| 548 } | |
| 549 | |
| 550 // init | |
| 551 mipmap->fCount = countLevels; | |
| 552 mipmap->fLevels = (Level*)mipmap->writable_data(); | |
| 553 | |
| 554 Level* levels = mipmap->fLevels; | |
| 555 uint8_t* baseAddr = (uint8_t*)&levels[countLevels]; | |
| 556 uint8_t* addr = baseAddr; | |
| 557 int width = src.width(); | |
| 558 int height = src.height(); | |
| 559 uint32_t rowBytes; | |
| 560 SkPixmap srcPM(srcPixmap); | |
| 561 | |
| 562 int prevW = width; | |
| 563 int prevH = height; | |
| 564 for (int i = 0; i < countLevels; ++i) { | |
| 565 width >>= 1; | |
| 566 height >>= 1; | |
| 567 rowBytes = SkToU32(SkColorTypeMinRowBytes(ct, width)); | |
| 568 | |
| 569 levels[i].fPixels = addr; | |
| 570 levels[i].fWidth = width; | |
| 571 levels[i].fHeight = height; | |
| 572 levels[i].fRowBytes = rowBytes; | |
| 573 levels[i].fScale = (float)width / src.width(); | |
| 574 | |
| 575 SkPixmap dstPM(SkImageInfo::Make(width, height, ct, at), addr, rowBytes) ; | |
| 576 | |
| 577 const size_t pixelSize = srcPM.info().bytesPerPixel(); | |
| 578 | |
| 579 const void* srcBasePtr = srcPM.addr(); | |
| 580 void* dstBasePtr = dstPM.writable_addr(); | |
| 581 | |
| 582 SkDownSampleProc* proc; | |
| 583 if (prevH & 1) { // src-height is 3 | |
| 584 if (prevW & 1) { // src-width is 3 | |
| 585 proc = proc3x3; | |
| 586 } else { // src-width is 2 | |
| 587 proc = proc2x3; | |
| 588 } | |
| 589 } else { // src-height is 2 | |
| 590 if (prevW & 1) { // src-width is 3 | |
| 591 proc = proc3x2; | |
| 592 } else { // src-width is 2 | |
| 593 proc = proc2x2; | |
| 594 } | |
| 595 } | |
| 596 | |
| 597 const size_t srcRB = srcPM.rowBytes(); | |
| 598 for (int y = 0; y < height; y++) { | |
| 599 const void* srcPtr = srcBasePtr; | |
| 600 void* dstPtr = dstBasePtr; | |
| 601 | |
| 602 for (int x = 0; x < width; x++) { | |
| 603 proc(dstPtr, srcPtr, srcRB); | |
| 604 srcPtr = (char*)srcPtr + pixelSize * 2; | |
| 605 dstPtr = (char*)dstPtr + pixelSize; | |
| 606 } | |
| 607 | |
| 608 srcBasePtr = (char*)srcBasePtr + srcRB * 2; // jump two rows | |
| 609 dstBasePtr = (char*)dstBasePtr + dstPM.rowBytes(); | |
| 610 } | |
| 611 srcPM = dstPM; | |
| 612 addr += height * rowBytes; | |
| 613 prevW = width; | |
| 614 prevH = height; | |
| 615 } | |
| 616 SkASSERT(addr == baseAddr + size); | |
| 617 | |
| 618 return mipmap; | |
| 619 } | |
| 620 #endif | |
| 621 | |
| 321 /////////////////////////////////////////////////////////////////////////////// | 622 /////////////////////////////////////////////////////////////////////////////// |
| 322 | 623 |
| 323 bool SkMipMap::extractLevel(SkScalar scale, Level* levelPtr) const { | 624 bool SkMipMap::extractLevel(SkScalar scale, Level* levelPtr) const { |
| 324 if (nullptr == fLevels) { | 625 if (nullptr == fLevels) { |
| 325 return false; | 626 return false; |
| 326 } | 627 } |
| 327 | 628 |
| 328 if (scale >= SK_Scalar1 || scale <= 0 || !SkScalarIsFinite(scale)) { | 629 if (scale >= SK_Scalar1 || scale <= 0 || !SkScalarIsFinite(scale)) { |
| 329 return false; | 630 return false; |
| 330 } | 631 } |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 344 } | 645 } |
| 345 | 646 |
| 346 if (level > fCount) { | 647 if (level > fCount) { |
| 347 level = fCount; | 648 level = fCount; |
| 348 } | 649 } |
| 349 if (levelPtr) { | 650 if (levelPtr) { |
| 350 *levelPtr = fLevels[level - 1]; | 651 *levelPtr = fLevels[level - 1]; |
| 351 } | 652 } |
| 352 return true; | 653 return true; |
| 353 } | 654 } |
| OLD | NEW |