Subversion Repositories filter_foundry

Rev

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