]> git.cworth.org Git - vogl/blob - src/voglcore/vogl_image.h
Initial vogl checkin
[vogl] / src / voglcore / vogl_image.h
1 /**************************************************************************
2  *
3  * Copyright 2013-2014 RAD Game Tools and Valve Software
4  * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
5  * All Rights Reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  *
25  **************************************************************************/
26
27 // File: vogl_image.h
28 #pragma once
29
30 #include "vogl_core.h"
31 #include "vogl_color.h"
32 #include "vogl_vec.h"
33 #include "vogl_pixel_format.h"
34 #include "vogl_rect.h"
35
36 namespace vogl
37 {
38     enum
39     {
40         BLIT_FLAG_SWAP_RGB = 1,
41         BLIT_FLAG_MULTIPLY_BY_ALPHA = 2,
42         BLIT_FLAG_UNPREMULTIPLY_BY_ALPHA = 4
43     };
44
45     template <typename color_type>
46     class image
47     {
48     public:
49         typedef color_type color_t;
50         typedef typename color_type::component_t component_t;
51
52         typedef vogl::vector<color_type> pixel_buf_t;
53
54         image()
55             : m_width(0),
56               m_height(0),
57               m_pitch(0),
58               m_total(0),
59               m_comp_flags(pixel_format_helpers::cDefaultCompFlags),
60               m_pPixels(NULL)
61         {
62         }
63
64         // pitch is in PIXELS, not bytes.
65         image(uint width, uint height, uint pitch = UINT_MAX, const color_type &background = color_type::make_black(), uint flags = pixel_format_helpers::cDefaultCompFlags)
66             : m_comp_flags(flags)
67         {
68             VOGL_ASSERT((width > 0) && (height > 0));
69             if (pitch == UINT_MAX)
70                 pitch = width;
71
72             m_pixel_buf.resize(pitch * height);
73
74             m_width = width;
75             m_height = height;
76             m_pitch = pitch;
77             m_total = m_pitch * m_height;
78
79             m_pPixels = &m_pixel_buf.front();
80
81             set_all(background);
82         }
83
84         // pitch is in PIXELS, not bytes.
85         image(color_type *pPixels, uint width, uint height, uint pitch = UINT_MAX, uint flags = pixel_format_helpers::cDefaultCompFlags)
86         {
87             alias(pPixels, width, height, pitch, flags);
88         }
89
90         image &operator=(const image &other)
91         {
92             if (this == &other)
93                 return *this;
94
95             if (other.m_pixel_buf.is_empty())
96             {
97                 // This doesn't look very safe - let's make a new instance.
98                 //m_pixel_buf.clear();
99                 //m_pPixels = other.m_pPixels;
100
101                 const uint total_pixels = other.m_pitch * other.m_height;
102                 if ((total_pixels) && (other.m_pPixels))
103                 {
104                     m_pixel_buf.resize(total_pixels);
105                     m_pixel_buf.insert(0, other.m_pPixels, m_pixel_buf.size());
106                     m_pPixels = &m_pixel_buf.front();
107                 }
108                 else
109                 {
110                     m_pixel_buf.clear();
111                     m_pPixels = NULL;
112                 }
113             }
114             else
115             {
116                 m_pixel_buf = other.m_pixel_buf;
117                 m_pPixels = &m_pixel_buf.front();
118             }
119
120             m_width = other.m_width;
121             m_height = other.m_height;
122             m_pitch = other.m_pitch;
123             m_total = other.m_total;
124             m_comp_flags = other.m_comp_flags;
125
126             return *this;
127         }
128
129         image(const image &other)
130             : m_width(0), m_height(0), m_pitch(0), m_total(0), m_comp_flags(pixel_format_helpers::cDefaultCompFlags), m_pPixels(NULL)
131         {
132             *this = other;
133         }
134
135         // pitch is in PIXELS, not bytes.
136         void alias(color_type *pPixels, uint width, uint height, uint pitch = UINT_MAX, uint flags = pixel_format_helpers::cDefaultCompFlags)
137         {
138             m_pixel_buf.clear();
139
140             m_pPixels = pPixels;
141
142             m_width = width;
143             m_height = height;
144             m_pitch = (pitch == UINT_MAX) ? width : pitch;
145             m_total = m_pitch * m_height;
146             m_comp_flags = flags;
147         }
148
149         // pitch is in PIXELS, not bytes.
150         bool grant_ownership(color_type *pPixels, uint width, uint height, uint pitch = UINT_MAX, uint flags = pixel_format_helpers::cDefaultCompFlags)
151         {
152             if (pitch == UINT_MAX)
153                 pitch = width;
154
155             if ((!pPixels) || (!width) || (!height) || (pitch < width))
156             {
157                 VOGL_ASSERT_ALWAYS;
158                 return false;
159             }
160
161             if (pPixels == get_ptr())
162             {
163                 VOGL_ASSERT_ALWAYS;
164                 return false;
165             }
166
167             clear();
168
169             if (!m_pixel_buf.grant_ownership(pPixels, height * pitch, height * pitch))
170                 return false;
171
172             m_pPixels = pPixels;
173
174             m_width = width;
175             m_height = height;
176             m_pitch = pitch;
177             m_total = pitch * height;
178             m_comp_flags = flags;
179
180             return true;
181         }
182
183         void clear()
184         {
185             m_pPixels = NULL;
186             m_pixel_buf.clear();
187             m_width = 0;
188             m_height = 0;
189             m_pitch = 0;
190             m_total = 0;
191             m_comp_flags = pixel_format_helpers::cDefaultCompFlags;
192         }
193
194         inline bool is_valid() const
195         {
196             return m_total > 0;
197         }
198
199         inline pixel_format_helpers::component_flags get_comp_flags() const
200         {
201             return static_cast<pixel_format_helpers::component_flags>(m_comp_flags);
202         }
203         inline void set_comp_flags(pixel_format_helpers::component_flags new_flags)
204         {
205             m_comp_flags = new_flags;
206         }
207         inline void reset_comp_flags()
208         {
209             m_comp_flags = pixel_format_helpers::cDefaultCompFlags;
210         }
211
212         inline bool is_component_valid(uint index) const
213         {
214             VOGL_ASSERT(index < 4U);
215             return utils::is_flag_set(m_comp_flags, index);
216         }
217         inline void set_component_valid(uint index, bool state)
218         {
219             VOGL_ASSERT(index < 4U);
220             utils::set_flag(m_comp_flags, index, state);
221         }
222         inline void set_valid_components(bool rgb, bool alpha)
223         {
224             set_comp_flags(static_cast<pixel_format_helpers::component_flags>((rgb ? pixel_format_helpers::cCompFlagsRGBAValid : 0) | (alpha ? pixel_format_helpers::cCompFlagAValid : 0)));
225         }
226
227         inline bool has_rgb() const
228         {
229             return is_component_valid(0) || is_component_valid(1) || is_component_valid(2);
230         }
231         inline bool has_alpha() const
232         {
233             return is_component_valid(3);
234         }
235
236         inline bool is_grayscale() const
237         {
238             return utils::is_bit_set(m_comp_flags, pixel_format_helpers::cCompFlagGrayscale);
239         }
240         inline void set_grayscale(bool state)
241         {
242             utils::set_bit(m_comp_flags, pixel_format_helpers::cCompFlagGrayscale, state);
243         }
244
245         void set_all(const color_type &c)
246         {
247             for (uint i = 0; i < m_total; i++)
248                 m_pPixels[i] = c;
249         }
250
251         void flip_x()
252         {
253             const uint half_width = m_width / 2;
254             for (uint y = 0; y < m_height; y++)
255             {
256                 for (uint x = 0; x < half_width; x++)
257                 {
258                     color_type c((*this)(x, y));
259                     (*this)(x, y) = (*this)(m_width - 1 - x, y);
260                     (*this)(m_width - 1 - x, y) = c;
261                 }
262             }
263         }
264
265         void flip_y()
266         {
267             const uint half_height = m_height / 2;
268             for (uint y = 0; y < half_height; y++)
269             {
270                 for (uint x = 0; x < m_width; x++)
271                 {
272                     color_type c((*this)(x, y));
273                     (*this)(x, y) = (*this)(x, m_height - 1 - y);
274                     (*this)(x, m_height - 1 - y) = c;
275                 }
276             }
277         }
278
279         void convert_to_grayscale()
280         {
281             for (uint y = 0; y < m_height; y++)
282                 for (uint x = 0; x < m_width; x++)
283                 {
284                     color_type c((*this)(x, y));
285                     typename color_type::component_t l = static_cast<typename color_type::component_t>(c.get_luma());
286                     c.r = l;
287                     c.g = l;
288                     c.b = l;
289                     (*this)(x, y) = c;
290                 }
291
292             set_grayscale(true);
293         }
294
295         void swizzle(uint r, uint g, uint b, uint a)
296         {
297             for (uint y = 0; y < m_height; y++)
298                 for (uint x = 0; x < m_width; x++)
299                 {
300                     const color_type &c = (*this)(x, y);
301
302                     (*this)(x, y) = color_type(c[r], c[g], c[b], c[a]);
303                 }
304         }
305
306         void set_alpha_to_luma()
307         {
308             for (uint y = 0; y < m_height; y++)
309                 for (uint x = 0; x < m_width; x++)
310                 {
311                     color_type c((*this)(x, y));
312                     typename color_type::component_t l = static_cast<typename color_type::component_t>(c.get_luma());
313                     c.a = l;
314                     (*this)(x, y) = c;
315                 }
316
317             set_component_valid(3, true);
318         }
319
320         bool extract_block_clamped(color_type *pDst, uint x, uint y, uint w, uint h, bool flip_xy = false) const
321         {
322             if ((x >= m_width) || (y >= m_height))
323             {
324                 VOGL_ASSERT_ALWAYS;
325                 return false;
326             }
327
328             if (flip_xy)
329             {
330                 for (uint y_ofs = 0; y_ofs < h; y_ofs++)
331                     for (uint x_ofs = 0; x_ofs < w; x_ofs++)
332                         pDst[x_ofs * h + y_ofs] = get_clamped(x_ofs + x, y_ofs + y); // 5/4/12 - this was incorrectly x_ofs * 4
333             }
334             else if (((x + w) > m_width) || ((y + h) > m_height))
335             {
336                 for (uint y_ofs = 0; y_ofs < h; y_ofs++)
337                     for (uint x_ofs = 0; x_ofs < w; x_ofs++)
338                         *pDst++ = get_clamped(x_ofs + x, y_ofs + y);
339             }
340             else
341             {
342                 const color_type *pSrc = get_scanline(y) + x;
343
344                 for (uint i = h; i; i--)
345                 {
346                     memcpy(pDst, pSrc, w * sizeof(color_type));
347                     pDst += w;
348
349                     pSrc += m_pitch;
350                 }
351             }
352
353             return true;
354         }
355
356         bool extract_block_wrapped(color_type *pDst, int x, int y, uint w, uint h, bool flip_xy = false) const
357         {
358             if (flip_xy)
359             {
360                 for (uint y_ofs = 0; y_ofs < h; y_ofs++)
361                     for (uint x_ofs = 0; x_ofs < w; x_ofs++)
362                         pDst[x_ofs * h + y_ofs] = get_wrapped(x_ofs + x, y_ofs + y);
363             }
364             else
365             {
366                 for (uint y_ofs = 0; y_ofs < h; y_ofs++)
367                     for (uint x_ofs = 0; x_ofs < w; x_ofs++)
368                         *pDst++ = get_wrapped(x_ofs + x, y_ofs + y);
369             }
370
371             return true;
372         }
373
374         // No clipping!
375         void unclipped_fill_box(uint x, uint y, uint w, uint h, const color_type &c)
376         {
377             if (((x + w) > m_width) || ((y + h) > m_height))
378             {
379                 VOGL_ASSERT_ALWAYS;
380                 return;
381             }
382
383             color_type *p = get_scanline(y) + x;
384
385             for (uint i = h; i; i--)
386             {
387                 color_type *q = p;
388                 for (uint j = w; j; j--)
389                     *q++ = c;
390                 p += m_pitch;
391             }
392         }
393
394         void draw_rect(int x, int y, uint width, uint height, const color_type &c)
395         {
396             draw_line(x, y, x + width - 1, y, c);
397             draw_line(x, y, x, y + height - 1, c);
398             draw_line(x + width - 1, y, x + width - 1, y + height - 1, c);
399             draw_line(x, y + height - 1, x + width - 1, y + height - 1, c);
400         }
401
402         // No clipping!
403         bool unclipped_blit(uint src_x, uint src_y, uint src_w, uint src_h, uint dst_x, uint dst_y, const image &src, uint blit_flags = 0)
404         {
405             if ((!is_valid()) || (!src.is_valid()))
406             {
407                 VOGL_ASSERT_ALWAYS;
408                 return false;
409             }
410
411             if (((src_x + src_w) > src.get_width()) || ((src_y + src_h) > src.get_height()))
412             {
413                 VOGL_ASSERT_ALWAYS;
414                 return false;
415             }
416
417             if (((dst_x + src_w) > get_width()) || ((dst_y + src_h) > get_height()))
418             {
419                 VOGL_ASSERT_ALWAYS;
420                 return false;
421             }
422
423             const color_type *VOGL_RESTRICT pS = &src(src_x, src_y);
424             color_type *VOGL_RESTRICT pD = &(*this)(dst_x, dst_y);
425
426             const uint bytes_to_copy_per_scanline = src_w * sizeof(color_type);
427             const uint src_pitch_minus_src_w = src.get_pitch() - src_w;
428             const uint dst_pitch_minus_src_w = get_pitch() - src_w;
429
430             if (blit_flags & BLIT_FLAG_UNPREMULTIPLY_BY_ALPHA)
431             {
432                 if (vogl_is_little_endian() && (!color_t::component_traits::cSigned) && (sizeof(component_t) == sizeof(uint8)))
433                 {
434                     VOGL_ASSERT(sizeof(color_t) == sizeof(uint32));
435                     for (uint i = src_h; i; i--)
436                     {
437                         color_quad_u8 *pD_end = pD + src_w;
438                         if (blit_flags & BLIT_FLAG_SWAP_RGB)
439                         {
440                             do
441                             {
442                                 uint32 s = *reinterpret_cast<const uint32 *>(pS);
443                                 pS++;
444
445                                 uint a = s >> 24, b = s & 0xFF, g = (s & 0xFF00) >> 8, r = (s & 0xFF0000) >> 16;
446                                 if ((a != 255) && (a))
447                                 {
448                                     uint round = a / 2;
449                                     r = math::clamp255((r * 255 + round) / a);
450                                     g = math::clamp255((g * 255 + round) / a);
451                                     b = math::clamp255((b * 255 + round) / a);
452                                 }
453
454                                 *reinterpret_cast<uint32 *>(pD) = ((a << 24) | (b << 16) | (g << 8) | r);
455                                 pD++;
456
457                             } while (pD != pD_end);
458                         }
459                         else
460                         {
461                             do
462                             {
463                                 uint32 s = *reinterpret_cast<const uint32 *>(pS);
464                                 pS++;
465
466                                 uint a = s >> 24;
467                                 if ((a != 255) && (a))
468                                 {
469                                     uint r = s & 0xFF, g = (s & 0xFF00) >> 8, b = (s & 0xFF0000) >> 16;
470                                     uint round = a / 2;
471                                     r = math::clamp255((r * 255 + round) / a);
472                                     g = math::clamp255((g * 255 + round) / a);
473                                     b = math::clamp255((b * 255 + round) / a);
474                                     *reinterpret_cast<uint32 *>(pD) = ((a << 24) | (b << 16) | (g << 8) | r);
475                                 }
476                                 else
477                                 {
478                                     *reinterpret_cast<uint32 *>(pD) = s;
479                                 }
480
481                                 pD++;
482
483                             } while (pD != pD_end);
484                         }
485
486                         pS += src_pitch_minus_src_w;
487                         pD += dst_pitch_minus_src_w;
488                     }
489                 }
490                 else
491                 {
492                     for (uint i = src_h; i; i--)
493                     {
494                         color_quad_u8 *pD_end = pD + src_w;
495                         do
496                         {
497                             color_type s(*pS++);
498
499                             if (blit_flags & BLIT_FLAG_SWAP_RGB)
500                                 std::swap(s.r, s.b);
501
502                             float flScale = 0.0f;
503                             if (s.a != 0)
504                             {
505                                 if (color_t::component_traits::cFloat)
506                                     flScale = 1.0f / s.a;
507                                 else
508                                     flScale = static_cast<float>(color_t::component_traits::cMax) / s.a;
509                             }
510                             s.scale_rgb_by_float(flScale);
511
512                             *pD++ = s;
513                         } while (pD != pD_end);
514
515                         pS += src_pitch_minus_src_w;
516                         pD += dst_pitch_minus_src_w;
517                     }
518                 }
519             }
520             else if (blit_flags & BLIT_FLAG_MULTIPLY_BY_ALPHA)
521             {
522                 if (vogl_is_little_endian() && (!color_t::component_traits::cSigned) && (sizeof(component_t) == sizeof(uint8)))
523                 {
524                     VOGL_ASSERT(sizeof(color_t) == sizeof(uint32));
525                     for (uint i = src_h; i; i--)
526                     {
527                         color_quad_u8 *pD_end = pD + src_w;
528                         if (blit_flags & BLIT_FLAG_SWAP_RGB)
529                         {
530                             do
531                             {
532                                 uint a = pS->a;
533                                 uint b = math::mul255(pS->r, a);
534                                 uint g = math::mul255(pS->g, a);
535                                 uint r = math::mul255(pS->b, a);
536                                 pS++;
537
538                                 *reinterpret_cast<uint32 *>(pD) = ((a << 24) | (b << 16) | (g << 8) | r);
539                                 pD++;
540                             } while (pD != pD_end);
541                         }
542                         else
543                         {
544                             do
545                             {
546                                 uint a = pS->a;
547                                 uint r = math::mul255(pS->r, a);
548                                 uint g = math::mul255(pS->g, a);
549                                 uint b = math::mul255(pS->b, a);
550                                 pS++;
551
552                                 *reinterpret_cast<uint32 *>(pD) = ((a << 24) | (b << 16) | (g << 8) | r);
553                                 pD++;
554                             } while (pD != pD_end);
555                         }
556
557                         pS += src_pitch_minus_src_w;
558                         pD += dst_pitch_minus_src_w;
559                     }
560                 }
561                 else
562                 {
563                     for (uint i = src_h; i; i--)
564                     {
565                         color_quad_u8 *pD_end = pD + src_w;
566                         do
567                         {
568                             color_type s(*pS++);
569
570                             if (blit_flags & BLIT_FLAG_SWAP_RGB)
571                                 std::swap(s.r, s.b);
572
573                             s.scale_rgb_by_component(s.a);
574
575                             *pD++ = s;
576                         } while (pD != pD_end);
577
578                         pS += src_pitch_minus_src_w;
579                         pD += dst_pitch_minus_src_w;
580                     }
581                 }
582             }
583             else if (blit_flags & BLIT_FLAG_SWAP_RGB)
584             {
585                 for (uint i = src_h; i; i--)
586                 {
587                     color_quad_u8 *pD_end = pD + src_w;
588                     if (vogl_is_little_endian() && (sizeof(component_t) == sizeof(uint8)))
589                     {
590                         VOGL_ASSERT(sizeof(color_t) == sizeof(uint32));
591                         do
592                         {
593                             uint32 c = *reinterpret_cast<const uint32 *>(pS);
594                             pS++;
595
596                             c = (c & 0xFF00FF00) | ((c & 0xFF) << 16) | ((c & 0xFF0000) >> 16);
597
598                             *reinterpret_cast<uint32 *>(pD) = c;
599                             pD++;
600                         } while (pD != pD_end);
601                     }
602                     else
603                     {
604                         do
605                         {
606                             color_type s(*pS++);
607                             std::swap(s.r, s.b);
608                             *pD++ = s;
609                         } while (pD != pD_end);
610                     }
611
612                     pS += src_pitch_minus_src_w;
613                     pD += dst_pitch_minus_src_w;
614                 }
615             }
616             else
617             {
618                 if ((bytes_to_copy_per_scanline == src.get_pitch_in_bytes()) && (bytes_to_copy_per_scanline == get_pitch_in_bytes()))
619                 {
620                     memcpy(pD, pS, bytes_to_copy_per_scanline * src_h);
621                 }
622                 else
623                 {
624                     for (uint i = src_h; i; i--)
625                     {
626                         memcpy(pD, pS, bytes_to_copy_per_scanline);
627
628                         pS += src.get_pitch();
629                         pD += get_pitch();
630                     }
631                 }
632             }
633
634             return true;
635         }
636
637         // With clipping.
638         bool blit(int dst_x, int dst_y, const image &src, uint blit_flags = 0)
639         {
640             if ((!is_valid()) || (!src.is_valid()))
641             {
642                 VOGL_ASSERT_ALWAYS;
643                 return false;
644             }
645
646             int src_x = 0;
647             int src_y = 0;
648
649             if (dst_x < 0)
650             {
651                 src_x = -dst_x;
652                 if (src_x >= static_cast<int>(src.get_width()))
653                     return false;
654                 dst_x = 0;
655             }
656
657             if (dst_y < 0)
658             {
659                 src_y = -dst_y;
660                 if (src_y >= static_cast<int>(src.get_height()))
661                     return false;
662                 dst_y = 0;
663             }
664
665             if ((dst_x >= (int)m_width) || (dst_y >= (int)m_height))
666                 return false;
667
668             uint width = math::minimum(m_width - dst_x, src.get_width() - src_x);
669             uint height = math::minimum(m_height - dst_y, src.get_height() - src_y);
670
671             bool success = unclipped_blit(src_x, src_y, width, height, dst_x, dst_y, src, blit_flags);
672             (void)success;
673             VOGL_ASSERT(success);
674
675             return true;
676         }
677
678         // With clipping.
679         bool blit(int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, const image &src, uint blit_flags = 0)
680         {
681             if ((!is_valid()) || (!src.is_valid()))
682             {
683                 VOGL_ASSERT_ALWAYS;
684                 return false;
685             }
686
687             rect src_rect(src_x, src_y, src_x + src_w, src_y + src_h);
688             if (!src_rect.intersect(src.get_bounds()))
689                 return false;
690
691             rect dst_rect(dst_x, dst_y, dst_x + src_rect.get_width(), dst_y + src_rect.get_height());
692             if (!dst_rect.intersect(get_bounds()))
693                 return false;
694
695             int src_x_ofs = dst_rect.get_left() - dst_x;
696             if (src_x_ofs >= static_cast<int>(src_rect.get_width()))
697                 return false;
698             src_rect[0][0] += src_x_ofs;
699
700             int src_y_ofs = dst_rect.get_top() - dst_y;
701             if (src_y_ofs >= static_cast<int>(src_rect.get_height()))
702                 return false;
703             src_rect[0][1] += src_y_ofs;
704             VOGL_ASSERT(src.is_valid());
705
706             bool success = unclipped_blit(
707                 src_rect.get_left(), src_rect.get_top(),
708                 math::minimum(src_rect.get_width(), dst_rect.get_width()), math::minimum(src_rect.get_height(), dst_rect.get_height()),
709                 dst_rect.get_left(), dst_rect.get_top(), src, blit_flags);
710             (void)success;
711             VOGL_ASSERT(success);
712
713             return true;
714         }
715
716         // In-place resize of image dimensions (cropping).
717         bool crop(uint new_width, uint new_height, uint new_pitch = UINT_MAX, const color_type background = color_type::make_black())
718         {
719             if (new_pitch == UINT_MAX)
720                 new_pitch = new_width;
721
722             if ((new_width == m_width) && (new_height == m_height) && (new_pitch == m_pitch))
723                 return true;
724
725             if ((!new_width) || (!new_height) || (!new_pitch))
726             {
727                 clear();
728                 return false;
729             }
730
731             pixel_buf_t existing_pixels;
732             existing_pixels.swap(m_pixel_buf);
733
734             if (!m_pixel_buf.try_resize(new_height * new_pitch))
735             {
736                 clear();
737                 return false;
738             }
739
740             for (uint y = 0; y < new_height; y++)
741             {
742                 for (uint x = 0; x < new_width; x++)
743                 {
744                     if ((x < m_width) && (y < m_height))
745                         m_pixel_buf[x + y * new_pitch] = existing_pixels[x + y * m_pitch];
746                     else
747                         m_pixel_buf[x + y * new_pitch] = background;
748                 }
749             }
750
751             m_width = new_width;
752             m_height = new_height;
753             m_pitch = new_pitch;
754             m_total = new_pitch * new_height;
755             m_pPixels = &m_pixel_buf.front();
756
757             return true;
758         }
759
760         // In-place resize of image dimensions (same as cropping).
761         bool resize(uint new_width, uint new_height, uint new_pitch = UINT_MAX, const color_type background = color_type::make_black())
762         {
763             return crop(new_width, new_height, new_pitch, background);
764         }
765
766         inline uint get_width() const
767         {
768             return m_width;
769         }
770         inline uint get_height() const
771         {
772             return m_height;
773         }
774         inline uint get_total_pixels() const
775         {
776             return m_width * m_height;
777         }
778
779         inline rect get_bounds() const
780         {
781             return rect(0, 0, m_width, m_height);
782         }
783
784         inline uint get_pitch() const
785         {
786             return m_pitch;
787         }
788         inline uint get_pitch_in_bytes() const
789         {
790             return m_pitch * sizeof(color_type);
791         }
792
793         // Returns pitch * height, NOT width * height!
794         inline uint get_total() const
795         {
796             return m_total;
797         }
798
799         inline uint get_block_width(uint block_size) const
800         {
801             return (m_width + block_size - 1) / block_size;
802         }
803         inline uint get_block_height(uint block_size) const
804         {
805             return (m_height + block_size - 1) / block_size;
806         }
807         inline uint get_total_blocks(uint block_size) const
808         {
809             return get_block_width(block_size) * get_block_height(block_size);
810         }
811
812         inline uint get_size_in_bytes() const
813         {
814             return sizeof(color_type) * m_total;
815         }
816
817         inline const color_type *get_pixels() const
818         {
819             return m_pPixels;
820         }
821         inline color_type *get_pixels()
822         {
823             return m_pPixels;
824         }
825
826         inline const color_type &operator()(uint x, uint y) const
827         {
828             VOGL_ASSERT((x < m_width) && (y < m_height));
829             return m_pPixels[x + y * m_pitch];
830         }
831
832         inline color_type &operator()(uint x, uint y)
833         {
834             VOGL_ASSERT((x < m_width) && (y < m_height));
835             return m_pPixels[x + y * m_pitch];
836         }
837
838         inline const color_type &get_unclamped(uint x, uint y) const
839         {
840             VOGL_ASSERT((x < m_width) && (y < m_height));
841             return m_pPixels[x + y * m_pitch];
842         }
843
844         inline color_type &get_unclamped(uint x, uint y)
845         {
846             VOGL_ASSERT((x < m_width) && (y < m_height));
847             return m_pPixels[x + y * m_pitch];
848         }
849
850         inline const color_type &get_clamped(int x, int y) const
851         {
852             x = math::clamp<int>(x, 0, m_width - 1);
853             y = math::clamp<int>(y, 0, m_height - 1);
854             return m_pPixels[x + y * m_pitch];
855         }
856
857         inline color_type &get_clamped(int x, int y)
858         {
859             x = math::clamp<int>(x, 0, m_width - 1);
860             y = math::clamp<int>(y, 0, m_height - 1);
861             return m_pPixels[x + y * m_pitch];
862         }
863
864         inline const color_type &get_wrapped(int x, int y) const
865         {
866             if (static_cast<uint>(x) >= m_width)
867                 x = math::posmod(x, m_width);
868             if (static_cast<uint>(y) >= m_height)
869                 y = math::posmod(y, m_height);
870             return m_pPixels[x + y * m_pitch];
871         }
872
873         inline color_type &get_wrapped(int x, int y)
874         {
875             if (static_cast<uint>(x) >= m_width)
876                 x = math::posmod(x, m_width);
877             if (static_cast<uint>(y) >= m_height)
878                 y = math::posmod(y, m_height);
879             return m_pPixels[x + y * m_pitch];
880         }
881
882         inline const color_type &get_clamped_or_wrapped(int x, int y, bool wrap_u, bool wrap_v) const
883         {
884             x = wrap_u ? math::posmod(x, m_width) : math::clamp<int>(x, 0, m_width - 1);
885             y = wrap_v ? math::posmod(y, m_height) : math::clamp<int>(y, 0, m_height - 1);
886             return m_pPixels[x + y * m_pitch];
887         }
888
889         inline color_type &get_clamped_or_wrapped(int x, int y, bool wrap_u, bool wrap_v)
890         {
891             x = wrap_u ? math::posmod(x, m_width) : math::clamp<int>(x, 0, m_width - 1);
892             y = wrap_v ? math::posmod(y, m_height) : math::clamp<int>(y, 0, m_height - 1);
893             return m_pPixels[x + y * m_pitch];
894         }
895
896         // Sample image with bilinear filtering.
897         // (x,y) - Continuous coordinates, where pixel centers are at (.5,.5), valid image coords are [0,width] and [0,height].
898         void get_filtered(float x, float y, color_type &result) const
899         {
900             x -= .5f;
901             y -= .5f;
902
903             int ix = (int)floor(x);
904             int iy = (int)floor(y);
905             float wx = x - ix;
906             float wy = y - iy;
907
908             color_type a(get_clamped(ix, iy));
909             color_type b(get_clamped(ix + 1, iy));
910             color_type c(get_clamped(ix, iy + 1));
911             color_type d(get_clamped(ix + 1, iy + 1));
912
913             for (uint i = 0; i < 4; i++)
914             {
915                 double top = math::lerp<double>(a[i], b[i], wx);
916                 double bot = math::lerp<double>(c[i], d[i], wx);
917                 double m = math::lerp<double>(top, bot, wy);
918
919                 if (!color_type::component_traits::cFloat)
920                     m += .5f;
921
922                 result.set_component(i, static_cast<typename color_type::parameter_t>(m));
923             }
924         }
925
926         void get_filtered(float x, float y, vec4F &result, bool wrap_u = false, bool wrap_v = false) const
927         {
928             x -= .5f;
929             y -= .5f;
930
931             int ix = (int)floor(x);
932             int iy = (int)floor(y);
933             float wx = x - ix;
934             float wy = y - iy;
935
936             color_type a(get_clamped_or_wrapped(ix, iy, wrap_u, wrap_v));
937             color_type b(get_clamped_or_wrapped(ix + 1, iy, wrap_u, wrap_v));
938             color_type c(get_clamped_or_wrapped(ix, iy + 1, wrap_u, wrap_v));
939             color_type d(get_clamped_or_wrapped(ix + 1, iy + 1, wrap_u, wrap_v));
940
941             for (uint i = 0; i < 4; i++)
942             {
943                 float top = math::lerp<float>(a[i], b[i], wx);
944                 float bot = math::lerp<float>(c[i], d[i], wx);
945                 float m = math::lerp<float>(top, bot, wy);
946
947                 result[i] = m;
948             }
949         }
950
951         void get_unfiltered(float x, float y, vec4F &result, bool wrap_u = false, bool wrap_v = false) const
952         {
953             x -= .5f;
954             y -= .5f;
955
956             int ix = (int)floor(x);
957             int iy = (int)floor(y);
958
959             color_type c(get_clamped_or_wrapped(ix, iy, wrap_u, wrap_v));
960             result.set(static_cast<float>(c[0]), static_cast<float>(c[1]), static_cast<float>(c[2]), static_cast<float>(c[3]));
961         }
962
963         inline void set_pixel_unclipped(uint x, uint y, const color_type &c)
964         {
965             VOGL_ASSERT((x < m_width) && (y < m_height));
966             m_pPixels[x + y * m_pitch] = c;
967         }
968
969         inline void set_pixel_clipped(int x, int y, const color_type &c)
970         {
971             if ((static_cast<uint>(x) >= m_width) || (static_cast<uint>(y) >= m_height))
972                 return;
973
974             m_pPixels[x + y * m_pitch] = c;
975         }
976
977         inline const color_type *get_scanline(uint y) const
978         {
979             VOGL_ASSERT(y < m_height);
980             return &m_pPixels[y * m_pitch];
981         }
982
983         inline color_type *get_scanline(uint y)
984         {
985             VOGL_ASSERT(y < m_height);
986             return &m_pPixels[y * m_pitch];
987         }
988
989         inline const color_type *get_ptr() const
990         {
991             return m_pPixels;
992         }
993
994         inline color_type *get_ptr()
995         {
996             return m_pPixels;
997         }
998
999         inline void swap(image &other)
1000         {
1001             utils::swap(m_width, other.m_width);
1002             utils::swap(m_height, other.m_height);
1003             utils::swap(m_pitch, other.m_pitch);
1004             utils::swap(m_total, other.m_total);
1005             utils::swap(m_comp_flags, other.m_comp_flags);
1006             utils::swap(m_pPixels, other.m_pPixels);
1007             m_pixel_buf.swap(other.m_pixel_buf);
1008         }
1009
1010         void draw_line(int xs, int ys, int xe, int ye, const color_type &color)
1011         {
1012             if (xs > xe)
1013             {
1014                 utils::swap(xs, xe);
1015                 utils::swap(ys, ye);
1016             }
1017
1018             int dx = xe - xs, dy = ye - ys;
1019             if (!dx)
1020             {
1021                 if (ys > ye)
1022                     utils::swap(ys, ye);
1023                 for (int i = ys; i <= ye; i++)
1024                     set_pixel_clipped(xs, i, color);
1025             }
1026             else if (!dy)
1027             {
1028                 for (int i = xs; i < xe; i++)
1029                     set_pixel_clipped(i, ys, color);
1030             }
1031             else if (dy > 0)
1032             {
1033                 if (dy <= dx)
1034                 {
1035                     int e = 2 * dy - dx, e_no_inc = 2 * dy, e_inc = 2 * (dy - dx);
1036                     rasterize_line(xs, ys, xe, ye, 0, 1, e, e_inc, e_no_inc, color);
1037                 }
1038                 else
1039                 {
1040                     int e = 2 * dx - dy, e_no_inc = 2 * dx, e_inc = 2 * (dx - dy);
1041                     rasterize_line(xs, ys, xe, ye, 1, 1, e, e_inc, e_no_inc, color);
1042                 }
1043             }
1044             else
1045             {
1046                 dy = -dy;
1047                 if (dy <= dx)
1048                 {
1049                     int e = 2 * dy - dx, e_no_inc = 2 * dy, e_inc = 2 * (dy - dx);
1050                     rasterize_line(xs, ys, xe, ye, 0, -1, e, e_inc, e_no_inc, color);
1051                 }
1052                 else
1053                 {
1054                     int e = 2 * dx - dy, e_no_inc = (2 * dx), e_inc = 2 * (dx - dy);
1055                     rasterize_line(xe, ye, xs, ys, 1, -1, e, e_inc, e_no_inc, color);
1056                 }
1057             }
1058         }
1059
1060         const pixel_buf_t &get_pixel_buf() const
1061         {
1062             return m_pixel_buf;
1063         }
1064         pixel_buf_t &get_pixel_buf()
1065         {
1066             return m_pixel_buf;
1067         }
1068
1069     private:
1070         uint m_width;
1071         uint m_height;
1072         uint m_pitch;
1073         uint m_total;
1074         uint m_comp_flags;
1075
1076         color_type *m_pPixels;
1077
1078         pixel_buf_t m_pixel_buf;
1079
1080         void rasterize_line(int xs, int ys, int xe, int ye, int pred, int inc_dec, int e, int e_inc, int e_no_inc, const color_type &color)
1081         {
1082             int start, end, var;
1083
1084             if (pred)
1085             {
1086                 start = ys;
1087                 end = ye;
1088                 var = xs;
1089                 for (int i = start; i <= end; i++)
1090                 {
1091                     set_pixel_clipped(var, i, color);
1092                     if (e < 0)
1093                         e += e_no_inc;
1094                     else
1095                     {
1096                         var += inc_dec;
1097                         e += e_inc;
1098                     }
1099                 }
1100             }
1101             else
1102             {
1103                 start = xs;
1104                 end = xe;
1105                 var = ys;
1106                 for (int i = start; i <= end; i++)
1107                 {
1108                     set_pixel_clipped(i, var, color);
1109                     if (e < 0)
1110                         e += e_no_inc;
1111                     else
1112                     {
1113                         var += inc_dec;
1114                         e += e_inc;
1115                     }
1116                 }
1117             }
1118         }
1119     };
1120
1121     typedef image<color_quad_u8> image_u8;
1122     typedef image<color_quad_i16> image_i16;
1123     typedef image<color_quad_u16> image_u16;
1124     typedef image<color_quad_i32> image_i32;
1125     typedef image<color_quad_u32> image_u32;
1126     typedef image<color_quad_f> image_f;
1127
1128     template <typename color_type>
1129     inline void swap(image<color_type> &a, image<color_type> &b)
1130     {
1131         a.swap(b);
1132     }
1133
1134 } // namespace vogl