Subversion Repositories filter_foundry

Rev

Rev 167 | Rev 172 | 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
171 dmarschall 3
    Copyright (C) 2003-2019 Toby Thain, toby@telegraphics.com.au
2 toby 4
 
5
    This program is free software; you can redistribute it and/or modify
103 toby 6
    it under the terms of the GNU General Public License as published by
2 toby 7
    the Free Software Foundation; either version 2 of the License, or
8
    (at your option) any later version.
9
 
10
    This program is distributed in the hope that it will be useful,
11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
    GNU General Public License for more details.
14
 
103 toby 15
    You should have received a copy of the GNU General Public License
2 toby 16
    along with this program; if not, write to the Free Software
17
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
*/
19
 
20
//#include <stdio.h>
21
//#include <sound.h>
22
 
23
#include "ff.h"
8 toby 24
 
2 toby 25
#include "node.h"
8 toby 26
#include "y.tab.h"
2 toby 27
#include "scripting.h"
28
 
29
struct node *tree[4];
30
char *err[4];
94 toby 31
int errpos[4],errstart[4],nplanes,cnvused,chunksize,toprow;
158 dmarschall 32
value_type slider[8],cell[NUM_CELLS],map[4][0x100];
89 toby 33
char *expr[4];
106 dmarschall 34
// long maxSpace;
71 toby 35
globals_t *gdata;
36
FilterRecordPtr gpb;
2 toby 37
 
38
#ifdef MAC_ENV
62 toby 39
        #define hDllInstance NULL /* fake this Windows-only global */
2 toby 40
#endif
41
 
159 dmarschall 42
#ifdef WIN_ENV
43
#include "manifest.h"
44
#endif
45
 
2 toby 46
extern struct sym_rec predefs[];
71 toby 47
extern int nplanes,varused[];
167 dmarschall 48
extern int persistent_savestate = false;
2 toby 49
 
15 toby 50
int checkandinitparams(Handle params);
2 toby 51
 
102 toby 52
// MPW MrC requires prototype
103 toby 53
DLLEXPORT MACPASCAL
102 toby 54
void ENTRYPOINT(short selector,FilterRecordPtr pb,intptr_t *data,short *result);
55
 
