Merge "Add post processor" into devel/master
authorSeungho BAEK <sbsh.baek@samsung.com>
Mon, 26 Apr 2021 05:22:04 +0000 (05:22 +0000)
committerGerrit Code Review <gerrit@review>
Mon, 26 Apr 2021 05:22:04 +0000 (05:22 +0000)
17 files changed:
automated-tests/.gitignore
automated-tests/src/dali-shader-generator/CMakeLists.txt [new file with mode: 0644]
automated-tests/src/dali-shader-generator/shader/fragment-shader.frag [new file with mode: 0644]
automated-tests/src/dali-shader-generator/shader/shader-define.def [new file with mode: 0644]
automated-tests/src/dali-shader-generator/shader/vertex-shader.vert [new file with mode: 0644]
automated-tests/src/dali-shader-generator/tct-dali-shader-generator-core.cpp [new file with mode: 0644]
automated-tests/src/dali-toolkit/utc-Dali-TextEditor.cpp
build/tizen/.gitignore
build/tizen/CMakeLists.txt
build/tizen/dali-scene-loader/CMakeLists.txt
build/tizen/shader-generator.sh [deleted file]
dali-toolkit/internal/file.list
dali-toolkit/internal/text/text-controller-relayouter.cpp
dali-toolkit/internal/text/text-controller-relayouter.h
dali-toolkit/public-api/dali-toolkit-version.cpp
dali-toolkit/shader-generator/shader-generator.cpp [new file with mode: 0644]
packaging/dali-toolkit.spec

index b12e784..17d54d9 100644 (file)
@@ -2,5 +2,5 @@
 build
 build.log
 tct*core.h
-CMakeLists.txt
+/CMakeLists.txt
 results_xml.*
diff --git a/automated-tests/src/dali-shader-generator/CMakeLists.txt b/automated-tests/src/dali-shader-generator/CMakeLists.txt
new file mode 100644 (file)
index 0000000..7057a36
--- /dev/null
@@ -0,0 +1,49 @@
+SET(PKG_NAME "dali-shader-generator")
+
+SET(EXEC_NAME "tct-${PKG_NAME}-core")
+
+PKG_CHECK_MODULES(${EXEC_NAME} REQUIRED
+  dali2-toolkit
+)
+
+ADD_EXECUTABLE(${EXEC_NAME} ${EXEC_NAME}.cpp)
+
+INSTALL(PROGRAMS ${EXEC_NAME}
+    DESTINATION ${BIN_DIR}/${EXEC_NAME}
+)
+
+SET(SHADER_GENERATOR dali-shader-generator)
+SET(GENERATED_FOLDER ${CMAKE_CURRENT_BINARY_DIR}/shader/generated)
+SET(SHADER_FOLDER ${CMAKE_CURRENT_SOURCE_DIR}/shader)
+
+ADD_CUSTOM_TARGET(test_help ALL COMMAND ${SHADER_GENERATOR} -h | grep "DALi Shader Generator" > /dev/null 2>&1 && echo "test_help Succeeded" VERBATIM)
+ADD_CUSTOM_TARGET(test_no_params ALL COMMAND ${SHADER_GENERATOR} > /dev/null 2>&1 || echo "test_no_params Succeeded" VERBATIM)
+ADD_CUSTOM_TARGET(test_version ALL COMMAND ${SHADER_GENERATOR} -v | wc -l | grep 1 > /dev/null 2>&1 && echo "test_version Succeeded" VERBATIM)
+ADD_CUSTOM_TARGET(test_invalid_option ALL COMMAND ${SHADER_GENERATOR} --undeclared > /dev/null 2>&1 || echo "test_invalid_option Succeeded" VERBATIM)
+ADD_CUSTOM_TARGET(test_too_many_params ALL COMMAND ${SHADER_GENERATOR} ONE TWO THREE > /dev/null 2>&1 || echo "test_too_many_params Succeeded" VERBATIM)
+ADD_CUSTOM_TARGET(test_invalid_indir ALL COMMAND ${SHADER_GENERATOR} ONE TWO > /dev/null 2>&1 || echo "test_invalid_indir Succeeded" VERBATIM)
+ADD_CUSTOM_TARGET(
+  test_check_built_in_created
+  ALL
+  COMMAND ${SHADER_GENERATOR} . ${GENERATED_FOLDER} | grep builtin-shader | wc -l | grep 2 > /dev/null 2>&1 && echo "test_check_built_in_created Succeeded"
+  VERBATIM)
+ADD_CUSTOM_TARGET(
+  test_check_built_in_not_created
+  ALL
+  COMMAND ${SHADER_GENERATOR} --skip . ${GENERATED_FOLDER} | grep builtin-shader > /dev/null 2>&1 || echo "test_check_built_in_not_created Succeeded"
+  VERBATIM)
+ADD_CUSTOM_TARGET(
+  test_frag_correct
+  ALL
+  COMMAND ${SHADER_GENERATOR} ${SHADER_FOLDER} ${GENERATED_FOLDER} | grep "SHADER_FRAGMENT_SHADER_FRAG" | grep "fragment-shader-frag.h" > /dev/null 2>&1 && echo "test_frag_correct Succeeded"
+  VERBATIM)
+ADD_CUSTOM_TARGET(
+  test_vert_correct
+  ALL
+  COMMAND ${SHADER_GENERATOR} ${SHADER_FOLDER} ${GENERATED_FOLDER} | grep "SHADER_VERTEX_SHADER_VERT" | grep "vertex-shader-vert.h" > /dev/null 2>&1 && echo "test_vert_correct Succeeded"
+  VERBATIM)
+ADD_CUSTOM_TARGET(
+  test_def_correct
+  ALL
+  COMMAND ${SHADER_GENERATOR} ${SHADER_FOLDER} ${GENERATED_FOLDER} | grep "SHADER_SHADER_DEFINE_DEF" | grep "shader-define-def.h" > /dev/null 2>&1 && echo "test_def_correct Succeeded"
+  VERBATIM)
diff --git a/automated-tests/src/dali-shader-generator/shader/fragment-shader.frag b/automated-tests/src/dali-shader-generator/shader/fragment-shader.frag
new file mode 100644 (file)
index 0000000..d9b3d5a
--- /dev/null
@@ -0,0 +1,14 @@
+varying mediump vec2 vTexCoord;
+
+uniform sampler2D sTexture;
+uniform lowp vec4 uColor;
+
+void main()
+{
+  mediump vec4 color = texture2D( sTexture, vTexCoord );
+  if(color.a <= 0.0001)
+  {
+    discard;
+  }
+  gl_FragColor = color * uColor;
+}
diff --git a/automated-tests/src/dali-shader-generator/shader/shader-define.def b/automated-tests/src/dali-shader-generator/shader/shader-define.def
new file mode 100644 (file)
index 0000000..c6e148b
--- /dev/null
@@ -0,0 +1,2 @@
+#version 300 es
+precision highp float;
diff --git a/automated-tests/src/dali-shader-generator/shader/vertex-shader.vert b/automated-tests/src/dali-shader-generator/shader/vertex-shader.vert
new file mode 100644 (file)
index 0000000..e5cbaa7
--- /dev/null
@@ -0,0 +1,59 @@
+attribute mediump vec2 aPosition;
+uniform highp mat4 uMvpMatrix;
+uniform highp vec3 uSize;
+
+uniform mediump vec2 start_point;
+uniform mediump vec2 end_point;
+uniform mediump vec2 rotate_center;
+uniform mediump float rotate_angle;
+
+varying mediump vec2 vTexCoord;
+varying mediump vec2 vStart;
+varying mediump vec2 vEnd;
+
+vec2 rotate(vec2 x, vec2 c, float a)
+{
+  vec2 d = x - c;
+  vec2 r = vec2(d.x * cos(a) - d.y * sin(a), d.x * sin(a) + d.y * cos(a));
+
+#ifdef UNIT_TYPE_BOUNDING_BOX
+  return r + c;
+#endif
+
+  /* UnitType::OBJECT_BOUNDING_BOX */
+#ifdef UNIT_TYPE_USER
+  return (r + c) / uSize.x;
+#endif
+  /* UnitType::USER_SPACE*/
+}
+
+//Visual size and offset
+uniform mediump vec2 offset;
+uniform highp vec2 size;
+uniform mediump vec4 offsetSizeMode;
+uniform mediump vec2 origin;
+uniform mediump vec2 anchorPoint;
+
+vec4 ComputeVertexPosition()
+{
+  vec2 visualSize = mix( uSize.xy*size, size, offsetSizeMode.zw );
+  vec2 visualOffset = mix( offset, offset/uSize.xy, offsetSizeMode.xy );
+  return vec4( (aPosition + anchorPoint)*visualSize + (visualOffset + origin)*uSize.xy, 0.0, 1.0 );
+}
+
+void main()
+{
+  vStart = rotate( start_point, rotate_center, rotate_angle );
+  vEnd = rotate( end_point, rotate_center, rotate_angle );
+  gl_Position = uMvpMatrix * ComputeVertexPosition();
+
+#ifdef UNIT_TYPE_BOUNDING_BOX
+  vTexCoord = vec2(aPosition.x, -aPosition.y);
+#endif
+/* UnitType::OBJECT_BOUNDING_BOX */
+
+#ifdef UNIT_TYPE_USER
+  vTexCoord = vec2(aPosition.x, -aPosition.y * uSize.y / uSize.x);
+#endif
+/* UnitType::USER_SPACE*/
+}
diff --git a/automated-tests/src/dali-shader-generator/tct-dali-shader-generator-core.cpp b/automated-tests/src/dali-shader-generator/tct-dali-shader-generator-core.cpp
new file mode 100644 (file)
index 0000000..842fd98
--- /dev/null
@@ -0,0 +1,6 @@
+#include <iostream>
+int main(int argc, char * const argv[])
+{
+  std::cout << "All tests run as part of Cmake build." << std::endl;
+  return 0;
+}
index 12d8805..62625ef 100644 (file)
@@ -3386,3 +3386,188 @@ int UtcDaliTextEditorLineCountAfterGetNaturalSize(void)
 
   END_TEST;
 }
