Subversion Repositories filter_foundry

Rev

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

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