Subversion Repositories filter_foundry

Rev

Rev 227 | Rev 232 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 toby 1
/*
18 toby 2
    This file is part of "Filter Foundry", a filter plugin for Adobe Photoshop
192 daniel-mar 3
    Copyright (C) 2003-2009 Toby Thain, toby@telegraphics.com.au
206 daniel-mar 4
    Copyright (C) 2018-2021 Daniel Marschall, ViaThinkSoft
2 toby 5
 
6
    This program is free software; you can redistribute it and/or modify
103 toby 7
    it under the terms of the GNU General Public License as published by
2 toby 8
    the Free Software Foundation; either version 2 of the License, or
9
    (at your option) any later version.
10
 
11
    This program is distributed in the hope that it will be useful,
12
    but WITHOUT ANY WARRANTY; without even the implied warranty of
13
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
    GNU General Public License for more details.
15
 
103 toby 16
    You should have received a copy of the GNU General Public License
2 toby 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
19
*/
20
 
21
//#include <stdio.h>
22
//#include <sound.h>
23
 
24
#include "ff.h"
8 toby 25
 
2 toby 26
#include "node.h"
172 dmarschall 27
#include "funcs.h"
8 toby 28
#include "y.tab.h"
2 toby 29
#include "scripting.h"
185 dmarschall 30
#include <math.h>
2 toby 31
 
32
struct node *tree[4];
33
char *err[4];
94 toby 34
int errpos[4],errstart[4],nplanes,cnvused,chunksize,toprow;
158 dmarschall 35
value_type slider[8],cell[NUM_CELLS],map[4][0x100];
89 toby 36
char *expr[4];
106 dmarschall 37
// long maxSpace;
71 toby 38
globals_t *gdata;
39
FilterRecordPtr gpb;
2 toby 40
 
41
#ifdef MAC_ENV
198 daniel-mar 42
        #define HINSTANCE HANDLE
62 toby 43
        #define hDllInstance NULL /* fake this Windows-only global */
2 toby 44
#endif
45
 
159 dmarschall 46
#ifdef WIN_ENV
47
#include "manifest.h"
48
#endif
49
 
2 toby 50
extern struct sym_rec predefs[];
71 toby 51
extern int nplanes,varused[];
2 toby 52
 
15 toby 53
int checkandinitparams(Handle params);
2 toby 54
 
102 toby 55
// MPW MrC requires prototype
103 toby 56
DLLEXPORT MACPASCAL
102 toby 57
void ENTRYPOINT(short selector,FilterRecordPtr pb,intptr_t *data,short *result);
58
 
