Subversion Repositories filter_foundry

Rev

Rev 190 | Rev 194 | 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-2019 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 <stdio.h>
  22. //#include <sound.h>
  23.  
  24. #include "ff.h"
  25.  
  26. #include "node.h"
  27. #include "funcs.h"
  28. #include "y.tab.h"
  29. #include "scripting.h"
  30. #include <math.h>
  31.  
  32. struct node *tree[4];
  33. char *err[4];
  34. int errpos[4],errstart[4],nplanes,cnvused,chunksize,toprow;
  35. value_type slider[8],cell[NUM_CELLS],map[4][0x100];
  36. char *expr[4];
  37. // long maxSpace;
  38. globals_t *gdata;
  39. FilterRecordPtr gpb;
  40.  
  41. #ifdef MAC_ENV
  42.         #define hDllInstance NULL /* fake this Windows-only global */
  43. #endif
  44.  
  45. #ifdef WIN_ENV
  46. #include "manifest.h"
  47. #endif
  48.  
  49. extern struct sym_rec predefs[];
  50. extern int nplanes,varused[];
  51. extern int persistent_savestate = false;
  52.  
  53. int checkandinitparams(Handle params);
  54.  
  55. // MPW MrC requires prototype
  56. DLLEXPORT MACPASCAL
  57. void ENTRYPOINT(short selector,FilterRecordPtr pb,intptr_t *data,short *result);
  58.  
  59. DLLEXPORT MACPASCAL
  60. void ENTRYPOINT(short selector, FilterRecordPtr pb, intptr_t *data, short *result){
  61.         static Boolean wantdialog = false;
  62.         OSErr e = noErr;
  63.         char *reason;
  64.  
  65. #ifdef WIN_ENV
  66.         // For Windows, we use an activation context to enforce that our Manifest resource will
  67.         // be used. This allows us to use Visual Styles, even if the host application does not
  68.         // support it.
  69.         ManifestActivationCtx manifestVars;
  70.         BOOL activationContextUsed;
  71.  
  72.         activationContextUsed = ActivateManifest(hDllInstance, 1, &manifestVars);
  73.  
  74.         __try {
  75. #endif
  76.  
  77.         if(selector != filterSelectorAbout && !*data){
  78.                 BufferID tempId;
  79.                 if( (*result = PS_BUFFER_ALLOC(sizeof(globals_t), &tempId)) )
  80.                         return;
  81.                 *data = (intptr_t)PS_BUFFER_LOCK(tempId, true);
  82.                 gdata = (globals_t*)*data;
  83.                 gdata->standalone = gdata->parmloaded = false;
  84.         }else
  85.                 gdata = (globals_t*)*data;
  86.  
  87.         EnterCodeResource();
  88.  
  89.         gpb = pb;
  90.  
  91.         nplanes = MIN(pb->planes,4);
  92.  
  93.         switch (selector){
  94.         case filterSelectorAbout:
  95.                 if(gdata && !gdata->parmloaded)
  96.                         gdata->standalone = gdata->parmloaded = readPARMresource(hDllInstance,&reason,1);
  97.                 DoAbout((AboutRecordPtr)pb);
  98.                 break;
  99.         case filterSelectorParameters:
  100.                 wantdialog = true;
  101.                 break;
  102.         case filterSelectorPrepare:
  103.                 DoPrepare(pb);
  104.                 init_symtab(predefs); // ready for parser calls
  105.                 init_trigtab();
  106.                 break;
  107.         case filterSelectorStart:
  108.                 /* initialise the parameter handle that Photoshop keeps for us */
  109.                 if(!pb->parameters)
  110.                         pb->parameters = PINEWHANDLE(1); // don't set initial size to 0, since some hosts (e.g. GIMP/PSPI) are incompatible with that.
  111.  
  112.                 wantdialog |= checkandinitparams(pb->parameters);
  113.  
  114.                 /* wantdialog = false means that we never got a Parameters call, so we're not supposed to ask user */
  115.                 if( wantdialog && (!gdata->standalone || gdata->parm.popDialog) ){
  116.                         if( maindialog(pb) ){
  117.                                 if (!host_preserves_parameters()) {
  118.                                         /* Workaround for GIMP/PSPI, to avoid that formulas vanish when you re-open the main window.
  119.                                            The reason is a bug in PSPI: The host should preserve the value of pb->parameters, which PSPI does not do.
  120.                                            Also, all global variables are unloaded, so the plugin cannot preserve any data.
  121.                                            Workaround in FF 1.7: If the host GIMP is detected, the new flag persistent_savestate will be set.
  122.                                            This mode saves the filter data into a temporary file "FilterFoundry.afs" and loads it
  123.                                            when the window is opened again. */
  124.                                         // Workaround: Save settings in "FilterFoundry.afs" if the host does not preserve pb->parameters
  125.                                         char outfilename[255];
  126.                                         char* tempdir;
  127.                                         StandardFileReply sfr;
  128.                                         sfr.sfGood = true;
  129.                                         sfr.sfReplacing = true;
  130.                                         sfr.sfType = PS_FILTER_FILETYPE;
  131.  
  132.                                         tempdir = getenv("TMP");
  133.                                         #ifdef WIN_ENV
  134.                                         if (strlen(tempdir) > 0) strcat(tempdir, "\\");
  135.                                         #else
  136.                                         if (strlen(tempdir) > 0) strcat(tempdir, "/");
  137.                                         #endif
  138.                                         sprintf(outfilename, "%sFilterFoundry.afs", tempdir);
  139.  
  140.                                         myc2pstrcpy(sfr.sfFile.name, outfilename);
  141.                                         #ifdef WIN_ENV
  142.                                         sfr.nFileExtension = (WORD)(strlen(outfilename) - strlen(".afs"));
  143.                                         #endif
  144.                                         sfr.sfScript = 0; // FIXME: is that ok?
  145.                                         savefile(&sfr);
  146.                                 }
  147.  
  148.                                 /* update stored parameters from new user settings */
  149.                                 saveparams(pb->parameters);
  150.                         }else
  151.                                 e = userCanceledErr;
  152.                 }
  153.                 wantdialog = false;
  154.  
  155.                 if(!e){
  156.                         if(setup(pb)){
  157.                                 DoStart(pb);
  158.                         }else{
  159.                                 SYSBEEP(1);
  160.                                 e = filterBadParameters;
  161.                         }
  162.                 }
  163.                 break;
  164.         case filterSelectorContinue:
  165.                 e = DoContinue(pb);
  166.                 break;
  167.         case filterSelectorFinish:
  168.                 DoFinish(pb);
  169.                 break;
  170.         default:
  171.                 e = filterBadParameters;
  172.         }
  173.  
  174.         *result = e;
  175.  
  176.         ExitCodeResource();
  177.  
  178. #ifdef WIN_ENV
  179.         } __finally {
  180.                 if (activationContextUsed) DeactivateManifest(&manifestVars);
  181.         }
  182. #endif
  183. }
  184.  
  185. int checkandinitparams(Handle params){
  186.         char *reasonstr,*reason;
  187.         int i,f,showdialog;
  188.  
  189.         if (!host_preserves_parameters()) {
  190.                 // Workaround: Load settings in "FilterFoundry.afs" if host does not preserve pb->parameters
  191.                 char outfilename[255];
  192.                 char* tempdir;
  193.                 StandardFileReply sfr;
  194.                 sfr.sfGood = true;
  195.                 sfr.sfReplacing = true;
  196.                 sfr.sfType = PS_FILTER_FILETYPE;
  197.  
  198.                 tempdir = getenv("TMP");
  199.                 #ifdef WIN_ENV
  200.                 if (strlen(tempdir) > 0) strcat(tempdir, "\\");
  201.                 #else
  202.                 if (strlen(tempdir) > 0) strcat(tempdir, "/");
  203.                 #endif
  204.                 sprintf(outfilename, "%sFilterFoundry.afs", tempdir);
  205.  
  206.                 myc2pstrcpy(sfr.sfFile.name, outfilename);
  207.                 #ifdef WIN_ENV
  208.                 sfr.nFileExtension = (WORD)(strlen(outfilename) - strlen(".afs"));
  209.                 #endif
  210.                 sfr.sfScript = 0; // FIXME: is that ok?
  211.                 if (loadfile(&sfr, &reason)) return true;
  212.         }
  213.  
  214.         if( (f = !(params && readparams(params,false,&reasonstr))) ){
  215.                 /* either the parameter handle was uninitialised,
  216.                    or the parameter data couldn't be read; set default values */
  217.  
  218.                 // see if saved parameters exist
  219.                 gdata->standalone = gdata->parmloaded = readPARMresource(hDllInstance,&reason,1);
  220.  
  221.                 if(!gdata->standalone){
  222.                         // no saved settings (not standalone)
  223.                         for(i = 0; i < 8; ++i)
  224.                                 slider[i] = i*10+100;
  225.                         for(i = 0; i < 4; ++i)
  226.                                 if(expr[i])
  227.                                         free(expr[i]);
  228.                         if(gpb->imageMode == plugInModeRGBColor){
  229.                                 expr[0] = my_strdup("r");
  230.                                 expr[1] = my_strdup("g");
  231.                                 expr[2] = my_strdup("b");
  232.                                 expr[3] = my_strdup("a");
  233.                         }else{
  234.                                 expr[0] = my_strdup("c");
  235.                                 expr[1] = my_strdup("c");
  236.                                 expr[2] = my_strdup("c");
  237.                                 expr[3] = my_strdup("c");
  238.                         }
  239.                 }
  240.         }
  241.  
  242.         // let scripting system change parameters, if we're scripted;
  243.         // user may want to force display of dialog during scripting playback
  244.         showdialog = ReadScriptParamsOnRead();
  245.  
  246.         saveparams(params);
  247.         return f || showdialog;
  248. }
  249.  
  250. Boolean host_preserves_parameters() {
  251.         if (gpb->hostSig == HOSTSIG_GIMP) return false;
  252.         if (gpb->hostSig == HOSTSIG_IRFANVIEW) return false;
  253.  
  254.         /*
  255.         char x[100];
  256.         sprintf(x, "Host Signature: %u", gpb->hostSig);
  257.         simplealert(x);
  258.         */
  259.  
  260.         // We just assume the other hosts preserve the parameters
  261.         return true;
  262. }
  263.  
  264. int64_t maxspace(){
  265.         if (gpb->maxSpace64 != 0) {
  266.                 // If this is non-zero, the host either support 64-bit OR the host ignored the rule "set reserved fields to 0".
  267.                 uint64_t maxSpace64 = gpb->maxSpace64;
  268.  
  269.                 /*
  270.                 char x[100];
  271.                 sprintf(x, "maxSpace64: %lld", maxSpace64);
  272.                 simplealert(x);
  273.                 */
  274.  
  275.                 // Note: IrfanView currently does not support maxSpace64, so we don't handle HOSTSIG_IRFANVIEW
  276.                 return maxSpace64;
  277.         } else {
  278.                 // Note: If maxSpace gets converted from Int32 to unsigned int, we can reach up to 4 GB RAM. However, after this, there will be a wrap to 0 GB again.
  279.                 unsigned int maxSpace32 = (unsigned int) gpb->maxSpace;
  280.                 uint64_t maxSpace64 = maxSpace32;
  281.  
  282.                 /*
  283.                 char x[100];
  284.                 sprintf(x, "maxSpace32: %lld", maxSpace64);
  285.                 simplealert(x);
  286.                 */
  287.  
  288.                 if (gpb->hostSig == HOSTSIG_IRFANVIEW) maxSpace64 *= 1024; // IrfanView is giving Kilobytes instead of Bytes
  289.                 // TODO: Serif PhotoPlus also gives Kilobytes instead of bytes. But since it uses not a unique host signature, there is nothing we can do???
  290.                 return maxSpace64;
  291.         }
  292. }
  293.  
  294. Boolean maxspace_available() {
  295.         // GIMP sets MaxSpace to hardcoded 100 MB
  296.         if (gpb->hostSig == HOSTSIG_GIMP) return false;
  297.  
  298.         return true;
  299. }
  300.  
  301. void DoPrepare(FilterRecordPtr pb){
  302.         int i;
  303.  
  304.         for(i = 4; i--;){
  305.                 if(expr[i]||tree[i]) DBG("expr[] or tree[] non-NULL in Prepare!");
  306.                 expr[i] = NULL;
  307.                 tree[i] = NULL;
  308.         }
  309.  
  310.         // Commented out by DM, 18 Dec 2018:
  311.         // This code did not work on systems with 8 GB RAM:
  312.         /*
  313.         long space = (pb->maxSpace*9)/10; // don't ask for more than 90% of available memory
  314.  
  315.         maxSpace = 512L<<10; // this is a wild guess, actually
  316.         if(maxSpace > space)
  317.                 maxSpace = space;
  318.         pb->maxSpace = maxSpace;
  319.         */
  320.  
  321.         // New variant:
  322.         if (maxspace_available()) {
  323.                 pb->maxSpace = (int32)ceil((maxspace()/10.)*9); // don't ask for more than 90% of available memory
  324.                 // FIXME: Also maxSpace64
  325.         }
  326. }
  327.  
  328. void RequestNext(FilterRecordPtr pb,long toprow){
  329.         /* Request next block of the image */
  330.  
  331.         pb->inLoPlane = pb->outLoPlane = 0;
  332.         pb->inHiPlane = pb->outHiPlane = nplanes-1;
  333.  
  334.         // if any of the formulae involve random access to image pixels,
  335.         // ask for the entire image
  336.         if(needall){
  337.                 SETRECT(pb->inRect,0,0,pb->imageSize.h,pb->imageSize.v);
  338.         }else{
  339.                 // TODO: This does not work with GIMP. So, if we are using GIMP, we should
  340.                 //       somehow always use "needall=true", and/or find out why this doesn't work
  341.                 //       with GIMP.
  342.  
  343.                 // otherwise, process the filtered area, by chunksize parts
  344.                 pb->inRect.left = pb->filterRect.left;
  345.                 pb->inRect.right = pb->filterRect.right;
  346.                 pb->inRect.top = (int16)toprow;
  347.                 pb->inRect.bottom = (int16)MIN(toprow + chunksize,pb->filterRect.bottom);
  348.  
  349.                 if(cnvused){
  350.                         // cnv() needs one extra pixel in each direction
  351.                         if(pb->inRect.left > 0)
  352.                                 --pb->inRect.left;
  353.                         if(pb->inRect.right < pb->imageSize.h)
  354.                                 ++pb->inRect.right;
  355.                         if(pb->inRect.top > 0)
  356.                                 --pb->inRect.top;
  357.                         if(pb->inRect.bottom < pb->imageSize.v)
  358.                                 ++pb->inRect.bottom;
  359.                 }
  360.         }
  361.         pb->outRect = pb->filterRect;
  362. /*
  363. {char s[0x100];sprintf(s,"RequestNext needall=%d inRect=(%d,%d,%d,%d) filterRect=(%d,%d,%d,%d)",
  364.         needall,
  365.         pb->inRect.left,pb->inRect.top,pb->inRect.right,pb->inRect.bottom,
  366.         pb->filterRect.left,pb->filterRect.top,pb->filterRect.right,pb->filterRect.bottom);dbg(s);}
  367. */
  368. }
  369.  
  370. void DoStart(FilterRecordPtr pb){
  371. //dbg("DoStart");
  372.         /* if src() or rad() functions are used, random access to the image data is required,
  373.            so we must request the entire image in a single chunk. */
  374.         chunksize = needall ? (pb->filterRect.bottom - pb->filterRect.top) : CHUNK_ROWS;
  375.         toprow = pb->filterRect.top;
  376.         RequestNext(pb,toprow);
  377. }
  378.  
  379. OSErr DoContinue(FilterRecordPtr pb){
  380.         OSErr e = noErr;
  381.         Rect fr;
  382.         long outoffset;
  383.  
  384.         if(needall)
  385.                 fr = pb->filterRect;  // filter whole selection at once
  386.         else if(cnvused){
  387.                 // we've requested one pixel extra all around
  388.                 // (see RequestNext()), just for access purposes. But filter
  389.                 // original selection only.
  390.                 fr.left = pb->filterRect.left;
  391.                 fr.right = pb->filterRect.right;
  392.                 fr.top = toprow;
  393.                 fr.bottom = MIN(toprow + chunksize,pb->filterRect.bottom);
  394.         }else  // filter whatever portion we've been given
  395.                 fr = pb->inRect;
  396.  
  397.         outoffset = (long)pb->outRowBytes*(fr.top - pb->outRect.top)
  398.                                 + (long)nplanes*(fr.left - pb->outRect.left);
  399.  
  400.         if(!(e = process_scaled(pb, true, &fr, &fr,
  401.                                 (Ptr)pb->outData+outoffset, pb->outRowBytes, 1.)))
  402.         {
  403.                 toprow += chunksize;
  404.                 if(toprow < pb->filterRect.bottom)
  405.                         RequestNext(pb,toprow);
  406.                 else{
  407.                         SETRECT(pb->inRect,0,0,0,0);
  408.                         pb->outRect = pb->maskRect = pb->inRect;
  409.                 }
  410.         }
  411.         return e;
  412. }
  413.  
  414. void DoFinish(FilterRecordPtr pb){
  415.         int i;
  416.  
  417.         WriteScriptParamsOnRead();
  418.  
  419.         for(i = 4; i--;){
  420.                 freetree(tree[i]);
  421.                 if(expr[i]) free(expr[i]);
  422.         }
  423. }
  424.