Subversion Repositories filter_foundry

Rev

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