1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
|
#include "../../include/mgl/graphics/texture.h"
#include "../../include/mgl/graphics/image.h"
#include "../../include/mgl/mgl.h"
#include <stdio.h>
/* TODO: Check for glTexImage2D/glTexSubImage2D failure */
static const mgl_texture *current_texture = NULL;
static int mgl_texture_format_to_opengl_format(mgl_texture_format format) {
switch(format) {
case MGL_TEXTURE_FORMAT_ALPHA: return GL_ALPHA8;
case MGL_TEXTURE_FORMAT_GRAY: return GL_LUMINANCE8;
case MGL_TEXTURE_FORMAT_GRAY_ALPHA: return GL_LUMINANCE8_ALPHA8;
case MGL_TEXTURE_FORMAT_RGB: return GL_RGB8;
case MGL_TEXTURE_FORMAT_RGBA: return GL_RGBA8;
}
return 0;
}
static int mgl_texture_format_to_compressed_opengl_format(mgl_texture_format format) {
switch(format) {
case MGL_TEXTURE_FORMAT_ALPHA: return GL_ALPHA8;
case MGL_TEXTURE_FORMAT_GRAY: return GL_LUMINANCE8;
case MGL_TEXTURE_FORMAT_GRAY_ALPHA: return GL_LUMINANCE8_ALPHA8;
case MGL_TEXTURE_FORMAT_RGB: return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
case MGL_TEXTURE_FORMAT_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
}
return 0;
}
static int mgl_texture_format_to_source_opengl_format(mgl_texture_format format) {
switch(format) {
case MGL_TEXTURE_FORMAT_ALPHA: return GL_ALPHA;
case MGL_TEXTURE_FORMAT_GRAY: return GL_LUMINANCE;
case MGL_TEXTURE_FORMAT_GRAY_ALPHA: return GL_LUMINANCE_ALPHA;
case MGL_TEXTURE_FORMAT_RGB: return GL_RGB;
case MGL_TEXTURE_FORMAT_RGBA: return GL_RGBA;
}
return 0;
}
static mgl_texture_format mgl_image_format_to_mgl_texture_format(mgl_image_format image_format) {
switch(image_format) {
case MGL_IMAGE_FORMAT_ALPHA: return MGL_TEXTURE_FORMAT_ALPHA;
case MGL_IMAGE_FORMAT_GRAY: return MGL_TEXTURE_FORMAT_GRAY;
case MGL_IMAGE_FORMAT_GRAY_ALPHA: return MGL_TEXTURE_FORMAT_GRAY_ALPHA;
case MGL_IMAGE_FORMAT_RGB: return MGL_TEXTURE_FORMAT_RGB;
case MGL_IMAGE_FORMAT_RGBA: return MGL_TEXTURE_FORMAT_RGBA;
}
return 0;
}
static void gl_get_texture_size(unsigned int texture_id, int *width, int *height) {
*width = 0;
*height = 0;
mgl_context *context = mgl_get_context();
context->gl.glBindTexture(GL_TEXTURE_2D, texture_id);
context->gl.glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, width);
context->gl.glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, height);
context->gl.glBindTexture(GL_TEXTURE_2D, 0);
}
int mgl_texture_init(mgl_texture *self) {
self->id = 0;
self->width = 0;
self->height = 0;
self->format = 0;
self->max_width = 0;
self->max_height = 0;
self->pixel_coordinates = false;
self->mipmap = false;
self->owned = true;
mgl_context *context = mgl_get_context();
context->gl.glGenTextures(1, &self->id);
if(self->id == 0) {
fprintf(stderr, "mgl error: failed to init texture (glGenTextures failed)\n");
return -1;
}
int max_texture_size = 0;
context->gl.glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
self->max_width = max_texture_size;
self->max_height = max_texture_size;
return 0;
}
int mgl_texture_init_reference_existing_gl_texture(mgl_texture *self, unsigned int texture_id, mgl_texture_format format, const mgl_texture_reference_options *reference_options) {
self->id = texture_id;
self->width = 0;
self->height = 0;
self->format = format;
self->max_width = 0;
self->max_height = 0;
self->pixel_coordinates = reference_options && reference_options->pixel_coordinates;
self->mipmap = false;
self->owned = false;
gl_get_texture_size(self->id, &self->width, &self->height);
int max_texture_size = 0;
mgl_context *context = mgl_get_context();
context->gl.glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
self->max_width = max_texture_size;
self->max_height = max_texture_size;
return 0;
}
int mgl_texture_load_from_file(mgl_texture *self, const char *filepath, const mgl_texture_load_options *load_options) {
mgl_image image;
if(mgl_image_load_from_file(&image, filepath) != 0)
return -1;
int result = mgl_texture_load_from_image(self, &image, load_options);
mgl_image_unload(&image);
return result;
}
int mgl_texture_load_from_image(mgl_texture *self, const mgl_image *image, const mgl_texture_load_options *load_options) {
return mgl_texture_load_from_memory(self, image->data, image->width, image->height, image->format, load_options);
}
int mgl_texture_load_from_memory(mgl_texture *self, const unsigned char *data, int width, int height, mgl_image_format format, const mgl_texture_load_options *load_options) {
if(width < 0 || height < 0)
return -1;
if(width > self->max_width || height > self->max_height)
return -1;
mgl_context *context = mgl_get_context();
self->width = width;
self->height = height;
self->format = mgl_image_format_to_mgl_texture_format(format);
self->pixel_coordinates = load_options && load_options->pixel_coordinates;
self->mipmap = load_options && load_options->mipmap && context->gl.glGenerateMipmap; /* TODO: Check if glGenerateMipmap is actually available */
int opengl_texture_format = mgl_texture_format_to_opengl_format(self->format);
if(load_options && load_options->compressed)
opengl_texture_format = mgl_texture_format_to_compressed_opengl_format(self->format);
context->gl.glBindTexture(GL_TEXTURE_2D, self->id);
context->gl.glTexImage2D(GL_TEXTURE_2D, 0, opengl_texture_format, self->width, self->height, 0, mgl_texture_format_to_source_opengl_format(self->format), GL_UNSIGNED_BYTE, data);
context->gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
context->gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if(self->mipmap) {
context->gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
context->gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
context->gl.glGenerateMipmap(GL_TEXTURE_2D);
} else {
context->gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
context->gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
context->gl.glBindTexture(GL_TEXTURE_2D, 0);
return 0;
}
int mgl_texture_update(mgl_texture *self, const unsigned char *data, int offset_x, int offset_y, int width, int height, mgl_image_format format) {
if(offset_x + width > self->width || offset_y + height > self->height)
return -1;
mgl_context *context = mgl_get_context();
context->gl.glBindTexture(GL_TEXTURE_2D, self->id);
const mgl_texture_format texture_format = mgl_image_format_to_mgl_texture_format(format);
context->gl.glTexSubImage2D(GL_TEXTURE_2D, 0, offset_x, offset_y, width, height, mgl_texture_format_to_source_opengl_format(texture_format), GL_UNSIGNED_BYTE, data);
if(self->mipmap) {
context->gl.glGenerateMipmap(GL_TEXTURE_2D);
}
context->gl.glBindTexture(GL_TEXTURE_2D, 0);
return 0;
}
int mgl_texture_resize(mgl_texture *self, int new_width, int new_height, const mgl_texture_load_options *load_options) {
if(new_width == self->width && new_height == self->height)
return 0;
if(new_width < 0 || new_height < 0)
return -1;
if(new_width > self->max_width || new_height > self->max_height)
return -1;
self->width = new_width;
self->height = new_height;
const int opengl_texture_format = load_options && load_options->compressed ? mgl_texture_format_to_compressed_opengl_format(self->format) : mgl_texture_format_to_opengl_format(self->format);
mgl_context *context = mgl_get_context();
context->gl.glBindTexture(GL_TEXTURE_2D, self->id);
context->gl.glTexImage2D(GL_TEXTURE_2D, 0, opengl_texture_format, self->width, self->height, 0, mgl_texture_format_to_source_opengl_format(self->format), GL_UNSIGNED_BYTE, NULL);
if(self->mipmap) {
context->gl.glGenerateMipmap(GL_TEXTURE_2D);
}
context->gl.glBindTexture(GL_TEXTURE_2D, 0);
return 0;
}
/* TODO: Optimize. Do not set matrix if the current coordinate type was the same as the previous one */
void mgl_texture_use(const mgl_texture *texture) {
mgl_context *context = mgl_get_context();
current_texture = texture;
if(texture) {
float matrix[16] = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
};
if(texture->pixel_coordinates) {
matrix[0] = 1.0f / (float)texture->width;
matrix[5] = 1.0f / (float)texture->height;
}
context->gl.glMatrixMode(GL_TEXTURE);
context->gl.glLoadMatrixf(matrix);
context->gl.glMatrixMode(GL_MODELVIEW);
context->gl.glBindTexture(GL_TEXTURE_2D, texture->id);
} else {
context->gl.glBindTexture(GL_TEXTURE_2D, 0);
}
}
const mgl_texture* mgl_texture_current_texture(void) {
return current_texture;
}
void mgl_texture_unload(mgl_texture *self) {
mgl_context *context = mgl_get_context();
if(!self->owned)
return;
if(self->id) {
context->gl.glDeleteTextures(1, &self->id);
self->id = 0;
}
}
|