Subversion Repositories filter_foundry

Rev

Rev 256 | Rev 264 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
259 daniel-mar 1
/*
2
        This file is part of "Filter Foundry", a filter plugin for Adobe Photoshop
3
        Copyright (C) 2003-2009 Toby Thain, toby@telegraphics.com.au
4
        Copyright (C) 2018-2021 Daniel Marschall, ViaThinkSoft
5
 
6
        This program is free software; you can redistribute it and/or modify
7
        it under the terms of the GNU General Public License as published by
8
        the Free Software Foundation; either version 2 of the License, or
9
        (at your option) any later version.
10
 
11
        This program is distributed in the hope that it will be useful,
12
        but WITHOUT ANY WARRANTY; without even the implied warranty of
13
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
        GNU General Public License for more details.
15
 
16
        You should have received a copy of the GNU General Public License
17
        along with this program; if not, write to the Free Software
18
        Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
*/
20
 
21
//#include <stdio.h>
22
//#include <sound.h>
23
 
24
#include "ff.h"
25
 
26
#include "node.h"
27
#include "funcs.h"
28
#include "y.tab.h"
29
#include "scripting.h"
30
#include <math.h>
31
#include "PIBufferSuite.h"
32
 
33
// GIMP (PSPI) and IrfanView preserve neither *data(gdata), nor pb->parameters between invocations!
34
// For debugging, we can simulate it here
35
//#define DEBUG_SIMULATE_GIMP
36
 
37
struct node *tree[4];
38
char *err[4];
39
int errpos[4],errstart[4],nplanes,cnvused,chunksize,toprow;
40
value_type slider[8],cell[NUM_CELLS],map[4][0x100];
41
char *expr[4];
42
// long maxSpace;
43
globals_t *gdata;
44
FilterRecordPtr gpb;
45
 
46
#ifdef MAC_ENV
47
        #define HINSTANCE HANDLE
48
        #define hDllInstance NULL /* fake this Windows-only global */
49
#endif
50
 
51
#ifdef WIN_ENV
52
#include "manifest.h"
53
#endif
54
 
55
extern struct sym_rec predefs[];
56
extern int nplanes,varused[];
57
 
58
int checkandinitparams(Handle params);
59
 
60
// MPW MrC requires prototype
61
DLLEXPORT MACPASCAL
62
void ENTRYPOINT(short selector,FilterRecordPtr pb,intptr_t *data,short *result);
63
 
64
unsigned long get_parm_hash(PARM_T parm) {
65
        unsigned long hash;
66
        int i;
67
 
68
        hash = djb2((char*)parm.category);
69
        hash += djb2((char*)parm.title);
70
        hash += djb2((char*)parm.copyright);
71
        hash += djb2((char*)parm.author);
72
        for (i = 0; i < 4; i++) hash += hash += djb2((char*)parm.map[i]);
73
        for (i = 0; i < 8; i++) hash += hash += djb2((char*)parm.ctl[i]);
74
        for (i = 0; i < 4; i++) hash += hash += djb2((char*)parm.formula[i]);
75
 
76
        return hash;
77
}
78
 
