if (channels == 1) {
identifier = "Pf";
outChannels = 1;
- } else {
+ } else if (channels <= 3) {
identifier = "PF";
- outChannels = 3;
+ outChannels = 4;
+ } else {
+ // Non-standard extension for 4 floats
+ identifier = "PX";
+ outChannels = 4;
}
break;
+ default:
+ assert(0);
}
os << identifier << "\n";
if (channelType == TYPE_UNORM8) {
os << "255" << "\n";
+ } else {
+ os << "1" << "\n";
}
const unsigned char *row;
* Need to add/remove channels, one pixel at a time.
*/
- unsigned char *tmp = new unsigned char[width*bytesPerPixel];
+ unsigned char *tmp = new unsigned char[width*outChannels*bytesPerChannel];
if (channelType == TYPE_UNORM8) {
/*
* General path for float images.
*/
+ unsigned copyChannels = std::min(channels, outChannels);
+
assert(channelType == TYPE_FLOAT);
for (row = start(); row != end(); row += stride()) {
float *dst = (float *)tmp;
for (unsigned x = 0; x < width; ++x) {
unsigned channel = 0;
- for (; channel < channels; ++channel) {
+ for (; channel < copyChannels; ++channel) {
*dst++ = *src++;
}
- for (; channel < channels; ++channel) {
+ for (; channel < outChannels; ++channel) {
*dst++ = 0;
}
}
- os.write((const char *)tmp, width*bytesPerPixel);
+ os.write((const char *)tmp, width*outChannels*bytesPerChannel);
}
}
}
+/**
+ * Parse PNM header.
+ *
+ * Returns pointer to data start, or NULL on failure.
+ */
const char *
-readPNMHeader(const char *buffer, size_t bufferSize, unsigned *channels, unsigned *width, unsigned *height)
+readPNMHeader(const char *buffer, size_t bufferSize, PNMInfo &info)
{
- *channels = 0;
- *width = 0;
- *height = 0;
+ info.channels = 0;
+ info.width = 0;
+ info.height = 0;
const char *currentBuffer = buffer;
const char *nextBuffer;
// parse number of channels
- int scannedChannels = sscanf(currentBuffer, "P%d\n", channels);
+ char c;
+ int scannedChannels = sscanf(currentBuffer, "P%c\n", &c);
if (scannedChannels != 1) { // validate scanning of channels
// invalid channel line
- return buffer;
+ return NULL;
}
// convert channel token to number of channels
- *channels = (*channels == 5) ? 1 : 3;
+ switch (c) {
+ case '5':
+ info.channels = 1;
+ info.channelType = TYPE_UNORM8;
+ break;
+ case '6':
+ info.channels = 3;
+ info.channelType = TYPE_UNORM8;
+ break;
+ case 'f':
+ info.channels = 1;
+ info.channelType = TYPE_FLOAT;
+ break;
+ case 'F':
+ info.channels = 3;
+ info.channelType = TYPE_FLOAT;
+ break;
+ case 'X':
+ info.channels = 4;
+ info.channelType = TYPE_FLOAT;
+ break;
+ default:
+ return NULL;
+ }
// advance past channel line
nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1;
}
// parse dimensions of image
- int scannedDimensions = sscanf(currentBuffer, "%d %d\n", width, height);
+ int scannedDimensions = sscanf(currentBuffer, "%u %u\n", &info.width, &info.height);
if (scannedDimensions != 2) { // validate scanning of dimensions
// invalid dimension line
- return buffer;
+ return NULL;
}
// advance past dimension line
bufferSize -= nextBuffer - currentBuffer;
currentBuffer = nextBuffer;
- // skip over "255\n" at end of header
+ // skip scale factor / endianness line
nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1;
// return start of image data
return nextBuffer;
}
+
+Image *
+readPNM(const char *buffer, size_t bufferSize)
+{
+ PNMInfo info;
+
+ const char *headerEnd = readPNMHeader(buffer, bufferSize, info);
+
+ // if invalid PNM header was encountered, ...
+ if (headerEnd == NULL) {
+ std::cerr << "error: invalid PNM header";
+ return NULL;
+ }
+
+ Image *image = new Image(info.width, info.height, info.channels, false, info.channelType);
+
+ size_t rowBytes = info.width * image->bytesPerPixel;
+ for (unsigned char *row = image->start(); row != image->end(); row += image->stride()) {
+ memcpy(row, headerEnd, rowBytes);
+ headerEnd += rowBytes;
+ }
+
+ return image;
+}
+
+
+
} /* namespace image */