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 |