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 }