Subversion Repositories filter_foundry

Rev

Rev 194 | Rev 203 | 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
4
    Copyright (C) 2018-2019 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;
2 toby 62
        OSErr e = noErr;
87 toby 63
        char *reason;
102 toby 64
 
158 dmarschall 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.
159 dmarschall 69
        ManifestActivationCtx manifestVars;
70
        BOOL activationContextUsed;
158 dmarschall 71
 
194 daniel-mar 72
        activationContextUsed = ActivateManifest((HMODULE)hDllInstance, 1, &manifestVars);
158 dmarschall 73
#endif
74
 
105 toby 75
        if(selector != filterSelectorAbout && !*data){
102 toby 76
                BufferID tempId;
194 daniel-mar 77
                if( (*result = PS_BUFFER_ALLOC(sizeof(globals_t), &tempId)) ) {
78
#ifdef WIN_ENV
79
                        if (activationContextUsed) DeactivateManifest(&manifestVars);
80
#endif
102 toby 81
                        return;
194 daniel-mar 82
                }
103 toby 83
                *data = (intptr_t)PS_BUFFER_LOCK(tempId, true);
84
                gdata = (globals_t*)*data;
2 toby 85
                gdata->standalone = gdata->parmloaded = false;
86
        }else
87
                gdata = (globals_t*)*data;
102 toby 88
 
89
        EnterCodeResource();
90
 
91
        gpb = pb;
92
 
2 toby 93
        nplanes = MIN(pb->planes,4);
94
 
95
        switch (selector){
87 toby 96
        case filterSelectorAbout:
102 toby 97
                if(gdata && !gdata->parmloaded)
194 daniel-mar 98
                        gdata->standalone = gdata->parmloaded = readPARMresource((HMODULE)hDllInstance,&reason,1);
103 toby 99
                DoAbout((AboutRecordPtr)pb);
2 toby 100
                break;
101
        case filterSelectorParameters:
15 toby 102
                wantdialog = true;
2 toby 103
                break;
104
        case filterSelectorPrepare:
105
                DoPrepare(pb);
106
                init_symtab(predefs); // ready for parser calls
135 dmarschall 107
                init_trigtab();
2 toby 108
                break;
89 toby 109
        case filterSelectorStart:
110
                /* initialise the parameter handle that Photoshop keeps for us */
111
                if(!pb->parameters)
167 dmarschall 112
                        pb->parameters = PINEWHANDLE(1); // don't set initial size to 0, since some hosts (e.g. GIMP/PSPI) are incompatible with that.
2 toby 113
 
15 toby 114
                wantdialog |= checkandinitparams(pb->parameters);
115
 
116
                /* wantdialog = false means that we never got a Parameters call, so we're not supposed to ask user */
2 toby 117
                if( wantdialog && (!gdata->standalone || gdata->parm.popDialog) ){
118
                        if( maindialog(pb) ){
183 dmarschall 119
                                if (!host_preserves_parameters()) {
120
                                        /* Workaround for GIMP/PSPI, to avoid that formulas vanish when you re-open the main window.
121
                                           The reason is a bug in PSPI: The host should preserve the value of pb->parameters, which PSPI does not do.
122
                                           Also, all global variables are unloaded, so the plugin cannot preserve any data.
194 daniel-mar 123
                                           Workaround in FF 1.7: If the host GIMP is detected, then a special mode will be activated.
183 dmarschall 124
                                           This mode saves the filter data into a temporary file "FilterFoundry.afs" and loads it
125
                                           when the window is opened again. */
126
                                        // Workaround: Save settings in "FilterFoundry.afs" if the host does not preserve pb->parameters
182 dmarschall 127
                                        char outfilename[255];
128
                                        char* tempdir;
167 dmarschall 129
                                        StandardFileReply sfr;
130
                                        sfr.sfGood = true;
131
                                        sfr.sfReplacing = true;
132
                                        sfr.sfType = PS_FILTER_FILETYPE;
182 dmarschall 133
 
134
                                        tempdir = getenv("TMP");
135
                                        #ifdef WIN_ENV
136
                                        if (strlen(tempdir) > 0) strcat(tempdir, "\\");
137
                                        #else
138
                                        if (strlen(tempdir) > 0) strcat(tempdir, "/");
139
                                        #endif
183 dmarschall 140
                                        sprintf(outfilename, "%sFilterFoundry.afs", tempdir);
182 dmarschall 141
 
142
                                        myc2pstrcpy(sfr.sfFile.name, outfilename);
188 dmarschall 143
                                        #ifdef WIN_ENV
185 dmarschall 144
                                        sfr.nFileExtension = (WORD)(strlen(outfilename) - strlen(".afs"));
188 dmarschall 145
                                        #endif
167 dmarschall 146
                                        sfr.sfScript = 0; // FIXME: is that ok?
147
                                        savefile(&sfr);
148
                                }
149
 
15 toby 150
                                /* update stored parameters from new user settings */
2 toby 151
                                saveparams(pb->parameters);
152
                        }else
153
                                e = userCanceledErr;
154
                }
18 toby 155
                wantdialog = false;
2 toby 156
 
157
                if(!e){
158
                        if(setup(pb)){
159
                                DoStart(pb);
160
                        }else{
161
                                SYSBEEP(1);
162
                                e = filterBadParameters;
163
                        }
164
                }
165
                break;
103 toby 166
        case filterSelectorContinue:
167
                e = DoContinue(pb);
2 toby 168
                break;
169
        case filterSelectorFinish:
170
                DoFinish(pb);
171
                break;
172
        default:
173
                e = filterBadParameters;
174
        }
