Subversion Repositories filter_foundry

Rev

Rev 433 | Rev 444 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

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