summaryrefslogtreecommitdiff
path: root/src/Format.cpp
diff options
context:
space:
mode:
authorSimon Robertshaw <simon@hardwired.org.uk>2012-08-11 19:24:48 (GMT)
committer Simon Robertshaw <simon@hardwired.org.uk>2012-08-11 19:24:48 (GMT)
commit08b4e5553aa10df74c4fdb0ba519fb700fa8100b (patch)
tree68cda233dcd82978c377978bb19b1fd61bbe0c70 /src/Format.cpp
parentecbb1e910352aec1cda4e2d4c36cf3599bc43963 (diff)
downloadpowder-08b4e5553aa10df74c4fdb0ba519fb700fa8100b.zip
powder-08b4e5553aa10df74c4fdb0ba519fb700fa8100b.tar.gz
Implement some missing lua functions, PNG format creation (requires zlib)
Diffstat (limited to 'src/Format.cpp')
-rw-r--r--src/Format.cpp263
1 files changed, 262 insertions, 1 deletions
diff --git a/src/Format.cpp b/src/Format.cpp
index 71a0e9e..ef0050d 100644
--- a/src/Format.cpp
+++ b/src/Format.cpp
@@ -1,7 +1,10 @@
-#include <time.h>
+#include <ctime>
#include <string>
+#include <stdexcept>
+#include <zlib.h>
#include "Format.h"
+#include "graphics/Graphics.h"
std::string format::UnixtimeToDate(time_t unixtime, std::string dateFormat)
{
@@ -33,3 +36,261 @@ std::string format::UnixtimeToDateMini(time_t unixtime)
return UnixtimeToDate(unixtime, "%H:%M:%S");
}
}
+
+struct PNGChunk
+{
+ int Length;
+ char Name[4];
+ char * Data;
+
+ //char[4] CRC();
+
+ PNGChunk(int length, std::string name)
+ {
+ if(name.length()!=4)
+ throw std::runtime_error("Invalid chunk name");
+ std::copy(name.begin(), name.begin()+4, Name);
+ Length = length;
+ if(length)
+ {
+ Data = new char[length];
+ std::fill(Data, Data+length, 0);
+ }
+ else
+ {
+ Data = NULL;
+ }
+ }
+ unsigned long CRC()
+ {
+ if(!Data)
+ {
+ return format::CalculateCRC((unsigned char*)Name, 4);
+ }
+ else
+ {
+ unsigned char * temp = new unsigned char[4+Length];
+ std::copy(Name, Name+4, temp);
+ std::copy(Data, Data+Length, temp+4);
+ unsigned long tempRet = format::CalculateCRC(temp, 4+Length);
+ delete[] temp;
+ return tempRet;
+ }
+ }
+ ~PNGChunk()
+ {
+ if(Data)
+ delete[] Data;
+ }
+};
+
+std::vector<char> format::VideoBufferToPNG(const VideoBuffer & vidBuf)
+{
+ std::vector<PNGChunk*> chunks;
+
+ //Begin IHDR (Image header) chunk (Image size and depth)
+ PNGChunk IHDRChunk = PNGChunk(13, "IHDR");
+
+ //Image Width
+ IHDRChunk.Data[0] = (vidBuf.Width>>24)&0xFF;
+ IHDRChunk.Data[1] = (vidBuf.Width>>16)&0xFF;
+ IHDRChunk.Data[2] = (vidBuf.Width>>8)&0xFF;
+ IHDRChunk.Data[3] = (vidBuf.Width)&0xFF;
+
+ //Image Height
+ IHDRChunk.Data[4] = (vidBuf.Height>>24)&0xFF;
+ IHDRChunk.Data[5] = (vidBuf.Height>>16)&0xFF;
+ IHDRChunk.Data[6] = (vidBuf.Height>>8)&0xFF;
+ IHDRChunk.Data[7] = (vidBuf.Height)&0xFF;
+
+ //Bit depth
+ IHDRChunk.Data[8] = 8; //8bits per channel or 24bpp
+
+ //Colour type
+ IHDRChunk.Data[9] = 2; //RGB triple
+
+ //Everything else is default
+ chunks.push_back(&IHDRChunk);
+
+ //Begin image data, format is 8bit RGB (24bit pixel)
+ int dataPos = 0;
+ unsigned char * uncompressedData = new unsigned char[(vidBuf.Width*vidBuf.Height*3)+vidBuf.Height];
+
+ //Byte ordering and filtering
+ unsigned char * previousRow = new unsigned char[vidBuf.Width*3];
+ std::fill(previousRow, previousRow+(vidBuf.Width*3), 0);
+ unsigned char * currentRow = new unsigned char[vidBuf.Width*3];
+ for(int y = 0; y < vidBuf.Height; y++)
+ {
+ int rowPos = 0;
+ for(int x = 0; x < vidBuf.Width; x++)
+ {
+ currentRow[rowPos++] = PIXR(vidBuf.Buffer[(y*vidBuf.Width)+x]);
+ currentRow[rowPos++] = PIXG(vidBuf.Buffer[(y*vidBuf.Width)+x]);
+ currentRow[rowPos++] = PIXB(vidBuf.Buffer[(y*vidBuf.Width)+x]);
+ }
+
+ uncompressedData[dataPos++] = 2; //Up Sub(x) filter
+ for(int b = 0; b < rowPos; b++)
+ {
+ int filteredByte = (currentRow[b]-previousRow[b])&0xFF;
+ uncompressedData[dataPos++] = filteredByte;
+ }
+
+ unsigned char * tempRow = previousRow;
+ previousRow = currentRow;
+ currentRow = tempRow;
+ }
+ delete[] currentRow;
+ delete[] previousRow;
+
+ //Compression
+ int compressedBufferSize = (vidBuf.Width*vidBuf.Height*3)*2;
+ unsigned char * compressedData = new unsigned char[compressedBufferSize];
+
+ int result;
+ z_stream zipStream;
+ zipStream.zalloc = Z_NULL;
+ zipStream.zfree = Z_NULL;
+ zipStream.opaque = Z_NULL;
+
+ result = deflateInit2(&zipStream,
+ 9, // level
+ Z_DEFLATED, // method
+ 10, // windowBits
+ 1, // memLevel
+ Z_DEFAULT_STRATEGY // strategy
+ );
+
+ if (result != Z_OK) exit(result);
+
+ zipStream.next_in = uncompressedData;
+ zipStream.avail_in = dataPos;
+
+ zipStream.next_out = compressedData;
+ zipStream.avail_out = compressedBufferSize;
+
+
+ result = deflate(&zipStream, Z_FINISH);
+ if (result != Z_STREAM_END) exit(result);
+
+ int compressedSize = compressedBufferSize-zipStream.avail_out;
+ PNGChunk IDATChunk = PNGChunk(compressedSize, "IDAT");
+ std::copy(compressedData, compressedData+compressedSize, IDATChunk.Data);
+ chunks.push_back(&IDATChunk);
+
+ deflateEnd(&zipStream);
+
+ delete[] compressedData;
+ delete[] uncompressedData;
+
+ PNGChunk IENDChunk = PNGChunk(0, "IEND");
+ chunks.push_back(&IENDChunk);
+
+ //Write chunks to output buffer
+ int finalDataSize = 8;
+ for(std::vector<PNGChunk*>::iterator iter = chunks.begin(), end = chunks.end(); iter != end; ++iter)
+ {
+ PNGChunk * cChunk = *iter;
+ finalDataSize += 4 + 4 + 4;
+ finalDataSize += cChunk->Length;
+ }
+ unsigned char * finalData = new unsigned char[finalDataSize];
+ int finalDataPos = 0;
+
+ //PNG File header
+ finalData[finalDataPos++] = 0x89;
+ finalData[finalDataPos++] = 0x50;
+ finalData[finalDataPos++] = 0x4E;
+ finalData[finalDataPos++] = 0x47;
+ finalData[finalDataPos++] = 0x0D;
+ finalData[finalDataPos++] = 0x0A;
+ finalData[finalDataPos++] = 0x1A;
+ finalData[finalDataPos++] = 0x0A;
+
+ for(std::vector<PNGChunk*>::iterator iter = chunks.begin(), end = chunks.end(); iter != end; ++iter)
+ {
+ PNGChunk * cChunk = *iter;
+
+ //Chunk length
+ finalData[finalDataPos++] = (cChunk->Length>>24)&0xFF;
+ finalData[finalDataPos++] = (cChunk->Length>>16)&0xFF;
+ finalData[finalDataPos++] = (cChunk->Length>>8)&0xFF;
+ finalData[finalDataPos++] = (cChunk->Length)&0xFF;
+
+ //Chunk name
+ std::copy(cChunk->Name, cChunk->Name+4, finalData+finalDataPos);
+ finalDataPos += 4;
+
+ //Chunk data
+ if(cChunk->Data)
+ {
+ std::copy(cChunk->Data, cChunk->Data+cChunk->Length, finalData+finalDataPos);
+ finalDataPos += cChunk->Length;
+ }
+
+ //Chunk CRC
+ unsigned long tempCRC = cChunk->CRC();
+ finalData[finalDataPos++] = (tempCRC>>24)&0xFF;
+ finalData[finalDataPos++] = (tempCRC>>16)&0xFF;
+ finalData[finalDataPos++] = (tempCRC>>8)&0xFF;
+ finalData[finalDataPos++] = (tempCRC)&0xFF;
+ }
+
+ std::vector<char> outputData(finalData, finalData+finalDataPos);
+
+ delete[] finalData;
+
+ return outputData;
+}
+
+//CRC functions, copypasta from W3 PNG spec.
+
+/* Table of CRCs of all 8-bit messages. */
+unsigned long crc_table[256];
+
+/* Flag: has the table been computed? Initially false. */
+int crc_table_computed = 0;
+
+/* Make the table for a fast CRC. */
+void make_crc_table(void)
+{
+ unsigned long c;
+ int n, k;
+
+ for (n = 0; n < 256; n++) {
+ c = (unsigned long) n;
+ for (k = 0; k < 8; k++) {
+ if (c & 1)
+ c = 0xedb88320L ^ (c >> 1);
+ else
+ c = c >> 1;
+ }
+ crc_table[n] = c;
+ }
+ crc_table_computed = 1;
+}
+
+/* Update a running CRC with the bytes buf[0..len-1]--the CRC
+ should be initialized to all 1's, and the transmitted value
+ is the 1's complement of the final running CRC (see the
+ crc() routine below)). */
+
+unsigned long update_crc(unsigned long crc, unsigned char *buf, int len)
+{
+ unsigned long c = crc;
+ int n;
+
+ if (!crc_table_computed)
+ make_crc_table();
+ for (n = 0; n < len; n++)
+ {
+ c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
+ }
+ return c;
+}
+
+unsigned long format::CalculateCRC(unsigned char * data, int len)
+{
+ return update_crc(0xffffffffL, data, len) ^ 0xffffffffL;
+} \ No newline at end of file