103 toby 59
DLLEXPORT MACPASCAL
102 toby 60
void ENTRYPOINT(short selector, FilterRecordPtr pb, intptr_t *data, short *result){
87 toby 61
        static Boolean wantdialog = false;
231 daniel-mar 62
        static Boolean premiereWarnedOnce = false;
2 toby 63
        OSErr e = noErr;
87 toby 64
        char *reason;
231 daniel-mar 65
#ifdef WIN_ENV
66
        // For Windows, we use an activation context to enforce that our Manifest resource will
67
        // be used. This allows us to use Visual Styles, even if the host application does not
68
        // support it.
69
        ManifestActivationCtx manifestVars;
70
        BOOL activationContextUsed;
71
#endif
102 toby 72
 
224 daniel-mar 73
        /*
74
        char* s = (char*)malloc(512);
227 daniel-mar 75
        sprintf(s, "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);
224 daniel-mar 76
        simplealert(s);
77
        */
78
 
231 daniel-mar 79
        if (pb->hostSig == HOSTSIG_ADOBE_PREMIERE) {
80
                // DM 19.07.2021 : Tried running the 8BF file in Adobe Premiere 5 (yes, that's possible,
81
                // and there is even a FilterFactory for Premeire!),
82
                // but it crashes in evalpixel() where there is write-access to the "outp".
83
                // Probably the canvas structure is different (maybe it contains frames to achieve transitions?)
84
                if (!premiereWarnedOnce) {
85
                        simplealert("This version of Filter Foundry is not compatible with Adobe Premiere!");
86
                }
87
                premiereWarnedOnce = true;
88
                *result = filterBadParameters;
89
                return;
90
        }
91
 
158 dmarschall 92
#ifdef WIN_ENV
194 daniel-mar 93
        activationContextUsed = ActivateManifest((HMODULE)hDllInstance, 1, &manifestVars);
158 dmarschall 94
#endif
95
 
105 toby 96
        if(selector != filterSelectorAbout && !*data){
102 toby 97
                BufferID tempId;
194 daniel-mar 98
                if( (*result = PS_BUFFER_ALLOC(sizeof(globals_t), &tempId)) ) {
99
#ifdef WIN_ENV
100
                        if (activationContextUsed) DeactivateManifest(&manifestVars);
101
#endif
102 toby 102
                        return;
194 daniel-mar 103
                }
103 toby 104
                *data = (intptr_t)PS_BUFFER_LOCK(tempId, true);
105
                gdata = (globals_t*)*data;
2 toby 106
                gdata->standalone = gdata->parmloaded = false;
210 daniel-mar 107
        } else {
2 toby 108
                gdata = (globals_t*)*data;
210 daniel-mar 109
        }
102 toby 110
 
111
        EnterCodeResource();
112
 
113
        gpb = pb;
114
 
2 toby 115
        nplanes = MIN(pb->planes,4);
116
 
117
        switch (selector){
87 toby 118
        case filterSelectorAbout:
210 daniel-mar 119
                if (!gdata) {
120
                        gdata = (globals_t*)malloc(sizeof(globals_t));
121
                        if (!gdata) break;
216 daniel-mar 122
                        gdata->standalone = gdata->parmloaded = readPARMresource((HMODULE)hDllInstance,&reason,READ_OBFUSC);
210 daniel-mar 123
                        DoAbout((AboutRecordPtr)pb);
124
                        free(gdata);
125
                        gdata = NULL;
126
                } else {
127
                        DoAbout((AboutRecordPtr)pb);
128
                }
2 toby 129
                break;
130
        case filterSelectorParameters:
15 toby 131
                wantdialog = true;
2 toby 132
                break;
133
        case filterSelectorPrepare:
134
                DoPrepare(pb);
135
                init_symtab(predefs); // ready for parser calls
135 dmarschall 136
                init_trigtab();
2 toby 137
                break;
89 toby 138
        case filterSelectorStart:
139
                /* initialise the parameter handle that Photoshop keeps for us */
140
                if(!pb->parameters)
167 dmarschall 141
                        pb->parameters = PINEWHANDLE(1); // don't set initial size to 0, since some hosts (e.g. GIMP/PSPI) are incompatible with that.
2 toby 142
 
15 toby 143
                wantdialog |= checkandinitparams(pb->parameters);
144
 
145
                /* wantdialog = false means that we never got a Parameters call, so we're not supposed to ask user */
2 toby 146
                if( wantdialog && (!gdata->standalone || gdata->parm.popDialog) ){
147
                        if( maindialog(pb) ){
183 dmarschall 148
                                if (!host_preserves_parameters()) {
149
                                        /* Workaround for GIMP/PSPI, to avoid that formulas vanish when you re-open the main window.
150
                                           The reason is a bug in PSPI: The host should preserve the value of pb->parameters, which PSPI does not do.
151
                                           Also, all global variables are unloaded, so the plugin cannot preserve any data.
194 daniel-mar 152
                                           Workaround in FF 1.7: If the host GIMP is detected, then a special mode will be activated.
183 dmarschall 153
                                           This mode saves the filter data into a temporary file "FilterFoundry.afs" and loads it
154
                                           when the window is opened again. */
155
                                        // Workaround: Save settings in "FilterFoundry.afs" if the host does not preserve pb->parameters
182 dmarschall 156
                                        char outfilename[255];
157
                                        char* tempdir;
167 dmarschall 158
                                        StandardFileReply sfr;
159
                                        sfr.sfGood = true;
160
                                        sfr.sfReplacing = true;
161
                                        sfr.sfType = PS_FILTER_FILETYPE;
182 dmarschall 162
 
163
                                        tempdir = getenv("TMP");
164
                                        #ifdef WIN_ENV
165
                                        if (strlen(tempdir) > 0) strcat(tempdir, "\\");
166
                                        #else
167
                                        if (strlen(tempdir) > 0) strcat(tempdir, "/");
168
                                        #endif
183 dmarschall 169
                                        sprintf(outfilename, "%sFilterFoundry.afs", tempdir);
182 dmarschall 170
 
171
                                        myc2pstrcpy(sfr.sfFile.name, outfilename);
188 dmarschall 172
                                        #ifdef WIN_ENV
185 dmarschall 173
                                        sfr.nFileExtension = (WORD)(strlen(outfilename) - strlen(".afs"));
188 dmarschall 174
                                        #endif
167 dmarschall 175
                                        sfr.sfScript = 0; // FIXME: is that ok?
176
                                        savefile(&sfr);
177
                                }
178
 
15 toby 179
                                /* update stored parameters from new user settings */
2 toby 180
                                saveparams(pb->parameters);
181
                        }else
182
                                e = userCanceledErr;
183
                }
18 toby 184
                wantdialog = false;
2 toby 185
 
186
                if(!e){
187
                        if(setup(pb)){
188
                                DoStart(pb);
189
                        }else{
190
                                SYSBEEP(1);
191
                                e = filterBadParameters;
192
                        }
193
                }
194
                break;
103 toby 195
        case filterSelectorContinue:
196
                e = DoContinue(pb);
2 toby 197
                break;
198
        case filterSelectorFinish:
199
                DoFinish(pb);
200
                break;
201
        default:
202
                e = filterBadParameters;
203
        }
103 toby 204
 
2 toby 205
        *result = e;
206
 
158 dmarschall 207
#ifdef WIN_ENV
194 daniel-mar 208
        if (activationContextUsed) DeactivateManifest(&manifestVars);
158 dmarschall 209
#endif
210 daniel-mar 210
 
211
        ExitCodeResource();
2 toby 212
}
213
 
