Group key config symbols together
[neverball] / share / image.c
index dad3929..4623547 100644 (file)
@@ -1,4 +1,4 @@
-/*   
+/*
  * Copyright (C) 2003 Robert Kooima
  *
  * NEVERBALL is  free software; you can redistribute  it and/or modify
 
 #include <SDL.h>
 #include <SDL_ttf.h>
-#include <SDL_image.h>
 #include <string.h>
 #include <math.h>
+#include <png.h>
+#include <stdlib.h>
 
 #include "glext.h"
 #include "image.h"
 #include "base_image.h"
 #include "config.h"
 
+#include "fs.h"
+#include "fs_png.h"
+
 /*---------------------------------------------------------------------------*/
 
-void image_snap(char *filename, int w, int h)
+void image_snap(const char *filename)
 {
+    fs_file     filep  = NULL;
+    png_structp writep = NULL;
+    png_infop   infop  = NULL;
+    png_bytep  *bytep  = NULL;
+
+    int w = config_get_d(CONFIG_WIDTH);
+    int h = config_get_d(CONFIG_HEIGHT);
     int i;
 
-    SDL_Surface *buf = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 24,
-                                            RMASK, GMASK, BMASK, 0);
-    SDL_Surface *img = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 24,
-                                            RMASK, GMASK, BMASK, 0);
+    unsigned char *p = NULL;
 
-    glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, buf->pixels);
+    /* Initialize all PNG export data structures. */
 
-    for (i = 0; i < h; i++)
-        memcpy((GLubyte *) img->pixels + 3 * w * i,
-               (GLubyte *) buf->pixels + 3 * w * (h - i), 3 * w);
+    if (!(filep = fs_open(filename, "w")))
+        return;
+    if (!(writep = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0)))
+        return;
+    if (!(infop = png_create_info_struct(writep)))
+        return;
 
-    SDL_SaveBMP(img, filename);
+    /* Enable the default PNG error handler. */
 
-    SDL_FreeSurface(img);
-    SDL_FreeSurface(buf);
-}
+    if (setjmp(png_jmpbuf(writep)) == 0)
+    {
+        /* Initialize the PNG header. */
 
-void image_size(int *W, int *H, int w, int h)
-{
-    *W = 1;
-    *H = 1;
+        png_set_write_fn(writep, filep, fs_png_write, fs_png_flush);
+        png_set_IHDR(writep, infop, w, h, 8,
+                     PNG_COLOR_TYPE_RGB,
+                     PNG_INTERLACE_NONE,
+                     PNG_COMPRESSION_TYPE_DEFAULT,
+                     PNG_FILTER_TYPE_DEFAULT);
+
+        /* Allocate the pixel buffer and copy pixels there. */
+
+        if ((p = (unsigned char *) malloc(w * h * 3)))
+        {
+            glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, p);
+
+            /* Allocate and initialize the row pointers. */
+
+            if ((bytep = (png_bytep *) png_malloc(writep, h * sizeof (png_bytep))))
+            {
+                for (i = 0; i < h; ++i)
+                    bytep[h - i - 1] = (png_bytep) (p + i * w * 3);
+
+                png_set_rows (writep, infop, bytep);
+
+                /* Write the PNG image file. */
 
-    while (*W < w) *W *= 2;
-    while (*H < h) *H *= 2;
+                png_write_info(writep, infop);
+                png_write_png (writep, infop, 0, NULL);
+
+                free(bytep);
+            }
+            free(p);
+        }
+    }
+
+    /* Release all resources. */
+
+    png_destroy_write_struct(&writep, &infop);
+    fs_close(filep);
 }
 
 /*---------------------------------------------------------------------------*/
 
 /*
- * Create on  OpenGL texture  object using the  given SDL  surface and
- * format,  scaled  using the  current  scale  factor.  When  scaling,
- * assume dimensions are used only for layout and lie about the size.
+ * Create an OpenGL texture object using the given image buffer.
  */
