X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=image%2Fimage_pnm.cpp;h=0473ac64025150269beac1001718518ba9867d86;hb=8ac914ee3885ead89dd3974a9d73b1960470baa9;hp=f9cd05d1fbc7593dafcb6e5bf77d48beb7505a1d;hpb=9db16b3989481f8d6dfc8932d760fcc16217ecbd;p=apitrace diff --git a/image/image_pnm.cpp b/image/image_pnm.cpp index f9cd05d..0473ac6 100644 --- a/image/image_pnm.cpp +++ b/image/image_pnm.cpp @@ -30,6 +30,8 @@ #include #include +#include + #include "image.hpp" @@ -38,95 +40,204 @@ namespace image { /** * http://en.wikipedia.org/wiki/Netpbm_format * http://netpbm.sourceforge.net/doc/ppm.html + * http://netpbm.sourceforge.net/doc/pfm.html */ void -Image::writePNM(std::ostream &os, const char *comment) const { - assert(channels == 1 || channels >= 3); +Image::writePNM(std::ostream &os, const char *comment) const +{ + const char *identifier; + unsigned outChannels; + + switch (channelType) { + case TYPE_UNORM8: + if (channels == 1) { + identifier = "P5"; + outChannels = 1; + } else { + identifier = "P6"; + outChannels = 3; + } + break; + case TYPE_FLOAT: + if (channels == 1) { + identifier = "Pf"; + outChannels = 1; + } else { + identifier = "PF"; + outChannels = 3; + } + break; + default: + assert(0); + } + + os << identifier << "\n"; - os << (channels == 1 ? "P5" : "P6") << "\n"; if (comment) { os << "#" << comment << "\n"; } os << width << " " << height << "\n"; - os << "255" << "\n"; + + if (channelType == TYPE_UNORM8) { + os << "255" << "\n"; + } else { + os << "1" << "\n"; + } const unsigned char *row; - if (channels == 1 || channels == 3) { + if (channels == outChannels) { + /* + * Write whole pixel spans straight from the image buffer. + */ + for (row = start(); row != end(); row += stride()) { - os.write((const char *)row, width*channels); + os.write((const char *)row, width*bytesPerPixel); } } else { - unsigned char *tmp = new unsigned char[width*3]; - if (channels == 4) { - for (row = start(); row != end(); row += stride()) { - const uint32_t *src = (const uint32_t *)row; - uint32_t *dst = (uint32_t *)tmp; - unsigned x; - for (x = 0; x + 4 <= width; x += 4) { - /* - * It's much faster to access dwords than bytes. - * - * FIXME: Big-endian version. - */ - - uint32_t rgba0 = *src++ & 0xffffff; - uint32_t rgba1 = *src++ & 0xffffff; - uint32_t rgba2 = *src++ & 0xffffff; - uint32_t rgba3 = *src++ & 0xffffff; - uint32_t rgb0 = rgba0 - | (rgba1 << 24); - uint32_t rgb1 = (rgba1 >> 8) - | (rgba2 << 16); - uint32_t rgb2 = (rgba2 >> 16) - | (rgba3 << 8); - *dst++ = rgb0; - *dst++ = rgb1; - *dst++ = rgb2; + /* + * Need to add/remove channels, one pixel at a time. + */ + + unsigned char *tmp = new unsigned char[width*outChannels*bytesPerChannel]; + + if (channelType == TYPE_UNORM8) { + /* + * Optimized path for 8bit unorms. + */ + + if (channels == 4) { + for (row = start(); row != end(); row += stride()) { + const uint32_t *src = (const uint32_t *)row; + uint32_t *dst = (uint32_t *)tmp; + unsigned x; + for (x = 0; x + 4 <= width; x += 4) { + /* + * It's much faster to access dwords than bytes. + * + * FIXME: Big-endian version. + */ + + uint32_t rgba0 = *src++ & 0xffffff; + uint32_t rgba1 = *src++ & 0xffffff; + uint32_t rgba2 = *src++ & 0xffffff; + uint32_t rgba3 = *src++ & 0xffffff; + uint32_t rgb0 = rgba0 + | (rgba1 << 24); + uint32_t rgb1 = (rgba1 >> 8) + | (rgba2 << 16); + uint32_t rgb2 = (rgba2 >> 16) + | (rgba3 << 8); + *dst++ = rgb0; + *dst++ = rgb1; + *dst++ = rgb2; + } + for (; x < width; ++x) { + tmp[x*3 + 0] = row[x*4 + 0]; + tmp[x*3 + 1] = row[x*4 + 1]; + tmp[x*3 + 2] = row[x*4 + 2]; + } + os.write((const char *)tmp, width*3); } - for (; x < width; ++x) { - tmp[x*3 + 0] = row[x*4 + 0]; - tmp[x*3 + 1] = row[x*4 + 1]; - tmp[x*3 + 2] = row[x*4 + 2]; + } else if (channels == 2) { + for (row = start(); row != end(); row += stride()) { + const unsigned char *src = row; + unsigned char *dst = tmp; + for (unsigned x = 0; x < width; ++x) { + *dst++ = *src++; + *dst++ = *src++; + *dst++ = 0; + } + os.write((const char *)tmp, width*3); } - os.write((const char *)tmp, width*3); + } else { + assert(0); } - } else if (channels == 2) { + } else { + /* + * General path for float images. + */ + + unsigned copyChannels = std::min(channels, outChannels); + + assert(channelType == TYPE_FLOAT); + for (row = start(); row != end(); row += stride()) { - const unsigned char *src = row; - unsigned char *dst = tmp; + const float *src = (const float *)row; + float *dst = (float *)tmp; for (unsigned x = 0; x < width; ++x) { - *dst++ = *src++; - *dst++ = *src++; - *dst++ = 0; + unsigned channel = 0; + for (; channel < copyChannels; ++channel) { + *dst++ = *src++; + } + for (; channel < outChannels; ++channel) { + *dst++ = 0; + } } - os.write((const char *)tmp, width*3); + os.write((const char *)tmp, width*outChannels*bytesPerChannel); } - } else { - assert(0); } + delete [] tmp; } } + +bool +Image::writePNM(const char *filename, const char *comment) const +{ + std::ofstream os(filename, std::ofstream::binary); + if (!os) { + return false; + } + writePNM(os, comment); + return true; +} + + +/** + * 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; + default: + return NULL; + } // advance past channel line nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1; @@ -142,10 +253,10 @@ readPNMHeader(const char *buffer, size_t bufferSize, unsigned *channels, unsigne } // 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 @@ -153,11 +264,38 @@ readPNMHeader(const char *buffer, size_t bufferSize, unsigned *channels, unsigne 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 */