fast_resize 1.0.2 → 1.0.3

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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/bindings/ruby/ext/fastresize/extconf.rb +79 -105
  4. data/bindings/ruby/lib/fastresize/platform.rb +102 -56
  5. data/bindings/ruby/lib/fastresize/version.rb +1 -1
  6. data/bindings/ruby/lib/fastresize.rb +321 -6
  7. data/bindings/ruby/prebuilt/linux-aarch64/bin/fast_resize +0 -0
  8. data/bindings/ruby/prebuilt/linux-aarch64.tar.gz +0 -0
  9. data/bindings/ruby/prebuilt/linux-x86_64/bin/fast_resize +0 -0
  10. data/bindings/ruby/prebuilt/linux-x86_64/lib/libfastresize.a +0 -0
  11. data/bindings/ruby/prebuilt/linux-x86_64.tar.gz +0 -0
  12. data/bindings/ruby/prebuilt/macos-arm64/bin/fast_resize +0 -0
  13. data/bindings/ruby/prebuilt/macos-arm64/lib/libfastresize.a +0 -0
  14. data/bindings/ruby/prebuilt/macos-arm64.tar.gz +0 -0
  15. data/bindings/ruby/prebuilt/macos-x86_64/bin/fast_resize +0 -0
  16. data/bindings/ruby/prebuilt/macos-x86_64/lib/libfastresize.a +0 -0
  17. data/bindings/ruby/prebuilt/macos-x86_64.tar.gz +0 -0
  18. metadata +4 -22
  19. data/CMakeLists.txt +0 -311
  20. data/bindings/ruby/ext/fastresize/fastresize_ext.cpp +0 -377
  21. data/include/fastresize.h +0 -189
  22. data/include/stb_image.h +0 -7988
  23. data/include/stb_image_resize2.h +0 -10651
  24. data/include/stb_image_write.h +0 -1724
  25. data/src/cli.cpp +0 -540
  26. data/src/decoder.cpp +0 -647
  27. data/src/encoder.cpp +0 -376
  28. data/src/fastresize.cpp +0 -445
  29. data/src/internal.h +0 -108
  30. data/src/pipeline.cpp +0 -284
  31. data/src/pipeline.h +0 -175
  32. data/src/resizer.cpp +0 -199
  33. data/src/simd_resize.cpp +0 -384
  34. data/src/simd_resize.h +0 -72
  35. data/src/simd_utils.h +0 -127
  36. data/src/thread_pool.cpp +0 -232
