MagickDrain.h
1 /*
2 
3 MIT License
4 
5 Copyright (c) 2017 FMI Open Development / Markus Peura, first.last@fmi.fi
6 
7 Permission is hereby granted, free of charge, to any person obtaining a copy
8 of this software and associated documentation files (the "Software"), to deal
9 in the Software without restriction, including without limitation the rights
10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 copies of the Software, and to permit persons to whom the Software is
12 furnished to do so, subject to the following conditions:
13 
14 The above copyright notice and this permission notice shall be included in all
15 copies or substantial portions of the Software.
16 
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 SOFTWARE.
24 
25 */
26 /*
27 Part of Rack development has been done in the BALTRAD projects part-financed
28 by the European Union (European Regional Development Fund and European
29 Neighbourhood Partnership Instrument, Baltic Sea Region Programme 2007-2013)
30 */
31 
32 #ifndef MAGICKDRAIN_H_
33 #define MAGICKDRAIN_H_
34 
35 
36 
37 
38 
39 #ifdef DRAIN_MAGICK_yes
40 #include <Magick++.h>
41 #endif
42 
43 // In compilation, use "Magick*-config" to get libs and includes right.
44 
45 
46 #include "Image.h"
47 
48 
49 namespace drain
50 {
51 
52 namespace image
53 {
54 
56 {
57 public:
58  //MagickDrain();
59  //virtual ~MagickDrain();
60 
61 #ifdef DRAIN_MAGICK_yes
64  template<class T>
65  static void convert(const ImageT<T> &drainImage, Magick::ImageT &magickImage);
66 
69  template<class T>
70  static void convert(Magick::ImageT &magickImage, ImageT<T> &drainImage);
71 
72 #endif
73 
74 };
75 
76 
77 
78 #ifdef DRAIN_MAGICK_yes
79 
80 // Looks like Magick inverts alpha channel
81 template <class T>
82 void MagickDrain::convert(const ImageT<T> &drainImage, Magick::ImageT &magickImage) {
83 
84  Magick::ImageType type = Magick::TrueColorMatteType;
85 
86  int imageChannels = drainImage.getImageChannelCount();
87  int alphaChannels = drainImage.getAlphaChannelCount();
88 
89  if (alphaChannels > 1){
90  std::cerr << "Warning: multiple alpha channel image, using 1st only \n";
91  std::cerr << " Image geometry:" << drainImage.getGeometry() << '\n';
92  alphaChannels = 1;
93  }
94 
95  const ImageT<T> *red = NULL, *green = NULL, *blue = NULL, *alpha = NULL;
96 
97  //std::string toOStr;
98  //drainImage.getGeometry().toString(toOStr);
99  //std::cout << "MagickDrain: " << drainImage.getGeometry() << std::endl;
100  /*
101  std::cerr << "img.chs:" << imageChannels << std::endl;
102  std::cerr << "alpha.chs:" << alphaChannels << std::endl;
103  std::cerr << "Extra debug:" << std::endl;
104  drainImage.debug();
105  */
106 
107  switch (imageChannels){
108  case 0:
109  if (alphaChannels == 0){
110  std::cerr << "Error: zero channel image!\n";
111  }
112  else {
113  type = Magick::GrayscaleType;
114  red =& drainImage.getChannel(0);
115  green =& drainImage.getChannel(0);
116  blue =& drainImage.getChannel(0);
117  }
118  break;
119  case 1:
120  type = Magick::GrayscaleType;
121  red =& drainImage.getChannel(0);
122  green =& drainImage.getChannel(0);
123  blue =& drainImage.getChannel(0);
124  if (alphaChannels > 0) {
125  type = Magick::GrayscaleMatteType;
126  alpha =& drainImage.getAlphaChannel();
127  }
128  break;
129  case 2:
130  if (!alphaChannels) {
131  type = Magick::GrayscaleMatteType;
132  std::cerr << "Notice: 2 channel image, storing 2nd as alpha channel \n";
133  red =& drainImage.getChannel(0);
134  green =& drainImage.getChannel(0);
135  blue =& drainImage.getChannel(0);
136  alpha =& drainImage.getChannel(1);
137  }
138  else {
139  std::cerr << "Notice: (2+alpha ) channel image, creating 'RGAA' image instead of 'RGBA'.\n";
140  type = Magick::TrueColorMatteType;
141  red =& drainImage.getChannel(0);
142  green =& drainImage.getChannel(1);
143  blue =& drainImage.getAlphaChannel();
144  alpha =& drainImage.getAlphaChannel();
145  }
146  break;
147  case 3:
148  type = Magick::TrueColorType;
149  red =& drainImage.getChannel(0);
150  green =& drainImage.getChannel(1);
151  blue =& drainImage.getChannel(2);
152  if (alphaChannels){
153  type = Magick::TrueColorMatteType;
154  alpha =& drainImage.getAlphaChannel();
155  }
156  break;
157  default:
158  type = Magick::TrueColorMatteType;
159  red =& drainImage.getChannel(0);
160  green =& drainImage.getChannel(1);
161  blue =& drainImage.getChannel(2);
162 
163  if (alphaChannels){
164  std::cerr << "Warning: (" << imageChannels << "+alpha) channel image, using (3+alpha). \n";
165  alpha =& drainImage.getAlphaChannel();
166  }
167  else if (imageChannels == 4){
168  std::cerr << "Notice: 4 channel image, storing 4th as alpha channel \n";
169  alpha =& drainImage.getChannel(3);
170  };
171  imageChannels = 3; // WHY?
172  }
173 
174 
175  const int width = drainImage.getGeometry().getWidth();
176  const int height = drainImage.getGeometry().getHeight();
177 
178 
179  //Magick::Image magickImage(Magick::Geometry(width,height),Magick::Color("black"));
180 
181 
182  try {
183  magickImage.classType(Magick::DirectClass); // here?
184  magickImage.size(Magick::Geometry(width,height));
185  //magickImage.read("cow.png");
186 
187  magickImage.modifyImage(); // actually: _prepare_to_ modify
188  magickImage.type(type); // The order copied from Magick examples
189 
190 
191  Magick::PixelPacket *pixel_cache = magickImage.getPixels(0,0,width,height);
192 
193 
194  Point2D<> p;
195  int &i = p.x;
196  int &j = p.y;
197 
198  // TODO: const unsigned drainBits = std::numeric_limits<unsigned char>::is_integer ? std::numeric_limits<unsigned char>::max() : //
199  const unsigned int drainMax = std::numeric_limits<unsigned char>::max();
200  const int shiftBits = magickImage.depth() - 8; // ????
201 
202 
203  int rowAddress = 0;
204  int address = 0;
205 
206  for (j=0; j<height; j++){
207  rowAddress = j*width;
208 
209  for (i=0; i<width; i++){
210  address = i + rowAddress;
211  pixel_cache[address].red = (red->at(i,j) << shiftBits);
212  pixel_cache[address].green = (green->at(i,j) << shiftBits);
213  pixel_cache[address].blue = (blue->at(i,j) << shiftBits);
214  if (alpha != NULL)
215  //pixel_cache[address].opacity = ((alpha->at(i,j)) << shiftBits); //WARNING!!
216  // Looks like Magick does NOT invert alpha channel IN THIS DIRECTION
217  pixel_cache[address].opacity = ((drainMax-alpha->at(i,j)) << shiftBits); //WARNING!!
218  }
219  }
220  //std::cerr << "synching" << std::endl;
221  //pixel_cache[rowAddress + 10 + width] = Magick::Color("green");
222 
223  magickImage.syncPixels();
224  }
225  catch (Magick::Error& e) {
226  // because 'Error' is derived from the standard C++ std::exception, it has a 'what()' method
227  std::cerr << "a Magick++ error occurred: " << e.what() << std::endl;
228  }
229  catch ( ... ) {
230  std::cerr << "MagickDrain: an unhandled error has occurred; exiting application." << std::endl;
231  exit(1);
232  }
233 
234 
235  // std::cerr << "magickImage.type = " << magickImage.type() << std::endl;
236 
237  // Store comments as KEY=VALUE pairs
238  std::stringstream sstr;
239  std::map<std::string,Data>::const_iterator it;
240  for (it = drainImage.properties.begin(); it != drainImage.properties.end(); it++){
241  sstr << it->first << '=' << it->second << '\n';
242  }
243  magickImage.comment(sstr.toStr());
244 
245 
246 }
247 #endif
248 
249 
250 
251 #ifdef DRAIN_MAGICK_yes
252 
253 // Looks like Magick inverts alpha channel
254 template <class T>
255 void MagickDrain::convert(Magick::ImageT &magickImage, ImageT<T> &drainImage) {
256 
257  const int w = magickImage.columns();
258  const int h = magickImage.rows();
259 
260  //drainImage.setGeometry(w,h)
261 
262  // TODO: redChannel = &drainImage.at(0,0,0);
263 
264  switch (magickImage.type()){
265  case Magick::GrayscaleType:
266  drainImage.setGeometry(w,h,1);
267  magickImage.write(0,0,w,h,"I",Magick::CharPixel,&drainImage.at(0,0));
268  break;
269  case Magick::GrayscaleMatteType:
270  drainImage.setGeometry(w,h,1,1);
271  magickImage.write(0,0,w,h,"I",Magick::CharPixel,&drainImage.at(0,0,0));
272  magickImage.write(0,0,w,h,"A",Magick::CharPixel,&drainImage.at(0,0,1));
273  break;
274  // case Magick::RGBColorspace:
275  case Magick::PaletteType: // just test status
276  case Magick::TrueColorType:
277  drainImage.setGeometry(w,h,3);
278  magickImage.write(0,0,w,h,"R",Magick::CharPixel,&drainImage.at(0,0,0));
279  magickImage.write(0,0,w,h,"G",Magick::CharPixel,&drainImage.at(0,0,1));
280  magickImage.write(0,0,w,h,"B",Magick::CharPixel,&drainImage.at(0,0,2));
281  break;
282  case Magick::PaletteMatteType:
283  case Magick::TrueColorMatteType:
284  drainImage.setGeometry(w,h,3,1);
285  magickImage.write(0,0,w,h,"R",Magick::CharPixel,&drainImage.at(0,0,0));
286  magickImage.write(0,0,w,h,"G",Magick::CharPixel,&drainImage.at(0,0,1));
287  magickImage.write(0,0,w,h,"B",Magick::CharPixel,&drainImage.at(0,0,2));
288  magickImage.write(0,0,w,h,"A",Magick::CharPixel,&drainImage.at(0,0,3));
289  // magickImage.write(0,0,w,h,"A",Magick::CharPixel,&drainImage.alphaChannel(0)[0]);
290  break;
291  // default:
292  default:
293  std::stringstream sstr;
294  sstr << "operator<<(image,magickImage) : Magick type " << magickImage.type() << " not handled.";
295  throw std::runtime_error(sstr.toStr());
296  }
297 
298  // TODO contradictory!
299  if (drainImage.getAlphaChannelCount()>0){
300  //Image<> & alpha = drainImage.getAlphaChannel();
301  //NegateOp<>().process(alpha,alpha); // Looks like Magick inverts alpha channel IN THIS DIRECTION.
302  //ScaleOp<>(-1.0,255).process(alpha,alpha); // Looks like Magick inverts alpha channel
303  }
304 
305  std::stringstream sstr(magickImage.comment()); // dont touch sstr!!
306  drainImage.properties.reader.read(sstr);
307 
308  if (drain::Debug > 5){ // TODO static (does not work)
309  std::cerr << "read magickImage.type = " << magickImage.type() << '\n';
310  std::cerr << "comment='" << magickImage.comment() << "'\n";
311  std::cerr << "::::::::::::::::::::\n";
312  std::cerr << drainImage.properties;
313  }
314 
315 
316 }
317 
318 #endif
319 
320 
321 
322 
323 } // image
324 
325 } // drain
326 
327 #endif /*MAGICKDRAIN_H_*/
328 //#endif // ImageMagick
329 
330 // Drain
FlexVariableMap properties
Container for user-defined KEY=VALUE metadata.
Definition: ImageFrame.h:369
A template class for images with static storage type.
Definition: ImageT.h:67
Definition: MagickDrain.h:56
Definition: DataSelector.cpp:1277