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