#include <GL/glew.h>
#include <GL/freeglut.h>
-#undef USE_SRGB
-
-#ifdef USE_SRGB
-#define COMP_FMT GL_COMPRESSED_SRGB8_ETC2
-#else
-#define COMP_FMT GL_COMPRESSED_RGB8_ETC2
-#endif
+struct texture {
+ unsigned int id;
+ int width, height;
+ unsigned int fmt;
+ unsigned int compsize;
+ void *data;
+};
int init(void);
-int texcomp(unsigned char *compix, unsigned int tofmt, unsigned char *pixels,
- int xsz, int ysz, unsigned int fromfmt, unsigned int fromtype);
void disp(void);
void reshape(int x, int y);
void keyb(unsigned char key, int x, int y);
-void gen_image(unsigned char *pixels, int xsz, int ysz);
-unsigned char *load_compressed_image(const char *fname, int *cszptr, int *xszptr, int *yszptr);
-int dump_compressed_image(const char *fname, unsigned char *data, int size, int w, int h);
+void idle(void);
+int load_texture(const char *fname, struct texture *tex);
+const char *fmtstr(int fmt);
void print_compressed_formats(void);
-unsigned int tex, tex2, comp_tex;
+struct texture tex;
+unsigned int tex2;
const char *texfile;
int subtest, copytest;
int main(int argc, char **argv)
{
- int i;
+ int i, loop = 0;
unsigned int glut_flags = GLUT_RGB | GLUT_DOUBLE;
-#ifdef USE_SRGB
- glut_flags |= GLUT_SRGB;
-#endif
for(i=1; i<argc; i++) {
if(argv[i][0] == '-') {
subtest = 1;
} else if(strcmp(argv[i], "-copytest") == 0) {
copytest = 1;
+ } else if(strcmp(argv[i], "-copytest-loop") == 0) {
+ copytest = 1;
+ loop = 1;
} else {
fprintf(stderr, "invalid option: %s\n", argv[i]);
return 1;
}
}
+ if(!texfile) {
+ fprintf(stderr, "you must specify a compressed texture file\n");
+ return 1;
+ }
+
glutInit(&argc, argv);
- glutInitWindowSize(800, 600);
glutInitDisplayMode(glut_flags);
glutCreateWindow("test");
glutReshapeFunc(reshape);
glutKeyboardFunc(keyb);
+ if (loop)
+ glutIdleFunc(idle);
+
glewInit();
if(init() == -1) {
int init(void)
{
- unsigned char *pixels, *buf;
- int xsz = 512;
- int ysz = 512;
+ unsigned char *buf;
int is_comp = 0;
- int comp_size = 0;
int tmp;
+ unsigned int intfmt;
print_compressed_formats();
- if(texfile) {
- if(!(pixels = load_compressed_image(texfile, &comp_size, &xsz, &ysz))) {
- return -1;
- }
- printf("loaded compressed texture file: %s (%dx%d)\n", texfile, xsz, ysz);
-
- } else {
- if(!(pixels = malloc(xsz * ysz * 3))) {
- abort();
- }
- gen_image(pixels, xsz, ysz);
-
- printf("compressing texture\n");
- if((comp_size = texcomp(pixels, COMP_FMT, pixels, xsz, ysz, GL_RGB, GL_UNSIGNED_BYTE)) == -1) {
- return -1;
- }
- printf("compressed texture is %d bytes (uncompressed was: %d)\n", comp_size, xsz * ysz * 3);
-
- dump_compressed_image("compressed_texture", pixels, comp_size, xsz, ysz);
+ if(load_texture(texfile, &tex) == -1) {
+ fprintf(stderr, "failed to load texture %s\n", texfile);
+ return -1;
}
- glGenTextures(1, &tex);
- glBindTexture(GL_TEXTURE_2D, tex);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glCompressedTexImage2D(GL_TEXTURE_2D, 0, COMP_FMT, xsz, ysz, 0, comp_size, pixels);
- if(glGetError()) {
- fprintf(stderr, "failed to upload compressed texture\n");
+ glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, (int*)&intfmt);
+ if(intfmt != tex.fmt) {
+ fprintf(stderr, "internal format differs (expected: %s [%x], got: %s [%x])\n",
+ fmtstr(tex.fmt), tex.fmt, fmtstr(intfmt), intfmt);
return -1;
}
-
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED, &is_comp);
if(!is_comp) {
fprintf(stderr, "texture is not compressed\n");
return -1;
}
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &tmp);
- if(tmp != comp_size) {
- fprintf(stderr, "internal compressed size differs (expected: %d, got: %d)!\n", comp_size, tmp);
+ if(tmp != tex.compsize) {
+ fprintf(stderr, "internal compressed size differs (expected: %d, got: %d)!\n", tex.compsize, tmp);
return -1;
}
- if(!(buf = malloc(comp_size))) {
- fprintf(stderr, "failed to allocate comparison image buffer (%d bytes)\n", comp_size);
+ if(!(buf = malloc(tex.compsize))) {
+ fprintf(stderr, "failed to allocate comparison image buffer (%d bytes)\n", tex.compsize);
return -1;
}
glGetCompressedTexImage(GL_TEXTURE_2D, 0, buf);
- if(memcmp(pixels, buf, comp_size) != 0) {
- fprintf(stderr, "submitted and retrieved pixel data differ!\n");
+ if(memcmp(tex.data, buf, tex.compsize) != 0) {
+ int i;
+ unsigned char *a = tex.data, *b = buf;
+
+ for(i=0; i<tex.compsize; i++) {
+ if(*a++ != *b++) break;
+ }
+ assert(i < tex.compsize);
+ fprintf(stderr, "submitted and retrieved pixel data differ! (at offset %d)\n", i);
} else {
- printf("submitted and retrieved sizes match (%d bytes)\n", comp_size);
+ printf("submitted and retrieved sizes match (%d bytes)\n", tex.compsize);
}
if(subtest) {
printf("testing glGetCompressedTextureSubImage and glCompressedTexSubImage2D\n");
- memset(buf, 0, comp_size);
- glGetCompressedTextureSubImage(tex, 0, 192, 64, 0, 64, 64, 1, comp_size, buf);
- glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 32, 32, 64, 64, COMP_FMT, 2048, buf);
+ memset(buf, 0, tex.compsize);
+ glGetCompressedTextureSubImage(tex.id, 0, 192, 64, 0, 64, 64, 1, tex.compsize, buf);
+ glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 32, 32, 64, 64, tex.fmt, 2048, buf);
}
if(copytest) {
glBindTexture(GL_TEXTURE_2D, tex2);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glCompressedTexImage2D(GL_TEXTURE_2D, 0, COMP_FMT, xsz, ysz, 0, comp_size, pixels);
+ glCompressedTexImage2D(GL_TEXTURE_2D, 0, tex.fmt, tex.width, tex.height, 0, tex.compsize, tex.data);
glBindTexture(GL_TEXTURE_2D, 0);
glCopyImageSubData(tex2, GL_TEXTURE_2D, 0, 128, 64, 0,
- tex, GL_TEXTURE_2D, 0, 32, 32, 0, 64, 64, 1);
+ tex.id, GL_TEXTURE_2D, 0, 32, 32, 0, 64, 64, 1);
- glBindTexture(GL_TEXTURE_2D, tex);
+ glBindTexture(GL_TEXTURE_2D, tex.id);
}
free(buf);
- free(pixels);
-
-#ifdef USE_SRGB
- glEnable(GL_FRAMEBUFFER_SRGB);
-#endif
glEnable(GL_TEXTURE_2D);
return 0;
}
-int texcomp(unsigned char *compix, unsigned int tofmt, unsigned char *pixels,
- int xsz, int ysz, unsigned int fromfmt, unsigned int fromtype)
+void disp(void)
{
- unsigned int tex;
- int is_comp = 0;
- int comp_size = 0;
+ int x = 0, y = 0;
+ int xsz = tex.width;
+ int ysz = tex.height;
- glGenTextures(1, &tex);
- glBindTexture(GL_TEXTURE_2D, tex);
- glTexImage2D(GL_TEXTURE_2D, 0, tofmt, xsz, ysz, 0, fromfmt, fromtype, pixels);
- if(glGetError()) {
- fprintf(stderr, "failed to compress texture\n");
- return -1;
- }
+ glClear(GL_COLOR_BUFFER_BIT);
- glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED, &is_comp);
- if(!is_comp) {
- fprintf(stderr, "texture is not compressed\n");
- return -1;
- }
- glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &comp_size);
+ glBindTexture(GL_TEXTURE_2D, tex.id);
+ glEnable(GL_TEXTURE_2D);
- glGetCompressedTexImage(GL_TEXTURE_2D, 0, compix);
- return comp_size;
-}
+ glBegin(GL_QUADS);
+ glTexCoord2f(0, 1); glVertex2f(0, 0);
+ glTexCoord2f(1, 1); glVertex2f(xsz, 0);
+ glTexCoord2f(1, 0); glVertex2f(xsz, ysz);
+ glTexCoord2f(0, 0); glVertex2f(0, ysz);
-void disp(void)
-{
- glClear(GL_COLOR_BUFFER_BIT);
+ x += xsz;
+ xsz /= 2;
+ ysz /= 2;
- glBegin(GL_QUADS);
- glTexCoord2f(0, 1); glVertex2f(-1, -1);
- glTexCoord2f(1, 1); glVertex2f(1, -1);
- glTexCoord2f(1, 0); glVertex2f(1, 1);
- glTexCoord2f(0, 0); glVertex2f(-1, 1);
+ while(xsz & ysz) {
+ glTexCoord2f(0, 1); glVertex2f(x, y);
+ glTexCoord2f(1, 1); glVertex2f(x + xsz, y);
+ glTexCoord2f(1, 0); glVertex2f(x + xsz, y + ysz);
+ glTexCoord2f(0, 0); glVertex2f(x, y + ysz);
+
+ y += ysz;
+
+ xsz /= 2;
+ ysz /= 2;
+ }
glEnd();
glutSwapBuffers();
void reshape(int x, int y)
{
- float aspect = (float)x / (float)y;
glViewport(0, 0, x, y);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
- glScalef(1.0 / aspect, 1, 1);
+ glOrtho(0, x, 0, y, -1, 1);
}
void keyb(unsigned char key, int x, int y)
}
}
+void idle(void)
+{
+ glutPostRedisplay();
+}
+
void gen_image(unsigned char *pixels, int xsz, int ysz)
{
int i, j;
}
}
-unsigned char *load_compressed_image(const char *fname, int *cszptr, int *xszptr, int *yszptr)
+struct header {
+ char magic[8];
+ uint32_t glfmt;
+ uint16_t flags;
+ uint16_t levels;
+ uint32_t width, height;
+ struct {
+ uint32_t offset, size;
+ } datadesc[20];
+ char unused[8];
+};
+
+int load_texture(const char *fname, struct texture *tex)
{
- unsigned char *pixels;
- long start, comp_size;
- int xsz, ysz;
+ int i;
FILE *fp;
+ struct header hdr;
+ void *buf;
- if(!(fp = fopen(texfile, "rb"))) {
- fprintf(stderr, "failed to open compressed texture file: %s: %s\n", texfile, strerror(errno));
- return 0;
+ if(!(fp = fopen(fname, "rb"))) {
+ fprintf(stderr, "failed to open file: %s: %s\n", fname, strerror(errno));
+ return -1;
}
-
- if(fread(&xsz, sizeof xsz, 1, fp) < 1 || fread(&ysz, sizeof ysz, 1, fp) < 1) {
- fprintf(stderr, "failed to read compressed texture file header: %s: %s\n", texfile, strerror(errno));
+ if(fread(&hdr, 1, sizeof hdr, fp) != sizeof hdr) {
+ fprintf(stderr, "failed to read image file header: %s: %s\n", fname, strerror(errno));
fclose(fp);
- return 0;
+ return -1;
}
- start = ftell(fp);
- fseek(fp, 0, SEEK_END);
- comp_size = ftell(fp) - start;
- fseek(fp, start, SEEK_SET);
-
- if(!(pixels = malloc(comp_size))) {
- perror("failed to allocate pixel buffer");
- return 0;
+ if(memcmp(hdr.magic, "COMPTEX0", sizeof hdr.magic) != 0 || hdr.levels < 0 ||
+ hdr.levels > 20 || !hdr.datadesc[0].size) {
+ fprintf(stderr, "%s is not a compressed texture file, or is corrupted\n", fname);
+ fclose(fp);
+ return -1;
}
- if(fread(pixels, 1, comp_size, fp) < comp_size) {
- fprintf(stderr, "failed to read compressed texture file: %s: %s\n", texfile, strerror(errno));
+
+ if(!(buf = malloc(hdr.datadesc[0].size))) {
+ fprintf(stderr, "failed to allocate compressed texture buffer (%d bytes): %s\n",
+ hdr.datadesc[0].size, strerror(errno));
fclose(fp);
- free(pixels);
- return 0;
+ return -1;
+ }
+ if(!(tex->data = malloc(hdr.datadesc[0].size))) {
+ fprintf(stderr, "failed to allocate data buffer\n");
+ fclose(fp);
+ free(buf);
+ return -1;
}
- fclose(fp);
- *cszptr = comp_size;
- *xszptr = xsz;
- *yszptr = ysz;
- return pixels;
-}
-int dump_compressed_image(const char *fname, unsigned char *data, int size, int w, int h)
-{
- FILE *fp;
+ glGenTextures(1, &tex->id);
+ glBindTexture(GL_TEXTURE_2D, tex->id);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+ hdr.levels > 1 ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- if(!(fp = fopen(fname, "wb"))) {
- fprintf(stderr, "failed to open compressed texture dump file: %s: %s\n", fname, strerror(errno));
- return -1;
- }
+ tex->fmt = hdr.glfmt;
+ tex->width = hdr.width;
+ tex->height = hdr.height;
+ tex->compsize = hdr.datadesc[0].size;
+
+ printf("%s: %dx%d format: %s\n", fname, tex->width, tex->height, fmtstr(tex->fmt));
+ glutReshapeWindow(tex->width + tex->width / 2, tex->height);
- if(fwrite(&w, sizeof w, 1, fp) < 1 ||
- fwrite(&h, sizeof h, 1, fp) < 1 ||
- fwrite(data, 1, size, fp) < size) {
- fprintf(stderr, "failed to dump compressed texture: %s\n", strerror(errno));
+ for(i=0; i<hdr.levels; i++) {
+ if(!hdr.datadesc[i].size) {
+ continue;
+ }
+
+ if(fread(buf, 1, hdr.datadesc[i].size, fp) != hdr.datadesc[i].size) {
+ fprintf(stderr, "unexpected EOF while reading texture: %s\n", fname);
+ fclose(fp);
+ free(buf);
+ glDeleteTextures(1, &tex->id);
+ return -1;
+ }
+ if(i == 0) {
+ memcpy(tex->data, buf, hdr.datadesc[0].size);
+ }
+ glCompressedTexImage2D(GL_TEXTURE_2D, i, hdr.glfmt, hdr.width, hdr.height, 0,
+ hdr.datadesc[i].size, buf);
+
+ hdr.width /= 2;
+ hdr.height /= 2;
}
+
+ free(buf);
fclose(fp);
return 0;
}
case 0x93DB: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10";
case 0x93DC: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12";
case 0x93DD: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12";
+ case GL_LUMINANCE:
+ case 1:
+ return "GL_LUMINANCE";
+ case GL_RGB:
+ case 3:
+ return "GL_RGB";
+ case GL_RGBA:
+ case 4:
+ return "GL_RGBA";
+ case GL_BGR: return "GL_BGR";
+ case GL_BGRA: return "GL_BGRA";
+ case GL_SLUMINANCE: return "GL_SLUMINANCE";
+ case GL_SLUMINANCE8: return "GL_SLUMINANCE8";
+ case GL_SLUMINANCE_ALPHA: return "GL_SLUMINANCE_ALPHA";
+ case GL_SLUMINANCE8_ALPHA8: return "GL_SLUMINANCE8_ALPHA8";
+ case GL_SRGB: return "GL_SRGB";
+ case GL_SRGB8: return "GL_SRGB8";
+ case GL_SRGB_ALPHA: return "GL_SRGB_ALPHA";
+ case GL_SRGB8_ALPHA8: return "GL_SRGB8_ALPHA8";
default:
break;
}