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