1 module caLib_util.image; 2 3 import std.exception : Exception, enforce; 4 import std.file : thisExePath; 5 import std.stdio : writeln; 6 import std.conv : to; 7 import std.string : split; 8 import core.stdc.stdlib : exit; 9 import caLib_util.build : os, arch; 10 11 public import derelict.freeimage.freeimage; 12 public import caLib_util.structs : Color; 13 14 15 16 shared static this() 17 { 18 DerelictFI.load(); 19 20 FreeImage_SetOutputMessage(&FreeImageErrorHandler); 21 } 22 23 24 25 class Image 26 { 27 28 private: 29 30 FIBITMAP* fiBitmap; 31 32 int width; 33 int height; 34 35 public: 36 37 this(int width, int height) 38 { 39 this(width, height, 24); 40 } 41 42 this(int width, int height, int bpp) 43 in 44 { 45 assert(width >= 0); 46 assert(height >= 0); 47 assert(bpp == 24 || bpp == 32); 48 } 49 body 50 { 51 this.width = width; 52 this.height = height; 53 54 fiBitmap = FreeImage_Allocate(width, height, bpp, 55 0x00FF0000, 0x0000FF00, 0x000000FF); 56 57 enforce(fiBitmap != null, "Fatal error. Unknown error occured when" 58 ~ "createing the internal bitmap"); 59 } 60 61 this(FIBITMAP* fiBitmap) 62 in 63 { assert(fiBitmap != null); } 64 body 65 { 66 width = FreeImage_GetWidth(fiBitmap); 67 height = FreeImage_GetHeight(fiBitmap); 68 69 if (FreeImage_GetBPP(fiBitmap) != 32) 70 fiBitmap = FreeImage_ConvertTo32Bits(fiBitmap); 71 72 this.fiBitmap = fiBitmap; 73 } 74 75 static Image fromFile(string path) 76 { 77 FREE_IMAGE_FORMAT format = FIF_UNKNOWN; 78 79 format = FreeImage_GetFileType(cast(const char*) path, 0); 80 81 if(format == FIF_UNKNOWN) 82 format = FreeImage_GetFIFFromFilename(cast(const char*) path); 83 84 enforce(format != FIF_UNKNOWN, "Unknown image format for file " ~ path); 85 86 enforce(FreeImage_FIFSupportsReading(format), 87 "No suitable decoder for format file " ~ path); 88 89 FIBITMAP* fiBitmap = FreeImage_Load(format, cast(const char*) path, 0); 90 91 enforce(fiBitmap != null, "The image could not be read"); 92 93 return new Image(fiBitmap); 94 } 95 96 static Image fromColors(Color* colors, int width, int height) 97 in 98 { assert(width >= 0 && height >= 0); } 99 body 100 { 101 Image image = new Image(width, height); 102 103 foreach(col ; 0..width) 104 { 105 foreach(row ; 0..height) 106 { 107 image.setPixel(col, row, colors[row * width + col]); 108 } 109 } 110 111 return image; 112 } 113 114 static Image fromColorValues(uint* values, int width, int height) 115 in 116 { assert(width >= 0 && height >= 0); } 117 body 118 { 119 Image image = new Image(width, height); 120 121 foreach(col ; 0..width) 122 { 123 foreach(row ; 0..height) 124 { 125 image.setPixel(col, row, Color(values[row * width + col])); 126 } 127 } 128 129 return image; 130 } 131 132 ~this() 133 { 134 FreeImage_Unload(fiBitmap); 135 } 136 137 void saveToFile(string path) 138 { 139 enforce(path[path.length-1] != '.', 140 "Error. Can't save as image. invalid path: \"" ~ path ~ "\""); 141 142 FREE_IMAGE_FORMAT format = 143 FreeImage_GetFIFFromFilename(cast(const char*) path); 144 145 if(format == FIF_UNKNOWN) 146 format = getFIFFromExstention(path); 147 148 enforce(format != FIF_UNKNOWN, "Error. Can't save as image." 149 ~ " Unknown image format or invalied path: " ~ "\"" ~ path ~ "\""); 150 151 int err = 152 FreeImage_Save(format, fiBitmap, cast(const char*) path, 0); 153 154 enforce(err == 1, "Error. Could not save image Error code: " 155 ~ to!string(err)); 156 } 157 158 int getWidth() 159 { 160 return width; 161 } 162 163 int getHeight() 164 { 165 return height; 166 } 167 168 Color getPixel(int x, int y) 169 in 170 { assert(x >= 0 && y >= 0); } 171 body 172 { 173 RGBQUAD* fiColor = new RGBQUAD(); 174 175 uint err = FreeImage_GetPixelColor(fiBitmap, x, height-y-1, fiColor); 176 177 assert(err == 1, "Fatal error. The internal bitmap structure is unable 178 to read its pixel. Error code: " 179 ~ to!string(err)); 180 181 return Color(fiColor.rgbRed, fiColor.rgbGreen, fiColor.rgbBlue, 182 fiColor.rgbReserved); 183 } 184 185 void setPixel(int x, int y, Color newColor) 186 in 187 { assert(x >= 0 && y >= 0); } 188 body 189 { 190 RGBQUAD fiColor = RGBQUAD(); 191 fiColor.rgbRed = newColor.r; 192 fiColor.rgbGreen = newColor.g; 193 fiColor.rgbBlue = newColor.b; 194 fiColor.rgbReserved = newColor.a; 195 196 FreeImage_SetPixelColor(fiBitmap, x, height-y-1, &fiColor); 197 } 198 199 void rescale(int newWidth, int newHeight) 200 in 201 { assert(newWidth >= 0 && newHeight >= 0); } 202 body 203 { 204 fiBitmap = FreeImage_Rescale(fiBitmap, newWidth, newHeight, FILTER_BOX); 205 206 enforce(fiBitmap != null, "Fatal error. The internal bitmap could not" 207 ~ "be rescaled.\nThis may be caused by a bit-depth that can't be" 208 ~ " handeled, or more likely: Not enough memory"); 209 210 width = FreeImage_GetWidth(fiBitmap); 211 height = FreeImage_GetHeight(fiBitmap); 212 } 213 214 FIBITMAP* getFiBitmap() 215 { 216 return fiBitmap; 217 } 218 219 invariant 220 { 221 assert(width >= 0); 222 assert(height >= 0); 223 assert(fiBitmap != null); 224 } 225 } 226 227 228 229 private FREE_IMAGE_FORMAT getFIFFromExstention(string path) 230 { 231 string exstention = path.split(".")[path.split.length]; 232 return 233 [ 234 "png" : FIF_PNG, 235 ].get(exstention, FIF_UNKNOWN); 236 } 237 238 239 240 extern (C) void FreeImageErrorHandler(FREE_IMAGE_FORMAT fif, const(char)* message) nothrow 241 { 242 try 243 { 244 writeln("FreeImage error:", message); 245 } 246 catch(Exception e) 247 { 248 exit(-99); 249 } 250 }