1 /*$
2  Copyright (C) 2016-2020 Azel.
3 
4  This file is part of AzPainterB.
5 
6  AzPainterB is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  AzPainterB is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 $*/
19 
20 /*****************************************
21  * TileImage
22  *
23  * 画像ファイルへの読み書き
24  *****************************************/
25 
26 #include <string.h>
27 
28 #include "mDef.h"
29 #include "mLoadImage.h"
30 #include "mSaveImage.h"
31 #include "mPopupProgress.h"
32 
33 #include "defTileImage.h"
34 #include "TileImage.h"
35 #include "TileImage_pv.h"
36 #include "TileImage_coltype.h"
37 #include "TileImageDrawInfo.h"
38 
39 #include "defFileFormat.h"
40 
41 
42 //===============================
43 // 画像読み込み
44 //===============================
45 
46 
47 typedef struct
48 {
49 	mLoadImage base;
50 	TileImageLoadFileInfo *dstinfo;
51 	mPopupProgress *prog;
52 	TileImage *img;
53 	int y,
54 		ignore_alpha;
55 }_loadimageinfo;
56 
57 
58 /** 情報取得 */
59 
_loadfile_getinfo(mLoadImage * param,mLoadImageInfo * info)60 static int _loadfile_getinfo(mLoadImage *param,mLoadImageInfo *info)
61 {
62 	_loadimageinfo *p = (_loadimageinfo *)param;
63 	int h,v;
64 
65 	//TileImage 作成
66 
67 	p->img = TileImage_new(TILEIMAGE_COLTYPE_RGBA, info->width, info->height);
68 	if(!p->img)
69 		return MLOADIMAGE_ERR_ALLOC;
70 
71 	p->y = (info->bottomup)? info->height - 1: 0;
72 
73 	//情報セット
74 
75 	if(p->dstinfo)
76 	{
77 		p->dstinfo->width = info->width;
78 		p->dstinfo->height = info->height;
79 		p->dstinfo->transparent = info->transparent_col;
80 
81 		if(mLoadImage_getResolution_dpi(param, &h, &v))
82 			p->dstinfo->dpi = h;
83 		else
84 			p->dstinfo->dpi = 0;
85 	}
86 
87 	//プログレス
88 
89 	if(p->prog)
90 		mPopupProgressThreadBeginSubStep_onestep(p->prog, 20, info->height);
91 
92 	return MLOADIMAGE_ERR_OK;
93 }
94 
95 /** Y1行取得 */
96 
_loadfile_getrow(mLoadImage * param,uint8_t * buf,int pitch)97 static int _loadfile_getrow(mLoadImage *param,uint8_t *buf,int pitch)
98 {
99 	_loadimageinfo *p = (_loadimageinfo *)param;
100 	int i;
101 	uint8_t *ps;
102 
103 	//アルファ値を無視
104 
105 	if(p->ignore_alpha)
106 	{
107 		ps = buf + 3;
108 
109 		for(i = param->info.width; i > 0; i--, ps += 4)
110 			*ps = 255;
111 	}
112 
113 	//タイルにセット
114 
115 	__TileImage_RGBA_setRow_fromRGBA(p->img, p->y, buf,
116 		param->info.width, param->info.height, param->info.bottomup);
117 
118 	//次へ
119 
120 	if(param->info.bottomup)
121 		p->y--;
122 	else
123 		p->y++;
124 
125 	if(p->prog)
126 		mPopupProgressThreadIncSubStep(p->prog);
127 
128 	return MLOADIMAGE_ERR_OK;
129 }
130 
131 /** ファイルから画像読み込み
132  *
133  * @param format   -1 で自動判別
134  * @param fileinfo NULL でなし
135  * @param prog     NULL でなし
136  * @param errmes   エラーメッセージ文字列のポインタが入る (NULLでなし。解放必要) */
137 
TileImage_loadFile(const char * filename,int format,mBool ignore_alpha,mBool leave_trans,int maxsize,TileImageLoadFileInfo * fileinfo,mPopupProgress * prog,char ** errmes)138 TileImage *TileImage_loadFile(const char *filename,
139 	int format,mBool ignore_alpha,mBool leave_trans,int maxsize,
140 	TileImageLoadFileInfo *fileinfo,mPopupProgress *prog,char **errmes)
141 {
142 	_loadimageinfo *p;
143 	mLoadImageFunc func;
144 	mBool ret;
145 	TileImage *img;
146 
147 	p = (_loadimageinfo *)mLoadImage_create(sizeof(_loadimageinfo));
148 	if(!p) return NULL;
149 
150 	p->dstinfo = fileinfo;
151 	p->prog = prog;
152 	p->ignore_alpha = ignore_alpha;
153 
154 	p->base.format = MLOADIMAGE_FORMAT_RGBA;
155 	p->base.flags = (leave_trans)? MLOADIMAGE_FLAGS_LEAVE_TRANSPARENT: 0;
156 	p->base.max_width = p->base.max_height = maxsize;
157 	p->base.src.type = MLOADIMAGE_SOURCE_TYPE_PATH;
158 	p->base.src.filename = filename;
159 	p->base.getinfo = _loadfile_getinfo;
160 	p->base.getrow = _loadfile_getrow;
161 
162 	//読込関数
163 
164 	if(format < 0)
165 	{
166 		//自動判別
167 
168 		if(!mLoadImage_checkFormat(&p->base.src, &func, MLOADIMAGE_CHECKFORMAT_F_ALL))
169 			return NULL;
170 	}
171 	else
172 	{
173 		switch(format)
174 		{
175 			case FILEFORMAT_PNG: func = mLoadImagePNG; break;
176 			case FILEFORMAT_JPEG: func = mLoadImageJPEG; break;
177 			case FILEFORMAT_GIF: func = mLoadImageGIF; break;
178 			case FILEFORMAT_BMP: func = mLoadImageBMP; break;
179 		}
180 	}
181 
182 	//実行
183 
184 	ret = (func)(M_LOADIMAGE(p));
185 
186 	img = p->img;
187 
188 	if(!ret)
189 	{
190 		TileImage_free(img);
191 		img = NULL;
192 
193 		if(errmes)
194 			*errmes = mStrdup(p->base.message);
195 	}
196 
197 	mLoadImage_free(M_LOADIMAGE(p));
198 
199 	return img;
200 }
201 
202 
203 //=========================================
204 // ADW/APD/PSD 読み込み用
205 //=========================================
206 
207 
208 /** タイルイメージを A8 => RGBA8 変換 */
209 
TileImage_convertTile_fromA8(uint8_t * buf,uint32_t col)210 void TileImage_convertTile_fromA8(uint8_t *buf,uint32_t col)
211 {
212 	int i;
213 	uint8_t *ps,*pd,r,g,b;
214 
215 	r = M_GET_R(col);
216 	g = M_GET_G(col);
217 	b = M_GET_B(col);
218 
219 	ps = buf + 64 * 64 - 1;
220 	pd = buf + (64 * 64 - 1) * 4;
221 
222 	for(i = 64 * 64; i; i--, ps--, pd -= 4)
223 	{
224 		pd[3] = *ps;
225 		pd[0] = r;
226 		pd[1] = g;
227 		pd[2] = b;
228 	}
229 }
230 
231 /** APD (ver2,ver3) のタイルイメージを RGBA8 に変換 */
232 
TileImage_convertTile_fromAPD(uint8_t * dst,uint8_t * src,int type,uint32_t col)233 void TileImage_convertTile_fromAPD(uint8_t *dst,uint8_t *src,int type,uint32_t col)
234 {
235 	int i,j;
236 	uint8_t *ps,*pd,r,g,b,f;
237 
238 	ps = src;
239 	pd = dst;
240 
241 	r = M_GET_R(col);
242 	g = M_GET_G(col);
243 	b = M_GET_B(col);
244 
245 	switch(type)
246 	{
247 		//RGBA 16bit (R(64x64)-G-B-A : BE)
248 		case 0:
249 			for(i = 0; i < 4; i++)
250 			{
251 				ps = src + (i << (6 + 6 + 1));
252 				pd = dst + i;
253 
254 				for(j = 64 * 64; j; j--, ps += 2, pd += 4)
255 					*pd = (((ps[0] << 8) | ps[1]) * 255 + 0x4000) >> 15;
256 			}
257 			break;
258 
259 		//GRAY+A 16bit (COL-A)
260 		case 1:
261 			//col
262 
263 			for(i = 64 * 64; i; i--, ps += 2, pd += 4)
264 				pd[0] = pd[1] = pd[2] = (((ps[0] << 8) | ps[1]) * 255 + 0x4000) >> 15;
265 
266 			//A
267 
268 			ps = src + 64 * 64 * 2;
269 			pd = dst + 3;
270 
271 			for(i = 64 * 64; i; i--, ps += 2, pd += 4)
272 				*pd = (((ps[0] << 8) | ps[1]) * 255 + 0x4000) >> 15;
273 			break;
274 
275 		//A 16bit
276 		case 2:
277 			for(i = 64 * 64; i; i--, ps += 2, pd += 4)
278 			{
279 				pd[0] = r;
280 				pd[1] = g;
281 				pd[2] = b;
282 				pd[3] = (((ps[0] << 8) | ps[1]) * 255 + 0x4000) >> 15;
283 			}
284 			break;
285 
286 		//A 1bit
287 		case 3:
288 			for(i = 64 * 64, f = 0x80; i; i--, pd += 4)
289 			{
290 				pd[0] = r;
291 				pd[1] = g;
292 				pd[2] = b;
293 				pd[3] = (*ps & f)? 255: 0;
294 
295 				f >>= 1;
296 				if(f == 0) f = 0x80, ps++;
297 			}
298 			break;
299 	}
300 }
301 
302 /** A8 イメージのY1行をセット */
303 
TileImage_setRowImage_forA8(TileImage * p,uint8_t * buf,int y,int w,int h,uint32_t col)304 void TileImage_setRowImage_forA8(TileImage *p,uint8_t *buf,int y,int w,int h,uint32_t col)
305 {
306 	int i;
307 	uint8_t r,g,b,*ps,*pd;
308 
309 	r = M_GET_R(col);
310 	g = M_GET_G(col);
311 	b = M_GET_B(col);
312 
313 	//A8 => RGBA 変換
314 
315 	ps = buf + w - 1;
316 	pd = buf + ((w - 1) << 2);
317 
318 	for(i = w; i > 0; i--, ps--, pd -= 4)
319 	{
320 		pd[3] = *ps;
321 		pd[0] = r;
322 		pd[1] = g;
323 		pd[2] = b;
324 	}
325 
326 	//セット
327 
328 	__TileImage_RGBA_setRow_fromRGBA(p, y, buf, w, h, FALSE);
329 }
330 
331 /** BGRA イメージからY1行セット (bottom-up) */
332 
TileImage_setRowImage_forBGRA_bottomup(TileImage * p,uint8_t * buf,int y,int w,int h)333 void TileImage_setRowImage_forBGRA_bottomup(TileImage *p,uint8_t *buf,int y,int w,int h)
334 {
335 	int i;
336 	uint8_t *pd,tmp;
337 
338 	//BGRA => RGBA
339 
340 	for(i = w, pd = buf; i > 0; i--, pd += 4)
341 	{
342 		tmp = pd[0];
343 		pd[0] = pd[2];
344 		pd[2] = tmp;
345 	}
346 
347 	//セット
348 
349 	__TileImage_RGBA_setRow_fromRGBA(p, y, buf, w, h, TRUE);
350 }
351 
352 /** 8bit グレイスケールイメージからセット
353  *
354  * PSD 一枚絵 (1bit, グレイスケール) 読み込み用 */
355 
TileImage_setImage_from8bitGray(TileImage * p,uint8_t * buf,int w,int h,mPopupProgress * prog)356 void TileImage_setImage_from8bitGray(TileImage *p,uint8_t *buf,int w,int h,mPopupProgress *prog)
357 {
358 	int ix,iy;
359 	PixelRGBA pix;
360 
361 	pix.a = 255;
362 
363 	for(iy = 0; iy < h; iy++)
364 	{
365 		for(ix = 0; ix < w; ix++)
366 		{
367 			pix.r = pix.g = pix.b = *(buf++);
368 
369 			TileImage_setPixel_new(p, ix, iy, &pix);
370 		}
371 
372 		mPopupProgressThreadIncSubStep(prog);
373 	}
374 }
375 
376 /** PSD のチャンネルイメージをセット
377  *
378  * 色より先にアルファ値をセットすること。
379  *
380  * @param chno 0-3
381  * @param grayscale G,B にも同じ値をセット */
382 
TileImage_setChannelImage(TileImage * p,uint8_t * chbuf,int chno,int srcw,int srch,mBool grayscale)383 mBool TileImage_setChannelImage(TileImage *p,uint8_t *chbuf,int chno,int srcw,int srch,mBool grayscale)
384 {
385 	uint8_t **pptile = p->ppbuf,*buf,*ps,*psX,*psY,*pd;
386 	uint32_t *p32;
387 	int tx,ty,i,pitch,w,h,remw,remh;
388 
389 	buf = (uint8_t *)mMalloc(64 * 64, FALSE);
390 	if(!buf) return FALSE;
391 
392 	//
393 
394 	psY = chbuf;
395 	pitch = srcw * 64;
396 
397 	for(ty = p->tileh, remh = srch; ty; ty--, psY += pitch, remh -= 64)
398 	{
399 		psX = psY;
400 		h = (remh > 64)? 64: remh;
401 
402 		for(tx = p->tilew, remw = srcw; tx; tx--, pptile++, psX += 64, remw -= 64)
403 		{
404 			/* 色のチャンネルで、タイルが作成されていない場合、
405 			 * すべて透明なので、何もしない */
406 
407 			if(chno != 3 && !(*pptile)) continue;
408 
409 			//buf に 64x64 で取得
410 
411 			ps = psX;
412 			pd = buf;
413 			w = (remw > 64)? 64: remw;
414 
415 			if(w != 64 || h != 64)
416 				memset(buf, 0, 64 * 64);
417 
418 			for(i = h; i; i--)
419 			{
420 				memcpy(pd, ps, w);
421 
422 				ps += srcw;
423 				pd += 64;
424 			}
425 
426 			//アルファ値の場合、タイル作成
427 
428 			if(chno == 3)
429 			{
430 				//すべて 0 なら何もしない
431 
432 				p32 = (uint32_t *)buf;
433 
434 				for(i = 64 * 64 / 4; i && !(*p32); i--, p32++);
435 
436 				if(i == 0) continue;
437 
438 				//タイル作成
439 
440 				*pptile = TileImage_allocTile(p, TRUE);
441 				if(!(*pptile))
442 				{
443 					mFree(buf);
444 					return FALSE;
445 				}
446 			}
447 
448 			//タイルにセット
449 
450 			ps = buf;
451 			pd = *pptile + chno;
452 
453 			if(grayscale)
454 			{
455 				for(i = 64 * 64; i; i--, pd += 4, ps++)
456 					pd[0] = pd[1] = pd[2] = *ps;
457 			}
458 			else
459 			{
460 				for(i = 64 * 64; i; i--, pd += 4)
461 					*pd = *(ps++);
462 			}
463 		}
464 	}
465 
466 	mFree(buf);
467 
468 	return TRUE;
469 }
470 
471 
472 //=================================
473 // PNG 保存
474 //=================================
475 
476 
477 typedef struct
478 {
479 	mSaveImage base;
480 
481 	TileImage *img;
482 	mPopupProgress *prog;
483 	mBox box;
484 	int cury;
485 }_saveimg_png_box;
486 
487 
488 /** Y1行を送る */
489 
_savepng_box_send_row(mSaveImage * info,uint8_t * buf,int pitch)490 static int _savepng_box_send_row(mSaveImage *info,uint8_t *buf,int pitch)
491 {
492 	_saveimg_png_box *p = (_saveimg_png_box *)info;
493 	int i,w,x,y;
494 	PixelRGBA pix;
495 
496 	w = p->box.w;
497 	x = p->box.x;
498 	y = p->cury;
499 
500 	for(i = 0; i < w; i++, buf += 4)
501 	{
502 		TileImage_getPixel(p->img, x + i, y, &pix);
503 
504 		buf[0] = pix.r;
505 		buf[1] = pix.g;
506 		buf[2] = pix.b;
507 		buf[3] = pix.a;
508 	}
509 
510 	p->cury++;
511 
512 	mPopupProgressThreadIncSubStep(p->prog);
513 
514 	return MSAVEIMAGE_ERR_OK;
515 }
516 
517 /** PNG (アルファ付き) に保存 */
518 
TileImage_savePNG_rgba(TileImage * p,const char * filename,int dpi,mBox * box,mPopupProgress * prog)519 mBool TileImage_savePNG_rgba(TileImage *p,const char *filename,int dpi,mBox *box,
520 	mPopupProgress *prog)
521 {
522 	_saveimg_png_box *info;
523 	mBool ret;
524 
525 	info = (_saveimg_png_box *)mSaveImage_create(sizeof(_saveimg_png_box));
526 	if(!info) return FALSE;
527 
528 	info->img = p;
529 	info->prog = prog;
530 	info->box = *box;
531 	info->cury = box->y;
532 
533 	info->base.output.type = MSAVEIMAGE_OUTPUT_TYPE_PATH;
534 	info->base.output.filename = filename;
535 	info->base.width = box->w;
536 	info->base.height = box->h;
537 	info->base.sample_bits = 8;
538 	info->base.coltype = MSAVEIMAGE_COLTYPE_RGBA;
539 	info->base.resolution_unit = MSAVEIMAGE_RESOLITION_UNIT_DPI;
540 	info->base.resolution_horz = dpi;
541 	info->base.resolution_vert = dpi;
542 	info->base.send_row = _savepng_box_send_row;
543 
544 	//保存
545 
546 	mPopupProgressThreadBeginSubStep_onestep(prog, 20, box->h);
547 
548 	ret = mSaveImagePNG(M_SAVEIMAGE(info));
549 
550 	mSaveImage_free(M_SAVEIMAGE(info));
551 
552 	return ret;
553 }
554