Subversion Repositories filter_foundry

Rev

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