texel 0.1.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.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +219 -0
- data/Rakefile +35 -0
- data/ext/texel/extconf.rb +28 -0
- data/ext/texel/shim.c +244 -0
- data/ext/texel/shim.h +35 -0
- data/ext/texel/texel_native.c +415 -0
- data/ext/texel/vendor/README.md +11 -0
- data/ext/texel/vendor/stb_image.h +7988 -0
- data/ext/texel/vendor/stb_image_resize2.h +10679 -0
- data/ext/texel/vendor/stb_image_write.h +1724 -0
- data/lib/texel/codec.rb +27 -0
- data/lib/texel/codecs/qoi.rb +228 -0
- data/lib/texel/codecs/stb.rb +104 -0
- data/lib/texel/color_transform.rb +63 -0
- data/lib/texel/errors.rb +9 -0
- data/lib/texel/gl.rb +36 -0
- data/lib/texel/half_float.rb +57 -0
- data/lib/texel/image.rb +106 -0
- data/lib/texel/image_transformations.rb +115 -0
- data/lib/texel/info.rb +10 -0
- data/lib/texel/native.rb +91 -0
- data/lib/texel/pixel_data.rb +79 -0
- data/lib/texel/registry.rb +66 -0
- data/lib/texel/resampler.rb +79 -0
- data/lib/texel/source_reader.rb +52 -0
- data/lib/texel/version.rb +5 -0
- data/lib/texel/wgpu.rb +138 -0
- data/lib/texel.rb +164 -0
- metadata +75 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: dc746766a443584c97dcf37b69f42f2a77e04337488d1dccd794be408d59da52
|
|
4
|
+
data.tar.gz: 26d50b5b34ae127a95f62b46c5f1c029d7fdd876013cb62aa12cf6efb9a3328e
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: '04686cc60e1595a187297ac36e487e571d300aa363090b1d32de7bd30ada0767cf697602f248b1acd22da8de21269701dfa5a393814b09b2ffab1aac5e2462bf'
|
|
7
|
+
data.tar.gz: a259720f0efc3eaa8d0928584ce4a0bd84ecec52b6a7d6f3a1d539b5a4d77fb0fad6512d238282bee033e531fc4da72ce8be7ad7ca57eed2d165cdd88400cf98
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yudai Takada
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# Texel
|
|
2
|
+
|
|
3
|
+
Texel is a texture-oriented image I/O library for Ruby. It combines a small C
|
|
4
|
+
extension built from vendored stb headers with a pure Ruby QOI codec, an
|
|
5
|
+
immutable pixel container, color and channel conversion, mipmap generation,
|
|
6
|
+
and WebGPU/OpenGL upload helpers.
|
|
7
|
+
|
|
8
|
+
Texel intentionally does not provide drawing, compositing, or image filters.
|
|
9
|
+
Its job is the path from encoded image bytes to tightly packed texture data.
|
|
10
|
+
|
|
11
|
+
## Requirements
|
|
12
|
+
|
|
13
|
+
- Ruby 3.2 or newer
|
|
14
|
+
- A C99 compiler when installing the source gem
|
|
15
|
+
- No runtime gem or system image-library dependencies
|
|
16
|
+
|
|
17
|
+
The stb headers are included in the gem. QOI remains usable when a compiler is
|
|
18
|
+
unavailable by setting `TEXEL_DISABLE_NATIVE=1` while installing:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
TEXEL_DISABLE_NATIVE=1 gem install texel
|
|
22
|
+
ruby your_qoi_program.rb
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The environment variable is only required during installation. At runtime,
|
|
26
|
+
Texel detects that the extension is absent and keeps the pure Ruby QOI codec
|
|
27
|
+
available automatically.
|
|
28
|
+
|
|
29
|
+
This source-gem fallback still requires the platform's `make` command because
|
|
30
|
+
RubyGems invokes it for every declared extension. Precompiled platform gems do
|
|
31
|
+
not require a compiler or `make`.
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
Add Texel to your bundle:
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
gem "texel", "~> 0.1"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Then run `bundle install`, or install it directly:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
gem install texel
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Release artifacts include precompiled gems for x86-64 and ARM64 Linux,
|
|
48
|
+
Intel and Apple Silicon macOS, and 64-bit UCRT Windows. Each platform gem
|
|
49
|
+
contains extensions for Ruby 3.2, 3.3, 3.4, and 4.0, selected automatically at
|
|
50
|
+
load time. RubyGems falls back to the source gem on other platforms.
|
|
51
|
+
|
|
52
|
+
## Loading and saving
|
|
53
|
+
|
|
54
|
+
Texel accepts a path, a readable IO, or an encoded binary String. The format is
|
|
55
|
+
detected from magic bytes, with the file extension used for TGA as a fallback.
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
require "texel"
|
|
59
|
+
|
|
60
|
+
image = Texel.load("albedo.png")
|
|
61
|
+
rgba = Texel.load("albedo.jpg", channels: 4)
|
|
62
|
+
floats = Texel.load(File.binread("albedo.png"), dtype: :f32)
|
|
63
|
+
normal = Texel.load("normal.png", color_space: :linear)
|
|
64
|
+
|
|
65
|
+
metadata = Texel.info("albedo.png")
|
|
66
|
+
# => #<data Texel::Info width=... height=... channels=... format=:png>
|
|
67
|
+
|
|
68
|
+
Texel.save(rgba, "output.png")
|
|
69
|
+
Texel.save(rgba, "output.jpg", quality: 90)
|
|
70
|
+
encoded = Texel.encode(rgba, :qoi)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`Texel.load_all(paths, threads: 4)` decodes a collection concurrently while
|
|
74
|
+
preserving input order. Native decode and resize operations of at least one
|
|
75
|
+
megapixel release the GVL.
|
|
76
|
+
|
|
77
|
+
### Formats
|
|
78
|
+
|
|
79
|
+
| Format | Decode | Encode | Notes |
|
|
80
|
+
|---|---:|---:|---|
|
|
81
|
+
| PNG | yes | yes | u8 and u16 decode |
|
|
82
|
+
| JPEG | yes | yes | `quality:` controls encoding |
|
|
83
|
+
| BMP | yes | yes | lossless |
|
|
84
|
+
| TGA | yes | yes | lossless |
|
|
85
|
+
| GIF | yes | no | first frame only |
|
|
86
|
+
| PSD | yes | no | composite image |
|
|
87
|
+
| HDR/RGBE | yes | yes | defaults to f32/linear |
|
|
88
|
+
| QOI | yes | yes | pure Ruby |
|
|
89
|
+
|
|
90
|
+
KTX2, EXR, and WebP signatures are recognized. Until their extension gems are
|
|
91
|
+
loaded, Texel raises `Texel::MissingCodecError` with the required gem name.
|
|
92
|
+
|
|
93
|
+
## Image data
|
|
94
|
+
|
|
95
|
+
`Texel::Image` stores frozen, tightly packed, top-left-origin pixel data:
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
image.width # Integer
|
|
99
|
+
image.height # Integer
|
|
100
|
+
image.channels # 1..4
|
|
101
|
+
image.dtype # :u8, :u16, or :f32
|
|
102
|
+
image.color_space # :srgb, :linear, or :unknown
|
|
103
|
+
image.data # frozen ASCII-8BIT String
|
|
104
|
+
image.row_bytes
|
|
105
|
+
image.pixel(0, 0) # debug-oriented Array access
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Frozen binary input is adopted directly; mutable input is copied once to keep
|
|
109
|
+
the container immutable. Native resize and f32-to-f16 conversion write into
|
|
110
|
+
Ruby-owned buffers directly. stb decode and encode require one ownership copy
|
|
111
|
+
because stb allocates their result buffers itself.
|
|
112
|
+
|
|
113
|
+
Construct images directly with packed data when needed:
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
image = Texel::Image.new(
|
|
117
|
+
width: 1,
|
|
118
|
+
height: 1,
|
|
119
|
+
channels: 4,
|
|
120
|
+
dtype: :f32,
|
|
121
|
+
color_space: :linear,
|
|
122
|
+
data: [1.0, 0.5, 0.25, 1.0].pack("e*")
|
|
123
|
+
)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The constructor checks dimensions, tags, and the exact data size. Transforming
|
|
127
|
+
methods return new images:
|
|
128
|
+
|
|
129
|
+
```ruby
|
|
130
|
+
image.flip_y
|
|
131
|
+
image.convert(channels: 4, dtype: :f32)
|
|
132
|
+
image.resize(512, 512, filter: :mitchell)
|
|
133
|
+
image.resize_to_fit(1024)
|
|
134
|
+
image.to_linear
|
|
135
|
+
image.to_srgb
|
|
136
|
+
image.premultiply
|
|
137
|
+
image.unpremultiply
|
|
138
|
+
image.mipmaps
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Resize filters are `:box`, `:triangle`, `:cubic`, `:mitchell`, and `:lanczos`.
|
|
142
|
+
sRGB mipmaps are resized in linear space by default; use `mipmaps(fast: true)`
|
|
143
|
+
to resize encoded sRGB values directly.
|
|
144
|
+
|
|
145
|
+
## WebGPU upload
|
|
146
|
+
|
|
147
|
+
The optional helper targets the keyword API used by `webgpu-ruby` and handles
|
|
148
|
+
format inference, mip generation, f32-to-f16 conversion, and 256-byte row
|
|
149
|
+
alignment:
|
|
150
|
+
|
|
151
|
+
```ruby
|
|
152
|
+
require "texel/wgpu"
|
|
153
|
+
|
|
154
|
+
texture = Texel::WGPU.upload(
|
|
155
|
+
image,
|
|
156
|
+
device: device,
|
|
157
|
+
queue: queue,
|
|
158
|
+
label: "albedo"
|
|
159
|
+
)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Supported inferred layouts are u8 R/RG/RGBA and f32 R/RG/RGBA. WebGPU has no
|
|
163
|
+
three-channel texture format, so RGB input raises `Texel::FormatError`; convert
|
|
164
|
+
it explicitly with `image.convert(channels: 4)`. `Texel::WGPU.pad_rows` is also
|
|
165
|
+
available when only transfer-buffer preparation is needed.
|
|
166
|
+
|
|
167
|
+
## OpenGL upload metadata
|
|
168
|
+
|
|
169
|
+
`texel/gl` returns the internal format, external format, and component type
|
|
170
|
+
without loading an OpenGL binding:
|
|
171
|
+
|
|
172
|
+
```ruby
|
|
173
|
+
require "texel/gl"
|
|
174
|
+
|
|
175
|
+
Texel::GL.format(image)
|
|
176
|
+
# => #<data Texel::GL::Format
|
|
177
|
+
# internal_format=:srgb8_alpha8 format=:rgba type=:unsigned_byte>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Errors
|
|
181
|
+
|
|
182
|
+
All library errors inherit from `Texel::Error`:
|
|
183
|
+
|
|
184
|
+
- `Texel::DecodeError`
|
|
185
|
+
- `Texel::EncodeError`
|
|
186
|
+
- `Texel::FormatError`
|
|
187
|
+
- `Texel::MissingCodecError`
|
|
188
|
+
|
|
189
|
+
## Development
|
|
190
|
+
|
|
191
|
+
Install development dependencies and run the complete suite:
|
|
192
|
+
|
|
193
|
+
```sh
|
|
194
|
+
bundle install
|
|
195
|
+
bundle exec rake spec
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
`rake spec` builds the native extension first. The pure Ruby fallback can be
|
|
199
|
+
checked independently:
|
|
200
|
+
|
|
201
|
+
```sh
|
|
202
|
+
TEXEL_DISABLE_NATIVE=1 bundle exec rspec
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
The committed minimal image corpus can be regenerated when ImageMagick is
|
|
206
|
+
available:
|
|
207
|
+
|
|
208
|
+
```sh
|
|
209
|
+
bundle exec rake fixtures
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
The `Native gems` GitHub Actions workflow compiles and tests all supported Ruby
|
|
213
|
+
and platform combinations. Tag builds collect the five platform gems and the
|
|
214
|
+
source gem into a single workflow artifact for release.
|
|
215
|
+
|
|
216
|
+
## License
|
|
217
|
+
|
|
218
|
+
Texel is available under the MIT License. The vendored stb headers retain their
|
|
219
|
+
upstream Public Domain/MIT dual-license notices.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bundler/gem_tasks"
|
|
4
|
+
require "rake/clean"
|
|
5
|
+
require "rspec/core/rake_task"
|
|
6
|
+
require "rbconfig"
|
|
7
|
+
|
|
8
|
+
EXTENSION_DIRECTORY = File.expand_path("ext/texel", __dir__)
|
|
9
|
+
EXTENSION_FILE = File.join(EXTENSION_DIRECTORY, "texel_native.#{RbConfig::CONFIG.fetch("DLEXT")}")
|
|
10
|
+
|
|
11
|
+
task :compile do
|
|
12
|
+
next if ENV["TEXEL_DISABLE_NATIVE"] == "1"
|
|
13
|
+
|
|
14
|
+
Dir.chdir(EXTENSION_DIRECTORY) do
|
|
15
|
+
sh RbConfig.ruby, "extconf.rb"
|
|
16
|
+
sh ENV.fetch("MAKE", "make")
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
21
|
+
task spec: :compile
|
|
22
|
+
|
|
23
|
+
desc "Regenerate the minimal image codec fixtures"
|
|
24
|
+
task fixtures: :compile do
|
|
25
|
+
sh RbConfig.ruby, File.expand_path("spec/fixtures/generate.rb", __dir__)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
CLEAN.include(
|
|
29
|
+
File.join(EXTENSION_DIRECTORY, "Makefile"),
|
|
30
|
+
File.join(EXTENSION_DIRECTORY, "*.o"),
|
|
31
|
+
EXTENSION_FILE,
|
|
32
|
+
File.join(EXTENSION_DIRECTORY, "mkmf.log")
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
task default: :spec
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
if ENV["TEXEL_DISABLE_NATIVE"] == "1"
|
|
4
|
+
File.write(
|
|
5
|
+
"Makefile",
|
|
6
|
+
<<~MAKEFILE
|
|
7
|
+
.PHONY: all install clean
|
|
8
|
+
all:
|
|
9
|
+
install:
|
|
10
|
+
clean:
|
|
11
|
+
MAKEFILE
|
|
12
|
+
)
|
|
13
|
+
exit
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
require "mkmf"
|
|
17
|
+
|
|
18
|
+
$CFLAGS = "#{$CFLAGS} -std=c99 -Wall -Wextra"
|
|
19
|
+
$CFLAGS = "#{$CFLAGS} -Wno-unused-function -Wno-sign-compare"
|
|
20
|
+
|
|
21
|
+
abort "texel requires a C99 compiler" unless try_compile(<<~C)
|
|
22
|
+
#include <stdint.h>
|
|
23
|
+
int main(void) { uint32_t value = 0; return (int)value; }
|
|
24
|
+
C
|
|
25
|
+
|
|
26
|
+
have_library("m", "sinf")
|
|
27
|
+
|
|
28
|
+
create_makefile("texel/texel_native")
|
data/ext/texel/shim.c
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
#include "shim.h"
|
|
2
|
+
|
|
3
|
+
#include <math.h>
|
|
4
|
+
#include <string.h>
|
|
5
|
+
|
|
6
|
+
#define STBI_FAILURE_USERMSG
|
|
7
|
+
#define STB_IMAGE_IMPLEMENTATION
|
|
8
|
+
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
|
9
|
+
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
|
10
|
+
#include "vendor/stb_image.h"
|
|
11
|
+
#include "vendor/stb_image_write.h"
|
|
12
|
+
#include "vendor/stb_image_resize2.h"
|
|
13
|
+
|
|
14
|
+
unsigned char *
|
|
15
|
+
tx_load_u8(const void *buffer, int length, int *width, int *height,
|
|
16
|
+
int *source_channels, int requested_channels, const char **error)
|
|
17
|
+
{
|
|
18
|
+
unsigned char *pixels = stbi_load_from_memory(
|
|
19
|
+
(const stbi_uc *)buffer, length, width, height, source_channels, requested_channels);
|
|
20
|
+
if (pixels == NULL && error != NULL) {
|
|
21
|
+
*error = stbi_failure_reason();
|
|
22
|
+
}
|
|
23
|
+
return pixels;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
unsigned short *
|
|
27
|
+
tx_load_u16(const void *buffer, int length, int *width, int *height,
|
|
28
|
+
int *source_channels, int requested_channels, const char **error)
|
|
29
|
+
{
|
|
30
|
+
unsigned short *pixels = stbi_load_16_from_memory(
|
|
31
|
+
(const stbi_uc *)buffer, length, width, height, source_channels, requested_channels);
|
|
32
|
+
if (pixels == NULL && error != NULL) {
|
|
33
|
+
*error = stbi_failure_reason();
|
|
34
|
+
}
|
|
35
|
+
return pixels;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
float *
|
|
39
|
+
tx_load_f32(const void *buffer, int length, int *width, int *height,
|
|
40
|
+
int *source_channels, int requested_channels, const char **error)
|
|
41
|
+
{
|
|
42
|
+
float *pixels = stbi_loadf_from_memory(
|
|
43
|
+
(const stbi_uc *)buffer, length, width, height, source_channels, requested_channels);
|
|
44
|
+
if (pixels == NULL && error != NULL) {
|
|
45
|
+
*error = stbi_failure_reason();
|
|
46
|
+
}
|
|
47
|
+
return pixels;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
int
|
|
51
|
+
tx_info(const void *buffer, int length, int *width, int *height, int *channels)
|
|
52
|
+
{
|
|
53
|
+
return stbi_info_from_memory((const stbi_uc *)buffer, length, width, height, channels);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
int
|
|
57
|
+
tx_is_16_bit(const void *buffer, int length)
|
|
58
|
+
{
|
|
59
|
+
return stbi_is_16_bit_from_memory((const stbi_uc *)buffer, length);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
int
|
|
63
|
+
tx_is_hdr(const void *buffer, int length)
|
|
64
|
+
{
|
|
65
|
+
return stbi_is_hdr_from_memory((const stbi_uc *)buffer, length);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
void
|
|
69
|
+
tx_free(void *pixels)
|
|
70
|
+
{
|
|
71
|
+
stbi_image_free(pixels);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
int
|
|
75
|
+
tx_write(int format, tx_write_cb callback, void *user_data, int width, int height,
|
|
76
|
+
int channels, const void *pixels, int quality_or_stride)
|
|
77
|
+
{
|
|
78
|
+
switch (format) {
|
|
79
|
+
case 0:
|
|
80
|
+
return stbi_write_png_to_func(callback, user_data, width, height, channels,
|
|
81
|
+
pixels, quality_or_stride);
|
|
82
|
+
case 1:
|
|
83
|
+
return stbi_write_jpg_to_func(callback, user_data, width, height, channels,
|
|
84
|
+
pixels, quality_or_stride);
|
|
85
|
+
case 2:
|
|
86
|
+
return stbi_write_bmp_to_func(callback, user_data, width, height, channels, pixels);
|
|
87
|
+
case 3:
|
|
88
|
+
return stbi_write_tga_to_func(callback, user_data, width, height, channels, pixels);
|
|
89
|
+
case 4:
|
|
90
|
+
return stbi_write_hdr_to_func(callback, user_data, width, height, channels,
|
|
91
|
+
(const float *)pixels);
|
|
92
|
+
default:
|
|
93
|
+
return 0;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
static stbir_pixel_layout
|
|
98
|
+
tx_pixel_layout(int channels, int premultiplied)
|
|
99
|
+
{
|
|
100
|
+
switch (channels) {
|
|
101
|
+
case 1:
|
|
102
|
+
return STBIR_1CHANNEL;
|
|
103
|
+
case 2:
|
|
104
|
+
return premultiplied ? STBIR_RA_PM : STBIR_RA;
|
|
105
|
+
case 3:
|
|
106
|
+
return STBIR_RGB;
|
|
107
|
+
case 4:
|
|
108
|
+
return premultiplied ? STBIR_RGBA_PM : STBIR_RGBA;
|
|
109
|
+
default:
|
|
110
|
+
return STBIR_1CHANNEL;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
static float
|
|
115
|
+
tx_lanczos_kernel(float x, float scale, void *user_data)
|
|
116
|
+
{
|
|
117
|
+
const float pi = 3.14159265358979323846f;
|
|
118
|
+
(void)scale;
|
|
119
|
+
(void)user_data;
|
|
120
|
+
if (x < 0.0f) {
|
|
121
|
+
x = -x;
|
|
122
|
+
}
|
|
123
|
+
if (x < 0.000001f) {
|
|
124
|
+
return 1.0f;
|
|
125
|
+
}
|
|
126
|
+
if (x >= 3.0f) {
|
|
127
|
+
return 0.0f;
|
|
128
|
+
}
|
|
129
|
+
return (sinf(pi * x) / (pi * x)) * (sinf((pi * x) / 3.0f) / ((pi * x) / 3.0f));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
static float
|
|
133
|
+
tx_lanczos_support(float scale, void *user_data)
|
|
134
|
+
{
|
|
135
|
+
(void)scale;
|
|
136
|
+
(void)user_data;
|
|
137
|
+
return 3.0f;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
int
|
|
141
|
+
tx_resize(int dtype, int srgb, const void *source, int source_width, int source_height,
|
|
142
|
+
void *destination, int width, int height, int channels, int filter, int edge,
|
|
143
|
+
int premultiplied)
|
|
144
|
+
{
|
|
145
|
+
stbir_datatype data_type;
|
|
146
|
+
if (dtype == 0) {
|
|
147
|
+
data_type = srgb ? STBIR_TYPE_UINT8_SRGB : STBIR_TYPE_UINT8;
|
|
148
|
+
} else if (dtype == 1) {
|
|
149
|
+
data_type = STBIR_TYPE_UINT16;
|
|
150
|
+
} else if (dtype == 2) {
|
|
151
|
+
data_type = STBIR_TYPE_FLOAT;
|
|
152
|
+
} else {
|
|
153
|
+
return 0;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (filter == STBIR_FILTER_OTHER) {
|
|
157
|
+
STBIR_RESIZE resize;
|
|
158
|
+
stbir_resize_init(&resize, source, source_width, source_height, 0,
|
|
159
|
+
destination, width, height, 0,
|
|
160
|
+
tx_pixel_layout(channels, premultiplied), data_type);
|
|
161
|
+
if (!stbir_set_edgemodes(&resize, (stbir_edge)edge, (stbir_edge)edge) ||
|
|
162
|
+
!stbir_set_filter_callbacks(&resize,
|
|
163
|
+
tx_lanczos_kernel, tx_lanczos_support,
|
|
164
|
+
tx_lanczos_kernel, tx_lanczos_support)) {
|
|
165
|
+
return 0;
|
|
166
|
+
}
|
|
167
|
+
return stbir_resize_extended(&resize);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return stbir_resize(source, source_width, source_height, 0,
|
|
171
|
+
destination, width, height, 0,
|
|
172
|
+
tx_pixel_layout(channels, premultiplied), data_type,
|
|
173
|
+
(stbir_edge)edge, (stbir_filter)filter) != NULL;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
static uint16_t
|
|
177
|
+
tx_float_to_half(float value)
|
|
178
|
+
{
|
|
179
|
+
union {
|
|
180
|
+
float floating;
|
|
181
|
+
uint32_t bits;
|
|
182
|
+
} input;
|
|
183
|
+
uint32_t sign;
|
|
184
|
+
uint32_t exponent;
|
|
185
|
+
uint32_t mantissa;
|
|
186
|
+
int half_exponent;
|
|
187
|
+
uint32_t half_mantissa;
|
|
188
|
+
uint32_t remainder;
|
|
189
|
+
int shift;
|
|
190
|
+
|
|
191
|
+
input.floating = value;
|
|
192
|
+
sign = (input.bits >> 16) & 0x8000u;
|
|
193
|
+
exponent = (input.bits >> 23) & 0xffu;
|
|
194
|
+
mantissa = input.bits & 0x7fffffu;
|
|
195
|
+
|
|
196
|
+
if (exponent == 0xffu) {
|
|
197
|
+
return (uint16_t)(sign | (mantissa == 0 ? 0x7c00u : 0x7e00u));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
half_exponent = (int)exponent - 127 + 15;
|
|
201
|
+
if (half_exponent >= 31) {
|
|
202
|
+
return (uint16_t)(sign | 0x7c00u);
|
|
203
|
+
}
|
|
204
|
+
if (half_exponent <= 0) {
|
|
205
|
+
if (half_exponent < -10) {
|
|
206
|
+
return (uint16_t)sign;
|
|
207
|
+
}
|
|
208
|
+
mantissa |= 0x800000u;
|
|
209
|
+
shift = 14 - half_exponent;
|
|
210
|
+
half_mantissa = mantissa >> shift;
|
|
211
|
+
remainder = mantissa & ((1u << shift) - 1u);
|
|
212
|
+
if (remainder > (1u << (shift - 1)) ||
|
|
213
|
+
(remainder == (1u << (shift - 1)) && (half_mantissa & 1u))) {
|
|
214
|
+
half_mantissa += 1u;
|
|
215
|
+
}
|
|
216
|
+
return (uint16_t)(sign | half_mantissa);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
half_mantissa = mantissa >> 13;
|
|
220
|
+
remainder = mantissa & 0x1fffu;
|
|
221
|
+
if (remainder > 0x1000u || (remainder == 0x1000u && (half_mantissa & 1u))) {
|
|
222
|
+
half_mantissa += 1u;
|
|
223
|
+
if (half_mantissa == 0x400u) {
|
|
224
|
+
half_mantissa = 0;
|
|
225
|
+
half_exponent += 1;
|
|
226
|
+
if (half_exponent >= 31) {
|
|
227
|
+
return (uint16_t)(sign | 0x7c00u);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return (uint16_t)(sign | ((uint32_t)half_exponent << 10) | half_mantissa);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
void
|
|
235
|
+
tx_f32_to_f16(const void *source, uint16_t *destination, size_t count)
|
|
236
|
+
{
|
|
237
|
+
const unsigned char *bytes = (const unsigned char *)source;
|
|
238
|
+
size_t index;
|
|
239
|
+
for (index = 0; index < count; index += 1) {
|
|
240
|
+
float value;
|
|
241
|
+
memcpy(&value, bytes + (index * sizeof(float)), sizeof(float));
|
|
242
|
+
destination[index] = tx_float_to_half(value);
|
|
243
|
+
}
|
|
244
|
+
}
|
data/ext/texel/shim.h
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#ifndef TEXEL_SHIM_H
|
|
2
|
+
#define TEXEL_SHIM_H
|
|
3
|
+
|
|
4
|
+
#include <stddef.h>
|
|
5
|
+
#include <stdint.h>
|
|
6
|
+
|
|
7
|
+
#ifdef __cplusplus
|
|
8
|
+
extern "C" {
|
|
9
|
+
#endif
|
|
10
|
+
|
|
11
|
+
typedef void tx_write_cb(void *user_data, void *data, int size);
|
|
12
|
+
|
|
13
|
+
unsigned char *tx_load_u8(const void *buffer, int length, int *width, int *height,
|
|
14
|
+
int *source_channels, int requested_channels, const char **error);
|
|
15
|
+
unsigned short *tx_load_u16(const void *buffer, int length, int *width, int *height,
|
|
16
|
+
int *source_channels, int requested_channels, const char **error);
|
|
17
|
+
float *tx_load_f32(const void *buffer, int length, int *width, int *height,
|
|
18
|
+
int *source_channels, int requested_channels, const char **error);
|
|
19
|
+
int tx_info(const void *buffer, int length, int *width, int *height, int *channels);
|
|
20
|
+
int tx_is_16_bit(const void *buffer, int length);
|
|
21
|
+
int tx_is_hdr(const void *buffer, int length);
|
|
22
|
+
void tx_free(void *pixels);
|
|
23
|
+
|
|
24
|
+
int tx_write(int format, tx_write_cb callback, void *user_data, int width, int height,
|
|
25
|
+
int channels, const void *pixels, int quality_or_stride);
|
|
26
|
+
int tx_resize(int dtype, int srgb, const void *source, int source_width, int source_height,
|
|
27
|
+
void *destination, int width, int height, int channels, int filter, int edge,
|
|
28
|
+
int premultiplied);
|
|
29
|
+
void tx_f32_to_f16(const void *source, uint16_t *destination, size_t count);
|
|
30
|
+
|
|
31
|
+
#ifdef __cplusplus
|
|
32
|
+
}
|
|
33
|
+
#endif
|
|
34
|
+
|
|
35
|
+
#endif
|