data/src/pipeline.cpp DELETED
@@ -1,284 +0,0 @@
1
- /*
2
- * FastResize - The Fastest Image Resizing Library On The Planet
3
- * Copyright (C) 2025 Tran Huu Canh (0xTh3OKrypt) and FastResize Contributors
4
- *
5
- * Resize 1,000 images in 2 seconds. Up to 2.9x faster than libvips,
6
- * 3.1x faster than imageflow. Uses 3-4x less RAM than alternatives.
7
- *
8
- * Author: Tran Huu Canh (0xTh3OKrypt)
9
- * Email: tranhuucanh39@gmail.com
10
- * Homepage: https://github.com/tranhuucanh/fast_resize
11
- *
12
- * BSD 3-Clause License
13
- *
14
- * Redistribution and use in source and binary forms, with or without
15
- * modification, are permitted provided that the following conditions are met:
16
- *
17
- * 1. Redistributions of source code must retain the above copyright notice,
18
- * this list of conditions and the following disclaimer.
19
- *
20
- * 2. Redistributions in binary form must reproduce the above copyright notice,
21
- * this list of conditions and the following disclaimer in the documentation
22
- * and/or other materials provided with the distribution.
23
- *
24
- * 3. Neither the name of the copyright holder nor the names of its
25
- * contributors may be used to endorse or promote products derived from
26
- * this software without specific prior written permission.
27
- *
28
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
29
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
32
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
38
- * THE POSSIBILITY OF SUCH DAMAGE.
39
- */
40
-
41
- #include "pipeline.h"
42
- #include <thread>
43
-
44
- namespace fastresize {
45
- namespace internal {
46
-
47
- PipelineProcessor::PipelineProcessor(
48
- size_t decode_threads,
49
- size_t resize_threads,
50
- size_t encode_threads,
51
- size_t queue_capacity
52
- )
53
- : decode_pool_(nullptr)
54
- , resize_pool_(nullptr)
55
- , encode_pool_(nullptr)
56
- , decode_queue_(queue_capacity)
57
- , resize_queue_(queue_capacity)
58
- , success_count_(0)
59
- , failed_count_(0)
60
- {
61
- decode_pool_ = create_thread_pool(decode_threads);
62
- resize_pool_ = create_thread_pool(resize_threads);
63
- encode_pool_ = create_thread_pool(encode_threads);
64
-
65
- for (size_t i = 0; i < encode_threads; ++i) {
66
- encode_buffer_pools_.push_back(create_buffer_pool());
67
- }
68
- }
69
-
70
- PipelineProcessor::~PipelineProcessor() {
71
- if (decode_pool_) destroy_thread_pool(decode_pool_);
72
- if (resize_pool_) destroy_thread_pool(resize_pool_);
73
- if (encode_pool_) destroy_thread_pool(encode_pool_);
74
-
75
- for (BufferPool* pool : encode_buffer_pools_) {
76
- destroy_buffer_pool(pool);
77
- }
78
- }
79
-
80
- void PipelineProcessor::decode_stage(const std::vector<BatchItem>& items) {
81
- for (size_t i = 0; i < items.size(); ++i) {
82
- thread_pool_enqueue(decode_pool_, [this, &items, i]() {
83
- const auto& item = items[i];
84
-
85
- DecodeResult result;
86
- result.task_id = i;
87
- result.output_path = item.output_path;
88
- result.options = item.options;
89
- result.success = false;
90
-
91
- ImageFormat fmt = detect_format(item.input_path);
92
- if (fmt == FORMAT_UNKNOWN) {
93
- result.error_message = "Unknown format: " + item.input_path;
94
- decode_queue_.push(std::move(result));
95
- return;
96
- }
97
-
98
- result.image = decode_image(item.input_path, fmt);
99
- if (result.image.pixels == nullptr) {
100
- result.error_message = "Decode failed: " + item.input_path;
101
- decode_queue_.push(std::move(result));
102
- return;
103
- }
104
-
105
- result.success = true;
106
- decode_queue_.push(std::move(result));
107
- });
108
- }
109
-
110
- thread_pool_wait(decode_pool_);
111
- decode_queue_.set_done();
112
- }
113
-
114
- void PipelineProcessor::resize_stage() {
115
- std::vector<std::thread> workers;
116
- for (size_t i = 0; i < 8; ++i) {
117
- workers.emplace_back([this]() {
118
- DecodeResult decode_result;
119
-
120
- while (decode_queue_.pop(decode_result)) {
121
- ResizeResult resize_result;
122
- resize_result.task_id = decode_result.task_id;
123
- resize_result.output_path = decode_result.output_path;
124
- resize_result.options = decode_result.options;
125
- resize_result.success = false;
126
-
127
- if (!decode_result.success) {
128
- resize_result.error_message = decode_result.error_message;
129
- resize_result.pixels = nullptr;
130
- resize_result.width = 0;
131
- resize_result.height = 0;
132
- resize_result.channels = 0;
133
- resize_queue_.push(std::move(resize_result));
134
- continue;
135
- }
136
-
137
- int out_w, out_h;
138
- calculate_dimensions(
139
- decode_result.image.width,
140
- decode_result.image.height,
141
- decode_result.options,
142
- out_w, out_h
143
- );
144
-
145
- unsigned char* resized_pixels = nullptr;
146
- bool resize_ok = resize_image(
147
- decode_result.image.pixels,
148
- decode_result.image.width,
149
- decode_result.image.height,
150
- decode_result.image.channels,
151
- &resized_pixels,
152
- out_w, out_h,
153
- decode_result.options
154
- );
155
-
156
- free_image_data(decode_result.image);
157
-
158
- if (!resize_ok || resized_pixels == nullptr) {
159
- resize_result.error_message = "Resize failed";
160
- resize_result.pixels = nullptr;
161
- resize_result.width = 0;
162
- resize_result.height = 0;
163
- resize_result.channels = 0;
164
- resize_queue_.push(std::move(resize_result));
165
- continue;
166
- }
167
-
168
- resize_result.pixels = resized_pixels;
169
- resize_result.width = out_w;
170
- resize_result.height = out_h;
171
- resize_result.channels = decode_result.image.channels;
172
- resize_result.success = true;
173
- resize_queue_.push(std::move(resize_result));
174
- }
175
- });
176
- }
177
-
178
- for (auto& worker : workers) {
179
- worker.join();
180
- }
181
-
182
- resize_queue_.set_done();
183
- }
184
-
185
- void PipelineProcessor::encode_stage() {
186
- std::vector<std::thread> workers;
187
- for (size_t i = 0; i < encode_buffer_pools_.size(); ++i) {
188
- workers.emplace_back([this, i]() {
189
- BufferPool* buffer_pool = encode_buffer_pools_[i];
190
- ResizeResult resize_result;
191
-
192
- while (resize_queue_.pop(resize_result)) {
193
- if (!resize_result.success) {
194
- failed_count_.fetch_add(1);
195
- if (!resize_result.error_message.empty()) {
196
- std::lock_guard<std::mutex> lock(errors_mutex_);
197
- errors_.push_back(resize_result.error_message);
198
- }
199
- continue;
200
- }
201
-
202
- ImageFormat out_fmt = FORMAT_UNKNOWN;
203
- size_t dot_pos = resize_result.output_path.find_last_of('.');
204
- if (dot_pos != std::string::npos) {
205
- std::string ext = resize_result.output_path.substr(dot_pos + 1);
206
- for (char& c : ext) {
207
- if (c >= 'A' && c <= 'Z') c = c - 'A' + 'a';
208
- }
209
- out_fmt = string_to_format(ext);
210
- }
211
-
212
- if (out_fmt == FORMAT_UNKNOWN) {
213
- out_fmt = FORMAT_JPEG;
214
- }
215
-
216
- if (resize_result.pixels == nullptr || resize_result.width <= 0 ||
217
- resize_result.height <= 0 || resize_result.channels <= 0) {
218
- failed_count_.fetch_add(1);
219
- std::lock_guard<std::mutex> lock(errors_mutex_);
220
- errors_.push_back("Invalid resize data for: " + resize_result.output_path);
221
- if (resize_result.pixels) delete[] resize_result.pixels;
222
- continue;
223
- }
224
-
225
- ImageData img_data;
226
- img_data.pixels = resize_result.pixels;
227
- img_data.width = resize_result.width;
228
- img_data.height = resize_result.height;
229
- img_data.channels = resize_result.channels;
230
-
231
- bool encode_ok = encode_image(
232
- resize_result.output_path,
233
- img_data,
234
- out_fmt,
235
- resize_result.options.quality,
236
- buffer_pool
237
- );
238
-
239
- delete[] resize_result.pixels;
240
-
241
- if (encode_ok) {
242
- success_count_.fetch_add(1);
243
- } else {
244
- failed_count_.fetch_add(1);
245
- std::lock_guard<std::mutex> lock(errors_mutex_);
246
- char buf[512];
247
- snprintf(buf, sizeof(buf), "Encode failed: %s (fmt=%d, %dx%d, %dch)",
248
- resize_result.output_path.c_str(), (int)out_fmt,
249
- resize_result.width, resize_result.height, resize_result.channels);
250
- errors_.push_back(buf);
251
- }
252
- }
253
- });
254
- }
255
-
256
- for (auto& worker : workers) {
257
- worker.join();
258
- }
259
- }
260
-
261
- BatchResult PipelineProcessor::process_batch(const std::vector<BatchItem>& items) {
262
- success_count_ = 0;
263
- failed_count_ = 0;
264
- errors_.clear();
265
-
266
- std::thread decode_thread([this, &items]() { decode_stage(items); });
267
- std::thread resize_thread([this]() { resize_stage(); });
268
- std::thread encode_thread([this]() { encode_stage(); });
269
-
270
- decode_thread.join();
271
- resize_thread.join();
272
- encode_thread.join();
273
-
274
- BatchResult result;
275
- result.total = items.size();
276
- result.success = success_count_.load();
277
- result.failed = failed_count_.load();
278
- result.errors = errors_;
279
-
280
- return result;
281
- }
282
-
283
- }
284
- }
data/src/pipeline.h DELETED
@@ -1,175 +0,0 @@
1
- /*
2
- * FastResize - The Fastest Image Resizing Library On The Planet
3
- * Copyright (C) 2025 Tran Huu Canh (0xTh3OKrypt) and FastResize Contributors
4
- *
5
- * Resize 1,000 images in 2 seconds. Up to 2.9x faster than libvips,
6
- * 3.1x faster than imageflow. Uses 3-4x less RAM than alternatives.
7
- *
8
- * Author: Tran Huu Canh (0xTh3OKrypt)
9
- * Email: tranhuucanh39@gmail.com
10
- * Homepage: https://github.com/tranhuucanh/fast_resize
11
- *
12
- * BSD 3-Clause License
13
- *
14
- * Redistribution and use in source and binary forms, with or without
15
- * modification, are permitted provided that the following conditions are met:
16
- *
17
- * 1. Redistributions of source code must retain the above copyright notice,
18
- * this list of conditions and the following disclaimer.
19
- *
20
- * 2. Redistributions in binary form must reproduce the above copyright notice,
21
- * this list of conditions and the following disclaimer in the documentation
22
- * and/or other materials provided with the distribution.
23
- *
24
- * 3. Neither the name of the copyright holder nor the names of its
25
- * contributors may be used to endorse or promote products derived from
26
- * this software without specific prior written permission.
27
- *
28
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
29
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
32
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
38
- * THE POSSIBILITY OF SUCH DAMAGE.
39
- */
40
-
41
- #pragma once
42
-
43
- #include "internal.h"
44
- #include <queue>
45
- #include <mutex>
46
- #include <condition_variable>
47
- #include <thread>
48
- #include <atomic>
49
-
50
- namespace fastresize {
51
- namespace internal {
52
-
53
- template<typename T>
54
- class BoundedQueue {
55
- public:
56
- BoundedQueue(size_t capacity)
57
- : capacity_(capacity)
58
- , done_(false)
59
- {}
60
-
61
- bool push(T&& item) {
62
- std::unique_lock<std::mutex> lock(mutex_);
63
-
64
- cv_not_full_.wait(lock, [this]() {
65
- return queue_.size() < capacity_ || done_;
66
- });
67
-
68
- if (done_) return false;
69
-
70
- queue_.push(std::move(item));
71
- lock.unlock();
72
- cv_not_empty_.notify_one();
73
- return true;
74
- }
75
-
76
- bool pop(T& item) {
77
- std::unique_lock<std::mutex> lock(mutex_);
78
-
79
- cv_not_empty_.wait(lock, [this]() {
80
- return !queue_.empty() || done_;
81
- });
82
-
83
- if (queue_.empty() && done_) return false;
84
-
85
- item = std::move(queue_.front());
86
- queue_.pop();
87
- lock.unlock();
88
- cv_not_full_.notify_one();
89
- return true;
90
- }
91
-
92
- void set_done() {
93
- std::unique_lock<std::mutex> lock(mutex_);
94
- done_ = true;
95
- lock.unlock();
96
- cv_not_empty_.notify_all();
97
- cv_not_full_.notify_all();
98
- }
99
-
100
- size_t size() const {
101
- std::lock_guard<std::mutex> lock(mutex_);
102
- return queue_.size();
103
- }
104
-
105
- private:
106
- size_t capacity_;
107
- std::queue<T> queue_;
108
- mutable std::mutex mutex_;
109
- std::condition_variable cv_not_empty_;
110
- std::condition_variable cv_not_full_;
111
- bool done_;
112
- };
113
-
114
- struct DecodeTask {
115
- std::string input_path;
116
- std::string output_path;
117
- ResizeOptions options;
118
- int task_id;
119
- };
120
-
121
- struct DecodeResult {
122
- ImageData image;
123
- std::string output_path;
124
- ResizeOptions options;
125
- int task_id;
126
- bool success;
127
- std::string error_message;
128
- };
129
-
130
- struct ResizeResult {
131
- unsigned char* pixels;
132
- int width;
133
- int height;
134
- int channels;
135
- std::string output_path;
136
- ResizeOptions options;
137
- int task_id;
138
- bool success;
139
- std::string error_message;
140
- };
141
-
142
- class PipelineProcessor {
143
- public:
144
- PipelineProcessor(
145
- size_t decode_threads = 4,
146
- size_t resize_threads = 8,
147
- size_t encode_threads = 4,
148
- size_t queue_capacity = 32
149
- );
150
-
151
- ~PipelineProcessor();
152
- BatchResult process_batch(const std::vector<BatchItem>& items);
153
-
154
- private:
155
- ThreadPool* decode_pool_;
156
- ThreadPool* resize_pool_;
157
- ThreadPool* encode_pool_;
158
-
159
- std::vector<BufferPool*> encode_buffer_pools_;
160
-
161
- BoundedQueue<DecodeResult> decode_queue_;
162
- BoundedQueue<ResizeResult> resize_queue_;
163
-
164
- std::atomic<int> success_count_;
165
- std::atomic<int> failed_count_;
166
- std::mutex errors_mutex_;
167
- std::vector<std::string> errors_;
168
-
169
- void decode_stage(const std::vector<BatchItem>& items);
170
- void resize_stage();
171
- void encode_stage();
172
- };
173
-
174
- }
175
- }
data/src/resizer.cpp DELETED
@@ -1,199 +0,0 @@
1
- /*
2
- * FastResize - The Fastest Image Resizing Library On The Planet
3
- * Copyright (C) 2025 Tran Huu Canh (0xTh3OKrypt) and FastResize Contributors
4
- *
5
- * Resize 1,000 images in 2 seconds. Up to 2.9x faster than libvips,
6
- * 3.1x faster than imageflow. Uses 3-4x less RAM than alternatives.
7
- *
8
- * Author: Tran Huu Canh (0xTh3OKrypt)
9
- * Email: tranhuucanh39@gmail.com
10
- * Homepage: https://github.com/tranhuucanh/fast_resize
11
- *
12
- * BSD 3-Clause License
13
- *
14
- * Redistribution and use in source and binary forms, with or without
15
- * modification, are permitted provided that the following conditions are met:
16
- *
17
- * 1. Redistributions of source code must retain the above copyright notice,
18
- * this list of conditions and the following disclaimer.
19
- *
20
- * 2. Redistributions in binary form must reproduce the above copyright notice,
21
- * this list of conditions and the following disclaimer in the documentation
22
- * and/or other materials provided with the distribution.
23
- *
24
- * 3. Neither the name of the copyright holder nor the names of its
25
- * contributors may be used to endorse or promote products derived from
26
- * this software without specific prior written permission.
27
- *
28
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
29
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
32
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
38
- * THE POSSIBILITY OF SUCH DAMAGE.
39
- */
40
-
41
- #include "internal.h"
42
- #include "simd_resize.h"
43
- #include <cmath>
44
-
45
- #define STB_IMAGE_RESIZE_IMPLEMENTATION
46
- #include "stb_image_resize2.h"
47
-
48
- namespace fastresize {
49
- namespace internal {
50
-
51
- void calculate_dimensions(
52
- int in_w, int in_h,
53
- const ResizeOptions& opts,
54
- int& out_w, int& out_h
55
- ) {
56
- switch (opts.mode) {
57
- case ResizeOptions::SCALE_PERCENT:
58
- out_w = static_cast<int>(std::round(in_w * opts.scale_percent));
59
- out_h = static_cast<int>(std::round(in_h * opts.scale_percent));
60
- break;
61
-
62
- case ResizeOptions::FIT_WIDTH:
63
- out_w = opts.target_width;
64
- if (opts.keep_aspect_ratio) {
65
- float ratio = static_cast<float>(out_w) / in_w;
66
- out_h = static_cast<int>(std::round(in_h * ratio));
67
- } else {
68
- out_h = in_h;
69
- }
70
- break;
71
-
72
- case ResizeOptions::FIT_HEIGHT:
73
- out_h = opts.target_height;
74
- if (opts.keep_aspect_ratio) {
75
- float ratio = static_cast<float>(out_h) / in_h;
76
- out_w = static_cast<int>(std::round(in_w * ratio));
77
- } else {
78
- out_w = in_w;
79
- }
80
- break;
81
-
82
- case ResizeOptions::EXACT_SIZE:
83
- out_w = opts.target_width;
84
- out_h = opts.target_height;
85
- if (opts.keep_aspect_ratio) {
86
- float ratio_w = static_cast<float>(out_w) / in_w;
87
- float ratio_h = static_cast<float>(out_h) / in_h;
88
- float ratio = std::min(ratio_w, ratio_h);
89
- out_w = static_cast<int>(std::round(in_w * ratio));
90
- out_h = static_cast<int>(std::round(in_h * ratio));
91
- }
92
- break;
93
- }
94
-
95
- if (out_w < 1) out_w = 1;
96
- if (out_h < 1) out_h = 1;
97
- }
98
-
99
- bool resize_image(
100
- const unsigned char* input_pixels,
101
- int input_w, int input_h, int channels,
102
- unsigned char** output_pixels,
103
- int output_w, int output_h,
104
- const ResizeOptions& opts
105
- ) {
106
- if (!input_pixels || input_w <= 0 || input_h <= 0 ||
107
- output_w <= 0 || output_h <= 0 || channels <= 0) {
108
- set_last_error(RESIZE_ERROR, "Invalid input parameters for resize");
109
- return false;
110
- }
111
-
112
- size_t output_size = static_cast<size_t>(output_w) * output_h * channels;
113
- *output_pixels = new unsigned char[output_size];
114
-
115
- float downscale_ratio_w = static_cast<float>(input_w) / output_w;
116
- float downscale_ratio_h = static_cast<float>(input_h) / output_h;
117
- float max_downscale = (downscale_ratio_w > downscale_ratio_h) ? downscale_ratio_w : downscale_ratio_h;
118
-
119
- stbir_filter stb_filter;
120
-
121
- if (max_downscale >= 3.0f && opts.filter == ResizeOptions::MITCHELL) {
122
- stb_filter = STBIR_FILTER_TRIANGLE;
123
- } else {
124
- switch (opts.filter) {
125
- case ResizeOptions::MITCHELL:
126
- stb_filter = STBIR_FILTER_MITCHELL;
127
- break;
128
- case ResizeOptions::CATMULL_ROM:
129
- stb_filter = STBIR_FILTER_CATMULLROM;
130
- break;
131
- case ResizeOptions::BOX:
132
- stb_filter = STBIR_FILTER_BOX;
133
- break;
134
- case ResizeOptions::TRIANGLE:
135
- stb_filter = STBIR_FILTER_TRIANGLE;
136
- break;
137
- default:
138
- stb_filter = STBIR_FILTER_MITCHELL;
139
- }
140
- }
141
-
142
- bool use_simd = (output_w < input_w && output_h < input_h) &&
143
- (stb_filter == STBIR_FILTER_TRIANGLE ||
144
- (stb_filter == STBIR_FILTER_MITCHELL && max_downscale < 3.0f)) &&
145
- (channels == 3 || channels == 4 || channels == 1);
146
-
147
- if (use_simd) {
148
- bool simd_ok = simd_resize(
149
- input_pixels, input_w, input_h, channels,
150
- *output_pixels, output_w, output_h,
151
- ResizeQuality::FAST
152
- );
153
-
154
- if (simd_ok) {
155
- return true;
156
- }
157
- }
158
-
159
- stbir_pixel_layout pixel_layout;
160
- switch (channels) {
161
- case 1:
162
- pixel_layout = STBIR_1CHANNEL;
163
- break;
164
- case 2:
165
- pixel_layout = STBIR_2CHANNEL;
166
- break;
167
- case 3:
168
- pixel_layout = STBIR_RGB;
169
- break;
170
- case 4:
171
- pixel_layout = STBIR_RGBA;
172
- break;
173
- default:
174
- delete[] *output_pixels;
175
- *output_pixels = nullptr;
176
- set_last_error(RESIZE_ERROR, "Unsupported number of channels");
177
- return false;
178
- }
179
-
180
- void* result = stbir_resize(
181
- input_pixels, input_w, input_h, 0,
182
- *output_pixels, output_w, output_h, 0,
183
- pixel_layout, STBIR_TYPE_UINT8,
184
- STBIR_EDGE_CLAMP,
185
- stb_filter
186
- );
187
-
188
- if (!result) {
189
- delete[] *output_pixels;
190
- *output_pixels = nullptr;
191
- set_last_error(RESIZE_ERROR, "stb_image_resize2 failed");
192
- return false;
193
- }
194
-
195
- return true;
196
- }
197
-
198
- }
199
- }