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 output.reserve(source.height / y * source.width / x); 130 for (int mY ; mY < source.height / y ; mY++){ 131 for (int mX ; mX < source.width / x ; mX++){ 132 T next = new T(x, y); 133 for (int lY ; lY < y ; lY++){ 134 memcpy(next.getPtr + (lY * length), source.getPtr + (pitch * lY) + (pitch0 * mY) + (length * mX), length); 135 } 136 output ~= next; 137 } 138 } 139 return output; 140 }else throw new Exception("Requested size cannot be divided by input file's sizes!"); 141 } 142 /** 143 * Loads a palette from a file. 144 */ 145 public Color[] loadPaletteFromFile(string filename) { 146 File f = File(filename); 147 switch(extension(filename)){ 148 case ".tga", ".TGA": 149 TGA imageFile = TGA.load(f); 150 return loadPaletteFromImage(imageFile); 151 case ".png", ".PNG": 152 PNG imageFile = PNG.load(f); 153 return loadPaletteFromImage(imageFile); 154 case ".bmp", ".BMP": 155 BMP imageFile = BMP.load(f); 156 return loadPaletteFromImage(imageFile); 157 default: 158 throw new Exception("Unsupported file format!"); 159 } 160 } 161 /** 162 * Loads a palette from image. 163 */ 164 public Color[] loadPaletteFromImage (Image img) { 165 Color[] palette; 166 IPalette sourcePalette = img.palette.convTo(PixelFormat.ARGB8888 | PixelFormat.BigEndian); 167 palette = reinterpretCast!Color(sourcePalette.raw); 168 169 assert(palette.length == sourcePalette.length, "Palette lenght import mismatch!"); 170 if(!(img.palette.paletteFormat & PixelFormat.ValidAlpha)){ 171 palette[0].a = 0x0; 172 for(int i = 1; i < palette.length; i++) { 173 palette[i].a = 0xFF; 174 } 175 } 176 return palette; 177 } 178 179 /** 180 * Loads a *.wav file if SDL2 mixer is used 181 */ 182 public Mix_Chunk* loadSoundFromFile(const char* filename){ 183 return Mix_LoadWAV(filename); 184 } 185 186 File loadFileFromDisk(string filename){ 187 return File(filename, "r"); 188 } 189 190 /** 191 * Implements the RIFF serialization system 192 */ 193 public struct RIFFHeader{ 194 char[4] data; 195 uint length; 196 }