Fix svace issue
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / image-visual-shader-debug.cpp
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 // CLASS HEADER
18 #include <dali-toolkit/internal/visuals/image-visual-shader-debug.h>
19
20 // EXTERNAL INCLUDES
21 #include <dali/devel-api/adaptor-framework/environment-variable.h>
22 #include <dali/devel-api/adaptor-framework/style-monitor.h> ///< for load json file.
23
24 #include <regex> ///< for redefine shader
25 #include <string_view>
26
27 #include <dali/integration-api/debug.h>
28
29 // INTERNAL INCLUDES
30 #include <dali-toolkit/devel-api/asset-manager/asset-manager.h>
31 #include <dali-toolkit/devel-api/builder/json-parser.h>
32
33 namespace Dali
34 {
35 namespace Toolkit
36 {
37 namespace Internal
38 {
39 namespace
40 {
41 constexpr auto DALI_DEBUG_IMAGE_VISUAL_SHADER_ENV = "DALI_DEBUG_IMAGE_VISUAL_SHADER";
42
43 bool DebugImageVisualShaderEnvironmentEnabled()
44 {
45   static bool enabled       = false;
46   static bool enabledSetted = false;
47   if(!enabledSetted)
48   {
49     enabledSetted           = true;
50     auto debugEnabledString = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_DEBUG_IMAGE_VISUAL_SHADER_ENV);
51     enabled                 = debugEnabledString ? std::atoi(debugEnabledString) : false;
52   }
53   return enabled;
54 }
55
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";
58
59 const std::string& GetScriptFilename()
60 {
61   // Set the full path for the default script file.
62   const static std::string styleDirPath{AssetManager::GetDaliStylePath()};
63   static std::string       mScriptFileName{};
64
65   if(DALI_UNLIKELY(mScriptFileName.empty()))
66   {
67     // Use user's own script if exist.
68     auto environmentScriptFilename = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_DEBUG_IMAGE_VISUAL_SHADER_SCRIPT_FILE_NAME_ENV);
69
70     mScriptFileName = environmentScriptFilename ? std::string(environmentScriptFilename) : styleDirPath + std::string(DEFAULT_DEBUG_IMAGE_VISUAL_SHADER_SCRIPT_FILE_NAME);
71
72     DALI_ASSERT_DEBUG(0 != mScriptFileName.length());
73   }
74
75   return mScriptFileName;
76 }
77
78 bool LoadJsonScript(std::string& stringOut)
79 {
80   auto styleMonitor = StyleMonitor::Get();
81
82   // as toolkit is platform agnostic, it cannot load files from filesystem
83   // ask style monitor to load the style sheet
84   if(styleMonitor)
85   {
86     try
87     {
88       return styleMonitor.LoadThemeFile(GetScriptFilename(), stringOut);
89     }
90     catch(const std::exception& e)
91     {
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());
94     }
95     catch(const Dali::DaliException& e)
96     {
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);
99     }
100     catch(...)
101     {
102       DALI_LOG_ERROR("Something unkown exception throwed during load script file![%s]\n", GetScriptFilename().c_str());
103     }
104   }
105
106   return false;
107 }
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";
116
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";
126
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;";
131
132 bool ParseScriptInfomation(Property::Map& result)
133 {
134   std::string stringOut;
135   if(!LoadJsonScript(stringOut))
136   {
137     DALI_LOG_ERROR("Fail to load script file [%s]\n", GetScriptFilename().c_str());
138     return false;
139   }
140
141   Toolkit::JsonParser parser = Toolkit::JsonParser::New();
142
143   if(!parser.Parse(stringOut))
144   {
145     std::ostringstream stream;
146     if(parser.ParseError())
147     {
148       stream << "position: " << parser.GetErrorPosition() << ", line: " << parser.GetErrorLineNumber() << ", column: " << parser.GetErrorColumn() << ", description: " << parser.GetErrorDescription() << ".";
149     }
150     DALI_LOG_ERROR("Fail to parse json script\nError : %s\nJson : %s\n", stream.str().c_str(), stringOut.c_str());
151     return false;
152   }
153
154   const auto* rootNode = parser.GetRoot();
155   if(!rootNode)
156   {
157     DALI_LOG_ERROR("Fail to get root node\n");
158     return false;
159   }
160
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;
163     oss.clear();
164
165     if(node)
166     {
167       const auto* childNode = node->GetChild(jsonKey);
168
169       if(childNode)
170       {
171         if(childNode->GetType() == TreeNode::FLOAT)
172         {
173           oss << childNode->GetFloat();
174         }
175         else if(childNode->GetType() == TreeNode::STRING)
176         {
177           oss << childNode->GetString();
178         }
179         else if(childNode->GetType() == TreeNode::ARRAY)
180         {
181           // Concat strings with line feed
182           bool isFirst = true;
183           for(auto iter = childNode->CBegin(), endIter = childNode->CEnd(); iter != endIter; ++iter)
184           {
185             if((*iter).second.GetType() == TreeNode::STRING)
186             {
187               if(isFirst)
188               {
189                 isFirst = false;
190               }
191               else
192               {
193                 oss << "\n";
194               }
195               oss << (*iter).second.GetString();
196             }
197           }
198         }
199       }
200     }
201
202     if(oss.str().empty())
203     {
204       oss << defaultValue;
205     }
206     result.Insert(std::string(macroKey), oss.str());
207   };
208
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);
213   };
214
215   // Get color rate
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);
218
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);
223
224   return true;
225 }
226
227 const Property::Map& GetScriptInfomation()
228 {
229   static Property::Map result;
230
231   if(DALI_UNLIKELY(result.Empty()))
232   {
233     if(!ParseScriptInfomation(result))
234     {
235       // Use default script information if parse failed.
236       result.Clear();
237
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));
246     }
247   }
248
249   return result;
250 }
251
252 void RedefineMacro(std::string& shaderCode, std::string macro, std::string value)
253 {
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!");
257
258   std::size_t insertionPoint = found + definition.length();
259
260   // Automatically insert line-continuation character into value
261   std::regex                 re("\n");
262   std::sregex_token_iterator first{value.begin(), value.end(), re, -1}, last;
263   for(auto i = first; i != last; ++i)
264   {
265     std::string line = std::string(" \\\n") + (*i).str();
266     shaderCode.insert(insertionPoint, line);
267     insertionPoint += line.length();
268   }
269 }
270
271 } // namespace
272
273 namespace ImageVisualShaderDebug
274 {
275 bool DebugImageVisualShaderEnabled()
276 {
277   return DebugImageVisualShaderEnvironmentEnabled();
278 }
279
280 void ApplyImageVisualShaderDebugScriptCode(std::string& fragmentShader)
281 {
282   const auto& resultMap = GetScriptInfomation();
283
284   for(std::size_t i = 0u; i < resultMap.Count(); ++i)
285   {
286     auto        key   = resultMap.GetKeyAt(i);
287     const auto& value = resultMap.GetValue(i);
288
289     RedefineMacro(fragmentShader, std::move(key.stringKey), value.Get<std::string>());
290   }
291 }
292 } // namespace ImageVisualShaderDebug
293
294 } // namespace Internal
295
296 } // namespace Toolkit
297
298 } // namespace Dali