|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+#include <stdio.h>
|
|
|
2
|
+
|
|
|
3
|
+// for error recovery
|
|
|
4
|
+#include <setjmp.h>
|
|
|
5
|
+
|
|
|
6
|
+#include <iostream>
|
|
|
7
|
+#include <fstream>
|
|
|
8
|
+#include <sstream>
|
|
|
9
|
+#include <vector>
|
|
|
10
|
+
|
|
|
11
|
+#include "jpeglib.h"
|
|
|
12
|
+
|
|
|
13
|
+#define STB_IMAGE_IMPLEMENTATION
|
|
|
14
|
+#include "stb_image.h"
|
|
|
15
|
+
|
|
|
16
|
+#include "image.h"
|
|
|
17
|
+
|
|
|
18
|
+using namespace std;
|
|
|
19
|
+using namespace haader;
|
|
|
20
|
+
|
|
|
21
|
+
|
|
|
22
|
+Image::Image():
|
|
|
23
|
+ m_width(0),
|
|
|
24
|
+ m_height(0),
|
|
|
25
|
+ m_components(0)
|
|
|
26
|
+{
|
|
|
27
|
+}
|
|
|
28
|
+
|
|
|
29
|
+void Image::to_string(string &s) {
|
|
|
30
|
+ stringstream ss;
|
|
|
31
|
+ ss << "Image(";
|
|
|
32
|
+ ss << m_width << 'x' << m_height << 'x' << m_components << ")";
|
|
|
33
|
+ s = ss.str();
|
|
|
34
|
+}
|
|
|
35
|
+
|
|
|
36
|
+bool Image::read_from_file(const char *file_path) {
|
|
|
37
|
+ int x = 0;
|
|
|
38
|
+ int y = 0;
|
|
|
39
|
+ int components = 0;
|
|
|
40
|
+
|
|
|
41
|
+ unsigned char *data = stbi_load(file_path, &x, &y, &components, 0);
|
|
|
42
|
+
|
|
|
43
|
+ if (data) {
|
|
|
44
|
+ m_width = x;
|
|
|
45
|
+ m_height = y;
|
|
|
46
|
+ m_components = components;
|
|
|
47
|
+ size_t size = (size_t)x * (size_t)y * (size_t)components;
|
|
|
48
|
+ m_image_data.assign(data, data + size);
|
|
|
49
|
+
|
|
|
50
|
+ return true;
|
|
|
51
|
+ } else {
|
|
|
52
|
+ cerr << "Can not read \"" << file_path << "\" as an image." << endl;
|
|
|
53
|
+ return false;
|
|
|
54
|
+ }
|
|
|
55
|
+
|
|
|
56
|
+ stbi_image_free(data);
|
|
|
57
|
+}
|
|
|
58
|
+
|
|
|
59
|
+struct my_error_mgr {
|
|
|
60
|
+ struct jpeg_error_mgr pub; /* "public" fields */
|
|
|
61
|
+
|
|
|
62
|
+ jmp_buf setjmp_buffer; /* for return to caller */
|
|
|
63
|
+};
|
|
|
64
|
+
|
|
|
65
|
+typedef struct my_error_mgr * my_error_ptr;
|
|
|
66
|
+
|
|
|
67
|
+/*
|
|
|
68
|
+ * Here's the routine that will replace the standard error_exit method:
|
|
|
69
|
+ */
|
|
|
70
|
+
|
|
|
71
|
+METHODDEF(void)
|
|
|
72
|
+my_error_exit (j_common_ptr cinfo)
|
|
|
73
|
+{
|
|
|
74
|
+ /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
|
|
|
75
|
+ my_error_ptr myerr = (my_error_ptr) cinfo->err;
|
|
|
76
|
+
|
|
|
77
|
+ /* Always display the message. */
|
|
|
78
|
+ /* We could postpone this until after returning, if we chose. */
|
|
|
79
|
+ (*cinfo->err->output_message) (cinfo);
|
|
|
80
|
+
|
|
|
81
|
+ /* Return control to the setjmp point */
|
|
|
82
|
+ longjmp(myerr->setjmp_buffer, 1);
|
|
|
83
|
+}
|
|
|
84
|
+
|
|
|
85
|
+bool Image::read_from_jpeg_file(const char *file_path) {
|
|
|
86
|
+ /* This struct contains the JPEG decompression parameters and pointers to
|
|
|
87
|
+ * working space (which is allocated as needed by the JPEG library).
|
|
|
88
|
+ */
|
|
|
89
|
+ struct jpeg_decompress_struct cinfo;
|
|
|
90
|
+
|
|
|
91
|
+ /* We use our private extension JPEG error handler.
|
|
|
92
|
+ * Note that this struct must live as long as the main JPEG parameter
|
|
|
93
|
+ * struct, to avoid dangling-pointer problems.
|
|
|
94
|
+ */
|
|
|
95
|
+ struct my_error_mgr jerr;
|
|
|
96
|
+
|
|
|
97
|
+ /* More stuff */
|
|
|
98
|
+ FILE * infile; /* source file */
|
|
|
99
|
+ JSAMPARRAY buffer; /* Output row buffer */
|
|
|
100
|
+ int row_stride; /* physical row width in output buffer */
|
|
|
101
|
+
|
|
|
102
|
+ /* In this example we want to open the input file before doing anything else,
|
|
|
103
|
+ * so that the setjmp() error recovery below can assume the file is open.
|
|
|
104
|
+ * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
|
|
|
105
|
+ * requires it in order to read binary files.
|
|
|
106
|
+ */
|
|
|
107
|
+
|
|
|
108
|
+ if ((infile = fopen(file_path, "rb")) == NULL) {
|
|
|
109
|
+ cerr << "Can not open \"" << file_path << "\"" << endl;
|
|
|
110
|
+ return false;
|
|
|
111
|
+ }
|
|
|
112
|
+
|
|
|
113
|
+ /* Step 1: allocate and initialize JPEG decompression object */
|
|
|
114
|
+
|
|
|
115
|
+ /* We set up the normal JPEG error routines, then override error_exit. */
|
|
|
116
|
+ cinfo.err = jpeg_std_error(&jerr.pub);
|
|
|
117
|
+ jerr.pub.error_exit = my_error_exit;
|
|
|
118
|
+ /* Establish the setjmp return context for my_error_exit to use. */
|
|
|
119
|
+ if (setjmp(jerr.setjmp_buffer)) {
|
|
|
120
|
+ /* If we get here, the JPEG code has signaled an error.
|
|
|
121
|
+ * We need to clean up the JPEG object, close the input file, and return.
|
|
|
122
|
+ */
|
|
|
123
|
+ jpeg_destroy_decompress(&cinfo);
|
|
|
124
|
+ fclose(infile);
|
|
|
125
|
+ return false;
|
|
|
126
|
+ }
|
|
|
127
|
+ /* Now we can initialize the JPEG decompression object. */
|
|
|
128
|
+ jpeg_create_decompress(&cinfo);
|
|
|
129
|
+
|
|
|
130
|
+ /* Step 2: specify data source (eg, a file) */
|
|
|
131
|
+
|
|
|
132
|
+ jpeg_stdio_src(&cinfo, infile);
|
|
|
133
|
+
|
|
|
134
|
+ /* Step 3: read file parameters with jpeg_read_header() */
|
|
|
135
|
+
|
|
|
136
|
+ (void) jpeg_read_header(&cinfo, TRUE);
|
|
|
137
|
+ /* We can ignore the return value from jpeg_read_header since
|
|
|
138
|
+ * (a) suspension is not possible with the stdio data source, and
|
|
|
139
|
+ * (b) we passed TRUE to reject a tables-only JPEG file as an error.
|
|
|
140
|
+ * See libjpeg.txt for more info.
|
|
|
141
|
+ */
|
|
|
142
|
+
|
|
|
143
|
+ /* Step 4: set parameters for decompression */
|
|
|
144
|
+
|
|
|
145
|
+ /* In this example, we don't need to change any of the defaults set by
|
|
|
146
|
+ * jpeg_read_header(), so we do nothing here.
|
|
|
147
|
+ */
|
|
|
148
|
+
|
|
|
149
|
+ /* Step 5: Start decompressor */
|
|
|
150
|
+
|
|
|
151
|
+ (void) jpeg_start_decompress(&cinfo);
|
|
|
152
|
+ /* We can ignore the return value since suspension is not possible
|
|
|
153
|
+ * with the stdio data source.
|
|
|
154
|
+ */
|
|
|
155
|
+
|
|
|
156
|
+ m_image_data.reserve(cinfo.output_width * cinfo.output_height * cinfo.output_components);
|
|
|
157
|
+
|
|
|
158
|
+ m_width = cinfo.output_width;
|
|
|
159
|
+ m_height = cinfo.output_height;
|
|
|
160
|
+ m_components = cinfo.output_components;
|
|
|
161
|
+
|
|
|
162
|
+ /* We may need to do some setup of our own at this point before reading
|
|
|
163
|
+ * the data. After jpeg_start_decompress() we have the correct scaled
|
|
|
164
|
+ * output image dimensions available, as well as the output colormap
|
|
|
165
|
+ * if we asked for color quantization.
|
|
|
166
|
+ * In this example, we need to make an output work buffer of the right size.
|
|
|
167
|
+ */
|
|
|
168
|
+ /* JSAMPLEs per row in output buffer */
|
|
|
169
|
+ row_stride = cinfo.output_width * cinfo.output_components;
|
|
|
170
|
+ /* Make a one-row-high sample array that will go away when done with image */
|
|
|
171
|
+ buffer = (*cinfo.mem->alloc_sarray)
|
|
|
172
|
+ ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
|
|
|
173
|
+
|
|
|
174
|
+ /* Step 6: while (scan lines remain to be read) */
|
|
|
175
|
+ /* jpeg_read_scanlines(...); */
|
|
|
176
|
+
|
|
|
177
|
+ /* Here we use the library's state variable cinfo.output_scanline as the
|
|
|
178
|
+ * loop counter, so that we don't have to keep track ourselves.
|
|
|
179
|
+ */
|
|
|
180
|
+ while (cinfo.output_scanline < cinfo.output_height) {
|
|
|
181
|
+ /* jpeg_read_scanlines expects an array of pointers to scanlines.
|
|
|
182
|
+ * Here the array is only one element long, but you could ask for
|
|
|
183
|
+ * more than one scanline at a time if that's more convenient.
|
|
|
184
|
+ */
|
|
|
185
|
+ (void) jpeg_read_scanlines(&cinfo, buffer, 1);
|
|
|
186
|
+ /* Assume put_scanline_someplace wants a pointer and sample count. */
|
|
|
187
|
+ //put_scanline_someplace(buffer[0], row_stride);
|
|
|
188
|
+
|
|
|
189
|
+ for (int i = 0; i < row_stride; i++) {
|
|
|
190
|
+ m_image_data.push_back(buffer[0][i]);
|
|
|
191
|
+ }
|
|
|
192
|
+ }
|
|
|
193
|
+
|
|
|
194
|
+ /* Step 7: Finish decompression */
|
|
|
195
|
+
|
|
|
196
|
+ (void) jpeg_finish_decompress(&cinfo);
|
|
|
197
|
+ /* We can ignore the return value since suspension is not possible
|
|
|
198
|
+ * with the stdio data source.
|
|
|
199
|
+ */
|
|
|
200
|
+
|
|
|
201
|
+ /* Step 8: Release JPEG decompression object */
|
|
|
202
|
+
|
|
|
203
|
+ /* This is an important step since it will release a good deal of memory. */
|
|
|
204
|
+ jpeg_destroy_decompress(&cinfo);
|
|
|
205
|
+
|
|
|
206
|
+ /* After finish_decompress, we can close the input file.
|
|
|
207
|
+ * Here we postpone it until after no more JPEG errors are possible,
|
|
|
208
|
+ * so as to simplify the setjmp error logic above. (Actually, I don't
|
|
|
209
|
+ * think that jpeg_destroy can do an error exit, but why assume anything...)
|
|
|
210
|
+ */
|
|
|
211
|
+ fclose(infile);
|
|
|
212
|
+
|
|
|
213
|
+ if (jerr.pub.num_warnings != 0) {
|
|
|
214
|
+ cout << jerr.pub.num_warnings << " errors occured." << endl;
|
|
|
215
|
+ return false;
|
|
|
216
|
+ } else {
|
|
|
217
|
+ return true;
|
|
|
218
|
+ }
|
|
|
219
|
+}
|
|
|
220
|
+
|
|
|
221
|
+bool Image::save_as_ppm_file(const char *file_path, bool ascii) {
|
|
|
222
|
+ if (ascii) {
|
|
|
223
|
+ // ASCII
|
|
|
224
|
+
|
|
|
225
|
+ ofstream out(file_path);
|
|
|
226
|
+ out << "P3\n";
|
|
|
227
|
+ out << m_width << ' ' << m_height << '\n';
|
|
|
228
|
+ out << "255\n";
|
|
|
229
|
+
|
|
|
230
|
+ unsigned int pixels = m_width * m_height;
|
|
|
231
|
+ unsigned int index = 0;
|
|
|
232
|
+ for (unsigned int i = 0; i < pixels; i++) {
|
|
|
233
|
+ out << (int)m_image_data[index] << ' ' << (int)m_image_data[index + 1] << ' ' << (int)m_image_data[index + 2] << '\n';
|
|
|
234
|
+ index += 3;
|
|
|
235
|
+ }
|
|
|
236
|
+
|
|
|
237
|
+ out.close();
|
|
|
238
|
+ return true;
|
|
|
239
|
+ } else {
|
|
|
240
|
+ // binary
|
|
|
241
|
+
|
|
|
242
|
+ ofstream out(file_path);
|
|
|
243
|
+ out << "P6\n";
|
|
|
244
|
+ out << m_width << ' ' << m_height << '\n';
|
|
|
245
|
+ out << "255\n";
|
|
|
246
|
+
|
|
|
247
|
+ unsigned int pixels = m_width * m_height;
|
|
|
248
|
+ unsigned int index = 0;
|
|
|
249
|
+ for (unsigned int i = 0; i < pixels; i++) {
|
|
|
250
|
+ out << m_image_data[index] << m_image_data[index + 1] << m_image_data[index + 2];
|
|
|
251
|
+ index += 3;
|
|
|
252
|
+ }
|
|
|
253
|
+
|
|
|
254
|
+ out.close();
|
|
|
255
|
+ return true;
|
|
|
256
|
+ }
|
|
|
257
|
+}
|
|
|
258
|
+
|
|
|
259
|
+unsigned int Image::get_width() {
|
|
|
260
|
+ return m_width;
|
|
|
261
|
+}
|
|
|
262
|
+
|
|
|
263
|
+unsigned int Image::get_height() {
|
|
|
264
|
+ return m_height;
|
|
|
265
|
+}
|
|
|
266
|
+
|
|
|
267
|
+unsigned int Image::get_components() {
|
|
|
268
|
+ return m_components;
|
|
|
269
|
+}
|
|
|
270
|
+
|
|
|
271
|
+bool Image::has_equal_dimensions(const Image &other) {
|
|
|
272
|
+ return (m_width == other.m_width &&
|
|
|
273
|
+ m_height == other.m_height &&
|
|
|
274
|
+ m_components == other.m_components);
|
|
|
275
|
+}
|