/******************************************************************************* * processrenderoptions.cpp * * --------------------------------------------------------------------------- * Persistence of Vision Ray Tracer ('POV-Ray') version 3.7. * Copyright 1991-2013 Persistence of Vision Raytracer Pty. Ltd. * * POV-Ray is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * POV-Ray is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * --------------------------------------------------------------------------- * POV-Ray is based on the popular DKB raytracer version 2.12. * DKBTrace was originally written by David K. Buck. * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins. * --------------------------------------------------------------------------- * $File: //depot/public/povray/3.x/source/frontend/processrenderoptions.cpp $ * $Revision: #1 $ * $Change: 6069 $ * $DateTime: 2013/11/06 11:59:40 $ * $Author: chrisc $ *******************************************************************************/ #include // configbase.h must always be the first POV file included within base *.cpp files #include "base/configbase.h" #include "base/types.h" #include "base/povmscpp.h" #include "base/fileinputoutput.h" #include "base/stringutilities.h" #include "base/textstream.h" #include "base/povms.h" #include "base/povmsgid.h" #include "base/pov_err.h" #include "frontend/configfrontend.h" #include "frontend/processrenderoptions.h" // this must be the last file included #include "base/povdebug.h" namespace pov_frontend { using namespace pov_base; /***************************************************************************** * Local preprocessor defines ******************************************************************************/ #define kUseSpecialHandler kPOVMSType_WildCard #define kNoParameter kPOVMSType_Null /***************************************************************************** * Local variables ******************************************************************************/ #ifdef SYS_HAS_GRAYSCALE #define SYS_GRAYSCALE_FLAG true #else // SYS_HAS_GRAYSCALE #define SYS_GRAYSCALE_FLAG false #endif // SYS_HAS_GRAYSCALE #ifdef SYS_HAS_ALPHA #define SYS_ALPHA_FLAG true #else // SYS_HAS_ALPHA #define SYS_ALPHA_FLAG false #endif // SYS_HAS_ALPHA /* Keyword table for the INI-file parser. The parser converts the INI-file options into a POVMS object using the specifications provided in this table. The first element is the INI-file keyword, the second element is the POVMS object attribute key, the third is the attribute type. Entries with a POVMS attribute key of 0 are superceded options that will generate a warning that the option no longer is supported and will generate an error in a later (unspecified) version of POV. */ struct ProcessOptions::INI_Parser_Table RenderOptions_INI_Table[] = { { "All_Console", kPOVAttrib_AllConsole, kPOVMSType_Bool }, { "All_File", kPOVAttrib_AllFile, kPOVMSType_UCS2String }, { "Antialias_Depth", kPOVAttrib_AntialiasDepth, kPOVMSType_Int }, { "Antialias", kPOVAttrib_Antialias, kPOVMSType_Bool }, { "Antialias_Threshold", kPOVAttrib_AntialiasThreshold, kPOVMSType_Float }, { "Antialias_Gamma", kPOVAttrib_AntialiasGamma, kPOVMSType_Float }, { "Append_File", kPOVAttrib_AppendConsoleFiles, kPOVMSType_Bool }, { "Bits_Per_Color", kPOVAttrib_BitsPerColor, kPOVMSType_Int }, { "Bits_Per_Colour", kPOVAttrib_BitsPerColor, kPOVMSType_Int }, { "Bounding", kPOVAttrib_Bounding, kPOVMSType_Bool }, { "Bounding_Method", kPOVAttrib_BoundingMethod, kPOVMSType_Int }, { "Bounding_Threshold", kPOVAttrib_BoundingThreshold, kPOVMSType_Int }, { "BSP_BaseAccessCost", kPOVAttrib_BSP_BaseAccessCost, kPOVMSType_Float }, { "BSP_ChildAccessCost", kPOVAttrib_BSP_ChildAccessCost,kPOVMSType_Float }, { "BSP_ISectCost", kPOVAttrib_BSP_ISectCost, kPOVMSType_Float }, { "BSP_MaxDepth", kPOVAttrib_BSP_MaxDepth, kPOVMSType_Int }, { "BSP_MissChance", kPOVAttrib_BSP_MissChance, kPOVMSType_Float }, { "Buffer_Output", 0, 0 }, { "Buffer_Size", 0, 0 }, { "Clock", kPOVAttrib_Clock, kPOVMSType_Float }, { "Clockless_Animation", kPOVAttrib_ClocklessAnimation, kPOVMSType_Bool }, { "Compression", kPOVAttrib_Compression, kPOVMSType_Int }, { "Continue_Trace", kPOVAttrib_ContinueTrace, kPOVMSType_Bool }, { "Create_Histogram", 0, 0 }, { "Create_Ini", kPOVAttrib_CreateIni, kPOVMSType_UCS2String }, { "Cyclic_Animation", kPOVAttrib_CyclicAnimation, kPOVMSType_Bool }, { "Debug_Console", kPOVAttrib_DebugConsole, kPOVMSType_Bool }, { "Debug_File", kPOVAttrib_DebugFile, kPOVMSType_UCS2String }, { "Declare", kPOVAttrib_Declare, kUseSpecialHandler }, { "Display", kPOVAttrib_Display, kPOVMSType_Bool }, { "Display_Gamma", kPOVAttrib_DisplayGamma, kUseSpecialHandler }, { "Dither", kPOVAttrib_Dither, kPOVMSType_Bool }, { "Dither_Method", kPOVAttrib_DitherMethod, kUseSpecialHandler }, { "Draw_Vistas", kPOVAttrib_DrawVistas, kPOVMSType_Bool }, { "End_Column", kPOVAttrib_Right, kPOVMSType_Float }, { "End_Row", kPOVAttrib_Bottom, kPOVMSType_Float }, { "Fatal_Console", kPOVAttrib_FatalConsole, kPOVMSType_Bool }, { "Fatal_Error_Command", kPOVAttrib_FatalErrorCommand, kUseSpecialHandler }, { "Fatal_Error_Return", kPOVAttrib_FatalErrorCommand, kUseSpecialHandler }, { "Fatal_File", kPOVAttrib_FatalFile, kPOVMSType_UCS2String }, { "Field_Render", kPOVAttrib_FieldRender, kPOVMSType_Bool }, { "File_Gamma", kPOVAttrib_FileGamma, kUseSpecialHandler }, { "Final_Clock", kPOVAttrib_FinalClock, kPOVMSType_Float }, { "Final_Frame", kPOVAttrib_FinalFrame, kPOVMSType_Int }, { "Frame_Step", kPOVAttrib_FrameStep, kPOVMSType_Int }, { "Grayscale_Output", kPOVAttrib_GrayscaleOutput, kPOVMSType_Bool }, { "Height", kPOVAttrib_Height, kPOVMSType_Int }, { "High_Reproducibility",kPOVAttrib_HighReproducibility,kPOVMSType_Bool }, { "Histogram_Name", 0, 0 }, { "Histogram_Grid_Size", 0, 0 }, { "Histogram_Type", 0, 0 }, { "Initial_Clock", kPOVAttrib_InitialClock, kPOVMSType_Float }, { "Initial_Frame", kPOVAttrib_InitialFrame, kPOVMSType_Int }, { "Input_File_Name", kPOVAttrib_InputFile, kPOVMSType_UCS2String }, { "Include_Header", kPOVAttrib_IncludeHeader, kPOVMSType_UCS2String }, { "Include_Ini", kPOVAttrib_IncludeIni, kUseSpecialHandler }, { "Jitter_Amount", kPOVAttrib_JitterAmount, kPOVMSType_Float }, { "Jitter", kPOVAttrib_Jitter, kPOVMSType_Bool }, { "Library_Path", kPOVAttrib_LibraryPath, kUseSpecialHandler }, { "Light_Buffer", kPOVAttrib_LightBuffer, kPOVMSType_Bool }, { "Max_Image_Buffer_Memory", kPOVAttrib_MaxImageBufferMem, kPOVMSType_Int }, { "Odd_Field", kPOVAttrib_OddField, kPOVMSType_Bool }, { "Output_Alpha", kPOVAttrib_OutputAlpha, kPOVMSType_Bool }, { "Output_File_Name", kPOVAttrib_OutputFile, kPOVMSType_UCS2String }, { "Output_File_Type", kPOVAttrib_OutputFileType, kUseSpecialHandler }, { "Output_To_File", kPOVAttrib_OutputToFile, kPOVMSType_Bool }, { "Palette", kPOVAttrib_Palette, kUseSpecialHandler }, { "Pause_When_Done", kPOVAttrib_PauseWhenDone, kPOVMSType_Bool }, { "Post_Frame_Command", kPOVAttrib_PostFrameCommand, kUseSpecialHandler }, { "Post_Frame_Return", kPOVAttrib_PostFrameCommand, kUseSpecialHandler }, { "Post_Scene_Command", kPOVAttrib_PostSceneCommand, kUseSpecialHandler }, { "Post_Scene_Return", kPOVAttrib_PostSceneCommand, kUseSpecialHandler }, { "Preview_End_Size", kPOVAttrib_PreviewEndSize, kPOVMSType_Int }, { "Preview_Start_Size", kPOVAttrib_PreviewStartSize, kPOVMSType_Int }, { "Pre_Frame_Command", kPOVAttrib_PreFrameCommand, kUseSpecialHandler }, { "Pre_Frame_Return", kPOVAttrib_PreFrameCommand, kUseSpecialHandler }, { "Pre_Scene_Command", kPOVAttrib_PreSceneCommand, kUseSpecialHandler }, { "Pre_Scene_Return", kPOVAttrib_PreSceneCommand, kUseSpecialHandler }, { "Quality", kPOVAttrib_Quality, kPOVMSType_Int }, { "Radiosity_File_Name", kPOVAttrib_RadiosityFileName, kPOVMSType_UCS2String }, { "Radiosity_From_File", kPOVAttrib_RadiosityFromFile, kPOVMSType_Bool }, { "Radiosity_To_File", kPOVAttrib_RadiosityToFile, kPOVMSType_Bool }, { "Radiosity_Vain_Pretrace", kPOVAttrib_RadiosityVainPretrace, kPOVMSType_Bool }, { "Real_Time_Raytracing",kPOVAttrib_RealTimeRaytracing, kPOVMSType_Bool }, { "Remove_Bounds", kPOVAttrib_RemoveBounds, kPOVMSType_Bool }, { "Render_Block_Size", kPOVAttrib_RenderBlockSize, kPOVMSType_Int }, { "Render_Block_Step", kPOVAttrib_RenderBlockStep, kPOVMSType_Int }, { "Render_Console", kPOVAttrib_RenderConsole, kPOVMSType_Bool }, { "Render_File", kPOVAttrib_RenderFile, kPOVMSType_UCS2String }, { "Render_Pattern", kPOVAttrib_RenderPattern, kPOVMSType_Int }, { "Sampling_Method", kPOVAttrib_SamplingMethod, kPOVMSType_Int }, { "Split_Unions", kPOVAttrib_SplitUnions, kPOVMSType_Bool }, { "Start_Column", kPOVAttrib_Left, kPOVMSType_Float }, { "Start_Row", kPOVAttrib_Top, kPOVMSType_Float }, { "Statistic_Console", kPOVAttrib_StatisticsConsole, kPOVMSType_Bool }, { "Statistic_File", kPOVAttrib_StatisticsFile, kPOVMSType_UCS2String }, { "Subset_End_Frame", kPOVAttrib_SubsetEndFrame, kPOVMSType_Float }, { "Subset_Start_Frame", kPOVAttrib_SubsetStartFrame, kPOVMSType_Float }, { "Test_Abort_Count", kPOVAttrib_TestAbortCount, kPOVMSType_Int }, { "Test_Abort", kPOVAttrib_TestAbort, kPOVMSType_Bool }, { "User_Abort_Command", kPOVAttrib_UserAbortCommand, kUseSpecialHandler }, { "User_Abort_Return", kPOVAttrib_UserAbortCommand, kUseSpecialHandler }, { "Verbose", kPOVAttrib_Verbose, kPOVMSType_Bool }, { "Version", kPOVAttrib_Version, kPOVMSType_Float }, { "Video_Mode", kPOVAttrib_VideoMode, kUseSpecialHandler }, { "Vista_Buffer", kPOVAttrib_VistaBuffer, kPOVMSType_Bool }, { "Warning_Console", kPOVAttrib_WarningConsole, kPOVMSType_Bool }, { "Warning_File", kPOVAttrib_WarningFile, kPOVMSType_UCS2String }, { "Warning_Level", kPOVAttrib_WarningLevel, kPOVMSType_Int }, { "Width", kPOVAttrib_Width, kPOVMSType_Int }, { "Work_Threads", kPOVAttrib_MaxRenderThreads, kPOVMSType_Int }, { NULL, 0, 0 } }; /* Keyword table for the command line parser. The parser converts the command line options into a POVMS object using the specifications provided in this table. The first element is the command keyword, the second element is the POVMS object attribute key of the parameter, the third is the attribute type and the last specifies if the +/- switch is used as boolean parameter if an attribute key is provided. */ struct ProcessOptions::Cmd_Parser_Table RenderOptions_Cmd_Table[] = { { "AG", kPOVAttrib_AntialiasGamma, kPOVMSType_Float, kNoParameter }, { "AM", kPOVAttrib_SamplingMethod, kPOVMSType_Int, kNoParameter }, { "A0", kPOVAttrib_AntialiasThreshold, kPOVMSType_Float, kPOVAttrib_Antialias }, { "A", kNoParameter, kNoParameter, kPOVAttrib_Antialias }, { "BM", kPOVAttrib_BoundingMethod, kPOVMSType_Int, kNoParameter }, { "BS", kPOVAttrib_RenderBlockSize, kPOVMSType_Int, kNoParameter }, { "B", kNoParameter, kNoParameter, kPOVAttrib_Bounding }, { "C", kNoParameter, kNoParameter, kPOVAttrib_ContinueTrace }, { "D", kPOVAttrib_Display, kUseSpecialHandler, kPOVAttrib_Display }, { "D", kNoParameter, kNoParameter, kPOVAttrib_Display }, { "EC", kPOVAttrib_Right, kPOVMSType_Float, kNoParameter }, { "EF0", kPOVAttrib_SubsetEndFrame, kPOVMSType_Float, kNoParameter }, { "EF", kPOVAttrib_SubsetEndFrame, kPOVMSType_Int, kNoParameter }, { "EP", kPOVAttrib_PreviewEndSize, kPOVMSType_Int, kNoParameter }, { "ER", kPOVAttrib_Bottom, kPOVMSType_Float, kNoParameter }, { "F", kPOVAttrib_OutputFileType, kUseSpecialHandler, kPOVAttrib_OutputToFile }, { "F", kNoParameter, kNoParameter, kPOVAttrib_OutputToFile }, { "GA", kPOVAttrib_AllFile, kPOVMSType_UCS2String, kPOVAttrib_AllConsole }, { "GA", kNoParameter, kNoParameter, kPOVAttrib_AllConsole }, { "GD", kPOVAttrib_DebugFile, kPOVMSType_UCS2String, kPOVAttrib_DebugConsole }, { "GD", kNoParameter, kNoParameter, kPOVAttrib_DebugConsole }, { "GF", kPOVAttrib_FatalFile, kPOVMSType_UCS2String, kPOVAttrib_FatalConsole }, { "GF", kNoParameter, kNoParameter, kPOVAttrib_FatalConsole }, { "GI", kPOVAttrib_CreateIni, kPOVMSType_UCS2String, kNoParameter }, { "GP", kNoParameter, kNoParameter, kPOVAttrib_AppendConsoleFiles }, { "GR", kPOVAttrib_RenderFile, kPOVMSType_UCS2String, kPOVAttrib_RenderConsole }, { "GR", kNoParameter, kNoParameter, kPOVAttrib_RenderConsole }, { "GS", kPOVAttrib_StatisticsFile, kPOVMSType_UCS2String, kPOVAttrib_StatisticsConsole }, { "GS", kNoParameter, kNoParameter, kPOVAttrib_StatisticsConsole }, { "GW", kPOVAttrib_WarningFile, kPOVMSType_UCS2String, kPOVAttrib_WarningConsole }, { "GW", kNoParameter, kNoParameter, kPOVAttrib_WarningConsole }, { "HI", kPOVAttrib_IncludeHeader, kPOVMSType_UCS2String, kNoParameter }, { "HR", kNoParameter, kNoParameter, kPOVAttrib_HighReproducibility }, { "H", kPOVAttrib_Height, kPOVMSType_Int, kNoParameter }, { "I", kPOVAttrib_InputFile, kPOVMSType_UCS2String, kNoParameter }, { "J", kPOVAttrib_JitterAmount, kPOVMSType_Float, kPOVAttrib_Jitter }, { "J", kNoParameter, kNoParameter, kPOVAttrib_Jitter }, { "KC", kNoParameter, kNoParameter, kPOVAttrib_CyclicAnimation }, { "KI", kPOVAttrib_InitialClock, kPOVMSType_Float, kNoParameter }, { "KFF", kPOVAttrib_FinalFrame, kPOVMSType_Int, kNoParameter }, { "KFI", kPOVAttrib_InitialFrame, kPOVMSType_Int, kNoParameter }, { "KF", kPOVAttrib_FinalClock, kPOVMSType_Float, kNoParameter }, { "KLA", kNoParameter, kNoParameter, kPOVAttrib_ClocklessAnimation }, { "K", kPOVAttrib_Clock, kPOVMSType_Float, kNoParameter }, { "L", kPOVAttrib_LibraryPath, kUseSpecialHandler, kNoParameter }, { "MB", kPOVAttrib_BoundingThreshold, kPOVMSType_Int, kPOVAttrib_Bounding }, { "MB", kNoParameter, kNoParameter, kPOVAttrib_Bounding }, { "MI", kPOVAttrib_MaxImageBufferMem, kPOVMSType_Int, kNoParameter }, { "MV", kPOVAttrib_Version, kPOVMSType_Float, kNoParameter }, { "O", kPOVAttrib_OutputFile, kPOVMSType_UCS2String, kNoParameter }, { "P", kNoParameter, kNoParameter, kPOVAttrib_PauseWhenDone }, { "Q", kPOVAttrib_Quality, kPOVMSType_Int, kNoParameter }, { "RFI", kNoParameter, kNoParameter, kPOVAttrib_RadiosityFromFile }, { "RFO", kNoParameter, kNoParameter, kPOVAttrib_RadiosityToFile }, { "RF", kPOVAttrib_RadiosityFileName, kPOVMSType_UCS2String, kNoParameter }, { "RS", kPOVAttrib_RenderBlockStep, kPOVMSType_Int, kNoParameter }, { "RP", kPOVAttrib_RenderPattern, kPOVMSType_Int, kNoParameter }, { "RTR", kNoParameter, kNoParameter, kPOVAttrib_RealTimeRaytracing }, { "RVP", kNoParameter, kNoParameter, kPOVAttrib_RadiosityVainPretrace }, { "R", kPOVAttrib_AntialiasDepth, kPOVMSType_Int, kNoParameter }, { "SC", kPOVAttrib_Left, kPOVMSType_Float, kNoParameter }, { "SF0", kPOVAttrib_SubsetStartFrame, kPOVMSType_Float, kNoParameter }, { "SF", kPOVAttrib_SubsetStartFrame, kPOVMSType_Int, kNoParameter }, { "SP", kPOVAttrib_PreviewStartSize, kPOVMSType_Int, kNoParameter }, { "SR", kPOVAttrib_Top, kPOVMSType_Float, kNoParameter }, { "STP", kPOVAttrib_FrameStep, kPOVMSType_Int, kNoParameter }, { "SU", kNoParameter, kNoParameter, kPOVAttrib_SplitUnions }, { "TH", kPOVAttrib_DitherMethod, kUseSpecialHandler, kPOVAttrib_Dither }, { "TH", kNoParameter, kNoParameter, kPOVAttrib_Dither }, { "UA", kNoParameter, kNoParameter, kPOVAttrib_OutputAlpha }, { "UD", kNoParameter, kNoParameter, kPOVAttrib_DrawVistas }, { "UF", kNoParameter, kNoParameter, kPOVAttrib_FieldRender }, { "UL", kNoParameter, kNoParameter, kPOVAttrib_LightBuffer }, { "UO", kNoParameter, kNoParameter, kPOVAttrib_OddField }, { "UR", kNoParameter, kNoParameter, kPOVAttrib_RemoveBounds }, { "UV", kNoParameter, kNoParameter, kPOVAttrib_VistaBuffer }, { "V", kNoParameter, kNoParameter, kPOVAttrib_Verbose }, { "WL", kPOVAttrib_WarningLevel, kPOVMSType_Int, kNoParameter }, { "WT", kPOVAttrib_MaxRenderThreads, kPOVMSType_Int, kNoParameter }, { "W", kPOVAttrib_Width, kPOVMSType_Int, kNoParameter }, { "X", kPOVAttrib_TestAbortCount, kUseSpecialHandler, kPOVAttrib_TestAbort }, { "X", kNoParameter, kNoParameter, kPOVAttrib_TestAbort }, { NULL, 0, 0, 0 } }; // TODO FIXME - The following are hacks of some sort, no idea what they are good for. They certainly use wrong types and probably contain other mistakes [trf] extern struct ProcessRenderOptions::Parameter_Code_Table DitherMethodTable[]; ProcessRenderOptions::ProcessRenderOptions() : ProcessOptions(RenderOptions_INI_Table, RenderOptions_Cmd_Table) { } ProcessRenderOptions::~ProcessRenderOptions() { } int ProcessRenderOptions::ReadSpecialOptionHandler(INI_Parser_Table *option, char *param, POVMSObjectPtr obj) { POVMSAttributeList list; double floatval = 0.0; int intval = 0; int intval2 = 0; int err = kNoErr; switch(option->key) { case kPOVAttrib_Palette: case kPOVAttrib_VideoMode: while(isspace(*param)) param++; err = POVMSUtil_SetInt(obj, option->key, tolower(*param)); break; case kPOVAttrib_DitherMethod: while(isspace(*param)) param++; err = ParseParameterCode(DitherMethodTable, param, &intval); if (err == kNoErr) err = POVMSUtil_SetInt(obj, option->key, intval); else ParseError("Unrecognized dither method '%s'.", param); break; case kPOVAttrib_OutputFileType: while(isspace(*param)) param++; err = ParseFileType(*param, option->key, &intval); if (err == kNoErr) err = POVMSUtil_SetInt(obj, option->key, intval); break; case kPOVAttrib_IncludeIni: case kPOVAttrib_LibraryPath: POVMSAttribute attr; if(err == kNoErr) { // parse INI file (recursive) if(option->key == kPOVAttrib_IncludeIni) err = ParseFile(param, obj); // create list if it isn't there if(err == kNoErr) { if(POVMSObject_Exist(obj, option->key) == kFalseErr) err = POVMSAttrList_New(&list); else if(POVMSObject_Exist(obj, option->key) != kNoErr) err = kObjectAccessErr; else err = POVMSObject_Get(obj, &list, option->key); } } else { ParseError("File name or path parameter expected for option '%s', found '%s'.", option->keyword, param); err = kParamErr; } // add path or file to list if(err == kNoErr) err = POVMSAttr_New(&attr); if(err == kNoErr) { err = POVMSAttr_SetUTF8String(&attr, kPOVMSType_UCS2String, param); if(err == kNoErr) err = POVMSAttrList_Append(&list, &attr); else err = POVMSAttr_Delete(&attr); } if(err == kNoErr) err = POVMSObject_Set(obj, &list, option->key); break; case kPOVAttrib_Declare: POVMSObject decobj; // create list if it isn't there if(POVMSObject_Exist(obj, option->key) == kFalseErr) err = POVMSAttrList_New(&list); else if(POVMSObject_Exist(obj, option->key) != kNoErr) err = kObjectAccessErr; else err = POVMSObject_Get(obj, &list, option->key); // add value to list if(err == kNoErr) err = POVMSObject_New(&decobj, kPOVMSType_WildCard); if(err == kNoErr) { char *ptr = NULL; err = POVMSUtil_SetString(&decobj, kPOVAttrib_Identifier, strtok(param, "=")); if(err == kNoErr) { ptr = strtok(NULL, ""); if(ptr == NULL) err = kParseErr; } if(err == kNoErr) { if(strchr(ptr, '"') != NULL) { ptr = strchr(ptr, '"') + 1; strtok(ptr, "\""); err = POVMSUtil_SetString(&decobj, kPOVAttrib_Value, ptr); } else err = POVMSUtil_SetFloat(&decobj, kPOVAttrib_Value, atof(ptr)); } if(err == kNoErr) err = POVMSAttrList_Append(&list, &decobj); else err = POVMSObject_Delete(&decobj); } if(err == kNoErr) err = POVMSObject_Set(obj, &list, option->key); break; case kPOVAttrib_FatalErrorCommand: case kPOVAttrib_PostFrameCommand: case kPOVAttrib_PostSceneCommand: case kPOVAttrib_PreFrameCommand: case kPOVAttrib_PreSceneCommand: case kPOVAttrib_UserAbortCommand: POVMSObject cmdobj; if(POVMSObject_Exist(obj, option->key) == kNoErr) err = POVMSObject_Get(obj, &cmdobj, option->key); else err = POVMSObject_New(&cmdobj, kPOVMSType_WildCard); if(toupper(*(option->keyword + strlen(option->keyword) - 1)) == 'D') { if(err == kNoErr) err = POVMSUtil_SetString(&cmdobj, kPOVAttrib_CommandString, param); } else { if(err == kNoErr) { int i = 0; if((*param == '-') || (*param == '!')) i = toupper(*(param + 1)); // use upper-case to indicate negation of process result else i = tolower(*param); // lower-case for normal interpretation of process result err = POVMSUtil_SetInt(&cmdobj, kPOVAttrib_ReturnAction, i); } } if(err == kNoErr) err = POVMSObject_Set(obj, &cmdobj, option->key); break; case kPOVAttrib_AntialiasGamma: case kPOVAttrib_DisplayGamma: case kPOVAttrib_FileGamma: POVMSType typeKey; switch (option->key) { case kPOVAttrib_AntialiasGamma: typeKey = kPOVAttrib_AntialiasGammaType; break; case kPOVAttrib_DisplayGamma: typeKey = kPOVAttrib_DisplayGammaType; break; case kPOVAttrib_FileGamma: typeKey = kPOVAttrib_FileGammaType; break; } floatval = atof(param); if (floatval == 1.0) intval = kPOVList_GammaType_Neutral; else if (floatval > 0) intval = kPOVList_GammaType_PowerLaw; else err = ParseGammaType(param, &intval); if (err == kNoErr) err = POVMSUtil_SetFloat(obj, option->key, fabs(floatval)); if (err == kNoErr) err = POVMSUtil_SetInt(obj, typeKey, intval); break; } return err; } int ProcessRenderOptions::ReadSpecialSwitchHandler(Cmd_Parser_Table *option, char *param, POVMSObjectPtr obj, bool) { int intval = 0; int intval2 = 0; int err = 0; char chr = 0; char file_type; bool has16BitGrayscale; switch(option->key) { case kPOVAttrib_Display: if(param[0] != '\0') { err = POVMSUtil_SetInt(obj, kPOVAttrib_VideoMode, (int)toupper(param[0])); if((param[1] != '\0') && (err == 0)) err = POVMSUtil_SetInt(obj, kPOVAttrib_Palette, (int)toupper(param[1])); } break; case kPOVAttrib_DitherMethod: err = ParseParameterCode(DitherMethodTable, param, &intval); if (err == kNoErr) err = POVMSUtil_SetInt(obj, option->key, intval); else ParseError("Unrecognized dither method '%s'.", param); break; case kPOVAttrib_OutputFileType: err = ParseFileType(*param, option->key, &intval, &has16BitGrayscale); if (err == kNoErr) { err = POVMSUtil_SetInt(obj, option->key, intval); file_type = *param++; } if ((err == kNoErr) && (*param > ' ')) { if (tolower(*param) == 'g') { if(!has16BitGrayscale) { ParseError("Grayscale not currently supported with output file format '%c'.", file_type); err = kParamErr; } else { if ((err = POVMSUtil_SetBool(obj, kPOVAttrib_GrayscaleOutput, true)) == kNoErr && *++param > ' ') { ParseError("Unexpected '%s' following grayscale flag in +F%c option.", param, file_type); err = kParamErr; } } } else if (isdigit(*param) != 0) { if (sscanf(param, "%d%n", &intval, &intval2) == 1) { if ((err = POVMSUtil_SetInt(obj, kPOVAttrib_BitsPerColor, intval)) == kNoErr) { param += intval2; if (*param > ' ') { ParseError("Unexpected '%s' following bits per color in +F%c option.", param, file_type); err = kParamErr; } } } else { ParseError("Invalid bits per color '%s' found in +F%c option.", param, file_type); err = kParamErr; } } else { ParseError("Invalid modifier '%s' following +F%c option.", param, file_type); err = kParamErr; } } break; case kPOVAttrib_LibraryPath: POVMSAttributeList list; POVMSAttribute attr; if(err == kNoErr) { // create list if it isn't there if(POVMSObject_Exist(obj, option->key) == kFalseErr) err = POVMSAttrList_New(&list); else if(POVMSObject_Exist(obj, option->key) != kNoErr) err = kObjectAccessErr; else err = POVMSObject_Get(obj, &list, option->key); } else { ParseError("File name or path parameter expected for switch '%s', found '%s'.", option->command, param); err = kParamErr; } // add path or file to list if(err == kNoErr) err = POVMSAttr_New(&attr); if(err == kNoErr) { err = POVMSAttr_SetUTF8String(&attr, kPOVMSType_UCS2String, param); if(err == kNoErr) err = POVMSAttrList_Append(&list, &attr); else err = POVMSAttr_Delete(&attr); } if(err == kNoErr) err = POVMSObject_Set(obj, &list, option->key); break; case kPOVAttrib_TestAbortCount: if((*param) == 0) break; if(sscanf(param, "%d", &intval) == 1) err = POVMSUtil_SetInt(obj, option->key, intval); else { ParseError("No or integer parameter expected for switch '%s', found '%s'.", option->command, param); err = kParamErr; } break; } return err; } int ProcessRenderOptions::WriteSpecialOptionHandler(INI_Parser_Table *option, POVMSObjectPtr obj, OTextStream *file) { POVMSAttributeList list; POVMSFloat floatval; POVMSInt intval; int err = 0; int l; int i,imax; POVMSAttribute item; char *bufptr; char chr; switch(option->key) { case kPOVAttrib_Palette: case kPOVAttrib_VideoMode: if(POVMSUtil_GetInt(obj, option->key, &intval) == 0) { chr = intval; if(chr > 32) file->printf("%s=%c\n", option->keyword, chr); } break; case kPOVAttrib_OutputFileType: if(POVMSUtil_GetInt(obj, option->key, &intval) == 0) { chr = UnparseFileType(intval); if(chr > 32) file->printf("%s=%c\n", option->keyword, chr); } break; case kPOVAttrib_IncludeIni: break; case kPOVAttrib_Declare: POVMSObject decobj; err = POVMSObject_Get(obj, &list, option->key); if(err != 0) break; l = 0; err = POVMSAttrList_Count(&list, &l); if(err != 0) break; if(l == 0) break; imax = l; for(i = 1; i <= imax; i++) { err = POVMSAttrList_GetNth(&list, i, &decobj); if(err == 0) err = POVMSObject_Get(&decobj, &item, kPOVAttrib_Identifier); if(err == 0) { l = 0; err = POVMSAttr_Size(&item, &l); if(l > 0) { bufptr = new char[l]; bufptr[0] = 0; if((POVMSUtil_GetFloat(&decobj, kPOVAttrib_Value, &floatval) == 0) && (POVMSAttr_Get(&item, kPOVMSType_CString, bufptr, &l) == 0)) file->printf("%s=%s=%g\n", option->keyword, bufptr, (float)floatval); delete[] bufptr; } (void)POVMSAttr_Delete(&item); } } break; case kPOVAttrib_LibraryPath: err = POVMSObject_Get(obj, &list, option->key); if(err != 0) break; l = 0; err = POVMSAttrList_Count(&list, &l); if(err != 0) break; if(l == 0) break; imax = l; for(i = 1; i <= imax; i++) { err = POVMSAttrList_GetNth(&list, i, &item); if(err == 0) { l = 0; err = POVMSAttr_Size(&item, &l); if(l > 0) { bufptr = new char[l * 3]; bufptr[0] = 0; if(POVMSAttr_GetUTF8String(&item, kPOVMSType_UCS2String, bufptr, &l) == 0) file->printf("%s=\"%s\"\n", option->keyword, bufptr); delete[] bufptr; } (void)POVMSAttr_Delete(&item); } } break; case kPOVAttrib_FatalErrorCommand: case kPOVAttrib_PostFrameCommand: case kPOVAttrib_PostSceneCommand: case kPOVAttrib_PreFrameCommand: case kPOVAttrib_PreSceneCommand: case kPOVAttrib_UserAbortCommand: POVMSObject cmdobj; err = POVMSObject_Get(obj, &cmdobj, option->key); if(err != 0) break; err = POVMSObject_Get(&cmdobj, &item, kPOVAttrib_CommandString); if(err == 0) { if(toupper(*(option->keyword + strlen(option->keyword) - 1)) == 'D') { l = 0; err = POVMSAttr_Size(&item, &l); if(l > 0) { bufptr = new char[l]; bufptr[0] = 0; if(POVMSAttr_Get(&item, kPOVMSType_CString, bufptr, &l) == 0) file->printf("%s=%s\n", option->keyword, bufptr); delete[] bufptr; } } else { if(POVMSUtil_GetInt(&cmdobj, kPOVAttrib_ReturnAction, &intval) == 0) { if(intval < 0) { chr = -intval; file->printf("%s=!%c\n", option->keyword, chr); } else { chr = intval; file->printf("%s=%c\n", option->keyword, chr); } } } } if(err == 0) err = POVMSObject_Delete(&cmdobj); break; } return err; } bool ProcessRenderOptions::WriteOptionFilter(INI_Parser_Table *table) { // So that we don't get both Bits_Per_Color and Bits_Per_Colour in the INI file. return (strcmp(table->keyword, "Bits_Per_Colour") != 0); } int ProcessRenderOptions::ProcessUnknownString(char *str, POVMSObjectPtr obj) { POVMSAttributeList list; POVMSAttribute attr; int state = 0; // INI file int err = kNoErr; if(str == NULL) { ParseError("Expected filename, nothing was found."); return kParamErr; } // add filename or path // see if it is a POV file if(state == 0) { char *ptr = strrchr(str, '.'); if(ptr != NULL) { if(pov_stricmp(ptr, ".pov") == 0) state = 1; // POV file } } // see if it is a path if(state == 0) { if(strlen(str) > 0) { if(str[strlen(str) - 1] == POV_FILE_SEPARATOR) state = 2; // library path #ifdef POV_FILE_SEPARATOR_2 else if(str[strlen(str) - 1] == POV_FILE_SEPARATOR_2) state = 2; // library path #endif } } switch(state) { // INI file case 0: // parse INI file (recursive) err = ParseFile(str, obj); if(err == kNoErr) { // create list if it isn't there if(POVMSObject_Exist(obj, kPOVAttrib_IncludeIni) == kFalseErr) err = POVMSAttrList_New(&list); else if(POVMSObject_Exist(obj, kPOVAttrib_IncludeIni) != kNoErr) err = kObjectAccessErr; else err = POVMSObject_Get(obj, &list, kPOVAttrib_IncludeIni); } // add INI file to list if(err == kNoErr) err = POVMSAttr_New(&attr); if(err == kNoErr) { err = POVMSAttr_SetUTF8String(&attr, kPOVMSType_UCS2String, str); if(err == kNoErr) err = POVMSAttrList_Append(&list, &attr); else err = POVMSAttr_Delete(&attr); } if(err == kNoErr) err = POVMSObject_Set(obj, &list, kPOVAttrib_IncludeIni); break; // POV file case 1: // set POV file err = POVMSUtil_SetUTF8String(obj, kPOVAttrib_InputFile, str); break; // library path case 2: // create list if it isn't there if(POVMSObject_Exist(obj, kPOVAttrib_LibraryPath) == kFalseErr) err = POVMSAttrList_New(&list); else if(POVMSObject_Exist(obj, kPOVAttrib_LibraryPath) != kNoErr) err = kObjectAccessErr; else err = POVMSObject_Get(obj, &list, kPOVAttrib_LibraryPath); // add library path to list if(err == kNoErr) err = POVMSAttr_New(&attr); if(err == kNoErr) { err = POVMSAttr_SetUTF8String(&attr, kPOVMSType_UCS2String, str); if(err == kNoErr) err = POVMSAttrList_Append(&list, &attr); else err = POVMSAttr_Delete(&attr); } if(err == kNoErr) err = POVMSObject_Set(obj, &list, kPOVAttrib_LibraryPath); break; } return err; } ITextStream *ProcessRenderOptions::OpenFileForRead(const char *filename, POVMSObjectPtr obj) { return OpenINIFileStream(filename, pov_base::POV_File_Text_INI, obj); } OTextStream *ProcessRenderOptions::OpenFileForWrite(const char *filename, POVMSObjectPtr) { return new OTextStream(ASCIItoUCS2String(filename).c_str(), pov_base::POV_File_Text_INI); } ITextStream *ProcessRenderOptions::OpenINIFileStream(const char *filename, unsigned int stype, POVMSObjectPtr obj) // TODO FIXME - Use new Path class! { // TODO FIXME - we should join forces with SceneData::FindFile() // TODO FIXME - use proper C++ strings instead of C character arrays int i,ii,l[POV_FILE_EXTENSIONS_PER_TYPE]; char pathname[1024]; char file[1024]; char file_x[POV_FILE_EXTENSIONS_PER_TYPE][1024]; int cnt = 0; int ll; POVMSAttribute attr, item; const char *xstr = strrchr(filename, '.'); bool hasextension = ((xstr != NULL) && (strlen(xstr) <= 4)); // TODO FIXME - we shouldn't rely on extensions being at most 1+3 chars long if(POV_ALLOW_FILE_READ(ASCIItoUCS2String(filename).c_str(),stype) == 0) // TODO FIXME - Remove dependency on this macro!!! [trf] return NULL; for(i = 0; i < POV_FILE_EXTENSIONS_PER_TYPE; i++) { if((l[i] = strlen(pov::gPOV_File_Extensions[stype].ext[i])) > 0) { strcpy(file_x[i], filename); strcat(file_x[i], pov::gPOV_File_Extensions[stype].ext[i]); } } // Check the current directory first if((hasextension == true) && (CheckIfFileExists(filename) == true)) { return new ITextStream(ASCIItoUCS2String(filename).c_str(), stype); } for(i = 0; i < POV_FILE_EXTENSIONS_PER_TYPE; i++) { if(l[i]) { if(CheckIfFileExists(file_x[i]) == true) { return new ITextStream(ASCIItoUCS2String(file_x[i]).c_str(), stype); } } } if(POVMSObject_Get(obj, &attr, kPOVAttrib_LibraryPath) != 0) return NULL; if(POVMSAttrList_Count(&attr, &cnt) != 0) { (void)POVMSAttrList_Delete(&attr); return NULL; } for (i = 1; i <= cnt; i++) { (void)POVMSAttr_New(&item); if(POVMSAttrList_GetNth(&attr, i, &item) != 0) continue; ll = 0; if(POVMSAttr_Size(&item, &ll) != 0) { (void)POVMSAttr_Delete(&item); continue; } if(ll <= 0) { (void)POVMSAttr_Delete(&item); continue; } if(POVMSAttr_GetUTF8String(&item, kPOVMSType_UCS2String, file, &ll) != 0) // TODO FIXME!!! { (void)POVMSAttr_Delete(&item); continue; } (void)POVMSAttr_Delete(&item); file[strlen(file)+1] = '\0'; file[strlen(file)] = POV_FILE_SEPARATOR; strcpy(pathname, file); strcat(pathname, filename); if((hasextension == true) && (CheckIfFileExists(pathname) == true)) { (void)POVMSAttrList_Delete(&attr); return new ITextStream(ASCIItoUCS2String(pathname).c_str(), stype); } for(ii = 0; ii < POV_FILE_EXTENSIONS_PER_TYPE; ii++) { if(l[ii]) { strcpy(pathname, file); strcat(pathname, file_x[ii]); if(CheckIfFileExists(pathname) == true) { (void)POVMSAttrList_Delete(&attr); return new ITextStream(ASCIItoUCS2String(pathname).c_str(), stype); } } } } (void)POVMSAttrList_Delete(&attr); if(l[0]) ParseError("Could not find file '%s%s'", filename, pov::gPOV_File_Extensions[stype].ext[0]); else ParseError("Could not find file '%s'", filename); return NULL; } // TODO - the following code might need reviewing, according to trf /* Supported output file types */ struct ProcessRenderOptions::Output_FileType_Table FileTypeTable[] = { // attribute-specific file types (must go first) // code, attribute, internalId, has16BitGrayscale hasAlpha // { 'C', kPOVAttrib_HistogramFileType, kPOVList_FileType_CSV, false, false }, // generic file types // code, attribute, internalId, has16BitGrayscale hasAlpha { 'T', 0, kPOVList_FileType_Targa, false, true }, { 'C', 0, kPOVList_FileType_CompressedTarga, false, true }, { 'N', 0, kPOVList_FileType_PNG, true, true }, { 'J', 0, kPOVList_FileType_JPEG, false, false }, { 'P', 0, kPOVList_FileType_PPM, true, false }, { 'B', 0, kPOVList_FileType_BMP, false, false /*[1]*/ }, { 'E', 0, kPOVList_FileType_OpenEXR, false /*[2]*/, true }, { 'H', 0, kPOVList_FileType_RadianceHDR, false, false }, #ifdef SYS_TO_STANDARD { 'S', 0, kPOVList_FileType_System, SYS_GRAYSCALE_FLAG, SYS_ALPHA_FLAG }, #endif // SYS_TO_STANDARD // [1] Alpha support for BMP uses an inofficial extension to the BMP file format, which is not recognized by // most image pocessing software. // [2] While OpenEXR does support greyscale output at >8 bits, the variants currently supported by POV-Ray // use 16-bit floating-point values with 10 bit mantissa, which might be insufficient for various purposes // such as height fields. // end-of-list marker { '\0', 0, 0, false } }; /* Supported special gamma types */ struct ProcessRenderOptions::Parameter_Code_Table GammaTypeTable[] = { // code, internalId, { "SRGB", kPOVList_GammaType_SRGB }, // end-of-list marker { NULL, 0 } }; /* Supported dither types */ struct ProcessRenderOptions::Parameter_Code_Table DitherMethodTable[] = { // code, internalId, { "B2", kPOVList_DitherMethod_Bayer2x2 }, { "B3", kPOVList_DitherMethod_Bayer3x3 }, { "B4", kPOVList_DitherMethod_Bayer4x4 }, { "D1", kPOVList_DitherMethod_Diffusion1D }, { "D2", kPOVList_DitherMethod_Diffusion2D }, { "FS", kPOVList_DitherMethod_FloydSteinberg }, // end-of-list marker { NULL, 0 } }; int ProcessRenderOptions::ParseFileType(char code, POVMSType attribute, int* pInternalId, bool* pHas16BitGreyscale) { *pInternalId = kPOVList_FileType_Unknown; int err = kNoErr; for (int i = 0; FileTypeTable[i].code != '\0'; i ++) { if ( (toupper(code) == FileTypeTable[i].code) && ((FileTypeTable[i].attribute == 0) || (FileTypeTable[i].attribute == attribute )) ) { if (pHas16BitGreyscale != NULL) *pHas16BitGreyscale = FileTypeTable[i].has16BitGrayscale; *pInternalId = FileTypeTable[i].internalId; break; } } #ifdef OPENEXR_MISSING if (*pInternalId == kPOVList_FileType_OpenEXR) { ParseError( "This unofficial POV-Ray binary was built without support for the OpenEXR \ file format. You must either use an official POV-Ray binary or recompile \ the POV-Ray sources on a system providing you with the OpenEXR library \ to make use of this facility. Alternatively, you may use any of the \ following built-in formats: HDR." ); err = kParamErr; } #endif // OPENEXR_MISSING if (*pInternalId == kPOVList_FileType_Unknown) { ParseError("Unrecognized output file format %c.", code); err = kParamErr; } return err; } char ProcessRenderOptions::UnparseFileType(int fileType) { for (int i = 0; FileTypeTable[i].code != '\0'; i ++) if (fileType == FileTypeTable[i].internalId) return FileTypeTable[i].code; return '\0'; } int ProcessRenderOptions::ParseGammaType(char* code, int* pInternalId) { *pInternalId = kPOVList_GammaType_Unknown; int err = ParseParameterCode(GammaTypeTable, code, pInternalId); if (err == kParamErr) ParseError("Unrecognized gamma setting '%s'.", code); return err; } const char* ProcessRenderOptions::UnparseGammaType(int gammaType) { return UnparseParameterCode(GammaTypeTable, gammaType); } int ProcessRenderOptions::ParseParameterCode(const ProcessRenderOptions::Parameter_Code_Table* codeTable, char* code, int* pInternalId) { for (int i = 0; code[i] != '\0'; i ++) code[i] = toupper(code[i]); for (int i = 0; codeTable[i].code != NULL; i ++) { if ( strcmp(code, codeTable[i].code) == 0 ) { *pInternalId = codeTable[i].internalId; return kNoErr; } } return kParamErr; } const char* ProcessRenderOptions::UnparseParameterCode(const ProcessRenderOptions::Parameter_Code_Table* codeTable, int internalId) { for (int i = 0; codeTable[i].code != NULL; i ++) if (internalId == codeTable[i].internalId) return codeTable[i].code; return NULL; } }