Subversion Repositories filter_foundry

Rev

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