$extrastylesheet
Olena  User documentation 2.1
An Image Processing Platform
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
magick/save.hh
1 // Copyright (C) 2009, 2010, 2011, 2012, 2013 EPITA Research and
2 // Development Laboratory (LRDE)
3 //
4 // This file is part of Olena.
5 //
6 // Olena is free software: you can redistribute it and/or modify it under
7 // the terms of the GNU General Public License as published by the Free
8 // Software Foundation, version 2 of the License.
9 //
10 // Olena is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Olena. If not, see <http://www.gnu.org/licenses/>.
17 //
18 // As a special exception, you may use this file as part of a free
19 // software project without restriction. Specifically, if other files
20 // instantiate templates or use macros or inline functions from this
21 // file, or you compile this file and link it with other files to produce
22 // an executable, this file does not by itself cause the resulting
23 // executable to be covered by the GNU General Public License. This
24 // exception does not however invalidate any other reasons why the
25 // executable file might be covered by the GNU General Public License.
26 
27 #ifndef MLN_IO_MAGICK_SAVE_HH
28 # define MLN_IO_MAGICK_SAVE_HH
29 
33 
34 # include <cstdlib>
35 
36 # include <Magick++.h>
37 
38 # include <mln/metal/equal.hh>
39 
40 # include <mln/core/alias/dpoint2d.hh>
41 # include <mln/core/pixter2d.hh>
42 
43 # include <mln/value/int_u8.hh>
44 # include <mln/value/rgb8.hh>
45 # include <mln/value/qt/rgb32.hh>
46 
47 # include <mln/geom/nrows.hh>
48 # include <mln/geom/ncols.hh>
49 
50 # include <mln/io/magick/internal/init_magick.hh>
51 
52 namespace mln
53 {
54 
55  namespace io
56  {
57 
58  namespace magick
59  {
60 
69  template <typename I>
70  void
71  save(const Image<I>& ima, const std::string& filename);
72 
85  template <typename I, typename J>
86  void
87  save(const Image<I>& ima, const Image<J>& opacity_mask,
88  const std::string& filename);
89 
90 
91  // FIXME: Unfinished?
92 #if 0
93 
97  template <typename T>
98  void
99  save(const Image< tiled2d<T> >& ima, const std::string& filename);
100 #endif
101 
102 
103 # ifndef MLN_INCLUDE_ONLY
104 
105  namespace impl
106  {
107 
108  inline
109  Magick::Color get_color(bool value)
110  {
111  return Magick::ColorMono(value);
112  }
113 
114  inline
115  Magick::Color get_color(const value::int_u8& value)
116  {
117  /* Each channel of a Magick++ image is coded on a
118  Magick::Quantum value, which can be an 8-, 16- or 32-bit
119  integer. Store the data from each mln::value::int_u8
120  values into the most significant bits of Magick::Color's
121  channels. */
122  return Magick::Color
123  (value << 8 * (sizeof(Magick::Quantum) - sizeof(value::int_u8)),
124  value << 8 * (sizeof(Magick::Quantum) - sizeof(value::int_u8)),
125  value << 8 * (sizeof(Magick::Quantum) - sizeof(value::int_u8)));
126  }
127 
128  inline
129  Magick::Color get_color(const value::rgb8& value)
130  {
131  /* Each channel of a Magick++ image is coded on a
132  Magick::Quantum value, which can be an 8-, 16- or 32-bit
133  integer. Store the data from each component of
134  mln::value::rgb8 values (of type mln::value::int_u8) into
135  the most significant bits of Magick::Color's
136  channels. */
137  return Magick::Color
138  (value.red() << 8 * (sizeof(Magick::Quantum)
139  - sizeof(value::rgb8::red_t)),
140  value.green() << 8 * (sizeof(Magick::Quantum)
141  - sizeof(value::rgb8::green_t)),
142  value.blue() << 8 * (sizeof(Magick::Quantum)
143  - sizeof(value::rgb8::blue_t)));
144  }
145 
146 
147  namespace generic
148  {
149 
150  template <typename I>
151  void
152  paste_data(const Image<I>& ima_, Magick::Image& magick_ima)
153  {
154  mln_trace("io::magick::impl::generic::paste_data");
155 
156  const I& ima = exact(ima_);
157 
158  def::coord
159  ncols = geom::ncols(ima),
160  nrows = geom::nrows(ima);
161 
162  // Ensure that there is only one reference to underlying image
163  // If this is not done, then image pixels will not be modified.
164  magick_ima.modifyImage();
165 
166  Magick::Pixels view(magick_ima);
167  // As above, `ncols' is passed before `nrows'.
168  Magick::PixelPacket* pixels = view.get(0, 0, ncols, nrows);
169 
170  mln_piter(I) p(ima.domain());
171  for_all(p)
172  *pixels++ = impl::get_color(ima(p));
173 
174  view.sync();
175 
176  }
177 
178  template <typename I, typename J>
179  void
180  paste_data_opacity(const Image<I>& ima_,
181  const Image<J>& opacity_mask_,
182  Magick::Image& magick_ima)
183  {
184  mln_trace("io::magick::impl::generic::paste_data_opacity");
185 
186  const I& ima = exact(ima_);
187  const J& opacity_mask = exact(opacity_mask_);
188 
189  def::coord
190  ncols = geom::ncols(ima),
191  nrows = geom::nrows(ima);
192 
193  // Ensure that there is only one reference to underlying image
194  // If this is not done, then image pixels will not be modified.
195  magick_ima.modifyImage();
196 
197  Magick::Pixels view(magick_ima);
198  // As above, `ncols' is passed before `nrows'.
199  Magick::PixelPacket* pixels = view.get(0, 0, ncols, nrows);
200 
201  mln_piter(I) p(ima.domain());
202  mln_piter(J) pm(opacity_mask.domain());
203 
204  for_all_2(p, pm)
205  {
206  *pixels = impl::get_color(ima(p));
207  (*pixels).opacity = (opacity_mask(pm) ? 255 : 0);
208  ++pixels;
209  }
210 
211  view.sync();
212 
213  }
214 
215  } // end of namespace mln::io::magick::impl::generic
216 
217 
218  template <typename I>
219  void
220  paste_data_fastest(const Image<I>& ima_, Magick::Image& magick_ima)
221  {
222  const I& ima = exact(ima_);
223 
224  def::coord
225  minrow = geom::min_row(ima),
226  mincol = geom::min_col(ima),
227  maxrow = geom::max_row(ima),
228  maxcol = geom::max_col(ima),
229  ncols = geom::ncols(ima),
230  nrows = geom::nrows(ima);
231 
232  // Ensure that there is only one reference to underlying image
233  // If this is not done, then image pixels will not be modified.
234  magick_ima.modifyImage();
235 
236  Magick::Pixels view(magick_ima);
237  // As above, `ncols' is passed before `nrows'.
238  Magick::PixelPacket* pixels = view.get(0, 0, ncols, nrows);
239  const mln_value(I) *ptr_ima = &ima(ima.domain().pmin());
240 
241  unsigned row_offset = ima.delta_offset(dpoint2d(+1, - ncols));
242 
243  for (def::coord row = minrow; row <= maxrow;
244  ++row, ptr_ima += row_offset)
245  for (def::coord col = mincol; col <= maxcol; ++col)
246  *pixels++ = impl::get_color(*ptr_ima++);
247 
248  view.sync();
249  }
250 
251  template <typename I>
252  void
253  paste_data_fast(const Image<I>& ima_, Magick::Image& magick_ima)
254  {
255  const I& ima = exact(ima_);
256 
257  def::coord
258  ncols = geom::ncols(ima),
259  nrows = geom::nrows(ima);
260 
261  // Ensure that there is only one reference to underlying image
262  // If this is not done, then image pixels will not be modified.
263  magick_ima.modifyImage();
264 
265  Magick::Pixels view(magick_ima);
266  // As above, `ncols' is passed before `nrows'.
267  Magick::PixelPacket* pixels = view.get(0, 0, ncols, nrows);
268 
269 
270  fwd_pixter2d<const I> pi(ima);
271  for_all(pi)
272  *pixels++ = impl::get_color(pi.val());
273 
274  view.sync();
275  }
276 
277  template <typename I, typename J>
278  void
279  paste_data_opacity_fastest(const Image<I>& ima_,
280  const Image<J>& opacity_mask_,
281  Magick::Image& magick_ima)
282  {
283  const I& ima = exact(ima_);
284  const J& opacity_mask = exact(opacity_mask_);
285 
286  def::coord
287  minrow = geom::min_row(ima),
288  mincol = geom::min_col(ima),
289  maxrow = geom::max_row(ima),
290  maxcol = geom::max_col(ima),
291  ncols = geom::ncols(ima),
292  nrows = geom::nrows(ima);
293 
294  // Ensure that there is only one reference to underlying image
295  // If this is not done, then image pixels will not be modified.
296  magick_ima.modifyImage();
297 
298  Magick::Pixels view(magick_ima);
299  // As above, `ncols' is passed before `nrows'.
300  Magick::PixelPacket* pixels = view.get(0, 0, ncols, nrows);
301  const mln_value(I) *ptr_ima = &ima(ima.domain().pmin());
302  const mln_value(J) *ptr_opacity_mask = &opacity_mask(opacity_mask.domain().pmin());
303 
304  unsigned row_offset = ima.delta_offset(dpoint2d(+1, - ncols));
305  unsigned opacity_row_offset = opacity_mask.delta_offset(dpoint2d(+1, - ncols));
306 
307  for (def::coord row = minrow; row <= maxrow;
308  ++row, ptr_ima += row_offset,
309  ptr_opacity_mask += opacity_row_offset)
310  for (def::coord col = mincol; col <= maxcol; ++col)
311  {
312  *pixels = impl::get_color(*ptr_ima++);
313  (*pixels).opacity = ((*ptr_opacity_mask++) ? 255 : 0);
314  ++pixels;
315  }
316 
317  view.sync();
318  }
319 
320 
321 
322 
323  template <typename I, typename J>
324  void
325  paste_data_opacity_fast(const Image<I>& ima_,
326  const Image<J>& opacity_mask_,
327  Magick::Image& magick_ima)
328  {
329  const I& ima = exact(ima_);
330  const J& opacity_mask = exact(opacity_mask_);
331 
332  def::coord
333  ncols = geom::ncols(ima),
334  nrows = geom::nrows(ima);
335 
336  // Ensure that there is only one reference to underlying image
337  // If this is not done, then image pixels will not be modified.
338  magick_ima.modifyImage();
339 
340  Magick::Pixels view(magick_ima);
341  // As above, `ncols' is passed before `nrows'.
342  Magick::PixelPacket* pixels = view.get(0, 0, ncols, nrows);
343 
344  mln_pixter(const I) pi(ima);
345  mln_pixter(const J) pom(opacity_mask);
346 
347  for_all_2(pi, pom)
348  {
349  *pixels = impl::get_color(pi.val());
350  (*pixels).opacity = (pom.val() ? 255 : 0);
351  ++pixels;
352  }
353 
354  view.sync();
355  }
356 
357  } // end of namespace mln::io::magick::impl
358 
359 
360  namespace internal
361  {
362 
363  // paste_data_opacity
364 
365  template <typename I, typename J>
366  void
367  paste_data_opacity_fast_dispatch(metal::false_, // No images are fast or fastest.
368  const Image<I>& ima,
369  const Image<J>& opacity_mask,
370  Magick::Image& magick_ima)
371  {
372  impl::generic::paste_data_opacity(ima, opacity_mask, magick_ima);
373  }
374 
375  template <typename I, typename J>
376  void
377  paste_data_opacity_fast_dispatch(metal::true_, // Bost images are "fast" or one is fast and the other one is fastest.
378  const Image<I>& ima,
379  const Image<J>& opacity_mask,
380  Magick::Image& magick_ima)
381  {
382  impl::paste_data_opacity_fast(ima, opacity_mask, magick_ima);
383  }
384 
385  template <typename I, typename J>
386  void
387  paste_data_opacity_fastest_dispatch(metal::false_, // At least one of the images is not "fastest".
388  const Image<I>& ima,
389  const Image<J>& opacity_mask,
390  Magick::Image& magick_ima)
391  {
392 
393  enum { fast = mlc_and(mlc_is(mln_trait_image_value_storage(I), trait::image::value_storage::one_block),
394  mlc_is(mln_trait_image_value_storage(J), trait::image::value_storage::one_block))::value };
395  paste_data_opacity_fast_dispatch(metal::bool_<fast>(),
396  ima, opacity_mask, magick_ima);
397  }
398 
399 
400  template <typename I, typename J>
401  void
402  paste_data_opacity_fastest_dispatch(metal::true_, // Both images are "fastest".
403  const Image<I>& ima,
404  const Image<J>& opacity_mask,
405  Magick::Image& magick_ima)
406  {
407  impl::paste_data_opacity_fastest(ima, opacity_mask, magick_ima);
408  }
409 
410  template <typename I, typename J>
411  void
412  paste_data_opacity_dispatch(const Image<I>& ima,
413  const Image<J>& opacity_mask,
414  Magick::Image& magick_ima)
415  {
416  enum { fastest = mlc_and(mlc_is(mln_trait_image_speed(I), trait::image::speed::fastest),
417  mlc_is(mln_trait_image_speed(J), trait::image::speed::fastest))::value };
418  paste_data_opacity_fastest_dispatch(metal::bool_<fastest>(),
419  ima, opacity_mask, magick_ima);
420  }
421 
422 
423  // paste_data
424 
425 
426  template <typename I>
427  void
428  paste_data_dispatch_fast(const mln::trait::image::value_storage::any&,
429  const Image<I>& ima, Magick::Image& magick_ima)
430  {
431  impl::generic::paste_data(ima, magick_ima);
432  }
433 
434 
435  template <typename I>
436  void
437  paste_data_dispatch_fast(const mln::trait::image::value_storage::one_block&,
438  const Image<I>& ima, Magick::Image& magick_ima)
439  {
440  impl::paste_data_fast(ima, magick_ima);
441  }
442 
443 
444  template <typename I>
445  void
446  paste_data_dispatch_fastest(const mln::trait::image::speed::any&,
447  const Image<I>& ima, Magick::Image& magick_ima)
448  {
449  paste_data_dispatch_fast(mln_trait_image_value_storage(I)(), ima, magick_ima);
450  }
451 
452 
453  template <typename I>
454  void
455  paste_data_dispatch_fastest(const mln::trait::image::speed::fastest&,
456  const Image<I>& ima, Magick::Image& magick_ima)
457  {
458  impl::paste_data_fastest(ima, magick_ima);
459  }
460 
461  template <typename I>
462  void
463  paste_data_dispatch(const Image<I>& ima, Magick::Image& magick_ima)
464  {
465  paste_data_dispatch_fastest(mln_trait_image_speed(I)(), ima, magick_ima);
466  }
467 
468  } // end of namespace mln::io::magick::internal
469 
470 
471  template <typename I, typename J>
472  void
473  save(const Image<I>& ima_, const Image<J>& opacity_mask_,
474  const std::string& filename)
475  {
476  mln_trace("mln::io::magick::save");
477 
478  mln_precondition(mln_site_(I)::dim == 2);
479  // Turn this into a static check?
480  if (!(mln::metal::equal<mln_value(I), bool>::value ||
481  mln::metal::equal<mln_value(I), value::int_u8>::value ||
482  mln::metal::equal<mln_value(I), value::rgb8>::value ||
483  mln::metal::equal<mln_value(I), value::qt::rgb32>::value))
484  {
485  std::cerr <<
486  "error: trying to save an unsupported format\n"
487  "supported formats are:\n"
488  " binary (bool)\n"
489  " 8-bit grayscale (mln::value::int_u8)\n"
490  " 3x8-bit truecolor (rgb8)" << std::endl;
491  abort();
492  }
493 
494  const I& ima = exact(ima_);
495  const J& opacity_mask = exact(opacity_mask_);
496 
497  // Initialize GraphicsMagick only once.
498  static internal::init_magick init;
499  (void) init;
500 
501  def::coord
502  ncols = geom::ncols(ima),
503  nrows = geom::nrows(ima);
504 
505 
506  // In the construction of a Geometry object, the width (i.e.
507  // `ncols') comes first, then the height (i.e. `nrows')
508  // follows.
509  //
510  // FIXME: Default pixel value is set to "white". If the image is
511  // declared with the default constructor, without specifying a
512  // default value, no data seems to be allocated and the Pixel view
513  // declared further fails and segfault...
514  Magick::Image magick_ima(Magick::Geometry(ncols, nrows), "white");
515 
516  if (opacity_mask.is_valid())
517  {
518  magick_ima.type(Magick::TrueColorMatteType);
519  internal::paste_data_opacity_dispatch(ima, opacity_mask, magick_ima);
520  }
521  else
522  {
523  magick_ima.type(Magick::TrueColorType);
524  internal::paste_data_dispatch(ima, magick_ima);
525  }
526 
527  magick_ima.write(filename);
528 
529  }
530 
531 
532 
533  template <typename I>
534  inline
535  void
536  save(const Image<I>& ima, const std::string& filename)
537  {
538  mln_ch_value(I,bool) opacity_mask;
539  save(ima, opacity_mask, filename);
540  }
541 
542 
543 # endif // ! MLN_INCLUDE_ONLY
544 
545  } // end of namespace mln::io::magick
546 
547  } // end of namespace mln::io
548 
549 } // end of namespace mln
550 
551 
552 #endif // ! MLN_IO_MAGICK_SAVE_HH