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