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 |