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