react-native-libyuv-resizer 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/LibyuvResizer.podspec +20 -0
- package/README.md +188 -0
- package/android/CMakeLists.txt +30 -0
- package/android/build.gradle +100 -0
- package/android/src/androidTest/java/com/libyuvresizer/ExifCopierTest.kt +131 -0
- package/android/src/androidTest/java/com/libyuvresizer/FakePromise.kt +71 -0
- package/android/src/androidTest/java/com/libyuvresizer/FakeReactContext.kt +55 -0
- package/android/src/androidTest/java/com/libyuvresizer/LibyuvResizerModuleErrorTest.kt +135 -0
- package/android/src/androidTest/java/com/libyuvresizer/LibyuvResizerModuleExifTest.kt +140 -0
- package/android/src/androidTest/java/com/libyuvresizer/LibyuvResizerModuleFilterModeTest.kt +85 -0
- package/android/src/androidTest/java/com/libyuvresizer/LibyuvResizerModuleFormatTest.kt +146 -0
- package/android/src/androidTest/java/com/libyuvresizer/LibyuvResizerModuleIntegrationTest.kt +157 -0
- package/android/src/androidTest/java/com/libyuvresizer/LibyuvResizerModuleOutputPathTest.kt +96 -0
- package/android/src/androidTest/java/com/libyuvresizer/LibyuvResizerModuleRotationTest.kt +120 -0
- package/android/src/androidTest/java/com/libyuvresizer/TestFixtures.kt +48 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/LibyuvResizerModule.cpp +137 -0
- package/android/src/main/java/com/libyuvresizer/DimensionCalculator.kt +52 -0
- package/android/src/main/java/com/libyuvresizer/ExifCopier.kt +133 -0
- package/android/src/main/java/com/libyuvresizer/LibyuvResizerModule.kt +179 -0
- package/android/src/main/java/com/libyuvresizer/LibyuvResizerPackage.kt +30 -0
- package/android/src/main/java/com/libyuvresizer/ResizeValidator.kt +71 -0
- package/android/src/test/java/com/libyuvresizer/DimensionCalculatorTest.kt +181 -0
- package/android/src/test/java/com/libyuvresizer/ResizeValidatorTest.kt +203 -0
- package/ios/LibyuvResizer.h +6 -0
- package/ios/LibyuvResizer.mm +31 -0
- package/lib/module/NativeLibyuvResizer.js +28 -0
- package/lib/module/NativeLibyuvResizer.js.map +1 -0
- package/lib/module/index.js +20 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/resizer.js +15 -0
- package/lib/module/resizer.js.map +1 -0
- package/lib/module/resizer.native.js +110 -0
- package/lib/module/resizer.native.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativeLibyuvResizer.d.ts +52 -0
- package/lib/typescript/src/NativeLibyuvResizer.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +19 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/resizer.d.ts +13 -0
- package/lib/typescript/src/resizer.d.ts.map +1 -0
- package/lib/typescript/src/resizer.native.d.ts +119 -0
- package/lib/typescript/src/resizer.native.d.ts.map +1 -0
- package/package.json +184 -0
- package/src/NativeLibyuvResizer.ts +81 -0
- package/src/index.tsx +23 -0
- package/src/resizer.native.tsx +175 -0
- package/src/resizer.tsx +31 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
package com.libyuvresizer
|
|
2
|
+
|
|
3
|
+
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
4
|
+
import androidx.test.platform.app.InstrumentationRegistry
|
|
5
|
+
import org.junit.After
|
|
6
|
+
import org.junit.Assert.assertEquals
|
|
7
|
+
import org.junit.Assert.assertFalse
|
|
8
|
+
import org.junit.Assert.assertTrue
|
|
9
|
+
import org.junit.Before
|
|
10
|
+
import org.junit.Test
|
|
11
|
+
import org.junit.runner.RunWith
|
|
12
|
+
|
|
13
|
+
@RunWith(AndroidJUnit4::class)
|
|
14
|
+
class LibyuvResizerModuleErrorTest {
|
|
15
|
+
|
|
16
|
+
private lateinit var module: LibyuvResizerModule
|
|
17
|
+
private lateinit var reactContext: FakeReactContext
|
|
18
|
+
private val createdFiles = mutableListOf<String>()
|
|
19
|
+
|
|
20
|
+
@Before
|
|
21
|
+
fun setUp() {
|
|
22
|
+
val ctx = InstrumentationRegistry.getInstrumentation().targetContext
|
|
23
|
+
reactContext = FakeReactContext(ctx)
|
|
24
|
+
module = LibyuvResizerModule(reactContext)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@After
|
|
28
|
+
fun tearDown() {
|
|
29
|
+
createdFiles.forEach { TestFixtures.deleteIfExists(it) }
|
|
30
|
+
createdFiles.clear()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private fun resize(
|
|
34
|
+
filePath: String = "/nonexistent/file.jpg",
|
|
35
|
+
targetW: Double = 100.0,
|
|
36
|
+
targetH: Double = 100.0,
|
|
37
|
+
quality: Double = 80.0,
|
|
38
|
+
rotation: Double = 0.0,
|
|
39
|
+
mode: String = "contain",
|
|
40
|
+
outputPath: String = "",
|
|
41
|
+
filterMode: String = "box",
|
|
42
|
+
format: String = "jpeg"
|
|
43
|
+
): FakePromise {
|
|
44
|
+
val promise = FakePromise()
|
|
45
|
+
module.resize(filePath, targetW, targetH, quality, rotation, mode, outputPath, filterMode, false, format, promise)
|
|
46
|
+
return promise
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@Test
|
|
50
|
+
fun resize_nonexistentFile_rejectsWithFileNotFound() {
|
|
51
|
+
val promise = resize(filePath = "/nonexistent/path/image.jpg")
|
|
52
|
+
|
|
53
|
+
assertTrue(promise.rejected)
|
|
54
|
+
assertFalse(promise.resolved)
|
|
55
|
+
assertEquals("E_FILE_NOT_FOUND", promise.errorCode)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@Test
|
|
59
|
+
fun resize_corruptFile_rejectsWithDecodeFailed() {
|
|
60
|
+
val corrupt = TestFixtures.createCorruptFile(reactContext, "corrupt.jpg")
|
|
61
|
+
createdFiles += corrupt
|
|
62
|
+
|
|
63
|
+
val promise = resize(filePath = corrupt)
|
|
64
|
+
|
|
65
|
+
assertTrue(promise.rejected)
|
|
66
|
+
assertEquals("E_DECODE_FAILED", promise.errorCode)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@Test
|
|
70
|
+
fun resize_zeroDimensions_rejectsWithInvalidDims() {
|
|
71
|
+
val src = TestFixtures.createJpeg(reactContext, 100, 100, "dims_test.jpg")
|
|
72
|
+
createdFiles += src
|
|
73
|
+
|
|
74
|
+
val promise = resize(filePath = src, targetW = 0.0, targetH = 100.0)
|
|
75
|
+
|
|
76
|
+
assertTrue(promise.rejected)
|
|
77
|
+
assertEquals("E_INVALID_DIMS", promise.errorCode)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@Test
|
|
81
|
+
fun resize_outOfRangeQuality_rejectsWithInvalidQuality() {
|
|
82
|
+
val src = TestFixtures.createJpeg(reactContext, 100, 100, "quality_test.jpg")
|
|
83
|
+
createdFiles += src
|
|
84
|
+
|
|
85
|
+
val promise = resize(filePath = src, quality = 0.0)
|
|
86
|
+
|
|
87
|
+
assertTrue(promise.rejected)
|
|
88
|
+
assertEquals("E_INVALID_QUALITY", promise.errorCode)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@Test
|
|
92
|
+
fun resize_invalidMode_rejectsWithInvalidMode() {
|
|
93
|
+
val src = TestFixtures.createJpeg(reactContext, 100, 100, "mode_test.jpg")
|
|
94
|
+
createdFiles += src
|
|
95
|
+
|
|
96
|
+
val promise = resize(filePath = src, mode = "fill")
|
|
97
|
+
|
|
98
|
+
assertTrue(promise.rejected)
|
|
99
|
+
assertEquals("E_INVALID_MODE", promise.errorCode)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@Test
|
|
103
|
+
fun resize_invalidFilterMode_rejectsWithInvalidFilterMode() {
|
|
104
|
+
val src = TestFixtures.createJpeg(reactContext, 100, 100, "filter_test.jpg")
|
|
105
|
+
createdFiles += src
|
|
106
|
+
|
|
107
|
+
val promise = resize(filePath = src, filterMode = "lanczos")
|
|
108
|
+
|
|
109
|
+
assertTrue(promise.rejected)
|
|
110
|
+
assertEquals("E_INVALID_FILTER_MODE", promise.errorCode)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
@Test
|
|
114
|
+
fun resize_invalidRotation_rejectsWithInvalidRotation() {
|
|
115
|
+
val src = TestFixtures.createJpeg(reactContext, 100, 100, "rot_test.jpg")
|
|
116
|
+
createdFiles += src
|
|
117
|
+
|
|
118
|
+
val promise = resize(filePath = src, rotation = 45.0)
|
|
119
|
+
|
|
120
|
+
assertTrue(promise.rejected)
|
|
121
|
+
assertEquals("E_INVALID_ROTATION", promise.errorCode)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
@Test
|
|
125
|
+
fun resize_outputPathIsFile_rejectsWithInvalidOutputPath() {
|
|
126
|
+
val src = TestFixtures.createJpeg(reactContext, 100, 100, "outpath_test.jpg")
|
|
127
|
+
createdFiles += src
|
|
128
|
+
|
|
129
|
+
// outputPath points to a file, not a directory
|
|
130
|
+
val promise = resize(filePath = src, outputPath = src)
|
|
131
|
+
|
|
132
|
+
assertTrue(promise.rejected)
|
|
133
|
+
assertEquals("E_INVALID_OUTPUT_PATH", promise.errorCode)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
package com.libyuvresizer
|
|
2
|
+
|
|
3
|
+
import androidx.exifinterface.media.ExifInterface
|
|
4
|
+
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
5
|
+
import androidx.test.platform.app.InstrumentationRegistry
|
|
6
|
+
import com.facebook.react.bridge.ReadableMap
|
|
7
|
+
import org.junit.After
|
|
8
|
+
import org.junit.Assert.assertEquals
|
|
9
|
+
import org.junit.Assert.assertNull
|
|
10
|
+
import org.junit.Assert.assertTrue
|
|
11
|
+
import org.junit.Before
|
|
12
|
+
import org.junit.Test
|
|
13
|
+
import org.junit.runner.RunWith
|
|
14
|
+
import java.io.File
|
|
15
|
+
|
|
16
|
+
@RunWith(AndroidJUnit4::class)
|
|
17
|
+
class LibyuvResizerModuleExifTest {
|
|
18
|
+
|
|
19
|
+
private lateinit var module: LibyuvResizerModule
|
|
20
|
+
private lateinit var reactContext: FakeReactContext
|
|
21
|
+
private val createdFiles = mutableListOf<String>()
|
|
22
|
+
|
|
23
|
+
@Before
|
|
24
|
+
fun setUp() {
|
|
25
|
+
val ctx = InstrumentationRegistry.getInstrumentation().targetContext
|
|
26
|
+
reactContext = FakeReactContext(ctx)
|
|
27
|
+
module = LibyuvResizerModule(reactContext)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@After
|
|
31
|
+
fun tearDown() {
|
|
32
|
+
createdFiles.forEach { TestFixtures.deleteIfExists(it) }
|
|
33
|
+
createdFiles.clear()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private fun createJpegWithGps(name: String): String {
|
|
37
|
+
val path = TestFixtures.createJpeg(reactContext, 200, 150, name)
|
|
38
|
+
createdFiles += path
|
|
39
|
+
ExifInterface(path).apply {
|
|
40
|
+
setAttribute(ExifInterface.TAG_GPS_LATITUDE, "48/1,51/1,29/1")
|
|
41
|
+
setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, "N")
|
|
42
|
+
setAttribute(ExifInterface.TAG_GPS_LONGITUDE, "2/1,17/1,40/1")
|
|
43
|
+
setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, "E")
|
|
44
|
+
setAttribute(ExifInterface.TAG_MAKE, "TestCamera")
|
|
45
|
+
setAttribute(ExifInterface.TAG_MODEL, "TestModel")
|
|
46
|
+
setAttribute(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_ROTATE_90.toString())
|
|
47
|
+
}.saveAttributes()
|
|
48
|
+
return path
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private fun resize(
|
|
52
|
+
filePath: String,
|
|
53
|
+
keepMeta: Boolean,
|
|
54
|
+
quality: Double = 80.0,
|
|
55
|
+
format: String = "jpeg"
|
|
56
|
+
): FakePromise {
|
|
57
|
+
val promise = FakePromise()
|
|
58
|
+
module.resize(filePath, 100.0, 100.0, quality, 0.0, "contain", "", "box", keepMeta, format, promise)
|
|
59
|
+
return promise
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@Test
|
|
63
|
+
fun `resize with keepMeta true preserves GPS coordinates`() {
|
|
64
|
+
val src = createJpegWithGps("exif_int_gps_src.jpg")
|
|
65
|
+
|
|
66
|
+
val promise = resize(src, keepMeta = true)
|
|
67
|
+
|
|
68
|
+
assertTrue(promise.resolved)
|
|
69
|
+
val outPath = (promise.result as ReadableMap).getString("path")!!
|
|
70
|
+
createdFiles += outPath
|
|
71
|
+
|
|
72
|
+
val exif = ExifInterface(outPath)
|
|
73
|
+
assertEquals("48/1,51/1,29/1", exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE))
|
|
74
|
+
assertEquals("N", exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF))
|
|
75
|
+
assertEquals("2/1,17/1,40/1", exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE))
|
|
76
|
+
assertEquals("E", exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF))
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@Test
|
|
80
|
+
fun `resize with keepMeta true resets orientation to normal`() {
|
|
81
|
+
val src = createJpegWithGps("exif_int_orient_src.jpg")
|
|
82
|
+
|
|
83
|
+
val promise = resize(src, keepMeta = true)
|
|
84
|
+
|
|
85
|
+
assertTrue(promise.resolved)
|
|
86
|
+
val outPath = (promise.result as ReadableMap).getString("path")!!
|
|
87
|
+
createdFiles += outPath
|
|
88
|
+
|
|
89
|
+
val exif = ExifInterface(outPath)
|
|
90
|
+
assertEquals(
|
|
91
|
+
ExifInterface.ORIENTATION_NORMAL,
|
|
92
|
+
exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@Test
|
|
97
|
+
fun `resize with keepMeta false produces no GPS in output`() {
|
|
98
|
+
val src = createJpegWithGps("exif_int_nometa_src.jpg")
|
|
99
|
+
|
|
100
|
+
val promise = resize(src, keepMeta = false)
|
|
101
|
+
|
|
102
|
+
assertTrue(promise.resolved)
|
|
103
|
+
val outPath = (promise.result as ReadableMap).getString("path")!!
|
|
104
|
+
createdFiles += outPath
|
|
105
|
+
|
|
106
|
+
val exif = ExifInterface(outPath)
|
|
107
|
+
assertNull(exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE))
|
|
108
|
+
assertNull(exif.getAttribute(ExifInterface.TAG_MAKE))
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@Test
|
|
112
|
+
fun `resize with keepMeta omitted default produces no EXIF`() {
|
|
113
|
+
val src = createJpegWithGps("exif_int_default_src.jpg")
|
|
114
|
+
|
|
115
|
+
// keepMeta defaults to false in the bridge call
|
|
116
|
+
val promise = FakePromise()
|
|
117
|
+
module.resize(src, 100.0, 100.0, 80.0, 0.0, "contain", "", "box", false, "jpeg", promise)
|
|
118
|
+
|
|
119
|
+
assertTrue(promise.resolved)
|
|
120
|
+
val outPath = (promise.result as ReadableMap).getString("path")!!
|
|
121
|
+
createdFiles += outPath
|
|
122
|
+
|
|
123
|
+
val exif = ExifInterface(outPath)
|
|
124
|
+
assertNull(exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE))
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
@Test
|
|
128
|
+
fun `resize PNG format with keepMeta true resolves without error`() {
|
|
129
|
+
val src = createJpegWithGps("exif_int_png_src.jpg")
|
|
130
|
+
|
|
131
|
+
// format=png → PNG output; keepMeta is no-op for PNG
|
|
132
|
+
val promise = resize(src, keepMeta = true, format = "png")
|
|
133
|
+
|
|
134
|
+
assertTrue("expected resolved, got: ${promise.errorCode}: ${promise.errorMessage}", promise.resolved)
|
|
135
|
+
val outPath = (promise.result as ReadableMap).getString("path")!!
|
|
136
|
+
createdFiles += outPath
|
|
137
|
+
assertTrue("output must be .png", outPath.endsWith(".png"))
|
|
138
|
+
assertTrue("output file must exist", File(outPath).exists())
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
package com.libyuvresizer
|
|
2
|
+
|
|
3
|
+
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
4
|
+
import androidx.test.platform.app.InstrumentationRegistry
|
|
5
|
+
import org.junit.After
|
|
6
|
+
import org.junit.Assert.assertTrue
|
|
7
|
+
import org.junit.Before
|
|
8
|
+
import org.junit.Test
|
|
9
|
+
import org.junit.runner.RunWith
|
|
10
|
+
import com.facebook.react.bridge.ReadableMap
|
|
11
|
+
import java.io.File
|
|
12
|
+
|
|
13
|
+
@RunWith(AndroidJUnit4::class)
|
|
14
|
+
class LibyuvResizerModuleFilterModeTest {
|
|
15
|
+
|
|
16
|
+
private lateinit var module: LibyuvResizerModule
|
|
17
|
+
private lateinit var reactContext: FakeReactContext
|
|
18
|
+
private val createdFiles = mutableListOf<String>()
|
|
19
|
+
private lateinit var srcPath: String
|
|
20
|
+
|
|
21
|
+
@Before
|
|
22
|
+
fun setUp() {
|
|
23
|
+
val ctx = InstrumentationRegistry.getInstrumentation().targetContext
|
|
24
|
+
reactContext = FakeReactContext(ctx)
|
|
25
|
+
module = LibyuvResizerModule(reactContext)
|
|
26
|
+
srcPath = TestFixtures.createJpeg(reactContext, 120, 80, "filter_src.jpg")
|
|
27
|
+
createdFiles += srcPath
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@After
|
|
31
|
+
fun tearDown() {
|
|
32
|
+
createdFiles.forEach { TestFixtures.deleteIfExists(it) }
|
|
33
|
+
createdFiles.clear()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private fun resizeWithFilter(filterMode: String): FakePromise {
|
|
37
|
+
val promise = FakePromise()
|
|
38
|
+
module.resize(srcPath, 60.0, 60.0, 80.0, 0.0, "contain", "", filterMode, false, "jpeg", promise)
|
|
39
|
+
return promise
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@Test
|
|
43
|
+
fun resize_filterModeBox_producesValidOutput() {
|
|
44
|
+
val promise = resizeWithFilter("box")
|
|
45
|
+
assertTrue(promise.resolved)
|
|
46
|
+
val outPath = (promise.result as ReadableMap).getString("path")!!
|
|
47
|
+
createdFiles += outPath
|
|
48
|
+
assertTrue(File(outPath).exists())
|
|
49
|
+
val (w, h) = TestFixtures.decodeDimensions(outPath)
|
|
50
|
+
assertTrue(w > 0 && h > 0)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@Test
|
|
54
|
+
fun resize_filterModeBilinear_producesValidOutput() {
|
|
55
|
+
val promise = resizeWithFilter("bilinear")
|
|
56
|
+
assertTrue(promise.resolved)
|
|
57
|
+
val outPath = (promise.result as ReadableMap).getString("path")!!
|
|
58
|
+
createdFiles += outPath
|
|
59
|
+
assertTrue(File(outPath).exists())
|
|
60
|
+
val (w, h) = TestFixtures.decodeDimensions(outPath)
|
|
61
|
+
assertTrue(w > 0 && h > 0)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@Test
|
|
65
|
+
fun resize_filterModeLinear_producesValidOutput() {
|
|
66
|
+
val promise = resizeWithFilter("linear")
|
|
67
|
+
assertTrue(promise.resolved)
|
|
68
|
+
val outPath = (promise.result as ReadableMap).getString("path")!!
|
|
69
|
+
createdFiles += outPath
|
|
70
|
+
assertTrue(File(outPath).exists())
|
|
71
|
+
val (w, h) = TestFixtures.decodeDimensions(outPath)
|
|
72
|
+
assertTrue(w > 0 && h > 0)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@Test
|
|
76
|
+
fun resize_filterModeNone_producesValidOutput() {
|
|
77
|
+
val promise = resizeWithFilter("none")
|
|
78
|
+
assertTrue(promise.resolved)
|
|
79
|
+
val outPath = (promise.result as ReadableMap).getString("path")!!
|
|
80
|
+
createdFiles += outPath
|
|
81
|
+
assertTrue(File(outPath).exists())
|
|
82
|
+
val (w, h) = TestFixtures.decodeDimensions(outPath)
|
|
83
|
+
assertTrue(w > 0 && h > 0)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
package com.libyuvresizer
|
|
2
|
+
|
|
3
|
+
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
4
|
+
import androidx.test.platform.app.InstrumentationRegistry
|
|
5
|
+
import com.facebook.react.bridge.ReadableMap
|
|
6
|
+
import org.junit.After
|
|
7
|
+
import org.junit.Assert.assertEquals
|
|
8
|
+
import org.junit.Assert.assertFalse
|
|
9
|
+
import org.junit.Assert.assertTrue
|
|
10
|
+
import org.junit.Before
|
|
11
|
+
import org.junit.Test
|
|
12
|
+
import org.junit.runner.RunWith
|
|
13
|
+
import java.io.File
|
|
14
|
+
|
|
15
|
+
@RunWith(AndroidJUnit4::class)
|
|
16
|
+
class LibyuvResizerModuleFormatTest {
|
|
17
|
+
|
|
18
|
+
private lateinit var module: LibyuvResizerModule
|
|
19
|
+
private lateinit var reactContext: FakeReactContext
|
|
20
|
+
private val createdFiles = mutableListOf<String>()
|
|
21
|
+
|
|
22
|
+
@Before
|
|
23
|
+
fun setUp() {
|
|
24
|
+
val ctx = InstrumentationRegistry.getInstrumentation().targetContext
|
|
25
|
+
reactContext = FakeReactContext(ctx)
|
|
26
|
+
module = LibyuvResizerModule(reactContext)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@After
|
|
30
|
+
fun tearDown() {
|
|
31
|
+
createdFiles.forEach { TestFixtures.deleteIfExists(it) }
|
|
32
|
+
createdFiles.clear()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private fun resize(filePath: String, format: String, quality: Double = 80.0, keepMeta: Boolean = false): FakePromise {
|
|
36
|
+
val promise = FakePromise()
|
|
37
|
+
module.resize(filePath, 100.0, 100.0, quality, 0.0, "contain", "", "box", keepMeta, format, promise)
|
|
38
|
+
return promise
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@Test
|
|
42
|
+
fun resize_formatWebp_outputPathEndsWithWebp() {
|
|
43
|
+
val src = TestFixtures.createJpeg(reactContext, 200, 150, "webp_test.jpg")
|
|
44
|
+
createdFiles += src
|
|
45
|
+
|
|
46
|
+
val promise = resize(src, format = "webp")
|
|
47
|
+
|
|
48
|
+
assertTrue("expected resolved, got error=${promise.errorCode}: ${promise.errorMessage}", promise.resolved)
|
|
49
|
+
val outPath = (promise.result as ReadableMap).getString("path")!!
|
|
50
|
+
createdFiles += outPath
|
|
51
|
+
assertTrue("output path must end with .webp", outPath.endsWith(".webp"))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@Test
|
|
55
|
+
fun resize_formatWebp_outputFileIsDecodable() {
|
|
56
|
+
val src = TestFixtures.createJpeg(reactContext, 200, 150, "webp_decode.jpg")
|
|
57
|
+
createdFiles += src
|
|
58
|
+
|
|
59
|
+
val promise = resize(src, format = "webp")
|
|
60
|
+
|
|
61
|
+
assertTrue(promise.resolved)
|
|
62
|
+
val outPath = (promise.result as ReadableMap).getString("path")!!
|
|
63
|
+
createdFiles += outPath
|
|
64
|
+
assertTrue("output file must exist", File(outPath).exists())
|
|
65
|
+
assertTrue("output file must be non-empty", File(outPath).length() > 0)
|
|
66
|
+
val (w, h) = TestFixtures.decodeDimensions(outPath)
|
|
67
|
+
assertTrue("decoded WebP width must be > 0", w > 0)
|
|
68
|
+
assertTrue("decoded WebP height must be > 0", h > 0)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@Test
|
|
72
|
+
fun resize_formatWebp_resultSizeIsPositive() {
|
|
73
|
+
val src = TestFixtures.createJpeg(reactContext, 200, 150, "webp_size.jpg")
|
|
74
|
+
createdFiles += src
|
|
75
|
+
|
|
76
|
+
val promise = resize(src, format = "webp")
|
|
77
|
+
|
|
78
|
+
assertTrue(promise.resolved)
|
|
79
|
+
val map = promise.result as ReadableMap
|
|
80
|
+
assertTrue("result size must be > 0", map.getDouble("size") > 0)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@Test
|
|
84
|
+
fun resize_formatWebp_resultNameEndsWithWebp() {
|
|
85
|
+
val src = TestFixtures.createJpeg(reactContext, 200, 150, "webp_name.jpg")
|
|
86
|
+
createdFiles += src
|
|
87
|
+
|
|
88
|
+
val promise = resize(src, format = "webp")
|
|
89
|
+
|
|
90
|
+
assertTrue(promise.resolved)
|
|
91
|
+
val name = (promise.result as ReadableMap).getString("name")!!
|
|
92
|
+
assertTrue("result name must end with .webp", name.endsWith(".webp"))
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@Test
|
|
96
|
+
fun resize_formatWebp_keepMetaTrue_resolvesWithoutError() {
|
|
97
|
+
val src = TestFixtures.createJpeg(reactContext, 200, 150, "webp_keepmeta.jpg")
|
|
98
|
+
createdFiles += src
|
|
99
|
+
|
|
100
|
+
val promise = resize(src, format = "webp", keepMeta = true)
|
|
101
|
+
|
|
102
|
+
assertTrue("keepMeta+webp must resolve, got: ${promise.errorCode}: ${promise.errorMessage}", promise.resolved)
|
|
103
|
+
assertFalse(promise.rejected)
|
|
104
|
+
val outPath = (promise.result as ReadableMap).getString("path")!!
|
|
105
|
+
createdFiles += outPath
|
|
106
|
+
assertTrue("output must be .webp", outPath.endsWith(".webp"))
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
@Test
|
|
110
|
+
fun resize_formatJpeg_explicit_outputPathEndsWithJpg() {
|
|
111
|
+
val src = TestFixtures.createJpeg(reactContext, 100, 100, "jpeg_explicit.jpg")
|
|
112
|
+
createdFiles += src
|
|
113
|
+
|
|
114
|
+
val promise = resize(src, format = "jpeg")
|
|
115
|
+
|
|
116
|
+
assertTrue(promise.resolved)
|
|
117
|
+
val outPath = (promise.result as ReadableMap).getString("path")!!
|
|
118
|
+
createdFiles += outPath
|
|
119
|
+
assertTrue("output path must end with .jpg", outPath.endsWith(".jpg"))
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@Test
|
|
123
|
+
fun resize_formatPng_explicit_outputPathEndsWithPng() {
|
|
124
|
+
val src = TestFixtures.createJpeg(reactContext, 100, 100, "png_explicit.jpg")
|
|
125
|
+
createdFiles += src
|
|
126
|
+
|
|
127
|
+
val promise = resize(src, format = "png")
|
|
128
|
+
|
|
129
|
+
assertTrue(promise.resolved)
|
|
130
|
+
val outPath = (promise.result as ReadableMap).getString("path")!!
|
|
131
|
+
createdFiles += outPath
|
|
132
|
+
assertTrue("output path must end with .png", outPath.endsWith(".png"))
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@Test
|
|
136
|
+
fun resize_invalidFormat_rejectsWithInvalidFormat() {
|
|
137
|
+
val src = TestFixtures.createJpeg(reactContext, 100, 100, "fmt_err.jpg")
|
|
138
|
+
createdFiles += src
|
|
139
|
+
|
|
140
|
+
val promise = resize(src, format = "gif")
|
|
141
|
+
|
|
142
|
+
assertTrue(promise.rejected)
|
|
143
|
+
assertFalse(promise.resolved)
|
|
144
|
+
assertEquals("E_INVALID_FORMAT", promise.errorCode)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
package com.libyuvresizer
|
|
2
|
+
|
|
3
|
+
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
4
|
+
import androidx.test.platform.app.InstrumentationRegistry
|
|
5
|
+
import org.junit.After
|
|
6
|
+
import org.junit.Assert.assertEquals
|
|
7
|
+
import org.junit.Assert.assertTrue
|
|
8
|
+
import org.junit.Before
|
|
9
|
+
import org.junit.Test
|
|
10
|
+
import org.junit.runner.RunWith
|
|
11
|
+
import com.facebook.react.bridge.ReadableMap
|
|
12
|
+
import java.io.File
|
|
13
|
+
|
|
14
|
+
@RunWith(AndroidJUnit4::class)
|
|
15
|
+
class LibyuvResizerModuleIntegrationTest {
|
|
16
|
+
|
|
17
|
+
private lateinit var module: LibyuvResizerModule
|
|
18
|
+
private lateinit var reactContext: FakeReactContext
|
|
19
|
+
private val createdFiles = mutableListOf<String>()
|
|
20
|
+
|
|
21
|
+
@Before
|
|
22
|
+
fun setUp() {
|
|
23
|
+
val ctx = InstrumentationRegistry.getInstrumentation().targetContext
|
|
24
|
+
reactContext = FakeReactContext(ctx)
|
|
25
|
+
module = LibyuvResizerModule(reactContext)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@After
|
|
29
|
+
fun tearDown() {
|
|
30
|
+
createdFiles.forEach { TestFixtures.deleteIfExists(it) }
|
|
31
|
+
createdFiles.clear()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private fun resize(
|
|
35
|
+
filePath: String,
|
|
36
|
+
targetW: Double,
|
|
37
|
+
targetH: Double,
|
|
38
|
+
quality: Double,
|
|
39
|
+
rotation: Double = 0.0,
|
|
40
|
+
mode: String = "contain",
|
|
41
|
+
outputPath: String = "",
|
|
42
|
+
filterMode: String = "box",
|
|
43
|
+
format: String = "jpeg"
|
|
44
|
+
): FakePromise {
|
|
45
|
+
val promise = FakePromise()
|
|
46
|
+
module.resize(filePath, targetW, targetH, quality, rotation, mode, outputPath, filterMode, false, format, promise)
|
|
47
|
+
return promise
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@Test
|
|
51
|
+
fun resize_landscape_contain_returnsScaledJpeg() {
|
|
52
|
+
// 200x100 → target 300x300 contain → scale=min(1.5,3.0)=1.5 → floor(300)x floor(150) = 300x150
|
|
53
|
+
val src = TestFixtures.createJpeg(reactContext, 200, 100, "landscape.jpg")
|
|
54
|
+
createdFiles += src
|
|
55
|
+
|
|
56
|
+
val promise = resize(src, 300.0, 300.0, 80.0)
|
|
57
|
+
|
|
58
|
+
assertTrue("expected resolved, got error=${promise.errorCode}: ${promise.errorMessage}", promise.resolved)
|
|
59
|
+
val outPath = (promise.result as ReadableMap).getString("path")!!
|
|
60
|
+
createdFiles += outPath
|
|
61
|
+
assertTrue("output file must exist", File(outPath).exists())
|
|
62
|
+
assertTrue("output must be .jpg", outPath.endsWith(".jpg"))
|
|
63
|
+
val (w, h) = TestFixtures.decodeDimensions(outPath)
|
|
64
|
+
assertEquals(300, w)
|
|
65
|
+
assertEquals(150, h)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@Test
|
|
69
|
+
fun resize_portrait_cover_producesLargerThanTarget() {
|
|
70
|
+
// 100x200 → target 150x150 cover → scale=max(1.5,0.75)=1.5 → round(150)x round(300) = 150x300
|
|
71
|
+
val src = TestFixtures.createJpeg(reactContext, 100, 200, "portrait.jpg")
|
|
72
|
+
createdFiles += src
|
|
73
|
+
|
|
74
|
+
val promise = resize(src, 150.0, 150.0, 80.0, mode = "cover")
|
|
75
|
+
|
|
76
|
+
assertTrue(promise.resolved)
|
|
77
|
+
val outPath = (promise.result as ReadableMap).getString("path")!!
|
|
78
|
+
createdFiles += outPath
|
|
79
|
+
val (w, h) = TestFixtures.decodeDimensions(outPath)
|
|
80
|
+
assertEquals(150, w)
|
|
81
|
+
assertEquals(300, h)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@Test
|
|
85
|
+
fun resize_stretch_ignoresAspectRatio() {
|
|
86
|
+
// 200x100 → target 300x150 stretch → output exactly 300x150
|
|
87
|
+
val src = TestFixtures.createJpeg(reactContext, 200, 100, "landscape_stretch.jpg")
|
|
88
|
+
createdFiles += src
|
|
89
|
+
|
|
90
|
+
val promise = resize(src, 300.0, 150.0, 80.0, mode = "stretch")
|
|
91
|
+
|
|
92
|
+
assertTrue(promise.resolved)
|
|
93
|
+
val outPath = (promise.result as ReadableMap).getString("path")!!
|
|
94
|
+
createdFiles += outPath
|
|
95
|
+
val (w, h) = TestFixtures.decodeDimensions(outPath)
|
|
96
|
+
assertEquals(300, w)
|
|
97
|
+
assertEquals(150, h)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@Test
|
|
101
|
+
fun resize_formatPng_writesPng() {
|
|
102
|
+
val src = TestFixtures.createJpeg(reactContext, 100, 100, "square.jpg")
|
|
103
|
+
createdFiles += src
|
|
104
|
+
|
|
105
|
+
val promise = resize(src, 50.0, 50.0, 80.0, format = "png")
|
|
106
|
+
|
|
107
|
+
assertTrue(promise.resolved)
|
|
108
|
+
val outPath = (promise.result as ReadableMap).getString("path")!!
|
|
109
|
+
createdFiles += outPath
|
|
110
|
+
assertTrue("output must be .png", outPath.endsWith(".png"))
|
|
111
|
+
val decoded = TestFixtures.decodeDimensions(outPath)
|
|
112
|
+
assertTrue("decoded PNG must be readable", decoded.first > 0 && decoded.second > 0)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@Test
|
|
116
|
+
fun resize_quality80_writesJpeg() {
|
|
117
|
+
val src = TestFixtures.createJpeg(reactContext, 100, 100, "square_jpeg.jpg")
|
|
118
|
+
createdFiles += src
|
|
119
|
+
|
|
120
|
+
val promise = resize(src, 50.0, 50.0, 80.0)
|
|
121
|
+
|
|
122
|
+
assertTrue(promise.resolved)
|
|
123
|
+
val outPath = (promise.result as ReadableMap).getString("path")!!
|
|
124
|
+
createdFiles += outPath
|
|
125
|
+
assertTrue("output must be .jpg", outPath.endsWith(".jpg"))
|
|
126
|
+
assertTrue("decoded JPEG must be readable", TestFixtures.decodeDimensions(outPath).first > 0)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
@Test
|
|
130
|
+
fun resize_smallImageToLargeTarget_contain_upscales() {
|
|
131
|
+
// 64x64 → 400x400 contain → scale=min(400/64, 400/64)=6.25 → floor(400)x floor(400) = 400x400
|
|
132
|
+
val src = TestFixtures.createJpeg(reactContext, 64, 64, "tiny.jpg")
|
|
133
|
+
createdFiles += src
|
|
134
|
+
|
|
135
|
+
val promise = resize(src, 400.0, 400.0, 80.0)
|
|
136
|
+
|
|
137
|
+
assertTrue(promise.resolved)
|
|
138
|
+
val outPath = (promise.result as ReadableMap).getString("path")!!
|
|
139
|
+
createdFiles += outPath
|
|
140
|
+
val (w, h) = TestFixtures.decodeDimensions(outPath)
|
|
141
|
+
assertEquals(400, w)
|
|
142
|
+
assertEquals(400, h)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
@Test
|
|
146
|
+
fun resize_pngSourceFile_succeeds() {
|
|
147
|
+
val src = TestFixtures.createPng(reactContext, 120, 80, "source.png")
|
|
148
|
+
createdFiles += src
|
|
149
|
+
|
|
150
|
+
val promise = resize(src, 60.0, 60.0, 80.0)
|
|
151
|
+
|
|
152
|
+
assertTrue(promise.resolved)
|
|
153
|
+
val outPath = (promise.result as ReadableMap).getString("path")!!
|
|
154
|
+
createdFiles += outPath
|
|
155
|
+
assertTrue(File(outPath).exists())
|
|
156
|
+
}
|
|
157
|
+
}
|