source: nanovis/trunk/PPMWriter.cpp @ 4923

Last change on this file since 4923 was 3605, checked in by ldelgass, 7 years ago

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

  • Property svn:eol-style set to native
File size: 6.2 KB
Line 
1/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 * Copyright (C) 2004-2012  HUBzero Foundation, LLC
4 *
5 * Author: George A. Howlett <gah@purdue.edu>
6 */
7
8#include <cstdio>
9#include <cstdlib>
10#include <cstring>
11#include <cerrno>
12#include <sys/uio.h>
13#include <sys/types.h>
14#include <sys/stat.h>
15#include <fcntl.h>
16#include <unistd.h>
17
18#include "Trace.h"
19#include "PPMWriter.h"
20#ifdef USE_THREADS
21#include "ResponseQueue.h"
22#endif
23
24using namespace nv;
25
26#define PPM_MAXVAL 255
27
28#ifdef USE_THREADS
29
30/**
31 * \brief Writes image data as PPM binary data to the client.
32 *
33 * The PPM binary format is very simple.
34 *
35 *     P6 w h 255\n
36 *     3-byte RGB pixel data.
37 *
38 * The client (using the TkImg library) will do less work to unpack this
39 * format, as opposed to BMP or PNG.
40 *
41 * Note that currently the image data has bottom to top scanlines.  This
42 * routine could be made even simpler (faster) if the image data had top
43 * to bottom scanlines.
44 *
45 * \param[in] queue Pointer to ResponseQueue to write to
46 * \param[in] cmdName Command name to send (byte length will be appended)
47 * \param[in] data Image data
48 * \param[in] width Width of image in pixels
49 * \param[in] height Height of image in pixels
50 */
51void
52nv::queuePPM(ResponseQueue *queue, const char *cmdName, 
53             const unsigned char *data, int width, int height)
54{
55    char header[200];
56
57    TRACE("Entering (%dx%d)\n", width, height);
58    // Generate the PPM binary file header
59    snprintf(header, sizeof(header), "P6 %d %d %d\n", width, height, 
60             PPM_MAXVAL);
61
62    size_t headerLength = strlen(header);
63    size_t dataLength = width * height * 3;
64
65    char command[200];
66    snprintf(command, sizeof(command), "%s %lu\n", cmdName, 
67             (unsigned long)headerLength + dataLength);
68
69    size_t cmdLength;
70    cmdLength = strlen(command);
71
72    size_t length = cmdLength + headerLength + dataLength;
73    unsigned char *mesg = (unsigned char *)malloc(length);
74    if (mesg == NULL) {
75        ERROR("can't allocate %ld bytes for the image message", length);
76        return;
77    }
78    memcpy(mesg, command, cmdLength);
79    memcpy(mesg + cmdLength, header, headerLength);
80
81    size_t bytesPerRow = width * 3;
82    unsigned char *destRowPtr = mesg + length - bytesPerRow;
83    unsigned char *srcRowPtr = const_cast<unsigned char *>(data);
84    for (int y = 0; y < height; y++) {
85        memcpy(destRowPtr, srcRowPtr, bytesPerRow);
86        srcRowPtr += bytesPerRow;
87        destRowPtr -= bytesPerRow;
88    }
89
90    Response *response;
91    if (strncmp(cmdName, "nv>legend", 9) == 0) {
92        response = new Response(Response::LEGEND);
93    } else {
94        response = new Response(Response::IMAGE);
95    }
96    response->setMessage(mesg, length, Response::DYNAMIC);
97    queue->enqueue(response);
98    TRACE("Leaving (%dx%d)\n", width, height);
99}
100#else
101
102/**
103 * \brief Writes image data as PPM binary data to the client.
104 *
105 * The PPM binary format is very simple.
106 *
107 *     P6 w h 255\n
108 *     3-byte RGB pixel data.
109 *
110 * The client (using the TkImg library) will do less work to unpack this
111 * format, as opposed to BMP or PNG.
112 *
113 * Note that currently the image data has bottom to top scanlines.  This
114 * routine could be made even simpler (faster) if the image data had top
115 * to bottom scanlines.
116 *
117 * \param[in] fd File descriptor that will be written to
118 * \param[in] cmdName Command name to send (byte length will be appended)
119 * \param[in] data Image data
120 * \param[in] width Width of image in pixels
121 * \param[in] height Height of image in pixels
122 */
123void
124nv::writePPM(int fd, const char *cmdName, 
125             const unsigned char *data, int width, int height)
126{
127    TRACE("Entering (%dx%d)\n", width, height);
128
129    // Generate the PPM binary file header
130    char header[200];
131    snprintf(header, sizeof(header), "P6 %d %d %d\n", width, height,
132             PPM_MAXVAL);
133
134    size_t headerLength = strlen(header);
135    size_t dataLength = width * height * 3;
136
137    char command[200];
138    snprintf(command, sizeof(command), "%s %lu\n", cmdName, 
139             (unsigned long)headerLength + dataLength);
140
141    size_t numRecords = height + 2;
142    struct iovec *iov = (struct iovec *)malloc(sizeof(struct iovec) * numRecords);
143
144    // Write the command, then the image header and data.
145    // Command
146    iov[0].iov_base = command;
147    iov[0].iov_len = strlen(command);
148    // Header of image data
149    iov[1].iov_base = header;
150    iov[1].iov_len = headerLength;
151
152    // Image data
153    size_t bytesPerRow = width * 3;
154    size_t rowLength = width * 3;
155    unsigned char *srcRowPtr = const_cast<unsigned char *>(data);
156    for (int y = height + 1; y >= 2; y--) {
157        iov[y].iov_base = srcRowPtr;
158        iov[y].iov_len = rowLength;
159        srcRowPtr += bytesPerRow;
160    }
161    if (writev(fd, iov, numRecords) < 0) {
162        ERROR("write failed: %s\n", strerror(errno));
163    }
164    free(iov);
165
166    TRACE("Leaving (%dx%d)\n", width, height);
167}
168
169#endif /*USE_THREADS*/
170
171bool
172nv::writePPMFile(const char *path,
173                 const unsigned char *data, int width, int height)
174{
175    // Open the named file for writing.
176    int fd = creat(path, 0600);
177    if (fd < 0) {
178        ERROR("Can't open temporary image file \"%s\": %s", path, strerror(errno));
179        return false;
180    }
181
182    // Generate the PPM binary file header
183    char header[200];
184    snprintf(header, sizeof(header), "P6 %d %d %d\n", width, height,
185             PPM_MAXVAL);
186
187    size_t headerLength = strlen(header);
188
189    size_t numRecords = height + 1;
190    struct iovec *iov = (struct iovec *)malloc(sizeof(struct iovec) * numRecords);
191
192    // Add the PPM image header.
193    iov[0].iov_base = header;
194    iov[0].iov_len = headerLength;
195
196    // Now add the image data, reversing the order of the rows.  PPM is top-to-bottom.
197    //size_t wordsPerRow = (width * 24 + 31) / 32;
198    //size_t bytesPerRow = wordsPerRow * 4;
199    size_t bytesPerRow = width * 3;
200    size_t rowLength = width * 3;
201    unsigned char *srcRowPtr = const_cast<unsigned char *>(data);
202    for (int y = height; y >= 1; y--) {
203        iov[y].iov_base = srcRowPtr;
204        iov[y].iov_len = rowLength;
205        srcRowPtr += bytesPerRow;
206    }
207    bool ret = true;
208    if (writev(fd, iov, numRecords) < 0) {
209        ERROR("writing image to \"%s\" failed: %s\n", path, strerror(errno));
210        ret = false;
211    }
212    close(fd);
213    free(iov);
214    return ret;
215}
Note: See TracBrowser for help on using the repository browser.