1 module PixelPerfectEngine.system.file;
2 /*
3  * Copyright (C) 2015-2017, by Laszlo Szeremi under the Boost license.
4  *
5  * Pixel Perfect Engine, file module
6  */
7 
8 import std.file;
9 import std.path;
10 import std.stdio;
11 import std.conv : to;
12 import core.stdc..string : memcpy;
13 import PixelPerfectEngine.system.etc;
14 import PixelPerfectEngine.system.exc;
15 
16 import PixelPerfectEngine.graphics.bitmap;
17 import PixelPerfectEngine.graphics.raster;
18 import PixelPerfectEngine.graphics.fontsets;
19 
20 //import PixelPerfectEngine.extbmp.extbmp;
21 
22 public import dimage.base;
23 import dimage.tga;
24 import dimage.png;
25 import dimage.bmp;
26 
27 
28 import vfile;
29 
30 import bindbc.sdl.mixer;
31 
32 /**
33  * Loads an Image from a File or VFile.
34  * Automatically detects format from file extension.
35  */
36 public Image loadImage(F = File)(F file) @trusted{
37 	switch(extension(file.name)){
38 		case ".tga", ".TGA":
39 			TGA imageFile = TGA.load!(F, true, true)(file);
40 			if(!imageFile.getHeader.topOrigin){
41 				imageFile.flipVertical;
42 			}
43 			return imageFile;
44 		case ".png", ".PNG":
45 			PNG imageFile = PNG.load!F(file);
46 			return imageFile;
47 		case ".bmp", ".BMP":
48 			BMP imageFile = BMP.load!F(file);
49 			return imageFile;
50 		default:
51 			throw new Exception("Unsupported file format!");
52 	}
53 }
54 /**
55  * Loads a bitmap from Image.
56  */
57 public T loadBitmapFromImage(T)(Image img) @trusted
58 		if (is(T == Bitmap4Bit) || is(T == Bitmap8Bit) || is(T == Bitmap16Bit) || is(T == Bitmap32Bit)) {
59 	// Later we might want to detect image type from classinfo, until then let's rely on similarities between types
60 	static if(is(T == Bitmap4Bit)){
61 		if (img.getBitdepth == 4)
62 			return new Bitmap4Bit(img.imageData.raw, img.width, img.height);
63 		else if (img.getBitdepth < 4)
64 			return new Bitmap4Bit(img.imageData.convTo(PixelFormat.Indexed4Bit).raw, img.width, img.height);
65 		else
66 			throw new BitmapFormatException("Bitdepth mismatch exception!");	
67 
68 	}else static if(is(T == Bitmap8Bit)){
69 		if (img.getBitdepth == 8)
70 			return new Bitmap8Bit(img.imageData.raw, img.width, img.height);
71 		else if (img.getBitdepth < 8)
72 			return new Bitmap8Bit(img.imageData.convTo(PixelFormat.Indexed8Bit).raw, img.width, img.height);
73 		else
74 			throw new BitmapFormatException("Bitdepth mismatch exception!");
75 
76 	}else static if(is(T == Bitmap16Bit)){
77 		if (img.getBitdepth == 16)
78 			return new Bitmap16Bit(reinterpretCast!ushort(img.imageData.raw), img.width, img.height);
79 		else if (img.getBitdepth < 16)
80 			return new Bitmap16Bit(reinterpretCast!ushort(img.imageData.convTo(PixelFormat.Indexed16Bit).raw), 
81 					img.width, img.height);
82 		else
83 			throw new BitmapFormatException("Bitdepth mismatch exception!");
84 		
85 	}else static if(is(T == Bitmap32Bit)){
86 		return new Bitmap32Bit(reinterpretCast!Color(img.imageData.convTo(PixelFormat.ARGB8888 | PixelFormat.BigEndian).raw), 
87 				img.width, img.height);
88 
89 	}
90 
91 }
92 //TODO: Make collision model loader for 1 bit bitmaps
93 /**
94  * Loads a bitmap from disk.
95  * Currently supported formats: *.tga, *.png, *.bmp
96  */
97 public T loadBitmapFromFile(T)(string filename)
98 		if (is(T == Bitmap4Bit) || is(T == Bitmap8Bit) || is(T == Bitmap16Bit) || is(T == Bitmap32Bit)) {
99 	File f = File(filename);
100 	return loadBitmapFromImage!T(loadImage(f));
101 }
102 /**
103  * Loads a bitmap sheet from file.
104  * This one doesn't require TGA devarea extensions.
105  */
106 public T[] loadBitmapSheetFromFile(T)(string filename, int x, int y)
107 		if (is(T == Bitmap4Bit) || is(T == Bitmap8Bit) || is(T == Bitmap16Bit) || is(T == Bitmap32Bit)) {
108 	//T source = loadBitmapFromFile!T(filename);
109 	return loadBitmapSheetFromImage!T(loadImage(File(filename)), x, y);
110 }
111 /**
112  * Creates a bitmap sheet from an image file.
113  * This one doesn't require embedded data.
114  */
115 public T[] loadBitmapSheetFromImage(T)(Image img, int x, int y)
116 		if (is(T == Bitmap4Bit) || is(T == Bitmap8Bit) || is(T == Bitmap16Bit) || is(T == Bitmap32Bit)) {
117 	T source = loadBitmapFromImage!T(img);
118 	if(source.width % x == 0 && source.height % y == 0){
119 		T[] output;
120 		static if (is(T == Bitmap4Bit))
121 			const size_t length = x / 2, pitch = source.width / 2;
122 		else static if (is(T == Bitmap8Bit))
123 			const size_t length = x, pitch = source.width;
124 		else static if (is(T == Bitmap16Bit))
125 			const size_t length = x * 2, pitch = source.width * 2;
126 		else static if (is(T == Bitmap32Bit))
127 			const size_t length = x * 4, pitch = source.width * 4;
128 		const size_t pitch0 = pitch * y;
129 		for (int mY ; mY < source.height / y ; mY++){
130 			for (int mX ; mX < source.width / x ; mX++){
131 				T next = new T(x, y);
132 				for (int lY ; lY < y ; lY++){
133 					memcpy(next.getPtr + (lY * length), source.getPtr + (pitch * lY) + (pitch0 * mY) + (length * mX), length);
134 				}
135 				output ~= next;
136 			}
137 		}
138 		return output;
139 	}else throw new Exception("Requested size cannot be divided by input file's sizes!");
140 }
141 /**
142  * Loads a palette from a file.
143  */
144 public Color[] loadPaletteFromFile(string filename) {
145 	File f = File(filename);
146 	switch(extension(filename)){
147 		case ".tga", ".TGA":
148 			TGA imageFile = TGA.load(f);
149 			return loadPaletteFromImage(imageFile);
150 		case ".png", ".PNG":
151 			PNG imageFile = PNG.load(f);
152 			return loadPaletteFromImage(imageFile);
153 		case ".bmp", ".BMP":
154 			BMP imageFile = BMP.load(f);
155 			return loadPaletteFromImage(imageFile);
156 		default:
157 			throw new Exception("Unsupported file format!");
158 	}
159 }
160 /**
161  * Loads a palette from image.
162  */
163 public Color[] loadPaletteFromImage (Image img) {
164 	Color[] palette;
165 	IPalette sourcePalette = img.palette.convTo(PixelFormat.ARGB8888 | PixelFormat.BigEndian);
166 	palette = reinterpretCast!Color(sourcePalette.raw);
167 
168 	assert(palette.length == sourcePalette.length, "Palette lenght import mismatch!");
169 	if(!(img.palette.paletteFormat & PixelFormat.ValidAlpha)){
170 		palette[0].a = 0x0;
171 		for(int i = 1; i < palette.length; i++) {
172 			palette[i].a = 0xFF;
173 		}
174 	}
175 	return palette;
176 }
177 
178 /**
179  * Loads a *.wav file if SDL2 mixer is used
180  */
181 public Mix_Chunk* loadSoundFromFile(const char* filename){
182 	return Mix_LoadWAV(filename);
183 }
184 
185 File loadFileFromDisk(string filename){
186 	return File(filename, "r");
187 }
188 
189 /**
190  * Implements the RIFF serialization system
191  */
192 public struct RIFFHeader{
193 	char[4] data;
194 	uint length;
195 }