libcrn  3.9.5
A document image processing library
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CRNImage.cpp
Go to the documentation of this file.
1 /* Copyright 2006-2016 Yann LEYDIER, INSA-Lyon, ENS-Lyon
2  *
3  * This file is part of libcrn.
4  *
5  * libcrn is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * libcrn 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
13  * GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with libcrn. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * file: CRNImage.cpp
19  * \author Yann LEYDIER
20  */
21 
22 #include <CRNImage/CRNImage.h>
23 #include <CRNImage/CRNImageBW.h>
24 #include <CRNImage/CRNImageGray.h>
25 #include <CRNImage/CRNImageRGB.h>
26 #include <CRNException.h>
27 #include <CRNIO/CRNFileShield.h>
28 #include <typeindex>
29 #include <CRNi18n.h>
30 
31 #ifdef CRN_USING_GDKPB
32 # include <gdk-pixbuf/gdk-pixbuf.h>
33 #endif
34 
35 #ifdef CRN_USING_GDIPLUS
36 # include <windows.h>
37 # include <objidl.h>
38 # include <gdiplus.h>
39 #endif
40 
41 #ifdef CRN_USING_LIBPNG
42 # include <png.h>
43 #endif
44 
45 #ifdef CRN_USING_LIBJPEG
46 # include <jpeglib.h>
47 # include <setjmp.h>
48 #endif
49 
50 using namespace crn;
51 using namespace crn::literals;
52 
53 ImageBase::~ImageBase() = default;
54 
56 Rect ImageBase::GetBBox() const noexcept
57 {
58  return Rect(0, 0, int(width) - 1, int(height) - 1);
59 }
60 
62 enum ColorType { RGB, GRAY, BW };
63 
68 void fclose_if_not_null(FILE *f)
69 {
70  if (f)
71  fclose(f);
72 }
73 
74 #ifdef CRN_USING_LIBPNG
75 // cf http://www.libpng.org/pub/png/libpng-1.2.5-manual.html
76 static std::pair<UImage, String> load_libpng(const Path &filename)
77 {
78  // libpng does not support URIs
79  auto fname = filename;
80  fname.ToLocal();
81 
82  /* open image file */
83  std::unique_ptr<FILE, decltype(fclose_if_not_null)*> fp(fopen(fname.CStr(), "rb"), fclose_if_not_null);
84  if (!fp)
85  return std::make_pair(UImage{}, String(_("Cannot open file ")) + fname);
86 
87  /* read magic number */
88  png_byte magic[8];
89  size_t readsize = fread(magic, 1, sizeof(magic), fp.get());
90  if (!readsize)
91  return std::make_pair(UImage{}, String(U"Empty file."));
92 
93  /* check for valid magic number */
94  if (png_sig_cmp(magic, 0, sizeof(magic)))
95  return std::make_pair(UImage{}, String(U"Not a PNG file."));
96 
97 
98  /* create a png read struct */
99  png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
100  if (!png_ptr)
101  return std::make_pair(UImage{}, String(_("Cannot create PNG reader.")));
102 
103  /* create a png info struct */
104  png_infop info_ptr = png_create_info_struct(png_ptr);
105  if (!info_ptr)
106  {
107  png_destroy_read_struct(&png_ptr, NULL, NULL);
108  return std::make_pair(UImage{}, String(_("Cannot create PNG info.")));
109  }
110 
111  /* Set up the error handling */
112  if (setjmp(png_jmpbuf(png_ptr)))
113  {
114  png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
115  return std::make_pair(UImage{}, String(_("Error while reading the PNG file ")) + fname);
116  }
117 
118  /* setup libpng for using standard C fread() function with our FILE pointer */
119  png_init_io(png_ptr, fp.get());
120  /* tell libpng that we have already read the magic number */
121  png_set_sig_bytes(png_ptr, sizeof(magic));
122 
123  /* Read informations */
124  png_read_info(png_ptr, info_ptr);
125  png_uint_32 width, height;
126  int bit_depth, color_type, interlace_type, compression_type, filter_method;
127  bool modified = false;
128  png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method);
129 
130  ColorType read_type(RGB);
131 
132  auto col0 = pixel::BWBlack;
133  auto col1 = pixel::BWWhite;
134  if (bit_depth == 1)
135  { // binary image
136  png_set_packing(png_ptr);
137  modified = true;
138  read_type = BW;
139  if (png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE))
140  { // has a palette -_-
141  // we convert it to black & white
142  png_colorp pal;
143  int numpal;
144  png_get_PLTE(png_ptr, info_ptr, &pal, &numpal);
145  if (numpal >= 2)
146  {
147  int m0 = pal[0].red + pal[0].green + pal[0].blue;
148  int m1 = pal[1].red + pal[1].green + pal[1].blue;
149  if (m1 < m0) // color 1 is darker than color 0
150  {
151  col0 = pixel::BWWhite;
152  col1 = pixel::BWBlack;
153  }
154  }
155  }
156  }
157  else if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
158  { // Grayscale image
159  if (bit_depth < 8)
160  {
161  png_set_expand_gray_1_2_4_to_8(png_ptr);
162  modified = true;
163  }
164  read_type = GRAY;
165  }
166  else if (color_type == PNG_COLOR_TYPE_PALETTE)
167  { // Changes palletted image to RGB
168  png_set_palette_to_rgb(png_ptr); // Just an alias of png_set_expand()
169  modified = true;
170  }
171  // Transform 16 bits samples to 8 bits samples
172  if (bit_depth == 16)
173  {
174  png_set_strip_16(png_ptr);
175  modified = true;
176  }
177  // Remove alpha channel
178  if (color_type & PNG_COLOR_MASK_ALPHA)
179  {
180  png_set_strip_alpha(png_ptr);
181  modified = true;
182  }
183 
184  // If any of the above were applied,
185  if (modified)
186  {
187  // After setting the transformations, libpng can update your png_info structure to reflect any transformations
188  // you've requested with this call. This is most useful to update the info structure's rowbytes field so you can
189  // use it to allocate your image memory. This function will also update your palette with the correct screen_gamma
190  // and background if these have been given with the calls above.
191  png_read_update_info(png_ptr, info_ptr);
192 
193  // Just in case any of these have changed
194  png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL);
195  }
196 
197  int channels = png_get_channels(png_ptr, info_ptr);
198 
199  png_bytepp row_pointers;
200 
201  /* we can now allocate memory for storing pixel data */
202  row_pointers = (png_bytep*)malloc(sizeof(png_byte*) * height);
203  for (size_t i = 0; i < height ; ++i)
204  {
205 #if (PNG_LIBPNG_VER > 10300)
206  row_pointers[i] = (png_bytep)malloc(png_get_rowbytes(png_ptr, info_ptr));
207 #else
208  row_pointers[i] = (png_bytep)malloc(info_ptr->rowbytes);
209 #endif
210  }
211  /* Read PNG file */
212  png_read_image(png_ptr, row_pointers);
213 
214  /* Conversion */
215  UImage img;
216  switch (read_type)
217  {
218  case RGB:
219  {
220  auto irgb = std::make_unique<ImageRGB>(size_t(width), size_t(height));
221  for (size_t y = 0; y < height; y++)
222  for (size_t x = 0; x < width; x++)
223  {
224  png_byte *pixel = &((row_pointers[y])[x*channels]);
225  irgb->At(x, y) = {pixel[0], pixel[1], pixel[2]};
226  }
227  img = std::move(irgb);
228  }
229  break;
230  case GRAY:
231  {
232  auto ig = std::make_unique<ImageGray>(size_t(width), size_t(height));
233  for (size_t y = 0; y < height; y++)
234  for (size_t x = 0; x < width; x++)
235  {
236  png_byte *pixel = &((row_pointers[y])[x]);
237  ig->At(x, y) = *pixel;
238  }
239  img = std::move(ig);
240  }
241  break;
242  case BW:
243  {
244  auto ibw = std::make_unique<ImageBW>(size_t(width), size_t(height));
245  for (size_t y = 0; y < height; y++)
246  for (size_t x = 0; x < width; x++)
247  {
248  png_byte *pixel = &((row_pointers[y])[x]);
249  ibw->At(x, y) = (*pixel) ? col1 : col0;
250  }
251  img = std::move(ibw);
252  }
253  break;
254  }
255 
256  /* read metadata */
257 /*
258  png_textp txt;
259  int nb = png_get_text(png_ptr, info_ptr, &txt, NULL);
260  CRNVerbose(nb);
261  for (int tmp = 0; tmp < nb; ++tmp)
262  {
263  String s(U"compression: ");
264  s += txt[tmp].compression;
265  s += U"\nkey: ";
266  s += txt[tmp].key;
267  s += U"\ntext: ";
268  s += txt[tmp].text;
269  s += U"\ntext length: ";
270  s += txt[tmp].text_length;
271 #ifdef PNG_iTXt_SUPPORTED // not supported in 1.2 branch
272  s += U"\ni text length: ";
273  s += txt[tmp].itxt_length;
274  s += U"\nlang: "
275  s += txt[tmp].lang;
276  s += U"\nlang key: "
277  s += txt[tmp].lang_key;
278 #endif
279  CRNVerbose(s);
280  }
281 */
282 
283  /* finish decompression and release memory */
284  png_read_end(png_ptr, NULL);
285  png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
286  for (size_t i = 0; i < height ; ++i)
287  {
288  free(row_pointers[i]);
289  }
290  free(row_pointers);
291 
292  return std::make_pair(std::move(img), String(U""));
293 }
294 #endif
295 
296 #ifdef CRN_USING_LIBJPEG
297 struct crn_jpeg_error_mgr
298 {
299  struct jpeg_error_mgr pub; /* "public" fields */
300  jmp_buf setjmp_buffer; /* for return to caller */
301 };
302 
303 typedef struct crn_jpeg_error_mgr * crn_jpeg_error_ptr;
304 
305 void crn_jpeg_error_exit(j_common_ptr cinfo)
306 {
307  crn_jpeg_error_ptr myerr = (crn_jpeg_error_ptr) cinfo->err;
308  /* display the message. */
309  //(*cinfo->err->output_message) (cinfo);
310  /* Return control to the setjmp point */
311  longjmp(myerr->setjmp_buffer, 1);
312 }
313 
314 static std::pair<UImage, String> load_libjpeg(const Path &filename)
315 {
316  // libjpeg does not support URIs
317  auto fname(filename);
318  fname.ToLocal();
319 
320  /* open image file */
321  std::unique_ptr<FILE, decltype(fclose_if_not_null)*> fp(fopen(fname.CStr(), "rb"), fclose_if_not_null);
322  if (!fp)
323  return std::make_pair(UImage{}, String(_("Cannot open file ")) + fname);
324 
325  struct jpeg_decompress_struct cinfo;
326  struct crn_jpeg_error_mgr jerr;
327  cinfo.err = jpeg_std_error(&jerr.pub);
328  jerr.pub.error_exit = crn_jpeg_error_exit;
329  if (setjmp(jerr.setjmp_buffer))
330  {
331  jpeg_destroy_decompress(&cinfo);
332  return std::make_pair(UImage{}, String(_("Not a JPEG file.")));
333  }
334  else
335  {
336  jpeg_create_decompress(&cinfo);
337  jpeg_stdio_src(&cinfo, fp.get());
338  jpeg_read_header(&cinfo, TRUE);
339  jpeg_start_decompress(&cinfo);
340  int w = cinfo.output_width;
341  int h = cinfo.output_height;
342  int bpp = cinfo.out_color_components;
343  UImageRGB irgb;
344  UImageGray ig;
345  if (bpp == 3)
346  {
347  irgb = std::make_unique<ImageRGB>(w, h);
348  }
349  else if (bpp == 1)
350  {
351  ig = std::make_unique<ImageGray>(w, h);
352  }
353  else
354  {
355  jpeg_finish_decompress(&cinfo);
356  jpeg_destroy_decompress(&cinfo);
357  return std::make_pair(UImage{}, String(_("JPEG file contains unnatural bytes per pixel count.")));
358  }
359  JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, w * bpp, 1);
360  int y = 0;
361  while (cinfo.output_scanline < cinfo.output_height)
362  {
363  (void) jpeg_read_scanlines(&cinfo, buffer, 1);
364  for (int x = 0; x < w; ++x)
365  {
366  if (bpp == 3)
367  {
368  irgb->At(x, y) = { buffer[0][3 * x], buffer[0][3 * x + 1], buffer[0][3 * x + 2] };
369  }
370  else
371  {
372  ig->At(x, y) = buffer[0][x];
373  }
374  }
375  y += 1;
376  }
377 
378  // read comments
379  // TODO
380  // jpeg_read_header() then cinfo->marker_list (linked list)
381 
382  jpeg_finish_decompress(&cinfo);
383  jpeg_destroy_decompress(&cinfo);
384  if (bpp == 3)
385  return std::make_pair(std::move(irgb), String(U""));
386  else
387  return std::make_pair(std::move(ig), String(U""));
388  }
389 }
390 #endif
391 
392 #ifdef CRN_USING_GDKPB
393 static std::pair<UImage, String> load_gdkpixbuf(const Path &fname)
394 {
395  GError *err = NULL;
396 
397  gsize i, o;
398  gchar *filename;
399  if (g_utf8_validate(fname.CStr(), -1, NULL))
400  {
401  filename = g_filename_from_utf8(fname.CStr(), -1, &i, &o, &err);
402  }
403  else
404  {
405  gchar *utfname;
406  utfname = g_locale_to_utf8(fname.CStr(), -1, &i, &o, &err);
407  if (!utfname)
408  {
409  String msg((char*)err->message);
410  g_error_free(err);
411  return std::make_pair(UImage{}, String(_("Cannot convert filename string. ")) + msg);
412  }
413  filename = g_filename_from_utf8(utfname, -1, &i, &o, &err);
414  g_free(utfname);
415  }
416  if (!filename)
417  {
418  String msg((char*)err->message);
419  g_error_free(err);
420  return std::make_pair(UImage{}, String(_("Cannot convert filename string. ")) + msg);
421  }
422  GdkPixbuf *pb = gdk_pixbuf_new_from_file(filename, &err);
423  g_free(filename);
424 
425  if (!pb)
426  {
427  String msg((char*)err->message);
428  g_error_free(err);
429  return std::make_pair(UImage{}, String(_("Cannot open image. ")) + msg);
430  }
431  int w = gdk_pixbuf_get_width(pb);
432  int h = gdk_pixbuf_get_height(pb);
433  int rs = gdk_pixbuf_get_rowstride(pb);
434  int word = gdk_pixbuf_get_has_alpha(pb) ? 4 : 3;
435 
436  UImageRGB img(std::make_unique<ImageRGB>(w, h));
437  guchar *oripix = gdk_pixbuf_get_pixels(pb);
438  for (int y = 0; y < h; y++)
439  for (int x = 0; x < w; x++)
440  {
441  const auto o2 = word * x + y * rs;
442  img->At(x, y) = { oripix[o2], oripix[o2 + 1], oripix[o2 + 2] };
443  }
444  g_object_unref(pb);
445  return std::make_pair(std::move(img), String(U""));
446 }
447 #endif
448 
449 #ifdef CRN_USING_GDIPLUS
450 struct crn::ImageBase::gditoken
451 {
452  Gdiplus::GdiplusStartupInput gdiplusStartupInput;
453  ULONG_PTR gdiplusToken;
454  gditoken()
455  {
456  Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
457  }
458  ~gditoken()
459  {
460  Gdiplus::GdiplusShutdown(gdiplusToken);
461  }
462 };
463 std::unique_ptr<ImageBase::gditoken> ImageBase::gdiinit;
464 
466  ImageBase::gdiinit = std::make_unique<ImageBase::gditoken>();
468 
469 static std::pair<UImage, String> load_gdiplus(const Path &filename)
470 {
471  // gdiplus does not support URIs
472  Path winname(filename);
473  winname.ToWindows();
474  const auto newsize = winname.Size() + 1;
475  auto wcstring = std::vector<wchar_t>(newsize);
476  auto convertedChars = size_t(0);
477  mbstowcs_s(&convertedChars, wcstring.data(), newsize, winname.CStr(), _TRUNCATE);
478  Gdiplus::Bitmap *img = Gdiplus::Bitmap::FromFile(wcstring.data(), FALSE);
479  Gdiplus::ColorPalette *pal = NULL;
480  if (!img)
481  {
482  return std::make_pair(UImage{}, String(_("Gdi+ could not load the image.")));
483  }
484  Gdiplus::Status stat = img->GetLastStatus();
485  if (stat != Gdiplus::Ok)
486  {
487  delete img;
488  return std::make_pair(UImage{}, String(_("Gdi+ could not load the image correctly. Error number ") + int(stat)));
489  }
490  int w = img->GetWidth();
491  int h = img->GetHeight();
492  Gdiplus::PixelFormat fmt = img->GetPixelFormat();
493  bool isrgb = false;
494  UImage ret;
495  if (fmt == PixelFormat1bppIndexed)
496  { // BW
497  auto rret = std::make_unique<ImageBW>(w, h);
498  Gdiplus::BitmapData bitmapData;
499  auto clip = Gdiplus::Rect(0, 0, img->GetWidth(), img->GetHeight());
500  Gdiplus::Status res = img->LockBits(&clip, Gdiplus::ImageLockModeRead, PixelFormat24bppRGB, &bitmapData);
501  if (res != Gdiplus::Ok)
502  {
503  delete img;
504  return std::make_pair(UImage{}, String(_("Could not lock pixels. Error number ") + int(res)));
505  }
506  auto *pixels = (uint8_t*)bitmapData.Scan0;
507 //#pragma omp parallel for
508  FOREACHPIXEL(x, y, *rret)
509  {
510  rret->At(x, y) = pixels[3 * x + y * bitmapData.Stride] ? true : false;
511  }
512  img->UnlockBits(&bitmapData);
513  ret = std::move(rret);
514  }
515  else if (fmt = PixelFormat8bppIndexed)
516  { // possibly Gray
517  auto psize = img->GetPaletteSize();
518  pal = (Gdiplus::ColorPalette*)malloc(psize);
519  img->GetPalette(pal, psize);
520  if (pal->Count == 0) // WTF!!!
521  isrgb = true;
522  else if ((pal->Flags & Gdiplus::PaletteFlagsGrayScale) == 0)
523  { // check all colors -_-
524  for (size_t tmp = 0; tmp < pal->Count; ++tmp)
525  {
526  unsigned int col = pal->Entries[tmp];
527  auto b = uint8_t(col & 0xFF);
528  col >>= 8;
529  auto g = uint8_t(col & 0xFF);
530  col >>= 8;
531  auto r = uint8_t(col & 0xFF);
532  /*
533  crn::String s(tmp);
534  s += L": r=";
535  s += int(r);
536  s += L": g=";
537  s += int(g);
538  s += L": b=";
539  s += int(b);
540  CRNVerbose(s);
541  */
542  if ((r != g) || (r != b))
543  {
544  isrgb = true;
545  break;
546  }
547  }
548  }
549  if (!isrgb)
550  { // grayscale image
551  auto rret = std::make_unique<ImageGray>(w, h);
552  Gdiplus::BitmapData bitmapData;
553  auto clip = Gdiplus::Rect(0, 0, img->GetWidth(), img->GetHeight());
554  Gdiplus::Status res = img->LockBits(&clip, Gdiplus::ImageLockModeRead, PixelFormat24bppRGB, &bitmapData);
555  if (res != Gdiplus::Ok)
556  {
557  delete img;
558  return std::make_pair(UImage{}, String(_("Could not lock pixels. Error number ") + int(res)));
559  }
560  auto *pixels = (uint8_t*)bitmapData.Scan0;
561 //#pragma omp parallel for
562  FOREACHPIXEL(x, y, *rret)
563  {
564  rret->At(x, y) = pixels[3 * x + y * bitmapData.Stride];
565  }
566  img->UnlockBits(&bitmapData);
567  ret = std::move(rret);
568  }
569  }
570  else
571  {
572  isrgb = true;
573  }
574  if (isrgb)
575  {
576  auto rret = std::make_unique<ImageRGB>(w, h);
577  Gdiplus::BitmapData bitmapData;
578  auto clip = Gdiplus::Rect(0, 0, img->GetWidth(), img->GetHeight());
579  Gdiplus::Status res = img->LockBits(&clip, Gdiplus::ImageLockModeRead, PixelFormat24bppRGB, &bitmapData);
580  if (res != Gdiplus::Ok)
581  {
582  delete img;
583  return std::make_pair(UImage{}, String(_("Could not lock pixels. Error number ") + int(res)));
584  }
585  auto *pixels = (uint8_t*)bitmapData.Scan0;
586 //#pragma omp parallel for
587  FOREACHPIXEL(x, y, *rret)
588  {
589  size_t offset = x * 3 + y * bitmapData.Stride;
590  rret->At(x, y) = { pixels[offset + 2], pixels[offset + 1], pixels[offset] };
591  }
592  img->UnlockBits(&bitmapData);
593  ret = std::move(rret);
594  }
595  delete img;
596  free(pal);
597  return std::make_pair(std::move(ret), String{});
598 }
599 #endif
600 
601 
602 
609 UImage crn::NewImageFromFile(const Path &fname)
610 {
611  if (!fname)
612  throw ExceptionInvalidArgument(StringUTF8("UImage NewImageFromFile(const Path &fname): ") +
613  _("Null file name."));
614 
615  std::lock_guard<std::mutex> lock(crn::FileShield::GetMutex(fname)); // lock the file
616 
617  std::pair<UImage, String> res(std::make_pair(UImage{}, String(U"")));
618  String errors;
619 #ifdef CRN_USING_LIBPNG
620  if (res.first.get() == nullptr)
621  {
622  res = load_libpng(fname);
623  errors += U" " + res.second;
624  }
625 #endif // CRN_USING_LIBPNG
626 #ifdef CRN_USING_LIBJPEG
627  if (res.first.get() == nullptr)
628  {
629  res = load_libjpeg(fname);
630  errors += U" " + res.second;
631  }
632 #endif // CRN_USING_LIBJPEG
633 #ifdef CRN_USING_GDIPLUS
634  if (res.first == NULL)
635  {
636  res = load_gdiplus(fname);
637  errors += U" " + res.second;
638  }
639 #endif // CRN_USING_GDIPLUS
640 #ifdef CRN_USING_GDKPB
641  if (res.first.get() == nullptr)
642  {
643  res = load_gdkpixbuf(fname);
644  errors += U" " + res.second;
645  }
646 #endif // CRN_USING_GDKPB
647  if (res.first.get() == nullptr)
648  throw ExceptionIO(StringUTF8("UImage NewImageFromFile(const Path &fname): ") +
649  _("No decoder could open the file ") + StringUTF8{ fname } + "\n" + errors.CStr());
650  return std::move(res.first);
651 }
652 
660 UImageRGB crn::NewImageRGBFromFile(const Path &fname)
661 {
662  auto img = NewImageFromFile(fname);
663  if (typeid(ImageRGB) == typeid(*img))
664  return std::unique_ptr<ImageRGB>(static_cast<ImageRGB*>(img.release()));
665  auto *ig = dynamic_cast<const ImageGray*>(img.get());
666  if (ig)
667  return std::make_unique<ImageRGB>(*ig);
668  auto *ibw = dynamic_cast<const ImageBW*>(img.get());
669  if (ibw)
670  return std::make_unique<ImageRGB>(*ibw);
671  throw ExceptionDomain("NewImageRGBFromFile(): "_s + _("unknown image format."));
672 }
673 
681 UImageGray crn::NewImageGrayFromFile(const Path &fname)
682 {
683  auto img = NewImageFromFile(fname);
684  auto *irgb = dynamic_cast<const ImageRGB*>(img.get());
685  if (irgb)
686  return MoveUnique(MakeImageGray(*irgb));
687  if (typeid(ImageGray) == typeid(*img))
688  return std::unique_ptr<ImageGray>(static_cast<ImageGray*>(img.release()));
689  auto *ibw = dynamic_cast<const ImageBW*>(img.get());
690  if (ibw)
691  return std::make_unique<ImageGray>(*ibw);
692  throw ExceptionDomain("NewImageGrayFromFile(): "_s + _("unknown image format."));
693 }
694 
702 UImageBW crn::NewImageBWFromFile(const Path &fname)
703 {
704  auto img = NewImageFromFile(fname);
705  auto *irgb = dynamic_cast<const ImageRGB*>(img.get());
706  if (irgb)
707  return MoveUnique(MakeImageBW(MakeImageGray(*irgb)));
708  auto *ig = dynamic_cast<const ImageGray*>(img.get());
709  if (ig)
710  return MoveUnique(MakeImageBW(*ig));
711  if (typeid(ImageBW) == typeid(*img))
712  return std::unique_ptr<ImageBW>(static_cast<ImageBW*>(img.release()));
713  throw ExceptionDomain("NewImageBWFromFile(): "_s + _("unknown image format."));
714 }
Abstract class for images.
Definition: CRNImage.h:141
virtual ~ImageBase()
ColorType
Definition: CRNImage.cpp:62
ImageBW MakeImageBW(const ImageGray &img)
UImageBW NewImageBWFromFile(const Path &fname)
Loads an image from a file and converts it if necessary.
Definition: CRNImage.cpp:702
#define _(String)
Definition: CRNi18n.h:51
std::unique_ptr< ImageBase > UImage
Definition: CRNImage.h:105
const char * CStr() const
Conversion to UTF8 cstring.
Definition: CRNString.cpp:167
#define CRN_END_CLASS_CONSTRUCTOR(classname)
Defines a class constructor.
Definition: CRNObject.h:198
#define FOREACHPIXEL(x, y, img)
Convenience macro to sweep an image.
Definition: CRNImage.h:37
ImageGray MakeImageGray(const ImageRGB &img)
static std::mutex & GetMutex(const Path &fname)
Gets the mutex associated to a file.
A UTF32 character string class.
Definition: CRNString.h:61
A generic domain error.
Definition: CRNException.h:83
std::unique_ptr< T > MoveUnique(T &&v)
Definition: CRNType.h:337
UImageGray NewImageGrayFromFile(const Path &fname)
Loads an image from a file and converts it if necessary.
Definition: CRNImage.cpp:681
const char * CStr() const noexcept
Conversion to UTF8 cstring.
UImageRGB NewImageRGBFromFile(const Path &fname)
Loads an image from a file and converts it if necessary.
Definition: CRNImage.cpp:660
A convenience class for file paths.
Definition: CRNPath.h:39
void fclose_if_not_null(FILE *f)
Definition: CRNImage.cpp:68
Definition: CRNImage.cpp:62
UImage NewImageFromFile(const Path &fname)
Loads an image from a file.
Definition: CRNImage.cpp:609
Definition: CRNImage.cpp:62
Rect GetBBox() const noexcept
Definition: CRNImage.cpp:56
Path & ToLocal()
Converts the path to the local format.
Definition: CRNPath.cpp:534
A character string class.
Definition: CRNStringUTF8.h:49
I/O error.
Definition: CRNException.h:179
#define TRUE
Definition: CRNProp3.cpp:28
Base class for images.
Definition: CRNImage.h:46
Invalid argument error (e.g.: nullptr pointer)
Definition: CRNException.h:107
#define CRN_BEGIN_CLASS_CONSTRUCTOR(classname)
Defines a class constructor.
Definition: CRNObject.h:185
A rectangle class.
Definition: CRNRect.h:46