1 /**
2  * Copyright:       Copyright (C) 2018 Gabriel Gheorghe, All Rights Reserved
3  * Authors:         $(Gabriel Gheorghe)
4  * License:         $(LINK2 https://www.gnu.org/licenses/gpl-3.0.txt, GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007)
5  * Source:          $(LINK2 https://github.com/GabyForceQ/LibertyEngine/blob/master/source/liberty/image/io.d)
6  * Documentation:
7  * Coverage:
8  *
9  * TODO:
10  *    - move in resource manager
11 **/
12 module liberty.image.io;
13 
14 import liberty.logger.impl;
15 import liberty.image.format;
16 import liberty.image.impl;
17 import liberty.io.manager;
18 import liberty.graphics.texture.impl;
19 
20 /**
21  * Singleton class used for loading image files.
22  * It's a manager class so it implements $(D ManagerBody).
23 **/
24 final abstract class ImageIO {
25   /**
26    *
27   **/
28   static Image loadImage(string path) {
29     import std.array : split;
30 
31     // Check extension
32     string[] splitArray = path.split(".");	
33     immutable extension = splitArray[$ - 1];
34     switch (extension) {
35       case "bmp", "BMP":
36         return loadBMPFile(path);
37       case "png", "PNG":
38         return loadPNGFile(path);
39       default:
40         Logger.error(	
41           "File format not supported for image data: " ~ extension,	
42           typeof(this).stringof	
43         );
44     }
45 
46     assert(0, "Unreachable");
47   }
48 
49   /**
50    *
51   **/
52   static BMPImage createImage(BMPHeader header, ubyte[] pixelData) {
53     return new BMPImage(header, pixelData);
54   }
55 
56   private static BMPImage loadBMPFile(string resourcePath) {
57     char[] buf;
58 
59     // Read bmp file and put its content into a buffer
60     if (!IOManager.readFileToBuffer(resourcePath, buf, "rb"))
61       Logger.error("Cannot read " ~ resourcePath ~ " file.", typeof(this).stringof);
62 
63     // Check if it is really a bmp image
64     if (!isBMPFormat(buf[0x00..0x02]))
65       Logger.error("Not *.bmp file.", typeof(this).stringof);
66 
67     BMPHeader header;
68     ubyte[] pixelData;
69 
70     // Fill bmp header
71     header.dataPosition = *cast(uint*)&buf[0x0A];
72     header.size = *cast(uint*)&buf[0x22];
73     header.width = *cast(uint*)&buf[0x12];
74     header.height = *cast(uint*)&buf[0x16];
75 
76     if (header.size == 0)
77       header.size = header.width * header.height * 4;
78     
79     if (header.dataPosition == 0)
80       header.dataPosition = IMPLICIT_BMP_HEADER_DATA_POSITION;
81 
82     // Fill bmp pixel data
83     pixelData = cast(ubyte[])buf[header.dataPosition..buf.length];
84 
85     // Create the image in memory and return it
86     return createImage(header, pixelData);
87   }
88 
89   private static PNGImage loadPNGFile(string resourcePath) {
90     char[] buf;
91 
92     // Read png file and put its content into a buffer
93     if (!IOManager.readFileToBuffer(resourcePath, buf, "rb")) {
94       assert(0, "Operation failed!");
95     }
96 
97     // Check if it is really a png image
98     if (!isPNGFormat(buf[0x00..0x08])) {
99       assert(0, "Not PNG image!");
100     }
101 
102     Logger.todo("static PNGImage loadPNGFile(string resourcePath) {..}", typeof(this).stringof);
103     Logger.error("Previous TODO", typeof(this).stringof);
104 
105     return null;
106   }
107 
108   private static bool isBMPFormat(in char[2] bytes)  {
109     return bytes[0] == 'B' && bytes[1] == 'M';
110   }
111 
112   private static bool isPNGFormat(in char[8] bytes)  {
113     return
114       cast(ubyte)bytes[0] == 137 &&
115       cast(ubyte)bytes[1] == 80 &&
116       cast(ubyte)bytes[2] == 78 &&
117       cast(ubyte)bytes[3] == 71 &&
118       cast(ubyte)bytes[4] == 13 &&
119       cast(ubyte)bytes[5] == 10 &&
120       cast(ubyte)bytes[6] == 26 &&
121       cast(ubyte)bytes[7] == 10;
122   }
123 }