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
data/lib/texel.rb
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "texel/version"
|
|
4
|
+
require_relative "texel/errors"
|
|
5
|
+
require_relative "texel/info"
|
|
6
|
+
require_relative "texel/pixel_data"
|
|
7
|
+
require_relative "texel/image"
|
|
8
|
+
require_relative "texel/color_transform"
|
|
9
|
+
require_relative "texel/native"
|
|
10
|
+
require_relative "texel/resampler"
|
|
11
|
+
require_relative "texel/image_transformations"
|
|
12
|
+
require_relative "texel/codec"
|
|
13
|
+
require_relative "texel/registry"
|
|
14
|
+
require_relative "texel/source_reader"
|
|
15
|
+
require_relative "texel/codecs/qoi"
|
|
16
|
+
require_relative "texel/codecs/stb"
|
|
17
|
+
|
|
18
|
+
module Texel
|
|
19
|
+
class << self
|
|
20
|
+
def load(source, channels: nil, dtype: nil, flip_y: false, color_space: nil)
|
|
21
|
+
validate_channels!(channels) if channels
|
|
22
|
+
validate_dtype!(dtype) if dtype
|
|
23
|
+
validate_color_space!(color_space) if color_space
|
|
24
|
+
|
|
25
|
+
input = SourceReader.read(source)
|
|
26
|
+
format = detect_format!(input)
|
|
27
|
+
codec = Registry.codec_for!(format, operation: :decode)
|
|
28
|
+
image = codec.decode(input.bytes, channels: channels, dtype: dtype, format: format)
|
|
29
|
+
image = image.dup_with(color_space: color_space) if color_space
|
|
30
|
+
flip_y ? image.flip_y : image
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def info(source)
|
|
34
|
+
input = SourceReader.read(source)
|
|
35
|
+
format = detect_format!(input)
|
|
36
|
+
Registry.codec_for!(format, operation: :decode).info(input.bytes, format: format)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def encode(image, format, **options)
|
|
40
|
+
raise FormatError, "expected a Texel::Image" unless image.is_a?(Image)
|
|
41
|
+
|
|
42
|
+
normalized = normalize_format(format)
|
|
43
|
+
encoded = String(Registry.codec_for!(normalized, operation: :encode).encode(image, normalized, **options))
|
|
44
|
+
encoded = encoded.dup.force_encoding(Encoding::BINARY) unless encoded.encoding == Encoding::BINARY
|
|
45
|
+
encoded.freeze
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def save(image, destination, format: nil, **options)
|
|
49
|
+
resolved_format = format || destination_format(destination)
|
|
50
|
+
bytes = encode(image, resolved_format, **options)
|
|
51
|
+
return write_io(destination, bytes) if destination.respond_to?(:write)
|
|
52
|
+
|
|
53
|
+
File.binwrite(destination, bytes)
|
|
54
|
+
destination
|
|
55
|
+
rescue SystemCallError => e
|
|
56
|
+
raise EncodeError, "could not write image: #{e.message}"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def load_all(paths, threads: 4, **options)
|
|
60
|
+
unless threads.is_a?(Integer) && threads.positive?
|
|
61
|
+
raise FormatError, "threads must be a positive integer"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
sources = paths.to_a
|
|
65
|
+
return [] if sources.empty?
|
|
66
|
+
|
|
67
|
+
queue = Queue.new
|
|
68
|
+
sources.each_index { |index| queue << index }
|
|
69
|
+
results = Array.new(sources.length)
|
|
70
|
+
errors = Array.new(sources.length)
|
|
71
|
+
workers = [threads, sources.length].min.times.map do
|
|
72
|
+
Thread.new do
|
|
73
|
+
loop do
|
|
74
|
+
index = queue.pop(true)
|
|
75
|
+
results[index] = load(sources[index], **options)
|
|
76
|
+
rescue ThreadError
|
|
77
|
+
break
|
|
78
|
+
rescue StandardError => e
|
|
79
|
+
errors[index] = e
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
workers.each(&:join)
|
|
84
|
+
raise errors.compact.first if errors.any?
|
|
85
|
+
|
|
86
|
+
results
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
def detect_format!(input)
|
|
92
|
+
Registry.detect(input.bytes.byteslice(0, 32).to_s, extension: input.extension) ||
|
|
93
|
+
raise(FormatError, "unknown image format")
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def destination_format(destination)
|
|
97
|
+
unless destination.respond_to?(:to_path) || destination.is_a?(String)
|
|
98
|
+
raise FormatError, "format is required when saving to an IO"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
path = destination.respond_to?(:to_path) ? destination.to_path : destination
|
|
102
|
+
extension = SourceReader.extension_for_path(path)
|
|
103
|
+
raise FormatError, "destination has no image format extension" unless extension
|
|
104
|
+
|
|
105
|
+
normalize_format(extension)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def normalize_format(format)
|
|
109
|
+
normalized = format.to_s.delete_prefix(".").downcase.to_sym
|
|
110
|
+
normalized == :jpeg ? :jpg : normalized
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def write_io(io, bytes)
|
|
114
|
+
written = io.write(bytes)
|
|
115
|
+
raise EncodeError, "IO wrote #{written} of #{bytes.bytesize} bytes" if written.is_a?(Integer) && written != bytes.bytesize
|
|
116
|
+
|
|
117
|
+
io
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def validate_channels!(channels)
|
|
121
|
+
return if channels.is_a?(Integer) && channels.between?(1, 4)
|
|
122
|
+
|
|
123
|
+
raise FormatError, "channels must be between 1 and 4"
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def validate_dtype!(dtype)
|
|
127
|
+
return if Image::DTYPE_SIZES.key?(dtype)
|
|
128
|
+
|
|
129
|
+
raise FormatError, "unsupported dtype #{dtype.inspect}"
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def validate_color_space!(color_space)
|
|
133
|
+
return if Image::COLOR_SPACES.include?(color_space)
|
|
134
|
+
|
|
135
|
+
raise FormatError, "unsupported color space #{color_space.inspect}"
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
Registry.register(:png, magic: "\x89PNG\r\n\x1a\n".b, codec: Codecs::Stb)
|
|
140
|
+
Registry.register(:jpg, magic: "\xff\xd8\xff".b, codec: Codecs::Stb, extensions: %w[jpg jpeg])
|
|
141
|
+
Registry.register(:gif, magic: ["GIF87a".b, "GIF89a".b], codec: Codecs::Stb)
|
|
142
|
+
Registry.register(:bmp, magic: "BM".b, codec: Codecs::Stb)
|
|
143
|
+
Registry.register(
|
|
144
|
+
:tga,
|
|
145
|
+
magic: lambda do |bytes|
|
|
146
|
+
bytes.bytesize >= 18 && [0, 1].include?(bytes.getbyte(1)) &&
|
|
147
|
+
[1, 2, 3, 9, 10, 11].include?(bytes.getbyte(2)) &&
|
|
148
|
+
[8, 15, 16, 24, 32].include?(bytes.getbyte(16)) &&
|
|
149
|
+
bytes.byteslice(12, 4).unpack("S<2").all?(&:positive?)
|
|
150
|
+
end,
|
|
151
|
+
codec: Codecs::Stb
|
|
152
|
+
)
|
|
153
|
+
Registry.register(:hdr, magic: ["#?RADIANCE".b, "#?RGBE".b], codec: Codecs::Stb)
|
|
154
|
+
Registry.register(:psd, magic: "8BPS".b, codec: Codecs::Stb)
|
|
155
|
+
Registry.register(:qoi, magic: "qoif".b, codec: Codecs::Qoi)
|
|
156
|
+
Registry.register(:ktx2, magic: "\xabKTX 20\xbb\r\n\x1a\n".b, codec: nil, required_gem: "texel-ktx")
|
|
157
|
+
Registry.register(:exr, magic: "\x76\x2f\x31\x01".b, codec: nil, required_gem: "texel-exr")
|
|
158
|
+
Registry.register(
|
|
159
|
+
:webp,
|
|
160
|
+
magic: ->(bytes) { bytes.start_with?("RIFF".b) && bytes.byteslice(8, 4) == "WEBP".b },
|
|
161
|
+
codec: nil,
|
|
162
|
+
required_gem: "texel-webp"
|
|
163
|
+
)
|
|
164
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: texel
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Yudai Takada
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: |-
|
|
13
|
+
Texel provides a compact immutable image container, stb-based codecs, QOI,
|
|
14
|
+
color and channel conversion, mipmap generation, and GPU upload helpers.
|
|
15
|
+
email:
|
|
16
|
+
- t.yudai92@gmail.com
|
|
17
|
+
executables: []
|
|
18
|
+
extensions:
|
|
19
|
+
- ext/texel/extconf.rb
|
|
20
|
+
extra_rdoc_files: []
|
|
21
|
+
files:
|
|
22
|
+
- LICENSE.txt
|
|
23
|
+
- README.md
|
|
24
|
+
- Rakefile
|
|
25
|
+
- ext/texel/extconf.rb
|
|
26
|
+
- ext/texel/shim.c
|
|
27
|
+
- ext/texel/shim.h
|
|
28
|
+
- ext/texel/texel_native.c
|
|
29
|
+
- ext/texel/vendor/README.md
|
|
30
|
+
- ext/texel/vendor/stb_image.h
|
|
31
|
+
- ext/texel/vendor/stb_image_resize2.h
|
|
32
|
+
- ext/texel/vendor/stb_image_write.h
|
|
33
|
+
- lib/texel.rb
|
|
34
|
+
- lib/texel/codec.rb
|
|
35
|
+
- lib/texel/codecs/qoi.rb
|
|
36
|
+
- lib/texel/codecs/stb.rb
|
|
37
|
+
- lib/texel/color_transform.rb
|
|
38
|
+
- lib/texel/errors.rb
|
|
39
|
+
- lib/texel/gl.rb
|
|
40
|
+
- lib/texel/half_float.rb
|
|
41
|
+
- lib/texel/image.rb
|
|
42
|
+
- lib/texel/image_transformations.rb
|
|
43
|
+
- lib/texel/info.rb
|
|
44
|
+
- lib/texel/native.rb
|
|
45
|
+
- lib/texel/pixel_data.rb
|
|
46
|
+
- lib/texel/registry.rb
|
|
47
|
+
- lib/texel/resampler.rb
|
|
48
|
+
- lib/texel/source_reader.rb
|
|
49
|
+
- lib/texel/version.rb
|
|
50
|
+
- lib/texel/wgpu.rb
|
|
51
|
+
homepage: https://github.com/ydah/texel
|
|
52
|
+
licenses:
|
|
53
|
+
- MIT
|
|
54
|
+
metadata:
|
|
55
|
+
source_code_uri: https://github.com/ydah/texel
|
|
56
|
+
changelog_uri: https://github.com/ydah/texel/releases
|
|
57
|
+
rubygems_mfa_required: 'true'
|
|
58
|
+
rdoc_options: []
|
|
59
|
+
require_paths:
|
|
60
|
+
- lib
|
|
61
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
62
|
+
requirements:
|
|
63
|
+
- - ">="
|
|
64
|
+
- !ruby/object:Gem::Version
|
|
65
|
+
version: 3.2.0
|
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
67
|
+
requirements:
|
|
68
|
+
- - ">="
|
|
69
|
+
- !ruby/object:Gem::Version
|
|
70
|
+
version: '0'
|
|
71
|
+
requirements: []
|
|
72
|
+
rubygems_version: 4.0.6
|
|
73
|
+
specification_version: 4
|
|
74
|
+
summary: Texture-oriented image decoding, conversion, and GPU upload
|
|
75
|
+
test_files: []
|