1 module dgt.texture;
2 import derelict.sdl2.sdl, derelict.sdl2.image;
3 import derelict.opengl;
4 import dgt.array : Array;
5 import dgt.io;
6 import dgt.geom : Rectangle;
7 import dgt.util : nullTerminate;
8 
9 import core.stdc..string;
10 
11 ///The format of each pixel in byte order
12 enum PixelFormat : GLenum
13 {
14     RGB = GL_RGB,
15     RGBA = GL_RGBA,
16     BGR = GL_BGR,
17     BGRA = GL_BGRA
18 }
19 
20 /**
21 A drawable texture which can also be a region of a larger texture
22 */
23 struct Texture
24 {
25     package uint id;
26     private:
27     int width, height;
28     Rectangle region;
29 
30     @disable this();
31 
32     @nogc nothrow public:
33     ///Create a Texture from data in memory
34     this(ubyte* data, int w, int h, PixelFormat format)
35     {
36         GLuint texture;
37         glGenTextures(1, &texture);
38         glBindTexture(GL_TEXTURE_2D, texture);
39         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
40         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
41         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
42         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
43         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, format, GL_UNSIGNED_BYTE, data);
44         glGenerateMipmap(GL_TEXTURE_2D);
45         id = texture;
46         width = w;
47         height = h;
48         region = Rectangle(0, 0, w, h);
49     }
50 
51     ///Load a texture from a file with a given path
52     this(string name)
53     {
54         auto nameNullTerminated = nullTerminate(name);
55         SDL_Surface* surface = IMG_Load(nameNullTerminated.ptr);
56         nameNullTerminated.destroy();
57         if (surface == null)
58         {
59             auto buffer = IMG_GetError();
60             println("Image loading error: ", buffer[0..strlen(buffer)]);
61             this(null, 0, 0, PixelFormat.RGB);
62         }
63         else
64         {
65             this(surface);
66             SDL_FreeSurface(surface);
67         }
68     }
69 
70     ///Load a texture from an SDL_Surface in memory
71     this(SDL_Surface* sur)
72     {
73         PixelFormat format;
74         if(sur.format.BytesPerPixel == 4)
75             if(sur.format.Rmask == 0x000000ff)
76                 format = PixelFormat.RGBA;
77             else
78                 format = PixelFormat.BGRA;
79         else
80             if(sur.format.Rmask == 0x000000ff)
81                 format = PixelFormat.RGB;
82             else
83                 format = PixelFormat.BGR;
84         this(cast(ubyte*)sur.pixels, sur.w, sur.h, format);
85     }
86 
87     ///Remove the texture from GPU memory
88     void destroy()
89     {
90         glDeleteTextures(1, &id);
91     }
92 
93     pure:
94     ///Get a texture that represents a region of a larger texture
95     Texture getSlice(Rectangle region)
96     {
97         Texture tex = this;
98         tex.region = Rectangle(this.region.x + region.x,
99                 this.region.y + region.y, region.width, region.height);
100         return tex;
101     }
102     ///Get the width of the source image
103     @property int sourceWidth() const { return width; }
104     ///Get the height of the source image
105     @property int sourceHeight() const { return height; }
106     ///Get the size of the texture's region
107     @property Rectangle size() const { return region; }
108 }