source: trunk/packages/vizservers/nanovis/BMPWriter.cpp @ 3875

Last change on this file since 3875 was 3605, checked in by ldelgass, 11 years ago

Add writer thread to nanovis (set USE_THREADS in Makefile), more refactoring.

  • Property svn:eol-style set to native
File size: 9.7 KB
Line 
1/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 * Copyright (c) 2004-2013  HUBzero Foundation, LLC
4 *
5 * Authors:
6 *   Wei Qiao <qiaow@purdue.edu>
7 *   Insoo Woo <iwoo@purdue.edu>
8 *   George A. Howlett <gah@purdue.edu>
9 */
10
11#include <cstdlib>
12#include <cstring>
13#include <cstdio>
14
15#include "nanovis.h"
16#include "BMPWriter.h"
17#ifdef USE_THREADS
18#include "ResponseQueue.h"
19#endif
20#include "Trace.h"
21
22using namespace nv;
23
24// used internally to build up the BMP file header
25// Writes an integer value into the header data structure at pos
26static inline void
27bmpHeaderAddInt(unsigned char* header, int& pos, int data)
28{
29#ifdef WORDS_BIGENDIAN
30    header[pos++] = (data >> 24) & 0xFF;
31    header[pos++] = (data >> 16) & 0xFF;
32    header[pos++] = (data >> 8)  & 0xFF;
33    header[pos++] = (data)       & 0xFF;
34#else
35    header[pos++] = data & 0xff;
36    header[pos++] = (data >> 8) & 0xff;
37    header[pos++] = (data >> 16) & 0xff;
38    header[pos++] = (data >> 24) & 0xff;
39#endif
40}
41
42/**
43 * \brief Write image data to a bitmap (BMP) file
44 *
45 * Note that this function assumes the imgData has 4 byte aligned
46 * rows (bottom to top) of RGB pixel data.
47 */
48bool
49nv::writeBMPFile(int frameNumber, const char *directoryName,
50                 unsigned char *imgData, int width, int height)
51{
52    unsigned char header[SIZEOF_BMP_HEADER];
53
54    // BE CAREFUL:  BMP files must have an even multiple of 4 bytes
55    // on each scan line.  If need be, we add padding to each line.
56    int pad = 0;
57    if ((3 * width) % 4 > 0) {
58        pad = 4 - ((3 * width) % 4);
59    }
60
61    size_t headerLength = SIZEOF_BMP_HEADER;
62    size_t pixelBytes = (3 * width + pad) * height;
63    int fsize = headerLength + pixelBytes;
64
65    // Prepare header
66    int pos = 0;
67    header[pos++] = 'B';
68    header[pos++] = 'M';
69
70    // file size in bytes
71    bmpHeaderAddInt(header, pos, fsize);
72
73    // reserved value (must be 0)
74    bmpHeaderAddInt(header, pos, 0);
75
76    // offset in bytes to start of bitmap data
77    bmpHeaderAddInt(header, pos, headerLength);
78
79    // size of the BITMAPINFOHEADER
80    bmpHeaderAddInt(header, pos, 40);
81
82    // width of the image in pixels
83    bmpHeaderAddInt(header, pos, width);
84
85    // height of the image in pixels
86    bmpHeaderAddInt(header, pos, height);
87
88    // 1 plane + (24 bits/pixel << 16)
89    bmpHeaderAddInt(header, pos, 1572865);
90
91    // no compression
92    // size of image for compression
93    bmpHeaderAddInt(header, pos, 0);
94    bmpHeaderAddInt(header, pos, 0);
95
96    // x pixels per meter
97    // y pixels per meter
98    bmpHeaderAddInt(header, pos, 0);
99    bmpHeaderAddInt(header, pos, 0);
100
101    // number of colors used (0 = compute from bits/pixel)
102    // number of important colors (0 = all colors important)
103    bmpHeaderAddInt(header, pos, 0);
104    bmpHeaderAddInt(header, pos, 0);
105
106    // BE CAREFUL: BMP format wants BGR ordering for screen data
107    unsigned char *scr = imgData;
108    for (int row = 0; row < height; row++) {
109        for (int col = 0; col < width; col++) {
110            unsigned char tmp = scr[2];
111            scr[2] = scr[0];  // B
112            scr[0] = tmp;     // R
113            scr += 3;
114        }
115        scr += pad;  // skip over padding already in screen data
116    }
117
118    bool ret = true;
119    FILE *filp;
120    char filename[100];
121    if (frameNumber >= 0) {
122        if (directoryName)
123            sprintf(filename, "%s/image%03d.bmp", directoryName, frameNumber);
124        else
125            sprintf(filename, "/tmp/flow_animation/image%03d.bmp", frameNumber);
126
127        TRACE("Writing %s", filename);
128        filp = fopen(filename, "wb");
129        if (filp == NULL) {
130            ERROR("cannot create file");
131            ret = false;
132        }
133    } else {
134        filp = fopen("/tmp/image.bmp", "wb");
135        if (filp == NULL) {
136            ERROR("cannot create file");
137            ret = false;
138        }
139    }
140    if (fwrite(header, headerLength, 1, filp) != 1) {
141        ERROR("can't write header: short write");
142        ret = false;
143    }
144    if (fwrite(imgData, pixelBytes, 1, filp) != 1) {
145        ERROR("can't write data: short write");
146        ret = false;
147    }
148    fclose(filp);
149    return ret;
150}
151
152#ifdef USE_THREADS
153
154/**
155 * \brief Write image data to a bitmap (BMP) stream
156 *
157 * Note that this function assumes the imgData has 4 byte aligned
158 * rows (bottom to top) of RGB pixel data.
159 */
160void
161nv::queueBMP(ResponseQueue *queue, const char *cmdName,
162             unsigned char *imgData, int width, int height)
163{
164    unsigned char header[SIZEOF_BMP_HEADER];
165
166    // BE CAREFUL:  BMP files must have an even multiple of 4 bytes
167    // on each scan line.  If need be, we add padding to each line.
168    int pad = 0;
169    if ((3 * width) % 4 > 0) {
170        pad = 4 - ((3 * width) % 4);
171    }
172
173    size_t headerLength = SIZEOF_BMP_HEADER;
174    size_t pixelBytes = (3 * width + pad) * height;
175    int fsize = headerLength + pixelBytes;
176
177    // Prepare header
178    int pos = 0;
179    header[pos++] = 'B';
180    header[pos++] = 'M';
181
182    // file size in bytes
183    bmpHeaderAddInt(header, pos, fsize);
184
185    // reserved value (must be 0)
186    bmpHeaderAddInt(header, pos, 0);
187
188    // offset in bytes to start of bitmap data
189    bmpHeaderAddInt(header, pos, headerLength);
190
191    // size of the BITMAPINFOHEADER
192    bmpHeaderAddInt(header, pos, 40);
193
194    // width of the image in pixels
195    bmpHeaderAddInt(header, pos, width);
196
197    // height of the image in pixels
198    bmpHeaderAddInt(header, pos, height);
199    // 1 plane + (24 bits/pixel << 16)
200    bmpHeaderAddInt(header, pos, 1572865);
201
202    // no compression
203    // size of image for compression
204    bmpHeaderAddInt(header, pos, 0);
205    bmpHeaderAddInt(header, pos, 0);
206
207    // x pixels per meter
208    // y pixels per meter
209    bmpHeaderAddInt(header, pos, 0);
210    bmpHeaderAddInt(header, pos, 0);
211
212    // number of colors used (0 = compute from bits/pixel)
213    // number of important colors (0 = all colors important)
214    bmpHeaderAddInt(header, pos, 0);
215    bmpHeaderAddInt(header, pos, 0);
216
217    char command[200];
218    sprintf(command, "%s %lu\n", cmdName,
219            (unsigned long)headerLength + pixelBytes);
220
221    size_t cmdLength;
222    cmdLength = strlen(command);
223
224    size_t length = cmdLength + headerLength + pixelBytes;
225    unsigned char *mesg = (unsigned char *)malloc(length);
226    if (mesg == NULL) {
227        ERROR("can't allocate %ld bytes for the image message", length);
228        return;
229    }
230    memcpy(mesg, command, cmdLength);
231    memcpy(mesg + cmdLength, header, headerLength);
232
233    // BE CAREFUL: BMP format wants BGR ordering for screen data
234    unsigned char *scr = imgData;
235    for (int row = 0; row < height; row++) {
236        for (int col = 0; col < width; col++) {
237            unsigned char tmp = scr[2];
238            scr[2] = scr[0];  // B
239            scr[0] = tmp;     // R
240            scr += 3;
241        }
242        scr += pad;  // skip over padding already in screen data
243    }
244
245    memcpy(mesg + cmdLength + headerLength, imgData, pixelBytes);
246
247    Response *response;
248    if (strncmp(cmdName, "nv>legend", 9) == 0) {
249        response = new Response(Response::LEGEND);
250    } else {
251        response = new Response(Response::IMAGE);
252    }
253    response->setMessage(mesg, length, Response::DYNAMIC);
254    queue->enqueue(response);
255}
256#else
257/**
258 * \brief Write image data to a bitmap (BMP) stream
259 *
260 * Note that this function assumes the imgData has 4 byte aligned
261 * rows (bottom to top) of RGB pixel data.
262 */
263void
264nv::writeBMP(int fd, const char *prefix,
265             unsigned char *imgData, int width, int height)
266{
267    unsigned char header[SIZEOF_BMP_HEADER];
268
269    // BE CAREFUL:  BMP files must have an even multiple of 4 bytes
270    // on each scan line.  If need be, we add padding to each line.
271    int pad = 0;
272    if ((3 * width) % 4 > 0) {
273        pad = 4 - ((3 * width) % 4);
274    }
275
276    size_t headerLength = SIZEOF_BMP_HEADER;
277    size_t pixelBytes = (3 * width + pad) * height;
278    int fsize = headerLength + pixelBytes;
279
280    char command[200];
281    sprintf(command, "%s %d\n", prefix, fsize);
282    if (write(fd, command, strlen(command)) != (ssize_t)strlen(command)) {
283        ERROR("Short write");
284        return;
285    }
286
287    // Prepare header
288    int pos = 0;
289    header[pos++] = 'B';
290    header[pos++] = 'M';
291
292    // file size in bytes
293    bmpHeaderAddInt(header, pos, fsize);
294
295    // reserved value (must be 0)
296    bmpHeaderAddInt(header, pos, 0);
297
298    // offset in bytes to start of bitmap data
299    bmpHeaderAddInt(header, pos, headerLength);
300
301    // size of the BITMAPINFOHEADER
302    bmpHeaderAddInt(header, pos, 40);
303
304    // width of the image in pixels
305    bmpHeaderAddInt(header, pos, width);
306
307    // height of the image in pixels
308    bmpHeaderAddInt(header, pos, height);
309
310    // 1 plane + (24 bits/pixel << 16)
311    bmpHeaderAddInt(header, pos, 1572865);
312
313    // no compression
314    // size of image for compression
315    bmpHeaderAddInt(header, pos, 0);
316    bmpHeaderAddInt(header, pos, 0);
317
318    // x pixels per meter
319    // y pixels per meter
320    bmpHeaderAddInt(header, pos, 0);
321    bmpHeaderAddInt(header, pos, 0);
322
323    // number of colors used (0 = compute from bits/pixel)
324    // number of important colors (0 = all colors important)
325    bmpHeaderAddInt(header, pos, 0);
326    bmpHeaderAddInt(header, pos, 0);
327
328    // BE CAREFUL: BMP format wants BGR ordering for screen data
329    unsigned char *scr = imgData;
330    for (int row = 0; row < height; row++) {
331        for (int col = 0; col < width; col++) {
332            unsigned char tmp = scr[2];
333            scr[2] = scr[0];  // B
334            scr[0] = tmp;     // R
335            scr += 3;
336        }
337        scr += pad;  // skip over padding already in screen data
338    }
339
340    if (write(fd, header, headerLength) != (ssize_t)headerLength) {
341        ERROR("Short write");
342        return;
343    }
344    if (write(fd, imgData, pixelBytes) != (ssize_t)pixelBytes) {
345        ERROR("Short write");
346        return;
347    }
348}
349#endif
Note: See TracBrowser for help on using the repository browser.