103 toby 56
DLLEXPORT MACPASCAL
102 toby 57
void ENTRYPOINT(short selector, FilterRecordPtr pb, intptr_t *data, short *result){
87 toby 58
        static Boolean wantdialog = false;
2 toby 59
        OSErr e = noErr;
87 toby 60
        char *reason;
102 toby 61
 
158 dmarschall 62
#ifdef WIN_ENV
63
        // For Windows, we use an activation context to enforce that our Manifest resource will
64
        // be used. This allows us to use Visual Styles, even if the host application does not
65
        // support it.
159 dmarschall 66
        ManifestActivationCtx manifestVars;
67
        BOOL activationContextUsed;
158 dmarschall 68
 
159 dmarschall 69
        activationContextUsed = ActivateManifest(hDllInstance, 1, &manifestVars);
158 dmarschall 70
 
159 dmarschall 71
        __try {
158 dmarschall 72
#endif
73
 
167 dmarschall 74
        /* Workaround for GIMP/PSPI, to avoid that formulas vanish when you re-open the main window.
75
           The reason is a bug in PSPI: The host should preserve the value of pb->parameters, which PSPI does not do.
76
           Also, all global variables are unloaded, so the plugin cannot preserve any data.
77
           Workaround in FF 1.7: If the host GIMP is detected, the new flag persistent_savestate will be set.
78
           This mode saves the filter data into a temporary file "tmp.afs" and loads it
79
           when the window is opened again. */
80
#if MAC_ENV
81
        if (pb->hostSig == 'GIMP') persistent_savestate = true;
82
#else
83
        if (pb->hostSig == 'PMIG') persistent_savestate = true;
84
#endif
85
 
105 toby 86
        if(selector != filterSelectorAbout && !*data){
102 toby 87
                BufferID tempId;
88
                if( (*result = PS_BUFFER_ALLOC(sizeof(globals_t), &tempId)) )
89
                        return;
103 toby 90
                *data = (intptr_t)PS_BUFFER_LOCK(tempId, true);
91
                gdata = (globals_t*)*data;
2 toby 92
                gdata->standalone = gdata->parmloaded = false;
93
        }else
94
                gdata = (globals_t*)*data;
102 toby 95
 
96
        EnterCodeResource();
97
 
98
        gpb = pb;
99
 
2 toby 100
        nplanes = MIN(pb->planes,4);
101
 
102
        switch (selector){
87 toby 103
        case filterSelectorAbout:
102 toby 104
                if(gdata && !gdata->parmloaded)
87 toby 105
                        gdata->standalone = gdata->parmloaded = readPARMresource(hDllInstance,&reason,1);
103 toby 106
                DoAbout((AboutRecordPtr)pb);
2 toby 107
                break;
108
        case filterSelectorParameters:
15 toby 109
                wantdialog = true;
2 toby 110
                break;
111
        case filterSelectorPrepare:
112
                DoPrepare(pb);
113
                init_symtab(predefs); // ready for parser calls
135 dmarschall 114
                init_trigtab();
2 toby 115
                break;
89 toby 116
        case filterSelectorStart:
117
                /* initialise the parameter handle that Photoshop keeps for us */
118
                if(!pb->parameters)
167 dmarschall 119
                        pb->parameters = PINEWHANDLE(1); // don't set initial size to 0, since some hosts (e.g. GIMP/PSPI) are incompatible with that.
2 toby 120
 
15 toby 121
                wantdialog |= checkandinitparams(pb->parameters);
122
 
123
                /* wantdialog = false means that we never got a Parameters call, so we're not supposed to ask user */
2 toby 124
                if( wantdialog && (!gdata->standalone || gdata->parm.popDialog) ){
125
                        if( maindialog(pb) ){
167 dmarschall 126
                                if (persistent_savestate) {
127
                                        StandardFileReply sfr;
128
                                        sfr.sfGood = true;
129
                                        sfr.sfReplacing = true;
130
                                        sfr.sfType = PS_FILTER_FILETYPE;
131
                                        myc2pstrcpy(sfr.sfFile.name, "tmp.afs");
132
                                        sfr.nFileExtension = 3; // length of "tmp"
133
                                        sfr.sfScript = 0; // FIXME: is that ok?
134
                                        savefile(&sfr);
135
                                }
136
 
15 toby 137
                                /* update stored parameters from new user settings */
2 toby 138
                                saveparams(pb->parameters);
139
                        }else
140
                                e = userCanceledErr;
141
                }
18 toby 142
                wantdialog = false;
2 toby 143
 
144
                if(!e){
145
                        if(setup(pb)){
146
                                DoStart(pb);
147
                        }else{
148
                                SYSBEEP(1);
149
                                e = filterBadParameters;
150
                        }
151
                }
152
                break;
103 toby 153
        case filterSelectorContinue:
154
                e = DoContinue(pb);
2 toby 155
                break;
156
        case filterSelectorFinish:
157
                DoFinish(pb);
158
                break;
159
        default:
160
                e = filterBadParameters;
161
        }
103 toby 162
 
2 toby 163
        *result = e;
164
 
165
        ExitCodeResource();
158 dmarschall 166
 
167
#ifdef WIN_ENV
159 dmarschall 168
        } __finally {
169
                if (activationContextUsed) DeactivateManifest(&manifestVars);
158 dmarschall 170
        }
171
#endif
2 toby 172
}
173
 
