1 /*
2  * Copyright (C) 2016 Open Source Robotics Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 #ifdef BOOL
18 #undef BOOL
19 #endif
20 #include <FreeImage.h>
21 
22 #include <string>
23 
24 #include <ignition/common/Console.hh>
25 #include <ignition/common/Util.hh>
26 #include <ignition/common/Image.hh>
27 
28 using namespace ignition;
29 using namespace common;
30 
31 namespace ignition
32 {
33   namespace common
34   {
35     /// \brief Private data class
36     class ImagePrivate
37     {
38       /// \brief bitmap data
39       public: FIBITMAP *bitmap;
40 
41       /// \brief path name of the image file
42       public: std::string fullName;
43 
44       /// \brief Implementation of GetData
45       public: void DataImpl(unsigned char **_data, unsigned int &_count,
46           FIBITMAP *_img) const;
47     };
48   }
49 }
50 
51 static int count = 0;
52 
53 //////////////////////////////////////////////////
Image(const std::string & _filename)54 Image::Image(const std::string &_filename)
55   : dataPtr(new ImagePrivate)
56 {
57   if (count == 0)
58     FreeImage_Initialise();
59 
60   count++;
61 
62   this->dataPtr->bitmap = NULL;
63   if (!_filename.empty())
64   {
65     std::string filename = ignition::common::findFile(_filename);
66     if (!filename.empty())
67       this->Load(filename);
68     else
69       ignerr << "Unable to find image[" << _filename << "]\n";
70   }
71 }
72 
73 //////////////////////////////////////////////////
~Image()74 Image::~Image()
75 {
76   count--;
77 
78   if (this->dataPtr->bitmap)
79     FreeImage_Unload(this->dataPtr->bitmap);
80   this->dataPtr->bitmap = NULL;
81 
82   if (count == 0)
83     FreeImage_DeInitialise();
84 }
85 
86 
87 //////////////////////////////////////////////////
Load(const std::string & _filename)88 int Image::Load(const std::string &_filename)
89 {
90   this->dataPtr->fullName = _filename;
91   if (!exists(this->dataPtr->fullName))
92   {
93     this->dataPtr->fullName = common::findFile(_filename);
94   }
95 
96   if (exists(this->dataPtr->fullName))
97   {
98     FREE_IMAGE_FORMAT fifmt =
99       FreeImage_GetFIFFromFilename(this->dataPtr->fullName.c_str());
100 
101     if (this->dataPtr->bitmap)
102       FreeImage_Unload(this->dataPtr->bitmap);
103     this->dataPtr->bitmap = NULL;
104 
105     if (fifmt == FIF_PNG)
106     {
107       this->dataPtr->bitmap = FreeImage_Load(fifmt,
108           this->dataPtr->fullName.c_str(), PNG_DEFAULT);
109     }
110     else if (fifmt == FIF_JPEG)
111     {
112       this->dataPtr->bitmap =
113         FreeImage_Load(fifmt, this->dataPtr->fullName.c_str(), JPEG_DEFAULT);
114     }
115     else if (fifmt == FIF_BMP)
116     {
117       this->dataPtr->bitmap = FreeImage_Load(fifmt,
118           this->dataPtr->fullName.c_str(), BMP_DEFAULT);
119     }
120     else
121     {
122       ignerr << "Unknown image format[" << this->dataPtr->fullName << "]\n";
123       return -1;
124     }
125 
126     return 0;
127   }
128 
129   ignerr << "Unable to open image file[" << this->dataPtr->fullName
130         << "], check your IGNITION_RESOURCE_PATH settings.\n";
131   return -1;
132 }
133 
134 //////////////////////////////////////////////////
SavePNG(const std::string & _filename)135 void Image::SavePNG(const std::string &_filename)
136 {
137   FreeImage_Save(FIF_PNG, this->dataPtr->bitmap, _filename.c_str(), 0);
138 }
139 
140 //////////////////////////////////////////////////
SetFromData(const unsigned char * _data,unsigned int _width,unsigned int _height,Image::PixelFormatType _format)141 void Image::SetFromData(const unsigned char *_data,
142     unsigned int _width,
143     unsigned int _height,
144     Image::PixelFormatType _format)
145 {
146   if (this->dataPtr->bitmap)
147     FreeImage_Unload(this->dataPtr->bitmap);
148   this->dataPtr->bitmap = NULL;
149 
150   // int redmask = FI_RGBA_RED_MASK;
151   int redmask = 0x0000ff;
152 
153   // int greenmask = FI_RGBA_GREEN_MASK;
154   int greenmask = 0x00ff00;
155 
156   // int bluemask = FI_RGBA_BLUE_MASK;
157   int bluemask = 0xff0000;
158 
159   unsigned int bpp;
160   int scanlineBytes;
161 
162   if (_format == L_INT8)
163   {
164     bpp = 8;
165     scanlineBytes = _width;
166   }
167   else if (_format == RGB_INT8)
168   {
169     bpp = 24;
170     redmask = 0xff0000;
171     greenmask = 0x00ff00;
172     bluemask = 0x0000ff;
173     scanlineBytes = _width * 3;
174   }
175   else if (_format == RGBA_INT8)
176   {
177     bpp = 32;
178     redmask = 0xff000000;
179     greenmask = 0x00ff0000;
180     bluemask = 0x0000ff00;
181     scanlineBytes = _width * 4;
182   }
183   else if (_format == BGR_INT8)
184   {
185     bpp = 24;
186     redmask = 0x0000ff;
187     greenmask = 0x00ff00;
188     bluemask = 0xff0000;
189     scanlineBytes = _width * 3;
190   }
191   else
192   {
193     ignerr << "Unable to handle format[" << _format << "]\n";
194     return;
195   }
196 
197   this->dataPtr->bitmap = FreeImage_ConvertFromRawBits(const_cast<BYTE*>(_data),
198       _width, _height, scanlineBytes, bpp, redmask, greenmask, bluemask, true);
199 }
200 
201 //////////////////////////////////////////////////
Pitch() const202 int Image::Pitch() const
203 {
204   return FreeImage_GetLine(this->dataPtr->bitmap);
205 }
206 
207 //////////////////////////////////////////////////
RGBData(unsigned char ** _data,unsigned int & _count) const208 void Image::RGBData(unsigned char **_data, unsigned int &_count) const
209 {
210   FIBITMAP *tmp = FreeImage_ConvertTo24Bits(this->dataPtr->bitmap);
211   this->dataPtr->DataImpl(_data, _count, tmp);
212   FreeImage_Unload(tmp);
213 }
214 
215 //////////////////////////////////////////////////
Data(unsigned char ** _data,unsigned int & _count) const216 void Image::Data(unsigned char **_data, unsigned int &_count) const
217 {
218   this->dataPtr->DataImpl(_data, _count, this->dataPtr->bitmap);
219 }
220 
221 //////////////////////////////////////////////////
DataImpl(unsigned char ** _data,unsigned int & _count,FIBITMAP * _img) const222 void ImagePrivate::DataImpl(unsigned char **_data, unsigned int &_count,
223                         FIBITMAP *_img) const
224 {
225   int redmask = FI_RGBA_RED_MASK;
226   // int bluemask = 0x00ff0000;
227 
228   int greenmask = FI_RGBA_GREEN_MASK;
229   // int greenmask = 0x0000ff00;
230 
231   int bluemask = FI_RGBA_BLUE_MASK;
232   // int redmask = 0x000000ff;
233 
234   int scanWidth = FreeImage_GetLine(_img);
235 
236   if (*_data)
237     delete [] *_data;
238 
239   _count = scanWidth * FreeImage_GetHeight(_img);
240   *_data = new unsigned char[_count];
241 
242   FreeImage_ConvertToRawBits(reinterpret_cast<BYTE*>(*_data), _img,
243       scanWidth, FreeImage_GetBPP(_img), redmask, greenmask, bluemask, true);
244 
245 #ifdef FREEIMAGE_COLORORDER
246   // cppcheck-suppress ConfigurationNotChecked
247   if (FREEIMAGE_COLORORDER != FREEIMAGE_COLORORDER_RGB)
248   {
249 #else
250 #ifdef FREEIMAGE_BIGENDIAN
251   if (false)
252   {
253 #else
254   {
255 #endif
256 #endif
257 //  FIXME:  why shift by 2 pixels?
258 //  this breaks heighmaps by wrapping artificially
259 //    int i = 0;
260 //    for (unsigned int y = 0; y < this->Height(); ++y)
261 //    {
262 //      for (unsigned int x = 0; x < this->Width(); ++x)
263 //      {
264 //        std::swap((*_data)[i], (*_data)[i+2]);
265 //        unsigned int d = FreeImage_GetBPP(this->dataPtr->bitmap)/8;
266 //        i += d;
267 //      }
268 //    }
269   }
270 }
271 
272 //////////////////////////////////////////////////
273 unsigned int Image::Width() const
274 {
275   if (!this->Valid())
276     return 0;
277 
278   return FreeImage_GetWidth(this->dataPtr->bitmap);
279 }
280 
281 //////////////////////////////////////////////////
282 unsigned int Image::Height() const
283 {
284   if (!this->Valid())
285     return 0;
286 
287   return FreeImage_GetHeight(this->dataPtr->bitmap);
288 }
289 
290 //////////////////////////////////////////////////
291 unsigned int Image::BPP() const
292 {
293   if (!this->Valid())
294     return 0;
295 
296   return FreeImage_GetBPP(this->dataPtr->bitmap);
297 }
298 
299 //////////////////////////////////////////////////
300 math::Color Image::Pixel(unsigned int _x, unsigned int _y) const
301 {
302   math::Color clr;
303 
304   if (!this->Valid())
305     return clr;
306 
307   FREE_IMAGE_COLOR_TYPE type = FreeImage_GetColorType(this->dataPtr->bitmap);
308 
309   if (type == FIC_RGB || type == FIC_RGBALPHA)
310   {
311     RGBQUAD firgb;
312 
313     if (FreeImage_GetPixelColor(this->dataPtr->bitmap, _x, _y, &firgb) == FALSE)
314     {
315       ignerr << "Image: Coordinates out of range["
316         << _x << " " << _y << "] \n";
317       return clr;
318     }
319 
320 #ifdef FREEIMAGE_COLORORDER
321     // cppcheck-suppress ConfigurationNotChecked
322     if (FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB)
323       clr.Set(firgb.rgbRed, firgb.rgbGreen, firgb.rgbBlue);
324     else
325       clr.Set(firgb.rgbBlue, firgb.rgbGreen, firgb.rgbRed);
326 #else
327 #ifdef FREEIMAGE_BIGENDIAN
328     clr.Set(firgb.rgbRed, firgb.rgbGreen, firgb.rgbBlue);
329 #else
330     clr.Set(firgb.rgbBlue, firgb.rgbGreen, firgb.rgbRed);
331 #endif
332 #endif
333   }
334   else
335   {
336     BYTE byteValue;
337     if (FreeImage_GetPixelIndex(
338           this->dataPtr->bitmap, _x, _y, &byteValue) == FALSE)
339     {
340       ignerr << "Image: Coordinates out of range ["
341         << _x << " " << _y << "] \n";
342       return clr;
343     }
344 
345     clr.Set(byteValue, byteValue, byteValue);
346   }
347 
348   return clr;
349 }
350 
351 //////////////////////////////////////////////////
352 math::Color Image::AvgColor()
353 {
354   unsigned int x, y;
355   double rsum, gsum, bsum;
356   math::Color pixel;
357 
358   rsum = gsum = bsum = 0.0;
359   for (y = 0; y < this->Height(); ++y)
360   {
361     for (x = 0; x < this->Width(); ++x)
362     {
363       pixel = this->Pixel(x, y);
364       rsum += pixel.R();
365       gsum += pixel.G();
366       bsum += pixel.B();
367     }
368   }
369 
370   rsum /= (this->Width() * this->Height());
371   gsum /= (this->Width() * this->Height());
372   bsum /= (this->Width() * this->Height());
373 
374   return math::Color(rsum, gsum, bsum);
375 }
376 
377 //////////////////////////////////////////////////
378 math::Color Image::MaxColor() const
379 {
380   unsigned int x, y;
381   math::Color clr;
382   math::Color maxClr;
383 
384   maxClr.Set(0, 0, 0, 0);
385 
386   for (y = 0; y < this->Height(); y++)
387   {
388     for (x = 0; x < this->Width(); x++)
389     {
390       clr = this->Pixel(x, y);
391 
392       if (clr.R() + clr.G() + clr.B() > maxClr.R() + maxClr.G() + maxClr.B())
393       {
394         maxClr = clr;
395       }
396     }
397   }
398 
399   return maxClr;
400 }
401 
402 //////////////////////////////////////////////////
403 void Image::Rescale(int _width, int _height)
404 {
405   this->dataPtr->bitmap = FreeImage_Rescale(
406       this->dataPtr->bitmap, _width, _height, FILTER_LANCZOS3);
407 }
408 
409 //////////////////////////////////////////////////
410 bool Image::Valid() const
411 {
412   return this->dataPtr->bitmap != NULL;
413 }
414 
415 //////////////////////////////////////////////////
416 std::string Image::Filename() const
417 {
418   return this->dataPtr->fullName;
419 }
420 
421 //////////////////////////////////////////////////
422 Image::PixelFormatType Image::PixelFormat() const
423 {
424   Image::PixelFormatType fmt = UNKNOWN_PIXEL_FORMAT;
425   FREE_IMAGE_TYPE type = FreeImage_GetImageType(this->dataPtr->bitmap);
426 
427   unsigned int redMask = FreeImage_GetRedMask(this->dataPtr->bitmap);
428   unsigned int bpp = this->BPP();
429 
430   if (type == FIT_BITMAP)
431   {
432     if (bpp == 8)
433       fmt = L_INT8;
434     else if (bpp == 16)
435       fmt = L_INT16;
436     else if (bpp == 24)
437       redMask == 0xff0000 ? fmt = RGB_INT8 : fmt = BGR_INT8;
438     else if (bpp == 32)
439     {
440       redMask == 0xff0000 || redMask == 0xff000000 ?
441         fmt = RGBA_INT8 : fmt = BGRA_INT8;
442     }
443   }
444   else if (type == FIT_RGB16)
445     fmt = RGB_INT16;
446   else if (type == FIT_RGBF)
447     fmt = RGB_FLOAT32;
448   else if (type == FIT_UINT16 || type == FIT_INT16)
449     fmt = L_INT16;
450 
451   return fmt;
452 }
453 
454 /////////////////////////////////////////////////
455 Image::PixelFormatType Image::ConvertPixelFormat(const std::string &_format)
456 {
457   // Handle old format strings
458   if (_format == "L8" || _format == "L_INT8")
459     return L_INT8;
460   else if (_format == "R8G8B8" || _format == "RGB_INT8")
461     return RGB_INT8;
462 
463   for (unsigned int i = 0; i < PIXEL_FORMAT_COUNT; ++i)
464     if (PixelFormatNames[i] == _format)
465       return static_cast<PixelFormatType>(i);
466 
467   return UNKNOWN_PIXEL_FORMAT;
468 }
469