Line data Source code
1 : /**
2 : Copyright (c) 2017-2022 Roman Katuntsev <sbkarr@stappler.org>
3 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
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.
22 : **/
23 :
24 : #include "SPBitmapFormat.h"
25 : #include "SPBytesView.h"
26 : #include "SPFilesystem.h"
27 :
28 : namespace STAPPLER_VERSIONIZED stappler::bitmap {
29 :
30 : const BitmapFormat &getDefaultFormat(uint32_t);
31 : static std::unique_lock<std::mutex> lockFormatList();
32 : static void addCustomFormat(BitmapFormat &&fmt);
33 : static const std::vector<BitmapFormat *> &getCustomFormats();
34 :
35 25 : void BitmapFormat::add(BitmapFormat &&fmt) {
36 25 : addCustomFormat(move(fmt));
37 25 : }
38 :
39 175 : BitmapFormat::BitmapFormat(FileFormat f, const check_fn &c, const size_fn &s, const info_fn &i,
40 175 : const load_fn &l, const write_fn &wr, const save_fn &sv)
41 175 : : check_ptr(c), size_ptr(s), info_ptr(i), load_ptr(l), write_ptr(wr), save_ptr(sv), _format(f), _name(), _mime(getMimeType(f)) {
42 175 : assert(f != FileFormat::Custom);
43 175 : if (check_ptr && size_ptr) {
44 175 : _flags |= Recognizable;
45 : }
46 175 : if (load_ptr) {
47 125 : _flags |= Readable;
48 : }
49 175 : if (save_ptr || write_ptr) {
50 100 : _flags |= Writable;
51 : }
52 :
53 175 : switch (_format) {
54 25 : case FileFormat::Png: _name = StringView("PNG"); break;
55 25 : case FileFormat::Jpeg: _name = StringView("JPEG"); break;
56 25 : case FileFormat::WebpLossless: _name = StringView("WebP-lossless"); break;
57 25 : case FileFormat::WebpLossy: _name = StringView("WebP-lossy"); break;
58 25 : case FileFormat::Svg: _name = StringView("SVG"); break;
59 25 : case FileFormat::Gif: _name = StringView("GIF"); break;
60 25 : case FileFormat::Tiff: _name = StringView("TIFF"); break;
61 0 : default: break;
62 : }
63 175 : }
64 :
65 25 : BitmapFormat::BitmapFormat(StringView n, StringView mime, const check_fn &c, const size_fn &s, const info_fn &i,
66 25 : const load_fn &l, const write_fn &wr, const save_fn &sv)
67 25 : : check_ptr(c), size_ptr(s), info_ptr(i), load_ptr(l), write_ptr(wr), save_ptr(sv), _format(FileFormat::Custom), _name(n), _mime(mime) {
68 25 : if (check_ptr && size_ptr) {
69 25 : _flags |= Recognizable;
70 : }
71 25 : if (load_ptr) {
72 25 : _flags |= Readable;
73 : }
74 25 : if (save_ptr || write_ptr) {
75 25 : _flags |= Writable;
76 : }
77 25 : }
78 :
79 26369 : bool BitmapFormat::isRecognizable() const {
80 26369 : return (_flags & Recognizable) != None;
81 : }
82 11260 : bool BitmapFormat::isReadable() const {
83 11260 : return (_flags & Readable) != None;
84 : }
85 2176 : bool BitmapFormat::isWritable() const {
86 2176 : return (_flags & Writable) != None;
87 : }
88 :
89 21785 : bool BitmapFormat::is(const uint8_t * data, size_t dataLen) const {
90 21785 : if (check_ptr) {
91 21785 : return check_ptr(data, dataLen);
92 : }
93 0 : return false;
94 : }
95 6394 : bool BitmapFormat::getSize(const io::Producer &file, StackBuffer<512> &buf, uint32_t &width, uint32_t &height) const {
96 6394 : if (size_ptr) {
97 6394 : return size_ptr(file, buf, width, height);
98 : }
99 0 : return false;
100 : }
101 :
102 1033 : bool BitmapFormat::getInfo(const uint8_t *data, size_t size, ImageInfo &info) const {
103 1033 : if (info_ptr) {
104 1033 : return info_ptr(data, size, info);
105 : }
106 0 : return false;
107 : }
108 :
109 1678 : bool BitmapFormat::load(const uint8_t *data, size_t size, BitmapWriter &state) const {
110 1678 : if (load_ptr) {
111 1678 : return load_ptr(data, size, state);
112 : }
113 0 : return false;
114 : }
115 :
116 501 : bool BitmapFormat::write(const uint8_t *data, BitmapWriter &state, bool invert) const {
117 501 : if (write_ptr) {
118 501 : return write_ptr(data, state, invert);
119 : }
120 0 : return false;
121 : }
122 :
123 1625 : bool BitmapFormat::save(StringView path, const uint8_t *data, BitmapWriter &state, bool invert) const {
124 1625 : if (save_ptr) {
125 1625 : return save_ptr(path, data, state, invert);
126 : }
127 0 : return false;
128 : }
129 :
130 669 : bool getImageSize(StringView path, uint32_t &width, uint32_t &height) {
131 669 : auto file = filesystem::openForReading(path);
132 1338 : return getImageSize(file, width, height);
133 669 : }
134 :
135 1819 : bool getImageSize(const io::Producer &file, uint32_t &width, uint32_t &height) {
136 1819 : StackBuffer<512> data;
137 1819 : auto dataSize = file.seekAndRead(0, data, 512);
138 1819 : if (dataSize < 32) {
139 0 : return false;
140 : }
141 :
142 6444 : for (int i = 0; i < toInt(FileFormat::Custom); ++i) {
143 6394 : if (getDefaultFormat(i).isRecognizable() && getDefaultFormat(i).getSize(file, data, width, height)) {
144 1769 : return true;
145 : }
146 : }
147 :
148 50 : memory::vector<BitmapFormat::size_fn> fns;
149 :
150 50 : auto lock = lockFormatList();
151 50 : fns.reserve(getCustomFormats().size());
152 :
153 100 : for (auto &it : getCustomFormats()) {
154 50 : if (it->isRecognizable()) {
155 50 : fns.emplace_back(it->getSizeFn());
156 : }
157 : }
158 :
159 50 : lock.unlock();
160 :
161 50 : for (auto &it : fns) {
162 50 : if (it(file, data, width, height)) {
163 50 : return true;
164 : }
165 : }
166 :
167 0 : return false;
168 50 : }
169 :
170 1158 : bool getImageInfo(BytesView data, ImageInfo &info) {
171 3033 : for (int i = 0; i < toInt(FileFormat::Custom); ++i) {
172 5341 : if (getDefaultFormat(i).isReadable() && getDefaultFormat(i).is(data.data(), data.size())
173 5341 : && getDefaultFormat(i).getInfo(data.data(), data.size(), info)) {
174 1008 : info.format = &getDefaultFormat(i);
175 1008 : return true;
176 : }
177 : }
178 :
179 150 : memory::vector<BitmapFormat *> fns;
180 :
181 150 : auto lock = lockFormatList();
182 150 : fns.reserve(getCustomFormats().size());
183 :
184 300 : for (auto &it : getCustomFormats()) {
185 150 : if (it->isReadable() && it->is(data.data(), data.size())) {
186 25 : fns.emplace_back(it);
187 : }
188 : }
189 :
190 150 : lock.unlock();
191 :
192 150 : for (auto &it : fns) {
193 25 : if (it->getInfo(data.data(), data.size(), info)) {
194 25 : info.format = it;
195 25 : return true;
196 : }
197 : }
198 :
199 125 : return false;
200 150 : }
201 :
202 950 : bool isImage(StringView path, bool readable) {
203 950 : auto file = filesystem::openForReading(path);
204 1900 : return isImage(file, readable);
205 950 : }
206 1900 : bool isImage(const io::Producer &file, bool readable) {
207 1900 : StackBuffer<512> data;
208 1900 : if (file.seekAndRead(0, data, 512) < 32) {
209 0 : return false;
210 : }
211 :
212 1900 : return isImage(data.data(), data.size(), readable);
213 : }
214 :
215 2850 : bool isImage(const uint8_t * data, size_t dataLen, bool readable) {
216 13275 : for (int i = 0; i < toInt(FileFormat::Custom); ++i) {
217 25500 : if (getDefaultFormat(i).isRecognizable() && (!readable || getDefaultFormat(i).isReadable())
218 25500 : && getDefaultFormat(i).is(data, dataLen)) {
219 2325 : return true;
220 : }
221 : }
222 :
223 525 : memory::vector<BitmapFormat::check_fn> fns;
224 :
225 525 : auto lock = lockFormatList();
226 525 : fns.reserve(getCustomFormats().size());
227 :
228 1050 : for (auto &it : getCustomFormats()) {
229 525 : if (it->isRecognizable() && (!readable || it->isReadable())) {
230 525 : fns.emplace_back(it->getCheckFn());
231 : }
232 : }
233 :
234 525 : lock.unlock();
235 :
236 900 : for (auto &it : fns) {
237 525 : if (it(data, dataLen)) {
238 150 : return true;
239 : }
240 : }
241 :
242 375 : return false;
243 525 : }
244 :
245 475 : Pair<FileFormat, StringView> detectFormat(StringView path) {
246 475 : auto file = filesystem::openForReading(path);
247 950 : return detectFormat(file);
248 475 : }
249 :
250 1075 : Pair<FileFormat, StringView> detectFormat(const io::Producer &file) {
251 1075 : StackBuffer<512> data;
252 1075 : if (file.seekAndRead(0, data, 512) < 32) {
253 0 : return pair(FileFormat::Custom, StringView());
254 : }
255 :
256 1075 : return detectFormat(data.data(), data.size());
257 : }
258 :
259 1550 : Pair<FileFormat, StringView> detectFormat(const uint8_t * data, size_t dataLen) {
260 6175 : for (int i = 0; i < toInt(FileFormat::Custom); ++i) {
261 6125 : if (getDefaultFormat(i).isRecognizable() && getDefaultFormat(i).is(data, dataLen)) {
262 1500 : return pair(getDefaultFormat(i).getFormat(), getDefaultFormat(i).getName());
263 : }
264 : }
265 :
266 50 : memory::vector<Pair<StringView, BitmapFormat::check_fn>> fns;
267 :
268 50 : auto lock = lockFormatList();
269 50 : fns.reserve(getCustomFormats().size());
270 :
271 100 : for (auto &it : getCustomFormats()) {
272 50 : if (it->isRecognizable()) {
273 50 : fns.emplace_back(it->getName(), it->getCheckFn());
274 : }
275 : }
276 :
277 50 : lock.unlock();
278 :
279 50 : for (auto &it : fns) {
280 50 : if (it.second(data, dataLen)) {
281 50 : return pair(FileFormat::Custom, it.first);
282 : }
283 : }
284 :
285 0 : return pair(FileFormat::Custom, StringView());
286 50 : }
287 :
288 300 : StringView getMimeType(FileFormat fmt) {
289 300 : switch (fmt) {
290 150 : case FileFormat::Png: return "image/png"; break;
291 25 : case FileFormat::Jpeg: return "image/jpeg"; break;
292 25 : case FileFormat::WebpLossless: return "image/webp"; break;
293 25 : case FileFormat::WebpLossy: return "image/webp"; break;
294 25 : case FileFormat::Svg: return "image/svg+xml"; break;
295 25 : case FileFormat::Gif: return "image/gif"; break;
296 25 : case FileFormat::Tiff: return "image/tiff"; break;
297 0 : case FileFormat::Custom: break;
298 : }
299 0 : return StringView();
300 : }
301 :
302 350 : StringView getMimeType(StringView name) {
303 1350 : for (uint32_t i = 0; i < toInt(FileFormat::Custom); ++i) {
304 1325 : if (getDefaultFormat(i).getName() == name) {
305 325 : return getDefaultFormat(i).getMime();
306 : }
307 : }
308 :
309 25 : auto lock = lockFormatList();
310 25 : for (auto &it : getCustomFormats()) {
311 25 : if (it->getName() == name) {
312 25 : return it->getMime();
313 : }
314 : }
315 0 : return StringView();
316 25 : }
317 :
318 75 : bool check(FileFormat fmt, const uint8_t * data, size_t dataLen) {
319 75 : assert(fmt != FileFormat::Custom);
320 75 : return getDefaultFormat(toInt(fmt)).is(data, dataLen);
321 : }
322 :
323 475 : bool check(StringView name, const uint8_t * data, size_t dataLen) {
324 475 : memory::vector<BitmapFormat::check_fn> fns;
325 :
326 475 : auto lock = lockFormatList();
327 475 : fns.reserve(getCustomFormats().size());
328 :
329 950 : for (auto &it : getCustomFormats()) {
330 475 : if (it->isRecognizable() && it->getName() == name) {
331 475 : fns.emplace_back(it->getCheckFn());
332 : }
333 : }
334 :
335 475 : lock.unlock();
336 :
337 950 : for (auto &it : fns) {
338 475 : if (it(data, dataLen)) {
339 0 : return true;
340 : }
341 : }
342 :
343 475 : return false;
344 475 : }
345 :
346 : template<>
347 18963 : void convertLine<PixelFormat::RGB888, PixelFormat::RGBA8888>(const uint8_t *in, uint8_t *out, uint32_t ins, uint32_t outs) {
348 7604163 : for (size_t i = 0, l = ins - 2; i < l; i += 3) {
349 7585200 : *out++ = in[i]; //R
350 7585200 : *out++ = in[i + 1]; //G
351 7585200 : *out++ = in[i + 2]; //B
352 7585200 : *out++ = 0xFF; //A
353 : }
354 18963 : }
355 :
356 : template<>
357 18963 : void convertLine<PixelFormat::I8, PixelFormat::RGB888>(const uint8_t *in, uint8_t *out, uint32_t ins, uint32_t outs) {
358 7604163 : for (size_t i = 0; i < ins; ++i) {
359 7585200 : *out++ = in[i]; //R
360 7585200 : *out++ = in[i]; //G
361 7585200 : *out++ = in[i]; //B
362 : }
363 18963 : }
364 :
365 : template<>
366 18963 : void convertLine<PixelFormat::IA88, PixelFormat::RGB888>(const uint8_t *in, uint8_t *out, uint32_t ins, uint32_t outs) {
367 7604163 : for (size_t i = 0, l = ins - 1; i < l; i += 2) {
368 7585200 : *out++ = in[i]; //R
369 7585200 : *out++ = in[i]; //G
370 7585200 : *out++ = in[i]; //B
371 : }
372 18963 : }
373 :
374 : template<>
375 18963 : void convertLine<PixelFormat::I8, PixelFormat::RGBA8888>(const uint8_t *in, uint8_t *out, uint32_t ins, uint32_t outs) {
376 7604163 : for (size_t i = 0; i < ins; ++i) {
377 7585200 : *out++ = in[i]; //R
378 7585200 : *out++ = in[i]; //G
379 7585200 : *out++ = in[i]; //B
380 7585200 : *out++ = 0xFF; //A
381 : }
382 18963 : }
383 :
384 : template<>
385 18963 : void convertLine<PixelFormat::IA88, PixelFormat::RGBA8888>(const uint8_t *in, uint8_t *out, uint32_t ins, uint32_t outs) {
386 7604163 : for (size_t i = 0, l = ins - 1; i < l; i += 2) {
387 7585200 : *out++ = in[i]; //R
388 7585200 : *out++ = in[i]; //G
389 7585200 : *out++ = in[i]; //B
390 7585200 : *out++ = in[i + 1]; //A
391 : }
392 18963 : }
393 :
394 : template<>
395 18963 : void convertLine<PixelFormat::I8, PixelFormat::IA88>(const uint8_t *in, uint8_t *out, uint32_t ins, uint32_t outs) {
396 7604163 : for (size_t i = 0; i < ins; ++i) {
397 7585200 : *out++ = in[i];
398 7585200 : *out++ = 0xFF;
399 : }
400 18963 : }
401 :
402 : template<>
403 18963 : void convertLine<PixelFormat::IA88, PixelFormat::A8>(const uint8_t *in, uint8_t *out, uint32_t ins, uint32_t outs) {
404 7604163 : for (size_t i = 1; i < ins; i += 2) {
405 7585200 : *out++ = in[i]; //A
406 : }
407 18963 : }
408 :
409 : template<>
410 7525 : void convertLine<PixelFormat::IA88, PixelFormat::I8>(const uint8_t *in, uint8_t *out, uint32_t ins, uint32_t outs) {
411 3017525 : for (size_t i = 0, l = ins - 1; i < l; i += 2) {
412 3010000 : *out++ = in[i]; //R
413 : }
414 7525 : }
415 :
416 : template<>
417 47257 : void convertLine<PixelFormat::RGBA8888, PixelFormat::RGB888>(const uint8_t *in, uint8_t *out, uint32_t ins, uint32_t outs) {
418 18950057 : for (size_t i = 0, l = ins - 3; i < l; i += 4) {
419 18902800 : *out++ = in[i]; //R
420 18902800 : *out++ = in[i + 1]; //G
421 18902800 : *out++ = in[i + 2]; //B
422 : }
423 47257 : }
424 :
425 : template<>
426 13244 : void convertLine<PixelFormat::RGB888, PixelFormat::I8>(const uint8_t *in, uint8_t *out, uint32_t ins, uint32_t outs) {
427 5310844 : for (size_t i = 0, l = ins - 2; i < l; i += 3) {
428 5297600 : *out++ = (in[i] * 299 + in[i + 1] * 587 + in[i + 2] * 114 + 500) / 1000; //I = (R*299 + G*587 + B*114 + 500) / 1000
429 : }
430 13244 : }
431 :
432 : template<>
433 15050 : void convertLine<PixelFormat::RGBA8888, PixelFormat::I8>(const uint8_t *in, uint8_t *out, uint32_t ins, uint32_t outs) {
434 6035050 : for (size_t i = 0, l = ins - 3; i < l; i += 4) {
435 6020000 : *out++ = (in[i] * 299 + in[i + 1] * 587 + in[i + 2] * 114 + 500) / 1000; //I = (R*299 + G*587 + B*114 + 500) / 1000
436 : }
437 15050 : }
438 :
439 : template<>
440 47257 : void convertLine<PixelFormat::RGBA8888, PixelFormat::A8>(const uint8_t *in, uint8_t *out, uint32_t ins, uint32_t outs) {
441 18950057 : for (size_t i = 0, l = ins -3; i < l; i += 4) {
442 18902800 : *out++ = in[i + 3]; //A
443 : }
444 47257 : }
445 :
446 : template<>
447 18963 : void convertLine<PixelFormat::RGB888, PixelFormat::IA88>(const uint8_t *in, uint8_t *out, uint32_t ins, uint32_t outs) {
448 7604163 : for (size_t i = 0, l = ins - 2; i < l; i += 3) {
449 7585200 : *out++ = (in[i] * 299 + in[i + 1] * 587 + in[i + 2] * 114 + 500) / 1000; //I = (R*299 + G*587 + B*114 + 500) / 1000
450 7585200 : *out++ = 0xFF;
451 : }
452 18963 : }
453 :
454 : template<>
455 47257 : void convertLine<PixelFormat::RGBA8888, PixelFormat::IA88>(const uint8_t *in, uint8_t *out, uint32_t ins, uint32_t outs) {
456 18950057 : for (size_t i = 0, l = ins - 3; i < l; i += 4) {
457 18902800 : *out++ = (in[i] * 299 + in[i + 1] * 587 + in[i + 2] * 114 + 500) / 1000; //I = (R*299 + G*587 + B*114 + 500) / 1000
458 18902800 : *out++ = in[i + 3];
459 : }
460 47257 : }
461 :
462 : template<>
463 7525 : void convertLine<PixelFormat::A8, PixelFormat::IA88>(const uint8_t *in, uint8_t *out, uint32_t ins, uint32_t outs) {
464 3017525 : for (size_t i = 0; i < ins; ++i) {
465 3010000 : *out++ = 0xFF;
466 3010000 : *out++ = in[i];
467 : }
468 7525 : }
469 :
470 : template<>
471 7525 : void convertLine<PixelFormat::A8, PixelFormat::RGB888>(const uint8_t *in, uint8_t *out, uint32_t ins, uint32_t outs) {
472 7525 : memset(out, 0, outs);
473 7525 : }
474 :
475 : template<>
476 7525 : void convertLine<PixelFormat::A8, PixelFormat::RGBA8888>(const uint8_t *in, uint8_t *out, uint32_t ins, uint32_t outs) {
477 3017525 : for (size_t i = 0; i < ins; ++i) {
478 3010000 : *out++ = 0x00;
479 3010000 : *out++ = 0x00;
480 3010000 : *out++ = 0x00;
481 3010000 : *out++ = in[i];
482 : }
483 7525 : }
484 :
485 : template<>
486 13244 : void convertLine<PixelFormat::RGB888, PixelFormat::A8>(const uint8_t *in, uint8_t *out, uint32_t ins, uint32_t outs) {
487 13244 : memset(out, 0, outs);
488 13244 : }
489 :
490 : }
|