79
DLLEXPORT MACPASCAL
80
void ENTRYPOINT(short selector, FilterRecordPtr pb, intptr_t *data, short *result){
81
        static Boolean wantdialog = false;
82
        static Boolean premiereWarnedOnce = false;
83
        OSErr e = noErr;
84
        char *reason;
85
#ifdef WIN_ENV
86
        // For Windows, we use an activation context to enforce that our Manifest resource will
87
        // be used. This allows us to use Visual Styles, even if the host application does not
88
        // support it.
89
        ManifestActivationCtx manifestVars;
90
        BOOL activationContextUsed;
91
#endif
92
 
93
        EnterCodeResource();
94
 
95
        /*
96
        char* s = (char*)malloc(512);
97
        sprintf(s, "Host signature: '%c%c%c%c' (%d)\nMaxSpace32 = %d\nMaxSpace64 = %lld\nNum buffer procs: %d", (pb->hostSig >> 24) & 0xFF, (pb->hostSig >> 16) & 0xFF, (pb->hostSig >> 8) & 0xFF, pb->hostSig & 0xFF, pb->hostSig, pb->maxSpace, pb->maxSpace64, pb->bufferProcs->numBufferProcs);
98
        simplealert(s);
99
        */
100
 
101
        if (pb->hostSig == HOSTSIG_ADOBE_PREMIERE) {
102
                // DM 19.07.2021 : Tried running the 8BF file in Adobe Premiere 5 (yes, that's possible,
103
                // and there is even a FilterFactory for Premeire!),
104
                // but it crashes in evalpixel() where there is write-access to the "outp".
105
                // Probably the canvas structure is different (maybe it contains frames to achieve transitions?)
106
                if (!premiereWarnedOnce) {
107
                        simplealert(_strdup("This version of Filter Foundry is not compatible with Adobe Premiere!"));
108
                }
109
                premiereWarnedOnce = true;
110
                *result = errPlugInHostInsufficient;
111
                return;
112
        }
113
 
114
        #ifdef DEBUG_SIMULATE_GIMP
115
        *data = 0;
116
        pb->parameters = pb->handleProcs->newProc(1);
117
        #endif
118
 
119
        // Register "gdata" that contains the PARM information and other things which need to be persistant
120
        // and preserve then in *data
121
        // TODO: memory leak? where is the stuff freed?
122
        if (selector != filterSelectorAbout && !*data) {
123
                /*
124
                PSBufferSuite1* pSBufferSuite32 = NULL;
125
 
126
                if ((pb->sSPBasic == 0) ||
127
                        (pb->sSPBasic->AcquireSuite(kPSBufferSuite, kPSBufferSuiteVersion1, (const void**)&pSBufferSuite32)) ||
128
                        (pSBufferSuite32 == NULL))
129
                {
130
                                // Old deprecated buffer suite
131
                                BufferID tempId;
132
                                if ((*result = pb->bufferProcs->allocateProc(sizeof(globals_t), &tempId))) return;
133
                                *data = (intptr_t)pb->bufferProcs->lockProc(tempId, true);
134
                                gdata = (globals_t*)*data;
135
                }
136
                else
137
                {
138
                                // New buffer suite (but only 32-bit version 1, because version 2 has problems with old Photoshop versions)
139
                                // Windows Photoshop 7 and CS 2 accepts kPSBufferSuiteVersion2, but doesn't correctly implement it:
140
                                // The symbols "New" and "GetSpace64" point to memory memory addresses outside the Photoshop.exe address range.
141
                                // (Other Photoshop versions were not tested.)
142
                                // 64-bit support for Windows was established in Photoshop CS 4,
143
                                // and PSBufferSuite2 was first documented in SDK CS 6.
144
                                // So, kPSBufferSuiteVersion2 probably was partically implemented as hidden "Work in progress" version
145
                                // before it was publicly documented.
146
                                // Side note:  pb->bufferSpace64/pb->maxSpace64 was documented in SDK CC 2017.
147
                                //             pb->bufferProcs->allocateProc64/spaceProc64 was documented in SDK CS 6.
148
                                unsigned32 siz = sizeof(globals_t);
149
                                *data = (intptr_t)pSBufferSuite32->New(&siz, siz);
150
                                if ((*data == 0) || (siz == 0)) {
151
                                                *result = errPlugInHostInsufficient; // TODO: what is the correct error code for "out of memory"?
152
                                                return;
153
                                }
154
                                gdata = (globals_t*)*data;
155
                                pb->sSPBasic->ReleaseSuite(kPSBufferSuite, kPSBufferSuiteVersion1);
156
                }
157
                gdata->standalone = gdata->parmloaded = false;
158
                */
159
 
160
                // We have 3 options:
161
                // - The deprecated buffer suite (pb->bufferProcs), works fine
162
                // - The recommended buffer suite (kPSBufferSuite), does NOT work (causes memory corruption?) and is not available on some hosts!
163
                //   Either I do something wrong, or maybe it cannot be used to store data between invocations?
164
                // - Using malloc(), which works also fine and is more independent from the host and easier
165
                *data = (intptr_t)malloc(sizeof(globals_t));
166
                if (*data == 0) return;
167
                gdata = (globals_t*)*data;
168
                gdata->standalone = gdata->parmloaded = false; // they will be set later
169
        }
170
        else {
171
                // We have data from the previous invocation. Use it instead
172
                gdata = (globals_t*)*data;
173
        }
174
 
175
        #ifdef WIN_ENV
176
        activationContextUsed = ActivateManifest((HMODULE)hDllInstance, 1, &manifestVars);
177
        #endif
178
 
179
        gpb = pb;
180
 
181
        nplanes = MIN(pb->planes,4);
182
 
183
        switch (selector){
184
        case filterSelectorAbout:
185
                if (!gdata) {
186
                        gdata = (globals_t*)malloc(sizeof(globals_t));
187
                        if (!gdata) break;
188
                        gdata->standalone = gdata->parmloaded = readPARMresource((HMODULE)hDllInstance,&reason,READ_OBFUSC);
189
                        DoAbout((AboutRecordPtr)pb);
190
                        free(gdata);
191
                        gdata = NULL;
192
                } else {
193
                        DoAbout((AboutRecordPtr)pb);
194
                }
195
                break;
196
        case filterSelectorParameters:
197
                wantdialog = true;
198
                break;
199
        case filterSelectorPrepare:
200
                DoPrepare(pb);
201
                init_symtab(predefs); // ready for parser calls
202
                init_trigtab();
203
                break;
204
        case filterSelectorStart:
205
                if (HAS_BIG_DOC(pb)) {
206
                        // The BigDocument structure is required if the document is larger than 30,000 pixels
207
                        // It deprecates imageSize, filterRect, inRect, outRect, maskRect, floatCoord, and wholeSize.
208
                        // By setting it to nonzero, we communicate to Photoshop that we support the BigDocument structure.
209
                        pb->bigDocumentData->PluginUsing32BitCoordinates = true;
210
                }
211
 
212
                /* initialise the parameter handle that Photoshop keeps for us */
213
                if(!pb->parameters)
214
                        pb->parameters = PINEWHANDLE(1); // don't set initial size to 0, since some hosts (e.g. GIMP/PSPI) are incompatible with that.
215
 
216
                wantdialog |= checkandinitparams(pb->parameters);
217
 
218
                /* wantdialog = false means that we never got a Parameters call, so we're not supposed to ask user */
219
                if( wantdialog && (!gdata->standalone || gdata->parm.popDialog) ){
220
                        if( maindialog(pb) ){
221
                                if (!host_preserves_parameters()) {
222
                                        if (!gdata->obfusc) { // If the filter is obfuscated, we may not save the formula to the .afs (TODO: just save the ctl/map, but not the r/g/b/a-formulas!)
223
                                                /* Workaround for GIMP/PSPI, to avoid that formulas vanish when you re-open the main window.
224
                                                   The reason is a bug in PSPI: The host should preserve the value of pb->parameters, which PSPI does not do.
225
                                                   Also, all global variables are unloaded, so the plugin cannot preserve any data.
226
                                                   Workaround in FF 1.7: If the host GIMP is detected, then a special mode will be activated.
227
                                                   This mode saves the filter data into a temporary file "FilterFoundryXX.afs" and loads it
228
                                                   when the window is opened again. */
229
                                                // Workaround: Save settings in "FilterFoundryXX.afs" if the host does not preserve pb->parameters
230
                                                char outfilename[255];
231
                                                char* tempdir;
232
                                                int hash;
233
                                                StandardFileReply sfr;
234
                                                sfr.sfGood = true;
235
                                                sfr.sfReplacing = true;
236
                                                sfr.sfType = PS_FILTER_FILETYPE;
237
 
238
                                                tempdir = getenv("TMP");
239
                                                #ifdef WIN_ENV
240
                                                if (strlen(tempdir) > 0) strcat(tempdir, "\\");
241
                                                #else
242
                                                if (strlen(tempdir) > 0) strcat(tempdir, "/");
243
                                                #endif
244
 
245
                                                hash = (gdata->standalone) ? get_parm_hash(gdata->parm) : 0;
246
                                                sprintf(outfilename, "%sFilterFoundry%d.afs", tempdir, hash);
247
 
248
                                                myc2pstrcpy(sfr.sfFile.name, outfilename);
249
                                                #ifdef WIN_ENV
250
                                                sfr.nFileExtension = (WORD)(strlen(outfilename) - strlen(".afs"));
251
                                                #endif
252
                                                sfr.sfScript = 0; // FIXME: is that ok?
253
                                                savefile(&sfr);
254
                                        }
255
                                }
256
 
257
                                /* update stored parameters from new user settings */
258
                                saveparams(pb->parameters);
259
                        }else
260
                                e = userCanceledErr;
261
                }
262
                wantdialog = false;
263
 
264
                if(!e){
265
                        if(setup(pb)){
266
                                DoStart(pb);
267
                        }else{
268
                                SYSBEEP(1);
269
                                e = filterBadParameters;
270
                        }
271
                }
272
                break;
273
        case filterSelectorContinue:
274
                e = DoContinue(pb);
275
                break;
276
        case filterSelectorFinish:
277
                DoFinish(pb);
278
                break;
279
        default:
280
                e = filterBadParameters;
281
        }
282
 
283
        *result = e;
284
 
285
        #ifdef WIN_ENV
286
        if (activationContextUsed) DeactivateManifest(&manifestVars);
287
        #endif
288
 
289
        ExitCodeResource();
290
}
291
 
