Chris@16
|
1 /*
|
Chris@16
|
2 Copyright 2005-2007 Adobe Systems Incorporated
|
Chris@16
|
3
|
Chris@16
|
4 Use, modification and distribution are subject to the Boost Software License,
|
Chris@16
|
5 Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
Chris@16
|
6 http://www.boost.org/LICENSE_1_0.txt).
|
Chris@16
|
7
|
Chris@16
|
8 See http://opensource.adobe.com/gil for most recent version including documentation.
|
Chris@16
|
9 */
|
Chris@16
|
10
|
Chris@16
|
11 /*************************************************************************************************/
|
Chris@16
|
12
|
Chris@16
|
13 #ifndef GIL_JPEG_IO_PRIVATE_H
|
Chris@16
|
14 #define GIL_JPEG_IO_PRIVATE_H
|
Chris@16
|
15
|
Chris@16
|
16 /// \file
|
Chris@16
|
17 /// \brief Internal support for reading and writing JPEG files
|
Chris@16
|
18 /// \author Hailin Jin and Lubomir Bourdev \n
|
Chris@16
|
19 /// Adobe Systems Incorporated
|
Chris@16
|
20 /// \date 2005-2007 \n Last updated September 24, 2006
|
Chris@16
|
21
|
Chris@16
|
22 #include <stdio.h>
|
Chris@16
|
23 #include <boost/static_assert.hpp>
|
Chris@16
|
24 #include <vector>
|
Chris@16
|
25 #include "../../gil_all.hpp"
|
Chris@16
|
26 #include "io_error.hpp"
|
Chris@16
|
27 #include <jpeglib.h>
|
Chris@16
|
28
|
Chris@16
|
29 namespace boost { namespace gil {
|
Chris@16
|
30
|
Chris@16
|
31 namespace detail {
|
Chris@16
|
32
|
Chris@16
|
33 // lbourdev: What is the advantage of having channel and colorspace together? Are there cases where they are interrelated?
|
Chris@16
|
34
|
Chris@16
|
35 template <typename Channel,typename ColorSpace>
|
Chris@16
|
36 struct jpeg_read_support_private {
|
Chris@16
|
37 BOOST_STATIC_CONSTANT(bool,is_supported=false);
|
Chris@16
|
38 BOOST_STATIC_CONSTANT(J_COLOR_SPACE,color_type=JCS_UNKNOWN);
|
Chris@16
|
39 };
|
Chris@16
|
40 template <>
|
Chris@16
|
41 struct jpeg_read_support_private<bits8,gray_t> {
|
Chris@16
|
42 BOOST_STATIC_ASSERT(BITS_IN_JSAMPLE==8);
|
Chris@16
|
43 BOOST_STATIC_CONSTANT(bool,is_supported=true);
|
Chris@16
|
44 BOOST_STATIC_CONSTANT(J_COLOR_SPACE,color_type=JCS_GRAYSCALE);
|
Chris@16
|
45 };
|
Chris@16
|
46 template <>
|
Chris@16
|
47 struct jpeg_read_support_private<bits8,rgb_t> {
|
Chris@16
|
48 BOOST_STATIC_ASSERT(BITS_IN_JSAMPLE==8);
|
Chris@16
|
49 BOOST_STATIC_CONSTANT(bool,is_supported=true);
|
Chris@16
|
50 BOOST_STATIC_CONSTANT(J_COLOR_SPACE,color_type=JCS_RGB);
|
Chris@16
|
51 };
|
Chris@16
|
52 template <>
|
Chris@16
|
53 struct jpeg_read_support_private<bits8,cmyk_t> {
|
Chris@16
|
54 BOOST_STATIC_ASSERT(BITS_IN_JSAMPLE==8);
|
Chris@16
|
55 BOOST_STATIC_CONSTANT(bool,is_supported=true);
|
Chris@16
|
56 BOOST_STATIC_CONSTANT(J_COLOR_SPACE,color_type=JCS_CMYK);
|
Chris@16
|
57 };
|
Chris@16
|
58 template <typename Channel,typename ColorSpace>
|
Chris@16
|
59 struct jpeg_write_support_private {
|
Chris@16
|
60 BOOST_STATIC_CONSTANT(bool,is_supported=false);
|
Chris@16
|
61 BOOST_STATIC_CONSTANT(J_COLOR_SPACE,color_type=JCS_UNKNOWN);
|
Chris@16
|
62 };
|
Chris@16
|
63 template <>
|
Chris@16
|
64 struct jpeg_write_support_private<bits8,gray_t> {
|
Chris@16
|
65 BOOST_STATIC_ASSERT(BITS_IN_JSAMPLE==8);
|
Chris@16
|
66 BOOST_STATIC_CONSTANT(bool,is_supported=true);
|
Chris@16
|
67 BOOST_STATIC_CONSTANT(J_COLOR_SPACE,color_type=JCS_GRAYSCALE);
|
Chris@16
|
68 };
|
Chris@16
|
69 template <>
|
Chris@16
|
70 struct jpeg_write_support_private<bits8,rgb_t> {
|
Chris@16
|
71 BOOST_STATIC_ASSERT(BITS_IN_JSAMPLE==8);
|
Chris@16
|
72 BOOST_STATIC_CONSTANT(bool,is_supported=true);
|
Chris@16
|
73 BOOST_STATIC_CONSTANT(J_COLOR_SPACE,color_type=JCS_RGB);
|
Chris@16
|
74 };
|
Chris@16
|
75 template <>
|
Chris@16
|
76 struct jpeg_write_support_private<bits8,cmyk_t> {
|
Chris@16
|
77 BOOST_STATIC_ASSERT(BITS_IN_JSAMPLE==8);
|
Chris@16
|
78 BOOST_STATIC_CONSTANT(bool,is_supported=true);
|
Chris@16
|
79 BOOST_STATIC_CONSTANT(J_COLOR_SPACE,color_type=JCS_CMYK);
|
Chris@16
|
80 };
|
Chris@16
|
81
|
Chris@16
|
82
|
Chris@16
|
83 class jpeg_reader : public file_mgr {
|
Chris@16
|
84 protected:
|
Chris@16
|
85 jpeg_decompress_struct _cinfo;
|
Chris@16
|
86 jpeg_error_mgr _jerr;
|
Chris@16
|
87
|
Chris@16
|
88 void init() {
|
Chris@16
|
89 _cinfo.err=jpeg_std_error(&_jerr);
|
Chris@16
|
90 jpeg_create_decompress(&_cinfo);
|
Chris@16
|
91 jpeg_stdio_src(&_cinfo,_fp.get());
|
Chris@16
|
92 jpeg_read_header(&_cinfo,TRUE);
|
Chris@16
|
93 }
|
Chris@16
|
94 public:
|
Chris@16
|
95 jpeg_reader(FILE* file) : file_mgr(file) { init(); }
|
Chris@16
|
96 jpeg_reader(const char* filename) : file_mgr(filename, "rb") { init(); }
|
Chris@16
|
97
|
Chris@16
|
98 ~jpeg_reader() { jpeg_destroy_decompress(&_cinfo); }
|
Chris@16
|
99
|
Chris@16
|
100 template <typename View>
|
Chris@16
|
101 void apply(const View& view) {
|
Chris@16
|
102 jpeg_start_decompress(&_cinfo); // lbourdev: Can this return an error? You need to check and throw. Check all other library methods that can return an error state...
|
Chris@16
|
103 io_error_if(_cinfo.data_precision!=8,"jpeg_reader::apply(): this image file is not supported");
|
Chris@16
|
104 io_error_if(_cinfo.out_color_space!=jpeg_read_support_private<typename channel_type<View>::type,
|
Chris@16
|
105 typename color_space_type<View>::type>::color_type,
|
Chris@16
|
106 "jpeg_reader::apply(): input view type does not match the image file");
|
Chris@16
|
107 io_error_if(view.dimensions() != get_dimensions(), "jpeg_reader::apply(): input view dimensions do not match the image file");
|
Chris@16
|
108 std::vector<pixel<bits8,layout<typename color_space_type<View>::type> > > row(view.width());
|
Chris@16
|
109 JSAMPLE* row_address=(JSAMPLE*)&row.front();
|
Chris@16
|
110 for(int y=0;y<view.height();++y) {
|
Chris@16
|
111 io_error_if(jpeg_read_scanlines(&_cinfo,(JSAMPARRAY)&row_address,1)!=1,
|
Chris@16
|
112 "jpeg_reader::apply(): fail to read JPEG file");
|
Chris@16
|
113 std::copy(row.begin(),row.end(),view.row_begin(y));
|
Chris@16
|
114 }
|
Chris@16
|
115 jpeg_finish_decompress(&_cinfo);
|
Chris@16
|
116 }
|
Chris@16
|
117
|
Chris@16
|
118 template <typename Image>
|
Chris@16
|
119 void read_image(Image& im) {
|
Chris@16
|
120 im.recreate(get_dimensions());
|
Chris@16
|
121 apply(view(im));
|
Chris@16
|
122 }
|
Chris@16
|
123
|
Chris@16
|
124 point2<std::ptrdiff_t> get_dimensions() const {
|
Chris@16
|
125 return point2<std::ptrdiff_t>(_cinfo.image_width,_cinfo.image_height);
|
Chris@16
|
126 }
|
Chris@16
|
127 };
|
Chris@16
|
128
|
Chris@16
|
129 // This code will be simplified...
|
Chris@16
|
130 template <typename CC>
|
Chris@16
|
131 class jpeg_reader_color_convert : public jpeg_reader {
|
Chris@16
|
132 private:
|
Chris@16
|
133 CC _cc;
|
Chris@16
|
134 public:
|
Chris@16
|
135 jpeg_reader_color_convert(FILE* file,CC cc_in) : jpeg_reader(file),_cc(cc_in) {}
|
Chris@16
|
136 jpeg_reader_color_convert(FILE* file) : jpeg_reader(file) {}
|
Chris@16
|
137 jpeg_reader_color_convert(const char* filename,CC cc_in) : jpeg_reader(filename),_cc(cc_in) {}
|
Chris@16
|
138 jpeg_reader_color_convert(const char* filename) : jpeg_reader(filename) {}
|
Chris@16
|
139 template <typename View>
|
Chris@16
|
140 void apply(const View& view) {
|
Chris@16
|
141 jpeg_start_decompress(&_cinfo); // lbourdev: Can this return an error? You need to check and throw. Check all other library methods that can return an error state...
|
Chris@16
|
142 io_error_if(_cinfo.data_precision!=8,"jpeg_reader_color_covert::apply(): this image file is not supported");
|
Chris@16
|
143 io_error_if(view.dimensions() != get_dimensions(), "jpeg_reader_color_covert::apply(): input view dimensions don't match the image file");
|
Chris@16
|
144 switch (_cinfo.out_color_space) {
|
Chris@16
|
145 case JCS_GRAYSCALE: {
|
Chris@16
|
146 std::vector<gray8_pixel_t> row(view.width());
|
Chris@16
|
147 JSAMPLE* row_address=(JSAMPLE*)&row.front();
|
Chris@16
|
148 for(int y=0;y<view.height();++y) {
|
Chris@16
|
149 io_error_if(jpeg_read_scanlines(&_cinfo,(JSAMPARRAY)&row_address,1)!=1,
|
Chris@16
|
150 "jpeg_reader_color_covert::apply(): fail to read JPEG file");
|
Chris@16
|
151 std::transform(row.begin(),row.end(),view.row_begin(y),color_convert_deref_fn<gray8_ref_t, typename View::value_type,CC>(_cc));
|
Chris@16
|
152 }
|
Chris@16
|
153 break;
|
Chris@16
|
154 }
|
Chris@16
|
155 case JCS_RGB: {
|
Chris@16
|
156 std::vector<rgb8_pixel_t> row(view.width());
|
Chris@16
|
157 JSAMPLE* row_address=(JSAMPLE*)&row.front();
|
Chris@16
|
158 for(int y=0;y<view.height();++y) {
|
Chris@16
|
159 io_error_if(jpeg_read_scanlines(&_cinfo,(JSAMPARRAY)&row_address,1)!=1,
|
Chris@16
|
160 "jpeg_reader_color_covert::apply(): fail to read JPEG file");
|
Chris@16
|
161 std::transform(row.begin(),row.end(),view.row_begin(y),color_convert_deref_fn<rgb8_ref_t, typename View::value_type,CC>(_cc));
|
Chris@16
|
162 }
|
Chris@16
|
163 break;
|
Chris@16
|
164 }
|
Chris@16
|
165 case JCS_CMYK: {
|
Chris@16
|
166 std::vector<cmyk8_pixel_t> row(view.width());
|
Chris@16
|
167 JSAMPLE* row_address=(JSAMPLE*)&row.front();
|
Chris@16
|
168 for(int y=0;y<view.height();++y) {
|
Chris@16
|
169 io_error_if(jpeg_read_scanlines(&_cinfo,(JSAMPARRAY)&row_address,1)!=1,
|
Chris@16
|
170 "jpeg_reader_color_covert::apply(): fail to read JPEG file");
|
Chris@16
|
171 std::transform(row.begin(),row.end(),view.row_begin(y),color_convert_deref_fn<cmyk8_ref_t, typename View::value_type,CC>(_cc));
|
Chris@16
|
172 }
|
Chris@16
|
173 break;
|
Chris@16
|
174 }
|
Chris@16
|
175 default:
|
Chris@16
|
176 io_error("jpeg_reader_color_covert::apply(): unknown color type");
|
Chris@16
|
177 }
|
Chris@16
|
178 jpeg_finish_decompress(&_cinfo);
|
Chris@16
|
179 }
|
Chris@16
|
180 template <typename Image>
|
Chris@16
|
181 void read_image(Image& im) {
|
Chris@16
|
182 im.recreate(get_dimensions());
|
Chris@16
|
183 apply(view(im));
|
Chris@16
|
184 }
|
Chris@16
|
185 };
|
Chris@16
|
186
|
Chris@16
|
187 class jpeg_writer : public file_mgr {
|
Chris@16
|
188 jpeg_compress_struct _cinfo;
|
Chris@16
|
189 jpeg_error_mgr _jerr;
|
Chris@16
|
190
|
Chris@16
|
191 void init() {
|
Chris@16
|
192 _cinfo.err=jpeg_std_error(&_jerr);
|
Chris@16
|
193 jpeg_create_compress(&_cinfo);
|
Chris@16
|
194 jpeg_stdio_dest(&_cinfo,_fp.get());
|
Chris@16
|
195 }
|
Chris@16
|
196 public:
|
Chris@16
|
197 jpeg_writer(FILE* file) : file_mgr(file) { init(); }
|
Chris@16
|
198 jpeg_writer(const char* filename) : file_mgr(filename, "wb") { init(); }
|
Chris@16
|
199 ~jpeg_writer() { jpeg_destroy_compress(&_cinfo); }
|
Chris@16
|
200
|
Chris@16
|
201 template <typename View>
|
Chris@16
|
202 void apply(const View& view,int quality=100) {
|
Chris@16
|
203 _cinfo.image_width = (JDIMENSION)view.width();
|
Chris@16
|
204 _cinfo.image_height = (JDIMENSION)view.height();
|
Chris@16
|
205 _cinfo.input_components=num_channels<View>::value;
|
Chris@16
|
206 _cinfo.in_color_space = jpeg_write_support_private<typename channel_type<View>::type,
|
Chris@16
|
207 typename color_space_type<View>::type>::color_type;
|
Chris@16
|
208 jpeg_set_defaults(&_cinfo);
|
Chris@16
|
209 jpeg_set_quality(&_cinfo, quality, TRUE);
|
Chris@16
|
210 jpeg_start_compress(&_cinfo, TRUE);
|
Chris@16
|
211 std::vector<pixel<bits8,layout<typename color_space_type<View>::type> > > row(view.width());
|
Chris@16
|
212 JSAMPLE* row_address=(JSAMPLE*)&row.front();
|
Chris@16
|
213 for (int y=0;y<view.height(); ++y) {
|
Chris@16
|
214 std::copy(view.row_begin(y),view.row_end(y),row.begin());
|
Chris@16
|
215 io_error_if(jpeg_write_scanlines(&_cinfo,(JSAMPARRAY)&row_address,1) != 1,
|
Chris@16
|
216 "jpeg_writer::apply(): fail to write file");
|
Chris@16
|
217 }
|
Chris@16
|
218 jpeg_finish_compress(&_cinfo);
|
Chris@16
|
219 }
|
Chris@16
|
220 };
|
Chris@16
|
221
|
Chris@16
|
222 } // namespace detail
|
Chris@16
|
223
|
Chris@16
|
224 } } // namespace boost::gil
|
Chris@16
|
225
|
Chris@16
|
226 #endif
|