1 module converter;
2 
3 //import imageformats;
4 
5 import PixelPerfectEngine.graphics.bitmap;
6 import PixelPerfectEngine.extbmp.extbmp;
7 import PixelPerfectEngine.system.exc;
8 import PixelPerfectEngine.system.etc;
9 import PixelPerfectEngine.map.mapload;
10 
11 import derelict.freeimage.freeimage;
12 //import derelict.freeimage.functions;
13 import derelict.freeimage.types;
14 
15 import std.stdio;
16 import std.path;
17 import std.bitmanip;
18 import std.conv;
19 
20 /*Bitmap32Bit import32BitBitmapFromFile(string filename){
21 
22 	FREE_IMAGE_FORMAT format;
23 	switch(filename[filename.length-3..filename.length]){
24 		case "png": format = FIF_PNG; break;
25 		case "tga": format = FIF_TARGA; break;
26 		case "bmp": format = FIF_BMP; break;
27 		default: break;
28 	}
29 
30 	const char* fn = std.string.toStringz(filename);
31 	FIBITMAP* source = FreeImage_Load(format, fn);
32 	int iX = FreeImage_GetWidth(source), iY = FreeImage_GetHeight(source);
33 	Bitmap32Bit result = new Bitmap32Bit(iX,iY);
34 	switch(FreeImage_GetBPP(source)){
35 		case 32:
36 			for(int y; y < iY; y++){
37 				for(int x; x < iX; x++){
38 					RGBQUAD c; FreeImage_GetPixelColor(source, x, iY - 1 - y, &c);
39 					result.writePixel(x,y,c.rgbRed,c.rgbGreen,c.rgbBlue,c.rgbReserved);
40 					//writeln(c.rgbRed,',',c.rgbGreen,',',c.rgbBlue,',',c.rgbReserved,',');
41 				}
42 			}
43 			break;
44 		default:
45 			for(int y; y < iY; y++){
46 				for(int x; x < iX; x++){
47 					RGBQUAD c; FreeImage_GetPixelColor(source, x, iY - 1 - y, &c);
48 					result.writePixel(x,y,c.rgbRed,c.rgbGreen,c.rgbBlue,255);
49 					//writeln(c.rgbRed,',',c.rgbGreen,',',c.rgbBlue,',',c.rgbReserved,',');
50 				}
51 			}
52 			break;
53 	}
54 
55 	return result;
56 }*/
57 
58 enum NumberingStyle{
59 	DECIMAL		=	0,
60 	OCTAL		=	1,
61 	HEXADECIMAL	=	2,
62 	CHAR		=	3,
63 	WCHAR		=	4
64 }
65 
66 class ImportData{
67 	string[] ID;
68 	string bitdepth, format;
69 	int x, y, IDpos;
70 	ushort paletteOffset;
71 	NamingConvention nc;
72 	this(string[] ID, string bitdepth, int x, int y, ushort paletteOffset){
73 		this.ID = ID;
74 		this.bitdepth = bitdepth;
75 		this.x = x;
76 		this.y = y;
77 		this.paletteOffset = paletteOffset;
78 		//this.numOfDigits = numOfDigits;
79 	}
80 	this(NamingConvention nc, string bitdepth, int x, int y, ushort paletteOffset){
81 		this.nc = nc;
82 		this.bitdepth = bitdepth;
83 		this.x = x;
84 		this.y = y;
85 		this.paletteOffset = paletteOffset;
86 		//this.numOfDigits = numOfDigits;
87 	}
88 	string getNextID(){
89 		if(nc is null){
90 			string result = ID[IDpos];
91 			IDpos++;
92 			return result;
93 		}else{
94 			string result = nc.wordA;
95 			switch(nc.incrStyle){
96 				case NumberingStyle.OCTAL: result ~= intToOct(IDpos+nc.startingPoint,nc.format); break;
97 				case NumberingStyle.HEXADECIMAL: result ~= intToHex(IDpos+nc.startingPoint,nc.format); break;
98 				default: result ~= to!string(IDpos+nc.startingPoint); break;
99 			}
100 			IDpos++;
101 			result ~= nc.wordB;
102 			return result;
103 		}
104 	}
105 	bool isMulti(){
106 		if(x > 0 && y > 0) return true;
107 		return false;
108 	}
109 
110 }
111 
112 class NamingConvention{
113 	string wordA, wordB;
114 	NumberingStyle incrStyle;
115 	int startingPoint, format;
116 	this(string wordA, string wordB, NumberingStyle incrStyle, int startingPoint, int format){
117 		this.wordA = wordA;
118 		this.wordB = wordB;
119 		this.incrStyle = incrStyle;
120 		this.startingPoint = startingPoint;
121 		this.format = format;
122 	}
123 }
124 
125 public class BitmapInfo{
126 	int width, height;
127 	public this(){
128 		
129 	}
130 }
131 
132 public BitmapInfo getBitmapInfo(string path){
133 	import std..string;
134 	FREE_IMAGE_FORMAT format;
135 	switch(extension(path)){
136 		case ".png": format = FIF_PNG; break;
137 		case ".tga": format = FIF_TARGA; break;
138 		case ".bmp": format = FIF_BMP; break;
139 		default: break;
140 	}
141 	const char* fn = std..string.toStringz(path);
142 	FIBITMAP* source = FreeImage_Load(format, fn);
143 	BitmapInfo bi = new BitmapInfo;
144 	bi.width = FreeImage_GetWidth(source);
145 	bi.height = FreeImage_GetHeight(source);
146 	if(source)
147 		FreeImage_Unload(source);
148 	return bi;
149 }
150 
151 public void importDirectlyToXMP(string path, string palette, ExtendibleBitmap target, ImportData id){
152 	import std..string;
153 	FREE_IMAGE_FORMAT format;
154 	switch(extension(path)){
155 		case ".png": format = FIF_PNG; break;
156 		case ".tga": format = FIF_TARGA; break;
157 		case ".bmp": format = FIF_BMP; break;
158 		default: break;
159 	}
160 	const char* fn = std..string.toStringz(path);
161 	FIBITMAP* source = FreeImage_Load(format, fn);
162 	int iX = FreeImage_GetWidth(source), iY = FreeImage_GetHeight(source);
163 	ubyte[] raw; ushort[] raw16;
164 	switch(id.bitdepth){
165 		case "1bit":
166 			BitArray ba = BitArray(cast(void[])raw, 0);
167 			ba.length(iX * iY);
168 			for(int y; y < iY; y++){
169 				for(int x; x < iX; x++){
170 					ubyte c; FreeImage_GetPixelIndex(source, x, iY - 1 - y, &c);
171 					if(c != 0){
172 						ba[x + (iX * y)] = true;
173 					}
174 				}
175 			}
176 			break;
177 		case "16bit": 
178 			for(int y; y < iY; y++){
179 				for(int x; x < iX; x++){
180 					ubyte c; FreeImage_GetPixelIndex(source, x, iY - 1 - y, &c);
181 					raw16 ~= to!ushort(id.paletteOffset + c);
182 				}
183 			} 
184 			break;
185 		default:
186 			switch(FreeImage_GetBPP(source)){
187 				case 1,2,4,8:
188 					for(int y; y < iY; y++){
189 						for(int x; x < iX; x++){
190 							ubyte c; FreeImage_GetPixelIndex(source, x, iY - 1 - y, &c);
191 							raw ~= c;
192 						}
193 					}
194 					break;
195 				case 32:
196 					for(int y; y < iY; y++){
197 						for(int x; x < iX; x++){
198 							RGBQUAD c; FreeImage_GetPixelColor(source, x, iY - 1 - y, &c);
199 							//result.writePixel(x,y,c.rgbRed,c.rgbGreen,c.rgbBlue,c.rgbReserved);
200 							//writeln(c.rgbRed,',',c.rgbGreen,',',c.rgbBlue,',',c.rgbReserved,',');
201 							raw ~= [c.rgbRed,c.rgbGreen,c.rgbBlue,c.rgbReserved];
202 						}
203 					}
204 					break;
205 				default:
206 					for(int y; y < iY; y++){
207 						for(int x; x < iX; x++){
208 							RGBQUAD c; FreeImage_GetPixelColor(source, x, iY - 1 - y, &c);
209 							//result.writePixel(x,y,c.rgbRed,c.rgbGreen,c.rgbBlue,255);
210 							//writeln(c.rgbRed,',',c.rgbGreen,',',c.rgbBlue,',',c.rgbReserved,',');
211 							raw ~= [c.rgbRed,c.rgbGreen,c.rgbBlue,255];
212 						}
213 					}
214 					break;
215 			}
216 			break;
217 	}
218 	if(id.isMulti){
219 		if(iX%id.x > 0 || iY%id.y > 0){
220 			throw new BitmapFormatException("Incorrect sizes for slicing!");
221 		}
222 		if(id.bitdepth == "16bit"){
223 			//target.addBitmap(raw16,id.x,id.y,id.bitdepth,id.ID[0]);
224 			for(int jY; jY < iY / id.y; jY++){
225 				for(int jX; jX < iX / id.x; jX++){
226 					ushort[] raw2;
227 					for(int y; y < id.y; y++){
228 						int from = ((jY * id.y * iX) + (y * iX) + (jX * id.x)), t = from + id.x;
229 						raw2 ~= raw16[from..t];
230 					}
231 					target.addBitmap(raw2,id.x,id.y,id.bitdepth,id.getNextID(),id.format,palette);
232 					//si++;
233 				}
234 			}
235 		}else{
236 			int pitch = 1;
237 			if(id.bitdepth == "32bit")pitch = 4;
238 			for(int jY; jY < iY / id.y; jY++){
239 				for(int jX; jX < iX / id.x; jX++){
240 					ubyte[] raw2;
241 					for(int y; y < id.y; y++){
242 						int from = pitch * ((jY * id.y * iX) + (y * iX) + (jX * id.x)), t = from + (id.x * pitch);
243 						raw2~= raw[from..t];
244 					}
245 					target.addBitmap(raw2,id.x,id.y,id.bitdepth,id.getNextID(),id.format,palette);
246 					//si++;
247 				}
248 			}
249 			
250 		}
251 	}else{
252 		if(id.bitdepth == "16bit"){
253 			target.addBitmap(raw16,iX,iY,id.bitdepth,id.ID[0]);
254 		}else{
255 			target.addBitmap(raw,iX,iY,id.bitdepth,id.ID[0],id.format);
256 		}
257 	}
258 	if(source)
259 		FreeImage_Unload(source);
260 }
261 
262 public void importPaletteDirectlyToXMP(string path, ExtendibleBitmap target, string paletteID, ushort offset = 0){
263 	import std..string;
264 	FREE_IMAGE_FORMAT format;
265 	switch(extension(path)){
266 		case ".png": format = FIF_PNG; break;
267 		case ".tga": format = FIF_TARGA; break;
268 		case ".bmp": format = FIF_BMP; break;
269 		default: break;
270 	}
271 	const char* fn = std..string.toStringz(path);
272 	FIBITMAP* source = FreeImage_Load(format, fn);
273 	uint bitdepth = FreeImage_GetBPP(source);
274 
275 	ubyte[] palette;
276 	switch(bitdepth){
277 		case 4: palette.length = 64; break;
278 		case 8: palette.length = 1024; break;
279 		default: break;
280 	}
281 	RGBQUAD* colors = FreeImage_GetPalette(source);
282 	//RGBQUAD.sizeof;
283 	palette[0] = 0;
284 	palette[1] = colors.rgbRed;
285 	palette[2] = colors.rgbGreen;
286 	palette[3] = colors.rgbBlue;
287 	for(int i = 4; i < palette.length; i+=4){
288 		colors++;
289 		palette[i] = 255;
290 		palette[i + 1] = colors.rgbRed;
291 		palette[i + 2] = colors.rgbGreen;
292 		palette[i + 3] = colors.rgbBlue;
293 
294 	}
295 	target.addPalette(cast(void[])palette, paletteID);
296 	if(source)
297 		FreeImage_Unload(source);
298 }
299 
300 public Bitmap32Bit getBitmapPreview(ExtendibleBitmap xmp, string ID){
301 	Bitmap32Bit result;
302 	switch(xmp.getBitDepth(ID)){
303 		case "32bit":
304 			result = new Bitmap32Bit(cast(Color[])xmp.getBitmap(ID),xmp.getXsize(ID),xmp.getYsize(ID));
305 			break;
306 		case "16bit":
307 			ushort[] raw = xmp.get16bitBitmap(ID);
308 			Color[] clut = cast(Color[])xmp.getPalette(xmp.getPaletteMode(ID));
309 			Color[] res2;
310 			foreach(c; raw){
311 				res2 ~= clut[c];
312 			}
313 			result = new Bitmap32Bit(res2,xmp.getXsize(ID),xmp.getYsize(ID));
314 			break;
315 		case "8bit":
316 			ubyte[] raw = xmp.get8bitBitmap(ID);
317 			Color[] clut = cast(Color[])xmp.getPalette(xmp.getPaletteMode(ID));
318 			Color[] res2;
319 			foreach(c; raw){
320 				res2 ~= clut[c];
321 			}
322 			result = new Bitmap32Bit(res2,xmp.getXsize(ID),xmp.getYsize(ID));
323 			break;
324 		default: break;
325 	}
326 	return result;
327 }
328 
329 public void autoloadFromXMP(string filename, ExtendibleMap map, int layerNum){
330 	ExtendibleBitmap xmpFile = new ExtendibleBitmap(filename);
331 	map.addFileToTileSource(layerNum, filename);
332 	for(int i ; i < xmpFile.bitmapID.length ; i++){
333 		try{
334 			if(xmpFile.bitmapID[i].length <= 4){
335 				throw new Exception("");
336 			}
337 			wchar ID = to!wchar(parseHex(xmpFile.bitmapID[i][0..4]));
338 			string descr = xmpFile.bitmapID[i].length > 5 ? xmpFile.bitmapID[i][5..xmpFile.bitmapID[i].length] : "";
339 			map.addTileToTileSource(layerNum, ID, descr, xmpFile.bitmapID[i], filename);
340 		}catch(Exception e){
341 			writeln("Bitmap \'"~xmpFile.bitmapID[i]~"\' does not follow the format xxxx\\{description} and will be skipped.");
342 		}
343 	}
344 }
345 
346 enum LookupMethod : uint{
347 	NearestValue	=	1,
348 	Dithering		=	2
349 }