292
int checkandinitparams(Handle params){
293
        char *reasonstr,*reason;
294
        int i,bUninitializedParams;
295
        Boolean showdialog;
296
 
297
        if (!host_preserves_parameters()) {
298
                // Workaround: Load settings in "FilterFoundryXX.afs" if host does not preserve pb->parameters
299
                char outfilename[255];
300
                char* tempdir;
301
                int hash;
302
                Boolean isStandalone;
303
                StandardFileReply sfr;
304
                sfr.sfGood = true;
305
                sfr.sfReplacing = true;
306
                sfr.sfType = PS_FILTER_FILETYPE;
307
 
308
                // We need to set gdata->standalone after loadfile(), but we must call readPARMresource() before loadfile()
309
                // Reason: readPARMresource() reads parameters from the DLL while loadfile() reads parameters from the AFS file
310
                // But loadfile() will reset gdata->standalone ...
311
                isStandalone = readPARMresource((HMODULE)hDllInstance, &reason, READ_OBFUSC);
312
 
313
                if (!gdata->obfusc) { // If the filter is obfuscated, we may not save the formula to the .afs (TODO: just save the ctl/map, but not the r/g/b/a-formulas!)
314
                        tempdir = getenv("TMP");
315
                        #ifdef WIN_ENV
316
                        if (strlen(tempdir) > 0) strcat(tempdir, "\\");
317
                        #else
318
                        if (strlen(tempdir) > 0) strcat(tempdir, "/");
319
                        #endif
320
 
321
                        hash = (isStandalone) ? get_parm_hash(gdata->parm) : 0;
322
                        sprintf(outfilename, "%sFilterFoundry%d.afs", tempdir, hash);
323
 
324
                        myc2pstrcpy(sfr.sfFile.name, outfilename);
325
                        #ifdef WIN_ENV
326
                        sfr.nFileExtension = (WORD)(strlen(outfilename) - strlen(".afs"));
327
                        #endif
328
                        sfr.sfScript = 0; // FIXME: is that ok?
329
 
330
                        if (loadfile(&sfr, &reason)) {
331
                                gdata->standalone = gdata->parmloaded = isStandalone;
332
                                return true;
333
                        }
334
                }
335
        }
336
 
337
        if( (bUninitializedParams = !(params && readparams(params,false,&reasonstr))) ){
338
                /* either the parameter handle was uninitialised,
339
                   or the parameter data couldn't be read; set default values */
340
 
341
                // see if saved parameters exist
342
                gdata->standalone = gdata->parmloaded = readPARMresource((HMODULE)hDllInstance,&reason,READ_OBFUSC);
343
 
344
 
345
                if(!gdata->standalone){
346
                        // no saved settings (not standalone)
347
                        for(i = 0; i < 8; ++i)
348
                                slider[i] = i*10+100;
349
                        for(i = 0; i < 4; ++i)
350
                                if(expr[i])
351
                                        free(expr[i]);
352
                        if(gpb->imageMode == plugInModeRGBColor){
353
                                expr[0] = _strdup("r");
354
                                expr[1] = _strdup("g");
355
                                expr[2] = _strdup("b");
356
                                expr[3] = _strdup("a");
357
                        }else{
358
                                expr[0] = _strdup("c");
359
                                expr[1] = _strdup("c");
360
                                expr[2] = _strdup("c");
361
                                expr[3] = _strdup("c");
362
                        }
363
                }
364
        }
365
 
366
        // let scripting system change parameters, if we're scripted;
367
        // user may want to force display of dialog during scripting playback
368
        switch (ReadScriptParamsOnRead()) {
369
        case SCR_SHOW_DIALOG:
370
                showdialog = true;
371
                break;
372
        case SCR_HIDE_DIALOG:
373
                showdialog = false;
374
                break;
375
        default:
376
        case SCR_NO_SCRIPT:
377
                showdialog = bUninitializedParams;
378
                break;
379
        }
380
 
381
        saveparams(params);
382
 
383
        return showdialog;
384
}
385
 
