summaryrefslogtreecommitdiff
path: root/tools/convpic
diff options
context:
space:
mode:
authorandreas 'randy' weinberger <vdr@smue.org>2010-02-21 19:58:27 +0100
committerandreas 'randy' weinberger <vdr@smue.org>2010-02-21 19:58:27 +0100
commit10ab31fa86dbf9875b5f6baa6ac59fefaaf86be3 (patch)
tree60ad7c856565f03e145b2996d1bb5f9cd64c0532 /tools/convpic
downloadgraphlcd-base-10ab31fa86dbf9875b5f6baa6ac59fefaaf86be3.tar.gz
graphlcd-base-10ab31fa86dbf9875b5f6baa6ac59fefaaf86be3.tar.bz2
initial git upload, based on graphlcd-base-0.1.5
Diffstat (limited to 'tools/convpic')
-rw-r--r--tools/convpic/Makefile46
-rw-r--r--tools/convpic/bmp.c375
-rw-r--r--tools/convpic/bmp.h45
-rwxr-xr-xtools/convpic/c_bmp2glcd10
-rwxr-xr-xtools/convpic/c_tif2glcd10
-rwxr-xr-xtools/convpic/convall10
-rw-r--r--tools/convpic/convpic.c279
-rw-r--r--tools/convpic/formats.txt32
-rw-r--r--tools/convpic/tiff.c201
-rw-r--r--tools/convpic/tiff.h42
-rw-r--r--tools/convpic/tuxbox.c280
-rw-r--r--tools/convpic/tuxbox.h44
12 files changed, 1374 insertions, 0 deletions
diff --git a/tools/convpic/Makefile b/tools/convpic/Makefile
new file mode 100644
index 0000000..91aaed4
--- /dev/null
+++ b/tools/convpic/Makefile
@@ -0,0 +1,46 @@
+#
+# Makefile for the GraphLCD tool convpic
+#
+
+-include ../../Make.config
+
+PRGNAME = convpic
+
+OBJS = $(PRGNAME).o bmp.o tiff.o tuxbox.o
+
+INCLUDES += -I../../
+LIBDIRS += -L../../glcdgraphics/
+
+
+all: $(PRGNAME)
+.PHONY: all
+
+# Implicit rules:
+
+%.o: %.c
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
+
+# Dependencies:
+
+MAKEDEP = $(CXX) -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): Makefile
+ @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
+
+-include $(DEPFILE)
+
+# The main program:
+
+$(PRGNAME): $(OBJS)
+ $(CXX) $(CXXFLAGS) -rdynamic $(OBJS) $(LIBS) $(LIBDIRS) -lglcdgraphics -lstdc++ -o $(PRGNAME)
+
+install: $(PRGNAME)
+ install -d $(BINDIR)
+ install -m 755 -o root -g root -s $(PRGNAME) $(BINDIR)
+
+uninstall:
+ rm -f $(BINDIR)/$(PRGNAME)
+
+clean:
+ @-rm -f $(OBJS) $(DEPFILE) $(PRGNAME) *~
+
diff --git a/tools/convpic/bmp.c b/tools/convpic/bmp.c
new file mode 100644
index 0000000..2316b6c
--- /dev/null
+++ b/tools/convpic/bmp.c
@@ -0,0 +1,375 @@
+/**
+ * GraphLCD plugin for the Video Disk Recorder
+ *
+ * bmp.c - bmp logo class
+ *
+ * (C) 2004 Andreas Brachold <vdr04 AT deltab de>
+ * (C) 2001-2004 Carsten Siebholz <c.siebholz AT t-online 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 <stdint.h>
+#include <string.h>
+
+#include <string>
+
+#include <glcdgraphics/bitmap.h>
+#include <glcdgraphics/image.h>
+
+#include "bmp.h"
+
+
+#pragma pack(1)
+typedef struct BMPH {
+ uint16_t bmpIdentifier;
+ uint32_t bmpFileSize;
+ uint32_t bmpReserved;
+ uint32_t bmpBitmapDataOffset;
+ uint32_t bmpBitmapHeaderSize;
+ uint32_t bmpWidth;
+ uint32_t bmpHeight;
+ uint16_t bmpPlanes;
+ uint16_t bmpBitsPerPixel;
+ uint32_t bmpCompression;
+ uint32_t bmpBitmapDataSize;
+ uint32_t bmpHResolution;
+ uint32_t bmpVResolution;
+ uint32_t bmpColors;
+ uint32_t bmpImportantColors;
+} BMPHEADER; // 54 bytes
+
+typedef struct RGBQ {
+ uint8_t rgbBlue;
+ uint8_t rgbGreen;
+ uint8_t rgbRed;
+ uint8_t rgbReserved;
+} RGBQUAD; // 4 bytes
+#pragma pack()
+
+
+uint8_t bitmask[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
+uint8_t bitmaskl[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff};
+uint8_t bitmaskr[8] = {0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01};
+
+cBMPFile::cBMPFile()
+{
+}
+
+cBMPFile::~cBMPFile()
+{
+}
+
+bool cBMPFile::Load(GLCD::cImage & image, const std::string & fileName)
+{
+ FILE *fIN;
+ BMPHEADER bmpHeader;
+ RGBQUAD *pPalette;
+ char *pByte;
+ char Dummy;
+ long iNumColors;
+ long iSize;
+ uint32_t x, y;
+ uint16_t iRead;
+ uint8_t * bitmap = NULL;
+ bool bInvert = false;
+
+ if (fileName.length() > 0)
+ {
+ fIN = fopen(fileName.c_str(), "rb");
+ if (fIN)
+ {
+ if (fread(&bmpHeader, sizeof(BMPHEADER), 1, fIN)!=1)
+ {
+ fclose(fIN);
+ return false;
+ }
+
+ // check for Windows BMP
+ if (bmpHeader.bmpBitmapHeaderSize != 0x00000028 )
+ {
+ fprintf(stderr, "ERROR: only Windows BMP images are allowed.\n");
+ fclose(fIN);
+ return false;
+ }
+
+ // check for 2 color
+ iNumColors = (1 << bmpHeader.bmpBitsPerPixel);
+ if (iNumColors != 2)
+ {
+ fprintf(stderr, "ERROR: the image has %ld colors, but only images with 2 colors are allowed.\n", iNumColors);
+ fclose(fIN);
+ return false;
+ }
+
+ iSize = bmpHeader.bmpHeight * bmpHeader.bmpWidth;
+
+ pPalette = (RGBQUAD *) malloc( iNumColors*sizeof(RGBQUAD));
+ if (!pPalette)
+ {
+ fprintf(stderr, "ERROR: cannot allocate memory\n");
+ fclose(fIN);
+ return false;
+ }
+
+ if (fread( pPalette, iNumColors*sizeof(RGBQUAD), 1, fIN)!=1)
+ {
+ free(pPalette);
+ fclose(fIN);
+ return false;
+ }
+
+ // check colors
+ if (pPalette->rgbBlue+pPalette->rgbGreen+pPalette->rgbRed <
+ (pPalette+1)->rgbBlue+(pPalette+1)->rgbGreen+(pPalette+1)->rgbRed)
+ {
+ // index 0 represents 'black', index 1 'white'
+ bInvert = !bInvert;
+ }
+ else
+ {
+ // index 0 represents 'white', index 1 'black'
+ }
+
+ if (fseek(fIN, bmpHeader.bmpBitmapDataOffset, SEEK_SET)==EOF)
+ {
+ free(pPalette);
+ fclose(fIN);
+ return false;
+ }
+
+ switch (bmpHeader.bmpCompression)
+ {
+ case 0: // BI_RGB no compression
+ image.Clear();
+ image.SetWidth(bmpHeader.bmpWidth);
+ image.SetHeight(bmpHeader.bmpHeight);
+ image.SetDelay(100);
+ bitmap = new unsigned char[bmpHeader.bmpHeight * ((bmpHeader.bmpWidth + 7) / 8)];
+ if (!bitmap)
+ {
+ fprintf(stderr, "ERROR: cannot allocate memory\n");
+ free(pPalette);
+ fclose(fIN);
+ image.Clear();
+ return false;
+ }
+
+ for (y = bmpHeader.bmpHeight; y > 0; y--)
+ {
+ pByte = (char*)bitmap + (y-1)*((bmpHeader.bmpWidth+7)/8);
+ iRead = 0;
+ for (x = 0; x < bmpHeader.bmpWidth / 8; x++)
+ {
+ if (fread(pByte, sizeof(char), 1, fIN) != 1)
+ {
+ delete[] bitmap;
+ free(pPalette);
+ fclose(fIN);
+ image.Clear();
+ return false;
+ }
+ iRead++;
+ if (bInvert)
+ *pByte = *pByte ^ 0xff;
+ pByte++;
+ }
+
+ if (bmpHeader.bmpWidth % 8)
+ {
+ if (fread(pByte, sizeof(char), 1, fIN) != 1)
+ {
+ delete [] bitmap;
+ free(pPalette);
+ fclose(fIN);
+ image.Clear();
+ return false;
+ }
+ iRead++;
+ if (bInvert)
+ *pByte = *pByte^0xff;
+ *pByte = *pByte & bitmaskl[bmpHeader.bmpWidth%8];
+ pByte++;
+ }
+
+ // Scan line must be 4-byte-alligned
+ while (iRead % 4)
+ {
+ if (fread(&Dummy, sizeof(char), 1, fIN) != 1)
+ {
+ delete [] bitmap;
+ free(pPalette);
+ fclose(fIN);
+ image.Clear();
+ return false;
+ }
+ iRead++;
+ }
+ }
+ image.AddBitmap(new GLCD::cBitmap(bmpHeader.bmpWidth, bmpHeader.bmpHeight, bitmap));
+ break;
+ case 1: // BI_RLE4 RLE 4bit/pixel
+ case 2: // BI_RLE8 RLE 8bit/pixel
+ case 3: // BI_BITFIELDS
+ default:
+ fprintf(stderr, "ERROR: only uncompressed RGB images are allowed.\n");
+
+ free(pPalette);
+ fclose(fIN);
+ return false;
+ }
+ fclose(fIN);
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: cannot open picture %s\n", fileName.c_str());
+ }
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: no FileName given!\n");
+ }
+ return true;
+}
+
+bool cBMPFile::Save(const GLCD::cBitmap * bitmap, const std::string & fileName)
+{
+ FILE *fOut;
+ BMPHEADER bmpHeader;
+ RGBQUAD bmpColor1, bmpColor2;
+ uint32_t dBDO, dBDSx, dBDS;
+ char *pByte;
+ char Dummy = 0x00;
+ uint32_t x, y;
+ uint16_t iWrote;
+ const uint8_t * bmpdata = bitmap->Data();
+
+ if (bitmap
+ && bitmap->Width() > 0
+ && bitmap->Height() > 0)
+ {
+ memset(&bmpHeader, 0, sizeof(BMPHEADER));
+
+ dBDO = sizeof(BMPHEADER)+2*sizeof(RGBQUAD);
+ dBDSx = ((bitmap->Width() + 7) / 8 + 3) & 0xfffffffc;
+ dBDS = dBDSx * bitmap->Height();
+
+ bmpHeader.bmpIdentifier = 0x4d42; // "BM"
+ bmpHeader.bmpFileSize = dBDO + dBDS;
+ bmpHeader.bmpBitmapDataOffset = dBDO;
+ bmpHeader.bmpBitmapHeaderSize = 0x28;
+ bmpHeader.bmpWidth = bitmap->Width();
+ bmpHeader.bmpHeight = bitmap->Height();
+ bmpHeader.bmpPlanes = 0x01;
+ bmpHeader.bmpBitsPerPixel = 0x01;
+ bmpHeader.bmpCompression = 0x00;
+ bmpHeader.bmpBitmapDataSize = dBDS;
+ bmpHeader.bmpHResolution = 0xb13; // 72dpi
+ bmpHeader.bmpVResolution = 0xb13; // 72dpi
+ bmpHeader.bmpColors = 0x02;
+ bmpHeader.bmpImportantColors = 0x02;
+
+ bmpColor1.rgbBlue = 0x00;
+ bmpColor1.rgbGreen = 0x00;
+ bmpColor1.rgbRed = 0x00;
+ bmpColor1.rgbReserved = 0x00;
+ bmpColor2.rgbBlue = 0xff;
+ bmpColor2.rgbGreen = 0xff;
+ bmpColor2.rgbRed = 0xff;
+ bmpColor2.rgbReserved = 0x00;
+
+
+ fOut = fopen(fileName.c_str(), "wb");
+ if (!fOut)
+ {
+ fprintf(stderr,"Cannot create file: %s\n", fileName.c_str());
+ return false;
+ }
+ fwrite(&bmpHeader, sizeof(BMPHEADER), 1, fOut);
+ fwrite(&bmpColor1, sizeof(RGBQUAD), 1, fOut);
+ fwrite(&bmpColor2, sizeof(RGBQUAD), 1, fOut);
+
+ for (y=bitmap->Height(); y>0; y--)
+ {
+ pByte = (char*)bmpdata + (y-1)*((bitmap->Width()+7)/8);
+ iWrote = 0;
+ for (x=0; x<(uint32_t) bitmap->Width()/8; x++)
+ {
+ *pByte = *pByte^0xff;
+ if (fwrite(pByte, sizeof(char), 1, fOut)!=1)
+ {
+ fclose(fOut);
+ return false;
+ }
+ iWrote++;
+ pByte++;
+ }
+ // Scan line must be 4-byte-alligned
+ while (iWrote%4)
+ {
+ if (fwrite(&Dummy, sizeof(char), 1, fOut)!=1)
+ {
+ fclose(fOut);
+ return 3;
+ }
+ iWrote++;
+ }
+ }
+ fclose(fOut);
+ }
+ return true;
+}
+
+bool cBMPFile::Save(GLCD::cImage & image, const std::string & fileName)
+{
+ const GLCD::cBitmap * bitmap;
+
+ if (image.Count() == 1)
+ {
+ bitmap = image.GetBitmap(0);
+ if (bitmap)
+ {
+ if (!Save(bitmap, fileName))
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ uint16_t i;
+ char tmpStr[256];
+
+ for (i = 0; i < image.Count(); i++)
+ {
+ sprintf(tmpStr, "%.248s.%05d", fileName.c_str(), i);
+ bitmap = image.GetBitmap(i);
+ if (bitmap)
+ {
+ if (!Save(bitmap, tmpStr))
+ {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
diff --git a/tools/convpic/bmp.h b/tools/convpic/bmp.h
new file mode 100644
index 0000000..8419838
--- /dev/null
+++ b/tools/convpic/bmp.h
@@ -0,0 +1,45 @@
+/**
+ * GraphLCD plugin for the Video Disk Recorder
+ *
+ * bmp.h - bmp logo class
+ *
+ * (C) 2004 Andreas Brachold <vdr04 AT deltab de>
+ * (C) 2001-2004 Carsten Siebholz <c.siebholz AT t-online 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 *
+ * *
+ ***************************************************************************/
+
+#ifndef _BMP_H_
+#define _BMP_H_
+
+#include <glcdgraphics/imagefile.h>
+
+class cBMPFile : public GLCD::cImageFile
+{
+private:
+ bool Save(const GLCD::cBitmap * bitmap, const std::string & fileName);
+public:
+ cBMPFile();
+ virtual ~cBMPFile();
+ virtual bool Load(GLCD::cImage & image, const std::string & fileName);
+ virtual bool Save(GLCD::cImage & image, const std::string & fileName);
+};
+
+#endif
diff --git a/tools/convpic/c_bmp2glcd b/tools/convpic/c_bmp2glcd
new file mode 100755
index 0000000..b629ee6
--- /dev/null
+++ b/tools/convpic/c_bmp2glcd
@@ -0,0 +1,10 @@
+#!/bin/sh
+# Converts all BMP images to *.glcd
+
+old=bmp
+new=glcd
+
+for file in ./*."$old"; do
+ convpic -i "$file" -o "`basename \"$file\" \"$old\"`$new"
+done
+
diff --git a/tools/convpic/c_tif2glcd b/tools/convpic/c_tif2glcd
new file mode 100755
index 0000000..d3983b8
--- /dev/null
+++ b/tools/convpic/c_tif2glcd
@@ -0,0 +1,10 @@
+#!/bin/sh
+# Converts all TIFF images to *.glcd
+
+old=tif
+new=glcd
+
+for file in ./*."$old"; do
+ convpic -i "$file" -o "`basename \"$file\" \"$old\"`$new"
+done
+
diff --git a/tools/convpic/convall b/tools/convpic/convall
new file mode 100755
index 0000000..f0f0207
--- /dev/null
+++ b/tools/convpic/convall
@@ -0,0 +1,10 @@
+#!/bin/sh
+# Converts all images in current directory
+
+old=$1
+new=$2
+
+for file in ./*."$old"; do
+ convpic -i "$file" -o "`basename \"$file\" \"$old\"`$new"
+done
+
diff --git a/tools/convpic/convpic.c b/tools/convpic/convpic.c
new file mode 100644
index 0000000..732738c
--- /dev/null
+++ b/tools/convpic/convpic.c
@@ -0,0 +1,279 @@
+/**
+ * convpic.c - a tool to convert images to
+ * own proprietary format of the logos and pictures
+ * for graphlcd plugin
+ *
+ * (C) 2004 Andreas Brachold <vdr04 AT deltab de>
+ * (C) 2001-2003 by Carsten Siebholz <c.siebholz AT t-online.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 <getopt.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <string>
+
+#include <glcdgraphics/bitmap.h>
+#include <glcdgraphics/image.h>
+#include <glcdgraphics/imagefile.h>
+#include <glcdgraphics/glcd.h>
+#include <glcdgraphics/pbm.h>
+
+#include "bmp.h"
+#include "tiff.h"
+#include "tuxbox.h"
+
+static const char *prgname = "convpic";
+static const char *VERSION = "0.1.1";
+
+unsigned int delay = 250;
+
+
+enum ePicFormat
+{
+ pfUndefined,
+ pfTIFF,
+ pfBMP,
+ pfGLCD,
+ pfPBM,
+ pfTUXBOX
+};
+
+void usage(void);
+
+ePicFormat getFormat(const char* szFile)
+{
+ static const struct tagformats {const char* szExt; ePicFormat picformat;} formats[] =
+ {
+ {".tiff", pfTIFF },
+ {".tif", pfTIFF },
+ {".bmp", pfBMP },
+ {".glcd", pfGLCD },
+ {".pbm", pfPBM },
+ {".ani", pfTUXBOX}
+ };
+ ePicFormat pf = pfUndefined;
+
+ if (szFile)
+ {
+ for (int i = strlen(szFile) - 1; i >= 0; i--)
+ {
+ if (*(szFile+i) == '.' && strlen(szFile + i + 1))
+ {
+ for (unsigned int n = 0; n < sizeof(formats)/sizeof(*formats); n++)
+ {
+ if (!strcasecmp((szFile+i), formats[n].szExt))
+ {
+ return formats[n].picformat;
+ }
+ }
+ }
+ }
+ }
+ return pf;
+}
+
+GLCD::cImageFile * GetFileTranslator(ePicFormat Format)
+{
+ switch (Format)
+ {
+ case pfGLCD:
+ return new GLCD::cGLCDFile();
+
+ case pfPBM:
+ return new GLCD::cPBMFile();
+
+ case pfBMP:
+ return new cBMPFile();
+
+ case pfTIFF:
+ return new cTIFFFile();
+
+ case pfTUXBOX:
+ return new cTuxBoxFile();
+
+ default:
+ return NULL;
+ }
+
+}
+
+int main(int argc, char *argv[]) {
+ ePicFormat inFormat = pfUndefined;
+ ePicFormat outFormat = pfUndefined;
+ std::string inFile = "";
+ std::string outFile = "";
+ GLCD::cImage image;
+ GLCD::cImage nextImage;
+ GLCD::cImageFile * pInBitmap = NULL;
+ GLCD::cImageFile * pOutBitmap = NULL;
+ bool bError = false;
+ bool bInvert = false;
+ bool bDelay = false;
+
+
+ static struct option long_options[] =
+ {
+ {"invert", no_argument, NULL, 'n'},
+ {"infile", required_argument, NULL, 'i'},
+ {"outfile", required_argument, NULL, 'o'},
+ {"delay", required_argument, NULL, 'd'},
+ { NULL}
+ };
+
+ int c, option_index = 0;
+ while ((c=getopt_long(argc,argv,"ni:o:d:",long_options, &option_index))!=-1) {
+ switch (c) {
+ case 'n':
+ bInvert = true;
+ break;
+
+ case 'i':
+ inFile = optarg;
+ break;
+
+ case 'o':
+ outFile = optarg;
+ break;
+
+ case 'd':
+ delay = atoi(optarg);
+ bDelay = true;
+ if (delay < 10)
+ {
+ fprintf(stderr, "Warning: You have specify a to short delay, minimum are 10 ms\n");
+ delay = 10;
+ }
+ break;
+
+ default:
+ return 1;
+ }
+ }
+
+ if (inFile.length() == 0)
+ {
+ fprintf(stderr, "ERROR: You have to specify the infile (-i filename)\n");
+ bError = true;
+ }
+
+ if (pfUndefined == (inFormat = getFormat(inFile.c_str())))
+ {
+ fprintf(stderr, "ERROR: You have to specify a correct extension for the %s\n", inFile.c_str());
+ bError = true;
+ }
+
+ if (outFile.length() == 0)
+ {
+ fprintf(stderr, "ERROR: You have to specify the outfile (-o filename)\n");
+ bError = true;
+ }
+
+ if (pfUndefined == (outFormat = getFormat(outFile.c_str())))
+ {
+ fprintf(stderr, "ERROR: You have to specify a correct extension for the %s \n", outFile.c_str());
+ bError = true;
+ }
+
+ if (bError)
+ {
+ usage();
+ return 1;
+ }
+
+
+ pInBitmap = GetFileTranslator(inFormat);
+ if (!pInBitmap)
+ return 2;
+
+ pOutBitmap = GetFileTranslator(outFormat);
+ if (!pOutBitmap)
+ return 3;
+
+ // Load Picture
+ fprintf(stdout, "loading %s\n", inFile.c_str());
+ bError = !pInBitmap->Load(image, inFile);
+ if (!bError)
+ {
+ // Load more in files
+ while (optind < argc && !bError)
+ {
+ inFile = argv[optind++];
+ inFormat = getFormat(inFile.c_str());
+ if (inFormat == pfUndefined)
+ {
+ fprintf(stderr, "ERROR: You have to specify a correct extension for the %s\n", inFile.c_str());
+ bError = true;
+ break;
+ }
+ pInBitmap = GetFileTranslator(inFormat);
+ if (!pInBitmap)
+ break;
+
+ fprintf(stdout, "loading %s\n", inFile.c_str());
+ if (pInBitmap->Load(nextImage, inFile))
+ {
+ uint16_t i;
+ for (i = 0; i < nextImage.Count(); i++)
+ {
+ image.AddBitmap(new GLCD::cBitmap(*nextImage.GetBitmap(i)));
+ }
+ }
+ }
+ if (bDelay)
+ image.SetDelay(delay);
+ if (bInvert)
+ {
+ uint16_t i;
+ for (i = 0; i < image.Count(); i++)
+ {
+ image.GetBitmap(i)->Invert();
+ }
+ }
+ fprintf(stdout, "saving %s\n", outFile.c_str());
+ bError = !pOutBitmap->Save(image, outFile);
+ }
+ if (bError) {
+ return 4;
+ }
+
+ fprintf(stdout, "conversion compeleted successfully.\n\n");
+
+ return 0;
+}
+
+void usage(void)
+{
+ fprintf(stdout, "\n");
+ fprintf(stdout, "%s v%s\n", prgname, VERSION);
+ fprintf(stdout, "%s is a tool to convert images to a simple format (*.glcd)\n", prgname);
+ fprintf(stdout, " that is used by the graphlcd plugin for VDR.\n\n");
+ fprintf(stdout, " Usage: %s [-n] -i file[s...] -o outfile \n\n", prgname);
+ fprintf(stdout, " -n --invert inverts the output (default: none)\n");
+ fprintf(stdout, " -i --infile specifies the name of the input file[s]\n");
+ fprintf(stdout, " -o --outfile specifies the name of the output file\n");
+ fprintf(stdout, " -d --delay specifies the delay between multiple images [Default: %d ms] \n",delay);
+ fprintf(stdout, "\n" );
+ fprintf(stdout, " example: %s -i vdr-logo.bmp -o vdr-logo.glcd \n", prgname );
+}
diff --git a/tools/convpic/formats.txt b/tools/convpic/formats.txt
new file mode 100644
index 0000000..578a211
--- /dev/null
+++ b/tools/convpic/formats.txt
@@ -0,0 +1,32 @@
+Dateiformat von "Graphlcd-Logo"
+
+
+Einzelbild für LCD
+=================================
+
+HEADER
+Position Typ Name Bedeutung
+0x0000 char[4] magic - "GLCD"
+0x0004 word width - Breite der Bilder (LE)
+0x0006 word height - Höhe der Bilder (LE)
+0x0008 char[] data - Einzel-Bild
+
+DATEN
+char[height][width/8], pro Byte 8 nebeneinander liegende Pixel
+
+Animation für LCD
+=================================
+
+HEADER
+Position Typ Name Bedeutung
+0x0000 char[4] magic - "GLCA"
+0x0004 word width - Breite der Bilder (LE)
+0x0006 word height - Höhe der Bilder (LE)
+0x0008 word count - Anzahl der Bilder (LE)
+0x000a long delay - Wartezeit zwischen den Bildern (in ms; LE)
+0x0010 char[] data - Einzel-Bilder
+
+DATEN
+char[height][width/8], pro Byte 8 nebeneinander liegende Pixel (1 = Pixel gesetzt)
+
+LE = Little Endian byte order = wie bei intel ;)
diff --git a/tools/convpic/tiff.c b/tools/convpic/tiff.c
new file mode 100644
index 0000000..4b8bf0e
--- /dev/null
+++ b/tools/convpic/tiff.c
@@ -0,0 +1,201 @@
+/**
+ * GraphLCD plugin for the Video Disk Recorder
+ *
+ * tiff.c - tiff logo class
+ *
+ * (c) 2004 Andreas Brachold <vdr04 AT deltab de>
+ * (c) 2001-2004 Carsten Siebholz <c.siebholz AT t-online 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 <string>
+
+#include <glcdgraphics/bitmap.h>
+#include <glcdgraphics/image.h>
+
+#include "tiff.h"
+
+
+#pragma pack(1)
+typedef struct TIFFT{
+ unsigned short tag;
+ unsigned short type;
+ unsigned long length;
+ /* 1 = BYTE. 8-bit unsigned integer. */
+ /* 2 = ASCII. 8-bit bytes that store ASCII codes; the last byte must be null. */
+ /* 3 = SHORT. A 16-bit (2-byte) unsigned integer. */
+ /* 4 = LONG. A 32-bit (4-byte) unsigned integer. */
+ /* 5 = RATIONAL. Two LONGs: the first represents the numerator of a fraction, the second the denominator. */
+ unsigned long off_val;
+} TIFFTAG;
+#pragma pack()
+
+#define GETANDCHECK { t=fgetc(fIN);if(t==EOF) {fclose(fIN);return false;};}
+
+cTIFFFile::cTIFFFile()
+{
+}
+
+cTIFFFile::~cTIFFFile()
+{
+}
+
+bool cTIFFFile::Load(GLCD::cImage & image, const std::string & fileName)
+{
+ FILE *fIN;
+ TIFFTAG tifftag;
+ unsigned int tiff_header, tiff_anztags, tiff_data;
+ unsigned char cl,ch,y,i;
+ unsigned char height, width, strip, invert;
+ unsigned char fLittleEndian=0;
+ int j;
+ int t;
+ unsigned char *bitmap = NULL;
+ bool bInvert = false;
+
+ if (fileName.length() > 0)
+ {
+ fIN = fopen(fileName.c_str(), "rb");
+ if (fIN)
+ {
+ // isyslog("graphlcd plugin: try to load logo %s.", szFileName);
+ if (fseek(fIN, 0, SEEK_SET)==EOF)
+ {
+ fclose(fIN);
+ return false;
+ }
+ GETANDCHECK; cl=(unsigned char)t;
+ GETANDCHECK; ch=(unsigned char)t;
+ if ((cl==0x49) && (ch==0x49))
+ {
+ fLittleEndian=1;
+ }
+
+ if (fseek(fIN, 4, SEEK_SET)==EOF)
+ {
+ fclose(fIN);
+ return false;
+ }
+ GETANDCHECK; cl=(unsigned char)t;
+ GETANDCHECK; ch=(unsigned char)t;
+ tiff_header = cl+256*ch;
+ //printf("tiff_header:%d %x\n", tiff_header, tiff_header);
+
+ if (fseek(fIN, tiff_header, SEEK_SET)==EOF)
+ {
+ fclose(fIN);
+ return false;
+ }
+
+ GETANDCHECK; cl=(unsigned char)t;
+ GETANDCHECK; ch=(unsigned char)t;
+ tiff_anztags = cl+256*ch;
+ //printf("tiff_anztags:%d %x\n", tiff_anztags, tiff_anztags);
+
+ height=0;
+ width=0;
+ strip=0;
+ invert=0;
+ for (i=0; (i<tiff_anztags)&&(!height||!width||!strip||!invert); i++)
+ {
+ if (fread(&tifftag, sizeof(tifftag), 1, fIN)!=1)
+ {
+ fclose(fIN);
+ return false;
+ }
+ if (tifftag.tag==0x0100) width=tifftag.off_val;
+ if (tifftag.tag==0x0101) height=tifftag.off_val;
+ if (tifftag.tag==0x0111) strip=tifftag.off_val;
+ if (tifftag.tag==0x0106) invert=tifftag.off_val+1;
+ //printf("tag%d: %d %d %ld %ld\n", i,tifftag.tag, tifftag.type, tifftag.length, tifftag.off_val );
+ }
+
+ if (fseek(fIN,strip, SEEK_SET)==EOF)
+ {
+ fclose(fIN);
+ return false;
+ }
+ GETANDCHECK; cl=(unsigned char)t;
+ GETANDCHECK; ch=(unsigned char)t;
+ tiff_data = cl+256*ch;
+ //printf("tiff_data:%d %x\n", tiff_data, tiff_data);
+
+ if (fseek(fIN, tiff_data, SEEK_SET)==EOF)
+ {
+ fclose(fIN);
+ return false;
+ }
+
+
+ image.Clear();
+ image.SetWidth(width);
+ image.SetHeight(height);
+ image.SetDelay(100);
+ bitmap = new unsigned char[height * ((width + 7) / 8)];
+ if (bitmap)
+ {
+ if (fread(bitmap, height*((width+7)/8), 1, fIN)!=1)
+ {
+ delete [] bitmap;
+ fclose(fIN);
+ image.Clear();
+ return false;
+ }
+
+ if (invert-1==1) bInvert = !bInvert; // 'Black is zero'
+ if (bInvert)
+ {
+ for (j=0; j < height * ((width+7)/8); j++)
+ {
+ (*(bitmap+j)) = (*(bitmap+j))^0xff;
+ }
+ }
+
+ // cut the rest of the line
+ if (width%8)
+ {
+ for (y=1; y<=height; y++) {
+ j=y*((width+7)/8)-1;
+ (*(bitmap+j)) = ((*(bitmap+j))>>(8-width%8))<<(8-width%8);
+ }
+ }
+ image.AddBitmap(new GLCD::cBitmap(width, height, bitmap));
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: cannot allocate memory\n");
+ }
+ fclose(fIN);
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: cannot open picture %s\n", fileName.c_str());
+ }
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: no szFileName given!\n");
+ }
+ return true;
+}
diff --git a/tools/convpic/tiff.h b/tools/convpic/tiff.h
new file mode 100644
index 0000000..f6e66c1
--- /dev/null
+++ b/tools/convpic/tiff.h
@@ -0,0 +1,42 @@
+/**
+ * GraphLCD plugin for the Video Disk Recorder
+ *
+ * tiff.h - tiff logo class
+ *
+ * (c) 2004 Andreas Brachold <vdr04 AT deltab de>
+ * (c) 2001-2004 Carsten Siebholz <c.siebholz AT t-online 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 *
+ * *
+ ***************************************************************************/
+
+#ifndef _TIFF_H_
+#define _TIFF_H_
+
+#include <glcdgraphics/imagefile.h>
+
+class cTIFFFile : public GLCD::cImageFile
+{
+public:
+ cTIFFFile();
+ virtual ~cTIFFFile();
+ virtual bool Load(GLCD::cImage & image, const std::string & fileName);
+};
+
+#endif
diff --git a/tools/convpic/tuxbox.c b/tools/convpic/tuxbox.c
new file mode 100644
index 0000000..55d6e1d
--- /dev/null
+++ b/tools/convpic/tuxbox.c
@@ -0,0 +1,280 @@
+/**
+ * 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));
+ }
+ }
+ }
+}
diff --git a/tools/convpic/tuxbox.h b/tools/convpic/tuxbox.h
new file mode 100644
index 0000000..1214fb5
--- /dev/null
+++ b/tools/convpic/tuxbox.h
@@ -0,0 +1,44 @@
+/**
+ * GraphLCD plugin for the Video Disk Recorder
+ *
+ * tuxbox.h - 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 *
+ * *
+ ***************************************************************************/
+#ifndef _TUXBOX_H_
+#define _TUXBOX_H_
+
+#include <glcdgraphics/imagefile.h>
+
+class cTuxBoxFile : public GLCD::cImageFile
+{
+private:
+ void vert2horz(const unsigned char* source, unsigned char* dest, int width, int height);
+ void horz2vert(const unsigned char* source, unsigned char* dest, int width, int height);
+public:
+ cTuxBoxFile();
+ virtual ~cTuxBoxFile();
+ virtual bool Load(GLCD::cImage & image, const std::string & fileName);
+ virtual bool Save(GLCD::cImage & image, const std::string & fileName);
+};
+
+#endif