15 toby 174
int checkandinitparams(Handle params){
175
        char *reasonstr,*reason;
21 toby 176
        int i,f,showdialog;
103 toby 177
 
167 dmarschall 178
        if (persistent_savestate) {
179
                StandardFileReply sfr;
180
                sfr.sfGood = true;
181
                sfr.sfReplacing = true;
182
                sfr.sfType = PS_FILTER_FILETYPE;
183
                myc2pstrcpy(sfr.sfFile.name, "tmp.afs");
184
                sfr.nFileExtension = 3; // length of "tmp"
185
                sfr.sfScript = 0; // FIXME: is that ok?
186
                if (loadfile(&sfr, &reason)) return true;
187
        }
188
 
23 toby 189
        if( (f = !(params && readparams(params,false,&reasonstr))) ){
2 toby 190
                /* either the parameter handle was uninitialised,
191
                   or the parameter data couldn't be read; set default values */
192
 
89 toby 193
                // see if saved parameters exist
87 toby 194
                gdata->standalone = gdata->parmloaded = readPARMresource(hDllInstance,&reason,1);
89 toby 195
 
87 toby 196
                if(!gdata->standalone){
21 toby 197
                        // no saved settings (not standalone)
85 toby 198
                        for(i = 0; i < 8; ++i)
2 toby 199
                                slider[i] = i*10+100;
146 dmarschall 200
                        for(i = 0; i < 4; ++i)
201
                                if(expr[i])
202
                                        free(expr[i]);
89 toby 203
                        if(gpb->imageMode == plugInModeRGBColor){
204
                                expr[0] = my_strdup("r");
205
                                expr[1] = my_strdup("g");
206
                                expr[2] = my_strdup("b");
207
                                expr[3] = my_strdup("a");
208
                        }else{
209
                                expr[0] = my_strdup("c");
210
                                expr[1] = my_strdup("c");
211
                                expr[2] = my_strdup("c");
212
                                expr[3] = my_strdup("c");
213
                        }
2 toby 214
                }
215
        }
103 toby 216
 
89 toby 217
        // let scripting system change parameters, if we're scripted;
103 toby 218
        // user may want to force display of dialog during scripting playback
21 toby 219
        showdialog = ReadScriptParamsOnRead();
2 toby 220
 
89 toby 221
        saveparams(params);
21 toby 222
        return f || showdialog;
2 toby 223
}
224
 
225
void DoPrepare(FilterRecordPtr pb){
226
        int i;
227
 
85 toby 228
        for(i = 4; i--;){
8 toby 229
                if(expr[i]||tree[i]) DBG("expr[] or tree[] non-NULL in Prepare!");
2 toby 230
                expr[i] = NULL;
231
                tree[i] = NULL;
232
        }
106 dmarschall 233
 
234
        // Commented out by DM, 18 Dec 2018:
235
        // This code did not work on systems with 8 GB RAM:
236
        /*
237
        long space = (pb->maxSpace*9)/10; // don't ask for more than 90% of available memory
238
 
103 toby 239
        maxSpace = 512L<<10; // this is a wild guess, actually
240
        if(maxSpace > space)
241
                maxSpace = space;
242
        pb->maxSpace = maxSpace;
106 dmarschall 243
        */
244
 
245
        // New variant:
246
        // TODO: Programmatically test if host supports pb->maxSpace64, and if it does so, use this value instead.
247
        pb->maxSpace = ((double)pb->maxSpace/10)*9; // don't ask for more than 90% of available memory
2 toby 248
}
249
 