386
Boolean host_preserves_parameters() {
387
        #ifdef DEBUG_SIMULATE_GIMP
388
        return false;
389
        #endif
390
 
391
        if (gpb->hostSig == HOSTSIG_GIMP) return false;
392
        if (gpb->hostSig == HOSTSIG_IRFANVIEW) return false;
393
 
394
        /*
395
        char x[100];
396
        sprintf(x, "Host Signature: %u", gpb->hostSig);
397
        simplealert(x);
398
        */
399
 
400
        // We just assume the other hosts preserve the parameters
401
        return true;
402
}
403
 
404
int64_t maxspace(){
405
        // Please see "Hosts.md" for details about the MaxSpace implementations of tested plugins
406
 
407
        // Plugins that don't support MaxSpace64 shall set the field to zero; then we will use MaxSpace instead.
408
        // Also check "gpb->bufferProcs->numBufferProcs" to see if 64 bit API is available
409
        if ((gpb->bufferProcs->numBufferProcs >= 8) && (gpb->maxSpace64 > 0)) {
410
                uint64_t maxSpace64 = gpb->maxSpace64;
411
 
412
                return maxSpace64;
413
        } else {
414
                // Note: If maxSpace gets converted from Int32 to unsigned int, we can reach up to 4 GB RAM. However, after this, there will be a wrap to 0 GB again.
415
                unsigned int maxSpace32 = (unsigned int) gpb->maxSpace;
416
                uint64_t maxSpace64 = maxSpace32;
417
 
418
                if (gpb->hostSig == HOSTSIG_IRFANVIEW) maxSpace64 *= 1024; // IrfanView is giving Kilobytes instead of Bytes
419
                //if (gpb->hostSig == HOSTSIG_SERIF_PHOTOPLUS) maxSpace64 *= 1024; // TODO: Serif PhotoPlus also gives Kilobytes instead of bytes. But since it uses not a unique host signature, there is nothing we can do???
420
 
421
                return maxSpace64;
422
        }
423
}
424
 
