store(kClass: KClass<*>, texelCoord: Vec1i, level: Int, texel: T) = super.store(kClass, Vec3i(texelCoord.x, 0, 0), 0, 0, level, texel)
87 | }
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on: [ push ]
4 |
5 | jobs:
6 |
7 | linux:
8 | name: 'Linux'
9 | runs-on: ubuntu-latest
10 |
11 | steps:
12 | - uses: actions/checkout@v3
13 | - uses: gradle/wrapper-validation-action@v1
14 | - name: Set up JDK 1.8
15 | uses: actions/setup-java@v3
16 | with:
17 | distribution: temurin
18 | java-version: 8
19 | - name: Grant execute permission for gradlew
20 | run: chmod +x gradlew
21 | # - name: Build with Gradle
22 | # run: ./gradlew build -x dokkaHtml -x dokkaHtmlJar
23 | - uses: burrunan/gradle-cache-action@v1
24 | name: Build
25 | with:
26 | arguments: assemble #-x dokkaHtml -x dokkaHtmlJar -x javadoc -x dokkaJavadocJar
27 | # - name: Cleanup Gradle Cache
28 | # # Remove some files from the Gradle cache, so they aren't cached by GitHub Actions.
29 | # # Restoring these files from a GitHub Actions cache might cause problems for future builds.
30 | # run: |
31 | # rm -f ~/.gradle/caches/modules-2/modules-2.lock
32 | # rm -f ~/.gradle/caches/modules-2/gc.properties
33 | # - name: Show Working directory content
34 | # run: ls
35 | # - uses: actions/checkout@master
36 | # with:
37 | # repository: kotlin-graphics/mary
38 | # path: ./mary
39 | - name: Show ../.. directory content
40 | run: ls ../..
41 | # - name: move mary up
42 | # run: mv ./mary ../..
43 | # - name: Show Working directory content
44 | # run: ls
45 | # - name: Show ../.. directory content
46 | # run: ls ../..
47 |
48 | windows:
49 | name: 'Windows'
50 | runs-on: windows-latest
51 |
52 | steps:
53 | - uses: actions/checkout@v3
54 | - uses: gradle/wrapper-validation-action@v1
55 | - name: Set up JDK 1.8
56 | uses: actions/setup-java@v3
57 | with:
58 | distribution: temurin
59 | java-version: 8
60 | # - name: Build with Gradle
61 | # run: .\gradlew.bat build -x dokkaHtml -x dokkaHtmlJar
62 | - uses: burrunan/gradle-cache-action@v1
63 | name: Build
64 | with:
65 | arguments: assemble #-x dokkaHtml -x dokkaHtmlJar -x javadoc -x dokkaJavadocJar
66 | # - name: Cleanup Gradle Cache
67 | # # Remove some files from the Gradle cache, so they aren't cached by GitHub Actions.
68 | # # Restoring these files from a GitHub Actions cache might cause problems for future builds.
69 | # run: |
70 | # rm -f ~/.gradle/caches/modules-2/modules-2.lock
71 | # rm -f ~/.gradle/caches/modules-2/gc.properties
72 |
73 | mac:
74 | name: 'Mac OS'
75 | runs-on: macos-latest
76 |
77 | steps:
78 | - uses: actions/checkout@v3
79 | - uses: gradle/wrapper-validation-action@v1
80 | - name: Set up JDK 1.8
81 | uses: actions/setup-java@v3
82 | with:
83 | distribution: temurin
84 | java-version: 8
85 | - name: Grant execute permission for gradlew
86 | run: chmod +x gradlew
87 | # - name: Build with Gradle
88 | # run: ./gradlew build -x dokkaHtml -x dokkaHtmlJar
89 | - uses: burrunan/gradle-cache-action@v1
90 | name: Build
91 | with:
92 | arguments: assemble #-x dokkaHtml -x dokkaHtmlJar -x javadoc -x dokkaJavadocJar
93 | # - name: Cleanup Gradle Cache
94 | # # Remove some files from the Gradle cache, so they aren't cached by GitHub Actions.
95 | # # Restoring these files from a GitHub Actions cache might cause problems for future builds.
96 | # run: |
97 | # rm -f ~/.gradle/caches/modules-2/modules-2.lock
98 | # rm -f ~/.gradle/caches/modules-2/gc.properties
99 |
--------------------------------------------------------------------------------
/src/test/kotlin/gli_/coreAddressing.kt:
--------------------------------------------------------------------------------
1 | package gli_
2 |
3 | import glm_.vec3.Vec3i
4 | import io.kotest.matchers.shouldBe
5 | import io.kotest.core.spec.style.StringSpec
6 |
7 | class coreAddressing : StringSpec() {
8 |
9 | init {
10 |
11 | "layers" {
12 |
13 | class Test(val dimensions: Vec3i, val format: Format, val baseOffset: Int, val size: Int)
14 |
15 | val tests = arrayOf(
16 | Test(Vec3i(4, 4, 1), Format.RGBA8_UINT_PACK8, 64, 128),
17 | Test(Vec3i(4, 4, 1), Format.RGB16_SFLOAT_PACK16, 96, 192),
18 | Test(Vec3i(4, 4, 1), Format.RGBA32_SFLOAT_PACK32, 256, 512),
19 | Test(Vec3i(4, 4, 1), Format.RGBA_DXT1_UNORM_BLOCK8, 8, 16),
20 | Test(Vec3i(8, 8, 1), Format.RGBA_DXT1_UNORM_BLOCK8, 32, 64),
21 | Test(Vec3i(4, 4, 1), Format.R_ATI1N_SNORM_BLOCK8, 8, 16))
22 |
23 | for (test in tests) {
24 |
25 | val storage = Storage(
26 | format = test.format,
27 | extent = test.dimensions,
28 | layers = 2,
29 | faces = 1,
30 | levels = 1)
31 |
32 | val baseOffset = storage.baseOffset(1, 0, 0)
33 | val size = storage.size()
34 |
35 | baseOffset shouldBe test.baseOffset
36 | size shouldBe test.size
37 | }
38 | }
39 |
40 | "faces" {
41 |
42 | class Test(val format: Format, val level: Int, val baseOffset: Int, val size: Int)
43 |
44 | val tests = arrayOf(
45 | Test(Format.RGBA8_UINT_PACK8, 0, 0, 340),
46 | Test(Format.RGBA8_UINT_PACK8, 1, 256, 340),
47 | Test(Format.R8_UINT_PACK8, 1, 64, 85),
48 | Test(Format.RGBA8_UINT_PACK8, 3, 336, 340),
49 | Test(Format.RGBA32_SFLOAT_PACK32, 0, 0, 1360),
50 | Test(Format.RGBA32_SFLOAT_PACK32, 1, 1024, 1360),
51 | Test(Format.RGB_DXT1_UNORM_BLOCK8, 0, 0, 56),
52 | Test(Format.RGB_DXT1_UNORM_BLOCK8, 1, 32, 56),
53 | Test(Format.RGBA_DXT5_UNORM_BLOCK16, 1, 64, 112))
54 |
55 | for (test in tests) {
56 |
57 | val storage = Storage(
58 | format = test.format,
59 | extent = Vec3i(8, 8, 1),
60 | layers = 1,
61 | faces = 1,
62 | levels = 4)
63 |
64 | val baseOffset = storage.baseOffset(0, 0, test.level)
65 | val size = storage.size()
66 |
67 | baseOffset shouldBe test.baseOffset
68 | size shouldBe test.size
69 | }
70 | }
71 |
72 | "levels" {
73 |
74 | class Test(val format: Format, val level: Int, val baseOffset: Int, val size: Int)
75 |
76 | val tests = arrayOf(
77 | Test(Format.RGBA8_UINT_PACK8, 0, 0, 340),
78 | Test(Format.RGBA8_UINT_PACK8, 1, 256, 340),
79 | Test(Format.RGBA8_UINT_PACK8, 3, 336, 340),
80 | Test(Format.RGBA32_SFLOAT_PACK32, 0, 0, 1360),
81 | Test(Format.RGBA32_SFLOAT_PACK32, 1, 1024, 1360),
82 | Test(Format.RGB_DXT1_UNORM_BLOCK8, 0, 0, 56),
83 | Test(Format.RGBA_DXT1_UNORM_BLOCK8, 1, 32, 56))
84 |
85 | for (test in tests) {
86 |
87 | val storage = Storage(
88 | format = test.format,
89 | extent = Vec3i(8, 8, 1),
90 | layers = 1,
91 | faces = 1,
92 | levels = 4)
93 |
94 | val baseOffset = storage.baseOffset(0, 0, test.level)
95 | val size = storage.size()
96 |
97 | baseOffset shouldBe test.baseOffset
98 | size shouldBe test.size
99 | }
100 | }
101 | }
102 | }
--------------------------------------------------------------------------------
/src/test/kotlin/gli_/coreLoadDds.kt:
--------------------------------------------------------------------------------
1 | package gli_
2 |
3 | import io.kotest.matchers.shouldBe
4 | import io.kotest.core.spec.style.StringSpec
5 | import java.nio.file.Files
6 |
7 | /**
8 | * Created by GBarbieri on 05.04.2017.
9 | */
10 |
11 | class coreLoadDds : StringSpec() {
12 |
13 | init {
14 |
15 | "load dds" {
16 |
17 | class Params(val filename: String, val format: Format)
18 |
19 | fun load(params: Params) {
20 |
21 | val textureA = gli.loadDds(uriOf(params.filename))
22 | textureA.format shouldBe params.format
23 |
24 | gli.saveDds(textureA, params.filename)
25 | val textureB = gli.loadDds(uriOf(params.filename))
26 | textureB.format shouldBe params.format
27 | Files.delete(pathOf(params.filename))
28 |
29 | textureA shouldBe textureB
30 | }
31 |
32 | val params = listOf(
33 | Params("kueken7_bgrx8_unorm.dds", Format.BGR8_UNORM_PACK32),
34 | Params("kueken7_rgba_dxt5_unorm1.dds", Format.RGBA_DXT5_UNORM_BLOCK16),
35 | Params("kueken7_rgba_dxt5_unorm2.dds", Format.RGBA_DXT5_UNORM_BLOCK16),
36 | Params("array_r8_uint.dds", Format.R8_UINT_PACK8),
37 | Params("kueken7_rgba_astc4x4_srgb.dds", Format.RGBA_ASTC_4X4_SRGB_BLOCK16),
38 | Params("kueken7_bgra8_srgb.dds", Format.BGRA8_SRGB_PACK8),
39 | Params("kueken7_r16_unorm.dds", Format.R16_UINT_PACK16),
40 | Params("kueken7_r8_sint.dds", Format.R8_SINT_PACK8),
41 | Params("kueken7_r8_uint.dds", Format.R8_UINT_PACK8),
42 | Params("kueken7_rgba4_unorm.dds", Format.BGRA4_UNORM_PACK16),
43 | Params("kueken7_r5g6b5_unorm.dds", Format.B5G6R5_UNORM_PACK16),
44 | Params("kueken7_rgb5a1_unorm.dds", Format.BGR5A1_UNORM_PACK16),
45 | Params("kueken7_rgba_dxt1_unorm.dds", Format.RGBA_DXT1_UNORM_BLOCK8),
46 | Params("kueken7_rgba_dxt1_srgb.dds", Format.RGBA_DXT1_SRGB_BLOCK8),
47 | Params("kueken8_rgba_dxt1_unorm.dds", Format.RGBA_DXT1_UNORM_BLOCK8),
48 | Params("kueken7_rgba_dxt5_unorm.dds", Format.RGBA_DXT5_UNORM_BLOCK16),
49 | Params("kueken7_rgba_dxt5_srgb.dds", Format.RGBA_DXT5_SRGB_BLOCK16),
50 | Params("kueken7_rgb_etc1_unorm.dds", Format.RGB_ETC_UNORM_BLOCK8),
51 | Params("kueken7_rgb_atc_unorm.dds", Format.RGB_ATC_UNORM_BLOCK8),
52 | Params("kueken7_rgba_atc_explicit_unorm.dds", Format.RGBA_ATCA_UNORM_BLOCK16),
53 | Params("kueken7_rgba_atc_interpolate_unorm.dds", Format.RGBA_ATCI_UNORM_BLOCK16),
54 | Params("kueken7_rgb_pvrtc_2bpp_unorm.dds", Format.RGB_PVRTC1_16X8_UNORM_BLOCK32),
55 | Params("kueken7_rgb_pvrtc_4bpp_unorm.dds", Format.RGB_PVRTC1_8X8_UNORM_BLOCK32),
56 | Params("kueken7_r_ati1n_unorm.dds", Format.R_ATI1N_UNORM_BLOCK8),
57 | Params("kueken7_rg_ati2n_unorm.dds", Format.RG_ATI2N_UNORM_BLOCK16),
58 | Params("kueken7_bgr8_unorm.dds", Format.BGR8_UNORM_PACK8),
59 | Params("kueken7_rgba8_srgb.dds", Format.RGBA8_SRGB_PACK8),
60 | Params("kueken7_bgra8_unorm.dds", Format.BGRA8_UNORM_PACK8),
61 | Params("kueken7_a8_unorm.dds", Format.A8_UNORM_PACK8),
62 | Params("kueken7_l8_unorm.dds", Format.L8_UNORM_PACK8),
63 | Params("kueken7_rgb10a2_unorm.dds", Format.RGB10A2_UNORM_PACK32),
64 | Params("kueken7_rgb10a2u.dds", Format.RGB10A2_UINT_PACK32),
65 | Params("kueken7_rgba8_snorm.dds", Format.RGBA8_SNORM_PACK8),
66 | Params("kueken7_rgba16_sfloat.dds", Format.RGBA16_SFLOAT_PACK16),
67 | Params("kueken7_rg11b10_ufloat.dds", Format.RG11B10_UFLOAT_PACK32),
68 | Params("kueken7_rgb9e5_ufloat.dds", Format.RGB9E5_UFLOAT_PACK32)
69 | )
70 |
71 | params.forEach { load(it) }
72 | }
73 | }
74 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.toptal.com/developers/gitignore/api/java,gradle,intellij,kotlin
2 | # Edit at https://www.toptal.com/developers/gitignore?templates=java,gradle,intellij,kotlin
3 |
4 | ### Intellij ###
5 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
6 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
7 |
8 | # User-specific stuff
9 | .idea/**/workspace.xml
10 | .idea/**/tasks.xml
11 | .idea/**/usage.statistics.xml
12 | .idea/**/dictionaries
13 | .idea/**/shelf
14 |
15 | # Generated files
16 | .idea/**/contentModel.xml
17 |
18 | # Sensitive or high-churn files
19 | .idea/**/dataSources/
20 | .idea/**/dataSources.ids
21 | .idea/**/dataSources.local.xml
22 | .idea/**/sqlDataSources.xml
23 | .idea/**/dynamic.xml
24 | .idea/**/uiDesigner.xml
25 | .idea/**/dbnavigator.xml
26 |
27 | # Gradle
28 | .idea/**/gradle.xml
29 | .idea/**/libraries
30 |
31 | # Gradle and Maven with auto-import
32 | # When using Gradle or Maven with auto-import, you should exclude module files,
33 | # since they will be recreated, and may cause churn. Uncomment if using
34 | # auto-import.
35 | .idea/artifacts
36 | .idea/compiler.xml
37 | .idea/jarRepositories.xml
38 | .idea/modules.xml
39 | .idea/*.iml
40 | .idea/modules
41 | *.iml
42 | *.ipr
43 |
44 | # CMake
45 | cmake-build-*/
46 |
47 | # Mongo Explorer plugin
48 | .idea/**/mongoSettings.xml
49 |
50 | # File-based project format
51 | *.iws
52 |
53 | # IntelliJ
54 | out/
55 |
56 | # mpeltonen/sbt-idea plugin
57 | .idea_modules/
58 |
59 | # JIRA plugin
60 | atlassian-ide-plugin.xml
61 |
62 | # Cursive Clojure plugin
63 | .idea/replstate.xml
64 |
65 | # Crashlytics plugin (for Android Studio and IntelliJ)
66 | com_crashlytics_export_strings.xml
67 | crashlytics.properties
68 | crashlytics-build.properties
69 | fabric.properties
70 |
71 | # Editor-based Rest Client
72 | .idea/httpRequests
73 |
74 | # Android studio 3.1+ serialized cache file
75 | .idea/caches/build_file_checksums.ser
76 |
77 | ### Intellij Patch ###
78 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
79 |
80 | # *.iml
81 | # modules.xml
82 | # .idea/misc.xml
83 | # *.ipr
84 |
85 | # Sonarlint plugin
86 | # https://plugins.jetbrains.com/plugin/7973-sonarlint
87 | .idea/**/sonarlint/
88 |
89 | # SonarQube Plugin
90 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
91 | .idea/**/sonarIssues.xml
92 |
93 | # Markdown Navigator plugin
94 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
95 | .idea/**/markdown-navigator.xml
96 | .idea/**/markdown-navigator-enh.xml
97 | .idea/**/markdown-navigator/
98 |
99 | # Cache file creation bug
100 | # See https://youtrack.jetbrains.com/issue/JBR-2257
101 | .idea/$CACHE_FILE$
102 |
103 | # CodeStream plugin
104 | # https://plugins.jetbrains.com/plugin/12206-codestream
105 | .idea/codestream.xml
106 |
107 | ### Java ###
108 | # Compiled class file
109 | *.class
110 |
111 | # Log file
112 | *.log
113 |
114 | # BlueJ files
115 | *.ctxt
116 |
117 | # Mobile Tools for Java (J2ME)
118 | .mtj.tmp/
119 |
120 | # Package Files #
121 | *.jar
122 | *.war
123 | *.nar
124 | *.ear
125 | *.zip
126 | *.tar.gz
127 | *.rar
128 |
129 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
130 | hs_err_pid*
131 |
132 | ### Kotlin ###
133 | # Compiled class file
134 |
135 | # Log file
136 |
137 | # BlueJ files
138 |
139 | # Mobile Tools for Java (J2ME)
140 |
141 | # Package Files #
142 |
143 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
144 |
145 | ### Gradle ###
146 | .gradle
147 | build/
148 |
149 | # Ignore Gradle GUI config
150 | gradle-app.setting
151 |
152 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
153 | !gradle-wrapper.jar
154 |
155 | # Cache of project
156 | .gradletasknamecache
157 |
158 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
159 | # gradle/wrapper/gradle-wrapper.properties
160 |
161 | ### Gradle Patch ###
162 | **/build/
163 |
164 | # End of https://www.toptal.com/developers/gitignore/api/java,gradle,intellij,kotlin
--------------------------------------------------------------------------------
/src/main/kotlin/gli_/saveKtx.kt:
--------------------------------------------------------------------------------
1 | package gli_
2 |
3 | import glm_.BYTES
4 | import glm_.glm
5 | import kool.*
6 | import java.nio.channels.FileChannel
7 | import java.nio.file.Path
8 | import java.nio.file.StandardOpenOption
9 |
10 | interface saveKtx {
11 |
12 | /** Save a texture storage_linear to a KTX file.
13 | * @param texture Source texture to save
14 | * @param path Path for where to save the file. It must include the filaname and filename extension.
15 | * This function ignores the filename extension in the path and save to KTX anyway but keep the requested
16 | * filename extension.
17 | * @return Returns false if the function fails to save the file. */
18 | fun saveKtx(texture: Texture, path: Path): Boolean {
19 |
20 | if (texture.empty()) return false
21 |
22 | gl.profile = gl.Profile.KTX
23 | val format = gl.translate(texture.format, texture.swizzles)
24 | val target = texture.target
25 |
26 | val desc = texture.format.formatInfo
27 |
28 | val data = Buffer(computeKtxStorageSize(texture))
29 |
30 | ktx.FOURCC_KTX10.forEach{ data.put(it) }
31 |
32 | ktx.Header10().apply {
33 |
34 | endianness = 0x04030201
35 | glType = format.type.i
36 | glTypeSize = if (format.type == gl.TypeFormat.NONE) 1 else desc.blockSize
37 | glFormat = format.external.i
38 | glInternalFormat = format.internal.i
39 | glBaseInternalFormat = format.external.i
40 | pixelWidth = texture.extent().x
41 | pixelHeight = if (!target.isTarget1d) texture.extent().y else 0
42 | pixelDepth = if (target == Target._3D) texture.extent().z else 0
43 | numberOfArrayElements = if (target.isTargetArray) texture.layers() else 0
44 | numberOfFaces = if (target.isTargetCube) texture.faces() else 1
45 | numberOfMipmapLevels = texture.levels()
46 | bytesOfKeyValueData = 0
47 |
48 | to(data)
49 | }
50 |
51 | for (level in 0 until texture.levels()) {
52 |
53 | var imageSize = data.pos
54 | data.pos += Int.BYTES
55 |
56 | for (layer in 0 until texture.layers())
57 | for (face in 0 until texture.faces()) {
58 |
59 | val faceSize = texture.size(level)
60 |
61 | memCopy(texture.data(layer, face, level).adr, data.adr, faceSize)
62 |
63 | val paddedSize = glm.ceilMultiple(faceSize, 4)
64 |
65 | imageSize += paddedSize
66 | data.pos += paddedSize
67 |
68 | assert(data.pos <= data.cap)
69 | }
70 |
71 | imageSize = glm.ceilMultiple(imageSize, 4)
72 | }
73 |
74 | FileChannel.open(path, StandardOpenOption.CREATE, StandardOpenOption.WRITE).use {
75 | data.pos = 0
76 | while (data.hasRemaining()) it.write(data)
77 | }
78 |
79 | data.free()
80 |
81 | return true
82 | }
83 |
84 | /** Save a texture storage_linear to a KTX file.
85 | * @param texture Source texture to save
86 | * @param filename String for where to save the file. It must include the filaname and filename extension.
87 | * This function ignores the filename extension in the path and save to KTX anyway but keep the requested
88 | * filename extension.
89 | * @return Returns false if the function fails to save the file. */
90 | fun saveKtx(texture: Texture, filename: String) = saveKtx(texture, pathOf(filename))
91 |
92 | fun computeKtxStorageSize(texture: Texture): Int {
93 |
94 | val blockSize = texture.format.blockSize
95 | var totalSize = ktx.FOURCC_KTX10.size + ktx.Header10.size
96 |
97 | for (level in 0 until texture.levels()) {
98 |
99 | totalSize += Int.BYTES
100 |
101 | for (layer in 0 until texture.layers())
102 | for (face in 0 until texture.faces()) {
103 |
104 | val faceSize = texture.size(level)
105 | val paddedSize = glm.max(blockSize, glm.ceilMultiple(faceSize, 4))
106 |
107 | totalSize += paddedSize
108 | }
109 | }
110 | return totalSize
111 | }
112 | }
--------------------------------------------------------------------------------
/src/test/kotlin/gli_/coreSave.kt:
--------------------------------------------------------------------------------
1 | package gli_
2 |
3 | import glm_.vec1.Vec1b
4 | import glm_.vec2.Vec2b
5 | import glm_.vec2.Vec2i
6 | import glm_.vec3.Vec3b
7 | import glm_.vec4.Vec4b
8 | import io.kotest.matchers.shouldBe
9 | import io.kotest.core.spec.style.StringSpec
10 | import java.nio.file.Files
11 |
12 | class coreSave : StringSpec() {
13 |
14 | init {
15 |
16 | "l8 unorm" {
17 |
18 | val texture = Texture2d(Format.L8_UNORM_PACK8, Vec2i(4))
19 | texture clear Vec1b(127)
20 |
21 | val dds = "orange_l8_unorm.dds"
22 | gli.save(texture, dds)
23 | val textureL8unormDDS = gli.load(dds)
24 | Files.delete(pathOf(dds))
25 |
26 | val ktx = "orange_l8_unorm.ktx"
27 | gli.save(texture, ktx)
28 | val textureL8unormKTX = gli.load(ktx)
29 | Files.delete(pathOf(ktx))
30 |
31 | texture shouldBe textureL8unormDDS
32 | texture shouldBe textureL8unormKTX
33 | textureL8unormDDS shouldBe textureL8unormKTX
34 | }
35 |
36 | "la8 unorm" {
37 |
38 | val texture = Texture2d(Format.LA8_UNORM_PACK8, Vec2i(4))
39 | texture clear Vec2b(255, 127)
40 |
41 | val dds = "orange_la8_unorm.dds"
42 | gli.save(texture, dds)
43 | val textureLA8unormDDS = gli.load(dds)
44 | Files.delete(pathOf(dds))
45 |
46 | val ktx = "orange_la8_unorm.ktx"
47 | gli.save(texture, ktx)
48 | val textureLA8unormKTX = gli.load(ktx)
49 | Files.delete(pathOf(ktx))
50 |
51 | texture shouldBe textureLA8unormDDS
52 | texture shouldBe textureLA8unormKTX
53 | textureLA8unormDDS shouldBe textureLA8unormKTX
54 | }
55 |
56 | "rgba8 unorm" {
57 |
58 | val texture = Texture2d(Format.RGBA8_UNORM_PACK8, Vec2i(4))
59 | texture clear Vec4b(255, 127, 0, 255)
60 |
61 | val dds = "orange_rgba8_unorm.dds"
62 | gli.save(texture, dds)
63 | val textureRGBA8unormDDS = gli.load(dds)
64 | Files.delete(pathOf(dds))
65 |
66 | val ktx = "orange_rgba8_unorm.ktx"
67 | gli.save(texture, ktx)
68 | val textureRGBA8unormKTX = gli.load(ktx)
69 | Files.delete(pathOf(ktx))
70 |
71 | texture shouldBe textureRGBA8unormDDS
72 | texture shouldBe textureRGBA8unormKTX
73 | textureRGBA8unormDDS shouldBe textureRGBA8unormKTX
74 | }
75 |
76 | "using ImageIO" {
77 | // without alpha
78 | run {
79 | val texture = Texture2d(Format.RGB8_UNORM_PACK8, Vec2i(4)) // no alpha, jpeg cant handle that
80 | texture clear Vec3b(255, 127, 255)
81 |
82 | // ico and bmp cant handle not 4 component images
83 | // icns and wbmp always fail
84 | val extensions = listOf(/*"bmp",*/ "gif", /*"ico",*/ /*"icns",*/ "iff", "jpeg", "jpg",
85 | "pam", "pbm", "pct", "pgm", "pict", "png", "pnm", "ppm", "targa", "tga", "tif", "tiff"/*, "wbmp"*/)
86 |
87 | for (ext in extensions) {
88 | val filename = "temp.$ext"
89 | gli.save(texture, filename) shouldBe true
90 |
91 | gli.load(filename)
92 |
93 | Files.delete(pathOf(filename))
94 | }
95 | }
96 | // with alpha
97 | run {
98 | val texture = Texture2d(Format.RGBA8_UNORM_PACK8, Vec2i(4))
99 | texture clear Vec4b(255, 127, 255, 255)
100 |
101 | // jpeg cant handle transparency,
102 | // icns, targa and wbmp always fail
103 | val extensions = listOf("bmp", "gif", "ico", /*"icns",*/ "iff", /*"jpeg", "jpg",*/
104 | "pam", /*"pbm",*/ "pct", /*"pgm",*/ "pict", "png", /*"pnm", "ppm", "targa", "tga",*/ "tif", "tiff"/*, "wbmp"*/)
105 |
106 | for (ext in extensions) {
107 | val filename = "temp.$ext"
108 | gli.save(texture, filename) shouldBe true
109 |
110 | gli.load(filename)
111 |
112 | Files.delete(pathOf(filename))
113 | }
114 | }
115 | }
116 | }
117 | }
--------------------------------------------------------------------------------
/src/main/kotlin/gli_/ByteBufferBackedInputStream.kt:
--------------------------------------------------------------------------------
1 | package gli_
2 |
3 | import java.io.*
4 | import java.nio.*
5 |
6 | class ByteBufferBackedInputStream(val buf: ByteBuffer) : InputStream() {
7 |
8 | /**
9 | * Reads the next byte of data from the input stream. The value byte is
10 | * returned as an int in the range 0 to
11 | * 255. If no byte is available because the end of the stream
12 | * has been reached, the value -1 is returned. This method
13 | * blocks until input data is available, the end of the stream is detected,
14 | * or an exception is thrown.
15 | *
16 | * A subclass must provide an implementation of this method.
17 | *
18 | * @return the next byte of data, or -1 if the end of the
19 | * stream is reached.
20 | * @exception IOException if an I/O error occurs.
21 | */
22 | override fun read(): Int {
23 | return if (!buf.hasRemaining()) {
24 | -1
25 | } else buf.get().toInt() and 0xFF
26 | }
27 |
28 | /**
29 | * Reads up to len bytes of data from the input stream into
30 | * an array of bytes. An attempt is made to read as many as
31 | * len bytes, but a smaller number may be read.
32 | * The number of bytes actually read is returned as an integer.
33 | *
34 | *
This method blocks until input data is available, end of file is
35 | * detected, or an exception is thrown.
36 | *
37 | *
If len is zero, then no bytes are read and
38 | * 0 is returned; otherwise, there is an attempt to read at
39 | * least one byte. If no byte is available because the stream is at end of
40 | * file, the value -1 is returned; otherwise, at least one
41 | * byte is read and stored into b.
42 | *
43 | *
The first byte read is stored into element b[off], the
44 | * next one into b[off+1], and so on. The number of bytes read
45 | * is, at most, equal to len. Let k be the number of
46 | * bytes actually read; these bytes will be stored in elements
47 | * b[off] through b[off+k-1],
48 | * leaving elements b[off+k] through
49 | * b[off+len-1] unaffected.
50 | *
51 | *
In every case, elements b[0] through
52 | * b[off] and elements b[off+len] through
53 | * b[b.length-1] are unaffected.
54 | *
55 | *
The read(b, off, len) method
56 | * for class InputStream simply calls the method
57 | * read() repeatedly. If the first such call results in an
58 | * IOException, that exception is returned from the call to
59 | * the read(b, off, len) method. If
60 | * any subsequent call to read() results in a
61 | * IOException, the exception is caught and treated as if it
62 | * were end of file; the bytes read up to that point are stored into
63 | * b and the number of bytes read before the exception
64 | * occurred is returned. The default implementation of this method blocks
65 | * until the requested amount of input data len has been read,
66 | * end of file is detected, or an exception is thrown. Subclasses are encouraged
67 | * to provide a more efficient implementation of this method.
68 | *
69 | * @param off the start offset in array b
70 | * at which the data is written.
71 | * @param len the maximum number of bytes to read.
72 | * @return the total number of bytes read into the buffer, or
73 | * -1 if there is no more data because the end of
74 | * the stream has been reached.
75 | * @exception IOException If the first byte cannot be read for any reason
76 | * other than end of file, or if the input stream has been closed, or if
77 | * some other I/O error occurs.
78 | * @exception NullPointerException If b is null.
79 | * @exception IndexOutOfBoundsException If off is negative,
80 | * len is negative, or len is greater than
81 | * b.length - off
82 | * @see java.io.InputStream#read()
83 | */
84 | override fun read(bytes: ByteArray, off: Int, len: Int): Int {
85 | @Suppress("NAME_SHADOWING")
86 | var len = len
87 | if (!buf.hasRemaining()) {
88 | return -1
89 | }
90 |
91 | len = Math.min(len, buf.remaining())
92 | buf.get(bytes, off, len)
93 | return len
94 | }
95 | }
--------------------------------------------------------------------------------
/src/test/kotlin/gli_/testCopySub.kt:
--------------------------------------------------------------------------------
1 | package gli_
2 |
3 | import glm_.b
4 | import glm_.vec2.Vec2i
5 | import glm_.vec3.Vec3
6 | import glm_.vec3.Vec3i
7 | import glm_.vec4.Vec4ub
8 | import io.kotest.matchers.shouldBe
9 | import io.kotest.matchers.shouldNotBe
10 | import io.kotest.core.spec.style.StringSpec
11 |
12 | class testCopySub : StringSpec() {
13 |
14 | init {
15 |
16 | "sub copy" {
17 |
18 | val source = Texture2d(Format.R8_UNORM_PACK8, Vec2i(4), 1)
19 | source clear 0.b
20 | source.store(Vec2i(1, 1), 0, 1.b)
21 | source.store(Vec2i(2, 1), 0, 2.b)
22 | source.store(Vec2i(2, 2), 0, 3.b)
23 | source.store(Vec2i(1, 2), 0, 4.b)
24 |
25 | val destination = Texture2d(source.format, source.extent_(), source.levels())
26 | destination clear 255.b
27 |
28 | destination.copy(source, 0, 0, 0, Vec3i(1, 1, 0), 0, 0, 0,
29 | Vec3i(1, 1, 0), Vec3i(2, 2, 1))
30 | for (indexY in 1..2)
31 | for (indexX in 1..2)
32 | source.load(Vec2i(indexX, indexY), 0) shouldBe destination.load(Vec2i(indexX, indexY), 0)
33 | }
34 |
35 | "sub copy2" {
36 |
37 | val source = Texture2d(Format.R8_UNORM_PACK8, Vec2i(5, 4), 1)
38 | source clear 0.b
39 | source.store(Vec2i(1, 1), 0, 1.b)
40 | source.store(Vec2i(2, 1), 0, 2.b)
41 | source.store(Vec2i(2, 2), 0, 3.b)
42 | source.store(Vec2i(1, 2), 0, 4.b)
43 |
44 | val destination = Texture2d(source.format, source.extent_(), source.levels())
45 | destination clear 255.b
46 |
47 | destination.copy(source, 0, 0, 0, Vec3i(1, 1, 0), 0, 0, 0,
48 | Vec3i(1, 1, 0), Vec3i(2, 2, 1))
49 | for (indexY in 0 until source.extent().y)
50 | for (indexX in 0 until source.extent().x) {
51 | val texelCoord = Vec2i(indexX, indexY)
52 | val texelSrc = source.load(texelCoord, 0)
53 | val texelDst = destination.load(texelCoord, 0)
54 | (texelSrc == texelDst || (texelSrc == 0.b && texelDst == 255.b)) shouldBe true
55 | }
56 | }
57 |
58 | "sub copy rgb32f" {
59 |
60 | val source = Texture2d(Format.RGB32_SFLOAT_PACK32, Vec2i(4, 2), 1)
61 | for (texelIndex in 0 until source.size())
62 | source.data()[texelIndex] = Vec3(texelIndex + 1)
63 |
64 | val destination = Texture2d(source.format, source.extent_(), source.levels())
65 | destination clear Vec3(255)
66 |
67 | destination.copy(source, 0, 0, 0, Vec3i(1, 1, 0), 0, 0, 0,
68 | Vec3i(1, 1, 0), Vec3i(2, 1, 1))
69 | for (indexY in 0 until source.extent().y)
70 | for (indexX in 0 until source.extent().x) {
71 | val texelCoord = Vec2i(indexX, indexY)
72 | val texelSrc = source.load(texelCoord, 0)
73 | val texelDst = destination.load(texelCoord, 0)
74 | (texelSrc == texelDst || (/*texelSrc == gli::u8vec4(0) &&*/ texelDst == Vec3(255))) shouldBe true
75 | }
76 | }
77 |
78 | "sub copy rgba8" {
79 |
80 | val source = Texture2d(Format.RGBA8_UNORM_PACK8, Vec2i(4, 2), 1)
81 | for (texelIndex in 0 until source.size())
82 | source.data()[texelIndex] = Vec4ub(texelIndex + 1)
83 |
84 | val destination = Texture2d(source.format, source.extent_(), source.levels())
85 | destination clear Vec4ub(255)
86 |
87 | destination.copy(source, 0, 0, 0, Vec3i(1, 1, 0), 0, 0, 0,
88 | Vec3i(1, 1, 0), Vec3i(2, 1, 1))
89 | for (indexY in 0 until source.extent().y)
90 | for (indexX in 0 until source.extent().x) {
91 | val texelCoord = Vec2i(indexX, indexY)
92 | val texelSrc = source.load(texelCoord, 0)
93 | val texelDst = destination.load(texelCoord, 0)
94 | (texelSrc == texelDst || (/*texelSrc == gli::u8vec4(0) &&*/ texelDst == Vec4ub(255))) shouldBe true
95 | }
96 | }
97 |
98 | "sub clear rgba8" {
99 |
100 | val clear = Texture2d(Format.RGBA8_UNORM_PACK8, Vec2i(4, 2), 1)
101 | clear clear Vec4ub(0)
102 |
103 | val source = Texture2d(Format.RGBA8_UNORM_PACK8, Vec2i(4, 2), 1)
104 | source clear Vec4ub(0)
105 | source.clear(0, 0, 0, Vec3i(1, 1, 0), Vec3i(2, 1, 1), Vec4ub(255))
106 |
107 | source shouldNotBe clear
108 |
109 | val destination = Texture2d(source.format, source.extent_(), source.levels())
110 | destination clear Vec4ub(0)
111 | destination.copy(source, 0, 0, 0, Vec3i(1, 1, 0), 0, 0, 0,
112 | Vec3i(1, 1, 0), Vec3i(2, 1, 1))
113 |
114 | destination shouldNotBe clear
115 |
116 | source shouldBe destination
117 | }
118 | }
119 | }
--------------------------------------------------------------------------------
/src/test/kotlin/gli_/coreLoad.kt:
--------------------------------------------------------------------------------
1 | package gli_
2 |
3 | import io.kotest.matchers.shouldBe
4 | import io.kotest.core.spec.style.StringSpec
5 | import org.lwjgl.BufferUtils
6 | import java.io.FileInputStream
7 | import java.nio.ByteBuffer
8 | import java.nio.file.Files
9 | import java.nio.file.Paths
10 |
11 |
12 | class coreLoad : StringSpec() {
13 |
14 | init {
15 |
16 | "load" {
17 |
18 | class Params(val filename: String, val format: Format)
19 |
20 | fun loadFileKtx(params: Params) {
21 |
22 | val ktx = params.filename + ".ktx"
23 | val textureKTX = gli.load(uriOf(ktx))
24 | textureKTX.format shouldBe params.format
25 |
26 | val dds = params.filename + ".dds"
27 | gli.save(textureKTX, dds)
28 | val textureSavedDDS = gli.load(dds)
29 | textureSavedDDS.format shouldBe params.format
30 | textureSavedDDS shouldBe textureKTX
31 | Files.delete(pathOf(dds))
32 |
33 | gli.save(textureKTX, ktx)
34 | val textureSavedKTX = gli.load(ktx)
35 | textureSavedKTX.format shouldBe params.format
36 | textureSavedDDS shouldBe textureSavedKTX
37 | Files.delete(pathOf(ktx))
38 | }
39 |
40 | fun loadFileKmg(params: Params) {
41 |
42 | val ktx = params.filename + ".ktx"
43 | val textureKTX = gli.load(uriOf(ktx))
44 | textureKTX.format shouldBe params.format
45 |
46 | val kmg = params.filename + ".kmg"
47 | gli.save(textureKTX, kmg)
48 | val textureSavedKMG = gli.load(kmg)
49 | textureSavedKMG.format shouldBe params.format
50 | textureSavedKMG shouldBe textureKTX
51 | Files.delete(pathOf(kmg))
52 |
53 | gli.save(textureKTX, ktx)
54 | val textureSavedKTX = gli.load(ktx)
55 | textureSavedKTX.format shouldBe params.format
56 | textureSavedKTX shouldBe textureKTX
57 | Files.delete(pathOf(ktx))
58 | }
59 |
60 | fun loaFileDds(params: Params) {
61 |
62 | val dds = params.filename + ".dds"
63 | val textureDDS = gli.load(uriOf(dds))
64 | textureDDS.format == params.format
65 |
66 | val kmg = params.filename + ".kmg"
67 | gli.save(textureDDS, kmg)
68 | val textureSavedKMG = gli.load(kmg)
69 | Files.delete(pathOf(kmg))
70 |
71 | textureSavedKMG.format shouldBe params.format
72 | textureSavedKMG shouldBe textureDDS
73 |
74 | gli.save(textureDDS, dds)
75 | val textureSavedDDS = gli.load(dds)
76 | Files.delete(pathOf(dds))
77 |
78 | textureSavedDDS.format shouldBe params.format
79 | textureSavedDDS shouldBe textureDDS
80 | }
81 |
82 | val params = listOf(
83 | Params("array_r8_uint", Format.R8_UINT_PACK8),
84 | Params("kueken7_rgba8_unorm", Format.RGBA8_UNORM_PACK8),
85 | Params("kueken7_rgba8_srgb", Format.RGBA8_SRGB_PACK8),
86 | Params("kueken7_bgra8_unorm", Format.BGRA8_UNORM_PACK8),
87 | Params("kueken7_bgra8_srgb", Format.BGRA8_SRGB_PACK8),
88 | Params("kueken7_r5g6b5_unorm", Format.B5G6R5_UNORM_PACK16),
89 | Params("kueken7_rgba4_unorm", Format.BGRA4_UNORM_PACK16),
90 | Params("kueken7_rgb5a1_unorm", Format.BGR5A1_UNORM_PACK16),
91 | Params("kueken8_rgba8_srgb", Format.RGBA8_SRGB_PACK8),
92 | Params("kueken7_rgba_dxt5_unorm", Format.RGBA_DXT5_UNORM_BLOCK16))
93 |
94 | params.forEach {
95 | loadFileKtx(it)
96 | loadFileKmg(it)
97 | loaFileDds(it)
98 | }
99 | }
100 |
101 | "loadPng" {
102 | val filename = "kueken7_srgb8.png"
103 |
104 | gli.load(uriOf(filename))
105 | }
106 |
107 | "loadTga" {
108 | val filename = "PlyonTexture.tga"
109 |
110 | gli.load(uriOf(filename))
111 | }
112 |
113 | "loadJpg" {
114 | val files = listOf("kueken7_rgb8.jpg", "kueken7_srgb8.jpg", "kueken8_srgb8.jpg")
115 | for(file in files) {
116 | gli.load(uriOf(file))
117 | }
118 | }
119 |
120 | "loadFromMemory" {
121 | val files = listOf("kueken7_srgb8.png",
122 | "kueken7_rgb8.jpg",
123 | "kueken7_srgb8.jpg",
124 | "kueken8_srgb8.jpg")
125 |
126 | for(file in files) {
127 |
128 | println("loading $file from mem")
129 |
130 | val uri = uriOf(file)
131 | val path = Paths.get(uri).toAbsolutePath().toString()
132 |
133 | val bytes = FileInputStream(path).use { it.readBytes() }
134 | val buffer = ByteBuffer.wrap(bytes)
135 |
136 | gli.load(buffer, file.substringAfterLast('.'))
137 | }
138 | }
139 | }
140 | }
--------------------------------------------------------------------------------
/src/test/kotlin/gli_/make_texture.kt:
--------------------------------------------------------------------------------
1 | package gli_
2 |
3 | import glm_.vec1.Vec1i
4 | import glm_.vec2.Vec2i
5 | import glm_.vec3.Vec3i
6 | import glm_.vec4.Vec4ub
7 | import io.kotest.matchers.shouldBe
8 | import io.kotest.core.spec.style.StringSpec
9 |
10 | class make_texture : StringSpec() {
11 |
12 | init {
13 |
14 | "is make_texture equivalent to ctor" {
15 |
16 | run {
17 | val textureA = gli.makeTexture1d(Format.RGBA8_UNORM_PACK8, Vec1i(4), 2)
18 | textureA clear Vec4ub(255, 127, 0, 255)
19 |
20 | val textureB = Texture1d(Format.RGBA8_UNORM_PACK8, Vec1i(4), 2)
21 | textureB clear Vec4ub(255, 127, 0, 255)
22 |
23 | val extentC = Vec3i(4, 1, 1)
24 | val textureC = Texture(Target._1D, Format.RGBA8_UNORM_PACK8, extentC, 1, 1, 2)
25 | textureC clear Vec4ub(255, 127, 0, 255)
26 |
27 | textureA shouldBe textureB
28 | textureA shouldBe textureC
29 | textureB shouldBe textureC
30 | }
31 |
32 | run {
33 | val textureA = gli.makeTexture1dArray(Format.RGBA8_UNORM_PACK8, Vec1i(4), 3, 2)
34 | textureA clear Vec4ub(255, 127, 0, 255)
35 |
36 | val textureB = Texture1dArray(Format.RGBA8_UNORM_PACK8, Vec1i(4), 3, 2)
37 | textureB clear Vec4ub(255, 127, 0, 255)
38 |
39 | val extentC = Vec3i(4, 1, 1)
40 | val textureC = Texture(Target._1D_ARRAY, Format.RGBA8_UNORM_PACK8, extentC, 3, 1, 2)
41 | textureC clear Vec4ub(255, 127, 0, 255)
42 |
43 | textureA shouldBe textureB
44 | textureA shouldBe textureC
45 | textureB shouldBe textureC
46 | }
47 |
48 | run {
49 | val textureA = gli.makeTexture2d(Format.RGBA8_UNORM_PACK8, Vec2i(4), 2)
50 | textureA clear Vec4ub(255, 127, 0, 255)
51 |
52 | val textureB = Texture2d(Format.RGBA8_UNORM_PACK8, Vec2i(4), 2)
53 | textureB clear Vec4ub(255, 127, 0, 255)
54 |
55 | val extentC = Vec3i(4, 4, 1)
56 | val textureC = Texture(Target._2D, Format.RGBA8_UNORM_PACK8, extentC, 1, 1, 2)
57 | textureC clear Vec4ub(255, 127, 0, 255)
58 |
59 | textureA shouldBe textureB
60 | textureA shouldBe textureC
61 | textureB shouldBe textureC
62 | }
63 |
64 | run {
65 | val textureA = gli.makeTexture2dArray(Format.RGBA8_UNORM_PACK8, Vec2i(4), 3, 2)
66 | textureA clear Vec4ub(255, 127, 0, 255)
67 |
68 | val textureB = Texture2dArray(Format.RGBA8_UNORM_PACK8, Vec2i(4), 3, 2)
69 | textureB clear Vec4ub(255, 127, 0, 255)
70 |
71 | val extentC = Vec3i(4, 4, 1)
72 | val textureC = Texture(Target._2D_ARRAY, Format.RGBA8_UNORM_PACK8, extentC, 3, 1, 2)
73 | textureC clear Vec4ub(255, 127, 0, 255)
74 |
75 | textureA shouldBe textureB
76 | textureA shouldBe textureC
77 | textureB shouldBe textureC
78 | }
79 |
80 | run {
81 | val textureA = gli.makeTexture3d(Format.RGBA8_UNORM_PACK8, Vec3i(4), 2)
82 | textureA clear Vec4ub(255, 127, 0, 255)
83 |
84 | val textureB = Texture3d(Format.RGBA8_UNORM_PACK8, Vec3i(4), 2)
85 | textureB clear Vec4ub(255, 127, 0, 255)
86 |
87 | val extentC = Vec3i(4)
88 | val textureC = Texture(Target._3D, Format.RGBA8_UNORM_PACK8, extentC, 1, 1, 2)
89 | textureC clear Vec4ub(255, 127, 0, 255)
90 |
91 | textureA shouldBe textureB
92 | textureA shouldBe textureC
93 | textureB shouldBe textureC
94 | }
95 |
96 | run {
97 | val textureA = gli.makeTextureCube(Format.RGBA8_UNORM_PACK8, Vec2i(4), 2)
98 | textureA clear Vec4ub(255, 127, 0, 255)
99 |
100 | val textureB = TextureCube(Format.RGBA8_UNORM_PACK8, Vec2i(4), 2)
101 | textureB clear Vec4ub(255, 127, 0, 255)
102 |
103 | val extentC = Vec3i(4, 4, 1)
104 | val textureC = Texture(Target.CUBE, Format.RGBA8_UNORM_PACK8, extentC, 1, 6, 2)
105 | textureC clear Vec4ub(255, 127, 0, 255)
106 |
107 | textureA shouldBe textureB
108 | textureA shouldBe textureC
109 | textureB shouldBe textureC
110 | }
111 |
112 | run {
113 | val textureA = gli.makeTextureCubeArray(Format.RGBA8_UNORM_PACK8, Vec2i(4), 3, 2)
114 | textureA clear Vec4ub(255, 127, 0, 255)
115 |
116 | val textureB = TextureCubeArray(Format.RGBA8_UNORM_PACK8, Vec2i(4), 3, 2)
117 | textureB clear Vec4ub(255, 127, 0, 255)
118 |
119 | val extentC = Vec3i(4, 4, 1)
120 | val textureC = Texture(Target.CUBE_ARRAY, Format.RGBA8_UNORM_PACK8, extentC, 3, 6, 2)
121 | textureC clear Vec4ub(255, 127, 0, 255)
122 |
123 | textureA shouldBe textureB
124 | textureA shouldBe textureC
125 | textureB shouldBe textureC
126 | }
127 | }
128 | }
129 | }
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn ( ) {
37 | echo "$*"
38 | }
39 |
40 | die ( ) {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save ( ) {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | [](https://github.com/kotlin-graphics/gli/actions?workflow=build)
4 | [](https://github.com/kotlin-graphics/gli/blob/master/LICENSE)
5 | [](https://jitpack.io/#kotlin-graphics/gli)
6 | 
7 | []()
8 | [](https://github.com/KotlinBy/awesome-kotlin)
9 |
10 | This is the Kotlin port of the original [OpenGL Image](http://gli.g-truc.net/) (*GLI*), written by [g-truc](https://github.com/Groovounet) ([repository](https://github.com/g-truc/gli)), a header only C++ image library for graphics software.
11 |
12 | *GLI* provides classes and functions to load image files, facilitate graphics APIs texture creation, compare textures, access texture texels, sample textures, convert textures, generate mipmaps, etc.
13 |
14 | This library works perfectly with *[OpenGL](https://www.opengl.org)* or *[Vulkan](https://www.khronos.org/vulkan)* but it also ensures interoperability with other third party libraries and SDK.
15 | It is a good candidate for software rendering (raytracing / rasterization), image processing, image based software testing or any development context that requires a simple and convenient image library.
16 |
17 | Don't hesitate to contribute to the project by submitting [issues](https://github.com/kotlin-graphics/gli/issues) or [pull requests](https://github.com/kotlin-graphics/gli/pulls) for bugs and features. Any feedback is welcome at [elect86@gmail.com](mailto://elect86@gmail.com).
18 |
19 | Kotlin:
20 | ```kotlin
21 | import gli_.gli
22 |
23 | fun createTexture(filename: String): Int {
24 |
25 | val texture = gli.load(filename)
26 | if(texture.empty())
27 | return 0
28 |
29 | gli.gl.profile = gl.Profile.GL33
30 | val format = gli.gl.translate(texture.format, texture.swizzles)
31 | val target = gli.gl.translate(texture.target)
32 | assert(texture.format.isCompressed && target == gl.Target._2D)
33 |
34 | val textureName = intBufferBig(1)
35 | glGenTextures(textureName)
36 | glBindTexture(target.i, textureName[0])
37 | glTexParameteri(target.i, GL_TEXTURE_BASE_LEVEL, 0)
38 | glTexParameteri(target.i, GL_TEXTURE_MAX_LEVEL, texture.levels() - 1)
39 | val swizzles = intBufferBig(4)
40 | format.swizzles to swizzles
41 | glTexParameteriv(target.i, GL_TEXTURE_SWIZZLE_RGBA, swizzles)
42 | var extent = texture.extent()
43 | glTexStorage2D(target.i, texture.levels(), format.internal.i, extent.x, extent.y)
44 | for(level in 0 until texture.levels()) {
45 | extent = texture.extent(level)
46 | glCompressedTexSubImage2D(
47 | target.i, level, 0, 0, extent.x, extent.y,
48 | format.internal.i, texture.data(0, 0, level))
49 | }
50 | val texName = textureName[0]
51 | textureName.free()
52 | return texName
53 | }
54 | ```
55 |
56 | Kotlin with [gl-next](https://github.com/kotlin-graphics/gln):
57 | ```kotlin
58 | fun createTexture(filename: String): Int {
59 |
60 | val texture = gli.load(filename)
61 | if(texture.empty())
62 | return 0
63 |
64 | gli.gl.profile = gl.Profile.GL33
65 | val (target, format) = gli.gl.translate(texture)
66 | assert(texture.format.isCompressed && target == gl.Target._2D)
67 |
68 | return initTexture2d {
69 | levels = 0 until texture.levels()
70 | swizzles = format.swizzles
71 | storage(texture.levels(), format.internal, texture.extent())
72 | levels.forEach {
73 | compressedSubImage(it, texture.extent(it), format.internal, texture.data(0, 0, it))
74 | }
75 | }
76 | }
77 | ```
78 |
79 | Java:
80 | ```java
81 | public static int createTexture(String filename) {
82 |
83 | Texture texture = gli.load(filename);
84 | if (texture.empty())
85 | return 0;
86 |
87 | gli_.gli.gl.setProfile(gl.Profile.GL33);
88 | gl.Format format = gli_.gli.gl.translate(texture.getFormat(), texture.getSwizzles());
89 | gl.Target target = gli_.gli.gl.translate(texture.getTarget());
90 | assert (texture.getFormat().isCompressed() && target == gl.Target._2D);
91 |
92 | IntBuffer textureName = intBufferBig(1);
93 | glGenTextures(textureName);
94 | glBindTexture(target.getI(), textureName.get(0));
95 | glTexParameteri(target.getI(), GL12.GL_TEXTURE_BASE_LEVEL, 0);
96 | glTexParameteri(target.getI(), GL12.GL_TEXTURE_MAX_LEVEL, texture.levels() - 1);
97 | IntBuffer swizzles = intBufferBig(4);
98 | texture.getSwizzles().to(swizzles);
99 | glTexParameteriv(target.getI(), GL33.GL_TEXTURE_SWIZZLE_RGBA, swizzles);
100 | Vec3i extent = texture.extent(0);
101 | glTexStorage2D(target.getI(), texture.levels(), format.getInternal().getI(), extent.x, extent.y);
102 | for (int level = 0; level < texture.levels(); level++) {
103 | extent = texture.extent(level);
104 | glCompressedTexSubImage2D(
105 | target.getI(), level, 0, 0, extent.x, extent.y,
106 | format.getInternal().getI(), texture.data(0, 0, level));
107 | }
108 | int texName = textureName.get(0);
109 | MemoryUtil.memFree(textureName);
110 | return texName
111 | }
112 | ```
113 |
114 | ### Supported Image Formats
115 |
116 | * [KTX](https://www.khronos.org/opengles/sdk/tools/KTX/)
117 | * [DDS](https://msdn.microsoft.com/en-us/library/windows/desktop/bb943990%28v=vs.85%29.aspx)
118 | * KMG
119 | * jpg
120 | * png
121 | * gif
122 | * bmp
123 | * tga
124 |
125 |
126 | ### How to retrieve it:
127 |
128 | ```kotlin
129 | repositories {
130 | maven("https://raw.githubusercontent.com/kotlin-graphics/mary/master")
131 | // or with magik plugin
132 | //github("kotlin-graphics/mary")
133 | }
134 | dependencies {
135 | implementation("kotlin.graphics:gli:0.8.3.0-20")
136 | }
137 | ```
138 |
139 | You can find more info by [mary](https://github.com/kotlin-graphics/mary)
--------------------------------------------------------------------------------
/src/main/kotlin/gli_/saveDds.kt:
--------------------------------------------------------------------------------
1 | package gli_
2 |
3 | import gli_.detail.has
4 | import gli_.detail.or
5 | import gli_.dx.has
6 | import glm_.L
7 | import glm_.b
8 | import kool.*
9 | import java.nio.channels.FileChannel
10 | import java.nio.file.Path
11 | import java.nio.file.Paths
12 | import java.nio.file.StandardOpenOption
13 |
14 | interface saveDds {
15 |
16 | private fun getFourcc(requireDX10Header: Boolean, format: Format, dxFormat: dx.Format) = when {
17 | requireDX10Header -> when {
18 | format.formatInfo.flags has detail.Cap.DDS_GLI_EXT_BIT -> dx.D3dfmt.GLI1
19 | else -> dx.D3dfmt.DX10
20 | }
21 | else -> if (dxFormat.ddPixelFormat has dx.Ddpf.FOURCC) dxFormat.d3DFormat else dx.D3dfmt.UNKNOWN
22 | }
23 |
24 |
25 | fun getDimension(target: Target): Int {
26 | val table = arrayOf(
27 | detail.D3d10resourceDimension.TEXTURE1D, //TARGET_1D,
28 | detail.D3d10resourceDimension.TEXTURE1D, //TARGET_1D_ARRAY,
29 | detail.D3d10resourceDimension.TEXTURE2D, //TARGET_2D,
30 | detail.D3d10resourceDimension.TEXTURE2D, //TARGET_2D_ARRAY,
31 | detail.D3d10resourceDimension.TEXTURE3D, //TARGET_3D,
32 | detail.D3d10resourceDimension.TEXTURE2D, //TARGET_RECT,
33 | detail.D3d10resourceDimension.TEXTURE2D, //TARGET_RECT_ARRAY,
34 | detail.D3d10resourceDimension.TEXTURE2D, //TARGET_CUBE,
35 | detail.D3d10resourceDimension.TEXTURE2D //TARGET_CUBE_ARRAY
36 | )
37 | assert(table.size == Target.COUNT) { "Table needs to be updated" }
38 | return table[target.i].i
39 | }
40 |
41 | /** Save a texture storage_linear to a DDS file.
42 | * @param texture Source texture to save
43 | * @param filename string for where to save the file. It must include the filaname and filename extension.
44 | * This function ignores the filename extension in the path and save to DDS anyway but keep the requested filename extension.
45 | * @return returns false if the function fails to save the file. */
46 | fun saveDds(texture: Texture, filename: String): Boolean {
47 |
48 | if (texture.empty()) return false // TODO check combinations
49 |
50 | return saveDds(texture, Paths.get(filename))
51 | }
52 |
53 | /** Save a texture storage_linear to a DDS file.
54 | *
55 | * @param texture Source texture to save
56 | * @param path path for where to save the file. It must include the filaname and filename extension.
57 | * This function ignores the filename extension in the path and save to DDS anyway but keep the requested filename extension.
58 | * @return Returns false if the function fails to save the file. */
59 | fun saveDds(texture: Texture, path: Path): Boolean {
60 |
61 | if (texture.empty()) return false
62 |
63 | val dxFormat = dx.translate(texture.format)
64 |
65 | val requireDX10Header = dxFormat.d3DFormat == dx.D3dfmt.GLI1 || dxFormat.d3DFormat == dx.D3dfmt.DX10 ||
66 | texture.target.isTargetArray || texture.target.isTarget1d
67 |
68 | var length = texture.size + detail.FOURCC_DDS.size + detail.DdsHeader.SIZE
69 | if (requireDX10Header) length += detail.DdsHeader10.SIZE
70 | val data = Buffer(length)
71 |
72 | detail.FOURCC_DDS.forEach { data.put(it.b) }
73 |
74 | val header = detail.DdsHeader()
75 |
76 | val desc = texture.format.formatInfo
77 |
78 | var caps = detail.DdsFlag.CAPS or detail.DdsFlag.WIDTH or detail.DdsFlag.PIXELFORMAT or detail.DdsFlag.MIPMAPCOUNT
79 | if (!texture.target.isTarget1d)
80 | caps = caps or detail.DdsFlag.HEIGHT
81 | if (texture.target == Target._3D)
82 | caps = caps or detail.DdsFlag.DEPTH
83 | //caps |= Storage.levels() > 1 ? detail::DDSD_MIPMAPCOUNT : 0;
84 | caps = caps or if (desc.flags has detail.Cap.COMPRESSED_BIT) detail.DdsFlag.LINEARSIZE else detail.DdsFlag.PITCH
85 |
86 | with(header) {
87 | size = detail.DdsHeader.SIZE
88 | flags = caps
89 | width = texture.extent().x
90 | height = texture.extent().y
91 | pitch = if (desc.flags has detail.Cap.COMPRESSED_BIT) texture.size / texture.faces() else 32
92 | depth = if (texture.extent().z > 1) texture.extent().z else 0
93 | mipMapLevels = texture.levels()
94 | with(format) {
95 | size = detail.DdsPixelFormat.SIZE
96 | flags = if (requireDX10Header) dx.Ddpf.FOURCC.i else dxFormat.ddPixelFormat.i
97 | fourCC = getFourcc(requireDX10Header, texture.format, dxFormat).i
98 | bpp = texture.format.bitsPerPixel
99 | mask = dxFormat.mask
100 | }
101 | //header.surfaceFlags = detail::DDSCAPS_TEXTURE | (Storage.levels() > 1 ? detail::DDSCAPS_MIPMAP : 0);
102 | surfaceFlags = detail.DdsSurfaceFlag.TEXTURE or detail.DdsSurfaceFlag.MIPMAP
103 | cubemapFlags = 0
104 |
105 | // Cubemap
106 | if (texture.faces() > 1) {
107 | assert(texture.faces() == 6)
108 | cubemapFlags = cubemapFlags or detail.DdsCubemapFlag.CUBEMAP_ALLFACES or detail.DdsCubemapFlag.CUBEMAP
109 | }
110 |
111 | // Texture3D
112 | if (texture.extent().z > 1)
113 | cubemapFlags = cubemapFlags or detail.DdsCubemapFlag.VOLUME
114 |
115 | to(data)
116 | }
117 |
118 | if (requireDX10Header) detail.DdsHeader10().apply {
119 | arraySize = texture.layers()
120 | resourceDimension = getDimension(texture.target)
121 | miscFlag = 0 //Storage.levels() > 0 ? detail::D3D10_RESOURCE_MISC_GENERATE_MIPS : 0;
122 | format = dxFormat.dxgiFormat.i
123 | alphaFlags = detail.DdsAlphaMode.UNKNOWN.i
124 |
125 | to(data)
126 | }
127 |
128 | val src = texture.data()
129 | val dst = data
130 | memCopy(src.adr, dst.adr, src.cap)
131 |
132 | FileChannel.open(path, StandardOpenOption.CREATE, StandardOpenOption.WRITE).use {
133 | data.pos = 0
134 | while (data.hasRemaining())
135 | it.write(data)
136 | // it.truncate(data.rem.L)
137 | }
138 |
139 | return true
140 | }
141 | }
--------------------------------------------------------------------------------
/src/main/kotlin/gli_/Storage.kt:
--------------------------------------------------------------------------------
1 | package gli_
2 |
3 | import kool.adr
4 | import kool.free
5 | import glm_.glm
6 | import glm_.vec1.Vec1i
7 | import glm_.vec2.Vec2i
8 | import glm_.vec3.Vec3i
9 | import kool.rem
10 | import org.lwjgl.system.MemoryUtil
11 | import java.nio.ByteBuffer
12 |
13 | /**
14 | * Created by GBarbieri on 03.04.2017.
15 | */
16 |
17 | class Storage {
18 |
19 | var layers = 0
20 | private set
21 | var faces = 0
22 | private set
23 | var levels = 0
24 | private set
25 | var blockSize = 0
26 | private set
27 |
28 | private var blockCount = Vec3i(0)
29 |
30 | var blockExtent = Vec3i(0)
31 | private set
32 |
33 | private var extent = Vec3i(0)
34 |
35 | private var data: ByteBuffer? = null
36 |
37 | constructor()
38 | constructor(storage: Storage) {
39 | layers = storage.layers
40 | faces = storage.faces
41 | levels = storage.levels
42 | blockSize = storage.blockSize
43 | blockCount = Vec3i(storage.blockCount)
44 | blockExtent = Vec3i(storage.blockExtent)
45 | extent = Vec3i(storage.extent)
46 | data = MemoryUtil.memByteBuffer(storage.data!!.adr.toLong(), storage.data!!.remaining())
47 | }
48 |
49 | constructor(format: Format, extent: Vec3i, layers: Int, faces: Int, levels: Int) {
50 | this.layers = layers
51 | this.faces = faces
52 | this.levels = levels
53 | this.blockSize = format.blockSize
54 | this.blockCount = glm.max(extent / format.blockExtend, 1)
55 | this.blockExtent = format.blockExtend
56 | this.extent = extent
57 |
58 | assert(layers >= 0 && faces >= 0 && levels >= 0)
59 | assert(glm.all(glm.greaterThan(extent, Vec3i(0))))
60 |
61 | data = MemoryUtil.memCalloc(layerSize(0, faces - 1, 0, levels - 1) * layers)
62 | }
63 |
64 | fun empty() = data == null
65 | fun notEmpty() = data != null
66 |
67 | fun blockCount(level: Int): Vec3i {
68 | assert(level in 0 until levels)
69 | return glm.ceilMultiple(extent(level), blockExtent) / blockExtent
70 | }
71 |
72 | fun extent(level: Int): Vec3i {
73 | assert(level in 0 until levels)
74 | return glm.max(extent shr level, 1)
75 | }
76 |
77 | fun size() = data!!.rem
78 |
79 | fun data() = data!!
80 |
81 | inline fun data() = getReinterpreter(T::class).apply { data = data() }
82 |
83 | /** Compute the relative memory offset to access the data for a specific layer, face and level */
84 | fun baseOffset(layer: Int, face: Int, level: Int): Int {
85 |
86 | assert(notEmpty())
87 | assert(layer in 0 until layers && face in 0 until faces && level in 0 until levels)
88 |
89 | val layerSize = layerSize(0, faces - 1, 0, levels - 1)
90 | val faceSize = faceSize(0, levels - 1)
91 |
92 | return layerSize * layer + faceSize * face + (0 until level).sumOf { levelSize(it) }
93 | }
94 |
95 | fun imageOffset(coord: Int, extend: Int): Int {
96 | assert(coord <= extend)
97 | return coord
98 | }
99 |
100 | fun imageOffset(coord: Vec1i, extend: Vec1i): Int {
101 | assert(glm.all(glm.lessThan(coord, extend)))
102 | return coord.x
103 | }
104 |
105 | fun imageOffset(coord: Vec2i, extend: Vec2i): Int {
106 | assert(glm.all(glm.lessThan(coord, extend)))
107 | return coord.x + coord.y * extend.x
108 | }
109 |
110 | fun imageOffset(coord: Vec3i, extent: Vec3i): Int {
111 | assert(glm.all(glm.lessThan(coord, extent)))
112 | return coord.x + coord.y * extent.x + coord.z * extent.x * extent.y
113 | }
114 |
115 | /** Copy a subset of a specific image of a texture */
116 | fun copy(storageSrc: Storage,
117 | layerSrc: Int, faceSrc: Int, levelSrc: Int, blockIndexSrc: Vec3i,
118 | layerDst: Int, faceDst: Int, levelDst: Int, blockIndexDst: Vec3i,
119 | blockCount: Vec3i) {
120 |
121 | val baseOffsetSrc = storageSrc.baseOffset(layerSrc, faceSrc, levelSrc)
122 | val baseOffsetDst = baseOffset(layerDst, faceDst, levelDst)
123 | val imageSrc = storageSrc.data!!.adr.toLong() + baseOffsetSrc
124 | val imageDst = data!!.adr.toLong() + baseOffsetDst
125 |
126 | for (blockIndexZ in 0 until blockCount.z)
127 | for (blockIndexY in 0 until blockCount.y) {
128 |
129 | val blockIndex = Vec3i(0, blockIndexY, blockIndexZ)
130 | val offsetSrc = storageSrc.imageOffset(blockIndexSrc + blockIndex, storageSrc.extent(levelSrc)) * storageSrc.blockSize
131 | val offsetDst = imageOffset(blockIndexDst + blockIndex, extent(levelDst)) * blockSize
132 | val dataSrc = imageSrc + offsetSrc
133 | val dataDst = imageDst + offsetDst
134 | memCopy(dataSrc, dataDst, blockSize * blockCount.x)
135 | }
136 | }
137 |
138 | fun levelSize(level: Int): Int {
139 | assert(level in 0 until levels)
140 | return blockSize * glm.compMul(blockCount(level))
141 | }
142 |
143 | fun faceSize(baseLevel: Int, maxLevel: Int): Int {
144 |
145 | assert(maxLevel in 0 until levels)
146 | assert(baseLevel in 0 until levels)
147 | assert(baseLevel <= maxLevel)
148 | // The size of a face is the sum of the size of each level.
149 | return (baseLevel..maxLevel).sumOf { levelSize(it) }
150 | }
151 |
152 | fun layerSize(baseFace: Int, maxFace: Int, baseLevel: Int, maxLevel: Int): Int {
153 |
154 | assert(maxFace in 0 until faces)
155 | assert(baseFace in 0 until faces)
156 | assert(maxLevel in 0 until levels)
157 | assert(baseLevel in 0 until levels)
158 | // The size of a layer is the sum of the size of each face. All the faces have the same size.
159 | return faceSize(baseLevel, maxLevel) * (maxFace - baseFace + 1)
160 | }
161 |
162 | fun dispose() = data?.free()
163 |
164 | override fun equals(other: Any?): Boolean {
165 | return if (other !is Storage) false
166 | else layers == other.layers &&
167 | faces == other.faces &&
168 | levels == other.levels &&
169 | blockSize == other.blockSize &&
170 | blockCount == other.blockCount &&
171 | blockExtent == other.blockExtent &&
172 | extent == other.extent &&
173 | data == other.data
174 | }
175 |
176 | override fun hashCode(): Int {
177 | var result = layers
178 | result = 31 * result + faces
179 | result = 31 * result + levels
180 | result = 31 * result + blockSize
181 | result = 31 * result + blockCount.hashCode()
182 | result = 31 * result + blockExtent.hashCode()
183 | result = 31 * result + extent.hashCode()
184 | result = 31 * result + (data?.hashCode() ?: 0)
185 | return result
186 | }
187 | }
--------------------------------------------------------------------------------
/src/main/kotlin/gli_/loadDds.kt:
--------------------------------------------------------------------------------
1 | package gli_
2 |
3 | import gli_.detail.has
4 | import gli_.dx.has
5 | import gli_.dx.or
6 | import glm_.b
7 | import glm_.glm
8 | import glm_.has
9 | import glm_.i
10 | import glm_.vec3.Vec3i
11 | import kool.*
12 | import java.io.File
13 | import java.net.URI
14 | import java.nio.ByteBuffer
15 | import java.nio.ByteOrder
16 | import java.nio.channels.FileChannel
17 | import java.nio.file.Path
18 | import java.nio.file.Paths
19 | import java.nio.file.StandardOpenOption
20 |
21 | /**
22 | * Created by GBarbieri on 05.04.2017.
23 | */
24 |
25 | interface loadDds {
26 |
27 | /** Loads a texture storage_linear from DDS memory. Returns an empty storage_linear in case of failure.
28 | * @param filename String of the file to open including filaname and filename extension */
29 | fun loadDds(filename: String) = loadDds(Paths.get(filename))
30 |
31 | fun loadDds(uri: URI) = loadDds(Paths.get(uri))
32 |
33 | /** Loads a texture storage_linear from DDS memory. Returns an empty storage_linear in case of failure.
34 | * @param uri Uri of the file to open including filaname and filename extension */
35 | fun loadDds(path: Path): Texture {
36 |
37 | val buffer = FileChannel.open(path, StandardOpenOption.READ).use { channel ->
38 | Buffer(channel.size().i).also {
39 | while (channel.read(it) > 0);
40 | it.pos = 0
41 | it.order(ByteOrder.nativeOrder())
42 | }
43 | }
44 |
45 | return loadDds(buffer)
46 | }
47 |
48 | /** Loads a texture storage_linear from DDS file. Returns an empty storage_linear in case of failure. */
49 | fun loadDds(data: ByteBuffer): Texture {
50 |
51 | assert(data.cap >= detail.FOURCC_DDS.size)
52 |
53 | if (!detail.FOURCC_DDS.all { data.get() == it.b })
54 | return Texture()
55 |
56 | assert(data.remaining() >= detail.DdsHeader.SIZE)
57 |
58 | val header = detail.DdsHeader(data)
59 |
60 | val header10 = with(header.format) {
61 | if (flags has dx.Ddpf.FOURCC && (fourCC == dx.D3dfmt.DX10.i || fourCC == dx.D3dfmt.GLI1.i))
62 | detail.DdsHeader10(data)
63 | else
64 | detail.DdsHeader10()
65 | }
66 |
67 | fun has(format: Format) = glm.all(glm.equal(header.format.mask, dx.translate(format).mask))
68 |
69 | val format = with(header.format) {
70 | if ((flags has (dx.Ddpf.RGB or dx.Ddpf.ALPHAPIXELS or dx.Ddpf.ALPHA or dx.Ddpf.YUV or dx.Ddpf.LUMINANCE)) && bpp != 0)
71 | when (bpp) {
72 | 8 -> when {
73 | has(Format.RG4_UNORM_PACK8) -> Format.RG4_UNORM_PACK8
74 | has(Format.RG4_UNORM_PACK8) -> Format.RG4_UNORM_PACK8
75 | has(Format.L8_UNORM_PACK8) -> Format.L8_UNORM_PACK8
76 | has(Format.A8_UNORM_PACK8) -> Format.A8_UNORM_PACK8
77 | has(Format.R8_UNORM_PACK8) -> Format.R8_UNORM_PACK8
78 | has(Format.RG3B2_UNORM_PACK8) -> Format.RG3B2_UNORM_PACK8
79 | else -> throw Error()
80 | }
81 | 16 -> when {
82 | has(Format.RGBA4_UNORM_PACK16) -> Format.RGBA4_UNORM_PACK16
83 | has(Format.BGRA4_UNORM_PACK16) -> Format.BGRA4_UNORM_PACK16
84 | has(Format.R5G6B5_UNORM_PACK16) -> Format.R5G6B5_UNORM_PACK16
85 | has(Format.B5G6R5_UNORM_PACK16) -> Format.B5G6R5_UNORM_PACK16
86 | has(Format.RGB5A1_UNORM_PACK16) -> Format.RGB5A1_UNORM_PACK16
87 | has(Format.BGR5A1_UNORM_PACK16) -> Format.BGR5A1_UNORM_PACK16
88 | has(Format.LA8_UNORM_PACK8) -> Format.LA8_UNORM_PACK8
89 | has(Format.RG8_UNORM_PACK8) -> Format.RG8_UNORM_PACK8
90 | has(Format.L16_UNORM_PACK16) -> Format.L16_UNORM_PACK16
91 | has(Format.A16_UNORM_PACK16) -> Format.A16_UNORM_PACK16
92 | has(Format.R16_UNORM_PACK16) -> Format.R16_UNORM_PACK16
93 | else -> throw Error()
94 | }
95 | 24 -> when {
96 | has(Format.RGB8_UNORM_PACK8) -> Format.RGB8_UNORM_PACK8
97 | has(Format.BGR8_UNORM_PACK8) -> Format.BGR8_UNORM_PACK8
98 | else -> throw Error()
99 | }
100 | 32 -> when {
101 | has(Format.BGR8_UNORM_PACK32) -> Format.BGR8_UNORM_PACK32
102 | has(Format.BGRA8_UNORM_PACK8) -> Format.BGRA8_UNORM_PACK8
103 | has(Format.RGBA8_UNORM_PACK8) -> Format.RGBA8_UNORM_PACK8
104 | has(Format.RGB10A2_UNORM_PACK32) -> Format.RGB10A2_UNORM_PACK32
105 | has(Format.LA16_UNORM_PACK16) -> Format.LA16_UNORM_PACK16
106 | has(Format.RG16_UNORM_PACK16) -> Format.RG16_UNORM_PACK16
107 | has(Format.R32_SFLOAT_PACK32) -> Format.R32_SFLOAT_PACK32
108 | else -> throw Error()
109 | }
110 | else -> throw Error()
111 | }
112 | else if ((flags has dx.Ddpf.FOURCC.i) && (fourCC != dx.D3dfmt.DX10.i) && (fourCC != dx.D3dfmt.GLI1.i))
113 | dx.find(detail.remapFourCC(fourCC))
114 | else if (fourCC == dx.D3dfmt.DX10.i || fourCC == dx.D3dfmt.GLI1.i)
115 | dx.find(dx.D3dfmt.of(fourCC), dx.DxgiFormat(header10.format))
116 | else throw Error()
117 | }
118 |
119 | val mipMapCount = if (header.flags has detail.DdsFlag.MIPMAPCOUNT.i) header.mipMapLevels else 1
120 | val faceCount = when {
121 | header.cubemapFlags has detail.DdsCubemapFlag.CUBEMAP.i ->
122 | glm.bitCount(header.cubemapFlags and detail.DdsCubemapFlag.CUBEMAP_ALLFACES.i)
123 | header10.miscFlag has detail.D3d10resourceMiscFlag.TEXTURECUBE -> 6
124 | else -> 1
125 | }
126 |
127 | val depthCount = when {
128 | header.cubemapFlags has detail.DdsCubemapFlag.VOLUME.i -> header.depth
129 | else -> 1
130 | }
131 |
132 | val texture = Texture(getTarget(header, header10), format, Vec3i(header.width, header.height, depthCount),
133 | glm.max(header10.arraySize, 1), faceCount, mipMapCount)
134 |
135 | assert(data.cap == data.pos + texture.size)
136 |
137 | memCopy(data.adr, texture.data().adr, texture.size)
138 |
139 | return texture
140 | }
141 |
142 | fun getTarget(header: detail.DdsHeader, header10: detail.DdsHeader10) = when {
143 |
144 | header.cubemapFlags has detail.DdsCubemapFlag.CUBEMAP || header10.miscFlag has detail.D3d10resourceMiscFlag.TEXTURECUBE ->
145 | if (header10.arraySize > 1) Target.CUBE_ARRAY
146 | else Target.CUBE
147 |
148 | header10.arraySize > 1 ->
149 | if (header.flags has detail.DdsFlag.HEIGHT) Target._2D_ARRAY
150 | else Target._1D_ARRAY
151 |
152 | header10.resourceDimension == detail.D3d10resourceDimension.TEXTURE1D.i -> Target._1D
153 |
154 | header10.resourceDimension == detail.D3d10resourceDimension.TEXTURE3D.i || (header.flags has detail.DdsFlag.DEPTH.i)
155 | || (header.cubemapFlags has detail.DdsCubemapFlag.VOLUME.i) -> Target._3D
156 |
157 | else -> Target._2D
158 | }
159 | }
--------------------------------------------------------------------------------
/src/main/kotlin/gli_/Image.kt:
--------------------------------------------------------------------------------
1 | package gli_
2 |
3 | import kool.adr
4 | import kool.cap
5 | import kool.free
6 | import glm_.glm
7 | import kool.set
8 | import glm_.vec3.Vec3i
9 | import org.lwjgl.system.MemoryUtil
10 | import org.lwjgl.system.MemoryUtil.memByteBuffer
11 | import java.nio.ByteBuffer
12 | import kotlin.reflect.KClass
13 |
14 | /**
15 | * Created by elect on 08/04/17.
16 | */
17 |
18 | /** Image, representation for a single texture level */
19 | class Image {
20 |
21 | private var storage: Storage? = null
22 |
23 | var format = Format.UNDEFINED
24 | private set
25 |
26 | var baseLevel = 0
27 | private set
28 |
29 | var size = 0
30 | private set
31 |
32 | private var data: ByteBuffer? = null
33 |
34 | /** Create an empty image instance */
35 | constructor()
36 |
37 | /** Create an image object and allocate an image storoge for it. */
38 | constructor(format: Format, extent: Vec3i) {
39 | storage = Storage(format, extent, 1, 1, 1)
40 | this.format = format
41 | baseLevel = 0
42 | data = memByteBuffer(storage!!.data().adr.toLong(), storage!!.data().remaining())
43 | size = computeSize(0)
44 | }
45 |
46 | /** Create an image object by sharing an existing image storage_linear from another image instance.
47 | * This image object is effectively an image view where format can be reinterpreted with a different
48 | * compatible image format.
49 | * For formats to be compatible, the block size of source and destination must match. */
50 | constructor(image: Image, format: Format) {
51 | storage = Storage(image.storage!!)
52 | this.format = format
53 | baseLevel = image.baseLevel
54 | data = memByteBuffer(image.data!!.adr.toLong(), image.data!!.remaining())
55 | size = image.size
56 | assert(format.blockSize == image.format.blockSize)
57 | }
58 |
59 | /** Create an image object by sharing an existing image storage_linear from another image instance.
60 | * This image object is effectively an image view where the layer, the face and the level allows identifying
61 | * a specific subset of the image storage_linear source.
62 | * This image object is effectively a image view where the format can be reinterpreted
63 | * with a different compatible image format. */
64 | constructor(storage: Storage, format: Format, baseLayer: Int, baseFace: Int, baseLevel: Int) {
65 | this.storage = Storage(storage)
66 | this.format = format
67 | this.baseLevel = baseLevel
68 | data = computeData(baseLayer, baseFace, baseLevel)
69 | size = computeSize(baseLevel)
70 | }
71 |
72 | fun computeData(baseLayer: Int, baseFace: Int, baseLevel: Int): ByteBuffer {
73 | val baseOffset = storage!!.baseOffset(baseLayer, baseFace, baseLevel)
74 | return memByteBuffer(storage!!.data().adr.toLong() + baseOffset, storage!!.data().remaining() - baseOffset)
75 | }
76 |
77 | fun computeSize(level: Int): Int {
78 | assert(notEmpty())
79 | return storage!!.levelSize(level)
80 | }
81 |
82 | /** Return whether the image instance is empty, no storage_linear or description have been assigned to the instance. */
83 | fun empty() = storage?.empty() ?: true
84 |
85 | fun notEmpty() = !empty()
86 |
87 | /** Return the dimensions of an image instance: width, height and depth. */
88 | fun extent(): Vec3i {
89 |
90 | assert(notEmpty())
91 |
92 | val srcExtent = storage!!.extent(baseLevel)
93 | val dstExtent = srcExtent * format.blockExtend / storage!!.blockExtent
94 |
95 | return glm.max(dstExtent, 1)
96 | }
97 |
98 | /** Return the memory size of an image instance storage_linear in bytes. */
99 | // fun size(): Int {
100 | // assert(notEmpty())
101 | // return size
102 | // }
103 |
104 | /** Return the number of blocks contained in an image instance storage_linear.
105 | * genType size must match the block size corresponding to the image format. */
106 | inline fun size() = size(T::class)
107 |
108 | fun size(kClass: KClass<*>): Int {
109 | val blockSize = getSize(kClass)
110 | assert(blockSize <= storage!!.blockSize)
111 | return size / blockSize
112 | }
113 |
114 | /** Return a pointer to the beginning of the image instance data. */
115 | fun data(): ByteBuffer? {
116 | assert(notEmpty())
117 | return data
118 | }
119 |
120 | inline fun data(): reinterpreter = data(T::class)
121 | fun data(kClass: KClass<*>) = getReinterpreter(kClass).apply { data = data()!! }
122 |
123 | /** Clear the entire image storage_linear with zeros */
124 | fun clear() {
125 | assert(notEmpty())
126 | repeat(storage!!.data().cap) { storage!!.data()[it] = 0 }
127 | }
128 |
129 | /** Clear the entire image storage_linear with Texel which type must match the image storage_linear format block size
130 | * If the type of genType doesn't match the type of the image format, no conversion is performed and the data will
131 | * be reinterpreted as if is was of the image format. */
132 | infix fun clear(texel: T) {
133 | assert(notEmpty() && format.blockSize == getSize(texel::class))
134 | for (i in 0 until size(texel::class))
135 | data(texel::class)[i] = texel
136 | }
137 |
138 | /** Load the texel located at TexelCoord coordinates.
139 | * It's an error to call this function if the format is compressed.
140 | * It's an error if TexelCoord values aren't between [0, dimensions]. */
141 | inline fun load(texelCoord: Vec3i): T {
142 | assert(notEmpty() && !format.isCompressed)
143 | assert(blockSize() == getSize(T::class))
144 | // GLI_ASSERT(glm::all(glm::lessThan(TexelCoord, this->extent()))); TODO
145 | return getReinterpreter(T::class).apply { data = data()!! }[textelLinearAddressing(extent(), texelCoord)]
146 | }
147 |
148 | fun blockSize() = storage!!.blockSize
149 | //
150 | // /// Store the texel located at TexelCoord coordinates.
151 | // /// It's an error to call this function if the format is compressed.
152 | // /// It's an error if TexelCoord values aren't between [0, dimensions].
153 | // template
154 | // void store(extent_type const & TexelCoord, genType const & Data);
155 |
156 | override fun equals(other: Any?) = when {
157 | other !is Image -> false
158 | extent() != other.extent() -> false
159 | size != other.size -> false
160 | else -> memCmp(other.data!!)
161 | }
162 |
163 | private fun memCmp(b: ByteBuffer): Boolean {
164 | for (i in 0 until size)
165 | if (data()!!.get(i) != b[i])
166 | return false
167 | return true
168 | }
169 |
170 | companion object {
171 | fun textelLinearAddressing(extent: Vec3i, texelCoord: Vec3i): Int {
172 | assert(glm.all(glm.lessThan(texelCoord, extent)))
173 | return texelCoord.x + extent.x * (texelCoord.y + extent.y * texelCoord.z)
174 | }
175 | }
176 |
177 | override fun hashCode(): Int {
178 | var result = storage?.hashCode() ?: 0
179 | result = 31 * result + format.hashCode()
180 | result = 31 * result + baseLevel
181 | result = 31 * result + size
182 | result = 31 * result + (data?.hashCode() ?: 0)
183 | return result
184 | }
185 |
186 | fun dispose() = data?.free()
187 | }
--------------------------------------------------------------------------------
/src/main/kotlin/gli_/loadImage.kt:
--------------------------------------------------------------------------------
1 | package gli_
2 |
3 | import glm_.b
4 | import glm_.or
5 | import glm_.vec3.Vec3i
6 | import kool.*
7 | import java.awt.image.*
8 | import java.awt.image.BufferedImage.*
9 | import java.io.File
10 | import java.net.URI
11 | import java.nio.ByteBuffer
12 | import java.nio.file.Path
13 | import java.nio.file.Paths
14 | import javax.imageio.ImageIO
15 |
16 | interface loadImage {
17 |
18 | fun loadImage(file: File, flipY: Boolean): Texture {
19 |
20 | if (!file.exists()) throw NoSuchFileException(file)
21 |
22 | val image = ImageIO.read(file)
23 | return loadImage(image, flipY)
24 | }
25 |
26 | fun loadImageFromMem(buffer: ByteBuffer, flipY: Boolean): Texture {
27 | val input = ByteBufferBackedInputStream(buffer)
28 | val image = ImageIO.read(input)
29 | return loadImage(image, flipY)
30 | }
31 |
32 | fun loadImage(image: BufferedImage, flipY: Boolean = false): Texture {
33 | val extent = Vec3i(image.width, image.height, 1)
34 | if (flipY)
35 | image.flipY()
36 | return when (image.type) {
37 | TYPE_INT_RGB -> Texture(Target._2D, Format.RGB8_UNORM_PACK8, extent, 1, 1, 1).apply {
38 | val dst = data()
39 | var i = 0
40 | (image.raster.dataBuffer as DataBufferInt).data.forEach {
41 | dst[i++] = (it ushr 16).b
42 | dst[i++] = (it ushr 8).b
43 | dst[i++] = it.b
44 | }
45 | }
46 | TYPE_INT_ARGB -> Texture(Target._2D, Format.RGBA8_UNORM_PACK8, extent, 1, 1, 1).apply {
47 | // push alpha at the end
48 | val dst = data()
49 | var i = 0
50 | (image.raster.dataBuffer as DataBufferInt).data.forEach {
51 | dst[i++] = (it ushr 16).b
52 | dst[i++] = (it ushr 8).b
53 | dst[i++] = it.b
54 | dst[i++] = (it ushr 24).b
55 | }
56 | }
57 | TYPE_INT_ARGB_PRE -> Texture(Target._2D, Format.RGBA8_UNORM_PACK8, extent, 1, 1, 1).apply {
58 | // push alpha at the end and demultiply
59 | val dst = data()
60 | var i = 0
61 | (image.raster.dataBuffer as DataBufferInt).data.forEach {
62 | val a = it ushr 24
63 | dst[i++] = ((it ushr 16) / a).b
64 | dst[i++] = ((it ushr 8) / a).b
65 | dst[i++] = (it / a).b
66 | dst[i++] = a.b
67 | }
68 | }
69 | TYPE_INT_BGR -> Texture(Target._2D, Format.RGB8_UNORM_PACK8, extent, 1, 1, 1).apply {
70 | // switch blue and red
71 | val dst = data()
72 | var i = 0
73 | (image.raster.dataBuffer as DataBufferInt).data.forEach {
74 | dst[i++] = it.b
75 | dst[i++] = (it ushr 8).b
76 | dst[i++] = (it ushr 16).b
77 | }
78 | }
79 | TYPE_3BYTE_BGR -> Texture(Target._2D, Format.RGB8_UNORM_PACK8, extent, 1, 1, 1).apply {
80 | // switch blue and red
81 | val dst = data()
82 | val src = (image.raster.dataBuffer as DataBufferByte).data
83 | for (i in src.indices step 3) {
84 | dst[i] = src[i + 2]
85 | dst[i + 1] = src[i + 1]
86 | dst[i + 2] = src[i]
87 | }
88 | }
89 | TYPE_4BYTE_ABGR -> Texture(Target._2D, Format.RGBA8_UNORM_PACK8, extent, 1, 1, 1).apply {
90 | // invert
91 | val dst = data()
92 | val src = (image.raster.dataBuffer as DataBufferByte).data
93 | for (i in src.indices step 4) {
94 | dst[i] = src[i + 3]
95 | dst[i + 1] = src[i + 2]
96 | dst[i + 2] = src[i + 1]
97 | dst[i + 3] = src[i]
98 | }
99 | }
100 | TYPE_4BYTE_ABGR_PRE -> Texture(Target._2D, Format.RGBA8_UNORM_PACK8, extent, 1, 1, 1).apply {
101 | // invert and demultiply
102 | val dst = data()
103 | val src = (image.raster.dataBuffer as DataBufferByte).data
104 | for (i in src.indices step 4) {
105 | val a = src[i]
106 | dst[i] = (src[i + 3] / a).b
107 | dst[i + 1] = (src[i + 2] / a).b
108 | dst[i + 2] = (src[i + 1] / a).b
109 | dst[i + 3] = a
110 | }
111 | }
112 | TYPE_USHORT_565_RGB -> Texture(Target._2D, Format.R5G6B5_UNORM_PACK16, extent, 1, 1, 1).apply {
113 | // 1 to 1
114 | val dst = data()
115 | var i = 0
116 | (image.raster.dataBuffer as DataBufferUShort).data.forEach {
117 | dst.putShort(i++ * Short.BYTES, it)
118 | }
119 | }
120 | TYPE_USHORT_555_RGB -> Texture(Target._2D, Format.A1RGB5_UNORM_PACK16, extent, 1, 1, 1).apply {
121 | // ~ 1 to 1
122 | val dst = data()
123 | var i = 0
124 | (image.raster.dataBuffer as DataBufferUShort).data.forEach {
125 | val a1rgb5 = it or 0b1000_0000_0000 // hardcode alpha to 1, opaque
126 | dst.putShort(i++ * Short.BYTES, a1rgb5)
127 | }
128 | }
129 | TYPE_BYTE_GRAY -> Texture(Target._2D, Format.R8_UNORM_PACK8, extent, 1, 1, 1).apply {
130 | // 1 to 1
131 | val dst = data()
132 | var i = 0
133 | val dataBuffer = image.raster.dataBuffer as DataBufferByte
134 | dataBuffer.data.forEach { dst[i++] = it }
135 | }
136 | TYPE_USHORT_GRAY -> Texture(Target._2D, Format.R16_UNORM_PACK16, extent, 1, 1, 1).apply {
137 | // 1 to 1
138 | val dst = data()
139 | var i = 0
140 | val dataBuffer = image.raster.dataBuffer as DataBufferUShort
141 | dataBuffer.data.forEach { dst.putShort(i++ * Short.BYTES, it) }
142 | }
143 | TYPE_BYTE_BINARY, TYPE_BYTE_INDEXED -> {
144 | val c = image.colorModel as IndexColorModel
145 | val colors = IntArray(c.mapSize)
146 | c.getRGBs(colors)
147 | val dataBuffer = image.raster.dataBuffer as DataBufferByte
148 | val src = dataBuffer.data
149 | when {
150 | c.hasAlpha() -> Texture(Target._2D, Format.RGBA8_UNORM_PACK8, extent, 1, 1, 1).apply {
151 | val dst = data()
152 | for (i in src.indices step 4) {
153 | dst[i + 0] = src[i + 1] // r
154 | dst[i + 1] = src[i + 2] // g
155 | dst[i + 2] = src[i + 3] // b
156 | dst[i + 3] = src[i + 0] // a
157 | }
158 | }
159 | else -> Texture(Target._2D, Format.RGB8_UNORM_PACK8, extent, 1, 1, 1).apply {
160 | val dst = data()
161 | var j = 0
162 | for (i in src.indices step 4) {
163 | dst[j++] = src[i + 1] // r
164 | dst[j++] = src[i + 2] // g
165 | dst[j++] = src[i + 3] // b
166 | }
167 | }
168 | }
169 | }
170 | else -> error("not yet supported")
171 | }
172 | }
173 | }
--------------------------------------------------------------------------------