1 /*
2 * Copyright (c) 2008-2016 VMware, Inc.
3 * All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sub license, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial portions
15 * of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
20 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
21 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26
27 #include "util/u_debug_image.h"
28 #include "util/format/u_format.h"
29 #include "util/u_inlines.h"
30 #include "util/u_memory.h"
31 #include "util/u_string.h"
32 #include "util/u_surface.h"
33 #include "util/u_tile.h"
34
35 #include <stdio.h>
36
37
38 #ifdef DEBUG
39
40 /**
41 * Dump an image to .ppm file.
42 * \param format PIPE_FORMAT_x
43 * \param cpp bytes per pixel
44 * \param width width in pixels
45 * \param height height in pixels
46 * \param stride row stride in bytes
47 */
48 void
debug_dump_image(const char * prefix,enum pipe_format format,UNUSED unsigned cpp,unsigned width,unsigned height,unsigned stride,const void * data)49 debug_dump_image(const char *prefix,
50 enum pipe_format format, UNUSED unsigned cpp,
51 unsigned width, unsigned height,
52 unsigned stride,
53 const void *data)
54 {
55 /* write a ppm file */
56 char filename[256];
57 unsigned char *rgb8;
58 FILE *f;
59
60 snprintf(filename, sizeof(filename), "%s.ppm", prefix);
61
62 rgb8 = MALLOC(height * width * 3);
63 if (!rgb8) {
64 return;
65 }
66
67 util_format_translate(
68 PIPE_FORMAT_R8G8B8_UNORM,
69 rgb8, width * 3,
70 0, 0,
71 format,
72 data, stride,
73 0, 0, width, height);
74
75 /* Must be opened in binary mode or DOS line ending causes data
76 * to be read with one byte offset.
77 */
78 f = fopen(filename, "wb");
79 if (f) {
80 fprintf(f, "P6\n");
81 fprintf(f, "# ppm-file created by gallium\n");
82 fprintf(f, "%i %i\n", width, height);
83 fprintf(f, "255\n");
84 fwrite(rgb8, 1, height * width * 3, f);
85 fclose(f);
86 }
87 else {
88 fprintf(stderr, "Can't open %s for writing\n", filename);
89 }
90
91 FREE(rgb8);
92 }
93
94
95 /* FIXME: dump resources, not surfaces... */
96 void
debug_dump_surface(struct pipe_context * pipe,const char * prefix,struct pipe_surface * surface)97 debug_dump_surface(struct pipe_context *pipe,
98 const char *prefix,
99 struct pipe_surface *surface)
100 {
101 struct pipe_resource *texture;
102 struct pipe_transfer *transfer;
103 void *data;
104
105 if (!surface)
106 return;
107
108 /* XXX: this doesn't necessarily work, as the driver may be using
109 * temporary storage for the surface which hasn't been propagated
110 * back into the texture. Need to nail down the semantics of views
111 * and transfers a bit better before we can say if extra work needs
112 * to be done here:
113 */
114 texture = surface->texture;
115
116 data = pipe_transfer_map(pipe, texture, surface->u.tex.level,
117 surface->u.tex.first_layer,
118 PIPE_MAP_READ,
119 0, 0, surface->width, surface->height, &transfer);
120 if (!data)
121 return;
122
123 debug_dump_image(prefix,
124 texture->format,
125 util_format_get_blocksize(texture->format),
126 util_format_get_nblocksx(texture->format, surface->width),
127 util_format_get_nblocksy(texture->format, surface->height),
128 transfer->stride,
129 data);
130
131 pipe->transfer_unmap(pipe, transfer);
132 }
133
134
135 void
debug_dump_texture(struct pipe_context * pipe,const char * prefix,struct pipe_resource * texture)136 debug_dump_texture(struct pipe_context *pipe,
137 const char *prefix,
138 struct pipe_resource *texture)
139 {
140 struct pipe_surface *surface, surf_tmpl;
141
142 if (!texture)
143 return;
144
145 /* XXX for now, just dump image for layer=0, level=0 */
146 u_surface_default_template(&surf_tmpl, texture);
147 surface = pipe->create_surface(pipe, texture, &surf_tmpl);
148 if (surface) {
149 debug_dump_surface(pipe, prefix, surface);
150 pipe->surface_destroy(pipe, surface);
151 }
152 }
153
154
155 #pragma pack(push,2)
156 struct bmp_file_header {
157 uint16_t bfType;
158 uint32_t bfSize;
159 uint16_t bfReserved1;
160 uint16_t bfReserved2;
161 uint32_t bfOffBits;
162 };
163 #pragma pack(pop)
164
165 struct bmp_info_header {
166 uint32_t biSize;
167 int32_t biWidth;
168 int32_t biHeight;
169 uint16_t biPlanes;
170 uint16_t biBitCount;
171 uint32_t biCompression;
172 uint32_t biSizeImage;
173 int32_t biXPelsPerMeter;
174 int32_t biYPelsPerMeter;
175 uint32_t biClrUsed;
176 uint32_t biClrImportant;
177 };
178
179 struct bmp_rgb_quad {
180 uint8_t rgbBlue;
181 uint8_t rgbGreen;
182 uint8_t rgbRed;
183 uint8_t rgbAlpha;
184 };
185
186 void
debug_dump_surface_bmp(struct pipe_context * pipe,const char * filename,struct pipe_surface * surface)187 debug_dump_surface_bmp(struct pipe_context *pipe,
188 const char *filename,
189 struct pipe_surface *surface)
190 {
191 struct pipe_transfer *transfer;
192 struct pipe_resource *texture = surface->texture;
193 void *ptr;
194
195 ptr = pipe_transfer_map(pipe, texture, surface->u.tex.level,
196 surface->u.tex.first_layer, PIPE_MAP_READ,
197 0, 0, surface->width, surface->height, &transfer);
198
199 debug_dump_transfer_bmp(pipe, filename, transfer, ptr);
200
201 pipe->transfer_unmap(pipe, transfer);
202 }
203
204 void
debug_dump_transfer_bmp(UNUSED struct pipe_context * pipe,const char * filename,struct pipe_transfer * transfer,void * ptr)205 debug_dump_transfer_bmp(UNUSED struct pipe_context *pipe,
206 const char *filename,
207 struct pipe_transfer *transfer, void *ptr)
208 {
209 float *rgba;
210
211 if (!transfer)
212 goto error1;
213
214 rgba = MALLOC(transfer->box.width *
215 transfer->box.height *
216 transfer->box.depth *
217 4*sizeof(float));
218 if (!rgba)
219 goto error1;
220
221 pipe_get_tile_rgba(transfer, ptr, 0, 0,
222 transfer->box.width, transfer->box.height,
223 transfer->resource->format,
224 rgba);
225
226 debug_dump_float_rgba_bmp(filename,
227 transfer->box.width, transfer->box.height,
228 rgba, transfer->box.width);
229
230 FREE(rgba);
231 error1:
232 ;
233 }
234
235 void
debug_dump_float_rgba_bmp(const char * filename,unsigned width,unsigned height,float * rgba,unsigned stride)236 debug_dump_float_rgba_bmp(const char *filename,
237 unsigned width, unsigned height,
238 float *rgba, unsigned stride)
239 {
240 FILE *stream;
241 struct bmp_file_header bmfh;
242 struct bmp_info_header bmih;
243 unsigned x, y;
244
245 if (!rgba)
246 goto error1;
247
248 bmfh.bfType = 0x4d42;
249 bmfh.bfSize = 14 + 40 + height*width*4;
250 bmfh.bfReserved1 = 0;
251 bmfh.bfReserved2 = 0;
252 bmfh.bfOffBits = 14 + 40;
253
254 bmih.biSize = 40;
255 bmih.biWidth = width;
256 bmih.biHeight = height;
257 bmih.biPlanes = 1;
258 bmih.biBitCount = 32;
259 bmih.biCompression = 0;
260 bmih.biSizeImage = height*width*4;
261 bmih.biXPelsPerMeter = 0;
262 bmih.biYPelsPerMeter = 0;
263 bmih.biClrUsed = 0;
264 bmih.biClrImportant = 0;
265
266 stream = fopen(filename, "wb");
267 if (!stream)
268 goto error1;
269
270 fwrite(&bmfh, 14, 1, stream);
271 fwrite(&bmih, 40, 1, stream);
272
273 y = height;
274 while (y--) {
275 float *ptr = rgba + (stride * y * 4);
276 for (x = 0; x < width; ++x) {
277 struct bmp_rgb_quad pixel;
278 pixel.rgbRed = float_to_ubyte(ptr[x*4 + 0]);
279 pixel.rgbGreen = float_to_ubyte(ptr[x*4 + 1]);
280 pixel.rgbBlue = float_to_ubyte(ptr[x*4 + 2]);
281 pixel.rgbAlpha = float_to_ubyte(ptr[x*4 + 3]);
282 fwrite(&pixel, 1, 4, stream);
283 }
284 }
285
286 fclose(stream);
287 error1:
288 ;
289 }
290
291 void
debug_dump_ubyte_rgba_bmp(const char * filename,unsigned width,unsigned height,const ubyte * rgba,unsigned stride)292 debug_dump_ubyte_rgba_bmp(const char *filename,
293 unsigned width, unsigned height,
294 const ubyte *rgba, unsigned stride)
295 {
296 FILE *stream;
297 struct bmp_file_header bmfh;
298 struct bmp_info_header bmih;
299 unsigned x, y;
300
301 assert(rgba);
302 if (!rgba)
303 goto error1;
304
305 bmfh.bfType = 0x4d42;
306 bmfh.bfSize = 14 + 40 + height*width*4;
307 bmfh.bfReserved1 = 0;
308 bmfh.bfReserved2 = 0;
309 bmfh.bfOffBits = 14 + 40;
310
311 bmih.biSize = 40;
312 bmih.biWidth = width;
313 bmih.biHeight = height;
314 bmih.biPlanes = 1;
315 bmih.biBitCount = 32;
316 bmih.biCompression = 0;
317 bmih.biSizeImage = height*width*4;
318 bmih.biXPelsPerMeter = 0;
319 bmih.biYPelsPerMeter = 0;
320 bmih.biClrUsed = 0;
321 bmih.biClrImportant = 0;
322
323 stream = fopen(filename, "wb");
324 assert(stream);
325 if (!stream)
326 goto error1;
327
328 fwrite(&bmfh, 14, 1, stream);
329 fwrite(&bmih, 40, 1, stream);
330
331 y = height;
332 while (y--) {
333 const ubyte *ptr = rgba + (stride * y * 4);
334 for (x = 0; x < width; ++x) {
335 struct bmp_rgb_quad pixel;
336 pixel.rgbRed = ptr[x*4 + 0];
337 pixel.rgbGreen = ptr[x*4 + 1];
338 pixel.rgbBlue = ptr[x*4 + 2];
339 pixel.rgbAlpha = ptr[x*4 + 3];
340 fwrite(&pixel, 1, 4, stream);
341 }
342 }
343
344 fclose(stream);
345 error1:
346 ;
347 }
348
349 #endif
350