-GLuint make_image_from_surf(int *w, int *h, SDL_Surface *s)
+static GLuint make_texture(const void *p, int w, int h, int b)
 {
-    int    t = config_get_d(CONFIG_TEXTURES);
+    static const GLenum format[] =
+        { 0, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA };
+
     GLuint o = 0;
 
-    glGenTextures(1, &o);
-    glBindTexture(GL_TEXTURE_2D, o);
+    /* Scale the image as configured, or to fit the OpenGL limitations. */
 
-    if (t > 1)
-    {
-        SDL_Surface *d = image_scale(s, t);
+#ifdef GL_TEXTURE_MAX_ANISOTROPY_EXT
+    int a = config_get_d(CONFIG_ANISO);
+#endif
+    int m = config_get_d(CONFIG_MIPMAP);
+    int k = config_get_d(CONFIG_TEXTURES);
+    int W = w;
+    int H = h;
 
-        /* Load the scaled image. */
+    GLint max;
 
-        if (d->format->BitsPerPixel == 32)
-            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, d->w, d->h, 0,
-                         GL_RGBA, GL_UNSIGNED_BYTE, d->pixels);
-        else
-            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,  d->w, d->h, 0,
-                         GL_RGB,  GL_UNSIGNED_BYTE, d->pixels);
+    void *q = NULL;
 
-        SDL_FreeSurface(d);
-    }
-    else
-    {
-        /* Load the unscaled image. */
-
-        if (s->format->BitsPerPixel == 32)
-            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, s->w, s->h, 0,
-                         GL_RGBA, GL_UNSIGNED_BYTE, s->pixels);
-        else
-            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,  s->w, s->h, 0,
-                         GL_RGB,  GL_UNSIGNED_BYTE, s->pixels);
-    }
+    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
+
+    while (w / k > (int) max || h / k > (int) max)
+        k *= 2;
+
+    if (k > 1)
+        q = image_scale(p, w, h, b, &W, &H, k);
+
+    /* Generate and configure a new OpenGL texture. */
+
+    glGenTextures(1, &o);
+    glBindTexture(GL_TEXTURE_2D, o);
 
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
 
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+#ifdef GL_GENERATE_MIPMAP_SGIS
+    if (m)
+    {
+        glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+                        GL_LINEAR_MIPMAP_LINEAR);
+    }
+#endif
+#ifdef GL_TEXTURE_MAX_ANISOTROPY_EXT
+    if (a) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, a);
+#endif
+
+    /* Copy the image to an OpenGL texture. */
+
+    glTexImage2D(GL_TEXTURE_2D, 0,
+                 format[b], W, H, 0,
+                 format[b], GL_UNSIGNED_BYTE, q ? q : p);
+
+    if (q) free(q);
 
-    if (w) *w = s->w;
-    if (h) *h = s->h;
 
     return o;
 }
 
-/*---------------------------------------------------------------------------*/
-
 /*
- * Load  an image  from the  named file.   If the  image is  not RGBA,
- * convert it to RGBA.  Return an OpenGL texture object.
+ * Load an image from the named file.  Return an OpenGL texture object.
  */
-GLuint make_image_from_file(int *W, int *H,
-                            int *w, int *h, const char *name)
+GLuint make_image_from_file(const char *filename)
 {
-    SDL_Surface *src;
-    SDL_Surface *dst;
-    SDL_Rect rect;
-
+    void  *p;
+    int    w;
+    int    h;
+    int    b;
     GLuint o = 0;
 
-    /* Load the file. */
+    /* Load the image. */
 
-    if ((src = IMG_Load(config_data(name))))
+    if ((p = image_load(filename, &w, &h, &b)))
     {
-        int w2;
-        int h2;
-
-        image_size(&w2, &h2, src->w, src->h);
-
-        if (w) *w = src->w;
-        if (h) *h = src->h;
-
-        /* Create a new destination surface. */
-        
-        if ((dst = SDL_CreateRGBSurface(SDL_SWSURFACE, w2, h2, 32,
-                                        RMASK, GMASK, BMASK, AMASK)))
-        {
-            /* Copy source pixels to the center of the destination. */
-
-            rect.x = (Sint16) (w2 - src->w) / 2;
-            rect.y = (Sint16) (h2 - src->h) / 2;
-
-            SDL_SetAlpha(src, 0, 0);
-            SDL_BlitSurface(src, NULL, dst, &rect);
-
-            o = make_image_from_surf(W, H, dst);
-
-            SDL_FreeSurface(dst);
-        }
-        SDL_FreeSurface(src);
+        o = make_texture(p, w, h, b);
+        free(p);
     }
+
     return o;
 }
 
