From f314041b6b37bb9274d9fc79946858341befa0f2 Mon Sep 17 00:00:00 2001 From: Mike Buland Date: Wed, 8 Apr 2015 08:40:05 -0600 Subject: Added image goo + font support. --- build.sh | 2 +- src/font.cpp | 192 +++++++++++++++++++++++++++++++++++++++++ src/font.h | 65 ++++++++++++++ src/image.cpp | 143 +++++++++++++++++++++++++++++++ src/image.h | 27 ++++++ src/main.cpp | 16 ++++ src/palette.cpp | 32 +++++++ src/palette.h | 22 +++++ src/renderpng.cpp | 251 ++++++++++++++++++++++++++++++++++-------------------- 9 files changed, 658 insertions(+), 92 deletions(-) create mode 100644 src/font.cpp create mode 100644 src/font.h create mode 100644 src/image.cpp create mode 100644 src/image.h create mode 100644 src/palette.cpp create mode 100644 src/palette.h diff --git a/build.sh b/build.sh index 5e8bb26..cf2880f 100644 --- a/build.sh +++ b/build.sh @@ -1,3 +1,3 @@ #!/bin/bash -exec g++ src/*.cpp -o lost.exe -L/usr/lib -lpng16 -I/usr/include -lz +exec g++ -ggdb src/*.cpp -o lost.exe -L/usr/lib -lpng16 -I/usr/include -lz -ggdb diff --git a/src/font.cpp b/src/font.cpp new file mode 100644 index 0000000..27204a7 --- /dev/null +++ b/src/font.cpp @@ -0,0 +1,192 @@ +#include "font.h" + +#include +#include + +#include + +#define fh (( (FILE *)rawfh )) + +Font::Font( const char *sSrcFile ) : + iTableTop( 0 ), + pTable( NULL ) +{ + iTableTop = 128; + pTable = new Glyph*[iTableTop]; + memset( pTable, 0, sizeof(Glyph*)*iTableTop ); + Parser p( sSrcFile, *this ); + p.parse(); +} + +Font::~Font() +{ +} + +Font::Parser::Parser( const char *sSrcFile, Font &rFont ) : + rFont( rFont ), + rawfh( NULL ), + sLine( NULL ), + bDone( false ) +{ + sLine = new char[1024]; + rawfh = fopen( sSrcFile, "rt" ); +} + +Font::Parser::~Parser() +{ + delete[] sLine; + fclose( fh ); +} + +void Font::Parser::parse() +{ + header(); + characters(); + if( strncasecmp("ENDFONT", sLine, 7 ) ) + { + printf("End of file reached, but no end marker.\n"); + throw std::exception(); + } +} + +void Font::Parser::getLine() +{ + for(;;) + { + if( fgets( sLine, 1024, fh ) == NULL ) + { + sLine[0] = '\0'; + bDone = true; + printf("Premature end of file.\n"); + throw std::exception(); + return; + } + + if( strncasecmp("COMMENT ", sLine, 8 ) ) + { + return; + } + } +} + +void Font::Parser::header() +{ + getLine(); + if( strncasecmp("STARTFONT 2.1", sLine, 13 ) ) + { + printf("Bad format: %s\n", sLine); + throw std::exception(); + } + + for(;;) + { + getLine(); + if( strncasecmp("SIZE ", sLine, 5 ) == 0 ) + { + } + else if( strncasecmp("FONTBOUNDINGBOX ", sLine, 16 ) == 0 ) + { + } + else if( strncasecmp("STARTPROPERTIES ", sLine, 16 ) == 0 ) + { + properties(); + } + else if( strncasecmp("CHARS ", sLine, 6 ) == 0 ) + { + return; + } + } +} + +void Font::Parser::properties() +{ + for(;;) + { + getLine(); + if( strncasecmp("ENDPROPERTIES", sLine, 13 ) == 0 ) + return; + } +} + +void Font::Parser::characters() +{ + for(;;) + { + getLine(); + if( strncasecmp("STARTCHAR ", sLine, 10 ) == 0 ) + character(); + else + return; + } +} + +uint8_t hex2byte( const char *str ) +{ + uint8_t ret = 0; + + ret |= ((*str >= '0' && *str <= '9')?(*str-'0'):( + (*str >= 'a' && *str <= 'f')?(*str-'a'+10):( + (*str >= 'A' && *str <= 'F')?(*str-'A'+10):0 + )))<<4; + str++; + ret |= (*str >= '0' && *str <= '9')?(*str-'0'):( + (*str >= 'a' && *str <= 'f')?(*str-'a'+10):( + (*str >= 'A' && *str <= 'F')?(*str-'A'+10):0 + )); + return ret; +} + +void Font::Parser::character() +{ + char sName[1024]; + int32_t iIdx = -1; + strcpy( sName, sLine+10 ); + Glyph *pChar = new Glyph(); + + for(;;) + { + getLine(); + if( strncasecmp("ENCODING ", sLine, 9 ) == 0 ) + { + sscanf( sLine+9, "%d", &iIdx ); + } + else if( strncasecmp("DWIDTH ", sLine, 7 ) == 0 ) + { + sscanf( sLine+7, "%d %d", &pChar->pDevOff.x, &pChar->pDevOff.y ); + } + else if( strncasecmp("BBX ", sLine, 4 ) == 0 ) + { + sscanf( sLine+4, "%d %d %d %d", + &pChar->pBox.x, &pChar->pBox.y, + &pChar->pOff.x, &pChar->pOff.y + ); + } + else if( strncasecmp("BITMAP", sLine, 6 ) == 0 ) + { + pChar->iRowBytes = pChar->pBox.x/8 + ((pChar->pBox.x%8)==0?0:1); + + pChar->pData = new uint8_t[pChar->iRowBytes*pChar->pBox.y]; + // Read bitmap + for( int y = 0; y < pChar->pBox.y; y++ ) + { + getLine(); + for( int x = 0; x < pChar->iRowBytes; x++ ) + { + pChar->pData[x+y*pChar->iRowBytes] = hex2byte( sLine+(2*x) ); + } + } + } + else if( strncasecmp("ENDCHAR", sLine, 7 ) == 0 ) + { + if( iIdx >= 0 && iIdx < rFont.iTableTop ) + { + rFont.pTable[iIdx] = pChar; + //printf("Char %d: %s\n", iIdx, sName ); + } + else + delete pChar; + return; + } + } +} + diff --git a/src/font.h b/src/font.h new file mode 100644 index 0000000..802de9d --- /dev/null +++ b/src/font.h @@ -0,0 +1,65 @@ +#ifndef FONT_H +#define FONT_H + +#include + +class Font +{ +public: + class Point + { + public: + int32_t x, y; + }; + + class Glyph + { + public: + Glyph() : + pData( NULL ) + { } + ~Glyph() + { delete[] pData; } + Point pBox; + Point pOff; + Point pDevOff; + + int iRowBytes; + uint8_t *pData; + }; + +public: + Font( const char *sSrcFile ); + virtual ~Font(); + + Glyph *get( char c ) { return pTable[(int32_t)c]; } + +private: + Point pDefBox; + Point pDefOff; + int32_t iTableTop; + Glyph **pTable; + +private: // Parser + class Parser + { + public: + Parser( const char *sSrcFile, Font &rFont ); + ~Parser(); + + void parse(); + void getLine(); + void header(); + void properties(); + void characters(); + void character(); + + private: + Font &rFont; + void *rawfh; + char *sLine; + bool bDone; + }; +}; + +#endif diff --git a/src/image.cpp b/src/image.cpp new file mode 100644 index 0000000..384de9a --- /dev/null +++ b/src/image.cpp @@ -0,0 +1,143 @@ +#include "image.h" +#include "palette.h" +#include "font.h" + +#include +#include +#include + +Image::Image() : + iWidth( 0 ), + iHeight( 0 ), + pPix( NULL ) +{ +} + +Image::Image( int32_t iWidth, int32_t iHeight ) : + iWidth( iWidth ), + iHeight( iHeight ), + pPix( NULL ) +{ + pPix = new uint8_t[iWidth*iHeight]; + clear(); +} + +Image::~Image() +{ + delete[] pPix; +} + +void Image::clear( uint8_t uColor ) +{ + memset( pPix, uColor, iWidth*iHeight ); +} + +void Image::set( int32_t x, int32_t y, uint8_t iCol ) +{ + if( x < 0 || y < 0 || x >= iWidth || y >= iHeight ) + return; + + pPix[x+y*iWidth] = iCol; +} + +void Image::save( const char *sPath, class Palette &rPal ) +{ + uint8_t **pRows = new uint8_t*[iHeight]; + for( int r = 0; r < iHeight; r++ ) + { + pRows[r] = pPix+(iWidth*r); + } + + png_structp png_ptr = png_create_write_struct( + PNG_LIBPNG_VER_STRING, NULL, NULL, NULL + ); + png_infop info_ptr = png_create_info_struct(png_ptr); + png_set_IHDR(png_ptr, info_ptr, iWidth, iHeight, + 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT + ); + png_color *pCol = new png_color[rPal.getCount()]; + for( int j = 0; j < rPal.getCount(); j++ ) + { + uint8_t r, g, b; + rPal.getColor( j, r, g, b ); + pCol[j].red = r; + pCol[j].green = g; + pCol[j].blue = b; + } + png_set_PLTE( png_ptr, info_ptr, pCol, rPal.getCount()); + + png_set_rows( png_ptr, info_ptr, pRows ); + + FILE *fp = fopen(sPath, "wb"); + if( fp == NULL ) + { + printf("Error opening file!\n"); + exit( 1 ); + } + + png_init_io( png_ptr, fp ); + png_write_png( png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL ); + png_destroy_write_struct( &png_ptr, &info_ptr ); + fclose( fp ); + + delete[] pCol; + delete[] pRows; +} + +void Image::drawText( class Font &rFnt, int32_t x, int32_t y, uint8_t uColor, + const char *sText ) +{ + for( const char *s = sText; *s; s++ ) + { + Font::Glyph *pGlyph = rFnt.get( *s ); + if( pGlyph == NULL ) + continue; + + for( int cy = 0; cy < pGlyph->pBox.y; cy++ ) + { + for( int cx = 0; cx < pGlyph->pBox.x; cx++ ) + { + int bit = 1<<(7-(cx%8)); + int byte = cx/8; + if( pGlyph->pData[byte+cy*pGlyph->iRowBytes]&bit ) + set( x+cx, y+cy-pGlyph->pBox.y-pGlyph->pOff.y, uColor ); + } + } + x += pGlyph->pDevOff.x; + y += pGlyph->pDevOff.y; + } +} + +void Image::drawLine( int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint8_t uColor ) +{ + if( x1 == x2 ) + { + // Vertical line, easy + if( y2 < y1 ) + { + int32_t r = y2; + y2 = y1; + y1 = r; + } + for( int j = y1; j <= y2; j++ ) + { + set( x1, j, uColor ); + } + } + else if( y1 == y2 ) + { + // Horizontal line, easy + if( x2 < x1 ) + { + int32_t r = x2; + x2 = x1; + x1 = r; + } + for( int j = x1; j <= x2; j++ ) + { + set( j, y1, uColor ); + } + } +} + diff --git a/src/image.h b/src/image.h new file mode 100644 index 0000000..cdc13e0 --- /dev/null +++ b/src/image.h @@ -0,0 +1,27 @@ +#ifndef IMAGE_H +#define IMAGE_H + +#include + +class Image +{ +public: + Image(); + Image( int32_t iWidth, int32_t iHeight ); + virtual ~Image(); + + void clear( uint8_t uColor=0 ); + void set( int32_t x, int32_t y, uint8_t iCol ); + + void save( const char *sPath, class Palette &rPal ); + void drawText( class Font &rFnt, int32_t x, int32_t y, uint8_t uColor, + const char *sText ); + void drawLine( int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint8_t uColor ); + +private: + int32_t iWidth; + int32_t iHeight; + uint8_t *pPix; +}; + +#endif diff --git a/src/main.cpp b/src/main.cpp index ef3f3de..00dfc8b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,8 +10,24 @@ #include #include +#include "image.h" +#include "palette.h" +#include "font.h" + int main( int argc, char *argv[] ) { + /* + Image im( 400, 400 ); + Font f("ter-u12n.bdf"); + Palette pl; + pl.addColor( 0, 0, 0 ); + pl.addColor( 255, 0, 0 ); + pl.addColor( 0, 0, 255 ); + im.drawLine( 0, 12, 399, 12, 2 ); + im.drawText( f, 12, 12, 1, "Hello World!yqjm (01234,56789)"); + im.save("test.png", pl); + return 0; +*/ srand( time( NULL ) ); if( argc <= 2 ) diff --git a/src/palette.cpp b/src/palette.cpp new file mode 100644 index 0000000..0e3a3b1 --- /dev/null +++ b/src/palette.cpp @@ -0,0 +1,32 @@ +#include "palette.h" + +#include + +Palette::Palette() : + iCount( 0 ), + piChannels( NULL ) +{ + piChannels = new uint8_t[256*3]; + memset( piChannels, 0, 256*3 ); +} + +Palette::~Palette() +{ + delete[] piChannels; +} + +int32_t Palette::addColor( uint8_t iRed, uint8_t iGreen, uint8_t iBlue ) +{ + piChannels[iCount*3] = iRed; + piChannels[iCount*3+1] = iGreen; + piChannels[iCount*3+2] = iBlue; + return iCount++; +} + +void Palette::getColor( int32_t iIdx, uint8_t &iRed, uint8_t &iGreen, + uint8_t &iBlue ) +{ + iRed = piChannels[iIdx*3]; + iGreen = piChannels[iIdx*3+1]; + iBlue = piChannels[iIdx*3+2]; +} diff --git a/src/palette.h b/src/palette.h new file mode 100644 index 0000000..3fb176a --- /dev/null +++ b/src/palette.h @@ -0,0 +1,22 @@ +#ifndef PALETTE_H +#define PALETTE_H + +#include + +class Palette +{ +public: + Palette(); + virtual ~Palette(); + + int32_t addColor( uint8_t iRed, uint8_t iGreen, uint8_t iBlue ); + int32_t getCount() const { return iCount; } + void getColor( int32_t iIdx, uint8_t &iRed, uint8_t &iGreen, + uint8_t &iBlue ); + +private: + int32_t iCount; + uint8_t *piChannels; +}; + +#endif diff --git a/src/renderpng.cpp b/src/renderpng.cpp index 21d35ba..6ff9fbe 100644 --- a/src/renderpng.cpp +++ b/src/renderpng.cpp @@ -3,11 +3,15 @@ #include "map.h" #include "cell.h" +#include "image.h" +#include "palette.h" +#include "font.h" + #include #include #include #include -#include +#include RenderPng::RenderPng( Map &rMap ) : rMap( rMap ) @@ -25,16 +29,18 @@ void RenderPng::render() if( iDims > 2 ) iSize = (int)ceil(sqrt((rMap.getDims()-2)*2)); - int iBufWidth = ((iSize+1)*rMap.getSize( 0 )+1)*5; - int iBufHeight = ((iSize+1)*rMap.getSize( 1 )+1)*5; - int iBufSize = iBufWidth*iBufHeight; - uint8_t *pBuf = new uint8_t[iBufSize]; - uint8_t **pRows = new uint8_t*[iBufHeight]; - for( int r = 0; r < iBufHeight; r++ ) - { - pRows[r] = pBuf+(iBufWidth*r); - } - memset( pBuf, 1, iBufSize ); + int iCellSize = 11; + int iCellMid = iCellSize/2; + int iBufWidth = ((iSize+1)*rMap.getSize( 0 )+1)*iCellSize; + int iBufHeight = ((iSize+1)*rMap.getSize( 1 )+1)*iCellSize+12; + int ox = 0; + int oy = 12; + Image im( iBufWidth, iBufHeight ); + Font fnt("ter-u12n.bdf"); + im.clear( 1 ); + Palette pal; + pal.addColor( 0, 0, 0 ); + pal.addColor( 255, 255, 255 ); Position p( iDims ); for(;;) @@ -50,86 +56,138 @@ void RenderPng::render() if( (iRow == 0 || iRow == iSize+1) && (iCol == 0 || iCol == iSize+1) ) { - pRows[(y+iRow)*5+2][(x+iCol)*5+2] = 0; + im.set( + ox+(x+iCol)*iCellSize+iCellMid, + oy+(y+iRow)*iCellSize+iCellMid, + 0 + ); if( iRow == 0 && iCol == 0 ) { if( (iWalls&(1<<2)) == 0 ) { - for( int i = 0; i < 3; i++ ) - pRows[(y+iRow)*5+2][(x+iCol)*5+i+3] = 0; + for( int i = 0; i < iCellMid+1; i++ ) + im.set( + ox+(x+iCol)*iCellSize+i+iCellMid+1, + oy+(y+iRow)*iCellSize+iCellMid, + 0 + ); } if( (iWalls&(1<<0)) == 0 ) { - for( int i = 0; i < 3; i++ ) - pRows[(y+iRow)*5+i+3][(x+iCol)*5+2] = 0; + for( int i = 0; i < iCellMid+1; i++ ) + im.set( + ox+(x+iCol)*iCellSize+iCellMid, + oy+(y+iRow)*iCellSize+i+iCellMid+1, + 0 + ); } } else if( iRow == 0 && iCol == iSize+1 ) { if( (iWalls&(1<<2)) == 0 ) { - for( int i = 0; i < 3; i++ ) - pRows[(y+iRow)*5+2][(x+iCol)*5+i] = 0; + for( int i = 0; i < iCellMid+1; i++ ) + im.set( + ox+(x+iCol)*iCellSize+i, + oy+(y+iRow)*iCellSize+iCellMid, + 0 + ); } if( (iWalls&(1<<1)) == 0 ) { - for( int i = 0; i < 3; i++ ) - pRows[(y+iRow)*5+i+3][(x+iCol)*5+2] = 0; + for( int i = 0; i < iCellMid+1; i++ ) + im.set( + ox+(x+iCol)*iCellSize+iCellMid, + oy+(y+iRow)*iCellSize+i+iCellMid+1, + 0 + ); } } else if( iRow == iSize+1 && iCol == 0 ) { if( (iWalls&(1<<3)) == 0 ) { - for( int i = 0; i < 3; i++ ) - pRows[(y+iRow)*5+2][(x+iCol)*5+i+3] = 0; + for( int i = 0; i < iCellMid+1; i++ ) + im.set( + ox+(x+iCol)*iCellSize+i+iCellMid+1, + oy+(y+iRow)*iCellSize+iCellMid, + 0 + ); } if( (iWalls&(1<<0)) == 0 ) { - for( int i = 0; i < 3; i++ ) - pRows[(y+iRow)*5+i][(x+iCol)*5+2] = 0; + for( int i = 0; i < iCellMid+1; i++ ) + im.set( + ox+(x+iCol)*iCellSize+iCellMid, + oy+(y+iRow)*iCellSize+i, + 0 + ); } } else if( iRow == iSize+1 && iCol == iSize+1 ) { if( (iWalls&(1<<3)) == 0 ) { - for( int i = 0; i < 3; i++ ) - pRows[(y+iRow)*5+2][(x+iCol)*5+i] = 0; + for( int i = 0; i < iCellMid+1; i++ ) + im.set( + ox+(x+iCol)*iCellSize+i, + oy+(y+iRow)*iCellSize+iCellMid, + 0 + ); } if( (iWalls&(1<<1)) == 0 ) { - for( int i = 0; i < 3; i++ ) - pRows[(y+iRow)*5+i][(x+iCol)*5+2] = 0; + for( int i = 0; i < iCellMid+1; i++ ) + im.set( + ox+(x+iCol)*iCellSize+iCellMid, + oy+(y+iRow)*iCellSize+i, + 0 + ); } } } else if( iRow == 0 && (iWalls&(1<<2)) == 0 ) { - for( int i = 0; i < 5; i++ ) + for( int i = 0; i < iCellSize; i++ ) { - pRows[(y+iRow)*5+2][(x+iCol)*5+i] = 0; + im.set( + ox+(x+iCol)*iCellSize+i, + oy+(y+iRow)*iCellSize+iCellMid, + 0 + ); } } else if( iRow == iSize+1 && (iWalls&(1<<3)) == 0 ) { - for( int i = 0; i < 5; i++ ) + for( int i = 0; i < iCellSize; i++ ) { - pRows[(y+iRow)*5+2][(x+iCol)*5+i] = 0; + im.set( + ox+(x+iCol)*iCellSize+i, + oy+(y+iRow)*iCellSize+iCellMid, + 0 + ); } } else if( iCol == 0 && (iWalls&(1<<0)) == 0 ) { - for( int i = 0; i < 5; i++ ) + for( int i = 0; i < iCellSize; i++ ) { - pRows[(y+iRow)*5+i][(x+iCol)*5+2] = 0; + im.set( + ox+(x+iCol)*iCellSize+iCellMid, + oy+(y+iRow)*iCellSize+i, + 0 + ); } } else if( iCol == iSize+1 && (iWalls&(1<<1)) == 0 ) { - for( int i = 0; i < 5; i++ ) + for( int i = 0; i < iCellSize; i++ ) { - pRows[(y+iRow)*5+i][(x+iCol)*5+2] = 0; + im.set( + ox+(x+iCol)*iCellSize+iCellMid, + oy+(y+iRow)*iCellSize+i, + 0 + ); } } } @@ -142,43 +200,73 @@ void RenderPng::render() int d = j*2+side; if( iWalls&(1<