1 /*
2  * Copyright (C) 2007,2008   Alex Shulgin
3  *
4  * This file is part of png++ the C++ wrapper for libpng.  PNG++ is free
5  * software; the exact copying conditions are as follows:
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice,
11  * this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in the
15  * documentation and/or other materials provided with the distribution.
16  *
17  * 3. The name of the author may not be used to endorse or promote products
18  * derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
23  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
25  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 #ifndef PNGPP_CONVERT_COLOR_SPACE_HPP_INCLUDED
32 #define PNGPP_CONVERT_COLOR_SPACE_HPP_INCLUDED
33 
34 #include "error.hpp"
35 #include "rgb_pixel.hpp"
36 #include "rgba_pixel.hpp"
37 #include "gray_pixel.hpp"
38 #include "ga_pixel.hpp"
39 #include "index_pixel.hpp"
40 #include "reader.hpp"
41 #include "writer.hpp"
42 
43 namespace png
44 {
45 
46     namespace detail
47     {
48 
49         /**
50          * \brief IO transformation class template.  Converts %image %color
51          * space.
52          */
53         template< typename pixel >
54         struct convert_color_space_impl
55         {
56             typedef pixel_traits< pixel > traits;
57             typedef typename traits::component_type component_type;
58             typedef basic_alpha_pixel_traits< component_type > alpha_traits;
59 
60             template< class reader >
operator ()png::detail::convert_color_space_impl61             void operator()(reader& io) const
62             {
63                 handle_16(io);
64                 handle_alpha(io, alpha_traits::get_alpha_filler());
65                 handle_palette(io);
66                 handle_rgb(io);
67                 handle_gray(io);
68 
69                 io.set_color_type(traits::get_color_type());
70                 io.set_bit_depth(traits::get_bit_depth());
71             }
72 
73         protected:
expand_8_to_16png::detail::convert_color_space_impl74             static void expand_8_to_16(png_struct*, png_row_info* row_info,
75                                        byte* row)
76             {
77 #ifdef DEBUG_EXPAND_8_16
78                 printf("row: width=%d, bytes=%d, channels=%d\n",
79                        row_info->width, row_info->rowbytes, row_info->channels);
80                 printf("<= ");
81                 dump_row(row, row_info->rowbytes);
82 #endif
83                 for (uint_32 i = row_info->rowbytes; i-- > 0; )
84                 {
85                     row[2*i + 1] = row[i];
86                     row[2*i + 0] = 0;
87                 }
88 #ifdef DEBUG_EXPAND_8_16
89                 printf("=> ");
90                 dump_row(row, 2*row_info->rowbytes);
91 #endif
92             }
93 
94 #ifdef DEBUG_EXPAND_8_16
dump_rowpng::detail::convert_color_space_impl95             static void dump_row(byte const* row, uint_32 width)
96             {
97                 printf("{");
98                 for (uint_32 i = 0; i < width; ++i)
99                 {
100                     printf(" %02x,", row[i]);
101                 }
102                 printf(" }\n");
103             }
104 #endif
105 
106             template< class reader >
handle_16png::detail::convert_color_space_impl107             static void handle_16(reader& io)
108             {
109                 if (io.get_bit_depth() == 16 && traits::get_bit_depth() == 8)
110                 {
111 #ifdef PNG_READ_16_TO_8_SUPPORTED
112                     io.set_strip_16();
113 #else
114                     throw error("expected 8-bit data but found 16-bit; recompile with PNG_READ_16_TO_8_SUPPORTED");
115 #endif
116                 }
117                 if (io.get_bit_depth() != 16 && traits::get_bit_depth() == 16)
118                 {
119 #ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
120                     io.set_read_user_transform(expand_8_to_16);
121                     io.set_user_transform_info(NULL, 16,
122                                                traits::get_channels());
123 #else
124                     throw error("expected 16-bit data but found 8-bit; recompile with PNG_READ_USER_TRANSFORM_SUPPORTED");
125 #endif
126                 }
127             }
128 
129             template< class reader >
handle_alphapng::detail::convert_color_space_impl130             static void handle_alpha(reader& io, uint_32 filler)
131             {
132                 bool src_alpha = (io.get_color_type() & color_mask_alpha);
133                 bool src_tRNS = io.has_chunk(chunk_tRNS);
134                 bool dst_alpha = traits::get_color_type() & color_mask_alpha;
135                 if ((src_alpha || src_tRNS) && !dst_alpha)
136                 {
137 #ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
138                     io.set_strip_alpha();
139 #else
140                     throw error("alpha channel unexpected; recompile with PNG_READ_STRIP_ALPHA_SUPPORTED");
141 #endif
142                 }
143                 if (!src_alpha && dst_alpha)
144                 {
145 #if defined(PNG_tRNS_SUPPORTED) && defined(PNG_READ_EXPAND_SUPPORTED)
146                     if (src_tRNS)
147                     {
148                         io.set_tRNS_to_alpha();
149                         return;
150                     }
151 #endif
152 #if defined(PNG_READ_FILLER_SUPPORTED) && !defined(PNG_1_0_X)
153                     io.set_add_alpha(filler, filler_after);
154 #else
155                     throw error("expected alpha channel but none found; recompile with PNG_READ_FILLER_SUPPORTED and be sure to use libpng > 1.0.x");
156 #endif
157                 }
158             }
159 
160             template< class reader >
handle_palettepng::detail::convert_color_space_impl161             static void handle_palette(reader& io)
162             {
163                 bool src_palette =
164                     io.get_color_type() == color_type_palette;
165                 bool dst_palette =
166                     traits::get_color_type() == color_type_palette;
167                 if (src_palette && !dst_palette)
168                 {
169 #ifdef PNG_READ_EXPAND_SUPPORTED
170                     io.set_palette_to_rgb();
171                     io.get_info().drop_palette();
172 #else
173                     throw error("indexed colors unexpected; recompile with PNG_READ_EXPAND_SUPPORTED");
174 #endif
175                 }
176                 else if (!src_palette && dst_palette)
177                 {
178                     throw error("conversion to indexed colors is unsupported (yet)");
179                 }
180                 else if (src_palette && dst_palette
181                          && io.get_bit_depth() != traits::get_bit_depth())
182                 {
183                     if (traits::get_bit_depth() == 8)
184                     {
185 #ifdef PNG_READ_PACK_SUPPORTED
186                         io.set_packing();
187 #endif
188                     }
189                     else
190                     {
191                         throw error("cannot convert to indexed colors with bit-depth < 8");
192                     }
193                 }
194             }
195 
196             template< class reader >
handle_rgbpng::detail::convert_color_space_impl197             static void handle_rgb(reader& io)
198             {
199                 bool src_rgb =
200                     io.get_color_type() & (color_mask_rgb | color_mask_palette);
201                 bool dst_rgb = traits::get_color_type() & color_mask_rgb;
202                 if (src_rgb && !dst_rgb)
203                 {
204 #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
205                     io.set_rgb_to_gray(/*rgb_to_gray_error*/);
206 #else
207                     throw error("grayscale data expected; recompile with PNG_READ_RGB_TO_GRAY_SUPPORTED");
208 #endif
209                 }
210                 if (!src_rgb && dst_rgb)
211                 {
212 #ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
213                     io.set_gray_to_rgb();
214 #else
215                     throw error("expected RGB data; recompile with PNG_READ_GRAY_TO_RGB_SUPPORTED");
216 #endif
217                 }
218             }
219 
220             template< class reader >
handle_graypng::detail::convert_color_space_impl221             static void handle_gray(reader& io)
222             {
223                 if ((io.get_color_type() & ~color_mask_alpha)
224                     == color_type_gray)
225                 {
226                     if (io.get_bit_depth() < 8 && traits::get_bit_depth() >= 8)
227                     {
228 #ifdef PNG_READ_EXPAND_SUPPORTED
229                         io.set_gray_1_2_4_to_8();
230 #else
231                         throw error("convert_color_space: expected 8-bit data; recompile with PNG_READ_EXPAND_SUPPORTED");
232 #endif
233                     }
234                 }
235             }
236         };
237 
238     } // namespace detal
239 
240     /**
241      * \brief IO transformation class template.  Converts %image %color
242      * space.
243      *
244      * This IO transformation class template is used to convert %color
245      * space of the source %image to the %color space of the target
246      * %image.  An error with human-readable description is thrown
247      * when the %color space could not be converted.  Often, this
248      * means that you have to recompile libpng with some more
249      * conversion options turned on.
250      *
251      * Not implemented--see specializations.
252      *
253      * \see image, image::read
254      */
255     template< typename pixel >
256     struct convert_color_space
257     {
258     };
259 
260     /**
261      * \brief Converts %image %color space.  A specialization for
262      * rgb_pixel type.
263      */
264     template<>
265     struct convert_color_space< rgb_pixel >
266         : detail::convert_color_space_impl< rgb_pixel >
267     {
268     };
269 
270     /**
271      * \brief Converts %image %color space.  A specialization for
272      * rgb_pixel_16 type.
273      */
274     template<>
275     struct convert_color_space< rgb_pixel_16 >
276         : detail::convert_color_space_impl< rgb_pixel_16 >
277     {
278     };
279 
280     /**
281      * \brief Converts %image %color space.  A specialization for
282      * rgba_pixel type.
283      */
284     template<>
285     struct convert_color_space< rgba_pixel >
286         : detail::convert_color_space_impl< rgba_pixel >
287     {
288     };
289 
290     /**
291      * \brief Converts %image %color space.  A specialization for
292      * rgba_pixel_16 type.
293      */
294     template<>
295     struct convert_color_space< rgba_pixel_16 >
296         : detail::convert_color_space_impl< rgba_pixel_16 >
297     {
298     };
299 
300     /**
301      * \brief Converts %image %color space.  A specialization for
302      * gray_pixel type.
303      */
304     template<>
305     struct convert_color_space< gray_pixel >
306         : detail::convert_color_space_impl< gray_pixel >
307     {
308     };
309 
310     /**
311      * \brief Converts %image %color space.  A specialization for
312      * gray_pixel_16 type.
313      */
314     template<>
315     struct convert_color_space< gray_pixel_16 >
316         : detail::convert_color_space_impl< gray_pixel_16 >
317     {
318     };
319 
320     /**
321      * \brief Converts %image %color space.  A specialization for
322      * ga_pixel type.
323      */
324     template<>
325     struct convert_color_space< ga_pixel >
326         : detail::convert_color_space_impl< ga_pixel >
327     {
328     };
329 
330     /**
331      * \brief Converts %image %color space.  A specialization for
332      * ga_pixel_16 type.
333      */
334     template<>
335     struct convert_color_space< ga_pixel_16 >
336         : detail::convert_color_space_impl< ga_pixel_16 >
337     {
338     };
339 
340     /**
341      * \brief Converts %image %color space.  A specialization for
342      * index_pixel type.
343      */
344     template<>
345     struct convert_color_space< index_pixel >
346         : detail::convert_color_space_impl< index_pixel >
347     {
348     };
349 
350 } // namespace png
351 
352 #endif // PNGPP_CONVERT_COLOR_SPACE_HPP_INCLUDED
353