Subversion Repositories filter_foundry

Rev

Rev 256 | Rev 264 | 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 <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. #include "PIBufferSuite.h"
  32.  
  33. // GIMP (PSPI) and IrfanView preserve neither *data(gdata), nor pb->parameters between invocations!
  34. // For debugging, we can simulate it here
  35. //#define DEBUG_SIMULATE_GIMP
  36.  
  37. struct node *tree[4];
  38. char *err[4];
  39. int errpos[4],errstart[4],nplanes,cnvused,chunksize,toprow;
  40. value_type slider[8],cell[NUM_CELLS],map[4][0x100];
  41. char *expr[4];
  42. // long maxSpace;
  43. globals_t *gdata;
  44. FilterRecordPtr gpb;
  45.  
  46. #ifdef MAC_ENV
  47.         #define HINSTANCE HANDLE
  48.         #define hDllInstance NULL /* fake this Windows-only global */
  49. #endif
  50.  
  51. #ifdef WIN_ENV
  52. #include "manifest.h"
  53. #endif
  54.  
  55. extern struct sym_rec predefs[];
  56. extern int nplanes,varused[];
  57.  
  58. int checkandinitparams(Handle params);
  59.  
  60. // MPW MrC requires prototype
  61. DLLEXPORT MACPASCAL
  62. void ENTRYPOINT(short selector,FilterRecordPtr pb,intptr_t *data,short *result);
  63.  
  64. unsigned long get_parm_hash(PARM_T parm) {
  65.         unsigned long hash;
  66.         int i;
  67.  
  68.         hash = djb2((char*)parm.category);
  69.         hash += djb2((char*)parm.title);
  70.         hash += djb2((char*)parm.copyright);
  71.         hash += djb2((char*)parm.author);
  72.         for (i = 0; i < 4; i++) hash += hash += djb2((char*)parm.map[i]);
  73.         for (i = 0; i < 8; i++) hash += hash += djb2((char*)parm.ctl[i]);
  74.         for (i = 0; i < 4; i++) hash += hash += djb2((char*)parm.formula[i]);
  75.  
  76.         return hash;
  77. }
  78.  
  79. DLLEXPORT MACPASCAL
  80. void ENTRYPOINT(short selector, FilterRecordPtr pb, intptr_t *data, short *result){
  81.         static Boolean wantdialog = false;
  82.         static Boolean premiereWarnedOnce = false;
  83.         OSErr e = noErr;
  84.         char *reason;
  85. #ifdef WIN_ENV
  86.         // For Windows, we use an activation context to enforce that our Manifest resource will
  87.         // be used. This allows us to use Visual Styles, even if the host application does not
  88.         // support it.
  89.         ManifestActivationCtx manifestVars;
  90.         BOOL activationContextUsed;
  91. #endif
  92.  
  93.         EnterCodeResource();
  94.  
  95.         /*
  96.         char* s = (char*)malloc(512);
  97.         sprintf(s, "Host signature: '%c%c%c%c' (%d)\nMaxSpace32 = %d\nMaxSpace64 = %lld\nNum buffer procs: %d", (pb->hostSig >> 24) & 0xFF, (pb->hostSig >> 16) & 0xFF, (pb->hostSig >> 8) & 0xFF, pb->hostSig & 0xFF, pb->hostSig, pb->maxSpace, pb->maxSpace64, pb->bufferProcs->numBufferProcs);
  98.         simplealert(s);
  99.         */
  100.  
  101.         if (pb->hostSig == HOSTSIG_ADOBE_PREMIERE) {
  102.                 // DM 19.07.2021 : Tried running the 8BF file in Adobe Premiere 5 (yes, that's possible,
  103.                 // and there is even a FilterFactory for Premeire!),
  104.                 // but it crashes in evalpixel() where there is write-access to the "outp".
  105.                 // Probably the canvas structure is different (maybe it contains frames to achieve transitions?)
  106.                 if (!premiereWarnedOnce) {
  107.                         simplealert(_strdup("This version of Filter Foundry is not compatible with Adobe Premiere!"));
  108.                 }
  109.                 premiereWarnedOnce = true;
  110.                 *result = errPlugInHostInsufficient;
  111.                 return;
  112.         }
  113.  
  114.         #ifdef DEBUG_SIMULATE_GIMP
  115.         *data = 0;
  116.         pb->parameters = pb->handleProcs->newProc(1);
  117.         #endif
  118.  
  119.         // Register "gdata" that contains the PARM information and other things which need to be persistant
  120.         // and preserve then in *data
  121.         // TODO: memory leak? where is the stuff freed?
  122.         if (selector != filterSelectorAbout && !*data) {
  123.                 /*
  124.                 PSBufferSuite1* pSBufferSuite32 = NULL;
  125.  
  126.                 if ((pb->sSPBasic == 0) ||
  127.                         (pb->sSPBasic->AcquireSuite(kPSBufferSuite, kPSBufferSuiteVersion1, (const void**)&pSBufferSuite32)) ||
  128.                         (pSBufferSuite32 == NULL))
  129.                 {
  130.                                 // Old deprecated buffer suite
  131.                                 BufferID tempId;
  132.                                 if ((*result = pb->bufferProcs->allocateProc(sizeof(globals_t), &tempId))) return;
  133.                                 *data = (intptr_t)pb->bufferProcs->lockProc(tempId, true);
  134.                                 gdata = (globals_t*)*data;
  135.                 }
  136.                 else
  137.                 {
  138.                                 // New buffer suite (but only 32-bit version 1, because version 2 has problems with old Photoshop versions)
  139.                                 // Windows Photoshop 7 and CS 2 accepts kPSBufferSuiteVersion2, but doesn't correctly implement it:
  140.                                 // The symbols "New" and "GetSpace64" point to memory memory addresses outside the Photoshop.exe address range.
  141.                                 // (Other Photoshop versions were not tested.)
  142.                                 // 64-bit support for Windows was established in Photoshop CS 4,
  143.                                 // and PSBufferSuite2 was first documented in SDK CS 6.
  144.                                 // So, kPSBufferSuiteVersion2 probably was partically implemented as hidden "Work in progress" version
  145.                                 // before it was publicly documented.
  146.                                 // Side note:  pb->bufferSpace64/pb->maxSpace64 was documented in SDK CC 2017.
  147.                                 //             pb->bufferProcs->allocateProc64/spaceProc64 was documented in SDK CS 6.
  148.                                 unsigned32 siz = sizeof(globals_t);
  149.                                 *data = (intptr_t)pSBufferSuite32->New(&siz, siz);
  150.                                 if ((*data == 0) || (siz == 0)) {
  151.                                                 *result = errPlugInHostInsufficient; // TODO: what is the correct error code for "out of memory"?
  152.                                                 return;
  153.                                 }
  154.                                 gdata = (globals_t*)*data;
  155.                                 pb->sSPBasic->ReleaseSuite(kPSBufferSuite, kPSBufferSuiteVersion1);
  156.                 }
  157.                 gdata->standalone = gdata->parmloaded = false;
  158.                 */
  159.  
  160.                 // We have 3 options:
  161.                 // - The deprecated buffer suite (pb->bufferProcs), works fine
  162.                 // - The recommended buffer suite (kPSBufferSuite), does NOT work (causes memory corruption?) and is not available on some hosts!
  163.                 //   Either I do something wrong, or maybe it cannot be used to store data between invocations?
  164.                 // - Using malloc(), which works also fine and is more independent from the host and easier
  165.                 *data = (intptr_t)malloc(sizeof(globals_t));
  166.                 if (*data == 0) return;
  167.                 gdata = (globals_t*)*data;
  168.                 gdata->standalone = gdata->parmloaded = false; // they will be set later
  169.         }
  170.         else {
  171.                 // We have data from the previous invocation. Use it instead
  172.                 gdata = (globals_t*)*data;
  173.         }
  174.  
  175.         #ifdef WIN_ENV
  176.         activationContextUsed = ActivateManifest((HMODULE)hDllInstance, 1, &manifestVars);
  177.         #endif
  178.  
  179.         gpb = pb;
  180.  
  181.         nplanes = MIN(pb->planes,4);
  182.  
  183.         switch (selector){
  184.         case filterSelectorAbout:
  185.                 if (!gdata) {
  186.                         gdata = (globals_t*)malloc(sizeof(globals_t));
  187.                         if (!gdata) break;
  188.                         gdata->standalone = gdata->parmloaded = readPARMresource((HMODULE)hDllInstance,&reason,READ_OBFUSC);
  189.                         DoAbout((AboutRecordPtr)pb);
  190.                         free(gdata);
  191.                         gdata = NULL;
  192.                 } else {
  193.                         DoAbout((AboutRecordPtr)pb);
  194.                 }
  195.                 break;
  196.         case filterSelectorParameters:
  197.                 wantdialog = true;
  198.                 break;
  199.         case filterSelectorPrepare:
  200.                 DoPrepare(pb);
  201.                 init_symtab(predefs); // ready for parser calls
  202.                 init_trigtab();
  203.                 break;
  204.         case filterSelectorStart:
  205.                 if (HAS_BIG_DOC(pb)) {
  206.                         // The BigDocument structure is required if the document is larger than 30,000 pixels
  207.                         // It deprecates imageSize, filterRect, inRect, outRect, maskRect, floatCoord, and wholeSize.
  208.                         // By setting it to nonzero, we communicate to Photoshop that we support the BigDocument structure.
  209.                         pb->bigDocumentData->PluginUsing32BitCoordinates = true;
  210.                 }
  211.  
  212.                 /* initialise the parameter handle that Photoshop keeps for us */
  213.                 if(!pb->parameters)
  214.                         pb->parameters = PINEWHANDLE(1); // don't set initial size to 0, since some hosts (e.g. GIMP/PSPI) are incompatible with that.
  215.  
  216.                 wantdialog |= checkandinitparams(pb->parameters);
  217.  
  218.                 /* wantdialog = false means that we never got a Parameters call, so we're not supposed to ask user */
  219.                 if( wantdialog && (!gdata->standalone || gdata->parm.popDialog) ){
  220.                         if( maindialog(pb) ){
  221.                                 if (!host_preserves_parameters()) {
  222.                                         if (!gdata->obfusc) { // If the filter is obfuscated, we may not save the formula to the .afs (TODO: just save the ctl/map, but not the r/g/b/a-formulas!)
  223.                                                 /* Workaround for GIMP/PSPI, to avoid that formulas vanish when you re-open the main window.
  224.                                                    The reason is a bug in PSPI: The host should preserve the value of pb->parameters, which PSPI does not do.
  225.                                                    Also, all global variables are unloaded, so the plugin cannot preserve any data.
  226.                                                    Workaround in FF 1.7: If the host GIMP is detected, then a special mode will be activated.
  227.                                                    This mode saves the filter data into a temporary file "FilterFoundryXX.afs" and loads it
  228.                                                    when the window is opened again. */
  229.                                                 // Workaround: Save settings in "FilterFoundryXX.afs" if the host does not preserve pb->parameters
  230.                                                 char outfilename[255];
  231.                                                 char* tempdir;
  232.                                                 int hash;
  233.                                                 StandardFileReply sfr;
  234.                                                 sfr.sfGood = true;
  235.                                                 sfr.sfReplacing = true;
  236.                                                 sfr.sfType = PS_FILTER_FILETYPE;
  237.  
  238.                                                 tempdir = getenv("TMP");
  239.                                                 #ifdef WIN_ENV
  240.                                                 if (strlen(tempdir) > 0) strcat(tempdir, "\\");
  241.                                                 #else
  242.                                                 if (strlen(tempdir) > 0) strcat(tempdir, "/");
  243.                                                 #endif
  244.  
  245.                                                 hash = (gdata->standalone) ? get_parm_hash(gdata->parm) : 0;
  246.                                                 sprintf(outfilename, "%sFilterFoundry%d.afs", tempdir, hash);
  247.  
  248.                                                 myc2pstrcpy(sfr.sfFile.name, outfilename);
  249.                                                 #ifdef WIN_ENV
  250.                                                 sfr.nFileExtension = (WORD)(strlen(outfilename) - strlen(".afs"));
  251.                                                 #endif
  252.                                                 sfr.sfScript = 0; // FIXME: is that ok?
  253.                                                 savefile(&sfr);
  254.                                         }
  255.                                 }
  256.  
  257.                                 /* update stored parameters from new user settings */
  258.                                 saveparams(pb->parameters);
  259.                         }else
  260.                                 e = userCanceledErr;
  261.                 }
  262.                 wantdialog = false;
  263.  
  264.                 if(!e){
  265.                         if(setup(pb)){
  266.                                 DoStart(pb);
  267.                         }else{
  268.                                 SYSBEEP(1);
  269.                                 e = filterBadParameters;
  270.                         }
  271.                 }
  272.                 break;
  273.         case filterSelectorContinue:
  274.                 e = DoContinue(pb);
  275.                 break;
  276.         case filterSelectorFinish:
  277.                 DoFinish(pb);
  278.                 break;
  279.         default:
  280.                 e = filterBadParameters;
  281.         }
  282.  
  283.         *result = e;
  284.  
  285.         #ifdef WIN_ENV
  286.         if (activationContextUsed) DeactivateManifest(&manifestVars);
  287.         #endif
  288.  
  289.         ExitCodeResource();
  290. }
  291.  
  292. int checkandinitparams(Handle params){
  293.         char *reasonstr,*reason;
  294.         int i,bUninitializedParams;
  295.         Boolean showdialog;
  296.  
  297.         if (!host_preserves_parameters()) {
  298.                 // Workaround: Load settings in "FilterFoundryXX.afs" if host does not preserve pb->parameters
  299.                 char outfilename[255];
  300.                 char* tempdir;
  301.                 int hash;
  302.                 Boolean isStandalone;
  303.                 StandardFileReply sfr;
  304.                 sfr.sfGood = true;
  305.                 sfr.sfReplacing = true;
  306.                 sfr.sfType = PS_FILTER_FILETYPE;
  307.  
  308.                 // We need to set gdata->standalone after loadfile(), but we must call readPARMresource() before loadfile()
  309.                 // Reason: readPARMresource() reads parameters from the DLL while loadfile() reads parameters from the AFS file
  310.                 // But loadfile() will reset gdata->standalone ...
  311.                 isStandalone = readPARMresource((HMODULE)hDllInstance, &reason, READ_OBFUSC);
  312.  
  313.                 if (!gdata->obfusc) { // If the filter is obfuscated, we may not save the formula to the .afs (TODO: just save the ctl/map, but not the r/g/b/a-formulas!)
  314.                         tempdir = getenv("TMP");
  315.                         #ifdef WIN_ENV
  316.                         if (strlen(tempdir) > 0) strcat(tempdir, "\\");
  317.                         #else
  318.                         if (strlen(tempdir) > 0) strcat(tempdir, "/");
  319.                         #endif
  320.  
  321.                         hash = (isStandalone) ? get_parm_hash(gdata->parm) : 0;
  322.                         sprintf(outfilename, "%sFilterFoundry%d.afs", tempdir, hash);
  323.  
  324.                         myc2pstrcpy(sfr.sfFile.name, outfilename);
  325.                         #ifdef WIN_ENV
  326.                         sfr.nFileExtension = (WORD)(strlen(outfilename) - strlen(".afs"));
  327.                         #endif
  328.                         sfr.sfScript = 0; // FIXME: is that ok?
  329.  
  330.                         if (loadfile(&sfr, &reason)) {
  331.                                 gdata->standalone = gdata->parmloaded = isStandalone;
  332.                                 return true;
  333.                         }
  334.                 }
  335.         }
  336.  
  337.         if( (bUninitializedParams = !(params && readparams(params,false,&reasonstr))) ){
  338.                 /* either the parameter handle was uninitialised,
  339.                    or the parameter data couldn't be read; set default values */
  340.  
  341.                 // see if saved parameters exist
  342.                 gdata->standalone = gdata->parmloaded = readPARMresource((HMODULE)hDllInstance,&reason,READ_OBFUSC);
  343.  
  344.  
  345.                 if(!gdata->standalone){
  346.                         // no saved settings (not standalone)
  347.                         for(i = 0; i < 8; ++i)
  348.                                 slider[i] = i*10+100;
  349.                         for(i = 0; i < 4; ++i)
  350.                                 if(expr[i])
  351.                                         free(expr[i]);
  352.                         if(gpb->imageMode == plugInModeRGBColor){
  353.                                 expr[0] = _strdup("r");
  354.                                 expr[1] = _strdup("g");
  355.                                 expr[2] = _strdup("b");
  356.                                 expr[3] = _strdup("a");
  357.                         }else{
  358.                                 expr[0] = _strdup("c");
  359.                                 expr[1] = _strdup("c");
  360.                                 expr[2] = _strdup("c");
  361.                                 expr[3] = _strdup("c");
  362.                         }
  363.                 }
  364.         }
  365.  
  366.         // let scripting system change parameters, if we're scripted;
  367.         // user may want to force display of dialog during scripting playback
  368.         switch (ReadScriptParamsOnRead()) {
  369.         case SCR_SHOW_DIALOG:
  370.                 showdialog = true;
  371.                 break;
  372.         case SCR_HIDE_DIALOG:
  373.                 showdialog = false;
  374.                 break;
  375.         default:
  376.         case SCR_NO_SCRIPT:
  377.                 showdialog = bUninitializedParams;
  378.                 break;
  379.         }
  380.  
  381.         saveparams(params);
  382.  
  383.         return showdialog;
  384. }
  385.  
  386. Boolean host_preserves_parameters() {
  387.         #ifdef DEBUG_SIMULATE_GIMP
  388.         return false;
  389.         #endif
  390.  
  391.         if (gpb->hostSig == HOSTSIG_GIMP) return false;
  392.         if (gpb->hostSig == HOSTSIG_IRFANVIEW) return false;
  393.  
  394.         /*
  395.         char x[100];
  396.         sprintf(x, "Host Signature: %u", gpb->hostSig);
  397.         simplealert(x);
  398.         */
  399.  
  400.         // We just assume the other hosts preserve the parameters
  401.         return true;
  402. }
  403.  
  404. int64_t maxspace(){
  405.         // Please see "Hosts.md" for details about the MaxSpace implementations of tested plugins
  406.  
  407.         // Plugins that don't support MaxSpace64 shall set the field to zero; then we will use MaxSpace instead.
  408.         // Also check "gpb->bufferProcs->numBufferProcs" to see if 64 bit API is available
  409.         if ((gpb->bufferProcs->numBufferProcs >= 8) && (gpb->maxSpace64 > 0)) {
  410.                 uint64_t maxSpace64 = gpb->maxSpace64;
  411.  
  412.                 return maxSpace64;
  413.         } else {
  414.                 // 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.
  415.                 unsigned int maxSpace32 = (unsigned int) gpb->maxSpace;
  416.                 uint64_t maxSpace64 = maxSpace32;
  417.  
  418.                 if (gpb->hostSig == HOSTSIG_IRFANVIEW) maxSpace64 *= 1024; // IrfanView is giving Kilobytes instead of Bytes
  419.                 //if (gpb->hostSig == HOSTSIG_SERIF_PHOTOPLUS) maxSpace64 *= 1024; // TODO: Serif PhotoPlus also gives Kilobytes instead of bytes. But since it uses not a unique host signature, there is nothing we can do???
  420.  
  421.                 return maxSpace64;
  422.         }
  423. }
  424.  
  425. Boolean maxspace_available() {
  426.         // Please see "Hosts.md" for details about the MaxSpace implementations of tested plugins
  427.  
  428.         // GIMP PSPI sets MaxSpace to hardcoded 100 MB
  429.         if (gpb->hostSig == HOSTSIG_GIMP) return false;
  430.  
  431.         // HOSTSIG_PAINT_NET sets MaxSpace to hardcoded 1 GB, see https://github.com/0xC0000054/PSFilterPdn/issues/5
  432.         // Comment by the host author "This was done to avoid any compatibility issues with plugins handling 2 GB - 1"
  433.         if (gpb->hostSig == HOSTSIG_PAINT_NET) return false;
  434.  
  435.         return true;
  436. }
  437.  
  438. void DoPrepare(FilterRecordPtr pb){
  439.         int i;
  440.  
  441.         for(i = 4; i--;){
  442.                 if(expr[i]||tree[i]) DBG("expr[] or tree[] non-NULL in Prepare!");
  443.                 expr[i] = NULL;
  444.                 tree[i] = NULL;
  445.         }
  446.  
  447.         // Commented out by DM, 18 Dec 2018:
  448.         // This code did not work on systems with 8 GB RAM:
  449.         /*
  450.         long space = (pb->maxSpace*9)/10; // don't ask for more than 90% of available memory
  451.  
  452.         maxSpace = 512L<<10; // this is a wild guess, actually
  453.         if(maxSpace > space)
  454.                         maxSpace = space;
  455.         pb->maxSpace = maxSpace;
  456.         */
  457.  
  458.         // New variant:
  459.         if (maxspace_available()) {
  460.                 pb->maxSpace = (int32)ceil((maxspace()/10.)*9); // don't ask for more than 90% of available memory
  461.                 // FIXME: Also maxSpace64
  462.         }
  463. }
  464.  
  465. void RequestNext(FilterRecordPtr pb,long toprow){
  466.         /* Request next block of the image */
  467.  
  468.         pb->inLoPlane = pb->outLoPlane = 0;
  469.         pb->inHiPlane = pb->outHiPlane = nplanes-1;
  470.  
  471.         if (HAS_BIG_DOC(pb)) {
  472.                 // if any of the formulae involve random access to image pixels,
  473.                 // ask for the entire image
  474.                 if (needall) {
  475.                         SETRECT(BIGDOC_IN_RECT(pb), 0, 0, BIGDOC_IMAGE_SIZE(pb).h, BIGDOC_IMAGE_SIZE(pb).v);
  476.                 } else {
  477.                         // TODO: This does not work with GIMP. So, if we are using GIMP, we should
  478.                         //       somehow always use "needall=true", and/or find out why this doesn't work
  479.                         //       with GIMP.
  480.  
  481.                         // otherwise, process the filtered area, by chunksize parts
  482.                         BIGDOC_IN_RECT(pb).left = BIGDOC_FILTER_RECT(pb).left;
  483.                         BIGDOC_IN_RECT(pb).right = BIGDOC_FILTER_RECT(pb).right;
  484.                         BIGDOC_IN_RECT(pb).top = (int32)toprow;
  485.                         BIGDOC_IN_RECT(pb).bottom = (int32)MIN(toprow + chunksize, BIGDOC_FILTER_RECT(pb).bottom);
  486.  
  487.                         if (cnvused) {
  488.                                 // cnv() needs one extra pixel in each direction
  489.                                 if (BIGDOC_IN_RECT(pb).left > 0)
  490.                                         --BIGDOC_IN_RECT(pb).left;
  491.                                 if (BIGDOC_IN_RECT(pb).right < BIGDOC_IMAGE_SIZE(pb).h)
  492.                                         ++BIGDOC_IN_RECT(pb).right;
  493.                                 if (BIGDOC_IN_RECT(pb).top > 0)
  494.                                         --BIGDOC_IN_RECT(pb).top;
  495.                                 if (BIGDOC_IN_RECT(pb).bottom < BIGDOC_IMAGE_SIZE(pb).v)
  496.                                         ++BIGDOC_IN_RECT(pb).bottom;
  497.                         }
  498.                 }
  499.                 BIGDOC_OUT_RECT(pb) = BIGDOC_FILTER_RECT(pb);
  500.                 /*
  501.                 {char s[0x100];sprintf(s,"RequestNext needall=%d inRect=(%d,%d,%d,%d) filterRect=(%d,%d,%d,%d)",
  502.                                 needall,
  503.                                 BIGDOC_IN_RECT(pb).left,BIGDOC_IN_RECT(pb).top,BIGDOC_IN_RECT(pb).right,BIGDOC_IN_RECT(pb).bottom,
  504.                                 BIGDOC_FILTER_RECT(pb).left,BIGDOC_FILTER_RECT(pb).top,BIGDOC_FILTER_RECT(pb).right,BIGDOC_FILTER_RECT(pb).bottom);dbg(s);}
  505.                 */
  506.         } else {
  507.                 // if any of the formulae involve random access to image pixels,
  508.                 // ask for the entire image
  509.                 if (needall) {
  510.                         SETRECT(IN_RECT(pb), 0, 0, IMAGE_SIZE(pb).h, IMAGE_SIZE(pb).v);
  511.                 }
  512.                 else {
  513.                         // TODO: This does not work with GIMP. So, if we are using GIMP, we should
  514.                         //       somehow always use "needall=true", and/or find out why this doesn't work
  515.                         //       with GIMP.
  516.  
  517.                         // otherwise, process the filtered area, by chunksize parts
  518.                         IN_RECT(pb).left = FILTER_RECT(pb).left;
  519.                         IN_RECT(pb).right = FILTER_RECT(pb).right;
  520.                         IN_RECT(pb).top = (int16)toprow;
  521.                         IN_RECT(pb).bottom = (int16)MIN(toprow + chunksize, FILTER_RECT(pb).bottom);
  522.  
  523.                         if (cnvused) {
  524.                                 // cnv() needs one extra pixel in each direction
  525.                                 if (IN_RECT(pb).left > 0)
  526.                                         --IN_RECT(pb).left;
  527.                                 if (IN_RECT(pb).right < IMAGE_SIZE(pb).h)
  528.                                         ++IN_RECT(pb).right;
  529.                                 if (IN_RECT(pb).top > 0)
  530.                                         --IN_RECT(pb).top;
  531.                                 if (IN_RECT(pb).bottom < IMAGE_SIZE(pb).v)
  532.                                         ++IN_RECT(pb).bottom;
  533.                         }
  534.                 }
  535.                 OUT_RECT(pb) = FILTER_RECT(pb);
  536.                 /*
  537.                 {char s[0x100];sprintf(s,"RequestNext needall=%d inRect=(%d,%d,%d,%d) filterRect=(%d,%d,%d,%d)",
  538.                                 needall,
  539.                                 IN_RECT(pb).left,IN_RECT(pb).top,IN_RECT(pb).right,IN_RECT(pb).bottom,
  540.                                 FILTER_RECT(pb).left,FILTER_RECT(pb).top,FILTER_RECT(pb).right,FILTER_RECT(pb).bottom);dbg(s);}
  541.                 */
  542.         }
  543. }
  544.  
  545. void DoStart(FilterRecordPtr pb){
  546. //dbg("DoStart");
  547.                         /* if src() or rad() functions are used, random access to the image data is required,
  548.                            so we must request the entire image in a single chunk. */
  549.         if (HAS_BIG_DOC(pb)) {
  550.                 chunksize = needall ? (BIGDOC_FILTER_RECT(pb).bottom - BIGDOC_FILTER_RECT(pb).top) : CHUNK_ROWS;
  551.                 toprow = BIGDOC_FILTER_RECT(pb).top;
  552.         } else {
  553.                 chunksize = needall ? (FILTER_RECT(pb).bottom - FILTER_RECT(pb).top) : CHUNK_ROWS;
  554.                 toprow = FILTER_RECT(pb).top;
  555.         }
  556.         RequestNext(pb, toprow);
  557. }
  558.  
  559. OSErr DoContinue(FilterRecordPtr pb){
  560.         OSErr e = noErr;
  561.         long outoffset;
  562.  
  563.         if (HAS_BIG_DOC(pb)) {
  564.                 VRect fr;
  565.                 if (needall) {
  566.                         fr = BIGDOC_FILTER_RECT(pb);  // filter whole selection at once
  567.                 } else if (cnvused) {
  568.                         // we've requested one pixel extra all around
  569.                         // (see RequestNext()), just for access purposes. But filter
  570.                         // original selection only.
  571.                         fr.left = BIGDOC_FILTER_RECT(pb).left;
  572.                         fr.right = BIGDOC_FILTER_RECT(pb).right;
  573.                         fr.top = toprow;
  574.                         fr.bottom = MIN(toprow + chunksize, BIGDOC_FILTER_RECT(pb).bottom);
  575.                 } else {  // filter whatever portion we've been given
  576.                         fr = BIGDOC_IN_RECT(pb);
  577.                 }
  578.  
  579.                 outoffset = (long)pb->outRowBytes * (fr.top - BIGDOC_OUT_RECT(pb).top)
  580.                         + (long)nplanes * (fr.left - BIGDOC_OUT_RECT(pb).left);
  581.  
  582.                 if (!(e = process_scaled_bigdoc(pb, true, fr, fr,
  583.                         (Ptr)pb->outData + outoffset, pb->outRowBytes, 1.)))
  584.                 {
  585.                         toprow += chunksize;
  586.                         if (toprow < BIGDOC_FILTER_RECT(pb).bottom)
  587.                                 RequestNext(pb, toprow);
  588.                         else {
  589.                                 SETRECT(BIGDOC_IN_RECT(pb), 0, 0, 0, 0);
  590.                                 BIGDOC_OUT_RECT(pb) = BIGDOC_MASK_RECT(pb) = BIGDOC_IN_RECT(pb);
  591.                         }
  592.                 }
  593.         } else {
  594.                 Rect fr;
  595.                 if (needall) {
  596.                         fr = FILTER_RECT(pb);  // filter whole selection at once
  597.                 } else if (cnvused) {
  598.                         // we've requested one pixel extra all around
  599.                         // (see RequestNext()), just for access purposes. But filter
  600.                         // original selection only.
  601.                         fr.left = FILTER_RECT(pb).left;
  602.                         fr.right = FILTER_RECT(pb).right;
  603.                         fr.top = toprow;
  604.                         fr.bottom = MIN(toprow + chunksize, FILTER_RECT(pb).bottom);
  605.                 } else {  // filter whatever portion we've been given
  606.                         fr = IN_RECT(pb);
  607.                 }
  608.  
  609.                 outoffset = (long)pb->outRowBytes*(fr.top - OUT_RECT(pb).top)
  610.                         + (long)nplanes*(fr.left - OUT_RECT(pb).left);
  611.  
  612.                 if(!(e = process_scaled_olddoc(pb, true, fr, fr,
  613.                         (Ptr)pb->outData+outoffset, pb->outRowBytes, 1.)))
  614.                 {
  615.                         toprow += chunksize;
  616.                         if(toprow < FILTER_RECT(pb).bottom)
  617.                                 RequestNext(pb,toprow);
  618.                         else{
  619.                                 SETRECT(IN_RECT(pb),0,0,0,0);
  620.                                 OUT_RECT(pb) = MASK_RECT(pb) = IN_RECT(pb);
  621.                         }
  622.                 }
  623.         }
  624.         return e;
  625. }
  626.  
  627. void DoFinish(FilterRecordPtr pb){
  628.         int i;
  629.  
  630.         WriteScriptParamsOnRead();
  631.  
  632.         for(i = 4; i--;){
  633.                 freetree(tree[i]);
  634.                 if(expr[i]) free(expr[i]);
  635.         }
  636. }
  637.