/* This file is part of "12bits", a filter plugin for Adobe Photoshop Copyright (C) 2004-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 */ /* 12bits plugin - Quantise each channel of an image, keeping only 4 most significant bits. */ #include #include #include #include "world.h" #include "SPConfig.h" #include "PIFilter.h" #include "PIAbout.h" #include "entry.h" #include "dbg.h" typedef int ERRTYPE; long toprow,chunkrows; DLLEXPORT MACPASCAL void ENTRYPOINT(short selector, FilterRecordPtr epb, intptr_t *data, short *result); void DoAbout(AboutRecordPtr); void DoPrepare(FilterRecordPtr epb); OSErr DoStart(FilterRecordPtr epb); void process(FilterRecordPtr pb); OSErr DoContinue(FilterRecordPtr epb); void DoFinish(FilterRecordPtr epb); void RequestNext(FilterRecordPtr epb, long, int); void startplane(FilterRecordPtr pb, int plane); int fsdither(int input, ERRTYPE *e0, ERRTYPE *e1); long errbytes; ERRTYPE *err0,*err1; //*outp++ = (*inp++ >> 4) * 17; // better quantisation function inspired by Jorge (thisismyother@hotmail.com) #define QUANTISE(v) (( ((v) + 8)/17 )*17) DLLEXPORT MACPASCAL void ENTRYPOINT(short selector, FilterRecordPtr pb, intptr_t *data, short *result){ OSErr e = noErr; EnterCodeResource(); switch (selector){ case filterSelectorAbout: DoAbout((AboutRecordPtr)pb); break; case filterSelectorParameters: break; case filterSelectorPrepare: DoPrepare(pb); break; case filterSelectorStart: e = DoStart(pb); break; case filterSelectorContinue: e = DoContinue(pb); break; case filterSelectorFinish: DoFinish(pb); break; default: e = filterBadParameters; } *result = e; ExitCodeResource(); } void DoPrepare(FilterRecordPtr pb){ /* maxSpace lets the plugŠin know the maximum number of bytes of information it can expect to be able to access at once (input area size + output area size + mask area size + bufferSpace). bufferSpace If the plugŠin is planning on allocating any large internal buffers or tables, it should set this field during the filterSelectorPrepare call to the number of bytes it is planning to allocate. Photoshop will then try to free up the requested amount of space before calling the filterSelectorStart routine. What this means for us is, since we're NOT planning to allocate any buffers (we only need access to input data and output data), we can work out how many rows it would be prudent to request at one time: */ //pb->maxSpace = 2 * chunkrows * pb->planes * ((pb->imageSize.h * pb->depth + 7)/8); chunkrows = (pb->maxSpace/4) / (pb->planes*(long)pb->imageSize.h); if(chunkrows > (pb->filterRect.bottom - pb->filterRect.top)) chunkrows = pb->filterRect.bottom - pb->filterRect.top; } void RequestNext(FilterRecordPtr pb, long toprow, int p){ /* Request next block of the image */ pb->inLoPlane = pb->outLoPlane = pb->inHiPlane = pb->outHiPlane = p; pb->inRect.left = pb->outRect.left = pb->filterRect.left; pb->inRect.right = pb->outRect.right = pb->filterRect.right; pb->inRect.top = pb->outRect.top = toprow; pb->inRect.bottom = toprow + chunkrows; if(pb->inRect.bottom > pb->filterRect.bottom) pb->inRect.bottom = pb->filterRect.bottom; pb->outRect.bottom = pb->inRect.bottom; // SPRINTF(s,"RequestNext inRect = %d,%d,%d,%d",pb->inRect.left,pb->inRect.top,pb->inRect.right,pb->inRect.bottom); dbg(s); } void startplane(FilterRecordPtr pb, int plane){ RequestNext(pb,toprow = pb->filterRect.top,plane); if(errbytes){ memset(err0,0,errbytes); memset(err1,0,errbytes); } } OSErr DoStart(FilterRecordPtr pb){ #ifdef DITHER /* get error buffers for F-S dither */ errbytes = sizeof(ERRTYPE)*((long)pb->imageSize.h+2); if( !(err0 = malloc(errbytes)) || !(err1 = malloc(errbytes)) ){ dbg("Can't allocate err0/err1!"); return memFullErr; } #else errbytes = 0; #endif startplane(pb,0); return noErr; } OSErr DoContinue (FilterRecordPtr pb){ /* See if user has aborted the operation */ if (pb->abortProc()) return userCanceledErr; else{ pb->progressProc(pb->inLoPlane*(pb->filterRect.bottom - pb->filterRect.top) + toprow - pb->filterRect.top, pb->planes*(pb->filterRect.bottom - pb->filterRect.top)); // SPRINTF(s,"RequestNext inRect = %d,%d,%d,%d",pb->inRect.left,pb->inRect.top,pb->inRect.right,pb->inRect.bottom); dbg(s); // got a chunk of the image; process it process(pb); toprow += chunkrows; if(toprow >= pb->filterRect.bottom){ ++pb->inLoPlane; if(pb->inLoPlane < pb->planes) startplane(pb,pb->inLoPlane); else{ pb->inRect.left = pb->inRect.right = pb->inRect.top = pb->inRect.bottom = 0 ; pb->outRect = pb->maskRect = pb->inRect; } }else RequestNext(pb,toprow,pb->inLoPlane); return noErr; } } void DoFinish(FilterRecordPtr pb){ if(errbytes){ free(err0); free(err1); } } int fsdither(int input, ERRTYPE *e0, ERRTYPE *e1){ int val,targ; // compute target RGB (taking accumulated error into account) targ = input + e0[0]/16; // get output RGB val = QUANTISE(targ); if(val < 0) val = 0; else if(val > 0xff) val = 0xff; // compute error targ -= val; // distribute error to neighbourhood, per Floyd-Steinberg (1975): // * 7 // 3 5 1 e0[ 1] += 7*targ; e1[-1] += 3*targ; e1[ 0] += 5*targ; e1[ 1] += targ; return val; } void process(FilterRecordPtr pb){ ERRTYPE *e0,*e1,*t; unsigned char *inrow,*outrow,*inp,*outp; long rows,cols,ncols = pb->inRect.right - pb->inRect.left; // actually munge the data. // each input pixel (byte) is quantised to sixteen values: 0,17,34,...,255 // (this slightly increases contrast, as input values are effectively multiplied by 17/16) for(inrow=pb->inData,outrow=pb->outData,rows=pb->inRect.bottom-pb->inRect.top ; rows-- ; inrow+=pb->inRowBytes,outrow+=pb->outRowBytes) { for(inp=inrow,outp=outrow,cols=ncols,e0=err0,e1=err1 ; cols-- ; ){ #ifdef DITHER *outp++ = fsdither(*inp++,++e0,++e1); #else *outp++ = QUANTISE(*inp++); #endif } if(errbytes){ t = err0; err0 = err1; err1 = t; memset(err1,0,errbytes); // clear new error row } } }