1 /*
2 * Copyright (C) 2021 Tetsuya Isaki
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
18 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
20 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26 #include "ImageLoaderJPEG.h"
27 #include <cassert>
28 #include <cstring>
29 #include <errno.h>
30 #include <jpeglib.h>
31
32 static void jpeg_output_message(j_common_ptr);
33 static void jpeg_init_source(j_decompress_ptr);
34 static boolean jpeg_fill_input_buffer(j_decompress_ptr);
35 static void jpeg_skip_input_data(j_decompress_ptr, long num_bytes);
36 static void jpeg_term_source(j_decompress_ptr);
37
38 // コンストラクタ
ImageLoaderJPEG(InputStream * stream_,const Diag & diag_)39 ImageLoaderJPEG::ImageLoaderJPEG(InputStream *stream_, const Diag& diag_)
40 : inherited(stream_, diag_)
41 {
42 }
43
44 // デストラクタ
~ImageLoaderJPEG()45 ImageLoaderJPEG::~ImageLoaderJPEG()
46 {
47 }
48
49 // stream が JPEG なら true を返す。
50 bool
Check() const51 ImageLoaderJPEG::Check() const
52 {
53 uint8 magic[2];
54
55 auto n = stream->Peek(magic, sizeof(magic));
56 if (n < sizeof(magic)) {
57 Trace(diag, "%s: Read(magic) failed: %s", __method__, strerror(errno));
58 return false;
59 }
60 // マジックを確認
61 if (magic[0] != 0xff || magic[1] != 0xd8) {
62 Trace(diag, "%s: Bad magic", __method__);
63 return false;
64 }
65 Trace(diag, "%s: OK", __method__);
66 return true;
67 }
68
69 // stream から画像をロードする。
70 bool
Load(Image & img)71 ImageLoaderJPEG::Load(Image& img)
72 {
73 struct jpeg_decompress_struct jinfo;
74 struct jpeg_source_mgr jsrc;
75 struct jpeg_error_mgr jerr;
76
77 memset(&jinfo, 0, sizeof(jinfo));
78 memset(&jsrc, 0, sizeof(jsrc));
79 memset(&jerr, 0, sizeof(jerr));
80 jinfo.client_data = this;
81 jinfo.err = jpeg_std_error(&jerr);
82 jerr.output_message = jpeg_output_message;
83
84 jpeg_create_decompress(&jinfo);
85
86 jsrc.init_source = jpeg_init_source;
87 jsrc.fill_input_buffer = jpeg_fill_input_buffer;
88 jsrc.skip_input_data = jpeg_skip_input_data;
89 jsrc.resync_to_restart = jpeg_resync_to_restart;
90 jsrc.term_source = jpeg_term_source;
91 jsrc.next_input_byte = NULL;
92 jsrc.bytes_in_buffer = 0;
93 jinfo.src = &jsrc;
94
95 // ヘッダ読み込み
96 Trace(diag, "%s read header", __method__);
97 jpeg_read_header(&jinfo, (boolean)TRUE);
98 Trace(diag, "%s read header done", __method__);
99
100 Size origsize;
101 Size reqsize;
102 origsize.w = jinfo.image_width;
103 origsize.h = jinfo.image_height;
104
105 // スケールの計算
106 CalcResize(reqsize, resize_axis, origsize);
107 if (reqsize.w < 1) {
108 reqsize.w = 1;
109 }
110 if (reqsize.h < 1) {
111 reqsize.h = 1;
112 }
113
114 // libjpeg では 1/16 までサポート。
115 // 1/1, 1/2, 1/4/, 1/8 しかサポートしないとも書いてある
116 int scalew = origsize.w / reqsize.w;
117 int scaleh = origsize.h / reqsize.h;
118 int scale;
119 if (scalew < scaleh) {
120 scale = scalew;
121 } else {
122 scale = scaleh;
123 }
124 if (scale < 1) {
125 scale = 1;
126 } else if (scale > 16) {
127 scale = 16;
128 }
129
130 Debug(diag, "%s size=(%d,%d) scalewh=(%d,%d) scale=%d", __method__,
131 origsize.w, origsize.h, scalew, scaleh, scale);
132
133 jinfo.scale_num = 1;
134 jinfo.scale_denom = scale;
135 jinfo.do_fancy_upsampling = (boolean)FALSE;
136 jinfo.do_block_smoothing = (boolean)FALSE;
137 jinfo.dct_method = JDCT_FASTEST;
138 jinfo.out_color_space = JCS_RGB;
139 jinfo.output_components = 3;
140
141 jpeg_calc_output_dimensions(&jinfo);
142
143 int width = jinfo.output_width;
144 int height = jinfo.output_height;
145 img.Create(width, height);
146
147 // スキャンラインメモリのポインタ配列
148 std::vector<uint8 *> lines(img.GetHeight());
149 for (int y = 0, end = lines.size(); y < end; y++) {
150 lines[y] = img.buf.data() + (y * img.GetStride());
151 }
152
153 Trace(diag, "%s start_decompress", __method__);
154 jpeg_start_decompress(&jinfo);
155 Trace(diag, "%s start_decompress done", __method__);
156
157 while (jinfo.output_scanline < jinfo.output_height) {
158 int prev_scanline = jinfo.output_scanline;
159
160 jpeg_read_scanlines(&jinfo,
161 &lines[jinfo.output_scanline],
162 jinfo.rec_outbuf_height);
163
164 if (jinfo.output_scanline == prev_scanline) {
165 // スキャンラインが進まない
166 jpeg_abort_decompress(&jinfo);
167 lines.clear();
168 return false; //RIC_ABORT_JPEG;
169 }
170 }
171
172 Trace(diag, "%s finish_decompress", __method__);
173 jpeg_finish_decompress(&jinfo);
174 Trace(diag, "%s finish_decompress done", __method__);
175
176 jpeg_destroy_decompress(&jinfo);
177
178 return true;
179 }
180
181 // リサイズ計算
182 void
CalcResize(Size & req,int axis,Size & orig)183 ImageLoaderJPEG::CalcResize(Size& req, int axis, Size& orig)
184 {
185 int scaledown =
186 (axis == ResizeAxisMode::ScaleDownBoth)
187 || (axis == ResizeAxisMode::ScaleDownWidth)
188 || (axis == ResizeAxisMode::ScaleDownHeight)
189 || (axis == ResizeAxisMode::ScaleDownLong)
190 || (axis == ResizeAxisMode::ScaleDownShort);
191
192 // まず丸めていく
193 switch (axis) {
194 case ResizeAxisMode::Both:
195 case ResizeAxisMode::ScaleDownBoth:
196 if (req.w <= 0) {
197 axis = ResizeAxisMode::Height;
198 } else if (req.h <= 0) {
199 axis = ResizeAxisMode::Width;
200 } else {
201 axis = ResizeAxisMode::Both;
202 }
203 break;
204 case ResizeAxisMode::Long:
205 case ResizeAxisMode::ScaleDownLong:
206 if (orig.w >= orig.h) {
207 axis = ResizeAxisMode::Width;
208 } else {
209 axis = ResizeAxisMode::Height;
210 }
211 break;
212 case ResizeAxisMode::Short:
213 case ResizeAxisMode::ScaleDownShort:
214 if (orig.w <= orig.h) {
215 axis = ResizeAxisMode::Width;
216 } else {
217 axis = ResizeAxisMode::Height;
218 }
219 break;
220 case ResizeAxisMode::ScaleDownWidth:
221 axis = ResizeAxisMode::Width;
222 break;
223 case ResizeAxisMode::ScaleDownHeight:
224 axis = ResizeAxisMode::Height;
225 break;
226 }
227
228 if (req.w <= 0)
229 req.w = orig.w;
230 if (req.h <= 0)
231 req.h = orig.h;
232
233 // 縮小のみ指示
234 if (scaledown) {
235 if (orig.w < req.w)
236 req.w = orig.w;
237 if (orig.h < req.h)
238 req.h = orig.h;
239 }
240
241 switch (axis) {
242 case ResizeAxisMode::Width:
243 req.h = orig.h * req.w / orig.w;
244 break;
245 case ResizeAxisMode::Height:
246 req.w = orig.w * req.h / orig.h;
247 break;
248 }
249 }
250
251
252 //
253 // ----- libjpeg から呼ばれるコールバック関数群
254 //
255
256 static const JOCTET faked_eoi[] = {
257 0xff, JPEG_EOI
258 };
259
260 // デバッグメッセージを表示
261 static void
jpeg_output_message(j_common_ptr cinfo)262 jpeg_output_message(j_common_ptr cinfo)
263 {
264 char msg[JMSG_LENGTH_MAX];
265
266 (*cinfo->err->format_message)(cinfo, msg);
267
268 assert(cinfo->client_data);
269 ImageLoaderJPEG *loader = (ImageLoaderJPEG *)cinfo->client_data;
270 Diag& diag = loader->GetDiag();
271 Debug(diag, "%s", msg);
272 }
273
274 // 本来ここで src に必要な初期化するのだが、
275 // うちではクラス内で行っているので不要
276 static void
jpeg_init_source(j_decompress_ptr cinfo)277 jpeg_init_source(j_decompress_ptr cinfo)
278 {
279 }
280
281 // src にデータを読み込む
282 static boolean
jpeg_fill_input_buffer(j_decompress_ptr cinfo)283 jpeg_fill_input_buffer(j_decompress_ptr cinfo)
284 {
285 assert(cinfo->client_data);
286 assert(cinfo->src);
287
288 ImageLoaderJPEG *loader = (ImageLoaderJPEG *)cinfo->client_data;
289 jpeg_source_mgr *src = cinfo->src;
290
291 int n = loader->Read();
292 if (n > 0) {
293 src->next_input_byte = loader->localbuf.data();
294 src->bytes_in_buffer = n;
295 } else {
296 // 読めなければ fake EOI を返す
297 src->next_input_byte = faked_eoi;
298 src->bytes_in_buffer = sizeof(faked_eoi);
299 }
300 return (boolean)TRUE;
301 }
302
303 // src の入力をスキップする
304 static void
jpeg_skip_input_data(j_decompress_ptr cinfo,long num_bytes)305 jpeg_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
306 {
307 assert(cinfo->src);
308 jpeg_source_mgr *src = cinfo->src;
309
310 while (num_bytes > (long)src->bytes_in_buffer) {
311 num_bytes -= src->bytes_in_buffer;
312 jpeg_fill_input_buffer(cinfo);
313 }
314 src->next_input_byte += num_bytes;
315 src->bytes_in_buffer -= num_bytes;
316 }
317
318 // 本来ならここで src を解放などするのだが、うちでは不要
319 static void
jpeg_term_source(j_decompress_ptr cinfo)320 jpeg_term_source(j_decompress_ptr cinfo)
321 {
322 }
323