--- /dev/null
+#include <string.h>
+#include "assman.h"
+#include "imago2.h"
+#include "image.h"
+
+
+static int pixel_elements(Image::Format fmt);
+static int elem_size(Image::Format fmt);
+static int pixel_size(Image::Format fmt);
+
+Image::Image()
+{
+ fmt = FMT_RGBA;
+ width = height = 0;
+ pixels = 0;
+}
+
+Image::~Image()
+{
+ delete [] (char*)pixels;
+}
+
+Image::Image(const Image &img)
+{
+ pixels = 0;
+ set_pixels(img.width, img.height, img.pixels, img.fmt);
+}
+
+Image &Image::operator =(const Image &img)
+{
+ if(&img != this) {
+ delete [] (char*)pixels;
+ pixels = 0;
+ set_pixels(img.width, img.height, img.pixels, img.fmt);
+ }
+ return *this;
+}
+
+Image::Image(Image &&img)
+{
+ width = img.width;
+ height = img.height;
+ pixels = img.pixels;
+ fmt = img.fmt;
+
+ img.pixels = 0;
+}
+
+Image &Image::operator =(Image &&img)
+{
+ if(&img != this) {
+ width = img.width;
+ height = img.height;
+ pixels = img.pixels;
+ fmt = img.fmt;
+
+ img.pixels = 0;
+ }
+ return *this;
+}
+
+int Image::get_width() const
+{
+ return width;
+}
+
+int Image::get_height() const
+{
+ return height;
+}
+
+Image::Format Image::get_format() const
+{
+ return fmt;
+}
+
+bool Image::create(int x, int y, Format fmt)
+{
+ width = x;
+ height = y;
+ this->fmt = fmt;
+
+ try {
+ pixels = new char[x * y * pixel_size(fmt)];
+ }
+ catch(...) {
+ return false;
+ }
+ return true;
+}
+
+bool Image::set_pixels(int x, int y, void *pixels, Format fmt)
+{
+ if(!create(x, y, fmt)) {
+ return false;
+ }
+ memcpy(this->pixels, pixels, x * y * pixel_size(fmt));
+ return true;
+}
+
+void *Image::get_pixels() const
+{
+ return pixels;
+}
+
+static size_t io_read(void *buf, size_t bytes, void *uptr)
+{
+ return ass_fread(buf, 1, bytes, uptr);
+}
+
+static long io_seek(long offs, int whence, void *uptr)
+{
+ return ass_fseek(uptr, offs, whence);
+}
+
+bool Image::load(const char *fname)
+{
+ struct img_pixmap pixmap;
+ struct img_io io;
+
+ if(!(io.uptr = ass_fopen(fname, "rb"))) {
+ fprintf(stderr, "failed to open image: %s\n", fname);
+ return false;
+ }
+ io.read = io_read;
+ io.write = 0;
+ io.seek = io_seek;
+
+ img_init(&pixmap);
+ if(img_read(&pixmap, &io) == -1) {
+ return false;
+ }
+ ass_fclose(io.uptr);
+
+ Format fmt;
+ switch(pixmap.fmt) {
+ case IMG_FMT_GREY8:
+ fmt = FMT_GREY;
+ break;
+ case IMG_FMT_RGB24:
+ fmt = FMT_RGB;
+ break;
+ case IMG_FMT_RGBA32:
+ fmt = FMT_RGBA;
+ break;
+ case IMG_FMT_GREYF:
+ fmt = FMT_GREY_FLOAT;
+ break;
+ case IMG_FMT_RGBF:
+ fmt = FMT_RGB_FLOAT;
+ break;
+ case IMG_FMT_RGBAF:
+ fmt = FMT_RGBA_FLOAT;
+ break;
+ default:
+ img_destroy(&pixmap);
+ return false;
+ }
+
+ if(!set_pixels(pixmap.width, pixmap.height, pixmap.pixels, fmt)) {
+ img_destroy(&pixmap);
+ return false;
+ }
+ img_destroy(&pixmap);
+ return true;
+}
+
+bool Image::save(const char *fname) const
+{
+ struct img_pixmap pixmap;
+
+ img_init(&pixmap);
+
+ switch(fmt) {
+ case FMT_GREY:
+ pixmap.fmt = IMG_FMT_GREY8;
+ break;
+ case FMT_GREY_FLOAT:
+ pixmap.fmt = IMG_FMT_GREYF;
+ break;
+ case FMT_RGB:
+ pixmap.fmt = IMG_FMT_RGB24;
+ break;
+ case FMT_RGB_FLOAT:
+ pixmap.fmt = IMG_FMT_RGBF;
+ break;
+ case FMT_RGBA:
+ pixmap.fmt = IMG_FMT_RGBA32;
+ break;
+ case FMT_RGBA_FLOAT:
+ pixmap.fmt = IMG_FMT_RGBAF;
+ break;
+ default:
+ return false;
+ }
+
+ pixmap.width = width;
+ pixmap.height = height;
+ pixmap.pixels = pixels;
+ pixmap.pixelsz = pixel_size(fmt);
+
+ if(img_save(&pixmap, fname) == -1) {
+ return false;
+ }
+ return true;
+}
+
+static int pixel_elements(Image::Format fmt)
+{
+ switch(fmt) {
+ case Image::FMT_GREY:
+ case Image::FMT_GREY_FLOAT:
+ return 1;
+
+ case Image::FMT_RGB:
+ case Image::FMT_RGB_FLOAT:
+ return 3;
+
+ case Image::FMT_RGBA:
+ case Image::FMT_RGBA_FLOAT:
+ return 4;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int elem_size(Image::Format fmt)
+{
+ switch(fmt) {
+ case Image::FMT_GREY:
+ case Image::FMT_RGB:
+ case Image::FMT_RGBA:
+ return 1;
+
+ case Image::FMT_GREY_FLOAT:
+ case Image::FMT_RGB_FLOAT:
+ case Image::FMT_RGBA_FLOAT:
+ return sizeof(float);
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int pixel_size(Image::Format fmt)
+{
+ return elem_size(fmt) * pixel_elements(fmt);
+}
--- /dev/null
+#include <stdlib.h>
+#include "texture.h"
+#include "image.h"
+#include "opengl.h"
+#include "imago2.h"
+
+#if defined(GL_ES_VERSION_2_0) || defined(GL_VERSION_3_0)
+#define USE_GL_GENERATE_MIPMAP
+#elif defined(__GLEW_H__)
+#define USE_SGIS_GENERATE_MIPMAP
+#endif
+
+static int glifmt_from_ifmt(unsigned int ifmt);
+static int glfmt_from_ifmt(unsigned int ifmt);
+static int gltype_from_ifmt(unsigned int ifmt);
+
+static int glifmt_from_imgfmt(Image::Format fmt);
+static Image::Format imgfmt_from_glifmt(unsigned int ifmt);
+
+static unsigned int cur_target[8] = {
+ GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D,
+ GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D
+};
+
+void bind_texture(Texture *tex, int tunit)
+{
+ if(tex) {
+ tex->bind(tunit);
+ } else {
+ glActiveTexture(GL_TEXTURE0 + tunit);
+ glBindTexture(cur_target[tunit], 0);
+ glActiveTexture(GL_TEXTURE0);
+ }
+}
+
+Texture *load_texture(const char *fname)
+{
+ TextureCube *texcube = new TextureCube;
+ if(texcube->load(fname)) {
+ return texcube;
+ }
+ delete texcube;
+
+ Texture2D *tex = new Texture2D;
+ if(tex->load(fname)) {
+ return tex;
+ }
+ delete tex;
+ return 0;
+}
+
+
+Texture::Texture()
+{
+ target = 0;
+ sz[0] = sz[1] = sz[2] = 0;
+ texfmt = 0;
+
+ glGenTextures(1, &id);
+}
+
+Texture::~Texture()
+{
+ if(id) {
+ glDeleteTextures(1, &id);
+ }
+}
+
+void Texture::set_wrapping(unsigned int wrap)
+{
+ if(!target) {
+ return;
+ }
+
+ glBindTexture(target, id);
+ glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap);
+ glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap);
+}
+
+void Texture::set_filtering(unsigned int filt)
+{
+ unsigned int mag_filter;
+
+ if(!target) {
+ return;
+ }
+
+ switch(filt) {
+ case GL_LINEAR_MIPMAP_NEAREST:
+ case GL_LINEAR_MIPMAP_LINEAR:
+ mag_filter = GL_LINEAR;
+ break;
+
+ case GL_NEAREST_MIPMAP_NEAREST:
+ case GL_NEAREST_MIPMAP_LINEAR:
+ mag_filter = GL_NEAREST;
+ break;
+
+ default:
+ mag_filter = filt;
+ }
+
+ set_filtering(filt, mag_filter);
+}
+
+void Texture::set_filtering(unsigned int min_filt, unsigned int mag_filt)
+{
+ glBindTexture(target, id);
+ glTexParameteri(target, GL_TEXTURE_MIN_FILTER, min_filt);
+ glTexParameteri(target, GL_TEXTURE_MAG_FILTER, mag_filt);
+}
+
+unsigned int Texture::get_format() const
+{
+ return texfmt;
+}
+
+int Texture::get_size(int dim) const
+{
+ if(dim < 0 || dim >= 3) {
+ return 0;
+ }
+ return sz[dim];
+}
+
+unsigned int Texture::get_id() const
+{
+ return id;
+}
+
+void Texture::bind(int tex_unit) const
+{
+ glActiveTexture(GL_TEXTURE0 + tex_unit);
+ glBindTexture(target, id);
+ glActiveTexture(GL_TEXTURE0);
+
+ cur_target[tex_unit] = target;
+}
+
+
+// ---- Texture2D ----
+
+Texture2D::Texture2D()
+{
+ target = GL_TEXTURE_2D;
+}
+
+TextureType Texture2D::get_type() const
+{
+ return TEXTURE_2D;
+}
+
+void Texture2D::create(int xsz, int ysz, unsigned int ifmt)
+{
+ int fmt = glfmt_from_ifmt(ifmt);
+ int type = gltype_from_ifmt(ifmt);
+
+ glBindTexture(GL_TEXTURE_2D, id);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexImage2D(GL_TEXTURE_2D, 0, glifmt_from_ifmt(ifmt), xsz, ysz, 0, fmt, type, 0);
+ sz[0] = xsz;
+ sz[1] = ysz;
+ texfmt = ifmt;
+}
+
+void Texture2D::set_image(const Image &img, int idx)
+{
+ texfmt = glifmt_from_imgfmt(img.get_format());
+ unsigned int fmt = glfmt_from_ifmt(texfmt);
+ unsigned int type = gltype_from_ifmt(texfmt);
+
+ sz[0] = img.get_width();
+ sz[1] = img.get_height();
+
+ glBindTexture(GL_TEXTURE_2D, id);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+#ifdef USE_SGIS_GENERATE_MIPMAP
+ if(GLEW_SGIS_generate_mipmap) {
+ glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
+#endif
+ glTexImage2D(GL_TEXTURE_2D, 0, texfmt, sz[0], sz[1], 0, fmt, type, img.get_pixels());
+#ifdef USE_SGIS_GENERATE_MIPMAP
+ } else {
+ gluBuild2DMipmaps(GL_TEXTURE_2D, texfmt, sz[0], sz[1], fmt, type, img.get_pixels());
+ }
+#endif
+
+#ifdef USE_GL_GENERATE_MIPMAP
+ glGenerateMipmap(GL_TEXTURE_2D);
+#endif
+}
+
+void Texture2D::get_image(Image *img, int idx) const
+{
+#ifndef GL_ES_VERSION_2_0
+ glBindTexture(GL_TEXTURE_2D, id);
+
+ Image::Format img_fmt = imgfmt_from_glifmt(texfmt);
+ img->create(sz[0], sz[1], img_fmt);
+
+ unsigned int fmt = glfmt_from_ifmt(texfmt);
+ unsigned int type = gltype_from_ifmt(texfmt);
+ glGetTexImage(GL_TEXTURE_2D, 0, fmt, type, img->get_pixels());
+#endif // GL_ES_VERSION_2_0
+}
+
+void Texture2D::set_subimage(const Image &img, int xoffs, int yoffs)
+{
+ unsigned int ifmt = glifmt_from_imgfmt(img.get_format());
+
+ glBindTexture(GL_TEXTURE_2D, id);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, xoffs, yoffs, img.get_width(), img.get_height(),
+ glfmt_from_ifmt(ifmt), gltype_from_ifmt(ifmt), img.get_pixels());
+}
+
+bool Texture2D::load(const char *fname)
+{
+ Image img;
+ if(!img.load(fname)) {
+ fprintf(stderr, "failed to load 2D texture: %s\n", fname);
+ return false;
+ }
+ set_image(img);
+
+ printf("loaded 2D texture: %s\n", fname);
+ return true;
+}
+
+bool Texture2D::save(const char *fname) const
+{
+#ifndef GL_ES_VERSION_2_0
+ unsigned char *pixels = new unsigned char[sz[0] * sz[1] * 4];
+
+ glBindTexture(GL_TEXTURE_2D, id);
+ glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+
+ if(img_save_pixels(fname, pixels, sz[0], sz[1]) == -1) {
+ fprintf(stderr, "failed to save 2D texture: %s\n", fname);
+ delete [] pixels;
+ return false;
+ }
+
+ printf("saved 2D texture: %s\n", fname);
+ delete [] pixels;
+ return true;
+#else
+ return false; // TODO
+#endif
+}
+
+// ---- TextureCube ----
+static unsigned int cube_faces[] = {
+ GL_TEXTURE_CUBE_MAP_POSITIVE_X,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
+ GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
+ GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
+};
+
+TextureCube::TextureCube()
+{
+ target = GL_TEXTURE_CUBE_MAP;
+}
+
+TextureType TextureCube::get_type() const
+{
+ return TEXTURE_CUBE;
+}
+
+
+void TextureCube::create(int xsz, int ysz, unsigned int ifmt)
+{
+ if(xsz != ysz) {
+ fprintf(stderr, "trying to create cubemap with different width and height (%dx%d)\n", xsz, ysz);
+ return;
+ }
+
+ texfmt = ifmt;
+
+ glBindTexture(GL_TEXTURE_CUBE_MAP, id);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ for(int i=0; i<6; i++) {
+ glTexImage2D(cube_faces[i], 0, ifmt, xsz, ysz, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
+ }
+}
+
+void TextureCube::set_image(const Image &img, int idx)
+{
+ // TODO
+}
+
+void TextureCube::get_image(Image *img, int idx) const
+{
+ // TODO
+}
+
+bool TextureCube::load(const char *fname)
+{
+ return false; // TODO
+}
+
+bool TextureCube::save(const char *fname) const
+{
+ return false; // TODO
+}
+
+static int glifmt_from_ifmt(unsigned int ifmt)
+{
+#ifdef GL_ES_VERSION_2_0
+ switch(ifmt) {
+ case GL_LUMINANCE16F:
+ case GL_LUMINANCE32F:
+ ifmt = GL_LUMINANCE;
+ break;
+
+ case GL_RGB16F:
+ case GL_RGB32F:
+ ifmt = GL_RGB;
+ break;
+
+ case GL_RGBA16F:
+ case GL_RGBA32F:
+ ifmt = GL_RGBA;
+ break;
+
+ default:
+ break;
+ }
+#endif
+ return ifmt; // by default just pass it through...
+}
+
+static int glfmt_from_ifmt(unsigned int ifmt)
+{
+ switch(ifmt) {
+ case GL_LUMINANCE16F:
+ case GL_LUMINANCE32F:
+ return GL_LUMINANCE;
+
+ case GL_RGB16F:
+ case GL_RGB32F:
+ return GL_RGB;
+
+ case GL_RGBA16F:
+ case GL_RGBA32F:
+ return GL_RGBA;
+
+ default:
+ break;
+ }
+ return ifmt;
+}
+
+static int gltype_from_ifmt(unsigned int ifmt)
+{
+ switch(ifmt) {
+ case GL_RGB16F:
+ case GL_RGBA16F:
+ case GL_LUMINANCE16F:
+#ifdef GL_ES_VERSION_2_0
+ return GL_HALF_FLOAT_OES;
+#endif
+ case GL_RGB32F:
+ case GL_RGBA32F:
+ case GL_LUMINANCE32F:
+ return GL_FLOAT;
+
+ default:
+ break;
+ }
+ return GL_UNSIGNED_BYTE;
+}
+
+static int glifmt_from_imgfmt(Image::Format fmt)
+{
+ switch(fmt) {
+ case Image::FMT_GREY:
+ return GL_LUMINANCE;
+ case Image::FMT_GREY_FLOAT:
+ return GL_LUMINANCE16F;
+ case Image::FMT_RGB:
+ return GL_RGB;
+ case Image::FMT_RGB_FLOAT:
+ return GL_RGB16F;
+ case Image::FMT_RGBA:
+ return GL_RGBA;
+ case Image::FMT_RGBA_FLOAT:
+ return GL_RGBA16F;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static Image::Format imgfmt_from_glifmt(unsigned int ifmt)
+{
+ switch(ifmt) {
+ case GL_LUMINANCE:
+ return Image::FMT_GREY;
+ case GL_LUMINANCE16F:
+ return Image::FMT_GREY_FLOAT;
+ case GL_RGB:
+ return Image::FMT_RGB;
+ case GL_RGB16F:
+ return Image::FMT_RGB_FLOAT;
+ case GL_RGBA:
+ return Image::FMT_RGBA;
+ case GL_RGBA16F:
+ return Image::FMT_RGBA_FLOAT;
+ default:
+ break;
+ }
+
+ fprintf(stderr, "imgfmt_from_glifmt: unknown internal format: %x\n", ifmt);
+#ifndef NDEBUG
+ abort();
+#endif
+ return Image::FMT_RGBA; // ... whatever
+}
--- /dev/null
+#ifndef TEXTURE_H_
+#define TEXTURE_H_
+
+#include "opengl.h"
+
+class Image;
+
+enum TextureType {
+ TEXTURE_2D,
+ TEXTURE_CUBE
+};
+
+class Texture {
+protected:
+ unsigned int id;
+ unsigned int target;
+ unsigned int texfmt;
+ int sz[3];
+
+ Texture(const Texture &tex) {}
+ Texture &operator =(const Texture &tex) {
+ return *this;
+ }
+
+public:
+ Texture();
+ virtual ~Texture();
+
+ virtual TextureType get_type() const = 0;
+
+ void set_wrapping(unsigned int wrap);
+ void set_filtering(unsigned int filt);
+ void set_filtering(unsigned int min_filt, unsigned int mag_filt);
+
+ unsigned int get_format() const;
+
+ virtual int get_size(int dim) const;
+
+ virtual void create(int xsz, int ysz, unsigned int ifmt = GL_RGBA) = 0;
+ virtual void set_image(const Image &img, int idx = 0) = 0;
+ virtual void get_image(Image *img, int idx = 0) const = 0;
+
+ virtual bool load(const char *fname) = 0;
+ virtual bool save(const char *fname) const = 0;
+
+ virtual unsigned int get_id() const;
+
+ virtual void bind(int tex_unit = 0) const;
+};
+
+class Texture2D : public Texture {
+public:
+ Texture2D();
+
+ virtual TextureType get_type() const;
+
+ virtual void create(int xsz, int ysz, unsigned int ifmt = GL_RGBA);
+ virtual void set_image(const Image &img, int idx = 0);
+ virtual void get_image(Image *img, int idx = 0) const;
+
+ virtual void set_subimage(const Image &img, int xoffs, int yoffs);
+
+ virtual bool load(const char *fname);
+ virtual bool save(const char *fname) const;
+};
+
+class TextureCube : public Texture {
+public:
+ TextureCube();
+
+ virtual TextureType get_type() const;
+
+ virtual void create(int xsz, int ysz, unsigned int ifmt = GL_RGBA);
+ virtual void set_image(const Image &img, int idx = 0);
+ virtual void get_image(Image *img, int idx = 0) const;
+
+ virtual bool load(const char *fname);
+ virtual bool save(const char *fname) const;
+};
+
+void bind_texture(Texture *tex, int tunit = 0);
+
+/** loads a texture autodetecting whether it's a 2D texture or
+ * cubemap and creating the correct Texture subclass instance.
+ */
+Texture *load_texture(const char *fname);
+
+#endif // TEXTURE_H_