1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
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
|