mxwcore-wotlk/deps/g3dlite/source/GImage_ppm.cpp

218 lines
6.1 KiB
C++

/**
@file GImage_ppm.cpp
@author Morgan McGuire, http://graphics.cs.williams.edu
@created 2002-05-27
@edited 2006-05-10
*/
#include "G3D/platform.h"
#include "G3D/GImage.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#include "G3D/TextInput.h"
#include "G3D/TextOutput.h"
#include "G3D/Log.h"
namespace G3D {
void GImage::encodePPMASCII(
BinaryOutput& out) const {
TextOutput::Settings ppmOptions;
ppmOptions.convertNewlines = false;
ppmOptions.numColumns = 70;
ppmOptions.wordWrap = TextOutput::Settings::WRAP_WITHOUT_BREAKING;
TextOutput ppm(ppmOptions);
switch (m_channels) {
case 1:
{
ppm.printf("P2\n%d %d\n255\n", m_width, m_height);
const Color1uint8* c = this->pixel1();
// Insert newlines every 70 characters max
for (uint32 i = 0; i < (uint32)(m_width * m_height); ++i) {
ppm.printf("%d%c", c[i].value, (i % (70/4) == 0) ? '\n' : ' ');
}
}
break;
case 3:
{
ppm.printf("P3\n%d %d\n255\n", m_width, m_height);
const Color3uint8* c = this->pixel3();
// Insert newlines every 70 characters max
for (uint32 i = 0; i < (uint32)(m_width * m_height); ++i) {
ppm.printf("%d %d %d%c", c[i].r, c[i].g, c[i].b,
(i % (70/12) == 0) ?
'\n' : ' ');
}
}
break;
default:
alwaysAssertM(false, "PPM requires either 1 or 3 channels exactly.");
}
const std::string& s = ppm.commitString();
out.writeBytes(s.c_str(), s.length());
}
void GImage::encodePPM(
BinaryOutput& out) const {
// http://netpbm.sourceforge.net/doc/ppm.html
if (m_channels == 3) {
std::string header = format("P6 %d %d 255 ", m_width, m_height);
out.writeBytes(header.c_str(), header.size());
out.writeBytes(this->pixel3(), m_width * m_height * 3);
} else if (m_channels == 1) {
std::string header = format("P5 %d %d 255 ", m_width, m_height);
out.writeBytes(header.c_str(), header.size());
out.writeBytes(this->pixel1(), m_width * m_height);
} else {
alwaysAssertM(false, "PPM requires either 1 or 3 channels exactly.");
}
}
void GImage::decodePPMASCII(
BinaryInput& input) {
int ppmWidth;
int ppmHeight;
double maxColor;
// Create a TextInput object to parse ascii format
// Mixed binary/ascii formats will require more
const std::string inputStr = input.readString();
TextInput::Settings ppmOptions;
ppmOptions.cppLineComments = false;
ppmOptions.otherCommentCharacter = '#';
ppmOptions.signedNumbers = true;
ppmOptions.singleQuotedStrings = false;
TextInput ppmInput(TextInput::FROM_STRING, inputStr, ppmOptions);
//Skip first line in header P#
std::string ppmType = ppmInput.readSymbol();
ppmWidth = (int)ppmInput.readNumber();
ppmHeight = (int)ppmInput.readNumber();
// Everything but a PBM will have a max color value
if (ppmType != "P2") {
maxColor = ppmInput.readNumber();
} else {
maxColor = 255;
}
if ((ppmWidth < 0) ||
(ppmHeight < 0) ||
(maxColor <= 0)) {
throw GImage::Error("Invalid PPM Header.", input.getFilename());
}
// I don't think it's proper to scale values less than 255
if (maxColor <= 255.0) {
maxColor = 255.0;
}
m_width = ppmWidth;
m_height = ppmHeight;
m_channels = 3;
// always scale down to 1 byte per channel
m_byte = (uint8*)m_memMan->alloc(m_width * m_height * 3);
// Read in the image data. I am not validating if the values match the maxColor
// requirements. I only scale if needed to fit within the byte available.
for (uint32 i = 0; i < (uint32)(m_width * m_height); ++i) {
// read in color and scale to max pixel defined in header
// A max color less than 255 might need to be left alone and not scaled.
Color3uint8& curPixel = *(pixel3() + i);
if (ppmType == "P3") {
curPixel.r = (uint8)(ppmInput.readNumber() * (255.0 / maxColor));
curPixel.g = (uint8)(ppmInput.readNumber() * (255.0 / maxColor));
curPixel.b = (uint8)(ppmInput.readNumber() * (255.0 / maxColor));
} else if (ppmType == "P2") {
uint8 pixel = (uint8)(ppmInput.readNumber() * (255.0 / maxColor));
curPixel.r = pixel;
curPixel.g = pixel;
curPixel.b = pixel;
} else if (ppmType == "P1") {
int pixel = (uint8)(ppmInput.readNumber() * maxColor);
curPixel.r = pixel;
curPixel.g = pixel;
curPixel.b = pixel;
}
}
}
/** Consumes whitespace up to and including a number, but not the following character */
static int scanUInt(BinaryInput& input) {
char c = input.readUInt8();
while (isWhiteSpace(c)) {
c = input.readUInt8();
}
std::string s;
s += c;
c = input.readUInt8();
while (!isWhiteSpace(c)) {
s += c;
c = input.readUInt8();
}
// Back up one to avoid consuming the last character
input.setPosition(input.getPosition() - 1);
int x;
sscanf(s.c_str(), "%d", &x);
return x;
}
void GImage::decodePPM(
BinaryInput& input) {
char head[2];
int w, h;
input.readBytes(head, 2);
if (head[0] != 'P' || ((head[1] != '6') && (head[1] != '5'))) {
throw GImage::Error("Invalid PPM Header.", input.getFilename());
}
w = scanUInt(input);
h = scanUInt(input);
// Skip the max color specifier
scanUInt(input);
if ((w < 0) ||
(h < 0) ||
(w > 100000) ||
(h > 100000)) {
throw GImage::Error("Invalid PPM size in header.", input.getFilename());
}
// Trailing whitespace
input.readUInt8();
if (head[1] == '6') {
// 3 channel
resize(w, h, 3);
input.readBytes(m_byte, m_width * m_height * 3);
} else if (head[1] == '5') {
// 1 channel
resize(w, h, 1);
input.readBytes(m_byte, m_width * m_height);
}
}
}