425
Boolean maxspace_available() {
426
        // Please see "Hosts.md" for details about the MaxSpace implementations of tested plugins
427
 
428
        // GIMP PSPI sets MaxSpace to hardcoded 100 MB
429
        if (gpb->hostSig == HOSTSIG_GIMP) return false;
430
 
431
        // HOSTSIG_PAINT_NET sets MaxSpace to hardcoded 1 GB, see https://github.com/0xC0000054/PSFilterPdn/issues/5
432
        // Comment by the host author "This was done to avoid any compatibility issues with plugins handling 2 GB - 1"
433
        if (gpb->hostSig == HOSTSIG_PAINT_NET) return false;
434
 
435
        return true;
436
}
437
 
438
void DoPrepare(FilterRecordPtr pb){
439
        int i;
440
 
441
        for(i = 4; i--;){
442
                if(expr[i]||tree[i]) DBG("expr[] or tree[] non-NULL in Prepare!");
443
                expr[i] = NULL;
444
                tree[i] = NULL;
445
        }
446
 
447
        // Commented out by DM, 18 Dec 2018:
448
        // This code did not work on systems with 8 GB RAM:
449
        /*
450
        long space = (pb->maxSpace*9)/10; // don't ask for more than 90% of available memory
451
 
452
        maxSpace = 512L<<10; // this is a wild guess, actually
453
        if(maxSpace > space)
454
                        maxSpace = space;
455
        pb->maxSpace = maxSpace;
456
        */
457
 
458
        // New variant:
459
        if (maxspace_available()) {
460
                pb->maxSpace = (int32)ceil((maxspace()/10.)*9); // don't ask for more than 90% of available memory
461
                // FIXME: Also maxSpace64
462
        }
463
}
464
 
