Subversion Repositories filter_foundry

Rev

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