103 toby 175
 
2 toby 176
        *result = e;
177
 
178
        ExitCodeResource();
158 dmarschall 179
 
180
#ifdef WIN_ENV
194 daniel-mar 181
        if (activationContextUsed) DeactivateManifest(&manifestVars);
158 dmarschall 182
#endif
2 toby 183
}
184
 
15 toby 185
int checkandinitparams(Handle params){
186
        char *reasonstr,*reason;
21 toby 187
        int i,f,showdialog;
103 toby 188
 
183 dmarschall 189
        if (!host_preserves_parameters()) {
190
                // Workaround: Load settings in "FilterFoundry.afs" if host does not preserve pb->parameters
182 dmarschall 191
                char outfilename[255];
192
                char* tempdir;
167 dmarschall 193
                StandardFileReply sfr;
194
                sfr.sfGood = true;
195
                sfr.sfReplacing = true;
196
                sfr.sfType = PS_FILTER_FILETYPE;
182 dmarschall 197
 
198
                tempdir = getenv("TMP");
199
                #ifdef WIN_ENV
200
                if (strlen(tempdir) > 0) strcat(tempdir, "\\");
201
                #else
202
                if (strlen(tempdir) > 0) strcat(tempdir, "/");
203
                #endif
183 dmarschall 204
                sprintf(outfilename, "%sFilterFoundry.afs", tempdir);
182 dmarschall 205
 
206
                myc2pstrcpy(sfr.sfFile.name, outfilename);
188 dmarschall 207
                #ifdef WIN_ENV
185 dmarschall 208
                sfr.nFileExtension = (WORD)(strlen(outfilename) - strlen(".afs"));
188 dmarschall 209
                #endif
167 dmarschall 210
                sfr.sfScript = 0; // FIXME: is that ok?
211
                if (loadfile(&sfr, &reason)) return true;
212
        }
213
 
23 toby 214
        if( (f = !(params && readparams(params,false,&reasonstr))) ){
2 toby 215
                /* either the parameter handle was uninitialised,
216
                   or the parameter data couldn't be read; set default values */
217
 
89 toby 218
                // see if saved parameters exist
194 daniel-mar 219
                gdata->standalone = gdata->parmloaded = readPARMresource((HMODULE)hDllInstance,&reason,1);
89 toby 220
 
87 toby 221
                if(!gdata->standalone){
21 toby 222
                        // no saved settings (not standalone)
85 toby 223
                        for(i = 0; i < 8; ++i)
2 toby 224
                                slider[i] = i*10+100;
146 dmarschall 225
                        for(i = 0; i < 4; ++i)
226
                                if(expr[i])
227
                                        free(expr[i]);
89 toby 228
                        if(gpb->imageMode == plugInModeRGBColor){
198 daniel-mar 229
                                expr[0] = _strdup("r");
230
                                expr[1] = _strdup("g");
231
                                expr[2] = _strdup("b");
232
                                expr[3] = _strdup("a");
89 toby 233
                        }else{
198 daniel-mar 234
                                expr[0] = _strdup("c");
235
                                expr[1] = _strdup("c");
236
                                expr[2] = _strdup("c");
237
                                expr[3] = _strdup("c");
89 toby 238
                        }
2 toby 239
                }
240
        }
103 toby 241
 
89 toby 242
        // let scripting system change parameters, if we're scripted;
103 toby 243
        // user may want to force display of dialog during scripting playback
21 toby 244
        showdialog = ReadScriptParamsOnRead();
2 toby 245
 
89 toby 246
        saveparams(params);
21 toby 247
        return f || showdialog;
2 toby 248
}
249
 
