1 //
2 // Copyright 2005-2007 Adobe Systems Incorporated
3 //
4 // Distributed under the Boost Software License, Version 1.0
5 // See accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt
7 //
8 #include <boost/gil.hpp>
9
10 #include <boost/core/ignore_unused.hpp>
11 #include <boost/mp11.hpp>
12
13 #include <exception>
14 #include <iostream>
15 #include <iterator>
16 #include <type_traits>
17
18 using namespace boost::gil;
19 using std::swap;
20 using namespace boost;
21
22 void error_if(bool condition);
23
24 struct increment {
operator ()increment25 template <typename Incrementable> void operator()(Incrementable& x) const { ++x; }
26 };
27
28 struct prev
29 {
30 template <typename Subtractable>
operator ()prev31 auto operator()(const Subtractable& x) const -> typename channel_traits<Subtractable>::value_type
32 {
33 using return_type = typename channel_traits<Subtractable>::value_type;
34 return static_cast<return_type>(x - 1);
35 }
36 };
37
operator ()set_to_one38 struct set_to_one{ int operator()() const { return 1; } };
39
40 // Construct with two pixel types. They must be compatible and the second must be mutable
41 template <typename C1, typename C2>
42 struct do_basic_test : public C1, public C2
43 {
44 using pixel1_t = typename C1::type;
45 using pixel2_t = typename C2::type;
46 using pixel1_value_t = typename C1::pixel_t::value_type;
47 using pixel2_value_t = typename C2::pixel_t::value_type;
48 using pixel_value_t = pixel1_value_t;
49
do_basic_testdo_basic_test50 do_basic_test(const pixel_value_t& v) : C1(v), C2(v) {}
51
test_alldo_basic_test52 void test_all() {
53 test_heterogeneous();
54
55 // test homogeneous algorithms - fill, max, min
56 static const int num_chan = num_channels<typename C2::pixel_t>::value;
57 static_fill(C2::_pixel, gil::at_c<0>(C1::_pixel)+1);
58 error_if(gil::at_c<0>(C2::_pixel) != gil::at_c<num_chan-1>(C2::_pixel));
59
60 C2::_pixel = C1::_pixel;
61 error_if(static_max(C2::_pixel) != static_max(C1::_pixel));
62 error_if(static_min(C2::_pixel) != static_min(C1::_pixel));
63 error_if(static_max(C2::_pixel) < static_min(C2::_pixel));
64
65 // test operator[]
66 C2::_pixel[0] = C1::_pixel[0]+1;
67 error_if(C2::_pixel[0] != C1::_pixel[0]+1);
68 }
69
test_heterogeneousdo_basic_test70 void test_heterogeneous() {
71 // Both must be pixel types (not necessarily pixel values). The second must be mutable. They must be compatible
72 boost::function_requires<PixelConcept<typename C1::pixel_t> >();
73 boost::function_requires<MutablePixelConcept<typename C2::pixel_t> >();
74 boost::function_requires<PixelsCompatibleConcept<typename C1::pixel_t,typename C2::pixel_t> >();
75
76 C2::_pixel = C1::_pixel; // test operator=
77 error_if(C1::_pixel != C2::_pixel); // test operator==
78
79 // construct a pixel value from it
80 pixel1_value_t v1(C1::_pixel);
81 pixel2_value_t v2(C2::_pixel);
82 error_if(v1 != v2);
83
84 // construct from a pixel value
85 pixel1_t c1(v1);
86 pixel2_t c2(v2);
87 error_if(c1 != c2);
88
89 // Invert the first semantic channel.
90 C2::_pixel = C1::_pixel;
91 semantic_at_c<0>(C2::_pixel) = channel_invert(semantic_at_c<0>(C2::_pixel));
92 error_if(C1::_pixel == C2::_pixel); // now they must not be equal
93
94 // test pixel algorithms
95 C2::_pixel = C1::_pixel;
96 static_for_each(C2::_pixel, increment());
97 static_transform(C2::_pixel, C2::_pixel, prev());
98 error_if(C1::_pixel!=C2::_pixel);
99
100 static_generate(C2::_pixel, set_to_one());
101 error_if(gil::at_c<0>(C2::_pixel) != 1);
102
103 // Test swap if both are mutable and if their value type is the same
104 // (We know the second one is mutable)
105 using p1_ref = typename boost::add_reference<typename C1::type>::type;
106 using is_swappable = std::integral_constant
107 <
108 bool,
109 pixel_reference_is_mutable<p1_ref>::value &&
110 std::is_same<pixel1_value_t, pixel2_value_t>::value
111 >;
112 test_swap(is_swappable{});
113 }
114
test_swapdo_basic_test115 void test_swap(std::false_type) {}
test_swapdo_basic_test116 void test_swap(std::true_type) {
117 // test swap
118 static_fill(C1::_pixel, 0);
119 static_fill(C2::_pixel, 1);
120 pixel_value_t pv1(C1::_pixel);
121 pixel_value_t pv2(C2::_pixel);
122 error_if(C2::_pixel == C1::_pixel);
123 swap(C1::_pixel, C2::_pixel);
124 error_if(C1::_pixel != pv2 || C2::_pixel != pv1);
125 }
126 };
127
128 template <typename PixelValue, int Tag=0>
129 class value_core
130 {
131 public:
132 using type = PixelValue;
133 using pixel_t = type;
134 type _pixel;
135
value_core()136 value_core() : _pixel(0) {}
value_core(const type & val)137 value_core(const type& val) : _pixel(val) { // test copy constructor
138 boost::function_requires<PixelValueConcept<pixel_t> >();
139 type p2; // test default constructor
140 boost::ignore_unused(p2);
141 }
142 };
143
144 template <typename PixelRef, int Tag=0>
145 class reference_core : public value_core<typename std::remove_reference<PixelRef>::type::value_type, Tag>
146 {
147 public:
148 using type = PixelRef;
149 using pixel_t = typename std::remove_reference<PixelRef>::type;
150 using parent_t = value_core<typename pixel_t::value_type, Tag>;
151
152 type _pixel;
153
reference_core()154 reference_core() : parent_t(), _pixel(parent_t::_pixel) {}
reference_core(const typename pixel_t::value_type & val)155 reference_core(const typename pixel_t::value_type& val) : parent_t(val), _pixel(parent_t::_pixel) {
156 boost::function_requires<PixelConcept<pixel_t> >();
157 }
158 };
159
160 // Use a subset of pixel models that covers all color spaces, channel depths, reference/value, planar/interleaved, const/mutable
161 // color conversion will be invoked on pairs of them. Having an exhaustive binary check would be too big/expensive.
162 using representative_pixels_t = mp11::mp_list
163 <
164 value_core<gray8_pixel_t>,
165 reference_core<gray16_pixel_t&>,
166 value_core<bgr8_pixel_t>,
167 reference_core<rgb8_planar_ref_t>,
168 value_core<argb32_pixel_t>,
169 reference_core<cmyk32f_pixel_t&>,
170 reference_core<abgr16c_ref_t>, // immutable reference
171 reference_core<rgb32fc_planar_ref_t>
172 >;
173
174 template <typename Pixel1>
175 struct ccv2 {
176 template <typename P1, typename P2>
color_convert_compatibleccv2177 void color_convert_compatible(const P1& p1, P2& p2, std::true_type) {
178 using value_t = typename P1::value_type;
179 p2 = p1;
180 value_t converted;
181 color_convert(p1, converted);
182 error_if(converted != p2);
183 }
184
185 template <typename P1, typename P2>
color_convert_compatibleccv2186 void color_convert_compatible(const P1& p1, P2& p2, std::false_type) {
187 color_convert(p1,p2);
188 }
189
190 template <typename P1, typename P2>
color_convert_implccv2191 void color_convert_impl(const P1& p1, P2& p2) {
192 using is_compatible = typename pixels_are_compatible<P1,P2>::type;
193 color_convert_compatible(p1, p2, is_compatible());
194 }
195
196 template <typename Pixel2>
operator ()ccv2197 void operator()(Pixel2) {
198 // convert from Pixel1 to Pixel2 (or, if Pixel2 is immutable, to its value type)
199 using p2_is_mutable = pixel_reference_is_mutable<typename Pixel2::type>;
200 using pixel_model_t = typename std::remove_reference<typename Pixel2::type>::type;
201 using p2_value_t = typename pixel_model_t::value_type;
202 using pixel2_mutable = mp11::mp_if<p2_is_mutable, Pixel2, value_core<p2_value_t>>;
203
204 Pixel1 p1;
205 pixel2_mutable p2;
206
207 color_convert_impl(p1._pixel, p2._pixel);
208 }
209 };
210
211 struct ccv1 {
212 template <typename Pixel>
operator ()ccv1213 void operator()(Pixel) {
214 mp11::mp_for_each<representative_pixels_t>(ccv2<Pixel>());
215 }
216 };
217
test_color_convert()218 void test_color_convert() {
219 mp11::mp_for_each<representative_pixels_t>(ccv1());
220 }
221
test_packed_pixel()222 void test_packed_pixel()
223 {
224 using rgb565_pixel_t = packed_pixel_type<uint16_t, mp11::mp_list_c<unsigned,5,6,5>, rgb_layout_t>::type;
225 boost::function_requires<PixelValueConcept<rgb565_pixel_t> >();
226 static_assert(sizeof(rgb565_pixel_t) == 2, "");
227
228 // define a bgr556 pixel
229 using bgr556_pixel_t = packed_pixel_type<uint16_t, mp11::mp_list_c<unsigned,5,6,5>, bgr_layout_t>::type;
230 boost::function_requires<PixelValueConcept<bgr556_pixel_t> >();
231
232 // Create a zero packed pixel and a full regular unpacked pixel.
233 rgb565_pixel_t r565;//((uint16_t)0);
234 rgb8_pixel_t rgb_full(255,255,255);
235
236 // Convert all channels of the unpacked pixel to the packed one & ensure the packed one is full
237 get_color(r565,red_t()) = channel_convert<kth_element_type<rgb565_pixel_t, 0>::type>(get_color(rgb_full,red_t()));
238 get_color(r565,green_t()) = channel_convert<kth_element_type<rgb565_pixel_t, 1>::type>(get_color(rgb_full,green_t()));
239 get_color(r565,blue_t()) = channel_convert<kth_element_type<rgb565_pixel_t, 2>::type>(get_color(rgb_full,blue_t()));
240 error_if(r565 != rgb565_pixel_t((uint16_t)65535));
241
242 // rgb565 is compatible with bgr556. Test interoperability
243 boost::function_requires<PixelsCompatibleConcept<rgb565_pixel_t,bgr556_pixel_t> >();
244
245 do_basic_test<value_core<rgb565_pixel_t,0>, value_core<bgr556_pixel_t,1> >(r565).test_heterogeneous();
246
247 color_convert(r565,rgb_full);
248 color_convert(rgb_full,r565);
249
250 // Test bit-aligned pixel reference
251 using bgr121_ref_t = const bit_aligned_pixel_reference<std::uint8_t, mp11::mp_list_c<int,1,2,1>, bgr_layout_t, true>;
252 using rgb121_ref_t = const bit_aligned_pixel_reference<std::uint8_t, mp11::mp_list_c<int,1,2,1>, rgb_layout_t, true>;
253 using rgb121_pixel_t = rgb121_ref_t::value_type;
254 rgb121_pixel_t p121;
255 do_basic_test<reference_core<bgr121_ref_t,0>, reference_core<rgb121_ref_t,1> >(p121).test_heterogeneous();
256 do_basic_test<value_core<rgb121_pixel_t,0>, reference_core<rgb121_ref_t,1> >(p121).test_heterogeneous();
257
258 static_assert(pixel_reference_is_proxy<rgb8_planar_ref_t>::value, "");
259 static_assert(pixel_reference_is_proxy<bgr121_ref_t>::value, "");
260
261 static_assert(!pixel_reference_is_proxy<rgb8_pixel_t>::value, "");
262 static_assert(!pixel_reference_is_proxy<rgb8_pixel_t&>::value, "");
263 static_assert(!pixel_reference_is_proxy<rgb8_pixel_t const&>::value, "");
264
265 static_assert(pixel_reference_is_mutable<rgb8_pixel_t&>::value, "");
266 static_assert(!pixel_reference_is_mutable<rgb8_pixel_t const&>::value, "");
267
268 static_assert(pixel_reference_is_mutable<rgb8_planar_ref_t>::value, "");
269 static_assert(pixel_reference_is_mutable<rgb8_planar_ref_t const&>::value, "");
270
271 static_assert(!pixel_reference_is_mutable<rgb8c_planar_ref_t>::value, "");
272 static_assert(!pixel_reference_is_mutable<rgb8c_planar_ref_t const&>::value, "");
273
274 static_assert(pixel_reference_is_mutable<bgr121_ref_t>::value, "");
275 static_assert(!pixel_reference_is_mutable<bgr121_ref_t::const_reference>::value, "");
276 }
277
test_pixel()278 void test_pixel() {
279 test_packed_pixel();
280 rgb8_pixel_t rgb8(1,2,3);
281
282 do_basic_test<value_core<rgb8_pixel_t,0>, reference_core<rgb8_pixel_t&,1> >(rgb8).test_all();
283 do_basic_test<value_core<bgr8_pixel_t,0>, reference_core<rgb8_planar_ref_t,1> >(rgb8).test_all();
284 do_basic_test<reference_core<rgb8_planar_ref_t,0>, reference_core<bgr8_pixel_t&,1> >(rgb8).test_all();
285 do_basic_test<reference_core<const rgb8_pixel_t&,0>, reference_core<rgb8_pixel_t&,1> >(rgb8).test_all();
286
287 test_color_convert();
288
289 // Semantic vs physical channel accessors. Named channel accessors
290 bgr8_pixel_t bgr8(rgb8);
291 error_if(bgr8[0] == rgb8[0]);
292 error_if(dynamic_at_c(bgr8,0) == dynamic_at_c(rgb8,0));
293 error_if(gil::at_c<0>(bgr8) == gil::at_c<0>(rgb8));
294 error_if(semantic_at_c<0>(bgr8) != semantic_at_c<0>(rgb8));
295 error_if(get_color(bgr8,blue_t()) != get_color(rgb8,blue_t()));
296
297 // Assigning a grayscale channel to a pixel
298 gray16_pixel_t g16(34);
299 g16 = 8;
300 uint16_t g = get_color(g16,gray_color_t());
301 error_if(g != 8);
302 error_if(g16 != 8);
303 }
304
305
main()306 int main()
307 {
308 try
309 {
310 test_pixel();
311 return EXIT_SUCCESS;
312 }
313 catch (std::exception const& e)
314 {
315 std::cerr << e.what() << std::endl;
316 return EXIT_FAILURE;
317 }
318 catch (...)
319 {
320 return EXIT_FAILURE;
321 }
322 }
323