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