Subversion Repositories filter_foundry

Rev

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

Rev Author Line No. Line
259 daniel-mar 1
/*
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
503 daniel-mar 4
    Copyright (C) 2018-2022 Daniel Marschall, ViaThinkSoft
259 daniel-mar 5
 
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
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
 
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
18
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
*/
20
 
21
#include "ff.h"
22
 
23
#include "symtab.h"
24
#include "node.h"
25
#include "funcs.h"
26
#include "y.tab.h"
27
 
28
extern value_type var[];
29
extern int nplanes,varused[],cnvused;
30
extern struct node *tree[];
31
 
32
// points to first row, first column of selection image data
33
// this is used by src() and cnv() functions to access pixels
34
unsigned char *image_ptr;
35
 
36
extern int needinput;
37
int state_changing_funcs_used;
38
 
39
/* get prepared to evaluate expression trees--
40
   this assumes that tree[] array is already set up
41
   return TRUE if we're ready to go
42
*/
43
 
44
// minimum setup required when formulae have not changed,
45
// and a new preview is to be generated. (Called by recalc_preview())
46
void evalinit(){
47
        int i;
48
 
500 daniel-mar 49
        initialize_rnd_variables();
259 daniel-mar 50
 
51
        for (i=0; i<NUM_CELLS; ++i) {
52
                cell[i] = 0;
53
        }
54
}
55
 
56
// full setup for evaluation, called when formulae have changed.
57
Boolean setup(FilterRecordPtr pb){
343 daniel-mar 58
        int srcrad;
259 daniel-mar 59
        int i;
60
 
61
        // Attention: If you introduce new variables, please define them also in lexer.l
62
        if (HAS_BIG_DOC(pb)) {
63
                var['X'] = BIGDOC_FILTER_RECT(pb).right - BIGDOC_FILTER_RECT(pb).left;
64
                var['Y'] = BIGDOC_FILTER_RECT(pb).bottom - BIGDOC_FILTER_RECT(pb).top;
65
        } else {
66
                var['X'] = FILTER_RECT(pb).right - FILTER_RECT(pb).left;
67
                var['Y'] = FILTER_RECT(pb).bottom - FILTER_RECT(pb).top;
68
        }
69
        var['Z'] = nplanes;
335 daniel-mar 70
        var['D'] = val_D;
294 daniel-mar 71
        var['M'] = ff_M();
259 daniel-mar 72
 
304 daniel-mar 73
        var['R'] = var['G'] = var['B'] = var['A'] = var['C'] = 255;
335 daniel-mar 74
        var['I'] = val_I;
75
        var['U'] = val_U;
76
        var['V'] = val_V;
304 daniel-mar 77
 
259 daniel-mar 78
        /* initialise flags for tracking special variable usage */
79
        for(i = 0; i < 0x100; i++)
80
                varused[i] = 0;
343 daniel-mar 81
        needall = srcrad = cnvused = state_changing_funcs_used = 0;
259 daniel-mar 82
        for(i = 0; i < nplanes; ++i){
312 daniel-mar 83
                //char s[100];sprintf(s,"expr[%d]=%#x",i,expr[i]);dbg(s);
259 daniel-mar 84
                if( tree[i] || (tree[i] = parseexpr(expr[i])) )
312 daniel-mar 85
                        // if src() and rad() is used => needall=1, since we need arbitary access to all pixels
343 daniel-mar 86
                        checkvars(tree[i],varused,&cnvused,&srcrad,&state_changing_funcs_used);
259 daniel-mar 87
                else
88
                        break;
89
        }
343 daniel-mar 90
        needall = srcrad;
259 daniel-mar 91
        needinput = ( cnvused || needall
92
                || varused['r'] || varused['g'] || varused['b'] || varused['a']
93
                || varused['i'] || varused['u'] || varused['v'] || varused['c'] );
94
 
95
        /*
96
         * Workaround for PSPI for GIMP:
97
         * Filters will only fill the bottom of the picture, not the whole canvas.
98
         * The reason is that OnContinue/main.c:RequestNext() processes the image in chunks,
99
         * and probably due to a bug, PSPI only applies the image data of the last chunk.
100
         * Workaround applied in FF 1.7: If the host is GIMP, then we set
101
         * needall=1 to disable chunked processing.
102
         */
103
        if (pb->hostSig == HOSTSIG_GIMP) needall = true;
104
 
288 daniel-mar 105
        // If we want accurate rnd(a,b) results (i.e. FilterFoundry and FilterFactory output
106
        // exactly the same picture), we must not use chunked processing.
107
        if (state_changing_funcs_used) needall = true;
108
 
343 daniel-mar 109
        // DM 09 Sep 2021: Added, because otherwise, some filters are very, very, very slow!
110
        // e.g. BlowOut by Greg Schorno:
111
    //     R = put(sin(d*ctl(0)/4+ctl(2)*4)/(val(1,256,16)*256/M),0),src(x+get(0),y+get(0),z)
112
    //     G = src(x+get(0),y+get(0),z)
113
    //     B = src(x+get(0),y+get(0),z)
114
        state_changing_funcs_used = 0;
115
 
259 daniel-mar 116
        evalinit();
117
        return i==nplanes; /* all required expressions parse OK */
118
}
119
 