250
void RequestNext(FilterRecordPtr pb,long toprow){
251
        /* Request next block of the image */
252
 
253
        pb->inLoPlane = pb->outLoPlane = 0;
254
        pb->inHiPlane = pb->outHiPlane = nplanes-1;
255
 
66 toby 256
        // if any of the formulae involve random access to image pixels,
89 toby 257
        // ask for the entire image
94 toby 258
        if(needall){
2 toby 259
                SETRECT(pb->inRect,0,0,pb->imageSize.h,pb->imageSize.v);
260
        }else{
164 dmarschall 261
                // TODO: This does not work with GIMP. So, if we are using GIMP, we should
262
                //       somehow always use "needall=true", and/or find out why this doesn't work
263
                //       with GIMP.
264
 
66 toby 265
                // otherwise, process the filtered area, by chunksize parts
2 toby 266
                pb->inRect.left = pb->filterRect.left;
267
                pb->inRect.right = pb->filterRect.right;
268
                pb->inRect.top = toprow;
269
                pb->inRect.bottom = MIN(toprow + chunksize,pb->filterRect.bottom);
103 toby 270
 
71 toby 271
                if(cnvused){
272
                        // cnv() needs one extra pixel in each direction
273
                        if(pb->inRect.left > 0)
274
                                --pb->inRect.left;
275
                        if(pb->inRect.right < pb->imageSize.h)
276
                                ++pb->inRect.right;
277
                        if(pb->inRect.top > 0)
278
                                --pb->inRect.top;
279
                        if(pb->inRect.bottom < pb->imageSize.v)
280
                                ++pb->inRect.bottom;
281
                }
2 toby 282
        }
66 toby 283
        pb->outRect = pb->filterRect;
2 toby 284
/*
94 toby 285
{char s[0x100];sprintf(s,"RequestNext needall=%d inRect=(%d,%d,%d,%d) filterRect=(%d,%d,%d,%d)",
103 toby 286
        needall,
2 toby 287
        pb->inRect.left,pb->inRect.top,pb->inRect.right,pb->inRect.bottom,
288
        pb->filterRect.left,pb->filterRect.top,pb->filterRect.right,pb->filterRect.bottom);dbg(s);}
289
*/
290
}
291
 
292
void DoStart(FilterRecordPtr pb){
293
//dbg("DoStart");
294
        /* if src() or rad() functions are used, random access to the image data is required,
295
           so we must request the entire image in a single chunk. */
94 toby 296
        chunksize = needall ? (pb->filterRect.bottom - pb->filterRect.top) : CHUNK_ROWS;
2 toby 297
        toprow = pb->filterRect.top;
298
        RequestNext(pb,toprow);
299
}
300
 
301
OSErr DoContinue(FilterRecordPtr pb){
302
        OSErr e = noErr;
71 toby 303
        Rect fr;
304
        long outoffset;
2 toby 305
 
94 toby 306
        if(needall)
71 toby 307
                fr = pb->filterRect;  // filter whole selection at once
308
        else if(cnvused){
309
                // we've requested one pixel extra all around
310
                // (see RequestNext()), just for access purposes. But filter
311
                // original selection only.
312
                fr.left = pb->filterRect.left;
313
                fr.right = pb->filterRect.right;
314
                fr.top = toprow;
315
                fr.bottom = MIN(toprow + chunksize,pb->filterRect.bottom);
316
        }else  // filter whatever portion we've been given
317
                fr = pb->inRect;
318
 
103 toby 319
        outoffset = (long)pb->outRowBytes*(fr.top - pb->outRect.top)
71 toby 320
                                + (long)nplanes*(fr.left - pb->outRect.left);
321
 
322
        if(!(e = process_scaled(pb, true, &fr, &fr,
62 toby 323
                                (Ptr)pb->outData+outoffset, pb->outRowBytes, 1.)))
324
        {
2 toby 325
                toprow += chunksize;
326
                if(toprow < pb->filterRect.bottom)
327
                        RequestNext(pb,toprow);
328
                else{
329
                        SETRECT(pb->inRect,0,0,0,0);
330
                        pb->outRect = pb->maskRect = pb->inRect;
331
                }
332
        }
333
        return e;
334
}
335
 
336
void DoFinish(FilterRecordPtr pb){
337
        int i;
89 toby 338
 
2 toby 339
        WriteScriptParamsOnRead();
340
 
85 toby 341
        for(i = 4; i--;){
2 toby 342
                freetree(tree[i]);
343
                if(expr[i]) free(expr[i]);
344
        }
345
}