#include #include #include #include #include #include #include #include "Format.h" #include "graphics/Graphics.h" std::string format::UnixtimeToDate(time_t unixtime, std::string dateFormat) { struct tm * timeData; char buffer[128]; timeData = localtime(&unixtime); strftime(buffer, 128, dateFormat.c_str(), timeData); return std::string(buffer); } std::string format::UnixtimeToDateMini(time_t unixtime) { time_t currentTime = time(NULL); struct tm currentTimeData = *localtime(¤tTime); struct tm timeData = *localtime(&unixtime); if(currentTimeData.tm_year != timeData.tm_year) { return UnixtimeToDate(unixtime, "%b %Y"); } else if(currentTimeData.tm_mon != timeData.tm_mon || currentTimeData.tm_mday != timeData.tm_mday) { return UnixtimeToDate(unixtime, "%d %B"); } else { return UnixtimeToDate(unixtime, "%H:%M:%S"); } } std::vector format::VideoBufferToPPM(const VideoBuffer & vidBuf) { std::vector data; char buffer[256]; sprintf(buffer, "P6\n%d %d\n255\n", vidBuf.Width, vidBuf.Height); data.insert(data.end(), buffer, buffer+strlen(buffer)); 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]); } data.insert(data.end(), currentRow, currentRow+(vidBuf.Width*3)); } delete currentRow; return data; } 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 format::VideoBufferToPNG(const VideoBuffer & vidBuf) { std::vector 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::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::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 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; }