1 /*
2     Copyright 2005-2007 Adobe Systems Incorporated
3 
4     Use, modification and distribution are subject to the Boost Software License,
5     Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
6     http://www.boost.org/LICENSE_1_0.txt).
7 
8     See http://opensource.adobe.com/gil for most recent version including documentation.
9 */
10 // pixel.cpp : Tests GIL pixels.
11 //
12 
13 #include <iterator>
14 #include <iostream>
15 #include <boost/type_traits.hpp>
16 #include <boost/mpl/vector.hpp>
17 #include <boost/mpl/size.hpp>
18 #include <boost/mpl/at.hpp>
19 #include <boost/mpl/size.hpp>
20 #include <boost/gil/planar_pixel_reference.hpp>
21 #include <boost/gil/packed_pixel.hpp>
22 #include <boost/gil/rgb.hpp>
23 #include <boost/gil/gray.hpp>
24 #include <boost/gil/rgba.hpp>
25 #include <boost/gil/cmyk.hpp>
26 #include <boost/gil/pixel.hpp>
27 #include <boost/gil/typedefs.hpp>
28 #include <boost/gil/channel_algorithm.hpp>
29 #include <boost/gil/color_convert.hpp>
30 #include <boost/gil/gil_concept.hpp>
31 #include <boost/gil/metafunctions.hpp>
32 #include <boost/gil/bit_aligned_pixel_reference.hpp>
33 
34 // Testing pixel references and values, pixel operations, color conversion
35 
36 using namespace boost::gil;
37 using std::swap;
38 using namespace boost;
39 
40 void error_if(bool condition);
41 
42 struct increment {
operator ()increment43     template <typename Incrementable> void operator()(Incrementable& x) const { ++x; }
44 };
45 struct prev {
46     template <typename Subtractable>
operator ()prev47     typename channel_traits<Subtractable>::value_type operator()(const Subtractable& x) const { return x-1; }
48 };
operator ()set_to_one49 struct set_to_one{ int operator()() const { return 1; } };
50 
51 // Construct with two pixel types. They must be compatible and the second must be mutable
52 template <typename C1, typename C2>
53 struct do_basic_test : public C1, public C2 {
54     typedef typename C1::type               pixel1_t;
55     typedef typename C2::type               pixel2_t;
56     typedef typename C1::pixel_t::value_type pixel1_value_t;
57     typedef typename C2::pixel_t::value_type pixel2_value_t;
58     typedef pixel1_value_t pixel_value_t;
59 
do_basic_testdo_basic_test60     do_basic_test(const pixel_value_t& v) : C1(v), C2(v) {}
61 
test_alldo_basic_test62     void test_all() {
63         test_heterogeneous();
64 
65         // test homogeneous algorithms - fill, max, min
66         static const int num_chan = num_channels<typename C2::pixel_t>::value;
67         static_fill(C2::_pixel, gil::at_c<0>(C1::_pixel)+1);
68         error_if(gil::at_c<0>(C2::_pixel) != gil::at_c<num_chan-1>(C2::_pixel));
69 
70         C2::_pixel = C1::_pixel;
71         error_if(static_max(C2::_pixel) != static_max(C1::_pixel));
72         error_if(static_min(C2::_pixel) != static_min(C1::_pixel));
73         error_if(static_max(C2::_pixel) < static_min(C2::_pixel));
74 
75         // test operator[]
76         C2::_pixel[0] = C1::_pixel[0]+1;
77         error_if(C2::_pixel[0] != C1::_pixel[0]+1);
78     }
79 
test_heterogeneousdo_basic_test80     void test_heterogeneous() {
81         // Both must be pixel types (not necessarily pixel values). The second must be mutable. They must be compatible
82         boost::function_requires<PixelConcept<typename C1::pixel_t> >();
83         boost::function_requires<MutablePixelConcept<typename C2::pixel_t> >();
84         boost::function_requires<PixelsCompatibleConcept<typename C1::pixel_t,typename C2::pixel_t> >();
85 
86         C2::_pixel = C1::_pixel;            // test operator=
87         error_if(C1::_pixel != C2::_pixel);   // test operator==
88 
89         // construct a pixel value from it
90         pixel1_value_t v1(C1::_pixel);
91         pixel2_value_t v2(C2::_pixel);
92         error_if(v1 != v2);
93 
94         // construct from a pixel value
95         pixel1_t c1(v1);
96         pixel2_t c2(v2);
97         error_if(c1 != c2);
98 
99         // Invert the first semantic channel.
100         C2::_pixel = C1::_pixel;
101         semantic_at_c<0>(C2::_pixel) = channel_invert(semantic_at_c<0>(C2::_pixel));
102         error_if(C1::_pixel == C2::_pixel);   // now they must not be equal
103 
104         // test pixel algorithms
105         C2::_pixel = C1::_pixel;
106         static_for_each(C2::_pixel, increment());
107         static_transform(C2::_pixel, C2::_pixel, prev());
108         error_if(C1::_pixel!=C2::_pixel);
109 
110         static_generate(C2::_pixel, set_to_one());
111         error_if(gil::at_c<0>(C2::_pixel) != 1);
112 
113         // Test swap if both are mutable and if their value type is the same
114         // (We know the second one is mutable)
115         typedef typename boost::add_reference<typename C1::type>::type p1_ref;
116         test_swap(
117             boost::mpl::bool_<
118                 pixel_reference_is_mutable<p1_ref>::value &&
119                 boost::is_same<pixel1_value_t,pixel2_value_t>::value> ());
120     }
121 
test_swapdo_basic_test122     void test_swap(boost::mpl::false_) {}
test_swapdo_basic_test123     void test_swap(boost::mpl::true_) {
124         // test swap
125         static_fill(C1::_pixel, 0);
126         static_fill(C2::_pixel, 1);
127         pixel_value_t pv1(C1::_pixel);
128         pixel_value_t pv2(C2::_pixel);
129         error_if(C2::_pixel == C1::_pixel);
130         swap(C1::_pixel, C2::_pixel);
131         error_if(C1::_pixel != pv2 || C2::_pixel != pv1);
132     }
133 };
134 
135 template <typename PixelValue, int Tag=0>
136 class value_core {
137 public:
138     typedef PixelValue type;
139     typedef type        pixel_t;
140     type _pixel;
141 
value_core()142     value_core() : _pixel(0) {}
value_core(const type & val)143     value_core(const type& val) : _pixel(val) {  // test copy constructor
144         boost::function_requires<PixelValueConcept<pixel_t> >();
145         type p2;            // test default constructor
146     }
147 };
148 
149 template <typename PixelRef, int Tag=0>
150 class reference_core : public value_core<typename boost::remove_reference<PixelRef>::type::value_type, Tag> {
151 public:
152     typedef PixelRef type;
153     typedef typename boost::remove_reference<PixelRef>::type pixel_t;
154     typedef value_core<typename pixel_t::value_type, Tag> parent_t;
155 
156     type _pixel;
157 
reference_core()158     reference_core() : parent_t(), _pixel(parent_t::_pixel) {}
reference_core(const typename pixel_t::value_type & val)159     reference_core(const typename pixel_t::value_type& val) : parent_t(val), _pixel(parent_t::_pixel) {
160         boost::function_requires<PixelConcept<pixel_t> >();
161     }
162 };
163 
164 
165 // Use a subset of pixel models that covers all color spaces, channel depths, reference/value, planar/interleaved, const/mutable
166 // color conversion will be invoked on pairs of them. Having an exhaustive binary check would be too big/expensive.
167 typedef mpl::vector<
168     value_core<gray8_pixel_t>,
169     reference_core<gray16_pixel_t&>,
170     value_core<bgr8_pixel_t>,
171     reference_core<rgb8_planar_ref_t>,
172     value_core<argb32_pixel_t>,
173     reference_core<cmyk32f_pixel_t&>,
174     reference_core<abgr16c_ref_t>,           // immutable reference
175     reference_core<rgb32fc_planar_ref_t>
176 > representative_pixels_t;
177 
178 
179 template <typename Vector, typename Fun, int K>
180 struct for_each_impl {
applyfor_each_impl181     static void apply(Fun fun) {
182         for_each_impl<Vector,Fun,K-1>::apply(fun);
183         fun(typename mpl::at_c<Vector,K>::type());
184     }
185 };
186 
187 template <typename Vector, typename Fun>
188 struct for_each_impl<Vector,Fun,-1> {
applyfor_each_impl189     static void apply(Fun fun) {}
190 };
191 
192 template <typename Vector, typename Fun>
for_each(Fun fun)193 void for_each(Fun fun) {
194     for_each_impl<Vector,Fun, mpl::size<Vector>::value-1>::apply(fun);
195 }
196 
197 template <typename Pixel1>
198 struct ccv2 {
199     template <typename P1, typename P2>
color_convert_compatibleccv2200     void color_convert_compatible(const P1& p1, P2& p2, mpl::true_) {
201         typedef typename P1::value_type value_t;
202         p2 = p1;
203         value_t converted;
204         color_convert(p1, converted);
205         error_if(converted != p2);
206     }
207 
208     template <typename P1, typename P2>
color_convert_compatibleccv2209     void color_convert_compatible(const P1& p1, P2& p2, mpl::false_) {
210         color_convert(p1,p2);
211     }
212 
213     template <typename P1, typename P2>
color_convert_implccv2214     void color_convert_impl(const P1& p1, P2& p2) {
215         color_convert_compatible(p1, p2, mpl::bool_<pixels_are_compatible<P1,P2>::value>());
216     }
217 
218 
219     template <typename Pixel2>
operator ()ccv2220     void operator()(Pixel2) {
221         // convert from Pixel1 to Pixel2 (or, if Pixel2 is immutable, to its value type)
222         static const int p2_is_mutable = pixel_reference_is_mutable<typename Pixel2::type>::type::value;
223         typedef typename boost::remove_reference<typename Pixel2::type>::type pixel_model_t;
224         typedef typename pixel_model_t::value_type p2_value_t;
225         typedef typename mpl::if_c<p2_is_mutable, Pixel2, value_core<p2_value_t> >::type pixel2_mutable;
226 
227         Pixel1 p1;
228         pixel2_mutable p2;
229 
230         color_convert_impl(p1._pixel, p2._pixel);
231     }
232 };
233 
234 struct ccv1 {
235     template <typename Pixel>
operator ()ccv1236     void operator()(Pixel) {
237         for_each<representative_pixels_t>(ccv2<Pixel>());
238     }
239 };
240 
test_color_convert()241 void test_color_convert() {
242    for_each<representative_pixels_t>(ccv1());
243 }
244 
test_packed_pixel()245 void test_packed_pixel() {
246     typedef packed_pixel_type<uint16_t, mpl::vector3_c<unsigned,5,6,5>, rgb_layout_t>::type rgb565_pixel_t;
247 
248     boost::function_requires<PixelValueConcept<rgb565_pixel_t> >();
249     BOOST_STATIC_ASSERT((sizeof(rgb565_pixel_t)==2));
250 
251     // define a bgr556 pixel
252     typedef packed_pixel_type<uint16_t, mpl::vector3_c<unsigned,5,6,5>, bgr_layout_t>::type bgr556_pixel_t;
253     boost::function_requires<PixelValueConcept<bgr556_pixel_t> >();
254 
255     // Create a zero packed pixel and a full regular unpacked pixel.
256     rgb565_pixel_t r565;//((uint16_t)0);
257     rgb8_pixel_t rgb_full(255,255,255);
258 
259     // Convert all channels of the unpacked pixel to the packed one & assert the packed one is full
260     get_color(r565,red_t())   = channel_convert<kth_element_type<rgb565_pixel_t, 0>::type>(get_color(rgb_full,red_t()));
261     get_color(r565,green_t()) = channel_convert<kth_element_type<rgb565_pixel_t, 1>::type>(get_color(rgb_full,green_t()));
262     get_color(r565,blue_t())  = channel_convert<kth_element_type<rgb565_pixel_t, 2>::type>(get_color(rgb_full,blue_t()));
263     error_if(r565 != rgb565_pixel_t((uint16_t)65535));
264 
265     // rgb565 is compatible with bgr556. Test interoperability
266     boost::function_requires<PixelsCompatibleConcept<rgb565_pixel_t,bgr556_pixel_t> >();
267 
268     do_basic_test<value_core<rgb565_pixel_t,0>, value_core<bgr556_pixel_t,1> >(r565).test_heterogeneous();
269 
270     color_convert(r565,rgb_full);
271     color_convert(rgb_full,r565);
272 
273     // Test bit-aligned pixel reference
274     typedef const bit_aligned_pixel_reference<boost::uint8_t, boost::mpl::vector3_c<int,1,2,1>, bgr_layout_t, true>  bgr121_ref_t;
275     typedef const bit_aligned_pixel_reference<boost::uint8_t, boost::mpl::vector3_c<int,1,2,1>, rgb_layout_t, true>  rgb121_ref_t;
276     typedef rgb121_ref_t::value_type rgb121_pixel_t;
277     rgb121_pixel_t p121;
278     do_basic_test<reference_core<bgr121_ref_t,0>, reference_core<rgb121_ref_t,1> >(p121).test_heterogeneous();
279     do_basic_test<value_core<rgb121_pixel_t,0>, reference_core<rgb121_ref_t,1> >(p121).test_heterogeneous();
280 
281     BOOST_STATIC_ASSERT((pixel_reference_is_proxy<rgb8_planar_ref_t>::value));
282     BOOST_STATIC_ASSERT((pixel_reference_is_proxy<bgr121_ref_t>::value));
283 
284     BOOST_STATIC_ASSERT(!(pixel_reference_is_proxy<rgb8_pixel_t>::value));
285     BOOST_STATIC_ASSERT(!(pixel_reference_is_proxy<rgb8_pixel_t&>::value));
286     BOOST_STATIC_ASSERT(!(pixel_reference_is_proxy<const rgb8_pixel_t&>::value));
287 
288     BOOST_STATIC_ASSERT( (pixel_reference_is_mutable<      rgb8_pixel_t&>::value));
289     BOOST_STATIC_ASSERT(!(pixel_reference_is_mutable<const rgb8_pixel_t&>::value));
290 
291     BOOST_STATIC_ASSERT((pixel_reference_is_mutable<const rgb8_planar_ref_t&>::value));
292     BOOST_STATIC_ASSERT((pixel_reference_is_mutable<      rgb8_planar_ref_t >::value));
293 
294     BOOST_STATIC_ASSERT(!(pixel_reference_is_mutable<const rgb8c_planar_ref_t&>::value));
295     BOOST_STATIC_ASSERT(!(pixel_reference_is_mutable<      rgb8c_planar_ref_t >::value));
296 
297     BOOST_STATIC_ASSERT( (pixel_reference_is_mutable<bgr121_ref_t>::value));
298     BOOST_STATIC_ASSERT(!(pixel_reference_is_mutable<bgr121_ref_t::const_reference>::value));
299 
300 }
301 
test_pixel()302 void test_pixel() {
303     test_packed_pixel();
304     rgb8_pixel_t rgb8(1,2,3);
305 
306     do_basic_test<value_core<rgb8_pixel_t,0>, reference_core<rgb8_pixel_t&,1> >(rgb8).test_all();
307     do_basic_test<value_core<bgr8_pixel_t,0>, reference_core<rgb8_planar_ref_t,1> >(rgb8).test_all();
308     do_basic_test<reference_core<rgb8_planar_ref_t,0>, reference_core<bgr8_pixel_t&,1> >(rgb8).test_all();
309     do_basic_test<reference_core<const rgb8_pixel_t&,0>, reference_core<rgb8_pixel_t&,1> >(rgb8).test_all();
310 
311     test_color_convert();
312 
313     // Semantic vs physical channel accessors. Named channel accessors
314     bgr8_pixel_t bgr8(rgb8);
315     error_if(bgr8[0] == rgb8[0]);
316     error_if(dynamic_at_c(bgr8,0) == dynamic_at_c(rgb8,0));
317     error_if(gil::at_c<0>(bgr8) == gil::at_c<0>(rgb8));
318     error_if(semantic_at_c<0>(bgr8) != semantic_at_c<0>(rgb8));
319     error_if(get_color(bgr8,blue_t()) != get_color(rgb8,blue_t()));
320 
321     // Assigning a grayscale channel to a pixel
322     gray16_pixel_t g16(34);
323     g16 = 8;
324     bits16 g = get_color(g16,gray_color_t());
325     error_if(g != 8);
326     error_if(g16 != 8);
327 }
328 
main(int argc,char * argv[])329 int main(int argc, char* argv[]) {
330     test_pixel();
331     return 0;
332 }
333 
334