185 dmarschall 250
Boolean host_preserves_parameters() {
183 dmarschall 251
        if (gpb->hostSig == HOSTSIG_GIMP) return false;
252
        if (gpb->hostSig == HOSTSIG_IRFANVIEW) return false;
253
 
254
        /*
255
        char x[100];
256
        sprintf(x, "Host Signature: %u", gpb->hostSig);
257
        simplealert(x);
258
        */
259
 
260
        // We just assume the other hosts preserve the parameters
261
        return true;
262
}
263
 
182 dmarschall 264
int64_t maxspace(){
181 dmarschall 265
        if (gpb->maxSpace64 != 0) {
266
                // If this is non-zero, the host either support 64-bit OR the host ignored the rule "set reserved fields to 0".
267
                uint64_t maxSpace64 = gpb->maxSpace64;
183 dmarschall 268
 
269
                /*
270
                char x[100];
271
                sprintf(x, "maxSpace64: %lld", maxSpace64);
272
                simplealert(x);
273
                */
274
 
275
                // Note: IrfanView currently does not support maxSpace64, so we don't handle HOSTSIG_IRFANVIEW
181 dmarschall 276
                return maxSpace64;
277
        } else {
278
                // 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.
279
                unsigned int maxSpace32 = (unsigned int) gpb->maxSpace;
280
                uint64_t maxSpace64 = maxSpace32;
183 dmarschall 281
 
282
                /*
283
                char x[100];
284
                sprintf(x, "maxSpace32: %lld", maxSpace64);
285
                simplealert(x);
286
                */
287
 
288
                if (gpb->hostSig == HOSTSIG_IRFANVIEW) maxSpace64 *= 1024; // IrfanView is giving Kilobytes instead of Bytes
190 dmarschall 289
                // TODO: Serif PhotoPlus also gives Kilobytes instead of bytes. But since it uses not a unique host signature, there is nothing we can do???
181 dmarschall 290
                return maxSpace64;
291
        }
292
}
293
 
185 dmarschall 294
Boolean maxspace_available() {
182 dmarschall 295
        // GIMP sets MaxSpace to hardcoded 100 MB
183 dmarschall 296
        if (gpb->hostSig == HOSTSIG_GIMP) return false;
182 dmarschall 297
 
298
        return true;
299
}
300
 
2 toby 301
void DoPrepare(FilterRecordPtr pb){
302
        int i;
303
 
85 toby 304
        for(i = 4; i--;){
8 toby 305
                if(expr[i]||tree[i]) DBG("expr[] or tree[] non-NULL in Prepare!");
2 toby 306
                expr[i] = NULL;
307
                tree[i] = NULL;
308
        }
106 dmarschall 309
 
310
        // Commented out by DM, 18 Dec 2018:
311
        // This code did not work on systems with 8 GB RAM:
312
        /*
313
        long space = (pb->maxSpace*9)/10; // don't ask for more than 90% of available memory
314
 
103 toby 315
        maxSpace = 512L<<10; // this is a wild guess, actually
316
        if(maxSpace > space)
317
                maxSpace = space;
318
        pb->maxSpace = maxSpace;
106 dmarschall 319
        */
320
 
321
        // New variant:
182 dmarschall 322
        if (maxspace_available()) {
185 dmarschall 323
                pb->maxSpace = (int32)ceil((maxspace()/10.)*9); // don't ask for more than 90% of available memory
324
                // FIXME: Also maxSpace64
182 dmarschall 325
        }
2 toby 326
}
327
 
