aboutsummaryrefslogtreecommitdiff
path: root/dep/g3dlite/source/GImage_ppm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dep/g3dlite/source/GImage_ppm.cpp')
-rw-r--r--dep/g3dlite/source/GImage_ppm.cpp217
1 files changed, 217 insertions, 0 deletions
diff --git a/dep/g3dlite/source/GImage_ppm.cpp b/dep/g3dlite/source/GImage_ppm.cpp
new file mode 100644
index 00000000000..f3065c6038b
--- /dev/null
+++ b/dep/g3dlite/source/GImage_ppm.cpp
@@ -0,0 +1,217 @@
+/**
+ @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);
+ }
+}
+
+}