Subversion Repositories filter_foundry

Rev

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

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