/* This file is part of SGI Format, a File Format plugin for Adobe Photoshop Copyright (C) 2003-9 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 "sgiformat.h" extern struct sgi_header hdr; extern unsigned char *indata,*rowdata; extern long rb,tabsize,*offsets,*counts; long offset; Boolean rle; unsigned fix16(unsigned x); long compressrow(unsigned char *src,unsigned char *dst,long n){ unsigned char *p,*q,*run,*dataend; int count,maxrun; dataend = src + n; for( run = src,q = dst ; n > 0 ; run = p, n -= count ){ maxrun = n < RLE_MAX_RUN ? n : RLE_MAX_RUN; if(run <= (dataend-3) && run[1] == run[0] && run[2] == run[0]){ for( p = run+3 ; p < (run+maxrun) && *p == run[0] ; ) ++p; count = p-run; *q++ = count; /* flag byte */ *q++ = run[0]; //dbg("duplicate run"); }else{ for( p = run ; p < (run+maxrun) ; ) if(p <= (dataend-3) && p[1] == p[0] && p[2] == p[0]) break; // 3 bytes repeated end verbatim run else ++p; count = p-run; *q++ = count | RLE_COPY_FLAG; /* flag byte */ memcpy(q,run,count); q += count; //dbg("verbatim run"); } //DPRINTF("count = %d",count); } *q++ = 0; /* zero count - end of data */ return q-dst; } long compressrow2(unsigned short *src,unsigned short *dst,long n){ unsigned short *p,*q,*run,*dataend; int count,maxrun,i; dataend = src + n; for( run = src,q = dst ; n > 0 ; run = p, n -= count ){ maxrun = n < RLE_MAX_RUN ? n : RLE_MAX_RUN; if(run <= (dataend-3) && run[1] == run[0] && run[2] == run[0]){ for( p = run+3 ; p < (run+maxrun) && *p == run[0] ; ) ++p; count = p-run; *q = 0; ((unsigned char*)q)[1] = count; ++q; /* flag byte */ *q++ = run[0]; //dbg("duplicate run"); }else{ for( p = run ; p < (run+maxrun) ; ) if(p <= (dataend-3) && p[1] == p[0] && p[2] == p[0]) break; // 3 bytes repeated end verbatim run else ++p; count = p-run; *q = 0; ((unsigned char*)q)[1] = count | RLE_COPY_FLAG; ++q; /* flag byte */ for( i = 0 ; i < count ; ) *q++ = run[i++]; //dbg("verbatim run"); } //DPRINTF("count = %d",count); } *q++ = 0; /* zero count - end of data */ return q-dst; } void getchannel(FormatRecordPtr pb){ pb->rowBytes = rb; pb->data = indata; SETRECT(pb->theRect,0,0,pb->imageSize.h,pb->imageSize.v); pb->hiPlane = pb->loPlane; pb->colBytes = hdr.bpc; } OSErr write_start(FormatRecordPtr pb){ OSErr e = noErr; hdr.magic = SGI_MAGIC; /* iris image file magic number */ hdr.storage = rle ? SGI_RLE : SGI_VERBATIM; /* storage format */ hdr.bpc = pb->depth/8; /* number of bytes per pixel channel */ if(pb->planes > 1) hdr.dimension = 3; /* number of dimensions */ else if(pb->imageSize.v > 1) hdr.dimension = 2; else hdr.dimension = 1; hdr.xsize = pb->imageSize.h; /* x size in pixels */ hdr.ysize = pb->imageSize.v; /* y size in pixels */ hdr.zsize = pb->planes; /* number of channels */ hdr.pixmin = (1L<depth)-1; /* minimum pixel value */ hdr.pixmax = 0; /* maximum pixel value */ hdr.dummy = 0; /* ignored */ memset(hdr.imagename,0,SGI_IMAGENAME_SIZE); /* image name */ hdr.colormap = SGI_NORMAL; /* colormap id */ tabsize = hdr.ysize*4L; rb = hdr.xsize*hdr.bpc; /* work out where image data will start */ offset = SGI_HEADER_SIZE; if(hdr.storage == SGI_RLE) offset += 2*hdr.zsize*tabsize; /* allow for RLE tables */ if( !(e = SETEOF(F,offset)) && !(e = SETFPOS(F,fsFromStart,offset)) ){ //if( !(e = write_sgi_header()) ) if( (offsets = malloc(tabsize)) && (counts = malloc(tabsize)) && (rowdata = malloc(2*rb)) && (indata = malloc(hdr.ysize*rb)) ){ pb->loPlane = 0; getchannel(pb); }else e = memFullErr; } return e; } OSErr write_continue(FormatRecordPtr pb){ unsigned i,j; unsigned char *row; FILECOUNT count; long v,*p; OSErr e = noErr; if(hdr.storage == SGI_RLE) e = SETFPOS(F,fsFromStart,offset); for( i = hdr.ysize,row = indata+rb*(hdr.ysize-1) ; i-- ; row -= rb ){ if( (e = checkpoint(pb,i)) ) break; for( j = hdr.xsize ; j-- ; ){ if(hdr.bpc == 2) v = ((unsigned short*)row)[j] = fix16( ((unsigned short*)row)[j] ); else v = row[j]; if(v < hdr.pixmin) hdr.pixmin = v; if(v > hdr.pixmax) hdr.pixmax = v; } if(hdr.bpc == 2 && platform_is_LittleEndian()) swapbytes(row,hdr.xsize); if(hdr.storage == SGI_VERBATIM){ count = rb; if( (e = FSWRITE(F,&count,row)) ) break; }else{ count = hdr.bpc == 1 ? compressrow(row,rowdata,hdr.xsize) : 2*compressrow2((unsigned short*)row,(unsigned short*)rowdata,hdr.xsize); counts[hdr.ysize-i-1] = count; offsets[hdr.ysize-i-1] = offset; offset += count; if( (e = FSWRITE(F,&count,rowdata)) ) break; } } if(hdr.storage == SGI_RLE){ if(!(e = SETFPOS(F,fsFromStart,SGI_HEADER_SIZE+pb->loPlane*tabsize)) ){ for( i = hdr.ysize,p = offsets ; i-- ; ) if( (e = write4B(F,*p++)) ){ dbg("error writing offsets"); break; } } if(!(e = SETFPOS(F,fsFromStart,SGI_HEADER_SIZE+(hdr.zsize+pb->loPlane)*tabsize)) ){ for( i = hdr.ysize,p = counts ; i-- ; ) if( (e = write4B(F,*p++)) ){ dbg("error writing offsets"); break; } } } if(++pb->loPlane < pb->planes){ getchannel(pb); }else{ pb->data = NULL; SETRECT(pb->theRect,0,0,0,0); } return e; } OSErr write_finish(FormatRecordPtr pb){ OSErr e; if(!(e = SETFPOS(F,fsFromStart,0)) ) e = write_sgi_header(F,&hdr); free(rowdata); free(indata); return e; }