Subversion Repositories filter_foundry

Rev

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