1 #include "pixel.h"
2 
get_color_map(GLenum format)3 static const colorlayout_t *get_color_map(GLenum format) {
4     #define map(fmt, ...)                               \
5         case fmt: {                                     \
6         static colorlayout_t layout = {fmt, __VA_ARGS__}; \
7         return &layout; }
8     switch (format) {
9         map(GL_RED, 0, -1, -1, -1);
10         map(GL_RG, 0, 1, -1, -1);
11         map(GL_RGBA, 0, 1, 2, 3);
12         map(GL_RGB, 0, 1, 2, -1);
13         map(GL_BGRA, 2, 1, 0, 3);
14         map(GL_BGR, 2, 1, 0, -1);
15         default:
16             printf("libGL: unknown pixel format %i\n", format);
17             break;
18     }
19     static colorlayout_t null = {0};
20     return &null;
21     #undef map
22 }
23 
24 static inline
remap_pixel(const GLvoid * src,GLvoid * dst,const colorlayout_t * src_color,GLenum src_type,const colorlayout_t * dst_color,GLenum dst_type)25 bool remap_pixel(const GLvoid *src, GLvoid *dst,
26                  const colorlayout_t *src_color, GLenum src_type,
27                  const colorlayout_t *dst_color, GLenum dst_type) {
28 
29     #define type_case(constant, type, ...)        \
30         case constant: {                          \
31             const type *s = (const type *)src;    \
32             type *d = (type *)dst;                \
33             type v = *s;                          \
34             __VA_ARGS__                           \
35             break;                                \
36         }
37 
38     #define default(arr, amod, vmod, key, def) \
39         key >= 0 ? arr[amod key] vmod : def
40 
41     #define carefully(arr, amod, key, value) \
42         if (key >= 0) d[amod key] = value;
43 
44     #define read_each(amod, vmod)                                 \
45         pixel.r = default(s, amod, vmod, src_color->red, 0);      \
46         pixel.g = default(s, amod, vmod, src_color->green, 0);    \
47         pixel.b = default(s, amod, vmod, src_color->blue, 0);     \
48         pixel.a = default(s, amod, vmod, src_color->alpha, 1.0f);
49 
50     #define write_each(amod, vmod)                         \
51         carefully(d, amod, dst_color->red, pixel.r vmod)   \
52         carefully(d, amod, dst_color->green, pixel.g vmod) \
53         carefully(d, amod, dst_color->blue, pixel.b vmod)  \
54         carefully(d, amod, dst_color->alpha, pixel.a vmod)
55 
56     // this pixel stores our intermediate color
57     // it will be RGBA and normalized to between (0.0 - 1.0f)
58     pixel_t pixel;
59     switch (src_type) {
60         type_case(GL_DOUBLE, GLdouble, read_each(,))
61         type_case(GL_FLOAT, GLfloat, read_each(,))
62         case GL_UNSIGNED_INT_8_8_8_8_REV:
63         type_case(GL_UNSIGNED_BYTE, GLubyte, read_each(, / 255.0))
64         type_case(GL_UNSIGNED_INT_8_8_8_8, GLubyte, read_each(3 - , / 255.0))
65         type_case(GL_UNSIGNED_SHORT_1_5_5_5_REV, GLushort,
66             s = (GLushort[]){
67                 v & 31,
68                 (v & 0x03e0 >> 5) / 31.0,
69                 (v & 0x7c00 >> 10) / 31.0,
70                 (v & 0x8000 >> 15) / 31.0,
71             };
72             read_each(,);
73         )
74         default:
75             // TODO: add glSetError?
76             printf("libGL: Unsupported source data type: %i\n", src_type);
77             return false;
78             break;
79     }
80 
81     switch (dst_type) {
82         type_case(GL_FLOAT, GLfloat, write_each(,))
83         type_case(GL_UNSIGNED_BYTE, GLubyte, write_each(, * 255.0))
84         // TODO: force 565 to RGB? then we can change [4] -> 3
85         type_case(GL_UNSIGNED_SHORT_5_6_5, GLushort,
86             GLfloat color[4];
87             color[dst_color->red] = pixel.r;
88             color[dst_color->green] = pixel.g;
89             color[dst_color->blue] = pixel.b;
90             *d = ((GLuint)(color[0] * 31) & 0x1f << 11) |
91                  ((GLuint)(color[1] * 63) & 0x3f << 5) |
92                  ((GLuint)(color[2] * 31) & 0x1f);
93         )
94         default:
95             printf("libGL: Unsupported target data type: %i\n", dst_type);
96             return false;
97             break;
98     }
99     return true;
100 
101     #undef type_case
102     #undef default
103     #undef carefully
104     #undef read_each
105     #undef write_each
106 }
107 
pixel_convert(const GLvoid * src,GLvoid ** dst,GLuint width,GLuint height,GLenum src_format,GLenum src_type,GLenum dst_format,GLenum dst_type)108 bool pixel_convert(const GLvoid *src, GLvoid **dst,
109                    GLuint width, GLuint height,
110                    GLenum src_format, GLenum src_type,
111                    GLenum dst_format, GLenum dst_type) {
112     const colorlayout_t *src_color, *dst_color;
113     GLuint pixels = width * height;
114     GLuint dst_size = pixels * pixel_sizeof(dst_format, dst_type);
115 
116     // printf("pixel conversion: %ix%i - %i, %i -> %i, %i\n", width, height, src_format, src_type, dst_format, dst_type);
117     src_color = get_color_map(src_format);
118     dst_color = get_color_map(dst_format);
119     if (!dst_size || !pixel_sizeof(src_format, src_type)
120         || !src_color->type || !dst_color->type)
121         return false;
122 
123     if (src_type == dst_type && src_color->type == dst_color->type) {
124         if (*dst != src) {
125             *dst = malloc(dst_size);
126             memcpy(*dst, src, dst_size);
127             return true;
128         }
129     } else {
130         GLsizei src_stride = pixel_sizeof(src_format, src_type);
131         GLsizei dst_stride = pixel_sizeof(dst_format, dst_type);
132         *dst = malloc(dst_size);
133         uintptr_t src_pos = (uintptr_t)src;
134         uintptr_t dst_pos = (uintptr_t)*dst;
135         for (int i = 0; i < pixels; i++) {
136             if (! remap_pixel((const GLvoid *)src_pos, (GLvoid *)dst_pos,
137                               src_color, src_type, dst_color, dst_type)) {
138                 // checking a boolean for each pixel like this might be a slowdown?
139                 // probably depends on how well branch prediction performs
140                 return false;
141             }
142             src_pos += src_stride;
143             dst_pos += dst_stride;
144         }
145         return true;
146     }
147     return false;
148 }
149 
pixel_scale(const GLvoid * old,GLvoid ** new,GLuint width,GLuint height,GLfloat ratio,GLenum format,GLenum type)150 bool pixel_scale(const GLvoid *old, GLvoid **new,
151                  GLuint width, GLuint height,
152                  GLfloat ratio,
153                  GLenum format, GLenum type) {
154     GLuint pixel_size, new_width, new_height;
155     new_width = width * ratio;
156     new_height = height * ratio;
157     printf("scaling %ux%u -> %ux%u\n", width, height, new_width, new_height);
158     GLvoid *dst;
159     uintptr_t src, pos, pixel;
160 
161     pixel_size = pixel_sizeof(format, type);
162     dst = malloc(pixel_size * new_width * new_height);
163     src = (uintptr_t)old;
164     pos = (uintptr_t)dst;
165     for (int x = 0; x < new_width; x++) {
166         for (int y = 0; y < new_height; y++) {
167             pixel = src + (x / ratio) +
168                           (y / ratio) * width;
169             memcpy((GLvoid *)pos, (GLvoid *)pixel, pixel_size);
170             pos += pixel_size;
171         }
172     }
173     *new = dst;
174     return true;
175 }
176 
pixel_to_ppm(const GLvoid * pixels,GLuint width,GLuint height,GLenum format,GLenum type,GLuint name)177 bool pixel_to_ppm(const GLvoid *pixels, GLuint width, GLuint height,
178                   GLenum format, GLenum type, GLuint name) {
179     if (! pixels)
180         return false;
181 
182     const GLvoid *src;
183     char filename[64];
184     int size = 4 * 3 * width * height;
185     if (format == GL_RGB && type == GL_UNSIGNED_BYTE) {
186         src = pixels;
187     } else {
188         if (! pixel_convert(pixels, (GLvoid **)&src, width, height, format, type, GL_RGB, GL_UNSIGNED_BYTE)) {
189             return false;
190         }
191     }
192 
193     snprintf(filename, 64, "/tmp/tex.%d.ppm", name);
194     FILE *fd = fopen(filename, "w");
195     fprintf(fd, "P6 %d %d %d\n", width, height, 255);
196     fwrite(src, 1, size, fd);
197     fclose(fd);
198     return true;
199 }
200