Subversion Repositories filter_foundry

Rev

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