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 import dimage.tga; 23 import dimage.png; 24 25 import bindbc.sdl.mixer; 26 27 /** 28 * Loads a bitmap from disk. 29 * Currently supported formats: *.tga, *.png 30 */ 31 public T loadBitmapFromFile(T)(string filename) 32 if(T.stringof == Bitmap4Bit.stringof || T.stringof == Bitmap8Bit.stringof || T.stringof == Bitmap16Bit.stringof 33 || T.stringof == Bitmap32Bit.stringof){ 34 switch(extension(filename)){ 35 case ".tga", ".TGA": 36 TGA imageFile = TGA.load(File(filename)); 37 if(!imageFile.getHeader.topOrigin){ 38 imageFile.flipVertical; 39 } 40 static if(T.stringof == Bitmap4Bit.stringof){ 41 if(imageFile.getBitdepth != 4) 42 throw new BitmapFormatException("Bitdepth mismatch exception!"); 43 return new Bitmap4Bit(imageFile.getImageData, imageFile.width, imageFile.height); 44 }else static if(T.stringof == Bitmap8Bit.stringof){ 45 if(imageFile.getBitdepth != 8) 46 throw new BitmapFormatException("Bitdepth mismatch exception!"); 47 return new Bitmap8Bit(imageFile.getImageData, imageFile.width, imageFile.height); 48 }else static if(T.stringof == Bitmap16Bit.stringof){ 49 if(imageFile.getBitdepth != 16) 50 throw new BitmapFormatException("Bitdepth mismatch exception!"); 51 return new Bitmap16Bit(imageFile.getImageData, imageFile.width, imageFile.height); 52 }else static if(T.stringof == Bitmap32Bit.stringof){ 53 if(imageFile.getBitdepth != 32) 54 throw new BitmapFormatException("Bitdepth mismatch exception!"); 55 return new Bitmap32Bit(imageFile.getImageData, imageFile.width, imageFile.height); 56 } 57 break; 58 case ".png", ".PNG": 59 PNG imageFile = PNG.load(File(filename)); 60 static if(T.stringof == Bitmap8Bit.stringof){ 61 if(imageFile.getBitdepth != 8) 62 throw new BitmapFormatException("Bitdepth mismatch exception!"); 63 return new Bitmap8Bit(imageFile.getImageData, imageFile.width, imageFile.height); 64 }else static if(T.stringof == Bitmap32Bit.stringof){ 65 if(imageFile.getBitdepth != 32) 66 throw new BitmapFormatException("Bitdepth mismatch exception!"); 67 return new Bitmap32Bit(imageFile.getImageData, imageFile.width, imageFile.height); 68 } 69 break; 70 default: 71 throw new Exception("Unsupported file format!"); 72 } 73 } 74 /** 75 * Loads a bitmap sheet from file. 76 * This one doesn't require TGA devarea extensions. 77 */ 78 public T[] loadBitmapSheetFromFile(T)(string filename, int x, int y) 79 if(T.stringof == Bitmap4Bit.stringof || T.stringof == Bitmap8Bit.stringof || T.stringof == Bitmap16Bit.stringof 80 || T.stringof == Bitmap32Bit.stringof){ 81 T source = loadBitmapFromFile(filename); 82 if(source.width % x == 0 && source.height % y == 0){ 83 84 }else throw new Exception("Requested size cannot be divided by input file's sizes!"); 85 T[] output; 86 static if (T.stringof == Bitmap4Bit.stringof) 87 const size_t length = x / 2, pitch = output.width / 2; 88 else static if (T.stringof == Bitmap8Bit.stringof) 89 const size_t length = x, pitch = output.width; 90 else static if (T.stringof == Bitmap16Bit.stringof) 91 const size_t length = x * 2, pitch = output.width * 2; 92 else static if (T.stringof == Bitmap32Bit.stringof) 93 const size_t length = x * 4, pitch = output.width * 4; 94 const size_t pitch0 = pitch * y; 95 for (int mY ; mY < source.height / y ; mY++){ 96 for (int mX ; mX < source.width / x ; mX++){ 97 T next = new T(x, y); 98 for (int lY ; lY < y ; lY++){ 99 memcpy(next.getPtr + (lY * length), source.getPtr + (pitch * lY) + (pitch * mY) + (x * mX), length); 100 } 101 } 102 } 103 return output; 104 } 105 /** 106 * Loads a palette from a file. 107 */ 108 public Color[] loadPaletteFromFile(string filename){ 109 File f = File(filename); 110 switch(extension(filename)){ 111 case ".tga", ".TGA": 112 TGA imageFile = TGA.load(f); 113 return cast(Color[])(cast(void[])imageFile.getPaletteData); 114 case ".png", ".PNG": 115 PNG imageFile = PNG.load(f); 116 return cast(Color[])(cast(void[])imageFile.getPaletteData); 117 default: 118 throw new Exception("Unsupported file format!"); 119 } 120 } 121 /** 122 * Gets a bitmap from the XMP file. 123 * DEPRECATED! Recommended to use *.tga with devarea extensions or even *.png files. 124 */ 125 T loadBitmapFromXMP(T)(ExtendibleBitmap xmp, string ID){ 126 static if(T.stringof == Bitmap4Bit.stringof || T.stringof == Bitmap8Bit.stringof){ 127 T result = new T(cast(ubyte[])xmp.getBitmap(ID),xmp.getXsize(ID),xmp.getYsize(ID),null); 128 return result; 129 }else static if(T.stringof == Bitmap16Bit.stringof){ 130 T result;// = new T(cast(ushort[])xmp.getBitmap(ID),xmp.getXsize(ID),xmp.getYsize(ID)); 131 switch(xmp.bitdepth[xmp.searchForID(ID)]){ 132 case "16bit": 133 result = new T(cast(ushort[])xmp.getBitmap(ID),xmp.getXsize(ID),xmp.getYsize(ID)); 134 break; 135 case "8bit": 136 ushort[] subresult; 137 ubyte[] input = cast(ubyte[])xmp.getBitmap(ID); 138 subresult.length = input.length; 139 for(int i ; i < subresult.length ; i++){ 140 subresult[i] = input[i]; 141 } 142 result = new T(subresult,xmp.getXsize(ID),xmp.getYsize(ID)); 143 break; 144 case "4bit": 145 ushort[] subresult; 146 ubyte[] input = cast(ubyte[])xmp.getBitmap(ID); 147 subresult.length = input.length; 148 for(int i ; i < subresult.length ; i++){ 149 if(i & 1) 150 subresult[i] = input[i>>1]>>4; 151 else 152 subresult[i] = input[i>>1]&0b0000_1111; 153 } 154 result = new T(subresult,xmp.getXsize(ID),xmp.getYsize(ID)); 155 break; 156 /*case "1bit": 157 158 break;*/ 159 default: 160 throw new FileAccessException("Bitdepth error!"); 161 } 162 163 return result; 164 }else static if(T.stringof == Bitmap32Bit.stringof){ 165 T result = new T(cast(Color[])xmp.getBitmap(ID),xmp.getXsize(ID),xmp.getYsize(ID)); 166 return result; 167 }else static if(T.stringof == ABitmap.stringof){ 168 169 switch(xmp.bitdepth[xmp.searchForID(ID)]){ 170 case "4bit": 171 return new Bitmap4Bit(cast(ubyte[])xmp.getBitmap(ID),xmp.getXsize(ID),xmp.getYsize(ID)); 172 case "8bit": 173 return new Bitmap8Bit(cast(ubyte[])xmp.getBitmap(ID),xmp.getXsize(ID),xmp.getYsize(ID)); 174 case "16bit": 175 return new Bitmap16Bit(cast(ushort[])xmp.getBitmap(ID),xmp.getXsize(ID),xmp.getYsize(ID)); 176 case "32bit": 177 return new Bitmap32Bit(cast(Color[])xmp.getBitmap(ID),xmp.getXsize(ID),xmp.getYsize(ID)); 178 default: 179 return null; 180 181 } 182 183 }else static assert("Template argument \'" ~ T.stringof ~ "\' not supported in function \'T loadBitmapFromXMP(T)(ExtendibleBitmap xmp, string ID)\'"); 184 } 185 /** 186 * Loads a palette from an XMP file. 187 * Deprecated! 188 */ 189 public void loadPaletteFromXMP(ExtendibleBitmap xmp, string ID, Raster target, int offset = 0){ 190 target.palette = cast(Color[])xmp.getPalette(ID); 191 //writeln(target.palette); 192 /*target.setupPalette(0); 193 int max = (palette.length / 3); 194 for(int i ; i < max ; i++){ 195 target.addColor(palette[(i * 3)], palette[(i * 3) + 1], palette[(i * 3) + 2]); 196 //writeln(i); 197 }*/ 198 199 } 200 /** 201 * Loads a fontset from an XMP file. 202 * Deprecated! 203 */ 204 /+Fontset!Bitmap8Bit loadFontsetFromXMP(ExtendibleBitmap xmp, string fontName){ 205 Bitmap8Bit[wchar] characters; 206 foreach(s;xmp.bitmapID){ 207 //writeln(parseHex(s[fontName.length..(s.length-1)])); 208 //if(fontName == s[0..(fontName.length-1)]){ 209 characters[to!wchar(parseHex(s[fontName.length..s.length]))] = loadBitmapFromXMP!Bitmap8Bit(xmp,s); 210 } 211 foreach(c;characters){ 212 for(int y; y < c.height; y++){ 213 for(int x; x < c.width; x++){ 214 if(c.readPixel(x,y)){ 215 c.writePixel(x,y, ubyte.max); 216 } 217 } 218 } 219 } 220 return new Fontset!Bitmap8Bit(fontName, characters['0'].height, characters); 221 }+/ 222 /** 223 * Loads a *.wav file if SDL2 mixer is used 224 */ 225 public Mix_Chunk* loadSoundFromFile(const char* filename){ 226 return Mix_LoadWAV(filename); 227 } 228 229 File loadFileFromDisk(string filename){ 230 return File(filename, "r"); 231 } 232 233 /** 234 * Implements the RIFF serialization system 235 */ 236 public struct RIFFHeader{ 237 char[4] data; 238 uint length; 239 }