465
void RequestNext(FilterRecordPtr pb,long toprow){
466
        /* Request next block of the image */
467
 
468
        pb->inLoPlane = pb->outLoPlane = 0;
469
        pb->inHiPlane = pb->outHiPlane = nplanes-1;
470
 
471
        if (HAS_BIG_DOC(pb)) {
472
                // if any of the formulae involve random access to image pixels,
473
                // ask for the entire image
474
                if (needall) {
475
                        SETRECT(BIGDOC_IN_RECT(pb), 0, 0, BIGDOC_IMAGE_SIZE(pb).h, BIGDOC_IMAGE_SIZE(pb).v);
476
                } else {
477
                        // TODO: This does not work with GIMP. So, if we are using GIMP, we should
478
                        //       somehow always use "needall=true", and/or find out why this doesn't work
479
                        //       with GIMP.
480
 
481
                        // otherwise, process the filtered area, by chunksize parts
482
                        BIGDOC_IN_RECT(pb).left = BIGDOC_FILTER_RECT(pb).left;
483
                        BIGDOC_IN_RECT(pb).right = BIGDOC_FILTER_RECT(pb).right;
484
                        BIGDOC_IN_RECT(pb).top = (int32)toprow;
485
                        BIGDOC_IN_RECT(pb).bottom = (int32)MIN(toprow + chunksize, BIGDOC_FILTER_RECT(pb).bottom);
486
 
487
                        if (cnvused) {
488
                                // cnv() needs one extra pixel in each direction
489
                                if (BIGDOC_IN_RECT(pb).left > 0)
490
                                        --BIGDOC_IN_RECT(pb).left;
491
                                if (BIGDOC_IN_RECT(pb).right < BIGDOC_IMAGE_SIZE(pb).h)
492
                                        ++BIGDOC_IN_RECT(pb).right;
493
                                if (BIGDOC_IN_RECT(pb).top > 0)
494
                                        --BIGDOC_IN_RECT(pb).top;
495
                                if (BIGDOC_IN_RECT(pb).bottom < BIGDOC_IMAGE_SIZE(pb).v)
496
                                        ++BIGDOC_IN_RECT(pb).bottom;
497
                        }
498
                }
499
                BIGDOC_OUT_RECT(pb) = BIGDOC_FILTER_RECT(pb);
500
                /*
501
                {char s[0x100];sprintf(s,"RequestNext needall=%d inRect=(%d,%d,%d,%d) filterRect=(%d,%d,%d,%d)",
502
                                needall,
503
                                BIGDOC_IN_RECT(pb).left,BIGDOC_IN_RECT(pb).top,BIGDOC_IN_RECT(pb).right,BIGDOC_IN_RECT(pb).bottom,
504
                                BIGDOC_FILTER_RECT(pb).left,BIGDOC_FILTER_RECT(pb).top,BIGDOC_FILTER_RECT(pb).right,BIGDOC_FILTER_RECT(pb).bottom);dbg(s);}
505
                */
506
        } else {
507
                // if any of the formulae involve random access to image pixels,
508
                // ask for the entire image
509
                if (needall) {
510
                        SETRECT(IN_RECT(pb), 0, 0, IMAGE_SIZE(pb).h, IMAGE_SIZE(pb).v);
511
                }
512
                else {
513
                        // TODO: This does not work with GIMP. So, if we are using GIMP, we should
514
                        //       somehow always use "needall=true", and/or find out why this doesn't work
515
                        //       with GIMP.
516
 
517
                        // otherwise, process the filtered area, by chunksize parts
518
                        IN_RECT(pb).left = FILTER_RECT(pb).left;
519
                        IN_RECT(pb).right = FILTER_RECT(pb).right;
520
                        IN_RECT(pb).top = (int16)toprow;
521
                        IN_RECT(pb).bottom = (int16)MIN(toprow + chunksize, FILTER_RECT(pb).bottom);
522
 
523
                        if (cnvused) {
524
                                // cnv() needs one extra pixel in each direction
525
                                if (IN_RECT(pb).left > 0)
526
                                        --IN_RECT(pb).left;
527
                                if (IN_RECT(pb).right < IMAGE_SIZE(pb).h)
528
                                        ++IN_RECT(pb).right;
529
                                if (IN_RECT(pb).top > 0)
530
                                        --IN_RECT(pb).top;
531
                                if (IN_RECT(pb).bottom < IMAGE_SIZE(pb).v)
532
                                        ++IN_RECT(pb).bottom;
533
                        }
534
                }
535
                OUT_RECT(pb) = FILTER_RECT(pb);
536
                /*
537
                {char s[0x100];sprintf(s,"RequestNext needall=%d inRect=(%d,%d,%d,%d) filterRect=(%d,%d,%d,%d)",
538
                                needall,
539
                                IN_RECT(pb).left,IN_RECT(pb).top,IN_RECT(pb).right,IN_RECT(pb).bottom,
540
                                FILTER_RECT(pb).left,FILTER_RECT(pb).top,FILTER_RECT(pb).right,FILTER_RECT(pb).bottom);dbg(s);}
541
                */
542
        }
543
}
544
 
