1 /*$
2 Copyright (C) 2013-2020 Azel.
3
4 This file is part of AzPainter.
5
6 AzPainter 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 AzPainter 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 * MaterialImgList
22 *
23 * 素材画像の管理リスト
24 *****************************************/
25 /*
26 * - 現在使用されているブラシ形状/テクスチャ画像を管理する。
27 * - 最近使われたものほどリストの先頭にある。
28 * - ブラシ用の形状/テクスチャ画像は、最近使ったものを最大5つ保存しておく。
29 * - レイヤのテクスチャは常に読み込んでおく。
30 */
31
32 #include <string.h>
33
34 #include "mDef.h"
35 #include "mGui.h"
36 #include "mList.h"
37 #include "mPath.h"
38 #include "mStr.h"
39
40 #include "ImageBuf8.h"
41 #include "ImageBuf24.h"
42
43 #include "defConfig.h"
44 #include "defMacros.h"
45
46 #include "MaterialImgList.h"
47
48
49 //-----------------------
50
51 /** MaterialImgList 本体データ */
52
53 typedef struct _MaterialImgList
54 {
55 mList list[2];
56 }MaterialImgList;
57
58 /** リストアイテムデータ */
59
60 typedef struct __item
61 {
62 mListItem i;
63
64 ImageBuf8 *img;
65 char *path; //ファイルパス
66 int refcnt; //参照カウンタ
67 /* 参照される毎に加算していく。
68 * 明示的に解放されるまで保持される。
69 * 0 の場合は、保存数より後ろの位置に来た場合、削除される。 */
70 }_item;
71
72 //-----------------------
73
74 static MaterialImgList g_matimglist;
75
76 //-----------------------
77
78 #define _TOPITEM(list) ((_item *)(list)->top)
79 #define _ITEM(p) ((_item *)(p))
80 #define _NEXTITEM(p) ((_item *)(p)->i.next)
81
82 #define _SAVE_NUM 5
83
84 #define _LIST_TEXTURE 0
85 #define _LIST_BRUSH 1
86
87 //-----------------------
88
89
90 //=========================
91 // sub
92 //=========================
93
94
95 /** リスト内に同じパスの画像があるか */
96
_search_path(mList * list,const char * path)97 static _item *_search_path(mList *list,const char *path)
98 {
99 _item *pi;
100
101 for(pi = _TOPITEM(list); pi; pi = _NEXTITEM(pi))
102 {
103 if(mPathCompareEq(path, pi->path))
104 return pi;
105 }
106
107 return NULL;
108 }
109
110 /** 古いものを削除 (新規追加前)
111 *
112 * relcnt が 0 のものが対象 */
113
_delete_old(mList * list)114 static void _delete_old(mList *list)
115 {
116 _item *pi,*next;
117 int cnt = 0;
118
119 for(pi = _TOPITEM(list); pi; pi = next)
120 {
121 next = _NEXTITEM(pi);
122
123 if(pi->refcnt == 0)
124 {
125 cnt++;
126
127 if(cnt >= _SAVE_NUM)
128 mListDelete(list, M_LISTITEM(pi));
129 }
130 }
131 }
132
133 /** 画像読み込み */
134
_load_image(int type,const char * path)135 static ImageBuf8 *_load_image(int type,const char *path)
136 {
137 ImageBuf24 *img24;
138 ImageBuf8 *img8;
139 mStr str = MSTR_INIT;
140
141 //パス
142
143 if(path[0] == '/')
144 {
145 //システム
146
147 mAppGetDataPath(&str,
148 (type == MATERIALIMGLIST_TYPE_BRUSH_SHAPE)? APP_BRUSH_PATH: APP_TEXTURE_PATH);
149
150 mStrPathAdd(&str, path + 1);
151 }
152 else
153 {
154 //ユーザー
155
156 mStrCopy(&str,
157 (type == MATERIALIMGLIST_TYPE_BRUSH_SHAPE)?
158 &APP_CONF->strUserBrushDir: &APP_CONF->strUserTextureDir);
159
160 mStrPathAdd(&str, path);
161 }
162
163 //24bit 読み込み
164
165 img24 = ImageBuf24_loadFile(str.buf);
166
167 mStrFree(&str);
168
169 if(!img24) return NULL;
170
171 //8bit 変換
172
173 if(type == MATERIALIMGLIST_TYPE_BRUSH_SHAPE)
174 img8 = ImageBuf8_createFromImageBuf24_forBrush(img24);
175 else
176 img8 = ImageBuf8_createFromImageBuf24(img24);
177
178 ImageBuf24_free(img24);
179
180 return img8;
181 }
182
183 /** アイテム破棄ハンドラ */
184
_destroy_item(mListItem * p)185 static void _destroy_item(mListItem *p)
186 {
187 ImageBuf8_free(_ITEM(p)->img);
188 mFree(_ITEM(p)->path);
189 }
190
191
192 //=========================
193 // main
194 //=========================
195
196
197 /** 解放 */
198
MaterialImgList_free()199 void MaterialImgList_free()
200 {
201 mListDeleteAll(&g_matimglist.list[0]);
202 mListDeleteAll(&g_matimglist.list[1]);
203 }
204
205 /** 初期化 */
206
MaterialImgList_init()207 void MaterialImgList_init()
208 {
209 mMemzero(&g_matimglist, sizeof(MaterialImgList));
210 }
211
212 /** イメージ取得
213 *
214 * keep = TRUE にしたブラシ画像の場合は、必ず明示的に MaterialImgList_releaseImage() を実行。
215 * 通常のブラシ画像の場合は解放する必要はない。
216 *
217 * @param keep イメージを常に保持する (レイヤテクスチャの場合は常に TRUE) */
218
MaterialImgList_getImage(int type,const char * path,mBool keep)219 ImageBuf8 *MaterialImgList_getImage(int type,const char *path,mBool keep)
220 {
221 mList *list;
222 _item *pi;
223 ImageBuf8 *img8;
224
225 //パスが NULL or 空 => 画像なし
226
227 if(!path || !path[0]) return NULL;
228
229 //ブラシテクスチャでパスが "?" => オプション指定を使う
230
231 if(type == MATERIALIMGLIST_TYPE_BRUSH_TEXTURE
232 && strcmp(path, "?") == 0)
233 return NULL;
234
235 //リスト内に同じものがあるか
236
237 list = g_matimglist.list + ((type == MATERIALIMGLIST_TYPE_BRUSH_SHAPE)? _LIST_BRUSH: _LIST_TEXTURE);
238
239 pi = _search_path(list, path);
240
241 //新規追加
242
243 if(!pi)
244 {
245 //画像読み込み
246
247 img8 = _load_image(type, path);
248 if(!img8) return NULL;
249
250 //古いものを削除
251
252 _delete_old(list);
253
254 //リストに追加
255
256 pi = (_item *)mListAppendNew(list, sizeof(_item), _destroy_item);
257 if(!pi)
258 {
259 ImageBuf8_free(img8);
260 return NULL;
261 }
262
263 pi->img = img8;
264 pi->path = mStrdup(path);
265 }
266
267 //アイテムを先頭へ移動
268
269 mListMoveTop(list, M_LISTITEM(pi));
270
271 //常に保持する場合は参照カウンタを +1
272
273 if(type == MATERIALIMGLIST_TYPE_LAYER_TEXTURE || keep)
274 pi->refcnt++;
275
276 //[debug]
277
278 /*
279 _item *pp;
280
281 for(pp = _TOPITEM(list); pp; pp = _NEXTITEM(pp))
282 mDebug("%d:%s\n", pp->refcnt, pp->path);
283
284 mDebug("---\n");
285 */
286
287 return pi->img;
288 }
289
290 /** 指定イメージをリストから解放
291 *
292 * 参照カウンタを引く。
293 * 参照カウンタが 0 になった場合は、保持数を外れたら自動解放される。 */
294
MaterialImgList_releaseImage(int type,ImageBuf8 * img)295 void MaterialImgList_releaseImage(int type,ImageBuf8 *img)
296 {
297 _item *pi;
298
299 if(!img) return;
300
301 for(pi = _ITEM(g_matimglist.list[type].top); pi; pi = _NEXTITEM(pi))
302 {
303 if(pi->img == img)
304 {
305 if(pi->refcnt) pi->refcnt--;
306 break;
307 }
308 }
309 }
310