Subversion Repositories filter_foundry

Rev

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