328
void RequestNext(FilterRecordPtr pb,long toprow){
329
        /* Request next block of the image */
330
 
331
        pb->inLoPlane = pb->outLoPlane = 0;
332
        pb->inHiPlane = pb->outHiPlane = nplanes-1;
333
 
66 toby 334
        // if any of the formulae involve random access to image pixels,
89 toby 335
        // ask for the entire image
94 toby 336
        if(needall){
2 toby 337
                SETRECT(pb->inRect,0,0,pb->imageSize.h,pb->imageSize.v);
338
        }else{
164 dmarschall 339
                // TODO: This does not work with GIMP. So, if we are using GIMP, we should
340
                //       somehow always use "needall=true", and/or find out why this doesn't work
341
                //       with GIMP.
342
 
66 toby 343
                // otherwise, process the filtered area, by chunksize parts
2 toby 344
                pb->inRect.left = pb->filterRect.left;
345
                pb->inRect.right = pb->filterRect.right;
185 dmarschall 346
                pb->inRect.top = (int16)toprow;
347
                pb->inRect.bottom = (int16)MIN(toprow + chunksize,pb->filterRect.bottom);
103 toby 348
 
71 toby 349
                if(cnvused){
350
                        // cnv() needs one extra pixel in each direction
351
                        if(pb->inRect.left > 0)
352
                                --pb->inRect.left;
353
                        if(pb->inRect.right < pb->imageSize.h)
354
                                ++pb->inRect.right;
355
                        if(pb->inRect.top > 0)
356
                                --pb->inRect.top;
357
                        if(pb->inRect.bottom < pb->imageSize.v)
358
                                ++pb->inRect.bottom;
359
                }
2 toby 360
        }
66 toby 361
        pb->outRect = pb->filterRect;
2 toby 362
/*
94 toby 363
{char s[0x100];sprintf(s,"RequestNext needall=%d inRect=(%d,%d,%d,%d) filterRect=(%d,%d,%d,%d)",
103 toby 364
        needall,
2 toby 365
        pb->inRect.left,pb->inRect.top,pb->inRect.right,pb->inRect.bottom,
366
        pb->filterRect.left,pb->filterRect.top,pb->filterRect.right,pb->filterRect.bottom);dbg(s);}
367
*/
368
}
369
 
370
void DoStart(FilterRecordPtr pb){
371
//dbg("DoStart");
372
        /* if src() or rad() functions are used, random access to the image data is required,
373
           so we must request the entire image in a single chunk. */
94 toby 374
        chunksize = needall ? (pb->filterRect.bottom - pb->filterRect.top) : CHUNK_ROWS;
2 toby 375
        toprow = pb->filterRect.top;
376
        RequestNext(pb,toprow);
377
}
378
 
379
OSErr DoContinue(FilterRecordPtr pb){
380
        OSErr e = noErr;
71 toby 381
        Rect fr;
382
        long outoffset;
2 toby 383
 
94 toby 384
        if(needall)
71 toby 385
                fr = pb->filterRect;  // filter whole selection at once
386
        else if(cnvused){
387
                // we've requested one pixel extra all around
388
                // (see RequestNext()), just for access purposes. But filter
389
                // original selection only.
390
                fr.left = pb->filterRect.left;
391
                fr.right = pb->filterRect.right;
392
                fr.top = toprow;
393
                fr.bottom = MIN(toprow + chunksize,pb->filterRect.bottom);
394
        }else  // filter whatever portion we've been given
395
                fr = pb->inRect;
396
 
103 toby 397
        outoffset = (long)pb->outRowBytes*(fr.top - pb->outRect.top)
71 toby 398
                                + (long)nplanes*(fr.left - pb->outRect.left);
399
 
400
        if(!(e = process_scaled(pb, true, &fr, &fr,
62 toby 401
                                (Ptr)pb->outData+outoffset, pb->outRowBytes, 1.)))
402
        {
2 toby 403
                toprow += chunksize;
404
                if(toprow < pb->filterRect.bottom)
405
                        RequestNext(pb,toprow);
406
                else{
407
                        SETRECT(pb->inRect,0,0,0,0);
408
                        pb->outRect = pb->maskRect = pb->inRect;
409
                }
410
        }
411
        return e;
412
}
413
 
414
void DoFinish(FilterRecordPtr pb){
415
        int i;
89 toby 416
 
2 toby 417
        WriteScriptParamsOnRead();
418
 
85 toby 419
        for(i = 4; i--;){
2 toby 420
                freetree(tree[i]);
421
                if(expr[i]) free(expr[i]);
422
        }
423
}