1 /*
2  * Copyright (C) 2015 Y.Sugahara (moveccr)
3  * Copyright (C) 2021 Tetsuya Isaki
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include "FileStream.h"
28 #include "Image.h"
29 #include "ImageLoaderJPEG.h"
30 #include "ImageLoaderPNG.h"
31 #include "SixelConverter.h"
32 #include "StringUtil.h"
33 #include "sayaka.h"
34 #include <cassert>
35 #include <cstring>
36 #include <errno.h>
37 
38 // コンストラクタ
SixelConverter()39 SixelConverter::SixelConverter()
40 {
41 	diag.SetClassname("SixelConverter");
42 }
43 
44 // コンストラクタ
SixelConverter(int debuglv)45 SixelConverter::SixelConverter(int debuglv)
46 	: SixelConverter()
47 {
48 	diag.SetLevel(debuglv);
49 	ir.Init(diag);
50 }
51 
52 // stream から画像を img に読み込む。
53 // 成功すれば true、失敗すれば false を返す。
54 bool
LoadFromStream(InputStream * stream)55 SixelConverter::LoadFromStream(InputStream *stream)
56 {
57 	Debug(diag, "ResizeMode=%s", SRM2str(ResizeMode));
58 
59 	{
60 		ImageLoaderJPEG loader(stream, diag);
61 		if (loader.Check()) {
62 			Trace(diag, "%s filetype is JPEG", __func__);
63 			if (loader.Load(img)) {
64 				LoadAfter();
65 				return true;
66 			}
67 			return false;
68 		}
69 	}
70 	{
71 		ImageLoaderPNG loader(stream, diag);
72 		if (loader.Check()) {
73 			Trace(diag, "%s filetype is PNG", __func__);
74 			if (loader.Load(img)) {
75 				LoadAfter();
76 				return true;
77 			}
78 			return false;
79 		}
80 	}
81 
82 	printf("Unknown picture format");
83 	return false;
84 }
85 
86 void
LoadAfter()87 SixelConverter::LoadAfter()
88 {
89 	Width  = img.GetWidth();
90 	Height = img.GetHeight();
91 
92 	Debug(diag, "Loaded size=(%d,%d) bits=%d nCh=%d rowstride=%d",
93 		Width,
94 		Height,
95 		img.GetChDepth(),
96 		img.GetChannels(),
97 		img.GetStride());
98 }
99 
100 
101 // リサイズ計算
102 void
CalcResize(int * widthp,int * heightp)103 SixelConverter::CalcResize(int *widthp, int *heightp)
104 {
105 	int& width = *widthp;
106 	int& height = *heightp;
107 
108 	auto ra = ResizeAxis;
109 	bool scaledown =
110 		(ra == ResizeAxisMode::ScaleDownBoth)
111 	 || (ra == ResizeAxisMode::ScaleDownWidth)
112 	 || (ra == ResizeAxisMode::ScaleDownHeight)
113 	 || (ra == ResizeAxisMode::ScaleDownLong)
114 	 || (ra == ResizeAxisMode::ScaleDownShort);
115 
116 	// 条件を丸めていく
117 	switch (ra) {
118 	 case ResizeAxisMode::Both:
119 	 case ResizeAxisMode::ScaleDownBoth:
120 		if (ResizeWidth == 0) {
121 			ra = ResizeAxisMode::Height;
122 		} else if (ResizeHeight == 0) {
123 			ra = ResizeAxisMode::Width;
124 		} else {
125 			ra = ResizeAxisMode::Both;
126 		}
127 		break;
128 
129 	 case ResizeAxisMode::Long:
130 	 case ResizeAxisMode::ScaleDownLong:
131 		if (Width >= Height) {
132 			ra = ResizeAxisMode::Width;
133 		} else {
134 			ra = ResizeAxisMode::Height;
135 		}
136 		break;
137 
138 	 case ResizeAxisMode::Short:
139 	 case ResizeAxisMode::ScaleDownShort:
140 		if (Width <= Height) {
141 			ra = ResizeAxisMode::Width;
142 		} else {
143 			ra = ResizeAxisMode::Height;
144 		}
145 		break;
146 
147 	 case ResizeAxisMode::ScaleDownWidth:
148 		ra = ResizeAxisMode::Width;
149 		break;
150 
151 	 case ResizeAxisMode::ScaleDownHeight:
152 		ra = ResizeAxisMode::Height;
153 		break;
154 
155 	 default:
156 		__builtin_unreachable();
157 		break;
158 	}
159 
160 	auto rw = ResizeWidth;
161 	auto rh = ResizeHeight;
162 
163 	if (rw <= 0)
164 		rw = Width;
165 	if (rh <= 0)
166 		rh = Height;
167 
168 	// 縮小のみ指示
169 	if (scaledown) {
170 		if (Width < rw)
171 			rw = Width;
172 		if (Height < rh)
173 			rh = Height;
174 	}
175 
176 	// 確定したので計算
177 	switch (ra) {
178 	 case ResizeAxisMode::Both:
179 		width = rw;
180 		height = rh;
181 		break;
182 	 case ResizeAxisMode::Width:
183 		width = rw;
184 		height = Height * width / Width;
185 		break;
186 	 case ResizeAxisMode::Height:
187 		height = rh;
188 		width = Width * height / Height;
189 		break;
190 	 default:
191 		__builtin_unreachable();
192 		break;
193 	}
194 }
195 
196 //
197 // ----- 前処理
198 //
199 
200 // インデックスカラーに変換する。
201 void
ConvertToIndexed()202 SixelConverter::ConvertToIndexed()
203 {
204 	// リサイズ
205 	int width = 0;
206 	int height = 0;
207 	CalcResize(&width, &height);
208 
209 	Debug(diag, "Resize to (%d,%d)", width, height);
210 
211 	Width = width;
212 	Height = height;
213 
214 	Indexed.resize(Width * Height);
215 
216 	Debug(diag, "SetColorMode(%s, %s, %d)",
217 		ImageReductor::RCM2str(ColorMode),
218 		ImageReductor::RFM2str(FinderMode),
219 		GrayCount);
220 	ir.SetColorMode(ColorMode, FinderMode, GrayCount);
221 
222 	Debug(diag, "SetAddNoiseLevel=%d", AddNoiseLevel);
223 	ir.SetAddNoiseLevel(AddNoiseLevel);
224 
225 	ir.Convert(ReduceMode, img, Indexed, Width, Height);
226 	Trace(diag, "Converted");
227 }
228 
229 //
230 // ----- Sixel 出力
231 //
232 
233 #define ESC "\x1b"
234 #define DCS ESC "P"
235 
236 // Sixel の開始コードとパレットを文字列で返す。
237 std::string
SixelPreamble()238 SixelConverter::SixelPreamble()
239 {
240 	std::string linebuf;
241 
242 	// Sixel 開始コード
243 	linebuf += DCS;
244 	linebuf += string_format("7;%d;q\"1;1;%d;%d", OutputMode, Width, Height);
245 
246 	// パレットを出力
247 	if (OutputPalette) {
248 		for (int i = 0; i < ir.GetPaletteCount(); i++) {
249 			const auto& col = ir.GetPalette(i);
250 			linebuf += string_format("#%d;%d;%d;%d;%d", i, 2,
251 				col.r * 100 / 255,
252 				col.g * 100 / 255,
253 				col.b * 100 / 255);
254 		}
255 	}
256 
257 	return linebuf;
258 }
259 
260 static int
MyLog2(int n)261 MyLog2(int n)
262 {
263 	for (int i = 0; i < 8; i++) {
264 		if (n <= (1 << i)) {
265 			return i;
266 		}
267 	}
268 	return 8;
269 }
270 
271 // OR モードで Sixel コア部分を stream に出力する。
272 // 成功すれば true、(書き込みに)失敗すれば false を返す。
273 bool
SixelToStreamCore_ORmode(OutputStream * stream)274 SixelConverter::SixelToStreamCore_ORmode(OutputStream *stream)
275 {
276 	uint8 *p0 = Indexed.data();
277 	int w = Width;
278 	int h = Height;
279 	int n;
280 
281 	// パレットのビット数
282 	int bcnt = MyLog2(ir.GetPaletteCount());
283 	Debug(diag, "%s bcnt=%d\n", __func__, bcnt);
284 
285 	uint8 sixelbuf[(w + 5) * bcnt];
286 
287 	uint8 *p = p0;
288 	int y;
289 	// 一つ手前の SIXEL 行まで変換
290 	for (y = 0; y < h - 6; y += 6) {
291 		int len = sixel_image_to_sixel_h6_ormode(sixelbuf, p, w, 6, bcnt);
292 		n = stream->Write(sixelbuf, len);
293 		if (n < len) {
294 			return false;
295 		}
296 		p += w * 6;
297 	}
298 	// 最終 SIXEL 行を変換
299 	int len = sixel_image_to_sixel_h6_ormode(sixelbuf, p, w, h - y, bcnt);
300 	n = stream->Write(sixelbuf, len);
301 	if (n < len) {
302 		return false;
303 	}
304 	return true;
305 }
306 
307 // Sixel コア部分を stream に出力する。
308 // 成功すれば true、(書き込みに)失敗すれば false を返す。
309 bool
SixelToStreamCore(OutputStream * stream)310 SixelConverter::SixelToStreamCore(OutputStream *stream)
311 {
312 	// 030 ターゲット
313 
314 	uint8 *p0 = Indexed.data();
315 	int w = Width;
316 	int h = Height;
317 	int src = 0;
318 
319 	int PaletteCount = ir.GetPaletteCount();
320 	Debug(diag, "%s Output=Normal PaletteCount=%d", __func__, PaletteCount);
321 
322 	// カラー番号ごとの、X 座標の min, max を計算する。
323 	// short でいいよね…
324 	int16 min_x[PaletteCount];
325 	int16 max_x[PaletteCount];
326 
327 	for (int16 y = 0; y < h; y += 6) {
328 		std::string linebuf;
329 
330 		src = y * w;
331 
332 		memset(min_x, -1, sizeof(min_x));
333 		memset(max_x,  0, sizeof(max_x));
334 
335 		// h が 6 の倍数でない時には溢れてしまうので、上界を計算する
336 		int16 max_dy = 6;
337 		if (y + max_dy > h) {
338 			max_dy = (int16)(h - y);
339 		}
340 
341 		// 各カラーの X 座標範囲を計算する
342 		for (int16 dy = 0; dy < max_dy; dy++) {
343 			for (int16 x = 0; x < w; x++) {
344 				uint8 I = p0[src++];
345 				if (min_x[I] < 0 || min_x[I] > x)
346 					min_x[I] = x;
347 				if (max_x[I] < x)
348 					max_x[I] = x;
349 			}
350 		}
351 
352 		for (;;) {
353 			// 出力するべきカラーがなくなるまでのループ
354 			Verbose(diag, "for1");
355 			int16 mx = -1;
356 
357 			for (;;) {
358 				// 1行の出力で出力できるカラーのループ
359 				Verbose(diag, "for2");
360 
361 				uint8 min_color = 0;
362 				int16 min = INT16_MAX;
363 
364 				// min_x から、mx より大きいもののうち最小のカラーを探して、
365 				// 塗っていく
366 				for (int16 c = 0; c < PaletteCount; c++) {
367 					if (mx < min_x[c] && min_x[c] < min) {
368 						min_color = (uint8)c;
369 						min = min_x[c];
370 					}
371 				}
372 				// なければ抜ける
373 				if (min_x[min_color] <= mx) {
374 					break;
375 				}
376 
377 				// Sixel に色コードを出力
378 				linebuf += string_format("#%d", min_color);
379 
380 				// 相対 X シーク処理
381 				int16 space = min_x[min_color] - (mx + 1);
382 				if (space > 0) {
383 					linebuf += SixelRepunit(space, 0);
384 				}
385 
386 				// パターンが変わったら、それまでのパターンを出していく
387 				// アルゴリズム
388 				uint8 prev_t = 0;
389 				int16 n = 0;
390 				for (int16 x = min_x[min_color]; x <= max_x[min_color]; x++) {
391 					uint8 t = 0;
392 					for (int16 dy = 0; dy < max_dy; dy++) {
393 						uint8 I = p0[(y + dy) * w + x];
394 						if (I == min_color) {
395 							t |= 1 << dy;
396 						}
397 					}
398 
399 					if (prev_t != t) {
400 						if (n > 0) {
401 							linebuf += SixelRepunit(n, prev_t);
402 						}
403 						prev_t = t;
404 						n = 1;
405 					} else {
406 						n++;
407 					}
408 				}
409 				// 最後のパターン
410 				if (prev_t != 0 && n > 0) {
411 					linebuf += SixelRepunit(n, prev_t);
412 				}
413 
414 				// X 位置を更新
415 				mx = max_x[min_color];
416 				// 済んだ印
417 				min_x[min_color] = -1;
418 			}
419 
420 			linebuf += '$';
421 
422 			// 最後までやったら抜ける
423 			if (mx == -1)
424 				break;
425 		}
426 
427 		linebuf += '-';
428 
429 		if (stream->Write(linebuf) == false) {
430 			return false;
431 		}
432 	}
433 	return true;
434 }
435 
436 // Sixel の終了コードを文字列で返す
437 std::string
SixelPostamble()438 SixelConverter::SixelPostamble()
439 {
440 	return ESC "\\";
441 }
442 
443 // Sixel を stream に出力する。
444 // 成功すれば true、失敗すれば false を返す。
445 bool
SixelToStream(OutputStream * stream)446 SixelConverter::SixelToStream(OutputStream *stream)
447 {
448 	Trace(diag, "%s", __func__);
449 	assert(ir.GetPaletteCount() != 0);
450 
451 	// 開始コードとかの出力
452 	if (stream->Write(SixelPreamble()) == false) {
453 		return false;
454 	}
455 
456 	if (OutputMode == SixelOutputMode::Or) {
457 		if (SixelToStreamCore_ORmode(stream) == false) {
458 			return false;
459 		}
460 	} else {
461 		if (SixelToStreamCore(stream) == false) {
462 			return false;
463 		}
464 	}
465 
466 	if (stream->Write(SixelPostamble()) == false) {
467 		return false;
468 	}
469 	return true;
470 }
471 
472 // 繰り返しのコードを考慮して、Sixel パターン文字列を返す
473 /*static*/ std::string
SixelRepunit(int n,uint8 ptn)474 SixelConverter::SixelRepunit(int n, uint8 ptn)
475 {
476 	std::string v;
477 
478 	if (n >= 4) {
479 		v = string_format("!%d%c", n, ptn + 0x3f);
480 	} else {
481 		v = std::string(n, ptn + 0x3f);
482 	}
483 	return v;
484 }
485 
486 //
487 // enum を文字列にしたやつ orz
488 //
489 
490 static const char *SRM2str_[] = {
491 	"ByLoad",
492 	"ByImageReductor",
493 };
494 
495 /*static*/ const char *
SOM2str(SixelOutputMode val)496 SixelConverter::SOM2str(SixelOutputMode val)
497 {
498 	if (val == Normal)	return "Normal";
499 	if (val == Or)		return "Or";
500 	return "?";
501 }
502 
503 /*static*/ const char *
SRM2str(SixelResizeMode val)504 SixelConverter::SRM2str(SixelResizeMode val)
505 {
506 	return ::SRM2str_[(int)val];
507 }
508