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 */
48void
49debug_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... */
96void
97debug_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_texture_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->texture_unmap(pipe, transfer);
132}
133
134
135void
136debug_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)
156struct 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
165struct 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
179struct bmp_rgb_quad {
180   uint8_t rgbBlue;
181   uint8_t rgbGreen;
182   uint8_t rgbRed;
183   uint8_t rgbAlpha;
184};
185
186void
187debug_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_texture_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->texture_unmap(pipe, transfer);
202}
203
204void
205debug_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);
231error1:
232   ;
233}
234
235void
236debug_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);
287error1:
288   ;
289}
290
291void
292debug_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);
345error1:
346   ;
347}
348
349#endif
350