15 toby 214
int checkandinitparams(Handle params){
215
        char *reasonstr,*reason;
204 daniel-mar 216
        int i,bUninitializedParams;
217
        Boolean showdialog;
103 toby 218
 
183 dmarschall 219
        if (!host_preserves_parameters()) {
220
                // Workaround: Load settings in "FilterFoundry.afs" if host does not preserve pb->parameters
182 dmarschall 221
                char outfilename[255];
222
                char* tempdir;
167 dmarschall 223
                StandardFileReply sfr;
224
                sfr.sfGood = true;
225
                sfr.sfReplacing = true;
226
                sfr.sfType = PS_FILTER_FILETYPE;
182 dmarschall 227
 
228
                tempdir = getenv("TMP");
229
                #ifdef WIN_ENV
230
                if (strlen(tempdir) > 0) strcat(tempdir, "\\");
231
                #else
232
                if (strlen(tempdir) > 0) strcat(tempdir, "/");
233
                #endif
183 dmarschall 234
                sprintf(outfilename, "%sFilterFoundry.afs", tempdir);
182 dmarschall 235
 
236
                myc2pstrcpy(sfr.sfFile.name, outfilename);
188 dmarschall 237
                #ifdef WIN_ENV
185 dmarschall 238
                sfr.nFileExtension = (WORD)(strlen(outfilename) - strlen(".afs"));
188 dmarschall 239
                #endif
167 dmarschall 240
                sfr.sfScript = 0; // FIXME: is that ok?
241
                if (loadfile(&sfr, &reason)) return true;
242
        }
243
 
203 daniel-mar 244
        if( (bUninitializedParams = !(params && readparams(params,false,&reasonstr))) ){
2 toby 245
                /* either the parameter handle was uninitialised,
246
                   or the parameter data couldn't be read; set default values */
247
 
89 toby 248
                // see if saved parameters exist
216 daniel-mar 249
                gdata->standalone = gdata->parmloaded = readPARMresource((HMODULE)hDllInstance,&reason,READ_OBFUSC);
89 toby 250
 
210 daniel-mar 251
 
87 toby 252
                if(!gdata->standalone){
21 toby 253
                        // no saved settings (not standalone)
85 toby 254
                        for(i = 0; i < 8; ++i)
2 toby 255
                                slider[i] = i*10+100;
146 dmarschall 256
                        for(i = 0; i < 4; ++i)
257
                                if(expr[i])
258
                                        free(expr[i]);
89 toby 259
                        if(gpb->imageMode == plugInModeRGBColor){
198 daniel-mar 260
                                expr[0] = _strdup("r");
261
                                expr[1] = _strdup("g");
262
                                expr[2] = _strdup("b");
263
                                expr[3] = _strdup("a");
89 toby 264
                        }else{
198 daniel-mar 265
                                expr[0] = _strdup("c");
266
                                expr[1] = _strdup("c");
267
                                expr[2] = _strdup("c");
268
                                expr[3] = _strdup("c");
89 toby 269
                        }
2 toby 270
                }
271
        }
103 toby 272
 
89 toby 273
        // let scripting system change parameters, if we're scripted;
103 toby 274
        // user may want to force display of dialog during scripting playback
203 daniel-mar 275
        switch (ReadScriptParamsOnRead()) {
276
                case SCR_SHOW_DIALOG:
204 daniel-mar 277
                        showdialog = true;
278
                        break;
203 daniel-mar 279
                case SCR_HIDE_DIALOG:
204 daniel-mar 280
                        showdialog = false;
281
                        break;
282
                default:
203 daniel-mar 283
                case SCR_NO_SCRIPT:
204 daniel-mar 284
                        showdialog = bUninitializedParams;
285
                        break;
203 daniel-mar 286
        }
204 daniel-mar 287
 
288
        saveparams(params);
289
 
290
        return showdialog;
2 toby 291
}
292
 
