21 #include <unordered_map>
30 void jpeg_error_throw(j_common_ptr p_cinfo) {
31 if (p_cinfo->is_decompressor)
32 jpeg_destroy_decompress(
33 reinterpret_cast<jpeg_decompress_struct *
>(p_cinfo));
35 jpeg_destroy_compress(
36 reinterpret_cast<jpeg_compress_struct *
>(p_cinfo));
37 char buffer[JMSG_LENGTH_MAX];
38 (*p_cinfo->err->format_message)(p_cinfo, buffer);
39 throw std::runtime_error(buffer);
44 png_image &pngimage) {
45 pngimage.width =
image.width_;
46 pngimage.height =
image.height_;
47 pngimage.format = pngimage.flags = 0;
49 if (
image.bytes_per_channel_ == 2) {
50 pngimage.format |= PNG_FORMAT_FLAG_LINEAR;
52 if (
image.num_of_channels_ >= 3) {
53 pngimage.format |= PNG_FORMAT_FLAG_COLOR;
55 if (
image.num_of_channels_ == 4) {
56 pngimage.format |= PNG_FORMAT_FLAG_ALPHA;
59 pngimage.flags |= PNG_IMAGE_FLAG_FAST;
63 using signature_decoder_t =
64 std::pair<std::string,
65 std::function<bool(
const std::string &, geometry::Image &)>>;
66 static const std::array<signature_decoder_t, 2> signature_decoder_list{
69 static constexpr uint8_t MAX_SIGNATURE_LEN = 8;
71 static const std::unordered_map<
73 std::function<bool(
const std::string &,
const geometry::Image &,
int)>>
74 file_extension_to_image_write_function{
85 auto image = std::make_shared<geometry::Image>();
92 const unsigned char *image_data_ptr,
93 size_t image_data_size) {
94 auto image = std::make_shared<geometry::Image>();
100 std::string signature_buffer(MAX_SIGNATURE_LEN, 0);
101 std::ifstream file(
filename, std::ios::binary);
102 file.read(&signature_buffer[0], MAX_SIGNATURE_LEN);
105 err_msg =
"Read geometry::Image failed for file {}. I/O error.";
108 for (
const auto &signature_decoder : signature_decoder_list) {
109 if (signature_buffer.compare(0, signature_decoder.first.size(),
110 signature_decoder.first) == 0) {
115 "Read geometry::Image failed for file {}. Unknown file "
116 "signature, only PNG and JPG are supported.";
124 const unsigned char *image_data_ptr,
125 size_t image_data_size,
131 }
else if (
format ==
"jpg") {
142 std::string filename_ext =
144 if (filename_ext.empty()) {
146 "Write geometry::Image failed: unknown file extension.");
149 auto map_itr = file_extension_to_image_write_function.find(filename_ext);
150 if (map_itr == file_extension_to_image_write_function.end()) {
152 "Write geometry::Image failed: unknown file extension.");
160 memset(&pngimage, 0,
sizeof(pngimage));
161 pngimage.version = PNG_IMAGE_VERSION;
162 if (png_image_begin_read_from_file(&pngimage,
filename.c_str()) == 0) {
169 if (pngimage.format & PNG_FORMAT_FLAG_COLORMAP) {
170 pngimage.format &= ~PNG_FORMAT_FLAG_COLORMAP;
173 image.Prepare(pngimage.width, pngimage.height,
174 PNG_IMAGE_SAMPLE_CHANNELS(pngimage.format),
175 PNG_IMAGE_SAMPLE_COMPONENT_SIZE(pngimage.format));
177 if (png_image_finish_read(&pngimage,
NULL,
image.data_.data(), 0,
NULL) ==
188 size_t image_data_size,
191 memset(&pngimage, 0,
sizeof(pngimage));
192 pngimage.version = PNG_IMAGE_VERSION;
193 if (png_image_begin_read_from_memory(&pngimage, image_data_ptr,
194 image_data_size) == 0) {
202 if (pngimage.format & PNG_FORMAT_FLAG_COLORMAP) {
203 pngimage.format &= ~PNG_FORMAT_FLAG_COLORMAP;
206 image.Prepare(pngimage.width, pngimage.height,
207 PNG_IMAGE_SAMPLE_CHANNELS(pngimage.format),
208 PNG_IMAGE_SAMPLE_COMPONENT_SIZE(pngimage.format));
210 if (png_image_finish_read(&pngimage,
NULL,
image.data_.data(), 0,
NULL) ==
222 if (!
image.HasData()) {
228 if (quality < 0 || quality > 9) {
230 "Write PNG failed: quality ({}) must be in the range [0,9]",
236 memset(&pngimage, 0,
sizeof(pngimage));
237 pngimage.version = PNG_IMAGE_VERSION;
239 if (png_image_write_to_file(&pngimage,
filename.c_str(), 0,
249 struct jpeg_decompress_struct cinfo;
250 struct jpeg_error_mgr jerr;
262 cinfo.err = jpeg_std_error(&jerr);
263 jerr.error_exit = jpeg_error_throw;
264 jpeg_create_decompress(&cinfo);
265 jpeg_stdio_src(&cinfo, file_in);
266 jpeg_read_header(&cinfo,
TRUE);
269 int num_of_channels = 3;
270 int bytes_per_channel = 1;
271 switch (cinfo.jpeg_color_space) {
274 cinfo.out_color_space = JCS_RGB;
275 cinfo.out_color_components = 3;
279 cinfo.jpeg_color_space = JCS_GRAYSCALE;
280 cinfo.out_color_components = 1;
287 "Read JPG failed: color space not supported.");
288 jpeg_destroy_decompress(&cinfo);
293 jpeg_start_decompress(&cinfo);
294 image.Prepare(cinfo.output_width, cinfo.output_height, num_of_channels,
296 int row_stride = cinfo.output_width * cinfo.output_components;
297 buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE,
299 uint8_t *pdata =
image.data_.data();
300 while (cinfo.output_scanline < cinfo.output_height) {
301 jpeg_read_scanlines(&cinfo, buffer, 1);
302 memcpy(pdata, buffer[0], row_stride);
305 jpeg_finish_decompress(&cinfo);
306 jpeg_destroy_decompress(&cinfo);
309 }
catch (
const std::runtime_error &err) {
318 size_t image_data_size,
320 struct jpeg_decompress_struct cinfo;
321 struct jpeg_error_mgr jerr;
325 cinfo.err = jpeg_std_error(&jerr);
326 jerr.error_exit = jpeg_error_throw;
327 jpeg_create_decompress(&cinfo);
328 jpeg_mem_src(&cinfo, image_data_ptr, image_data_size);
329 jpeg_read_header(&cinfo,
TRUE);
332 int num_of_channels = 3;
333 int bytes_per_channel = 1;
334 switch (cinfo.jpeg_color_space) {
337 cinfo.out_color_space = JCS_RGB;
338 cinfo.out_color_components = 3;
342 cinfo.jpeg_color_space = JCS_GRAYSCALE;
343 cinfo.out_color_components = 1;
350 "Read JPG failed: color space not supported.");
351 jpeg_destroy_decompress(&cinfo);
355 jpeg_start_decompress(&cinfo);
356 image.Prepare(cinfo.output_width, cinfo.output_height, num_of_channels,
358 int row_stride = cinfo.output_width * cinfo.output_components;
359 buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE,
361 uint8_t *pdata =
image.data_.data();
362 while (cinfo.output_scanline < cinfo.output_height) {
363 jpeg_read_scanlines(&cinfo, buffer, 1);
364 memcpy(pdata, buffer[0], row_stride);
367 jpeg_finish_decompress(&cinfo);
368 jpeg_destroy_decompress(&cinfo);
370 }
catch (
const std::runtime_error &err) {
380 if (!
image.HasData()) {
384 if (
image.bytes_per_channel_ != 1 ||
385 (
image.num_of_channels_ != 1 &&
image.num_of_channels_ != 3)) {
391 if (quality < 0 || quality > 100) {
393 "Write JPG failed: image quality should be in the range "
398 struct jpeg_compress_struct cinfo;
399 struct jpeg_error_mgr jerr;
401 JSAMPROW row_pointer[1];
410 cinfo.err = jpeg_std_error(&jerr);
411 jerr.error_exit = jpeg_error_throw;
412 jpeg_create_compress(&cinfo);
413 jpeg_stdio_dest(&cinfo, file_out);
414 cinfo.image_width =
image.width_;
415 cinfo.image_height =
image.height_;
416 cinfo.input_components =
image.num_of_channels_;
417 cinfo.in_color_space =
418 (cinfo.input_components == 1 ? JCS_GRAYSCALE : JCS_RGB);
419 jpeg_set_defaults(&cinfo);
420 jpeg_set_quality(&cinfo, quality,
TRUE);
421 jpeg_start_compress(&cinfo,
TRUE);
422 int row_stride =
image.width_ *
image.num_of_channels_;
423 const uint8_t *pdata =
image.data_.data();
424 std::vector<uint8_t> buffer(row_stride);
425 while (cinfo.next_scanline < cinfo.image_height) {
426 memcpy(buffer.data(), pdata, row_stride);
427 row_pointer[0] = buffer.data();
428 jpeg_write_scanlines(&cinfo, row_pointer, 1);
431 jpeg_finish_compress(&cinfo);
433 jpeg_destroy_compress(&cinfo);
435 }
catch (
const std::runtime_error &err) {
std::shared_ptr< core::Tensor > image
filament::Texture::InternalFormat format
filament::Texture::Format image_format
The Image class stores image with customizable width, height, num of channels and bytes per channel.
bool WriteImage(const std::string &filename, const geometry::Image &image, int quality=kCloudViewerImageIODefaultQuality)
bool ReadImageFromJPG(const std::string &filename, geometry::Image &image)
constexpr int kCloudViewerImageIODefaultQuality
bool ReadJPGFromMemory(const unsigned char *image_data_ptr, size_t image_data_size, geometry::Image &image)
bool ReadImageFromPNG(const std::string &filename, geometry::Image &image)
bool ReadPNGFromMemory(const unsigned char *image_data_ptr, size_t image_data_size, geometry::Image &image)
bool ReadImage(const std::string &filename, geometry::Image &image)
std::shared_ptr< geometry::Image > CreateImageFromMemory(const std::string &image_format, const unsigned char *image_data_ptr, size_t image_data_size)
Factory function to create an image from memory.
std::shared_ptr< geometry::Image > CreateImageFromFile(const std::string &filename)
bool WriteImageToPNG(const std::string &filename, const geometry::Image &image, int quality=kCloudViewerImageIODefaultQuality)
bool ReadImageFromMemory(const std::string &image_format, const unsigned char *image_data_ptr, size_t image_data_size, geometry::Image &image)
bool WriteImageToJPG(const std::string &filename, const geometry::Image &image, int quality=kCloudViewerImageIODefaultQuality)
static void SetPNGImageFromImage(const geometry::Image &image, int quality, png_image &pngimage)
std::string GetFileExtensionInLowerCase(const std::string &filename)
FILE * FOpen(const std::string &filename, const std::string &mode)
Generic file read and write utility for python interface.