updated URL of forum and table in readme file
[neverball] / share / image.c
1 /*
2  * Copyright (C) 2003 Robert Kooima
3  *
4  * NEVERBALL is  free software; you can redistribute  it and/or modify
5  * it under the  terms of the GNU General  Public License as published
6  * by the Free  Software Foundation; either version 2  of the License,
7  * or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT  ANY  WARRANTY;  without   even  the  implied  warranty  of
11  * MERCHANTABILITY or  FITNESS FOR A PARTICULAR PURPOSE.   See the GNU
12  * General Public License for more details.
13  */
14
15 #include <SDL.h>
16 #include <SDL_ttf.h>
17 #include <SDL_image.h>
18 #include <string.h>
19 #include <math.h>
20 #include <png.h>
21 #include <stdlib.h>
22
23 #include "glext.h"
24 #include "image.h"
25 #include "base_image.h"
26 #include "config.h"
27
28 /*---------------------------------------------------------------------------*/
29
30 void image_snap(char *filename)
31 {
32     FILE       *filep  = NULL;
33     png_structp writep = NULL;
34     png_infop   infop  = NULL;
35     png_bytep  *bytep  = NULL;
36
37     int w = config_get_d(CONFIG_WIDTH);
38     int h = config_get_d(CONFIG_HEIGHT);
39     int i;
40
41     unsigned char *p = NULL;
42
43     /* Initialize all PNG export data structures. */
44
45     if (!(filep = fopen(filename, FMODE_WB)))
46         return;
47     if (!(writep = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0)))
48         return;
49     if (!(infop = png_create_info_struct(writep)))
50         return;
51
52     /* Enable the default PNG error handler. */
53
54     if (setjmp(png_jmpbuf(writep)) == 0)
55     {
56         /* Initialize the PNG header. */
57
58         png_init_io (writep, filep);
59         png_set_IHDR(writep, infop, w, h, 8,
60                      PNG_COLOR_TYPE_RGB,
61                      PNG_INTERLACE_NONE,
62                      PNG_COMPRESSION_TYPE_DEFAULT,
63                      PNG_FILTER_TYPE_DEFAULT);
64
65         /* Allocate the pixel buffer and copy pixels there. */
66
67         if ((p = (unsigned char *) malloc(w * h * 4)))
68         {
69             glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, p);
70
71             /* Allocate and initialize the row pointers. */
72
73             if ((bytep = png_malloc(writep, h * sizeof (png_bytep))))
74             {
75                 for (i = 0; i < h; ++i)
76                     bytep[h - i - 1] = (png_bytep) (p + i * w * 3);
77
78                 png_set_rows (writep, infop, bytep);
79
80                 /* Write the PNG image file. */
81
82                 png_write_info(writep, infop);
83                 png_write_png (writep, infop, 0, NULL);
84
85                 free(bytep);
86             }
87             free(p);
88         }
89     }
90
91     /* Release all resources. */
92
93     png_destroy_write_struct(&writep, &infop);
94     fclose(filep);
95 }
96
97 void image_size(int *W, int *H, int w, int h)
98 {
99     *W = 1;
100     *H = 1;
101
102     while (*W < w) *W *= 2;
103     while (*H < h) *H *= 2;
104 }
105
106 /*---------------------------------------------------------------------------*/
107
108 /*
109  * Create on  OpenGL texture  object using the  given SDL  surface and
110  * format,  scaled  using the  current  scale  factor.  When  scaling,
111  * assume dimensions are used only for layout and lie about the size.
112  */
113 GLuint make_image_from_surf(int *w, int *h, SDL_Surface *s)
114 {
115     int    t = config_get_d(CONFIG_TEXTURES);
116     GLuint o = 0;
117
118     glGenTextures(1, &o);
119     glBindTexture(GL_TEXTURE_2D, o);
120
121     if (t > 1)
122     {
123         SDL_Surface *d = image_scale(s, t);
124
125         /* Load the scaled image. */
126
127         if (d->format->BitsPerPixel == 32)
128             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, d->w, d->h, 0,
129                          GL_RGBA, GL_UNSIGNED_BYTE, d->pixels);
130         else
131             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,  d->w, d->h, 0,
132                          GL_RGB,  GL_UNSIGNED_BYTE, d->pixels);
133
134         SDL_FreeSurface(d);
135     }
136     else
137     {
138         /* Load the unscaled image. */
139
140         if (s->format->BitsPerPixel == 32)
141             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, s->w, s->h, 0,
142                          GL_RGBA, GL_UNSIGNED_BYTE, s->pixels);
143         else
144             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,  s->w, s->h, 0,
145                          GL_RGB,  GL_UNSIGNED_BYTE, s->pixels);
146     }
147
148     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
149     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
150
151     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
152     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
153
154     if (w) *w = s->w;
155     if (h) *h = s->h;
156
157     return o;
158 }
159
160 /*---------------------------------------------------------------------------*/
161
162 /*
163  * Load  an image  from the  named file.   If the  image is  not RGBA,
164  * convert it to RGBA.  Return an OpenGL texture object.
165  */
166 GLuint make_image_from_file(int *W, int *H,
167                             int *w, int *h, const char *name)
168 {
169     SDL_Surface *src;
170     SDL_Surface *dst;
171     SDL_Rect rect;
172
173     GLuint o = 0;
174
175     /* Load the file. */
176
177     if ((src = IMG_Load(config_data(name))))
178     {
179         int w2;
180         int h2;
181
182         image_size(&w2, &h2, src->w, src->h);
183
184         if (w) *w = src->w;
185         if (h) *h = src->h;
186
187         /* Create a new destination surface. */
188
189         if ((dst = SDL_CreateRGBSurface(SDL_SWSURFACE, w2, h2, 32,
190                                         RMASK, GMASK, BMASK, AMASK)))
191         {
192             /* Copy source pixels to the center of the destination. */
193
194             rect.x = (Sint16) (w2 - src->w) / 2;
195             rect.y = (Sint16) (h2 - src->h) / 2;
196
197             SDL_SetAlpha(src, 0, 0);
198             SDL_BlitSurface(src, NULL, dst, &rect);
199
200             o = make_image_from_surf(W, H, dst);
201
202             SDL_FreeSurface(dst);
203         }
204         SDL_FreeSurface(src);
205     }
206     return o;
207 }
208
209 /*---------------------------------------------------------------------------*/
210
211 /*
212  * Render the given  string using the given font.   Transfer the image
213  * to a  surface of  power-of-2 size large  enough to fit  the string.
214  * Return an OpenGL texture object.
215  */
216 GLuint make_image_from_font(int *W, int *H,
217                             int *w, int *h, const char *text, TTF_Font *font, int k)
218 {
219     SDL_Color fg = { 0xFF, 0xFF, 0xFF, 0xFF };
220
221     SDL_Surface *src;
222     SDL_Surface *dst;
223     SDL_Rect rect;
224
225     GLuint o = 0;
226
227     /* Render the text. */
228
229     if (text && strlen(text) > 0)
230     {
231         if ((src = TTF_RenderUTF8_Blended(font, text, fg)))
232         {
233             int w2;
234             int h2;
235
236             image_size(&w2, &h2, src->w, src->h);
237
238             if (w) *w = src->w;
239             if (h) *h = src->h;
240
241             /* Create a new destination surface. */
242
243             if ((dst = SDL_CreateRGBSurface(SDL_SWSURFACE, w2, h2, 32,
244                                             RMASK, GMASK, BMASK, AMASK)))
245             {
246                 /* Copy source pixels to the center of the destination. */
247
248                 rect.x = (Sint16) (w2 - src->w) / 2;
249                 rect.y = (Sint16) (h2 - src->h) / 2;
250
251                 SDL_SetAlpha(src, 0, 0);
252                 SDL_BlitSurface(src, NULL, dst, &rect);
253
254                 image_white(dst);
255
256                 o = make_image_from_surf(W, H, dst);
257
258                 SDL_FreeSurface(dst);
259             }
260             SDL_FreeSurface(src);
261         }
262
263         if (W) *W *= k;
264         if (H) *H *= k;
265         if (w) *w *= k;
266         if (h) *h *= k;
267     }
268     else
269     {
270         if (W) *W = 0;
271         if (H) *H = 0;
272         if (w) *w = 0;
273         if (h) *h = 0;
274     }
275
276     return o;
277 }
278
279 /*---------------------------------------------------------------------------*/