31 #ifdef CRN_USING_GDKPB
32 # include <gdk-pixbuf/gdk-pixbuf.h>
35 #ifdef CRN_USING_GDIPLUS
41 #ifdef CRN_USING_LIBPNG
45 #ifdef CRN_USING_LIBJPEG
51 using namespace crn::literals;
58 return Rect(0, 0,
int(width) - 1,
int(height) - 1);
74 #ifdef CRN_USING_LIBPNG
76 static std::pair<UImage, String> load_libpng(
const Path &filename)
79 auto fname = filename;
83 std::unique_ptr<FILE, decltype(fclose_if_not_null)*> fp(fopen(fname.CStr(),
"rb"),
fclose_if_not_null);
85 return std::make_pair(
UImage{},
String(
_(
"Cannot open file ")) + fname);
89 size_t readsize = fread(magic, 1,
sizeof(magic), fp.get());
94 if (png_sig_cmp(magic, 0,
sizeof(magic)))
95 return std::make_pair(
UImage{},
String(U
"Not a PNG file."));
99 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
101 return std::make_pair(
UImage{},
String(
_(
"Cannot create PNG reader.")));
104 png_infop info_ptr = png_create_info_struct(png_ptr);
107 png_destroy_read_struct(&png_ptr, NULL, NULL);
108 return std::make_pair(
UImage{},
String(
_(
"Cannot create PNG info.")));
112 if (setjmp(png_jmpbuf(png_ptr)))
114 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
115 return std::make_pair(
UImage{},
String(
_(
"Error while reading the PNG file ")) + fname);
119 png_init_io(png_ptr, fp.get());
121 png_set_sig_bytes(png_ptr,
sizeof(magic));
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);
132 auto col0 = pixel::BWBlack;
133 auto col1 = pixel::BWWhite;
136 png_set_packing(png_ptr);
139 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE))
144 png_get_PLTE(png_ptr, info_ptr, &pal, &numpal);
147 int m0 = pal[0].red + pal[0].green + pal[0].blue;
148 int m1 = pal[1].red + pal[1].green + pal[1].blue;
151 col0 = pixel::BWWhite;
152 col1 = pixel::BWBlack;
157 else if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
161 png_set_expand_gray_1_2_4_to_8(png_ptr);
166 else if (color_type == PNG_COLOR_TYPE_PALETTE)
168 png_set_palette_to_rgb(png_ptr);
174 png_set_strip_16(png_ptr);
178 if (color_type & PNG_COLOR_MASK_ALPHA)
180 png_set_strip_alpha(png_ptr);
191 png_read_update_info(png_ptr, info_ptr);
194 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL);
197 int channels = png_get_channels(png_ptr, info_ptr);
199 png_bytepp row_pointers;
202 row_pointers = (png_bytep*)malloc(
sizeof(png_byte*) * height);
203 for (
size_t i = 0; i < height ; ++i)
205 #if (PNG_LIBPNG_VER > 10300)
206 row_pointers[i] = (png_bytep)malloc(png_get_rowbytes(png_ptr, info_ptr));
208 row_pointers[i] = (png_bytep)malloc(info_ptr->rowbytes);
212 png_read_image(png_ptr, row_pointers);
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++)
224 png_byte *pixel = &((row_pointers[y])[x*channels]);
225 irgb->At(x, y) = {pixel[0], pixel[1], pixel[2]};
227 img = std::move(irgb);
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++)
236 png_byte *pixel = &((row_pointers[y])[x]);
237 ig->At(x, y) = *pixel;
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++)
248 png_byte *pixel = &((row_pointers[y])[x]);
249 ibw->At(x, y) = (*pixel) ? col1 : col0;
251 img = std::move(ibw);
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)
288 free(row_pointers[i]);
292 return std::make_pair(std::move(img),
String(U
""));
296 #ifdef CRN_USING_LIBJPEG
297 struct crn_jpeg_error_mgr
299 struct jpeg_error_mgr pub;
300 jmp_buf setjmp_buffer;
303 typedef struct crn_jpeg_error_mgr * crn_jpeg_error_ptr;
305 void crn_jpeg_error_exit(j_common_ptr cinfo)
307 crn_jpeg_error_ptr myerr = (crn_jpeg_error_ptr) cinfo->err;
311 longjmp(myerr->setjmp_buffer, 1);
314 static std::pair<UImage, String> load_libjpeg(
const Path &filename)
317 auto fname(filename);
321 std::unique_ptr<FILE, decltype(fclose_if_not_null)*> fp(fopen(fname.CStr(),
"rb"),
fclose_if_not_null);
323 return std::make_pair(
UImage{},
String(
_(
"Cannot open file ")) + fname);
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))
331 jpeg_destroy_decompress(&cinfo);
332 return std::make_pair(
UImage{},
String(
_(
"Not a JPEG file.")));
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;
347 irgb = std::make_unique<ImageRGB>(w, h);
351 ig = std::make_unique<ImageGray>(w, h);
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.")));
359 JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, w * bpp, 1);
361 while (cinfo.output_scanline < cinfo.output_height)
363 (void) jpeg_read_scanlines(&cinfo, buffer, 1);
364 for (
int x = 0; x < w; ++x)
368 irgb->At(x, y) = { buffer[0][3 * x], buffer[0][3 * x + 1], buffer[0][3 * x + 2] };
372 ig->At(x, y) = buffer[0][x];
382 jpeg_finish_decompress(&cinfo);
383 jpeg_destroy_decompress(&cinfo);
385 return std::make_pair(std::move(irgb),
String(U
""));
387 return std::make_pair(std::move(ig),
String(U
""));
392 #ifdef CRN_USING_GDKPB
393 static std::pair<UImage, String> load_gdkpixbuf(
const Path &fname)
399 if (g_utf8_validate(fname.
CStr(), -1, NULL))
401 filename = g_filename_from_utf8(fname.
CStr(), -1, &i, &o, &err);
406 utfname = g_locale_to_utf8(fname.
CStr(), -1, &i, &o, &err);
409 String msg((
char*)err->message);
411 return std::make_pair(
UImage{},
String(
_(
"Cannot convert filename string. ")) + msg);
413 filename = g_filename_from_utf8(utfname, -1, &i, &o, &err);
418 String msg((
char*)err->message);
420 return std::make_pair(
UImage{},
String(
_(
"Cannot convert filename string. ")) + msg);
422 GdkPixbuf *pb = gdk_pixbuf_new_from_file(filename, &err);
427 String msg((
char*)err->message);
429 return std::make_pair(
UImage{},
String(
_(
"Cannot open image. ")) + msg);
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;
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++)
441 const auto o2 = word * x + y * rs;
442 img->At(x, y) = { oripix[o2], oripix[o2 + 1], oripix[o2 + 2] };
445 return std::make_pair(std::move(img),
String(U
""));
449 #ifdef CRN_USING_GDIPLUS
450 struct crn::ImageBase::gditoken
452 Gdiplus::GdiplusStartupInput gdiplusStartupInput;
453 ULONG_PTR gdiplusToken;
456 Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput,
nullptr);
460 Gdiplus::GdiplusShutdown(gdiplusToken);
463 std::unique_ptr<ImageBase::gditoken> ImageBase::gdiinit;
472 Path winname(filename);
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;
482 return std::make_pair(UImage{},
String(
_(
"Gdi+ could not load the image.")));
484 Gdiplus::Status stat = img->GetLastStatus();
485 if (stat != Gdiplus::Ok)
488 return std::make_pair(UImage{},
String(
_(
"Gdi+ could not load the image correctly. Error number ") +
int(stat)));
490 int w = img->GetWidth();
491 int h = img->GetHeight();
492 Gdiplus::PixelFormat fmt = img->GetPixelFormat();
495 if (fmt == PixelFormat1bppIndexed)
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)
504 return std::make_pair(UImage{},
String(
_(
"Could not lock pixels. Error number ") +
int(res)));
506 auto *pixels = (uint8_t*)bitmapData.Scan0;
510 rret->At(x, y) = pixels[3 * x + y * bitmapData.Stride] ?
true :
false;
512 img->UnlockBits(&bitmapData);
513 ret = std::move(rret);
515 else if (fmt = PixelFormat8bppIndexed)
517 auto psize = img->GetPaletteSize();
518 pal = (Gdiplus::ColorPalette*)malloc(psize);
519 img->GetPalette(pal, psize);
522 else if ((pal->Flags & Gdiplus::PaletteFlagsGrayScale) == 0)
524 for (
size_t tmp = 0; tmp < pal->Count; ++tmp)
526 unsigned int col = pal->Entries[tmp];
527 auto b = uint8_t(col & 0xFF);
529 auto g = uint8_t(col & 0xFF);
531 auto r = uint8_t(col & 0xFF);
542 if ((r != g) || (r != b))
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)
558 return std::make_pair(UImage{},
String(
_(
"Could not lock pixels. Error number ") +
int(res)));
560 auto *pixels = (uint8_t*)bitmapData.Scan0;
564 rret->At(x, y) = pixels[3 * x + y * bitmapData.Stride];
566 img->UnlockBits(&bitmapData);
567 ret = std::move(rret);
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)
583 return std::make_pair(UImage{},
String(
_(
"Could not lock pixels. Error number ") +
int(res)));
585 auto *pixels = (uint8_t*)bitmapData.Scan0;
589 size_t offset = x * 3 + y * bitmapData.Stride;
590 rret->At(x, y) = { pixels[offset + 2], pixels[offset + 1], pixels[offset] };
592 img->UnlockBits(&bitmapData);
593 ret = std::move(rret);
597 return std::make_pair(std::move(ret),
String{});
613 _(
"Null file name."));
617 std::pair<UImage, String> res(std::make_pair(UImage{},
String(U
"")));
619 #ifdef CRN_USING_LIBPNG
620 if (res.first.get() ==
nullptr)
622 res = load_libpng(fname);
623 errors += U
" " + res.second;
625 #endif // CRN_USING_LIBPNG
626 #ifdef CRN_USING_LIBJPEG
627 if (res.first.get() ==
nullptr)
629 res = load_libjpeg(fname);
630 errors += U
" " + res.second;
632 #endif // CRN_USING_LIBJPEG
633 #ifdef CRN_USING_GDIPLUS
634 if (res.first == NULL)
636 res = load_gdiplus(fname);
637 errors += U
" " + res.second;
639 #endif // CRN_USING_GDIPLUS
640 #ifdef CRN_USING_GDKPB
641 if (res.first.get() ==
nullptr)
643 res = load_gdkpixbuf(fname);
644 errors += U
" " + res.second;
646 #endif // CRN_USING_GDKPB
647 if (res.first.get() ==
nullptr)
649 _(
"No decoder could open the file ") +
StringUTF8{ fname } +
"\n" + errors.
CStr());
650 return std::move(res.first);
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());
667 return std::make_unique<ImageRGB>(*ig);
668 auto *ibw =
dynamic_cast<const ImageBW*
>(img.get());
670 return std::make_unique<ImageRGB>(*ibw);
671 throw ExceptionDomain(
"NewImageRGBFromFile(): "_s +
_(
"unknown image format."));
684 auto *irgb =
dynamic_cast<const ImageRGB*
>(img.get());
688 return std::unique_ptr<ImageGray>(
static_cast<ImageGray*
>(img.release()));
689 auto *ibw =
dynamic_cast<const ImageBW*
>(img.get());
691 return std::make_unique<ImageGray>(*ibw);
692 throw ExceptionDomain(
"NewImageGrayFromFile(): "_s +
_(
"unknown image format."));
705 auto *irgb =
dynamic_cast<const ImageRGB*
>(img.get());
708 auto *ig =
dynamic_cast<const ImageGray*
>(img.get());
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."));
Abstract class for images.
ImageBW MakeImageBW(const ImageGray &img)
UImageBW NewImageBWFromFile(const Path &fname)
Loads an image from a file and converts it if necessary.
std::unique_ptr< ImageBase > UImage
const char * CStr() const
Conversion to UTF8 cstring.
#define CRN_END_CLASS_CONSTRUCTOR(classname)
Defines a class constructor.
#define FOREACHPIXEL(x, y, img)
Convenience macro to sweep an image.
ImageGray MakeImageGray(const ImageRGB &img)
static std::mutex & GetMutex(const Path &fname)
Gets the mutex associated to a file.
A UTF32 character string class.
std::unique_ptr< T > MoveUnique(T &&v)
UImageGray NewImageGrayFromFile(const Path &fname)
Loads an image from a file and converts it if necessary.
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.
A convenience class for file paths.
void fclose_if_not_null(FILE *f)
UImage NewImageFromFile(const Path &fname)
Loads an image from a file.
Rect GetBBox() const noexcept
Path & ToLocal()
Converts the path to the local format.
A character string class.
Invalid argument error (e.g.: nullptr pointer)
#define CRN_BEGIN_CLASS_CONSTRUCTOR(classname)
Defines a class constructor.