Subversion Repositories filter_foundry

Rev

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