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