/** * GraphLCD plugin for the Video Disk Recorder * * tuxbox.c - tuxbox logo class * * (c) 2004 Andreas Brachold <vdr04 AT deltab de> **/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; * * if not, write to the Free Software Foundation, Inc., * * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * ***************************************************************************/ #include <stdio.h> #include <string.h> #include <netinet/in.h> #include <string> #include <glcdgraphics/bitmap.h> #include <glcdgraphics/image.h> #include "tuxbox.h" #pragma pack(1) struct ani_header { unsigned char magic[4]; // = "LCDA" unsigned short format; // Format unsigned short width; // Breite unsigned short height; // H�he unsigned short count; // Anzahl Einzelbilder unsigned long delay; // �s zwischen Einzelbildern }; #pragma pack() cTuxBoxFile::cTuxBoxFile() { } cTuxBoxFile::~cTuxBoxFile() { } bool cTuxBoxFile::Load(GLCD::cImage & image, const std::string & fileName) { bool ret = false; FILE * fIN; long fileLen; struct ani_header header; bool bInvert = false; fIN = fopen(fileName.c_str(), "rb"); if (fIN) { // get len of file if (fseek(fIN, 0, SEEK_END)) { fclose(fIN); return false; } fileLen = ftell(fIN); // rewind and get Header if (fseek(fIN, 0, SEEK_SET)) { fclose(fIN); return false; } // Read header if (fread(&header, sizeof(header), 1, fIN) != 1) { fclose(fIN); return false; } image.Clear(); image.SetWidth(ntohs(header.width)); image.SetHeight(ntohs(header.height)); image.SetDelay(ntohl(header.delay) / 1000); // check Header if (strncmp((const char*)header.magic, "LCDA", sizeof(header.magic)) || !image.Width() || !image.Height() || ntohs(header.format) != 0) { fprintf(stderr, "ERROR: load %s failed, wrong header.\n", fileName.c_str()); fclose(fIN); return false; } //fprintf(stderr,"%d %dx%d (%d %d) %d\n",ntohs(header.count),image.Width(),image.Height(),fileLen, ( (ntohs(header.count) * (image.Width() * ((image.Height() + 7) / 8))) + sizeof(header)),lhdr.delay); // check file length if (!ntohs(header.count) || (fileLen != (long) ( (ntohs(header.count) * (image.Width() * ((image.Height() + 7) / 8))) + sizeof(header)))) { fprintf(stderr, "ERROR: load %s failed, wrong size.\n", fileName.c_str()); fclose(fIN); return false; } // Set minimal limit for next image if (image.Delay() < 10) image.SetDelay(10); for (unsigned int n=0;n<ntohs(header.count);++n) { ret = false; unsigned int nBmpSize = image.Height() * ((image.Width() + 7) / 8); unsigned char *bitmap = new unsigned char[nBmpSize]; if (!bitmap) { fprintf(stderr, "ERROR: malloc failed."); break; } unsigned int nAniSize = image.Width() * ((image.Height() + 7) / 8); unsigned char *pAni = new unsigned char[nAniSize]; if (!pAni) { delete[] bitmap; fprintf(stderr, "ERROR: malloc failed."); break; } if (1 != fread(pAni, nAniSize, 1, fIN)) { fprintf(stderr,"ERROR: Cannot read filedata: %s\n", fileName.c_str()); delete[] bitmap; delete[] pAni; break; } vert2horz(pAni,bitmap, image.Width(), image.Height()); delete[] pAni; if (bInvert) for (unsigned int i=0;i<nBmpSize;++i) bitmap[i] ^= 0xFF; image.AddBitmap(new GLCD::cBitmap(image.Width(), image.Height(), bitmap)); ret = true; } fclose(fIN); if (!ret) image.Clear(); } return ret; } bool cTuxBoxFile::Save(GLCD::cImage & image, const std::string & fileName) { FILE * fOut; struct ani_header header; bool bRet = false; if (image.Count() > 0 && image.Width() && image.Height()) { memcpy(header.magic, "LCDA", 4); header.format = htons(0); header.width = htons(image.Width()); header.height = htons(image.Height()); header.count = htons(image.Count()); header.delay = htonl(image.Delay() * 1000); if (image.Width() != 120 || image.Height() != 64) { fprintf(stderr,"WARNING: Maybe wrong image dimension (for all I know is 120x64 wanted) %s\n", fileName.c_str()); } fOut = fopen(fileName.c_str(), "wb"); if (!fOut) { fprintf(stderr,"ERROR: Cannot create file: %s\n", fileName.c_str()); return false; } if (1 != fwrite(&header, sizeof(header), 1, fOut)) { fprintf(stderr,"ERROR: Cannot write fileheader: %s\n", fileName.c_str()); fclose(fOut); return false; } for (unsigned int n = 0; n < image.Count(); n++) { bRet = false; unsigned int nAniSize = image.Width() * ((image.Height() + 7) / 8); unsigned char *pAni = new unsigned char[nAniSize]; if (!pAni) { fprintf(stderr, "ERROR: malloc failed."); break; } horz2vert(image.GetBitmap(n)->Data(), pAni, image.Width(), image.Height()); if (1 != fwrite(pAni, nAniSize, 1, fOut)) { delete [] pAni; fprintf(stderr,"ERROR: Cannot write filedata: %s\n", fileName.c_str()); break; } delete [] pAni; bRet = true; } fclose(fOut); } return bRet; } /** Translate memory alignment from vertical to horizontal rotate from {Byte} to {Byte} {o}[o][o][o][o][o][o][o] => { oooooooo } {o}[o][o][o][o][o][o][o] => [ oooooooo ] {o}[o][o][o][o][o][o][o] => [ oooooooo ] {o}[o][o][o][o][o][o][o] => [ oooooooo ] {o}[o][o][o][o][o][o][o] => [ oooooooo ] {o}[o][o][o][o][o][o][o] => [ oooooooo ] {o}[o][o][o][o][o][o][o] => [ oooooooo ] {o}[o][o][o][o][o][o][o] => [ oooooooo ]*/ void cTuxBoxFile::vert2horz(const unsigned char* source, unsigned char* dest, int width, int height) { int x, y, off; memset(dest,0,height*((width+7)/8)); for (y=0; y<height; ++y) { for (x=0; x<width; ++x) { off = x + ((y/8) * width); if (source[off] & (0x1 << (y % 8))) { off = (x / 8) + (y * ((width+7)/8)); dest[off] |= (unsigned char)(0x80 >> (x % 8)); } } } } /** Translate memory alignment from horizontal to vertical (rotate byte) rotate from {Byte} to {Byte} { oooooooo } => {o}[o][o][o][o][o][o][o] [ oooooooo ] => {o}[o][o][o][o][o][o][o] [ oooooooo ] => {o}[o][o][o][o][o][o][o] [ oooooooo ] => {o}[o][o][o][o][o][o][o] [ oooooooo ] => {o}[o][o][o][o][o][o][o] [ oooooooo ] => {o}[o][o][o][o][o][o][o] [ oooooooo ] => {o}[o][o][o][o][o][o][o] [ oooooooo ] => {o}[o][o][o][o][o][o][o]*/ void cTuxBoxFile::horz2vert(const unsigned char* source, unsigned char* dest, int width, int height) { int x, y, off; memset(dest,0,width*((height+7)/8)); for (y=0; y<height; ++y) { for (x=0; x<width; ++x) { off = (x / 8) + ((y) * ((width+7)/8)); if (source[off] & (0x80 >> (x % 8))) { off = x + ((y/8) * width); dest[off] |= (unsigned char)(0x1 << (y % 8)); } } } }