summaryrefslogtreecommitdiff
path: root/src/image.cpp
diff options
context:
space:
mode:
authorDominick Allen <djallen@librehumanitas.org>2024-09-23 00:33:33 -0500
committerDominick Allen <djallen@librehumanitas.org>2024-09-23 00:33:33 -0500
commit500e1f9892dd41419663e9f72cf47bab5b2aca0b (patch)
treea0709693d5317a6b89cdda7ac3143886b93552f4 /src/image.cpp
parentfa4b4097d3283e1d6e6376c70910e245f0b1f6ec (diff)
Saving work.
Diffstat (limited to 'src/image.cpp')
-rw-r--r--src/image.cpp206
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