Imported Upstream version 1.72.0
[platform/upstream/boost.git] / libs / gil / doc / design / dynamic_image.rst
1 Dynamic images and image views
2 ==============================
3
4 The GIL extension called ``dynamic_image`` allows for images, image views
5 or any GIL constructs to have their parameters defined at run time.
6
7 The color space, channel depth, channel ordering, and interleaved/planar
8 structure of an image are defined by the type of its template argument, which
9 makes them compile-time bound. Often some of these parameters are available
10 only at run time. Consider, for example, writing a module that opens the image
11 at a given file path, rotates it and saves it back in its original color space
12 and channel depth. How can we possibly write this using our generic image?
13 What type is the image loading code supposed to return?
14
15 Here is an example:
16
17 .. code-block:: cpp
18
19   #include <boost/gil/extension/dynamic_image/dynamic_image_all.hpp>
20   using namespace boost;
21
22   #define ASSERT_SAME(A,B) static_assert(is_same< A,B >::value, "")
23
24   // Define the set of allowed images
25   typedef mpl::vector<rgb8_image_t, cmyk16_planar_image_t> my_images_t;
26
27   // Create any_image class (or any_image_view) class
28   typedef any_image<my_images_t> my_any_image_t;
29
30   // Associated view types are available (equivalent to the ones in image_t)
31   typedef any_image_view<mpl::vector2<rgb8_view_t,  cmyk16_planar_view_t > > AV;
32   ASSERT_SAME(my_any_image_t::view_t, AV);
33
34   typedef any_image_view<mpl::vector2<rgb8c_view_t, cmyk16c_planar_view_t> > CAV;
35   ASSERT_SAME(my_any_image_t::const_view_t, CAV);
36   ASSERT_SAME(my_any_image_t::const_view_t, my_any_image_t::view_t::const_t);
37
38   typedef any_image_view<mpl::vector2<rgb8_step_view_t, cmyk16_planar_step_view_t> > SAV;
39   ASSERT_SAME(typename dynamic_x_step_type<my_any_image_t::view_t>::type, SAV);
40
41   // Assign it a concrete image at run time:
42   my_any_image_t myImg = my_any_image_t(rgb8_image_t(100,100));
43
44   // Change it to another at run time. The previous image gets destroyed
45   myImg = cmyk16_planar_image_t(200,100);
46
47   // Assigning to an image not in the allowed set throws an exception
48   myImg = gray8_image_t();        // will throw std::bad_cast
49
50 The ``any_image`` and ``any_image_view`` subclass from GIL ``variant`` class,
51 which breaks down the instantiated type into a non-templated underlying base
52 type and a unique instantiation type identifier. The underlying base instance
53 is represented as a block of bytes.
54 The block is large enough to hold the largest of the specified types.
55
56 GIL variant is similar to ``boost::variant`` in spirit (hence we borrow the
57 name from there) but it differs in several ways from the current boost
58 implementation. Perhaps the biggest difference is that GIL variant always
59 takes a single argument, which is a model of MPL Random Access Sequence
60 enumerating the allowed types. Having a single interface allows GIL variant
61 to be used easier in generic code. Synopsis:
62
63 .. code-block:: cpp
64
65   template <typename Types>    // models MPL Random Access Container
66   class variant
67   {
68     ...         _bits;
69     std::size_t _index;
70   public:
71     typedef Types types_t;
72
73     variant();
74     variant(const variant& v);
75     virtual ~variant();
76
77     variant& operator=(const variant& v);
78     template <typename TS> friend bool operator==(const variant<TS>& x, const variant<TS>& y);
79     template <typename TS> friend bool operator!=(const variant<TS>& x, const variant<TS>& y);
80
81     // Construct/assign to type T. Throws std::bad_cast if T is not in Types
82     template <typename T> explicit variant(const T& obj);
83     template <typename T> variant& operator=(const T& obj);
84
85     // Construct/assign by swapping T with its current instance. Only possible if they are swappable
86     template <typename T> explicit variant(T& obj, bool do_swap);
87     template <typename T> void move_in(T& obj);
88
89     template <typename T> static bool has_type();
90
91     template <typename T> const T& _dynamic_cast() const;
92     template <typename T>       T& _dynamic_cast();
93
94     template <typename T> bool current_type_is() const;
95   };
96
97   template <typename UOP, typename Types>
98    UOP::result_type apply_operation(variant<Types>& v, UOP op);
99   template <typename UOP, typename Types>
100    UOP::result_type apply_operation(const variant<Types>& v, UOP op);
101
102   template <typename BOP, typename Types1, typename Types2>
103    BOP::result_type apply_operation(      variant<Types1>& v1,       variant<Types2>& v2, UOP op);
104
105   template <typename BOP, typename Types1, typename Types2>
106    BOP::result_type apply_operation(const variant<Types1>& v1,       variant<Types2>& v2, UOP op);
107
108   template <typename BOP, typename Types1, typename Types2>
109    BOP::result_type apply_operation(const variant<Types1>& v1, const variant<Types2>& v2, UOP op);
110
111 GIL ``any_image_view`` and ``any_image`` are subclasses of ``variant``:
112
113 .. code-block:: cpp
114
115   template <typename ImageViewTypes>
116   class any_image_view : public variant<ImageViewTypes>
117   {
118   public:
119     typedef ... const_t; // immutable equivalent of this
120     typedef std::ptrdiff_t x_coord_t;
121     typedef std::ptrdiff_t y_coord_t;
122     typedef point<std::ptrdiff_t> point_t;
123
124     any_image_view();
125     template <typename T> explicit any_image_view(const T& obj);
126     any_image_view(const any_image_view& v);
127
128     template <typename T> any_image_view& operator=(const T& obj);
129     any_image_view&                       operator=(const any_image_view& v);
130
131     // parameters of the currently instantiated view
132     std::size_t num_channels()  const;
133     point_t     dimensions()    const;
134     x_coord_t   width()         const;
135     y_coord_t   height()        const;
136   };
137
138   template <typename ImageTypes>
139   class any_image : public variant<ImageTypes>
140   {
141     typedef variant<ImageTypes> parent_t;
142   public:
143     typedef ... const_view_t;
144     typedef ... view_t;
145     typedef std::ptrdiff_t x_coord_t;
146     typedef std::ptrdiff_t y_coord_t;
147     typedef point<std::ptrdiff_t> point_t;
148
149     any_image();
150     template <typename T> explicit any_image(const T& obj);
151     template <typename T> explicit any_image(T& obj, bool do_swap);
152     any_image(const any_image& v);
153
154     template <typename T> any_image& operator=(const T& obj);
155     any_image&                       operator=(const any_image& v);
156
157     void recreate(const point_t& dims, unsigned alignment=1);
158     void recreate(x_coord_t width, y_coord_t height, unsigned alignment=1);
159
160     std::size_t num_channels()  const;
161     point_t     dimensions()    const;
162     x_coord_t   width()         const;
163     y_coord_t   height()        const;
164   };
165
166 Operations are invoked on variants via ``apply_operation`` passing a
167 function object to perform the operation. The code for every allowed
168 type in the variant is instantiated and the appropriate instantiation
169 is selected via a switch statement. Since image view algorithms
170 typically have time complexity at least linear on the number of
171 pixels, the single switch statement of image view variant adds
172 practically no measurable performance overhead compared to templated
173 image views.
174
175 Variants behave like the underlying type. Their copy constructor will
176 invoke the copy constructor of the underlying instance. Equality
177 operator will check if the two instances are of the same type and then
178 invoke their ``operator==``, etc. The default constructor of a variant
179 will default-construct the first type. That means that
180 ``any_image_view`` has shallow default-constructor, copy-constructor,
181 assignment and equality comparison, whereas ``any_image`` has deep
182 ones.
183
184 It is important to note that even though ``any_image_view`` and
185 ``any_image`` resemble the static ``image_view`` and ``image``, they
186 do not model the full requirements of ``ImageViewConcept`` and
187 ``ImageConcept``. In particular they don't provide access to the
188 pixels. There is no "any_pixel" or "any_pixel_iterator" in GIL. Such
189 constructs could be provided via the ``variant`` mechanism, but doing
190 so would result in inefficient algorithms, since the type resolution
191 would have to be performed per pixel. Image-level algorithms should be
192 implemented via ``apply_operation``. That said, many common operations
193 are shared between the static and dynamic types. In addition, all of
194 the image view transformations and many STL-like image view algorithms
195 have overloads operating on ``any_image_view``, as illustrated with
196 ``copy_pixels``:
197
198 .. code-block:: cpp
199
200   rgb8_view_t v1(...);  // concrete image view
201   bgr8_view_t v2(...);  // concrete image view compatible with v1 and of the same size
202   any_image_view<Types>  av(...);  // run-time specified image view
203
204   // Copies the pixels from v1 into v2.
205   // If the pixels are incompatible triggers compile error
206   copy_pixels(v1,v2);
207
208   // The source or destination (or both) may be run-time instantiated.
209   // If they happen to be incompatible, throws std::bad_cast
210   copy_pixels(v1, av);
211   copy_pixels(av, v2);
212   copy_pixels(av, av);
213
214 By having algorithm overloads supporting dynamic constructs, we create
215 a base upon which it is possible to write algorithms that can work
216 with either compile-time or runtime images or views. The following
217 code, for example, uses the GIL I/O extension to turn an image on disk
218 upside down:
219
220 .. code-block:: cpp
221
222   #include <boost\gil\extension\io\jpeg_dynamic_io.hpp>
223
224   template <typename Image>    // Could be rgb8_image_t or any_image<...>
225   void save_180rot(const std::string& file_name)
226   {
227     Image img;
228     jpeg_read_image(file_name, img);
229     jpeg_write_view(file_name, rotated180_view(view(img)));
230   }
231
232 It can be instantiated with either a compile-time or a runtime image
233 because all functions it uses have overloads taking runtime
234 constructs. For example, here is how ``rotated180_view`` is
235 implemented:
236
237 .. code-block:: cpp
238
239   // implementation using templated view
240   template <typename View>
241   typename dynamic_xy_step_type<View>::type rotated180_view(const View& src) { ... }
242
243   namespace detail
244   {
245     // the function, wrapped inside a function object
246     template <typename Result> struct rotated180_view_fn
247     {
248         typedef Result result_type;
249         template <typename View> result_type operator()(const View& src) const
250   {
251             return result_type(rotated180_view(src));
252         }
253     };
254   }
255
256   // overloading of the function using variant. Takes and returns run-time bound view.
257   // The returned view has a dynamic step
258   template <typename ViewTypes> inline // Models MPL Random Access Container of models of ImageViewConcept
259   typename dynamic_xy_step_type<any_image_view<ViewTypes> >::type rotated180_view(const any_image_view<ViewTypes>& src)
260   {
261     return apply_operation(src,detail::rotated180_view_fn<typename dynamic_xy_step_type<any_image_view<ViewTypes> >::type>());
262   }
263
264 Variants should be used with caution (especially algorithms that take
265 more than one variant) because they instantiate the algorithm for
266 every possible model that the variant can take. This can take a toll
267 on compile time and executable size. Despite these limitations,
268 ``variant`` is a powerful technique that allows us to combine the
269 speed of compile-time resolution with the flexibility of run-time
270 resolution. It allows us to treat images of different parameters
271 uniformly as a collection and store them in the same container.