/* This file is part of "Count Colours", a filter plugin for Adobe Photoshop Copyright (C) 2003-7 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 */ /* Count Colours plugin */ #include #include "world.h" #include "PIFilter.h" #include "entry.h" #include "ui.h" //#include "dbg.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 typedef char FLAG_T; typedef unsigned long KEY_T; typedef unsigned long long KEY64_T; enum{ CHUNK_ROWS = 128, BLOCKBYTES = 1L<<20, // get this much memory at a time SUBKEYBITS = 4, // key is broken into sub-indexes this wide; should be factor of 24: 12, 8, 6, 4... SUBKEYMASK = (1<maxSpace = 0; /* all allocations done via Photoshop */ break; case filterSelectorStart: // pb->depth is only available to Filters in v5.0 or later bitdepth = pb->depth ? pb->depth : 8; total = mem = blocks = 0; if(pb->planes == 1){ if( (indicesh = NewPIHandle(sizeof(FLAG_T)<planes == 3){ if( (rooth = NewPIHandle(TABLESIZE)) ){ root = (Handle*)LockPIHandle(rooth,false); memset(root,0,TABLESIZE); }else e = memFullErr; }else{ alert("Count Colours can process only 1 or 3 channels at once."); e = userCanceledErr; } toprow = pb->filterRect.top; RequestNext(pb,toprow); break; case filterSelectorContinue: e = DoContinue(pb); break; case filterSelectorFinish: DoFinish(pb); freepool(pb,lastblock); if(indicesh) DisposePIHandle(indicesh); if(rooth) DisposePIHandle(rooth); e = userCanceledErr; // so Photoshop doesn't think the image data has changed break; default: e = filterBadParameters; } *result = e; ExitCodeResource(); } void RequestNext(FilterRecordPtr pb,long toprow){ /* Request next block of the image */ pb->inLoPlane = pb->outLoPlane = 0; pb->inHiPlane = pb->outHiPlane = pb->planes-1; 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 + CHUNK_ROWS; if(pb->inRect.bottom > pb->filterRect.bottom) pb->inRect.bottom = pb->filterRect.bottom; pb->outRect.bottom = pb->inRect.bottom; } Ptr newfrompool(FilterRecordPtr pb,long n){ Ptr p; if(blockbytes < n){ // won't fit in head block, need another one Handle h = NewPIHandle(BLOCKBYTES); mem += BLOCKBYTES; ++blocks; // just accounting lastptr = LockPIHandle(h,false); // add it to chain (*(Handle*)lastptr) = lastblock; lastblock = h; lastptr += sizeof(Handle); blockbytes = BLOCKBYTES - sizeof(Handle); } p = lastptr; lastptr += n; blockbytes -= n; return p; } void freepool(FilterRecordPtr pb,Handle root){ if(root){ Handle *p = (Handle*)LockPIHandle(root,false), h = *p; DisposePIHandle(root); freepool(pb,h); } } FLAG_T *lazyindir(FilterRecordPtr pb,Ptr *tbl,KEY_T key,int shift){ shift -= SUBKEYBITS; if(!shift) // down to leaf table? return (FLAG_T*)tbl + (key&SUBKEYMASK); else{ // no, must go deeper int j = (key>>shift) & SUBKEYMASK; if(!tbl[j]){ // missing entry? int n = shift==SUBKEYBITS /*new leaf table?*/ ? sizeof(FLAG_T)<>shift) & SUBKEYMASK; if(!tbl[j]){ int n = shift==SUBKEYBITS /*new leaf table?*/ ? (1<inRect.right - pb->inRect.left, nrows = pb->inRect.bottom - pb->inRect.top, i,j; for( inrow = pb->inData, i = 0 ; i < nrows ; ++i, inrow += pb->inRowBytes ){ if(pb->planes == 3){ // RGB image if(bitdepth == 8) for( inp = inrow, j = 0 ; j < ncols ; ++j, inp += pb->planes ){ KEY_T k = (inp[0]<<16) | (inp[1]<<8) | inp[2]; // construct key FLAG_T *p = lazyindir(pb,(Ptr*)root,k,24); // look it up if(!*p){ // not found? *p = 1; ++total; // count a new colour } } else // 16 bit depth for( inp16 = (unsigned short*)inrow, j = 0 ; j < ncols ; ++j, inp16 += pb->planes ){ KEY64_T k = ((KEY64_T)inp16[0]<<32) | ((KEY64_T)inp16[1]<<16) | (KEY64_T)inp16[2]; FLAG_T *p = lazyindir64(pb,(Ptr*)root,k,48); if(!*p){ *p = 1; ++total; } } }else{ // single channel if(bitdepth == 8) for( inp = inrow, j = 0 ; j < ncols ; ++j ) indices[*inp++] = 1; else // 16 bit depth for( inp16 = (unsigned short*)inrow, j = 0 ; j < ncols ; ++j ) indices[*inp16++] = 1; } } return noErr; } OSErr DoContinue(FilterRecordPtr pb){ OSErr e; /* See if user has aborted the operation */ if(pb->abortProc()) return userCanceledErr; else{ //char s[0x100]; //sprintf(s,"mem = %ld = %ld M\nblocks = %ld\nblockbytes = %ld",mem,mem>>20,blocks,blockbytes); //alert(s); pb->progressProc(toprow - pb->filterRect.top, pb->filterRect.bottom - pb->filterRect.top); // got a chunk of the image; process it if( (e = process(pb)) ) return e; else{ toprow += CHUNK_ROWS; if(toprow < pb->filterRect.bottom) RequestNext(pb,toprow); else{ // tell Photoshop we're done pb->inRect.left = pb->inRect.right = pb->inRect.top = pb->inRect.bottom = 0; pb->outRect = pb->maskRect = pb->inRect; } return noErr; } } } void DoFinish(FilterRecordPtr pb){ int i,rgb = pb->planes == 3, w = pb->filterRect.right - pb->filterRect.left, h = pb->filterRect.bottom - pb->filterRect.top; char s[0x100]; long n; if(rgb) n = total; else for( i = 1<planes,rgb ? "s" : "",bitdepth,w,h,(long)w*h); alert(s); //sprintf(s,"mem = %ld = %ld M\nblocks = %ld\nblockbytes = %ld",mem,mem>>20,blocks,blockbytes); //alert(s); }