/* This file is part of "Histotext", a filter plugin for Adobe Photoshop Copyright (C) 2005-8 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 "world.h" #include "PIFilter.h" #include "entry.h" #include "file_compat.h" #include "choosefile.h" //#include "dbg.h" #include "ui.h" /* use Photoshop's handle suite, since we don't know in advance how much memory will be needed */ #define NewPIHandle pb->handleProcs->newProc #define LockPIHandle pb->handleProcs->lockProc #define UnlockPIHandle pb->handleProcs->unlockProc #define DisposePIHandle pb->handleProcs->disposeProc #define MALLOC NewPIHandle #define LOCK(h) LockPIHandle(h,false) #define UNLOCK UnlockPIHandle #define FREE(h) DisposePIHandle((Handle)h) enum{ CHUNK_ROWS = 128, MAXCHANNELS = 32 }; long toprow, ncols, nrows; FILEREF outfile; long hist[0x100]; short min[MAXCHANNELS], med[MAXCHANNELS], max[MAXCHANNELS]; NavReplyRecord reply; long long total[MAXCHANNELS]; unsigned char *pixels, *pixrow; Handle hpixels; DLLEXPORT MACPASCAL void ENTRYPOINT(short selector, FilterRecordPtr epb, long *data, short *result); void DoPrepare(FilterRecordPtr pb); void DoStart(FilterRecordPtr pb, int p); OSErr process(FilterRecordPtr pb); OSErr DoContinue(FilterRecordPtr pb); OSErr DoFinish(FilterRecordPtr pb); void RequestNext(FilterRecordPtr pb, int p, long); DLLEXPORT MACPASCAL void ENTRYPOINT(short selector, FilterRecordPtr pb, long *data, short *result){ OSErr e = noErr; char buf[0x100]; long count; StandardFileReply sfr; EnterCodeResource(); switch (selector){ case filterSelectorAbout: DoAbout((AboutRecordPtr)pb); break; case filterSelectorParameters: break; case filterSelectorPrepare: pb->maxSpace = 4096; /* nominal memory requirement */ ncols = pb->filterRect.right - pb->filterRect.left; nrows = pb->filterRect.bottom - pb->filterRect.top; if( !(hpixels = NewPIHandle(ncols*nrows)) ) e = memFullErr; //sprintf(buf,"nrows=%ld ncols=%ld hpixels=%#x\n",nrows,ncols,hpixels);dbg(buf); break; case filterSelectorStart: pixels = (unsigned char*)LockPIHandle(hpixels, false); if(putfile("\pSave histogram", "\phistotext.txt", 'TEXT', 'ttxt', &reply, &sfr, "TXT", "Text file (.TXT)\0*.TXT\0\0", 1)) { //FSMakeFSSpec(0,0,"\phistotext.txt",&spec); //FSpDelete(&sfr.sfFile); FSpCreate(&sfr.sfFile, 'ttxt', 'TEXT', sfr.sfScript); /* ignore any error - most likely due to pre-existing file */ if( !(e = FSpOpenDF(&sfr.sfFile, fsWrPerm, &outfile)) ){ SetEOF(outfile, 0); /* truncate file to empty */ count = sprintf(buf, "Planes\t Cols\t Rows\t Count\015\012%6d\t%6ld\t%6ld\t%6ld\015\012", pb->planes, ncols, nrows, ncols*nrows); if( !(e = FSWRITE(outfile, &count, buf)) ) DoStart(pb, 0); } } break; case filterSelectorContinue: e = DoContinue(pb); break; case filterSelectorFinish: DoFinish(pb); FSClose(outfile); completesave(&reply); DisposePIHandle(hpixels); e = userCanceledErr; // so Photoshop doesn't think the image data has changed break; default: e = filterBadParameters; } *result = e; ExitCodeResource(); } void RequestNext(FilterRecordPtr pb, int p, long toprow){ /* Request next block of the image */ pb->inLoPlane = pb->outLoPlane = pb->inHiPlane = pb->outHiPlane = p; pb->inRect.left = pb->filterRect.left; pb->inRect.right = pb->filterRect.right; pb->inRect.top = toprow; pb->inRect.bottom = toprow + CHUNK_ROWS; if(pb->inRect.bottom > pb->filterRect.bottom) pb->inRect.bottom = pb->filterRect.bottom; pb->outRect = pb->inRect; // sprintf(s,"RequestNext inRect = %d,%d,%d,%d",pb->inRect.left,pb->inRect.top,pb->inRect.right,pb->inRect.bottom); dbg(s); } void DoStart (FilterRecordPtr pb, int p){ int i; for(i = 0; i < 0x100; ++i) hist[i] = 0; total[p] = 0; toprow = pb->filterRect.top; pixrow = pixels; RequestNext(pb, p, toprow); } OSErr process(FilterRecordPtr pb){ unsigned char *inrow, *inp; int nr, i, j, nc; nr = pb->inRect.bottom - pb->inRect.top; nc = pb->inRect.right - pb->inRect.left; for(inrow = pb->inData, i = 0; i < nr; ++i, inrow += pb->inRowBytes){ memcpy(pixrow, inrow, nc); pixrow += nc; for(inp = inrow, j = 0; j < nc; ++j){ ++hist[*inp]; total[pb->inLoPlane] += *inp; ++inp; } } return noErr; } int cmp(const void *, const void *); int cmppix(const void *, const void *); int cmp(const void *a, const void *b){ /* not just a subtraction, which would be wrong if int is smaller than long */ return *(long*)a < *(long*)b ? -1 : *(long*)a > *(long*)b; } int cmppix(const void *a, const void *b){ return *(unsigned char*)a - *(unsigned char*)b; } OSErr DoContinue(FilterRecordPtr pb){ OSErr e = noErr; char *p, buf[0x100 * 7 + 10]; /* enough space for one line */ int i, pl = pb->inLoPlane; long count; /* See if user has aborted the operation */ if(pb->abortProc()) return userCanceledErr; else{ pb->progressProc(pl*nrows + toprow - pb->filterRect.top, pb->planes*nrows); // got a chunk of the image; process it if( !(e = process(pb)) ){ toprow += CHUNK_ROWS; if(toprow < pb->filterRect.bottom) RequestNext(pb, pl, toprow); else{ /* finished with this plane, output the histogram counts */ for(i = 0, p = buf; i < 0x100; ++i) p += sprintf(p, "%6ld\t", hist[i]); *p++ = '\015'; /* CR */ *p++ = '\012'; /* LF */ count = p - buf; e = FSWRITE(outfile, &count, buf); qsort(pixels, ncols*nrows, 1, cmppix); /* for amusement, copy sorted data back to image pixrow = pixels; for( outrow = pb->outData, i = 0 ; i < nrows ; ++i,outrow += pb->outRowBytes ){ memcpy(outrow,pixrow,ncols); pixrow += ncols; }*/ min[pl] = pixels[0]; med[pl] = pixels[(ncols*nrows)/2] + pixels[(ncols*nrows)/2 + 1]; max[pl] = pixels[ncols*nrows - 1]; ++ pl; if(!e && pl < pb->planes){ /* ask for next plane, starting at top */ DoStart(pb, pl); }else{ /* no more planes: we're finished */ SETRECT(pb->inRect, 0,0,0,0); pb->outRect = pb->maskRect = pb->inRect; } } } } return e; } OSErr DoFinish(FilterRecordPtr pb){ char buf[0x100], *p; int i; long count; double npix = nrows*ncols; p = buf + sprintf(buf, " Mean\t Min\tMedian\t Max\015\012"); for(i = 0; i < pb->planes; ++i){ p += sprintf(p, "%7.3f\t%6d\t%6.1f\t%6d\t\015\012", total[i]/npix, min[i], med[i]/2., max[i]); } count = p - buf; return FSWRITE(outfile, &count, buf); }