Subversion Repositories filter_foundry

Rev

Rev 536 | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. /*
  2.     This file is part of "Filter Foundry", a filter plugin for Adobe Photoshop
  3.     Copyright (C) 2003-2009 Toby Thain, toby@telegraphics.net
  4.     Copyright (C) 2018-2023 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(void){
  47.         int i;
  48.  
  49.         initialize_rnd_variables();
  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 srcrad;
  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;
  70.         var['D'] = val_D;
  71.         var['M'] = ff_M();
  72.  
  73.         var['R'] = var['G'] = var['B'] = var['A'] = var['C'] = 255;
  74.         var['I'] = val_I;
  75.         var['U'] = val_U;
  76.         var['V'] = val_V;
  77.  
  78.         /* initialise flags for tracking special variable usage */
  79.         for(i = 0; i < 0x100; i++)
  80.                 varused[i] = 0;
  81.         needall = srcrad = cnvused = state_changing_funcs_used = 0;
  82.         for(i = 0; i < nplanes; ++i){
  83.                 //char s[100];sprintf(s,"gdata->parm.szFormula[%d]=%#x",i,gdata->parm.szFormula[i]);dbg(s);
  84.                 if( tree[i] || (tree[i] = parseexpr(gdata->parm.szFormula[i])) )
  85.                         // if src() and rad() is used => needall=1, since we need arbitary access to all pixels
  86.                         checkvars(tree[i],varused,&cnvused,&srcrad,&state_changing_funcs_used);
  87.                 else
  88.                         break;
  89.         }
  90.         needall = srcrad;
  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.  
  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.  
  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.  
  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
  130.                 if(varused['i']) var['i'] = ff_i();
  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.
  135.                 if(varused['u']) var['u'] = ff_u();
  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.
  140.                 if(varused['v']) var['v'] = ff_v();
  141.         }
  142.  
  143.         if(varused['d']) var['d'] = ff_d();
  144.         if(varused['m']) var['m'] = ff_m();
  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.                         }
  347.                 }else{
  348.                         #ifdef MAC_ENV
  349.                         /* to stop delays during typing of expressions,
  350.                            immediately abort preview calculation if a key or mouse has been pressed. */
  351.                         EventRecord event;
  352.                         if(EventAvail(mDownMask|keyDownMask|autoKeyMask,&event)) {
  353.                                 return userCanceledErr;
  354.                         }
  355.                         #endif
  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. }
  385.