From f53903b14ef5bb60cae4fdcdcf5e2c6a93505bf8 Mon Sep 17 00:00:00 2001 From: panpf Date: Mon, 14 Oct 2024 16:26:43 +0800 Subject: [PATCH] fix: Fixed the bug that Transformations such as blur and rotate on the Android platform did not keep the ColorSpace unchanged. (#213) --- CHANGELOG.md | 2 + CHANGELOG_zh.md | 2 + .../core/android/test/BitmapAndroidTest.kt | 114 +++++ .../android/test/util/BitmapsAndroidTest.kt | 407 ++++++++++++----- .../com/github/panpf/sketch/Bitmap.android.kt | 40 +- .../panpf/sketch/util/bitmaps.android.kt | 64 +-- .../panpf/sketch/util/bitmaps.common.kt | 2 - .../panpf/sketch/util/bitmaps.nonAndroid.kt | 1 - .../test/util/BitmapsNonAndroidTest.kt | 431 ++++++++++++------ 9 files changed, 752 insertions(+), 311 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53cb301168..5172db6d18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ Translations: [简体中文](CHANGELOG_zh.md) expected. [#210](https://github.com/panpf/sketch/issues/210) * fix: Fix the bug that the filterQuality parameter of AsyncImage is invalid. [#211](https://github.com/panpf/sketch/issues/211) +* fix: Fixed the bug that Transformations such as blur and rotate on the Android platform did not + keep the ColorSpace unchanged. [#213](https://github.com/panpf/sketch/issues/211) * remove: Remove ComposeBitmapImage * remove: Remove Image.getPixels() * change: SkiaBitmapImage is now cached in the memory cache on non-Android platforms, not diff --git a/CHANGELOG_zh.md b/CHANGELOG_zh.md index 82f4c733aa..d722cd2159 100644 --- a/CHANGELOG_zh.md +++ b/CHANGELOG_zh.md @@ -16,6 +16,8 @@ 的 equals 方法未按预期执行的 bug。 [#210](https://github.com/panpf/sketch/issues/210) * fix: 修复 AsyncImage 的 filterQuality 参数无效的 bug。 [#211](https://github.com/panpf/sketch/issues/211) +* fix: 修复 Android 平台上 blur、rotate 等 Transformation 没有保持 ColorSpace 不变的 + bug。 [#213](https://github.com/panpf/sketch/issues/211) * remove: 移除 ComposeBitmapImage * remove: 删除 Image.getPixels() * change: 现在非安卓平台上内存缓存中缓存的是 SkiaBitmapImage,不再是 ComposeBitmapImage diff --git a/sketch-core/src/androidInstrumentedTest/kotlin/com/github/panpf/sketch/core/android/test/BitmapAndroidTest.kt b/sketch-core/src/androidInstrumentedTest/kotlin/com/github/panpf/sketch/core/android/test/BitmapAndroidTest.kt index 9d394085cd..4a8bc232a2 100644 --- a/sketch-core/src/androidInstrumentedTest/kotlin/com/github/panpf/sketch/core/android/test/BitmapAndroidTest.kt +++ b/sketch-core/src/androidInstrumentedTest/kotlin/com/github/panpf/sketch/core/android/test/BitmapAndroidTest.kt @@ -1,11 +1,14 @@ package com.github.panpf.sketch.core.android.test +import android.graphics.Bitmap import android.graphics.Bitmap.Config.ARGB_8888 import android.graphics.Bitmap.Config.RGB_565 import android.graphics.ColorSpace +import android.os.Build import com.github.panpf.sketch.ColorType import com.github.panpf.sketch.colorType import com.github.panpf.sketch.createBitmap +import com.github.panpf.sketch.createEmptyBitmapWith import com.github.panpf.sketch.images.ResourceImages import com.github.panpf.sketch.isImmutable import com.github.panpf.sketch.size @@ -119,4 +122,115 @@ class BitmapAndroidTest { ) } } + + @Test + fun testCreateEmptyBitmapWith() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + Bitmap.createBitmap( + /* width = */ 101, + /* height = */ 202, + /* config = */ Bitmap.Config.ARGB_8888, + /* hasAlpha = */ true, + /* colorSpace = */ ColorSpace.get(ColorSpace.Named.SRGB) + ).apply { + assertEquals(expected = 101, actual = width) + assertEquals(expected = 202, actual = height) + assertEquals(expected = ColorType.ARGB_8888, actual = config) + assertEquals(expected = true, actual = hasAlpha()) + assertEquals(expected = ColorSpace.get(ColorSpace.Named.SRGB), actual = colorSpace) + }.createEmptyBitmapWith( + width = 202, + height = 101, + colorType = RGB_565, + hasAlpha = false + ).apply { + assertEquals(expected = 202, actual = width) + assertEquals(expected = 101, actual = height) + assertEquals(expected = ColorType.RGB_565, actual = config) + assertEquals(expected = false, actual = hasAlpha()) + assertEquals(expected = ColorSpace.get(ColorSpace.Named.SRGB), actual = colorSpace) + }.createEmptyBitmapWith( + size = Size(100, 100), + colorType = RGB_565, + hasAlpha = false + ).apply { + assertEquals(expected = 100, actual = width) + assertEquals(expected = 100, actual = height) + assertEquals(expected = ColorType.RGB_565, actual = config) + assertEquals(expected = false, actual = hasAlpha()) + assertEquals(expected = ColorSpace.get(ColorSpace.Named.SRGB), actual = colorSpace) + } + + Bitmap.createBitmap( + /* width = */ 101, + /* height = */ 202, + /* config = */ Bitmap.Config.ARGB_8888, + /* hasAlpha = */ true, + /* colorSpace = */ ColorSpace.get(ColorSpace.Named.DISPLAY_P3) + ).apply { + assertEquals(expected = 101, actual = width) + assertEquals(expected = 202, actual = height) + assertEquals(expected = ColorType.ARGB_8888, actual = config) + assertEquals(expected = true, actual = hasAlpha()) + assertEquals( + expected = ColorSpace.get(ColorSpace.Named.DISPLAY_P3), + actual = colorSpace + ) + }.createEmptyBitmapWith( + width = 202, + height = 101, + colorType = RGB_565, + hasAlpha = false + ).apply { + assertEquals(expected = 202, actual = width) + assertEquals(expected = 101, actual = height) + assertEquals(expected = ColorType.RGB_565, actual = config) + assertEquals(expected = false, actual = hasAlpha()) + assertEquals( + expected = ColorSpace.get(ColorSpace.Named.DISPLAY_P3), + actual = colorSpace + ) + }.createEmptyBitmapWith( + size = Size(100, 100), + colorType = RGB_565, + hasAlpha = false + ).apply { + assertEquals(expected = 100, actual = width) + assertEquals(expected = 100, actual = height) + assertEquals(expected = ColorType.RGB_565, actual = config) + assertEquals(expected = false, actual = hasAlpha()) + assertEquals( + expected = ColorSpace.get(ColorSpace.Named.DISPLAY_P3), + actual = colorSpace + ) + } + } else { + Bitmap.createBitmap( + /* width = */ 101, + /* height = */ 202, + /* config = */ Bitmap.Config.ARGB_8888, + ).apply { + assertEquals(expected = 101, actual = width) + assertEquals(expected = 202, actual = height) + assertEquals(expected = ColorType.ARGB_8888, actual = config) + }.createEmptyBitmapWith( + width = 202, + height = 101, + colorType = RGB_565, + hasAlpha = false + ).apply { + assertEquals(expected = 202, actual = width) + assertEquals(expected = 101, actual = height) + assertEquals(expected = ColorType.RGB_565, actual = config) + }.createEmptyBitmapWith( + size = Size(100, 100), + colorType = RGB_565, + hasAlpha = false + ).apply { + assertEquals(expected = 100, actual = width) + assertEquals(expected = 100, actual = height) + assertEquals(expected = ColorType.RGB_565, actual = config) + } + } + } } \ No newline at end of file diff --git a/sketch-core/src/androidInstrumentedTest/kotlin/com/github/panpf/sketch/core/android/test/util/BitmapsAndroidTest.kt b/sketch-core/src/androidInstrumentedTest/kotlin/com/github/panpf/sketch/core/android/test/util/BitmapsAndroidTest.kt index bb1c9a6b84..3102a5aa1c 100644 --- a/sketch-core/src/androidInstrumentedTest/kotlin/com/github/panpf/sketch/core/android/test/util/BitmapsAndroidTest.kt +++ b/sketch-core/src/androidInstrumentedTest/kotlin/com/github/panpf/sketch/core/android/test/util/BitmapsAndroidTest.kt @@ -18,9 +18,11 @@ package com.github.panpf.sketch.core.android.test.util import android.graphics.Bitmap import android.graphics.Bitmap.Config.RGB_565 +import android.graphics.Color import android.graphics.ColorSpace import android.os.Build.VERSION import android.os.Build.VERSION_CODES +import androidx.core.graphics.ColorUtils import androidx.test.ext.junit.runners.AndroidJUnit4 import com.github.panpf.sketch.ColorType import com.github.panpf.sketch.createBitmap @@ -45,6 +47,7 @@ import com.github.panpf.sketch.test.utils.decode import com.github.panpf.sketch.test.utils.getTestContext import com.github.panpf.sketch.test.utils.hammingDistance import com.github.panpf.sketch.test.utils.produceFingerPrint +import com.github.panpf.sketch.test.utils.runBlock import com.github.panpf.sketch.test.utils.shortInfoColorSpace import com.github.panpf.sketch.test.utils.similarity import com.github.panpf.sketch.util.Rect @@ -76,6 +79,7 @@ import com.github.panpf.sketch.util.toHexString import com.github.panpf.sketch.util.toInfoString import com.github.panpf.sketch.util.toLogString import com.github.panpf.sketch.util.toShortInfoString +import kotlinx.coroutines.test.runTest import org.junit.runner.RunWith import kotlin.math.max import kotlin.math.roundToInt @@ -987,111 +991,127 @@ class BitmapsAndroidTest { @Test @Suppress("UNUSED_VARIABLE") - fun testBackground() { - val sourceBitmapFinger: String - val sourceBitmapCorners: List - val sourceBitmap = ResourceImages.jpeg.decode().bitmap.apply { - assertEquals( - expected = "Bitmap(1291x1936,ARGB_8888${shortInfoColorSpace("SRGB")})", - actual = toShortInfoString() - ) - sourceBitmapFinger = this.produceFingerPrint() - sourceBitmapCorners = corners() - } + fun testBackground() = runTest { + runBlock { + val sourceBitmapFinger: String + val sourceBitmapCorners: List + val sourceBitmap = ResourceImages.jpeg.decode().bitmap.apply { + assertEquals( + expected = "Bitmap(1291x1936,ARGB_8888${shortInfoColorSpace("SRGB")})", + actual = toShortInfoString() + ) + sourceBitmapFinger = this.produceFingerPrint() + sourceBitmapCorners = corners() + } - val redBgBitmapFinger: String - val redBgBitmapCorners: List - val redBgBitmap = sourceBitmap.background(TestColor.RED).apply { - assertEquals( - expected = "Bitmap(1291x1936,ARGB_8888${shortInfoColorSpace("SRGB")})", - actual = toShortInfoString() - ) - redBgBitmapFinger = this.produceFingerPrint() - redBgBitmapCorners = corners() - } + val redBgBitmapFinger: String + val redBgBitmapCorners: List + val redBgBitmap = sourceBitmap.background(TestColor.RED).apply { + assertEquals( + expected = "Bitmap(1291x1936,ARGB_8888${shortInfoColorSpace("SRGB")})", + actual = toShortInfoString() + ) + redBgBitmapFinger = this.produceFingerPrint() + redBgBitmapCorners = corners() + } - val blueBgBitmapFinger: String - val blueBgBitmapCorners: List - val blueBgBitmap = sourceBitmap.background(TestColor.BLUE).apply { - assertEquals( - expected = "Bitmap(1291x1936,ARGB_8888${shortInfoColorSpace("SRGB")})", - actual = toShortInfoString() - ) - blueBgBitmapFinger = this.produceFingerPrint() - blueBgBitmapCorners = corners() - } + val blueBgBitmapFinger: String + val blueBgBitmapCorners: List + val blueBgBitmap = sourceBitmap.background(TestColor.BLUE).apply { + assertEquals( + expected = "Bitmap(1291x1936,ARGB_8888${shortInfoColorSpace("SRGB")})", + actual = toShortInfoString() + ) + blueBgBitmapFinger = this.produceFingerPrint() + blueBgBitmapCorners = corners() + } - assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = sourceBitmapCorners) - assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = redBgBitmapCorners) - assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = blueBgBitmapCorners) + assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = sourceBitmapCorners) + assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = redBgBitmapCorners) + assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = blueBgBitmapCorners) - assertEquals(expected = sourceBitmapCorners, actual = redBgBitmapCorners) - assertEquals(expected = sourceBitmapCorners, actual = blueBgBitmapCorners) - assertEquals(expected = redBgBitmapCorners, actual = blueBgBitmapCorners) + assertEquals(expected = sourceBitmapCorners, actual = redBgBitmapCorners) + assertEquals(expected = sourceBitmapCorners, actual = blueBgBitmapCorners) + assertEquals(expected = redBgBitmapCorners, actual = blueBgBitmapCorners) - // Fingerprints ignore color, so it's all the same - assertEquals(expected = sourceBitmapFinger, actual = redBgBitmapFinger) - assertEquals(expected = sourceBitmapFinger, actual = blueBgBitmapFinger) - assertEquals(expected = redBgBitmapFinger, actual = blueBgBitmapFinger) - } + // Fingerprints ignore color, so it's all the same + assertEquals(expected = sourceBitmapFinger, actual = redBgBitmapFinger) + assertEquals(expected = sourceBitmapFinger, actual = blueBgBitmapFinger) + assertEquals(expected = redBgBitmapFinger, actual = blueBgBitmapFinger) + } - @Test - @Suppress("UNUSED_VARIABLE") - fun testBackground2() { - val sourceBitmapFinger: String - val sourceBitmapCorners: List - val sourceBitmap = - ResourceImages.png.decode().bitmap.apply { + runBlock { + val sourceBitmapFinger: String + val sourceBitmapCorners: List + val sourceBitmap = + ResourceImages.png.decode().bitmap.apply { + assertEquals( + expected = "Bitmap(750x719,ARGB_8888${shortInfoColorSpace("SRGB")})", + actual = toShortInfoString() + ) + sourceBitmapFinger = this.produceFingerPrint() + sourceBitmapCorners = corners() + } + + val redBgBitmapFinger: String + val redBgBitmapCorners: List + val redBgBitmap = sourceBitmap.background(TestColor.RED).apply { assertEquals( expected = "Bitmap(750x719,ARGB_8888${shortInfoColorSpace("SRGB")})", actual = toShortInfoString() ) - sourceBitmapFinger = this.produceFingerPrint() - sourceBitmapCorners = corners() + redBgBitmapFinger = this.produceFingerPrint() + redBgBitmapCorners = corners() } - val redBgBitmapFinger: String - val redBgBitmapCorners: List - val redBgBitmap = sourceBitmap.background(TestColor.RED).apply { - assertEquals( - expected = "Bitmap(750x719,ARGB_8888${shortInfoColorSpace("SRGB")})", - actual = toShortInfoString() + val blueBgBitmapFinger: String + val blueBgBitmapCorners: List + val blueBgBitmap = sourceBitmap.background(TestColor.BLUE).apply { + assertEquals( + expected = "Bitmap(750x719,ARGB_8888${shortInfoColorSpace("SRGB")})", + actual = toShortInfoString() + ) + blueBgBitmapFinger = this.produceFingerPrint() + blueBgBitmapCorners = corners() + } + + assertEquals(expected = listOf(0, 0, 0, 0), actual = sourceBitmapCorners) + assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = redBgBitmapCorners) + assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = blueBgBitmapCorners) + + assertNotEquals(illegal = sourceBitmapCorners, actual = redBgBitmapCorners) + assertNotEquals(illegal = sourceBitmapCorners, actual = blueBgBitmapCorners) + assertNotEquals(illegal = redBgBitmapCorners, actual = blueBgBitmapCorners) + + assertTrue( + actual = hammingDistance(sourceBitmapFinger, redBgBitmapFinger) < 5, + message = hammingDistance(sourceBitmapFinger, redBgBitmapFinger).toString() + ) + assertTrue( + actual = hammingDistance(sourceBitmapFinger, blueBgBitmapFinger) < 5, + message = hammingDistance(sourceBitmapFinger, blueBgBitmapFinger).toString() + ) + assertTrue( + actual = hammingDistance(redBgBitmapFinger, blueBgBitmapFinger) < 5, + message = hammingDistance(redBgBitmapFinger, blueBgBitmapFinger).toString() ) - redBgBitmapFinger = this.produceFingerPrint() - redBgBitmapCorners = corners() } - val blueBgBitmapFinger: String - val blueBgBitmapCorners: List - val blueBgBitmap = sourceBitmap.background(TestColor.BLUE).apply { + // colorType, colorSpace + ResourceImages.jpeg.decode( + colorType = BitmapColorType(colorType = RGB_565), + colorSpace = "DISPLAY_P3" + ).bitmap.apply { assertEquals( - expected = "Bitmap(750x719,ARGB_8888${shortInfoColorSpace("SRGB")})", + expected = "Bitmap(1291x1936,RGB_565${shortInfoColorSpace("DISPLAY_P3")})", + actual = toShortInfoString() + ) + }.background(Color.RED).apply { + assertEquals( + expected = "Bitmap(1291x1936,RGB_565${shortInfoColorSpace("DISPLAY_P3")})", actual = toShortInfoString() ) - blueBgBitmapFinger = this.produceFingerPrint() - blueBgBitmapCorners = corners() } - - assertEquals(expected = listOf(0, 0, 0, 0), actual = sourceBitmapCorners) - assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = redBgBitmapCorners) - assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = blueBgBitmapCorners) - - assertNotEquals(illegal = sourceBitmapCorners, actual = redBgBitmapCorners) - assertNotEquals(illegal = sourceBitmapCorners, actual = blueBgBitmapCorners) - assertNotEquals(illegal = redBgBitmapCorners, actual = blueBgBitmapCorners) - - assertTrue( - actual = hammingDistance(sourceBitmapFinger, redBgBitmapFinger) < 5, - message = hammingDistance(sourceBitmapFinger, redBgBitmapFinger).toString() - ) - assertTrue( - actual = hammingDistance(sourceBitmapFinger, blueBgBitmapFinger) < 5, - message = hammingDistance(sourceBitmapFinger, blueBgBitmapFinger).toString() - ) - assertTrue( - actual = hammingDistance(redBgBitmapFinger, blueBgBitmapFinger) < 5, - message = hammingDistance(redBgBitmapFinger, blueBgBitmapFinger).toString() - ) } @Test @@ -1171,6 +1191,32 @@ class BitmapsAndroidTest { assertTrue(blur2ImmutableBitmap.isMutable) assertNotSame(immutableBitmap, blur1ImmutableBitmap) assertNotSame(immutableBitmap, blur2ImmutableBitmap) + + // colorType, colorSpace + ResourceImages.jpeg.decode( + colorType = BitmapColorType(colorType = RGB_565), + colorSpace = "DISPLAY_P3" + ).bitmap.mutableCopy().apply { + assertTrue(isMutable) + assertEquals( + expected = "Bitmap(1291x1936,RGB_565${shortInfoColorSpace("DISPLAY_P3")})", + actual = toShortInfoString() + ) + }.also { + it.blur(20, firstReuseSelf = true).apply { + assertEquals( + expected = "Bitmap(1291x1936,RGB_565${shortInfoColorSpace("DISPLAY_P3")})", + actual = toShortInfoString() + ) + } + + it.blur(20, firstReuseSelf = false).apply { + assertEquals( + expected = "Bitmap(1291x1936,RGB_565${shortInfoColorSpace("DISPLAY_P3")})", + actual = toShortInfoString() + ) + } + } } @Test @@ -1258,21 +1304,21 @@ class BitmapsAndroidTest { message = hammingDistance(centerCropBitmapFinger, endCropBitmapFinger).toString() ) - - ResourceImages.jpeg.decode().bitmap.apply { - assertEquals(expected = Bitmap.Config.ARGB_8888, actual = config) - assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = corners()) - }.circleCrop(Scale.CENTER_CROP).apply { - assertEquals(expected = Bitmap.Config.ARGB_8888, actual = config) - assertEquals(expected = listOf(0, 0, 0, 0), actual = corners()) - } - - val bitmapColorType = BitmapColorType(Bitmap.Config.RGB_565) - ResourceImages.jpeg.decode(bitmapColorType).bitmap.apply { - assertEquals(expected = Bitmap.Config.RGB_565, actual = config) + // colorType, colorSpace + ResourceImages.jpeg.decode( + colorType = BitmapColorType(colorType = RGB_565), + colorSpace = "DISPLAY_P3" + ).bitmap.apply { + assertEquals( + expected = "Bitmap(1291x1936,RGB_565${shortInfoColorSpace("DISPLAY_P3")})", + actual = toShortInfoString() + ) assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = corners()) }.circleCrop(Scale.CENTER_CROP).apply { - assertEquals(expected = Bitmap.Config.ARGB_8888, actual = config) + assertEquals( + expected = "Bitmap(1291x1291,ARGB_8888${shortInfoColorSpace("DISPLAY_P3")})", + actual = toShortInfoString() + ) assertEquals(expected = listOf(0, 0, 0, 0), actual = corners()) } } @@ -1360,6 +1406,22 @@ class BitmapsAndroidTest { verFlippedBitmap.cornerA, ) ) + + // colorType, colorSpace + ResourceImages.jpeg.decode( + colorType = BitmapColorType(colorType = RGB_565), + colorSpace = "DISPLAY_P3" + ).bitmap.apply { + assertEquals( + expected = "Bitmap(1291x1936,RGB_565${shortInfoColorSpace("DISPLAY_P3")})", + actual = toShortInfoString() + ) + }.flip(horizontal = true).apply { + assertEquals( + expected = "Bitmap(1291x1936,RGB_565${shortInfoColorSpace("DISPLAY_P3")})", + actual = toShortInfoString() + ) + } } @Test @@ -1488,6 +1550,31 @@ class BitmapsAndroidTest { actual = hammingDistance(resize3BitmapFinger, resize4BitmapFinger) >= 5, message = hammingDistance(resize3BitmapFinger, resize4BitmapFinger).toString() ) + + // colorType, colorSpace + ResourceImages.jpeg.decode( + colorType = BitmapColorType(colorType = RGB_565), + colorSpace = "DISPLAY_P3" + ).bitmap.apply { + assertEquals( + expected = "Bitmap(1291x1936,RGB_565${shortInfoColorSpace("DISPLAY_P3")})", + actual = toShortInfoString() + ) + }.let { + it.mapping( + mapping = Resize( + width = 300, + height = 300, + precision = Precision.EXACTLY, + scale = Scale.CENTER_CROP + ).calculateMapping(it.size) + ) + }.apply { + assertEquals( + expected = "Bitmap(300x300,RGB_565${shortInfoColorSpace("DISPLAY_P3")})", + actual = toShortInfoString() + ) + } } @Test @@ -1556,6 +1643,32 @@ class BitmapsAndroidTest { assertTrue(mask2ImmutableBitmap.isMutable) assertNotSame(immutableBitmap, mask1ImmutableBitmap) assertNotSame(immutableBitmap, mask2ImmutableBitmap) + + // colorType, colorSpace + ResourceImages.jpeg.decode( + colorType = BitmapColorType(colorType = RGB_565), + colorSpace = "DISPLAY_P3" + ).bitmap.mutableCopy().apply { + assertTrue(isMutable) + assertEquals( + expected = "Bitmap(1291x1936,RGB_565${shortInfoColorSpace("DISPLAY_P3")})", + actual = toShortInfoString() + ) + }.also { + it.mask(ColorUtils.setAlphaComponent(Color.RED, 100), firstReuseSelf = true).apply { + assertEquals( + expected = "Bitmap(1291x1936,RGB_565${shortInfoColorSpace("DISPLAY_P3")})", + actual = toShortInfoString() + ) + } + + it.mask(ColorUtils.setAlphaComponent(Color.RED, 100), firstReuseSelf = false).apply { + assertEquals( + expected = "Bitmap(1291x1936,RGB_565${shortInfoColorSpace("DISPLAY_P3")})", + actual = toShortInfoString() + ) + } + } } @Test @@ -1671,22 +1784,32 @@ class BitmapsAndroidTest { message = hammingDistance(rotate270BitmapFinger, rotate360BitmapFinger).toString() ) - - ResourceImages.jpeg.decode().bitmap.apply { - assertEquals(expected = Bitmap.Config.ARGB_8888, actual = config) + // colorType, colorSpace + ResourceImages.jpeg.decode( + colorType = BitmapColorType(colorType = RGB_565), + colorSpace = "DISPLAY_P3" + ).bitmap.apply { + assertEquals( + expected = "Bitmap(1291x1936,RGB_565${shortInfoColorSpace("DISPLAY_P3")})", + actual = toShortInfoString() + ) assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = corners()) - }.rotate(130).apply { - assertEquals(expected = Bitmap.Config.ARGB_8888, actual = config) - assertEquals(expected = listOf(0, 0, 0, 0), actual = corners()) - } + }.also { + it.rotate(90).apply { + assertEquals( + expected = "Bitmap(1936x1291,RGB_565${shortInfoColorSpace("DISPLAY_P3")})", + actual = toShortInfoString() + ) + assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = corners()) + } - val bitmapColorType = BitmapColorType(Bitmap.Config.RGB_565) - ResourceImages.jpeg.decode(bitmapColorType).bitmap.apply { - assertEquals(expected = Bitmap.Config.RGB_565, actual = config) - assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = corners()) - }.rotate(130).apply { - assertEquals(expected = Bitmap.Config.ARGB_8888, actual = config) - assertEquals(expected = listOf(0, 0, 0, 0), actual = corners()) + it.rotate(130).apply { + assertEquals( + expected = "Bitmap(2312x2233,ARGB_8888${shortInfoColorSpace("DISPLAY_P3")})", + actual = toShortInfoString() + ) + assertEquals(expected = listOf(0, 0, 0, 0), actual = corners()) + } } } @@ -1752,21 +1875,21 @@ class BitmapsAndroidTest { actual = bigRoundedCorneredBitmapFinger ) - - ResourceImages.jpeg.decode().bitmap.apply { - assertEquals(expected = Bitmap.Config.ARGB_8888, actual = config) - assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = corners()) - }.roundedCorners(floatArrayOf(20f, 20f, 20f, 20f, 20f, 20f, 20f, 20f)).apply { - assertEquals(expected = Bitmap.Config.ARGB_8888, actual = config) - assertEquals(expected = listOf(0, 0, 0, 0), actual = corners()) - } - - val bitmapColorType = BitmapColorType(Bitmap.Config.RGB_565) - ResourceImages.jpeg.decode(bitmapColorType).bitmap.apply { - assertEquals(expected = Bitmap.Config.RGB_565, actual = config) + // colorType, colorSpace + ResourceImages.jpeg.decode( + colorType = BitmapColorType(colorType = RGB_565), + colorSpace = "DISPLAY_P3" + ).bitmap.apply { + assertEquals( + expected = "Bitmap(1291x1936,RGB_565${shortInfoColorSpace("DISPLAY_P3")})", + actual = toShortInfoString() + ) assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = corners()) }.roundedCorners(floatArrayOf(20f, 20f, 20f, 20f, 20f, 20f, 20f, 20f)).apply { - assertEquals(expected = Bitmap.Config.ARGB_8888, actual = config) + assertEquals( + expected = "Bitmap(1291x1936,ARGB_8888${shortInfoColorSpace("DISPLAY_P3")})", + actual = toShortInfoString() + ) assertEquals(expected = listOf(0, 0, 0, 0), actual = corners()) } } @@ -1791,6 +1914,22 @@ class BitmapsAndroidTest { toShortInfoString() ) } + + // colorType, colorSpace + ResourceImages.jpeg.decode( + colorType = BitmapColorType(colorType = RGB_565), + colorSpace = "DISPLAY_P3" + ).bitmap.apply { + assertEquals( + expected = "Bitmap(1291x1936,RGB_565${shortInfoColorSpace("DISPLAY_P3")})", + actual = toShortInfoString() + ) + }.scale(1.5f).apply { + assertEquals( + "Bitmap(1937x2904,RGB_565${shortInfoColorSpace("DISPLAY_P3")})", + toShortInfoString() + ) + } } @Test @@ -1808,5 +1947,21 @@ class BitmapsAndroidTest { ) } assertEquals(expected = 0, actual = bitmap.similarity(thumbnailBitmap)) + + // colorType, colorSpace + ResourceImages.jpeg.decode( + colorType = BitmapColorType(colorType = RGB_565), + colorSpace = "DISPLAY_P3" + ).bitmap.apply { + assertEquals( + expected = "Bitmap(1291x1936,RGB_565${shortInfoColorSpace("DISPLAY_P3")})", + actual = toShortInfoString() + ) + }.thumbnail(100, 100).apply { + assertEquals( + "Bitmap(100x100,RGB_565${shortInfoColorSpace("DISPLAY_P3")})", + toShortInfoString() + ) + } } } \ No newline at end of file diff --git a/sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/Bitmap.android.kt b/sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/Bitmap.android.kt index 487e3f98c1..2035834b9b 100644 --- a/sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/Bitmap.android.kt +++ b/sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/Bitmap.android.kt @@ -21,6 +21,7 @@ package com.github.panpf.sketch import android.graphics.ColorSpace import android.os.Build import androidx.annotation.RequiresApi +import com.github.panpf.sketch.util.Size import com.github.panpf.sketch.util.configOrNull /** @@ -113,4 +114,41 @@ fun createBitmap( config: ColorType = ColorType.ARGB_8888, hasAlpha: Boolean, colorSpace: ColorSpace -): Bitmap = Bitmap.createBitmap(width, height, config, hasAlpha, colorSpace) \ No newline at end of file +): Bitmap = Bitmap.createBitmap(width, height, config, hasAlpha, colorSpace) + +/** + * Create a blank Bitmap based on the width, height, transparency, and color type of the original Bitmap + * + * @see com.github.panpf.sketch.core.android.test.BitmapAndroidTest.testCreateEmptyBitmapWith + */ +fun Bitmap.createEmptyBitmapWith( + width: Int = this.width, + height: Int = this.height, + colorType: ColorType = this.colorType ?: ColorType.ARGB_8888, + hasAlpha: Boolean = this.hasAlpha(), +): Bitmap = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + Bitmap.createBitmap( + /* width = */ width, + /* height = */ height, + /* config = */ colorType, + /* hasAlpha = */ hasAlpha, + /* colorSpace = */ this.colorSpace ?: ColorSpace.get(ColorSpace.Named.SRGB) + ) +} else { + Bitmap.createBitmap( + /* width = */ width, + /* height = */ height, + /* config = */ colorType, + ) +} + +/** + * Create a blank Bitmap based on the width, height, transparency, and color type of the original Bitmap + * + * @see com.github.panpf.sketch.core.android.test.BitmapAndroidTest.testCreateEmptyBitmapWith + */ +fun Bitmap.createEmptyBitmapWith( + size: Size = this.size, + colorType: ColorType = this.colorType ?: ColorType.ARGB_8888, + hasAlpha: Boolean = this.hasAlpha(), +): Bitmap = createEmptyBitmapWith(size.width, size.height, colorType, hasAlpha) diff --git a/sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/util/bitmaps.android.kt b/sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/util/bitmaps.android.kt index 98ae657d35..d2b3fba301 100644 --- a/sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/util/bitmaps.android.kt +++ b/sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/util/bitmaps.android.kt @@ -33,6 +33,7 @@ import android.os.Build.VERSION_CODES import androidx.annotation.RequiresApi import com.github.panpf.sketch.Bitmap import com.github.panpf.sketch.ColorType +import com.github.panpf.sketch.createEmptyBitmapWith import com.github.panpf.sketch.resize.Precision.SAME_ASPECT_RATIO import com.github.panpf.sketch.resize.Resize import com.github.panpf.sketch.resize.ResizeMapping @@ -261,11 +262,10 @@ actual fun Bitmap.readIntPixel(x: Int, y: Int): Int = getPixel(x, y) * Add a background color to the current Bitmap * * @see com.github.panpf.sketch.core.android.test.util.BitmapsAndroidTest.testBackground - * @see com.github.panpf.sketch.core.android.test.util.BitmapsAndroidTest.testBackground2 */ actual fun Bitmap.background(color: Int): Bitmap { val inputBitmap = this - val bitmap = Bitmap.createBitmap( + val bitmap = inputBitmap.createEmptyBitmapWith( /* width = */ inputBitmap.width, /* height = */ inputBitmap.height, /* config = */ inputBitmap.safeConfig.safeToSoftware(), @@ -283,8 +283,8 @@ actual fun Bitmap.background(color: Int): Bitmap { */ actual fun Bitmap.blur(radius: Int, firstReuseSelf: Boolean): Bitmap { val inputBitmap = this - val outBitmap = - if (firstReuseSelf) inputBitmap.mutableCopyOrSelf() else inputBitmap.mutableCopy() + val outBitmap = if (firstReuseSelf) + inputBitmap.mutableCopyOrSelf() else inputBitmap.mutableCopy() val imageWidth = outBitmap.width val imageHeight = outBitmap.height val pixels = IntArray(imageWidth * imageHeight) @@ -323,10 +323,11 @@ actual fun Bitmap.circleCrop(scale: Scale): Bitmap { // Circle cropped require support alpha newConfig = ColorType.ARGB_8888 } - val outBitmap = Bitmap.createBitmap( - /* width = */ newSize, - /* height = */ newSize, - /* config = */ newConfig, + val outBitmap = inputBitmap.createEmptyBitmapWith( + width = newSize, + height = newSize, + colorType = newConfig, + hasAlpha = true ) val paint = Paint().apply { isAntiAlias = true @@ -387,12 +388,11 @@ actual fun Bitmap.flip(horizontal: Boolean): Bitmap { */ actual fun Bitmap.mapping(mapping: ResizeMapping): Bitmap { val inputBitmap = this - val outBitmap = Bitmap.createBitmap( - /* width = */ mapping.newSize.width, - /* height = */ mapping.newSize.height, - /* config = */ inputBitmap.safeConfig.safeToSoftware(), + val newConfig = inputBitmap.safeConfig.safeToSoftware() + val outBitmap = inputBitmap.createEmptyBitmapWith( + size = mapping.newSize, + colorType = newConfig ) - // TODO keep ColorSpace Canvas(outBitmap).drawBitmap( /* bitmap = */ inputBitmap, /* src = */ mapping.srcRect.toAndroidRect(), @@ -409,8 +409,8 @@ actual fun Bitmap.mapping(mapping: ResizeMapping): Bitmap { */ actual fun Bitmap.mask(maskColor: Int, firstReuseSelf: Boolean): Bitmap { val inputBitmap = this - val outBitmap = - if (firstReuseSelf) inputBitmap.mutableCopyOrSelf() else inputBitmap.mutableCopy() + val outBitmap = if (firstReuseSelf) + inputBitmap.mutableCopyOrSelf() else inputBitmap.mutableCopy() val canvas = Canvas(outBitmap) val paint = Paint().apply { color = maskColor @@ -453,10 +453,11 @@ actual fun Bitmap.rotate(angle: Int): Bitmap { // Non-positive angle require support alpha newConfig = ColorType.ARGB_8888 } - val outBitmap = Bitmap.createBitmap( - /* width = */ newWidth, - /* height = */ newHeight, - /* config = */ newConfig, + val outBitmap = inputBitmap.createEmptyBitmapWith( + width = newWidth, + height = newHeight, + colorType = newConfig, + hasAlpha = true ) matrix.postTranslate(-newRect.left, -newRect.top) @@ -480,16 +481,17 @@ actual fun Bitmap.roundedCorners(radiusArray: FloatArray): Bitmap { // Rounded corners require support alpha newConfig = ColorType.ARGB_8888 } - val newBitmap = Bitmap.createBitmap( - /* width = */ inputBitmap.width, - /* height = */ inputBitmap.height, - /* config = */ newConfig, + val outBitmap = inputBitmap.createEmptyBitmapWith( + width = inputBitmap.width, + height = inputBitmap.height, + colorType = newConfig, + hasAlpha = true ) val paint = Paint().apply { isAntiAlias = true color = Color.BLACK } - val canvas = Canvas(newBitmap).apply { + val canvas = Canvas(outBitmap).apply { drawARGB(0, 0, 0, 0) } val path = Path().apply { @@ -506,7 +508,7 @@ actual fun Bitmap.roundedCorners(radiusArray: FloatArray): Bitmap { paint.xfermode = PorterDuffXfermode(SRC_IN) val rect = Rect(0, 0, inputBitmap.width, inputBitmap.height) canvas.drawBitmap(inputBitmap, rect, rect, paint) - return newBitmap + return outBitmap } /** @@ -518,17 +520,17 @@ actual fun Bitmap.scale(scaleFactor: Float): Bitmap { val scaledWidth = ceil(width * scaleFactor).toInt() val scaledHeight = ceil(height * scaleFactor).toInt() val newConfig = this.safeConfig.safeToSoftware() - val newBitmap = Bitmap.createBitmap( - /* width = */ scaledWidth, - /* height = */ scaledHeight, - /* config = */ newConfig, + val outputBitmap = this.createEmptyBitmapWith( + width = scaledWidth, + height = scaledHeight, + colorType = newConfig, ) - val canvas = Canvas(newBitmap) + val canvas = Canvas(outputBitmap) val matrix = Matrix().apply { postScale(scaleFactor, scaleFactor) } canvas.drawBitmap(this, matrix, null) - return newBitmap + return outputBitmap } /** diff --git a/sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/bitmaps.common.kt b/sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/bitmaps.common.kt index df57696b28..2e65bce221 100644 --- a/sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/bitmaps.common.kt +++ b/sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/bitmaps.common.kt @@ -79,9 +79,7 @@ expect fun Bitmap.readIntPixel(x: Int, y: Int): Int * Add a background color to the current Bitmap * * @see com.github.panpf.sketch.core.android.test.util.BitmapsAndroidTest.testBackground - * @see com.github.panpf.sketch.core.android.test.util.BitmapsAndroidTest.testBackground2 * @see com.github.panpf.sketch.core.nonandroid.test.util.BitmapsNonAndroidTest.testBackground - * @see com.github.panpf.sketch.core.nonandroid.test.util.BitmapsNonAndroidTest.testBackground2 */ expect fun Bitmap.background(color: Int): Bitmap diff --git a/sketch-core/src/nonAndroidMain/kotlin/com/github/panpf/sketch/util/bitmaps.nonAndroid.kt b/sketch-core/src/nonAndroidMain/kotlin/com/github/panpf/sketch/util/bitmaps.nonAndroid.kt index 861b8a635f..6ebc5e0116 100644 --- a/sketch-core/src/nonAndroidMain/kotlin/com/github/panpf/sketch/util/bitmaps.nonAndroid.kt +++ b/sketch-core/src/nonAndroidMain/kotlin/com/github/panpf/sketch/util/bitmaps.nonAndroid.kt @@ -225,7 +225,6 @@ actual fun Bitmap.readIntPixel(x: Int, y: Int): Int { * Returns a new Bitmap that is a copy of this Bitmap with a background color. * * @see com.github.panpf.sketch.core.nonandroid.test.util.BitmapsNonAndroidTest.testBackground - * @see com.github.panpf.sketch.core.nonandroid.test.util.BitmapsNonAndroidTest.testBackground2 */ actual fun Bitmap.background(color: Int): Bitmap { val inputBitmap = this diff --git a/sketch-core/src/nonAndroidTest/kotlin/com/github/panpf/sketch/core/nonandroid/test/util/BitmapsNonAndroidTest.kt b/sketch-core/src/nonAndroidTest/kotlin/com/github/panpf/sketch/core/nonandroid/test/util/BitmapsNonAndroidTest.kt index 3c683098e5..e8d81637c0 100644 --- a/sketch-core/src/nonAndroidTest/kotlin/com/github/panpf/sketch/core/nonandroid/test/util/BitmapsNonAndroidTest.kt +++ b/sketch-core/src/nonAndroidTest/kotlin/com/github/panpf/sketch/core/nonandroid/test/util/BitmapsNonAndroidTest.kt @@ -20,6 +20,7 @@ import com.github.panpf.sketch.test.utils.corners import com.github.panpf.sketch.test.utils.decode import com.github.panpf.sketch.test.utils.hammingDistance import com.github.panpf.sketch.test.utils.produceFingerPrint +import com.github.panpf.sketch.test.utils.runBlock import com.github.panpf.sketch.test.utils.similarity import com.github.panpf.sketch.util.Rect import com.github.panpf.sketch.util.Size @@ -44,6 +45,8 @@ import com.github.panpf.sketch.util.toHexString import com.github.panpf.sketch.util.toInfoString import com.github.panpf.sketch.util.toLogString import com.github.panpf.sketch.util.toShortInfoString +import kotlinx.coroutines.test.runTest +import org.jetbrains.skia.Color import org.jetbrains.skia.ColorAlphaType import org.jetbrains.skia.ColorSpace import org.jetbrains.skia.ColorType @@ -591,110 +594,126 @@ class BitmapsNonAndroidTest { @Test @Suppress("UNUSED_VARIABLE") - fun testBackground() { - val sourceBitmapFinger: String - val sourceBitmapCorners: List - val sourceBitmap = ResourceImages.jpeg.decode().bitmap.apply { - assertEquals( - expected = "Bitmap(1291x1936,RGBA_8888,sRGB)", - actual = toShortInfoString() - ) - sourceBitmapFinger = this.produceFingerPrint() - sourceBitmapCorners = corners() - } + fun testBackground() = runTest { + runBlock { + val sourceBitmapFinger: String + val sourceBitmapCorners: List + val sourceBitmap = ResourceImages.jpeg.decode().bitmap.apply { + assertEquals( + expected = "Bitmap(1291x1936,RGBA_8888,sRGB)", + actual = toShortInfoString() + ) + sourceBitmapFinger = this.produceFingerPrint() + sourceBitmapCorners = corners() + } - val redBgBitmapFinger: String - val redBgBitmapCorners: List - val redBgBitmap = sourceBitmap.background(TestColor.RED).apply { - assertEquals( - expected = "Bitmap(1291x1936,RGBA_8888,sRGB)", - actual = toShortInfoString() - ) - redBgBitmapFinger = this.produceFingerPrint() - redBgBitmapCorners = corners() - } + val redBgBitmapFinger: String + val redBgBitmapCorners: List + val redBgBitmap = sourceBitmap.background(TestColor.RED).apply { + assertEquals( + expected = "Bitmap(1291x1936,RGBA_8888,sRGB)", + actual = toShortInfoString() + ) + redBgBitmapFinger = this.produceFingerPrint() + redBgBitmapCorners = corners() + } - val blueBgBitmapFinger: String - val blueBgBitmapCorners: List - val blueBgBitmap = sourceBitmap.background(TestColor.BLUE).apply { - assertEquals( - expected = "Bitmap(1291x1936,RGBA_8888,sRGB)", - actual = toShortInfoString() - ) - blueBgBitmapFinger = this.produceFingerPrint() - blueBgBitmapCorners = corners() + val blueBgBitmapFinger: String + val blueBgBitmapCorners: List + val blueBgBitmap = sourceBitmap.background(TestColor.BLUE).apply { + assertEquals( + expected = "Bitmap(1291x1936,RGBA_8888,sRGB)", + actual = toShortInfoString() + ) + blueBgBitmapFinger = this.produceFingerPrint() + blueBgBitmapCorners = corners() + } + + assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = sourceBitmapCorners) + assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = redBgBitmapCorners) + assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = blueBgBitmapCorners) + + assertEquals(expected = sourceBitmapCorners, actual = redBgBitmapCorners) + assertEquals(expected = sourceBitmapCorners, actual = blueBgBitmapCorners) + assertEquals(expected = redBgBitmapCorners, actual = blueBgBitmapCorners) + + // Fingerprints ignore color, so it's all the same + assertEquals(expected = sourceBitmapFinger, actual = redBgBitmapFinger) + assertEquals(expected = sourceBitmapFinger, actual = blueBgBitmapFinger) + assertEquals(expected = redBgBitmapFinger, actual = blueBgBitmapFinger) } - assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = sourceBitmapCorners) - assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = redBgBitmapCorners) - assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = blueBgBitmapCorners) + runBlock { + val sourceBitmapFinger: String + val sourceBitmapCorners: List + val sourceBitmap = ResourceImages.png.decode().bitmap.apply { + assertEquals( + expected = "Bitmap(750x719,RGBA_8888,sRGB)", + actual = toShortInfoString() + ) + sourceBitmapFinger = this.produceFingerPrint() + sourceBitmapCorners = corners() + } - assertEquals(expected = sourceBitmapCorners, actual = redBgBitmapCorners) - assertEquals(expected = sourceBitmapCorners, actual = blueBgBitmapCorners) - assertEquals(expected = redBgBitmapCorners, actual = blueBgBitmapCorners) + val redBgBitmapFinger: String + val redBgBitmapCorners: List + val redBgBitmap = sourceBitmap.background(TestColor.RED).apply { + assertEquals( + expected = "Bitmap(750x719,RGBA_8888,sRGB)", + actual = toShortInfoString() + ) + redBgBitmapFinger = this.produceFingerPrint() + redBgBitmapCorners = corners() + } - // Fingerprints ignore color, so it's all the same - assertEquals(expected = sourceBitmapFinger, actual = redBgBitmapFinger) - assertEquals(expected = sourceBitmapFinger, actual = blueBgBitmapFinger) - assertEquals(expected = redBgBitmapFinger, actual = blueBgBitmapFinger) - } + val blueBgBitmapFinger: String + val blueBgBitmapCorners: List + val blueBgBitmap = sourceBitmap.background(TestColor.BLUE).apply { + assertEquals( + expected = "Bitmap(750x719,RGBA_8888,sRGB)", + actual = toShortInfoString() + ) + blueBgBitmapFinger = this.produceFingerPrint() + blueBgBitmapCorners = corners() + } - @Test - @Suppress("UNUSED_VARIABLE") - fun testBackground2() { - val sourceBitmapFinger: String - val sourceBitmapCorners: List - val sourceBitmap = ResourceImages.png.decode().bitmap.apply { - assertEquals( - expected = "Bitmap(750x719,RGBA_8888,sRGB)", - actual = toShortInfoString() + assertEquals(expected = listOf(0, 0, 0, 0), actual = sourceBitmapCorners) + assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = redBgBitmapCorners) + assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = blueBgBitmapCorners) + + assertNotEquals(illegal = sourceBitmapCorners, actual = redBgBitmapCorners) + assertNotEquals(illegal = sourceBitmapCorners, actual = blueBgBitmapCorners) + assertNotEquals(illegal = redBgBitmapCorners, actual = blueBgBitmapCorners) + + assertTrue( + actual = hammingDistance(sourceBitmapFinger, redBgBitmapFinger) < 5, + message = hammingDistance(sourceBitmapFinger, redBgBitmapFinger).toString() + ) + assertTrue( + actual = hammingDistance(sourceBitmapFinger, blueBgBitmapFinger) < 5, + message = hammingDistance(sourceBitmapFinger, blueBgBitmapFinger).toString() + ) + assertTrue( + actual = hammingDistance(redBgBitmapFinger, blueBgBitmapFinger) < 5, + message = hammingDistance(redBgBitmapFinger, blueBgBitmapFinger).toString() ) - sourceBitmapFinger = this.produceFingerPrint() - sourceBitmapCorners = corners() } - val redBgBitmapFinger: String - val redBgBitmapCorners: List - val redBgBitmap = sourceBitmap.background(TestColor.RED).apply { + // colorType, colorSpace + ResourceImages.jpeg.decode( + colorType = BitmapColorType(colorType = ColorType.RGB_565), + colorSpace = "displayP3" + ).bitmap.apply { assertEquals( - expected = "Bitmap(750x719,RGBA_8888,sRGB)", + expected = "Bitmap(1291x1936,RGB_565,displayP3)", actual = toShortInfoString() ) - redBgBitmapFinger = this.produceFingerPrint() - redBgBitmapCorners = corners() - } - - val blueBgBitmapFinger: String - val blueBgBitmapCorners: List - val blueBgBitmap = sourceBitmap.background(TestColor.BLUE).apply { + }.background(Color.RED).apply { assertEquals( - expected = "Bitmap(750x719,RGBA_8888,sRGB)", + expected = "Bitmap(1291x1936,RGB_565,displayP3)", actual = toShortInfoString() ) - blueBgBitmapFinger = this.produceFingerPrint() - blueBgBitmapCorners = corners() } - - assertEquals(expected = listOf(0, 0, 0, 0), actual = sourceBitmapCorners) - assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = redBgBitmapCorners) - assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = blueBgBitmapCorners) - - assertNotEquals(illegal = sourceBitmapCorners, actual = redBgBitmapCorners) - assertNotEquals(illegal = sourceBitmapCorners, actual = blueBgBitmapCorners) - assertNotEquals(illegal = redBgBitmapCorners, actual = blueBgBitmapCorners) - - assertTrue( - actual = hammingDistance(sourceBitmapFinger, redBgBitmapFinger) < 5, - message = hammingDistance(sourceBitmapFinger, redBgBitmapFinger).toString() - ) - assertTrue( - actual = hammingDistance(sourceBitmapFinger, blueBgBitmapFinger) < 5, - message = hammingDistance(sourceBitmapFinger, blueBgBitmapFinger).toString() - ) - assertTrue( - actual = hammingDistance(redBgBitmapFinger, blueBgBitmapFinger) < 5, - message = hammingDistance(redBgBitmapFinger, blueBgBitmapFinger).toString() - ) } @Test @@ -774,6 +793,32 @@ class BitmapsNonAndroidTest { assertTrue(blur2ImmutableBitmap.isMutable) assertNotSame(immutableBitmap, blur1ImmutableBitmap) assertNotSame(immutableBitmap, blur2ImmutableBitmap) + + // colorType, colorSpace + ResourceImages.jpeg.decode( + colorType = BitmapColorType(colorType = ColorType.RGB_565), + colorSpace = "displayP3" + ).bitmap.mutableCopy().apply { + assertTrue(isMutable) + assertEquals( + expected = "Bitmap(1291x1936,RGB_565,displayP3)", + actual = toShortInfoString() + ) + }.also { + it.blur(20, firstReuseSelf = true).apply { + assertEquals( + expected = "Bitmap(1291x1936,RGB_565,displayP3)", + actual = toShortInfoString() + ) + } + + it.blur(20, firstReuseSelf = false).apply { + assertEquals( + expected = "Bitmap(1291x1936,RGB_565,displayP3)", + actual = toShortInfoString() + ) + } + } } @Test @@ -860,29 +905,21 @@ class BitmapsNonAndroidTest { message = hammingDistance(centerCropBitmapFinger, endCropBitmapFinger).toString() ) - - ResourceImages.jpeg.decode().bitmap.apply { - assertEquals(expected = ColorType.RGBA_8888, actual = colorType) - assertEquals(expected = ColorAlphaType.OPAQUE, actual = alphaType) - assertTrue(actual = isOpaque) - assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = corners()) - }.circleCrop(Scale.CENTER_CROP).apply { - assertEquals(expected = ColorType.RGBA_8888, actual = colorType) - assertEquals(expected = ColorAlphaType.PREMUL, actual = alphaType) - assertFalse(actual = isOpaque) - assertEquals(expected = listOf(0, 0, 0, 0), actual = corners()) - } - - val bitmapColorType = BitmapColorType(ColorType.RGB_565) - ResourceImages.jpeg.decode(bitmapColorType).bitmap.apply { - assertEquals(expected = ColorType.RGB_565, actual = colorType) - assertEquals(expected = ColorAlphaType.OPAQUE, actual = alphaType) - assertTrue(actual = isOpaque) + // colorType, colorSpace + ResourceImages.jpeg.decode( + colorType = BitmapColorType(colorType = ColorType.RGB_565), + colorSpace = "displayP3" + ).bitmap.apply { + assertEquals( + expected = "Bitmap(1291x1936,RGB_565,displayP3)", + actual = toShortInfoString() + ) assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = corners()) }.circleCrop(Scale.CENTER_CROP).apply { - assertEquals(expected = ColorType.RGBA_8888, actual = colorType) - assertEquals(expected = ColorAlphaType.PREMUL, actual = alphaType) - assertFalse(actual = isOpaque) + assertEquals( + expected = "Bitmap(1291x1291,RGBA_8888,displayP3)", + actual = toShortInfoString() + ) assertEquals(expected = listOf(0, 0, 0, 0), actual = corners()) } } @@ -970,6 +1007,22 @@ class BitmapsNonAndroidTest { verFlippedBitmap.cornerA, ) ) + + // colorType, colorSpace + ResourceImages.jpeg.decode( + colorType = BitmapColorType(colorType = ColorType.RGB_565), + colorSpace = "displayP3" + ).bitmap.apply { + assertEquals( + expected = "Bitmap(1291x1936,RGB_565,displayP3)", + actual = toShortInfoString() + ) + }.flip(horizontal = true).apply { + assertEquals( + expected = "Bitmap(1291x1936,RGB_565,displayP3)", + actual = toShortInfoString() + ) + } } @Test @@ -1096,6 +1149,31 @@ class BitmapsNonAndroidTest { actual = hammingDistance(resize3BitmapFinger, resize4BitmapFinger) >= 5, message = hammingDistance(resize3BitmapFinger, resize4BitmapFinger).toString() ) + + // colorType, colorSpace + ResourceImages.jpeg.decode( + colorType = BitmapColorType(colorType = ColorType.RGB_565), + colorSpace = "displayP3" + ).bitmap.apply { + assertEquals( + expected = "Bitmap(1291x1936,RGB_565,displayP3)", + actual = toShortInfoString() + ) + }.let { + it.mapping( + mapping = Resize( + width = 300, + height = 300, + precision = Precision.EXACTLY, + scale = Scale.CENTER_CROP + ).calculateMapping(it.size) + ) + }.apply { + assertEquals( + expected = "Bitmap(300x300,RGB_565,displayP3)", + actual = toShortInfoString() + ) + } } @Test @@ -1164,6 +1242,32 @@ class BitmapsNonAndroidTest { assertTrue(mask2ImmutableBitmap.isMutable) assertNotSame(immutableBitmap, mask1ImmutableBitmap) assertNotSame(immutableBitmap, mask2ImmutableBitmap) + + // colorType, colorSpace + ResourceImages.jpeg.decode( + colorType = BitmapColorType(colorType = ColorType.RGB_565), + colorSpace = "displayP3" + ).bitmap.mutableCopy().apply { + assertTrue(isMutable) + assertEquals( + expected = "Bitmap(1291x1936,RGB_565,displayP3)", + actual = toShortInfoString() + ) + }.also { + it.mask(Color.withA(Color.RED, 100), firstReuseSelf = true).apply { + assertEquals( + expected = "Bitmap(1291x1936,RGB_565,displayP3)", + actual = toShortInfoString() + ) + } + + it.mask(Color.withA(Color.RED, 100), firstReuseSelf = false).apply { + assertEquals( + expected = "Bitmap(1291x1936,RGB_565,displayP3)", + actual = toShortInfoString() + ) + } + } } @Test @@ -1279,30 +1383,32 @@ class BitmapsNonAndroidTest { message = hammingDistance(rotate270BitmapFinger, rotate360BitmapFinger).toString() ) - - ResourceImages.jpeg.decode().bitmap.apply { - assertEquals(expected = ColorType.RGBA_8888, actual = colorType) - assertEquals(expected = ColorAlphaType.OPAQUE, actual = alphaType) - assertTrue(actual = isOpaque) + // colorType, colorSpace + ResourceImages.jpeg.decode( + colorType = BitmapColorType(colorType = ColorType.RGB_565), + colorSpace = "displayP3" + ).bitmap.apply { + assertEquals( + expected = "Bitmap(1291x1936,RGB_565,displayP3)", + actual = toShortInfoString() + ) assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = corners()) - }.rotate(130).apply { - assertEquals(expected = ColorType.RGBA_8888, actual = colorType) - assertEquals(expected = ColorAlphaType.PREMUL, actual = alphaType) - assertFalse(actual = isOpaque) - assertEquals(expected = listOf(0, 0, 0, 0), actual = corners()) - } + }.also { + it.rotate(90).apply { + assertEquals( + expected = "Bitmap(1936x1291,RGB_565,displayP3)", + actual = toShortInfoString() + ) + assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = corners()) + } - val bitmapColorType = BitmapColorType(ColorType.RGB_565) - ResourceImages.jpeg.decode(bitmapColorType).bitmap.apply { - assertEquals(expected = ColorType.RGB_565, actual = colorType) - assertEquals(expected = ColorAlphaType.OPAQUE, actual = alphaType) - assertTrue(actual = isOpaque) - assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = corners()) - }.rotate(130).apply { - assertEquals(expected = ColorType.RGBA_8888, actual = colorType) - assertEquals(expected = ColorAlphaType.PREMUL, actual = alphaType) - assertFalse(actual = isOpaque) - assertEquals(expected = listOf(0, 0, 0, 0), actual = corners()) + it.rotate(130).apply { + assertEquals( + expected = "Bitmap(2312x2233,RGBA_8888,displayP3)", + actual = toShortInfoString() + ) + assertEquals(expected = listOf(0, 0, 0, 0), actual = corners()) + } } } @@ -1368,28 +1474,21 @@ class BitmapsNonAndroidTest { actual = bigRoundedCorneredBitmapFinger ) - ResourceImages.jpeg.decode().bitmap.apply { - assertEquals(expected = ColorType.RGBA_8888, actual = colorType) - assertEquals(expected = ColorAlphaType.OPAQUE, actual = alphaType) - assertTrue(actual = isOpaque) - assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = corners()) - }.roundedCorners(floatArrayOf(20f, 20f, 20f, 20f, 20f, 20f, 20f, 20f)).apply { - assertEquals(expected = ColorType.RGBA_8888, actual = colorType) - assertEquals(expected = ColorAlphaType.PREMUL, actual = alphaType) - assertFalse(actual = isOpaque) - assertEquals(expected = listOf(0, 0, 0, 0), actual = corners()) - } - - val bitmapColorType = BitmapColorType(ColorType.RGB_565) - ResourceImages.jpeg.decode(bitmapColorType).bitmap.apply { - assertEquals(expected = ColorType.RGB_565, actual = colorType) - assertEquals(expected = ColorAlphaType.OPAQUE, actual = alphaType) - assertTrue(actual = isOpaque) + // colorType, colorSpace + ResourceImages.jpeg.decode( + colorType = BitmapColorType(colorType = ColorType.RGB_565), + colorSpace = "displayP3" + ).bitmap.apply { + assertEquals( + expected = "Bitmap(1291x1936,RGB_565,displayP3)", + actual = toShortInfoString() + ) assertNotEquals(illegal = listOf(0, 0, 0, 0), actual = corners()) }.roundedCorners(floatArrayOf(20f, 20f, 20f, 20f, 20f, 20f, 20f, 20f)).apply { - assertEquals(expected = ColorType.RGBA_8888, actual = colorType) - assertEquals(expected = ColorAlphaType.PREMUL, actual = alphaType) - assertFalse(actual = isOpaque) + assertEquals( + expected = "Bitmap(1291x1936,RGBA_8888,displayP3)", + actual = toShortInfoString() + ) assertEquals(expected = listOf(0, 0, 0, 0), actual = corners()) } } @@ -1405,6 +1504,22 @@ class BitmapsNonAndroidTest { bitmap.scale(0.5f).apply { assertEquals("Bitmap(646x968,RGBA_8888,sRGB)", toShortInfoString()) } + + // colorType, colorSpace + ResourceImages.jpeg.decode( + colorType = BitmapColorType(colorType = ColorType.RGB_565), + colorSpace = "displayP3" + ).bitmap.apply { + assertEquals( + expected = "Bitmap(1291x1936,RGB_565,displayP3)", + actual = toShortInfoString() + ) + }.scale(1.5f).apply { + assertEquals( + "Bitmap(1937x2904,RGB_565,displayP3)", + toShortInfoString() + ) + } } @Test @@ -1422,5 +1537,21 @@ class BitmapsNonAndroidTest { ) } assertEquals(expected = 1, actual = bitmap.similarity(thumbnailBitmap)) + + // colorType, colorSpace + ResourceImages.jpeg.decode( + colorType = BitmapColorType(colorType = ColorType.RGB_565), + colorSpace = "displayP3" + ).bitmap.apply { + assertEquals( + expected = "Bitmap(1291x1936,RGB_565,displayP3)", + actual = toShortInfoString() + ) + }.thumbnail(100, 100).apply { + assertEquals( + "Bitmap(100x100,RGB_565,displayP3)", + toShortInfoString() + ) + } } } \ No newline at end of file