Subversion Repositories filter_foundry

Rev

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