+
+
+int utcDaliTextEditorGetHeightForWidthDoesNotChangeLineCountScrollingCase(void)
+{
+  ToolkitTestApplication application;
+
+  tet_infoline(" utcDaliTextEditorGetHeightForWidthDoesNotChangeLineCountScrollingCase ");
+
+  int lineCountBefore =0 ;
+  int lineCountAfter =0 ;
+
+  // Create a text editor
+  TextEditor textEditor = TextEditor::New();
+  //Set very large font-size using point-size
+  textEditor.SetProperty( TextEditor::Property::POINT_SIZE, 10) ;
+  //Specify font-family
+  textEditor.SetProperty( TextEditor::Property::FONT_FAMILY, "DejaVu Sans");
+  //Specify size
+  textEditor.SetProperty( Actor::Property::SIZE, Vector2( 100.f, 100.f ) );
+  //Set text longer than width of textEditor
+  textEditor.SetProperty( TextEditor::Property::TEXT, "TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ");
+
+  application.GetScene().Add( textEditor );
+
+  application.SendNotification();
+  application.Render();
+
+  //Failed case is the GetHeightForWidth change LineCount then the scrollor will not arrive to latest line
+  //GetHeightForWidth is a retrieval method which should not modify object
+  lineCountBefore =  textEditor.GetProperty<int>( TextEditor::Property::LINE_COUNT );
+  textEditor.GetHeightForWidth(200.f);
+
+  //This is to simulate focus into text editor after calling GetHeightForWidth
+  //Create a tap event to touch the text editor.
+  TestGenerateTap( application, 18.0f, 25.0f );
+
+  application.SendNotification();
+  application.Render();
+
+  lineCountAfter =  textEditor.GetProperty<int>( TextEditor::Property::LINE_COUNT );
+
+  //The LineCount must not be changed when calling GetHeightForWidth.
+  DALI_TEST_EQUALS( lineCountAfter , lineCountBefore, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int utcDaliTextEditorGetHeightForWidthDoesNotChangeLineCountLineWrapCharCase(void)
+{
+  ToolkitTestApplication application;
+
+  tet_infoline(" utcDaliTextEditorGetHeightForWidthDoesNotChangeLineCountLineWrapCharCase ");
+
+  int lineCountBefore =0 ;
+  int lineCountAfter =0 ;
+
+  // Create a text editor
+  TextEditor textEditor = TextEditor::New();
+  //Set very large font-size using point-size
+  textEditor.SetProperty( TextEditor::Property::POINT_SIZE, 10) ;
+  //Specify font-family
+  textEditor.SetProperty( TextEditor::Property::FONT_FAMILY, "DejaVu Sans");
+  //Specify size
+  textEditor.SetProperty( Actor::Property::SIZE, Vector2( 50.f, 100.f ) );
+  //Set text longer than width of textEditor
+  textEditor.SetProperty( TextEditor::Property::TEXT, "qwertyuiopasdfghjklzxcvbnm\n");
+  //Set line wrap mode Character
+  textEditor.SetProperty(TextEditor::Property::LINE_WRAP_MODE, "CHARACTER");
+
+  application.GetScene().Add( textEditor );
+
+  application.SendNotification();
+  application.Render();
+
+  //Failed case is the GetHeightForWidth change LineCount which make position of cursor invalid in TextEditor
+  //GetHeightForWidth is a retrieval method which should not modify object
+  lineCountBefore =  textEditor.GetProperty<int>( TextEditor::Property::LINE_COUNT );
+  textEditor.GetHeightForWidth(200.f);
+
+  //This is to simulate focus into text editor after calling GetHeightForWidth
+  //Create a tap event to touch the text editor.
+  TestGenerateTap( application, 18.0f, 25.0f );
+
+  application.SendNotification();
+  application.Render();
+
+  lineCountAfter =  textEditor.GetProperty<int>( TextEditor::Property::LINE_COUNT );
+
+  //The LineCount must not be changed when calling GetHeightForWidth.
+  DALI_TEST_EQUALS( lineCountAfter , lineCountBefore, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int utcDaliTextEditorGetNaturalSizeDoesNotChangeLineCountScrollingCase(void)
+{
+  ToolkitTestApplication application;
+
+  tet_infoline(" utcDaliTextEditorGetNaturalSizeDoesNotChangeLineCountScrollingCase ");
+
+  int lineCountBefore =0 ;
+  int lineCountAfter =0 ;
+
+  // Create a text editor
+  TextEditor textEditor = TextEditor::New();
+  //Set very large font-size using point-size
+  textEditor.SetProperty( TextEditor::Property::POINT_SIZE, 10) ;
+  //Specify font-family
+  textEditor.SetProperty( TextEditor::Property::FONT_FAMILY, "DejaVu Sans");
+  //Specify size
+  textEditor.SetProperty( Actor::Property::SIZE, Vector2( 100.f, 100.f ) );
+  //Set text longer than width of textEditor
+  textEditor.SetProperty( TextEditor::Property::TEXT, "TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ");
+
+  application.GetScene().Add( textEditor );
+
+  application.SendNotification();
+  application.Render();
+
+  //Failed case is the GetNaturalSize change LineCount then the scrollor will not arrive to latest line
+  //GetNaturalSize is a retrieval method which should not modify object
+  lineCountBefore =  textEditor.GetProperty<int>( TextEditor::Property::LINE_COUNT );
+  textEditor.GetNaturalSize();
+
+  //This is to simulate focus into text editor after calling GetNaturalSize
+  //Create a tap event to touch the text editor.
+  TestGenerateTap( application, 18.0f, 25.0f );
+
+  application.SendNotification();
+  application.Render();
+
+  lineCountAfter =  textEditor.GetProperty<int>( TextEditor::Property::LINE_COUNT );
+
+  //The LineCount must not be changed when calling GetNaturalSize.
+  DALI_TEST_EQUALS( lineCountAfter , lineCountBefore, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int utcDaliTextEditorGetNaturalSizeDoesNotChangeLineCountLineWrapCharCase(void)
+{
+  ToolkitTestApplication application;
+
+  tet_infoline(" utcDaliTextEditorGetNaturalSizeDoesNotChangeLineCountLineWrapCharCase ");
+
+  int lineCountBefore =0 ;
+  int lineCountAfter =0 ;
+
+  // Create a text editor
+  TextEditor textEditor = TextEditor::New();
+  //Set very large font-size using point-size
+  textEditor.SetProperty( TextEditor::Property::POINT_SIZE, 10) ;
+  //Specify font-family
+  textEditor.SetProperty( TextEditor::Property::FONT_FAMILY, "DejaVu Sans");
+  //Specify size
+  textEditor.SetProperty( Actor::Property::SIZE, Vector2( 50.f, 100.f ) );
+  //Set text longer than width of textEditor
+  textEditor.SetProperty( TextEditor::Property::TEXT, "qwertyuiopasdfghjklzxcvbnm\n");
+  //Set line wrap mode Character
+  textEditor.SetProperty(TextEditor::Property::LINE_WRAP_MODE, "CHARACTER");
+
+  application.GetScene().Add( textEditor );
+
+  application.SendNotification();
+  application.Render();
+
+  //Failed case is the GetNaturalSize change LineCount which make position of cursor invalid in TextEditor
+  //GetNaturalSize is a retrieval method which should not modify object
+  lineCountBefore =  textEditor.GetProperty<int>( TextEditor::Property::LINE_COUNT );
+  textEditor.GetNaturalSize( );
+
+  //This is to simulate focus into text editor after calling GetNaturalSize
+  //Create a tap event to touch the text editor.
+  TestGenerateTap( application, 18.0f, 25.0f );
+
+  application.SendNotification();
+  application.Render();
+
+  lineCountAfter =  textEditor.GetProperty<int>( TextEditor::Property::LINE_COUNT );
+
+  //The LineCount must not be changed when calling GetNaturalSize.
+  DALI_TEST_EQUALS( lineCountAfter , lineCountBefore, TEST_LOCATION );
+
+  END_TEST;
+}
\ No newline at end of file
index e59a17f..2d253c9 100644 (file)
@@ -2,4 +2,5 @@ build
 dali.info
 *.dylib
 dali2-*-config.cmake
-libdali2-scene-loader.so*
\ No newline at end of file
+libdali2-scene-loader.so*
+dali-shader-generator
index 8be2625..906453d 100644 (file)
@@ -286,13 +286,40 @@ ENDIF()
 # Generate source files for shaders
 SET(SHADER_SOURCE_DIR "${ROOT_SRC_DIR}/dali-toolkit/internal/graphics/shaders/")
 SET(SHADER_GENERATED_DIR "${ROOT_SRC_DIR}/dali-toolkit/internal/graphics/generated")
-EXECUTE_PROCESS( COMMAND bash -c "${CMAKE_CURRENT_SOURCE_DIR}/shader-generator.sh ${SHADER_SOURCE_DIR} ${SHADER_GENERATED_DIR}" )
 
 SET(GENERATED_SHADER_DIR ${ROOT_SRC_DIR}/dali-toolkit/internal/graphics/)
 SET_PROPERTY(DIRECTORY PROPERTY ADDITIONAL_MAKE_CLEAN_FILES
              "${GENERATED_SHADER_DIR}/generated/"
              "${GENERATED_SHADER_DIR}/builtin-shader-extern-gen.h")
 
+SET(SHADER_GENERATOR_NAME dali-shader-generator)
+SET(SHADER_GENERATOR_SOURCES ${ROOT_SRC_DIR}/dali-toolkit/shader-generator/shader-generator.cpp)
+
+IF(NOT ANDROID)
+  ADD_EXECUTABLE(${SHADER_GENERATOR_NAME} ${SHADER_GENERATOR_SOURCES})
+  INSTALL(TARGETS ${SHADER_GENERATOR_NAME} DESTINATION ${BINDIR})
+ELSE()
+  # Need to build dali-shader-generator using the host compiler, not the android cross-compiler so
+  # that it can be run on the host machine
+  OPTION(ANDROID_HOST_COMPILER "Provide the host compiler used by Android (Mandatory for Android")
+  IF(${ANDROID_HOST_COMPILER} STREQUAL "OFF")
+    MESSAGE(FATAL_ERROR "-DANDROID_HOST_COMPILER=\"Compiler\" must be set")
+  ENDIF()
+
+  ADD_CUSTOM_COMMAND(OUTPUT ${SHADER_GENERATOR_NAME}
+                     COMMAND ${ANDROID_HOST_COMPILER} -o ${CMAKE_CURRENT_BINARY_DIR}/${SHADER_GENERATOR_NAME} -std=c++17 ${SHADER_GENERATOR_SOURCES})
+  INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${SHADER_GENERATOR_NAME}
+          PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
+          DESTINATION bin)
+ENDIF()
+
+SET(BUILT_IN_SHADER_GEN_CPP "${GENERATED_SHADER_DIR}/generated/builtin-shader-gen.cpp")
+ADD_CUSTOM_COMMAND(OUTPUT ${BUILT_IN_SHADER_GEN_CPP}
+                   DEPENDS ${SHADER_GENERATOR_NAME}
+                   COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${SHADER_GENERATOR_NAME} ${SHADER_SOURCE_DIR} ${SHADER_GENERATED_DIR})
+
+SET(SOURCES ${SOURCES} ${BUILT_IN_SHADER_GEN_CPP})
+
 IF( WIN32 OR APPLE )
   SET( DALICORE_LDFLAGS
         "${DALICORE_LDFLAGS}"
index ae7fea2..7789dda 100644 (file)
@@ -77,6 +77,22 @@ include_directories(${repo_root_dir}
        ${prefix_include_dir}
 )
 
+# Generate source files for shaders
+SET(SHADER_SOURCE_DIR "${scene_loader_dir}/internal/graphics/shaders/")
+SET(SHADER_GENERATED_DIR "${scene_loader_dir}/internal/graphics/generated")
+
+SET(GENERATED_SHADER_DIR ${scene_loader_dir}/internal/graphics/)
+SET_PROPERTY(DIRECTORY PROPERTY ADDITIONAL_MAKE_CLEAN_FILES
+             "${GENERATED_SHADER_DIR}/generated/"
+             "${GENERATED_SHADER_DIR}/builtin-shader-extern-gen.h")
+
+SET( BUILT_IN_SHADER_GEN_CPP "${GENERATED_SHADER_DIR}/generated/builtin-shader-gen.cpp")
+ADD_CUSTOM_COMMAND(OUTPUT ${BUILT_IN_SHADER_GEN_CPP}
+                   DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/../${SHADER_GENERATOR_NAME}
+                   COMMAND ${CMAKE_CURRENT_BINARY_DIR}/../${SHADER_GENERATOR_NAME} ${SHADER_SOURCE_DIR} ${SHADER_GENERATED_DIR})
+
+SET( scene_loader_src_files ${scene_loader_src_files} ${BUILT_IN_SHADER_GEN_CPP} )
+
 add_library(${name} SHARED ${scene_loader_src_files})
 
 target_link_libraries(${name} ${DALICORE_LDFLAGS} ${DALIADAPTOR_LDFLAGS}
@@ -87,16 +103,6 @@ if( ANDROID )
        target_link_libraries(${name} log)
 endif()
 
-# Generate source files for shaders
-SET(SHADER_SOURCE_DIR "${scene_loader_dir}/internal/graphics/shaders/")
-SET(SHADER_GENERATED_DIR "${scene_loader_dir}/internal/graphics/generated")
-EXECUTE_PROCESS( COMMAND bash -c "${repo_root_dir}/build/tizen/shader-generator.sh ${SHADER_SOURCE_DIR} ${SHADER_GENERATED_DIR}" )
-
-SET(GENERATED_SHADER_DIR ${scene_loader_dir}/internal/graphics/)
-SET_PROPERTY(DIRECTORY PROPERTY ADDITIONAL_MAKE_CLEAN_FILES
-             "${GENERATED_SHADER_DIR}/generated/"
-             "${GENERATED_SHADER_DIR}/builtin-shader-extern-gen.h")
-
 IF( INSTALL_CMAKE_MODULES )
        SET_TARGET_PROPERTIES( ${name}
                PROPERTIES
diff --git a/build/tizen/shader-generator.sh b/build/tizen/shader-generator.sh
deleted file mode 100755 (executable)
index 7b6b2fc..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-#!/bin/bash
-
-indir=$1
-outdir=$2
-
-mkdir -p $outdir
-
-if [ ! -e $indir ] ; then
-    echo "Error! "$indir" not found!"
-    exit 0
-fi
-
-cd $indir
-all_shaders=$(ls -1 *.{vert,frag,def})
-cd $OLDPWD
-
-# Generate one header file per shader which is defined as a const std::string_view
-for name in $all_shaders ; do
-  echo "Generating header files for $name..."
-  varname=$(echo "SHADER_$name" | tr [a-z] [A-Z] | sed -e 's/-/_/g;s/\./_/g;')
-
-  newname=$(echo ${name} | sed -e 's/\./-/;')".h"
-  echo Writing $newname
-
-  shader_fullpath=$(echo ${indir})$name
-
-  header_name="${varname}_GEN_H"
-  echo "const std::string_view" "$varname""{" > $outdir/$newname
-  cat $shader_fullpath | sed -e 's/^..*$/"&\\n"/' >> $outdir/$newname
-  echo "};" >> $outdir/$newname
-done
-
-# Generate one cpp file that includes all the previously generated string_views for shaders
-echo "Generating cpp file..."
-echo -e "#include \"../builtin-shader-extern-gen.h\"\n" > $outdir/builtin-shader-gen.cpp
-
-varnames=
-for name in $all_shaders ; do
-    varname=$(echo "SHADER_$name" | tr [a-z] [A-Z] | sed -e 's/-/_/g;s/\./_/g;')
-    newname=$(echo ${name} | sed -e 's/\./-/;')".h"
-    varnames="${varnames} $varname"
-    echo "#include \"$newname\"" >> $outdir/builtin-shader-gen.cpp
-done
-
-# Generate one header file that defines all the shader string_views as extern variables
-echo "Generating extern header file ( for external use )..."
-echo "#ifndef GRAPHICS_BUILTIN_SHADER_EXTERN_GEN_H" > $outdir/../builtin-shader-extern-gen.h
-echo -e "#define GRAPHICS_BUILTIN_SHADER_EXTERN_GEN_H\n" >> $outdir/../builtin-shader-extern-gen.h
-
-echo "#include <string_view>" >> $outdir/../builtin-shader-extern-gen.h
-echo "" >> $outdir/../builtin-shader-extern-gen.h
-
-for name in $all_shaders ; do
-    varname=$(echo "SHADER_$name" | tr [a-z] [A-Z] | sed -e 's/-/_/g;s/\./_/g;')
-    newname=$(echo ${name} | sed -e 's/\./-/;')".h"
-    echo "extern const std::string_view $varname;" >> $outdir/../builtin-shader-extern-gen.h
-done
-cat >> $outdir/../builtin-shader-extern-gen.h << EOF
-
-#endif // GRAPHICS_BUILTIN_SHADER_EXTERN_GEN_H
-EOF
-
index ede26c1..35ac87b 100644 (file)
@@ -182,7 +182,6 @@ SET( toolkit_src_files
    ${toolkit_src_dir}/transition-effects/cube-transition-wave-effect-impl.cpp
    ${toolkit_src_dir}/text/xhtml-entities.cpp
    ${toolkit_src_dir}/drag-drop-detector/drag-and-drop-detector-impl.cpp
-   ${toolkit_src_dir}/graphics/generated/builtin-shader-gen.cpp
 )
 
 SET( SOURCES ${SOURCES}
index 227d889..78e7331 100644 (file)
@@ -48,81 +48,122 @@ namespace Toolkit
 {
 namespace Text
 {
-Vector3 Controller::Relayouter::GetNaturalSize(Controller& controller)
-{
-  DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::GetNaturalSize\n");
-  Vector3 naturalSize;
 
-  // Make sure the model is up-to-date before layouting
-  controller.ProcessModifyEvents();
+Size Controller::Relayouter::CalculateLayoutSizeOnRequiredControllerSize(Controller& controller, const Size& requestedControllerSize, const OperationsMask& requestedOperationsMask, bool restoreLinesAndGlyphPositions)
+{
+  DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->CalculateLayoutSizeOnRequiredControllerSize\n");
+  Size calculatedLayoutSize;
 
   Controller::Impl& impl        = *controller.mImpl;
   ModelPtr&         model       = impl.mModel;
   VisualModelPtr&   visualModel = model->mVisualModel;
-  if(impl.mRecalculateNaturalSize)
+
+  // Store the pending operations mask so that it can be restored later on with no modifications made on it
+  // while getting the natural size were reflected on the original mask.
+  OperationsMask operationsPendingBackUp = static_cast<OperationsMask>(impl.mOperationsPending);
+
+  // This is a hotfix for side effect on Scrolling, LineWrap and Invalid position of cursor in TextEditor after calling CalculateLayoutSizeOnRequiredControllerSize.
+  // The number of lines and glyph-positions inside visualModel have been changed by calling DoRelayout with requestedControllerSize.
+  // Store the mLines and mGlyphPositions from visualModel so that they can be restored later on with no modifications made on them.
+  //TODO: Refactor "DoRelayout" and extract common code of size calculation without modifying attributes of mVisualModel, and then blah, blah, etc.
+  Vector<LineRun> linesBackup = visualModel->mLines;
+  Vector<Vector2> glyphPositionsBackup = visualModel->mGlyphPositions;
+
+  // Operations that can be done only once until the text changes.
+  const OperationsMask onlyOnceOperations = static_cast<OperationsMask>(CONVERT_TO_UTF32 |
+                                                                        GET_SCRIPTS |
+                                                                        VALIDATE_FONTS |
+                                                                        GET_LINE_BREAKS |
+                                                                        BIDI_INFO |
+                                                                        SHAPE_TEXT |
+                                                                        GET_GLYPH_METRICS);
+
+  // Set the update info to relayout the whole text.
+  TextUpdateInfo& textUpdateInfo              = impl.mTextUpdateInfo;
+  textUpdateInfo.mParagraphCharacterIndex     = 0u;
+  textUpdateInfo.mRequestedNumberOfCharacters = model->mLogicalModel->mText.Count();
+
+  // Make sure the model is up-to-date before layouting
+  impl.UpdateModel(onlyOnceOperations);
+
+  // Get a reference to the pending operations member
+  OperationsMask& operationsPending = impl.mOperationsPending;
+
+  // Layout the text for the new width.
+  operationsPending = static_cast<OperationsMask>(operationsPending | requestedOperationsMask);
+
+  // Store the actual control's size to restore later.
+  const Size actualControlSize = visualModel->mControlSize;
+
+  DoRelayout(controller,
+              requestedControllerSize,
+              static_cast<OperationsMask>(onlyOnceOperations |
+                                          requestedOperationsMask),
+              calculatedLayoutSize);
+
+
+  // Clear the update info. This info will be set the next time the text is updated.
+  textUpdateInfo.Clear();
+  textUpdateInfo.mClearAll = true;
+
+  // Restore the actual control's size.
+  visualModel->mControlSize = actualControlSize;
+  // Restore the previously backed-up pending operations' mask without the only once operations.
+  impl.mOperationsPending = static_cast<OperationsMask>(operationsPendingBackUp & ~onlyOnceOperations);
+
+  // Restore the previously backed-up mLines and mGlyphPositions from visualModel.
+  if(restoreLinesAndGlyphPositions)
   {
-    // Store the pending operations mask so that it can be restored later on with no modifications made on it
-    // while getting the natural size were reflected on the original mask.
-    OperationsMask operationsPendingBackUp = static_cast<OperationsMask>(impl.mOperationsPending);
-    // Operations that can be done only once until the text changes.
-    const OperationsMask onlyOnceOperations = static_cast<OperationsMask>(CONVERT_TO_UTF32 |
-                                                                          GET_SCRIPTS |
-                                                                          VALIDATE_FONTS |
-                                                                          GET_LINE_BREAKS |
-                                                                          BIDI_INFO |
-                                                                          SHAPE_TEXT |
-                                                                          GET_GLYPH_METRICS);
+    visualModel->mLines = linesBackup;
+    visualModel->mGlyphPositions = glyphPositionsBackup;
+  }
 
-    // Set the update info to relayout the whole text.
-    TextUpdateInfo& textUpdateInfo              = impl.mTextUpdateInfo;
-    textUpdateInfo.mParagraphCharacterIndex     = 0u;
-    textUpdateInfo.mRequestedNumberOfCharacters = model->mLogicalModel->mText.Count();
+  return calculatedLayoutSize;
+}
 
-    // Make sure the model is up-to-date before layouting
-    impl.UpdateModel(onlyOnceOperations);
 
-    // Get a reference to the pending operations member
-    OperationsMask& operationsPending = impl.mOperationsPending;
+Vector3 Controller::Relayouter::GetNaturalSize(Controller& controller)
+{
+  DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::GetNaturalSize\n");
+  Vector3 naturalSizeVec3;
 
-    // Layout the text for the new width.
-    operationsPending = static_cast<OperationsMask>(operationsPending | LAYOUT | REORDER);
+  // Make sure the model is up-to-date before layouting
+  controller.ProcessModifyEvents();
+
+  Controller::Impl& impl           = *controller.mImpl;
+  ModelPtr&         model          = impl.mModel;
+  VisualModelPtr&   visualModel    = model->mVisualModel;
+
+  if(impl.mRecalculateNaturalSize)
+  {
+    Size naturalSize;
 
-    // Store the actual control's size to restore later.
-    const Size actualControlSize = visualModel->mControlSize;
+    // Layout the text for the new width.
+    OperationsMask requestedOperationsMask = static_cast<OperationsMask>(LAYOUT | REORDER);
+    Size sizeMaxWidthAndMaxHeight = Size(MAX_FLOAT, MAX_FLOAT);
 
-    DoRelayout(controller,
-               Size(MAX_FLOAT, MAX_FLOAT),
-               static_cast<OperationsMask>(onlyOnceOperations |
-                                           LAYOUT | REORDER),
-               naturalSize.GetVectorXY());
+    naturalSize = CalculateLayoutSizeOnRequiredControllerSize(controller, sizeMaxWidthAndMaxHeight, requestedOperationsMask, true);
 
     // Stores the natural size to avoid recalculate it again
     // unless the text/style changes.
-    visualModel->SetNaturalSize(naturalSize.GetVectorXY());
+    visualModel->SetNaturalSize(naturalSize);
+    naturalSizeVec3 = naturalSize;
 
     impl.mRecalculateNaturalSize = false;
 
-    // Clear the update info. This info will be set the next time the text is updated.
-    textUpdateInfo.Clear();
-    textUpdateInfo.mClearAll = true;
-
-    // Restore the actual control's size.
-    visualModel->mControlSize = actualControlSize;
-    // Restore the previously backed-up pending operations' mask without the only once operations.
-    impl.mOperationsPending = static_cast<OperationsMask>(operationsPendingBackUp & ~onlyOnceOperations);
-    DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize calculated %f,%f,%f\n", naturalSize.x, naturalSize.y, naturalSize.z);
+    DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize calculated %f,%f,%f\n", naturalSizeVec3.x, naturalSizeVec3.y, naturalSizeVec3.z);
   }
   else
   {
-    naturalSize = visualModel->GetNaturalSize();
+    naturalSizeVec3 = visualModel->GetNaturalSize();
 
-    DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize cached %f,%f,%f\n", naturalSize.x, naturalSize.y, naturalSize.z);
+    DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize cached %f,%f,%f\n", naturalSizeVec3.x, naturalSizeVec3.y, naturalSizeVec3.z);
   }
 
-  naturalSize.x = ConvertToEven(naturalSize.x);
-  naturalSize.y = ConvertToEven(naturalSize.y);
+  naturalSizeVec3.x = ConvertToEven(naturalSizeVec3.x);
+  naturalSizeVec3.y = ConvertToEven(naturalSizeVec3.y);
 
-  return naturalSize;
+  return naturalSizeVec3;
 }
 
 bool Controller::Relayouter::CheckForTextFit(Controller& controller, float pointSize, const Size& layoutSize)
@@ -226,6 +267,7 @@ void Controller::Relayouter::FitPointSizeforLayout(Controller& controller, const
 float Controller::Relayouter::GetHeightForWidth(Controller& controller, float width)
 {
   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::GetHeightForWidth %p width %f\n", &controller, width);
+
   // Make sure the model is up-to-date before layouting
   controller.ProcessModifyEvents();
 
@@ -235,52 +277,24 @@ float Controller::Relayouter::GetHeightForWidth(Controller& controller, float wi
   TextUpdateInfo&   textUpdateInfo = impl.mTextUpdateInfo;
 
   Size layoutSize;
+
   if(fabsf(width - visualModel->mControlSize.width) > Math::MACHINE_EPSILON_1000 ||
      textUpdateInfo.mFullRelayoutNeeded ||
      textUpdateInfo.mClearAll)
   {
-    // Store the pending operations mask so that it can be restored later on with no modifications made on it
-    // while getting the natural size were reflected on the original mask.
-    OperationsMask operationsPendingBackUp = static_cast<OperationsMask>(impl.mOperationsPending);
-    // Operations that can be done only once until the text changes.
-    const OperationsMask onlyOnceOperations = static_cast<OperationsMask>(CONVERT_TO_UTF32 |
-                                                                          GET_SCRIPTS |
-                                                                          VALIDATE_FONTS |
-                                                                          GET_LINE_BREAKS |
-                                                                          BIDI_INFO |
-                                                                          SHAPE_TEXT |
-                                                                          GET_GLYPH_METRICS);
-
-    // Set the update info to relayout the whole text.
-    textUpdateInfo.mParagraphCharacterIndex     = 0u;
-    textUpdateInfo.mRequestedNumberOfCharacters = model->mLogicalModel->mText.Count();
-
-    // Make sure the model is up-to-date before layouting
-    impl.UpdateModel(onlyOnceOperations);
-
-    // Get a reference to the pending operations member
-    OperationsMask& operationsPending = impl.mOperationsPending;
 
     // Layout the text for the new width.
-    operationsPending = static_cast<OperationsMask>(operationsPending | LAYOUT);
+    OperationsMask requestedOperationsMask = static_cast<OperationsMask>(LAYOUT);
+    Size sizeRequestedWidthAndMaxHeight = Size(width, MAX_FLOAT);
 
-    // Store the actual control's width.
-    const float actualControlWidth = visualModel->mControlSize.width;
+    // Skip restore, because if GetHeightForWidth called before rendering and layouting then visualModel->mControlSize will be zero which will make LineCount zero.
+    // The implementation of Get LineCount property depends on calling GetHeightForWidth then read mLines.Count() from visualModel direct.
+    // If the LineCount property is requested before rendering and layouting then the value will be zero, which is incorrect.
+    // So we will not restore the previously backed-up mLines and mGlyphPositions from visualModel in such case.
+    bool restoreLinesAndGlyphPositions = visualModel->mControlSize.width>0 && visualModel->mControlSize.height>0;
 
-    DoRelayout(controller,
-               Size(width, MAX_FLOAT),
-               static_cast<OperationsMask>(onlyOnceOperations |
-                                           LAYOUT),
-               layoutSize);
-
-    // Clear the update info. This info will be set the next time the text is updated.
-    textUpdateInfo.Clear();
-    textUpdateInfo.mClearAll = true;
+    layoutSize = CalculateLayoutSizeOnRequiredControllerSize(controller, sizeRequestedWidthAndMaxHeight, requestedOperationsMask, restoreLinesAndGlyphPositions);
 
-    // Restore the actual control's width.
-    visualModel->mControlSize.width = actualControlWidth;
-    // Restore the previously backed-up pending operations' mask without the only once operations.
-    impl.mOperationsPending = static_cast<OperationsMask>(operationsPendingBackUp & ~onlyOnceOperations);
     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::GetHeightForWidth calculated %f\n", layoutSize.height);
   }
   else
index d2d8670..1b586b2 100644 (file)
@@ -100,6 +100,21 @@ struct Controller::Relayouter
    * @param[in] controlSize The control size
    */
   static void CalculateVerticalOffset(Controller& controller, const Size& controlSize);
+
+  /**
+  * @brief Calculates the layout size of control according to @p requestedControllerSize and @p requestedOperationsMask
+  *
+  * GetNaturalSize() and GetHeightForWidth() calls this method.
+  *
+  * @param[in] controller The controller to calcualte size on it.
+  * @param[in] requestedControllerSize The requested size of controller to calcualte layout size on it.
+  * @param[in] requestedOperationsMask The requested operations-mask to calcualte layout size according to it.
+  * @param[in] restoreLinesAndGlyphPositions whether to restore lines and glyph-positions to status before requesting calculation on size.
+  *
+  * @return The calculated layout-size.
+  */
+  static Size CalculateLayoutSizeOnRequiredControllerSize(Controller& controller, const Size& requestedControllerSize, const OperationsMask& requestedOperationsMask, bool restoreLinesAndGlyphPositions);
+
 };
 
 } // namespace Text
index 7d2ab8a..2f475a2 100644 (file)
@@ -29,7 +29,7 @@ namespace Toolkit
 {
 const unsigned int TOOLKIT_MAJOR_VERSION = 2;
 const unsigned int TOOLKIT_MINOR_VERSION = 0;
-const unsigned int TOOLKIT_MICRO_VERSION = 22;
+const unsigned int TOOLKIT_MICRO_VERSION = 23;
 const char* const  TOOLKIT_BUILD_DATE    = __DATE__ " " __TIME__;
 
 #ifdef DEBUG_ENABLED
diff --git a/dali-toolkit/shader-generator/shader-generator.cpp b/dali-toolkit/shader-generator/shader-generator.cpp
new file mode 100644 (file)
index 0000000..d386fb3
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <algorithm>
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <string_view>
+#include <vector>
+
+using namespace std;
+namespace fs = filesystem;
+
+namespace
+{
+///////////////////////////////////////////////////////////////////////////////////////////////////
+string      PROGRAM_NAME; ///< We set the program name on this global early on for use in Usage.
+string_view VERSION = "1.0.0";
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/// Supported extensions for the files in the input directory.
+// clang-format off
+constexpr string_view SHADER_EXTENSIONS[] =
+{
+  ".vert",
+  ".frag",
+  ".def"
+};
+// clang-format on
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/// Function & variable to retrieve the size of the extension with the largest string size.
+constexpr auto GetShaderExtensionMaxSize()
+{
+  auto maxSize = 0u;
+  for(const auto& extension : SHADER_EXTENSIONS)
+  {
+    if(extension.size() > maxSize)
+    {
+      maxSize = extension.size();
+    }
+  }
+  return maxSize;
+}
+constexpr const int SHADER_MAX_EXTENSION_SIZE(GetShaderExtensionMaxSize());
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/// Prints out the Usage to standard output.
+void Usage()
+{
+  cout << "Usage: " << PROGRAM_NAME << " [OPTIONS] [IN_DIR] [OUT_DIR]" << endl;
+  cout << "  IN_DIR:  Input Directory which has all the shader files." << endl;
+  cout << "           Supported extensions:";
+  string extensions;
+  for(const auto& extension : SHADER_EXTENSIONS)
+  {
+    extensions = extensions + " \"" + string(extension) + "\",";
+  }
+  extensions.pop_back(); // Remove the last comma.
+  cout << extensions << "." << endl;
+  cout << "  OUT_DIR: Directory where the generated shader source code will be outputted to." << endl;
+  cout << "           This directory will be created if it does not exist." << endl;
+  cout << "           Any existing files of the same name in the directory will be overwritten." << endl;
+  cout << "  Options: " << endl;
+  cout << "     -s|--skip     Skips the generation of the built-in header and source files" << endl;
+  cout << "     -v|--version  Prints out the version" << endl;
+  cout << "     -h|--help     Help" << endl;
+  cout << "  NOTE: The options can be placed after the IN_DIR & OUT_DIR as well" << endl;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/// Uses the filename to generate the shader variable name to use in source code.
+/// @param[in]  filename  The filename of the shader (including the extension)
+/// @return The shader variable name
+string GetShaderVariableName(const string& filename)
+{
+  string shaderVariableName("SHADER_" + filename);
+  for_each(
+    shaderVariableName.begin(),
+    shaderVariableName.end(),
+    [](char& character) {
+      switch(character)
+      {
+        case '-':
+        case '.':
+        {
+          character = '_';
+          break;
+        }
+
+        default:
+        {
+          character = ::toupper(character);
+          break;
+        }
+      }
+    });
+  return shaderVariableName;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/// Uses the ourDir & filename to generate the path of the output header file for the shader.
+/// @param[in]  outDir    The directory where the readable shaders will be outputted to
+/// @param[in]  filename  The filename of the shader (including the extension)
+/// @return The path to the output file
+fs::path GetShaderOutputFilePath(fs::path& outDir, const string& filename)
+{
+  string outFilename(filename);
+  replace(outFilename.end() - SHADER_MAX_EXTENSION_SIZE, outFilename.end(), '.', '-');
+  outFilename = outDir.string() + "/" + outFilename + ".h";
+  return outFilename;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/// Generates the header file from the input shader file.
+/// @param[in]  shaderFile          The full path of the input shader file
+/// @param[in]  shaderVariableName  The variable name to use for the string_view
+/// @param[in]  outFilePath         The full path to the output file
+void GenerateHeaderFile(
+  ifstream&       shaderFile,
+  const string&   shaderVariableName,
+  const fs::path& outFilePath)
+{
+  cout << "  Generating \"" << shaderVariableName << "\" in " << outFilePath.filename();
+  ofstream outFile(outFilePath);
+  if(outFile.is_open())
+  {
+    outFile << "#pragma once" << endl
+            << endl;
+    outFile << "const std::string_view " << shaderVariableName << endl;
+    outFile << "{" << endl;
+    string line;
+    while(getline(shaderFile, line))
+    {
+      outFile << "\"" << line << "\\n\"" << endl;
+    }
+    outFile << "};" << endl;
+    cout << " [OK]" << endl;
+  }
+  else
+  {
+    cout << " [FAIL]" << endl;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/// If required, this accumulates data about all the shaders & generates the built-in cpp & header
+class BuiltInFilesGenerator
+{
+public:
+  /// Constructor
+  /// @param[in]  outDir  The path to the output directory
+  BuiltInFilesGenerator(const fs::path& outDir)
+  : mHeaderFilePath(outDir.string() + "/../" + string(HEADER_FILE_NAME)),
+    mSourceFilePath(outDir.string() + "/" + string(SOURCE_FILE_NAME))
+  {
+  }
+
+  /// Default destructor
+  ~BuiltInFilesGenerator() = default;
+
+  /// Adds the variable and the header file name to the appropriate vectors.
+  /// @param[in]  variableName    The string_view variable name used
+  /// @param[in]  headerFileName  The name of the header used
+  void Add(string&& variableName, const std::string& headerFilename)
+  {
+    mVariableNames.emplace_back(variableName);
+    mHeaderFileNames.emplace_back(headerFilename);
+  }
+
+  // Generates the built in files.
+  void Generate()
+  {
+    GenerateFile(
+      mVariableNames,
+      mHeaderFilePath,
+      "#pragma once\n\n#include <string_view>\n\n",
+      "extern const std::string_view ",
+      ";");
+
+    GenerateFile(
+      mHeaderFileNames,
+      mSourceFilePath,
+      "#include \"../" + string(HEADER_FILE_NAME) + "\"\n\n",
+      "#include \"",
+      "\"");
+  }
+
+private:
+  /// Generates the required file.
+  /// @param[in]  strings   A reference to the vector to parse
+  /// @param[in]  filePath  Outputs the data to this file
+  /// @param[in]  header    Puts this before parsing any of the vector
+  /// @param[in]  before    For each string, puts this string before it on every line
+  /// @param[in]  after     For each string, puts this string after it on every line
+  void GenerateFile(
+    vector<string>&   strings,
+    const string&     filePath,
+    const string_view header,
+    const string_view before,
+    const string_view after)
+  {
+    sort(strings.begin(), strings.end());
+    cout << "  Generating \"" << filePath << "\"";
+    ofstream outFile(filePath);
+    if(outFile)
+    {
+      outFile << header;
+      for(auto& current : strings)
+      {
+        outFile << before << current << after << endl;
+      }
+      cout << " [OK]" << endl;
+    }
+    else
+    {
+      cout << " [FAIL]" << endl;
+    }
+  }
+
+  constexpr static string_view HEADER_FILE_NAME = "builtin-shader-extern-gen.h";
+  constexpr static string_view SOURCE_FILE_NAME = "builtin-shader-gen.cpp";
+
+  const string   mHeaderFilePath;  ///< Path to the header file to generate
+  const string   mSourceFilePath;  ///< Path to the source file to generate
+  vector<string> mVariableNames;   ///< Holds all the variable names added through Add
+  vector<string> mHeaderFileNames; ///< Holds all the header file names added through Add
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/// Generates the header files from the shaders in the input directory & built-in files if reqruied.
+///
+/// @param[in]  inDir                 The directory where all the input shader source is
+/// @param[in]  outDir                The directory where the readable shaders will be outputted to
+/// @param[in]  generateBuiltInFiles  If true, we generate the built-in files as well
+/// @return 0 if successful, 1 if failure
+int GenerateShaderSources(fs::path inDir, fs::path outDir, const bool generateBuiltInFiles)
+{
+  if(!fs::is_directory(inDir))
+  {
+    cerr << "ERROR: " << inDir << " is not a valid directory" << endl;
+    Usage();
+    return 1;
+  }
+
+  try
+  {
+    fs::create_directories(outDir);
+  }
+  catch(...)
+  {
+    cerr << "ERROR: Unable to create directory " << outDir << endl;
+    return 1;
+  }
+
+  cout << "====================================================================" << endl;
+  cout << "Shader Input Directory:  " << inDir << endl;
+  cout << "Shader Output Directory: " << outDir << endl;
+  cout << "====================================================================" << endl;
+
+  BuiltInFilesGenerator generator(outDir);
+
+  for(auto& file : fs::directory_iterator(inDir))
+  {
+    if(file.is_regular_file())
+    {
+      for(const auto& extension : SHADER_EXTENSIONS)
+      {
+        if(file.path().extension() == extension)
+        {
+          const fs::path& path(file.path());
+          const string    filename(path.filename().string());
+          string          shaderVariableName(GetShaderVariableName(filename));
+          ifstream        shaderFile(path);
+          if(shaderFile.is_open())
+          {
+            fs::path outFilePath(GetShaderOutputFilePath(outDir, filename));
+            GenerateHeaderFile(shaderFile, shaderVariableName, outFilePath);
+            generator.Add(std::move(shaderVariableName), outFilePath.filename().string());
+          }
+          break;
+        }
+      }
+    }
+  }
+
+  if(generateBuiltInFiles)
+  {
+    generator.Generate();
+  }
+
+  cout << "====================================================================" << endl;
+  return 0;
+}
+
+} // unnamed namespace
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/// MAIN.
+int main(int argc, char* argv[])
+{
+  PROGRAM_NAME = argv[0];
+
+  bool generateBuiltInFiles = true;
+
+  string inDir;
+  string outDir;
+
+  for(auto i = 1; i < argc; ++i)
+  {
+    string option(argv[i]);
+    if(option == "--skip" || option == "-s")
+    {
+      generateBuiltInFiles = false;
+    }
+    else if(option == "--help" || option == "-h")
+    {
+      cout << "DALi Shader Generator v" << VERSION << endl
+           << endl;
+      Usage();
+      return 0;
+    }
+    else if(option == "--version" || option == "-v")
+    {
+      cout << VERSION << endl;
+      return 0;
+    }
+    else if(*option.begin() == '-')
+    {
+      cerr << "ERROR: " << option << " is not a supported option" << endl;
+      Usage();
+      return 1;
+    }
+    else if(inDir.empty())
+    {
+      inDir = option;
+    }
+    else if(outDir.empty())
+    {
+      outDir = option;
+    }
+    else if(inDir.size() && outDir.size())
+    {
+      cerr << "ERROR: Too many options" << endl;
+      Usage();
+      return 1;
+    }
+  }
+
+  if(inDir.empty() || outDir.empty())
+  {
+    cerr << "ERROR: Both IN_DIR & OUT_DIR not provided" << endl;
+    Usage();
+    return 1;
+  }
+
+  return GenerateShaderSources(inDir, outDir, generateBuiltInFiles);
+}
index 674addb..ba6c2b6 100644 (file)
@@ -1,6 +1,6 @@
 Name:       dali2-toolkit
 Summary:    Dali 3D engine Toolkit
-Version:    2.0.22
+Version:    2.0.23
 Release:    1
 Group:      System/Libraries
 License:    Apache-2.0 and BSD-3-Clause and MIT
@@ -379,6 +379,7 @@ esac
 %defattr(-,root,root,-)
 %{dev_include_path}/dali-toolkit/*
 %{_libdir}/pkgconfig/dali2-toolkit.pc
+%{_bindir}/dali-shader-generator
 
 %files resources_360x360
 %manifest dali-toolkit-resources.manifest