120
void evalpixel(unsigned char *outp,unsigned char *inp){
121
        int f,k;
122
 
123
        if(needinput){
124
                var['r'] = inp[0];
125
                var['g'] = nplanes > 1 ? inp[1] : 0;
126
                var['b'] = nplanes > 2 ? inp[2] : 0;
127
                var['a'] = nplanes > 3 ? inp[3] : 0;
128
 
129
                // For Y, the definition is Y := 0.299R + 0.587G + 0.114B
294 daniel-mar 130
                if(varused['i']) var['i'] = ff_i();
259 daniel-mar 131
 
132
                // For U, the definition is U := (B-Y) * 0.493; the range would be [-111..111]
133
                // Filter Factory divided it by 2, resulting in a range of [-55..55].
134
                // Due to compatibility reasons, we adopt that behavior.
294 daniel-mar 135
                if(varused['u']) var['u'] = ff_u();
259 daniel-mar 136
 
137
                // For V, the definition is V := (R-Y) * 0.877; the range would be [-156..156]
138
                // Filter Factory divided it by 2, resulting in a range of [-78..78].
139
                // Due to compatibility reasons, we adopt that behavior.
294 daniel-mar 140
                if(varused['v']) var['v'] = ff_v();
259 daniel-mar 141
        }
312 daniel-mar 142
 
294 daniel-mar 143
        if(varused['d']) var['d'] = ff_d();
144
        if(varused['m']) var['m'] = ff_m();
259 daniel-mar 145
 
146
        for(k = 0; k < nplanes; ++k){
147
                if(needinput)
148
                        var['c'] = inp[k];
149
                var['z'] = k;
150
                var['p'] = k; // undocumented alias of z
151
                f = eval(tree[k]);
152
                if (outp)
153
                        outp[k] = f<0 ? 0 : (f>255 ? 255 : f); // clamp channel value to 0-255
154
        }
155
}
156
 
157
/* Zoom and filter image.
158
 * Parameters:  pb          - Photoshop Filter parameter block
159
 *              progress    - whether to use Photoshop's progress bar
160
 *                            (not appropriate during preview)
161
 *              filterRect  - rectangle (within pb->inRect)
162
 *                            of area to be filtered. This may not correspond
163
 *                            to pb->filterRect, it may be just a piece.
164
 *              outPiece    - rectangle defining scaled output buffer.
165
 *                            In case of zoomed preview, this is physically
166
 *                            scaled FROM filterRect (or equal to filterRect
167
 *                            for unscaled 1:1 filtering).
168
 *              outData     - pointer to output data buffer
169
 *              outRowBytes - row stride of output data buffer
170
 *              zoom        - pixel scale factor (both horiz & vert)
171
 *                            e.g. 2.0 means 1 output pixel per 2 input pixels.
172
 */
