Subversion Repositories filter_foundry

Rev

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