@@ -166,66 +195,122 @@ GLuint make_image_from_file(int *W, int *H,
  * Return an OpenGL texture object.
  */
 GLuint make_image_from_font(int *W, int *H,
-                            int *w, int *h, const char *text, TTF_Font *font, int k)
+                            int *w, int *h,
+                            const char *text, TTF_Font *font)
 {
-    SDL_Color fg = { 0xFF, 0xFF, 0xFF, 0xFF };
-
-    SDL_Surface *src;
-    SDL_Surface *dst;
-    SDL_Rect rect;
-
     GLuint o = 0;
 
     /* Render the text. */
 
     if (text && strlen(text) > 0)
     {
-        if ((src = TTF_RenderUTF8_Blended(font, text, fg)))
+        SDL_Color    col = { 0xFF, 0xFF, 0xFF, 0xFF };
+        SDL_Surface *orig;
+
+        if ((orig = TTF_RenderUTF8_Blended(font, text, col)))
         {
-            int w2;
-            int h2;
+            void *p;
+            int  w2;
+            int  h2;
+            int   b = orig->format->BitsPerPixel / 8;
 
-            image_size(&w2, &h2, src->w, src->h);
+            SDL_Surface *src;
+            SDL_PixelFormat fmt;
 
-            if (w) *w = src->w;
-            if (h) *h = src->h;
+            fmt = *orig->format;
 
-            /* Create a new destination surface. */
-            
-            if ((dst = SDL_CreateRGBSurface(SDL_SWSURFACE, w2, h2, 32,
-                                            RMASK, GMASK, BMASK, AMASK)))
+            fmt.Rmask = RMASK;
+            fmt.Gmask = GMASK;
+            fmt.Bmask = BMASK;
+            fmt.Amask = AMASK;
+
+            if ((src = SDL_ConvertSurface(orig, &fmt, orig->flags)) == NULL)
             {
-                /* Copy source pixels to the center of the destination. */
+                fprintf(stderr, _("Failed to convert SDL_ttf surface: %s\n"),
+                        SDL_GetError());
 
-                rect.x = (Sint16) (w2 - src->w) / 2;
-                rect.y = (Sint16) (h2 - src->h) / 2;
+                /* Pretend everything's just fine. */
 
-                SDL_SetAlpha(src, 0, 0);
-                SDL_BlitSurface(src, NULL, dst, &rect);
+                src = orig;
+            }
+            else
+                SDL_FreeSurface(orig);
 
-                image_white(dst);
+            /* Pad the text to power-of-two. */
 
-                o = make_image_from_surf(W, H, dst);
+            p = image_next2(src->pixels, src->w, src->h, b, &w2, &h2);
 
-                SDL_FreeSurface(dst);
-            }
+            if (w) *w = src->w;
+            if (h) *h = src->h;
+            if (W) *W = w2;
+            if (H) *H = h2;
+
+            /* Saturate the color channels.  Modulate ONLY in alpha. */
+
+            image_white(p, w2, h2, b);
+
+            /* Create the OpenGL texture object. */
+
+            o = make_texture(p, w2, h2, b);
+
+            free(p);
             SDL_FreeSurface(src);
         }
-
-        if (W) *W *= k;
-        if (H) *H *= k;
-        if (w) *w *= k;
-        if (h) *h *= k;
     }
     else
     {
-        if (W) *W = 0;
-        if (H) *H = 0;
+        /* Empty string. */
+
         if (w) *w = 0;
         if (h) *h = 0;
+        if (W) *W = 0;
+        if (H) *H = 0;
     }
 
     return o;
 }
 
 /*---------------------------------------------------------------------------*/
+
+/*
+ * Load an image from the named file.  Return an SDL surface.
+ */
+SDL_Surface *load_surface(const char *filename)
+{
+    void  *p;
+    int    w;
+    int    h;
+    int    b;
+
+    SDL_Surface *srf = NULL;
+
+    Uint32 rmask;
+    Uint32 gmask;
+    Uint32 bmask;
+    Uint32 amask;
+
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+    rmask = 0xFF000000;
+    gmask = 0x00FF0000;
+    bmask = 0x0000FF00;
+    amask = 0x000000FF;
+#else
+    rmask = 0x000000FF;
+    gmask = 0x0000FF00;
+    bmask = 0x00FF0000;
+    amask = 0xFF000000;
+#endif
+
+    if ((p = image_load(filename, &w, &h, &b)))
+    {
+        void *q;
+
+        if ((q = image_flip(p, w, h, b, 0, 1)))
+            srf = SDL_CreateRGBSurfaceFrom(q, w, h, b * 8, w * b,
+                                           rmask, gmask, bmask, amask);
+        free(p);
+    }
+    return srf;
+}
+
+/*---------------------------------------------------------------------------*/