#include "ET++.ph" #include "Converter.h" #include "Class.h" #include "Data.h" #include "Bitmap.h" #include "DevBitmap.h" #include "String.h" #include "ColorMapper.h" #include "TypeMatcher.h" #include "ET_stdio.h" static const Symbol cDocTypePBM("PBM"); //---- PBMMatcher -------------------------------------------------------------- class PBMMatcher: public TypeMatcher { public: PBMMatcher(const Symbol &t) : TypeMatcher(t, 0, 0, FALSE) { } bool MatchContents(Data*, const char *buf, int len) { return len >= 2 && buf[0] == 'P' && buf[1] >= '1' && buf[1] <= '6'; } bool MatchExtension(Data*, const char *e) { return strlen(e) == 3 && e[0] == 'p' && e[2] == 'm' && (e[1] == 'b' || e[1] == 'g' || e[1] == 'p' || e[1] == 'n'); } Bitmap *GetBitmap() { return gImageIcon; } }; static PBMMatcher pbmmatcher(cDocTypePBM); //---- PbmConverter ------------------------------------------------------------ class PbmConverter : public Converter { public: MetaDef(PbmConverter); PbmConverter() { } const char *AsString() { return "Portable Bitmap (PBM)"; } char *DoConvert(IStream &is, Object **); Object *Convert(Data *data, Class *want); bool CanConvert(Data *data, Class *want) { return data->Type() == cDocTypePBM && want == Meta(Bitmap); } bool CanConvertTo(Object *op) { return op->IsKindOf(Bitmap); } bool ConvertTo(Data *data, Object *op); }; NewMetaImpl0(PbmConverter,Converter); // utils ----------------------------------------------------------------------- static int IntTable[256]; static unsigned int Initialized= 0; #define NOTINT -1 #define COMMENT -2 #define SPACE -3 #define NEWLINE -4 static int pbmReadChar(IStream &is) { byte c; is.get(c); if (is.eof()) return -1; if (IntTable[c] == COMMENT) { do { is.get(c); if (is.eof()) return -1; } while (IntTable[c] != NEWLINE); } return c; } static int pbmReadInt(IStream &is) { int c, value; for (;;) { c= pbmReadChar(is); if (c < 0) return -1; if (IntTable[c] >= 0) break; } value= IntTable[c]; for (;;) { c= pbmReadChar(is); if (c < 0) return -1; if (IntTable[c] < 0) return value; value= (value * 10) + IntTable[c]; } } char *PbmConverter::DoConvert(IStream &is, Object **op) { Bitmap *BM; register DevBitmap *dbm; int x, y, width, height, linelen, depth; int src, components, maxval= 1, bitsperpixel= 1; byte buf[4], *row= 0, *rp; bool raw, readmax; RGB rgb; if (! Initialized) { unsigned int a; for (a= 0; a < 256; a++) IntTable[a]= NOTINT; IntTable['#']= COMMENT; IntTable['\n']= NEWLINE; IntTable['\r']= IntTable['\t']= IntTable[' ']= SPACE; IntTable['0']= 0; IntTable['1']= 1; IntTable['2']= 2; IntTable['3']= 3; IntTable['4']= 4; IntTable['5']= 5; IntTable['6']= 6; IntTable['7']= 7; IntTable['8']= 8; IntTable['9']= 9; Initialized= 1; } if (is.read(buf, 2) != 2) return "file too short"; if (buf[0] != 'P') return "not a pbm file"; switch (buf[1]) { case '1': readmax= FALSE; components= 1; raw= FALSE; break; case '2': readmax= TRUE; components= 1; raw= FALSE; break; case '3': readmax= TRUE; components= 3; raw= FALSE; break; case '4': readmax= FALSE; components= 1; raw= TRUE; break; case '5': readmax= TRUE; components= 1; raw= TRUE; break; case '6': readmax= TRUE; components= 3; raw= TRUE; break; default: return "not a pbm file"; } if ((width= pbmReadInt(is)) < 0) return "wrong width"; if ((height= pbmReadInt(is)) < 0) return "wrong height"; if (readmax && (maxval= pbmReadInt(is)) < 0) return "wrong maxval"; if (maxval == 1) bitsperpixel= 1; else if (maxval < 16) bitsperpixel= 4; else if (maxval < 256) bitsperpixel= 8; else return "too big maxval"; depth= bitsperpixel * components; BM= new Bitmap(Point(width, height), depth); dbm= BM->GetDevBitmap(); if (buf[1] == '2' || buf[1] == '5') { for (int i= 0; i <= maxval; i++) { rgb.red= rgb.green= rgb.blue= (255 * i) / maxval; BM->ChangeRGB(i, &rgb); } } switch (buf[1]) { case '1': // normal PBM for (y= 0; y < height; y++) { for (x= 0; x < width; x++) { do { if ((src= pbmReadChar(is)) < 0) return "Short image"; if (IntTable[src] == NOTINT) return "Bad image data"; } while (IntTable[src] < 0); switch (IntTable[src]) { case 1: dbm->SetPixel(x, y, 1); break; case 0: dbm->SetPixel(x, y, 0); break; default: return "pixel value out of range"; } } } break; case '2': // normal PGM for (y= 0; y < height; y++) { for (x= 0; x < width; x++) { src= pbmReadInt(is); if (src < 0 || src > maxval) return "pixel value out of range"; dbm->SetPixel(x, y, (src*255)/maxval); } } break; case '3': // normal PPM for (y= 0; y < height; y++) { for (x= 0; x < width; x++) { if ((src= pbmReadInt(is)) < 0 || src > maxval) return "pixel value out of range"; rgb.red= (src*255)/maxval; if ((src= pbmReadInt(is)) < 0 || src > maxval) return "pixel value out of range"; rgb.green= (src*255)/maxval; if ((src= pbmReadInt(is)) < 0 || src > maxval) return "pixel value out of range"; rgb.blue= (src*255)/maxval; BM->SetRGB(x, y, &rgb); } } break; case '4': // raw PBM linelen= (width+7)/8; row= new byte[linelen]; { BitmapInfo bi(Point(width, height), 1, 1); for (y= 0; y < height; y++) { if (is.read(row, linelen) != linelen) { delete row; return "Short image"; } dbm->SetRow(&bi, y, row); } } break; case '5': // raw PGM linelen= width; row= new byte[linelen]; { BitmapInfo bi(Point(width, height), 8, 1); for (y= 0; y < height; y++) { if (is.read(row, linelen) != linelen) { delete row; return "Short image"; } rp= row; for (x= 0; x < width; x++) *rp++= (*rp * 255)/maxval; dbm->SetRow(&bi, y, row); } } break; case '6': // raw PPM linelen= width*3; row= new byte[linelen]; for (y= 0; y < height; y++) { if (is.read(row, linelen) != linelen) { delete row; return "Short image"; } rp= row; for (x= 0; x < width; x++) { rgb.red= (*rp++ * 255)/maxval; rgb.green= (*rp++ * 255)/maxval; rgb.blue= (*rp++ * 255)/maxval; BM->SetRGB(x, y, &rgb); } } break; } SafeDelete(row); *op= BM; return 0; } Object *PbmConverter::Convert(Data *data, Class*) { Object *op= 0; IStream is(data->GetStreamBufForReading(), 0, TRUE); char *msg= DoConvert(is, &op); if (msg) fprintf(stderr, "PbmConverter::Convert: %s\n", msg); return op; } bool PbmConverter::ConvertTo(Data *data, Object *op) { register int x, y; Bitmap *bm= (Bitmap*) op; register DevBitmap *dbm= bm->GetDevBitmap(); Point e= bm->Size(); RGB rgb; int rowlen; char magic; byte *row= 0, *rp; OStream ois(data->GetStreamBufForWriting(), TRUE); if (dbm->Depth() == 1) { magic= '4'; rowlen= (e.x+7)/8; } else if (bm->GetColorMapper()->IsGrey()) { magic= '5'; rowlen= e.x; } else { magic= '6'; rowlen= e.x*3; } ois << "P" << magic NL; ois << e.x SP << e.y NL; if (magic != '4') ois << 255 NL; row= new byte[rowlen]; for (y= 0; y < e.y; y++) { rp= row; switch (magic) { case '4': { BitmapInfo bi(e, 1, 1); dbm->GetRow(&bi, y, row); } break; case '5': for (x= 0; x < e.x; x++) { bm->GetRGB(x, y, &rgb); *rp++= rgb.red; } break; case '6': for (x= 0; x < e.x; x++) { bm->GetRGB(x, y, &rgb); *rp++= rgb.red; *rp++= rgb.green; *rp++= rgb.blue; } break; } if (ois.write(row, rowlen) != rowlen) { fprintf(stderr, "PbmConverter::ConvertTo: can't write\n"); break; } } SafeDelete(row); return TRUE; }