185 dmarschall 293
Boolean host_preserves_parameters() {
183 dmarschall 294
        if (gpb->hostSig == HOSTSIG_GIMP) return false;
295
        if (gpb->hostSig == HOSTSIG_IRFANVIEW) return false;
296
 
297
        /*
298
        char x[100];
299
        sprintf(x, "Host Signature: %u", gpb->hostSig);
300
        simplealert(x);
301
        */
302
 
303
        // We just assume the other hosts preserve the parameters
304
        return true;
305
}
306
 
182 dmarschall 307
int64_t maxspace(){
224 daniel-mar 308
        // Please see "Hosts.md" for details about the MaxSpace implementations of tested plugins
309
 
310
        // Plugins that don't support MaxSpace64 shall set the field to zero; then we will use MaxSpace instead.
227 daniel-mar 311
        // Also check "gpb->bufferProcs->numBufferProcs" to see if 64 bit API is available
312
        if ((gpb->bufferProcs->numBufferProcs >= 8) && (gpb->maxSpace64 > 0)) {
181 dmarschall 313
                uint64_t maxSpace64 = gpb->maxSpace64;
183 dmarschall 314
 
181 dmarschall 315
                return maxSpace64;
316
        } else {
317
                // 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.
318
                unsigned int maxSpace32 = (unsigned int) gpb->maxSpace;
319
                uint64_t maxSpace64 = maxSpace32;
183 dmarschall 320
 
224 daniel-mar 321
                if (gpb->hostSig == HOSTSIG_IRFANVIEW) maxSpace64 *= 1024; // IrfanView is giving Kilobytes instead of Bytes
322
                //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???
183 dmarschall 323
 
181 dmarschall 324
                return maxSpace64;
325
        }
326
}
327
 
185 dmarschall 328
Boolean maxspace_available() {
224 daniel-mar 329
        // Please see "Hosts.md" for details about the MaxSpace implementations of tested plugins
330
 
331
        // GIMP PSPI sets MaxSpace to hardcoded 100 MB
183 dmarschall 332
        if (gpb->hostSig == HOSTSIG_GIMP) return false;
224 daniel-mar 333
 
227 daniel-mar 334
        // HOSTSIG_PAINT_NET sets MaxSpace to hardcoded 1 GB, see https://github.com/0xC0000054/PSFilterPdn/issues/5
335
        // Comment by the host author "This was done to avoid any compatibility issues with plugins handling 2 GB - 1"
224 daniel-mar 336
        if (gpb->hostSig == HOSTSIG_PAINT_NET) return false;
182 dmarschall 337
 
338
        return true;
339
}
340
 
2 toby 341
void DoPrepare(FilterRecordPtr pb){
342
        int i;
343
 
85 toby 344
        for(i = 4; i--;){
8 toby 345
                if(expr[i]||tree[i]) DBG("expr[] or tree[] non-NULL in Prepare!");
2 toby 346
                expr[i] = NULL;
347
                tree[i] = NULL;
348
        }
106 dmarschall 349
 
350
        // Commented out by DM, 18 Dec 2018:
351
        // This code did not work on systems with 8 GB RAM:
352
        /*
353
        long space = (pb->maxSpace*9)/10; // don't ask for more than 90% of available memory
354
 
103 toby 355
        maxSpace = 512L<<10; // this is a wild guess, actually
356
        if(maxSpace > space)
357
                maxSpace = space;
358
        pb->maxSpace = maxSpace;
106 dmarschall 359
        */
360
 
361
        // New variant:
182 dmarschall 362
        if (maxspace_available()) {
185 dmarschall 363
                pb->maxSpace = (int32)ceil((maxspace()/10.)*9); // don't ask for more than 90% of available memory
364
                // FIXME: Also maxSpace64
182 dmarschall 365
        }
2 toby 366
}
367
 