173
 
174
//#define PROCESS_SCALED_GAP_DEBUG 1
175
 
176
OSErr process_scaled_bigdoc(FilterRecordPtr pb, Boolean progress,
177
                          VRect filterPiece, VRect outPiece,
178
                          void *outData, long outRowBytes, double zoom){
179
        unsigned char *inrow,*outrow,*outp;
180
        int j,i;
181
        int64_t t, ticks = TICKCOUNT();
182
        double x,y,k;
183
 
184
        #ifdef PROCESS_SCALED_GAP_DEBUG
185
        char s[0x200];
186
        int last_good_x, last_good_y;
187
        last_good_y = -1;
188
        #endif
189
 
190
        VRect filterRect;
191
        VRect inRect;
192
 
193
        if (HAS_BIG_DOC(pb)) {
194
                filterRect = BIGDOC_FILTER_RECT(pb);
195
                inRect = BIGDOC_IN_RECT(pb);
196
        } else {
197
                filterRect.bottom = FILTER_RECT(pb).bottom;
198
                filterRect.left = FILTER_RECT(pb).left;
199
                filterRect.right = FILTER_RECT(pb).right;
200
                filterRect.top = FILTER_RECT(pb).top;
201
                inRect.bottom = IN_RECT(pb).bottom;
202
                inRect.left = IN_RECT(pb).left;
203
                inRect.right = IN_RECT(pb).right;
204
                inRect.top = IN_RECT(pb).top;
205
        }
206
 
207
        // find base pointer to selection image data
208
        image_ptr = (unsigned char*)pb->inData
209
                                + (long)pb->inRowBytes*(filterRect.top - inRect.top)
210
                                + (long)nplanes*(filterRect.left - inRect.left);
211
 
212
        if (state_changing_funcs_used) {
213
                // Fill gap between selection/filter top border and top preview zoomed border
214
                for (y = 0; y < (double)filterPiece.top - (double)filterRect.top; ++y) {
215
                        #ifdef PROCESS_SCALED_GAP_DEBUG
216
                        if (state_changing_funcs_used && last_good_y != (int)floor(y-1)) { sprintf(s, "Non calculated Y gap, type 1: %f, last good %d, zoom %f\n", y, last_good_y, zoom); simplealert(s); } last_good_y = (int)floor(y);
217
                        #endif
218
 
219
                        var['y'] = (value_type)y;
220
                        inrow = image_ptr + (long)(y)*pb->inRowBytes;
221
 
222
                        #ifdef PROCESS_SCALED_GAP_DEBUG
223
                        last_good_x = -1;
224
                        #endif
225
 
226
                        for (x = 0; x < (double)filterRect.right - (double)filterRect.left; ++x) {
227
                                #ifdef PROCESS_SCALED_GAP_DEBUG
228
                                if (state_changing_funcs_used && last_good_x != (int)floor(x-1)) { sprintf(s, "Non calculated X gap, type 1a: %f, last good %d, zoom %f\n", x, last_good_x, zoom); simplealert(s); } last_good_x = (int)floor(x);
229
                                #endif
230
 
231
                                var['x'] = (value_type)x;
232
                                evalpixel(NULL,inrow + (long)(x)*nplanes);
233
                        }
234
 
235
                        #ifdef PROCESS_SCALED_GAP_DEBUG
236
                        if (var['x'] != var['X']-1) { sprintf(s, "X not at right border #1: x=%d, X=%d\n", var['x'], var['X']); simplealert(s); }
237
                        #endif
238
                }
239
        }
240
 
241
        // j indexes scaled output rows
242
        for( j = outPiece.top, outrow = (unsigned char*)outData, y = (double)filterPiece.top - (double)filterRect.top ;
243
                 j < outPiece.bottom ; ++j, outrow += outRowBytes, y += zoom )
244
        {
245
                #ifdef PROCESS_SCALED_GAP_DEBUG
246
                if (state_changing_funcs_used && last_good_y != (int)floor(y-1)) { sprintf(s, "Non calculated Y gap, type 1: %f, last good %d, zoom %f\n", y, last_good_y, zoom); simplealert(s); } last_good_y = (int)floor(y);
247
                #endif
248
 
249
                var['y'] = (value_type)y;  // index of corresponding *input* row, top of selection == 0
250
                inrow = image_ptr + (long)y*pb->inRowBytes;
251
 
252
                #ifdef PROCESS_SCALED_GAP_DEBUG
253
                last_good_x = -1;
254
                #endif
255
 
256
                if (state_changing_funcs_used) {
257
                        // Fill gap between left selection/image border and left border of the preview-area
258
                        for (x = 0; x < (double)filterPiece.left - (double)filterRect.left; ++x) {
259
                                #ifdef PROCESS_SCALED_GAP_DEBUG
260
                                if (state_changing_funcs_used && last_good_x != (int)floor(x-1)) { sprintf(s, "Non calculated X gap, type 2a: %f, last good %d, zoom %f\n", x, last_good_x, zoom); simplealert(s); } last_good_x = (int)floor(x);
261
                                #endif
262
 
263
                                var['x'] = (value_type)x;
264
                                evalpixel(NULL,inrow + (long)(x)*nplanes);
265
                        }
266
                }
267
 
268
                // i indexes scaled output columns
269
                for( outp = outrow, i = outPiece.left, x = (double)filterPiece.left - (double)filterRect.left ;
270
                         i < outPiece.right ; ++i, outp += nplanes, x += zoom )
271
                {
272
                        #ifdef PROCESS_SCALED_GAP_DEBUG
273
                        if (state_changing_funcs_used && last_good_x != (int)floor(x-1)) { sprintf(s, "Non calculated X gap, type 2b: %f, last good %d, zoom %f\n", x, last_good_x, zoom); simplealert(s); } last_good_x = (int)floor(x);
274
                        #endif
275
 
276
                        var['x'] = (value_type)x;  // index of corresponding *input* column, left of selection == 0
277
                        evalpixel(outp,inrow + (long)(x)*nplanes); /* var['x'] & var['y'] are implicit parameters */
278
 
279
                        if (state_changing_funcs_used) {
280
                                // Fill gap between each X-preview-pixel (discarded pixels due to zoom level)
281
                                for (k = x+1; floor(k) < floor(x + zoom); ++k) {
282
                                        #ifdef PROCESS_SCALED_GAP_DEBUG
283
                                        if (state_changing_funcs_used && last_good_x != (int)floor(k-1)) { sprintf(s, "Non calculated X gap, type 2c: %f (x=%f), last good %d, zoom %f\n", k, x, last_good_x, zoom); simplealert(s); } last_good_x = (int)floor(k);
284
                                        #endif
285
 
286
                                        var['x'] = (value_type)k;
287
                                        if (var['x'] >= var['X']) break;
288
                                        evalpixel(NULL,inrow + (long)(k)*nplanes);
289
                                }
290
                        }
291
                }
292
 
293
                if (state_changing_funcs_used) {
294
                        // Fill gap between right border of preview-area and right border of selection/image border
295
 
296
                        for (x = (double)var['x']+1; x < (double)filterRect.right - (double)filterRect.left; ++x) {
297
                                #ifdef PROCESS_SCALED_GAP_DEBUG
298
                                if (state_changing_funcs_used && last_good_x != (int)floor(x-1)) { sprintf(s, "Non calculated X gap, type 2d: %f, last good %d, zoom %f\n", x, last_good_x, zoom); simplealert(s); } last_good_x = (int)floor(x);
299
                                #endif
300
 
301
                                var['x'] = (value_type)x;
302
                                evalpixel(NULL,inrow + (long)(x)*nplanes);
303
                        }
304
 
305
                        #ifdef PROCESS_SCALED_GAP_DEBUG
306
                        if (var['x'] != var['X']-1) { sprintf(s, "X not at right border #2: x=%d, X=%d\n", var['x'], var['X']); simplealert(s);}
307
                        #endif
308
 
309
                        // Fill gap between each Y-preview-pixel (discarded pixels due to zoom level),
310
                        // but not for the very last line, since we are then done drawing our preview picture
311
                        for (k = y+1; floor(k) < floor(y + zoom) && (j < outPiece.bottom-1); ++k) {
312
                                #ifdef PROCESS_SCALED_GAP_DEBUG
313
                                if (state_changing_funcs_used && last_good_y != (int)floor(k-1)) { sprintf(s, "Non calculated Y gap, type 3a: %f (y=%f), last good %d, zoom %f\n", k, y, last_good_y, zoom); simplealert(s); } last_good_y = (int)floor(k);
314
                                #endif
315
 
316
                                var['y'] = (value_type)k;
317
                                if (var['y'] >= var['Y']) break;
318
                                inrow = image_ptr + (long)(k)*pb->inRowBytes;
319
 
320
                                #ifdef PROCESS_SCALED_GAP_DEBUG
321
                                last_good_x = -1;
322
                                #endif
323
 
324
                                for (x = 0; x < (double)filterRect.right - (double)filterRect.left; ++x) {
325
                                        #ifdef PROCESS_SCALED_GAP_DEBUG
326
                                        if (state_changing_funcs_used && last_good_x != (int)floor(x-1)) { sprintf(s, "Non calculated X gap, type 3b: %f, last good %d, zoom %f\n", x, last_good_x, zoom); simplealert(s); } last_good_x = (int)floor(x);
327
                                        #endif
328
 
329
                                        var['x'] = (value_type)x;
330
                                        evalpixel(NULL,inrow + (long)(x)*nplanes);
331
                                }
332
 
333
                                #ifdef PROCESS_SCALED_GAP_DEBUG
334
                                if (var['x'] != var['X']-1) {sprintf(s, "X not at right border #3: x=%d, X=%d\n", var['x'], var['X']); simplealert(s);}
335
                                #endif
336
                        }
337
                }
338
 
339
                if(progress){
340
                        if((t = TICKCOUNT()) > ticks){
341
                                ticks = t + TICKS_SEC/4;
342
                                if(pb->abortProc())
343
                                        return userCanceledErr;
344
                                else
345
                                        pb->progressProc((int)y - filterRect.top,filterRect.bottom - filterRect.top);
346
                        }
268 daniel-mar 347
                }else{
348
                        #ifdef MAC_ENV
259 daniel-mar 349
                        /* to stop delays during typing of expressions,
350
                           immediately abort preview calculation if a key or mouse has been pressed. */
351
                        EventRecord event;
268 daniel-mar 352
                        if(EventAvail(mDownMask|keyDownMask|autoKeyMask,&event)) {
259 daniel-mar 353
                                return userCanceledErr;
268 daniel-mar 354
                        }
355
                        #endif
259 daniel-mar 356
                }
357
        }
358
 
359
        // Note for state_changing_funcs_used: We will not evaluate the gap between bottom border
360
        // of preview area and the bottom border of the selection/filter, because there are no
361
        // preview output pixels left that could be affected by these gap evaluations.
362
 
363
        return noErr;
364
}
365
 
366
OSErr process_scaled_olddoc(FilterRecordPtr pb, Boolean progress,
367
        Rect filterPiece, Rect outPiece,
368
        void* outData, long outRowBytes, double zoom) {
369
 
370
        VRect filterPiece32;
371
        VRect outPiece32;
372
 
373
        filterPiece32.bottom = filterPiece.bottom;
374
        filterPiece32.left = filterPiece.left;
375
        filterPiece32.right = filterPiece.right;
376
        filterPiece32.top = filterPiece.top;
377
 
378
        outPiece32.bottom = outPiece.bottom;
379
        outPiece32.left = outPiece.left;
380
        outPiece32.right = outPiece.right;
381
        outPiece32.top = outPiece.top;
382
 
383
        return process_scaled_bigdoc(pb, progress, filterPiece32, outPiece32, outData, outRowBytes, zoom);
384
}