source: geovis/trunk/PPMWriter.cpp @ 4636

Last change on this file since 4636 was 3998, checked in by ldelgass, 10 years ago

Add prelimilary skeleton for geovis map rendering server. Not functional, not
integrated into configure, etc.

File size: 4.8 KB
Line 
1/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 * Copyright (C) 2004-2013  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
14#include "Trace.h"
15#include "PPMWriter.h"
16#ifdef USE_THREADS
17#include "ResponseQueue.h"
18#endif
19
20using namespace GeoVis;
21
22#ifdef USE_THREADS
23
24/**
25 * \brief Writes image data as PPM binary data to the client.
26 *
27 * The PPM binary format is very simple.
28 *
29 *     P6 w h 255\n
30 *     3-byte RGB pixel data.
31 *
32 * The client (using the TkImg library) will do less work to unpack this
33 * format, as opposed to BMP or PNG.
34 *
35 * Note that currently the image data has bottom to top scanlines.  This
36 * routine could be made even simpler (faster) if the image data had top
37 * to bottom scanlines.
38 *
39 * \param[in] queue Pointer to ResponseQueue to write to
40 * \param[in] cmdName Command name to send (byte length will be appended)
41 * \param[in] data Image data
42 * \param[in] width Width of image in pixels
43 * \param[in] height Height of image in pixels
44 */
45void
46GeoVis::queuePPM(ResponseQueue *queue, const char *cmdName,
47                 const unsigned char *data, int width, int height)
48{
49#define PPM_MAXVAL 255
50    char header[200];
51
52    TRACE("Entering (%dx%d)\n", width, height);
53    // Generate the PPM binary file header
54    snprintf(header, sizeof(header), "P6 %d %d %d\n", width, height,
55             PPM_MAXVAL);
56
57    size_t headerLength = strlen(header);
58    size_t dataLength = width * height * 3;
59
60    char command[200];
61    snprintf(command, sizeof(command), "%s %lu\n", cmdName,
62             (unsigned long)headerLength + dataLength);
63
64    size_t cmdLength;
65    cmdLength = strlen(command);
66
67    size_t length;
68    unsigned char *mesg;
69
70    length = headerLength + dataLength + cmdLength;
71    mesg = (unsigned char *)malloc(length);
72    if (mesg == NULL) {
73        ERROR("can't allocate %ld bytes for the image message", length);
74        return;
75    }
76    memcpy(mesg, command, cmdLength);
77    memcpy(mesg + cmdLength, header, headerLength);
78
79    size_t bytesPerRow = width * 3;
80    unsigned char *destRowPtr = mesg + length - bytesPerRow;
81    int y;
82    const unsigned char *srcRowPtr = data;
83    for (y = 0; y < height; y++) {
84        memcpy(destRowPtr, srcRowPtr, bytesPerRow);
85        srcRowPtr += bytesPerRow;
86        destRowPtr -= bytesPerRow;
87    }
88
89    Response *response;
90    if (strncmp(cmdName, "nv>legend", 9) == 0) {
91        response = new Response(Response::LEGEND);
92    } else {
93        response = new Response(Response::IMAGE);
94    }
95    response->setMessage(mesg, length, Response::DYNAMIC);
96    queue->enqueue(response);
97    TRACE("Leaving (%dx%d)\n", width, height);
98}
99#else
100
101/**
102 * \brief Writes image data as PPM binary data to the client.
103 *
104 * The PPM binary format is very simple.
105 *
106 *     P6 w h 255\n
107 *     3-byte RGB pixel data.
108 *
109 * The client (using the TkImg library) will do less work to unpack this
110 * format, as opposed to BMP or PNG.
111 *
112 * Note that currently the image data has bottom to top scanlines.  This
113 * routine could be made even simpler (faster) if the image data had top
114 * to bottom scanlines.
115 *
116 * \param[in] fd File descriptor that will be written to
117 * \param[in] cmdName Command name to send (byte length will be appended)
118 * \param[in] data Image data
119 * \param[in] width Width of image in pixels
120 * \param[in] height Height of image in pixels
121 */
122void
123GeoVis::writePPM(int fd, const char *cmdName,
124                 const unsigned char *data, int width, int height)
125{
126#define PPM_MAXVAL 255
127    char header[200];
128
129    TRACE("Entering (%dx%d)\n", width, height);
130    // Generate the PPM binary file header
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 nRecs = height + 2;
142
143    struct iovec *iov;
144    iov = (struct iovec *)malloc(sizeof(struct iovec) * nRecs);
145
146    // Write the command, then the image header and data.
147    // Command
148    iov[0].iov_base = command;
149    iov[0].iov_len = strlen(command);
150    // Header of image data
151    iov[1].iov_base = header;
152    iov[1].iov_len = headerLength;
153
154    // Image data
155    size_t bytesPerRow = width * 3;
156    int y;
157    unsigned char *srcRowPtr = const_cast<unsigned char *>(data);
158    for (y = height + 1; y >= 2; y--) {
159        iov[y].iov_base = srcRowPtr;
160        iov[y].iov_len = bytesPerRow;
161        srcRowPtr += bytesPerRow;
162    }
163    if (writev(fd, iov, nRecs) < 0) {
164        ERROR("write failed: %s\n", strerror(errno));
165    }
166    free(iov);
167
168    TRACE("Leaving (%dx%d)\n", width, height);
169}
170
171#endif /*USE_THREADS*/
Note: See TracBrowser for help on using the repository browser.