Subversion Repositories filter_foundry

Rev

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