1 | |
2 | // |
3 | // This source file is part of appleseed. |
4 | // Visit http://appleseedhq.net/ for additional information and resources. |
5 | // |
6 | // This software is released under the MIT license. |
7 | // |
8 | // Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited |
9 | // Copyright (c) 2014-2017 Francois Beaune, The appleseedhq Organization |
10 | // |
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy |
12 | // of this software and associated documentation files (the "Software"), to deal |
13 | // in the Software without restriction, including without limitation the rights |
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
15 | // copies of the Software, and to permit persons to whom the Software is |
16 | // furnished to do so, subject to the following conditions: |
17 | // |
18 | // The above copyright notice and this permission notice shall be included in |
19 | // all copies or substantial portions of the Software. |
20 | // |
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
27 | // THE SOFTWARE. |
28 | // |
29 | |
30 | #ifndef APPLESEED_FOUNDATION_IMAGE_PIXEL_H |
31 | #define APPLESEED_FOUNDATION_IMAGE_PIXEL_H |
32 | |
33 | // appleseed.foundation headers. |
34 | #include "foundation/math/scalar.h" |
35 | #include "foundation/platform/types.h" |
36 | #include "foundation/utility/otherwise.h" |
37 | |
38 | // appleseed.main headers. |
39 | #include "main/dllsymbol.h" |
40 | |
41 | // OpenEXR headers. |
42 | #include "foundation/platform/exrheaderguards.h" |
43 | BEGIN_EXR_INCLUDES |
44 | #include "OpenEXR/half.h" |
45 | END_EXR_INCLUDES |
46 | |
47 | // Standard headers. |
48 | #include <cassert> |
49 | #include <cstddef> |
50 | |
51 | namespace foundation |
52 | { |
53 | |
54 | // todo: apply jittering/dithering when converting to lower precision formats. |
55 | // todo: throw exceptions for unsupported / invalid conversions. |
56 | |
57 | |
58 | // |
59 | // Supported pixel formats. |
60 | // |
61 | |
62 | enum PixelFormat |
63 | { |
64 | PixelFormatUInt8, |
65 | PixelFormatUInt16, |
66 | PixelFormatUInt32, |
67 | PixelFormatHalf, |
68 | PixelFormatFloat, |
69 | PixelFormatDouble |
70 | }; |
71 | |
72 | // Return a string identifying a pixel format. |
73 | APPLESEED_DLLSYMBOL const char* pixel_format_name(const PixelFormat pixel_format); |
74 | |
75 | |
76 | // |
77 | // Pixel class, providing types and functions related to pixels. |
78 | // |
79 | |
80 | class Pixel |
81 | { |
82 | public: |
83 | // Return the size in bytes of a given pixel format. |
84 | static size_t size(PixelFormat format); |
85 | |
86 | // |
87 | // The convert_*() methods below allow conversion of a given range of value |
88 | // in a given pixel format to another pixel format, with arbitrary striding |
89 | // in both source and destination. They take advantage of the fact that in |
90 | // most cases, either the source format or the destination format is known |
91 | // at compile time. |
92 | |
93 | // |
94 | // The non-specialized versions of these methods are intentionally left |
95 | // unimplemented so that attempting to convert to or from a non-supported |
96 | // pixel format will result in a compilation error. |
97 | // |
98 | |
99 | // Convert from templatized format to variable format. |
100 | template <typename T> |
101 | static void convert_to_format( |
102 | const T* src_begin, // points to the first value to convert |
103 | const T* src_end, // one beyond the last value to convert |
104 | const size_t src_stride, // source stride (in words) |
105 | const PixelFormat dest_format, // destination format |
106 | void* dest, // destination |
107 | const size_t dest_stride); // destination stride (in words) |
108 | |
109 | // Convert from variable format to templatized format. |
110 | template <typename T> |
111 | static void convert_from_format( |
112 | const PixelFormat src_format, // source format |
113 | const void* src_begin, // points to the first value to convert |
114 | const void* src_end, // one beyond the last value to convert |
115 | const size_t src_stride, // source stride (in words) |
116 | T* dest, // destination |
117 | const size_t dest_stride); // destination stride (in words) |
118 | |
119 | // Convert from variable format to variable format. |
120 | static void convert( |
121 | const PixelFormat src_format, // source format |
122 | const void* src_begin, // points to the first value to convert |
123 | const void* src_end, // one beyond the last value to convert |
124 | const size_t src_stride, // source stride (in words) |
125 | const PixelFormat dest_format, // destination format |
126 | void* dest, // destination |
127 | const size_t dest_stride); // destination stride (in words) |
128 | |
129 | // |
130 | // The convert_and_shuffle() method allow conversion of a given range of pixels, |
131 | // with a given number of channels in a given pixel format, to a new set of |
132 | // pixels with potentially a different number of channels, in different order, |
133 | // in a different pixel format. |
134 | // |
135 | // Example: the following converts a set of pixels in RGBA order with one 32-bit |
136 | // float per channel to a new set of pixels in BGR order (omitting the alpha |
137 | // channel) with one 8-bit integer per channel: |
138 | // |
139 | // const size_t shuffle_table[4] = { 2, 1, 0, Pixel::SkipChannel }; |
140 | // |
141 | // Pixel::convert_and_shuffle( |
142 | // PixelFormatFloat, // source format |
143 | // 4, // source channels |
144 | // src_begin, // source begin |
145 | // src_end, // source end |
146 | // PixelFormatUInt8, // destination format |
147 | // 3, // destination channels |
148 | // dest, // destination |
149 | // shuffle_table); // channel shuffling table |
150 | // |
151 | |
152 | static void convert_and_shuffle( |
153 | const PixelFormat src_format, // source format |
154 | const size_t src_channels, // number of source channels |
155 | const void* src_begin, // points to the first value to convert |
156 | const void* src_end, // one beyond the last value to convert |
157 | const PixelFormat dest_format, // destination format |
158 | const size_t dest_channels, // number of destination channels |
159 | void* dest, // destination |
160 | const size_t* shuffle_table); // channel shuffling table |
161 | |
162 | // Use this value in a channel shuffling table to indicate that a channel |
163 | // must be skipped. |
164 | static const size_t SkipChannel = ~0; |
165 | |
166 | // Return the number of destination channels specified by a channel shuffling table. |
167 | static size_t get_dest_channel_count( |
168 | const size_t src_channels, // number of source channels |
169 | const size_t* shuffle_table); // channel shuffling table |
170 | |
171 | }; |
172 | |
173 | |
174 | // |
175 | // Pixel class implementation. |
176 | // |
177 | |
178 | inline size_t Pixel::size(PixelFormat format) |
179 | { |
180 | switch (format) |
181 | { |
182 | case PixelFormatUInt8: return 1; |
183 | case PixelFormatUInt16: return 2; |
184 | case PixelFormatUInt32: return 4; |
185 | case PixelFormatHalf: return 2; |
186 | case PixelFormatFloat: return 4; |
187 | case PixelFormatDouble: return 8; |
188 | default: |
189 | assert(false); |
190 | return 0; |
191 | } |
192 | } |
193 | |
194 | template <> |
195 | inline void Pixel::convert_to_format<uint8>( |
196 | const uint8* src_begin, |
197 | const uint8* src_end, |
198 | const size_t src_stride, |
199 | const PixelFormat dest_format, |
200 | void* dest, |
201 | const size_t dest_stride) |
202 | { |
203 | assert(src_begin); |
204 | assert(src_end); |
205 | assert(dest); |
206 | |
207 | switch (dest_format) |
208 | { |
209 | case PixelFormatUInt8: // lossless uint8 -> uint8 |
210 | { |
211 | uint8* typed_dest = reinterpret_cast<uint8*>(dest); |
212 | for (const uint8* it = src_begin; it < src_end; it += src_stride) |
213 | { |
214 | *typed_dest = *it; |
215 | typed_dest += dest_stride; |
216 | } |
217 | } |
218 | break; |
219 | |
220 | case PixelFormatUInt16: // lossless uint8 -> uint16 |
221 | { |
222 | uint16* typed_dest = reinterpret_cast<uint16*>(dest); |
223 | for (const uint8* it = src_begin; it < src_end; it += src_stride) |
224 | { |
225 | *typed_dest = static_cast<uint16>(*it) * (65535 / 255); |
226 | typed_dest += dest_stride; |
227 | } |
228 | } |
229 | break; |
230 | |
231 | case PixelFormatUInt32: // lossless uint8 -> uint32 |
232 | { |
233 | uint32* typed_dest = reinterpret_cast<uint32*>(dest); |
234 | for (const uint8* it = src_begin; it < src_end; it += src_stride) |
235 | { |
236 | *typed_dest = static_cast<uint32>(*it) * (4294967295UL / 255); |
237 | typed_dest += dest_stride; |
238 | } |
239 | } |
240 | break; |
241 | |
242 | case PixelFormatHalf: // lossless uint8 -> half |
243 | { |
244 | half* typed_dest = reinterpret_cast<half*>(dest); |
245 | for (const uint8* it = src_begin; it < src_end; it += src_stride) |
246 | { |
247 | *typed_dest = static_cast<half>(static_cast<float>(*it) * (1.0f / 255)); |
248 | typed_dest += dest_stride; |
249 | } |
250 | } |
251 | break; |
252 | |
253 | case PixelFormatFloat: // lossless uint8 -> float |
254 | { |
255 | float* typed_dest = reinterpret_cast<float*>(dest); |
256 | for (const uint8* it = src_begin; it < src_end; it += src_stride) |
257 | { |
258 | *typed_dest = static_cast<float>(*it) * (1.0f / 255); |
259 | typed_dest += dest_stride; |
260 | } |
261 | } |
262 | break; |
263 | |
264 | case PixelFormatDouble: // lossless uint8 -> double |
265 | { |
266 | double* typed_dest = reinterpret_cast<double*>(dest); |
267 | for (const uint8* it = src_begin; it < src_end; it += src_stride) |
268 | { |
269 | *typed_dest = static_cast<double>(*it) * (1.0 / 255); |
270 | typed_dest += dest_stride; |
271 | } |
272 | } |
273 | break; |
274 | |
275 | assert_otherwise; |
276 | } |
277 | } |
278 | |
279 | template <> |
280 | inline void Pixel::convert_to_format<uint16>( |
281 | const uint16* src_begin, |
282 | const uint16* src_end, |
283 | const size_t src_stride, |
284 | const PixelFormat dest_format, |
285 | void* dest, |
286 | const size_t dest_stride) |
287 | { |
288 | assert(src_begin); |
289 | assert(src_end); |
290 | assert(dest); |
291 | |
292 | switch (dest_format) |
293 | { |
294 | case PixelFormatUInt8: // lossy uint16 -> uint8 |
295 | { |
296 | uint8* typed_dest = reinterpret_cast<uint8*>(dest); |
297 | for (const uint16* it = src_begin; it < src_end; it += src_stride) |
298 | { |
299 | *typed_dest = static_cast<uint8>(*it >> 8); |
300 | typed_dest += dest_stride; |
301 | } |
302 | } |
303 | break; |
304 | |
305 | case PixelFormatUInt16: // lossless uint16 -> uint16 |
306 | { |
307 | uint16* typed_dest = reinterpret_cast<uint16*>(dest); |
308 | for (const uint16* it = src_begin; it < src_end; it += src_stride) |
309 | { |
310 | *typed_dest = *it; |
311 | typed_dest += dest_stride; |
312 | } |
313 | } |
314 | break; |
315 | |
316 | case PixelFormatUInt32: // lossless uint16 -> uint32 |
317 | { |
318 | uint32* typed_dest = reinterpret_cast<uint32*>(dest); |
319 | for (const uint16* it = src_begin; it < src_end; it += src_stride) |
320 | { |
321 | *typed_dest = static_cast<uint32>(*it) * (4294967295UL / 65535); |
322 | typed_dest += dest_stride; |
323 | } |
324 | } |
325 | break; |
326 | |
327 | case PixelFormatHalf: // lossy uint16 -> half |
328 | { |
329 | half* typed_dest = reinterpret_cast<half*>(dest); |
330 | for (const uint16* it = src_begin; it < src_end; it += src_stride) |
331 | { |
332 | *typed_dest = static_cast<half>(static_cast<float>(*it) * (1.0f / 65535)); |
333 | typed_dest += dest_stride; |
334 | } |
335 | } |
336 | break; |
337 | |
338 | case PixelFormatFloat: // lossless uint16 -> float |
339 | { |
340 | float* typed_dest = reinterpret_cast<float*>(dest); |
341 | for (const uint16* it = src_begin; it < src_end; it += src_stride) |
342 | { |
343 | *typed_dest = static_cast<float>(*it) * (1.0f / 65535); |
344 | typed_dest += dest_stride; |
345 | } |
346 | } |
347 | break; |
348 | |
349 | case PixelFormatDouble: // lossless uint16 -> double |
350 | { |
351 | double* typed_dest = reinterpret_cast<double*>(dest); |
352 | for (const uint16* it = src_begin; it < src_end; it += src_stride) |
353 | { |
354 | *typed_dest = static_cast<double>(*it) * (1.0 / 65535); |
355 | typed_dest += dest_stride; |
356 | } |
357 | } |
358 | break; |
359 | |
360 | assert_otherwise; |
361 | } |
362 | } |
363 | |
364 | template <> |
365 | inline void Pixel::convert_to_format<uint32>( |
366 | const uint32* src_begin, |
367 | const uint32* src_end, |
368 | const size_t src_stride, |
369 | const PixelFormat dest_format, |
370 | void* dest, |
371 | const size_t dest_stride) |
372 | { |
373 | assert(src_begin); |
374 | assert(src_end); |
375 | assert(dest); |
376 | |
377 | switch (dest_format) |
378 | { |
379 | case PixelFormatUInt8: // lossy uint32 -> uint8 |
380 | { |
381 | uint8* typed_dest = reinterpret_cast<uint8*>(dest); |
382 | for (const uint32* it = src_begin; it < src_end; it += src_stride) |
383 | { |
384 | *typed_dest = static_cast<uint8>(*it >> 24); |
385 | typed_dest += dest_stride; |
386 | } |
387 | } |
388 | break; |
389 | |
390 | case PixelFormatUInt16: // lossy uint32 -> uint16 |
391 | { |
392 | uint16* typed_dest = reinterpret_cast<uint16*>(dest); |
393 | for (const uint32* it = src_begin; it < src_end; it += src_stride) |
394 | { |
395 | *typed_dest = static_cast<uint16>(*it >> 16); |
396 | typed_dest += dest_stride; |
397 | } |
398 | } |
399 | break; |
400 | |
401 | case PixelFormatUInt32: // lossless uint32 -> uint32 |
402 | { |
403 | uint32* typed_dest = reinterpret_cast<uint32*>(dest); |
404 | for (const uint32* it = src_begin; it < src_end; it += src_stride) |
405 | { |
406 | *typed_dest = *it; |
407 | typed_dest += dest_stride; |
408 | } |
409 | } |
410 | break; |
411 | |
412 | case PixelFormatHalf: // lossy uint32 -> half |
413 | { |
414 | half* typed_dest = reinterpret_cast<half*>(dest); |
415 | for (const uint32* it = src_begin; it < src_end; it += src_stride) |
416 | { |
417 | *typed_dest = static_cast<half>(static_cast<float>(*it) * (1.0f / 4294967295UL)); |
418 | typed_dest += dest_stride; |
419 | } |
420 | } |
421 | break; |
422 | |
423 | case PixelFormatFloat: // lossy uint32 -> float |
424 | { |
425 | float* typed_dest = reinterpret_cast<float*>(dest); |
426 | for (const uint32* it = src_begin; it < src_end; it += src_stride) |
427 | { |
428 | *typed_dest = static_cast<float>(*it) * (1.0f / 4294967295UL); |
429 | typed_dest += dest_stride; |
430 | } |
431 | } |
432 | break; |
433 | |
434 | case PixelFormatDouble: // lossless uint32 -> double |
435 | { |
436 | double* typed_dest = reinterpret_cast<double*>(dest); |
437 | for (const uint32* it = src_begin; it < src_end; it += src_stride) |
438 | { |
439 | *typed_dest = static_cast<double>(*it) * (1.0 / 4294967295UL); |
440 | typed_dest += dest_stride; |
441 | } |
442 | } |
443 | break; |
444 | |
445 | assert_otherwise; |
446 | } |
447 | } |
448 | |
449 | template <> |
450 | inline void Pixel::convert_to_format<half>( |
451 | const half* src_begin, |
452 | const half* src_end, |
453 | const size_t src_stride, |
454 | const PixelFormat dest_format, |
455 | void* dest, |
456 | const size_t dest_stride) |
457 | { |
458 | assert(src_begin); |
459 | assert(src_end); |
460 | assert(dest); |
461 | |
462 | switch (dest_format) |
463 | { |
464 | case PixelFormatUInt8: // lossy half -> uint8 |
465 | { |
466 | uint8* typed_dest = reinterpret_cast<uint8*>(dest); |
467 | for (const half* it = src_begin; it < src_end; it += src_stride) |
468 | { |
469 | const float val = clamp(*it * 256.0f, 0.0f, 255.0f); |
470 | *typed_dest = truncate<uint8>(val); |
471 | typed_dest += dest_stride; |
472 | } |
473 | } |
474 | break; |
475 | |
476 | case PixelFormatUInt16: // lossy half -> uint16 |
477 | { |
478 | uint16* typed_dest = reinterpret_cast<uint16*>(dest); |
479 | for (const half* it = src_begin; it < src_end; it += src_stride) |
480 | { |
481 | const float val = clamp(*it * 65536.0f, 0.0f, 65535.0f); |
482 | *typed_dest = truncate<uint16>(val); |
483 | typed_dest += dest_stride; |
484 | } |
485 | } |
486 | break; |
487 | |
488 | case PixelFormatUInt32: // lossy half -> uint32 |
489 | { |
490 | uint32* typed_dest = reinterpret_cast<uint32*>(dest); |
491 | for (const half* it = src_begin; it < src_end; it += src_stride) |
492 | { |
493 | const double val = clamp(static_cast<double>(*it) * 4294967296.0, 0.0, 4294967295.0); |
494 | *typed_dest = truncate<uint32>(val); |
495 | typed_dest += dest_stride; |
496 | } |
497 | } |
498 | break; |
499 | |
500 | case PixelFormatHalf: // lossless half -> half |
501 | { |
502 | half* typed_dest = reinterpret_cast<half*>(dest); |
503 | for (const half* it = src_begin; it < src_end; it += src_stride) |
504 | { |
505 | *typed_dest = *it; |
506 | typed_dest += dest_stride; |
507 | } |
508 | } |
509 | break; |
510 | |
511 | case PixelFormatFloat: // lossless half -> float |
512 | { |
513 | float* typed_dest = reinterpret_cast<float*>(dest); |
514 | for (const half* it = src_begin; it < src_end; it += src_stride) |
515 | { |
516 | *typed_dest = static_cast<float>(*it); |
517 | typed_dest += dest_stride; |
518 | } |
519 | } |
520 | break; |
521 | |
522 | case PixelFormatDouble: // lossless half -> double |
523 | { |
524 | double* typed_dest = reinterpret_cast<double*>(dest); |
525 | for (const half* it = src_begin; it < src_end; it += src_stride) |
526 | { |
527 | *typed_dest = static_cast<double>(*it); |
528 | typed_dest += dest_stride; |
529 | } |
530 | } |
531 | break; |
532 | |
533 | assert_otherwise; |
534 | } |
535 | } |
536 | |
537 | template <> |
538 | inline void Pixel::convert_to_format<float>( |
539 | const float* src_begin, |
540 | const float* src_end, |
541 | const size_t src_stride, |
542 | const PixelFormat dest_format, |
543 | void* dest, |
544 | const size_t dest_stride) |
545 | { |
546 | assert(src_begin); |
547 | assert(src_end); |
548 | assert(dest); |
549 | |
550 | switch (dest_format) |
551 | { |
552 | case PixelFormatUInt8: // lossy float -> uint8 |
553 | { |
554 | // todo: optimize this case using SSE? |
555 | uint8* typed_dest = reinterpret_cast<uint8*>(dest); |
556 | for (const float* it = src_begin; it < src_end; it += src_stride) |
557 | { |
558 | const float val = clamp(*it * 256.0f, 0.0f, 255.0f); |
559 | *typed_dest = truncate<uint8>(val); |
560 | typed_dest += dest_stride; |
561 | } |
562 | } |
563 | break; |
564 | |
565 | case PixelFormatUInt16: // lossy float -> uint16 |
566 | { |
567 | uint16* typed_dest = reinterpret_cast<uint16*>(dest); |
568 | for (const float* it = src_begin; it < src_end; it += src_stride) |
569 | { |
570 | const float val = clamp(*it * 65536.0f, 0.0f, 65535.0f); |
571 | *typed_dest = truncate<uint16>(val); |
572 | typed_dest += dest_stride; |
573 | } |
574 | } |
575 | break; |
576 | |
577 | case PixelFormatUInt32: // lossy float -> uint32 |
578 | { |
579 | uint32* typed_dest = reinterpret_cast<uint32*>(dest); |
580 | for (const float* it = src_begin; it < src_end; it += src_stride) |
581 | { |
582 | const double val = clamp(static_cast<double>(*it) * 4294967296.0, 0.0, 4294967295.0); |
583 | *typed_dest = truncate<uint32>(val); |
584 | typed_dest += dest_stride; |
585 | } |
586 | } |
587 | break; |
588 | |
589 | case PixelFormatHalf: // lossy float -> half |
590 | { |
591 | half* typed_dest = reinterpret_cast<half*>(dest); |
592 | for (const float* it = src_begin; it < src_end; it += src_stride) |
593 | { |
594 | *typed_dest = static_cast<half>(*it); |
595 | typed_dest += dest_stride; |
596 | } |
597 | } |
598 | break; |
599 | |
600 | case PixelFormatFloat: // lossless float -> float |
601 | { |
602 | float* typed_dest = reinterpret_cast<float*>(dest); |
603 | for (const float* it = src_begin; it < src_end; it += src_stride) |
604 | { |
605 | *typed_dest = *it; |
606 | typed_dest += dest_stride; |
607 | } |
608 | } |
609 | break; |
610 | |
611 | case PixelFormatDouble: // lossless float -> double |
612 | { |
613 | double* typed_dest = reinterpret_cast<double*>(dest); |
614 | for (const float* it = src_begin; it < src_end; it += src_stride) |
615 | { |
616 | *typed_dest = static_cast<double>(*it); |
617 | typed_dest += dest_stride; |
618 | } |
619 | } |
620 | break; |
621 | |
622 | assert_otherwise; |
623 | } |
624 | } |
625 | |
626 | template <> |
627 | inline void Pixel::convert_to_format<double>( |
628 | const double* src_begin, |
629 | const double* src_end, |
630 | const size_t src_stride, |
631 | const PixelFormat dest_format, |
632 | void* dest, |
633 | const size_t dest_stride) |
634 | { |
635 | assert(src_begin); |
636 | assert(src_end); |
637 | assert(dest); |
638 | |
639 | switch (dest_format) |
640 | { |
641 | case PixelFormatUInt8: // lossy double -> uint8 |
642 | { |
643 | uint8* typed_dest = reinterpret_cast<uint8*>(dest); |
644 | for (const double* it = src_begin; it < src_end; it += src_stride) |
645 | { |
646 | const double val = clamp(*it * 256.0, 0.0, 255.0); |
647 | *typed_dest = truncate<uint8>(val); |
648 | typed_dest += dest_stride; |
649 | } |
650 | } |
651 | break; |
652 | |
653 | case PixelFormatUInt16: // lossy double -> uint16 |
654 | { |
655 | uint16* typed_dest = reinterpret_cast<uint16*>(dest); |
656 | for (const double* it = src_begin; it < src_end; it += src_stride) |
657 | { |
658 | const double val = clamp(*it * 65536.0, 0.0, 65535.0); |
659 | *typed_dest = truncate<uint16>(val); |
660 | typed_dest += dest_stride; |
661 | } |
662 | } |
663 | break; |
664 | |
665 | case PixelFormatUInt32: // lossy double -> uint32 |
666 | { |
667 | uint32* typed_dest = reinterpret_cast<uint32*>(dest); |
668 | for (const double* it = src_begin; it < src_end; it += src_stride) |
669 | { |
670 | const double val = clamp(*it * 4294967296.0, 0.0, 4294967295.0); |
671 | *typed_dest = truncate<uint32>(val); |
672 | typed_dest += dest_stride; |
673 | } |
674 | } |
675 | break; |
676 | |
677 | case PixelFormatHalf: // lossy double -> half |
678 | { |
679 | half* typed_dest = reinterpret_cast<half*>(dest); |
680 | for (const double* it = src_begin; it < src_end; it += src_stride) |
681 | { |
682 | *typed_dest = static_cast<half>(static_cast<float>(*it)); |
683 | typed_dest += dest_stride; |
684 | } |
685 | } |
686 | break; |
687 | |
688 | case PixelFormatFloat: // lossy double -> float |
689 | { |
690 | float* typed_dest = reinterpret_cast<float*>(dest); |
691 | for (const double* it = src_begin; it < src_end; it += src_stride) |
692 | { |
693 | *typed_dest = static_cast<float>(*it); |
694 | typed_dest += dest_stride; |
695 | } |
696 | } |
697 | break; |
698 | |
699 | case PixelFormatDouble: // lossless double -> double |
700 | { |
701 | double* typed_dest = reinterpret_cast<double*>(dest); |
702 | for (const double* it = src_begin; it < src_end; it += src_stride) |
703 | { |
704 | *typed_dest = *it; |
705 | typed_dest += dest_stride; |
706 | } |
707 | } |
708 | break; |
709 | |
710 | assert_otherwise; |
711 | } |
712 | } |
713 | |
714 | template <> |
715 | inline void Pixel::convert_from_format<uint8>( |
716 | const PixelFormat src_format, |
717 | const void* src_begin, |
718 | const void* src_end, |
719 | const size_t src_stride, |
720 | uint8* dest, |
721 | const size_t dest_stride) |
722 | { |
723 | assert(src_begin); |
724 | assert(src_end); |
725 | assert(dest); |
726 | |
727 | switch (src_format) |
728 | { |
729 | case PixelFormatUInt8: // lossless uint8 -> uint8 |
730 | { |
731 | const uint8* it = reinterpret_cast<const uint8*>(src_begin); |
732 | for (; it < src_end; it += src_stride) |
733 | { |
734 | *dest = *it; |
735 | dest += dest_stride; |
736 | } |
737 | } |
738 | break; |
739 | |
740 | case PixelFormatUInt16: // lossy uint16 -> uint8 |
741 | { |
742 | const uint16* it = reinterpret_cast<const uint16*>(src_begin); |
743 | for (; it < reinterpret_cast<const uint16*>(src_end); it += src_stride) |
744 | { |
745 | *dest = static_cast<uint8>(*it >> 8); |
746 | dest += dest_stride; |
747 | } |
748 | } |
749 | break; |
750 | |
751 | case PixelFormatUInt32: // lossy uint32 -> uint8 |
752 | { |
753 | const uint32* it = reinterpret_cast<const uint32*>(src_begin); |
754 | for (; it < reinterpret_cast<const uint32*>(src_end); it += src_stride) |
755 | { |
756 | *dest = static_cast<uint8>(*it >> 24); |
757 | dest += dest_stride; |
758 | } |
759 | } |
760 | break; |
761 | |
762 | case PixelFormatHalf: // lossy half -> uint8 |
763 | { |
764 | const half* it = reinterpret_cast<const half*>(src_begin); |
765 | for (; it < reinterpret_cast<const half*>(src_end); it += src_stride) |
766 | { |
767 | const half val = static_cast<half>(clamp(*it * 256.0f, 0.0f, 255.0f)); |
768 | *dest = truncate<uint8>(val); |
769 | dest += dest_stride; |
770 | } |
771 | } |
772 | break; |
773 | |
774 | case PixelFormatFloat: // lossy float -> uint8 |
775 | { |
776 | const float* it = reinterpret_cast<const float*>(src_begin); |
777 | for (; it < reinterpret_cast<const float*>(src_end); it += src_stride) |
778 | { |
779 | const float val = clamp(*it * 256.0f, 0.0f, 255.0f); |
780 | *dest = truncate<uint8>(val); |
781 | dest += dest_stride; |
782 | } |
783 | } |
784 | break; |
785 | |
786 | case PixelFormatDouble: // lossy double -> uint8 |
787 | { |
788 | const double* it = reinterpret_cast<const double*>(src_begin); |
789 | for (; it < reinterpret_cast<const double*>(src_end); it += src_stride) |
790 | { |
791 | const double val = clamp(*it * 256.0, 0.0, 255.0); |
792 | *dest = truncate<uint8>(val); |
793 | dest += dest_stride; |
794 | } |
795 | } |
796 | break; |
797 | |
798 | assert_otherwise; |
799 | } |
800 | } |
801 | |
802 | template <> |
803 | inline void Pixel::convert_from_format<uint16>( |
804 | const PixelFormat src_format, |
805 | const void* src_begin, |
806 | const void* src_end, |
807 | const size_t src_stride, |
808 | uint16* dest, |
809 | const size_t dest_stride) |
810 | { |
811 | assert(src_begin); |
812 | assert(src_end); |
813 | assert(dest); |
814 | |
815 | switch (src_format) |
816 | { |
817 | case PixelFormatUInt8: // lossless uint8 -> uint16 |
818 | { |
819 | const uint8* it = reinterpret_cast<const uint8*>(src_begin); |
820 | for (; it < reinterpret_cast<const uint8*>(src_end); it += src_stride) |
821 | { |
822 | *dest = static_cast<uint16>(*it) * (65535 / 255); |
823 | dest += dest_stride; |
824 | } |
825 | } |
826 | break; |
827 | |
828 | case PixelFormatUInt16: // lossless uint16 -> uint16 |
829 | { |
830 | const uint16* it = reinterpret_cast<const uint16*>(src_begin); |
831 | for (; it < reinterpret_cast<const uint16*>(src_end); it += src_stride) |
832 | { |
833 | *dest = *it; |
834 | dest += dest_stride; |
835 | } |
836 | } |
837 | break; |
838 | |
839 | case PixelFormatUInt32: // lossy uint32 -> uint16 |
840 | { |
841 | const uint32* it = reinterpret_cast<const uint32*>(src_begin); |
842 | for (; it < reinterpret_cast<const uint32*>(src_end); it += src_stride) |
843 | { |
844 | *dest = static_cast<uint16>(*it >> 16); |
845 | dest += dest_stride; |
846 | } |
847 | } |
848 | break; |
849 | |
850 | case PixelFormatHalf: // lossy half -> uint16 |
851 | { |
852 | const half* it = reinterpret_cast<const half*>(src_begin); |
853 | for (; it < reinterpret_cast<const half*>(src_end); it += src_stride) |
854 | { |
855 | const half val = static_cast<half>(clamp(*it * 65536.0f, 0.0f, 65535.0f)); |
856 | *dest = truncate<uint16>(val); |
857 | dest += dest_stride; |
858 | } |
859 | } |
860 | break; |
861 | |
862 | case PixelFormatFloat: // lossy float -> uint16 |
863 | { |
864 | const float* it = reinterpret_cast<const float*>(src_begin); |
865 | for (; it < reinterpret_cast<const float*>(src_end); it += src_stride) |
866 | { |
867 | const float val = clamp(*it * 65536.0f, 0.0f, 65535.0f); |
868 | *dest = truncate<uint16>(val); |
869 | dest += dest_stride; |
870 | } |
871 | } |
872 | break; |
873 | |
874 | case PixelFormatDouble: // lossy double -> uint16 |
875 | { |
876 | const double* it = reinterpret_cast<const double*>(src_begin); |
877 | for (; it < reinterpret_cast<const double*>(src_end); it += src_stride) |
878 | { |
879 | const double val = clamp(*it * 65536.0, 0.0, 65535.0); |
880 | *dest = truncate<uint16>(val); |
881 | dest += dest_stride; |
882 | } |
883 | } |
884 | break; |
885 | |
886 | assert_otherwise; |
887 | } |
888 | } |
889 | |
890 | template <> |
891 | inline void Pixel::convert_from_format<uint32>( |
892 | const PixelFormat src_format, |
893 | const void* src_begin, |
894 | const void* src_end, |
895 | const size_t src_stride, |
896 | uint32* dest, |
897 | const size_t dest_stride) |
898 | { |
899 | assert(src_begin); |
900 | assert(src_end); |
901 | assert(dest); |
902 | |
903 | switch (src_format) |
904 | { |
905 | case PixelFormatUInt8: // lossless uint8 -> uint32 |
906 | { |
907 | const uint8* it = reinterpret_cast<const uint8*>(src_begin); |
908 | for (; it < reinterpret_cast<const uint8*>(src_end); it += src_stride) |
909 | { |
910 | *dest = static_cast<uint32>(*it) * (4294967295UL / 255); |
911 | dest += dest_stride; |
912 | } |
913 | } |
914 | break; |
915 | |
916 | case PixelFormatUInt16: // lossless uint16 -> uint32 |
917 | { |
918 | const uint16* it = reinterpret_cast<const uint16*>(src_begin); |
919 | for (; it < reinterpret_cast<const uint16*>(src_end); it += src_stride) |
920 | { |
921 | *dest = static_cast<uint32>(*it) * (4294967295UL / 65535); |
922 | dest += dest_stride; |
923 | } |
924 | } |
925 | break; |
926 | |
927 | case PixelFormatUInt32: // lossless uint32 -> uint32 |
928 | { |
929 | const uint32* it = reinterpret_cast<const uint32*>(src_begin); |
930 | for (; it < reinterpret_cast<const uint32*>(src_end); it += src_stride) |
931 | { |
932 | *dest = *it; |
933 | dest += dest_stride; |
934 | } |
935 | } |
936 | break; |
937 | |
938 | case PixelFormatHalf: // lossy half -> uint32 |
939 | { |
940 | const half* it = reinterpret_cast<const half*>(src_begin); |
941 | for (; it < reinterpret_cast<const half*>(src_end); it += src_stride) |
942 | { |
943 | const double val = clamp(static_cast<double>(*it) * 4294967296.0, 0.0, 4294967295.0); |
944 | *dest = truncate<uint32>(val); |
945 | dest += dest_stride; |
946 | } |
947 | } |
948 | break; |
949 | |
950 | case PixelFormatFloat: // lossy float -> uint32 |
951 | { |
952 | const float* it = reinterpret_cast<const float*>(src_begin); |
953 | for (; it < reinterpret_cast<const float*>(src_end); it += src_stride) |
954 | { |
955 | const double val = clamp(static_cast<double>(*it) * 4294967296.0, 0.0, 4294967295.0); |
956 | *dest = truncate<uint32>(val); |
957 | dest += dest_stride; |
958 | } |
959 | } |
960 | break; |
961 | |
962 | case PixelFormatDouble: // lossy double -> uint32 |
963 | { |
964 | const double* it = reinterpret_cast<const double*>(src_begin); |
965 | for (; it < reinterpret_cast<const double*>(src_end); it += src_stride) |
966 | { |
967 | const double val = clamp(*it * 4294967296.0, 0.0, 4294967295.0); |
968 | *dest = truncate<uint32>(val); |
969 | dest += dest_stride; |
970 | } |
971 | } |
972 | break; |
973 | |
974 | assert_otherwise; |
975 | } |
976 | } |
977 | |
978 | template <> |
979 | inline void Pixel::convert_from_format<float>( |
980 | const PixelFormat src_format, |
981 | const void* src_begin, |
982 | const void* src_end, |
983 | const size_t src_stride, |
984 | float* dest, |
985 | const size_t dest_stride) |
986 | { |
987 | assert(src_begin); |
988 | assert(src_end); |
989 | assert(dest); |
990 | |
991 | switch (src_format) |
992 | { |
993 | case PixelFormatUInt8: // lossless uint8 -> float |
994 | { |
995 | const uint8* it = reinterpret_cast<const uint8*>(src_begin); |
996 | for (; it < reinterpret_cast<const uint8*>(src_end); it += src_stride) |
997 | { |
998 | *dest = static_cast<float>(*it) * (1.0f / 255); |
999 | dest += dest_stride; |
1000 | } |
1001 | } |
1002 | break; |
1003 | |
1004 | case PixelFormatUInt16: // lossless uint16 -> float |
1005 | { |
1006 | const uint16* it = reinterpret_cast<const uint16*>(src_begin); |
1007 | for (; it < reinterpret_cast<const uint16*>(src_end); it += src_stride) |
1008 | { |
1009 | *dest = static_cast<float>(*it) * (1.0f / 65535); |
1010 | dest += dest_stride; |
1011 | } |
1012 | } |
1013 | break; |
1014 | |
1015 | case PixelFormatUInt32: // lossy uint32 -> float |
1016 | { |
1017 | const uint32* it = reinterpret_cast<const uint32*>(src_begin); |
1018 | for (; it < reinterpret_cast<const uint32*>(src_end); it += src_stride) |
1019 | { |
1020 | *dest = static_cast<float>(*it) * (1.0f / 4294967295UL); |
1021 | dest += dest_stride; |
1022 | } |
1023 | } |
1024 | break; |
1025 | |
1026 | case PixelFormatHalf: // lossless half -> float |
1027 | { |
1028 | const half* it = reinterpret_cast<const half*>(src_begin); |
1029 | for (; it < reinterpret_cast<const half*>(src_end); it += src_stride) |
1030 | { |
1031 | *dest = static_cast<float>(*it); |
1032 | dest += dest_stride; |
1033 | } |
1034 | } |
1035 | break; |
1036 | |
1037 | case PixelFormatFloat: // lossless float -> float |
1038 | { |
1039 | const float* it = reinterpret_cast<const float*>(src_begin); |
1040 | for (; it < reinterpret_cast<const float*>(src_end); it += src_stride) |
1041 | { |
1042 | *dest = *it; |
1043 | dest += dest_stride; |
1044 | } |
1045 | } |
1046 | break; |
1047 | |
1048 | case PixelFormatDouble: // lossy double -> float |
1049 | { |
1050 | const double* it = reinterpret_cast<const double*>(src_begin); |
1051 | for (; it < reinterpret_cast<const double*>(src_end); it += src_stride) |
1052 | { |
1053 | *dest = static_cast<float>(*it); |
1054 | dest += dest_stride; |
1055 | } |
1056 | } |
1057 | break; |
1058 | |
1059 | assert_otherwise; |
1060 | } |
1061 | } |
1062 | |
1063 | template <> |
1064 | inline void Pixel::convert_from_format<double>( |
1065 | const PixelFormat src_format, |
1066 | const void* src_begin, |
1067 | const void* src_end, |
1068 | const size_t src_stride, |
1069 | double* dest, |
1070 | const size_t dest_stride) |
1071 | { |
1072 | assert(src_begin); |
1073 | assert(src_end); |
1074 | assert(dest); |
1075 | |
1076 | switch (src_format) |
1077 | { |
1078 | case PixelFormatUInt8: // lossless uint8 -> double |
1079 | { |
1080 | const uint8* it = reinterpret_cast<const uint8*>(src_begin); |
1081 | for (; it < reinterpret_cast<const uint8*>(src_end); it += src_stride) |
1082 | { |
1083 | *dest = static_cast<double>(*it) * (1.0 / 255); |
1084 | dest += dest_stride; |
1085 | } |
1086 | } |
1087 | break; |
1088 | |
1089 | case PixelFormatUInt16: // lossless uint16 -> double |
1090 | { |
1091 | const uint16* it = reinterpret_cast<const uint16*>(src_begin); |
1092 | for (; it < reinterpret_cast<const uint16*>(src_end); it += src_stride) |
1093 | { |
1094 | *dest = static_cast<double>(*it) * (1.0 / 65535); |
1095 | dest += dest_stride; |
1096 | } |
1097 | } |
1098 | break; |
1099 | |
1100 | case PixelFormatUInt32: // lossless uint32 -> double |
1101 | { |
1102 | const uint32* it = reinterpret_cast<const uint32*>(src_begin); |
1103 | for (; it < reinterpret_cast<const uint32*>(src_end); it += src_stride) |
1104 | { |
1105 | *dest = static_cast<double>(*it) * (1.0 / 4294967295UL); |
1106 | dest += dest_stride; |
1107 | } |
1108 | } |
1109 | break; |
1110 | |
1111 | case PixelFormatHalf: // lossless half -> double |
1112 | { |
1113 | const half* it = reinterpret_cast<const half*>(src_begin); |
1114 | for (; it < reinterpret_cast<const half*>(src_end); it += src_stride) |
1115 | { |
1116 | *dest = static_cast<double>(*it); |
1117 | dest += dest_stride; |
1118 | } |
1119 | } |
1120 | break; |
1121 | |
1122 | case PixelFormatFloat: // lossless float -> double |
1123 | { |
1124 | const float* it = reinterpret_cast<const float*>(src_begin); |
1125 | for (; it < reinterpret_cast<const float*>(src_end); it += src_stride) |
1126 | { |
1127 | *dest = static_cast<double>(*it); |
1128 | dest += dest_stride; |
1129 | } |
1130 | } |
1131 | break; |
1132 | |
1133 | case PixelFormatDouble: // lossless double -> double |
1134 | { |
1135 | const double* it = reinterpret_cast<const double*>(src_begin); |
1136 | for (; it < reinterpret_cast<const double*>(src_end); it += src_stride) |
1137 | { |
1138 | *dest = *it; |
1139 | dest += dest_stride; |
1140 | } |
1141 | } |
1142 | break; |
1143 | |
1144 | assert_otherwise; |
1145 | } |
1146 | } |
1147 | |
1148 | inline void Pixel::convert( |
1149 | const PixelFormat src_format, |
1150 | const void* src_begin, |
1151 | const void* src_end, |
1152 | const size_t src_stride, |
1153 | const PixelFormat dest_format, |
1154 | void* dest, |
1155 | const size_t dest_stride) |
1156 | { |
1157 | assert(src_begin); |
1158 | assert(src_end); |
1159 | assert(dest); |
1160 | |
1161 | switch (src_format) |
1162 | { |
1163 | case PixelFormatUInt8: // uint8 -> destination format |
1164 | convert_to_format<uint8>( |
1165 | reinterpret_cast<const uint8*>(src_begin), |
1166 | reinterpret_cast<const uint8*>(src_end), |
1167 | src_stride, |
1168 | dest_format, |
1169 | dest, |
1170 | dest_stride); |
1171 | break; |
1172 | |
1173 | case PixelFormatUInt16: // uint16 -> destination format |
1174 | convert_to_format<uint16>( |
1175 | reinterpret_cast<const uint16*>(src_begin), |
1176 | reinterpret_cast<const uint16*>(src_end), |
1177 | src_stride, |
1178 | dest_format, |
1179 | dest, |
1180 | dest_stride); |
1181 | break; |
1182 | |
1183 | case PixelFormatUInt32: // uint32 -> destination format |
1184 | convert_to_format<uint32>( |
1185 | reinterpret_cast<const uint32*>(src_begin), |
1186 | reinterpret_cast<const uint32*>(src_end), |
1187 | src_stride, |
1188 | dest_format, |
1189 | dest, |
1190 | dest_stride); |
1191 | break; |
1192 | |
1193 | case PixelFormatHalf: // half -> destination format |
1194 | convert_to_format<half>( |
1195 | reinterpret_cast<const half*>(src_begin), |
1196 | reinterpret_cast<const half*>(src_end), |
1197 | src_stride, |
1198 | dest_format, |
1199 | dest, |
1200 | dest_stride); |
1201 | break; |
1202 | |
1203 | case PixelFormatFloat: // float -> destination format |
1204 | convert_to_format<float>( |
1205 | reinterpret_cast<const float*>(src_begin), |
1206 | reinterpret_cast<const float*>(src_end), |
1207 | src_stride, |
1208 | dest_format, |
1209 | dest, |
1210 | dest_stride); |
1211 | break; |
1212 | |
1213 | case PixelFormatDouble: // double -> destination format |
1214 | convert_to_format<double>( |
1215 | reinterpret_cast<const double*>(src_begin), |
1216 | reinterpret_cast<const double*>(src_end), |
1217 | src_stride, |
1218 | dest_format, |
1219 | dest, |
1220 | dest_stride); |
1221 | break; |
1222 | |
1223 | assert_otherwise; |
1224 | } |
1225 | } |
1226 | |
1227 | } // namespace foundation |
1228 | |
1229 | #endif // !APPLESEED_FOUNDATION_IMAGE_PIXEL_H |
1230 | |