545
void DoStart(FilterRecordPtr pb){
546
//dbg("DoStart");
547
                        /* if src() or rad() functions are used, random access to the image data is required,
548
                           so we must request the entire image in a single chunk. */
549
        if (HAS_BIG_DOC(pb)) {
550
                chunksize = needall ? (BIGDOC_FILTER_RECT(pb).bottom - BIGDOC_FILTER_RECT(pb).top) : CHUNK_ROWS;
551
                toprow = BIGDOC_FILTER_RECT(pb).top;
552
        } else {
553
                chunksize = needall ? (FILTER_RECT(pb).bottom - FILTER_RECT(pb).top) : CHUNK_ROWS;
554
                toprow = FILTER_RECT(pb).top;
555
        }
556
        RequestNext(pb, toprow);
557
}
558
 
559
OSErr DoContinue(FilterRecordPtr pb){
560
        OSErr e = noErr;
561
        long outoffset;
562
 
563
        if (HAS_BIG_DOC(pb)) {
564
                VRect fr;
565
                if (needall) {
566
                        fr = BIGDOC_FILTER_RECT(pb);  // filter whole selection at once
567
                } else if (cnvused) {
568
                        // we've requested one pixel extra all around
569
                        // (see RequestNext()), just for access purposes. But filter
570
                        // original selection only.
571
                        fr.left = BIGDOC_FILTER_RECT(pb).left;
572
                        fr.right = BIGDOC_FILTER_RECT(pb).right;
573
                        fr.top = toprow;
574
                        fr.bottom = MIN(toprow + chunksize, BIGDOC_FILTER_RECT(pb).bottom);
575
                } else {  // filter whatever portion we've been given
576
                        fr = BIGDOC_IN_RECT(pb);
577
                }
578
 
579
                outoffset = (long)pb->outRowBytes * (fr.top - BIGDOC_OUT_RECT(pb).top)
580
                        + (long)nplanes * (fr.left - BIGDOC_OUT_RECT(pb).left);
581
 
582
                if (!(e = process_scaled_bigdoc(pb, true, fr, fr,
583
                        (Ptr)pb->outData + outoffset, pb->outRowBytes, 1.)))
584
                {
585
                        toprow += chunksize;
586
                        if (toprow < BIGDOC_FILTER_RECT(pb).bottom)
587
                                RequestNext(pb, toprow);
588
                        else {
589
                                SETRECT(BIGDOC_IN_RECT(pb), 0, 0, 0, 0);
590
                                BIGDOC_OUT_RECT(pb) = BIGDOC_MASK_RECT(pb) = BIGDOC_IN_RECT(pb);
591
                        }
592
                }
593
        } else {
594
                Rect fr;
595
                if (needall) {
596
                        fr = FILTER_RECT(pb);  // filter whole selection at once
597
                } else if (cnvused) {
598
                        // we've requested one pixel extra all around
599
                        // (see RequestNext()), just for access purposes. But filter
600
                        // original selection only.
601
                        fr.left = FILTER_RECT(pb).left;
602
                        fr.right = FILTER_RECT(pb).right;
603
                        fr.top = toprow;
604
                        fr.bottom = MIN(toprow + chunksize, FILTER_RECT(pb).bottom);
605
                } else {  // filter whatever portion we've been given
606
                        fr = IN_RECT(pb);
607
                }
608
 
609
                outoffset = (long)pb->outRowBytes*(fr.top - OUT_RECT(pb).top)
610
                        + (long)nplanes*(fr.left - OUT_RECT(pb).left);
611
 
612
                if(!(e = process_scaled_olddoc(pb, true, fr, fr,
613
                        (Ptr)pb->outData+outoffset, pb->outRowBytes, 1.)))
614
                {
615
                        toprow += chunksize;
616
                        if(toprow < FILTER_RECT(pb).bottom)
617
                                RequestNext(pb,toprow);
618
                        else{
619
                                SETRECT(IN_RECT(pb),0,0,0,0);
620
                                OUT_RECT(pb) = MASK_RECT(pb) = IN_RECT(pb);
621
                        }
622
                }
623
        }
624
        return e;
625
}
626
 
627
void DoFinish(FilterRecordPtr pb){
628
        int i;
629
 
630
        WriteScriptParamsOnRead();
631
 
632
        for(i = 4; i--;){
633
                freetree(tree[i]);
634
                if(expr[i]) free(expr[i]);
635
        }
636
}