diff options
Diffstat (limited to 'src/image.cpp')
-rw-r--r-- | src/image.cpp | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/src/image.cpp b/src/image.cpp new file mode 100644 index 0000000..ab0b2f9 --- /dev/null +++ b/src/image.cpp @@ -0,0 +1,206 @@ +#include "image.hpp" + +#include "stb_image.h" + +#include <csetjmp> +#include <format> +#include <fud_c_file.hpp> +#include <spdlog/spdlog.h> + +// Include jpeglib last +#include <jpeglib.h> +#include <turbojpeg.h> + +namespace bookmouse { + +class JpegFile { + public: + ~JpegFile(); + void decompress(fud::CBinaryFile& inFile); + + private: + jpeg_decompress_struct m_cinfo{}; +}; + +JpegFile::~JpegFile() +{ + jpeg_destroy_decompress(&m_cinfo); +} + +void JpegFile::decompress(fud::CBinaryFile& inFile) +{ + jpeg_create_decompress(&m_cinfo); + auto* file = inFile.file(); + if (file == nullptr) { + // early return + } + jpeg_stdio_src(&m_cinfo, inFile.file()); + auto result = jpeg_read_header(&m_cinfo, true); + if (result == JPEG_SUSPENDED) { + // early return + } +} + +struct my_error_mgr { + struct jpeg_error_mgr pub; /* "public" fields */ + + jmp_buf setjmp_buffer; /* for return to caller */ +}; + +typedef struct my_error_mgr* my_error_ptr; + +METHODDEF(void) +my_error_exit(j_common_ptr cinfo) __attribute__((__noreturn__)); + +METHODDEF(void) +my_error_exit(j_common_ptr cinfo) +{ + /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */ + my_error_ptr myerr = (my_error_ptr)cinfo->err; + + /* Always display the message. */ + /* We could postpone this until after returning, if we chose. */ + (*cinfo->err->output_message)(cinfo); + + /* Return control to the setjmp point */ + longjmp(myerr->setjmp_buffer, 1); +} + +ImageResult JpegImage::output() const +{ + auto err = [](ImageError eid) { return ImageResult::error(eid); }; + ImageOutput image{}; + + struct jpeg_decompress_struct cinfo; + + struct my_error_mgr jerr; + + fud::CBinaryFile infile{m_filename, fud::CFileMode::ReadOnly}; + JSAMPARRAY buffer = nullptr; /* Output row buffer */ + J12SAMPARRAY buffer12 = nullptr; /* 12-bit output row buffer */ + int col; + int row_stride; /* physical row width in output buffer */ + + /* In this example we want to open the input and output files before doing + * anything else, so that the setjmp() error recovery below can assume the + * files are open. + * + * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that + * requires it in order to read/write binary files. + */ + + auto fileResult = infile.open(); + using fud::FileResult; + if (fileResult == FileResult::Error) { + spdlog::error("can't open {}\n", m_filename.c_str()); + return err(ImageError::FileError); + } + + /* Step 1: allocate and initialize JPEG decompression object */ + + /* We set up the normal JPEG error routines, then override error_exit. */ + cinfo.err = jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = my_error_exit; + /* Establish the setjmp return context for my_error_exit to use. */ + if (setjmp(jerr.setjmp_buffer)) { + /* If we get here, the JPEG code has signaled an error. + * We need to clean up the JPEG object, close the input file, and return. + */ + jpeg_destroy_decompress(&cinfo); + return err(ImageError::JpegError); + } + /* Now we can initialize the JPEG decompression object. */ + jpeg_create_decompress(&cinfo); + + /* Step 2: specify data source (eg, a file) */ + jpeg_stdio_src(&cinfo, infile.file()); + + /* Step 3: read file parameters with jpeg_read_header() */ + + (void)jpeg_read_header(&cinfo, TRUE); + /* We can ignore the return value from jpeg_read_header since + * (a) suspension is not possible with the stdio data source, and + * (b) we passed TRUE to reject a tables-only JPEG file as an error. + * See libjpeg.txt for more info. + */ + + /* Step 4: Start decompressor */ + + jpeg_start_decompress(&cinfo); + /* We can ignore the return value since suspension is not possible + * with the stdio data source. + */ + + /* We may need to do some setup of our own at this point before reading + * the data. After jpeg_start_decompress() we have the correct scaled + * output image dimensions available, as well as the output colormap + * if we asked for color quantization. + * In this example, we need to make an output work buffer of the right size. + */ + /* Samples per row in output buffer */ + if (cinfo.output_components < 0) { + // handle it + return err(ImageError::JpegError); + } + row_stride = static_cast<int>(cinfo.output_width) * cinfo.output_components; + /* Make a one-row-high sample array that will go away when done with image */ + if (cinfo.data_precision == 12) { + buffer12 = (J12SAMPARRAY)(*cinfo.mem->alloc_sarray)( + (j_common_ptr)&cinfo, + JPOOL_IMAGE, + static_cast<JDIMENSION>(row_stride), + 1); + } else { + buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, static_cast<JDIMENSION>(row_stride), 1); + } + + /* Step 5: while (scan lines remain to be read) */ + /* jpeg_read_scanlines(...); */ + + /* Here we use the library's state variable cinfo.output_scanline as the + * loop counter, so that we don't have to keep track ourselves. + */ + if (cinfo.data_precision == 12) { + while (cinfo.output_scanline < cinfo.output_height) { + /* jpeg12_read_scanlines expects an array of pointers to scanlines. + * Here the array is only one element long, but you could ask for + * more than one scanline at a time if that's more convenient. + */ + (void)jpeg12_read_scanlines(&cinfo, buffer12, 1); + /* Swap MSB and LSB in each sample */ + for (col = 0; col < row_stride; col++) { + buffer12[0][col] = // break + static_cast<J12SAMPLE>((buffer12[0][col] & 0xFF) << 8) | // break + static_cast<J12SAMPLE>((buffer12[0][col] >> 8) & 0xFF); + } + } + } else { + while (cinfo.output_scanline < cinfo.output_height) { + /* jpeg_read_scanlines expects an array of pointers to scanlines. + * Here the array is only one element long, but you could ask for + * more than one scanline at a time if that's more convenient. + */ + (void)jpeg_read_scanlines(&cinfo, buffer, 1); + } + } + + /* Step 6: Finish decompression */ + + (void)jpeg_finish_decompress(&cinfo); + /* We can ignore the return value since suspension is not possible + * with the stdio data source. + */ + + /* Step 7: Release JPEG decompression object */ + + /* This is an important step since it will release a good deal of memory. */ + + /* At this point you may want to check to see whether any corrupt-data + * warnings occurred (test whether jerr.pub.num_warnings is nonzero). + */ + + /* And we're done! */ + return ImageResult::okay(image); +} + +} // namespace bookmouse |