/* This file is part of Netpbm Formats, a File Format plugin for Adobe Photoshop Copyright (C) 2004-6 Toby Thain, toby@telegraphics.com.au 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 #include #include #include #include "file_io.h" #include "pbm.h" #define DBG extern int chunk,top; // only one or the other of these will be used, whether USEMALLOC is defined char *indata; // pointer to malloc'd buffer Handle bufhandle; // pointer to buffer allocated by Photoshop callback int subformat; long rb; /* format specs: http://netpbm.sourceforge.net/doc/pbm.html http://netpbm.sourceforge.net/doc/pgm.html http://netpbm.sourceforge.net/doc/ppm.html */ int space_num(FILEREF f){ char digits[MAXDIGITS+1]; int i,c; /* read a decimal number from input, skipping any preceding whitespace and any comments (which begin with '#' and continue till end of line) */ for(;;){ /* skip whitespace */ while( isspace(c = bufgetc(f)) ) ; if(c=='#'){ /* begins a comment; skip characters until end of line */ do c = bufgetc(f); while(c != EOF && c != CR && c != LF); }else if(isdigit(c)){ /* read number */ for(i = 0; isdigit(c) && i < MAXDIGITS; c = bufgetc(f)) digits[i++] = c; digits[i] = 0; return atoi(digits); }else return -1; /* found non-digit, EOF, or number not followed by whitespace */ } } OSErr readchunk(FormatRecordPtr pb){ OSErr e = noErr; int i,j,n,v,mask; unsigned char *p; unsigned short *q; long count = rb * chunk; pb->data = indata; pb->theRect.left = 0; pb->theRect.right = pb->imageSize.h; pb->theRect.top = top; pb->theRect.bottom = top+chunk; //sprintf(s,"data=%#x theRect.l=%d theRect.r=%d theRect.t=%d theRect.b=%d \nloPlane=%d hiPlane=%d rowBytes=%d colBytes=%d planeBytes=%d", // pb->data,pb->theRect.left,pb->theRect.right,pb->theRect.top,pb->theRect.bottom,pb->loPlane,pb->hiPlane,pb->rowBytes,pb->colBytes,pb->planeBytes); //dbg(s); if(subformat & 4){ /* raw data */ if( !(e = bufgetbytes((FILEREF)pb->dataFork,count,pb->data)) && pb->depth == 16 && platform_is_LittleEndian() ){ /* must byte swap the file data words for this platform */ for( i = count/2, q = (unsigned short*)pb->data, p = pb->data ; i-- ; p += 2 ) *q++ = p[0]<<8 | p[1]; } }else{ /* "Plain" text encoded data */ if(pb->depth == 1) memset(pb->data,0,count); for(j = chunk,p = pb->data ; j-- ; p += rb){ // initialise variables for 1-bit output n = 0; // bit counter mask = 0x80; // current bit mask (shifted for every pixel) q = (unsigned short*)p; for( i = 0 ; i < pb->imageSize.h*pb->planes ; ++i ){ v = space_num(F); if(v == -1){ // bad data encountered //char s[0x100]; //sprintf(s,"row %d, column %d: bad input value, or end of file",top+(chunk-j),i); //DBG(s); return formatCannotRead; } switch(pb->depth){ case 1: if(v) p[n] |= mask; /* set the bit */ /* move to next bit; or next byte if no more bits */ if(!(mask >>= 1)){ mask = 0x80; ++n; } break; case 8: p[i] = v; break; case 16: q[i] = v; break; } } } e = noErr; } return e; } OSErr read_start(FormatRecordPtr pb){ OSErr e = formatCannotRead; int n; if(bufgetc(F)=='P'){ subformat = bufgetc(F); if( (pb->imageSize.h = space_num(F))>0 && (pb->imageSize.v = space_num(F))>0 ) { pb->planes = 1; switch( subformat ){ case PLAINPBM_KEY: case PBM_KEY: pb->depth = 1; pb->imageMode = plugInModeBitmap; break; case PLAINPPM_KEY: case PPM_KEY: pb->planes = 3; case PLAINPGM_KEY: case PGM_KEY: n = space_num(F); if(n>0){ int isgrey = subformat==PLAINPGM_KEY||subformat==PGM_KEY; if(n < 256){ pb->depth = 8; pb->imageMode = isgrey ? plugInModeGrayScale : plugInModeRGBColor; }else{ pb->depth = 16; pb->imageMode = isgrey ? plugInModeGray16 : plugInModeRGB48; } break; } /* fall through */ default: DBG("unknown subformat"); return formatCannotRead; } rb = ((long)pb->imageSize.h*pb->depth*pb->planes + 7)>>3; pb->loPlane = 0; pb->hiPlane = pb->planes-1; pb->rowBytes = rb; pb->colBytes = pb->planes*pb->depth/8; pb->planeBytes = pb->depth/8; pb->imageHRes = pb->imageVRes = 72L<<16; #ifdef USEMALLOC chunk = (pb->maxData - 16384) / rb; #else // only request a megabyte or so as read buffer chunk = (1L<<20)/rb; #endif if(chunk < 1) chunk = 1; else if(chunk > pb->imageSize.v) chunk = pb->imageSize.v; #ifdef USEMALLOC indata = malloc(rb*chunk + 4); #else bufhandle = pb->handleProcs->newProc(rb*chunk + 4); indata = bufhandle ? pb->handleProcs->lockProc(bufhandle,false) : NULL; #endif if(indata){ //char s[0x100];sprintf(s,"starting read: chunk=%d indata=%#x \nimageSize.h = %d imageSize.v = %d planes = %d depth = %d imageMode=%d", // chunk,indata,pb->imageSize.h,pb->imageSize.v,pb->planes,pb->depth,pb->imageMode);DBG(s); top = 0; e = readchunk(pb); }else e = memFullErr; }else DBG("bad header"); }else DBG("file doesn't start with 'P'"); return e; } OSErr read_continue(FormatRecordPtr pb){ OSErr e = noErr; top += chunk; if(top < pb->imageSize.v){ if(chunk > pb->imageSize.v-top) chunk = pb->imageSize.v-top; e = readchunk(pb); }else /* finished! */ pb->data = NULL; return e; } OSErr read_finish(FormatRecordPtr pb){ #ifdef USEMALLOC free(indata); #else pb->handleProcs->disposeProc(bufhandle); #endif return noErr; }