Line data Source code
1 : /**
2 : Copyright (c) 2016-2022 Roman Katuntsev <sbkarr@stappler.org>
3 : Copyright (c) 2023-2024 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 : #ifndef STAPPLER_BITMAP_SPBITMAP_H_
25 : #define STAPPLER_BITMAP_SPBITMAP_H_
26 :
27 : #include "SPBitmapFormat.h"
28 : #include "SPFilepath.h"
29 : #include "SPLog.h" // for SPASSERT
30 :
31 : namespace STAPPLER_VERSIONIZED stappler::bitmap {
32 :
33 : enum class ResampleFilter {
34 : Box, // "box"
35 : Tent, // "tent"
36 : Bell, // "bell"
37 : BSpline, // "b-spline"
38 : Mitchell, // "mitchell"
39 : Lanczos3, // "lanczos3"
40 : Blackman, // "blackman"
41 : Lanczos4, // "Lanczos4"
42 : Lanczos6, // "lanczos6"
43 : Lanczos12, // "lanczos12"
44 : Kaiser, // "kaiser"
45 : Gaussian, // "gaussian"
46 : Catmullrom, // "catmullrom"
47 : QuadInterp, // "quadratic_interp"
48 : QuadApprox, // "quadratic_approx"
49 : QuadMix, // "quadratic_mix"
50 :
51 : Default = Lanczos4,
52 : };
53 :
54 : template <typename Interface>
55 : class BitmapTemplate : public Interface::AllocBaseType {
56 : public:
57 : BitmapTemplate();
58 :
59 : // init with jpeg, png or another formatted data
60 : BitmapTemplate(BytesView, const StrideFn &strideFn = nullptr);
61 :
62 : BitmapTemplate(const uint8_t *, size_t, const StrideFn &strideFn = nullptr);
63 :
64 : // init with raw data
65 : BitmapTemplate(const uint8_t *d, uint32_t width, uint32_t height,
66 : PixelFormat c = PixelFormat::RGBA8888, AlphaFormat a = AlphaFormat::Premultiplied, uint32_t stride = 0);
67 :
68 : BitmapTemplate(BytesView d, uint32_t width, uint32_t height,
69 : PixelFormat c = PixelFormat::RGBA8888, AlphaFormat a = AlphaFormat::Premultiplied, uint32_t stride = 0);
70 :
71 : BitmapTemplate(typename Interface::BytesType &&d, uint32_t width, uint32_t height,
72 : PixelFormat c = PixelFormat::RGBA8888, AlphaFormat a = AlphaFormat::Premultiplied, uint32_t stride = 0);
73 :
74 : BitmapTemplate(const BitmapTemplate &) = delete;
75 : BitmapTemplate &operator =(const BitmapTemplate &) = delete;
76 :
77 : BitmapTemplate(BitmapTemplate &&);
78 : BitmapTemplate &operator =(BitmapTemplate &&);
79 :
80 88767247 : uint32_t width() const { return _width; }
81 150622 : uint32_t height() const { return _height; }
82 5303 : uint32_t stride() const { return _stride; }
83 :
84 : bool hasStrideOffset() const { return _width * getBytesPerPixel(_color) < _stride; }
85 :
86 3485 : AlphaFormat alpha() const { return _alpha; }
87 4403 : PixelFormat format() const { return _color; }
88 :
89 354 : BytesView data() const { return _data; }
90 :
91 1271 : uint8_t *dataPtr() { return _data.data(); }
92 975 : const uint8_t *dataPtr() const { return _data.data(); }
93 :
94 : bool updateStride(const StrideFn &strideFn);
95 : bool convert(PixelFormat, const StrideFn &strideFn = nullptr);
96 : bool truncate(PixelFormat, const StrideFn &strideFn = nullptr);
97 :
98 : // target should be large enough
99 : bool convertWithTarget(uint8_t *target, PixelFormat, const StrideFn &strideFn = nullptr) const;
100 :
101 : // init with jpeg or png data
102 : bool loadData(const uint8_t * data, size_t dataLen, const StrideFn &strideFn = nullptr);
103 : bool loadData(BytesView, const StrideFn &strideFn = nullptr);
104 :
105 : // init with raw data
106 : void loadBitmap(const uint8_t *d, uint32_t w, uint32_t h,
107 : PixelFormat = PixelFormat::RGBA8888, AlphaFormat a = AlphaFormat::Unpremultiplied, uint32_t stride = 0);
108 : void loadBitmap(BytesView d, uint32_t w, uint32_t h,
109 : PixelFormat = PixelFormat::RGBA8888, AlphaFormat a = AlphaFormat::Unpremultiplied, uint32_t stride = 0);
110 : void loadBitmap(typename Interface::BytesType &&d, uint32_t w, uint32_t h,
111 : PixelFormat = PixelFormat::RGBA8888, AlphaFormat a = AlphaFormat::Unpremultiplied, uint32_t stride = 0);
112 :
113 : void alloc(uint32_t w, uint32_t h,
114 : PixelFormat = PixelFormat::RGBA8888, AlphaFormat a = AlphaFormat::Unpremultiplied, uint32_t stride = 0);
115 :
116 150 : explicit operator bool () const { return _data.size() != 0; }
117 :
118 : void clear() { _data.clear(); }
119 975 : bool empty() const { return _data.empty(); }
120 : size_t size() const { return _data.size(); }
121 :
122 250 : FileFormat getOriginalFormat() const { return _originalFormat; }
123 25 : StringView getOriginalFormatName() const { return _originalFormatName; }
124 :
125 : bool save(StringView, bool invert = false);
126 : bool save(FileFormat, StringView, bool invert = false);
127 : bool save(StringView name, StringView path, bool invert = false);
128 :
129 : auto write(FileFormat = FileFormat::Png, bool invert = false) -> typename Interface::BytesType;
130 : auto write(StringView name, bool invert = false) -> typename Interface::BytesType;
131 :
132 : // resample with default filter (usually Lanczos4)
133 : BitmapTemplate resample(uint32_t width, uint32_t height, uint32_t stride = 0) const;
134 :
135 : BitmapTemplate resample(ResampleFilter, uint32_t width, uint32_t height, uint32_t stride = 0) const;
136 :
137 : protected:
138 : void setInfo(uint32_t w, uint32_t h, PixelFormat c, AlphaFormat a = AlphaFormat::Unpremultiplied, uint32_t stride = 0);
139 :
140 : PixelFormat _color = PixelFormat::RGBA8888;
141 : AlphaFormat _alpha = AlphaFormat::Opaque;
142 :
143 : uint32_t _width = 0;
144 : uint32_t _height = 0;
145 : uint32_t _stride = 0; // in bytes
146 : typename Interface::BytesType _data;
147 :
148 : FileFormat _originalFormat = FileFormat::Custom;
149 : StringView _originalFormatName;
150 : };
151 :
152 :
153 : template <typename Interface>
154 967 : BitmapTemplate<Interface>::BitmapTemplate() { }
155 :
156 : template <typename Interface>
157 1152 : BitmapTemplate<Interface>::BitmapTemplate(BytesView data, const StrideFn &strideFn)
158 1152 : : BitmapTemplate(data.data(), data.size(), strideFn) { }
159 :
160 :
161 : template <typename Interface>
162 1177 : BitmapTemplate<Interface>::BitmapTemplate(const uint8_t *data, size_t size, const StrideFn &strideFn) {
163 1177 : if (!loadData(data, size, strideFn)) {
164 0 : _data.clear();
165 : }
166 1177 : }
167 :
168 : template <typename Interface>
169 16 : BitmapTemplate<Interface>::BitmapTemplate(const uint8_t *d, uint32_t width, uint32_t height,
170 : PixelFormat c, AlphaFormat a, uint32_t stride)
171 16 : : _color(c), _alpha(a), _width(width), _height(height)
172 16 : , _stride(max(stride, width * getBytesPerPixel(c))), _data(d, d + _stride * height) {
173 16 : SPASSERT(c != PixelFormat::Auto, "Bitmap: Format::Auto should not be used with Bitmap directly");
174 16 : }
175 :
176 : template <typename Interface>
177 : BitmapTemplate<Interface>::BitmapTemplate(BytesView d, uint32_t width, uint32_t height,
178 : PixelFormat c, AlphaFormat a, uint32_t stride)
179 : : _color(c), _alpha(a), _width(width), _height(height)
180 : , _stride(max(stride, width * getBytesPerPixel(c))), _data(d.bytes<Interface>()) {
181 : SPASSERT(c != PixelFormat::Auto, "Bitmap: Format::Auto should not be used with Bitmap directly");
182 : }
183 :
184 : template <typename Interface>
185 : BitmapTemplate<Interface>::BitmapTemplate(typename Interface::BytesType &&d, uint32_t width, uint32_t height,
186 : PixelFormat c, AlphaFormat a, uint32_t stride)
187 : : _color(c), _alpha(a), _width(width), _height(height)
188 : , _stride(max(stride, width * getBytesPerPixel(c))), _data(std::move(d)) {
189 : SPASSERT(c != PixelFormat::Auto, "Bitmap: Format::Auto should not be used with Bitmap directly");
190 : }
191 :
192 : template <typename Interface>
193 25 : BitmapTemplate<Interface>::BitmapTemplate(BitmapTemplate &&other)
194 25 : : _color(other._color), _alpha(other._alpha)
195 25 : , _width(other._width), _height(other._height), _stride(other._stride)
196 25 : , _data(std::move(other._data))
197 25 : , _originalFormat(other._originalFormat)
198 25 : , _originalFormatName(move(other._originalFormatName)) {
199 25 : other._data.clear();
200 25 : }
201 :
202 : template <typename Interface>
203 350 : auto BitmapTemplate<Interface>::operator =(BitmapTemplate &&other) -> BitmapTemplate & {
204 350 : _alpha = other._alpha;
205 350 : _color = other._color;
206 350 : _width = other._width;
207 350 : _height = other._height;
208 350 : _stride = other._stride;
209 350 : _data = std::move(other._data);
210 350 : _originalFormat = other._originalFormat;
211 350 : _originalFormatName = move(other._originalFormatName);
212 350 : other._data.clear();
213 350 : return *this;
214 : }
215 :
216 : template <typename Interface>
217 25 : void BitmapTemplate<Interface>::loadBitmap(const uint8_t *d, uint32_t w, uint32_t h, PixelFormat c, AlphaFormat a, uint32_t stride) {
218 25 : SPASSERT(c != PixelFormat::Auto, "Bitmap: Format::Auto should not be used with Bitmap directly");
219 25 : setInfo(w, h, c, a, stride);
220 25 : _data.clear();
221 25 : _data.resize(_stride * h);
222 25 : memcpy(_data.data(), d, _data.size());
223 25 : _originalFormat = FileFormat::Custom;
224 25 : _originalFormatName.clear();
225 25 : }
226 :
227 : template <typename Interface>
228 25 : void BitmapTemplate<Interface>::loadBitmap(BytesView d, uint32_t w, uint32_t h, PixelFormat c, AlphaFormat a, uint32_t stride) {
229 25 : SPASSERT(c != PixelFormat::Auto, "Bitmap: Format::Auto should not be used with Bitmap directly");
230 25 : setInfo(w, h, c, a, stride);
231 25 : _data = d.bytes<Interface>();
232 25 : _originalFormat = FileFormat::Custom;
233 25 : _originalFormatName.clear();
234 25 : }
235 :
236 : template <typename Interface>
237 25 : void BitmapTemplate<Interface>::loadBitmap(typename Interface::BytesType &&d, uint32_t w, uint32_t h, PixelFormat c, AlphaFormat a, uint32_t stride) {
238 25 : SPASSERT(c != PixelFormat::Auto, "Bitmap: Format::Auto should not be used with Bitmap directly");
239 25 : setInfo(w, h, c, a, stride);
240 25 : _data = std::move(d);
241 25 : _originalFormat = FileFormat::Custom;
242 25 : _originalFormatName.clear();
243 25 : }
244 :
245 : template <typename Interface>
246 967 : void BitmapTemplate<Interface>::alloc(uint32_t w, uint32_t h, PixelFormat c, AlphaFormat a, uint32_t stride) {
247 967 : SPASSERT(c != PixelFormat::Auto, "Bitmap: Format::Auto should not be used with Bitmap directly");
248 967 : setInfo(w, h, c, a, stride);
249 967 : _data.clear();
250 967 : _data.resize(_stride * h);
251 967 : _originalFormat = FileFormat::Custom;
252 967 : _originalFormatName.clear();
253 967 : }
254 :
255 : template <typename Interface>
256 1050 : void BitmapTemplate<Interface>::setInfo(uint32_t w, uint32_t h, PixelFormat c, AlphaFormat a, uint32_t stride) {
257 1050 : SPASSERT(c != PixelFormat::Auto, "Bitmap: Format::Auto should not be used with Bitmap directly");
258 1050 : _width = w;
259 1050 : _height = h;
260 1050 : _stride = max(stride, w * getBytesPerPixel(c));
261 1050 : _color = c;
262 1050 : _alpha = a;
263 1050 : }
264 :
265 : template <typename Interface>
266 274 : bool BitmapTemplate<Interface>::updateStride(const StrideFn &strideFn) {
267 274 : uint32_t outStride = (strideFn != nullptr) ? max(strideFn(_color, _width), _width * getBytesPerPixel(_color)):_width * getBytesPerPixel(_color);
268 274 : if (outStride != _stride) {
269 25 : typename Interface::BytesType out;
270 25 : out.resize(_height * outStride);
271 25 : size_t minStride = _width * getBytesPerPixel(_color);
272 7550 : for (size_t j = 0; j < _height; j ++) {
273 7525 : memcpy(out.data() + j * outStride, _data.data() + j * _stride, minStride);
274 : }
275 25 : _data = std::move(out);
276 25 : _stride = outStride;
277 25 : return true;
278 25 : }
279 249 : return true;
280 : }
281 :
282 : template <typename Interface>
283 1230 : bool BitmapTemplate<Interface>::convert(PixelFormat color, const StrideFn &strideFn) {
284 1230 : if (color == PixelFormat::Auto) {
285 0 : color = _color;
286 : }
287 1230 : if (_color == color) {
288 201 : return updateStride(strideFn);
289 : }
290 :
291 1029 : bool ret = false;
292 1029 : typename Interface::BytesType out;
293 1029 : uint32_t outStride = (strideFn != nullptr) ? max(strideFn(color, _width), _width * getBytesPerPixel(color)) : _width * getBytesPerPixel(color);
294 1029 : out.resize(_height * outStride);
295 :
296 1029 : ret = convertWithTarget(out.data(), color, strideFn);
297 :
298 1029 : if (ret) {
299 1029 : _color = color;
300 1029 : _data = std::move(out);
301 1029 : _stride = outStride;
302 : }
303 :
304 1029 : return ret;
305 1029 : }
306 :
307 : template <typename Interface>
308 96 : bool BitmapTemplate<Interface>::truncate(PixelFormat color, const StrideFn &strideFn) {
309 96 : if (color == PixelFormat::Auto) {
310 0 : color = _color;
311 : }
312 96 : if (_color == color) {
313 48 : return updateStride(strideFn);
314 : }
315 :
316 48 : if (getBytesPerPixel(color) == getBytesPerPixel(_color)) {
317 0 : _color = color;
318 0 : return true;
319 : }
320 :
321 48 : auto height = _height;
322 48 : auto data = _data.data();
323 48 : auto bppIn = getBytesPerPixel(_color);
324 :
325 48 : typename Interface::BytesType out;
326 48 : uint32_t outStride = (strideFn != nullptr) ? max(strideFn(color, _width), _width * getBytesPerPixel(color)) : _width * getBytesPerPixel(color);
327 48 : out.resize(height * outStride);
328 48 : auto bppOut = getBytesPerPixel(color);
329 :
330 48 : auto fillBytes = min(bppIn, bppOut);
331 48 : auto clearBytes = bppOut - fillBytes;
332 :
333 4144 : for (size_t j = 0; j < height; j ++) {
334 4096 : auto inData = data + _stride * j;
335 4096 : auto outData = out.data() + outStride * j;
336 364544 : for (size_t i = 0; i < _stride; i += bppIn) {
337 1441792 : for (uint8_t k = 0; k < fillBytes; ++ k) {
338 1081344 : *outData++ = inData[i * bppIn + k];
339 : }
340 360448 : for (uint8_t k = 0; k < clearBytes; ++ k) {
341 0 : *outData++ = 0;
342 : }
343 : }
344 : }
345 :
346 48 : _color = color;
347 48 : _data = std::move(out);
348 48 : _stride = outStride;
349 :
350 48 : return true;
351 48 : }
352 :
353 : template <typename Interface>
354 1257 : bool BitmapTemplate<Interface>::convertWithTarget(uint8_t *target, PixelFormat color, const StrideFn &strideFn) const {
355 1257 : bool ret = false;
356 1257 : uint32_t outStride = (strideFn != nullptr) ? max(strideFn(color, _width), _width * getBytesPerPixel(color)) : _width * getBytesPerPixel(color);
357 1257 : BytesView out(target, _height * outStride);
358 :
359 1257 : switch (_color) {
360 100 : case PixelFormat::A8:
361 : switch (color) {
362 0 : case PixelFormat::A8: return true; break;
363 25 : case PixelFormat::I8: return true; break;
364 25 : case PixelFormat::IA88: ret = convertData<PixelFormat::A8, PixelFormat::IA88>(_data, out, _stride, outStride); break;
365 25 : case PixelFormat::RGB888: ret = convertData<PixelFormat::A8, PixelFormat::RGB888>(_data, out, _stride, outStride); break;
366 25 : case PixelFormat::RGBA8888: ret = convertData<PixelFormat::A8, PixelFormat::RGBA8888>(_data, out, _stride, outStride); break;
367 0 : case PixelFormat::Auto: return false; break;
368 : }
369 75 : break;
370 208 : case PixelFormat::I8:
371 : switch (color) {
372 19 : case PixelFormat::A8: return true; break;
373 0 : case PixelFormat::I8: return true; break;
374 63 : case PixelFormat::IA88: ret = convertData<PixelFormat::I8, PixelFormat::IA88>(_data, out, _stride, outStride); break;
375 63 : case PixelFormat::RGB888: ret = convertData<PixelFormat::I8, PixelFormat::RGB888>(_data, out, _stride, outStride); break;
376 63 : case PixelFormat::RGBA8888: ret = convertData<PixelFormat::I8, PixelFormat::RGBA8888>(_data, out, _stride, outStride); break;
377 0 : case PixelFormat::Auto: return false; break;
378 : }
379 189 : break;
380 214 : case PixelFormat::IA88:
381 : switch (color) {
382 63 : case PixelFormat::A8: ret = convertData<PixelFormat::IA88, PixelFormat::A8>(_data, out, _stride, outStride); break;
383 25 : case PixelFormat::I8: ret = convertData<PixelFormat::IA88, PixelFormat::I8>(_data, out, _stride, outStride); break;
384 0 : case PixelFormat::IA88: return true; break;
385 63 : case PixelFormat::RGB888: ret = convertData<PixelFormat::IA88, PixelFormat::RGB888>(_data, out, _stride, outStride); break;
386 63 : case PixelFormat::RGBA8888: ret = convertData<PixelFormat::IA88, PixelFormat::RGBA8888>(_data, out, _stride, outStride); break;
387 0 : case PixelFormat::Auto: return false; break;
388 : }
389 214 : break;
390 214 : case PixelFormat::RGB888:
391 : switch (color) {
392 44 : case PixelFormat::A8: ret = convertData<PixelFormat::RGB888, PixelFormat::A8>(_data, out, _stride, outStride); break;
393 44 : case PixelFormat::I8: ret = convertData<PixelFormat::RGB888, PixelFormat::I8>(_data, out, _stride, outStride); break;
394 63 : case PixelFormat::IA88: ret = convertData<PixelFormat::RGB888, PixelFormat::IA88>(_data, out, _stride, outStride); break;
395 0 : case PixelFormat::RGB888: return true; break;
396 63 : case PixelFormat::RGBA8888: ret = convertData<PixelFormat::RGB888, PixelFormat::RGBA8888>(_data, out, _stride, outStride); break;
397 0 : case PixelFormat::Auto: return false; break;
398 : }
399 214 : break;
400 521 : case PixelFormat::RGBA8888:
401 : switch (color) {
402 157 : case PixelFormat::A8: ret = convertData<PixelFormat::RGBA8888, PixelFormat::A8>(_data, out, _stride, outStride); break;
403 50 : case PixelFormat::I8: ret = convertData<PixelFormat::RGBA8888, PixelFormat::I8>(_data, out, _stride, outStride); break;
404 157 : case PixelFormat::IA88: ret = convertData<PixelFormat::RGBA8888, PixelFormat::IA88>(_data, out, _stride, outStride); break;
405 157 : case PixelFormat::RGB888: ret = convertData<PixelFormat::RGBA8888, PixelFormat::RGB888>(_data, out, _stride, outStride); break;
406 0 : case PixelFormat::RGBA8888: return true; break;
407 0 : case PixelFormat::Auto: return false; break;
408 : }
409 521 : break;
410 0 : case PixelFormat::Auto: return false; break;
411 : }
412 :
413 1213 : return ret;
414 : }
415 :
416 : template <typename Interface>
417 75 : bool BitmapTemplate<Interface>::save(StringView path, bool invert) {
418 75 : FileFormat fmt = FileFormat::Png;
419 75 : auto ext = filepath::lastExtension(path);
420 75 : if (ext == "png") {
421 25 : fmt = FileFormat::Png;
422 50 : } else if (ext == "jpeg" || ext == "jpg") {
423 25 : fmt = FileFormat::Jpeg;
424 25 : } else if (ext == "webp") {
425 25 : fmt = FileFormat::WebpLossless;
426 : }
427 150 : return save(fmt, path, invert);
428 : }
429 :
430 : template <typename Interface>
431 : bool BitmapTemplate<Interface>::loadData(BytesView d, const StrideFn &strideFn) {
432 : return loadData(d.data(), d.size(), strideFn);
433 : }
434 :
435 : }
436 :
437 : namespace STAPPLER_VERSIONIZED stappler::mem_std {
438 :
439 : using Bitmap = bitmap::BitmapTemplate<memory::StandartInterface>;
440 :
441 : }
442 :
443 : namespace STAPPLER_VERSIONIZED stappler::mem_pool {
444 :
445 : using Bitmap = bitmap::BitmapTemplate<memory::PoolInterface>;
446 :
447 : }
448 :
449 : #endif /* STAPPLER_BITMAP_SPBITMAP_H_ */
|