368
void RequestNext(FilterRecordPtr pb,long toprow){
369
        /* Request next block of the image */
370
 
371
        pb->inLoPlane = pb->outLoPlane = 0;
372
        pb->inHiPlane = pb->outHiPlane = nplanes-1;
373
 
66 toby 374
        // if any of the formulae involve random access to image pixels,
89 toby 375
        // ask for the entire image
94 toby 376
        if(needall){
2 toby 377
                SETRECT(pb->inRect,0,0,pb->imageSize.h,pb->imageSize.v);
378
        }else{
164 dmarschall 379
                // TODO: This does not work with GIMP. So, if we are using GIMP, we should
380
                //       somehow always use "needall=true", and/or find out why this doesn't work
381
                //       with GIMP.
382
 
66 toby 383
                // otherwise, process the filtered area, by chunksize parts
2 toby 384
                pb->inRect.left = pb->filterRect.left;
385
                pb->inRect.right = pb->filterRect.right;
185 dmarschall 386
                pb->inRect.top = (int16)toprow;
387
                pb->inRect.bottom = (int16)MIN(toprow + chunksize,pb->filterRect.bottom);
103 toby 388
 
71 toby 389
                if(cnvused){
390
                        // cnv() needs one extra pixel in each direction
391
                        if(pb->inRect.left > 0)
392
                                --pb->inRect.left;
393
                        if(pb->inRect.right < pb->imageSize.h)
394
                                ++pb->inRect.right;
395
                        if(pb->inRect.top > 0)
396
                                --pb->inRect.top;
397
                        if(pb->inRect.bottom < pb->imageSize.v)
398
                                ++pb->inRect.bottom;
399
                }
2 toby 400
        }
66 toby 401
        pb->outRect = pb->filterRect;
2 toby 402
/*
94 toby 403
{char s[0x100];sprintf(s,"RequestNext needall=%d inRect=(%d,%d,%d,%d) filterRect=(%d,%d,%d,%d)",
103 toby 404
        needall,
2 toby 405
        pb->inRect.left,pb->inRect.top,pb->inRect.right,pb->inRect.bottom,
406
        pb->filterRect.left,pb->filterRect.top,pb->filterRect.right,pb->filterRect.bottom);dbg(s);}
407
*/
408
}
409
 
410
void DoStart(FilterRecordPtr pb){
411
//dbg("DoStart");
412
        /* if src() or rad() functions are used, random access to the image data is required,
413
           so we must request the entire image in a single chunk. */
94 toby 414
        chunksize = needall ? (pb->filterRect.bottom - pb->filterRect.top) : CHUNK_ROWS;
2 toby 415
        toprow = pb->filterRect.top;
416
        RequestNext(pb,toprow);
417
}
418
 
419
OSErr DoContinue(FilterRecordPtr pb){
420
        OSErr e = noErr;
71 toby 421
        Rect fr;
422
        long outoffset;
2 toby 423
 
94 toby 424
        if(needall)
71 toby 425
                fr = pb->filterRect;  // filter whole selection at once
426
        else if(cnvused){
427
                // we've requested one pixel extra all around
428
                // (see RequestNext()), just for access purposes. But filter
429
                // original selection only.
430
                fr.left = pb->filterRect.left;
431
                fr.right = pb->filterRect.right;
432
                fr.top = toprow;
433
                fr.bottom = MIN(toprow + chunksize,pb->filterRect.bottom);
434
        }else  // filter whatever portion we've been given
435
                fr = pb->inRect;
436
 
103 toby 437
        outoffset = (long)pb->outRowBytes*(fr.top - pb->outRect.top)
71 toby 438
                                + (long)nplanes*(fr.left - pb->outRect.left);
439
 
440
        if(!(e = process_scaled(pb, true, &fr, &fr,
62 toby 441
                                (Ptr)pb->outData+outoffset, pb->outRowBytes, 1.)))
442
        {
2 toby 443
                toprow += chunksize;
444
                if(toprow < pb->filterRect.bottom)
445
                        RequestNext(pb,toprow);
446
                else{
447
                        SETRECT(pb->inRect,0,0,0,0);
448
                        pb->outRect = pb->maskRect = pb->inRect;
449
                }
450
        }
451
        return e;
452
}
453
 
454
void DoFinish(FilterRecordPtr pb){
455
        int i;
89 toby 456
 
2 toby 457
        WriteScriptParamsOnRead();
458
 
85 toby 459
        for(i = 4; i--;){
2 toby 460
                freetree(tree[i]);
461
                if(expr[i]) free(expr[i]);
462
        }
463
}