2 * Copyright (c) 2023 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include <dali-toolkit/internal/visuals/image-visual-shader-debug.h>
21 #include <dali/devel-api/adaptor-framework/environment-variable.h>
22 #include <dali/devel-api/adaptor-framework/style-monitor.h> ///< for load json file.
24 #include <regex> ///< for redefine shader
25 #include <string_view>
27 #include <dali/integration-api/debug.h>
30 #include <dali-toolkit/devel-api/asset-manager/asset-manager.h>
31 #include <dali-toolkit/devel-api/builder/json-parser.h>
41 constexpr auto DALI_DEBUG_IMAGE_VISUAL_SHADER_ENV = "DALI_DEBUG_IMAGE_VISUAL_SHADER";
43 bool DebugImageVisualShaderEnvironmentEnabled()
45 static bool enabled = false;
46 static bool enabledSetted = false;
50 auto debugEnabledString = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_DEBUG_IMAGE_VISUAL_SHADER_ENV);
51 enabled = debugEnabledString ? std::atoi(debugEnabledString) : false;
56 constexpr auto DALI_DEBUG_IMAGE_VISUAL_SHADER_SCRIPT_FILE_NAME_ENV = "DALI_DEBUG_IMAGE_VISUAL_SHADER_SCRIPT_FILE_NAME";
57 constexpr auto DEFAULT_DEBUG_IMAGE_VISUAL_SHADER_SCRIPT_FILE_NAME = "debug-image-visual-shader-script.json";
59 const std::string& GetScriptFilename()
61 // Set the full path for the default script file.
62 const static std::string styleDirPath{AssetManager::GetDaliStylePath()};
63 static std::string mScriptFileName{};
65 if(DALI_UNLIKELY(mScriptFileName.empty()))
67 // Use user's own script if exist.
68 auto environmentScriptFilename = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_DEBUG_IMAGE_VISUAL_SHADER_SCRIPT_FILE_NAME_ENV);
70 mScriptFileName = environmentScriptFilename ? std::string(environmentScriptFilename) : styleDirPath + std::string(DEFAULT_DEBUG_IMAGE_VISUAL_SHADER_SCRIPT_FILE_NAME);
72 DALI_ASSERT_DEBUG(0 != mScriptFileName.length());
75 return mScriptFileName;
78 bool LoadJsonScript(std::string& stringOut)
80 auto styleMonitor = StyleMonitor::Get();
82 // as toolkit is platform agnostic, it cannot load files from filesystem
83 // ask style monitor to load the style sheet
88 return styleMonitor.LoadThemeFile(GetScriptFilename(), stringOut);
90 catch(const std::exception& e)
92 DALI_LOG_ERROR("Something system exception throwed during load script file![%s]\n", GetScriptFilename().c_str());
93 DALI_LOG_ERROR("Error message : [%s]\n", e.what());
95 catch(const Dali::DaliException& e)
97 DALI_LOG_ERROR("Something Dali exception throwed during load script file![%s]\n", GetScriptFilename().c_str());
98 DALI_LOG_ERROR("Error message : [%s]\n", e.condition);
102 DALI_LOG_ERROR("Something unkown exception throwed during load script file![%s]\n", GetScriptFilename().c_str());
108 // Json keywords what we will get information from json.
109 constexpr std::string_view MINIMUM_DEBUG_COLOR_RATE_JSON_KEY = "minimumColorRate";
110 constexpr std::string_view MAXIMUM_DEBUG_COLOR_RATE_JSON_KEY = "maximumColorRate";
111 constexpr std::string_view DEBUG_RED_CHANNEL_CODE_JSON_KEY = "redChannelCodes";
112 constexpr std::string_view DEBUG_GREEN_CHANNEL_CODE_JSON_KEY = "greenChannelCodes";
113 constexpr std::string_view DEBUG_BLUE_CHANNEL_CODE_JSON_KEY = "blueChannelCodes";
114 constexpr std::string_view DEBUG_TRIGGER_CODE_JSON_KEY = "triggerCode";
115 constexpr std::string_view DEBUG_RATIO_CODE_JSON_KEY = "ratioCode";
117 // Macro keywords what we will replace at fragment shader.
118 constexpr std::string_view MINIMUM_DEBUG_COLOR_RATE_MACRO_KEY = "MINIMUM_DEBUG_COLOR_RATE";
119 constexpr std::string_view MAXIMUM_DEBUG_COLOR_RATE_MACRO_KEY = "MAXIMUM_DEBUG_COLOR_RATE";
120 constexpr std::string_view DEBUG_TRIGGER_RED_CODE_MACRO_KEY = "DEBUG_TRIGGER_RED_CODE";
121 constexpr std::string_view DEBUG_TRIGGER_GREEN_CODE_MACRO_KEY = "DEBUG_TRIGGER_GREEN_CODE";
122 constexpr std::string_view DEBUG_TRIGGER_BLUE_CODE_MACRO_KEY = "DEBUG_TRIGGER_BLUE_CODE";
123 constexpr std::string_view DEBUG_RATIO_RED_CODE_MACRO_KEY = "DEBUG_RATIO_RED_CODE";
124 constexpr std::string_view DEBUG_RATIO_GREEN_CODE_MACRO_KEY = "DEBUG_RATIO_GREEN_CODE";
125 constexpr std::string_view DEBUG_RATIO_BLUE_CODE_MACRO_KEY = "DEBUG_RATIO_BLUE_CODE";
127 // Default macro keywords when we fail to parse script.
128 constexpr std::string_view DEFAULT_DEBUG_COLOR_RATE_MACRO_VALUE = "0.0";
129 constexpr std::string_view DEFAULT_DEBUG_TRIGGER_CODE_MACRO_VALUE = "return false;";
130 constexpr std::string_view DEFAULT_DEBUG_RATIO_CODE_MACRO_VALUE = "return 0.0;";
132 bool ParseScriptInfomation(Property::Map& result)
134 std::string stringOut;
135 if(!LoadJsonScript(stringOut))
137 DALI_LOG_ERROR("Fail to load script file [%s]\n", GetScriptFilename().c_str());
141 Toolkit::JsonParser parser = Toolkit::JsonParser::New();
143 if(!parser.Parse(stringOut))
145 std::ostringstream stream;
146 if(parser.ParseError())
148 stream << "position: " << parser.GetErrorPosition() << ", line: " << parser.GetErrorLineNumber() << ", column: " << parser.GetErrorColumn() << ", description: " << parser.GetErrorDescription() << ".";
150 DALI_LOG_ERROR("Fail to parse json script\nError : %s\nJson : %s\n", stream.str().c_str(), stringOut.c_str());
154 const auto* rootNode = parser.GetRoot();
157 DALI_LOG_ERROR("Fail to get root node\n");
161 auto InsertScriptMap = [](Property::Map& result, const TreeNode* node, const std::string_view& jsonKey, const std::string_view& macroKey, const std::string_view& defaultValue) {
162 std::ostringstream oss;
167 const auto* childNode = node->GetChild(jsonKey);
171 if(childNode->GetType() == TreeNode::FLOAT)
173 oss << childNode->GetFloat();
175 else if(childNode->GetType() == TreeNode::STRING)
177 oss << childNode->GetString();
179 else if(childNode->GetType() == TreeNode::ARRAY)
181 // Concat strings with line feed
183 for(auto iter = childNode->CBegin(), endIter = childNode->CEnd(); iter != endIter; ++iter)
185 if((*iter).second.GetType() == TreeNode::STRING)
195 oss << (*iter).second.GetString();
202 if(oss.str().empty())
206 result.Insert(std::string(macroKey), oss.str());
209 auto InsertChannelScriptMap = [&InsertScriptMap](Property::Map& result, const TreeNode* node, const std::string_view& channelJsonKey, const std::string_view& triggerMacroKey, const std::string_view& ratioMacroKey) {
210 const auto* channelNode = node->GetChild(channelJsonKey);
211 InsertScriptMap(result, channelNode, DEBUG_TRIGGER_CODE_JSON_KEY, triggerMacroKey, DEFAULT_DEBUG_TRIGGER_CODE_MACRO_VALUE);
212 InsertScriptMap(result, channelNode, DEBUG_RATIO_CODE_JSON_KEY, ratioMacroKey, DEFAULT_DEBUG_RATIO_CODE_MACRO_VALUE);
216 InsertScriptMap(result, rootNode, MINIMUM_DEBUG_COLOR_RATE_JSON_KEY, MINIMUM_DEBUG_COLOR_RATE_MACRO_KEY, DEFAULT_DEBUG_COLOR_RATE_MACRO_VALUE);
217 InsertScriptMap(result, rootNode, MAXIMUM_DEBUG_COLOR_RATE_JSON_KEY, MAXIMUM_DEBUG_COLOR_RATE_MACRO_KEY, DEFAULT_DEBUG_COLOR_RATE_MACRO_VALUE);
219 // Get each color ChannelCodes
220 InsertChannelScriptMap(result, rootNode, DEBUG_RED_CHANNEL_CODE_JSON_KEY, DEBUG_TRIGGER_RED_CODE_MACRO_KEY, DEBUG_RATIO_RED_CODE_MACRO_KEY);
221 InsertChannelScriptMap(result, rootNode, DEBUG_GREEN_CHANNEL_CODE_JSON_KEY, DEBUG_TRIGGER_GREEN_CODE_MACRO_KEY, DEBUG_RATIO_GREEN_CODE_MACRO_KEY);
222 InsertChannelScriptMap(result, rootNode, DEBUG_BLUE_CHANNEL_CODE_JSON_KEY, DEBUG_TRIGGER_BLUE_CODE_MACRO_KEY, DEBUG_RATIO_BLUE_CODE_MACRO_KEY);
227 const Property::Map& GetScriptInfomation()
229 static Property::Map result;
231 if(DALI_UNLIKELY(result.Empty()))
233 if(!ParseScriptInfomation(result))
235 // Use default script information if parse failed.
238 result.Insert(std::string(MINIMUM_DEBUG_COLOR_RATE_MACRO_KEY), std::string(DEFAULT_DEBUG_COLOR_RATE_MACRO_VALUE));
239 result.Insert(std::string(MAXIMUM_DEBUG_COLOR_RATE_MACRO_KEY), std::string(DEFAULT_DEBUG_COLOR_RATE_MACRO_VALUE));
240 result.Insert(std::string(DEBUG_TRIGGER_RED_CODE_MACRO_KEY), std::string(DEFAULT_DEBUG_TRIGGER_CODE_MACRO_VALUE));
241 result.Insert(std::string(DEBUG_TRIGGER_GREEN_CODE_MACRO_KEY), std::string(DEFAULT_DEBUG_TRIGGER_CODE_MACRO_VALUE));
242 result.Insert(std::string(DEBUG_TRIGGER_BLUE_CODE_MACRO_KEY), std::string(DEFAULT_DEBUG_TRIGGER_CODE_MACRO_VALUE));
243 result.Insert(std::string(DEBUG_RATIO_RED_CODE_MACRO_KEY), std::string(DEFAULT_DEBUG_RATIO_CODE_MACRO_VALUE));
244 result.Insert(std::string(DEBUG_RATIO_GREEN_CODE_MACRO_KEY), std::string(DEFAULT_DEBUG_RATIO_CODE_MACRO_VALUE));
245 result.Insert(std::string(DEBUG_RATIO_BLUE_CODE_MACRO_KEY), std::string(DEFAULT_DEBUG_RATIO_CODE_MACRO_VALUE));
252 void RedefineMacro(std::string& shaderCode, std::string macro, std::string value)
254 std::string definition = "#define " + macro;
255 std::size_t found = shaderCode.find(definition);
256 DALI_ASSERT_ALWAYS(found != std::string::npos && "Macro keyword was not exist in shader code!");
258 std::size_t insertionPoint = found + definition.length();
260 // Automatically insert line-continuation character into value
262 std::sregex_token_iterator first{value.begin(), value.end(), re, -1}, last;
263 for(auto i = first; i != last; ++i)
265 std::string line = std::string(" \\\n") + (*i).str();
266 shaderCode.insert(insertionPoint, line);
267 insertionPoint += line.length();
273 namespace ImageVisualShaderDebug
275 bool DebugImageVisualShaderEnabled()
277 return DebugImageVisualShaderEnvironmentEnabled();
280 void ApplyImageVisualShaderDebugScriptCode(std::string& fragmentShader)
282 const auto& resultMap = GetScriptInfomation();
284 for(std::size_t i = 0u; i < resultMap.Count(); ++i)
286 auto key = resultMap.GetKeyAt(i);
287 const auto& value = resultMap.GetValue(i);
289 RedefineMacro(fragmentShader, std::move(key.stringKey), value.Get<std::string>());
292 } // namespace ImageVisualShaderDebug
294 } // namespace Internal
296 } // namespace Toolkit