--- /dev/null
+<svg width="100" height="100">
+ <circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" />
+</svg>
+
utc-Dali-BidirectionalSupport.cpp
utc-Dali-Text-Shaping.cpp
utc-Dali-VisualModel.cpp
+ utc-Dali-Text-Layout.cpp
)
# Append list of test harness files (Won't get parsed for test cases)
unsigned int numberOfCharacters; ///< The number of characters.
unsigned int numberOfLineInfo; ///< The number or reordered lines.
BidiLineData* bidiLineData; ///< The bidirectional line info.
+ unsigned int numberOfLines; ///< The number of laid-out lines.
+ bool* lineDirections; ///< The directions of the lines.
};
struct GetMirroredTextData
Size layoutSize;
// Create the model.
+ const Vector<FontDescriptionRun> fontDescriptions;
+ const LayoutOptions options;
CreateTextModel( data.text,
textArea,
+ fontDescriptions,
+ options,
layoutSize,
logicalModel,
visualModel );
Size layoutSize;
// Create the model.
+ const Vector<FontDescriptionRun> fontDescriptions;
+ const LayoutOptions options;
CreateTextModel( data.text,
textArea,
+ fontDescriptions,
+ options,
layoutSize,
logicalModel,
visualModel );
}
}
+ if( data.numberOfLines != visualModel->mLines.Count() )
+ {
+ std::cout << "Different number of lines : " << visualModel->mLines.Count() << ", expected : " << data.numberOfLines << std::endl;
+
+ unsigned int index = 0u;
+ for( Vector<LineRun>::ConstIterator it = visualModel->mLines.Begin(),
+ endIt = visualModel->mLines.End();
+ it != endIt;
+ ++it, ++index )
+ {
+ const LineRun& line = *it;
+
+ if( line.direction != *( data.lineDirections + index ) )
+ {
+ std::cout << " Different line direction : " << line.direction << ", expected : " << *( data.lineDirections + index ) << std::endl;
+ return false;
+ }
+ }
+ }
+
return true;
}
Size layoutSize;
// Create the model.
+ const Vector<FontDescriptionRun> fontDescriptions;
+ const LayoutOptions options;
CreateTextModel( data.text,
textArea,
+ fontDescriptions,
+ options,
layoutSize,
logicalModel,
visualModel );
Size layoutSize;
// Create the model.
+ const Vector<FontDescriptionRun> fontDescriptions;
+ const LayoutOptions options;
CreateTextModel( data.text,
textArea,
+ fontDescriptions,
+ options,
layoutSize,
logicalModel,
visualModel );
},
};
+ bool directions02[] = { false, false, false, false, false, false };
+ bool directions03[] = { false, false, false, false, false, true, true, true, true, true, false, false, false, false, false, true, true, true, true, true };
+
struct ReorderLinesData data[] =
{
{
0u,
0u,
0u,
- bidiLine01
+ bidiLine01,
+ 0u,
+ NULL
},
{
"Left to right text only.",
0u,
51u,
0u,
- bidiLine02
+ bidiLine02,
+ 6u,
+ directions02
},
{
"Bidirectional paragraphs.",
0u,
195u,
16u,
- bidiLine03
+ bidiLine03,
+ 20u,
+ directions03
},
{
"Bidirectional paragraphs. Update initial paragraphs.",
0u,
44u,
16u,
- bidiLine03
+ bidiLine03,
+ 20u,
+ directions03
},
{
"Bidirectional paragraphs. Update middle paragraphs.",
44u,
54u,
16u,
- bidiLine03
+ bidiLine03,
+ 20u,
+ directions03
},
{
"Bidirectional paragraphs. Update final paragraphs.",
142u,
53u,
16u,
- bidiLine03
+ bidiLine03,
+ 20u,
+ directions03
},
};
const unsigned int numberOfTests = 6u;
Size layoutSize;
// Create the model with the whole text.
+ const Vector<FontDescriptionRun> fontDescriptions;
+ const LayoutOptions options;
CreateTextModel( data.text,
data.textArea,
+ fontDescriptions,
+ options,
layoutSize,
logicalModel,
visualModel );
--- /dev/null
+/*
+ * Copyright (c) 2016 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 <iostream>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <dali-toolkit/internal/text/layouts/layout-engine.h>
+#include <dali-toolkit/internal/text/layouts/layout-parameters.h>
+#include <dali-toolkit/internal/text/text-run-container.h>
+#include <dali-toolkit-test-suite-utils.h>
+#include <dali-toolkit/dali-toolkit.h>
+#include <toolkit-text-model.h>
+
+using namespace Dali;
+using namespace Toolkit;
+using namespace Text;
+
+// Tests the LayoutEngine methods.
+
+//////////////////////////////////////////////////////////
+
+namespace
+{
+
+const std::string DEFAULT_FONT_DIR( "/resources/fonts" );
+
+struct LayoutTextData
+{
+ std::string description;
+ std::string text;
+ Size textArea;
+ unsigned int numberOfFonts;
+ FontDescriptionRun* fontDescriptions;
+ Size layoutSize;
+ unsigned int totalNumberOfGlyphs;
+ float* positions;
+ unsigned int numberOfLines;
+ LineRun* lines;
+ LayoutEngine::Layout layout;
+ unsigned int startIndex;
+ unsigned int numberOfGlyphs;
+ bool ellipsis:1;
+ bool updated:1;
+};
+
+void Print( const LineRun& line )
+{
+ std::cout << " glyph run, index : " << line.glyphRun.glyphIndex << ", num glyphs : " << line.glyphRun.numberOfGlyphs << std::endl;
+ std::cout << " character run, index : " << line.characterRun.characterIndex << ", num chars : " << line.characterRun.numberOfCharacters << std::endl;
+ std::cout << " width : " << line.width << std::endl;
+ std::cout << " ascender : " << line.ascender << std::endl;
+ std::cout << " descender : " << line.descender << std::endl;
+ std::cout << " extraLength : " << line.extraLength << std::endl;
+ std::cout << " alignmentOffset : " << line.alignmentOffset << std::endl;
+ std::cout << " direction : " << line.direction << std::endl;
+ std::cout << " ellipsis : " << line.ellipsis << std::endl;
+}
+
+bool LayoutTextTest( const LayoutTextData& data )
+{
+ // Load some fonts.
+ TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+ fontClient.SetDpi( 96u, 96u );
+
+ char* pathNamePtr = get_current_dir_name();
+ const std::string pathName( pathNamePtr );
+ free( pathNamePtr );
+
+ fontClient.GetFontId( pathName + DEFAULT_FONT_DIR + "/tizen/TizenSansRegular.ttf" );
+ fontClient.GetFontId( pathName + DEFAULT_FONT_DIR + "/tizen/TizenSansHebrewRegular.ttf" );
+ fontClient.GetFontId( pathName + DEFAULT_FONT_DIR + "/tizen/TizenSansArabicRegular.ttf" );
+
+ // 1) Create the model.
+ LogicalModelPtr logicalModel = LogicalModel::New();
+ VisualModelPtr visualModel = VisualModel::New();
+ Size layoutSize;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ if( 0u != data.numberOfFonts )
+ {
+ fontDescriptionRuns.Insert( fontDescriptionRuns.End(),
+ data.fontDescriptions,
+ data.fontDescriptions + data.numberOfFonts );
+ }
+
+ LayoutOptions options;
+ options.reorder = false;
+ options.align = false;
+ CreateTextModel( data.text,
+ data.textArea,
+ fontDescriptionRuns,
+ options,
+ layoutSize,
+ logicalModel,
+ visualModel );
+
+ // 2) Clear the layout.
+ Vector<LineRun>& lines = visualModel->mLines;
+
+ const Length numberOfCharacters = logicalModel->mText.Count();
+ const bool isLastNewParagraph = ( 0u == numberOfCharacters ) ? false : TextAbstraction::IsNewParagraph( *( logicalModel->mText.Begin() + ( numberOfCharacters - 1u ) ) );
+ const GlyphIndex lastGlyphIndex = data.startIndex + data.numberOfGlyphs - 1u;
+ const bool removeLastLine = isLastNewParagraph && ( lastGlyphIndex + 1u == visualModel->mGlyphs.Count() );
+
+ LineIndex startRemoveIndex = 0u;
+
+ if( 0u != lines.Count() )
+ {
+ startRemoveIndex = lines.Count();
+ LineIndex endRemoveIndex = startRemoveIndex;
+ ClearGlyphRuns( data.startIndex,
+ lastGlyphIndex + ( removeLastLine ? 1u : 0u ),
+ lines,
+ startRemoveIndex,
+ endRemoveIndex );
+
+ // Update the character runs of the lines.
+ const CharacterIndex* const glyphsToCharactersBuffer = visualModel->mGlyphsToCharacters.Begin();
+ const Length* const charactersPerGlyph = visualModel->mCharactersPerGlyph.Begin();
+ const CharacterIndex startCharacterIndex = *( glyphsToCharactersBuffer + data.startIndex );
+ const CharacterIndex lastCharacterIndex = *( glyphsToCharactersBuffer + lastGlyphIndex ) + *( charactersPerGlyph + lastGlyphIndex ) - 1u;
+ ClearCharacterRuns( startCharacterIndex,
+ lastCharacterIndex + ( removeLastLine ? 1u : 0u ),
+ lines,
+ startRemoveIndex,
+ endRemoveIndex );
+
+ lines.Erase( lines.Begin() + startRemoveIndex,
+ lines.Begin() + endRemoveIndex );
+ }
+
+ Vector<Vector2>& glyphPositions = visualModel->mGlyphPositions;
+
+ glyphPositions.Erase( glyphPositions.Begin() + data.startIndex,
+ glyphPositions.Begin() + data.startIndex + data.numberOfGlyphs );
+
+ // 3) Layout
+ MetricsPtr metrics = Metrics::New( fontClient );
+
+ LayoutEngine engine;
+ engine.SetMetrics( metrics );
+ engine.SetTextEllipsisEnabled( data.ellipsis );
+ engine.SetLayout( data.layout );
+
+ const Length totalNumberOfGlyphs = visualModel->mGlyphs.Count();
+
+ LayoutParameters layoutParameters( data.textArea,
+ logicalModel->mText.Begin(),
+ logicalModel->mLineBreakInfo.Begin(),
+ logicalModel->mWordBreakInfo.Begin(),
+ ( 0u != logicalModel->mCharacterDirections.Count() ) ? logicalModel->mCharacterDirections.Begin() : NULL,
+ visualModel->mGlyphs.Begin(),
+ visualModel->mGlyphsToCharacters.Begin(),
+ visualModel->mCharactersPerGlyph.Begin(),
+ visualModel->mCharactersToGlyph.Begin(),
+ visualModel->mGlyphsPerCharacter.Begin(),
+ totalNumberOfGlyphs );
+
+ layoutParameters.isLastNewParagraph = isLastNewParagraph;
+
+ // The initial glyph and the number of glyphs to layout.
+ layoutParameters.startGlyphIndex = data.startIndex;
+ layoutParameters.numberOfGlyphs = data.numberOfGlyphs;
+ layoutParameters.startLineIndex = startRemoveIndex;
+
+ layoutSize = Vector2::ZERO;
+
+ const bool updated = engine.LayoutText( layoutParameters,
+ glyphPositions,
+ lines,
+ layoutSize );
+
+ // 4) Compare the results.
+
+ if( updated != data.updated )
+ {
+ std::cout << " Different updated bool : " << updated << ", expected : " << data.updated << std::endl;
+ return false;
+ }
+
+ if( layoutSize != data.layoutSize )
+ {
+ std::cout << " Different layout size : " << layoutSize << ", expected : " << data.layoutSize << std::endl;
+ return false;
+ }
+
+ for( unsigned int index = 0u; index < data.totalNumberOfGlyphs; ++index )
+ {
+ const Vector2& position = *( glyphPositions.Begin() + index );
+
+ if( fabsf( position.x - *( data.positions + 2u * index ) ) > Math::MACHINE_EPSILON_1000 )
+ {
+ std::cout << " Different position for glyph " << index << " x : " << position.x << ", expected : " << *( data.positions + 2u * index ) << std::endl;
+ return false;
+ }
+ if( fabsf( position.y - *( data.positions + 2u * index + 1u ) ) > Math::MACHINE_EPSILON_1000 )
+ {
+ std::cout << " Different position for glyph " << index << " y : " << position.y << ", expected : " << *( data.positions + 2u * index + 1u ) << std::endl;
+ return false;
+ }
+ }
+
+ if( lines.Count() != data.numberOfLines )
+ {
+ std::cout << " Different number of lines : " << lines.Count() << ", expected : " << data.numberOfLines << std::endl;
+ return false;
+ }
+
+ for( unsigned int index = 0u; index < data.numberOfLines; ++index )
+ {
+ const LineRun& line = *( lines.Begin() + index );
+ const LineRun& expectedLine = *( data.lines + index );
+
+ if( line.glyphRun.glyphIndex != expectedLine.glyphRun.glyphIndex )
+ {
+ std::cout << " Different line info for line : " << index << std::endl;
+ Print( line );
+ std::cout << " expected" << std::endl;
+ Print( expectedLine );
+ return false;
+ }
+ if( line.glyphRun.numberOfGlyphs != expectedLine.glyphRun.numberOfGlyphs )
+ {
+ std::cout << " Different line info for line : " << index << std::endl;
+ Print( line );
+ std::cout << " expected" << std::endl;
+ Print( expectedLine );
+ return false;
+ }
+
+ if( line.characterRun.characterIndex != expectedLine.characterRun.characterIndex )
+ {
+ std::cout << " Different line info for line : " << index << std::endl;
+ Print( line );
+ std::cout << " expected" << std::endl;
+ Print( expectedLine );
+ return false;
+ }
+ if( line.characterRun.numberOfCharacters != expectedLine.characterRun.numberOfCharacters )
+ {
+ std::cout << " Different line info for line : " << index << std::endl;
+ Print( line );
+ std::cout << " expected" << std::endl;
+ Print( expectedLine );
+ return false;
+ }
+
+ if( fabsf( line.width - expectedLine.width ) > Math::MACHINE_EPSILON_1 )
+ {
+ std::cout << " Different line info for line : " << index << std::endl;
+ Print( line );
+ std::cout << " expected" << std::endl;
+ Print( expectedLine );
+ return false;
+ }
+
+ if( fabsf( line.ascender - expectedLine.ascender ) > Math::MACHINE_EPSILON_1 )
+ {
+ std::cout << " Different line info for line : " << index << std::endl;
+ Print( line );
+ std::cout << " expected" << std::endl;
+ Print( expectedLine );
+ return false;
+ }
+
+ if( fabsf( line.descender - expectedLine.descender ) > Math::MACHINE_EPSILON_1 )
+ {
+ std::cout << " Different line info for line : " << index << std::endl;
+ Print( line );
+ std::cout << " expected" << std::endl;
+ Print( expectedLine );
+ return false;
+ }
+
+ if( fabsf( line.extraLength - expectedLine.extraLength ) > Math::MACHINE_EPSILON_1 )
+ {
+ std::cout << " Different line info for line : " << index << std::endl;
+ Print( line );
+ std::cout << " expected" << std::endl;
+ Print( expectedLine );
+ return false;
+ }
+
+ if( line.ellipsis != expectedLine.ellipsis )
+ {
+ std::cout << " Different line info for line : " << index << std::endl;
+ Print( line );
+ std::cout << " expected" << std::endl;
+ Print( expectedLine );
+ return false;
+ }
+
+ // Do not compare the alignment offset as it's not calculated in the layout.
+ // Do not compare the line direction as it's not set in the layout.
+ }
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////
+
+struct ReLayoutRightToLeftLinesData
+{
+ std::string description;
+ std::string text;
+ Size textArea;
+ unsigned int numberOfFonts;
+ FontDescriptionRun* fontDescriptions;
+ unsigned int totalNumberOfGlyphs;
+ float* positions;
+ unsigned int startIndex;
+ unsigned int numberOfCharacters;
+};
+
+bool ReLayoutRightToLeftLinesTest( const ReLayoutRightToLeftLinesData& data )
+{
+ // Load some fonts.
+ TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+ fontClient.SetDpi( 96u, 96u );
+
+ char* pathNamePtr = get_current_dir_name();
+ const std::string pathName( pathNamePtr );
+ free( pathNamePtr );
+
+ fontClient.GetFontId( pathName + DEFAULT_FONT_DIR + "/tizen/TizenSansRegular.ttf" );
+ fontClient.GetFontId( pathName + DEFAULT_FONT_DIR + "/tizen/TizenSansHebrewRegular.ttf" );
+ fontClient.GetFontId( pathName + DEFAULT_FONT_DIR + "/tizen/TizenSansArabicRegular.ttf" );
+
+ // 1) Create the model.
+ LogicalModelPtr logicalModel = LogicalModel::New();
+ VisualModelPtr visualModel = VisualModel::New();
+ Size layoutSize;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ if( 0u != data.numberOfFonts )
+ {
+ fontDescriptionRuns.Insert( fontDescriptionRuns.End(),
+ data.fontDescriptions,
+ data.fontDescriptions + data.numberOfFonts );
+ }
+
+ LayoutOptions options;
+ options.reorder = false;
+ options.align = false;
+ CreateTextModel( data.text,
+ data.textArea,
+ fontDescriptionRuns,
+ options,
+ layoutSize,
+ logicalModel,
+ visualModel );
+
+ // 2) Call the ReLayoutRightToLeftLines() method.
+ MetricsPtr metrics = Metrics::New( fontClient );
+
+ LayoutEngine engine;
+ engine.SetMetrics( metrics );
+
+ LayoutParameters layoutParameters( data.textArea,
+ logicalModel->mText.Begin(),
+ logicalModel->mLineBreakInfo.Begin(),
+ logicalModel->mWordBreakInfo.Begin(),
+ ( 0u != logicalModel->mCharacterDirections.Count() ) ? logicalModel->mCharacterDirections.Begin() : NULL,
+ visualModel->mGlyphs.Begin(),
+ visualModel->mGlyphsToCharacters.Begin(),
+ visualModel->mCharactersPerGlyph.Begin(),
+ visualModel->mCharactersToGlyph.Begin(),
+ visualModel->mGlyphsPerCharacter.Begin(),
+ visualModel->mGlyphs.Count() );
+
+ layoutParameters.numberOfBidirectionalInfoRuns = logicalModel->mBidirectionalLineInfo.Count();
+ layoutParameters.lineBidirectionalInfoRunsBuffer = logicalModel->mBidirectionalLineInfo.Begin();
+
+ engine.ReLayoutRightToLeftLines( layoutParameters,
+ data.startIndex,
+ data.numberOfCharacters,
+ visualModel->mGlyphPositions );
+
+ // 3) Compare the results.
+ Vector<Vector2>& glyphPositions = visualModel->mGlyphPositions;
+
+ if( data.totalNumberOfGlyphs != visualModel->mGlyphs.Count() )
+ {
+ std::cout << " Different number of glyphs : " << visualModel->mGlyphs.Count() << ", expected : " << data.totalNumberOfGlyphs << std::endl;
+ return false;
+ }
+
+ for( unsigned int index = 0u; index < data.totalNumberOfGlyphs; ++index )
+ {
+ const Vector2& position = *( glyphPositions.Begin() + index );
+
+ if( fabsf( position.x - *( data.positions + 2u * index ) ) > Math::MACHINE_EPSILON_1000 )
+ {
+ std::cout << " Different position for glyph " << index << " x : " << position.x << ", expected : " << *( data.positions + 2u * index ) << std::endl;
+ return false;
+ }
+ if( fabsf( position.y - *( data.positions + 2u * index + 1u ) ) > Math::MACHINE_EPSILON_1000 )
+ {
+ std::cout << " Different position for glyph " << index << " y : " << position.y << ", expected : " << *( data.positions + 2u * index + 1u ) << std::endl;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////
+
+struct AlignData
+{
+ std::string description;
+ std::string text;
+ Size textArea;
+ unsigned int numberOfFonts;
+ FontDescriptionRun* fontDescriptions;
+ LayoutEngine::HorizontalAlignment horizontalAlignment;
+ LayoutEngine::VerticalAlignment verticalAlignment;
+ unsigned int startIndex;
+ unsigned int numberOfCharacters;
+ unsigned int numberOfLines;
+ float* lineOffsets;
+};
+
+bool AlignTest( const AlignData& data )
+{
+ // Load some fonts.
+ TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+ fontClient.SetDpi( 96u, 96u );
+
+ char* pathNamePtr = get_current_dir_name();
+ const std::string pathName( pathNamePtr );
+ free( pathNamePtr );
+
+ fontClient.GetFontId( pathName + DEFAULT_FONT_DIR + "/tizen/TizenSansRegular.ttf" );
+ fontClient.GetFontId( pathName + DEFAULT_FONT_DIR + "/tizen/TizenSansHebrewRegular.ttf" );
+ fontClient.GetFontId( pathName + DEFAULT_FONT_DIR + "/tizen/TizenSansArabicRegular.ttf" );
+
+ // 1) Create the model.
+ LogicalModelPtr logicalModel = LogicalModel::New();
+ VisualModelPtr visualModel = VisualModel::New();
+ Size layoutSize;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ if( 0u != data.numberOfFonts )
+ {
+ fontDescriptionRuns.Insert( fontDescriptionRuns.End(),
+ data.fontDescriptions,
+ data.fontDescriptions + data.numberOfFonts );
+ }
+
+ LayoutOptions options;
+ options.align = false;
+ CreateTextModel( data.text,
+ data.textArea,
+ fontDescriptionRuns,
+ options,
+ layoutSize,
+ logicalModel,
+ visualModel );
+
+ // Call the Align method.
+ MetricsPtr metrics = Metrics::New( fontClient );
+
+ LayoutEngine engine;
+ engine.SetMetrics( metrics );
+
+ engine.SetHorizontalAlignment( data.horizontalAlignment );
+ engine.SetVerticalAlignment( data.verticalAlignment );
+
+ engine.Align( data.textArea,
+ data.startIndex,
+ data.numberOfCharacters,
+ visualModel->mLines );
+
+ // Compare results.
+ if( data.numberOfLines != visualModel->mLines.Count() )
+ {
+ std::cout << " Different number of lines : " << visualModel->mLines.Count() << ", expected : " << data.numberOfLines << std::endl;
+ return false;
+ }
+
+ const LineRun* const linesBuffer = visualModel->mLines.Begin();
+ for( unsigned int index = 0u; index < data.numberOfLines; ++index )
+ {
+ const LineRun& line = *( linesBuffer + index );
+
+ if( line.alignmentOffset != *( data.lineOffsets + index ) )
+ {
+ std::cout << " different line offset for index " << index << " : " << line.alignmentOffset << ", expected : " << *( data.lineOffsets + index ) << std::endl;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace
+
+//////////////////////////////////////////////////////////
+//
+// UtcDaliTextLayoutSetGetLayout
+// UtcDaliTextLayoutSetGetTextEllipsisEnabled
+// UtcDaliTextLayoutSetGetHorizontalAlignment
+// UtcDaliTextLayoutSetGetVerticalAlignment
+// UtcDaliTextLayoutSetGetCursorWidth
+// UtcDaliTextLayoutNoText
+// UtcDaliTextLayoutSmallTextArea01
+// UtcDaliTextLayoutSmallTextArea02
+// UtcDaliTextLayoutMultilineText01
+// UtcDaliTextLayoutMultilineText02
+// UtcDaliTextLayoutMultilineText03
+// UtcDaliTextLayoutMultilineText04
+// UtcDaliTextLayoutMultilineText05
+// UtcDaliTextUpdateLayout01
+// UtcDaliTextUpdateLayout02
+// UtcDaliTextUpdateLayout03
+// UtcDaliTextLayoutEllipsis01
+// UtcDaliTextLayoutEllipsis02
+// UtcDaliTextLayoutEllipsis03
+// UtcDaliTextLayoutEllipsis04
+// UtcDaliTextLayoutEllipsis04
+// UtcDaliTextReorderLayout01
+// UtcDaliTextReorderLayout02
+// UtcDaliTextReorderLayout03
+// UtcDaliTextReorderLayout04
+// UtcDaliTextAlign01
+// UtcDaliTextAlign02
+// UtcDaliTextAlign03
+// UtcDaliTextAlign04
+// UtcDaliTextAlign05
+// UtcDaliTextAlign06
+// UtcDaliTextAlign07
+// UtcDaliTextAlign08
+// UtcDaliTextAlign09
+//
+//////////////////////////////////////////////////////////
+
+int UtcDaliTextLayoutSetGetLayout(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextLayoutSetGetLayout");
+
+ LayoutEngine engine;
+
+ DALI_TEST_CHECK( LayoutEngine::SINGLE_LINE_BOX == engine.GetLayout() );
+
+ engine.SetLayout( LayoutEngine::MULTI_LINE_BOX );
+ DALI_TEST_CHECK( LayoutEngine::MULTI_LINE_BOX == engine.GetLayout() );
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextLayoutSetGetTextEllipsisEnabled(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextLayoutSetGetTextEllipsisEnabled");
+
+ LayoutEngine engine;
+
+ DALI_TEST_CHECK( !engine.GetTextEllipsisEnabled() );
+
+ engine.SetTextEllipsisEnabled( true );
+ DALI_TEST_CHECK( engine.GetTextEllipsisEnabled() );
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextLayoutSetGetHorizontalAlignment(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" ");
+
+ LayoutEngine engine;
+
+ DALI_TEST_CHECK( LayoutEngine::HORIZONTAL_ALIGN_BEGIN == engine.GetHorizontalAlignment() );
+
+ engine.SetHorizontalAlignment( LayoutEngine::HORIZONTAL_ALIGN_END );
+ DALI_TEST_CHECK( LayoutEngine::HORIZONTAL_ALIGN_END == engine.GetHorizontalAlignment() );
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextLayoutSetGetVerticalAlignment(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextLayoutSetGetVerticalAlignment");
+
+ LayoutEngine engine;
+
+ DALI_TEST_CHECK( LayoutEngine::VERTICAL_ALIGN_TOP == engine.GetVerticalAlignment() );
+
+ engine.SetVerticalAlignment( LayoutEngine::VERTICAL_ALIGN_TOP );
+ DALI_TEST_CHECK( LayoutEngine::VERTICAL_ALIGN_TOP == engine.GetVerticalAlignment() );
+
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextLayoutSetGetCursorWidth(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" ");
+
+ LayoutEngine engine;
+
+ DALI_TEST_EQUALS( 1, engine.GetCursorWidth(), TEST_LOCATION );
+
+ engine.SetCursorWidth( 2 );
+ DALI_TEST_EQUALS( 2, engine.GetCursorWidth(), TEST_LOCATION );
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextLayoutNoText(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextLayoutNoText");
+
+ Size textArea(100.f, 60.f);
+ Size layoutSize = Vector2::ZERO;
+ LayoutTextData data =
+ {
+ "No text",
+ "",
+ textArea,
+ 0u,
+ NULL,
+ layoutSize,
+ 0u,
+ NULL,
+ 0u,
+ NULL,
+ LayoutEngine::MULTI_LINE_BOX,
+ 0u,
+ 0u,
+ false,
+ false
+ };
+
+ if( !LayoutTextTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextLayoutSmallTextArea01(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextLayoutSmallTextArea01");
+
+ // Won't layout the text in multi-line if the width is too small.
+
+ const std::string fontFamily( "TizenSans" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun;
+ fontDescriptionRun.characterRun.characterIndex = 0u;
+ fontDescriptionRun.characterRun.numberOfCharacters = 11u;
+ fontDescriptionRun.familyLength = fontFamily.size();
+ fontDescriptionRun.familyName = new char[fontDescriptionRun.familyLength];
+ memcpy( fontDescriptionRun.familyName, fontFamily.c_str(), fontDescriptionRun.familyLength );
+ fontDescriptionRun.familyDefined = true;
+ fontDescriptionRun.weightDefined = false;
+ fontDescriptionRun.widthDefined = false;
+ fontDescriptionRun.slantDefined = false;
+ fontDescriptionRun.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun );
+ Size textArea(1.f, 1.f);
+ Size layoutSize = Vector2::ZERO;
+ LayoutTextData data =
+ {
+ "Layout text in a small area",
+ "Hello world",
+ textArea,
+ 1u,
+ fontDescriptionRuns.Begin(),
+ layoutSize,
+ 0u,
+ NULL,
+ 0u,
+ NULL,
+ LayoutEngine::MULTI_LINE_BOX,
+ 0u,
+ 11u,
+ false,
+ false
+ };
+
+ if( !LayoutTextTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextLayoutSmallTextArea02(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextLayoutSmallTextArea02");
+
+ // Will layout the text in single line as it can be scrolled.
+
+ const std::string fontFamily( "TizenSans" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun;
+ fontDescriptionRun.characterRun.characterIndex = 0u;
+ fontDescriptionRun.characterRun.numberOfCharacters = 11u;
+ fontDescriptionRun.familyLength = fontFamily.size();
+ fontDescriptionRun.familyName = new char[fontDescriptionRun.familyLength];
+ memcpy( fontDescriptionRun.familyName, fontFamily.c_str(), fontDescriptionRun.familyLength );
+ fontDescriptionRun.familyDefined = true;
+ fontDescriptionRun.weightDefined = false;
+ fontDescriptionRun.widthDefined = false;
+ fontDescriptionRun.slantDefined = false;
+ fontDescriptionRun.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun );
+ Size textArea(1.f, 1.f);
+ Size layoutSize(80.f, 20.f);
+ float positions[] = { 1.f, 3.f, 12.f, 6.f, 20.f, 2.f, 24.f, 2.f, 27.f, 6.f, 36.f, 15.f, 40.f, 6.f, 51.f, 6.f, 61.f, 6.f, 67.f, 2.f, 70.f, 2.f };
+ struct LineRun line =
+ {
+ { 0u, 11u },
+ { 0u, 11u },
+ 80.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ Vector<LineRun> lines;
+ lines.PushBack( line );
+
+ LayoutTextData data =
+ {
+ "Layout text in a small area",
+ "Hello world",
+ textArea,
+ 1u,
+ fontDescriptionRuns.Begin(),
+ layoutSize,
+ 11u,
+ positions,
+ 1u,
+ lines.Begin(),
+ LayoutEngine::SINGLE_LINE_BOX,
+ 0u,
+ 11u,
+ false,
+ true
+ };
+
+ if( !LayoutTextTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextLayoutMultilineText01(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextLayoutMultilineText01");
+
+ // Layout some lines of left to right text.
+
+ const std::string fontFamily( "TizenSans" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun1;
+ fontDescriptionRun1.characterRun.characterIndex = 0u;
+ fontDescriptionRun1.characterRun.numberOfCharacters = 18u;
+ fontDescriptionRun1.familyLength = fontFamily.size();
+ fontDescriptionRun1.familyName = new char[fontDescriptionRun1.familyLength];
+ memcpy( fontDescriptionRun1.familyName, fontFamily.c_str(), fontDescriptionRun1.familyLength );
+ fontDescriptionRun1.familyDefined = true;
+ fontDescriptionRun1.weightDefined = false;
+ fontDescriptionRun1.widthDefined = false;
+ fontDescriptionRun1.slantDefined = false;
+ fontDescriptionRun1.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun2;
+ fontDescriptionRun2.characterRun.characterIndex = 18u;
+ fontDescriptionRun2.characterRun.numberOfCharacters = 31u;
+ fontDescriptionRun2.familyLength = fontFamily.size();
+ fontDescriptionRun2.familyName = new char[fontDescriptionRun2.familyLength];
+ memcpy( fontDescriptionRun2.familyName, fontFamily.c_str(), fontDescriptionRun2.familyLength );
+ fontDescriptionRun2.familyDefined = true;
+ fontDescriptionRun2.weightDefined = false;
+ fontDescriptionRun2.widthDefined = false;
+ fontDescriptionRun2.slantDefined = false;
+ fontDescriptionRun2.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun1 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun2 );
+ Size textArea(100.f, 300.f);
+ Size layoutSize(95.f, 97.f);
+ float positions[] =
+ {
+ 1.f, 3.f, 12.f, 6.f, 20.f, 2.f, 24.f, 2.f, 27.f, 6.f, 36.f, 15.f, 40.f, 6.f, 51.f, 6.f, 61.f, 6.f, 67.f, 2.f, 70.f, 2.f, 79.f, 15.f,
+ 0.f, 22.f, 10.f, 26.f, 18.f, 26.f, 30.f, 26.f, 39.f, 32.f, 42.f, 23.f,
+ 1.f, 43.f, 9.f, 46.f, 17.f, 46.f, 27.f, 46.f, 36.f, 46.f, 45.f, 44.f, 51.f, 55.f,
+ 1.f, 62.f, 11.f, 62.f, 15.f, 62.f, 26.f, 65.f, 35.f, 65.f, 41.f, 65.f, 50.f, 65.f, 59.f, 63.f, 65.f, 74.f,
+ 1.f, 81.f, 5.f, 81.f, 9.f, 84.f, 19.f, 84.f, 28.f, 84.f, 35.f, 93.f, 41.f, 84.f, 50.f, 81.f, 54.f, 93.f, 59.f, 82.f, 66.f, 84.f, 75.f, 84.f, 83.f, 82.f, 91.f, 91.f
+ };
+ struct LineRun line0 =
+ {
+ { 0u, 12u },
+ { 0u, 12u },
+ 81.f,
+ 15.f,
+ -5.f,
+ 3.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line1 =
+ {
+ { 12u, 6u },
+ { 12u, 6u },
+ 43.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line2 =
+ {
+ { 18u, 7u },
+ { 18u, 7u },
+ 52.f,
+ 15.f,
+ -4.f,
+ 5.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line3 =
+ {
+ { 25u, 9u },
+ { 25u, 10u },
+ 66.f,
+ 15.f,
+ -4.f,
+ 5.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line4 =
+ {
+ { 34u, 14u },
+ { 35u, 14u },
+ 95.f,
+ 15.f,
+ -4.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ Vector<LineRun> lines;
+ lines.PushBack( line0 );
+ lines.PushBack( line1 );
+ lines.PushBack( line2 );
+ lines.PushBack( line3 );
+ lines.PushBack( line4 );
+
+ LayoutTextData data =
+ {
+ "Layout simple multi-line text",
+ "Hello world demo.\n"
+ "Layout different lines of text.",
+ textArea,
+ 1u,
+ fontDescriptionRuns.Begin(),
+ layoutSize,
+ 48u,
+ positions,
+ 5u,
+ lines.Begin(),
+ LayoutEngine::MULTI_LINE_BOX,
+ 0u,
+ 48u,
+ false,
+ true
+ };
+
+ if( !LayoutTextTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextLayoutMultilineText02(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextLayoutMultilineText02");
+
+ // Layout some lines of bidirectional text.
+
+ const std::string fontFamily1( "TizenSans" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun1;
+ fontDescriptionRun1.characterRun.characterIndex = 0u;
+ fontDescriptionRun1.characterRun.numberOfCharacters = 17u;
+ fontDescriptionRun1.familyLength = fontFamily1.size();
+ fontDescriptionRun1.familyName = new char[fontDescriptionRun1.familyLength];
+ memcpy( fontDescriptionRun1.familyName, fontFamily1.c_str(), fontDescriptionRun1.familyLength );
+ fontDescriptionRun1.familyDefined = true;
+ fontDescriptionRun1.weightDefined = false;
+ fontDescriptionRun1.widthDefined = false;
+ fontDescriptionRun1.slantDefined = false;
+ fontDescriptionRun1.sizeDefined = false;
+
+ const std::string fontFamily2( "TizenSansHebrew" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun2;
+ fontDescriptionRun2.characterRun.characterIndex = 17u;
+ fontDescriptionRun2.characterRun.numberOfCharacters = 11u;
+ fontDescriptionRun2.familyLength = fontFamily2.size();
+ fontDescriptionRun2.familyName = new char[fontDescriptionRun2.familyLength];
+ memcpy( fontDescriptionRun2.familyName, fontFamily2.c_str(), fontDescriptionRun2.familyLength );
+ fontDescriptionRun2.familyDefined = true;
+ fontDescriptionRun2.weightDefined = false;
+ fontDescriptionRun2.widthDefined = false;
+ fontDescriptionRun2.slantDefined = false;
+ fontDescriptionRun2.sizeDefined = false;
+
+ const std::string fontFamily3( "TizenSansHebrew" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun3;
+ fontDescriptionRun3.characterRun.characterIndex = 28u;
+ fontDescriptionRun3.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun3.familyLength = fontFamily3.size();
+ fontDescriptionRun3.familyName = new char[fontDescriptionRun3.familyLength];
+ memcpy( fontDescriptionRun3.familyName, fontFamily3.c_str(), fontDescriptionRun3.familyLength );
+ fontDescriptionRun3.familyDefined = true;
+ fontDescriptionRun3.weightDefined = false;
+ fontDescriptionRun3.widthDefined = false;
+ fontDescriptionRun3.slantDefined = false;
+ fontDescriptionRun3.sizeDefined = false;
+
+ const std::string fontFamily4( "TizenSans" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun4;
+ fontDescriptionRun4.characterRun.characterIndex = 38u;
+ fontDescriptionRun4.characterRun.numberOfCharacters = 17u;
+ fontDescriptionRun4.familyLength = fontFamily4.size();
+ fontDescriptionRun4.familyName = new char[fontDescriptionRun4.familyLength];
+ memcpy( fontDescriptionRun4.familyName, fontFamily4.c_str(), fontDescriptionRun4.familyLength );
+ fontDescriptionRun4.familyDefined = true;
+ fontDescriptionRun4.weightDefined = false;
+ fontDescriptionRun4.widthDefined = false;
+ fontDescriptionRun4.slantDefined = false;
+ fontDescriptionRun4.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun1 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun2 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun3 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun4 );
+ Size textArea(100.f, 300.f);
+ Size layoutSize(81.f, 120.f);
+ float positions[] =
+ {
+ 1.f, 3.f, 12.f, 6.f, 20.f, 2.f, 24.f, 2.f, 27.f, 6.f, 36.f, 15.f, 40.f, 6.f, 51.f, 6.f, 61.f, 6.f, 67.f, 2.f, 70.f, 2.f, 79.f, 15.f,
+ 0.f, 22.f, 10.f, 26.f, 18.f, 26.f, 30.f, 26.f, 39.f, 35.f, 44.f, 25.f, 55.f, 22.f, 62.f, 25.f, 67.f, 25.f, 75.f, 35.f,
+ 1.f, 45.f, 9.f, 45.f, 14.f, 42.f, 22.f, 45.f, 32.f, 53.f, 35.f, 44.f,
+ 1.f, 65.f, 12.f, 62.f, 19.f, 65.f, 24.f, 65.f, 32.f, 75.f, 37.f, 65.f, 45.f, 65.f, 50.f, 62.f, 58.f, 65.f, 66.f, 75.f,
+ 1.f, 82.f, 10.f, 86.f, 18.f, 82.f, 22.f, 82.f, 25.f, 86.f, 34.f, 95.f, 38.f, 86.f, 49.f, 86.f, 59.f, 86.f, 65.f, 82.f, 68.f, 82.f, 77.f, 95.f,
+ 0.f, 102.f, 10.f, 106.f, 18.f, 106.f, 30.f, 106.f, 39.f, 112.f
+ };
+ struct LineRun line0 =
+ {
+ { 0u, 12u },
+ { 0u, 12u },
+ 81.f,
+ 15.f,
+ -5.f,
+ 3.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line1 =
+ {
+ { 12u, 10u },
+ { 12u, 10u },
+ 76.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line2 =
+ {
+ { 22u, 6u },
+ { 22u, 6u },
+ 36.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line3 =
+ {
+ { 28u, 10u },
+ { 28u, 10u },
+ 67.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line4 =
+ {
+ { 38u, 12u },
+ { 38u, 12u },
+ 79.f,
+ 15.f,
+ -5.f,
+ 3.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line5 =
+ {
+ { 50u, 5u },
+ { 50u, 5u },
+ 43.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ Vector<LineRun> lines;
+ lines.PushBack( line0 );
+ lines.PushBack( line1 );
+ lines.PushBack( line2 );
+ lines.PushBack( line3 );
+ lines.PushBack( line4 );
+ lines.PushBack( line5 );
+
+ LayoutTextData data =
+ {
+ "Layout bidirectional text.",
+ "Hello world demo שלום עולם.\n"
+ "שלום עולם hello world demo.",
+ textArea,
+ 4u,
+ fontDescriptionRuns.Begin(),
+ layoutSize,
+ 55u,
+ positions,
+ 6u,
+ lines.Begin(),
+ LayoutEngine::MULTI_LINE_BOX,
+ 0u,
+ 55u,
+ false,
+ true
+ };
+
+ if( !LayoutTextTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextLayoutMultilineText03(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextLayoutMultilineText03");
+
+ // Layout a long word which doesn't fit in the width of the text area.
+
+ const std::string fontFamily( "TizenSans" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun;
+ fontDescriptionRun.characterRun.characterIndex = 0u;
+ fontDescriptionRun.characterRun.numberOfCharacters = 29u;
+ fontDescriptionRun.familyLength = fontFamily.size();
+ fontDescriptionRun.familyName = new char[fontDescriptionRun.familyLength];
+ memcpy( fontDescriptionRun.familyName, fontFamily.c_str(), fontDescriptionRun.familyLength );
+ fontDescriptionRun.familyDefined = true;
+ fontDescriptionRun.weightDefined = false;
+ fontDescriptionRun.widthDefined = false;
+ fontDescriptionRun.slantDefined = false;
+ fontDescriptionRun.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun );
+ Size textArea(100.f, 300.f);
+ Size layoutSize(96.f, 60.f);
+ float positions[] =
+ {
+ 1.f, 3.f, 12.f, 6.f, 20.f, 2.f, 24.f, 2.f, 27.f, 6.f, 36.f, 6.f, 47.f, 6.f, 57.f, 6.f, 63.f, 2.f, 66.f, 2.f, 75.f, 2.f, 85.f, 6.f,
+ 1.f, 26.f, 13.f, 26.f, 23.f, 22.f, 32.f, 26.f, 40.f, 22.f, 44.f, 22.f, 47.f, 26.f, 56.f, 26.f, 67.f, 26.f, 77.f, 26.f, 83.f, 22.f, 86.f, 22.f,
+ 0.f, 42.f, 10.f, 46.f, 18.f, 46.f, 30.f, 46.f, 39.f, 52.f
+ };
+ struct LineRun line0 =
+ {
+ { 0u, 12u },
+ { 0u, 12u },
+ 94.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line1 =
+ {
+ { 12u, 12u },
+ { 12u, 12u },
+ 96.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line2 =
+ {
+ { 24u, 5u },
+ { 24u, 5u },
+ 43.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ Vector<LineRun> lines;
+ lines.PushBack( line0 );
+ lines.PushBack( line1 );
+ lines.PushBack( line2 );
+
+ LayoutTextData data =
+ {
+ "Layout a long word which doesn't fit in the width of the text area.",
+ "Helloworlddemohelloworlddemo.",
+ textArea,
+ 1u,
+ fontDescriptionRuns.Begin(),
+ layoutSize,
+ 29u,
+ positions,
+ 3u,
+ lines.Begin(),
+ LayoutEngine::MULTI_LINE_BOX,
+ 0u,
+ 29u,
+ false,
+ true
+ };
+
+ if( !LayoutTextTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextLayoutMultilineText04(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextLayoutMultilineText04");
+
+ // Layout simple text ending with a \n. It has to add a void line at the end.
+
+ const std::string fontFamily( "TizenSans" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun;
+ fontDescriptionRun.characterRun.characterIndex = 0u;
+ fontDescriptionRun.characterRun.numberOfCharacters = 13u;
+ fontDescriptionRun.familyLength = fontFamily.size();
+ fontDescriptionRun.familyName = new char[fontDescriptionRun.familyLength];
+ memcpy( fontDescriptionRun.familyName, fontFamily.c_str(), fontDescriptionRun.familyLength );
+ fontDescriptionRun.familyDefined = true;
+ fontDescriptionRun.weightDefined = false;
+ fontDescriptionRun.widthDefined = false;
+ fontDescriptionRun.slantDefined = false;
+ fontDescriptionRun.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun );
+ Size textArea(100.f, 300.f);
+ Size layoutSize(83.f, 40.f);
+ float positions[] =
+ {
+ 1.f, 3.f, 12.f, 6.f, 20.f, 2.f, 24.f, 2.f, 27.f, 6.f, 36.f, 15.f, 40.f, 6.f, 51.f, 6.f, 61.f, 6.f, 67.f, 2.f, 70.f, 2.f, 79.f, 12.f, 82.f, 3.f
+ };
+ struct LineRun line0 =
+ {
+ { 0u, 13u },
+ { 0u, 13u },
+ 83.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line1 =
+ {
+ { 13u, 0u },
+ { 13u, 0u },
+ 0.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ Vector<LineRun> lines;
+ lines.PushBack( line0 );
+ lines.PushBack( line1 );
+
+ LayoutTextData data =
+ {
+ "Layout simple text ending with a \n.",
+ "Hello world.\n",
+ textArea,
+ 1u,
+ fontDescriptionRuns.Begin(),
+ layoutSize,
+ 13u,
+ positions,
+ 2u,
+ lines.Begin(),
+ LayoutEngine::MULTI_LINE_BOX,
+ 0u,
+ 13u,
+ false,
+ true
+ };
+
+ if( !LayoutTextTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextLayoutMultilineText05(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextLayoutMultilineText05");
+
+ // Layout simple text with one character with a different font size.
+
+ const std::string fontFamily( "TizenSans" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun1;
+ fontDescriptionRun1.characterRun.characterIndex = 0u;
+ fontDescriptionRun1.characterRun.numberOfCharacters = 6u;
+ fontDescriptionRun1.familyLength = fontFamily.size();
+ fontDescriptionRun1.familyName = new char[fontDescriptionRun1.familyLength];
+ memcpy( fontDescriptionRun1.familyName, fontFamily.c_str(), fontDescriptionRun1.familyLength );
+ fontDescriptionRun1.familyDefined = true;
+ fontDescriptionRun1.weightDefined = false;
+ fontDescriptionRun1.widthDefined = false;
+ fontDescriptionRun1.slantDefined = false;
+ fontDescriptionRun1.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun2;
+ fontDescriptionRun2.characterRun.characterIndex = 6u;
+ fontDescriptionRun2.characterRun.numberOfCharacters = 1u;
+ fontDescriptionRun2.familyLength = fontFamily.size();
+ fontDescriptionRun2.familyName = new char[fontDescriptionRun2.familyLength];
+ memcpy( fontDescriptionRun2.familyName, fontFamily.c_str(), fontDescriptionRun2.familyLength );
+ fontDescriptionRun2.size = 1280u;
+ fontDescriptionRun2.familyDefined = true;
+ fontDescriptionRun2.weightDefined = false;
+ fontDescriptionRun2.widthDefined = false;
+ fontDescriptionRun2.slantDefined = false;
+ fontDescriptionRun2.sizeDefined = true;
+
+ FontDescriptionRun fontDescriptionRun3;
+ fontDescriptionRun3.characterRun.characterIndex = 7u;
+ fontDescriptionRun3.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun3.familyLength = fontFamily.size();
+ fontDescriptionRun3.familyName = new char[fontDescriptionRun3.familyLength];
+ memcpy( fontDescriptionRun3.familyName, fontFamily.c_str(), fontDescriptionRun3.familyLength );
+ fontDescriptionRun3.familyDefined = true;
+ fontDescriptionRun3.weightDefined = false;
+ fontDescriptionRun3.widthDefined = false;
+ fontDescriptionRun3.slantDefined = false;
+ fontDescriptionRun3.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun1 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun2 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun3 );
+ Size textArea(100.f, 300.f);
+ Size layoutSize(88.f, 53.f);
+ float positions[] =
+ {
+ 1.f, 13.f, 12.f, 16.f, 20.f, 12.f, 24.f, 12.f, 27.f, 16.f, 36.f, 25.f, 40.f, 11.f, 59.f, 16.f, 69.f, 16.f, 75.f, 12.f, 78.f, 12.f, 87.f, 25.f,
+ 0.f, 35.f, 10.f, 39.f, 18.f, 39.f, 30.f, 39.f, 39.f, 45.f
+ };
+ struct LineRun line0 =
+ {
+ { 0u, 12u },
+ { 0u, 12u },
+ 88.f,
+ 25.f,
+ -8.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line1 =
+ {
+ { 12u, 5u },
+ { 12u, 5u },
+ 43.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ Vector<LineRun> lines;
+ lines.PushBack( line0 );
+ lines.PushBack( line1 );
+
+ LayoutTextData data =
+ {
+ "Layout simple text with one character with a different font size.",
+ "Hello world demo.",
+ textArea,
+ 3u,
+ fontDescriptionRuns.Begin(),
+ layoutSize,
+ 17u,
+ positions,
+ 2u,
+ lines.Begin(),
+ LayoutEngine::MULTI_LINE_BOX,
+ 0u,
+ 17u,
+ false,
+ true
+ };
+
+ if( !LayoutTextTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextUpdateLayout01(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextUpdateLayout01");
+
+ // Layout some lines of bidirectional text. Update the paragraphs at the beginning.
+
+ const std::string fontLatin( "TizenSans" );
+ const std::string fontHebrew( "TizenSansHebrew" );
+ const std::string fontArabic( "TizenSansArabic" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun01;
+ fontDescriptionRun01.characterRun.characterIndex = 0u;
+ fontDescriptionRun01.characterRun.numberOfCharacters = 17u;
+ fontDescriptionRun01.familyLength = fontLatin.size();
+ fontDescriptionRun01.familyName = new char[fontDescriptionRun01.familyLength];
+ memcpy( fontDescriptionRun01.familyName, fontLatin.c_str(), fontDescriptionRun01.familyLength );
+ fontDescriptionRun01.familyDefined = true;
+ fontDescriptionRun01.weightDefined = false;
+ fontDescriptionRun01.widthDefined = false;
+ fontDescriptionRun01.slantDefined = false;
+ fontDescriptionRun01.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun02;
+ fontDescriptionRun02.characterRun.characterIndex = 17u;
+ fontDescriptionRun02.characterRun.numberOfCharacters = 11u;
+ fontDescriptionRun02.familyLength = fontHebrew.size();
+ fontDescriptionRun02.familyName = new char[fontDescriptionRun02.familyLength];
+ memcpy( fontDescriptionRun02.familyName, fontHebrew.c_str(), fontDescriptionRun02.familyLength );
+ fontDescriptionRun02.familyDefined = true;
+ fontDescriptionRun02.weightDefined = false;
+ fontDescriptionRun02.widthDefined = false;
+ fontDescriptionRun02.slantDefined = false;
+ fontDescriptionRun02.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun03;
+ fontDescriptionRun03.characterRun.characterIndex = 28u;
+ fontDescriptionRun03.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun03.familyLength = fontArabic.size();
+ fontDescriptionRun03.familyName = new char[fontDescriptionRun03.familyLength];
+ memcpy( fontDescriptionRun03.familyName, fontArabic.c_str(), fontDescriptionRun03.familyLength );
+ fontDescriptionRun03.familyDefined = true;
+ fontDescriptionRun03.weightDefined = false;
+ fontDescriptionRun03.widthDefined = false;
+ fontDescriptionRun03.slantDefined = false;
+ fontDescriptionRun03.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun04;
+ fontDescriptionRun04.characterRun.characterIndex = 42u;
+ fontDescriptionRun04.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun04.familyLength = fontLatin.size();
+ fontDescriptionRun04.familyName = new char[fontDescriptionRun04.familyLength];
+ memcpy( fontDescriptionRun04.familyName, fontLatin.c_str(), fontDescriptionRun04.familyLength );
+ fontDescriptionRun04.familyDefined = true;
+ fontDescriptionRun04.weightDefined = false;
+ fontDescriptionRun04.widthDefined = false;
+ fontDescriptionRun04.slantDefined = false;
+ fontDescriptionRun04.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun05;
+ fontDescriptionRun05.characterRun.characterIndex = 54u;
+ fontDescriptionRun05.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun05.familyLength = fontHebrew.size();
+ fontDescriptionRun05.familyName = new char[fontDescriptionRun05.familyLength];
+ memcpy( fontDescriptionRun05.familyName, fontHebrew.c_str(), fontDescriptionRun05.familyLength );
+ fontDescriptionRun05.familyDefined = true;
+ fontDescriptionRun05.weightDefined = false;
+ fontDescriptionRun05.widthDefined = false;
+ fontDescriptionRun05.slantDefined = false;
+ fontDescriptionRun05.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun06;
+ fontDescriptionRun06.characterRun.characterIndex = 64u;
+ fontDescriptionRun06.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun06.familyLength = fontHebrew.size();
+ fontDescriptionRun06.familyName = new char[fontDescriptionRun06.familyLength];
+ memcpy( fontDescriptionRun06.familyName, fontHebrew.c_str(), fontDescriptionRun06.familyLength );
+ fontDescriptionRun06.familyDefined = true;
+ fontDescriptionRun06.weightDefined = false;
+ fontDescriptionRun06.widthDefined = false;
+ fontDescriptionRun06.slantDefined = false;
+ fontDescriptionRun06.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun07;
+ fontDescriptionRun07.characterRun.characterIndex = 74u;
+ fontDescriptionRun07.characterRun.numberOfCharacters = 18u;
+ fontDescriptionRun07.familyLength = fontLatin.size();
+ fontDescriptionRun07.familyName = new char[fontDescriptionRun07.familyLength];
+ memcpy( fontDescriptionRun07.familyName, fontLatin.c_str(), fontDescriptionRun07.familyLength );
+ fontDescriptionRun07.familyDefined = true;
+ fontDescriptionRun07.weightDefined = false;
+ fontDescriptionRun07.widthDefined = false;
+ fontDescriptionRun07.slantDefined = false;
+ fontDescriptionRun07.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun08;
+ fontDescriptionRun08.characterRun.characterIndex = 92u;
+ fontDescriptionRun08.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun08.familyLength = fontLatin.size();
+ fontDescriptionRun08.familyName = new char[fontDescriptionRun08.familyLength];
+ memcpy( fontDescriptionRun08.familyName, fontLatin.c_str(), fontDescriptionRun08.familyLength );
+ fontDescriptionRun08.familyDefined = true;
+ fontDescriptionRun08.weightDefined = false;
+ fontDescriptionRun08.widthDefined = false;
+ fontDescriptionRun08.slantDefined = false;
+ fontDescriptionRun08.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun09;
+ fontDescriptionRun09.characterRun.characterIndex = 104u;
+ fontDescriptionRun09.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun09.familyLength = fontArabic.size();
+ fontDescriptionRun09.familyName = new char[fontDescriptionRun09.familyLength];
+ memcpy( fontDescriptionRun09.familyName, fontArabic.c_str(), fontDescriptionRun09.familyLength );
+ fontDescriptionRun09.familyDefined = true;
+ fontDescriptionRun09.weightDefined = false;
+ fontDescriptionRun09.widthDefined = false;
+ fontDescriptionRun09.slantDefined = false;
+ fontDescriptionRun09.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun10;
+ fontDescriptionRun10.characterRun.characterIndex = 118u;
+ fontDescriptionRun10.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun10.familyLength = fontHebrew.size();
+ fontDescriptionRun10.familyName = new char[fontDescriptionRun10.familyLength];
+ memcpy( fontDescriptionRun10.familyName, fontHebrew.c_str(), fontDescriptionRun10.familyLength );
+ fontDescriptionRun10.familyDefined = true;
+ fontDescriptionRun10.weightDefined = false;
+ fontDescriptionRun10.widthDefined = false;
+ fontDescriptionRun10.slantDefined = false;
+ fontDescriptionRun10.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun11;
+ fontDescriptionRun11.characterRun.characterIndex = 128u;
+ fontDescriptionRun11.characterRun.numberOfCharacters = 17u;
+ fontDescriptionRun11.familyLength = fontLatin.size();
+ fontDescriptionRun11.familyName = new char[fontDescriptionRun11.familyLength];
+ memcpy( fontDescriptionRun11.familyName, fontLatin.c_str(), fontDescriptionRun11.familyLength );
+ fontDescriptionRun11.familyDefined = true;
+ fontDescriptionRun11.weightDefined = false;
+ fontDescriptionRun11.widthDefined = false;
+ fontDescriptionRun11.slantDefined = false;
+ fontDescriptionRun11.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun12;
+ fontDescriptionRun12.characterRun.characterIndex = 145u;
+ fontDescriptionRun12.characterRun.numberOfCharacters = 11u;
+ fontDescriptionRun12.familyLength = fontHebrew.size();
+ fontDescriptionRun12.familyName = new char[fontDescriptionRun12.familyLength];
+ memcpy( fontDescriptionRun12.familyName, fontHebrew.c_str(), fontDescriptionRun12.familyLength );
+ fontDescriptionRun12.familyDefined = true;
+ fontDescriptionRun12.weightDefined = false;
+ fontDescriptionRun12.widthDefined = false;
+ fontDescriptionRun12.slantDefined = false;
+ fontDescriptionRun12.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun13;
+ fontDescriptionRun13.characterRun.characterIndex = 156u;
+ fontDescriptionRun13.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun13.familyLength = fontHebrew.size();
+ fontDescriptionRun13.familyName = new char[fontDescriptionRun13.familyLength];
+ memcpy( fontDescriptionRun13.familyName, fontHebrew.c_str(), fontDescriptionRun13.familyLength );
+ fontDescriptionRun13.familyDefined = true;
+ fontDescriptionRun13.weightDefined = false;
+ fontDescriptionRun13.widthDefined = false;
+ fontDescriptionRun13.slantDefined = false;
+ fontDescriptionRun13.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun14;
+ fontDescriptionRun14.characterRun.characterIndex = 166u;
+ fontDescriptionRun14.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun14.familyLength = fontLatin.size();
+ fontDescriptionRun14.familyName = new char[fontDescriptionRun14.familyLength];
+ memcpy( fontDescriptionRun14.familyName, fontLatin.c_str(), fontDescriptionRun14.familyLength );
+ fontDescriptionRun14.familyDefined = true;
+ fontDescriptionRun14.weightDefined = false;
+ fontDescriptionRun14.widthDefined = false;
+ fontDescriptionRun14.slantDefined = false;
+ fontDescriptionRun14.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun15;
+ fontDescriptionRun15.characterRun.characterIndex = 178u;
+ fontDescriptionRun15.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun15.familyLength = fontArabic.size();
+ fontDescriptionRun15.familyName = new char[fontDescriptionRun15.familyLength];
+ memcpy( fontDescriptionRun15.familyName, fontArabic.c_str(), fontDescriptionRun15.familyLength );
+ fontDescriptionRun15.familyDefined = true;
+ fontDescriptionRun15.weightDefined = false;
+ fontDescriptionRun15.widthDefined = false;
+ fontDescriptionRun15.slantDefined = false;
+ fontDescriptionRun15.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun01 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun02 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun03 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun04 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun05 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun06 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun07 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun08 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun09 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun10 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun11 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun12 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun13 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun14 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun15 );
+ Size textArea(100.f, 300.f);
+ Size layoutSize(92.f, 380.f);
+ float positions[] =
+ {
+ 1.f, 3.f, 12.f, 6.f, 20.f, 2.f, 24.f, 2.f, 27.f, 6.f, 36.f, 15.f, 40.f, 6.f, 51.f, 6.f, 61.f, 6.f, 67.f, 2.f, 70.f, 2.f, 79.f, 15.f,
+ 0.f, 22.f, 10.f, 26.f, 18.f, 26.f, 30.f, 26.f, 39.f, 35.f, 44.f, 25.f, 55.f, 22.f, 62.f, 25.f, 67.f, 25.f, 75.f, 35.f,
+ 1.f, 45.f, 9.f, 45.f, 14.f, 42.f, 22.f, 45.f, 32.f, 53.f, 35.f, 44.f,
+ 0.f, 67.f, 7.f, 69.f, 12.f, 68.f, 18.f, 68.f, 23.f, 64.f, 25.f, 75.f, 27.f, 68.f, 32.f, 64.f, 33.f, 64.f, 37.f, 67.f, 44.f, 64.f, 45.f, 64.f, 49.f, 67.f, 55.f, 75.f, 59.f, 62.f, 68.f, 66.f, 76.f, 62.f, 80.f, 62.f, 83.f, 66.f, 92.f, 75.f,
+ 0.f, 86.f, 11.f, 86.f, 21.f, 86.f, 27.f, 82.f, 30.f, 82.f, 39.f, 95.f, 44.f, 85.f, 55.f, 82.f, 62.f, 85.f, 67.f, 85.f, 75.f, 95.f,
+ 1.f, 105.f, 9.f, 105.f, 14.f, 102.f, 22.f, 105.f, 30.f, 115.f,
+ 1.f, 125.f, 12.f, 122.f, 19.f, 125.f, 24.f, 125.f, 32.f, 135.f, 37.f, 125.f, 45.f, 125.f, 50.f, 122.f, 58.f, 125.f, 66.f, 135.f,
+ 1.f, 142.f, 10.f, 146.f, 18.f, 142.f, 22.f, 142.f, 25.f, 146.f, 34.f, 155.f, 38.f, 146.f, 49.f, 146.f, 59.f, 146.f, 65.f, 142.f, 68.f, 142.f, 77.f, 155.f,
+ 0.f, 162.f, 10.f, 166.f, 18.f, 166.f, 30.f, 166.f, 39.f, 172.f, 42.f, 163.f,
+ 1.f, 182.f, 10.f, 186.f, 18.f, 182.f, 22.f, 182.f, 25.f, 186.f, 34.f, 195.f, 38.f, 186.f, 49.f, 186.f, 59.f, 186.f, 65.f, 182.f, 68.f, 182.f, 77.f, 195.f,
+ 0.f, 207.f, 7.f, 209.f, 12.f, 208.f, 18.f, 208.f, 23.f, 204.f, 25.f, 215.f, 27.f, 208.f, 32.f, 204.f, 33.f, 204.f, 37.f, 207.f, 44.f, 204.f, 45.f, 204.f, 49.f, 207.f, 55.f, 215.f, 59.f, 205.f, 70.f, 202.f, 77.f, 205.f, 82.f, 205.f, 90.f, 215.f,
+ 1.f, 225.f, 9.f, 225.f, 14.f, 222.f, 22.f, 225.f, 30.f, 235.f,
+ 1.f, 243.f, 12.f, 246.f, 20.f, 242.f, 24.f, 242.f, 27.f, 246.f, 36.f, 255.f, 40.f, 246.f, 51.f, 246.f, 61.f, 246.f, 67.f, 242.f, 70.f, 242.f, 79.f, 255.f,
+ 0.f, 262.f, 10.f, 266.f, 18.f, 266.f, 30.f, 266.f, 39.f, 275.f, 44.f, 265.f, 55.f, 262.f, 62.f, 265.f, 67.f, 265.f, 75.f, 275.f,
+ 1.f, 285.f, 9.f, 285.f, 14.f, 282.f, 22.f, 285.f, 32.f, 293.f, 35.f, 284.f,
+ 1.f, 305.f, 12.f, 302.f, 19.f, 305.f, 24.f, 305.f, 32.f, 315.f, 37.f, 305.f, 45.f, 305.f, 50.f, 302.f, 58.f, 305.f, 66.f, 315.f,
+ 1.f, 322.f, 10.f, 326.f, 18.f, 322.f, 22.f, 322.f, 25.f, 326.f, 34.f, 335.f, 38.f, 326.f, 49.f, 326.f, 59.f, 326.f, 65.f, 322.f, 68.f, 322.f, 77.f, 335.f,
+ 0.f, 347.f, 7.f, 349.f, 12.f, 348.f, 18.f, 348.f, 23.f, 344.f, 25.f, 355.f, 27.f, 348.f, 32.f, 344.f, 33.f, 344.f, 37.f, 347.f, 44.f, 344.f, 45.f, 344.f, 49.f, 347.f, 55.f, 355.f,
+ };
+ struct LineRun line01 =
+ {
+ { 0u, 12u },
+ { 0u, 12u },
+ 81.f,
+ 15.f,
+ -5.f,
+ 3.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line02 =
+ {
+ { 12u, 10u },
+ { 12u, 10u },
+ 76.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line03 =
+ {
+ { 22u, 6u },
+ { 22u, 6u },
+ 36.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line04 =
+ {
+ { 28u, 20u },
+ { 28u, 20u },
+ 92.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line05 =
+ {
+ { 48u, 11u },
+ { 48u, 11u },
+ 76.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line06 =
+ {
+ { 59u, 5u },
+ { 59u, 5u },
+ 31.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line07 =
+ {
+ { 64u, 10u },
+ { 64u, 10u },
+ 67.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line08 =
+ {
+ { 74u, 12u },
+ { 74u, 12u },
+ 79.f,
+ 15.f,
+ -5.f,
+ 3.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line09 =
+ {
+ { 86u, 6u },
+ { 86u, 6u },
+ 43.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line10 =
+ {
+ { 92u, 12u },
+ { 92u, 12u },
+ 78.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line11 =
+ {
+ { 104u, 19u },
+ { 104u, 19u },
+ 90.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line12 =
+ {
+ { 123u, 5u },
+ { 123u, 5u },
+ 31.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line13 =
+ {
+ { 128u, 12u },
+ { 128u, 12u },
+ 81.f,
+ 15.f,
+ -5.f,
+ 3.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line14 =
+ {
+ { 140u, 10u },
+ { 140u, 10u },
+ 76.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line15 =
+ {
+ { 150u, 6u },
+ { 150u, 6u },
+ 36.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line16 =
+ {
+ { 156u, 10u },
+ { 156u, 10u },
+ 67.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line17 =
+ {
+ { 166u, 12u },
+ { 166u, 12u },
+ 79.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line18 =
+ {
+ { 178u, 14u },
+ { 178u, 14u },
+ 55.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line19 =
+ {
+ { 192u, 0u },
+ { 192u, 0u },
+ 0.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ Vector<LineRun> lines;
+ lines.PushBack( line01 );
+ lines.PushBack( line02 );
+ lines.PushBack( line03 );
+ lines.PushBack( line04 );
+ lines.PushBack( line05 );
+ lines.PushBack( line06 );
+ lines.PushBack( line07 );
+ lines.PushBack( line08 );
+ lines.PushBack( line09 );
+ lines.PushBack( line10 );
+ lines.PushBack( line11 );
+ lines.PushBack( line12 );
+ lines.PushBack( line13 );
+ lines.PushBack( line14 );
+ lines.PushBack( line15 );
+ lines.PushBack( line16 );
+ lines.PushBack( line17 );
+ lines.PushBack( line18 );
+ lines.PushBack( line19 );
+
+ LayoutTextData data =
+ {
+ "Layout bidirectional text.",
+ "Hello world demo שלום עולם.\n"
+ "مرحبا بالعالم hello world שלום עולם\n"
+ "שלום עולם hello world demo.\n"
+ "hello world مرحبا بالعالم שלום עולם\n"
+ "Hello world demo שלום עולם.\n"
+ "שלום עולם hello world مرحبا بالعالم\n",
+ textArea,
+ 15u,
+ fontDescriptionRuns.Begin(),
+ layoutSize,
+ 192u,
+ positions,
+ 19u,
+ lines.Begin(),
+ LayoutEngine::MULTI_LINE_BOX,
+ 0u,
+ 64u,
+ false,
+ true
+ };
+
+ if( !LayoutTextTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextUpdateLayout02(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextUpdateLayout02");
+
+ // Layout some lines of bidirectional text. Update the paragraphs at the middle.
+
+ const std::string fontLatin( "TizenSans" );
+ const std::string fontHebrew( "TizenSansHebrew" );
+ const std::string fontArabic( "TizenSansArabic" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun01;
+ fontDescriptionRun01.characterRun.characterIndex = 0u;
+ fontDescriptionRun01.characterRun.numberOfCharacters = 17u;
+ fontDescriptionRun01.familyLength = fontLatin.size();
+ fontDescriptionRun01.familyName = new char[fontDescriptionRun01.familyLength];
+ memcpy( fontDescriptionRun01.familyName, fontLatin.c_str(), fontDescriptionRun01.familyLength );
+ fontDescriptionRun01.familyDefined = true;
+ fontDescriptionRun01.weightDefined = false;
+ fontDescriptionRun01.widthDefined = false;
+ fontDescriptionRun01.slantDefined = false;
+ fontDescriptionRun01.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun02;
+ fontDescriptionRun02.characterRun.characterIndex = 17u;
+ fontDescriptionRun02.characterRun.numberOfCharacters = 11u;
+ fontDescriptionRun02.familyLength = fontHebrew.size();
+ fontDescriptionRun02.familyName = new char[fontDescriptionRun02.familyLength];
+ memcpy( fontDescriptionRun02.familyName, fontHebrew.c_str(), fontDescriptionRun02.familyLength );
+ fontDescriptionRun02.familyDefined = true;
+ fontDescriptionRun02.weightDefined = false;
+ fontDescriptionRun02.widthDefined = false;
+ fontDescriptionRun02.slantDefined = false;
+ fontDescriptionRun02.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun03;
+ fontDescriptionRun03.characterRun.characterIndex = 28u;
+ fontDescriptionRun03.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun03.familyLength = fontArabic.size();
+ fontDescriptionRun03.familyName = new char[fontDescriptionRun03.familyLength];
+ memcpy( fontDescriptionRun03.familyName, fontArabic.c_str(), fontDescriptionRun03.familyLength );
+ fontDescriptionRun03.familyDefined = true;
+ fontDescriptionRun03.weightDefined = false;
+ fontDescriptionRun03.widthDefined = false;
+ fontDescriptionRun03.slantDefined = false;
+ fontDescriptionRun03.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun04;
+ fontDescriptionRun04.characterRun.characterIndex = 42u;
+ fontDescriptionRun04.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun04.familyLength = fontLatin.size();
+ fontDescriptionRun04.familyName = new char[fontDescriptionRun04.familyLength];
+ memcpy( fontDescriptionRun04.familyName, fontLatin.c_str(), fontDescriptionRun04.familyLength );
+ fontDescriptionRun04.familyDefined = true;
+ fontDescriptionRun04.weightDefined = false;
+ fontDescriptionRun04.widthDefined = false;
+ fontDescriptionRun04.slantDefined = false;
+ fontDescriptionRun04.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun05;
+ fontDescriptionRun05.characterRun.characterIndex = 54u;
+ fontDescriptionRun05.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun05.familyLength = fontHebrew.size();
+ fontDescriptionRun05.familyName = new char[fontDescriptionRun05.familyLength];
+ memcpy( fontDescriptionRun05.familyName, fontHebrew.c_str(), fontDescriptionRun05.familyLength );
+ fontDescriptionRun05.familyDefined = true;
+ fontDescriptionRun05.weightDefined = false;
+ fontDescriptionRun05.widthDefined = false;
+ fontDescriptionRun05.slantDefined = false;
+ fontDescriptionRun05.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun06;
+ fontDescriptionRun06.characterRun.characterIndex = 64u;
+ fontDescriptionRun06.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun06.familyLength = fontHebrew.size();
+ fontDescriptionRun06.familyName = new char[fontDescriptionRun06.familyLength];
+ memcpy( fontDescriptionRun06.familyName, fontHebrew.c_str(), fontDescriptionRun06.familyLength );
+ fontDescriptionRun06.familyDefined = true;
+ fontDescriptionRun06.weightDefined = false;
+ fontDescriptionRun06.widthDefined = false;
+ fontDescriptionRun06.slantDefined = false;
+ fontDescriptionRun06.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun07;
+ fontDescriptionRun07.characterRun.characterIndex = 74u;
+ fontDescriptionRun07.characterRun.numberOfCharacters = 18u;
+ fontDescriptionRun07.familyLength = fontLatin.size();
+ fontDescriptionRun07.familyName = new char[fontDescriptionRun07.familyLength];
+ memcpy( fontDescriptionRun07.familyName, fontLatin.c_str(), fontDescriptionRun07.familyLength );
+ fontDescriptionRun07.familyDefined = true;
+ fontDescriptionRun07.weightDefined = false;
+ fontDescriptionRun07.widthDefined = false;
+ fontDescriptionRun07.slantDefined = false;
+ fontDescriptionRun07.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun08;
+ fontDescriptionRun08.characterRun.characterIndex = 92u;
+ fontDescriptionRun08.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun08.familyLength = fontLatin.size();
+ fontDescriptionRun08.familyName = new char[fontDescriptionRun08.familyLength];
+ memcpy( fontDescriptionRun08.familyName, fontLatin.c_str(), fontDescriptionRun08.familyLength );
+ fontDescriptionRun08.familyDefined = true;
+ fontDescriptionRun08.weightDefined = false;
+ fontDescriptionRun08.widthDefined = false;
+ fontDescriptionRun08.slantDefined = false;
+ fontDescriptionRun08.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun09;
+ fontDescriptionRun09.characterRun.characterIndex = 104u;
+ fontDescriptionRun09.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun09.familyLength = fontArabic.size();
+ fontDescriptionRun09.familyName = new char[fontDescriptionRun09.familyLength];
+ memcpy( fontDescriptionRun09.familyName, fontArabic.c_str(), fontDescriptionRun09.familyLength );
+ fontDescriptionRun09.familyDefined = true;
+ fontDescriptionRun09.weightDefined = false;
+ fontDescriptionRun09.widthDefined = false;
+ fontDescriptionRun09.slantDefined = false;
+ fontDescriptionRun09.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun10;
+ fontDescriptionRun10.characterRun.characterIndex = 118u;
+ fontDescriptionRun10.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun10.familyLength = fontHebrew.size();
+ fontDescriptionRun10.familyName = new char[fontDescriptionRun10.familyLength];
+ memcpy( fontDescriptionRun10.familyName, fontHebrew.c_str(), fontDescriptionRun10.familyLength );
+ fontDescriptionRun10.familyDefined = true;
+ fontDescriptionRun10.weightDefined = false;
+ fontDescriptionRun10.widthDefined = false;
+ fontDescriptionRun10.slantDefined = false;
+ fontDescriptionRun10.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun11;
+ fontDescriptionRun11.characterRun.characterIndex = 128u;
+ fontDescriptionRun11.characterRun.numberOfCharacters = 17u;
+ fontDescriptionRun11.familyLength = fontLatin.size();
+ fontDescriptionRun11.familyName = new char[fontDescriptionRun11.familyLength];
+ memcpy( fontDescriptionRun11.familyName, fontLatin.c_str(), fontDescriptionRun11.familyLength );
+ fontDescriptionRun11.familyDefined = true;
+ fontDescriptionRun11.weightDefined = false;
+ fontDescriptionRun11.widthDefined = false;
+ fontDescriptionRun11.slantDefined = false;
+ fontDescriptionRun11.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun12;
+ fontDescriptionRun12.characterRun.characterIndex = 145u;
+ fontDescriptionRun12.characterRun.numberOfCharacters = 11u;
+ fontDescriptionRun12.familyLength = fontHebrew.size();
+ fontDescriptionRun12.familyName = new char[fontDescriptionRun12.familyLength];
+ memcpy( fontDescriptionRun12.familyName, fontHebrew.c_str(), fontDescriptionRun12.familyLength );
+ fontDescriptionRun12.familyDefined = true;
+ fontDescriptionRun12.weightDefined = false;
+ fontDescriptionRun12.widthDefined = false;
+ fontDescriptionRun12.slantDefined = false;
+ fontDescriptionRun12.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun13;
+ fontDescriptionRun13.characterRun.characterIndex = 156u;
+ fontDescriptionRun13.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun13.familyLength = fontHebrew.size();
+ fontDescriptionRun13.familyName = new char[fontDescriptionRun13.familyLength];
+ memcpy( fontDescriptionRun13.familyName, fontHebrew.c_str(), fontDescriptionRun13.familyLength );
+ fontDescriptionRun13.familyDefined = true;
+ fontDescriptionRun13.weightDefined = false;
+ fontDescriptionRun13.widthDefined = false;
+ fontDescriptionRun13.slantDefined = false;
+ fontDescriptionRun13.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun14;
+ fontDescriptionRun14.characterRun.characterIndex = 166u;
+ fontDescriptionRun14.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun14.familyLength = fontLatin.size();
+ fontDescriptionRun14.familyName = new char[fontDescriptionRun14.familyLength];
+ memcpy( fontDescriptionRun14.familyName, fontLatin.c_str(), fontDescriptionRun14.familyLength );
+ fontDescriptionRun14.familyDefined = true;
+ fontDescriptionRun14.weightDefined = false;
+ fontDescriptionRun14.widthDefined = false;
+ fontDescriptionRun14.slantDefined = false;
+ fontDescriptionRun14.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun15;
+ fontDescriptionRun15.characterRun.characterIndex = 178u;
+ fontDescriptionRun15.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun15.familyLength = fontArabic.size();
+ fontDescriptionRun15.familyName = new char[fontDescriptionRun15.familyLength];
+ memcpy( fontDescriptionRun15.familyName, fontArabic.c_str(), fontDescriptionRun15.familyLength );
+ fontDescriptionRun15.familyDefined = true;
+ fontDescriptionRun15.weightDefined = false;
+ fontDescriptionRun15.widthDefined = false;
+ fontDescriptionRun15.slantDefined = false;
+ fontDescriptionRun15.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun01 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun02 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun03 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun04 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun05 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun06 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun07 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun08 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun09 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun10 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun11 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun12 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun13 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun14 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun15 );
+ Size textArea(100.f, 300.f);
+ Size layoutSize(92.f, 380.f);
+ float positions[] =
+ {
+ 1.f, 3.f, 12.f, 6.f, 20.f, 2.f, 24.f, 2.f, 27.f, 6.f, 36.f, 15.f, 40.f, 6.f, 51.f, 6.f, 61.f, 6.f, 67.f, 2.f, 70.f, 2.f, 79.f, 15.f,
+ 0.f, 22.f, 10.f, 26.f, 18.f, 26.f, 30.f, 26.f, 39.f, 35.f, 44.f, 25.f, 55.f, 22.f, 62.f, 25.f, 67.f, 25.f, 75.f, 35.f,
+ 1.f, 45.f, 9.f, 45.f, 14.f, 42.f, 22.f, 45.f, 32.f, 53.f, 35.f, 44.f,
+ 0.f, 67.f, 7.f, 69.f, 12.f, 68.f, 18.f, 68.f, 23.f, 64.f, 25.f, 75.f, 27.f, 68.f, 32.f, 64.f, 33.f, 64.f, 37.f, 67.f, 44.f, 64.f, 45.f, 64.f, 49.f, 67.f, 55.f, 75.f, 59.f, 62.f, 68.f, 66.f, 76.f, 62.f, 80.f, 62.f, 83.f, 66.f, 92.f, 75.f,
+ 0.f, 86.f, 11.f, 86.f, 21.f, 86.f, 27.f, 82.f, 30.f, 82.f, 39.f, 95.f, 44.f, 85.f, 55.f, 82.f, 62.f, 85.f, 67.f, 85.f, 75.f, 95.f,
+ 1.f, 105.f, 9.f, 105.f, 14.f, 102.f, 22.f, 105.f, 30.f, 115.f,
+ 1.f, 125.f, 12.f, 122.f, 19.f, 125.f, 24.f, 125.f, 32.f, 135.f, 37.f, 125.f, 45.f, 125.f, 50.f, 122.f, 58.f, 125.f, 66.f, 135.f,
+ 1.f, 142.f, 10.f, 146.f, 18.f, 142.f, 22.f, 142.f, 25.f, 146.f, 34.f, 155.f, 38.f, 146.f, 49.f, 146.f, 59.f, 146.f, 65.f, 142.f, 68.f, 142.f, 77.f, 155.f,
+ 0.f, 162.f, 10.f, 166.f, 18.f, 166.f, 30.f, 166.f, 39.f, 172.f, 42.f, 163.f,
+ 1.f, 182.f, 10.f, 186.f, 18.f, 182.f, 22.f, 182.f, 25.f, 186.f, 34.f, 195.f, 38.f, 186.f, 49.f, 186.f, 59.f, 186.f, 65.f, 182.f, 68.f, 182.f, 77.f, 195.f,
+ 0.f, 207.f, 7.f, 209.f, 12.f, 208.f, 18.f, 208.f, 23.f, 204.f, 25.f, 215.f, 27.f, 208.f, 32.f, 204.f, 33.f, 204.f, 37.f, 207.f, 44.f, 204.f, 45.f, 204.f, 49.f, 207.f, 55.f, 215.f, 59.f, 205.f, 70.f, 202.f, 77.f, 205.f, 82.f, 205.f, 90.f, 215.f,
+ 1.f, 225.f, 9.f, 225.f, 14.f, 222.f, 22.f, 225.f, 30.f, 235.f,
+ 1.f, 243.f, 12.f, 246.f, 20.f, 242.f, 24.f, 242.f, 27.f, 246.f, 36.f, 255.f, 40.f, 246.f, 51.f, 246.f, 61.f, 246.f, 67.f, 242.f, 70.f, 242.f, 79.f, 255.f,
+ 0.f, 262.f, 10.f, 266.f, 18.f, 266.f, 30.f, 266.f, 39.f, 275.f, 44.f, 265.f, 55.f, 262.f, 62.f, 265.f, 67.f, 265.f, 75.f, 275.f,
+ 1.f, 285.f, 9.f, 285.f, 14.f, 282.f, 22.f, 285.f, 32.f, 293.f, 35.f, 284.f,
+ 1.f, 305.f, 12.f, 302.f, 19.f, 305.f, 24.f, 305.f, 32.f, 315.f, 37.f, 305.f, 45.f, 305.f, 50.f, 302.f, 58.f, 305.f, 66.f, 315.f,
+ 1.f, 322.f, 10.f, 326.f, 18.f, 322.f, 22.f, 322.f, 25.f, 326.f, 34.f, 335.f, 38.f, 326.f, 49.f, 326.f, 59.f, 326.f, 65.f, 322.f, 68.f, 322.f, 77.f, 335.f,
+ 0.f, 347.f, 7.f, 349.f, 12.f, 348.f, 18.f, 348.f, 23.f, 344.f, 25.f, 355.f, 27.f, 348.f, 32.f, 344.f, 33.f, 344.f, 37.f, 347.f, 44.f, 344.f, 45.f, 344.f, 49.f, 347.f, 55.f, 355.f,
+ };
+ struct LineRun line01 =
+ {
+ { 0u, 12u },
+ { 0u, 12u },
+ 81.f,
+ 15.f,
+ -5.f,
+ 3.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line02 =
+ {
+ { 12u, 10u },
+ { 12u, 10u },
+ 76.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line03 =
+ {
+ { 22u, 6u },
+ { 22u, 6u },
+ 36.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line04 =
+ {
+ { 28u, 20u },
+ { 28u, 20u },
+ 92.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line05 =
+ {
+ { 48u, 11u },
+ { 48u, 11u },
+ 76.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line06 =
+ {
+ { 59u, 5u },
+ { 59u, 5u },
+ 31.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line07 =
+ {
+ { 64u, 10u },
+ { 64u, 10u },
+ 67.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line08 =
+ {
+ { 74u, 12u },
+ { 74u, 12u },
+ 79.f,
+ 15.f,
+ -5.f,
+ 3.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line09 =
+ {
+ { 86u, 6u },
+ { 86u, 6u },
+ 43.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line10 =
+ {
+ { 92u, 12u },
+ { 92u, 12u },
+ 78.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line11 =
+ {
+ { 104u, 19u },
+ { 104u, 19u },
+ 90.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line12 =
+ {
+ { 123u, 5u },
+ { 123u, 5u },
+ 31.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line13 =
+ {
+ { 128u, 12u },
+ { 128u, 12u },
+ 81.f,
+ 15.f,
+ -5.f,
+ 3.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line14 =
+ {
+ { 140u, 10u },
+ { 140u, 10u },
+ 76.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line15 =
+ {
+ { 150u, 6u },
+ { 150u, 6u },
+ 36.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line16 =
+ {
+ { 156u, 10u },
+ { 156u, 10u },
+ 67.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line17 =
+ {
+ { 166u, 12u },
+ { 166u, 12u },
+ 79.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line18 =
+ {
+ { 178u, 14u },
+ { 178u, 14u },
+ 55.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line19 =
+ {
+ { 192u, 0u },
+ { 192u, 0u },
+ 0.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ Vector<LineRun> lines;
+ lines.PushBack( line01 );
+ lines.PushBack( line02 );
+ lines.PushBack( line03 );
+ lines.PushBack( line04 );
+ lines.PushBack( line05 );
+ lines.PushBack( line06 );
+ lines.PushBack( line07 );
+ lines.PushBack( line08 );
+ lines.PushBack( line09 );
+ lines.PushBack( line10 );
+ lines.PushBack( line11 );
+ lines.PushBack( line12 );
+ lines.PushBack( line13 );
+ lines.PushBack( line14 );
+ lines.PushBack( line15 );
+ lines.PushBack( line16 );
+ lines.PushBack( line17 );
+ lines.PushBack( line18 );
+ lines.PushBack( line19 );
+
+ LayoutTextData data =
+ {
+ "Layout bidirectional text.",
+ "Hello world demo שלום עולם.\n"
+ "مرحبا بالعالم hello world שלום עולם\n"
+ "שלום עולם hello world demo.\n"
+ "hello world مرحبا بالعالم שלום עולם\n"
+ "Hello world demo שלום עולם.\n"
+ "שלום עולם hello world مرحبا بالعالم\n",
+ textArea,
+ 15u,
+ fontDescriptionRuns.Begin(),
+ layoutSize,
+ 192u,
+ positions,
+ 19u,
+ lines.Begin(),
+ LayoutEngine::MULTI_LINE_BOX,
+ 64u,
+ 64u,
+ false,
+ true
+ };
+
+ if( !LayoutTextTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextUpdateLayout03(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextUpdateLayout03");
+
+ // Layout some lines of bidirectional text. Update the paragraphs at the middle.
+
+ const std::string fontLatin( "TizenSans" );
+ const std::string fontHebrew( "TizenSansHebrew" );
+ const std::string fontArabic( "TizenSansArabic" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun01;
+ fontDescriptionRun01.characterRun.characterIndex = 0u;
+ fontDescriptionRun01.characterRun.numberOfCharacters = 17u;
+ fontDescriptionRun01.familyLength = fontLatin.size();
+ fontDescriptionRun01.familyName = new char[fontDescriptionRun01.familyLength];
+ memcpy( fontDescriptionRun01.familyName, fontLatin.c_str(), fontDescriptionRun01.familyLength );
+ fontDescriptionRun01.familyDefined = true;
+ fontDescriptionRun01.weightDefined = false;
+ fontDescriptionRun01.widthDefined = false;
+ fontDescriptionRun01.slantDefined = false;
+ fontDescriptionRun01.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun02;
+ fontDescriptionRun02.characterRun.characterIndex = 17u;
+ fontDescriptionRun02.characterRun.numberOfCharacters = 11u;
+ fontDescriptionRun02.familyLength = fontHebrew.size();
+ fontDescriptionRun02.familyName = new char[fontDescriptionRun02.familyLength];
+ memcpy( fontDescriptionRun02.familyName, fontHebrew.c_str(), fontDescriptionRun02.familyLength );
+ fontDescriptionRun02.familyDefined = true;
+ fontDescriptionRun02.weightDefined = false;
+ fontDescriptionRun02.widthDefined = false;
+ fontDescriptionRun02.slantDefined = false;
+ fontDescriptionRun02.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun03;
+ fontDescriptionRun03.characterRun.characterIndex = 28u;
+ fontDescriptionRun03.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun03.familyLength = fontArabic.size();
+ fontDescriptionRun03.familyName = new char[fontDescriptionRun03.familyLength];
+ memcpy( fontDescriptionRun03.familyName, fontArabic.c_str(), fontDescriptionRun03.familyLength );
+ fontDescriptionRun03.familyDefined = true;
+ fontDescriptionRun03.weightDefined = false;
+ fontDescriptionRun03.widthDefined = false;
+ fontDescriptionRun03.slantDefined = false;
+ fontDescriptionRun03.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun04;
+ fontDescriptionRun04.characterRun.characterIndex = 42u;
+ fontDescriptionRun04.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun04.familyLength = fontLatin.size();
+ fontDescriptionRun04.familyName = new char[fontDescriptionRun04.familyLength];
+ memcpy( fontDescriptionRun04.familyName, fontLatin.c_str(), fontDescriptionRun04.familyLength );
+ fontDescriptionRun04.familyDefined = true;
+ fontDescriptionRun04.weightDefined = false;
+ fontDescriptionRun04.widthDefined = false;
+ fontDescriptionRun04.slantDefined = false;
+ fontDescriptionRun04.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun05;
+ fontDescriptionRun05.characterRun.characterIndex = 54u;
+ fontDescriptionRun05.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun05.familyLength = fontHebrew.size();
+ fontDescriptionRun05.familyName = new char[fontDescriptionRun05.familyLength];
+ memcpy( fontDescriptionRun05.familyName, fontHebrew.c_str(), fontDescriptionRun05.familyLength );
+ fontDescriptionRun05.familyDefined = true;
+ fontDescriptionRun05.weightDefined = false;
+ fontDescriptionRun05.widthDefined = false;
+ fontDescriptionRun05.slantDefined = false;
+ fontDescriptionRun05.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun06;
+ fontDescriptionRun06.characterRun.characterIndex = 64u;
+ fontDescriptionRun06.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun06.familyLength = fontHebrew.size();
+ fontDescriptionRun06.familyName = new char[fontDescriptionRun06.familyLength];
+ memcpy( fontDescriptionRun06.familyName, fontHebrew.c_str(), fontDescriptionRun06.familyLength );
+ fontDescriptionRun06.familyDefined = true;
+ fontDescriptionRun06.weightDefined = false;
+ fontDescriptionRun06.widthDefined = false;
+ fontDescriptionRun06.slantDefined = false;
+ fontDescriptionRun06.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun07;
+ fontDescriptionRun07.characterRun.characterIndex = 74u;
+ fontDescriptionRun07.characterRun.numberOfCharacters = 18u;
+ fontDescriptionRun07.familyLength = fontLatin.size();
+ fontDescriptionRun07.familyName = new char[fontDescriptionRun07.familyLength];
+ memcpy( fontDescriptionRun07.familyName, fontLatin.c_str(), fontDescriptionRun07.familyLength );
+ fontDescriptionRun07.familyDefined = true;
+ fontDescriptionRun07.weightDefined = false;
+ fontDescriptionRun07.widthDefined = false;
+ fontDescriptionRun07.slantDefined = false;
+ fontDescriptionRun07.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun08;
+ fontDescriptionRun08.characterRun.characterIndex = 92u;
+ fontDescriptionRun08.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun08.familyLength = fontLatin.size();
+ fontDescriptionRun08.familyName = new char[fontDescriptionRun08.familyLength];
+ memcpy( fontDescriptionRun08.familyName, fontLatin.c_str(), fontDescriptionRun08.familyLength );
+ fontDescriptionRun08.familyDefined = true;
+ fontDescriptionRun08.weightDefined = false;
+ fontDescriptionRun08.widthDefined = false;
+ fontDescriptionRun08.slantDefined = false;
+ fontDescriptionRun08.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun09;
+ fontDescriptionRun09.characterRun.characterIndex = 104u;
+ fontDescriptionRun09.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun09.familyLength = fontArabic.size();
+ fontDescriptionRun09.familyName = new char[fontDescriptionRun09.familyLength];
+ memcpy( fontDescriptionRun09.familyName, fontArabic.c_str(), fontDescriptionRun09.familyLength );
+ fontDescriptionRun09.familyDefined = true;
+ fontDescriptionRun09.weightDefined = false;
+ fontDescriptionRun09.widthDefined = false;
+ fontDescriptionRun09.slantDefined = false;
+ fontDescriptionRun09.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun10;
+ fontDescriptionRun10.characterRun.characterIndex = 118u;
+ fontDescriptionRun10.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun10.familyLength = fontHebrew.size();
+ fontDescriptionRun10.familyName = new char[fontDescriptionRun10.familyLength];
+ memcpy( fontDescriptionRun10.familyName, fontHebrew.c_str(), fontDescriptionRun10.familyLength );
+ fontDescriptionRun10.familyDefined = true;
+ fontDescriptionRun10.weightDefined = false;
+ fontDescriptionRun10.widthDefined = false;
+ fontDescriptionRun10.slantDefined = false;
+ fontDescriptionRun10.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun11;
+ fontDescriptionRun11.characterRun.characterIndex = 128u;
+ fontDescriptionRun11.characterRun.numberOfCharacters = 17u;
+ fontDescriptionRun11.familyLength = fontLatin.size();
+ fontDescriptionRun11.familyName = new char[fontDescriptionRun11.familyLength];
+ memcpy( fontDescriptionRun11.familyName, fontLatin.c_str(), fontDescriptionRun11.familyLength );
+ fontDescriptionRun11.familyDefined = true;
+ fontDescriptionRun11.weightDefined = false;
+ fontDescriptionRun11.widthDefined = false;
+ fontDescriptionRun11.slantDefined = false;
+ fontDescriptionRun11.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun12;
+ fontDescriptionRun12.characterRun.characterIndex = 145u;
+ fontDescriptionRun12.characterRun.numberOfCharacters = 11u;
+ fontDescriptionRun12.familyLength = fontHebrew.size();
+ fontDescriptionRun12.familyName = new char[fontDescriptionRun12.familyLength];
+ memcpy( fontDescriptionRun12.familyName, fontHebrew.c_str(), fontDescriptionRun12.familyLength );
+ fontDescriptionRun12.familyDefined = true;
+ fontDescriptionRun12.weightDefined = false;
+ fontDescriptionRun12.widthDefined = false;
+ fontDescriptionRun12.slantDefined = false;
+ fontDescriptionRun12.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun13;
+ fontDescriptionRun13.characterRun.characterIndex = 156u;
+ fontDescriptionRun13.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun13.familyLength = fontHebrew.size();
+ fontDescriptionRun13.familyName = new char[fontDescriptionRun13.familyLength];
+ memcpy( fontDescriptionRun13.familyName, fontHebrew.c_str(), fontDescriptionRun13.familyLength );
+ fontDescriptionRun13.familyDefined = true;
+ fontDescriptionRun13.weightDefined = false;
+ fontDescriptionRun13.widthDefined = false;
+ fontDescriptionRun13.slantDefined = false;
+ fontDescriptionRun13.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun14;
+ fontDescriptionRun14.characterRun.characterIndex = 166u;
+ fontDescriptionRun14.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun14.familyLength = fontLatin.size();
+ fontDescriptionRun14.familyName = new char[fontDescriptionRun14.familyLength];
+ memcpy( fontDescriptionRun14.familyName, fontLatin.c_str(), fontDescriptionRun14.familyLength );
+ fontDescriptionRun14.familyDefined = true;
+ fontDescriptionRun14.weightDefined = false;
+ fontDescriptionRun14.widthDefined = false;
+ fontDescriptionRun14.slantDefined = false;
+ fontDescriptionRun14.sizeDefined = false;
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun15;
+ fontDescriptionRun15.characterRun.characterIndex = 178u;
+ fontDescriptionRun15.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun15.familyLength = fontArabic.size();
+ fontDescriptionRun15.familyName = new char[fontDescriptionRun15.familyLength];
+ memcpy( fontDescriptionRun15.familyName, fontArabic.c_str(), fontDescriptionRun15.familyLength );
+ fontDescriptionRun15.familyDefined = true;
+ fontDescriptionRun15.weightDefined = false;
+ fontDescriptionRun15.widthDefined = false;
+ fontDescriptionRun15.slantDefined = false;
+ fontDescriptionRun15.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun01 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun02 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun03 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun04 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun05 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun06 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun07 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun08 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun09 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun10 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun11 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun12 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun13 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun14 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun15 );
+ Size textArea(100.f, 300.f);
+ Size layoutSize(92.f, 380.f);
+ float positions[] =
+ {
+ 1.f, 3.f, 12.f, 6.f, 20.f, 2.f, 24.f, 2.f, 27.f, 6.f, 36.f, 15.f, 40.f, 6.f, 51.f, 6.f, 61.f, 6.f, 67.f, 2.f, 70.f, 2.f, 79.f, 15.f,
+ 0.f, 22.f, 10.f, 26.f, 18.f, 26.f, 30.f, 26.f, 39.f, 35.f, 44.f, 25.f, 55.f, 22.f, 62.f, 25.f, 67.f, 25.f, 75.f, 35.f,
+ 1.f, 45.f, 9.f, 45.f, 14.f, 42.f, 22.f, 45.f, 32.f, 53.f, 35.f, 44.f,
+ 0.f, 67.f, 7.f, 69.f, 12.f, 68.f, 18.f, 68.f, 23.f, 64.f, 25.f, 75.f, 27.f, 68.f, 32.f, 64.f, 33.f, 64.f, 37.f, 67.f, 44.f, 64.f, 45.f, 64.f, 49.f, 67.f, 55.f, 75.f, 59.f, 62.f, 68.f, 66.f, 76.f, 62.f, 80.f, 62.f, 83.f, 66.f, 92.f, 75.f,
+ 0.f, 86.f, 11.f, 86.f, 21.f, 86.f, 27.f, 82.f, 30.f, 82.f, 39.f, 95.f, 44.f, 85.f, 55.f, 82.f, 62.f, 85.f, 67.f, 85.f, 75.f, 95.f,
+ 1.f, 105.f, 9.f, 105.f, 14.f, 102.f, 22.f, 105.f, 30.f, 115.f,
+ 1.f, 125.f, 12.f, 122.f, 19.f, 125.f, 24.f, 125.f, 32.f, 135.f, 37.f, 125.f, 45.f, 125.f, 50.f, 122.f, 58.f, 125.f, 66.f, 135.f,
+ 1.f, 142.f, 10.f, 146.f, 18.f, 142.f, 22.f, 142.f, 25.f, 146.f, 34.f, 155.f, 38.f, 146.f, 49.f, 146.f, 59.f, 146.f, 65.f, 142.f, 68.f, 142.f, 77.f, 155.f,
+ 0.f, 162.f, 10.f, 166.f, 18.f, 166.f, 30.f, 166.f, 39.f, 172.f, 42.f, 163.f,
+ 1.f, 182.f, 10.f, 186.f, 18.f, 182.f, 22.f, 182.f, 25.f, 186.f, 34.f, 195.f, 38.f, 186.f, 49.f, 186.f, 59.f, 186.f, 65.f, 182.f, 68.f, 182.f, 77.f, 195.f,
+ 0.f, 207.f, 7.f, 209.f, 12.f, 208.f, 18.f, 208.f, 23.f, 204.f, 25.f, 215.f, 27.f, 208.f, 32.f, 204.f, 33.f, 204.f, 37.f, 207.f, 44.f, 204.f, 45.f, 204.f, 49.f, 207.f, 55.f, 215.f, 59.f, 205.f, 70.f, 202.f, 77.f, 205.f, 82.f, 205.f, 90.f, 215.f,
+ 1.f, 225.f, 9.f, 225.f, 14.f, 222.f, 22.f, 225.f, 30.f, 235.f,
+ 1.f, 243.f, 12.f, 246.f, 20.f, 242.f, 24.f, 242.f, 27.f, 246.f, 36.f, 255.f, 40.f, 246.f, 51.f, 246.f, 61.f, 246.f, 67.f, 242.f, 70.f, 242.f, 79.f, 255.f,
+ 0.f, 262.f, 10.f, 266.f, 18.f, 266.f, 30.f, 266.f, 39.f, 275.f, 44.f, 265.f, 55.f, 262.f, 62.f, 265.f, 67.f, 265.f, 75.f, 275.f,
+ 1.f, 285.f, 9.f, 285.f, 14.f, 282.f, 22.f, 285.f, 32.f, 293.f, 35.f, 284.f,
+ 1.f, 305.f, 12.f, 302.f, 19.f, 305.f, 24.f, 305.f, 32.f, 315.f, 37.f, 305.f, 45.f, 305.f, 50.f, 302.f, 58.f, 305.f, 66.f, 315.f,
+ 1.f, 322.f, 10.f, 326.f, 18.f, 322.f, 22.f, 322.f, 25.f, 326.f, 34.f, 335.f, 38.f, 326.f, 49.f, 326.f, 59.f, 326.f, 65.f, 322.f, 68.f, 322.f, 77.f, 335.f,
+ 0.f, 347.f, 7.f, 349.f, 12.f, 348.f, 18.f, 348.f, 23.f, 344.f, 25.f, 355.f, 27.f, 348.f, 32.f, 344.f, 33.f, 344.f, 37.f, 347.f, 44.f, 344.f, 45.f, 344.f, 49.f, 347.f, 55.f, 355.f,
+ };
+ struct LineRun line01 =
+ {
+ { 0u, 12u },
+ { 0u, 12u },
+ 81.f,
+ 15.f,
+ -5.f,
+ 3.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line02 =
+ {
+ { 12u, 10u },
+ { 12u, 10u },
+ 76.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line03 =
+ {
+ { 22u, 6u },
+ { 22u, 6u },
+ 36.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line04 =
+ {
+ { 28u, 20u },
+ { 28u, 20u },
+ 92.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line05 =
+ {
+ { 48u, 11u },
+ { 48u, 11u },
+ 76.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line06 =
+ {
+ { 59u, 5u },
+ { 59u, 5u },
+ 31.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line07 =
+ {
+ { 64u, 10u },
+ { 64u, 10u },
+ 67.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line08 =
+ {
+ { 74u, 12u },
+ { 74u, 12u },
+ 79.f,
+ 15.f,
+ -5.f,
+ 3.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line09 =
+ {
+ { 86u, 6u },
+ { 86u, 6u },
+ 43.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line10 =
+ {
+ { 92u, 12u },
+ { 92u, 12u },
+ 78.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line11 =
+ {
+ { 104u, 19u },
+ { 104u, 19u },
+ 90.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line12 =
+ {
+ { 123u, 5u },
+ { 123u, 5u },
+ 31.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line13 =
+ {
+ { 128u, 12u },
+ { 128u, 12u },
+ 81.f,
+ 15.f,
+ -5.f,
+ 3.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line14 =
+ {
+ { 140u, 10u },
+ { 140u, 10u },
+ 76.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line15 =
+ {
+ { 150u, 6u },
+ { 150u, 6u },
+ 36.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line16 =
+ {
+ { 156u, 10u },
+ { 156u, 10u },
+ 67.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line17 =
+ {
+ { 166u, 12u },
+ { 166u, 12u },
+ 79.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line18 =
+ {
+ { 178u, 14u },
+ { 178u, 14u },
+ 55.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line19 =
+ {
+ { 192u, 0u },
+ { 192u, 0u },
+ 0.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ false
+ };
+ Vector<LineRun> lines;
+ lines.PushBack( line01 );
+ lines.PushBack( line02 );
+ lines.PushBack( line03 );
+ lines.PushBack( line04 );
+ lines.PushBack( line05 );
+ lines.PushBack( line06 );
+ lines.PushBack( line07 );
+ lines.PushBack( line08 );
+ lines.PushBack( line09 );
+ lines.PushBack( line10 );
+ lines.PushBack( line11 );
+ lines.PushBack( line12 );
+ lines.PushBack( line13 );
+ lines.PushBack( line14 );
+ lines.PushBack( line15 );
+ lines.PushBack( line16 );
+ lines.PushBack( line17 );
+ lines.PushBack( line18 );
+ lines.PushBack( line19 );
+
+ LayoutTextData data =
+ {
+ "Layout bidirectional text.",
+ "Hello world demo שלום עולם.\n"
+ "مرحبا بالعالم hello world שלום עולם\n"
+ "שלום עולם hello world demo.\n"
+ "hello world مرحبا بالعالم שלום עולם\n"
+ "Hello world demo שלום עולם.\n"
+ "שלום עולם hello world مرحبا بالعالم\n",
+ textArea,
+ 15u,
+ fontDescriptionRuns.Begin(),
+ layoutSize,
+ 192u,
+ positions,
+ 19u,
+ lines.Begin(),
+ LayoutEngine::MULTI_LINE_BOX,
+ 128u,
+ 64u,
+ false,
+ true
+ };
+
+ if( !LayoutTextTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextLayoutEllipsis01(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextLayoutEllipsis01");
+
+ // Layout single-line LTR text with ellipsis.
+
+ const std::string fontLatin( "TizenSans" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun01;
+ fontDescriptionRun01.characterRun.characterIndex = 0u;
+ fontDescriptionRun01.characterRun.numberOfCharacters = 51u;
+ fontDescriptionRun01.familyLength = fontLatin.size();
+ fontDescriptionRun01.familyName = new char[fontDescriptionRun01.familyLength];
+ memcpy( fontDescriptionRun01.familyName, fontLatin.c_str(), fontDescriptionRun01.familyLength );
+ fontDescriptionRun01.familyDefined = true;
+ fontDescriptionRun01.weightDefined = false;
+ fontDescriptionRun01.widthDefined = false;
+ fontDescriptionRun01.slantDefined = false;
+ fontDescriptionRun01.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun01 );
+
+ struct LineRun line01 =
+ {
+ { 0u, 13u },
+ { 0u, 13u },
+ 93.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ true
+ };
+ Vector<LineRun> lines;
+ lines.PushBack( line01 );
+
+ float positions[] =
+ {
+ 1.f, 3.f, 12.f, 6.f, 20.f, 2.f, 24.f, 2.f, 27.f, 6.f, 36.f, 15.f, 40.f, 6.f, 51.f, 6.f, 61.f, 6.f, 67.f, 2.f, 70.f, 2.f, 79.f, 15.f, 83.f, 2.f
+ };
+
+ Size textArea( 100.f, 50.f );
+ Size layoutSize( 100.f, 20.f );
+
+ LayoutTextData data =
+ {
+ "Layout single-line LTR text with ellipsis.",
+ "Hello world demo hello world demo hello world demo.",
+ textArea,
+ 1u,
+ fontDescriptionRuns.Begin(),
+ layoutSize,
+ 13u,
+ positions,
+ 1u,
+ lines.Begin(),
+ LayoutEngine::SINGLE_LINE_BOX,
+ 0u,
+ 51u,
+ true,
+ true
+ };
+
+ if( !LayoutTextTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextLayoutEllipsis02(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextLayoutEllipsis02");
+
+ // Layout multi-line LTR text with ellipsis.
+
+ const std::string fontLatin( "TizenSans" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun01;
+ fontDescriptionRun01.characterRun.characterIndex = 0u;
+ fontDescriptionRun01.characterRun.numberOfCharacters = 51u;
+ fontDescriptionRun01.familyLength = fontLatin.size();
+ fontDescriptionRun01.familyName = new char[fontDescriptionRun01.familyLength];
+ memcpy( fontDescriptionRun01.familyName, fontLatin.c_str(), fontDescriptionRun01.familyLength );
+ fontDescriptionRun01.familyDefined = true;
+ fontDescriptionRun01.weightDefined = false;
+ fontDescriptionRun01.widthDefined = false;
+ fontDescriptionRun01.slantDefined = false;
+ fontDescriptionRun01.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun01 );
+
+ struct LineRun line01 =
+ {
+ { 0u, 12u },
+ { 0u, 12u },
+ 81.f,
+ 15.f,
+ -5.f,
+ 3.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line02 =
+ {
+ { 12u, 12u },
+ { 12u, 12u },
+ 93.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ true
+ };
+ Vector<LineRun> lines;
+ lines.PushBack( line01 );
+ lines.PushBack( line02 );
+
+ float positions[] =
+ {
+ 1.f, 3.f, 12.f, 6.f, 20.f, 2.f, 24.f, 2.f, 27.f, 6.f, 36.f, 15.f, 40.f, 6.f, 51.f, 6.f, 61.f, 6.f, 67.f, 2.f, 70.f, 2.f, 79.f, 15.f,
+ 0.f, 22.f, 10.f, 26.f, 18.f, 26.f, 30.f, 26.f, 39.f, 35.f, 44.f, 22.f, 53.f, 26.f, 61.f, 22.f, 65.f, 22.f, 68.f, 26.f, 77.f, 35.f, 81.f, 26.f
+ };
+
+ Size textArea( 100.f, 50.f );
+ Size layoutSize( 100.f, 60.f );
+
+ LayoutTextData data =
+ {
+ "Layout multi-line LTR text with ellipsis.",
+ "Hello world demo hello world demo hello world demo.",
+ textArea,
+ 1u,
+ fontDescriptionRuns.Begin(),
+ layoutSize,
+ 24u,
+ positions,
+ 2u,
+ lines.Begin(),
+ LayoutEngine::MULTI_LINE_BOX,
+ 0u,
+ 51u,
+ true,
+ true
+ };
+
+ if( !LayoutTextTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextLayoutEllipsis03(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextLayoutEllipsis03");
+
+ // Layout single-line RTL text with ellipsis.
+
+ const std::string fontHebrew( "TizenSansHebrew" );
+ const std::string fontArabic( "TizenSansArabic" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun01;
+ fontDescriptionRun01.characterRun.characterIndex = 0u;
+ fontDescriptionRun01.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun01.familyLength = fontHebrew.size();
+ fontDescriptionRun01.familyName = new char[fontDescriptionRun01.familyLength];
+ memcpy( fontDescriptionRun01.familyName, fontHebrew.c_str(), fontDescriptionRun01.familyLength );
+ fontDescriptionRun01.familyDefined = true;
+ fontDescriptionRun01.weightDefined = false;
+ fontDescriptionRun01.widthDefined = false;
+ fontDescriptionRun01.slantDefined = false;
+ fontDescriptionRun01.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun02;
+ fontDescriptionRun02.characterRun.characterIndex = 10u;
+ fontDescriptionRun02.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun02.familyLength = fontArabic.size();
+ fontDescriptionRun02.familyName = new char[fontDescriptionRun02.familyLength];
+ memcpy( fontDescriptionRun02.familyName, fontArabic.c_str(), fontDescriptionRun02.familyLength );
+ fontDescriptionRun02.familyDefined = true;
+ fontDescriptionRun02.weightDefined = false;
+ fontDescriptionRun02.widthDefined = false;
+ fontDescriptionRun02.slantDefined = false;
+ fontDescriptionRun02.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun03;
+ fontDescriptionRun03.characterRun.characterIndex = 24u;
+ fontDescriptionRun03.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun03.familyLength = fontHebrew.size();
+ fontDescriptionRun03.familyName = new char[fontDescriptionRun03.familyLength];
+ memcpy( fontDescriptionRun03.familyName, fontHebrew.c_str(), fontDescriptionRun03.familyLength );
+ fontDescriptionRun03.familyDefined = true;
+ fontDescriptionRun03.weightDefined = false;
+ fontDescriptionRun03.widthDefined = false;
+ fontDescriptionRun03.slantDefined = false;
+ fontDescriptionRun03.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun04;
+ fontDescriptionRun04.characterRun.characterIndex = 34u;
+ fontDescriptionRun04.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun04.familyLength = fontArabic.size();
+ fontDescriptionRun04.familyName = new char[fontDescriptionRun04.familyLength];
+ memcpy( fontDescriptionRun04.familyName, fontArabic.c_str(), fontDescriptionRun04.familyLength );
+ fontDescriptionRun04.familyDefined = true;
+ fontDescriptionRun04.weightDefined = false;
+ fontDescriptionRun04.widthDefined = false;
+ fontDescriptionRun04.slantDefined = false;
+ fontDescriptionRun04.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun05;
+ fontDescriptionRun05.characterRun.characterIndex = 48u;
+ fontDescriptionRun05.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun05.familyLength = fontHebrew.size();
+ fontDescriptionRun05.familyName = new char[fontDescriptionRun05.familyLength];
+ memcpy( fontDescriptionRun05.familyName, fontHebrew.c_str(), fontDescriptionRun05.familyLength );
+ fontDescriptionRun05.familyDefined = true;
+ fontDescriptionRun05.weightDefined = false;
+ fontDescriptionRun05.widthDefined = false;
+ fontDescriptionRun05.slantDefined = false;
+ fontDescriptionRun05.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun06;
+ fontDescriptionRun06.characterRun.characterIndex = 58u;
+ fontDescriptionRun06.characterRun.numberOfCharacters = 15u;
+ fontDescriptionRun06.familyLength = fontArabic.size();
+ fontDescriptionRun06.familyName = new char[fontDescriptionRun06.familyLength];
+ memcpy( fontDescriptionRun06.familyName, fontArabic.c_str(), fontDescriptionRun06.familyLength );
+ fontDescriptionRun06.familyDefined = true;
+ fontDescriptionRun06.weightDefined = false;
+ fontDescriptionRun06.widthDefined = false;
+ fontDescriptionRun06.slantDefined = false;
+ fontDescriptionRun06.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun01 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun02 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun03 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun04 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun05 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun06 );
+
+ struct LineRun line01 =
+ {
+ { 0u, 16u },
+ { 0u, 16u },
+ 95.f,
+ 15.f,
+ -5.f,
+ 0.f,
+ 0.f,
+ false,
+ true
+ };
+ Vector<LineRun> lines;
+ lines.PushBack( line01 );
+
+ float positions[] =
+ {
+ 1.f, 5.f, 12.f, 2.f, 19.f, 5.f, 24.f, 5.f, 32.f, 15.f, 37.f, 5.f, 45.f, 5.f, 50.f, 2.f, 58.f, 5.f, 66.f, 15.f, 69.f, 7.f, 76.f, 9.f, 81.f, 8.f, 87.f, 8.f, 92.f, 4.f, 94.f, 15.f,
+ };
+
+ Size textArea( 100.f, 50.f );
+ Size layoutSize( 100.f, 20.f );
+
+ LayoutTextData data =
+ {
+ "Layout single-line RTL text with ellipsis.",
+ "שלום עולם مرحبا بالعالم שלום עולם مرحبا بالعالم שלום עולם مرحبا بالعالم.",
+ textArea,
+ 6u,
+ fontDescriptionRuns.Begin(),
+ layoutSize,
+ 16u,
+ positions,
+ 1u,
+ lines.Begin(),
+ LayoutEngine::SINGLE_LINE_BOX,
+ 0u,
+ 72u,
+ true,
+ true
+ };
+
+ if( !LayoutTextTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextLayoutEllipsis04(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextLayoutEllipsis04");
+
+ // Layout multi-line RTL text with ellipsis.
+
+ const std::string fontHebrew( "TizenSansHebrew" );
+ const std::string fontArabic( "TizenSansArabic" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun01;
+ fontDescriptionRun01.characterRun.characterIndex = 0u;
+ fontDescriptionRun01.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun01.familyLength = fontHebrew.size();
+ fontDescriptionRun01.familyName = new char[fontDescriptionRun01.familyLength];
+ memcpy( fontDescriptionRun01.familyName, fontHebrew.c_str(), fontDescriptionRun01.familyLength );
+ fontDescriptionRun01.familyDefined = true;
+ fontDescriptionRun01.weightDefined = false;
+ fontDescriptionRun01.widthDefined = false;
+ fontDescriptionRun01.slantDefined = false;
+ fontDescriptionRun01.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun02;
+ fontDescriptionRun02.characterRun.characterIndex = 10u;
+ fontDescriptionRun02.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun02.familyLength = fontArabic.size();
+ fontDescriptionRun02.familyName = new char[fontDescriptionRun02.familyLength];
+ memcpy( fontDescriptionRun02.familyName, fontArabic.c_str(), fontDescriptionRun02.familyLength );
+ fontDescriptionRun02.familyDefined = true;
+ fontDescriptionRun02.weightDefined = false;
+ fontDescriptionRun02.widthDefined = false;
+ fontDescriptionRun02.slantDefined = false;
+ fontDescriptionRun02.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun03;
+ fontDescriptionRun03.characterRun.characterIndex = 24u;
+ fontDescriptionRun03.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun03.familyLength = fontHebrew.size();
+ fontDescriptionRun03.familyName = new char[fontDescriptionRun03.familyLength];
+ memcpy( fontDescriptionRun03.familyName, fontHebrew.c_str(), fontDescriptionRun03.familyLength );
+ fontDescriptionRun03.familyDefined = true;
+ fontDescriptionRun03.weightDefined = false;
+ fontDescriptionRun03.widthDefined = false;
+ fontDescriptionRun03.slantDefined = false;
+ fontDescriptionRun03.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun04;
+ fontDescriptionRun04.characterRun.characterIndex = 34u;
+ fontDescriptionRun04.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun04.familyLength = fontArabic.size();
+ fontDescriptionRun04.familyName = new char[fontDescriptionRun04.familyLength];
+ memcpy( fontDescriptionRun04.familyName, fontArabic.c_str(), fontDescriptionRun04.familyLength );
+ fontDescriptionRun04.familyDefined = true;
+ fontDescriptionRun04.weightDefined = false;
+ fontDescriptionRun04.widthDefined = false;
+ fontDescriptionRun04.slantDefined = false;
+ fontDescriptionRun04.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun05;
+ fontDescriptionRun05.characterRun.characterIndex = 48u;
+ fontDescriptionRun05.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun05.familyLength = fontHebrew.size();
+ fontDescriptionRun05.familyName = new char[fontDescriptionRun05.familyLength];
+ memcpy( fontDescriptionRun05.familyName, fontHebrew.c_str(), fontDescriptionRun05.familyLength );
+ fontDescriptionRun05.familyDefined = true;
+ fontDescriptionRun05.weightDefined = false;
+ fontDescriptionRun05.widthDefined = false;
+ fontDescriptionRun05.slantDefined = false;
+ fontDescriptionRun05.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun06;
+ fontDescriptionRun06.characterRun.characterIndex = 58u;
+ fontDescriptionRun06.characterRun.numberOfCharacters = 15u;
+ fontDescriptionRun06.familyLength = fontArabic.size();
+ fontDescriptionRun06.familyName = new char[fontDescriptionRun06.familyLength];
+ memcpy( fontDescriptionRun06.familyName, fontArabic.c_str(), fontDescriptionRun06.familyLength );
+ fontDescriptionRun06.familyDefined = true;
+ fontDescriptionRun06.weightDefined = false;
+ fontDescriptionRun06.widthDefined = false;
+ fontDescriptionRun06.slantDefined = false;
+ fontDescriptionRun06.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun01 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun02 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun03 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun04 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun05 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun06 );
+
+ struct LineRun line01 =
+ {
+ { 0u, 16u },
+ { 0u, 16u },
+ 96.f,
+ 15.f,
+ -5.f,
+ 3.f,
+ 0.f,
+ false,
+ false
+ };
+ struct LineRun line02 =
+ {
+ { 16u, 18u },
+ { 16u, 18u },
+ 97.f,
+ 15.f,
+ -5.f,
+ 4.f,
+ 0.f,
+ false,
+ true
+ };
+ Vector<LineRun> lines;
+ lines.PushBack( line01 );
+ lines.PushBack( line02 );
+
+ float positions[] =
+ {
+ 1.f, 5.f, 12.f, 2.f, 19.f, 5.f, 24.f, 5.f, 32.f, 15.f, 37.f, 5.f, 45.f, 5.f, 50.f, 2.f, 58.f, 5.f, 66.f, 15.f, 69.f, 7.f, 76.f, 9.f, 81.f, 8.f, 87.f, 8.f, 92.f, 4.f, 94.f, 15.f,
+ 0.f, 28.f, 5.f, 24.f, 6.f, 24.f, 10.f, 27.f, 17.f, 24.f, 18.f, 24.f, 22.f, 27.f, 28.f, 35.f, 32.f, 25.f, 43.f, 22.f, 50.f, 25.f, 55.f, 25.f, 63.f, 35.f, 68.f, 25.f, 76.f, 25.f, 81.f, 22.f, 89.f, 25.f, 97.f, 35.f
+ };
+
+ Size textArea( 100.f, 50.f );
+ Size layoutSize( 100.f, 60.f );
+
+ LayoutTextData data =
+ {
+ "Layout single-line RTL text with ellipsis.",
+ "שלום עולם مرحبا بالعالم שלום עולם مرحبا بالعالم שלום עולם مرحبا بالعالم.",
+ textArea,
+ 6u,
+ fontDescriptionRuns.Begin(),
+ layoutSize,
+ 34u,
+ positions,
+ 2u,
+ lines.Begin(),
+ LayoutEngine::MULTI_LINE_BOX,
+ 0u,
+ 72u,
+ true,
+ true
+ };
+
+ if( !LayoutTextTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextReorderLayout01(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextReorderLayout01");
+
+ // Reorder lines. No right to left characters.
+
+ const std::string fontLatin( "TizenSans" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun01;
+ fontDescriptionRun01.characterRun.characterIndex = 0u;
+ fontDescriptionRun01.characterRun.numberOfCharacters = 11u;
+ fontDescriptionRun01.familyLength = fontLatin.size();
+ fontDescriptionRun01.familyName = new char[fontDescriptionRun01.familyLength];
+ memcpy( fontDescriptionRun01.familyName, fontLatin.c_str(), fontDescriptionRun01.familyLength );
+ fontDescriptionRun01.familyDefined = true;
+ fontDescriptionRun01.weightDefined = false;
+ fontDescriptionRun01.widthDefined = false;
+ fontDescriptionRun01.slantDefined = false;
+ fontDescriptionRun01.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun01 );
+
+ float positions[] =
+ {
+ 1.f, 3.f, 12.f, 6.f, 20.f, 2.f, 24.f, 2.f, 27.f, 6.f, 36.f, 15.f, 40.f, 6.f, 51.f, 6.f, 61.f, 6.f, 67.f, 2.f, 70.f, 2.f
+ };
+
+ Size textArea( 100.f, 300.f );
+
+ ReLayoutRightToLeftLinesData data =
+ {
+ "Text with no right to left text.",
+ "Hello world",
+ textArea,
+ 1u,
+ fontDescriptionRuns.Begin(),
+ 11u,
+ positions,
+ 0u,
+ 11u
+ };
+
+ if( !ReLayoutRightToLeftLinesTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextReorderLayout02(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextReorderLayout02");
+
+ // Reorder lines of the first paragraph.
+
+ const std::string fontHebrew( "TizenSansHebrew" );
+ const std::string fontArabic( "TizenSansArabic" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun01;
+ fontDescriptionRun01.characterRun.characterIndex = 0u;
+ fontDescriptionRun01.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun01.familyLength = fontHebrew.size();
+ fontDescriptionRun01.familyName = new char[fontDescriptionRun01.familyLength];
+ memcpy( fontDescriptionRun01.familyName, fontHebrew.c_str(), fontDescriptionRun01.familyLength );
+ fontDescriptionRun01.familyDefined = true;
+ fontDescriptionRun01.weightDefined = false;
+ fontDescriptionRun01.widthDefined = false;
+ fontDescriptionRun01.slantDefined = false;
+ fontDescriptionRun01.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun02;
+ fontDescriptionRun02.characterRun.characterIndex = 10u;
+ fontDescriptionRun02.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun02.familyLength = fontArabic.size();
+ fontDescriptionRun02.familyName = new char[fontDescriptionRun02.familyLength];
+ memcpy( fontDescriptionRun02.familyName, fontArabic.c_str(), fontDescriptionRun02.familyLength );
+ fontDescriptionRun02.familyDefined = true;
+ fontDescriptionRun02.weightDefined = false;
+ fontDescriptionRun02.widthDefined = false;
+ fontDescriptionRun02.slantDefined = false;
+ fontDescriptionRun02.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun03;
+ fontDescriptionRun03.characterRun.characterIndex = 24u;
+ fontDescriptionRun03.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun03.familyLength = fontArabic.size();
+ fontDescriptionRun03.familyName = new char[fontDescriptionRun03.familyLength];
+ memcpy( fontDescriptionRun03.familyName, fontArabic.c_str(), fontDescriptionRun03.familyLength );
+ fontDescriptionRun03.familyDefined = true;
+ fontDescriptionRun03.weightDefined = false;
+ fontDescriptionRun03.widthDefined = false;
+ fontDescriptionRun03.slantDefined = false;
+ fontDescriptionRun03.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun04;
+ fontDescriptionRun04.characterRun.characterIndex = 38u;
+ fontDescriptionRun04.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun04.familyLength = fontHebrew.size();
+ fontDescriptionRun04.familyName = new char[fontDescriptionRun04.familyLength];
+ memcpy( fontDescriptionRun04.familyName, fontHebrew.c_str(), fontDescriptionRun04.familyLength );
+ fontDescriptionRun04.familyDefined = true;
+ fontDescriptionRun04.weightDefined = false;
+ fontDescriptionRun04.widthDefined = false;
+ fontDescriptionRun04.slantDefined = false;
+ fontDescriptionRun04.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun05;
+ fontDescriptionRun05.characterRun.characterIndex = 48u;
+ fontDescriptionRun05.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun05.familyLength = fontHebrew.size();
+ fontDescriptionRun05.familyName = new char[fontDescriptionRun05.familyLength];
+ memcpy( fontDescriptionRun05.familyName, fontHebrew.c_str(), fontDescriptionRun05.familyLength );
+ fontDescriptionRun05.familyDefined = true;
+ fontDescriptionRun05.weightDefined = false;
+ fontDescriptionRun05.widthDefined = false;
+ fontDescriptionRun05.slantDefined = false;
+ fontDescriptionRun05.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun06;
+ fontDescriptionRun06.characterRun.characterIndex = 58u;
+ fontDescriptionRun06.characterRun.numberOfCharacters = 15u;
+ fontDescriptionRun06.familyLength = fontArabic.size();
+ fontDescriptionRun06.familyName = new char[fontDescriptionRun06.familyLength];
+ memcpy( fontDescriptionRun06.familyName, fontArabic.c_str(), fontDescriptionRun06.familyLength );
+ fontDescriptionRun06.familyDefined = true;
+ fontDescriptionRun06.weightDefined = false;
+ fontDescriptionRun06.widthDefined = false;
+ fontDescriptionRun06.slantDefined = false;
+ fontDescriptionRun06.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun01 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun02 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun03 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun04 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun05 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun06 );
+
+ float positions[] =
+ {
+ 87.f, 5.f, 79.f, 2.f, 74.f, 5.f, 66.f, 5.f, 61.f, 15.f, 53.f, 5.f, 48.f, 5.f, 41.f, 2.f, 32.f, 5.f, 27.f, 15.f, 20.f, 7.f, 15.f, 9.f, 8.f, 8.f, 5.f, 8.f, 4.f, 4.f, 0.f, 15.f,
+ 23.f, 28.f, 22.f, 24.f, 17.f, 24.f, 12.f, 27.f, 10.f, 24.f, 5.f, 24.f, 0.f, 27.f, 0.f, 35.f,
+ 0.f, 47.f, 7.f, 49.f, 12.f, 48.f, 18.f, 48.f, 23.f, 44.f, 25.f, 55.f, 27.f, 48.f, 32.f, 44.f, 33.f, 44.f, 37.f, 47.f, 44.f, 44.f, 45.f, 44.f, 49.f, 47.f, 55.f, 55.f, 59.f, 45.f, 70.f, 42.f, 77.f, 45.f, 82.f, 45.f, 90.f, 55.f,
+ 1.f, 65.f, 9.f, 65.f, 14.f, 62.f, 22.f, 65.f, 30.f, 75.f,
+ 1.f, 85.f, 12.f, 82.f, 19.f, 85.f, 24.f, 85.f, 32.f, 95.f, 37.f, 85.f, 45.f, 85.f, 50.f, 82.f, 58.f, 85.f, 66.f, 95.f, 69.f, 87.f, 76.f, 89.f, 81.f, 88.f, 87.f, 88.f, 92.f, 84.f, 94.f, 95.f,
+ 0.f, 108.f, 5.f, 104.f, 6.f, 104.f, 10.f, 107.f, 17.f, 104.f, 18.f, 104.f, 22.f, 107.f, 30.f, 113.f
+ };
+
+ Size textArea( 100.f, 300.f );
+
+ ReLayoutRightToLeftLinesData data =
+ {
+ "Paragraphs with right to left text.",
+ "שלום עולם مرحبا بالعالم\n"
+ "مرحبا بالعالم שלום עולם\n"
+ "שלום עולם مرحبا بالعالم.",
+ textArea,
+ 6u,
+ fontDescriptionRuns.Begin(),
+ 72u,
+ positions,
+ 0u,
+ 24u
+ };
+
+ if( !ReLayoutRightToLeftLinesTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextReorderLayout03(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextReorderLayout03");
+
+ // Reorder lines of the mid paragraph.
+
+ const std::string fontHebrew( "TizenSansHebrew" );
+ const std::string fontArabic( "TizenSansArabic" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun01;
+ fontDescriptionRun01.characterRun.characterIndex = 0u;
+ fontDescriptionRun01.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun01.familyLength = fontHebrew.size();
+ fontDescriptionRun01.familyName = new char[fontDescriptionRun01.familyLength];
+ memcpy( fontDescriptionRun01.familyName, fontHebrew.c_str(), fontDescriptionRun01.familyLength );
+ fontDescriptionRun01.familyDefined = true;
+ fontDescriptionRun01.weightDefined = false;
+ fontDescriptionRun01.widthDefined = false;
+ fontDescriptionRun01.slantDefined = false;
+ fontDescriptionRun01.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun02;
+ fontDescriptionRun02.characterRun.characterIndex = 10u;
+ fontDescriptionRun02.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun02.familyLength = fontArabic.size();
+ fontDescriptionRun02.familyName = new char[fontDescriptionRun02.familyLength];
+ memcpy( fontDescriptionRun02.familyName, fontArabic.c_str(), fontDescriptionRun02.familyLength );
+ fontDescriptionRun02.familyDefined = true;
+ fontDescriptionRun02.weightDefined = false;
+ fontDescriptionRun02.widthDefined = false;
+ fontDescriptionRun02.slantDefined = false;
+ fontDescriptionRun02.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun03;
+ fontDescriptionRun03.characterRun.characterIndex = 24u;
+ fontDescriptionRun03.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun03.familyLength = fontArabic.size();
+ fontDescriptionRun03.familyName = new char[fontDescriptionRun03.familyLength];
+ memcpy( fontDescriptionRun03.familyName, fontArabic.c_str(), fontDescriptionRun03.familyLength );
+ fontDescriptionRun03.familyDefined = true;
+ fontDescriptionRun03.weightDefined = false;
+ fontDescriptionRun03.widthDefined = false;
+ fontDescriptionRun03.slantDefined = false;
+ fontDescriptionRun03.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun04;
+ fontDescriptionRun04.characterRun.characterIndex = 38u;
+ fontDescriptionRun04.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun04.familyLength = fontHebrew.size();
+ fontDescriptionRun04.familyName = new char[fontDescriptionRun04.familyLength];
+ memcpy( fontDescriptionRun04.familyName, fontHebrew.c_str(), fontDescriptionRun04.familyLength );
+ fontDescriptionRun04.familyDefined = true;
+ fontDescriptionRun04.weightDefined = false;
+ fontDescriptionRun04.widthDefined = false;
+ fontDescriptionRun04.slantDefined = false;
+ fontDescriptionRun04.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun05;
+ fontDescriptionRun05.characterRun.characterIndex = 48u;
+ fontDescriptionRun05.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun05.familyLength = fontHebrew.size();
+ fontDescriptionRun05.familyName = new char[fontDescriptionRun05.familyLength];
+ memcpy( fontDescriptionRun05.familyName, fontHebrew.c_str(), fontDescriptionRun05.familyLength );
+ fontDescriptionRun05.familyDefined = true;
+ fontDescriptionRun05.weightDefined = false;
+ fontDescriptionRun05.widthDefined = false;
+ fontDescriptionRun05.slantDefined = false;
+ fontDescriptionRun05.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun06;
+ fontDescriptionRun06.characterRun.characterIndex = 58u;
+ fontDescriptionRun06.characterRun.numberOfCharacters = 15u;
+ fontDescriptionRun06.familyLength = fontArabic.size();
+ fontDescriptionRun06.familyName = new char[fontDescriptionRun06.familyLength];
+ memcpy( fontDescriptionRun06.familyName, fontArabic.c_str(), fontDescriptionRun06.familyLength );
+ fontDescriptionRun06.familyDefined = true;
+ fontDescriptionRun06.weightDefined = false;
+ fontDescriptionRun06.widthDefined = false;
+ fontDescriptionRun06.slantDefined = false;
+ fontDescriptionRun06.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun01 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun02 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun03 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun04 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun05 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun06 );
+
+ float positions[] =
+ {
+ 1.f, 5.f, 12.f, 2.f, 19.f, 5.f, 24.f, 5.f, 32.f, 15.f, 37.f, 5.f, 45.f, 5.f, 50.f, 2.f, 58.f, 5.f, 66.f, 15.f, 69.f, 7.f, 76.f, 9.f, 81.f, 8.f, 87.f, 8.f, 92.f, 4.f, 94.f, 15.f,
+ 0.f, 28.f, 5.f, 24.f, 6.f, 24.f, 10.f, 27.f, 17.f, 24.f, 18.f, 24.f, 22.f, 27.f, 28.f, 35.f,
+ 86.f, 47.f, 81.f, 49.f, 74.f, 48.f, 71.f, 48.f, 70.f, 44.f, 66.f, 55.f, 62.f, 48.f, 61.f, 44.f, 56.f, 44.f, 51.f, 47.f, 49.f, 44.f, 44.f, 44.f, 39.f, 47.f, 36.f, 55.f, 26.f, 45.f, 18.f, 42.f, 13.f, 45.f, 5.f, 45.f, 0.f, 55.f,
+ 22.f, 65.f, 17.f, 65.f, 10.f, 62.f, 1.f, 65.f, 0.f, 75.f,
+ 1.f, 85.f, 12.f, 82.f, 19.f, 85.f, 24.f, 85.f, 32.f, 95.f, 37.f, 85.f, 45.f, 85.f, 50.f, 82.f, 58.f, 85.f, 66.f, 95.f, 69.f, 87.f, 76.f, 89.f, 81.f, 88.f, 87.f, 88.f, 92.f, 84.f, 94.f, 95.f,
+ 0.f, 108.f, 5.f, 104.f, 6.f, 104.f, 10.f, 107.f, 17.f, 104.f, 18.f, 104.f, 22.f, 107.f, 30.f, 113.f
+ };
+
+ Size textArea( 100.f, 300.f );
+
+ ReLayoutRightToLeftLinesData data =
+ {
+ "Paragraphs with right to left text.",
+ "שלום עולם مرحبا بالعالم\n"
+ "مرحبا بالعالم שלום עולם\n"
+ "שלום עולם مرحبا بالعالم.",
+ textArea,
+ 6u,
+ fontDescriptionRuns.Begin(),
+ 72u,
+ positions,
+ 24u,
+ 24u
+ };
+
+ if( !ReLayoutRightToLeftLinesTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextReorderLayout04(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextReorderLayout04");
+
+ // Reorder lines of the last paragraph.
+
+ const std::string fontHebrew( "TizenSansHebrew" );
+ const std::string fontArabic( "TizenSansArabic" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun01;
+ fontDescriptionRun01.characterRun.characterIndex = 0u;
+ fontDescriptionRun01.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun01.familyLength = fontHebrew.size();
+ fontDescriptionRun01.familyName = new char[fontDescriptionRun01.familyLength];
+ memcpy( fontDescriptionRun01.familyName, fontHebrew.c_str(), fontDescriptionRun01.familyLength );
+ fontDescriptionRun01.familyDefined = true;
+ fontDescriptionRun01.weightDefined = false;
+ fontDescriptionRun01.widthDefined = false;
+ fontDescriptionRun01.slantDefined = false;
+ fontDescriptionRun01.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun02;
+ fontDescriptionRun02.characterRun.characterIndex = 10u;
+ fontDescriptionRun02.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun02.familyLength = fontArabic.size();
+ fontDescriptionRun02.familyName = new char[fontDescriptionRun02.familyLength];
+ memcpy( fontDescriptionRun02.familyName, fontArabic.c_str(), fontDescriptionRun02.familyLength );
+ fontDescriptionRun02.familyDefined = true;
+ fontDescriptionRun02.weightDefined = false;
+ fontDescriptionRun02.widthDefined = false;
+ fontDescriptionRun02.slantDefined = false;
+ fontDescriptionRun02.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun03;
+ fontDescriptionRun03.characterRun.characterIndex = 24u;
+ fontDescriptionRun03.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun03.familyLength = fontArabic.size();
+ fontDescriptionRun03.familyName = new char[fontDescriptionRun03.familyLength];
+ memcpy( fontDescriptionRun03.familyName, fontArabic.c_str(), fontDescriptionRun03.familyLength );
+ fontDescriptionRun03.familyDefined = true;
+ fontDescriptionRun03.weightDefined = false;
+ fontDescriptionRun03.widthDefined = false;
+ fontDescriptionRun03.slantDefined = false;
+ fontDescriptionRun03.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun04;
+ fontDescriptionRun04.characterRun.characterIndex = 38u;
+ fontDescriptionRun04.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun04.familyLength = fontHebrew.size();
+ fontDescriptionRun04.familyName = new char[fontDescriptionRun04.familyLength];
+ memcpy( fontDescriptionRun04.familyName, fontHebrew.c_str(), fontDescriptionRun04.familyLength );
+ fontDescriptionRun04.familyDefined = true;
+ fontDescriptionRun04.weightDefined = false;
+ fontDescriptionRun04.widthDefined = false;
+ fontDescriptionRun04.slantDefined = false;
+ fontDescriptionRun04.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun05;
+ fontDescriptionRun05.characterRun.characterIndex = 48u;
+ fontDescriptionRun05.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun05.familyLength = fontHebrew.size();
+ fontDescriptionRun05.familyName = new char[fontDescriptionRun05.familyLength];
+ memcpy( fontDescriptionRun05.familyName, fontHebrew.c_str(), fontDescriptionRun05.familyLength );
+ fontDescriptionRun05.familyDefined = true;
+ fontDescriptionRun05.weightDefined = false;
+ fontDescriptionRun05.widthDefined = false;
+ fontDescriptionRun05.slantDefined = false;
+ fontDescriptionRun05.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun06;
+ fontDescriptionRun06.characterRun.characterIndex = 58u;
+ fontDescriptionRun06.characterRun.numberOfCharacters = 15u;
+ fontDescriptionRun06.familyLength = fontArabic.size();
+ fontDescriptionRun06.familyName = new char[fontDescriptionRun06.familyLength];
+ memcpy( fontDescriptionRun06.familyName, fontArabic.c_str(), fontDescriptionRun06.familyLength );
+ fontDescriptionRun06.familyDefined = true;
+ fontDescriptionRun06.weightDefined = false;
+ fontDescriptionRun06.widthDefined = false;
+ fontDescriptionRun06.slantDefined = false;
+ fontDescriptionRun06.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun01 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun02 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun03 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun04 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun05 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun06 );
+
+ float positions[] =
+ {
+ 1.f, 5.f, 12.f, 2.f, 19.f, 5.f, 24.f, 5.f, 32.f, 15.f, 37.f, 5.f, 45.f, 5.f, 50.f, 2.f, 58.f, 5.f, 66.f, 15.f, 69.f, 7.f, 76.f, 9.f, 81.f, 8.f, 87.f, 8.f, 92.f, 4.f, 94.f, 15.f,
+ 0.f, 28.f, 5.f, 24.f, 6.f, 24.f, 10.f, 27.f, 17.f, 24.f, 18.f, 24.f, 22.f, 27.f, 28.f, 35.f,
+ 0.f, 47.f, 7.f, 49.f, 12.f, 48.f, 18.f, 48.f, 23.f, 44.f, 25.f, 55.f, 27.f, 48.f, 32.f, 44.f, 33.f, 44.f, 37.f, 47.f, 44.f, 44.f, 45.f, 44.f, 49.f, 47.f, 55.f, 55.f, 59.f, 45.f, 70.f, 42.f, 77.f, 45.f, 82.f, 45.f, 90.f, 55.f,
+ 1.f, 65.f, 9.f, 65.f, 14.f, 62.f, 22.f, 65.f, 30.f, 75.f,
+ 87.f, 85.f, 79.f, 82.f, 74.f, 85.f, 66.f, 85.f, 61.f, 95.f, 53.f, 85.f, 48.f, 85.f, 41.f, 82.f, 32.f, 85.f, 27.f, 95.f, 20.f, 87.f, 15.f, 89.f, 8.f, 88.f, 5.f, 88.f, 4.f, 84.f, 0.f, 95.f,
+ 28.f, 108.f, 27.f, 104.f, 22.f, 104.f, 17.f, 107.f, 15.f, 104.f, 10.f, 104.f, 5.f, 107.f, 2.f, 113.f
+ };
+
+ Size textArea( 100.f, 300.f );
+
+ ReLayoutRightToLeftLinesData data =
+ {
+ "Paragraphs with right to left text.",
+ "שלום עולם مرحبا بالعالم\n"
+ "مرحبا بالعالم שלום עולם\n"
+ "שלום עולם مرحبا بالعالم.",
+ textArea,
+ 6u,
+ fontDescriptionRuns.Begin(),
+ 72u,
+ positions,
+ 48u,
+ 24u
+ };
+
+ if( !ReLayoutRightToLeftLinesTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextAlign01(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextAlign01");
+
+ // Calculate text alignment.
+
+ const std::string fontLatin( "TizenSans" );
+ const std::string fontHebrew( "TizenSansHebrew" );
+ const std::string fontArabic( "TizenSansArabic" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun01;
+ fontDescriptionRun01.characterRun.characterIndex = 0u;
+ fontDescriptionRun01.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun01.familyLength = fontLatin.size();
+ fontDescriptionRun01.familyName = new char[fontDescriptionRun01.familyLength];
+ memcpy( fontDescriptionRun01.familyName, fontLatin.c_str(), fontDescriptionRun01.familyLength );
+ fontDescriptionRun01.familyDefined = true;
+ fontDescriptionRun01.weightDefined = false;
+ fontDescriptionRun01.widthDefined = false;
+ fontDescriptionRun01.slantDefined = false;
+ fontDescriptionRun01.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun02;
+ fontDescriptionRun02.characterRun.characterIndex = 12u;
+ fontDescriptionRun02.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun02.familyLength = fontHebrew.size();
+ fontDescriptionRun02.familyName = new char[fontDescriptionRun02.familyLength];
+ memcpy( fontDescriptionRun02.familyName, fontHebrew.c_str(), fontDescriptionRun02.familyLength );
+ fontDescriptionRun02.familyDefined = true;
+ fontDescriptionRun02.weightDefined = false;
+ fontDescriptionRun02.widthDefined = false;
+ fontDescriptionRun02.slantDefined = false;
+ fontDescriptionRun02.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun03;
+ fontDescriptionRun03.characterRun.characterIndex = 22u;
+ fontDescriptionRun03.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun03.familyLength = fontArabic.size();
+ fontDescriptionRun03.familyName = new char[fontDescriptionRun03.familyLength];
+ memcpy( fontDescriptionRun03.familyName, fontArabic.c_str(), fontDescriptionRun03.familyLength );
+ fontDescriptionRun03.familyDefined = true;
+ fontDescriptionRun03.weightDefined = false;
+ fontDescriptionRun03.widthDefined = false;
+ fontDescriptionRun03.slantDefined = false;
+ fontDescriptionRun03.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun04;
+ fontDescriptionRun04.characterRun.characterIndex = 36u;
+ fontDescriptionRun04.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun04.familyLength = fontLatin.size();
+ fontDescriptionRun04.familyName = new char[fontDescriptionRun04.familyLength];
+ memcpy( fontDescriptionRun04.familyName, fontLatin.c_str(), fontDescriptionRun04.familyLength );
+ fontDescriptionRun04.familyDefined = true;
+ fontDescriptionRun04.weightDefined = false;
+ fontDescriptionRun04.widthDefined = false;
+ fontDescriptionRun04.slantDefined = false;
+ fontDescriptionRun04.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun05;
+ fontDescriptionRun05.characterRun.characterIndex = 48u;
+ fontDescriptionRun05.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun05.familyLength = fontLatin.size();
+ fontDescriptionRun05.familyName = new char[fontDescriptionRun05.familyLength];
+ memcpy( fontDescriptionRun05.familyName, fontLatin.c_str(), fontDescriptionRun05.familyLength );
+ fontDescriptionRun05.familyDefined = true;
+ fontDescriptionRun05.weightDefined = false;
+ fontDescriptionRun05.widthDefined = false;
+ fontDescriptionRun05.slantDefined = false;
+ fontDescriptionRun05.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun06;
+ fontDescriptionRun06.characterRun.characterIndex = 60u;
+ fontDescriptionRun06.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun06.familyLength = fontArabic.size();
+ fontDescriptionRun06.familyName = new char[fontDescriptionRun06.familyLength];
+ memcpy( fontDescriptionRun06.familyName, fontArabic.c_str(), fontDescriptionRun06.familyLength );
+ fontDescriptionRun06.familyDefined = true;
+ fontDescriptionRun06.weightDefined = false;
+ fontDescriptionRun06.widthDefined = false;
+ fontDescriptionRun06.slantDefined = false;
+ fontDescriptionRun06.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun01 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun02 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun03 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun04 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun05 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun06 );
+
+ float positions[] = { 0.f, 0.f, 0.f, 0.f, 0.f, 0.f };
+
+ Size textArea( 100.f, 300.f );
+ AlignData data =
+ {
+ "Begin alignment for the first paragraph.",
+ "Hello world שלום עולם\nمرحبا بالعالم Hello world\nHello world مرحبا بالعالم.",
+ textArea,
+ 6u,
+ fontDescriptionRuns.Begin(),
+ LayoutEngine::HORIZONTAL_ALIGN_BEGIN,
+ LayoutEngine::VERTICAL_ALIGN_TOP,
+ 0u,
+ 22u,
+ 6u,
+ positions
+ };
+
+ if( !AlignTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextAlign02(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextAlign02");
+
+ // Calculate text alignment.
+
+ const std::string fontLatin( "TizenSans" );
+ const std::string fontHebrew( "TizenSansHebrew" );
+ const std::string fontArabic( "TizenSansArabic" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun01;
+ fontDescriptionRun01.characterRun.characterIndex = 0u;
+ fontDescriptionRun01.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun01.familyLength = fontLatin.size();
+ fontDescriptionRun01.familyName = new char[fontDescriptionRun01.familyLength];
+ memcpy( fontDescriptionRun01.familyName, fontLatin.c_str(), fontDescriptionRun01.familyLength );
+ fontDescriptionRun01.familyDefined = true;
+ fontDescriptionRun01.weightDefined = false;
+ fontDescriptionRun01.widthDefined = false;
+ fontDescriptionRun01.slantDefined = false;
+ fontDescriptionRun01.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun02;
+ fontDescriptionRun02.characterRun.characterIndex = 12u;
+ fontDescriptionRun02.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun02.familyLength = fontHebrew.size();
+ fontDescriptionRun02.familyName = new char[fontDescriptionRun02.familyLength];
+ memcpy( fontDescriptionRun02.familyName, fontHebrew.c_str(), fontDescriptionRun02.familyLength );
+ fontDescriptionRun02.familyDefined = true;
+ fontDescriptionRun02.weightDefined = false;
+ fontDescriptionRun02.widthDefined = false;
+ fontDescriptionRun02.slantDefined = false;
+ fontDescriptionRun02.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun03;
+ fontDescriptionRun03.characterRun.characterIndex = 22u;
+ fontDescriptionRun03.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun03.familyLength = fontArabic.size();
+ fontDescriptionRun03.familyName = new char[fontDescriptionRun03.familyLength];
+ memcpy( fontDescriptionRun03.familyName, fontArabic.c_str(), fontDescriptionRun03.familyLength );
+ fontDescriptionRun03.familyDefined = true;
+ fontDescriptionRun03.weightDefined = false;
+ fontDescriptionRun03.widthDefined = false;
+ fontDescriptionRun03.slantDefined = false;
+ fontDescriptionRun03.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun04;
+ fontDescriptionRun04.characterRun.characterIndex = 36u;
+ fontDescriptionRun04.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun04.familyLength = fontLatin.size();
+ fontDescriptionRun04.familyName = new char[fontDescriptionRun04.familyLength];
+ memcpy( fontDescriptionRun04.familyName, fontLatin.c_str(), fontDescriptionRun04.familyLength );
+ fontDescriptionRun04.familyDefined = true;
+ fontDescriptionRun04.weightDefined = false;
+ fontDescriptionRun04.widthDefined = false;
+ fontDescriptionRun04.slantDefined = false;
+ fontDescriptionRun04.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun05;
+ fontDescriptionRun05.characterRun.characterIndex = 48u;
+ fontDescriptionRun05.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun05.familyLength = fontLatin.size();
+ fontDescriptionRun05.familyName = new char[fontDescriptionRun05.familyLength];
+ memcpy( fontDescriptionRun05.familyName, fontLatin.c_str(), fontDescriptionRun05.familyLength );
+ fontDescriptionRun05.familyDefined = true;
+ fontDescriptionRun05.weightDefined = false;
+ fontDescriptionRun05.widthDefined = false;
+ fontDescriptionRun05.slantDefined = false;
+ fontDescriptionRun05.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun06;
+ fontDescriptionRun06.characterRun.characterIndex = 60u;
+ fontDescriptionRun06.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun06.familyLength = fontArabic.size();
+ fontDescriptionRun06.familyName = new char[fontDescriptionRun06.familyLength];
+ memcpy( fontDescriptionRun06.familyName, fontArabic.c_str(), fontDescriptionRun06.familyLength );
+ fontDescriptionRun06.familyDefined = true;
+ fontDescriptionRun06.weightDefined = false;
+ fontDescriptionRun06.widthDefined = false;
+ fontDescriptionRun06.slantDefined = false;
+ fontDescriptionRun06.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun01 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun02 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun03 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun04 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun05 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun06 );
+
+ float positions[] = { 0.f, 0.f, 2.f, 60.f, 0.f, 0.f };
+
+ Size textArea( 100.f, 300.f );
+ AlignData data =
+ {
+ "Begin alignment for the mid paragraph.",
+ "Hello world שלום עולם\nمرحبا بالعالم Hello world\nHello world مرحبا بالعالم.",
+ textArea,
+ 6u,
+ fontDescriptionRuns.Begin(),
+ LayoutEngine::HORIZONTAL_ALIGN_BEGIN,
+ LayoutEngine::VERTICAL_ALIGN_TOP,
+ 22u,
+ 26u,
+ 6u,
+ positions
+ };
+
+ if( !AlignTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextAlign03(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextAlign03");
+
+ // Calculate text alignment.
+
+ const std::string fontLatin( "TizenSans" );
+ const std::string fontHebrew( "TizenSansHebrew" );
+ const std::string fontArabic( "TizenSansArabic" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun01;
+ fontDescriptionRun01.characterRun.characterIndex = 0u;
+ fontDescriptionRun01.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun01.familyLength = fontLatin.size();
+ fontDescriptionRun01.familyName = new char[fontDescriptionRun01.familyLength];
+ memcpy( fontDescriptionRun01.familyName, fontLatin.c_str(), fontDescriptionRun01.familyLength );
+ fontDescriptionRun01.familyDefined = true;
+ fontDescriptionRun01.weightDefined = false;
+ fontDescriptionRun01.widthDefined = false;
+ fontDescriptionRun01.slantDefined = false;
+ fontDescriptionRun01.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun02;
+ fontDescriptionRun02.characterRun.characterIndex = 12u;
+ fontDescriptionRun02.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun02.familyLength = fontHebrew.size();
+ fontDescriptionRun02.familyName = new char[fontDescriptionRun02.familyLength];
+ memcpy( fontDescriptionRun02.familyName, fontHebrew.c_str(), fontDescriptionRun02.familyLength );
+ fontDescriptionRun02.familyDefined = true;
+ fontDescriptionRun02.weightDefined = false;
+ fontDescriptionRun02.widthDefined = false;
+ fontDescriptionRun02.slantDefined = false;
+ fontDescriptionRun02.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun03;
+ fontDescriptionRun03.characterRun.characterIndex = 22u;
+ fontDescriptionRun03.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun03.familyLength = fontArabic.size();
+ fontDescriptionRun03.familyName = new char[fontDescriptionRun03.familyLength];
+ memcpy( fontDescriptionRun03.familyName, fontArabic.c_str(), fontDescriptionRun03.familyLength );
+ fontDescriptionRun03.familyDefined = true;
+ fontDescriptionRun03.weightDefined = false;
+ fontDescriptionRun03.widthDefined = false;
+ fontDescriptionRun03.slantDefined = false;
+ fontDescriptionRun03.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun04;
+ fontDescriptionRun04.characterRun.characterIndex = 36u;
+ fontDescriptionRun04.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun04.familyLength = fontLatin.size();
+ fontDescriptionRun04.familyName = new char[fontDescriptionRun04.familyLength];
+ memcpy( fontDescriptionRun04.familyName, fontLatin.c_str(), fontDescriptionRun04.familyLength );
+ fontDescriptionRun04.familyDefined = true;
+ fontDescriptionRun04.weightDefined = false;
+ fontDescriptionRun04.widthDefined = false;
+ fontDescriptionRun04.slantDefined = false;
+ fontDescriptionRun04.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun05;
+ fontDescriptionRun05.characterRun.characterIndex = 48u;
+ fontDescriptionRun05.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun05.familyLength = fontLatin.size();
+ fontDescriptionRun05.familyName = new char[fontDescriptionRun05.familyLength];
+ memcpy( fontDescriptionRun05.familyName, fontLatin.c_str(), fontDescriptionRun05.familyLength );
+ fontDescriptionRun05.familyDefined = true;
+ fontDescriptionRun05.weightDefined = false;
+ fontDescriptionRun05.widthDefined = false;
+ fontDescriptionRun05.slantDefined = false;
+ fontDescriptionRun05.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun06;
+ fontDescriptionRun06.characterRun.characterIndex = 60u;
+ fontDescriptionRun06.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun06.familyLength = fontArabic.size();
+ fontDescriptionRun06.familyName = new char[fontDescriptionRun06.familyLength];
+ memcpy( fontDescriptionRun06.familyName, fontArabic.c_str(), fontDescriptionRun06.familyLength );
+ fontDescriptionRun06.familyDefined = true;
+ fontDescriptionRun06.weightDefined = false;
+ fontDescriptionRun06.widthDefined = false;
+ fontDescriptionRun06.slantDefined = false;
+ fontDescriptionRun06.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun01 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun02 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun03 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun04 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun05 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun06 );
+
+ float positions[] = { 0.f, 0.f, 0.f, 0.f, 0.f, 0.f };
+
+ Size textArea( 100.f, 300.f );
+ AlignData data =
+ {
+ "Begin alignment for the last paragraph.",
+ "Hello world שלום עולם\nمرحبا بالعالم Hello world\nHello world مرحبا بالعالم.",
+ textArea,
+ 6u,
+ fontDescriptionRuns.Begin(),
+ LayoutEngine::HORIZONTAL_ALIGN_BEGIN,
+ LayoutEngine::VERTICAL_ALIGN_TOP,
+ 48u,
+ 26u,
+ 6u,
+ positions
+ };
+
+ if( !AlignTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextAlign04(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextAlign04");
+
+ // Calculate text alignment.
+
+ const std::string fontLatin( "TizenSans" );
+ const std::string fontHebrew( "TizenSansHebrew" );
+ const std::string fontArabic( "TizenSansArabic" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun01;
+ fontDescriptionRun01.characterRun.characterIndex = 0u;
+ fontDescriptionRun01.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun01.familyLength = fontLatin.size();
+ fontDescriptionRun01.familyName = new char[fontDescriptionRun01.familyLength];
+ memcpy( fontDescriptionRun01.familyName, fontLatin.c_str(), fontDescriptionRun01.familyLength );
+ fontDescriptionRun01.familyDefined = true;
+ fontDescriptionRun01.weightDefined = false;
+ fontDescriptionRun01.widthDefined = false;
+ fontDescriptionRun01.slantDefined = false;
+ fontDescriptionRun01.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun02;
+ fontDescriptionRun02.characterRun.characterIndex = 12u;
+ fontDescriptionRun02.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun02.familyLength = fontHebrew.size();
+ fontDescriptionRun02.familyName = new char[fontDescriptionRun02.familyLength];
+ memcpy( fontDescriptionRun02.familyName, fontHebrew.c_str(), fontDescriptionRun02.familyLength );
+ fontDescriptionRun02.familyDefined = true;
+ fontDescriptionRun02.weightDefined = false;
+ fontDescriptionRun02.widthDefined = false;
+ fontDescriptionRun02.slantDefined = false;
+ fontDescriptionRun02.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun03;
+ fontDescriptionRun03.characterRun.characterIndex = 22u;
+ fontDescriptionRun03.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun03.familyLength = fontArabic.size();
+ fontDescriptionRun03.familyName = new char[fontDescriptionRun03.familyLength];
+ memcpy( fontDescriptionRun03.familyName, fontArabic.c_str(), fontDescriptionRun03.familyLength );
+ fontDescriptionRun03.familyDefined = true;
+ fontDescriptionRun03.weightDefined = false;
+ fontDescriptionRun03.widthDefined = false;
+ fontDescriptionRun03.slantDefined = false;
+ fontDescriptionRun03.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun04;
+ fontDescriptionRun04.characterRun.characterIndex = 36u;
+ fontDescriptionRun04.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun04.familyLength = fontLatin.size();
+ fontDescriptionRun04.familyName = new char[fontDescriptionRun04.familyLength];
+ memcpy( fontDescriptionRun04.familyName, fontLatin.c_str(), fontDescriptionRun04.familyLength );
+ fontDescriptionRun04.familyDefined = true;
+ fontDescriptionRun04.weightDefined = false;
+ fontDescriptionRun04.widthDefined = false;
+ fontDescriptionRun04.slantDefined = false;
+ fontDescriptionRun04.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun05;
+ fontDescriptionRun05.characterRun.characterIndex = 48u;
+ fontDescriptionRun05.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun05.familyLength = fontLatin.size();
+ fontDescriptionRun05.familyName = new char[fontDescriptionRun05.familyLength];
+ memcpy( fontDescriptionRun05.familyName, fontLatin.c_str(), fontDescriptionRun05.familyLength );
+ fontDescriptionRun05.familyDefined = true;
+ fontDescriptionRun05.weightDefined = false;
+ fontDescriptionRun05.widthDefined = false;
+ fontDescriptionRun05.slantDefined = false;
+ fontDescriptionRun05.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun06;
+ fontDescriptionRun06.characterRun.characterIndex = 60u;
+ fontDescriptionRun06.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun06.familyLength = fontArabic.size();
+ fontDescriptionRun06.familyName = new char[fontDescriptionRun06.familyLength];
+ memcpy( fontDescriptionRun06.familyName, fontArabic.c_str(), fontDescriptionRun06.familyLength );
+ fontDescriptionRun06.familyDefined = true;
+ fontDescriptionRun06.weightDefined = false;
+ fontDescriptionRun06.widthDefined = false;
+ fontDescriptionRun06.slantDefined = false;
+ fontDescriptionRun06.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun01 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun02 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun03 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun04 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun05 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun06 );
+
+ float positions[] = { 10.f, 16.f, 0.f, 0.f, 0.f, 0.f };
+
+ Size textArea( 100.f, 300.f );
+ AlignData data =
+ {
+ "Center alignment for the first paragraph.",
+ "Hello world שלום עולם\nمرحبا بالعالم Hello world\nHello world مرحبا بالعالم.",
+ textArea,
+ 6u,
+ fontDescriptionRuns.Begin(),
+ LayoutEngine::HORIZONTAL_ALIGN_CENTER,
+ LayoutEngine::VERTICAL_ALIGN_TOP,
+ 0u,
+ 22u,
+ 6u,
+ positions
+ };
+
+ if( !AlignTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextAlign05(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextAlign05");
+
+ // Calculate text alignment.
+
+ const std::string fontLatin( "TizenSans" );
+ const std::string fontHebrew( "TizenSansHebrew" );
+ const std::string fontArabic( "TizenSansArabic" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun01;
+ fontDescriptionRun01.characterRun.characterIndex = 0u;
+ fontDescriptionRun01.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun01.familyLength = fontLatin.size();
+ fontDescriptionRun01.familyName = new char[fontDescriptionRun01.familyLength];
+ memcpy( fontDescriptionRun01.familyName, fontLatin.c_str(), fontDescriptionRun01.familyLength );
+ fontDescriptionRun01.familyDefined = true;
+ fontDescriptionRun01.weightDefined = false;
+ fontDescriptionRun01.widthDefined = false;
+ fontDescriptionRun01.slantDefined = false;
+ fontDescriptionRun01.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun02;
+ fontDescriptionRun02.characterRun.characterIndex = 12u;
+ fontDescriptionRun02.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun02.familyLength = fontHebrew.size();
+ fontDescriptionRun02.familyName = new char[fontDescriptionRun02.familyLength];
+ memcpy( fontDescriptionRun02.familyName, fontHebrew.c_str(), fontDescriptionRun02.familyLength );
+ fontDescriptionRun02.familyDefined = true;
+ fontDescriptionRun02.weightDefined = false;
+ fontDescriptionRun02.widthDefined = false;
+ fontDescriptionRun02.slantDefined = false;
+ fontDescriptionRun02.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun03;
+ fontDescriptionRun03.characterRun.characterIndex = 22u;
+ fontDescriptionRun03.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun03.familyLength = fontArabic.size();
+ fontDescriptionRun03.familyName = new char[fontDescriptionRun03.familyLength];
+ memcpy( fontDescriptionRun03.familyName, fontArabic.c_str(), fontDescriptionRun03.familyLength );
+ fontDescriptionRun03.familyDefined = true;
+ fontDescriptionRun03.weightDefined = false;
+ fontDescriptionRun03.widthDefined = false;
+ fontDescriptionRun03.slantDefined = false;
+ fontDescriptionRun03.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun04;
+ fontDescriptionRun04.characterRun.characterIndex = 36u;
+ fontDescriptionRun04.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun04.familyLength = fontLatin.size();
+ fontDescriptionRun04.familyName = new char[fontDescriptionRun04.familyLength];
+ memcpy( fontDescriptionRun04.familyName, fontLatin.c_str(), fontDescriptionRun04.familyLength );
+ fontDescriptionRun04.familyDefined = true;
+ fontDescriptionRun04.weightDefined = false;
+ fontDescriptionRun04.widthDefined = false;
+ fontDescriptionRun04.slantDefined = false;
+ fontDescriptionRun04.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun05;
+ fontDescriptionRun05.characterRun.characterIndex = 48u;
+ fontDescriptionRun05.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun05.familyLength = fontLatin.size();
+ fontDescriptionRun05.familyName = new char[fontDescriptionRun05.familyLength];
+ memcpy( fontDescriptionRun05.familyName, fontLatin.c_str(), fontDescriptionRun05.familyLength );
+ fontDescriptionRun05.familyDefined = true;
+ fontDescriptionRun05.weightDefined = false;
+ fontDescriptionRun05.widthDefined = false;
+ fontDescriptionRun05.slantDefined = false;
+ fontDescriptionRun05.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun06;
+ fontDescriptionRun06.characterRun.characterIndex = 60u;
+ fontDescriptionRun06.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun06.familyLength = fontArabic.size();
+ fontDescriptionRun06.familyName = new char[fontDescriptionRun06.familyLength];
+ memcpy( fontDescriptionRun06.familyName, fontArabic.c_str(), fontDescriptionRun06.familyLength );
+ fontDescriptionRun06.familyDefined = true;
+ fontDescriptionRun06.weightDefined = false;
+ fontDescriptionRun06.widthDefined = false;
+ fontDescriptionRun06.slantDefined = false;
+ fontDescriptionRun06.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun01 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun02 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun03 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun04 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun05 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun06 );
+
+ float positions[] = { 0.f, 0.f, -1.f, 30.f, 0.f, 0.f };
+
+ Size textArea( 100.f, 300.f );
+ AlignData data =
+ {
+ "Center alignment for the mid paragraph.",
+ "Hello world שלום עולם\nمرحبا بالعالم Hello world\nHello world مرحبا بالعالم.",
+ textArea,
+ 6u,
+ fontDescriptionRuns.Begin(),
+ LayoutEngine::HORIZONTAL_ALIGN_CENTER,
+ LayoutEngine::VERTICAL_ALIGN_TOP,
+ 22u,
+ 26u,
+ 6u,
+ positions
+ };
+
+ if( !AlignTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextAlign06(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextAlign06");
+
+ // Calculate text alignment.
+
+ const std::string fontLatin( "TizenSans" );
+ const std::string fontHebrew( "TizenSansHebrew" );
+ const std::string fontArabic( "TizenSansArabic" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun01;
+ fontDescriptionRun01.characterRun.characterIndex = 0u;
+ fontDescriptionRun01.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun01.familyLength = fontLatin.size();
+ fontDescriptionRun01.familyName = new char[fontDescriptionRun01.familyLength];
+ memcpy( fontDescriptionRun01.familyName, fontLatin.c_str(), fontDescriptionRun01.familyLength );
+ fontDescriptionRun01.familyDefined = true;
+ fontDescriptionRun01.weightDefined = false;
+ fontDescriptionRun01.widthDefined = false;
+ fontDescriptionRun01.slantDefined = false;
+ fontDescriptionRun01.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun02;
+ fontDescriptionRun02.characterRun.characterIndex = 12u;
+ fontDescriptionRun02.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun02.familyLength = fontHebrew.size();
+ fontDescriptionRun02.familyName = new char[fontDescriptionRun02.familyLength];
+ memcpy( fontDescriptionRun02.familyName, fontHebrew.c_str(), fontDescriptionRun02.familyLength );
+ fontDescriptionRun02.familyDefined = true;
+ fontDescriptionRun02.weightDefined = false;
+ fontDescriptionRun02.widthDefined = false;
+ fontDescriptionRun02.slantDefined = false;
+ fontDescriptionRun02.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun03;
+ fontDescriptionRun03.characterRun.characterIndex = 22u;
+ fontDescriptionRun03.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun03.familyLength = fontArabic.size();
+ fontDescriptionRun03.familyName = new char[fontDescriptionRun03.familyLength];
+ memcpy( fontDescriptionRun03.familyName, fontArabic.c_str(), fontDescriptionRun03.familyLength );
+ fontDescriptionRun03.familyDefined = true;
+ fontDescriptionRun03.weightDefined = false;
+ fontDescriptionRun03.widthDefined = false;
+ fontDescriptionRun03.slantDefined = false;
+ fontDescriptionRun03.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun04;
+ fontDescriptionRun04.characterRun.characterIndex = 36u;
+ fontDescriptionRun04.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun04.familyLength = fontLatin.size();
+ fontDescriptionRun04.familyName = new char[fontDescriptionRun04.familyLength];
+ memcpy( fontDescriptionRun04.familyName, fontLatin.c_str(), fontDescriptionRun04.familyLength );
+ fontDescriptionRun04.familyDefined = true;
+ fontDescriptionRun04.weightDefined = false;
+ fontDescriptionRun04.widthDefined = false;
+ fontDescriptionRun04.slantDefined = false;
+ fontDescriptionRun04.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun05;
+ fontDescriptionRun05.characterRun.characterIndex = 48u;
+ fontDescriptionRun05.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun05.familyLength = fontLatin.size();
+ fontDescriptionRun05.familyName = new char[fontDescriptionRun05.familyLength];
+ memcpy( fontDescriptionRun05.familyName, fontLatin.c_str(), fontDescriptionRun05.familyLength );
+ fontDescriptionRun05.familyDefined = true;
+ fontDescriptionRun05.weightDefined = false;
+ fontDescriptionRun05.widthDefined = false;
+ fontDescriptionRun05.slantDefined = false;
+ fontDescriptionRun05.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun06;
+ fontDescriptionRun06.characterRun.characterIndex = 60u;
+ fontDescriptionRun06.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun06.familyLength = fontArabic.size();
+ fontDescriptionRun06.familyName = new char[fontDescriptionRun06.familyLength];
+ memcpy( fontDescriptionRun06.familyName, fontArabic.c_str(), fontDescriptionRun06.familyLength );
+ fontDescriptionRun06.familyDefined = true;
+ fontDescriptionRun06.weightDefined = false;
+ fontDescriptionRun06.widthDefined = false;
+ fontDescriptionRun06.slantDefined = false;
+ fontDescriptionRun06.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun01 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun02 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun03 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun04 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun05 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun06 );
+
+ float positions[] = { 0.f, 0.f, 0.f, 0.f, 10.f, 20.f };
+
+ Size textArea( 100.f, 300.f );
+ AlignData data =
+ {
+ "Center alignment for the last paragraph.",
+ "Hello world שלום עולם\nمرحبا بالعالم Hello world\nHello world مرحبا بالعالم.",
+ textArea,
+ 6u,
+ fontDescriptionRuns.Begin(),
+ LayoutEngine::HORIZONTAL_ALIGN_CENTER,
+ LayoutEngine::VERTICAL_ALIGN_TOP,
+ 48u,
+ 26u,
+ 6u,
+ positions
+ };
+
+ if( !AlignTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextAlign07(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextAlign07");
+
+ // Calculate text alignment.
+
+ const std::string fontLatin( "TizenSans" );
+ const std::string fontHebrew( "TizenSansHebrew" );
+ const std::string fontArabic( "TizenSansArabic" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun01;
+ fontDescriptionRun01.characterRun.characterIndex = 0u;
+ fontDescriptionRun01.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun01.familyLength = fontLatin.size();
+ fontDescriptionRun01.familyName = new char[fontDescriptionRun01.familyLength];
+ memcpy( fontDescriptionRun01.familyName, fontLatin.c_str(), fontDescriptionRun01.familyLength );
+ fontDescriptionRun01.familyDefined = true;
+ fontDescriptionRun01.weightDefined = false;
+ fontDescriptionRun01.widthDefined = false;
+ fontDescriptionRun01.slantDefined = false;
+ fontDescriptionRun01.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun02;
+ fontDescriptionRun02.characterRun.characterIndex = 12u;
+ fontDescriptionRun02.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun02.familyLength = fontHebrew.size();
+ fontDescriptionRun02.familyName = new char[fontDescriptionRun02.familyLength];
+ memcpy( fontDescriptionRun02.familyName, fontHebrew.c_str(), fontDescriptionRun02.familyLength );
+ fontDescriptionRun02.familyDefined = true;
+ fontDescriptionRun02.weightDefined = false;
+ fontDescriptionRun02.widthDefined = false;
+ fontDescriptionRun02.slantDefined = false;
+ fontDescriptionRun02.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun03;
+ fontDescriptionRun03.characterRun.characterIndex = 22u;
+ fontDescriptionRun03.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun03.familyLength = fontArabic.size();
+ fontDescriptionRun03.familyName = new char[fontDescriptionRun03.familyLength];
+ memcpy( fontDescriptionRun03.familyName, fontArabic.c_str(), fontDescriptionRun03.familyLength );
+ fontDescriptionRun03.familyDefined = true;
+ fontDescriptionRun03.weightDefined = false;
+ fontDescriptionRun03.widthDefined = false;
+ fontDescriptionRun03.slantDefined = false;
+ fontDescriptionRun03.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun04;
+ fontDescriptionRun04.characterRun.characterIndex = 36u;
+ fontDescriptionRun04.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun04.familyLength = fontLatin.size();
+ fontDescriptionRun04.familyName = new char[fontDescriptionRun04.familyLength];
+ memcpy( fontDescriptionRun04.familyName, fontLatin.c_str(), fontDescriptionRun04.familyLength );
+ fontDescriptionRun04.familyDefined = true;
+ fontDescriptionRun04.weightDefined = false;
+ fontDescriptionRun04.widthDefined = false;
+ fontDescriptionRun04.slantDefined = false;
+ fontDescriptionRun04.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun05;
+ fontDescriptionRun05.characterRun.characterIndex = 48u;
+ fontDescriptionRun05.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun05.familyLength = fontLatin.size();
+ fontDescriptionRun05.familyName = new char[fontDescriptionRun05.familyLength];
+ memcpy( fontDescriptionRun05.familyName, fontLatin.c_str(), fontDescriptionRun05.familyLength );
+ fontDescriptionRun05.familyDefined = true;
+ fontDescriptionRun05.weightDefined = false;
+ fontDescriptionRun05.widthDefined = false;
+ fontDescriptionRun05.slantDefined = false;
+ fontDescriptionRun05.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun06;
+ fontDescriptionRun06.characterRun.characterIndex = 60u;
+ fontDescriptionRun06.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun06.familyLength = fontArabic.size();
+ fontDescriptionRun06.familyName = new char[fontDescriptionRun06.familyLength];
+ memcpy( fontDescriptionRun06.familyName, fontArabic.c_str(), fontDescriptionRun06.familyLength );
+ fontDescriptionRun06.familyDefined = true;
+ fontDescriptionRun06.weightDefined = false;
+ fontDescriptionRun06.widthDefined = false;
+ fontDescriptionRun06.slantDefined = false;
+ fontDescriptionRun06.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun01 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun02 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun03 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun04 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun05 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun06 );
+
+ float positions[] = { 20.f, 33.f, 0.f, 0.f, 0.f, 0.f };
+
+ Size textArea( 100.f, 300.f );
+ AlignData data =
+ {
+ "End alignment for the first paragraph.",
+ "Hello world שלום עולם\nمرحبا بالعالم Hello world\nHello world مرحبا بالعالم.",
+ textArea,
+ 6u,
+ fontDescriptionRuns.Begin(),
+ LayoutEngine::HORIZONTAL_ALIGN_END,
+ LayoutEngine::VERTICAL_ALIGN_TOP,
+ 0u,
+ 22u,
+ 6u,
+ positions
+ };
+
+ if( !AlignTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextAlign08(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextAlign08");
+
+ // Calculate text alignment.
+
+ const std::string fontLatin( "TizenSans" );
+ const std::string fontHebrew( "TizenSansHebrew" );
+ const std::string fontArabic( "TizenSansArabic" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun01;
+ fontDescriptionRun01.characterRun.characterIndex = 0u;
+ fontDescriptionRun01.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun01.familyLength = fontLatin.size();
+ fontDescriptionRun01.familyName = new char[fontDescriptionRun01.familyLength];
+ memcpy( fontDescriptionRun01.familyName, fontLatin.c_str(), fontDescriptionRun01.familyLength );
+ fontDescriptionRun01.familyDefined = true;
+ fontDescriptionRun01.weightDefined = false;
+ fontDescriptionRun01.widthDefined = false;
+ fontDescriptionRun01.slantDefined = false;
+ fontDescriptionRun01.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun02;
+ fontDescriptionRun02.characterRun.characterIndex = 12u;
+ fontDescriptionRun02.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun02.familyLength = fontHebrew.size();
+ fontDescriptionRun02.familyName = new char[fontDescriptionRun02.familyLength];
+ memcpy( fontDescriptionRun02.familyName, fontHebrew.c_str(), fontDescriptionRun02.familyLength );
+ fontDescriptionRun02.familyDefined = true;
+ fontDescriptionRun02.weightDefined = false;
+ fontDescriptionRun02.widthDefined = false;
+ fontDescriptionRun02.slantDefined = false;
+ fontDescriptionRun02.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun03;
+ fontDescriptionRun03.characterRun.characterIndex = 22u;
+ fontDescriptionRun03.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun03.familyLength = fontArabic.size();
+ fontDescriptionRun03.familyName = new char[fontDescriptionRun03.familyLength];
+ memcpy( fontDescriptionRun03.familyName, fontArabic.c_str(), fontDescriptionRun03.familyLength );
+ fontDescriptionRun03.familyDefined = true;
+ fontDescriptionRun03.weightDefined = false;
+ fontDescriptionRun03.widthDefined = false;
+ fontDescriptionRun03.slantDefined = false;
+ fontDescriptionRun03.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun04;
+ fontDescriptionRun04.characterRun.characterIndex = 36u;
+ fontDescriptionRun04.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun04.familyLength = fontLatin.size();
+ fontDescriptionRun04.familyName = new char[fontDescriptionRun04.familyLength];
+ memcpy( fontDescriptionRun04.familyName, fontLatin.c_str(), fontDescriptionRun04.familyLength );
+ fontDescriptionRun04.familyDefined = true;
+ fontDescriptionRun04.weightDefined = false;
+ fontDescriptionRun04.widthDefined = false;
+ fontDescriptionRun04.slantDefined = false;
+ fontDescriptionRun04.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun05;
+ fontDescriptionRun05.characterRun.characterIndex = 48u;
+ fontDescriptionRun05.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun05.familyLength = fontLatin.size();
+ fontDescriptionRun05.familyName = new char[fontDescriptionRun05.familyLength];
+ memcpy( fontDescriptionRun05.familyName, fontLatin.c_str(), fontDescriptionRun05.familyLength );
+ fontDescriptionRun05.familyDefined = true;
+ fontDescriptionRun05.weightDefined = false;
+ fontDescriptionRun05.widthDefined = false;
+ fontDescriptionRun05.slantDefined = false;
+ fontDescriptionRun05.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun06;
+ fontDescriptionRun06.characterRun.characterIndex = 60u;
+ fontDescriptionRun06.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun06.familyLength = fontArabic.size();
+ fontDescriptionRun06.familyName = new char[fontDescriptionRun06.familyLength];
+ memcpy( fontDescriptionRun06.familyName, fontArabic.c_str(), fontDescriptionRun06.familyLength );
+ fontDescriptionRun06.familyDefined = true;
+ fontDescriptionRun06.weightDefined = false;
+ fontDescriptionRun06.widthDefined = false;
+ fontDescriptionRun06.slantDefined = false;
+ fontDescriptionRun06.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun01 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun02 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun03 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun04 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun05 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun06 );
+
+ float positions[] = { 0.f, 0.f, -4.f, 0.f, 0.f, 0.f };
+
+ Size textArea( 100.f, 300.f );
+ AlignData data =
+ {
+ "End alignment for the mid paragraph.",
+ "Hello world שלום עולם\nمرحبا بالعالم Hello world\nHello world مرحبا بالعالم.",
+ textArea,
+ 6u,
+ fontDescriptionRuns.Begin(),
+ LayoutEngine::HORIZONTAL_ALIGN_END,
+ LayoutEngine::VERTICAL_ALIGN_TOP,
+ 22u,
+ 26u,
+ 6u,
+ positions
+ };
+
+ if( !AlignTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
+
+int UtcDaliTextAlign09(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliTextAlign09");
+
+ // Calculate text alignment.
+
+ const std::string fontLatin( "TizenSans" );
+ const std::string fontHebrew( "TizenSansHebrew" );
+ const std::string fontArabic( "TizenSansArabic" );
+
+ // Set a known font description
+ FontDescriptionRun fontDescriptionRun01;
+ fontDescriptionRun01.characterRun.characterIndex = 0u;
+ fontDescriptionRun01.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun01.familyLength = fontLatin.size();
+ fontDescriptionRun01.familyName = new char[fontDescriptionRun01.familyLength];
+ memcpy( fontDescriptionRun01.familyName, fontLatin.c_str(), fontDescriptionRun01.familyLength );
+ fontDescriptionRun01.familyDefined = true;
+ fontDescriptionRun01.weightDefined = false;
+ fontDescriptionRun01.widthDefined = false;
+ fontDescriptionRun01.slantDefined = false;
+ fontDescriptionRun01.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun02;
+ fontDescriptionRun02.characterRun.characterIndex = 12u;
+ fontDescriptionRun02.characterRun.numberOfCharacters = 10u;
+ fontDescriptionRun02.familyLength = fontHebrew.size();
+ fontDescriptionRun02.familyName = new char[fontDescriptionRun02.familyLength];
+ memcpy( fontDescriptionRun02.familyName, fontHebrew.c_str(), fontDescriptionRun02.familyLength );
+ fontDescriptionRun02.familyDefined = true;
+ fontDescriptionRun02.weightDefined = false;
+ fontDescriptionRun02.widthDefined = false;
+ fontDescriptionRun02.slantDefined = false;
+ fontDescriptionRun02.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun03;
+ fontDescriptionRun03.characterRun.characterIndex = 22u;
+ fontDescriptionRun03.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun03.familyLength = fontArabic.size();
+ fontDescriptionRun03.familyName = new char[fontDescriptionRun03.familyLength];
+ memcpy( fontDescriptionRun03.familyName, fontArabic.c_str(), fontDescriptionRun03.familyLength );
+ fontDescriptionRun03.familyDefined = true;
+ fontDescriptionRun03.weightDefined = false;
+ fontDescriptionRun03.widthDefined = false;
+ fontDescriptionRun03.slantDefined = false;
+ fontDescriptionRun03.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun04;
+ fontDescriptionRun04.characterRun.characterIndex = 36u;
+ fontDescriptionRun04.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun04.familyLength = fontLatin.size();
+ fontDescriptionRun04.familyName = new char[fontDescriptionRun04.familyLength];
+ memcpy( fontDescriptionRun04.familyName, fontLatin.c_str(), fontDescriptionRun04.familyLength );
+ fontDescriptionRun04.familyDefined = true;
+ fontDescriptionRun04.weightDefined = false;
+ fontDescriptionRun04.widthDefined = false;
+ fontDescriptionRun04.slantDefined = false;
+ fontDescriptionRun04.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun05;
+ fontDescriptionRun05.characterRun.characterIndex = 48u;
+ fontDescriptionRun05.characterRun.numberOfCharacters = 12u;
+ fontDescriptionRun05.familyLength = fontLatin.size();
+ fontDescriptionRun05.familyName = new char[fontDescriptionRun05.familyLength];
+ memcpy( fontDescriptionRun05.familyName, fontLatin.c_str(), fontDescriptionRun05.familyLength );
+ fontDescriptionRun05.familyDefined = true;
+ fontDescriptionRun05.weightDefined = false;
+ fontDescriptionRun05.widthDefined = false;
+ fontDescriptionRun05.slantDefined = false;
+ fontDescriptionRun05.sizeDefined = false;
+
+ FontDescriptionRun fontDescriptionRun06;
+ fontDescriptionRun06.characterRun.characterIndex = 60u;
+ fontDescriptionRun06.characterRun.numberOfCharacters = 14u;
+ fontDescriptionRun06.familyLength = fontArabic.size();
+ fontDescriptionRun06.familyName = new char[fontDescriptionRun06.familyLength];
+ memcpy( fontDescriptionRun06.familyName, fontArabic.c_str(), fontDescriptionRun06.familyLength );
+ fontDescriptionRun06.familyDefined = true;
+ fontDescriptionRun06.weightDefined = false;
+ fontDescriptionRun06.widthDefined = false;
+ fontDescriptionRun06.slantDefined = false;
+ fontDescriptionRun06.sizeDefined = false;
+
+ Vector<FontDescriptionRun> fontDescriptionRuns;
+ fontDescriptionRuns.PushBack( fontDescriptionRun01 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun02 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun03 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun04 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun05 );
+ fontDescriptionRuns.PushBack( fontDescriptionRun06 );
+
+ float positions[] = { 0.f, 0.f, 0.f, 0.f, 20.f, 40.f };
+
+ Size textArea( 100.f, 300.f );
+ AlignData data =
+ {
+ "End alignment for the last paragraph.",
+ "Hello world שלום עולם\nمرحبا بالعالم Hello world\nHello world مرحبا بالعالم.",
+ textArea,
+ 6u,
+ fontDescriptionRuns.Begin(),
+ LayoutEngine::HORIZONTAL_ALIGN_END,
+ LayoutEngine::VERTICAL_ALIGN_TOP,
+ 48u,
+ 26u,
+ 6u,
+ positions
+ };
+
+ if( !AlignTest( data ) )
+ {
+ tet_result(TET_FAIL);
+ }
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
const FontId defaultFontId = fontClient.GetFontId( pathName + DEFAULT_FONT_DIR + data.defaultFont,
data.defaultFontSize );
- // To be completed ...
Vector<FontRun> fontRuns;
// 3) Validate the fonts.
Size textArea(100.f, 60.f);
Size layoutSize;
+ const Vector<FontDescriptionRun> fontDescriptions;
+ const LayoutOptions options;
CreateTextModel( data.text,
textArea,
+ fontDescriptions,
+ options,
layoutSize,
logicalModel,
visualModel );
Size textArea(100.f, 60.f);
Size layoutSize;
+ const Vector<FontDescriptionRun> fontDescriptions;
+ const LayoutOptions options;
CreateTextModel( data.text,
textArea,
+ fontDescriptions,
+ options,
layoutSize,
logicalModel,
visualModel );
Size textArea(100.f, 60.f);
Size layoutSize;
+ const Vector<FontDescriptionRun> fontDescriptions;
+ const LayoutOptions options;
CreateTextModel( data.text,
textArea,
+ fontDescriptions,
+ options,
layoutSize,
logicalModel,
visualModel );
dali-toolkit-test-utils/toolkit-tts-player.cpp
dali-toolkit-test-utils/dummy-control.cpp
dali-toolkit-test-utils/dali-test-suite-utils.cpp
+ dali-toolkit-test-utils/test-animation-data.cpp
+ dali-toolkit-test-utils/test-button.cpp
dali-toolkit-test-utils/test-application.cpp
dali-toolkit-test-utils/test-platform-abstraction.cpp
dali-toolkit-test-utils/test-gesture-manager.cpp
--- /dev/null
+/*
+ * Copyright (c) 2016 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 <dali/dali.h>
+#include <test-animation-data.h>
+
+using namespace Dali;
+
+namespace Test
+{
+
+TestAnimationData::TestAnimationData()
+{
+}
+
+TestAnimationData::~TestAnimationData()
+{
+}
+
+TestAnimationData::AnimationDataElement::AnimationDataElement()
+: alphaFunction( AlphaFunction::DEFAULT ),
+ timePeriodDelay( 0.0f ),
+ timePeriodDuration( 1.0f )
+{
+}
+
+void TestAnimationData::Add( AnimationDataElement* animationDataElement )
+{
+ mAnimationDataList.PushBack( animationDataElement );
+}
+
+std::size_t TestAnimationData::Size() const
+{
+ return mAnimationDataList.Size();
+}
+
+void TestAnimationData::Clear()
+{
+ AnimationDataList::Iterator end = mAnimationDataList.End();
+ for( AnimationDataList::Iterator iter = mAnimationDataList.Begin(); iter != end; ++iter )
+ {
+ delete ( *iter );
+ }
+ mAnimationDataList.Clear();
+}
+
+
+void NewAnimator( const Property::Map& map, TestAnimationData::AnimationDataElement& element )
+{
+ // Now set the properties, or create children
+ for( unsigned int i = 0, animationMapCount = map.Count(); i < animationMapCount; ++i )
+ {
+ const StringValuePair& pair( map.GetPair( i ) );
+ const std::string& key( pair.first );
+ const Property::Value& value( pair.second );
+
+ if( key == "actor" || key == "target" )
+ {
+ element.target = value.Get< std::string >();
+ }
+ else if( key == "property" )
+ {
+ element.property = value.Get< std::string >();
+ }
+ else if( key == "value" )
+ {
+ element.value = value;
+ }
+ else if( key == "alphaFunction" )
+ {
+ std::string alphaFunctionValue = value.Get< std::string >();
+
+ if( alphaFunctionValue == "LINEAR" )
+ {
+ element.alphaFunction = AlphaFunction::LINEAR;
+ }
+ else if( alphaFunctionValue == "REVERSE" )
+ {
+ element.alphaFunction = AlphaFunction::REVERSE;
+ }
+ else if( alphaFunctionValue == "EASE_IN_SQUARE" )
+ {
+ element.alphaFunction = AlphaFunction::EASE_IN_SQUARE;
+ }
+ else if( alphaFunctionValue == "EASE_OUT_SQUARE" )
+ {
+ element.alphaFunction = AlphaFunction::EASE_OUT_SQUARE;
+ }
+ else if( alphaFunctionValue == "EASE_IN" )
+ {
+ element.alphaFunction = AlphaFunction::EASE_IN;
+ }
+ else if( alphaFunctionValue == "EASE_OUT" )
+ {
+ element.alphaFunction = AlphaFunction::EASE_OUT;
+ }
+ else if( alphaFunctionValue == "EASE_IN_OUT" )
+ {
+ element.alphaFunction = AlphaFunction::EASE_IN_OUT;
+ }
+ else if( alphaFunctionValue == "EASE_IN_SINE" )
+ {
+ element.alphaFunction = AlphaFunction::EASE_IN_SINE;
+ }
+ else if( alphaFunctionValue == "EASE_OUT_SINE" )
+ {
+ element.alphaFunction = AlphaFunction::EASE_OUT_SINE;
+ }
+ else if( alphaFunctionValue == "EASE_IN_OUT_SINE" )
+ {
+ element.alphaFunction = AlphaFunction::EASE_IN_OUT_SINE;
+ }
+ else if( alphaFunctionValue == "BOUNCE" )
+ {
+ element.alphaFunction = AlphaFunction::BOUNCE;
+ }
+ else if( alphaFunctionValue == "SIN" )
+ {
+ element.alphaFunction = AlphaFunction::SIN;
+ }
+ else if( alphaFunctionValue == "EASE_OUT_BACK" )
+ {
+ element.alphaFunction = AlphaFunction::EASE_OUT_BACK;
+ }
+ }
+ else if( key == "timePeriod" )
+ {
+ Property::Map timeMap = value.Get< Property::Map >();
+ for( unsigned int i = 0; i < timeMap.Count(); ++i )
+ {
+ const StringValuePair& pair( timeMap.GetPair( i ) );
+ if( pair.first == "delay" )
+ {
+ element.timePeriodDelay = pair.second.Get< float >();
+ }
+ else if( pair.first == "duration" )
+ {
+ element.timePeriodDuration = pair.second.Get< float >();
+ }
+ }
+ }
+ else if( key == "animator" )
+ {
+ if( value.GetType() == Property::MAP )
+ {
+ Property::Map* map = value.GetMap();
+ const Property::Map& mapref = *map;
+ NewAnimator( mapref, element ); // Merge the map into element
+ }
+ }
+ }
+}
+
+void NewAnimation( const Property::Map& map, TestAnimationData& outputAnimationData )
+{
+ TestAnimationData::AnimationDataElement* element = new TestAnimationData::AnimationDataElement();
+ NewAnimator( map, *element );
+
+ outputAnimationData.Add( element );
+}
+
+void NewAnimation( const Property::Array& array, TestAnimationData& outputAnimationData )
+{
+ for(unsigned int i=0; i<array.Size(); ++i )
+ {
+ TestAnimationData::AnimationDataElement* element = new TestAnimationData::AnimationDataElement();
+ const Property::Value& value = array.GetElementAt(i);
+ if( value.GetType() == Property::MAP )
+ {
+ Property::Map* map = value.GetMap();
+ NewAnimator( *map, *element );
+ outputAnimationData.Add( element );
+ }
+ }
+}
+
+} // Test
--- /dev/null
+#ifndef DALI_TOOLKIT_TEST_TEST_ANIMATION_DATA_H
+#define DALI_TOOLKIT_TEST_TEST_ANIMATION_DATA_H
+
+/*
+ * Copyright (c) 2016 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 <dali/dali.h>
+#include <string>
+
+namespace Test
+{
+
+class TestAnimationData
+{
+public:
+ TestAnimationData();
+ ~TestAnimationData();
+
+ /**
+ * @brief AnimationDataElement Describes one part of an animation.
+ */
+ struct AnimationDataElement
+ {
+ std::string target;
+ std::string property;
+ Dali::Property::Value value;
+ Dali::AlphaFunction::BuiltinFunction alphaFunction;
+ float timePeriodDelay;
+ float timePeriodDuration;
+
+ AnimationDataElement();
+ };
+
+ /**
+ * @brief AnimationData holds the required data required to define an
+ * animation to be performed on a property source.
+ */
+ typedef Dali::Vector< AnimationDataElement* > AnimationDataList;
+
+ /**
+ * @brief Adds one AnimationDataElement to the list to describe one animation.
+ * @param[in] animationDataElement A pre-populated struct to add
+ */
+ void Add( AnimationDataElement* animationDataElement );
+
+ std::size_t Size() const;
+
+ void Clear();
+
+ AnimationDataList mAnimationDataList;
+};
+
+void NewAnimator( const Dali::Property::Map& map, TestAnimationData::AnimationDataElement& element );
+void NewAnimation( const Dali::Property::Map& map, TestAnimationData& outputAnimationData );
+void NewAnimation( const Dali::Property::Array& array, TestAnimationData& outputAnimationData );
+} // Test
+
+#endif //DALI_TOOLKIT_TEST_TEST_ANIMATION_DATA_H
--- /dev/null
+/*
+ * Copyright (c) 2016 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 <dali/dali.h>
+#include <dali-toolkit/dali-toolkit.h>
+#include <test-button.h>
+#include <dali/devel-api/object/type-registry-helper.h>
+
+using namespace Dali;
+using namespace Dali::Toolkit;
+
+namespace
+{
+Property::Value ConvertAnimationMap( const Test::TestAnimationData& animationMap )
+{
+ // We have a data structure. Now convert it back into an array:
+ Property::Array animators;
+ for( unsigned int i=0; i<animationMap.Size(); ++i )
+ {
+ Property::Map animator;
+ animator.Insert( "target", Property::Value(animationMap.mAnimationDataList[i]->target ));
+ animator.Insert( "property", Property::Value(animationMap.mAnimationDataList[i]->property ));
+ animator.Insert( "value", Property::Value(animationMap.mAnimationDataList[i]->value ));
+ animator.Insert( "alphaFunction", Property::Value(animationMap.mAnimationDataList[i]->alphaFunction ));
+ animator.Insert( "timePeriodDelay", Property::Value(animationMap.mAnimationDataList[i]->timePeriodDelay ));
+ animator.Insert( "timePeriodDuration", Property::Value(animationMap.mAnimationDataList[i]->timePeriodDuration ));
+ animators.PushBack( animator );
+ }
+ Property::Value animation( animators );
+ return animation;
+}
+}
+
+namespace Test
+{
+namespace Impl
+{
+
+Test::TestButton TestButton::New()
+{
+ IntrusivePtr<TestButton> internalTestButton = new TestButton();
+ Test::TestButton button( *internalTestButton );
+ internalTestButton->Initialize();
+ return button;
+}
+
+TestButton::TestButton()
+: Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS|REQUIRES_STYLE_CHANGE_SIGNALS ) )
+{
+}
+
+TestButton::~TestButton()
+{
+}
+
+void TestButton::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
+{
+ Test::TestButton button = Test::TestButton::DownCast( Dali::BaseHandle( object ) );
+
+ if ( button )
+ {
+ switch ( index )
+ {
+ case Test::TestButton::Property::PRESS_TRANSITION:
+ {
+ if( value.GetType() == Property::MAP )
+ {
+ Property::Map* valueMap = value.GetMap();
+ TestButton& buttonImpl = GetImpl(button);
+ buttonImpl.mPressTransitionData.Clear();
+ NewAnimation( *valueMap, buttonImpl.mPressTransitionData );
+ }
+ else if( value.GetType() == Property::ARRAY )
+ {
+ Property::Array* valueArray = value.GetArray();
+ TestButton& buttonImpl = GetImpl(button);
+ buttonImpl.mPressTransitionData.Clear();
+ NewAnimation( *valueArray, buttonImpl.mPressTransitionData );
+ }
+ break;
+ }
+ }
+ }
+}
+
+Property::Value TestButton::GetProperty( BaseObject* object, Property::Index propertyIndex )
+{
+ Test::TestButton button = Test::TestButton::DownCast( Dali::BaseHandle( object ) );
+
+ if ( button )
+ {
+ TestButton& buttonImpl = GetImpl(button);
+ switch ( propertyIndex )
+ {
+ case Test::TestButton::Property::PRESS_TRANSITION:
+ {
+ return ConvertAnimationMap(buttonImpl.mPressTransitionData);
+ break;
+ }
+ case Test::TestButton::Property::RELEASE_TRANSITION:
+ {
+ return ConvertAnimationMap(buttonImpl.mReleaseTransitionData);
+ break;
+ }
+ case Test::TestButton::Property::DISABLED_TRANSITION:
+ {
+ return ConvertAnimationMap(buttonImpl.mDisabledTransitionData);
+ break;
+ }
+ case Test::TestButton::Property::ENABLED_TRANSITION:
+ {
+ return ConvertAnimationMap(buttonImpl.mEnabledTransitionData);
+ break;
+ }
+ }
+ }
+ return Property::Value();
+}
+
+BaseHandle Create()
+{
+ return TestButton::New();
+}
+
+// Generates typeRegistration static variable.
+DALI_TYPE_REGISTRATION_BEGIN( Test::TestButton, Dali::Toolkit::Control, Create )
+
+DALI_PROPERTY_REGISTRATION( Test, TestButton, "pressTransition", ARRAY, PRESS_TRANSITION )
+DALI_PROPERTY_REGISTRATION( Test, TestButton, "releaseTransition", ARRAY, RELEASE_TRANSITION)
+DALI_PROPERTY_REGISTRATION( Test, TestButton, "disabledTransition", ARRAY, DISABLED_TRANSITION )
+DALI_PROPERTY_REGISTRATION( Test, TestButton, "enabledTransition", ARRAY, ENABLED_TRANSITION )
+
+DALI_TYPE_REGISTRATION_END()
+
+} // Impl Namespace
+
+TestButton::TestButton()
+: Control()
+{
+}
+
+TestButton::TestButton(const TestButton& button)
+: Control( button )
+{
+}
+
+TestButton::TestButton(Impl::TestButton& impl)
+: Control(impl)
+{
+}
+
+TestButton::TestButton(Dali::Internal::CustomActor* internal)
+: Control(internal)
+{
+ VerifyCustomActorPointer<Impl::TestButton>(internal);
+}
+
+TestButton& TestButton::operator=( const TestButton& button)
+{
+ if(&button != this)
+ {
+ Control::operator=(button);
+ }
+ return *this;
+}
+
+TestButton::~TestButton()
+{
+}
+
+TestButton TestButton::New()
+{
+ return Impl::TestButton::New();
+}
+
+TestButton TestButton::DownCast( BaseHandle handle )
+{
+ return Control::DownCast<TestButton,Impl::TestButton>(handle);
+}
+
+} // namespace Test
--- /dev/null
+#ifndef DALI_TOOLKIT_TEST_TEST_BUTTON_H
+#define DALI_TOOLKIT_TEST_TEST_BUTTON_H
+
+/*
+ * Copyright (c) 2016 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 <dali-toolkit/dali-toolkit.h>
+#include <test-animation-data.h>
+
+namespace Test
+{
+namespace Impl
+{
+class TestButton;
+}
+
+class TestButton : public Dali::Toolkit::Control
+{
+public:
+ enum PropertyRange
+ {
+ PROPERTY_START_INDEX = Dali::Toolkit::Control::CONTROL_PROPERTY_END_INDEX + 1,
+ PROPERTY_END_INDEX = PROPERTY_START_INDEX + 1000
+ };
+ struct Property
+ {
+ enum
+ {
+ PRESS_TRANSITION = PROPERTY_START_INDEX,
+ RELEASE_TRANSITION,
+ DISABLED_TRANSITION,
+ ENABLED_TRANSITION
+ };
+ };
+ TestButton();
+ TestButton(const TestButton& button);
+ TestButton(Impl::TestButton& impl);
+ TestButton(Dali::Internal::CustomActor* internal);
+ TestButton& operator=( const TestButton& button);
+ ~TestButton();
+ static TestButton New();
+ static TestButton DownCast( Dali::BaseHandle handle );
+};
+
+namespace Impl
+{
+
+class TestButton : public Dali::Toolkit::Internal::Control
+{
+public:
+ static Test::TestButton New();
+
+ static void SetProperty( Dali::BaseObject* object,
+ Dali::Property::Index index,
+ const Dali::Property::Value& value );
+
+ static Dali::Property::Value GetProperty( Dali::BaseObject* object,
+ Dali::Property::Index propertyIndex );
+
+protected:
+ TestButton();
+ virtual ~TestButton();
+
+public:
+ Test::TestAnimationData mPressTransitionData;
+ Test::TestAnimationData mReleaseTransitionData;
+ Test::TestAnimationData mDisabledTransitionData;
+ Test::TestAnimationData mEnabledTransitionData;
+};
+
+inline TestButton& GetImpl( Test::TestButton& handle )
+{
+ DALI_ASSERT_ALWAYS( handle );
+ Dali::RefObject& object = handle.GetImplementation();
+ return static_cast<TestButton&>( object );
+}
+
+inline const TestButton& GetImpl( const Test::TestButton& handle )
+{
+ DALI_ASSERT_ALWAYS( handle );
+ const Dali::RefObject& object = handle.GetImplementation();
+ return static_cast<const TestButton&>( object );
+}
+
+} // Impl
+} // Test
+
+
+
+#endif // DALI_TOOLKIT_TEST_TEST_BUTTON_H
void CreateTextModel( const std::string& text,
const Size& textArea,
+ const Vector<FontDescriptionRun>& fontDescriptions,
+ const LayoutOptions& options,
Size& layoutSize,
LogicalModelPtr logicalModel,
VisualModelPtr visualModel )
// 4) Set the font info
Vector<FontDescriptionRun>& fontDescriptionRuns = logicalModel->mFontDescriptionRuns;
+ fontDescriptionRuns = fontDescriptions;
Vector<FontRun>& validFonts = logicalModel->mFontRuns;
// The default font id.
// Set the layout parameters.
const Vector<GlyphIndex>& charactersToGlyph = visualModel->mCharactersToGlyph;
const Vector<Length>& glyphsPerCharacter = visualModel->mGlyphsPerCharacter;
+
LayoutParameters layoutParameters( textArea,
utf32Characters.Begin(),
lineBreakInfo.Begin(),
wordBreakInfo.Begin(),
( 0u != characterDirections.Count() ) ? characterDirections.Begin() : NULL,
- glyphs.Count(),
glyphs.Begin(),
glyphsToCharactersMap.Begin(),
- charactersPerGlyph.Begin() );
-
- // Get the character to glyph conversion table and set into the layout.
- layoutParameters.charactersToGlyphsBuffer = charactersToGlyph.Begin();
- // Get the glyphs per character table and set into the layout.
- layoutParameters.glyphsPerCharacterBuffer = glyphsPerCharacter.Begin();
+ charactersPerGlyph.Begin(),
+ charactersToGlyph.Begin(),
+ glyphsPerCharacter.Begin(),
+ numberOfGlyphs );
Vector<LineRun>& lines = visualModel->mLines;
Vector<Vector2>& glyphPositions = visualModel->mGlyphPositions;
glyphPositions.Resize( numberOfGlyphs );
- layoutParameters.isLastNewParagraph = TextAbstraction::IsNewParagraph( *( utf32Characters.Begin() + ( logicalModel->mText.Count() - 1u ) ) );
+ layoutParameters.isLastNewParagraph = TextAbstraction::IsNewParagraph( *( utf32Characters.Begin() + ( numberOfCharacters - 1u ) ) );
+
+ // The initial glyph and the number of glyphs to layout.
+ layoutParameters.startGlyphIndex = 0u;
+ layoutParameters.numberOfGlyphs = numberOfGlyphs;
layoutEngine.LayoutText( layoutParameters,
glyphPositions,
0u,
numberOfCharacters );
- // Re-layout the text. Reorder those lines with right to left characters.
- layoutEngine.ReLayoutRightToLeftLines( layoutParameters,
- glyphPositions );
+ if( options.reorder )
+ {
+ // Re-layout the text. Reorder those lines with right to left characters.
+ layoutEngine.ReLayoutRightToLeftLines( layoutParameters,
+ 0u,
+ numberOfCharacters,
+ glyphPositions );
+ }
+ }
+
+ if( options.align )
+ {
+ layoutEngine.Align( textArea,
+ 0u,
+ numberOfCharacters,
+ lines );
}
}
{
/**
+ * @brief Some layout options.
+ */
+struct LayoutOptions
+{
+ LayoutOptions()
+ : reorder( true ),
+ align( true )
+ {}
+
+ bool reorder : 1; ///< Whether to reorder the bidirectional lines.
+ bool align : 1; ///< Whether to align the lines.
+};
+
+/**
* @brief Creates and fills all the vectors of a text model: characters in utf32, segmentation info,
* scripts, fonts, bidi info, glyphs, conversion tables, etc.
*
* @param[in] text The given text.
* @param[in] textArea The area where to layout the text.
+ * @param[in] fontDescriptions The fonts to be used.
+ * @param[in] options Layout options.
* @param[out] layoutSize The laid-out size.
- * @param[out] Pointer to a logical text model instance.
- * @param[out] Pointer to a visual text model instance.
+ * @param[out] logicalModel Pointer to a logical text model instance.
+ * @param[out] visualModel Pointer to a visual text model instance.
*/
void CreateTextModel( const std::string& text,
const Size& textArea,
+ const Vector<FontDescriptionRun>& fontDescriptions,
+ const LayoutOptions& options,
Size& layoutSize,
LogicalModelPtr logicalModel,
VisualModelPtr visualModel );
/*
- * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2014-2016 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.
#include <dali-toolkit-test-suite-utils.h>
#include <dali-toolkit/devel-api/builder/builder.h>
#include <dali/integration-api/events/touch-event-integ.h>
+#include <dali-toolkit/dali-toolkit.h>
+#include <dali/devel-api/object/type-registry-helper.h>
+#include <test-button.h>
+#include <test-animation-data.h>
#define STRINGIFY(A)#A
END_TEST;
}
+
+#define CHECK_MAP_ELEMENT( xMap, xKey, xType, xPropType, xExpected, xLocation ) \
+ { \
+ Property::Value* value = xMap->Find( xKey ); \
+ DALI_TEST_EQUALS( value==NULL, false, xLocation); \
+ if( value != NULL ) \
+ { \
+ DALI_TEST_EQUALS( value->GetType(), xPropType, xLocation ); \
+ xType result; \
+ value->Get(result); \
+ DALI_TEST_EQUALS( result, xExpected, TEST_LOCATION ); \
+ std::ostringstream oss; \
+ oss << "Animation element " << xKey << "= " << result << std::endl; \
+ tet_printf( oss.str().c_str() ); \
+ } \
+ else \
+ { \
+ tet_printf("Can't find map element " xKey "\n"); \
+ } \
+ }
+
+
+int UtcDaliBuilderMapping01(void)
+{
+ ToolkitTestApplication application;
+
+ const char* json =
+ "{\n"
+ " \"mappings\":\n"
+ " {\n"
+ " \"buttonPressFadeOut\":{\n"
+ " \"alphaFunction\":\"EASE_OUT\",\n"
+ " \"timePeriod\":{\n"
+ " \"delay\":0.0,\n"
+ " \"duration\":0.4\n"
+ " }\n"
+ " },\n"
+ " \"buttonPressFadeIn\":{\n"
+ " \"alphaFunction\":\"EASE_IN\",\n"
+ " \"timePeriod\":{\n"
+ " \"delay\":0.4,\n"
+ " \"duration\":0.5\n"
+ " }\n"
+ " },\n"
+ " \"transition:buttonPressed\":\n"
+ " [\n"
+ " {\n"
+ " \"target\": \"unselectedBackgroundRenderer\",\n"
+ " \"property\": \"opacity\",\n"
+ " \"value\": 0,\n"
+ " \"animator\":\"<buttonPressFadeOut>\"\n"
+ " }\n"
+ " ],\n"
+ " \"transition:buttonReleased\":\n"
+ " [\n"
+ " {\n"
+ " \"target\": \"unselectedBackgroundRenderer\",\n"
+ " \"property\": \"opacity\",\n"
+ " \"value\": 1,\n"
+ " \"animator\":\"<buttonPressFadeIn>\"\n"
+ " },\n"
+ " {\n"
+ " \"target\": \"unselectedForegroundRenderer\",\n"
+ " \"property\": \"scale\",\n"
+ " \"value\": [ 1, 1, 1 ],\n"
+ " \"animator\":\"<buttonPressFadeIn>\"\n"
+ " },\n"
+ " {\n"
+ " \"target\": \"selectedBackgroundRenderer\",\n"
+ " \"property\": \"opacity\",\n"
+ " \"value\": 0,\n"
+ " \"animator\": \"<buttonPressFadeOut>\"\n"
+ " },\n"
+ " {\n"
+ " \"target\": \"selectedForegroundRenderer\",\n"
+ " \"property\": \"scale\",\n"
+ " \"value\": [ 0, 0, 0 ],\n"
+ " \"animator\":\"<buttonPressFadeOut>\"\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"styles\":\n"
+ " {\n"
+ " \"testbutton\":\n"
+ " {\n"
+ " \"pressTransition\":\"<transition:buttonPressed>\",\n"
+ " \"releaseTransition\":\"<transition:buttonReleased>\"\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ Builder builder = Builder::New();
+ builder.LoadFromString( json );
+
+ Test::TestButton testButton = Test::TestButton::New();
+ Stage::GetCurrent().Add( testButton );
+
+ // Render and notify
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_CHECK( builder.ApplyStyle( "testbutton", testButton ) );
+
+ // Now check that it has loaded the transition correctly:
+ Property::Value transition = testButton.GetProperty(Test::TestButton::Property::PRESS_TRANSITION);
+ DALI_TEST_EQUALS( transition.GetType(), Property::ARRAY, TEST_LOCATION );
+ Property::Array* array = transition.GetArray();
+
+ DALI_TEST_EQUALS( array->Size(), 1, TEST_LOCATION );
+ Property::Value element = array->GetElementAt(0);
+ DALI_TEST_CHECK( element.GetType() == Property::MAP );
+ Property::Map* map = element.GetMap();
+
+ CHECK_MAP_ELEMENT(map, "target", std::string, Property::STRING, "unselectedBackgroundRenderer", TEST_LOCATION);
+ CHECK_MAP_ELEMENT(map, "property", std::string, Property::STRING, "opacity", TEST_LOCATION);
+ CHECK_MAP_ELEMENT(map, "alphaFunction", int, Property::INTEGER, (int)Dali::AlphaFunction::EASE_OUT, TEST_LOCATION);
+ CHECK_MAP_ELEMENT(map, "timePeriodDelay", float, Property::FLOAT, 0.0f, TEST_LOCATION);
+ CHECK_MAP_ELEMENT(map, "timePeriodDuration", float, Property::FLOAT, 0.4f, TEST_LOCATION);
+
+ END_TEST;
+}
+
+
+int UtcDaliBuilderMappingCycleCheck(void)
+{
+ ToolkitTestApplication application;
+
+ std::string json(
+ "{\n"
+ " \"mappings\":\n"
+ " {\n"
+ " \"cyclicKey1\":\"<cyclicKey1>\",\n"
+ " \"cyclicKey2\":\"<cyclicKey3>\",\n"
+ " \"cyclicKey3\":\"<cyclicKey2>\",\n"
+ " \"FadeOut\":{\n"
+ " \"alphaFunction\":\"EASE_IN\",\n"
+ " \"timePeriod\":{\n"
+ " \"delay\":\"<cyclicKey3>\",\n"
+ " \"duration\":0.6\n"
+ " }\n"
+ " },\n"
+ " \"transition:buttonPressed\":\n"
+ " [\n"
+ " {\n"
+ " \"target\": \"<cyclicKey1>\",\n"
+ " \"property\": \"<cyclicKey2>\",\n"
+ " \"value\": 0,\n"
+ " \"animator\":\"<FadeOut>\"\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"styles\":\n"
+ " {\n"
+ " \"testbutton\":\n"
+ " {\n"
+ " \"pressTransition\":\"<transition:buttonPressed>\",\n"
+ " \"releaseTransition\":\"<cyclicKey2>\",\n"
+ " \"disabledTransition\":\"<cyclicKey3>\",\n"
+ " \"enabledTransition\":\"<unknownKey>\"\n"
+ " }\n"
+ " }\n"
+ "}\n");
+
+ Builder builder = Builder::New();
+ builder.LoadFromString( json );
+
+ Test::TestButton testButton = Test::TestButton::New();
+ Stage::GetCurrent().Add( testButton );
+
+ // Render and notify
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_CHECK( builder.ApplyStyle( "testbutton", testButton ) );
+
+ // Now check that it has loaded the transition correctly:
+ Property::Value transition = testButton.GetProperty(Test::TestButton::Property::PRESS_TRANSITION);
+ DALI_TEST_EQUALS( transition.GetType(), Property::ARRAY, TEST_LOCATION );
+ Property::Array* array = transition.GetArray();
+
+ DALI_TEST_EQUALS( array->Size(), 1, TEST_LOCATION );
+ Property::Value element = array->GetElementAt(0);
+ DALI_TEST_CHECK( element.GetType() == Property::MAP );
+ Property::Map* map = element.GetMap();
+
+ CHECK_MAP_ELEMENT(map, "target", std::string, Property::STRING, "", TEST_LOCATION);
+ CHECK_MAP_ELEMENT(map, "property", std::string, Property::STRING, "", TEST_LOCATION);
+ CHECK_MAP_ELEMENT(map, "timePeriodDuration", float, Property::FLOAT, 0.6f, TEST_LOCATION);
+
+ END_TEST;
+}
{
const char* TEST_IMAGE_FILE_NAME = "gallery_image_01.jpg";
const char* TEST_NPATCH_FILE_NAME = "gallery_image_01.9.jpg";
+const char* TEST_SVG_FILE_NAME = TEST_RESOURCE_DIR "/svg1.svg";
}
void dali_control_renderer_startup(void)
gradientRenderer.GetNaturalSize(naturalSize);
DALI_TEST_EQUALS( naturalSize, Vector2::ZERO,TEST_LOCATION );
+ //svg renderer
+ ControlRenderer svgRenderer = factory.GetControlRenderer( TEST_SVG_FILE_NAME );
+ svgRenderer.SetSize( rendererSize );
+ DALI_TEST_EQUALS( svgRenderer.GetSize(), rendererSize, TEST_LOCATION );
+ svgRenderer.GetNaturalSize(naturalSize);
+ // TEST_SVG_FILE:
+ // <svg width="100" height="100">
+ // <circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" />
+ // </svg>
+ DALI_TEST_EQUALS( naturalSize, Vector2(100.f, 100.f), TEST_LOCATION );
END_TEST;
}
END_TEST;
}
+
+int UtcDaliControlRendererGetPropertyMap7(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline( "UtcDaliControlRendererGetPropertyMap7: SvgRenderer" );
+
+ // request SvgRenderer with a property map
+ RendererFactory factory = RendererFactory::Get();
+ Property::Map propertyMap;
+ propertyMap.Insert( "rendererType", "svg" );
+ propertyMap.Insert( "imageUrl", TEST_SVG_FILE_NAME );
+ ControlRenderer svgRenderer = factory.GetControlRenderer( propertyMap );
+
+ Property::Map resultMap;
+ svgRenderer.CreatePropertyMap( resultMap );
+ // check the property values from the returned map from control renderer
+ Property::Value* value = resultMap.Find( "rendererType", Property::STRING );
+ DALI_TEST_CHECK( value );
+ DALI_TEST_CHECK( value->Get<std::string>() == "svg" );
+
+ value = resultMap.Find( "imageUrl", Property::STRING );
+ DALI_TEST_CHECK( value );
+ DALI_TEST_CHECK( value->Get<std::string>() == TEST_SVG_FILE_NAME );
+
+ // request SvgRenderer with an URL
+ ControlRenderer svgRenderer2 = factory.GetControlRenderer( TEST_SVG_FILE_NAME );
+ resultMap.Clear();
+ svgRenderer2.CreatePropertyMap( resultMap );
+ // check the property values from the returned map from control renderer
+ value = resultMap.Find( "rendererType", Property::STRING );
+ DALI_TEST_CHECK( value );
+ DALI_TEST_CHECK( value->Get<std::string>() == "svg" );
+
+ value = resultMap.Find( "imageUrl", Property::STRING );
+ DALI_TEST_CHECK( value );
+ DALI_TEST_CHECK( value->Get<std::string>() == TEST_SVG_FILE_NAME );
+
+ END_TEST;
+}
#include <iostream>
#include <stdlib.h>
#include <dali-toolkit-test-suite-utils.h>
+#include <toolkit-event-thread-callback.h>
#include <dali/devel-api/rendering/renderer.h>
#include <dali/devel-api/rendering/material.h>
#include <dali/devel-api/rendering/shader.h>
const char* TEST_IMAGE_FILE_NAME = "gallery_image_01.jpg";
const char* TEST_NPATCH_FILE_NAME = "gallery_image_01.9.jpg";
+const char* TEST_SVG_FILE_NAME = TEST_RESOURCE_DIR "/svg1.svg";
+
Integration::Bitmap* CreateBitmap( unsigned int imageWidth, unsigned int imageHeight, unsigned int initialColor, Pixel::Format pixelFormat )
{
Integration::Bitmap* bitmap = Integration::Bitmap::New( Integration::Bitmap::BITMAP_2D_PACKED_PIXELS, ResourcePolicy::OWNED_RETAIN );
END_TEST;
}
+int UtcDaliRendererFactoryGetSvgRenderer(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline( "UtcDaliRendererFactoryGetSvgRenderer: Request svg renderer with a svg url" );
+
+ RendererFactory factory = RendererFactory::Get();
+ ControlRenderer controlRenderer = factory.GetControlRenderer( TEST_SVG_FILE_NAME );
+ DALI_TEST_CHECK( controlRenderer );
+
+ Actor actor = Actor::New();
+ actor.SetSize( 200.f, 200.f );
+ Stage::GetCurrent().Add( actor );
+ controlRenderer.SetSize( Vector2(200.f, 200.f) );
+ controlRenderer.SetOnStage( actor );
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
+ // texture is not added until the rasterization completed.
+ DALI_TEST_CHECK( actor.GetRendererAt(0u).GetMaterial().GetNumberOfTextures() == 0 );
+
+ EventThreadCallback* eventTrigger = EventThreadCallback::Get();
+ CallbackBase* callback = eventTrigger->GetCallback();
+
+ eventTrigger->WaitingForTrigger( 1 );// waiting until the svg image is rasterized.
+ CallbackBase::Execute( *callback );
+
+ DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
+ DALI_TEST_CHECK( actor.GetRendererAt(0u).GetMaterial().GetNumberOfTextures() == 1 );
+
+ // waiting for the resource uploading
+ application.SendNotification();
+ application.Render();
+
+ TestGlAbstraction& gl = application.GetGlAbstraction();
+ int textureUnit = -1;
+ DALI_TEST_CHECK( gl.GetUniformValue< int >( "sTexture", textureUnit ) );
+ DALI_TEST_EQUALS( textureUnit, 0, TEST_LOCATION );
+
+ END_TEST;
+}
+
int UtcDaliRendererFactoryResetRenderer1(void)
{
ToolkitTestApplication application;
END_TEST;
}
+
+int UtcDaliRendererFactoryResetRenderer3(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline( "UtcDaliRendererFactoryResetRenderer3" );
+
+ Actor actor = Actor::New();
+ actor.SetSize(200.f, 200.f);
+ Stage::GetCurrent().Add( actor );
+ RendererFactory factory = RendererFactory::Get();
+ DALI_TEST_CHECK( factory );
+
+ // Get renderer for rendering a resource image
+ Image resourceImage = ResourceImage::New(TEST_IMAGE_FILE_NAME);
+ ControlRenderer controlRenderer = factory.GetControlRenderer( resourceImage );
+ DALI_TEST_CHECK( controlRenderer );
+ controlRenderer.SetSize(Vector2(200.f, 200.f));
+ controlRenderer.SetOnStage( actor );
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
+ DALI_TEST_CHECK( actor.GetRendererAt(0u).GetMaterial().GetNumberOfTextures() == 1 );
+
+ // reset the renderer to renderer a svg image
+ factory.ResetRenderer( controlRenderer, actor, TEST_SVG_FILE_NAME, ImageDimensions( 100, 100 ) );
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
+ // texture is not added until the rasterization completed.
+ DALI_TEST_CHECK( actor.GetRendererAt(0u).GetMaterial().GetNumberOfTextures() == 0 );
+
+ EventThreadCallback* eventTrigger = EventThreadCallback::Get();
+ CallbackBase* callback = eventTrigger->GetCallback();
+
+ eventTrigger->WaitingForTrigger( 1 );// waiting until the svg image is rasterized.
+ CallbackBase::Execute( *callback );
+
+ DALI_TEST_CHECK( actor.GetRendererAt(0u).GetMaterial().GetNumberOfTextures() == 1 );
+
+ END_TEST;
+}
} // namespace Toolkit
} // namespace Dali
-
STRING,
INTEGER,
FLOAT,
- BOOLEAN,
+ BOOLEAN
};
/*
return Control::DownCast<BloomView, Internal::BloomView>(handle);
}
-void BloomView::Add(Actor child)
-{
- GetImpl(*this).Add(child);
-}
-
-void BloomView::Remove(Actor child)
-{
- GetImpl(*this).Remove(child);
-}
-
void BloomView::Activate()
{
GetImpl(*this).Activate();
const float downsampleWidthScale, const float downsampleHeightScale);
/**
- * Adds a child Actor to this Actor.
- * NOTE! if the child already has a parent, it will be removed from old parent
- * and reparented to this actor. This may change childs position, color, shader effect,
- * scale etc as it now inherits them from this actor
- * @pre This Actor (the parent) has been initialized.
- * @pre The child actor has been initialized.
- * @pre The child actor is not the same as the parent actor.
- * @pre The actor is not the Root actor
- * @param [in] child The child.
- * @post The child will be referenced by its parent. This means that the child will be kept alive,
- * even if the handle passed into this method is reset or destroyed.
- */
- void Add(Actor child);
-
- /**
- * Removes a child Actor from this Actor.
- * If the actor was not a child of this actor, this is a no-op.
- * @pre This Actor (the parent) has been initialized.
- * @pre The child actor is not the same as the parent actor.
- * @param [in] child The child.
- */
- void Remove(Actor child);
-
- /**
* Start rendering the BloomView. Must be called after you Add() it to the stage.
*/
void Activate();
return Control::DownCast<ShadowView, Internal::ShadowView>(handle);
}
-void ShadowView::Add(Actor child)
-{
- GetImpl(*this).Add(child);
-}
-
-void ShadowView::Remove(Actor child)
-{
- GetImpl(*this).Remove(child);
-}
-
void ShadowView::SetShadowPlaneBackground(Actor shadowPlaneBackground)
{
GetImpl(*this).SetShadowPlaneBackground(shadowPlaneBackground);
static ShadowView New(float downsampleWidthScale, float downsampleHeightScale);
/**
- * Adds a child Actor to this Actor.
- * NOTE! if the child already has a parent, it will be removed from old parent
- * and reparented to this actor. This may change childs position, color, shader effect,
- * scale etc as it now inherits them from this actor
- * @pre This Actor (the parent) has been initialized.
- * @pre The child actor has been initialized.
- * @pre The child actor is not the same as the parent actor.
- * @pre The actor is not the Root actor
- * @param [in] child The child.
- * @post The child will be referenced by its parent. This means that the child will be kept alive,
- * even if the handle passed into this method is reset or destroyed.
- */
- void Add(Actor child);
-
- /**
- * Removes a child Actor from this Actor.
- * If the actor was not a child of this actor, this is a no-op.
- * @pre This Actor (the parent) has been initialized.
- * @pre The child actor is not the same as the parent actor.
- * @param [in] child The child.
- */
- void Remove(Actor child);
-
- /**
* Set the Shadow Plane Background for the shadow effect.
*
* @param[in] shadowPlaneBackground An actor representing the shadow
return GetImplementation(*this).Upload( textureRect, url, size, fittingMode, orientationCorrection );
}
+bool ImageAtlas::Upload( Vector4& textureRect, PixelDataPtr pixelData )
+{
+ return GetImplementation(*this).Upload( textureRect, pixelData );
+}
+
void ImageAtlas::Remove(const Vector4& textureRect)
{
GetImplementation(*this).Remove( textureRect );
#include <dali/public-api/images/image.h>
#include <dali/public-api/images/image-operations.h>
#include <dali/public-api/images/pixel.h>
+#include <dali/devel-api/images/pixel-data.h>
namespace Dali
{
namespace Toolkit
{
-
namespace Internal DALI_INTERNAL
{
class ImageAtlas;
bool orientationCorrection = true );
/**
+ * @brief Upload a pixel buffer to atlas
+ *
+ * @param [out] textureRect The texture area of the resource image in the atlas.
+ * @param [in] pixelData The pixel data.
+ */
+ bool Upload( Vector4& textureRect, PixelDataPtr pixelData );
+
+ /**
* @brief Remove the image at the given rectangle.
*
* The rectangular area is marked unoccupied, so new image can be added to this area.
return OptionalString();
}
}
+
inline OptionalFloat IsFloat(const OptionalChild& node)
{
OptionalFloat ret;
}
}
+
// copy N Numbers
template <typename T>
inline bool CopyNumbers(TreeNode::ConstIterator iter, int N, T& vector)
--- /dev/null
+/*
+ * Copyright (c) 2016 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.
+ */
+
+#ifdef DEBUG_ENABLED
+#include <dali-toolkit/internal/builder/builder-impl-debug.h>
+#include <dali-toolkit/internal/builder/builder-impl.h>
+#include <dali-toolkit/internal/builder/builder-get-is.inl.h>
+#include <iostream>
+#include <cstring>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+
+void LogTree( const Toolkit::JsonParser& parser )
+{
+ if( OptionalChild constants = IsChild(parser.GetRoot(), "constants") )
+ {
+ for(TreeNode::ConstIterator iter = (*constants).CBegin();
+ iter != (*constants).CEnd(); ++iter)
+ {
+ if( ( (*iter).first && strcmp( (*iter).first, "DUMP_TREE" ) == 0 ) ||
+ ( (*iter).second.GetType() == TreeNode::STRING && strcmp( (*iter).second.GetString(), "DUMP_TREE" ) == 0 ) )
+ {
+ std::ostringstream oss;
+ parser.Write(oss, 2);
+ std::cout << oss.str() << std::endl;
+ }
+ }
+ }
+}
+
+std::string PropertyValueToString( const Property::Value& value )
+{
+ std::ostringstream oss;
+ oss << value;
+
+ return oss.str();
+}
+
+} // Internal
+} // Toolkit
+} // Dali
+
+#endif // DEBUG_ENABLED
--- /dev/null
+#ifndef DALI_TOOLKIT_INTERNAL_BUILDER_IMPL_DEBUG_H
+#define DALI_TOOLKIT_INTERNAL_BUILDER_IMPL_DEBUG_H
+
+/*
+ * Copyright (c) 2016 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 <dali/integration-api/debug.h>
+#include <dali-toolkit/devel-api/builder/json-parser.h>
+
+#if defined( DEBUG_ENABLED )
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+
+#define DUMP_PARSE_TREE(parser) LogTree(parser)
+#define DUMP_TEST_MAPPINGS(parser) \
+ OptionalChild mappings = IsChild( parser.GetRoot(), KEYNAME_MAPPINGS ); \
+ if( mappings ) \
+ { \
+ std::ostringstream oss; \
+ oss << "Mappings: {" << std::endl; \
+ for( TreeNode::ConstIterator iter = (*mappings).CBegin(); iter != (*mappings).CEnd(); ++iter ) \
+ { \
+ Property::Value value; \
+ bool converted = GetPropertyMap(*mappings, (*iter).first, Property::NONE, value ); \
+ if( converted ) \
+ { \
+ oss << " " << (*iter).first << ":" << value << std::endl; \
+ } \
+ } \
+ oss << "}" << std::endl; \
+ DALI_LOG_INFO( gFilterScript, Debug::Verbose, oss.str().c_str() ); \
+ }
+
+
+void LogTree( const Toolkit::JsonParser& mParser );
+
+std::string PropertyValueToString( const Property::Value& value );
+
+
+} // Internal
+} // Toolkit
+} // Dali
+
+#else
+
+#define DUMP_PARSE_TREE(parser)
+#define DUMP_TEST_MAPPINGS(parser)
+
+#endif // DEBUG_ENABLED
+#endif // DALI_TOOLKIT_INTERNAL_BUILDER_IMPL_DEBUG_H
// EXTERNAL INCLUDES
#include <sys/stat.h>
#include <sstream>
+
#include <dali/public-api/render-tasks/render-task-list.h>
#include <dali/public-api/object/type-info.h>
#include <dali/public-api/object/type-registry.h>
#include <dali-toolkit/internal/builder/builder-get-is.inl.h>
#include <dali-toolkit/internal/builder/builder-filesystem.h>
#include <dali-toolkit/internal/builder/builder-declarations.h>
+#include <dali-toolkit/internal/builder/builder-set-property.h>
#include <dali-toolkit/internal/builder/replacement.h>
+#include <dali-toolkit/internal/builder/tree-node-manipulator.h>
+
+#include <dali-toolkit/internal/builder/builder-impl-debug.h>
namespace Dali
{
class Replacement;
extern Animation CreateAnimation(const TreeNode& child, const Replacement& replacements, const Dali::Actor searchRoot, Builder* const builder );
-extern void DeterminePropertyFromNode( const TreeNode& node, Property::Value& value );
-extern void DeterminePropertyFromNode( const TreeNode& node, Property::Value& value, const Replacement& replacements );
-extern bool DeterminePropertyFromNode( const TreeNode& node, Property::Type type, Property::Value& value );
-extern bool DeterminePropertyFromNode( const TreeNode& node, Property::Type type, Property::Value& value, const Replacement& replacements );
extern Actor SetupSignalAction(ConnectionTracker* tracker, const TreeNode &root, const TreeNode &child, Actor actor, Dali::Toolkit::Internal::Builder* const builder);
extern Actor SetupPropertyNotification(ConnectionTracker* tracker, const TreeNode &root, const TreeNode &child, Actor actor, Dali::Toolkit::Internal::Builder* const builder);
extern Actor SetupActor( const TreeNode& node, Actor& actor, const Replacement& constant );
const std::string KEYNAME_NAME = "name";
const std::string KEYNAME_TEMPLATES = "templates";
const std::string KEYNAME_INCLUDES = "includes";
+const std::string KEYNAME_MAPPINGS = "mappings";
typedef std::vector<const TreeNode*> TreeNodeList;
-template <typename T>
-std::string ToString(const T& value)
-{
- std::stringstream ss;
- ss << value;
- return ss.str();
-}
-
-template <>
-std::string ToString(const Rect<int>& value)
-{
- std::stringstream ss;
- ss << value.x << "," << value.y << "," << value.width << "," << value.height;
- return ss.str();
-}
-
-#if defined(DEBUG_ENABLED)
-std::string PropertyValueToString( const Property::Value& value )
+bool GetMappingKey( const std::string& str, std::string& key )
{
- std::string ret;
-
- switch( value.GetType() )
+ bool result = false;
+ std::string test( str );
+ if( ! test.empty() )
{
- case Property::NONE:
- {
- ret = "NONE";
- break;
- } ///< No type
- case Property::BOOLEAN:
- {
- ret = value.Get<bool>() ? "True" : "False";
- break;
- }
- case Property::FLOAT:
- {
-
- ret = ToString( value.Get<float>() );
- break;
- }
- case Property::INTEGER:
- {
- ret = ToString( value.Get<int>() );
- break;
- }
- case Property::VECTOR2:
- {
- ret = ToString( value.Get<Vector2>() );
- break;
- }
- case Property::VECTOR3:
- {
- ret = ToString( value.Get<Vector3>() );
- break;
- }
- case Property::VECTOR4:
- {
- ret = ToString( value.Get<Vector4>() );
- break;
- }
- case Property::MATRIX3:
- {
- ret = ToString( value.Get<Matrix3>() );
- break;
- }
- case Property::MATRIX:
+ if( test.at(0) == '<' )
{
- ret = ToString( value.Get<Matrix>() );
- break;
- }
- case Property::RECTANGLE:
- {
- ret = ToString( value.Get< Rect<int> >() );
- break;
- }
- case Property::ROTATION:
- {
- break;
- }
- case Property::STRING:
- {
- ret = value.Get<std::string>();
- break;
- }
- case Property::ARRAY:
- {
- ret = std::string("Array Size=") + ToString( value.Get<Property::Array>().Size() );
- break;
- }
- case Property::MAP:
- {
- ret = std::string("Map Size=") + ToString( value.Get<Property::Map>().Count() );
- break;
+ if( test.at(test.length()-1) == '>' )
+ {
+ key = test.substr( 1, test.length()-2 );
+ result = true;
+ }
}
}
-
- return ret;
+ return result;
}
-#endif // DEBUG_ENABLED
/*
* Recursively collects all stylesin a node (An array of style names).
{
if( handle )
{
+
for( TreeNode::ConstIterator iter = node.CBegin(); iter != node.CEnd(); ++iter )
{
const TreeNode::KeyNodePair& keyChild = *iter;
std::string key( keyChild.first );
// ignore special fields; type,actors,signals,styles
- if(key == KEYNAME_TYPE || key == KEYNAME_ACTORS || key == KEYNAME_SIGNALS || key == KEYNAME_STYLES)
+ if(key == KEYNAME_TYPE || key == KEYNAME_ACTORS || key == KEYNAME_SIGNALS || key == KEYNAME_STYLES || key == KEYNAME_MAPPINGS )
{
continue;
}
// special field 'image' usually contains an json object description
// although sometimes refers to a framebuffer
- if( 0 == keyChild.second.Size() )
+ if( key == "image" )
{
- if(key == "image")
+ if( 0 == keyChild.second.Size() )
{
ImageActor imageActor = ImageActor::DownCast(handle);
if(imageActor)
}
// special field 'effect' references the shader effect instances
- if(key == "effect")
+ if( key == "effect" )
{
ImageActor actor = ImageActor::DownCast(handle);
if( actor )
if( Property::INVALID_INDEX != index )
{
Property::Type type = propertyObject.GetPropertyType(index);
-
Property::Value value;
- if( !DeterminePropertyFromNode( keyChild.second, type, value, constant ) )
+ bool mapped = false;
+
+ // if node.value is a mapping, get the property value from the "mappings" table
+ if( keyChild.second.GetType() == TreeNode::STRING )
{
- // verbose as this might not be a problem
- // eg parentOrigin can be a string which is picked up later
- DALI_SCRIPT_VERBOSE("Could not convert property:%s\n", key.c_str());
+ std::string mappingKey;
+ if( GetMappingKey(keyChild.second.GetString(), mappingKey) )
+ {
+ OptionalChild mappingRoot = IsChild( mParser.GetRoot(), KEYNAME_MAPPINGS );
+ mapped = GetPropertyMap( *mappingRoot, mappingKey.c_str(), type, value );
+ }
}
- else
+ if( ! mapped )
+ {
+ mapped = DeterminePropertyFromNode( keyChild.second, type, value, constant );
+ if( ! mapped )
+ {
+ // verbose as this might not be a problem
+ // eg parentOrigin can be a string which is picked up later
+ DALI_SCRIPT_VERBOSE("Could not convert property:%s\n", key.c_str());
+ }
+ }
+ if( mapped )
{
DALI_SCRIPT_VERBOSE("SetProperty '%s' Index=:%d Value Type=%d Value '%s'\n", key.c_str(), index, value.GetType(), PropertyValueToString(value).c_str() );
bool Builder::IsLinearConstrainer( const std::string& name )
{
- //Search the LinearConstrainer in the LUT
+ // Search the LinearConstrainer in the LUT
size_t count( mLinearConstrainerLut.size() );
for( size_t i(0); i!=count; ++i )
{
return CreateAnimation( animationName, replacement, Dali::Stage::GetCurrent().GetRootLayer() );
}
+bool Builder::ConvertChildValue( const TreeNode& mappingRoot, KeyStack& keyStack, Property::Value& child )
+{
+ bool result = false;
+
+ switch( child.GetType() )
+ {
+ case Property::STRING:
+ {
+ std::string value;
+ if( child.Get( value ) )
+ {
+ std::string key;
+ if( GetMappingKey( value, key ) )
+ {
+ // Check key for cycles:
+ result=true;
+ for( KeyStack::iterator iter = keyStack.begin() ; iter != keyStack.end(); ++iter )
+ {
+ if( key.compare(*iter) == 0 )
+ {
+ // key is already in stack; stop.
+ DALI_LOG_WARNING("Detected cycle in stylesheet mapping table:%s\n", key.c_str());
+ child = Property::Value("");
+ result=false;
+ break;
+ }
+ }
+
+ if( result )
+ {
+ // The following call will overwrite the child with the value
+ // from the mapping.
+ RecursePropertyMap( mappingRoot, keyStack, key.c_str(), Property::NONE, child );
+ result = true;
+ }
+ }
+ }
+ break;
+ }
+
+ case Property::MAP:
+ {
+ Property::Map* map = child.GetMap();
+ for( Property::Map::SizeType i=0; i < map->Count(); ++i )
+ {
+ Property::Value& child = map->GetValue(i);
+ ConvertChildValue(mappingRoot, keyStack, child);
+ }
+ break;
+ }
+
+ case Property::ARRAY:
+ {
+ Property::Array* array = child.GetArray();
+ for( Property::Array::SizeType i=0; i < array->Count(); ++i )
+ {
+ Property::Value& child = array->GetElementAt(i);
+ ConvertChildValue(mappingRoot, keyStack, child);
+ }
+ break;
+ }
+
+ default:
+ // Ignore other types.
+ break;
+ }
+
+ return result;
+}
+
+bool Builder::RecursePropertyMap( const TreeNode& mappingRoot, KeyStack& keyStack, const char* theKey, Property::Type propertyType, Property::Value& value )
+{
+ Replacement replacer( mReplacementMap );
+ bool result = false;
+
+ keyStack.push_back( theKey );
+
+ for( TreeNode::ConstIterator iter = mappingRoot.CBegin(); iter != mappingRoot.CEnd(); ++iter )
+ {
+ std::string aKey( (*iter).first );
+ if( aKey.compare( theKey ) == 0 )
+ {
+ if( propertyType == Property::NONE )
+ {
+ DeterminePropertyFromNode( (*iter).second, value, replacer );
+ result = true;
+ }
+ else
+ {
+ result = DeterminePropertyFromNode( (*iter).second, propertyType, value, replacer );
+ }
+
+ if( result )
+ {
+ ConvertChildValue(mappingRoot, keyStack, value);
+ }
+ break;
+ }
+ }
+ keyStack.pop_back();
+
+ return result;
+}
+
+
+bool Builder::GetPropertyMap( const TreeNode& mappingRoot, const char* theKey, Property::Type propertyType, Property::Value& value )
+{
+ KeyStack keyStack;
+ return RecursePropertyMap( mappingRoot, keyStack, theKey, propertyType, value );
+}
+
+
void Builder::LoadFromString( std::string const& data, Dali::Toolkit::Builder::UIFormat format )
{
// parser to get constants and includes only
}
}
+ DUMP_PARSE_TREE(parser); // This macro only writes out if DEBUG is enabled and the "DUMP_TREE" constant is defined in the stylesheet.
+ DUMP_TEST_MAPPINGS(parser);
+
DALI_ASSERT_ALWAYS(mParser.GetRoot() && "Cannot parse JSON");
}
Property::Map mReplacementMap;
+ typedef std::vector< TreeNode::KeyNodePair > MappingsLut;
+ MappingsLut mCompleteMappings;
+
BaseHandle Create( const std::string& templateName, const Replacement& constant );
BaseHandle DoCreate( const TreeNode& root, const TreeNode& node, Actor parent, const Replacement& replacements );
Animation CreateAnimation( const std::string& animationName, const Replacement& replacement, Dali::Actor sourceActor );
+ typedef std::vector<const char*> KeyStack;
+
+ /**
+ * Tests if the value is a string delimited by <>. If it is, then it attempts to
+ * change the value to the mapping from a matching key in the mappings table.
+ * @param[in] mappingRoot The JSON node containing the mappings
+ * @param[in,out] keyStack the stack of visited keys
+ * @param[in,out] value The string value to test and write back to.
+ * @return true if the value was converted, false otherwise.
+ */
+ bool ConvertChildValue( const TreeNode& mappingRoot, KeyStack& keyStack, Property::Value& value );
+
+ /**
+ * Find the key in the mapping table, if it's present, then generate a property value for it (of the given type if available), recursing as necessary, and stopping if any cycles
+ * are detected.
+ * @param[in] mappingRoot The JSON node containing the mappings
+ * @param[in] theKey The key to search for
+ * @param[in,out] keyStack the stack of visited keys
+ * @param[in] propertyType The property type if known, or NONE
+ * @param[in,out] value The string value to test and write back to.
+ */
+ bool RecursePropertyMap( const TreeNode& mappingRoot, KeyStack& keyStack, const char* theKey, Property::Type propertyType, Property::Value& value );
+
+ /**
+ * Find the key in the mapping table, if it's present, then generate a property value for it (of the given type if available), recursing as necessary, and stopping if any cycles
+ * are detected.
+ * @param[in] mappingRoot The JSON node containing the mappings
+ * @param[in] theKey The key to search for
+ * @param[in] propertyType The property type if known, or NONE
+ * @param[in,out] value The string value to test and write back to.
+ */
+ bool GetPropertyMap( const TreeNode& mappingRoot, const char* theKey, Property::Type propertyType, Property::Value& value );
+
void ApplyProperties( const TreeNode& root, const TreeNode& node,
Dali::Handle& handle, const Replacement& constant );
#include <dali-toolkit/internal/builder/builder-impl.h>
#include <dali-toolkit/internal/builder/builder-get-is.inl.h>
#include <dali-toolkit/internal/builder/replacement.h>
-
+#include <dali-toolkit/internal/builder/builder-set-property.h>
namespace Dali
{
namespace Internal
{
-/*
- * Set a property value from a tree node.
- * This function determines the type of the property from the format of the string in the node.
- * This is not always possible and if the type cannot be determined then then the type will default to Array.
- * @param node The node string to convert from
- * @param value The property value to set
- */
-void DeterminePropertyFromNode( const TreeNode& node, Property::Value& value );
-
-/*
- * Set a property value from a tree node as SetPropertyFromNode() above
- * This function determines the type of the property from the format of the string in the node.
- * This is not always possible and if the type cannot be determined then then the type will default to Array.
- * @param node The node string to convert from
- * @param value The property value to set
- * @param replacement The overriding replacement map (if any)
- */
-void DeterminePropertyFromNode( const TreeNode& node, Property::Value& value,
- const Replacement& replacement );
-
-/*
- * Set a property value as the given type from a tree node.
- * @param node The node string to convert from
- * @param type The property type to convert to.
- * @param value The property value to set
- * @return true if the string could be converted to the correct type.
- */
-bool DeterminePropertyFromNode( const TreeNode& node, Property::Type type, Property::Value& value );
-
-/*
- * Set a property value as the given type from a tree node as SetPropertyFromNode() above
- * @param node The node string to convert from
- * @param type The property type to convert to.
- * @param value The property value to set
- * @param replacement The overriding replacement map (if any)
- * @return true if the string could be converted to the correct type.
- */
-bool DeterminePropertyFromNode( const TreeNode& node, Property::Type type, Property::Value& value,
- const Replacement& replacement );
-
namespace
{
* A property value type can be forced when its unknown by a disambiguation convention in the json
* ie "myarray": [1,2,3,4] ; would be a vector but
* "myarray": {"typeCast":"array", "value":[1,2,3,4]} would be an array
- * @param child The node whos string to search for a disambiguated type
+ * @param child The node whose string to search for a disambiguated type
* @param value The value to set
- * @param overrideMap The user overriding constant map
- * @param defaultMap The default map.
+ * @param replacement The user overriding constant map
* @return True if child contained a disambiguated string that could be converted.
*/
-bool Disambiguated(const TreeNode& child, // ConstantLut& constantLut,
+bool Disambiguated(const TreeNode& child,
Dali::Property::Value& value,
const Replacement& replacement )
{
}
bool DeterminePropertyFromNode( const TreeNode& node, Property::Type type, Property::Value& value,
- const Replacement& replacer )
+ const Replacement& replacer )
{
bool done = false;
}
void DeterminePropertyFromNode( const TreeNode& node, Property::Value& value,
- const Replacement& replacer )
+ const Replacement& replacer )
{
TreeNode::NodeType nodeType = node.GetType();
}
else
{
- // string always succeeds with the current json parser so its last
value = *aString;
}
} // if aBool
--- /dev/null
+#ifndef DALI_TOOLKIT_INTERNAL_BUILDER_SET_PROPERTY_H
+#define DALI_TOOLKIT_INTERNAL_BUILDER_SET_PROPERTY_H
+
+/*
+ * Copyright (c) 2016 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.
+ */
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+
+/**
+ * Set a property value from a tree node.
+ * This function determines the type of the property from the format of the string in the node.
+ * This is not always possible and if the type cannot be determined then then the type will default to Array.
+ * @param node The node string to convert from
+ * @param value The property value to set
+ */
+void DeterminePropertyFromNode( const TreeNode& node,
+ Property::Value& value );
+
+/**
+ * Set a property value from a tree node as DeterminePropertyFromNode() above
+ * This function determines the type of the property from the format of the string in the node.
+ * This is not always possible and if the type cannot be determined then then the type will default to Array.
+ * @param node The node string to convert from
+ * @param value The property value to set
+ * @param replacement The overriding replacement map (if any)
+ */
+void DeterminePropertyFromNode( const TreeNode& node,
+ Property::Value& value,
+ const Replacement& replacement );
+
+/**
+ * Set a property value as the given type from a tree node.
+ * @param node The node string to convert from
+ * @param type The property type to convert to.
+ * @param value The property value to set
+ * @return true if the string could be converted to the correct type.
+ */
+bool DeterminePropertyFromNode( const TreeNode& node,
+ Property::Type type,
+ Property::Value& value );
+
+/**
+ * Set a property value as the given type from a tree node as DeterminePropertyFromNode() above
+ * @param node The node string to convert from
+ * @param type The property type to convert to.
+ * @param value The property value to set
+ * @param replacement The overriding replacement map (if any)
+ * @return true if the string could be converted to the correct type.
+ */
+bool DeterminePropertyFromNode( const TreeNode& node,
+ Property::Type type,
+ Property::Value& value,
+ const Replacement& replacement );
+
+
+} // Internal namespace
+} // Toolkit namespace
+} // Dali namespace
+
+#endif //DALI_TOOLKIT_INTERNAL_BUILDER_SET_PROPERTY_H
, mTargetSize(Vector2::ZERO)
, mLastSize(Vector2::ZERO)
, mChildrenRoot(Actor::New())
+ , mInternalRoot(Actor::New() )
, mBloomThresholdPropertyIndex(Property::INVALID_INDEX)
, mBlurStrengthPropertyIndex(Property::INVALID_INDEX)
, mBloomIntensityPropertyIndex(Property::INVALID_INDEX)
, mTargetSize(Vector2::ZERO)
, mLastSize(Vector2::ZERO)
, mChildrenRoot(Actor::New())
+ , mInternalRoot(Actor::New())
, mBloomThresholdPropertyIndex(Property::INVALID_INDEX)
, mBlurStrengthPropertyIndex(Property::INVALID_INDEX)
, mBloomIntensityPropertyIndex(Property::INVALID_INDEX)
return handle;
}
-/////////////////////////////////////////////////////////////
-// for creating a subtree for all user added child actors, so that we can have them exclusive to the mRenderChildrenTask and our other actors exclusive to our other tasks
-// TODO: overloading Actor::Add()/Remove() not nice since breaks polymorphism. Need another method to pass ownership of added child actors to our internal actor root.
-void BloomView::Add(Actor child)
-{
- mChildrenRoot.Add(child);
-}
-
-void BloomView::Remove(Actor child)
-{
- mChildrenRoot.Remove(child);
-}
-
-
-
-
-
-
///////////////////////////////////////////////////////////
//
// Private methods
{
// root actor to parent all user added actors, needed to allow us to set that subtree as exclusive for our child render task
mChildrenRoot.SetParentOrigin( ParentOrigin::CENTER );
+ mInternalRoot.SetParentOrigin( ParentOrigin::CENTER );
//////////////////////////////////////////////////////
// Create actors
////////////////////////////////
// Connect to actor tree
Self().Add( mChildrenRoot );
- Self().Add( mBloomExtractImageActor );
- Self().Add( mGaussianBlurView );
- Self().Add( mCompositeImageActor );
- Self().Add( mTargetImageActor );
- Self().Add( mRenderDownsampledCamera );
- Self().Add( mRenderFullSizeCamera );
+ Self().Add( mInternalRoot );
+ mInternalRoot.Add( mBloomExtractImageActor );
+ mInternalRoot.Add( mGaussianBlurView );
+ mInternalRoot.Add( mCompositeImageActor );
+ mInternalRoot.Add( mTargetImageActor );
+ mInternalRoot.Add( mRenderDownsampledCamera );
+ mInternalRoot.Add( mRenderFullSizeCamera );
// bind properties for / set shader constants to defaults
SetupProperties();
}
}
+void BloomView::OnControlChildAdd( Actor& child )
+{
+ if( child != mChildrenRoot && child != mInternalRoot)
+ {
+ mChildrenRoot.Add( child );
+ }
+}
+
+void BloomView::OnControlChildRemove( Actor& child )
+{
+ mChildrenRoot.Remove( child );
+}
+
void BloomView::AllocateResources()
{
// size of render targets etc is based on the size of this actor, ignoring z
- if(mTargetSize != mLastSize)
+ if(mTargetSize != mLastSize || !mActivated)
{
mLastSize = mTargetSize;
// stop render tasks processing
// Note: render target resources are automatically freed since we set the Image::Unused flag
RemoveRenderTasks();
+ mRenderTargetForRenderingChildren.Reset();
+ mBloomExtractTarget.Reset();
+ mOutputRenderTarget.Reset();
mActivated = false;
}
static Dali::Toolkit::BloomView New();
static Dali::Toolkit::BloomView New( const unsigned int numSamples, const float blurBellCurveWidth, const Pixel::Format renderTargetPixelFormat,
const float downsampleWidthScale, const float downsampleHeightScale);
-
- void Add(Actor child);
- void Remove(Actor child);
-
void Activate();
void Deactivate();
virtual void OnInitialize();
virtual void OnSizeSet(const Vector3& targetSize);
+ /**
+ * @copydoc Control::OnControlChildAdd()
+ */
+ virtual void OnControlChildAdd( Actor& child );
+
+ /**
+ * @copydoc Control::OnControlChildRemove()
+ */
+ virtual void OnControlChildRemove( Actor& child );
+
void AllocateResources();
void CreateRenderTasks();
void RemoveRenderTasks();
/////////////////////////////////////////////////////////////
// for creating a subtree for all user added child actors, so that we can have them exclusive to the mRenderChildrenTask and our other actors exclusive to our other tasks
Actor mChildrenRoot;
+ // for creating a subtree for the internal actors
+ Actor mInternalRoot;
/////////////////////////////////////////////////////////////
// for mapping offscreen renders to render target sizes
vertexFormat["aIndex"] = Property::FLOAT;
vertexFormat["aPosition"] = Property::VECTOR2;
vertexFormat["aTexCoord"] = Property::VECTOR2;
- PropertyBuffer vertices = PropertyBuffer::New( vertexFormat, numVertex );
- vertices.SetData( &vertexData[0] );
+ PropertyBuffer vertices = PropertyBuffer::New( vertexFormat );
+ vertices.SetData( &vertexData[0], numVertex );
Property::Map indexFormat;
indexFormat["indices"] = Property::INTEGER;
- PropertyBuffer indices = PropertyBuffer::New( indexFormat, numIndex );
- indices.SetData( &indexData[0] );
+ PropertyBuffer indices = PropertyBuffer::New( indexFormat );
+ indices.SetData( &indexData[0], numIndex );
Geometry geometry = Geometry::New();
geometry.AddVertexBuffer( vertices );
// pixel format / size - set from JSON
// aspect ratio property needs to be able to be constrained also for cameras, not possible currently. Therefore changing aspect ratio of GaussianBlurView won't currently work
// default near clip value
-// mChildrenRoot Add()/Remove() overloads - better solution
// Manager object - re-use render targets if there are multiple GaussianBlurViews created
, mTargetSize(Vector2::ZERO)
, mLastSize(Vector2::ZERO)
, mChildrenRoot(Actor::New())
+ , mInternalRoot(Actor::New())
, mBlurStrengthPropertyIndex(Property::INVALID_INDEX)
, mActivated( false )
{
, mTargetSize(Vector2::ZERO)
, mLastSize(Vector2::ZERO)
, mChildrenRoot(Actor::New())
+ , mInternalRoot(Actor::New())
, mBlurStrengthPropertyIndex(Property::INVALID_INDEX)
, mActivated( false )
{
/////////////////////////////////////////////////////////////
// for creating a subtree for all user added child actors, so that we can have them exclusive to the mRenderChildrenTask and our other actors exclusive to our other tasks
-// TODO: overloading Actor::Add()/Remove() not nice since breaks polymorphism. Need another method to pass ownership of added child actors to our internal actor root.
+// DEPRECATED: overloading Actor::Add()/Remove() not nice since breaks polymorphism. Need another method to pass ownership of added child actors to our internal actor root.
void GaussianBlurView::Add(Actor child)
{
mChildrenRoot.Add(child);
{
// root actor to parent all user added actors, needed to allow us to set that subtree as exclusive for our child render task
mChildrenRoot.SetParentOrigin(ParentOrigin::CENTER);
+ mInternalRoot.SetParentOrigin(ParentOrigin::CENTER);
//////////////////////////////////////////////////////
// Create shaders
mImageActorVertBlur.SetProperty( Toolkit::ImageView::Property::IMAGE, rendererMap );
// Register a property that the user can control to fade the blur in / out via the GaussianBlurView object
- mBlurStrengthPropertyIndex = Self().RegisterProperty(GAUSSIAN_BLUR_VIEW_STRENGTH_PROPERTY_NAME, GAUSSIAN_BLUR_VIEW_DEFAULT_BLUR_STRENGTH);
+ Actor self = Self();
+ mBlurStrengthPropertyIndex = self.RegisterProperty(GAUSSIAN_BLUR_VIEW_STRENGTH_PROPERTY_NAME, GAUSSIAN_BLUR_VIEW_DEFAULT_BLUR_STRENGTH);
// Create an ImageActor for compositing the blur and the original child actors render
if(!mBlurUserImage)
mImageActorComposite.SetOpacity(GAUSSIAN_BLUR_VIEW_DEFAULT_BLUR_STRENGTH); // ensure alpha is enabled for this object and set default value
Constraint blurStrengthConstraint = Constraint::New<float>( mImageActorComposite, Actor::Property::COLOR_ALPHA, EqualToConstraint());
- blurStrengthConstraint.AddSource( ParentSource(mBlurStrengthPropertyIndex) );
+ blurStrengthConstraint.AddSource( Source( self, mBlurStrengthPropertyIndex) );
blurStrengthConstraint.Apply();
// Create an ImageActor for holding final result, i.e. the blurred image. This will get rendered to screen later, via default / user render task
//////////////////////////////////////////////////////
// Connect to actor tree
- Self().Add( mImageActorComposite );
- Self().Add( mTargetActor );
- Self().Add( mRenderFullSizeCamera );
+ mInternalRoot.Add( mImageActorComposite );
+ mInternalRoot.Add( mTargetActor );
+ mInternalRoot.Add( mRenderFullSizeCamera );
}
//////////////////////////////////////////////////////
// Connect to actor tree
Self().Add( mChildrenRoot );
- Self().Add( mImageActorHorizBlur );
- Self().Add( mImageActorVertBlur );
- Self().Add( mRenderDownsampledCamera );
+ Self().Add( mInternalRoot );
+ mInternalRoot.Add( mImageActorHorizBlur );
+ mInternalRoot.Add( mImageActorVertBlur );
+ mInternalRoot.Add( mRenderDownsampledCamera );
}
}
}
+void GaussianBlurView::OnControlChildAdd( Actor& child )
+{
+ if( child != mChildrenRoot && child != mInternalRoot)
+ {
+ mChildrenRoot.Add( child );
+ }
+}
+
+void GaussianBlurView::OnControlChildRemove( Actor& child )
+{
+ mChildrenRoot.Remove( child );
+}
+
void GaussianBlurView::AllocateResources()
{
// size of render targets etc is based on the size of this actor, ignoring z
virtual void OnInitialize();
virtual void OnSizeSet(const Vector3& targetSize);
+ /**
+ * @copydoc Control::OnControlChildAdd()
+ */
+ virtual void OnControlChildAdd( Actor& child );
+
+ /**
+ * @copydoc Control::OnControlChildRemove()
+ */
+ virtual void OnControlChildRemove( Actor& child );
+
void SetBlurBellCurveWidth(float blurBellCurveWidth);
float CalcGaussianWeight(float x);
void SetShaderConstants();
/////////////////////////////////////////////////////////////
// for creating a subtree for all user added child actors, so that we can have them exclusive to the mRenderChildrenTask and our other actors exclusive to our other tasks
Actor mChildrenRoot;
+ // for creating a subtree for the internal actors
+ Actor mInternalRoot;
/////////////////////////////////////////////////////////////
// for mapping offscreen renders to render target sizes
// INTERNAL INCLUDES
#include <dali-toolkit/public-api/controls/image-view/image-view.h>
#include <dali-toolkit/devel-api/controls/renderer-factory/renderer-factory.h>
-#include <dali-toolkit/internal/controls/renderers/image/image-renderer.h>
namespace Dali
{
mUrl = url;
- if( size.GetWidth() == 0u && size.GetHeight() == 0u )
- {
- mImageSize = ResourceImage::GetImageSize( mUrl );
- }
- else
+ if( size.GetWidth() != 0u && size.GetHeight() != 0u )
{
mImageSize = size;
}
Actor self = Self();
- Toolkit::RendererFactory::Get().ResetRenderer( mRenderer, self, mUrl, mImageSize );
+ Toolkit::RendererFactory::Get().ResetRenderer( mRenderer, self, mUrl, size );
RelayoutRequest();
}
void ImageView::EnablePreMultipliedAlpha( bool preMultipled )
{
- mPremultipledAlphaEnabled = preMultipled;
-
if( mRenderer )
{
- ControlRenderer& rendererImpl = GetImplementation( mRenderer );
- if (&typeid( rendererImpl ) == &typeid(ImageRenderer) )
- {
- ImageRenderer* imageRenderer = static_cast<ImageRenderer*>( &rendererImpl );
- imageRenderer->EnablePreMultipliedAlpha( preMultipled );
- }
+ GetImplementation( mRenderer ).EnablePreMultipliedAlpha( preMultipled );
}
}
bool ImageView::IsPreMultipliedAlphaEnabled() const
{
- return mPremultipledAlphaEnabled;
+ if( mRenderer )
+ {
+ return GetImplementation( mRenderer ).IsPreMultipliedAlphaEnabled();
+ }
+ return false;
}
void ImageView::SetDepthIndex( int depthIndex )
Vector3 ImageView::GetNaturalSize()
{
- Vector3 size;
+ if( mRenderer )
+ {
+ Vector2 rendererNaturalSize;
+ mRenderer.GetNaturalSize( rendererNaturalSize );
+ return Vector3( rendererNaturalSize );
+ }
+ Vector3 size;
size.x = mImageSize.GetWidth();
size.y = mImageSize.GetHeight();
Control::OnStageDisconnection();
}
+void ImageView::OnSizeSet( const Vector3& targetSize )
+{
+ if( mRenderer )
+ {
+ Vector2 size( targetSize );
+ mRenderer.SetSize( size );
+ }
+}
+
///////////////////////////////////////////////////////////
//
// Properties
virtual void OnStageDisconnection();
/**
+ * @copydoc Toolkit::Control::OnSizeSet()
+ */
+ virtual void OnSizeSet( const Vector3& targetSize );
+
+ /**
* @copydoc Toolkit::Control::GetNaturalSize
*/
virtual Vector3 GetNaturalSize();
Property::Map vertexFormat;
vertexFormat["aPosition"] = Property::VECTOR3;
vertexFormat["aNormal"] = Property::VECTOR3;
- PropertyBuffer surfaceVertices = PropertyBuffer::New( vertexFormat, vertices.Size() );
- surfaceVertices.SetData( &vertices[0] );
+ PropertyBuffer surfaceVertices = PropertyBuffer::New( vertexFormat );
+ surfaceVertices.SetData( &vertices[0], vertices.Size() );
Geometry surface = Geometry::New();
surface.AddVertexBuffer( surfaceVertices );
{
Property::Map textureFormat;
textureFormat["aTexCoord"] = Property::VECTOR2;
- PropertyBuffer extraVertices = PropertyBuffer::New( textureFormat, textures.Size() );
- extraVertices.SetData( &textures[0] );
+ PropertyBuffer extraVertices = PropertyBuffer::New( textureFormat );
+ extraVertices.SetData( &textures[0], textures.Size() );
surface.AddVertexBuffer( extraVertices );
}
Property::Map vertexExtFormat;
vertexExtFormat["aTangent"] = Property::VECTOR3;
vertexExtFormat["aBiNormal"] = Property::VECTOR3;
- PropertyBuffer extraVertices = PropertyBuffer::New( vertexExtFormat, verticesExt.Size() );
- extraVertices.SetData( &verticesExt[0] );
+ PropertyBuffer extraVertices = PropertyBuffer::New( vertexExtFormat );
+ extraVertices.SetData( &verticesExt[0], verticesExt.Size() );
surface.AddVertexBuffer( extraVertices );
}
//Indices
Property::Map indicesVertexFormat;
indicesVertexFormat["aIndices"] = Property::INTEGER;
- PropertyBuffer indicesToVertices = PropertyBuffer::New( indicesVertexFormat, indices.Size() );
- indicesToVertices.SetData(&indices[0]);
+ PropertyBuffer indicesToVertices = PropertyBuffer::New( indicesVertexFormat );
+ indicesToVertices.SetData( &indices[0], indices.Size() );
surface.SetIndexBuffer ( indicesToVertices );
}
Property::Map borderVertexFormat;
borderVertexFormat[POSITION_ATTRIBUTE_NAME] = Property::VECTOR2;
borderVertexFormat[DRIFT_ATTRIBUTE_NAME] = Property::VECTOR2;
- PropertyBuffer borderVertices = PropertyBuffer::New( borderVertexFormat, 16 );
- borderVertices.SetData(borderVertexData);
+ PropertyBuffer borderVertices = PropertyBuffer::New( borderVertexFormat );
+ borderVertices.SetData( borderVertexData, 16 );
// Create indices
unsigned int indexData[24] = { 1,5,2,6,3,7,7,6,11,10,15,14,14,10,13,9,12,8,8,9,4,5,0,1};
Property::Map indexFormat;
indexFormat[INDEX_NAME] = Property::INTEGER;
- PropertyBuffer indices = PropertyBuffer::New( indexFormat, 24 );
- indices.SetData(indexData);
+ PropertyBuffer indices = PropertyBuffer::New( indexFormat );
+ indices.SetData( indexData, 24 );
// Create the geometry object
Geometry geometry = Geometry::New();
enum Flags
{
IS_ON_STAGE = 1,
- IS_FROM_CACHE = 1 << 1
+ IS_FROM_CACHE = 1 << 1,
+ IS_PREMULTIPLIED_ALPHA = 1 << 2
};
struct CustomShader
Vector2 mSize;
Vector2 mOffset;
- Rect<int> mClipRect;
float mDepthIndex;
int mFlags;
void ControlRenderer::SetClipRect( const Rect<int>& clipRect )
{
- mImpl->mClipRect = clipRect;
}
void ControlRenderer::SetOffset( const Vector2& offset )
{
DoSetOnStage( actor );
+ mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, IsPreMultipliedAlphaEnabled());
mImpl->mRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, mImpl->mDepthIndex );
actor.AddRenderer( mImpl->mRenderer );
mImpl->mFlags |= Impl::IS_ON_STAGE;
}
}
+void ControlRenderer::EnablePreMultipliedAlpha( bool preMultipled )
+{
+ mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
+ if( mImpl->mRenderer )
+ {
+ mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, preMultipled);
+ }
+}
+
+bool ControlRenderer::IsPreMultipliedAlphaEnabled() const
+{
+ return mImpl->mFlags & Impl::IS_PREMULTIPLIED_ALPHA;
+}
+
void ControlRenderer::DoSetOnStage( Actor& actor )
{
}
*/
void CreatePropertyMap( Property::Map& map ) const;
+ /**
+ * @brief Set whether the Pre-multiplied Alpha Blending is required
+ *
+ * @param[in] preMultipled whether alpha is pre-multiplied.
+ */
+ void EnablePreMultipliedAlpha( bool preMultipled );
+
+ /**
+ * @brief Query whether alpha is pre-multiplied.
+ *
+ * @return True is alpha is pre-multiplied, false otherwise.
+ */
+ bool IsPreMultipliedAlphaEnabled() const;
+
protected:
/**
Property::Map quadVertexFormat;
quadVertexFormat[POSITION_ATTRIBUTE_NAME] = Property::VECTOR2;
- PropertyBuffer quadVertices = PropertyBuffer::New( quadVertexFormat, 4 );
- quadVertices.SetData(quadVertexData);
+ PropertyBuffer quadVertices = PropertyBuffer::New( quadVertexFormat );
+ quadVertices.SetData( quadVertexData, 4 );
// Create indices
unsigned int indexData[10] = { 0, 1, 1, 2, 2, 3, 3, 0 };
Property::Map indexFormat;
indexFormat[INDEX_NAME] = Property::INTEGER;
- PropertyBuffer indices = PropertyBuffer::New( indexFormat, sizeof(indexData)/sizeof(indexData[0]) );
- indices.SetData(indexData);
+ PropertyBuffer indices = PropertyBuffer::New( indexFormat );
+ indices.SetData( indexData, sizeof(indexData)/sizeof(indexData[0]) );
// Create the geometry object
Geometry geometry = Geometry::New();
i++;
}
- Toolkit::ImageAtlas newAtlas = Toolkit::ImageAtlas::New( DEFAULT_ATLAS_SIZE, DEFAULT_ATLAS_SIZE );
- if( !mBrokenImageUrl.empty() )
+ CreateNewAtlas();
+ mAtlasList.back().Upload( textureRect, url, size, fittingMode, orientationCorrection );
+ return mMaterialList.back();
+}
+
+Material ImageAtlasManager::Add( Vector4& textureRect,
+ PixelDataPtr pixelData )
+{
+
+ // big buffer, atlasing is not applied
+ if( static_cast<uint32_t>(pixelData->GetWidth()) * static_cast<uint32_t>(pixelData->GetHeight()) > MAX_ITEM_AREA
+ || pixelData->GetWidth()>DEFAULT_ATLAS_SIZE
+ || pixelData->GetHeight()>DEFAULT_ATLAS_SIZE )
{
- newAtlas.SetBrokenImage( mBrokenImageUrl );
+ return Material();
+ }
+
+ unsigned int i = 0;
+ for( AtlasContainer::iterator iter = mAtlasList.begin(); iter != mAtlasList.end(); ++iter)
+ {
+ if( (*iter).Upload( textureRect, pixelData ) )
+ {
+ return mMaterialList[i];
+ }
+ i++;
}
- mAtlasList.push_back( newAtlas );
- Material newMaterial = Material::New( mShader );
- newMaterial.AddTexture( newAtlas.GetAtlas(), mTextureUniformName );
- mMaterialList.push_back( newMaterial );
- newAtlas.Upload( textureRect, url, size, fittingMode, orientationCorrection );
+ CreateNewAtlas();
+ mAtlasList.back().Upload( textureRect, pixelData );
+ return mMaterialList.back();
- return newMaterial;
}
void ImageAtlasManager::Remove( Material material, const Vector4& textureRect )
}
}
+void ImageAtlasManager::CreateNewAtlas()
+{
+ Toolkit::ImageAtlas newAtlas = Toolkit::ImageAtlas::New( DEFAULT_ATLAS_SIZE, DEFAULT_ATLAS_SIZE );
+ if( !mBrokenImageUrl.empty() )
+ {
+ newAtlas.SetBrokenImage( mBrokenImageUrl );
+ }
+ mAtlasList.push_back( newAtlas );
+ Material newMaterial = Material::New( mShader );
+ newMaterial.AddTexture( newAtlas.GetAtlas(), mTextureUniformName );
+ mMaterialList.push_back( newMaterial );
+}
+
} // namespace Internal
} // namespace Toolkit
* @param [in] size The width and height to fit the loaded image to.
* @param [in] fittingMode The method used to fit the shape of the image before loading to the shape defined by the size parameter.
* @param [in] orientationCorrection Reorient the image to respect any orientation metadata in its header.
- * @return True if there is enough space to fit this image in,false otherwise.
+ * @return The material containing the image.
*/
Material Add( Vector4& textureRect,
const std::string& url,
bool orientationCorrection = true );
/**
+ * @brief Add a pixel buffer to the atlas
+ *
+ * @param [out] textureRect The texture area of the resource image in the atlas.
+ * @param [in] pixelData The pixel data.
+ * @return The material containing the image.
+ */
+ Material Add( Vector4& textureRect,
+ PixelDataPtr pixelData );
+
+ /**
* Remove the image at the given rectangle from the material.
*
* @param [in] material The material containing the atlas image.
*/
void SetBrokenImage( const std::string& brokenImageUrl );
+private:
+
+ /**
+ * @brief Create a new atlas.
+ *
+ * This method is called when the newly added image or pixel buffer cannot fit into the current atlas list.
+ */
+ void CreateNewAtlas();
+
protected:
/**
{
Property::Map vertexFormat;
vertexFormat[ "aPosition" ] = Property::VECTOR2;
- PropertyBuffer vertexPropertyBuffer = PropertyBuffer::New( vertexFormat, vertices.Size() );
+ PropertyBuffer vertexPropertyBuffer = PropertyBuffer::New( vertexFormat );
if( vertices.Size() > 0 )
{
- vertexPropertyBuffer.SetData( &vertices[ 0 ] );
+ vertexPropertyBuffer.SetData( &vertices[ 0 ], vertices.Size() );
}
Property::Map indexFormat;
indexFormat[ "indices" ] = Property::INTEGER;
- PropertyBuffer indexPropertyBuffer = PropertyBuffer::New( indexFormat, indices.Size() );
+ PropertyBuffer indexPropertyBuffer = PropertyBuffer::New( indexFormat );
if( indices.Size() > 0 )
{
- indexPropertyBuffer.SetData( &indices[ 0 ] );
+ indexPropertyBuffer.SetData( &indices[ 0 ], indices.Size() );
}
// Create the geometry object
mDesiredSize(),
mFittingMode( FittingMode::DEFAULT ),
mSamplingMode( SamplingMode::DEFAULT ),
- mIsAlphaPreMultiplied( false ),
mNativeFragmentShaderCode( ),
mNativeImageFlag( false )
{
InitializeRenderer( mImage );
}
- mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, mIsAlphaPreMultiplied);
}
void ImageRenderer::DoSetOffStage( Actor& actor )
{
actor.RemoveRenderer( mImpl->mRenderer );
CleanCache(mImageUrl);
-
mImage.Reset();
}
else
}
}
-void ImageRenderer::EnablePreMultipliedAlpha( bool preMultipled )
-{
- mIsAlphaPreMultiplied = preMultipled;
- if( mImpl->mRenderer )
- {
- mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, mIsAlphaPreMultiplied);
- }
-}
-
void ImageRenderer::ApplyImageToSampler( const Image& image )
{
if( image )
*/
void SetImage( Actor& actor, const Image& image );
- /**
- * @brief Set whether the Pre-multiplied Alpha Blending is required
- *
- * @param[in] preMultipled whether alpha is pre-multiplied.
- */
- void EnablePreMultipliedAlpha( bool preMultipled );
-
private:
/**
Dali::ImageDimensions mDesiredSize;
Dali::FittingMode::Type mFittingMode;
Dali::SamplingMode::Type mSamplingMode;
- bool mIsAlphaPreMultiplied;
std::string mNativeFragmentShaderCode;
bool mNativeImageFlag;
{
Property::Map vertexFormat;
vertexFormat[ "aPosition" ] = Property::VECTOR2;
- PropertyBuffer vertexPropertyBuffer = PropertyBuffer::New( vertexFormat, vertices.Size() );
+ PropertyBuffer vertexPropertyBuffer = PropertyBuffer::New( vertexFormat );
if( vertices.Size() > 0 )
{
- vertexPropertyBuffer.SetData( &vertices[ 0 ] );
+ vertexPropertyBuffer.SetData( &vertices[ 0 ], vertices.Size() );
}
Property::Map indexFormat;
indexFormat[ "indices" ] = Property::INTEGER;
- PropertyBuffer indexPropertyBuffer = PropertyBuffer::New( indexFormat, indices.Size() );
+ PropertyBuffer indexPropertyBuffer = PropertyBuffer::New( indexFormat );
if( indices.Size() > 0 )
{
- indexPropertyBuffer.SetData( &indices[ 0 ] );
+ indexPropertyBuffer.SetData( &indices[ 0 ], indices.Size() );
}
// Create the geometry object
// INTERNAL HEADER
#include <dali-toolkit/internal/controls/renderers/color/color-renderer.h>
+#include <dali-toolkit/internal/controls/renderers/svg/svg-renderer.h>
namespace Dali
{
{
RendererFactoryCache::RendererFactoryCache()
+: mSvgRasterizeThread( NULL )
{
}
RendererFactoryCache::~RendererFactoryCache()
{
+ SvgRasterizeThread::TerminateThread( mSvgRasterizeThread );
}
Geometry RendererFactoryCache::GetGeometry( GeometryType type )
Property::Map quadVertexFormat;
quadVertexFormat["aPosition"] = Property::VECTOR2;
- PropertyBuffer quadVertices = PropertyBuffer::New( quadVertexFormat, 4 );
- quadVertices.SetData(quadVertexData);
+ PropertyBuffer quadVertices = PropertyBuffer::New( quadVertexFormat );
+ quadVertices.SetData( quadVertexData, 4 );
// Create the geometry object
Geometry geometry = Geometry::New();
return geometry;
}
+SvgRasterizeThread* RendererFactoryCache::GetSVGRasterizationThread()
+{
+ if( !mSvgRasterizeThread )
+ {
+ mSvgRasterizeThread = new SvgRasterizeThread( new EventThreadCallback( MakeCallback( this, &RendererFactoryCache::ApplyRasterizedSVGToSampler ) ) );
+ mSvgRasterizeThread->Start();
+ }
+ return mSvgRasterizeThread;
+}
+
+void RendererFactoryCache::ApplyRasterizedSVGToSampler()
+{
+ while( RasterizingTaskPtr task = mSvgRasterizeThread->NextCompletedTask() )
+ {
+ task->GetSvgRenderer()->ApplyRasterizedImage( task->GetPixelData() );
+ }
+}
+
} // namespace Internal
} // namespace Toolkit
* limitations under the License.
*/
-#include <map>
+// INTERNAL INCLUDES
+#include "svg/svg-rasterize-thread.h"
// EXTERNAL INCLUDES
#include <dali/public-api/object/ref-object.h>
*/
Renderer GetDebugRenderer();
+ /**
+ * Get the SVG rasterization thread.
+ * @return A pointer pointing to the SVG rasterization thread.
+ */
+ SvgRasterizeThread* GetSVGRasterizationThread();
+
+private: // for svg rasterization thread
+
+ /**
+ * Applies the rasterized image to material
+ */
+ void ApplyRasterizedSVGToSampler();
+
protected:
/**
int FindRenderer( const std::string& key ) const;
private:
- // ToDo: test whether using the WeakHandle could improve the performance
- // With WeakHandle, the resource would be released automatically when no control is using it
-
Geometry mGeometry[GEOMETRY_TYPE_MAX+1];
Shader mShader[SHADER_TYPE_MAX+1];
CachedRenderers mRenderers;
Renderer mDebugRenderer;
+
+ SvgRasterizeThread* mSvgRasterizeThread;
};
} // namespace Internal
#include <dali-toolkit/internal/controls/renderers/gradient/gradient-renderer.h>
#include <dali-toolkit/internal/controls/renderers/npatch/npatch-renderer.h>
#include <dali-toolkit/internal/controls/renderers/image/image-renderer.h>
+#include <dali-toolkit/internal/controls/renderers/svg/svg-renderer.h>
#include <dali-toolkit/internal/controls/renderers/renderer-factory-cache.h>
#include <dali-toolkit/internal/controls/renderers/image-atlas-manager.h>
const char * const GRADIENT_RENDERER("gradient");
const char * const IMAGE_RENDERER("image");
const char * const N_PATCH_RENDERER("nPatch");
+const char * const SVG_RENDERER("svg");
const std::string TEXTURE_UNIFORM_NAME = "sTexture";
const char * const BROKEN_RENDERER_IMAGE_URL( DALI_IMAGE_DIR "broken.png");
+
}
namespace Dali
{
rendererPtr = new BorderRenderer( *( mFactoryCache.Get() ) );
}
+ else if( typeValue == SVG_RENDERER )
+ {
+ CreateAtlasManager();
+ rendererPtr = new SvgRenderer( *( mFactoryCache.Get() ), *( mAtlasManager.Get() ) );
+ }
}
if( rendererPtr )
return Toolkit::ControlRenderer( rendererPtr );
}
+ else if( SvgRenderer::IsSvgUrl( url ) )
+ {
+ CreateAtlasManager();
+ SvgRenderer* rendererPtr = new SvgRenderer( *( mFactoryCache.Get() ), *( mAtlasManager.Get() ) );
+ rendererPtr->SetImage( url, size );
+ return Toolkit::ControlRenderer( rendererPtr );
+ }
else
{
CreateAtlasManager();
return;
}
}
+ else if( SvgRenderer::IsSvgUrl( url ) )
+ {
+ SvgRenderer* rendererPtr = dynamic_cast< SvgRenderer* >( &GetImplementation( renderer ) );
+ if( rendererPtr )
+ {
+ rendererPtr->SetImage( url, size );
+ return;
+ }
+ }
else
{
ImageRenderer* rendererPtr = dynamic_cast< ImageRenderer* >( &GetImplementation( renderer ) );
//If there's no renderer type specified or if there hasn't been a renderer type change then we can reuse the renderer
if( !type || !type->Get( typeValue ) ||
- ( typeValue == IMAGE_RENDERER && typeid( controlRenderer ) == typeid( ImageRenderer ) ) ||
- ( typeValue == N_PATCH_RENDERER && typeid( controlRenderer ) == typeid( NPatchRenderer ) ) ||
- ( typeValue == COLOR_RENDERER && typeid( controlRenderer ) == typeid( ColorRenderer ) )||
- ( typeValue == GRADIENT_RENDERER && typeid( controlRenderer ) == typeid( GradientRenderer ) ) ||
- ( typeValue == BORDER_RENDERER && typeid( controlRenderer ) == typeid( BorderRenderer ) ) )
+ ( typeValue == IMAGE_RENDERER && typeid( controlRenderer ) == typeid( ImageRenderer ) ) ||
+ ( typeValue == N_PATCH_RENDERER && typeid( controlRenderer ) == typeid( NPatchRenderer ) ) ||
+ ( typeValue == COLOR_RENDERER && typeid( controlRenderer ) == typeid( ColorRenderer ) )||
+ ( typeValue == GRADIENT_RENDERER && typeid( controlRenderer ) == typeid( GradientRenderer ) ) ||
+ ( typeValue == BORDER_RENDERER && typeid( controlRenderer ) == typeid( BorderRenderer ) ) ||
+ ( typeValue == SVG_RENDERER && typeid( controlRenderer ) == typeid( SvgRenderer ) ) )
{
controlRenderer.Initialize( actor, propertyMap );
return;
--- /dev/null
+/*
+ * Copyright (c) 2013-14 Mikko Mononen memon@inside.org
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * The SVG parser is based on Anti-Grain Geometry 2.4 SVG example
+ * Copyright (C) 2002-2004 Maxim Shemanarev (McSeem) (http://www.antigrain.com/)
+ *
+ * Arc calculation code based on canvg (https://code.google.com/p/canvg/)
+ *
+ * Bounding box calculation based on http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
+ *
+ */
+
+#include "nanosvg.h"
+
+/**
+ * In the original software, The nanosvg implementation was included in the header file.
+ * We have separated the implementation to source file here.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+#define NSVG_PI (3.14159265358979323846264338327f)
+#define NSVG_KAPPA90 (0.5522847493f) // Lenght proportional to radius of a cubic bezier handle for 90deg arcs.
+
+#define NSVG_ALIGN_MIN 0
+#define NSVG_ALIGN_MID 1
+#define NSVG_ALIGN_MAX 2
+#define NSVG_ALIGN_NONE 0
+#define NSVG_ALIGN_MEET 1
+#define NSVG_ALIGN_SLICE 2
+
+#define NSVG_NOTUSED(v) do { (void)(1 ? (void)0 : ( (void)(v) ) ); } while(0)
+#define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16))
+
+#define NSVG_INLINE inline
+
+
+static int nsvg__isspace(char c)
+{
+ return strchr(" \t\n\v\f\r", c) != 0;
+}
+
+static int nsvg__isdigit(char c)
+{
+ return strchr("0123456789", c) != 0;
+}
+
+static int nsvg__isnum(char c)
+{
+ return strchr("0123456789+-.eE", c) != 0;
+}
+
+static NSVG_INLINE float nsvg__minf(float a, float b) { return a < b ? a : b; }
+static NSVG_INLINE float nsvg__maxf(float a, float b) { return a > b ? a : b; }
+
+
+// Simple XML parser
+
+#define NSVG_XML_TAG 1
+#define NSVG_XML_CONTENT 2
+#define NSVG_XML_MAX_ATTRIBS 256
+
+static void nsvg__parseContent(char* s,
+ void (*contentCb)(void* ud, const char* s),
+ void* ud)
+{
+ // Trim start white spaces
+ while (*s && nsvg__isspace(*s)) s++;
+ if (!*s) return;
+
+ if (contentCb)
+ (*contentCb)(ud, s);
+}
+
+static void nsvg__parseElement(char* s,
+ void (*startelCb)(void* ud, const char* el, const char** attr),
+ void (*endelCb)(void* ud, const char* el),
+ void* ud)
+{
+ const char* attr[NSVG_XML_MAX_ATTRIBS];
+ int nattr = 0;
+ char* name;
+ int start = 0;
+ int end = 0;
+ char quote;
+
+ // Skip white space after the '<'
+ while (*s && nsvg__isspace(*s)) s++;
+
+ // Check if the tag is end tag
+ if (*s == '/') {
+ s++;
+ end = 1;
+ } else {
+ start = 1;
+ }
+
+ // Skip comments, data and preprocessor stuff.
+ if (!*s || *s == '?' || *s == '!')
+ return;
+
+ // Get tag name
+ name = s;
+ while (*s && !nsvg__isspace(*s)) s++;
+ if (*s) { *s++ = '\0'; }
+
+ // Get attribs
+ while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS-3) {
+ // Skip white space before the attrib name
+ while (*s && nsvg__isspace(*s)) s++;
+ if (!*s) break;
+ if (*s == '/') {
+ end = 1;
+ break;
+ }
+ attr[nattr++] = s;
+ // Find end of the attrib name.
+ while (*s && !nsvg__isspace(*s) && *s != '=') s++;
+ if (*s) { *s++ = '\0'; }
+ // Skip until the beginning of the value.
+ while (*s && *s != '\"' && *s != '\'') s++;
+ if (!*s) break;
+ quote = *s;
+ s++;
+ // Store value and find the end of it.
+ attr[nattr++] = s;
+ while (*s && *s != quote) s++;
+ if (*s) { *s++ = '\0'; }
+ }
+
+ // List terminator
+ attr[nattr++] = 0;
+ attr[nattr++] = 0;
+
+ // Call callbacks.
+ if (start && startelCb)
+ (*startelCb)(ud, name, attr);
+ if (end && endelCb)
+ (*endelCb)(ud, name);
+}
+
+int nsvg__parseXML(char* input,
+ void (*startelCb)(void* ud, const char* el, const char** attr),
+ void (*endelCb)(void* ud, const char* el),
+ void (*contentCb)(void* ud, const char* s),
+ void* ud)
+{
+ char* s = input;
+ char* mark = s;
+ int state = NSVG_XML_CONTENT;
+ while (*s) {
+ if (*s == '<' && state == NSVG_XML_CONTENT) {
+ // Start of a tag
+ *s++ = '\0';
+ nsvg__parseContent(mark, contentCb, ud);
+ mark = s;
+ state = NSVG_XML_TAG;
+ } else if (*s == '>' && state == NSVG_XML_TAG) {
+ // Start of a content or new tag.
+ *s++ = '\0';
+ nsvg__parseElement(mark, startelCb, endelCb, ud);
+ mark = s;
+ state = NSVG_XML_CONTENT;
+ } else {
+ s++;
+ }
+ }
+
+ return 1;
+}
+
+
+/* Simple SVG parser. */
+
+#define NSVG_MAX_ATTR 128
+
+enum NSVGgradientUnits {
+ NSVG_USER_SPACE = 0,
+ NSVG_OBJECT_SPACE = 1,
+};
+
+#define NSVG_MAX_DASHES 8
+
+enum NSVGunits {
+ NSVG_UNITS_USER,
+ NSVG_UNITS_PX,
+ NSVG_UNITS_PT,
+ NSVG_UNITS_PC,
+ NSVG_UNITS_MM,
+ NSVG_UNITS_CM,
+ NSVG_UNITS_IN,
+ NSVG_UNITS_PERCENT,
+ NSVG_UNITS_EM,
+ NSVG_UNITS_EX,
+};
+
+typedef struct NSVGcoordinate {
+ float value;
+ int units;
+} NSVGcoordinate;
+
+typedef struct NSVGlinearData {
+ NSVGcoordinate x1, y1, x2, y2;
+} NSVGlinearData;
+
+typedef struct NSVGradialData {
+ NSVGcoordinate cx, cy, r, fx, fy;
+} NSVGradialData;
+
+typedef struct NSVGgradientData
+{
+ char id[64];
+ char ref[64];
+ char type;
+ union {
+ NSVGlinearData linear;
+ NSVGradialData radial;
+ };
+ char spread;
+ char units;
+ float xform[6];
+ int nstops;
+ NSVGgradientStop* stops;
+ struct NSVGgradientData* next;
+} NSVGgradientData;
+
+typedef struct NSVGattrib
+{
+ char id[64];
+ float xform[6];
+ unsigned int fillColor;
+ unsigned int strokeColor;
+ float opacity;
+ float fillOpacity;
+ float strokeOpacity;
+ char fillGradient[64];
+ char strokeGradient[64];
+ float strokeWidth;
+ float strokeDashOffset;
+ float strokeDashArray[NSVG_MAX_DASHES];
+ int strokeDashCount;
+ char strokeLineJoin;
+ char strokeLineCap;
+ char fillRule;
+ float fontSize;
+ unsigned int stopColor;
+ float stopOpacity;
+ float stopOffset;
+ char hasFill;
+ char hasStroke;
+ char visible;
+} NSVGattrib;
+
+typedef struct NSVGparser
+{
+ NSVGattrib attr[NSVG_MAX_ATTR];
+ int attrHead;
+ float* pts;
+ int npts;
+ int cpts;
+ NSVGpath* plist;
+ NSVGimage* image;
+ NSVGgradientData* gradients;
+ float viewMinx, viewMiny, viewWidth, viewHeight;
+ int alignX, alignY, alignType;
+ float dpi;
+ char pathFlag;
+ char defsFlag;
+} NSVGparser;
+
+static void nsvg__xformIdentity(float* t)
+{
+ t[0] = 1.0f; t[1] = 0.0f;
+ t[2] = 0.0f; t[3] = 1.0f;
+ t[4] = 0.0f; t[5] = 0.0f;
+}
+
+static void nsvg__xformSetTranslation(float* t, float tx, float ty)
+{
+ t[0] = 1.0f; t[1] = 0.0f;
+ t[2] = 0.0f; t[3] = 1.0f;
+ t[4] = tx; t[5] = ty;
+}
+
+static void nsvg__xformSetScale(float* t, float sx, float sy)
+{
+ t[0] = sx; t[1] = 0.0f;
+ t[2] = 0.0f; t[3] = sy;
+ t[4] = 0.0f; t[5] = 0.0f;
+}
+
+static void nsvg__xformSetSkewX(float* t, float a)
+{
+ t[0] = 1.0f; t[1] = 0.0f;
+ t[2] = tanf(a); t[3] = 1.0f;
+ t[4] = 0.0f; t[5] = 0.0f;
+}
+
+static void nsvg__xformSetSkewY(float* t, float a)
+{
+ t[0] = 1.0f; t[1] = tanf(a);
+ t[2] = 0.0f; t[3] = 1.0f;
+ t[4] = 0.0f; t[5] = 0.0f;
+}
+
+static void nsvg__xformSetRotation(float* t, float a)
+{
+ float cs = cosf(a), sn = sinf(a);
+ t[0] = cs; t[1] = sn;
+ t[2] = -sn; t[3] = cs;
+ t[4] = 0.0f; t[5] = 0.0f;
+}
+
+static void nsvg__xformMultiply(float* t, float* s)
+{
+ float t0 = t[0] * s[0] + t[1] * s[2];
+ float t2 = t[2] * s[0] + t[3] * s[2];
+ float t4 = t[4] * s[0] + t[5] * s[2] + s[4];
+ t[1] = t[0] * s[1] + t[1] * s[3];
+ t[3] = t[2] * s[1] + t[3] * s[3];
+ t[5] = t[4] * s[1] + t[5] * s[3] + s[5];
+ t[0] = t0;
+ t[2] = t2;
+ t[4] = t4;
+}
+
+static void nsvg__xformInverse(float* inv, float* t)
+{
+ double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1];
+ if (det > -1e-6 && det < 1e-6) {
+ nsvg__xformIdentity(t);
+ return;
+ }
+ invdet = 1.0 / det;
+ inv[0] = (float)(t[3] * invdet);
+ inv[2] = (float)(-t[2] * invdet);
+ inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet);
+ inv[1] = (float)(-t[1] * invdet);
+ inv[3] = (float)(t[0] * invdet);
+ inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet);
+}
+
+static void nsvg__xformPremultiply(float* t, float* s)
+{
+ float s2[6];
+ memcpy(s2, s, sizeof(float)*6);
+ nsvg__xformMultiply(s2, t);
+ memcpy(t, s2, sizeof(float)*6);
+}
+
+static void nsvg__xformPoint(float* dx, float* dy, float x, float y, float* t)
+{
+ *dx = x*t[0] + y*t[2] + t[4];
+ *dy = x*t[1] + y*t[3] + t[5];
+}
+
+static void nsvg__xformVec(float* dx, float* dy, float x, float y, float* t)
+{
+ *dx = x*t[0] + y*t[2];
+ *dy = x*t[1] + y*t[3];
+}
+
+#define NSVG_EPSILON (1e-12)
+
+static int nsvg__ptInBounds(float* pt, float* bounds)
+{
+ return pt[0] >= bounds[0] && pt[0] <= bounds[2] && pt[1] >= bounds[1] && pt[1] <= bounds[3];
+}
+
+
+static double nsvg__evalBezier(double t, double p0, double p1, double p2, double p3)
+{
+ double it = 1.0-t;
+ return it*it*it*p0 + 3.0*it*it*t*p1 + 3.0*it*t*t*p2 + t*t*t*p3;
+}
+
+static void nsvg__curveBounds(float* bounds, float* curve)
+{
+ int i, j, count;
+ double roots[2], a, b, c, b2ac, t, v;
+ float* v0 = &curve[0];
+ float* v1 = &curve[2];
+ float* v2 = &curve[4];
+ float* v3 = &curve[6];
+
+ // Start the bounding box by end points
+ bounds[0] = nsvg__minf(v0[0], v3[0]);
+ bounds[1] = nsvg__minf(v0[1], v3[1]);
+ bounds[2] = nsvg__maxf(v0[0], v3[0]);
+ bounds[3] = nsvg__maxf(v0[1], v3[1]);
+
+ // Bezier curve fits inside the convex hull of it's control points.
+ // If control points are inside the bounds, we're done.
+ if (nsvg__ptInBounds(v1, bounds) && nsvg__ptInBounds(v2, bounds))
+ return;
+
+ // Add bezier curve inflection points in X and Y.
+ for (i = 0; i < 2; i++) {
+ a = -3.0 * v0[i] + 9.0 * v1[i] - 9.0 * v2[i] + 3.0 * v3[i];
+ b = 6.0 * v0[i] - 12.0 * v1[i] + 6.0 * v2[i];
+ c = 3.0 * v1[i] - 3.0 * v0[i];
+ count = 0;
+ if (fabs(a) < NSVG_EPSILON) {
+ if (fabs(b) > NSVG_EPSILON) {
+ t = -c / b;
+ if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON)
+ roots[count++] = t;
+ }
+ } else {
+ b2ac = b*b - 4.0*c*a;
+ if (b2ac > NSVG_EPSILON) {
+ t = (-b + sqrt(b2ac)) / (2.0 * a);
+ if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON)
+ roots[count++] = t;
+ t = (-b - sqrt(b2ac)) / (2.0 * a);
+ if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON)
+ roots[count++] = t;
+ }
+ }
+ for (j = 0; j < count; j++) {
+ v = nsvg__evalBezier(roots[j], v0[i], v1[i], v2[i], v3[i]);
+ bounds[0+i] = nsvg__minf(bounds[0+i], (float)v);
+ bounds[2+i] = nsvg__maxf(bounds[2+i], (float)v);
+ }
+ }
+}
+
+static NSVGparser* nsvg__createParser()
+{
+ NSVGparser* p;
+ p = (NSVGparser*)malloc(sizeof(NSVGparser));
+ if (p == NULL) goto error;
+ memset(p, 0, sizeof(NSVGparser));
+
+ p->image = (NSVGimage*)malloc(sizeof(NSVGimage));
+ if (p->image == NULL) goto error;
+ memset(p->image, 0, sizeof(NSVGimage));
+
+ // Init style
+ nsvg__xformIdentity(p->attr[0].xform);
+ memset(p->attr[0].id, 0, sizeof p->attr[0].id);
+ p->attr[0].fillColor = NSVG_RGB(0,0,0);
+ p->attr[0].strokeColor = NSVG_RGB(0,0,0);
+ p->attr[0].opacity = 1;
+ p->attr[0].fillOpacity = 1;
+ p->attr[0].strokeOpacity = 1;
+ p->attr[0].stopOpacity = 1;
+ p->attr[0].strokeWidth = 1;
+ p->attr[0].strokeLineJoin = NSVG_JOIN_MITER;
+ p->attr[0].strokeLineCap = NSVG_CAP_BUTT;
+ p->attr[0].fillRule = NSVG_FILLRULE_NONZERO;
+ p->attr[0].hasFill = 1;
+ p->attr[0].visible = 1;
+
+ return p;
+
+error:
+ if (p) {
+ if (p->image) free(p->image);
+ free(p);
+ }
+ return NULL;
+}
+
+static void nsvg__deletePaths(NSVGpath* path)
+{
+ while (path) {
+ NSVGpath *next = path->next;
+ if (path->pts != NULL)
+ free(path->pts);
+ free(path);
+ path = next;
+ }
+}
+
+static void nsvg__deletePaint(NSVGpaint* paint)
+{
+ if (paint->type == NSVG_PAINT_LINEAR_GRADIENT || paint->type == NSVG_PAINT_RADIAL_GRADIENT)
+ free(paint->gradient);
+}
+
+static void nsvg__deleteGradientData(NSVGgradientData* grad)
+{
+ NSVGgradientData* next;
+ while (grad != NULL) {
+ next = grad->next;
+ free(grad->stops);
+ free(grad);
+ grad = next;
+ }
+}
+
+static void nsvg__deleteParser(NSVGparser* p)
+{
+ if (p != NULL) {
+ nsvg__deletePaths(p->plist);
+ nsvg__deleteGradientData(p->gradients);
+ nsvgDelete(p->image);
+ free(p->pts);
+ free(p);
+ }
+}
+
+static void nsvg__resetPath(NSVGparser* p)
+{
+ p->npts = 0;
+}
+
+static void nsvg__addPoint(NSVGparser* p, float x, float y)
+{
+ if (p->npts+1 > p->cpts) {
+ p->cpts = p->cpts ? p->cpts*2 : 8;
+ p->pts = (float*)realloc(p->pts, p->cpts*2*sizeof(float));
+ if (!p->pts) return;
+ }
+ p->pts[p->npts*2+0] = x;
+ p->pts[p->npts*2+1] = y;
+ p->npts++;
+}
+
+static void nsvg__moveTo(NSVGparser* p, float x, float y)
+{
+ if (p->npts > 0) {
+ p->pts[(p->npts-1)*2+0] = x;
+ p->pts[(p->npts-1)*2+1] = y;
+ } else {
+ nsvg__addPoint(p, x, y);
+ }
+}
+
+static void nsvg__lineTo(NSVGparser* p, float x, float y)
+{
+ float px,py, dx,dy;
+ if (p->npts > 0) {
+ px = p->pts[(p->npts-1)*2+0];
+ py = p->pts[(p->npts-1)*2+1];
+ dx = x - px;
+ dy = y - py;
+ nsvg__addPoint(p, px + dx/3.0f, py + dy/3.0f);
+ nsvg__addPoint(p, x - dx/3.0f, y - dy/3.0f);
+ nsvg__addPoint(p, x, y);
+ }
+}
+
+static void nsvg__cubicBezTo(NSVGparser* p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y)
+{
+ nsvg__addPoint(p, cpx1, cpy1);
+ nsvg__addPoint(p, cpx2, cpy2);
+ nsvg__addPoint(p, x, y);
+}
+
+static NSVGattrib* nsvg__getAttr(NSVGparser* p)
+{
+ return &p->attr[p->attrHead];
+}
+
+static void nsvg__pushAttr(NSVGparser* p)
+{
+ if (p->attrHead < NSVG_MAX_ATTR-1) {
+ p->attrHead++;
+ memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead-1], sizeof(NSVGattrib));
+ }
+}
+
+static void nsvg__popAttr(NSVGparser* p)
+{
+ if (p->attrHead > 0)
+ p->attrHead--;
+}
+
+static float nsvg__actualOrigX(NSVGparser* p)
+{
+ return p->viewMinx;
+}
+
+static float nsvg__actualOrigY(NSVGparser* p)
+{
+ return p->viewMiny;
+}
+
+static float nsvg__actualWidth(NSVGparser* p)
+{
+ return p->viewWidth;
+}
+
+static float nsvg__actualHeight(NSVGparser* p)
+{
+ return p->viewHeight;
+}
+
+static float nsvg__actualLength(NSVGparser* p)
+{
+ float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p);
+ return sqrtf(w*w + h*h) / sqrtf(2.0f);
+}
+
+static float nsvg__convertToPixels(NSVGparser* p, NSVGcoordinate c, float orig, float length)
+{
+ NSVGattrib* attr = nsvg__getAttr(p);
+ switch (c.units) {
+ case NSVG_UNITS_USER: return c.value;
+ case NSVG_UNITS_PX: return c.value;
+ case NSVG_UNITS_PT: return c.value / 72.0f * p->dpi;
+ case NSVG_UNITS_PC: return c.value / 6.0f * p->dpi;
+ case NSVG_UNITS_MM: return c.value / 25.4f * p->dpi;
+ case NSVG_UNITS_CM: return c.value / 2.54f * p->dpi;
+ case NSVG_UNITS_IN: return c.value * p->dpi;
+ case NSVG_UNITS_EM: return c.value * attr->fontSize;
+ case NSVG_UNITS_EX: return c.value * attr->fontSize * 0.52f; // x-height of Helvetica.
+ case NSVG_UNITS_PERCENT: return orig + c.value / 100.0f * length;
+ default: return c.value;
+ }
+ return c.value;
+}
+
+static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id)
+{
+ NSVGgradientData* grad = p->gradients;
+ while (grad) {
+ if (strcmp(grad->id, id) == 0)
+ return grad;
+ grad = grad->next;
+ }
+ return NULL;
+}
+
+static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const float* localBounds, char* paintType)
+{
+ NSVGattrib* attr = nsvg__getAttr(p);
+ NSVGgradientData* data = NULL;
+ NSVGgradientData* ref = NULL;
+ NSVGgradientStop* stops = NULL;
+ NSVGgradient* grad;
+ float ox, oy, sw, sh, sl;
+ int nstops = 0;
+
+ data = nsvg__findGradientData(p, id);
+ if (data == NULL) return NULL;
+
+ // TODO: use ref to fill in all unset values too.
+ ref = data;
+ while (ref != NULL) {
+ if (stops == NULL && ref->stops != NULL) {
+ stops = ref->stops;
+ nstops = ref->nstops;
+ break;
+ }
+ ref = nsvg__findGradientData(p, ref->ref);
+ }
+ if (stops == NULL) return NULL;
+
+ grad = (NSVGgradient*)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop)*(nstops-1));
+ if (grad == NULL) return NULL;
+
+ // The shape width and height.
+ if (data->units == NSVG_OBJECT_SPACE) {
+ ox = localBounds[0];
+ oy = localBounds[1];
+ sw = localBounds[2] - localBounds[0];
+ sh = localBounds[3] - localBounds[1];
+ } else {
+ ox = nsvg__actualOrigX(p);
+ oy = nsvg__actualOrigY(p);
+ sw = nsvg__actualWidth(p);
+ sh = nsvg__actualHeight(p);
+ }
+ sl = sqrtf(sw*sw + sh*sh) / sqrtf(2.0f);
+
+ if (data->type == NSVG_PAINT_LINEAR_GRADIENT) {
+ float x1, y1, x2, y2, dx, dy;
+ x1 = nsvg__convertToPixels(p, data->linear.x1, ox, sw);
+ y1 = nsvg__convertToPixels(p, data->linear.y1, oy, sh);
+ x2 = nsvg__convertToPixels(p, data->linear.x2, ox, sw);
+ y2 = nsvg__convertToPixels(p, data->linear.y2, oy, sh);
+ // Calculate transform aligned to the line
+ dx = x2 - x1;
+ dy = y2 - y1;
+ grad->xform[0] = dy; grad->xform[1] = -dx;
+ grad->xform[2] = dx; grad->xform[3] = dy;
+ grad->xform[4] = x1; grad->xform[5] = y1;
+ } else {
+ float cx, cy, fx, fy, r;
+ cx = nsvg__convertToPixels(p, data->radial.cx, ox, sw);
+ cy = nsvg__convertToPixels(p, data->radial.cy, oy, sh);
+ fx = nsvg__convertToPixels(p, data->radial.fx, ox, sw);
+ fy = nsvg__convertToPixels(p, data->radial.fy, oy, sh);
+ r = nsvg__convertToPixels(p, data->radial.r, 0, sl);
+ // Calculate transform aligned to the circle
+ grad->xform[0] = r; grad->xform[1] = 0;
+ grad->xform[2] = 0; grad->xform[3] = r;
+ grad->xform[4] = cx; grad->xform[5] = cy;
+ grad->fx = fx / r;
+ grad->fy = fy / r;
+ }
+
+ if( data->units == NSVG_OBJECT_SPACE)
+ {
+ float scaling[6],t[6];
+ scaling[0] = 1.0 / ( localBounds[2] - localBounds[0] );
+ scaling[1] = 0.0;
+ scaling[2] = 0.0;
+ scaling[3] = 1.0 / ( localBounds[3] - localBounds[1]);
+ scaling[4] = -localBounds[0] * scaling[0];
+ scaling[5] = -localBounds[1] * scaling[3];
+ memcpy(t, scaling, sizeof(float)*6);
+ nsvg__xformInverse(scaling, t);
+ nsvg__xformMultiply(grad->xform, data->xform);
+ nsvg__xformMultiply(grad->xform, scaling);
+ nsvg__xformMultiply(grad->xform, attr->xform);
+ }
+ else
+ {
+ nsvg__xformMultiply(grad->xform, data->xform);
+ nsvg__xformMultiply(grad->xform, attr->xform);
+ }
+
+ grad->spread = data->spread;
+ memcpy(grad->stops, stops, nstops*sizeof(NSVGgradientStop));
+ grad->nstops = nstops;
+
+ *paintType = data->type;
+
+ return grad;
+}
+
+static float nsvg__getAverageScale(float* t)
+{
+ float sx = sqrtf(t[0]*t[0] + t[2]*t[2]);
+ float sy = sqrtf(t[1]*t[1] + t[3]*t[3]);
+ return (sx + sy) * 0.5f;
+}
+
+static void nsvg__getLocalBounds(float* bounds, NSVGshape *shape, float* xform)
+{
+ NSVGpath* path;
+ float curve[4*2], curveBounds[4];
+ int i, first = 1;
+ for (path = shape->paths; path != NULL; path = path->next) {
+ nsvg__xformPoint(&curve[0], &curve[1], path->pts[0], path->pts[1], xform);
+ for (i = 0; i < path->npts-1; i += 3) {
+ nsvg__xformPoint(&curve[2], &curve[3], path->pts[(i+1)*2], path->pts[(i+1)*2+1], xform);
+ nsvg__xformPoint(&curve[4], &curve[5], path->pts[(i+2)*2], path->pts[(i+2)*2+1], xform);
+ nsvg__xformPoint(&curve[6], &curve[7], path->pts[(i+3)*2], path->pts[(i+3)*2+1], xform);
+ nsvg__curveBounds(curveBounds, curve);
+ if (first) {
+ bounds[0] = curveBounds[0];
+ bounds[1] = curveBounds[1];
+ bounds[2] = curveBounds[2];
+ bounds[3] = curveBounds[3];
+ first = 0;
+ } else {
+ bounds[0] = nsvg__minf(bounds[0], curveBounds[0]);
+ bounds[1] = nsvg__minf(bounds[1], curveBounds[1]);
+ bounds[2] = nsvg__maxf(bounds[2], curveBounds[2]);
+ bounds[3] = nsvg__maxf(bounds[3], curveBounds[3]);
+ }
+ curve[0] = curve[6];
+ curve[1] = curve[7];
+ }
+ }
+}
+
+static void nsvg__addShape(NSVGparser* p)
+{
+ NSVGattrib* attr = nsvg__getAttr(p);
+ float scale = 1.0f;
+ NSVGshape *shape, *cur, *prev;
+ NSVGpath* path;
+ int i;
+
+ if (p->plist == NULL)
+ return;
+
+ shape = (NSVGshape*)malloc(sizeof(NSVGshape));
+ if (shape == NULL) goto error;
+ memset(shape, 0, sizeof(NSVGshape));
+
+ memcpy(shape->id, attr->id, sizeof shape->id);
+ scale = nsvg__getAverageScale(attr->xform);
+ shape->strokeWidth = attr->strokeWidth * scale;
+ shape->strokeDashOffset = attr->strokeDashOffset * scale;
+ shape->strokeDashCount = attr->strokeDashCount;
+ for (i = 0; i < attr->strokeDashCount; i++)
+ shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale;
+ shape->strokeLineJoin = attr->strokeLineJoin;
+ shape->strokeLineCap = attr->strokeLineCap;
+ shape->fillRule = attr->fillRule;
+ shape->opacity = attr->opacity;
+
+ shape->paths = p->plist;
+ p->plist = NULL;
+
+ // Calculate shape bounds
+ shape->bounds[0] = shape->paths->bounds[0];
+ shape->bounds[1] = shape->paths->bounds[1];
+ shape->bounds[2] = shape->paths->bounds[2];
+ shape->bounds[3] = shape->paths->bounds[3];
+ for (path = shape->paths->next; path != NULL; path = path->next) {
+ shape->bounds[0] = nsvg__minf(shape->bounds[0], path->bounds[0]);
+ shape->bounds[1] = nsvg__minf(shape->bounds[1], path->bounds[1]);
+ shape->bounds[2] = nsvg__maxf(shape->bounds[2], path->bounds[2]);
+ shape->bounds[3] = nsvg__maxf(shape->bounds[3], path->bounds[3]);
+ }
+
+ // Set fill
+ if (attr->hasFill == 0) {
+ shape->fill.type = NSVG_PAINT_NONE;
+ } else if (attr->hasFill == 1) {
+ shape->fill.type = NSVG_PAINT_COLOR;
+ shape->fill.color = attr->fillColor;
+ shape->fill.color |= (unsigned int)(attr->fillOpacity*255) << 24;
+ } else if (attr->hasFill == 2) {
+ shape->opacity *= attr->fillOpacity;
+ float inv[6], localBounds[4];
+ nsvg__xformInverse(inv, attr->xform);
+ nsvg__getLocalBounds(localBounds, shape, inv);
+ shape->fill.gradient = nsvg__createGradient(p, attr->fillGradient, localBounds, &shape->fill.type);
+ if (shape->fill.gradient == NULL) {
+ shape->fill.type = NSVG_PAINT_NONE;
+ }
+ }
+
+ // Set stroke
+ if (attr->hasStroke == 0) {
+ shape->stroke.type = NSVG_PAINT_NONE;
+ } else if (attr->hasStroke == 1) {
+ shape->stroke.type = NSVG_PAINT_COLOR;
+ shape->stroke.color = attr->strokeColor;
+ shape->stroke.color |= (unsigned int)(attr->strokeOpacity*255) << 24;
+ } else if (attr->hasStroke == 2) {
+ float inv[6], localBounds[4];
+ nsvg__xformInverse(inv, attr->xform);
+ nsvg__getLocalBounds(localBounds, shape, inv);
+ shape->stroke.gradient = nsvg__createGradient(p, attr->strokeGradient, localBounds, &shape->stroke.type);
+ if (shape->stroke.gradient == NULL)
+ shape->stroke.type = NSVG_PAINT_NONE;
+ }
+
+ // Set flags
+ shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00);
+
+ // Add to tail
+ prev = NULL;
+ cur = p->image->shapes;
+ while (cur != NULL) {
+ prev = cur;
+ cur = cur->next;
+ }
+ if (prev == NULL)
+ p->image->shapes = shape;
+ else
+ prev->next = shape;
+
+ return;
+
+error:
+ if (shape) free(shape);
+}
+
+static void nsvg__addPath(NSVGparser* p, char closed)
+{
+ NSVGattrib* attr = nsvg__getAttr(p);
+ NSVGpath* path = NULL;
+ float bounds[4];
+ float* curve;
+ int i;
+
+ if (p->npts < 4)
+ return;
+
+ if (closed)
+ nsvg__lineTo(p, p->pts[0], p->pts[1]);
+
+ path = (NSVGpath*)malloc(sizeof(NSVGpath));
+ if (path == NULL) goto error;
+ memset(path, 0, sizeof(NSVGpath));
+
+ path->pts = (float*)malloc(p->npts*2*sizeof(float));
+ if (path->pts == NULL) goto error;
+ path->closed = closed;
+ path->npts = p->npts;
+
+ // Transform path.
+ for (i = 0; i < p->npts; ++i)
+ nsvg__xformPoint(&path->pts[i*2], &path->pts[i*2+1], p->pts[i*2], p->pts[i*2+1], attr->xform);
+
+ // Find bounds
+ for (i = 0; i < path->npts-1; i += 3) {
+ curve = &path->pts[i*2];
+ nsvg__curveBounds(bounds, curve);
+ if (i == 0) {
+ path->bounds[0] = bounds[0];
+ path->bounds[1] = bounds[1];
+ path->bounds[2] = bounds[2];
+ path->bounds[3] = bounds[3];
+ } else {
+ path->bounds[0] = nsvg__minf(path->bounds[0], bounds[0]);
+ path->bounds[1] = nsvg__minf(path->bounds[1], bounds[1]);
+ path->bounds[2] = nsvg__maxf(path->bounds[2], bounds[2]);
+ path->bounds[3] = nsvg__maxf(path->bounds[3], bounds[3]);
+ }
+ }
+
+ path->next = p->plist;
+ p->plist = path;
+
+ return;
+
+error:
+ if (path != NULL) {
+ if (path->pts != NULL) free(path->pts);
+ free(path);
+ }
+}
+
+static const char* nsvg__parseNumber(const char* s, char* it, const int size)
+{
+ const int last = size-1;
+ int i = 0;
+
+ // sign
+ if (*s == '-' || *s == '+') {
+ if (i < last) it[i++] = *s;
+ s++;
+ }
+ // integer part
+ while (*s && nsvg__isdigit(*s)) {
+ if (i < last) it[i++] = *s;
+ s++;
+ }
+ if (*s == '.') {
+ // decimal point
+ if (i < last) it[i++] = *s;
+ s++;
+ // fraction part
+ while (*s && nsvg__isdigit(*s)) {
+ if (i < last) it[i++] = *s;
+ s++;
+ }
+ }
+ // exponent
+ if (*s == 'e' || *s == 'E') {
+ if (i < last) it[i++] = *s;
+ s++;
+ if (*s == '-' || *s == '+') {
+ if (i < last) it[i++] = *s;
+ s++;
+ }
+ while (*s && nsvg__isdigit(*s)) {
+ if (i < last) it[i++] = *s;
+ s++;
+ }
+ }
+ it[i] = '\0';
+
+ return s;
+}
+
+static const char* nsvg__getNextPathItem(const char* s, char* it)
+{
+ it[0] = '\0';
+ // Skip white spaces and commas
+ while (*s && (nsvg__isspace(*s) || *s == ',')) s++;
+ if (!*s) return s;
+ if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) {
+ s = nsvg__parseNumber(s, it, 64);
+ } else {
+ // Parse command
+ it[0] = *s++;
+ it[1] = '\0';
+ return s;
+ }
+
+ return s;
+}
+
+static unsigned int nsvg__parseColorHex(const char* str)
+{
+ unsigned int c = 0, r = 0, g = 0, b = 0;
+ int n = 0;
+ str++; // skip #
+ // Calculate number of characters.
+ while(str[n] && !nsvg__isspace(str[n]))
+ n++;
+ if (n == 6) {
+ sscanf(str, "%x", &c);
+ } else if (n == 3) {
+ sscanf(str, "%x", &c);
+ c = (c&0xf) | ((c&0xf0) << 4) | ((c&0xf00) << 8);
+ c |= c<<4;
+ }
+ r = (c >> 16) & 0xff;
+ g = (c >> 8) & 0xff;
+ b = c & 0xff;
+ return NSVG_RGB(r,g,b);
+}
+
+static unsigned int nsvg__parseColorRGB(const char* str)
+{
+ int r = -1, g = -1, b = -1;
+ char s1[32]="", s2[32]="";
+ sscanf(str + 4, "%d%[%%, \t]%d%[%%, \t]%d", &r, s1, &g, s2, &b);
+ if (strchr(s1, '%')) {
+ return NSVG_RGB((r*255)/100,(g*255)/100,(b*255)/100);
+ } else {
+ return NSVG_RGB(r,g,b);
+ }
+}
+
+typedef struct NSVGNamedColor {
+ const char* name;
+ unsigned int color;
+} NSVGNamedColor;
+
+NSVGNamedColor nsvg__colors[] = {
+
+ { "red", NSVG_RGB(255, 0, 0) },
+ { "green", NSVG_RGB( 0, 128, 0) },
+ { "blue", NSVG_RGB( 0, 0, 255) },
+ { "yellow", NSVG_RGB(255, 255, 0) },
+ { "cyan", NSVG_RGB( 0, 255, 255) },
+ { "magenta", NSVG_RGB(255, 0, 255) },
+ { "black", NSVG_RGB( 0, 0, 0) },
+ { "grey", NSVG_RGB(128, 128, 128) },
+ { "gray", NSVG_RGB(128, 128, 128) },
+ { "white", NSVG_RGB(255, 255, 255) },
+
+ { "aliceblue", NSVG_RGB(240, 248, 255) },
+ { "antiquewhite", NSVG_RGB(250, 235, 215) },
+ { "aqua", NSVG_RGB( 0, 255, 255) },
+ { "aquamarine", NSVG_RGB(127, 255, 212) },
+ { "azure", NSVG_RGB(240, 255, 255) },
+ { "beige", NSVG_RGB(245, 245, 220) },
+ { "bisque", NSVG_RGB(255, 228, 196) },
+ { "blanchedalmond", NSVG_RGB(255, 235, 205) },
+ { "blueviolet", NSVG_RGB(138, 43, 226) },
+ { "brown", NSVG_RGB(165, 42, 42) },
+ { "burlywood", NSVG_RGB(222, 184, 135) },
+ { "cadetblue", NSVG_RGB( 95, 158, 160) },
+ { "chartreuse", NSVG_RGB(127, 255, 0) },
+ { "chocolate", NSVG_RGB(210, 105, 30) },
+ { "coral", NSVG_RGB(255, 127, 80) },
+ { "cornflowerblue", NSVG_RGB(100, 149, 237) },
+ { "cornsilk", NSVG_RGB(255, 248, 220) },
+ { "crimson", NSVG_RGB(220, 20, 60) },
+ { "darkblue", NSVG_RGB( 0, 0, 139) },
+ { "darkcyan", NSVG_RGB( 0, 139, 139) },
+ { "darkgoldenrod", NSVG_RGB(184, 134, 11) },
+ { "darkgray", NSVG_RGB(169, 169, 169) },
+ { "darkgreen", NSVG_RGB( 0, 100, 0) },
+ { "darkgrey", NSVG_RGB(169, 169, 169) },
+ { "darkkhaki", NSVG_RGB(189, 183, 107) },
+ { "darkmagenta", NSVG_RGB(139, 0, 139) },
+ { "darkolivegreen", NSVG_RGB( 85, 107, 47) },
+ { "darkorange", NSVG_RGB(255, 140, 0) },
+ { "darkorchid", NSVG_RGB(153, 50, 204) },
+ { "darkred", NSVG_RGB(139, 0, 0) },
+ { "darksalmon", NSVG_RGB(233, 150, 122) },
+ { "darkseagreen", NSVG_RGB(143, 188, 143) },
+ { "darkslateblue", NSVG_RGB( 72, 61, 139) },
+ { "darkslategray", NSVG_RGB( 47, 79, 79) },
+ { "darkslategrey", NSVG_RGB( 47, 79, 79) },
+ { "darkturquoise", NSVG_RGB( 0, 206, 209) },
+ { "darkviolet", NSVG_RGB(148, 0, 211) },
+ { "deeppink", NSVG_RGB(255, 20, 147) },
+ { "deepskyblue", NSVG_RGB( 0, 191, 255) },
+ { "dimgray", NSVG_RGB(105, 105, 105) },
+ { "dimgrey", NSVG_RGB(105, 105, 105) },
+ { "dodgerblue", NSVG_RGB( 30, 144, 255) },
+ { "firebrick", NSVG_RGB(178, 34, 34) },
+ { "floralwhite", NSVG_RGB(255, 250, 240) },
+ { "forestgreen", NSVG_RGB( 34, 139, 34) },
+ { "fuchsia", NSVG_RGB(255, 0, 255) },
+ { "gainsboro", NSVG_RGB(220, 220, 220) },
+ { "ghostwhite", NSVG_RGB(248, 248, 255) },
+ { "gold", NSVG_RGB(255, 215, 0) },
+ { "goldenrod", NSVG_RGB(218, 165, 32) },
+ { "greenyellow", NSVG_RGB(173, 255, 47) },
+ { "honeydew", NSVG_RGB(240, 255, 240) },
+ { "hotpink", NSVG_RGB(255, 105, 180) },
+ { "indianred", NSVG_RGB(205, 92, 92) },
+ { "indigo", NSVG_RGB( 75, 0, 130) },
+ { "ivory", NSVG_RGB(255, 255, 240) },
+ { "khaki", NSVG_RGB(240, 230, 140) },
+ { "lavender", NSVG_RGB(230, 230, 250) },
+ { "lavenderblush", NSVG_RGB(255, 240, 245) },
+ { "lawngreen", NSVG_RGB(124, 252, 0) },
+ { "lemonchiffon", NSVG_RGB(255, 250, 205) },
+ { "lightblue", NSVG_RGB(173, 216, 230) },
+ { "lightcoral", NSVG_RGB(240, 128, 128) },
+ { "lightcyan", NSVG_RGB(224, 255, 255) },
+ { "lightgoldenrodyellow", NSVG_RGB(250, 250, 210) },
+ { "lightgray", NSVG_RGB(211, 211, 211) },
+ { "lightgreen", NSVG_RGB(144, 238, 144) },
+ { "lightgrey", NSVG_RGB(211, 211, 211) },
+ { "lightpink", NSVG_RGB(255, 182, 193) },
+ { "lightsalmon", NSVG_RGB(255, 160, 122) },
+ { "lightseagreen", NSVG_RGB( 32, 178, 170) },
+ { "lightskyblue", NSVG_RGB(135, 206, 250) },
+ { "lightslategray", NSVG_RGB(119, 136, 153) },
+ { "lightslategrey", NSVG_RGB(119, 136, 153) },
+ { "lightsteelblue", NSVG_RGB(176, 196, 222) },
+ { "lightyellow", NSVG_RGB(255, 255, 224) },
+ { "lime", NSVG_RGB( 0, 255, 0) },
+ { "limegreen", NSVG_RGB( 50, 205, 50) },
+ { "linen", NSVG_RGB(250, 240, 230) },
+ { "maroon", NSVG_RGB(128, 0, 0) },
+ { "mediumaquamarine", NSVG_RGB(102, 205, 170) },
+ { "mediumblue", NSVG_RGB( 0, 0, 205) },
+ { "mediumorchid", NSVG_RGB(186, 85, 211) },
+ { "mediumpurple", NSVG_RGB(147, 112, 219) },
+ { "mediumseagreen", NSVG_RGB( 60, 179, 113) },
+ { "mediumslateblue", NSVG_RGB(123, 104, 238) },
+ { "mediumspringgreen", NSVG_RGB( 0, 250, 154) },
+ { "mediumturquoise", NSVG_RGB( 72, 209, 204) },
+ { "mediumvioletred", NSVG_RGB(199, 21, 133) },
+ { "midnightblue", NSVG_RGB( 25, 25, 112) },
+ { "mintcream", NSVG_RGB(245, 255, 250) },
+ { "mistyrose", NSVG_RGB(255, 228, 225) },
+ { "moccasin", NSVG_RGB(255, 228, 181) },
+ { "navajowhite", NSVG_RGB(255, 222, 173) },
+ { "navy", NSVG_RGB( 0, 0, 128) },
+ { "oldlace", NSVG_RGB(253, 245, 230) },
+ { "olive", NSVG_RGB(128, 128, 0) },
+ { "olivedrab", NSVG_RGB(107, 142, 35) },
+ { "orange", NSVG_RGB(255, 165, 0) },
+ { "orangered", NSVG_RGB(255, 69, 0) },
+ { "orchid", NSVG_RGB(218, 112, 214) },
+ { "palegoldenrod", NSVG_RGB(238, 232, 170) },
+ { "palegreen", NSVG_RGB(152, 251, 152) },
+ { "paleturquoise", NSVG_RGB(175, 238, 238) },
+ { "palevioletred", NSVG_RGB(219, 112, 147) },
+ { "papayawhip", NSVG_RGB(255, 239, 213) },
+ { "peachpuff", NSVG_RGB(255, 218, 185) },
+ { "peru", NSVG_RGB(205, 133, 63) },
+ { "pink", NSVG_RGB(255, 192, 203) },
+ { "plum", NSVG_RGB(221, 160, 221) },
+ { "powderblue", NSVG_RGB(176, 224, 230) },
+ { "purple", NSVG_RGB(128, 0, 128) },
+ { "rosybrown", NSVG_RGB(188, 143, 143) },
+ { "royalblue", NSVG_RGB( 65, 105, 225) },
+ { "saddlebrown", NSVG_RGB(139, 69, 19) },
+ { "salmon", NSVG_RGB(250, 128, 114) },
+ { "sandybrown", NSVG_RGB(244, 164, 96) },
+ { "seagreen", NSVG_RGB( 46, 139, 87) },
+ { "seashell", NSVG_RGB(255, 245, 238) },
+ { "sienna", NSVG_RGB(160, 82, 45) },
+ { "silver", NSVG_RGB(192, 192, 192) },
+ { "skyblue", NSVG_RGB(135, 206, 235) },
+ { "slateblue", NSVG_RGB(106, 90, 205) },
+ { "slategray", NSVG_RGB(112, 128, 144) },
+ { "slategrey", NSVG_RGB(112, 128, 144) },
+ { "snow", NSVG_RGB(255, 250, 250) },
+ { "springgreen", NSVG_RGB( 0, 255, 127) },
+ { "steelblue", NSVG_RGB( 70, 130, 180) },
+ { "tan", NSVG_RGB(210, 180, 140) },
+ { "teal", NSVG_RGB( 0, 128, 128) },
+ { "thistle", NSVG_RGB(216, 191, 216) },
+ { "tomato", NSVG_RGB(255, 99, 71) },
+ { "turquoise", NSVG_RGB( 64, 224, 208) },
+ { "violet", NSVG_RGB(238, 130, 238) },
+ { "wheat", NSVG_RGB(245, 222, 179) },
+ { "whitesmoke", NSVG_RGB(245, 245, 245) },
+ { "yellowgreen", NSVG_RGB(154, 205, 50) },
+};
+
+static unsigned int nsvg__parseColorName(const char* str)
+{
+ int i, ncolors = sizeof(nsvg__colors) / sizeof(NSVGNamedColor);
+
+ for (i = 0; i < ncolors; i++) {
+ if (strcmp(nsvg__colors[i].name, str) == 0) {
+ return nsvg__colors[i].color;
+ }
+ }
+
+ return NSVG_RGB(128, 128, 128);
+}
+
+static unsigned int nsvg__parseColor(const char* str)
+{
+ size_t len = 0;
+ while(*str == ' ') ++str;
+ len = strlen(str);
+ if (len >= 1 && *str == '#')
+ return nsvg__parseColorHex(str);
+ else if (len >= 4 && str[0] == 'r' && str[1] == 'g' && str[2] == 'b' && str[3] == '(')
+ return nsvg__parseColorRGB(str);
+ return nsvg__parseColorName(str);
+}
+
+static float nsvg__parseOpacity(const char* str)
+{
+ float val = 0;
+ sscanf(str, "%f", &val);
+ if (val < 0.0f) val = 0.0f;
+ if (val > 1.0f) val = 1.0f;
+ return val;
+}
+
+static int nsvg__parseUnits(const char* units)
+{
+ if (units[0] == 'p' && units[1] == 'x')
+ return NSVG_UNITS_PX;
+ else if (units[0] == 'p' && units[1] == 't')
+ return NSVG_UNITS_PT;
+ else if (units[0] == 'p' && units[1] == 'c')
+ return NSVG_UNITS_PC;
+ else if (units[0] == 'm' && units[1] == 'm')
+ return NSVG_UNITS_MM;
+ else if (units[0] == 'c' && units[1] == 'm')
+ return NSVG_UNITS_CM;
+ else if (units[0] == 'i' && units[1] == 'n')
+ return NSVG_UNITS_IN;
+ else if (units[0] == '%')
+ return NSVG_UNITS_PERCENT;
+ else if (units[0] == 'e' && units[1] == 'm')
+ return NSVG_UNITS_EM;
+ else if (units[0] == 'e' && units[1] == 'x')
+ return NSVG_UNITS_EX;
+ return NSVG_UNITS_USER;
+}
+
+static NSVGcoordinate nsvg__parseCoordinateRaw(const char* str)
+{
+ NSVGcoordinate coord = {0, NSVG_UNITS_USER};
+ char units[32]="";
+ sscanf(str, "%f%s", &coord.value, units);
+ coord.units = nsvg__parseUnits(units);
+ return coord;
+}
+
+static NSVGcoordinate nsvg__coord(float v, int units)
+{
+ NSVGcoordinate coord = {v, units};
+ return coord;
+}
+
+static float nsvg__parseCoordinate(NSVGparser* p, const char* str, float orig, float length)
+{
+ NSVGcoordinate coord = nsvg__parseCoordinateRaw(str);
+ return nsvg__convertToPixels(p, coord, orig, length);
+}
+
+static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int* na)
+{
+ const char* end;
+ const char* ptr;
+ char it[64];
+
+ *na = 0;
+ ptr = str;
+ while (*ptr && *ptr != '(') ++ptr;
+ if (*ptr == 0)
+ return 1;
+ end = ptr;
+ while (*end && *end != ')') ++end;
+ if (*end == 0)
+ return 1;
+
+ while (ptr < end) {
+ if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) {
+ if (*na >= maxNa) return 0;
+ ptr = nsvg__parseNumber(ptr, it, 64);
+ args[(*na)++] = (float)atof(it);
+ } else {
+ ++ptr;
+ }
+ }
+ return (int)(end - str);
+}
+
+
+static int nsvg__parseMatrix(float* xform, const char* str)
+{
+ float t[6];
+ int na = 0;
+ int len = nsvg__parseTransformArgs(str, t, 6, &na);
+ if (na != 6) return len;
+ memcpy(xform, t, sizeof(float)*6);
+ return len;
+}
+
+static int nsvg__parseTranslate(float* xform, const char* str)
+{
+ float args[2];
+ float t[6];
+ int na = 0;
+ int len = nsvg__parseTransformArgs(str, args, 2, &na);
+ if (na == 1) args[1] = 0.0;
+
+ nsvg__xformSetTranslation(t, args[0], args[1]);
+ memcpy(xform, t, sizeof(float)*6);
+ return len;
+}
+
+static int nsvg__parseScale(float* xform, const char* str)
+{
+ float args[2];
+ int na = 0;
+ float t[6];
+ int len = nsvg__parseTransformArgs(str, args, 2, &na);
+ if (na == 1) args[1] = args[0];
+ nsvg__xformSetScale(t, args[0], args[1]);
+ memcpy(xform, t, sizeof(float)*6);
+ return len;
+}
+
+static int nsvg__parseSkewX(float* xform, const char* str)
+{
+ float args[1];
+ int na = 0;
+ float t[6];
+ int len = nsvg__parseTransformArgs(str, args, 1, &na);
+ nsvg__xformSetSkewX(t, args[0]/180.0f*NSVG_PI);
+ memcpy(xform, t, sizeof(float)*6);
+ return len;
+}
+
+static int nsvg__parseSkewY(float* xform, const char* str)
+{
+ float args[1];
+ int na = 0;
+ float t[6];
+ int len = nsvg__parseTransformArgs(str, args, 1, &na);
+ nsvg__xformSetSkewY(t, args[0]/180.0f*NSVG_PI);
+ memcpy(xform, t, sizeof(float)*6);
+ return len;
+}
+
+static int nsvg__parseRotate(float* xform, const char* str)
+{
+ float args[3];
+ int na = 0;
+ float m[6];
+ float t[6];
+ int len = nsvg__parseTransformArgs(str, args, 3, &na);
+ if (na == 1)
+ args[1] = args[2] = 0.0f;
+ nsvg__xformIdentity(m);
+
+ if (na > 1) {
+ nsvg__xformSetTranslation(t, -args[1], -args[2]);
+ nsvg__xformMultiply(m, t);
+ }
+
+ nsvg__xformSetRotation(t, args[0]/180.0f*NSVG_PI);
+ nsvg__xformMultiply(m, t);
+
+ if (na > 1) {
+ nsvg__xformSetTranslation(t, args[1], args[2]);
+ nsvg__xformMultiply(m, t);
+ }
+
+ memcpy(xform, m, sizeof(float)*6);
+
+ return len;
+}
+
+static void nsvg__parseTransform(float* xform, const char* str)
+{
+ float t[6];
+ nsvg__xformIdentity(xform);
+ while (*str)
+ {
+ if (strncmp(str, "matrix", 6) == 0)
+ str += nsvg__parseMatrix(t, str);
+ else if (strncmp(str, "translate", 9) == 0)
+ str += nsvg__parseTranslate(t, str);
+ else if (strncmp(str, "scale", 5) == 0)
+ str += nsvg__parseScale(t, str);
+ else if (strncmp(str, "rotate", 6) == 0)
+ str += nsvg__parseRotate(t, str);
+ else if (strncmp(str, "skewX", 5) == 0)
+ str += nsvg__parseSkewX(t, str);
+ else if (strncmp(str, "skewY", 5) == 0)
+ str += nsvg__parseSkewY(t, str);
+ else{
+ ++str;
+ continue;
+ }
+
+ nsvg__xformPremultiply(xform, t);
+ }
+}
+
+static void nsvg__parseUrl(char* id, const char* str)
+{
+ int i = 0;
+ str += 4; // "url(";
+ if (*str == '#')
+ str++;
+ while (i < 63 && *str != ')') {
+ id[i] = *str++;
+ i++;
+ }
+ id[i] = '\0';
+}
+
+static char nsvg__parseLineCap(const char* str)
+{
+ if (strcmp(str, "butt") == 0)
+ return NSVG_CAP_BUTT;
+ else if (strcmp(str, "round") == 0)
+ return NSVG_CAP_ROUND;
+ else if (strcmp(str, "square") == 0)
+ return NSVG_CAP_SQUARE;
+ // TODO: handle inherit.
+ return NSVG_CAP_BUTT;
+}
+
+static char nsvg__parseLineJoin(const char* str)
+{
+ if (strcmp(str, "miter") == 0)
+ return NSVG_JOIN_MITER;
+ else if (strcmp(str, "round") == 0)
+ return NSVG_JOIN_ROUND;
+ else if (strcmp(str, "bevel") == 0)
+ return NSVG_JOIN_BEVEL;
+ // TODO: handle inherit.
+ return NSVG_CAP_BUTT;
+}
+
+static char nsvg__parseFillRule(const char* str)
+{
+ if (strcmp(str, "nonzero") == 0)
+ return NSVG_FILLRULE_NONZERO;
+ else if (strcmp(str, "evenodd") == 0)
+ return NSVG_FILLRULE_EVENODD;
+ // TODO: handle inherit.
+ return NSVG_FILLRULE_NONZERO;
+}
+
+static const char* nsvg__getNextDashItem(const char* s, char* it)
+{
+ int n = 0;
+ it[0] = '\0';
+ // Skip white spaces and commas
+ while (*s && (nsvg__isspace(*s) || *s == ',')) s++;
+ // Advance until whitespace, comma or end.
+ while (*s && (!nsvg__isspace(*s) && *s != ',')) {
+ if (n < 63)
+ it[n++] = *s;
+ s++;
+ }
+ it[n++] = '\0';
+ return s;
+}
+
+static int nsvg__parseStrokeDashArray(NSVGparser* p, const char* str, float* strokeDashArray)
+{
+ char item[64];
+ int count = 0, i;
+ float sum = 0.0f;
+
+ // Handle "none"
+ if (str[0] == 'n')
+ return 0;
+
+ // Parse dashes
+ while (*str) {
+ str = nsvg__getNextDashItem(str, item);
+ if (!*item) break;
+ if (count < NSVG_MAX_DASHES)
+ strokeDashArray[count++] = fabsf(nsvg__parseCoordinate(p, item, 0.0f, nsvg__actualLength(p)));
+ }
+
+ for (i = 0; i < count; i++)
+ sum += strokeDashArray[i];
+ if (sum <= 1e-6f)
+ count = 0;
+
+ return count;
+}
+
+static void nsvg__parseStyle(NSVGparser* p, const char* str);
+
+static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value)
+{
+ float xform[6];
+ NSVGattrib* attr = nsvg__getAttr(p);
+ if (!attr) return 0;
+
+ if (strcmp(name, "style") == 0) {
+ nsvg__parseStyle(p, value);
+ } else if (strcmp(name, "display") == 0) {
+ if (strcmp(value, "none") == 0)
+ attr->visible = 0;
+ // Don't reset ->visible on display:inline, one display:none hides the whole subtree
+
+ } else if (strcmp(name, "fill") == 0) {
+ if (strcmp(value, "none") == 0) {
+ attr->hasFill = 0;
+ } else if (strncmp(value, "url(", 4) == 0) {
+ attr->hasFill = 2;
+ nsvg__parseUrl(attr->fillGradient, value);
+ } else {
+ attr->hasFill = 1;
+ attr->fillColor = nsvg__parseColor(value);
+ }
+ } else if (strcmp(name, "opacity") == 0) {
+ attr->opacity = nsvg__parseOpacity(value);
+ } else if (strcmp(name, "fill-opacity") == 0) {
+ attr->fillOpacity = nsvg__parseOpacity(value);
+ } else if (strcmp(name, "stroke") == 0) {
+ if (strcmp(value, "none") == 0) {
+ attr->hasStroke = 0;
+ } else if (strncmp(value, "url(", 4) == 0) {
+ attr->hasStroke = 2;
+ nsvg__parseUrl(attr->strokeGradient, value);
+ } else {
+ attr->hasStroke = 1;
+ attr->strokeColor = nsvg__parseColor(value);
+ }
+ } else if (strcmp(name, "stroke-width") == 0) {
+ attr->strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
+ } else if (strcmp(name, "stroke-dasharray") == 0) {
+ attr->strokeDashCount = nsvg__parseStrokeDashArray(p, value, attr->strokeDashArray);
+ } else if (strcmp(name, "stroke-dashoffset") == 0) {
+ attr->strokeDashOffset = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
+ } else if (strcmp(name, "stroke-opacity") == 0) {
+ attr->strokeOpacity = nsvg__parseOpacity(value);
+ } else if (strcmp(name, "stroke-linecap") == 0) {
+ attr->strokeLineCap = nsvg__parseLineCap(value);
+ } else if (strcmp(name, "stroke-linejoin") == 0) {
+ attr->strokeLineJoin = nsvg__parseLineJoin(value);
+ } else if (strcmp(name, "fill-rule") == 0) {
+ attr->fillRule = nsvg__parseFillRule(value);
+ } else if (strcmp(name, "font-size") == 0) {
+ attr->fontSize = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
+ } else if (strcmp(name, "transform") == 0) {
+ nsvg__parseTransform(xform, value);
+ nsvg__xformPremultiply(attr->xform, xform);
+ } else if (strcmp(name, "stop-color") == 0) {
+ attr->stopColor = nsvg__parseColor(value);
+ } else if (strcmp(name, "stop-opacity") == 0) {
+ attr->stopOpacity = nsvg__parseOpacity(value);
+ } else if (strcmp(name, "offset") == 0) {
+ attr->stopOffset = nsvg__parseCoordinate(p, value, 0.0f, 1.0f);
+ } else if (strcmp(name, "id") == 0) {
+ strncpy(attr->id, value, 63);
+ attr->id[63] = '\0';
+ } else {
+ return 0;
+ }
+ return 1;
+}
+
+static int nsvg__parseNameValue(NSVGparser* p, const char* start, const char* end)
+{
+ const char* str;
+ const char* val;
+ char name[512];
+ char value[512];
+ int n;
+
+ str = start;
+ while (str < end && *str != ':') ++str;
+
+ val = str;
+
+ // Right Trim
+ while (str > start && (*str == ':' || nsvg__isspace(*str))) --str;
+ ++str;
+
+ n = (int)(str - start);
+ if (n > 511) n = 511;
+ if (n) memcpy(name, start, n);
+ name[n] = 0;
+
+ while (val < end && (*val == ':' || nsvg__isspace(*val))) ++val;
+
+ n = (int)(end - val);
+ if (n > 511) n = 511;
+ if (n) memcpy(value, val, n);
+ value[n] = 0;
+
+ return nsvg__parseAttr(p, name, value);
+}
+
+static void nsvg__parseStyle(NSVGparser* p, const char* str)
+{
+ const char* start;
+ const char* end;
+
+ while (*str) {
+ // Left Trim
+ while(*str && nsvg__isspace(*str)) ++str;
+ start = str;
+ while(*str && *str != ';') ++str;
+ end = str;
+
+ // Right Trim
+ while (end > start && (*end == ';' || nsvg__isspace(*end))) --end;
+ ++end;
+
+ nsvg__parseNameValue(p, start, end);
+ if (*str) ++str;
+ }
+}
+
+static void nsvg__parseAttribs(NSVGparser* p, const char** attr)
+{
+ int i;
+ for (i = 0; attr[i]; i += 2)
+ {
+ if (strcmp(attr[i], "style") == 0)
+ nsvg__parseStyle(p, attr[i + 1]);
+ else
+ nsvg__parseAttr(p, attr[i], attr[i + 1]);
+ }
+}
+
+static int nsvg__getArgsPerElement(char cmd)
+{
+ switch (cmd) {
+ case 'v':
+ case 'V':
+ case 'h':
+ case 'H':
+ return 1;
+ case 'm':
+ case 'M':
+ case 'l':
+ case 'L':
+ case 't':
+ case 'T':
+ return 2;
+ case 'q':
+ case 'Q':
+ case 's':
+ case 'S':
+ return 4;
+ case 'c':
+ case 'C':
+ return 6;
+ case 'a':
+ case 'A':
+ return 7;
+ }
+ return 0;
+}
+
+static void nsvg__pathMoveTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
+{
+ if (rel) {
+ *cpx += args[0];
+ *cpy += args[1];
+ } else {
+ *cpx = args[0];
+ *cpy = args[1];
+ }
+ nsvg__moveTo(p, *cpx, *cpy);
+}
+
+static void nsvg__pathLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
+{
+ if (rel) {
+ *cpx += args[0];
+ *cpy += args[1];
+ } else {
+ *cpx = args[0];
+ *cpy = args[1];
+ }
+ nsvg__lineTo(p, *cpx, *cpy);
+}
+
+static void nsvg__pathHLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
+{
+ if (rel)
+ *cpx += args[0];
+ else
+ *cpx = args[0];
+ nsvg__lineTo(p, *cpx, *cpy);
+}
+
+static void nsvg__pathVLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
+{
+ if (rel)
+ *cpy += args[0];
+ else
+ *cpy = args[0];
+ nsvg__lineTo(p, *cpx, *cpy);
+}
+
+static void nsvg__pathCubicBezTo(NSVGparser* p, float* cpx, float* cpy,
+ float* cpx2, float* cpy2, float* args, int rel)
+{
+ float x2, y2, cx1, cy1, cx2, cy2;
+
+ if (rel) {
+ cx1 = *cpx + args[0];
+ cy1 = *cpy + args[1];
+ cx2 = *cpx + args[2];
+ cy2 = *cpy + args[3];
+ x2 = *cpx + args[4];
+ y2 = *cpy + args[5];
+ } else {
+ cx1 = args[0];
+ cy1 = args[1];
+ cx2 = args[2];
+ cy2 = args[3];
+ x2 = args[4];
+ y2 = args[5];
+ }
+
+ nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
+
+ *cpx2 = cx2;
+ *cpy2 = cy2;
+ *cpx = x2;
+ *cpy = y2;
+}
+
+static void nsvg__pathCubicBezShortTo(NSVGparser* p, float* cpx, float* cpy,
+ float* cpx2, float* cpy2, float* args, int rel)
+{
+ float x1, y1, x2, y2, cx1, cy1, cx2, cy2;
+
+ x1 = *cpx;
+ y1 = *cpy;
+ if (rel) {
+ cx2 = *cpx + args[0];
+ cy2 = *cpy + args[1];
+ x2 = *cpx + args[2];
+ y2 = *cpy + args[3];
+ } else {
+ cx2 = args[0];
+ cy2 = args[1];
+ x2 = args[2];
+ y2 = args[3];
+ }
+
+ cx1 = 2*x1 - *cpx2;
+ cy1 = 2*y1 - *cpy2;
+
+ nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
+
+ *cpx2 = cx2;
+ *cpy2 = cy2;
+ *cpx = x2;
+ *cpy = y2;
+}
+
+static void nsvg__pathQuadBezTo(NSVGparser* p, float* cpx, float* cpy,
+ float* cpx2, float* cpy2, float* args, int rel)
+{
+ float x1, y1, x2, y2, cx, cy;
+ float cx1, cy1, cx2, cy2;
+
+ x1 = *cpx;
+ y1 = *cpy;
+ if (rel) {
+ cx = *cpx + args[0];
+ cy = *cpy + args[1];
+ x2 = *cpx + args[2];
+ y2 = *cpy + args[3];
+ } else {
+ cx = args[0];
+ cy = args[1];
+ x2 = args[2];
+ y2 = args[3];
+ }
+
+ // Convert to cubic bezier
+ cx1 = x1 + 2.0f/3.0f*(cx - x1);
+ cy1 = y1 + 2.0f/3.0f*(cy - y1);
+ cx2 = x2 + 2.0f/3.0f*(cx - x2);
+ cy2 = y2 + 2.0f/3.0f*(cy - y2);
+
+ nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
+
+ *cpx2 = cx;
+ *cpy2 = cy;
+ *cpx = x2;
+ *cpy = y2;
+}
+
+static void nsvg__pathQuadBezShortTo(NSVGparser* p, float* cpx, float* cpy,
+ float* cpx2, float* cpy2, float* args, int rel)
+{
+ float x1, y1, x2, y2, cx, cy;
+ float cx1, cy1, cx2, cy2;
+
+ x1 = *cpx;
+ y1 = *cpy;
+ if (rel) {
+ x2 = *cpx + args[0];
+ y2 = *cpy + args[1];
+ } else {
+ x2 = args[0];
+ y2 = args[1];
+ }
+
+ cx = 2*x1 - *cpx2;
+ cy = 2*y1 - *cpy2;
+
+ // Convert to cubix bezier
+ cx1 = x1 + 2.0f/3.0f*(cx - x1);
+ cy1 = y1 + 2.0f/3.0f*(cy - y1);
+ cx2 = x2 + 2.0f/3.0f*(cx - x2);
+ cy2 = y2 + 2.0f/3.0f*(cy - y2);
+
+ nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
+
+ *cpx2 = cx;
+ *cpy2 = cy;
+ *cpx = x2;
+ *cpy = y2;
+}
+
+static float nsvg__sqr(float x) { return x*x; }
+static float nsvg__vmag(float x, float y) { return sqrtf(x*x + y*y); }
+
+static float nsvg__vecrat(float ux, float uy, float vx, float vy)
+{
+ return (ux*vx + uy*vy) / (nsvg__vmag(ux,uy) * nsvg__vmag(vx,vy));
+}
+
+static float nsvg__vecang(float ux, float uy, float vx, float vy)
+{
+ float r = nsvg__vecrat(ux,uy, vx,vy);
+ if (r < -1.0f) r = -1.0f;
+ if (r > 1.0f) r = 1.0f;
+ return ((ux*vy < uy*vx) ? -1.0f : 1.0f) * acosf(r);
+}
+
+static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
+{
+ // Ported from canvg (https://code.google.com/p/canvg/)
+ float rx, ry, rotx;
+ float x1, y1, x2, y2, cx, cy, dx, dy, d;
+ float x1p, y1p, cxp, cyp, s, sa, sb;
+ float ux, uy, vx, vy, a1, da;
+ float x, y, tanx, tany, a, px = 0, py = 0, ptanx = 0, ptany = 0, t[6];
+ float sinrx, cosrx;
+ int fa, fs;
+ int i, ndivs;
+ float hda, kappa;
+
+ rx = fabsf(args[0]); // y radius
+ ry = fabsf(args[1]); // x radius
+ rotx = args[2] / 180.0f * NSVG_PI; // x rotation engle
+ fa = fabsf(args[3]) > 1e-6 ? 1 : 0; // Large arc
+ fs = fabsf(args[4]) > 1e-6 ? 1 : 0; // Sweep direction
+ x1 = *cpx; // start point
+ y1 = *cpy;
+ if (rel) { // end point
+ x2 = *cpx + args[5];
+ y2 = *cpy + args[6];
+ } else {
+ x2 = args[5];
+ y2 = args[6];
+ }
+
+ dx = x1 - x2;
+ dy = y1 - y2;
+ d = sqrtf(dx*dx + dy*dy);
+ if (d < 1e-6f || rx < 1e-6f || ry < 1e-6f) {
+ // The arc degenerates to a line
+ nsvg__lineTo(p, x2, y2);
+ *cpx = x2;
+ *cpy = y2;
+ return;
+ }
+
+ sinrx = sinf(rotx);
+ cosrx = cosf(rotx);
+
+ // Convert to center point parameterization.
+ // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
+ // 1) Compute x1', y1'
+ x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f;
+ y1p = -sinrx * dx / 2.0f + cosrx * dy / 2.0f;
+ d = nsvg__sqr(x1p)/nsvg__sqr(rx) + nsvg__sqr(y1p)/nsvg__sqr(ry);
+ if (d > 1) {
+ d = sqrtf(d);
+ rx *= d;
+ ry *= d;
+ }
+ // 2) Compute cx', cy'
+ s = 0.0f;
+ sa = nsvg__sqr(rx)*nsvg__sqr(ry) - nsvg__sqr(rx)*nsvg__sqr(y1p) - nsvg__sqr(ry)*nsvg__sqr(x1p);
+ sb = nsvg__sqr(rx)*nsvg__sqr(y1p) + nsvg__sqr(ry)*nsvg__sqr(x1p);
+ if (sa < 0.0f) sa = 0.0f;
+ if (sb > 0.0f)
+ s = sqrtf(sa / sb);
+ if (fa == fs)
+ s = -s;
+ cxp = s * rx * y1p / ry;
+ cyp = s * -ry * x1p / rx;
+
+ // 3) Compute cx,cy from cx',cy'
+ cx = (x1 + x2)/2.0f + cosrx*cxp - sinrx*cyp;
+ cy = (y1 + y2)/2.0f + sinrx*cxp + cosrx*cyp;
+
+ // 4) Calculate theta1, and delta theta.
+ ux = (x1p - cxp) / rx;
+ uy = (y1p - cyp) / ry;
+ vx = (-x1p - cxp) / rx;
+ vy = (-y1p - cyp) / ry;
+ a1 = nsvg__vecang(1.0f,0.0f, ux,uy); // Initial angle
+ da = nsvg__vecang(ux,uy, vx,vy); // Delta angle
+
+// if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI;
+// if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0;
+
+ if (fa) {
+ // Choose large arc
+ if (da > 0.0f)
+ da = da - 2*NSVG_PI;
+ else
+ da = 2*NSVG_PI + da;
+ }
+
+ // Approximate the arc using cubic spline segments.
+ t[0] = cosrx; t[1] = sinrx;
+ t[2] = -sinrx; t[3] = cosrx;
+ t[4] = cx; t[5] = cy;
+
+ // Split arc into max 90 degree segments.
+ // The loop assumes an iteration per end point (including start and end), this +1.
+ ndivs = (int)(fabsf(da) / (NSVG_PI*0.5f) + 1.0f);
+ hda = (da / (float)ndivs) / 2.0f;
+ kappa = fabsf(4.0f / 3.0f * (1.0f - cosf(hda)) / sinf(hda));
+ if (da < 0.0f)
+ kappa = -kappa;
+
+ for (i = 0; i <= ndivs; i++) {
+ a = a1 + da * (i/(float)ndivs);
+ dx = cosf(a);
+ dy = sinf(a);
+ nsvg__xformPoint(&x, &y, dx*rx, dy*ry, t); // position
+ nsvg__xformVec(&tanx, &tany, -dy*rx * kappa, dx*ry * kappa, t); // tangent
+ if (i > 0)
+ nsvg__cubicBezTo(p, px+ptanx,py+ptany, x-tanx, y-tany, x, y);
+ px = x;
+ py = y;
+ ptanx = tanx;
+ ptany = tany;
+ }
+
+ *cpx = x2;
+ *cpy = y2;
+}
+
+static void nsvg__parsePath(NSVGparser* p, const char** attr)
+{
+ const char* s = NULL;
+ char cmd = '\0';
+ float args[10];
+ int nargs;
+ int rargs = 0;
+ float cpx, cpy, cpx2, cpy2;
+ const char* tmp[4];
+ char closedFlag;
+ int i;
+ char item[64];
+
+ for (i = 0; attr[i]; i += 2) {
+ if (strcmp(attr[i], "d") == 0) {
+ s = attr[i + 1];
+ } else {
+ tmp[0] = attr[i];
+ tmp[1] = attr[i + 1];
+ tmp[2] = 0;
+ tmp[3] = 0;
+ nsvg__parseAttribs(p, tmp);
+ }
+ }
+
+ if (s) {
+ nsvg__resetPath(p);
+ cpx = 0; cpy = 0;
+ cpx2 = 0; cpy2 = 0;
+ closedFlag = 0;
+ nargs = 0;
+
+ while (*s) {
+ s = nsvg__getNextPathItem(s, item);
+ if (!*item) break;
+ if (nsvg__isnum(item[0])) {
+ if (nargs < 10)
+ args[nargs++] = (float)atof(item);
+ if (nargs >= rargs) {
+ switch (cmd) {
+ case 'm':
+ case 'M':
+ nsvg__pathMoveTo(p, &cpx, &cpy, args, cmd == 'm' ? 1 : 0);
+ // Moveto can be followed by multiple coordinate pairs,
+ // which should be treated as linetos.
+ cmd = (cmd == 'm') ? 'l' : 'L';
+ rargs = nsvg__getArgsPerElement(cmd);
+ cpx2 = cpx; cpy2 = cpy;
+ break;
+ case 'l':
+ case 'L':
+ nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0);
+ cpx2 = cpx; cpy2 = cpy;
+ break;
+ case 'H':
+ case 'h':
+ nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0);
+ cpx2 = cpx; cpy2 = cpy;
+ break;
+ case 'V':
+ case 'v':
+ nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0);
+ cpx2 = cpx; cpy2 = cpy;
+ break;
+ case 'C':
+ case 'c':
+ nsvg__pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0);
+ break;
+ case 'S':
+ case 's':
+ nsvg__pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0);
+ break;
+ case 'Q':
+ case 'q':
+ nsvg__pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0);
+ break;
+ case 'T':
+ case 't':
+ nsvg__pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 't' ? 1 : 0);
+ break;
+ case 'A':
+ case 'a':
+ nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0);
+ cpx2 = cpx; cpy2 = cpy;
+ break;
+ default:
+ if (nargs >= 2) {
+ cpx = args[nargs-2];
+ cpy = args[nargs-1];
+ cpx2 = cpx; cpy2 = cpy;
+ }
+ break;
+ }
+ nargs = 0;
+ }
+ } else {
+ cmd = item[0];
+ rargs = nsvg__getArgsPerElement(cmd);
+ if (cmd == 'M' || cmd == 'm') {
+ // Commit path.
+ if (p->npts > 0)
+ nsvg__addPath(p, closedFlag);
+ // Start new subpath.
+ nsvg__resetPath(p);
+ closedFlag = 0;
+ nargs = 0;
+ } else if (cmd == 'Z' || cmd == 'z') {
+ closedFlag = 1;
+ // Commit path.
+ if (p->npts > 0) {
+ // Move current point to first point
+ cpx = p->pts[0];
+ cpy = p->pts[1];
+ cpx2 = cpx; cpy2 = cpy;
+ nsvg__addPath(p, closedFlag);
+ }
+ // Start new subpath.
+ nsvg__resetPath(p);
+ nsvg__moveTo(p, cpx, cpy);
+ closedFlag = 0;
+ nargs = 0;
+ }
+ }
+ }
+ // Commit path.
+ if (p->npts)
+ nsvg__addPath(p, closedFlag);
+ }
+
+ nsvg__addShape(p);
+}
+
+static void nsvg__parseRect(NSVGparser* p, const char** attr)
+{
+ float x = 0.0f;
+ float y = 0.0f;
+ float w = 0.0f;
+ float h = 0.0f;
+ float rx = -1.0f; // marks not set
+ float ry = -1.0f;
+ int i;
+
+ for (i = 0; attr[i]; i += 2) {
+ if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
+ if (strcmp(attr[i], "x") == 0) x = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
+ if (strcmp(attr[i], "y") == 0) y = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
+ if (strcmp(attr[i], "width") == 0) w = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p));
+ if (strcmp(attr[i], "height") == 0) h = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p));
+ if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)));
+ if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)));
+ }
+ }
+
+ if (rx < 0.0f && ry > 0.0f) rx = ry;
+ if (ry < 0.0f && rx > 0.0f) ry = rx;
+ if (rx < 0.0f) rx = 0.0f;
+ if (ry < 0.0f) ry = 0.0f;
+ if (rx > w/2.0f) rx = w/2.0f;
+ if (ry > h/2.0f) ry = h/2.0f;
+
+ if (w != 0.0f && h != 0.0f) {
+ nsvg__resetPath(p);
+
+ if (rx < 0.00001f || ry < 0.0001f) {
+ nsvg__moveTo(p, x, y);
+ nsvg__lineTo(p, x+w, y);
+ nsvg__lineTo(p, x+w, y+h);
+ nsvg__lineTo(p, x, y+h);
+ } else {
+ // Rounded rectangle
+ nsvg__moveTo(p, x+rx, y);
+ nsvg__lineTo(p, x+w-rx, y);
+ nsvg__cubicBezTo(p, x+w-rx*(1-NSVG_KAPPA90), y, x+w, y+ry*(1-NSVG_KAPPA90), x+w, y+ry);
+ nsvg__lineTo(p, x+w, y+h-ry);
+ nsvg__cubicBezTo(p, x+w, y+h-ry*(1-NSVG_KAPPA90), x+w-rx*(1-NSVG_KAPPA90), y+h, x+w-rx, y+h);
+ nsvg__lineTo(p, x+rx, y+h);
+ nsvg__cubicBezTo(p, x+rx*(1-NSVG_KAPPA90), y+h, x, y+h-ry*(1-NSVG_KAPPA90), x, y+h-ry);
+ nsvg__lineTo(p, x, y+ry);
+ nsvg__cubicBezTo(p, x, y+ry*(1-NSVG_KAPPA90), x+rx*(1-NSVG_KAPPA90), y, x+rx, y);
+ }
+
+ nsvg__addPath(p, 1);
+
+ nsvg__addShape(p);
+ }
+}
+
+static void nsvg__parseCircle(NSVGparser* p, const char** attr)
+{
+ float cx = 0.0f;
+ float cy = 0.0f;
+ float r = 0.0f;
+ int i;
+
+ for (i = 0; attr[i]; i += 2) {
+ if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
+ if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
+ if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
+ if (strcmp(attr[i], "r") == 0) r = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualLength(p)));
+ }
+ }
+
+ if (r > 0.0f) {
+ nsvg__resetPath(p);
+
+ nsvg__moveTo(p, cx+r, cy);
+ nsvg__cubicBezTo(p, cx+r, cy+r*NSVG_KAPPA90, cx+r*NSVG_KAPPA90, cy+r, cx, cy+r);
+ nsvg__cubicBezTo(p, cx-r*NSVG_KAPPA90, cy+r, cx-r, cy+r*NSVG_KAPPA90, cx-r, cy);
+ nsvg__cubicBezTo(p, cx-r, cy-r*NSVG_KAPPA90, cx-r*NSVG_KAPPA90, cy-r, cx, cy-r);
+ nsvg__cubicBezTo(p, cx+r*NSVG_KAPPA90, cy-r, cx+r, cy-r*NSVG_KAPPA90, cx+r, cy);
+
+ nsvg__addPath(p, 1);
+
+ nsvg__addShape(p);
+ }
+}
+
+static void nsvg__parseEllipse(NSVGparser* p, const char** attr)
+{
+ float cx = 0.0f;
+ float cy = 0.0f;
+ float rx = 0.0f;
+ float ry = 0.0f;
+ int i;
+
+ for (i = 0; attr[i]; i += 2) {
+ if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
+ if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
+ if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
+ if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)));
+ if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)));
+ }
+ }
+
+ if (rx > 0.0f && ry > 0.0f) {
+
+ nsvg__resetPath(p);
+
+ nsvg__moveTo(p, cx+rx, cy);
+ nsvg__cubicBezTo(p, cx+rx, cy+ry*NSVG_KAPPA90, cx+rx*NSVG_KAPPA90, cy+ry, cx, cy+ry);
+ nsvg__cubicBezTo(p, cx-rx*NSVG_KAPPA90, cy+ry, cx-rx, cy+ry*NSVG_KAPPA90, cx-rx, cy);
+ nsvg__cubicBezTo(p, cx-rx, cy-ry*NSVG_KAPPA90, cx-rx*NSVG_KAPPA90, cy-ry, cx, cy-ry);
+ nsvg__cubicBezTo(p, cx+rx*NSVG_KAPPA90, cy-ry, cx+rx, cy-ry*NSVG_KAPPA90, cx+rx, cy);
+
+ nsvg__addPath(p, 1);
+
+ nsvg__addShape(p);
+ }
+}
+
+static void nsvg__parseLine(NSVGparser* p, const char** attr)
+{
+ float x1 = 0.0;
+ float y1 = 0.0;
+ float x2 = 0.0;
+ float y2 = 0.0;
+ int i;
+
+ for (i = 0; attr[i]; i += 2) {
+ if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
+ if (strcmp(attr[i], "x1") == 0) x1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
+ if (strcmp(attr[i], "y1") == 0) y1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
+ if (strcmp(attr[i], "x2") == 0) x2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
+ if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
+ }
+ }
+
+ nsvg__resetPath(p);
+
+ nsvg__moveTo(p, x1, y1);
+ nsvg__lineTo(p, x2, y2);
+
+ nsvg__addPath(p, 0);
+
+ nsvg__addShape(p);
+}
+
+static void nsvg__parsePoly(NSVGparser* p, const char** attr, int closeFlag)
+{
+ int i;
+ const char* s;
+ float args[2];
+ int nargs, npts = 0;
+ char item[64];
+
+ nsvg__resetPath(p);
+
+ for (i = 0; attr[i]; i += 2) {
+ if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
+ if (strcmp(attr[i], "points") == 0) {
+ s = attr[i + 1];
+ nargs = 0;
+ while (*s) {
+ s = nsvg__getNextPathItem(s, item);
+ args[nargs++] = (float)atof(item);
+ if (nargs >= 2) {
+ if (npts == 0)
+ nsvg__moveTo(p, args[0], args[1]);
+ else
+ nsvg__lineTo(p, args[0], args[1]);
+ nargs = 0;
+ npts++;
+ }
+ }
+ }
+ }
+ }
+
+ nsvg__addPath(p, (char)closeFlag);
+
+ nsvg__addShape(p);
+}
+
+static void nsvg__parseSVG(NSVGparser* p, const char** attr)
+{
+ int i;
+ for (i = 0; attr[i]; i += 2) {
+ if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
+ if (strcmp(attr[i], "width") == 0) {
+ p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 1.0f);
+ } else if (strcmp(attr[i], "height") == 0) {
+ p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 1.0f);
+ } else if (strcmp(attr[i], "viewBox") == 0) {
+ sscanf(attr[i + 1], "%f%*[%%, \t]%f%*[%%, \t]%f%*[%%, \t]%f", &p->viewMinx, &p->viewMiny, &p->viewWidth, &p->viewHeight);
+ } else if (strcmp(attr[i], "preserveAspectRatio") == 0) {
+ if (strstr(attr[i + 1], "none") != 0) {
+ // No uniform scaling
+ p->alignType = NSVG_ALIGN_NONE;
+ } else {
+ // Parse X align
+ if (strstr(attr[i + 1], "xMin") != 0)
+ p->alignX = NSVG_ALIGN_MIN;
+ else if (strstr(attr[i + 1], "xMid") != 0)
+ p->alignX = NSVG_ALIGN_MID;
+ else if (strstr(attr[i + 1], "xMax") != 0)
+ p->alignX = NSVG_ALIGN_MAX;
+ // Parse X align
+ if (strstr(attr[i + 1], "yMin") != 0)
+ p->alignY = NSVG_ALIGN_MIN;
+ else if (strstr(attr[i + 1], "yMid") != 0)
+ p->alignY = NSVG_ALIGN_MID;
+ else if (strstr(attr[i + 1], "yMax") != 0)
+ p->alignY = NSVG_ALIGN_MAX;
+ // Parse meet/slice
+ p->alignType = NSVG_ALIGN_MEET;
+ if (strstr(attr[i + 1], "slice") != 0)
+ p->alignType = NSVG_ALIGN_SLICE;
+ }
+ }
+ }
+ }
+}
+
+static void nsvg__parseGradient(NSVGparser* p, const char** attr, char type)
+{
+ int i;
+ NSVGgradientData* grad = (NSVGgradientData*)malloc(sizeof(NSVGgradientData));
+ if (grad == NULL) return;
+ memset(grad, 0, sizeof(NSVGgradientData));
+ grad->units = NSVG_OBJECT_SPACE;
+ grad->type = type;
+ if (grad->type == NSVG_PAINT_LINEAR_GRADIENT) {
+ grad->linear.x1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT);
+ grad->linear.y1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT);
+ grad->linear.x2 = nsvg__coord(100.0f, NSVG_UNITS_PERCENT);
+ grad->linear.y2 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT);
+ } else if (grad->type == NSVG_PAINT_RADIAL_GRADIENT) {
+ grad->radial.cx = nsvg__coord(50.0f, NSVG_UNITS_PERCENT);
+ grad->radial.cy = nsvg__coord(50.0f, NSVG_UNITS_PERCENT);
+ grad->radial.r = nsvg__coord(50.0f, NSVG_UNITS_PERCENT);
+ }
+
+ nsvg__xformIdentity(grad->xform);
+
+ for (i = 0; attr[i]; i += 2) {
+ if (strcmp(attr[i], "id") == 0) {
+ strncpy(grad->id, attr[i+1], 63);
+ grad->id[63] = '\0';
+ } else if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
+ if (strcmp(attr[i], "gradientUnits") == 0) {
+ if (strcmp(attr[i+1], "objectBoundingBox") == 0)
+ grad->units = NSVG_OBJECT_SPACE;
+ else
+ grad->units = NSVG_USER_SPACE;
+ } else if (strcmp(attr[i], "gradientTransform") == 0) {
+ nsvg__parseTransform(grad->xform, attr[i + 1]);
+ } else if (strcmp(attr[i], "cx") == 0) {
+ grad->radial.cx = nsvg__parseCoordinateRaw(attr[i + 1]);
+ } else if (strcmp(attr[i], "cy") == 0) {
+ grad->radial.cy = nsvg__parseCoordinateRaw(attr[i + 1]);
+ } else if (strcmp(attr[i], "r") == 0) {
+ grad->radial.r = nsvg__parseCoordinateRaw(attr[i + 1]);
+ } else if (strcmp(attr[i], "fx") == 0) {
+ grad->radial.fx = nsvg__parseCoordinateRaw(attr[i + 1]);
+ } else if (strcmp(attr[i], "fy") == 0) {
+ grad->radial.fy = nsvg__parseCoordinateRaw(attr[i + 1]);
+ } else if (strcmp(attr[i], "x1") == 0) {
+ grad->linear.x1 = nsvg__parseCoordinateRaw(attr[i + 1]);
+ } else if (strcmp(attr[i], "y1") == 0) {
+ grad->linear.y1 = nsvg__parseCoordinateRaw(attr[i + 1]);
+ } else if (strcmp(attr[i], "x2") == 0) {
+ grad->linear.x2 = nsvg__parseCoordinateRaw(attr[i + 1]);
+ } else if (strcmp(attr[i], "y2") == 0) {
+ grad->linear.y2 = nsvg__parseCoordinateRaw(attr[i + 1]);
+ } else if (strcmp(attr[i], "spreadMethod") == 0) {
+ if (strcmp(attr[i+1], "pad") == 0)
+ grad->spread = NSVG_SPREAD_PAD;
+ else if (strcmp(attr[i+1], "reflect") == 0)
+ grad->spread = NSVG_SPREAD_REFLECT;
+ else if (strcmp(attr[i+1], "repeat") == 0)
+ grad->spread = NSVG_SPREAD_REPEAT;
+ } else if (strcmp(attr[i], "xlink:href") == 0) {
+ const char *href = attr[i+1];
+ strncpy(grad->ref, href+1, 62);
+ grad->ref[62] = '\0';
+ }
+ }
+ }
+
+ grad->next = p->gradients;
+ p->gradients = grad;
+}
+
+static void nsvg__parseGradientStop(NSVGparser* p, const char** attr)
+{
+ NSVGattrib* curAttr = nsvg__getAttr(p);
+ NSVGgradientData* grad;
+ NSVGgradientStop* stop;
+ int i, idx;
+
+ curAttr->stopOffset = 0;
+ curAttr->stopColor = 0;
+ curAttr->stopOpacity = 1.0f;
+
+ for (i = 0; attr[i]; i += 2) {
+ nsvg__parseAttr(p, attr[i], attr[i + 1]);
+ }
+
+ // Add stop to the last gradient.
+ grad = p->gradients;
+ if (grad == NULL) return;
+
+ grad->nstops++;
+ grad->stops = (NSVGgradientStop*)realloc(grad->stops, sizeof(NSVGgradientStop)*grad->nstops);
+ if (grad->stops == NULL) return;
+
+ // Insert
+ idx = grad->nstops-1;
+ for (i = 0; i < grad->nstops-1; i++) {
+ if (curAttr->stopOffset < grad->stops[i].offset) {
+ idx = i;
+ break;
+ }
+ }
+ if (idx != grad->nstops-1) {
+ for (i = grad->nstops-1; i > idx; i--)
+ grad->stops[i] = grad->stops[i-1];
+ }
+
+ stop = &grad->stops[idx];
+ stop->color = curAttr->stopColor;
+ stop->color |= (unsigned int)(curAttr->stopOpacity*255) << 24;
+ stop->offset = curAttr->stopOffset;
+}
+
+static void nsvg__startElement(void* ud, const char* el, const char** attr)
+{
+ NSVGparser* p = (NSVGparser*)ud;
+
+ if (p->defsFlag) {
+ // Skip everything but gradients in defs
+ if (strcmp(el, "linearGradient") == 0) {
+ nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT);
+ } else if (strcmp(el, "radialGradient") == 0) {
+ nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT);
+ } else if (strcmp(el, "stop") == 0) {
+ nsvg__parseGradientStop(p, attr);
+ }
+ return;
+ }
+
+ if (strcmp(el, "g") == 0) {
+ nsvg__pushAttr(p);
+ nsvg__parseAttribs(p, attr);
+ } else if (strcmp(el, "path") == 0) {
+ if (p->pathFlag) // Do not allow nested paths.
+ return;
+ nsvg__pushAttr(p);
+ nsvg__parsePath(p, attr);
+ nsvg__popAttr(p);
+ } else if (strcmp(el, "rect") == 0) {
+ nsvg__pushAttr(p);
+ nsvg__parseRect(p, attr);
+ nsvg__popAttr(p);
+ } else if (strcmp(el, "circle") == 0) {
+ nsvg__pushAttr(p);
+ nsvg__parseCircle(p, attr);
+ nsvg__popAttr(p);
+ } else if (strcmp(el, "ellipse") == 0) {
+ nsvg__pushAttr(p);
+ nsvg__parseEllipse(p, attr);
+ nsvg__popAttr(p);
+ } else if (strcmp(el, "line") == 0) {
+ nsvg__pushAttr(p);
+ nsvg__parseLine(p, attr);
+ nsvg__popAttr(p);
+ } else if (strcmp(el, "polyline") == 0) {
+ nsvg__pushAttr(p);
+ nsvg__parsePoly(p, attr, 0);
+ nsvg__popAttr(p);
+ } else if (strcmp(el, "polygon") == 0) {
+ nsvg__pushAttr(p);
+ nsvg__parsePoly(p, attr, 1);
+ nsvg__popAttr(p);
+ } else if (strcmp(el, "linearGradient") == 0) {
+ nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT);
+ } else if (strcmp(el, "radialGradient") == 0) {
+ nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT);
+ } else if (strcmp(el, "stop") == 0) {
+ nsvg__parseGradientStop(p, attr);
+ } else if (strcmp(el, "defs") == 0) {
+ p->defsFlag = 1;
+ } else if (strcmp(el, "svg") == 0) {
+ nsvg__parseSVG(p, attr);
+ }
+}
+
+static void nsvg__endElement(void* ud, const char* el)
+{
+ NSVGparser* p = (NSVGparser*)ud;
+
+ if (strcmp(el, "g") == 0) {
+ nsvg__popAttr(p);
+ } else if (strcmp(el, "path") == 0) {
+ p->pathFlag = 0;
+ } else if (strcmp(el, "defs") == 0) {
+ p->defsFlag = 0;
+ }
+}
+
+static void nsvg__content(void* ud, const char* s)
+{
+ NSVG_NOTUSED(ud);
+ NSVG_NOTUSED(s);
+ // empty
+}
+
+static void nsvg__imageBounds(NSVGparser* p, float* bounds)
+{
+ NSVGshape* shape;
+ shape = p->image->shapes;
+ if (shape == NULL) {
+ bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0;
+ return;
+ }
+ bounds[0] = shape->bounds[0];
+ bounds[1] = shape->bounds[1];
+ bounds[2] = shape->bounds[2];
+ bounds[3] = shape->bounds[3];
+ for (shape = shape->next; shape != NULL; shape = shape->next) {
+ bounds[0] = nsvg__minf(bounds[0], shape->bounds[0]);
+ bounds[1] = nsvg__minf(bounds[1], shape->bounds[1]);
+ bounds[2] = nsvg__maxf(bounds[2], shape->bounds[2]);
+ bounds[3] = nsvg__maxf(bounds[3], shape->bounds[3]);
+ }
+}
+
+static float nsvg__viewAlign(float content, float container, int type)
+{
+ if (type == NSVG_ALIGN_MIN)
+ return 0;
+ else if (type == NSVG_ALIGN_MAX)
+ return container - content;
+ // mid
+ return (container - content) * 0.5f;
+}
+
+static void nsvg__scaleGradient(NSVGgradient* grad, float tx, float ty, float sx, float sy)
+{
+ grad->xform[0] *= sx;
+ grad->xform[1] *= sx;
+ grad->xform[2] *= sy;
+ grad->xform[3] *= sy;
+ grad->xform[4] += tx*sx;
+ grad->xform[5] += ty*sx;
+}
+
+static void nsvg__scaleToViewbox(NSVGparser* p, const char* units)
+{
+ NSVGshape* shape;
+ NSVGpath* path;
+ float tx, ty, sx, sy, us, bounds[4], t[6], avgs;
+ int i;
+ float* pt;
+
+ // Guess image size if not set completely.
+ nsvg__imageBounds(p, bounds);
+
+ if (p->viewWidth == 0) {
+ if (p->image->width > 0) {
+ p->viewWidth = p->image->width;
+ } else {
+ p->viewMinx = bounds[0];
+ p->viewWidth = bounds[2] - bounds[0];
+ }
+ }
+ if (p->viewHeight == 0) {
+ if (p->image->height > 0) {
+ p->viewHeight = p->image->height;
+ } else {
+ p->viewMiny = bounds[1];
+ p->viewHeight = bounds[3] - bounds[1];
+ }
+ }
+ if (p->image->width <= 1)
+ p->image->width = p->viewWidth;
+ if (p->image->height <= 1)
+ p->image->height = p->viewHeight;
+
+ tx = -p->viewMinx;
+ ty = -p->viewMiny;
+ sx = p->viewWidth > 0 ? p->image->width / p->viewWidth : 0;
+ sy = p->viewHeight > 0 ? p->image->height / p->viewHeight : 0;
+ // Unit scaling
+ us = 1.0f / nsvg__convertToPixels(p, nsvg__coord(1.0f, nsvg__parseUnits(units)), 0.0f, 1.0f);
+
+ // Fix aspect ratio
+ if (p->alignType == NSVG_ALIGN_MEET) {
+ // fit whole image into viewbox
+ sx = sy = nsvg__minf(sx, sy);
+ tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx;
+ ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy;
+ } else if (p->alignType == NSVG_ALIGN_SLICE) {
+ // fill whole viewbox with image
+ sx = sy = nsvg__maxf(sx, sy);
+ tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx;
+ ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy;
+ }
+
+ // Transform
+ sx *= us;
+ sy *= us;
+ avgs = (sx+sy) / 2.0f;
+ for (shape = p->image->shapes; shape != NULL; shape = shape->next) {
+ shape->bounds[0] = (shape->bounds[0] + tx) * sx;
+ shape->bounds[1] = (shape->bounds[1] + ty) * sy;
+ shape->bounds[2] = (shape->bounds[2] + tx) * sx;
+ shape->bounds[3] = (shape->bounds[3] + ty) * sy;
+ for (path = shape->paths; path != NULL; path = path->next) {
+ path->bounds[0] = (path->bounds[0] + tx) * sx;
+ path->bounds[1] = (path->bounds[1] + ty) * sy;
+ path->bounds[2] = (path->bounds[2] + tx) * sx;
+ path->bounds[3] = (path->bounds[3] + ty) * sy;
+ for (i =0; i < path->npts; i++) {
+ pt = &path->pts[i*2];
+ pt[0] = (pt[0] + tx) * sx;
+ pt[1] = (pt[1] + ty) * sy;
+ }
+ }
+
+ if (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT || shape->fill.type == NSVG_PAINT_RADIAL_GRADIENT) {
+ nsvg__scaleGradient(shape->fill.gradient, tx,ty, sx,sy);
+ memcpy(t, shape->fill.gradient->xform, sizeof(float)*6);
+ nsvg__xformInverse(shape->fill.gradient->xform, t);
+ }
+ if (shape->stroke.type == NSVG_PAINT_LINEAR_GRADIENT || shape->stroke.type == NSVG_PAINT_RADIAL_GRADIENT) {
+ nsvg__scaleGradient(shape->stroke.gradient, tx,ty, sx,sy);
+ memcpy(t, shape->stroke.gradient->xform, sizeof(float)*6);
+ nsvg__xformInverse(shape->stroke.gradient->xform, t);
+ }
+
+ shape->strokeWidth *= avgs;
+ shape->strokeDashOffset *= avgs;
+ for (i = 0; i < shape->strokeDashCount; i++)
+ shape->strokeDashArray[i] *= avgs;
+ }
+}
+
+NSVGimage* nsvgParse(char* input, const char* units, float dpi)
+{
+ NSVGparser* p;
+ NSVGimage* ret = 0;
+
+ p = nsvg__createParser();
+ if (p == NULL) {
+ return NULL;
+ }
+ p->dpi = dpi;
+
+ nsvg__parseXML(input, nsvg__startElement, nsvg__endElement, nsvg__content, p);
+
+ // Scale to viewBox
+ nsvg__scaleToViewbox(p, units);
+
+ ret = p->image;
+ p->image = NULL;
+
+ nsvg__deleteParser(p);
+
+ return ret;
+}
+
+NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi)
+{
+ FILE* fp = NULL;
+ size_t size;
+ char* data = NULL;
+ NSVGimage* image = NULL;
+
+ fp = fopen(filename, "rb");
+ if (!fp) goto error;
+ fseek(fp, 0, SEEK_END);
+ size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ data = (char*)malloc(size+1);
+ if (data == NULL) goto error;
+ if (fread(data, 1, size, fp) != size) goto error;
+ data[size] = '\0'; // Must be null terminated.
+ fclose(fp);
+ image = nsvgParse(data, units, dpi);
+ free(data);
+
+ return image;
+
+error:
+ if (fp) fclose(fp);
+ if (data) free(data);
+ if (image) nsvgDelete(image);
+ return NULL;
+}
+
+void nsvgDelete(NSVGimage* image)
+{
+ if (image == NULL) return;
+
+ NSVGshape *snext, *shape;
+ shape = image->shapes;
+ while (shape != NULL) {
+ snext = shape->next;
+ nsvg__deletePaths(shape->paths);
+ nsvg__deletePaint(&shape->fill);
+ nsvg__deletePaint(&shape->stroke);
+ free(shape);
+ shape = snext;
+ }
+ free(image);
+}
--- /dev/null
+/*
+ * Copyright (c) 2013-14 Mikko Mononen memon@inside.org
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * The SVG parser is based on Anti-Grain Geometry 2.4 SVG example
+ * Copyright (C) 2002-2004 Maxim Shemanarev (McSeem) (http://www.antigrain.com/)
+ *
+ * Arc calculation code based on canvg (https://code.google.com/p/canvg/)
+ *
+ * Bounding box calculation based on http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
+ *
+ */
+
+#ifndef NANOSVG_H
+#define NANOSVG_H
+
+// NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of cubic bezier shapes.
+//
+// The library suits well for anything from rendering scalable icons in your editor application to prototyping a game.
+//
+// NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create a pull request!
+//
+// The shapes in the SVG images are transformed by the viewBox and converted to specified units.
+// That is, you should get the same looking data as your designed in your favorite app.
+//
+// NanoSVG can return the paths in few different units. For example if you want to render an image, you may choose
+// to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters.
+//
+// The units passed to NanoVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'.
+// DPI (dots-per-inch) controls how the unit conversion is done.
+//
+// If you don't know or care about the units stuff, "px" and 96 should get you going.
+
+
+/* Example Usage:
+ // Load
+ SNVGImage* image;
+ image = nsvgParseFromFile("test.svg", "px", 96);
+ printf("size: %f x %f\n", image->width, image->height);
+ // Use...
+ for (shape = image->shapes; shape != NULL; shape = shape->next) {
+ for (path = shape->paths; path != NULL; path = path->next) {
+ for (i = 0; i < path->npts-1; i += 3) {
+ float* p = &path->pts[i*2];
+ drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]);
+ }
+ }
+ }
+ // Delete
+ nsvgDelete(image);
+*/
+
+enum NSVGpaintType {
+ NSVG_PAINT_NONE = 0,
+ NSVG_PAINT_COLOR = 1,
+ NSVG_PAINT_LINEAR_GRADIENT = 2,
+ NSVG_PAINT_RADIAL_GRADIENT = 3,
+};
+
+enum NSVGspreadType {
+ NSVG_SPREAD_PAD = 0,
+ NSVG_SPREAD_REFLECT = 1,
+ NSVG_SPREAD_REPEAT = 2,
+};
+
+enum NSVGlineJoin {
+ NSVG_JOIN_MITER = 0,
+ NSVG_JOIN_ROUND = 1,
+ NSVG_JOIN_BEVEL = 2,
+};
+
+enum NSVGlineCap {
+ NSVG_CAP_BUTT = 0,
+ NSVG_CAP_ROUND = 1,
+ NSVG_CAP_SQUARE = 2,
+};
+
+enum NSVGfillRule {
+ NSVG_FILLRULE_NONZERO = 0,
+ NSVG_FILLRULE_EVENODD = 1,
+};
+
+enum NSVGflags {
+ NSVG_FLAGS_VISIBLE = 0x01
+};
+
+typedef struct NSVGgradientStop {
+ unsigned int color;
+ float offset;
+} NSVGgradientStop;
+
+typedef struct NSVGgradient {
+ float xform[6];
+ char spread;
+ float fx, fy;
+ int nstops;
+ NSVGgradientStop stops[1];
+} NSVGgradient;
+
+typedef struct NSVGpaint {
+ char type;
+ union {
+ unsigned int color;
+ NSVGgradient* gradient;
+ };
+} NSVGpaint;
+
+typedef struct NSVGpath
+{
+ float* pts; // Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ...
+ int npts; // Total number of bezier points.
+ char closed; // Flag indicating if shapes should be treated as closed.
+ float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy].
+ struct NSVGpath* next; // Pointer to next path, or NULL if last element.
+} NSVGpath;
+
+typedef struct NSVGshape
+{
+ char id[64]; // Optional 'id' attr of the shape or its group
+ NSVGpaint fill; // Fill paint
+ NSVGpaint stroke; // Stroke paint
+ float opacity; // Opacity of the shape.
+ float strokeWidth; // Stroke width (scaled).
+ float strokeDashOffset; // Stroke dash offset (scaled).
+ float strokeDashArray[8]; // Stroke dash array (scaled).
+ char strokeDashCount; // Number of dash values in dash array.
+ char strokeLineJoin; // Stroke join type.
+ char strokeLineCap; // Stroke cap type.
+ char fillRule; // Fill rule, see NSVGfillRule.
+ unsigned char flags; // Logical or of NSVG_FLAGS_* flags
+ float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy].
+ NSVGpath* paths; // Linked list of paths in the image.
+ struct NSVGshape* next; // Pointer to next shape, or NULL if last element.
+} NSVGshape;
+
+typedef struct NSVGimage
+{
+ float width; // Width of the image.
+ float height; // Height of the image.
+ NSVGshape* shapes; // Linked list of shapes in the image.
+} NSVGimage;
+
+// Parses SVG file from a file, returns SVG image as paths.
+NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi);
+
+// Parses SVG file from a null terminated string, returns SVG image as paths.
+// Important note: changes the string.
+NSVGimage* nsvgParse(char* input, const char* units, float dpi);
+
+// Deletes list of paths.
+void nsvgDelete(NSVGimage* image);
+
+#endif // NANOSVG_H
+
+/**
+ * In the original software, The nanosvg implementation was followed here.
+ * We have moved the implementation to nanosvg.cc.
+ */
--- /dev/null
+/*
+ * Copyright (c) 2013-14 Mikko Mononen memon@inside.org
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * The polygon rasterization is heavily based on stb_truetype rasterizer
+ * by Sean Barrett - http://nothings.org/
+ *
+ */
+
+#include "nanosvgrast.h"
+
+/**
+ * In the original software, The nanosvgrast implementation was included in the header file.
+ * We have separated the implementation to source file here.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+#define NSVG__SUBSAMPLES 5
+#define NSVG__FIXSHIFT 10
+#define NSVG__FIX (1 << NSVG__FIXSHIFT)
+#define NSVG__FIXMASK (NSVG__FIX-1)
+#define NSVG__MEMPAGE_SIZE 1024
+
+typedef struct NSVGedge {
+ float x0,y0, x1,y1;
+ int dir;
+ struct NSVGedge* next;
+} NSVGedge;
+
+typedef struct NSVGpoint {
+ float x, y;
+ float dx, dy;
+ float len;
+ float dmx, dmy;
+ unsigned char flags;
+} NSVGpoint;
+
+typedef struct NSVGactiveEdge {
+ int x,dx;
+ float ey;
+ int dir;
+ struct NSVGactiveEdge *next;
+} NSVGactiveEdge;
+
+typedef struct NSVGmemPage {
+ unsigned char mem[NSVG__MEMPAGE_SIZE];
+ int size;
+ struct NSVGmemPage* next;
+} NSVGmemPage;
+
+typedef struct NSVGcachedPaint {
+ char type;
+ char spread;
+ float xform[6];
+ unsigned int colors[256];
+} NSVGcachedPaint;
+
+struct NSVGrasterizer
+{
+ float px, py;
+
+ float tessTol;
+ float distTol;
+
+ NSVGedge* edges;
+ int nedges;
+ int cedges;
+
+ NSVGpoint* points;
+ int npoints;
+ int cpoints;
+
+ NSVGpoint* points2;
+ int npoints2;
+ int cpoints2;
+
+ NSVGactiveEdge* freelist;
+ NSVGmemPage* pages;
+ NSVGmemPage* curpage;
+
+ unsigned char* scanline;
+ int cscanline;
+
+ unsigned char* bitmap;
+ int width, height, stride;
+};
+
+NSVGrasterizer* nsvgCreateRasterizer()
+{
+ NSVGrasterizer* r = (NSVGrasterizer*)malloc(sizeof(NSVGrasterizer));
+ if (r == NULL) goto error;
+ memset(r, 0, sizeof(NSVGrasterizer));
+
+ r->tessTol = 0.25f;
+ r->distTol = 0.01f;
+
+ return r;
+
+error:
+ nsvgDeleteRasterizer(r);
+ return NULL;
+}
+
+void nsvgDeleteRasterizer(NSVGrasterizer* r)
+{
+ if (r == NULL) return;
+
+ NSVGmemPage* p;
+ p = r->pages;
+ while (p != NULL) {
+ NSVGmemPage* next = p->next;
+ free(p);
+ p = next;
+ }
+
+ if (r->edges) free(r->edges);
+ if (r->points) free(r->points);
+ if (r->points2) free(r->points2);
+ if (r->scanline) free(r->scanline);
+
+ free(r);
+}
+
+static NSVGmemPage* nsvg__nextPage(NSVGrasterizer* r, NSVGmemPage* cur)
+{
+ NSVGmemPage *newp;
+
+ // If using existing chain, return the next page in chain
+ if (cur != NULL && cur->next != NULL) {
+ return cur->next;
+ }
+
+ // Alloc new page
+ newp = (NSVGmemPage*)malloc(sizeof(NSVGmemPage));
+ if (newp == NULL) return NULL;
+ memset(newp, 0, sizeof(NSVGmemPage));
+
+ // Add to linked list
+ if (cur != NULL)
+ cur->next = newp;
+ else
+ r->pages = newp;
+
+ return newp;
+}
+
+static void nsvg__resetPool(NSVGrasterizer* r)
+{
+ NSVGmemPage* p = r->pages;
+ while (p != NULL) {
+ p->size = 0;
+ p = p->next;
+ }
+ r->curpage = r->pages;
+}
+
+static unsigned char* nsvg__alloc(NSVGrasterizer* r, int size)
+{
+ unsigned char* buf;
+ if (size > NSVG__MEMPAGE_SIZE) return NULL;
+ if (r->curpage == NULL || r->curpage->size+size > NSVG__MEMPAGE_SIZE) {
+ r->curpage = nsvg__nextPage(r, r->curpage);
+ }
+ buf = &r->curpage->mem[r->curpage->size];
+ r->curpage->size += size;
+ return buf;
+}
+
+static int nsvg__ptEquals(float x1, float y1, float x2, float y2, float tol)
+{
+ float dx = x2 - x1;
+ float dy = y2 - y1;
+ return dx*dx + dy*dy < tol*tol;
+}
+
+static void nsvg__addPathPoint(NSVGrasterizer* r, float x, float y, int flags)
+{
+ NSVGpoint* pt;
+
+ if (r->npoints > 0) {
+ pt = &r->points[r->npoints-1];
+ if (nsvg__ptEquals(pt->x,pt->y, x,y, r->distTol)) {
+ pt->flags |= flags;
+ return;
+ }
+ }
+
+ if (r->npoints+1 > r->cpoints) {
+ r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64;
+ r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints);
+ if (r->points == NULL) return;
+ }
+
+ pt = &r->points[r->npoints];
+ pt->x = x;
+ pt->y = y;
+ pt->flags = (unsigned char)flags;
+ r->npoints++;
+}
+
+static void nsvg__appendPathPoint(NSVGrasterizer* r, NSVGpoint pt)
+{
+ if (r->npoints+1 > r->cpoints) {
+ r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64;
+ r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints);
+ if (r->points == NULL) return;
+ }
+ r->points[r->npoints] = pt;
+ r->npoints++;
+}
+
+static void nsvg__duplicatePoints(NSVGrasterizer* r)
+{
+ if (r->npoints > r->cpoints2) {
+ r->cpoints2 = r->npoints;
+ r->points2 = (NSVGpoint*)realloc(r->points2, sizeof(NSVGpoint) * r->cpoints2);
+ if (r->points2 == NULL) return;
+ }
+
+ memcpy(r->points2, r->points, sizeof(NSVGpoint) * r->npoints);
+ r->npoints2 = r->npoints;
+}
+
+static void nsvg__addEdge(NSVGrasterizer* r, float x0, float y0, float x1, float y1)
+{
+ NSVGedge* e;
+
+ // Skip horizontal edges
+ if (y0 == y1)
+ return;
+
+ if (r->nedges+1 > r->cedges) {
+ r->cedges = r->cedges > 0 ? r->cedges * 2 : 64;
+ r->edges = (NSVGedge*)realloc(r->edges, sizeof(NSVGedge) * r->cedges);
+ if (r->edges == NULL) return;
+ }
+
+ e = &r->edges[r->nedges];
+ r->nedges++;
+
+ if (y0 < y1) {
+ e->x0 = x0;
+ e->y0 = y0;
+ e->x1 = x1;
+ e->y1 = y1;
+ e->dir = 1;
+ } else {
+ e->x0 = x1;
+ e->y0 = y1;
+ e->x1 = x0;
+ e->y1 = y0;
+ e->dir = -1;
+ }
+}
+
+static float nsvg__normalize(float *x, float* y)
+{
+ float d = sqrtf((*x)*(*x) + (*y)*(*y));
+ if (d > 1e-6f) {
+ float id = 1.0f / d;
+ *x *= id;
+ *y *= id;
+ }
+ return d;
+}
+
+static float nsvg__absf(float x) { return x < 0 ? -x : x; }
+
+static void nsvg__flattenCubicBez(NSVGrasterizer* r,
+ float x1, float y1, float x2, float y2,
+ float x3, float y3, float x4, float y4,
+ int level, int type)
+{
+ float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234;
+ float dx,dy,d2,d3;
+
+ if (level > 10) return;
+
+ x12 = (x1+x2)*0.5f;
+ y12 = (y1+y2)*0.5f;
+ x23 = (x2+x3)*0.5f;
+ y23 = (y2+y3)*0.5f;
+ x34 = (x3+x4)*0.5f;
+ y34 = (y3+y4)*0.5f;
+ x123 = (x12+x23)*0.5f;
+ y123 = (y12+y23)*0.5f;
+
+ dx = x4 - x1;
+ dy = y4 - y1;
+ d2 = nsvg__absf(((x2 - x4) * dy - (y2 - y4) * dx));
+ d3 = nsvg__absf(((x3 - x4) * dy - (y3 - y4) * dx));
+
+ if ((d2 + d3)*(d2 + d3) < r->tessTol * (dx*dx + dy*dy)) {
+ nsvg__addPathPoint(r, x4, y4, type);
+ return;
+ }
+
+ x234 = (x23+x34)*0.5f;
+ y234 = (y23+y34)*0.5f;
+ x1234 = (x123+x234)*0.5f;
+ y1234 = (y123+y234)*0.5f;
+
+ nsvg__flattenCubicBez(r, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0);
+ nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type);
+}
+
+static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float scale)
+{
+ int i, j;
+ NSVGpath* path;
+
+ for (path = shape->paths; path != NULL; path = path->next) {
+ r->npoints = 0;
+ // Flatten path
+ nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0);
+ for (i = 0; i < path->npts-1; i += 3) {
+ float* p = &path->pts[i*2];
+ nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, 0);
+ }
+ // Close path
+ nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0);
+ // Build edges
+ for (i = 0, j = r->npoints-1; i < r->npoints; j = i++)
+ nsvg__addEdge(r, r->points[j].x, r->points[j].y, r->points[i].x, r->points[i].y);
+ }
+}
+
+enum NSVGpointFlags
+{
+ NSVG_PT_CORNER = 0x01,
+ NSVG_PT_BEVEL = 0x02,
+ NSVG_PT_LEFT = 0x04,
+};
+
+static void nsvg__initClosed(NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth)
+{
+ float w = lineWidth * 0.5f;
+ float dx = p1->x - p0->x;
+ float dy = p1->y - p0->y;
+ float len = nsvg__normalize(&dx, &dy);
+ float px = p0->x + dx*len*0.5f, py = p0->y + dy*len*0.5f;
+ float dlx = dy, dly = -dx;
+ float lx = px - dlx*w, ly = py - dly*w;
+ float rx = px + dlx*w, ry = py + dly*w;
+ left->x = lx; left->y = ly;
+ right->x = rx; right->y = ry;
+}
+
+static void nsvg__buttCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect)
+{
+ float w = lineWidth * 0.5f;
+ float px = p->x, py = p->y;
+ float dlx = dy, dly = -dx;
+ float lx = px - dlx*w, ly = py - dly*w;
+ float rx = px + dlx*w, ry = py + dly*w;
+
+ nsvg__addEdge(r, lx, ly, rx, ry);
+
+ if (connect) {
+ nsvg__addEdge(r, left->x, left->y, lx, ly);
+ nsvg__addEdge(r, rx, ry, right->x, right->y);
+ }
+ left->x = lx; left->y = ly;
+ right->x = rx; right->y = ry;
+}
+
+static void nsvg__squareCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect)
+{
+ float w = lineWidth * 0.5f;
+ float px = p->x - dx*w, py = p->y - dy*w;
+ float dlx = dy, dly = -dx;
+ float lx = px - dlx*w, ly = py - dly*w;
+ float rx = px + dlx*w, ry = py + dly*w;
+
+ nsvg__addEdge(r, lx, ly, rx, ry);
+
+ if (connect) {
+ nsvg__addEdge(r, left->x, left->y, lx, ly);
+ nsvg__addEdge(r, rx, ry, right->x, right->y);
+ }
+ left->x = lx; left->y = ly;
+ right->x = rx; right->y = ry;
+}
+
+#ifndef NSVG_PI
+#define NSVG_PI (3.14159265358979323846264338327f)
+#endif
+
+static void nsvg__roundCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int ncap, int connect)
+{
+ int i;
+ float w = lineWidth * 0.5f;
+ float px = p->x, py = p->y;
+ float dlx = dy, dly = -dx;
+ float lx = 0, ly = 0, rx = 0, ry = 0, prevx = 0, prevy = 0;
+
+ for (i = 0; i < ncap; i++) {
+ float a = i/(float)(ncap-1)*NSVG_PI;
+ float ax = cosf(a) * w, ay = sinf(a) * w;
+ float x = px - dlx*ax - dx*ay;
+ float y = py - dly*ax - dy*ay;
+
+ if (i > 0)
+ nsvg__addEdge(r, prevx, prevy, x, y);
+
+ prevx = x;
+ prevy = y;
+
+ if (i == 0) {
+ lx = x; ly = y;
+ } else if (i == ncap-1) {
+ rx = x; ry = y;
+ }
+ }
+
+ if (connect) {
+ nsvg__addEdge(r, left->x, left->y, lx, ly);
+ nsvg__addEdge(r, rx, ry, right->x, right->y);
+ }
+
+ left->x = lx; left->y = ly;
+ right->x = rx; right->y = ry;
+}
+
+static void nsvg__bevelJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth)
+{
+ float w = lineWidth * 0.5f;
+ float dlx0 = p0->dy, dly0 = -p0->dx;
+ float dlx1 = p1->dy, dly1 = -p1->dx;
+ float lx0 = p1->x - (dlx0 * w), ly0 = p1->y - (dly0 * w);
+ float rx0 = p1->x + (dlx0 * w), ry0 = p1->y + (dly0 * w);
+ float lx1 = p1->x - (dlx1 * w), ly1 = p1->y - (dly1 * w);
+ float rx1 = p1->x + (dlx1 * w), ry1 = p1->y + (dly1 * w);
+
+ nsvg__addEdge(r, lx0, ly0, left->x, left->y);
+ nsvg__addEdge(r, lx1, ly1, lx0, ly0);
+
+ nsvg__addEdge(r, right->x, right->y, rx0, ry0);
+ nsvg__addEdge(r, rx0, ry0, rx1, ry1);
+
+ left->x = lx1; left->y = ly1;
+ right->x = rx1; right->y = ry1;
+}
+
+static void nsvg__miterJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth)
+{
+ float w = lineWidth * 0.5f;
+ float dlx0 = p0->dy, dly0 = -p0->dx;
+ float dlx1 = p1->dy, dly1 = -p1->dx;
+ float lx0, rx0, lx1, rx1;
+ float ly0, ry0, ly1, ry1;
+
+ if (p1->flags & NSVG_PT_LEFT) {
+ lx0 = lx1 = p1->x - p1->dmx * w;
+ ly0 = ly1 = p1->y - p1->dmy * w;
+ nsvg__addEdge(r, lx1, ly1, left->x, left->y);
+
+ rx0 = p1->x + (dlx0 * w);
+ ry0 = p1->y + (dly0 * w);
+ rx1 = p1->x + (dlx1 * w);
+ ry1 = p1->y + (dly1 * w);
+ nsvg__addEdge(r, right->x, right->y, rx0, ry0);
+ nsvg__addEdge(r, rx0, ry0, rx1, ry1);
+ } else {
+ lx0 = p1->x - (dlx0 * w);
+ ly0 = p1->y - (dly0 * w);
+ lx1 = p1->x - (dlx1 * w);
+ ly1 = p1->y - (dly1 * w);
+ nsvg__addEdge(r, lx0, ly0, left->x, left->y);
+ nsvg__addEdge(r, lx1, ly1, lx0, ly0);
+
+ rx0 = rx1 = p1->x + p1->dmx * w;
+ ry0 = ry1 = p1->y + p1->dmy * w;
+ nsvg__addEdge(r, right->x, right->y, rx1, ry1);
+ }
+
+ left->x = lx1; left->y = ly1;
+ right->x = rx1; right->y = ry1;
+}
+
+static void nsvg__roundJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth, int ncap)
+{
+ int i, n;
+ float w = lineWidth * 0.5f;
+ float dlx0 = p0->dy, dly0 = -p0->dx;
+ float dlx1 = p1->dy, dly1 = -p1->dx;
+ float a0 = atan2f(dly0, dlx0);
+ float a1 = atan2f(dly1, dlx1);
+ float da = a1 - a0;
+ float lx, ly, rx, ry;
+
+ if (da < NSVG_PI) da += NSVG_PI*2;
+ if (da > NSVG_PI) da -= NSVG_PI*2;
+
+ n = (int)ceilf((nsvg__absf(da) / NSVG_PI) * ncap);
+ if (n < 2) n = 2;
+ if (n > ncap) n = ncap;
+
+ lx = left->x;
+ ly = left->y;
+ rx = right->x;
+ ry = right->y;
+
+ for (i = 0; i < n; i++) {
+ float u = i/(float)(n-1);
+ float a = a0 + u*da;
+ float ax = cosf(a) * w, ay = sinf(a) * w;
+ float lx1 = p1->x - ax, ly1 = p1->y - ay;
+ float rx1 = p1->x + ax, ry1 = p1->y + ay;
+
+ nsvg__addEdge(r, lx1, ly1, lx, ly);
+ nsvg__addEdge(r, rx, ry, rx1, ry1);
+
+ lx = lx1; ly = ly1;
+ rx = rx1; ry = ry1;
+ }
+
+ left->x = lx; left->y = ly;
+ right->x = rx; right->y = ry;
+}
+
+static void nsvg__straightJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p1, float lineWidth)
+{
+ float w = lineWidth * 0.5f;
+ float lx = p1->x - (p1->dmx * w), ly = p1->y - (p1->dmy * w);
+ float rx = p1->x + (p1->dmx * w), ry = p1->y + (p1->dmy * w);
+
+ nsvg__addEdge(r, lx, ly, left->x, left->y);
+ nsvg__addEdge(r, right->x, right->y, rx, ry);
+
+ left->x = lx; left->y = ly;
+ right->x = rx; right->y = ry;
+}
+
+static int nsvg__curveDivs(float r, float arc, float tol)
+{
+ float da = acosf(r / (r + tol)) * 2.0f;
+ int divs = (int)ceilf(arc / da);
+ if (divs < 2) divs = 2;
+ return divs;
+}
+
+static void nsvg__expandStroke(NSVGrasterizer* r, NSVGpoint* points, int npoints, int closed, int lineJoin, int lineCap, float lineWidth)
+{
+ int ncap = nsvg__curveDivs(lineWidth*0.5f, NSVG_PI, r->tessTol); // Calculate divisions per half circle.
+ NSVGpoint left = {0,0,0,0,0,0,0,0}, right = {0,0,0,0,0,0,0,0}, firstLeft = {0,0,0,0,0,0,0,0}, firstRight = {0,0,0,0,0,0,0,0};
+ NSVGpoint* p0, *p1;
+ int j, s, e;
+
+ // Build stroke edges
+ if (closed) {
+ // Looping
+ p0 = &points[npoints-1];
+ p1 = &points[0];
+ s = 0;
+ e = npoints;
+ } else {
+ // Add cap
+ p0 = &points[0];
+ p1 = &points[1];
+ s = 1;
+ e = npoints-1;
+ }
+
+ if (closed) {
+ nsvg__initClosed(&left, &right, p0, p1, lineWidth);
+ firstLeft = left;
+ firstRight = right;
+ } else {
+ // Add cap
+ float dx = p1->x - p0->x;
+ float dy = p1->y - p0->y;
+ nsvg__normalize(&dx, &dy);
+ if (lineCap == NSVG_CAP_BUTT)
+ nsvg__buttCap(r, &left, &right, p0, dx, dy, lineWidth, 0);
+ else if (lineCap == NSVG_CAP_SQUARE)
+ nsvg__squareCap(r, &left, &right, p0, dx, dy, lineWidth, 0);
+ else if (lineCap == NSVG_CAP_ROUND)
+ nsvg__roundCap(r, &left, &right, p0, dx, dy, lineWidth, ncap, 0);
+ }
+
+ for (j = s; j < e; ++j) {
+ if (p1->flags & NSVG_PT_CORNER) {
+ if (lineJoin == NSVG_JOIN_ROUND)
+ nsvg__roundJoin(r, &left, &right, p0, p1, lineWidth, ncap);
+ else if (lineJoin == NSVG_JOIN_BEVEL || (p1->flags & NSVG_PT_BEVEL))
+ nsvg__bevelJoin(r, &left, &right, p0, p1, lineWidth);
+ else
+ nsvg__miterJoin(r, &left, &right, p0, p1, lineWidth);
+ } else {
+ nsvg__straightJoin(r, &left, &right, p1, lineWidth);
+ }
+ p0 = p1++;
+ }
+
+ if (closed) {
+ // Loop it
+ nsvg__addEdge(r, firstLeft.x, firstLeft.y, left.x, left.y);
+ nsvg__addEdge(r, right.x, right.y, firstRight.x, firstRight.y);
+ } else {
+ // Add cap
+ float dx = p1->x - p0->x;
+ float dy = p1->y - p0->y;
+ nsvg__normalize(&dx, &dy);
+ if (lineCap == NSVG_CAP_BUTT)
+ nsvg__buttCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1);
+ else if (lineCap == NSVG_CAP_SQUARE)
+ nsvg__squareCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1);
+ else if (lineCap == NSVG_CAP_ROUND)
+ nsvg__roundCap(r, &right, &left, p1, -dx, -dy, lineWidth, ncap, 1);
+ }
+}
+
+static void nsvg__prepareStroke(NSVGrasterizer* r, float miterLimit, int lineJoin)
+{
+ int i, j;
+ NSVGpoint* p0, *p1;
+
+ p0 = &r->points[r->npoints-1];
+ p1 = &r->points[0];
+ for (i = 0; i < r->npoints; i++) {
+ // Calculate segment direction and length
+ p0->dx = p1->x - p0->x;
+ p0->dy = p1->y - p0->y;
+ p0->len = nsvg__normalize(&p0->dx, &p0->dy);
+ // Advance
+ p0 = p1++;
+ }
+
+ // calculate joins
+ p0 = &r->points[r->npoints-1];
+ p1 = &r->points[0];
+ for (j = 0; j < r->npoints; j++) {
+ float dlx0, dly0, dlx1, dly1, dmr2, cross;
+ dlx0 = p0->dy;
+ dly0 = -p0->dx;
+ dlx1 = p1->dy;
+ dly1 = -p1->dx;
+ // Calculate extrusions
+ p1->dmx = (dlx0 + dlx1) * 0.5f;
+ p1->dmy = (dly0 + dly1) * 0.5f;
+ dmr2 = p1->dmx*p1->dmx + p1->dmy*p1->dmy;
+ if (dmr2 > 0.000001f) {
+ float s2 = 1.0f / dmr2;
+ if (s2 > 600.0f) {
+ s2 = 600.0f;
+ }
+ p1->dmx *= s2;
+ p1->dmy *= s2;
+ }
+
+ // Clear flags, but keep the corner.
+ p1->flags = (p1->flags & NSVG_PT_CORNER) ? NSVG_PT_CORNER : 0;
+
+ // Keep track of left turns.
+ cross = p1->dx * p0->dy - p0->dx * p1->dy;
+ if (cross > 0.0f)
+ p1->flags |= NSVG_PT_LEFT;
+
+ // Check to see if the corner needs to be beveled.
+ if (p1->flags & NSVG_PT_CORNER) {
+ if ((dmr2 * miterLimit*miterLimit) < 1.0f || lineJoin == NSVG_JOIN_BEVEL || lineJoin == NSVG_JOIN_ROUND) {
+ p1->flags |= NSVG_PT_BEVEL;
+ }
+ }
+
+ p0 = p1++;
+ }
+}
+
+static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float scale)
+{
+ int i, j, closed;
+ NSVGpath* path;
+ NSVGpoint* p0, *p1;
+ float miterLimit = 4;
+ int lineJoin = shape->strokeLineJoin;
+ int lineCap = shape->strokeLineCap;
+ float lineWidth = shape->strokeWidth * scale;
+
+ for (path = shape->paths; path != NULL; path = path->next) {
+ // Flatten path
+ r->npoints = 0;
+ nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, NSVG_PT_CORNER);
+ for (i = 0; i < path->npts-1; i += 3) {
+ float* p = &path->pts[i*2];
+ nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, NSVG_PT_CORNER);
+ }
+ if (r->npoints < 2)
+ continue;
+
+ closed = path->closed;
+
+ // If the first and last points are the same, remove the last, mark as closed path.
+ p0 = &r->points[r->npoints-1];
+ p1 = &r->points[0];
+ if (nsvg__ptEquals(p0->x,p0->y, p1->x,p1->y, r->distTol)) {
+ r->npoints--;
+ p0 = &r->points[r->npoints-1];
+ closed = 1;
+ }
+
+ if (shape->strokeDashCount > 0) {
+ int idash = 0, dashState = 1;
+ float totalDist = 0, dashLen, allDashLen, dashOffset;
+ NSVGpoint cur;
+
+ if (closed)
+ nsvg__appendPathPoint(r, r->points[0]);
+
+ // Duplicate points -> points2.
+ nsvg__duplicatePoints(r);
+
+ r->npoints = 0;
+ cur = r->points2[0];
+ nsvg__appendPathPoint(r, cur);
+
+ // Figure out dash offset.
+ allDashLen = 0;
+ for (j = 0; j < shape->strokeDashCount; j++)
+ allDashLen += shape->strokeDashArray[j];
+ if (shape->strokeDashCount & 1)
+ allDashLen *= 2.0f;
+ // Find location inside pattern
+ dashOffset = fmodf(shape->strokeDashOffset, allDashLen);
+ if (dashOffset < 0.0f)
+ dashOffset += allDashLen;
+
+ while (dashOffset > shape->strokeDashArray[idash]) {
+ dashOffset -= shape->strokeDashArray[idash];
+ idash = (idash + 1) % shape->strokeDashCount;
+ }
+ dashLen = (shape->strokeDashArray[idash] - dashOffset) * scale;
+
+ for (j = 1; j < r->npoints2; ) {
+ float dx = r->points2[j].x - cur.x;
+ float dy = r->points2[j].y - cur.y;
+ float dist = sqrtf(dx*dx + dy*dy);
+
+ if ((totalDist + dist) > dashLen) {
+ // Calculate intermediate point
+ float d = (dashLen - totalDist) / dist;
+ float x = cur.x + dx * d;
+ float y = cur.y + dy * d;
+ nsvg__addPathPoint(r, x, y, NSVG_PT_CORNER);
+
+ // Stroke
+ if (r->npoints > 1 && dashState) {
+ nsvg__prepareStroke(r, miterLimit, lineJoin);
+ nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth);
+ }
+ // Advance dash pattern
+ dashState = !dashState;
+ idash = (idash+1) % shape->strokeDashCount;
+ dashLen = shape->strokeDashArray[idash] * scale;
+ // Restart
+ cur.x = x;
+ cur.y = y;
+ cur.flags = NSVG_PT_CORNER;
+ totalDist = 0.0f;
+ r->npoints = 0;
+ nsvg__appendPathPoint(r, cur);
+ } else {
+ totalDist += dist;
+ cur = r->points2[j];
+ nsvg__appendPathPoint(r, cur);
+ j++;
+ }
+ }
+ // Stroke any leftover path
+ if (r->npoints > 1 && dashState)
+ nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth);
+ } else {
+ nsvg__prepareStroke(r, miterLimit, lineJoin);
+ nsvg__expandStroke(r, r->points, r->npoints, closed, lineJoin, lineCap, lineWidth);
+ }
+ }
+}
+
+static int nsvg__cmpEdge(const void *p, const void *q)
+{
+ NSVGedge* a = (NSVGedge*)p;
+ NSVGedge* b = (NSVGedge*)q;
+
+ if (a->y0 < b->y0) return -1;
+ if (a->y0 > b->y0) return 1;
+ return 0;
+}
+
+
+static NSVGactiveEdge* nsvg__addActive(NSVGrasterizer* r, NSVGedge* e, float startPoint)
+{
+ NSVGactiveEdge* z;
+
+ if (r->freelist != NULL) {
+ // Restore from freelist.
+ z = r->freelist;
+ r->freelist = z->next;
+ } else {
+ // Alloc new edge.
+ z = (NSVGactiveEdge*)nsvg__alloc(r, sizeof(NSVGactiveEdge));
+ if (z == NULL) return NULL;
+ }
+
+ float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
+// STBTT_assert(e->y0 <= start_point);
+ // round dx down to avoid going too far
+ if (dxdy < 0)
+ z->dx = (int)(-floorf(NSVG__FIX * -dxdy));
+ else
+ z->dx = (int)floorf(NSVG__FIX * dxdy);
+ z->x = (int)floorf(NSVG__FIX * (e->x0 + dxdy * (startPoint - e->y0)));
+// z->x -= off_x * FIX;
+ z->ey = e->y1;
+ z->next = 0;
+ z->dir = e->dir;
+
+ return z;
+}
+
+static void nsvg__freeActive(NSVGrasterizer* r, NSVGactiveEdge* z)
+{
+ z->next = r->freelist;
+ r->freelist = z;
+}
+
+static void nsvg__fillScanline(unsigned char* scanline, int len, int x0, int x1, int maxWeight, int* xmin, int* xmax)
+{
+ int i = x0 >> NSVG__FIXSHIFT;
+ int j = x1 >> NSVG__FIXSHIFT;
+ if (i < *xmin) *xmin = i;
+ if (j > *xmax) *xmax = j;
+ if (i < len && j >= 0) {
+ if (i == j) {
+ // x0,x1 are the same pixel, so compute combined coverage
+ scanline[i] += (unsigned char)((x1 - x0) * maxWeight >> NSVG__FIXSHIFT);
+ } else {
+ if (i >= 0) // add antialiasing for x0
+ scanline[i] += (unsigned char)(((NSVG__FIX - (x0 & NSVG__FIXMASK)) * maxWeight) >> NSVG__FIXSHIFT);
+ else
+ i = -1; // clip
+
+ if (j < len) // add antialiasing for x1
+ scanline[j] += (unsigned char)(((x1 & NSVG__FIXMASK) * maxWeight) >> NSVG__FIXSHIFT);
+ else
+ j = len; // clip
+
+ for (++i; i < j; ++i) // fill pixels between x0 and x1
+ scanline[i] += (unsigned char)maxWeight;
+ }
+ }
+}
+
+// note: this routine clips fills that extend off the edges... ideally this
+// wouldn't happen, but it could happen if the truetype glyph bounding boxes
+// are wrong, or if the user supplies a too-small bitmap
+static void nsvg__fillActiveEdges(unsigned char* scanline, int len, NSVGactiveEdge* e, int maxWeight, int* xmin, int* xmax, char fillRule)
+{
+ // non-zero winding fill
+ int x0 = 0, w = 0;
+
+ if (fillRule == NSVG_FILLRULE_NONZERO) {
+ // Non-zero
+ while (e != NULL) {
+ if (w == 0) {
+ // if we're currently at zero, we need to record the edge start point
+ x0 = e->x; w += e->dir;
+ } else {
+ int x1 = e->x; w += e->dir;
+ // if we went to zero, we need to draw
+ if (w == 0)
+ nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax);
+ }
+ e = e->next;
+ }
+ } else if (fillRule == NSVG_FILLRULE_EVENODD) {
+ // Even-odd
+ while (e != NULL) {
+ if (w == 0) {
+ // if we're currently at zero, we need to record the edge start point
+ x0 = e->x; w = 1;
+ } else {
+ int x1 = e->x; w = 0;
+ nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax);
+ }
+ e = e->next;
+ }
+ }
+}
+
+static float nsvg__clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); }
+
+static unsigned int nsvg__RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
+{
+ return (r) | (g << 8) | (b << 16) | (a << 24);
+}
+
+static unsigned int nsvg__lerpRGBA(unsigned int c0, unsigned int c1, float u)
+{
+ int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f);
+ int r = (((c0) & 0xff)*(256-iu) + (((c1) & 0xff)*iu)) >> 8;
+ int g = (((c0>>8) & 0xff)*(256-iu) + (((c1>>8) & 0xff)*iu)) >> 8;
+ int b = (((c0>>16) & 0xff)*(256-iu) + (((c1>>16) & 0xff)*iu)) >> 8;
+ int a = (((c0>>24) & 0xff)*(256-iu) + (((c1>>24) & 0xff)*iu)) >> 8;
+ return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a);
+}
+
+static unsigned int nsvg__applyOpacity(unsigned int c, float u)
+{
+ int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f);
+ int r = (c) & 0xff;
+ int g = (c>>8) & 0xff;
+ int b = (c>>16) & 0xff;
+ int a = (((c>>24) & 0xff)*iu) >> 8;
+ return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a);
+}
+
+static inline int nsvg__div255(int x)
+{
+ return ((x+1) * 257) >> 16;
+}
+
+static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* cover, int x, int y,
+ float tx, float ty, float scale, NSVGcachedPaint* cache)
+{
+
+ if (cache->type == NSVG_PAINT_COLOR) {
+ int i, cr, cg, cb, ca;
+ cr = cache->colors[0] & 0xff;
+ cg = (cache->colors[0] >> 8) & 0xff;
+ cb = (cache->colors[0] >> 16) & 0xff;
+ ca = (cache->colors[0] >> 24) & 0xff;
+
+ for (i = 0; i < count; i++) {
+ int r,g,b;
+ int a = nsvg__div255((int)cover[0] * ca);
+ int ia = 255 - a;
+ // Premultiply
+ r = nsvg__div255(cr * a);
+ g = nsvg__div255(cg * a);
+ b = nsvg__div255(cb * a);
+
+ // Blend over
+ r += nsvg__div255(ia * (int)dst[0]);
+ g += nsvg__div255(ia * (int)dst[1]);
+ b += nsvg__div255(ia * (int)dst[2]);
+ a += nsvg__div255(ia * (int)dst[3]);
+
+ dst[0] = (unsigned char)r;
+ dst[1] = (unsigned char)g;
+ dst[2] = (unsigned char)b;
+ dst[3] = (unsigned char)a;
+
+ cover++;
+ dst += 4;
+ }
+ } else if (cache->type == NSVG_PAINT_LINEAR_GRADIENT) {
+ // TODO: spread modes.
+ // TODO: plenty of opportunities to optimize.
+ float fx, fy, dx, gy;
+ float* t = cache->xform;
+ int i, cr, cg, cb, ca;
+ unsigned int c;
+
+ fx = (x - tx) / scale;
+ fy = (y - ty) / scale;
+ dx = 1.0f / scale;
+
+ for (i = 0; i < count; i++) {
+ int r,g,b,a,ia;
+ gy = fx*t[1] + fy*t[3] + t[5];
+ c = cache->colors[(int)nsvg__clampf(gy*255.0f, 0, 255.0f)];
+ cr = (c) & 0xff;
+ cg = (c >> 8) & 0xff;
+ cb = (c >> 16) & 0xff;
+ ca = (c >> 24) & 0xff;
+
+ a = nsvg__div255((int)cover[0] * ca);
+ ia = 255 - a;
+
+ // Premultiply
+ r = nsvg__div255(cr * a);
+ g = nsvg__div255(cg * a);
+ b = nsvg__div255(cb * a);
+
+ // Blend over
+ r += nsvg__div255(ia * (int)dst[0]);
+ g += nsvg__div255(ia * (int)dst[1]);
+ b += nsvg__div255(ia * (int)dst[2]);
+ a += nsvg__div255(ia * (int)dst[3]);
+
+ dst[0] = (unsigned char)r;
+ dst[1] = (unsigned char)g;
+ dst[2] = (unsigned char)b;
+ dst[3] = (unsigned char)a;
+
+ cover++;
+ dst += 4;
+ fx += dx;
+ }
+ } else if (cache->type == NSVG_PAINT_RADIAL_GRADIENT) {
+ // TODO: spread modes.
+ // TODO: plenty of opportunities to optimize.
+ // TODO: focus (fx,fy)
+ float fx, fy, dx, gx, gy, gd;
+ float* t = cache->xform;
+ int i, cr, cg, cb, ca;
+ unsigned int c;
+
+ fx = (x - tx) / scale;
+ fy = (y - ty) / scale;
+ dx = 1.0f / scale;
+
+ for (i = 0; i < count; i++) {
+ int r,g,b,a,ia;
+ gx = fx*t[0] + fy*t[2] + t[4];
+ gy = fx*t[1] + fy*t[3] + t[5];
+ gd = sqrtf(gx*gx + gy*gy);
+ c = cache->colors[(int)nsvg__clampf(gd*255.0f, 0, 255.0f)];
+ cr = (c) & 0xff;
+ cg = (c >> 8) & 0xff;
+ cb = (c >> 16) & 0xff;
+ ca = (c >> 24) & 0xff;
+
+ a = nsvg__div255((int)cover[0] * ca);
+ ia = 255 - a;
+
+ // Premultiply
+ r = nsvg__div255(cr * a);
+ g = nsvg__div255(cg * a);
+ b = nsvg__div255(cb * a);
+
+ // Blend over
+ r += nsvg__div255(ia * (int)dst[0]);
+ g += nsvg__div255(ia * (int)dst[1]);
+ b += nsvg__div255(ia * (int)dst[2]);
+ a += nsvg__div255(ia * (int)dst[3]);
+
+ dst[0] = (unsigned char)r;
+ dst[1] = (unsigned char)g;
+ dst[2] = (unsigned char)b;
+ dst[3] = (unsigned char)a;
+
+ cover++;
+ dst += 4;
+ fx += dx;
+ }
+ }
+}
+
+static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float scale, NSVGcachedPaint* cache, char fillRule)
+{
+ NSVGactiveEdge *active = NULL;
+ int y, s;
+ int e = 0;
+ int maxWeight = (255 / NSVG__SUBSAMPLES); // weight per vertical scanline
+ int xmin, xmax;
+
+ for (y = 0; y < r->height; y++) {
+ memset(r->scanline, 0, r->width);
+ xmin = r->width;
+ xmax = 0;
+ for (s = 0; s < NSVG__SUBSAMPLES; ++s) {
+ // find center of pixel for this scanline
+ float scany = y*NSVG__SUBSAMPLES + s + 0.5f;
+ NSVGactiveEdge **step = &active;
+
+ // update all active edges;
+ // remove all active edges that terminate before the center of this scanline
+ while (*step) {
+ NSVGactiveEdge *z = *step;
+ if (z->ey <= scany) {
+ *step = z->next; // delete from list
+// NSVG__assert(z->valid);
+ nsvg__freeActive(r, z);
+ } else {
+ z->x += z->dx; // advance to position for current scanline
+ step = &((*step)->next); // advance through list
+ }
+ }
+
+ // resort the list if needed
+ for (;;) {
+ int changed = 0;
+ step = &active;
+ while (*step && (*step)->next) {
+ if ((*step)->x > (*step)->next->x) {
+ NSVGactiveEdge* t = *step;
+ NSVGactiveEdge* q = t->next;
+ t->next = q->next;
+ q->next = t;
+ *step = q;
+ changed = 1;
+ }
+ step = &(*step)->next;
+ }
+ if (!changed) break;
+ }
+
+ // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline
+ while (e < r->nedges && r->edges[e].y0 <= scany) {
+ if (r->edges[e].y1 > scany) {
+ NSVGactiveEdge* z = nsvg__addActive(r, &r->edges[e], scany);
+ if (z == NULL) break;
+ // find insertion point
+ if (active == NULL) {
+ active = z;
+ } else if (z->x < active->x) {
+ // insert at front
+ z->next = active;
+ active = z;
+ } else {
+ // find thing to insert AFTER
+ NSVGactiveEdge* p = active;
+ while (p->next && p->next->x < z->x)
+ p = p->next;
+ // at this point, p->next->x is NOT < z->x
+ z->next = p->next;
+ p->next = z;
+ }
+ }
+ e++;
+ }
+
+ // now process all active edges in non-zero fashion
+ if (active != NULL)
+ nsvg__fillActiveEdges(r->scanline, r->width, active, maxWeight, &xmin, &xmax, fillRule);
+ }
+ // Blit
+ if (xmin < 0) xmin = 0;
+ if (xmax > r->width-1) xmax = r->width-1;
+ if (xmin <= xmax) {
+ nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty, scale, cache);
+ }
+ }
+
+}
+
+/**
+ * In the original software, an function 'static void nsvg__unpremultiplyAlpha(unsigned char* image, int w, int h, int stride)' is implemented here.
+ * We removed this function, as our renderer renders the pre-multiplied alpha format directly, so this process is not required.
+ */
+
+
+static void nsvg__initPaint(NSVGcachedPaint* cache, NSVGpaint* paint, float opacity)
+{
+ int i, j;
+ NSVGgradient* grad;
+
+ cache->type = paint->type;
+
+ if (paint->type == NSVG_PAINT_COLOR) {
+ cache->colors[0] = nsvg__applyOpacity(paint->color, opacity);
+ return;
+ }
+
+ grad = paint->gradient;
+
+ cache->spread = grad->spread;
+ memcpy(cache->xform, grad->xform, sizeof(float)*6);
+
+ if (grad->nstops == 0) {
+ for (i = 0; i < 256; i++)
+ cache->colors[i] = 0;
+ } if (grad->nstops == 1) {
+ for (i = 0; i < 256; i++)
+ cache->colors[i] = nsvg__applyOpacity(grad->stops[i].color, opacity);
+ } else {
+ unsigned int ca, cb = 0;
+ float ua, ub, du, u;
+ int ia, ib, count;
+
+ ca = nsvg__applyOpacity(grad->stops[0].color, opacity);
+ ua = nsvg__clampf(grad->stops[0].offset, 0, 1);
+ ub = nsvg__clampf(grad->stops[grad->nstops-1].offset, ua, 1);
+ ia = (int)(ua * 255.0f);
+ ib = (int)(ub * 255.0f);
+ for (i = 0; i < ia; i++) {
+ cache->colors[i] = ca;
+ }
+
+ for (i = 0; i < grad->nstops-1; i++) {
+ ca = nsvg__applyOpacity(grad->stops[i].color, opacity);
+ cb = nsvg__applyOpacity(grad->stops[i+1].color, opacity);
+ ua = nsvg__clampf(grad->stops[i].offset, 0, 1);
+ ub = nsvg__clampf(grad->stops[i+1].offset, 0, 1);
+ ia = (int)(ua * 255.0f);
+ ib = (int)(ub * 255.0f);
+ count = ib - ia;
+ if (count <= 0) continue;
+ u = 0;
+ du = 1.0f / (float)count;
+ for (j = 0; j < count; j++) {
+ cache->colors[ia+j] = nsvg__lerpRGBA(ca,cb,u);
+ u += du;
+ }
+ }
+
+ for (i = ib; i < 256; i++)
+ cache->colors[i] = cb;
+ }
+
+}
+
+void nsvgRasterize(NSVGrasterizer* r,
+ NSVGimage* image, float tx, float ty, float scale,
+ unsigned char* dst, int w, int h, int stride)
+{
+ NSVGshape *shape = NULL;
+ NSVGedge *e = NULL;
+ NSVGcachedPaint cache;
+ int i;
+
+ r->bitmap = dst;
+ r->width = w;
+ r->height = h;
+ r->stride = stride;
+
+ if (w > r->cscanline) {
+ r->cscanline = w;
+ r->scanline = (unsigned char*)realloc(r->scanline, w);
+ if (r->scanline == NULL) return;
+ }
+
+ for (i = 0; i < h; i++)
+ memset(&dst[i*stride], 0, w*4);
+
+ for (shape = image->shapes; shape != NULL; shape = shape->next) {
+ if (!(shape->flags & NSVG_FLAGS_VISIBLE))
+ continue;
+
+ if (shape->fill.type != NSVG_PAINT_NONE) {
+ nsvg__resetPool(r);
+ r->freelist = NULL;
+ r->nedges = 0;
+
+ nsvg__flattenShape(r, shape, scale);
+
+ // Scale and translate edges
+ for (i = 0; i < r->nedges; i++) {
+ e = &r->edges[i];
+ e->x0 = tx + e->x0;
+ e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES;
+ e->x1 = tx + e->x1;
+ e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES;
+ }
+
+ // Rasterize edges
+ qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge);
+
+ // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
+ nsvg__initPaint(&cache, &shape->fill, shape->opacity);
+
+ nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, shape->fillRule);
+ }
+ if (shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * scale) > 0.01f) {
+ nsvg__resetPool(r);
+ r->freelist = NULL;
+ r->nedges = 0;
+
+ nsvg__flattenShapeStroke(r, shape, scale);
+
+// dumpEdges(r, "edge.svg");
+
+ // Scale and translate edges
+ for (i = 0; i < r->nedges; i++) {
+ e = &r->edges[i];
+ e->x0 = tx + e->x0;
+ e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES;
+ e->x1 = tx + e->x1;
+ e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES;
+ }
+
+ // Rasterize edges
+ qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge);
+
+ // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
+ nsvg__initPaint(&cache, &shape->stroke, shape->opacity);
+
+ nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, NSVG_FILLRULE_NONZERO);
+ }
+ }
+
+ /**
+ * In the original file, the pre-multiplied alpha format is transformed to the convertional non-pre format.
+ * We skip this process here, and render the pre-multiplied alpha format directly in our svg renderer.
+ */
+
+ r->bitmap = NULL;
+ r->width = 0;
+ r->height = 0;
+ r->stride = 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2013-14 Mikko Mononen memon@inside.org
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * The polygon rasterization is heavily based on stb_truetype rasterizer
+ * by Sean Barrett - http://nothings.org/
+ *
+ */
+
+#ifndef NANOSVGRAST_H
+#define NANOSVGRAST_H
+
+#include "nanosvg.h"
+
+typedef struct NSVGrasterizer NSVGrasterizer;
+
+/* Example Usage:
+ // Load SVG
+ struct SNVGImage* image = nsvgParseFromFile("test.svg.");
+
+ // Create rasterizer (can be used to render multiple images).
+ struct NSVGrasterizer* rast = nsvgCreateRasterizer();
+ // Allocate memory for image
+ unsigned char* img = malloc(w*h*4);
+ // Rasterize
+ nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4);
+*/
+
+// Allocated rasterizer context.
+NSVGrasterizer* nsvgCreateRasterizer();
+
+// Rasterizes SVG image, returns RGBA image (premultiplied alpha)
+// r - pointer to rasterizer context
+// image - pointer to image to rasterize
+// tx,ty - image offset (applied after scaling)
+// scale - image scale
+// dst - pointer to destination image data, 4 bytes per pixel (RGBA)
+// w - width of the image to render
+// h - height of the image to render
+// stride - number of bytes per scaleline in the destination buffer
+void nsvgRasterize(NSVGrasterizer* r,
+ NSVGimage* image, float tx, float ty, float scale,
+ unsigned char* dst, int w, int h, int stride);
+
+// Deletes rasterizer context.
+void nsvgDeleteRasterizer(NSVGrasterizer*);
+
+
+#endif // NANOSVGRAST_H
+
+/**
+ * In the original software, The nanosvgrast implementation was followed here.
+ * We have moved the implementation to nanosvgrast.cc.
+ */
--- /dev/null
+/*
+ * Copyright (c) 2016 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.
+ *
+ */
+
+// CLASS HEADER
+#include "svg-rasterize-thread.h"
+
+// INTERNAL INCLUDES
+#include "nanosvg/nanosvgrast.h"
+#include "svg-renderer.h"
+
+namespace
+{
+const std::string TEXTURE_UNIFORM_NAME = "sTexture";
+}
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+RasterizingTask::RasterizingTask( SvgRenderer* svgRenderer, NSVGimage* parsedSvg, unsigned int width, unsigned int height )
+: mSvgRenderer( svgRenderer ),
+ mParsedSvg( parsedSvg ),
+ mWidth( width ),
+ mHeight( height )
+{
+}
+
+void RasterizingTask::Rasterize( NSVGrasterizer* rasterizer )
+{
+ if( mWidth > 0u && mHeight > 0u )
+ {
+ float scaleX = static_cast<float>( mWidth ) / mParsedSvg->width;
+ float scaleY = static_cast<float>( mHeight ) / mParsedSvg->height;
+ float scale = scaleX < scaleY ? scaleX : scaleY;
+ unsigned int bufferStride = mWidth*Pixel::GetBytesPerPixel( Pixel::RGBA8888 );
+
+ unsigned char* buffer = new unsigned char [bufferStride*mHeight];
+ nsvgRasterize(rasterizer, mParsedSvg, 0.f,0.f,scale,
+ buffer, mWidth, mHeight,
+ bufferStride );
+
+ mPixelData = Dali::PixelData::New( buffer, mWidth, mHeight, Pixel::RGBA8888, Dali::PixelData::DELETE_ARRAY );
+ }
+}
+
+SvgRenderer* RasterizingTask::GetSvgRenderer() const
+{
+ return mSvgRenderer.Get();
+}
+
+PixelDataPtr RasterizingTask::GetPixelData() const
+{
+ return mPixelData;
+}
+
+SvgRasterizeThread::SvgRasterizeThread( EventThreadCallback* trigger )
+: mTrigger( trigger ),
+ mIsThreadWaiting( false )
+{
+ mRasterizer = nsvgCreateRasterizer();
+}
+
+SvgRasterizeThread::~SvgRasterizeThread()
+{
+
+ nsvgDeleteRasterizer( mRasterizer );
+ delete mTrigger;
+}
+
+void SvgRasterizeThread::TerminateThread( SvgRasterizeThread*& thread )
+{
+ if( thread )
+ {
+ // add an empty task would stop the thread from conditional wait.
+ thread->AddTask( RasterizingTaskPtr() );
+ // stop the thread
+ thread->Join();
+ // delete the thread
+ delete thread;
+ thread = NULL;
+ }
+}
+
+void SvgRasterizeThread::AddTask( RasterizingTaskPtr task )
+{
+ bool wasEmpty = false;
+
+ {
+ // Lock while adding task to the queue
+ ConditionalWait::ScopedLock lock( mConditionalWait );
+ wasEmpty = mRasterizeTasks.empty();
+ if( !wasEmpty && task != NULL)
+ {
+ // Remove the tasks with the same material.
+ // Older task which waiting to rasterize and apply the svg to the same material is expired.
+ for( std::vector< RasterizingTaskPtr >::iterator it = mRasterizeTasks.begin(), endIt = mRasterizeTasks.end(); it != endIt; ++it )
+ {
+ if( (*it) && (*it)->GetSvgRenderer() == task->GetSvgRenderer() )
+ {
+ mRasterizeTasks.erase( it );
+ break;
+ }
+ }
+ }
+ mRasterizeTasks.push_back( task );
+ }
+
+ if( wasEmpty)
+ {
+ // wake up the image loading thread
+ mConditionalWait.Notify();
+ }
+}
+
+RasterizingTaskPtr SvgRasterizeThread::NextCompletedTask()
+{
+ // Lock while popping task out from the queue
+ Mutex::ScopedLock lock( mMutex );
+
+ if( mCompletedTasks.empty() )
+ {
+ return RasterizingTaskPtr();
+ }
+
+ std::vector< RasterizingTaskPtr >::iterator next = mCompletedTasks.begin();
+ RasterizingTaskPtr nextTask = *next;
+ mCompletedTasks.erase( next );
+
+ return nextTask;
+}
+
+void SvgRasterizeThread::RemoveTask( SvgRenderer* renderer )
+{
+ // Lock while remove task from the queue
+ ConditionalWait::ScopedLock lock( mConditionalWait );
+ if( !mRasterizeTasks.empty() )
+ {
+ for( std::vector< RasterizingTaskPtr >::iterator it = mRasterizeTasks.begin(), endIt = mRasterizeTasks.end(); it != endIt; ++it )
+ {
+ if( (*it) && (*it)->GetSvgRenderer() == renderer )
+ {
+ mRasterizeTasks.erase( it );
+ break;
+ }
+ }
+ }
+}
+
+void SvgRasterizeThread::DeleteImage( NSVGimage* parsedSvg )
+{
+ // Lock while adding image to the delete queue
+ ConditionalWait::ScopedLock lock( mConditionalWait );
+
+ if( mIsThreadWaiting ) // no rasterization is ongoing, save to delete
+ {
+ nsvgDelete( parsedSvg );
+ }
+ else // wait to delete until current rasterization completed.
+ {
+ mDeleteSvg.PushBack( parsedSvg );
+ }
+}
+
+RasterizingTaskPtr SvgRasterizeThread::NextTaskToProcess()
+{
+ // Lock while popping task out from the queue
+ ConditionalWait::ScopedLock lock( mConditionalWait );
+
+ // Delete the image here to make sure that it is not used in the nsvgRasterize()
+ if( !mDeleteSvg.Empty() )
+ {
+ for( Vector< NSVGimage* >::Iterator it = mDeleteSvg.Begin(), endIt = mDeleteSvg.End();
+ it != endIt;
+ ++it )
+ {
+ nsvgDelete( *it );
+ }
+ mDeleteSvg.Clear();
+ }
+
+ // conditional wait
+ while( mRasterizeTasks.empty() )
+ {
+ mIsThreadWaiting = true;
+ mConditionalWait.Wait( lock );
+ }
+ mIsThreadWaiting = false;
+
+ // pop out the next task from the queue
+ std::vector< RasterizingTaskPtr >::iterator next = mRasterizeTasks.begin();
+ RasterizingTaskPtr nextTask = *next;
+ mRasterizeTasks.erase( next );
+
+ return nextTask;
+}
+
+void SvgRasterizeThread::AddCompletedTask( RasterizingTaskPtr task )
+{
+ // Lock while adding task to the queue
+ Mutex::ScopedLock lock( mMutex );
+ mCompletedTasks.push_back( task );
+
+ // wake up the main thread
+ mTrigger->Trigger();
+}
+
+void SvgRasterizeThread::Run()
+{
+ while( RasterizingTaskPtr task = NextTaskToProcess() )
+ {
+ task->Rasterize( mRasterizer );
+ AddCompletedTask( task );
+ }
+}
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
--- /dev/null
+#ifndef __DALI_TOOLKIT_SVG_RASTERIZE_THREAD_H__
+#define __DALI_TOOLKIT_SVG_RASTERIZE_THREAD_H__
+
+/*
+ * Copyright (c) 2016 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.
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/threading/conditional-wait.h>
+#include <dali/devel-api/threading/mutex.h>
+#include <dali/devel-api/threading/thread.h>
+#include <dali/devel-api/rendering/material.h>
+#include <dali/devel-api/images/pixel-data.h>
+#include <dali/public-api/images/buffer-image.h>
+#include <dali/public-api/common/intrusive-ptr.h>
+#include <dali/public-api/common/vector-wrapper.h>
+#include <dali/public-api/object/ref-object.h>
+#include <dali/devel-api/adaptor-framework/event-thread-callback.h>
+
+struct NSVGimage;
+struct NSVGrasterizer;
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+class SvgRenderer;
+typedef IntrusivePtr< SvgRenderer > SvgRendererPtr;
+class RasterizingTask;
+typedef IntrusivePtr< RasterizingTask > RasterizingTaskPtr;
+
+/**
+ * The svg rasterizing tasks to be processed in the worker thread.
+ *
+ * Life cycle of a rasterizing task is as follows:
+ * 1. Created by SvgRenderer in the main thread
+ * 2. Queued in the worked thread waiting to be processed.
+ * 3. If this task gets its turn to do the rasterization, it triggers main thread to apply the rasterized image to material then been deleted in main thread call back
+ * Or if this task is been removed ( new image/size set to the renderer or actor off stage) before its turn to be processed, it then been deleted in the worker thread.
+ */
+class RasterizingTask : public RefObject
+{
+public:
+ /**
+ * Constructor
+ *
+ * @param[in] svgRenderer The renderer which the rasterized image to be applied.
+ * @param[in] parsedSvg The parsed svg for rasterizing.
+ * Note, after the task is added to the worker thread, the worker thread takes over the ownership.
+ * When the image is to be deleted, delete it in the worker thread by calling SvgRasterizeThread::DeleteImage( parsedSvg ).
+ * @param[in] width The rasterization width.
+ * @param[in] height The rasterization height.
+ */
+ RasterizingTask( SvgRenderer* svgRenderer, NSVGimage* parsedSvg, unsigned int width, unsigned int height );
+
+ /**
+ * Do the rasterization with the given rasterizer.
+ *@param[in] rasterizer The rasterizer that rasterize the SVG to a buffer image
+ */
+ void Rasterize( NSVGrasterizer* rasterizer );
+
+ /**
+ * Get the svg renderer
+ */
+ SvgRenderer* GetSvgRenderer() const;
+
+ /**
+ * Get the rasterization result.
+ * @return The pixel data with the rasterized pixels.
+ */
+ PixelDataPtr GetPixelData() const;
+
+private:
+
+ // Undefined
+ RasterizingTask( const RasterizingTask& task );
+
+ // Undefined
+ RasterizingTask& operator=( const RasterizingTask& task );
+
+private:
+ SvgRendererPtr mSvgRenderer;
+ PixelDataPtr mPixelData;
+ NSVGimage* mParsedSvg;
+ unsigned int mWidth;
+ unsigned int mHeight;
+};
+
+
+/**
+ * The worker thread for SVG rasterization.
+ */
+class SvgRasterizeThread : public Thread
+{
+public:
+
+ /**
+ * Constructor.
+ *
+ * @param[in] trigger The trigger to wake up the main thread.
+ */
+ SvgRasterizeThread( EventThreadCallback* trigger );
+
+ /**
+ * Terminate the svg rasterize thread, join and delete.
+ */
+ static void TerminateThread( SvgRasterizeThread*& thread );
+
+ /**
+ * Add a rasterization task into the waiting queue, called by main thread.
+ *
+ * @param[in] task The task added to the queue.
+ */
+ void AddTask( RasterizingTaskPtr task );
+
+ /**
+ * Pop the next task out from the completed queue, called by main thread.
+ *
+ * @return The next task in the completed queue.
+ */
+ RasterizingTaskPtr NextCompletedTask();
+
+ /**
+ * Remove the task with the given material from the waiting queue, called by main thread.
+ *
+ * Typically called when the actor is put off stage, so the material is not needed anymore.
+ *
+ * @param[in] renderer The renderer pointer.
+ */
+ void RemoveTask( SvgRenderer* renderer );
+
+ /**
+ * Delete the parsed SVG image, called by main thread.
+ *
+ * The parsed svg should be delelted in worker thread, as the main thread does not know whether a rasterization of this svg is ongoing.
+ *
+ * @param[in] parsedImage The image to be deleted
+ */
+ void DeleteImage( NSVGimage* parsedSvg );
+
+private:
+
+ /**
+ * Pop the next task out from the queue.
+ *
+ * @return The next task to be processed.
+ */
+ RasterizingTaskPtr NextTaskToProcess();
+
+ /**
+ * Add a task in to the queue
+ *
+ * @param[in] task The task added to the queue.
+ */
+ void AddCompletedTask( RasterizingTaskPtr task );
+
+protected:
+
+ /**
+ * Destructor.
+ */
+ virtual ~SvgRasterizeThread();
+
+
+ /**
+ * The entry function of the worker thread.
+ * It fetches task from the Queue, rasterizes the image and apply to the material.
+ */
+ virtual void Run();
+
+private:
+
+ // Undefined
+ SvgRasterizeThread( const SvgRasterizeThread& thread );
+
+ // Undefined
+ SvgRasterizeThread& operator=( const SvgRasterizeThread& thread );
+
+private:
+
+ std::vector<RasterizingTaskPtr> mRasterizeTasks; //The queue of the tasks waiting to rasterize the SVG image
+ std::vector <RasterizingTaskPtr> mCompletedTasks; //The queue of the tasks with the SVG rasterization completed
+ Vector<NSVGimage*> mDeleteSvg; //The images that the event thread requested to delete
+
+ ConditionalWait mConditionalWait;
+ Dali::Mutex mMutex;
+ EventThreadCallback* mTrigger;
+
+ NSVGrasterizer* mRasterizer;
+ bool mIsThreadWaiting;
+};
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif /* __DALI_TOOLKIT_SVG_RASTERIZE_THREAD_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2016 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.
+ *
+ */
+
+// CLASS HEADER
+#include "svg-renderer.h"
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/images/buffer-image.h>
+#include <dali/public-api/common/stage.h>
+#include <dali/public-api/math/vector4.h>
+#include <dali/devel-api/images/atlas.h>
+#include <dali/integration-api/debug.h>
+
+// INTERNAL INCLUDES
+#include "nanosvg/nanosvg.h"
+#include "svg-rasterize-thread.h"
+#include <dali-toolkit/internal/controls/renderers/image/image-renderer.h>
+#include <dali-toolkit/internal/controls/renderers/renderer-factory-cache.h>
+#include <dali-toolkit/internal/controls/renderers/control-renderer-data-impl.h>
+
+
+namespace
+{
+const char * const RENDERER_TYPE("rendererType");
+const char * const RENDERER_TYPE_VALUE("svg");
+const char * const IMAGE_URL_NAME("imageUrl");
+const char * const UNITS("px");
+
+const std::string TEXTURE_UNIFORM_NAME = "sTexture";
+const std::string ATLAS_RECT_UNIFORM_NAME = "uAtlasRect";
+
+const Dali::Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
+}
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+SvgRenderer::SvgRenderer( RendererFactoryCache& factoryCache, ImageAtlasManager& atlasManager )
+: ControlRenderer( factoryCache ),
+ mAtlasRect( FULL_TEXTURE_RECT ),
+ mAtlasManager( atlasManager ),
+ mParsedImage( NULL )
+{
+ // the rasterized image is with pre-multiplied alpha format
+ mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
+}
+
+SvgRenderer::~SvgRenderer()
+{
+ if( mParsedImage )
+ {
+ nsvgDelete( mParsedImage );
+ }
+}
+
+bool SvgRenderer::IsSvgUrl( const std::string& url )
+{
+ return url.substr( url.find_last_of(".") + 1 ) == "svg";
+}
+
+void SvgRenderer::DoInitialize( Actor& actor, const Property::Map& propertyMap )
+{
+ Property::Value* imageURLValue = propertyMap.Find( IMAGE_URL_NAME );
+ if( imageURLValue )
+ {
+ std::string imageUrl;
+ if( imageURLValue->Get( imageUrl ) )
+ {
+ SetImage( imageUrl );
+ }
+ else
+ {
+ DALI_LOG_ERROR( "The property '%s' is not a string\n", IMAGE_URL_NAME );
+ }
+ }
+}
+
+void SvgRenderer::DoSetOnStage( Actor& actor )
+{
+ Shader shader = ImageRenderer::GetImageShader( mFactoryCache );
+ Geometry geometry = mFactoryCache.GetGeometry( RendererFactoryCache::QUAD_GEOMETRY );
+ if( !geometry )
+ {
+ geometry = mFactoryCache.CreateQuadGeometry();
+ mFactoryCache.SaveGeometry( RendererFactoryCache::QUAD_GEOMETRY, geometry );
+ }
+ Material material = Material::New( shader );
+ mImpl->mRenderer = Renderer::New( geometry, material );
+
+ if( mImpl->mSize != Vector2::ZERO && mParsedImage )
+ {
+ AddRasterizationTask( mImpl->mSize );
+ }
+}
+
+void SvgRenderer::DoSetOffStage( Actor& actor )
+{
+ mFactoryCache.GetSVGRasterizationThread()->RemoveTask( this );
+
+ actor.RemoveRenderer( mImpl->mRenderer );
+ mImpl->mRenderer.Reset();
+}
+
+void SvgRenderer::GetNaturalSize( Vector2& naturalSize ) const
+{
+ if( mParsedImage )
+ {
+ naturalSize.x = mParsedImage->width;
+ naturalSize.y = mParsedImage->height;
+ }
+ else
+ {
+ naturalSize = Vector2::ZERO;
+ }
+}
+
+void SvgRenderer::SetSize( const Vector2& size )
+{
+ if(mImpl->mSize != size && mParsedImage && GetIsOnStage() )
+ {
+ AddRasterizationTask( size );
+ }
+ mImpl->mSize = size;
+}
+
+void SvgRenderer::DoCreatePropertyMap( Property::Map& map ) const
+{
+ map.Clear();
+ map.Insert( RENDERER_TYPE, RENDERER_TYPE_VALUE );
+ if( !mImageUrl.empty() )
+ {
+ map.Insert( IMAGE_URL_NAME, mImageUrl );
+ }
+}
+
+void SvgRenderer::SetImage( const std::string& imageUrl, ImageDimensions size )
+{
+ if( mImageUrl != imageUrl )
+ {
+ mImageUrl = imageUrl;
+
+ NSVGimage* parsedImageOld = mParsedImage;
+
+ Vector2 dpi = Stage::GetCurrent().GetDpi();
+ float meanDpi = (dpi.height + dpi.width) * 0.5f;
+ mParsedImage = nsvgParseFromFile(mImageUrl.c_str(), UNITS, meanDpi);
+
+ if( size.GetWidth() != 0u && size.GetHeight() != 0u)
+ {
+ mImpl->mSize.x = size.GetWidth();
+ mImpl->mSize.y = size.GetHeight();
+ }
+
+ if( mImpl->mSize != Vector2::ZERO && GetIsOnStage() )
+ {
+ AddRasterizationTask( mImpl->mSize );
+ }
+
+ mFactoryCache.GetSVGRasterizationThread()->DeleteImage( parsedImageOld );
+ }
+}
+
+void SvgRenderer::AddRasterizationTask( const Vector2& size )
+{
+ if( mImpl->mRenderer && mParsedImage )
+ {
+ unsigned int width = static_cast<unsigned int>(size.width);
+ unsigned int height = static_cast<unsigned int>( size.height );
+ BufferImage image = BufferImage::New( width, height, Pixel::RGBA8888);
+
+ RasterizingTaskPtr newTask = new RasterizingTask( this, mParsedImage, width, height );
+ mFactoryCache.GetSVGRasterizationThread()->AddTask( newTask );
+ }
+}
+
+void SvgRenderer::ApplyRasterizedImage( PixelDataPtr rasterizedPixelData )
+{
+ if( GetIsOnStage() )
+ {
+ Material currentMaterial = mImpl->mRenderer.GetMaterial();
+ if( mAtlasRect != FULL_TEXTURE_RECT )
+ {
+ mAtlasManager.Remove( currentMaterial, mAtlasRect );
+ }
+
+ Vector4 atlasRect;
+ Material material = mAtlasManager.Add(atlasRect, rasterizedPixelData );
+ if( material ) // atlasing
+ {
+ if( material != currentMaterial )
+ {
+ mImpl->mRenderer.SetMaterial( material );
+ }
+ mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, atlasRect );
+ mAtlasRect = atlasRect;
+ }
+ else // no atlasing
+ {
+ Atlas texture = Atlas::New( rasterizedPixelData->GetWidth(), rasterizedPixelData->GetHeight() );
+ texture.Upload( rasterizedPixelData, 0, 0 );
+
+ if( mAtlasRect == FULL_TEXTURE_RECT )
+ {
+ material = currentMaterial;
+ }
+ else
+ {
+ material = Material::New( ImageRenderer::GetImageShader( mFactoryCache ) );
+ mImpl->mRenderer.SetMaterial( material );
+
+ mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT );
+ mAtlasRect = FULL_TEXTURE_RECT;
+ }
+
+ if( material )
+ {
+ int index = material.GetTextureIndex( TEXTURE_UNIFORM_NAME );
+ if( index != -1 )
+ {
+ material.SetTextureImage( index, texture );
+ return;
+ }
+
+ material.AddTexture( texture, TEXTURE_UNIFORM_NAME );
+ }
+ }
+ }
+}
+
+
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
--- /dev/null
+#ifndef __DALI_TOOLKIT_INTERNAL_SVG_RENDERER_H__
+#define __DALI_TOOLKIT_INTERNAL_SVG_RENDERER_H__
+
+/*
+ * Copyright (c) 2016 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.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/controls/renderers/control-renderer-impl.h>
+#include <dali-toolkit/internal/controls/renderers/image-atlas-manager.h>
+
+struct NSVGimage;
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+/**
+ * The renderer which renders a svg image
+ *
+ * The following property is essential
+ *
+ * | %Property Name | Type |
+ * |--------------------------|------------------|
+ * | imageUrl | STRING |
+ *
+ */
+class SvgRenderer: public ControlRenderer
+{
+public:
+
+ /**
+ * @brief Constructor.
+ *
+ * @param[in] factoryCache A pointer pointing to the RendererFactoryCache object
+ */
+ SvgRenderer( RendererFactoryCache& factoryCache, ImageAtlasManager& atlasManager );
+
+ /**
+ * @brief A reference counted object may only be deleted by calling Unreference().
+ */
+ ~SvgRenderer();
+
+public: // from ControlRenderer
+
+ /**
+ * @copydoc ControlRenderer::GetNaturalSize
+ */
+ virtual void GetNaturalSize( Vector2& naturalSize ) const;
+
+ /**
+ * @copydoc ControlRenderer::SetSize
+ */
+ virtual void SetSize( const Vector2& size );
+
+ /**
+ * @copydoc ControlRenderer::CreatePropertyMap
+ */
+ virtual void DoCreatePropertyMap( Property::Map& map ) const;
+
+protected:
+
+ /**
+ * @copydoc ControlRenderer::DoInitialize
+ */
+ virtual void DoInitialize( Actor& actor, const Property::Map& propertyMap );
+
+ /**
+ * @copydoc ControlRenderer::DoSetOnStage
+ */
+ virtual void DoSetOnStage( Actor& actor );
+
+ /**
+ * @copydoc ControlRenderer::DoSetOffStage
+ */
+ virtual void DoSetOffStage( Actor& actor );
+
+public:
+
+ /**
+ * @brief Helper method to determine whether the url indicate that it is a svg image.
+ *
+ * @param [in] url The URL of the image file.
+ * @return true if it is a svg image
+ */
+ static bool IsSvgUrl( const std::string& url );
+
+ /**
+ * @brief Sets the svg image of this renderer to the resource at imageUrl
+ * The renderer will parse the svg image once it is set.
+ * And rasterize it into BufferImage synchronously when the associated actor is put on stage, and destroy the BufferImage when it is off stage
+ *
+ * @param[in] imageUrl The URL to svg resource to use
+ */
+ void SetImage( const std::string& imageUrl, ImageDimensions size = ImageDimensions() );
+
+ /**
+ * @bried Apply the rasterized image to the renderer.
+ *
+ * @param[in] rasterizedPixelData The pixel buffer with the rasterized pixels
+ */
+ void ApplyRasterizedImage( PixelDataPtr rasterizedPixelData );
+
+private:
+ /**
+ * @bried Rasterize the svg with the given size, and add it to the material.
+ *
+ * @param[in] size The target size of the SVG rasterization.
+ */
+ void AddRasterizationTask( const Vector2& size );
+
+
+ // Undefined
+ SvgRenderer( const SvgRenderer& svgRenderer );
+
+ // Undefined
+ SvgRenderer& operator=( const SvgRenderer& svgRenderer );
+
+private:
+ Vector4 mAtlasRect;
+ ImageAtlasManager& mAtlasManager;
+ std::string mImageUrl;
+ NSVGimage* mParsedImage;
+
+};
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif /* __DALI_TOOLKIT_INTERNAL_SVG_RENDERER_H__ */
Property::Map vertexFormat;
vertexFormat["aPosition1"] = Property::VECTOR3;
vertexFormat["aPosition2"] = Property::VECTOR3;
- PropertyBuffer vertices = PropertyBuffer::New( vertexFormat, 20u );
- vertices.SetData( vertexData );
+ PropertyBuffer vertices = PropertyBuffer::New( vertexFormat );
+ vertices.SetData( vertexData, 20u );
unsigned int indexData[30] = { 0,3,1,0,2,3,4,7,5,4,6,7,8,11,9,8,10,11,12,15,13,12,14,15,16,19,17,16,18,19};
Property::Map indexFormat;
indexFormat["indices"] = Property::INTEGER;
- PropertyBuffer indices = PropertyBuffer::New( indexFormat, 30u );
- indices.SetData( indexData );
+ PropertyBuffer indices = PropertyBuffer::New( indexFormat );
+ indices.SetData( indexData, 30u );
Geometry meshGeometry = Geometry::New();
meshGeometry.AddVertexBuffer( vertices );
// pixel format / size - set from JSON
// aspect ratio property needs to be able to be constrained also for cameras. (now do-able)
// default near clip value
-// mChildrenRoot Add()/Remove() overloads - better solution
/////////////////////////////////////////////////////////
return handle;
}
-/////////////////////////////////////////////////////////////
-// for creating a subtree for all user added child actors.
-// TODO: overloading Actor::Add()/Remove() not nice since breaks polymorphism. Need another method to pass ownership of added child actors to our internal actor root.
-void ShadowView::Add(Actor child)
-{
- mChildrenRoot.Add(child);
-}
-
-void ShadowView::Remove(Actor child)
-{
- mChildrenRoot.Remove(child);
-}
-
void ShadowView::SetShadowPlaneBackground(Actor shadowPlaneBackground)
{
mShadowPlaneBg = shadowPlaneBackground;
blurStrengthConstraint.Apply();
}
-void ShadowView::OnSizeSet(const Vector3& targetSize)
+void ShadowView::OnControlChildAdd( Actor& child )
+{
+ if( child != mChildrenRoot && child != mBlurRootActor)
+ {
+ mChildrenRoot.Add( child );
+ }
+}
+
+void ShadowView::OnControlChildRemove( Actor& child )
{
+ mChildrenRoot.Remove( child );
}
void ShadowView::ConstrainCamera()
static Dali::Toolkit::ShadowView New(float downsampleWidthScale, float downsampleHeightScale);
/**
- * @copydoc Dali::Toolkit::ShadowView::Add(Actor child)
- */
- void Add(Actor child);
-
- /**
- * @copydoc Dali::Toolkit::ShadowView::Remove(Actor child)
- */
- void Remove(Actor child);
-
- /**
* @copydoc Dali::Toolkit::ShadowView::SetShadowPlaneBackground(Actor shadowPlaneBackground)
*/
void SetShadowPlaneBackground(Actor shadowPlaneBackground);
private:
virtual void OnInitialize();
- virtual void OnSizeSet(const Vector3& targetSize);
+
+ /**
+ * @copydoc Control::OnControlChildAdd()
+ */
+ virtual void OnControlChildAdd( Actor& child );
+
+ /**
+ * @copydoc Control::OnControlChildRemove()
+ */
+ virtual void OnControlChildRemove( Actor& child );
/**
* Constrain the camera actor to the position of the point light, pointing
if( mRenderableActor )
{
- const Vector2 offset = mController->GetScrollPosition() + mController->GetAlignmentOffset();
+ // TODO: Scroll and alignment needs to be refactored.
+ const Vector2& alignmentOffset = mController->GetAlignmentOffset();
+ const Vector2& scrollOffset = mController->GetScrollPosition();
- mRenderableActor.SetPosition( offset.x, offset.y );
+ mRenderableActor.SetPosition( scrollOffset.x, alignmentOffset.y + scrollOffset.y );
Actor clipRootActor;
if( mClipper )
if( mRenderableActor )
{
- const Vector2 offset = mController->GetScrollPosition() + mController->GetAlignmentOffset();
+ // TODO: Scroll and alignment needs to be refactored.
+ const Vector2& alignmentOffset = mController->GetAlignmentOffset();
+ const Vector2& scrollOffset = mController->GetScrollPosition();
- mRenderableActor.SetPosition( offset.x, offset.y );
+ mRenderableActor.SetPosition( scrollOffset.x, alignmentOffset.y + scrollOffset.y );
Actor clipRootActor;
if( mClipper )
if( renderableActor )
{
+ // TODO: Scroll and alignment needs to be refactored.
const Vector2& alignmentOffset = mController->GetAlignmentOffset();
- renderableActor.SetPosition( alignmentOffset.x, alignmentOffset.y );
+ renderableActor.SetPosition( 0.f, alignmentOffset.y );
self.Add( renderableActor );
}
$(toolkit_src_dir)/builder/builder-actor.cpp \
$(toolkit_src_dir)/builder/builder-animations.cpp \
$(toolkit_src_dir)/builder/builder-impl.cpp \
+ $(toolkit_src_dir)/builder/builder-impl-debug.cpp \
$(toolkit_src_dir)/builder/builder-set-property.cpp \
$(toolkit_src_dir)/builder/builder-signals.cpp \
$(toolkit_src_dir)/builder/json-parser-state.cpp \
$(toolkit_src_dir)/controls/renderers/gradient/linear-gradient.cpp \
$(toolkit_src_dir)/controls/renderers/gradient/radial-gradient.cpp \
$(toolkit_src_dir)/controls/renderers/gradient/gradient-renderer.cpp \
+ $(toolkit_src_dir)/controls/renderers/svg/nanosvg/nanosvg.cc \
+ $(toolkit_src_dir)/controls/renderers/svg/nanosvg/nanosvgrast.cc \
+ $(toolkit_src_dir)/controls/renderers/svg/svg-rasterize-thread.cpp \
+ $(toolkit_src_dir)/controls/renderers/svg/svg-renderer.cpp \
$(toolkit_src_dir)/controls/alignment/alignment-impl.cpp \
$(toolkit_src_dir)/controls/bloom-view/bloom-view-impl.cpp \
$(toolkit_src_dir)/controls/bubble-effect/bubble-emitter-impl.cpp \
$(toolkit_src_dir)/transition-effects/cube-transition-wave-effect-impl.cpp \
$(toolkit_src_dir)/scripting/script-impl.cpp \
$(toolkit_src_dir)/scripting/script-plugin-proxy.cpp
-
-
return false;
}
+bool ImageAtlas::Upload( Vector4& textureRect, PixelDataPtr pixelData )
+{
+ unsigned int packPositionX = 0;
+ unsigned int packPositionY = 0;
+ if( mPacker.Pack( pixelData->GetWidth(), pixelData->GetHeight(), packPositionX, packPositionY ) )
+ {
+ mAtlas.Upload( pixelData, packPositionX, packPositionY );
+
+ // apply the half pixel correction
+ textureRect.x = ( static_cast<float>( packPositionX ) +0.5f ) / mWidth; // left
+ textureRect.y = ( static_cast<float>( packPositionY ) +0.5f ) / mHeight; // right
+ textureRect.z = ( static_cast<float>( packPositionX + pixelData->GetWidth() )-0.5f ) / mWidth; // right
+ textureRect.w = ( static_cast<float>( packPositionY + pixelData->GetHeight() )-0.5f ) / mHeight;// bottom
+
+ return true;
+ }
+
+ return false;
+}
+
void ImageAtlas::Remove( const Vector4& textureRect )
{
mPacker.DeleteBlock( static_cast<SizeType>(textureRect.x*mWidth),
void SetBrokenImage( const std::string& brokenImageUrl );
/**
- * @copydoc Toolkit::ImageAtlas::Upload
+ * @copydoc Toolkit::ImageAtlas::Upload( Vector4&, const std::string&, ImageDimensions,FittingMode::Type, bool )
*/
bool Upload( Vector4& textureRect,
const std::string& url,
bool orientationCorrection);
/**
+ * @copydoc Toolkit::ImageAtlas::Upload( Vector4&, PixelDataPtr )
+ */
+ bool Upload( Vector4& textureRect, PixelDataPtr pixelData );
+
+ /**
* @copydoc Toolkit::ImageAtlas::Remove
*/
void Remove( const Vector4& textureRect );
indices.PushBack( v + 3 );
}
- if( mQuadVertices )
+ if( ! mQuadVertices )
{
- mQuadVertices.SetSize( vertices.Size() );
- }
- else
- {
- mQuadVertices = PropertyBuffer::New( mQuadVertexFormat, vertices.Size() );
+ mQuadVertices = PropertyBuffer::New( mQuadVertexFormat );
}
- if( mQuadIndices )
- {
- mQuadIndices.SetSize( indices.Size() );
- }
- else
+ if( ! mQuadIndices )
{
- mQuadIndices = PropertyBuffer::New( mQuadIndexFormat, indices.Size() );
+ mQuadIndices = PropertyBuffer::New( mQuadIndexFormat );
}
- mQuadVertices.SetData( &vertices[ 0 ] );
- mQuadIndices.SetData( &indices[ 0 ] );
+ mQuadVertices.SetData( &vertices[ 0 ], vertices.Size() );
+ mQuadIndices.SetData( &indices[ 0 ], indices.Size() );
if( !mQuadGeometry )
{
const bool RTL = true;
const float CURSOR_WIDTH = 1.f;
+Length CountParagraphs( const LayoutParameters& layoutParameters )
+{
+ Length numberOfParagraphs = 0u;
+
+ const CharacterIndex startCharacterIndex = *( layoutParameters.glyphsToCharactersBuffer + layoutParameters.startGlyphIndex );
+
+ const GlyphIndex lastGlyphIndex = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs - 1u;
+ const CharacterIndex lastCharacterIndexPlusOne = *( layoutParameters.glyphsToCharactersBuffer + lastGlyphIndex ) + *( layoutParameters.charactersPerGlyphBuffer + lastGlyphIndex );
+
+ for( CharacterIndex index = startCharacterIndex; index < lastCharacterIndexPlusOne; ++index )
+ {
+ if( TextAbstraction::LINE_MUST_BREAK == *( layoutParameters.lineBreakInfoBuffer + index ) )
+ {
+ ++numberOfParagraphs;
+ }
+ }
+
+ return numberOfParagraphs;
+}
+
} //namespace
/**
* @note This method lais out text as it were left to right. At this point is not possible to reorder the line
* because the number of characters of the line is not known (one of the responsabilities of this method
* is calculate that). Due to glyph's 'x' bearing, width and advance, when right to left or mixed right to left
- * and left to right text is laid out, it can be small differences in the line length. One solution is to
+ * and left to right text is laid-out, it can be small differences in the line length. One solution is to
* reorder and re-lay out the text after this method and add or remove one extra glyph if needed. However,
* this method calculates which are the first and last glyphs of the line (the ones that causes the
* differences). This is a good point to check if there is problems with the text exceeding the boundaries
bool oneWordLaidOut = false;
+ const GlyphIndex lastGlyphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
- glyphIndex < parameters.totalNumberOfGlyphs;
+ glyphIndex < lastGlyphPlusOne;
++glyphIndex )
{
DALI_LOG_INFO( gLogFilter, Debug::Verbose, " glyph index : %d\n", glyphIndex );
{
DALI_LOG_INFO( gLogFilter, Debug::Verbose, " Break the word by character\n" );
- // The word's with doesn't fit in the control's with. It needs to be split by character.
+ // The word doesn't fit in the control's width. It needs to be split by character.
if( tmpLineLayout.numberOfGlyphs > 0u )
{
tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
( TextAbstraction::WORD_BREAK == wordBreakInfo ) )
{
oneWordLaidOut = true;
- DALI_LOG_INFO( gLogFilter, Debug::Verbose, " One word laid out\n" );
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, " One word laid-out\n" );
// Current glyph is the last one of the current word.
// Add the temporal layout to the current one.
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
}
+ /**
+ * @brief Calculates the vertical offset to add to the new laid-out glyphs.
+ *
+ * @pre @p lineIndex must be between 0 and the number of lines (both inclusive).
+ *
+ * @param[in] lines The previously laid-out lines.
+ * @param[in] lineIndex Index to the line where the new laid-out lines are inserted.
+ *
+ * @return The vertical offset of the lines starting from the beginning to the line @p lineIndex.
+ */
+ float SetParagraphOffset( const Vector<LineRun>& lines,
+ LineIndex lineIndex )
+ {
+ float offset = 0.f;
+
+ for( Vector<LineRun>::ConstIterator it = lines.Begin(),
+ endIt = lines.Begin() + lineIndex;
+ it != endIt;
+ ++it )
+ {
+ const LineRun& line = *it;
+
+ offset += line.ascender + -line.descender;
+ }
+
+ return offset;
+ }
+
void SetGlyphPositions( const GlyphInfo* const glyphsBuffer,
Length numberOfGlyphs,
float penY,
}
}
+ /**
+ * @brief Resizes the line buffer.
+ *
+ * @param[in,out] lines The vector of lines. Used when the layout is created from scratch.
+ * @param[in,out] newLines The vector of lines used instead of @p lines when the layout is updated.
+ * @param[in,out] linesCapacity The capacity of the vector (either lines or newLines).
+ * @param[in] updateCurrentBuffer Whether the layout is updated.
+ *
+ * @return Pointer to either lines or newLines.
+ */
+ LineRun* ResizeLinesBuffer( Vector<LineRun>& lines,
+ Vector<LineRun>& newLines,
+ Length& linesCapacity,
+ bool updateCurrentBuffer )
+ {
+ LineRun* linesBuffer = NULL;
+ // Reserve more space for the next lines.
+ linesCapacity *= 2u;
+ if( updateCurrentBuffer )
+ {
+ newLines.Resize( linesCapacity );
+ linesBuffer = newLines.Begin();
+ }
+ else
+ {
+ lines.Resize( linesCapacity );
+ linesBuffer = lines.Begin();
+ }
+
+ return linesBuffer;
+ }
+
+ /**
+ * Ellipsis a line if it exceeds the width's of the bounding box.
+ *
+ * @param[in] layoutParameters The parameters needed to layout the text.
+ * @param[in] layout The line layout.
+ * @param[in,out] layoutSize The text's layout size.
+ * @param[in,out] linesBuffer Pointer to the line's buffer.
+ * @param[in,out] glyphPositionsBuffer Pointer to the position's buffer.
+ * @param[in,out] numberOfLines The number of laid-out lines.
+ * @param[in] penY The vertical layout position.
+ * @param[in] currentParagraphDirection The current paragraph's direction.
+ *
+ * return Whether the line is ellipsized.
+ */
+ bool EllipsisLine( const LayoutParameters& layoutParameters,
+ const LineLayout& layout,
+ Size& layoutSize,
+ LineRun* linesBuffer,
+ Vector2* glyphPositionsBuffer,
+ Length& numberOfLines,
+ float penY,
+ CharacterDirection currentParagraphDirection )
+ {
+ const bool ellipsis = ( ( penY - layout.descender > layoutParameters.boundingBox.height ) ||
+ ( ( mLayout == SINGLE_LINE_BOX ) &&
+ ( layout.extraBearing + layout.length + layout.extraWidth > layoutParameters.boundingBox.width ) ) );
+
+ if( ellipsis )
+ {
+ // Do not layout more lines if ellipsis is enabled.
+
+ // The last line needs to be completely filled with characters.
+ // Part of a word may be used.
+
+ LineRun* lineRun = NULL;
+ LineLayout ellipsisLayout;
+ if( 0u != numberOfLines )
+ {
+ // Get the last line and layout it again with the 'completelyFill' flag to true.
+ lineRun = linesBuffer + ( numberOfLines - 1u );
+
+ penY -= layout.ascender - lineRun->descender;
+
+ ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
+ }
+ else
+ {
+ // At least there is space reserved for one line.
+ lineRun = linesBuffer;
+
+ lineRun->glyphRun.glyphIndex = 0u;
+ ellipsisLayout.glyphIndex = 0u;
+
+ ++numberOfLines;
+ }
+
+ GetLineLayoutForBox( layoutParameters,
+ ellipsisLayout,
+ currentParagraphDirection,
+ true );
+
+ lineRun->glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
+ lineRun->characterRun.characterIndex = ellipsisLayout.characterIndex;
+ lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
+ lineRun->width = ellipsisLayout.length;
+ lineRun->extraLength = ( ellipsisLayout.wsLengthEndOfLine > 0.f ) ? ellipsisLayout.wsLengthEndOfLine - ellipsisLayout.extraWidth : 0.f;
+ lineRun->ascender = ellipsisLayout.ascender;
+ lineRun->descender = ellipsisLayout.descender;
+ lineRun->direction = !RTL;
+ lineRun->ellipsis = true;
+
+ layoutSize.width = layoutParameters.boundingBox.width;
+ layoutSize.height += ( lineRun->ascender + -lineRun->descender );
+
+ SetGlyphPositions( layoutParameters.glyphsBuffer + lineRun->glyphRun.glyphIndex,
+ ellipsisLayout.numberOfGlyphs,
+ penY,
+ glyphPositionsBuffer + lineRun->glyphRun.glyphIndex - layoutParameters.startGlyphIndex );
+ }
+
+ return ellipsis;
+ }
+
+ /**
+ * @brief Updates the text layout with a new laid-out line.
+ *
+ * @param[in] layoutParameters The parameters needed to layout the text.
+ * @param[in] layout The line layout.
+ * @param[in,out] layoutSize The text's layout size.
+ * @param[in,out] linesBuffer Pointer to the line's buffer.
+ * @param[in] index Index to the vector of glyphs.
+ * @param[in,out] numberOfLines The number of laid-out lines.
+ * @param[in] isLastLine Whether the laid-out line is the last one.
+ */
+ void UpdateTextLayout( const LayoutParameters& layoutParameters,
+ const LineLayout& layout,
+ Size& layoutSize,
+ LineRun* linesBuffer,
+ GlyphIndex index,
+ Length& numberOfLines,
+ bool isLastLine )
+ {
+ LineRun& lineRun = *( linesBuffer + numberOfLines );
+ ++numberOfLines;
+
+ lineRun.glyphRun.glyphIndex = index;
+ lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
+ lineRun.characterRun.characterIndex = layout.characterIndex;
+ lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
+ if( isLastLine && !layoutParameters.isLastNewParagraph )
+ {
+ const float width = layout.extraBearing + layout.length + layout.extraWidth + layout.wsLengthEndOfLine;
+ if( MULTI_LINE_BOX == mLayout )
+ {
+ lineRun.width = ( width > layoutParameters.boundingBox.width ) ? layoutParameters.boundingBox.width : width;
+ }
+ else
+ {
+ lineRun.width = width;
+ }
+
+ lineRun.extraLength = 0.f;
+ }
+ else
+ {
+ lineRun.width = layout.extraBearing + layout.length + layout.extraWidth;
+ lineRun.extraLength = ( layout.wsLengthEndOfLine > 0.f ) ? layout.wsLengthEndOfLine - layout.extraWidth : 0.f;
+ }
+ lineRun.ascender = layout.ascender;
+ lineRun.descender = layout.descender;
+ lineRun.direction = !RTL;
+ lineRun.ellipsis = false;
+
+ // Update the actual size.
+ if( lineRun.width > layoutSize.width )
+ {
+ layoutSize.width = lineRun.width;
+ }
+
+ layoutSize.height += ( lineRun.ascender + -lineRun.descender );
+ }
+
+ /**
+ * @brief Updates the text layout with the last laid-out line.
+ *
+ * @param[in] layoutParameters The parameters needed to layout the text.
+ * @param[in] layout The line layout.
+ * @param[in,out] layoutSize The text's layout size.
+ * @param[in,out] linesBuffer Pointer to the line's buffer.
+ * @param[in] index Index to the vector of glyphs.
+ * @param[in,out] numberOfLines The number of laid-out lines.
+ */
+ void UpdateTextLayout( const LayoutParameters& layoutParameters,
+ const LineLayout& layout,
+ Size& layoutSize,
+ LineRun* linesBuffer,
+ GlyphIndex index,
+ Length& numberOfLines )
+ {
+ // Need to add a new line with no characters but with height to increase the layoutSize.height
+ const GlyphInfo& glyphInfo = *( layoutParameters.glyphsBuffer + layoutParameters.totalNumberOfGlyphs - 1u );
+
+ Text::FontMetrics fontMetrics;
+ mMetrics->GetFontMetrics( glyphInfo.fontId, fontMetrics );
+
+ LineRun& lineRun = *( linesBuffer + numberOfLines );
+ ++numberOfLines;
+
+ lineRun.glyphRun.glyphIndex = index + layout.numberOfGlyphs;
+ lineRun.glyphRun.numberOfGlyphs = 0u;
+ lineRun.characterRun.characterIndex = layout.characterIndex + layout.numberOfCharacters;
+ lineRun.characterRun.numberOfCharacters = 0u;
+ lineRun.width = 0.f;
+ lineRun.ascender = fontMetrics.ascender;
+ lineRun.descender = fontMetrics.descender;
+ lineRun.extraLength = 0.f;
+ lineRun.alignmentOffset = 0.f;
+ lineRun.direction = !RTL;
+ lineRun.ellipsis = false;
+
+ layoutSize.height += ( lineRun.ascender + -lineRun.descender );
+ }
+
+ /**
+ * @brief Updates the text's layout size adding the size of the previously laid-out lines.
+ *
+ * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
+ * @param[in,out] layoutSize The text's layout size.
+ */
+ void UpdateLayoutSize( const Vector<LineRun>& lines,
+ Size& layoutSize )
+ {
+ for( Vector<LineRun>::ConstIterator it = lines.Begin(),
+ endIt = lines.End();
+ it != endIt;
+ ++it )
+ {
+ const LineRun& line = *it;
+
+ if( line.width > layoutSize.width )
+ {
+ layoutSize.width = line.width;
+ }
+
+ layoutSize.height += ( line.ascender + -line.descender );
+ }
+ }
+
+ /**
+ * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
+ *
+ * @param[in] layoutParameters The parameters needed to layout the text.
+ * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
+ * @param[in] characterOffset The offset to be added to the runs of characters.
+ * @param[in] glyphOffset The offset to be added to the runs of glyphs.
+ */
+ void UpdateLineIndexOffsets( const LayoutParameters& layoutParameters,
+ Vector<LineRun>& lines,
+ Length characterOffset,
+ Length glyphOffset )
+ {
+ // Update the glyph and character runs.
+ for( Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
+ endIt = lines.End();
+ it != endIt;
+ ++it )
+ {
+ LineRun& line = *it;
+
+ line.glyphRun.glyphIndex = glyphOffset;
+ line.characterRun.characterIndex = characterOffset;
+
+ glyphOffset += line.glyphRun.numberOfGlyphs;
+ characterOffset += line.characterRun.numberOfCharacters;
+ }
+ }
+
bool LayoutText( const LayoutParameters& layoutParameters,
Vector<Vector2>& glyphPositions,
Vector<LineRun>& lines,
- Size& actualSize )
+ Size& layoutSize )
{
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->LayoutText\n" );
DALI_LOG_INFO( gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height );
+ if( 0u == layoutParameters.numberOfGlyphs )
+ {
+ // Nothing to do if there are no glyphs to layout.
+ return false;
+ }
+
// Set the first paragraph's direction.
CharacterDirection paragraphDirection = ( NULL != layoutParameters.characterDirectionBuffer ) ? *layoutParameters.characterDirectionBuffer : !RTL;
- float penY = 0.f;
- for( GlyphIndex index = 0u; index < layoutParameters.totalNumberOfGlyphs; )
+ // Whether the layout is being updated or set from scratch.
+ const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < layoutParameters.totalNumberOfGlyphs;
+
+ Vector2* glyphPositionsBuffer = NULL;
+ Vector<Vector2> newGlyphPositions;
+
+ LineRun* linesBuffer = NULL;
+ Vector<LineRun> newLines;
+
+ // Estimate the number of lines.
+ // TODO: In a next patch the paragraphs are properly managed and this can be removed.
+ Length linesCapacity = CountParagraphs( layoutParameters );
+ Length numberOfLines = 0u;
+
+ if( updateCurrentBuffer )
+ {
+ newGlyphPositions.Resize( layoutParameters.numberOfGlyphs );
+ glyphPositionsBuffer = newGlyphPositions.Begin();
+
+ newLines.Resize( linesCapacity );
+ linesBuffer = newLines.Begin();
+ }
+ else
+ {
+ glyphPositionsBuffer = glyphPositions.Begin();
+
+ lines.Resize( linesCapacity );
+ linesBuffer = lines.Begin();
+ }
+
+ float penY = SetParagraphOffset( lines,
+ layoutParameters.startLineIndex );
+
+ const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
+ for( GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne; )
{
CharacterDirection currentParagraphDirection = paragraphDirection;
{
// The width is too small and no characters are laid-out.
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n" );
+
+ lines.Resize( numberOfLines );
return false;
}
penY += layout.ascender;
DALI_LOG_INFO( gLogFilter, Debug::Verbose, " pen y %f\n", penY );
- if( mEllipsisEnabled &&
- ( ( penY - layout.descender > layoutParameters.boundingBox.height ) ||
- ( ( mLayout == SINGLE_LINE_BOX ) &&
- ( layout.extraBearing + layout.length + layout.extraWidth > layoutParameters.boundingBox.width ) ) ) )
- {
- // Do not layout more lines if ellipsis is enabled.
-
- // The last line needs to be completely filled with characters.
- // Part of a word may be used.
-
- const Length numberOfLines = lines.Count();
-
- LineRun lineRun;
- LineLayout ellipsisLayout;
- if( 0u != numberOfLines )
- {
- // Get the last line and layout it again with the 'completelyFill' flag to true.
- lineRun = *( lines.Begin() + ( numberOfLines - 1u ) );
- penY -= layout.ascender - lineRun.descender;
-
- ellipsisLayout.glyphIndex = lineRun.glyphRun.glyphIndex;
- }
- else
- {
- lineRun.glyphRun.glyphIndex = 0u;
- ellipsisLayout.glyphIndex = 0u;
- }
-
- GetLineLayoutForBox( layoutParameters,
- ellipsisLayout,
- currentParagraphDirection,
- true );
-
- lineRun.glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
- lineRun.characterRun.characterIndex = ellipsisLayout.characterIndex;
- lineRun.characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
- lineRun.width = ellipsisLayout.length;
- lineRun.extraLength = ( ellipsisLayout.wsLengthEndOfLine > 0.f ) ? ellipsisLayout.wsLengthEndOfLine - ellipsisLayout.extraWidth : 0.f;
- lineRun.ascender = ellipsisLayout.ascender;
- lineRun.descender = ellipsisLayout.descender;
- lineRun.direction = !RTL;
- lineRun.ellipsis = true;
-
- actualSize.width = layoutParameters.boundingBox.width;
- actualSize.height += ( lineRun.ascender + -lineRun.descender );
-
- SetGlyphPositions( layoutParameters.glyphsBuffer + lineRun.glyphRun.glyphIndex,
- ellipsisLayout.numberOfGlyphs,
- penY,
- glyphPositions.Begin() + lineRun.glyphRun.glyphIndex );
-
- if( 0u != numberOfLines )
- {
- // Set the last line with the ellipsis layout.
- *( lines.Begin() + ( numberOfLines - 1u ) ) = lineRun;
- }
- else
- {
- // Push the line.
- lines.PushBack( lineRun );
- }
+ bool ellipsis = false;
+ if( mEllipsisEnabled )
+ {
+ // Does the ellipsis of the last line.
+ ellipsis = EllipsisLine( layoutParameters,
+ layout,
+ layoutSize,
+ linesBuffer,
+ glyphPositionsBuffer,
+ numberOfLines,
+ penY,
+ currentParagraphDirection );
+ }
+ if( ellipsis )
+ {
+ // No more lines to layout.
break;
}
else
{
+ // Whether the last line has been laid-out.
const bool isLastLine = index + layout.numberOfGlyphs == layoutParameters.totalNumberOfGlyphs;
- LineRun lineRun;
- lineRun.glyphRun.glyphIndex = index;
- lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
- lineRun.characterRun.characterIndex = layout.characterIndex;
- lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
- if( isLastLine && !layoutParameters.isLastNewParagraph )
+ if( numberOfLines == linesCapacity )
{
- const float width = layout.extraBearing + layout.length + layout.extraWidth + layout.wsLengthEndOfLine;
- if( MULTI_LINE_BOX == mLayout )
- {
- lineRun.width = ( width > layoutParameters.boundingBox.width ) ? layoutParameters.boundingBox.width : width;
- }
- else
- {
- lineRun.width = width;
- }
-
- lineRun.extraLength = 0.f;
+ // Reserve more space for the next lines.
+ linesBuffer = ResizeLinesBuffer( lines,
+ newLines,
+ linesCapacity,
+ updateCurrentBuffer );
}
- else
- {
- lineRun.width = layout.extraBearing + layout.length + layout.extraWidth;
- lineRun.extraLength = ( layout.wsLengthEndOfLine > 0.f ) ? layout.wsLengthEndOfLine - layout.extraWidth : 0.f;
- }
- lineRun.ascender = layout.ascender;
- lineRun.descender = layout.descender;
- lineRun.direction = !RTL;
- lineRun.ellipsis = false;
- lines.PushBack( lineRun );
+ // Updates the current text's layout with the line's layout.
+ UpdateTextLayout( layoutParameters,
+ layout,
+ layoutSize,
+ linesBuffer,
+ index,
+ numberOfLines,
+ isLastLine );
- // Update the actual size.
- if( lineRun.width > actualSize.width )
+ if( isLastLine &&
+ layoutParameters.isLastNewParagraph &&
+ ( mLayout == MULTI_LINE_BOX ) )
{
- actualSize.width = lineRun.width;
- }
+ // The last character of the text is a new paragraph character.
+ // An extra line with no characters is added to increase the text's height
+ // in order to place the cursor.
+
+ if( numberOfLines == linesCapacity )
+ {
+ // Reserve more space for the next lines.
+ linesBuffer = ResizeLinesBuffer( lines,
+ newLines,
+ linesCapacity,
+ updateCurrentBuffer );
+ }
- actualSize.height += ( lineRun.ascender + -lineRun.descender );
+ UpdateTextLayout( layoutParameters,
+ layout,
+ layoutSize,
+ linesBuffer,
+ index,
+ numberOfLines );
+ } // whether to add a last line.
+ // Sets the positions of the glyphs.
SetGlyphPositions( layoutParameters.glyphsBuffer + index,
layout.numberOfGlyphs,
penY,
- glyphPositions.Begin() + index );
+ glyphPositionsBuffer + index - layoutParameters.startGlyphIndex );
+ // Updates the vertical pen's position.
penY += -layout.descender;
// Increase the glyph index.
index += layout.numberOfGlyphs;
- if( isLastLine &&
- layoutParameters.isLastNewParagraph &&
- ( mLayout == MULTI_LINE_BOX ) )
- {
- // Need to add a new line with no characters but with height to increase the actualSize.height
- const GlyphInfo& glyphInfo = *( layoutParameters.glyphsBuffer + layoutParameters.totalNumberOfGlyphs - 1u );
-
- Text::FontMetrics fontMetrics;
- mMetrics->GetFontMetrics( glyphInfo.fontId, fontMetrics );
-
- LineRun lineRun;
- lineRun.glyphRun.glyphIndex = 0u;
- lineRun.glyphRun.numberOfGlyphs = 0u;
- lineRun.characterRun.characterIndex = 0u;
- lineRun.characterRun.numberOfCharacters = 0u;
- lineRun.width = 0.f;
- lineRun.ascender = fontMetrics.ascender;
- lineRun.descender = fontMetrics.descender;
- lineRun.extraLength = 0.f;
- lineRun.alignmentOffset = 0.f;
- lineRun.direction = !RTL;
- lineRun.ellipsis = false;
-
- actualSize.height += ( lineRun.ascender + -lineRun.descender );
-
- lines.PushBack( lineRun );
- }
- }
+ } // no ellipsis
} // end for() traversing glyphs.
+ if( updateCurrentBuffer )
+ {
+ glyphPositions.Insert( glyphPositions.Begin() + layoutParameters.startGlyphIndex,
+ newGlyphPositions.Begin(),
+ newGlyphPositions.End() );
+
+ newLines.Resize( numberOfLines );
+
+ // Current text's layout size adds only the newly laid-out lines.
+ // Updates the layout size with the previously laid-out lines.
+ UpdateLayoutSize( lines,
+ layoutSize );
+
+ if( 0u != newLines.Count() )
+ {
+ const LineRun& lastLine = *( newLines.End() - 1u );
+
+ const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
+ const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
+
+ // Update the indices of the runs before the new laid-out lines are inserted.
+ UpdateLineIndexOffsets( layoutParameters,
+ lines,
+ characterOffset,
+ glyphOffset );
+
+ // Insert the lines.
+ lines.Insert( lines.Begin() + layoutParameters.startLineIndex,
+ newLines.Begin(),
+ newLines.End() );
+ }
+ }
+ else
+ {
+ lines.Resize( numberOfLines );
+ }
+
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText\n\n" );
return true;
}
void ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
+ CharacterIndex startIndex,
+ Length numberOfCharacters,
Vector<Vector2>& glyphPositions )
{
+ const CharacterIndex lastCharacterIndex = startIndex + numberOfCharacters;
+
// Traverses the paragraphs with right to left characters.
for( LineIndex lineIndex = 0u; lineIndex < layoutParameters.numberOfBidirectionalInfoRuns; ++lineIndex )
{
const BidirectionalLineInfoRun& bidiLine = *( layoutParameters.lineBidirectionalInfoRunsBuffer + lineIndex );
+ if( startIndex >= bidiLine.characterRun.characterIndex + bidiLine.characterRun.numberOfCharacters )
+ {
+ // Do not reorder the line if it has been already reordered.
+ continue;
+ }
+
+ if( bidiLine.characterRun.characterIndex >= lastCharacterIndex )
+ {
+ // Do not reorder the lines after the last requested character.
+ break;
+ }
+
const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *bidiLine.visualToLogicalMap;
const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) );
}
}
- void Align( const Size& layoutSize,
+ void Align( const Size& size,
+ CharacterIndex startIndex,
+ Length numberOfCharacters,
Vector<LineRun>& lines )
{
- // Traverse all lines and align the glyphs.
+ const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
+ // Traverse all lines and align the glyphs.
for( Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
it != endIt;
++it )
{
LineRun& line = *it;
- const bool isLastLine = lines.End() == it + 1u;
- // Calculate the alignment offset accordingly with the align option,
- // the box width, line length, and the paragraphs direction.
- CalculateHorizontalAlignment( layoutSize.width,
- line,
- isLastLine );
+ if( line.characterRun.characterIndex < startIndex )
+ {
+ // Do not align lines which have already been aligned.
+ continue;
+ }
+
+ if( line.characterRun.characterIndex >= lastCharacterPlusOne )
+ {
+ // Do not align lines beyond the last laid-out character.
+ break;
+ }
+
+ // Calculate the line's alignment offset accordingly with the align option,
+ // the box width, line length, and the paragraph's direction.
+ CalculateHorizontalAlignment( size.width,
+ line );
}
}
void CalculateHorizontalAlignment( float boxWidth,
- LineRun& line,
- bool isLastLine )
+ LineRun& line )
{
line.alignmentOffset = 0.f;
const bool isRTL = RTL == line.direction;
float lineLength = line.width;
HorizontalAlignment alignment = mHorizontalAlignment;
- if( isRTL &&
- ( HORIZONTAL_ALIGN_CENTER != alignment ) )
+ if( isRTL )
{
- if( HORIZONTAL_ALIGN_BEGIN == alignment )
- {
- alignment = HORIZONTAL_ALIGN_END;
- }
- else
+ // Swap the alignment type if the line is right to left.
+ switch( alignment )
{
- alignment = HORIZONTAL_ALIGN_BEGIN;
+ case HORIZONTAL_ALIGN_BEGIN:
+ {
+ alignment = HORIZONTAL_ALIGN_END;
+ break;
+ }
+ case HORIZONTAL_ALIGN_CENTER:
+ {
+ // Nothing to do.
+ break;
+ }
+ case HORIZONTAL_ALIGN_END:
+ {
+ alignment = HORIZONTAL_ALIGN_BEGIN;
+ break;
+ }
}
}
+ // Calculate the horizontal line offset.
switch( alignment )
{
case HORIZONTAL_ALIGN_BEGIN:
{
// 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
line.alignmentOffset -= line.extraLength;
-
- if( isLastLine )
- {
- line.alignmentOffset += std::min( line.extraLength, boxWidth - lineLength );
- }
}
break;
}
case HORIZONTAL_ALIGN_CENTER:
{
- if( isLastLine && !isRTL )
- {
- // Add the length of the white saces at the end of the line.
- lineLength += line.extraLength;
- if( lineLength > boxWidth )
- {
- // The line's length is longer than the box's width.
- // Set the line's offset to 0 and nothing else to do.
- line.alignmentOffset = 0.f;
- break;
- }
- }
-
line.alignmentOffset = 0.5f * ( boxWidth - lineLength );
if( isRTL )
{
line.alignmentOffset -= line.extraLength;
-
- if( isLastLine )
- {
- line.alignmentOffset += 0.5f * std::min( line.extraLength, boxWidth - lineLength );
- }
}
line.alignmentOffset = floorf( line.alignmentOffset ); // try to avoid pixel alignment.
}
case HORIZONTAL_ALIGN_END:
{
- if( isLastLine && !isRTL )
- {
- lineLength += line.extraLength;
- if( lineLength > boxWidth )
- {
- line.alignmentOffset = 0.f;
- break;
- }
- }
-
if( isRTL )
{
lineLength += line.extraLength;
mImpl->mLayout = layout;
}
-unsigned int LayoutEngine::GetLayout() const
+LayoutEngine::Layout LayoutEngine::GetLayout() const
{
return mImpl->mLayout;
}
bool LayoutEngine::LayoutText( const LayoutParameters& layoutParameters,
Vector<Vector2>& glyphPositions,
Vector<LineRun>& lines,
- Size& actualSize )
+ Size& layoutSize )
{
return mImpl->LayoutText( layoutParameters,
glyphPositions,
lines,
- actualSize );
+ layoutSize );
}
void LayoutEngine::ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
+ CharacterIndex startIndex,
+ Length numberOfCharacters,
Vector<Vector2>& glyphPositions )
{
mImpl->ReLayoutRightToLeftLines( layoutParameters,
+ startIndex,
+ numberOfCharacters,
glyphPositions );
}
-void LayoutEngine::Align( const Size& layoutSize,
+void LayoutEngine::Align( const Size& size,
+ CharacterIndex startIndex,
+ Length numberOfCharacters,
Vector<LineRun>& lines )
{
- mImpl->Align( layoutSize,
+ mImpl->Align( size,
+ startIndex,
+ numberOfCharacters,
lines );
}
*
* @return The required layout.
*/
- unsigned int GetLayout() const;
+ Layout GetLayout() const;
/**
* @brief Enable or disable the text ellipsis.
* @param[in] layoutParameters The parameters needed to layout the text.
* @param[out] glyphPositions The positions of all the glyphs.
* @param[out] lines The laid-out lines.
- * @param[out] actualSize The size of the text after it has been laid-out.
+ * @param[out] layoutSize The size of the text after it has been laid-out.
*
* @return \e true if the text has been re-laid-out. \e false means the given width is too small to layout even a single character.
*/
bool LayoutText( const LayoutParameters& layoutParameters,
Vector<Vector2>& glyphPositions,
Vector<LineRun>& lines,
- Size& actualSize );
+ Size& layoutSize );
/**
* @brief Re-lays out those lines with right to left characters.
* It doesn't change the phisical position of the glyphs in the model but sets their new position.
*
* @param[in] layoutParameters The parameters needed to layout the text.
+ * @param[in] startIndex Character index of the line from where the lines are reordered.
+ * @param[in] numberOfCharacters The number of characters.
* @param[in,out] glyphPositions The positions of all the glyphs.
*/
void ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
+ CharacterIndex startIndex,
+ Length numberOfCharacters,
Vector<Vector2>& glyphPositions );
/**
* @brief Aligns the laid out lines.
*
- * @param[in] layoutSize The size of the laid out the text.
+ * @param[in] size The size of the container where the text is laid-out.
+ * @param[in] startIndex Character index of the line from where the lines are aligned.
+ * @param[in] numberOfCharacters The number of characters.
* @param[in,out] lines The laid-out lines.
*/
- void Align( const Size& layoutSize,
+ void Align( const Size& size,
+ CharacterIndex startIndex,
+ Length numberOfCharacters,
Vector<LineRun>& lines );
private:
* @param[in] textBuffer The text buffer.
* @param[in] lineBreakInfoBuffer The line break info.
* @param[in] wordBreakInfoBuffer The word break info.
- * @param[in] totalNumberOfGlyphs The number of glyphs.
- * @param[in] glyphsBuffer A vector with glyphs.
+ * @param[in] characterDirectionBuffer Vector with the direction of each character.
+ * @param[in] glyphsBuffer Vector with glyphs.
* @param[in] glyphsToCharactersBuffer Vector with indices pointing the first character of each glyph.
* @param[in] charactersPerGlyphBuffer Vector with the number of characters that forms each glyph.
+ * @param[in] charactersToGlyphsBuffer Vector with indices pointing the first glyph of each character.
+ * @param[in] glyphsPerCharacterBuffer Vector with the number of glyphs shaped from the character.
+ * @param[in] totalNumberOfGlyphs The number of glyphs.
*/
LayoutParameters( const Vector2& boundingBox,
const Character* const textBuffer,
const LineBreakInfo* const lineBreakInfoBuffer,
const WordBreakInfo* const wordBreakInfoBuffer,
const CharacterDirection* const characterDirectionBuffer,
- Length totalNumberOfGlyphs,
const GlyphInfo* const glyphsBuffer,
const CharacterIndex* const glyphsToCharactersBuffer,
- const Length* const charactersPerGlyphBuffer )
+ const Length* const charactersPerGlyphBuffer,
+ const GlyphIndex* const charactersToGlyphsBuffer,
+ const Length* const glyphsPerCharacterBuffer,
+ Length totalNumberOfGlyphs )
: boundingBox( boundingBox ),
textBuffer( textBuffer ),
lineBreakInfoBuffer( lineBreakInfoBuffer ),
wordBreakInfoBuffer( wordBreakInfoBuffer ),
characterDirectionBuffer( characterDirectionBuffer ),
- totalNumberOfGlyphs( totalNumberOfGlyphs ),
glyphsBuffer( glyphsBuffer ),
glyphsToCharactersBuffer( glyphsToCharactersBuffer ),
charactersPerGlyphBuffer( charactersPerGlyphBuffer ),
- charactersToGlyphsBuffer( NULL ),
- glyphsPerCharacterBuffer( NULL ),
+ charactersToGlyphsBuffer( charactersToGlyphsBuffer ),
+ glyphsPerCharacterBuffer( glyphsPerCharacterBuffer ),
lineBidirectionalInfoRunsBuffer( NULL ),
numberOfBidirectionalInfoRuns( 0u ),
+ startGlyphIndex( 0u ),
+ numberOfGlyphs( 0u ),
+ totalNumberOfGlyphs( totalNumberOfGlyphs ),
+ startLineIndex( 0u ),
isLastNewParagraph( false )
{}
- Vector2 boundingBox;
- const Character* const textBuffer;
- const LineBreakInfo* const lineBreakInfoBuffer;
- const WordBreakInfo* const wordBreakInfoBuffer;
- const CharacterDirection* const characterDirectionBuffer;
- Length totalNumberOfGlyphs;
- const GlyphInfo* const glyphsBuffer;
- const CharacterIndex* const glyphsToCharactersBuffer;
- const Length* const charactersPerGlyphBuffer;
- GlyphIndex* charactersToGlyphsBuffer; ///< The character to glyph conversion table.
- Length* glyphsPerCharacterBuffer; ///< The number of glyphs per character.
+ Vector2 boundingBox; ///< The size of the box containing the text.
+ const Character* const textBuffer; ///< The text buffer.
+ const LineBreakInfo* const lineBreakInfoBuffer; ///< The line break info.
+ const WordBreakInfo* const wordBreakInfoBuffer; ///< The word break info.
+ const CharacterDirection* const characterDirectionBuffer; ///< Vector with the direction of each character.
+ const GlyphInfo* const glyphsBuffer; ///< Vector with glyphs.
+ const CharacterIndex* const glyphsToCharactersBuffer; ///< Vector with indices pointing the first character of each glyph.
+ const Length* const charactersPerGlyphBuffer; ///< Vector with the number of characters that forms each glyph.
+ const GlyphIndex* const charactersToGlyphsBuffer; ///< Vector with indices pointing the first glyph of each character.
+ const Length* const glyphsPerCharacterBuffer; ///< Vector with the number of glyphs shaped from the character.
BidirectionalLineInfoRun* lineBidirectionalInfoRunsBuffer; ///< Bidirectional conversion tables per line.
Length numberOfBidirectionalInfoRuns; ///< The number of lines with bidirectional info.
+ GlyphIndex startGlyphIndex; ///< Index to the first glyph to layout.
+ Length numberOfGlyphs; ///< The number of glyphs to layout.
+ Length totalNumberOfGlyphs; ///< The number of glyphs.
+ LineIndex startLineIndex; ///< The line index where to insert the new lines.
bool isLastNewParagraph; ///< Whether the last character is a new paragraph character.
};
mDepth = depth;
const Vector2& actorSize( view.GetControlSize() );
- const Vector2 halfActorSize( actorSize * 0.5f );
+ const Vector2& textSize( view.GetLayoutSize() );
+ const Vector2 halfTextSize( textSize * 0.5f );
const Vector2& shadowOffset( view.GetShadowOffset() );
const Vector4& shadowColor( view.GetShadowColor() );
const bool underlineEnabled( view.IsUnderlineEnabled() );
}
// Move the origin (0,0) of the mesh to the center of the actor
- const Vector2 position = *( positionsBuffer + i ) - halfActorSize;
+ const Vector2 position = *( positionsBuffer + i ) - halfTextSize;
// Generate mesh data for this quad, plugging in our supplied position
AtlasManager::Mesh2D newMesh;
{
MeshRecord& meshRecord = *it;
- Actor actor = CreateMeshActor( meshRecord, actorSize );
+ Actor actor = CreateMeshActor( meshRecord, textSize );
// Create an effect if necessary
if( style == STYLE_DROP_SHADOW )
containerActor.SetParentOrigin( ParentOrigin::CENTER );
containerActor.SetSize( actorSize );
- Actor shadowActor = CreateMeshActor( meshRecord, actorSize );
+ Actor shadowActor = CreateMeshActor( meshRecord, textSize );
#if defined(DEBUG_ENABLED)
shadowActor.SetName( "Text Shadow renderable actor" );
#endif
Dali::Renderer renderer( shadowActor.GetRendererAt( 0 ) );
int depthIndex = renderer.GetProperty<int>(Dali::Renderer::Property::DEPTH_INDEX);
renderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, depthIndex - 1 );
- shadowActor.SetParentOrigin( ParentOrigin::CENTER );
- shadowActor.SetSize( actorSize );
containerActor.Add( shadowActor );
containerActor.Add( actor );
actor = containerActor;
Actor CreateMeshActor( const MeshRecord& meshRecord, const Vector2& actorSize )
{
- PropertyBuffer quadVertices = PropertyBuffer::New( mQuadVertexFormat, meshRecord.mMesh.mVertices.Size() );
- PropertyBuffer quadIndices = PropertyBuffer::New( mQuadIndexFormat, meshRecord.mMesh.mIndices.Size() );
- quadVertices.SetData( const_cast< AtlasManager::Vertex2D* >( &meshRecord.mMesh.mVertices[ 0 ] ) );
- quadIndices.SetData( const_cast< unsigned int* >( &meshRecord.mMesh.mIndices[ 0 ] ) );
+ PropertyBuffer quadVertices = PropertyBuffer::New( mQuadVertexFormat );
+ PropertyBuffer quadIndices = PropertyBuffer::New( mQuadIndexFormat );
+ quadVertices.SetData( const_cast< AtlasManager::Vertex2D* >( &meshRecord.mMesh.mVertices[ 0 ] ), meshRecord.mMesh.mVertices.Size() );
+ quadIndices.SetData( const_cast< unsigned int* >( &meshRecord.mMesh.mIndices[ 0 ] ), meshRecord.mMesh.mIndices.Size() );
Geometry quadGeometry = Geometry::New();
quadGeometry.AddVertexBuffer( quadVertices );
actor.SetName( "Text renderable actor" );
#endif
actor.AddRenderer( renderer );
- actor.SetParentOrigin( ParentOrigin::CENTER ); // Keep all of the origins aligned
+
+ // Keep all of the origins aligned
+ actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
+ actor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
+
actor.SetSize( actorSize );
actor.RegisterProperty("uOffset", Vector2::ZERO );
return actor;
if( Gesture::Started == state ||
Gesture::Continuing == state )
{
- const Vector2& actualSize = mVisualModel->GetActualSize();
+ const Vector2& actualSize = mVisualModel->GetLayoutSize();
const Vector2 currentScroll = mEventData->mScrollPosition;
if( mEventData->mHorizontalScrollingEnabled )
else if( HANDLE_SCROLLING == state )
{
const float xSpeed = event.p2.mFloat;
- const Vector2& actualSize = mVisualModel->GetActualSize();
+ const Vector2& actualSize = mVisualModel->GetLayoutSize();
const Vector2 currentScrollPosition = mEventData->mScrollPosition;
mEventData->mScrollPosition.x += xSpeed;
cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + secondaryPosition.x + ( isCurrentRightToLeft ? 0.f : glyphMetrics.advance );
cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
}
+
+ if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
+ {
+ // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
+
+ // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
+ // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
+
+ if( 0.f > cursorInfo.primaryPosition.x )
+ {
+ cursorInfo.primaryPosition.x = 0.f;
+ }
+
+ const float edgeWidth = mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
+ if( cursorInfo.primaryPosition.x > edgeWidth )
+ {
+ cursorInfo.primaryPosition.x = edgeWidth;
+ }
+ }
}
CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
const float positionEnd = position.x + ( mEventData->mDecorator ? mEventData->mDecorator->GetCursorWidth() : 0.f );
// Transform the position to decorator coords.
- const float offset = mEventData->mScrollPosition.x + mAlignmentOffset.x;
+ const float alignment = IsShowingRealText() ? mAlignmentOffset.x : 0.f;
+ const float offset = mEventData->mScrollPosition.x + alignment;
const float decoratorPositionBegin = position.x + offset;
const float decoratorPositionEnd = positionEnd + offset;
if( decoratorPositionBegin < 0.f )
{
- mEventData->mScrollPosition.x = -position.x - mAlignmentOffset.x;
+ mEventData->mScrollPosition.x = -position.x - alignment;
}
else if( decoratorPositionEnd > mVisualModel->mControlSize.width )
{
- mEventData->mScrollPosition.x = mVisualModel->mControlSize.width - positionEnd - mAlignmentOffset.x;
+ mEventData->mScrollPosition.x = mVisualModel->mControlSize.width - positionEnd - alignment;
}
}
// Calculate the offset to match the cursor position before the character was deleted.
mEventData->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x - mAlignmentOffset.x;
- ClampHorizontalScroll( mVisualModel->GetActualSize() );
+ ClampHorizontalScroll( mVisualModel->GetLayoutSize() );
}
void Controller::Impl::RequestRelayout()
*
* @pre mEventData must not be NULL. (there is a text-input or selection capabilities).
*
- * @param[in] position A position in decorator coords.
+ * @param[in] position A position in text coords.
*
* This method is called after inserting text, moving the cursor with the grab handle or the keypad,
* or moving the selection handles.
*
* This method is called after deleting text.
*/
- void ScrollTextToMatchCursor( const CursorInfo& cursorInfo);
+ void ScrollTextToMatchCursor( const CursorInfo& cursorInfo );
ControlInterface& mControlInterface; ///< Reference to the text controller.
LogicalModelPtr mLogicalModel; ///< Pointer to the logical model.
const unsigned int POINTS_PER_INCH = 72;
const std::string EMPTY_STRING("");
-const unsigned int ZERO = 0u;
float ConvertToEven( float value )
{
}
else
{
- layoutSize = mImpl->mVisualModel->GetActualSize();
+ layoutSize = mImpl->mVisualModel->GetLayoutSize();
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetHeightForWidth cached %f\n", layoutSize.height );
}
return glyphsRemoved;
}
+ // Whether a new size has been set.
const bool newSize = ( size != mImpl->mVisualModel->mControlSize );
if( newSize )
if( 0u == numberOfGlyphs )
{
+ if( UPDATE_ACTUAL_SIZE & operations )
+ {
+ mImpl->mVisualModel->SetLayoutSize( Size::ZERO );
+ }
+
// Nothing else to do if there is no glyphs.
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout no glyphs, view updated true\n" );
return true;
const Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
const Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
const Character* const textBuffer = mImpl->mLogicalModel->mText.Begin();
+ const Vector<GlyphIndex>& charactersToGlyph = mImpl->mVisualModel->mCharactersToGlyph;
+ const Vector<Length>& glyphsPerCharacter = mImpl->mVisualModel->mGlyphsPerCharacter;
// Set the layout parameters.
LayoutParameters layoutParameters( size,
lineBreakInfo.Begin(),
wordBreakInfo.Begin(),
( 0u != characterDirection.Count() ) ? characterDirection.Begin() : NULL,
- numberOfGlyphs,
glyphs.Begin(),
glyphsToCharactersMap.Begin(),
- charactersPerGlyph.Begin() );
+ charactersPerGlyph.Begin(),
+ charactersToGlyph.Begin(),
+ glyphsPerCharacter.Begin(),
+ numberOfGlyphs );
// The laid-out lines.
// It's not possible to know in how many lines the text is going to be laid-out,
// Whether the last character is a new paragraph character.
layoutParameters.isLastNewParagraph = TextAbstraction::IsNewParagraph( *( textBuffer + ( mImpl->mLogicalModel->mText.Count() - 1u ) ) );
+ // The initial glyph and the number of glyphs to layout.
+ layoutParameters.startGlyphIndex = 0u;
+ layoutParameters.numberOfGlyphs = numberOfGlyphs;
+
// Update the visual model.
viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
glyphPositions,
// Get the lines
const Length numberOfLines = mImpl->mVisualModel->mLines.Count();
const CharacterIndex startIndex = 0u;
- Length requestedNumberOfCharacters = mImpl->mLogicalModel->mText.Count();
+ const Length requestedNumberOfCharacters = mImpl->mLogicalModel->mText.Count();
// Reorder the lines.
bidirectionalLineInfo.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
startIndex,
requestedNumberOfCharacters );
- // Get the character to glyph conversion table and set into the layout.
- layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
- // Get the glyphs per character table and set into the layout.
- layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
-
// Re-layout the text. Reorder those lines with right to left characters.
mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
+ startIndex,
+ requestedNumberOfCharacters,
glyphPositions );
// Free the allocated memory used to store the conversion table in the bidirectional line info run.
// Sets the actual size.
if( UPDATE_ACTUAL_SIZE & operations )
{
- mImpl->mVisualModel->SetActualSize( layoutSize );
+ mImpl->mVisualModel->SetLayoutSize( layoutSize );
}
} // view updated
}
else
{
- layoutSize = mImpl->mVisualModel->GetActualSize();
+ layoutSize = mImpl->mVisualModel->GetLayoutSize();
}
if( ALIGN & operations )
// The laid-out lines.
Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
- mImpl->mLayoutEngine.Align( layoutSize,
+ const CharacterIndex startIndex = 0u;
+ const Length requestedNumberOfCharacters = mImpl->mLogicalModel->mText.Count();
+
+ mImpl->mLayoutEngine.Align( size,
+ startIndex,
+ requestedNumberOfCharacters,
lines );
viewUpdated = true;
return mImpl->mLayoutEngine.GetVerticalAlignment();
}
-void Controller::CalculateTextAlignment( const Size& size )
+void Controller::CalculateTextAlignment( const Size& controlSize )
{
- // Get the direction of the first character.
- const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
+ Size layoutSize = mImpl->mVisualModel->GetLayoutSize();
- Size actualSize = mImpl->mVisualModel->GetActualSize();
- if( fabsf( actualSize.height ) < Math::MACHINE_EPSILON_1000 )
+ if( fabsf( layoutSize.height ) < Math::MACHINE_EPSILON_1000 )
{
// Get the line height of the default font.
- actualSize.height = mImpl->GetDefaultFontLineHeight();
+ layoutSize.height = mImpl->GetDefaultFontLineHeight();
}
- // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
- LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
- if( firstParagraphDirection &&
- ( LayoutEngine::HORIZONTAL_ALIGN_CENTER != horizontalAlignment ) )
+ if( LayoutEngine::SINGLE_LINE_BOX == mImpl->mLayoutEngine.GetLayout() )
{
- if( LayoutEngine::HORIZONTAL_ALIGN_BEGIN == horizontalAlignment )
- {
- horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
- }
- else
- {
- horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
- }
- }
+ // Get the direction of the first character.
+ const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
- switch( horizontalAlignment )
- {
- case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
+ // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
+ LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
+ if( firstParagraphDirection )
{
- mImpl->mAlignmentOffset.x = 0.f;
- break;
- }
- case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
- {
- mImpl->mAlignmentOffset.x = floorf( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
- break;
+ switch( horizontalAlignment )
+ {
+ case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
+ {
+ horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
+ break;
+ }
+ case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
+ {
+ // Nothing to do.
+ break;
+ }
+ case LayoutEngine::HORIZONTAL_ALIGN_END:
+ {
+ horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
+ break;
+ }
+ }
}
- case LayoutEngine::HORIZONTAL_ALIGN_END:
+
+ switch( horizontalAlignment )
{
- mImpl->mAlignmentOffset.x = size.width - actualSize.width;
- break;
+ case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
+ {
+ mImpl->mAlignmentOffset.x = 0.f;
+ break;
+ }
+ case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
+ {
+ mImpl->mAlignmentOffset.x = floorf( 0.5f * ( controlSize.width - layoutSize.width ) ); // try to avoid pixel alignment.
+ break;
+ }
+ case LayoutEngine::HORIZONTAL_ALIGN_END:
+ {
+ mImpl->mAlignmentOffset.x = controlSize.width - layoutSize.width;
+ break;
+ }
}
}
}
case LayoutEngine::VERTICAL_ALIGN_CENTER:
{
- mImpl->mAlignmentOffset.y = floorf( 0.5f * ( size.height - actualSize.height ) ); // try to avoid pixel alignment.
+ mImpl->mAlignmentOffset.y = floorf( 0.5f * ( controlSize.height - layoutSize.height ) ); // try to avoid pixel alignment.
break;
}
case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
{
- mImpl->mAlignmentOffset.y = size.height - actualSize.height;
+ mImpl->mAlignmentOffset.y = controlSize.height - layoutSize.height;
break;
}
}
const Length numberOfCharactersInModel = mImpl->mLogicalModel->mText.Count();
// Restrict new text to fit within Maximum characters setting
- Length maxSizeOfNewText = std::min ( ( mImpl->mMaximumNumberOfCharacters - numberOfCharactersInModel ), characterCount );
+ Length maxSizeOfNewText = std::min( ( mImpl->mMaximumNumberOfCharacters - numberOfCharactersInModel ), characterCount );
maxLengthReached = ( characterCount > maxSizeOfNewText );
// The cursor position.
uint32_t& endRemoveIndex )
{
T* runsBuffer = runs.Begin();
+ T* run = runsBuffer;
const Length length = runs.Count();
- for( Length index = 0u; index < length; ++index )
+ Length index = 0u;
+ for( index = 0u; index < length; ++index )
{
- T* run = ( runsBuffer + index );
-
if( ( run->characterRun.characterIndex <= endIndex ) &&
( startIndex < run->characterRun.characterIndex + run->characterRun.numberOfCharacters ) )
{
startRemoveIndex = index;
break;
}
+
+ ++run;
}
- for( Length index = startRemoveIndex; index < length; ++index )
+ run = ( runsBuffer + startRemoveIndex );
+ for( index = startRemoveIndex; index < length; ++index )
{
- T* run = ( runsBuffer + index );
-
- if( ( run->characterRun.characterIndex <= endIndex ) &&
- ( startIndex < run->characterRun.characterIndex + run->characterRun.numberOfCharacters ) )
- {
- // Update the index to the last run to be removed.
- endRemoveIndex = index + 1u;
- }
- else
+ if( ( run->characterRun.characterIndex > endIndex ) ||
+ ( startIndex >= run->characterRun.characterIndex + run->characterRun.numberOfCharacters ) )
{
// Run found. Nothing else to do.
break;
}
+ ++run;
}
+ endRemoveIndex = index;
// The number of characters to remove.
const Length numberOfCharactersRemoved = 1u + endIndex - startIndex;
// Update the character index of the next runs.
+ run = runsBuffer;
for( Length index = 0u; index < length; ++index )
{
- T* run = ( runsBuffer + index );
-
if( run->characterRun.characterIndex > startIndex )
{
run->characterRun.characterIndex -= numberOfCharactersRemoved;
}
+
+ ++run;
}
}
}
}
+/**
+ * @brief Clears the runs starting from the given glyph index.
+ *
+ * @param[in] startIndex The starting glyph index used to remove runs.
+ * @param[in] endIndex The ending glyph index used to remove runs.
+ * @param[in,out] runs The text's runs.
+ * @param[out] startRemoveIndex The index to the first run to be removed.
+ * @param[out] endRemoveIndex The index to the last run to be removed.
+ */
+template< typename T >
+void ClearGlyphRuns( GlyphIndex startIndex,
+ GlyphIndex endIndex,
+ Vector<T>& runs,
+ uint32_t& startRemoveIndex,
+ uint32_t& endRemoveIndex )
+{
+ T* runsBuffer = runs.Begin();
+ T* run = runsBuffer;
+
+ const Length length = runs.Count();
+ Length index = 0u;
+ for( index = 0u; index < length; ++index )
+ {
+ if( ( run->glyphRun.glyphIndex <= endIndex ) &&
+ ( startIndex < run->glyphRun.glyphIndex + run->glyphRun.numberOfGlyphs ) )
+ {
+ // Run found.
+
+ // Set the index to the first run to be removed.
+ startRemoveIndex = index;
+ break;
+ }
+ ++run;
+ }
+
+ run = ( runsBuffer + startRemoveIndex );
+ for( index = startRemoveIndex; index < length; ++index )
+ {
+ if( ( run->glyphRun.glyphIndex > endIndex ) ||
+ ( startIndex >= run->glyphRun.glyphIndex + run->glyphRun.numberOfGlyphs ) )
+ {
+ // Run found. Nothing else to do.
+ }
+
+ ++run;
+ }
+ endRemoveIndex = index;
+
+ // The number of glyphs to remove.
+ const Length numberOfGlyphsRemoved = 1u + endIndex - startIndex;
+
+ // Update the glyph index of the next runs.
+ run = runsBuffer;
+ for( Length index = 0u; index < length; ++index )
+ {
+
+ if( run->glyphRun.glyphIndex > startIndex )
+ {
+ run->glyphRun.glyphIndex -= numberOfGlyphsRemoved;
+ }
+ }
+}
+
+/**
+ * @brief Clears the runs starting from the given glyph index.
+ *
+ * @param[in] startIndex The starting glyph index used to remove runs.
+ * @param[in] endIndex The ending glyph index used to remove runs.
+ * @param[in,out] runs The text's runs.
+ */
+template< typename T >
+void ClearGlyphRuns( GlyphIndex startIndex,
+ GlyphIndex endIndex,
+ Vector<T>& runs )
+{
+ uint32_t startRemoveIndex = runs.Count();
+ uint32_t endRemoveIndex = startRemoveIndex;
+ ClearGlyphRuns( startIndex,
+ endIndex,
+ runs,
+ startRemoveIndex,
+ endRemoveIndex );
+
+ // Remove all remaining runs.
+ T* runBuffer = runs.Begin();
+ runs.Erase( runBuffer + startRemoveIndex, runBuffer + endRemoveIndex );
+}
+
} // namespace Text
} // namespace Toolkit
virtual const Vector2& GetControlSize() const = 0;
/**
+ * @brief Retrieves the text's layout size.
+ *
+ * @return The text's size. Note that this may be larger than the control size,
+ * in the case where text is scrolling/clipped.
+ */
+ virtual const Vector2& GetLayoutSize() const = 0;
+
+ /**
* Retrieves the number of glyphs.
*
* @return The number of glyphs.
return Vector2::ZERO;
}
+const Vector2& View::GetLayoutSize() const
+{
+ if ( mImpl->mVisualModel )
+ {
+ return mImpl->mVisualModel->GetLayoutSize();
+ }
+
+ return Vector2::ZERO;
+}
+
Length View::GetNumberOfGlyphs() const
{
if( mImpl->mVisualModel )
virtual const Vector2& GetControlSize() const;
/**
+ * @copydoc Dali::Toolkit::Text::ViewInterface::GetLayoutSize()
+ */
+ virtual const Vector2& GetLayoutSize() const;
+
+ /**
* @copydoc Dali::Toolkit::Text::ViewInterface::GetNumberOfGlyphs()
*/
virtual Length GetNumberOfGlyphs() const;
return mNaturalSize;
}
-void VisualModel::SetActualSize( const Vector2& size )
+void VisualModel::SetLayoutSize( const Vector2& size )
{
- mActualSize = size;
+ mLayoutSize = size;
}
-const Vector2& VisualModel::GetActualSize() const
+const Vector2& VisualModel::GetLayoutSize() const
{
- return mActualSize;
+ return mLayoutSize;
}
void VisualModel::SetTextColor( const Vector4& textColor )
mShadowOffset( Vector2::ZERO ),
mUnderlineHeight( 0.0f ),
mNaturalSize(),
- mActualSize(),
+ mLayoutSize(),
mCachedLineIndex( 0u ),
mUnderlineEnabled( false ),
mUnderlineColorSet( false )
const Vector2& GetNaturalSize() const;
/**
- * @brief Sets the text's actual size after it has been laid out.
+ * @brief Sets the text's layout size.
*
* @param[in] size The text's size.
*/
- void SetActualSize( const Vector2& size );
+ void SetLayoutSize( const Vector2& size );
/**
- * @brief Retrieves the text's actual size after it has been laid out.
+ * @brief Retrieves the text's layout size.
*
* @return The text's size.
*/
- const Vector2& GetActualSize() const;
+ const Vector2& GetLayoutSize() const;
/**
* @brief Set the text's color
private:
Size mNaturalSize; ///< Size of the text with no line wrapping.
- Size mActualSize; ///< Size of the laid-out text considering the layout properties set.
+ Size mLayoutSize; ///< Size of the laid-out text considering the layout properties set.
// Caches to increase performance in some consecutive operations.
LineIndex mCachedLineIndex; ///< Used to increase performance in consecutive calls to GetLineOfGlyph() or GetLineOfCharacter() with consecutive glyphs or characters.
Property::Map quadVertexFormat;
quadVertexFormat["aPosition"] = Property::VECTOR2;
- PropertyBuffer quadVertices = PropertyBuffer::New( quadVertexFormat, 4 );
- quadVertices.SetData(quadVertexData);
+ PropertyBuffer quadVertices = PropertyBuffer::New( quadVertexFormat );
+ quadVertices.SetData( quadVertexData, 4 );
// Create the geometry object
Geometry geometry = Geometry::New();
bool blurUserImage = false);
/**
+ * @DEPRECATED_1_1.28 Use Actor::Add(Actor) instead
* @brief Adds a child Actor to this Actor.
* @SINCE_1_0.0
* @param [in] child The child.
void Add(Actor child);
/**
+ * @DEPRECATED_1_1.28 Use Actor::Remove(Actor) instead
* @brief Removes a child Actor from this Actor.
*
* If the actor was not a child of this actor, this is a no-op.
const unsigned int TOOLKIT_MAJOR_VERSION = 1;
const unsigned int TOOLKIT_MINOR_VERSION = 1;
-const unsigned int TOOLKIT_MICRO_VERSION = 26;
+const unsigned int TOOLKIT_MICRO_VERSION = 28;
const char * const TOOLKIT_BUILD_DATE = __DATE__ " " __TIME__;
#ifdef DEBUG_ENABLED
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="Layer_1"
+ x="0px"
+ y="0px"
+ viewBox="0 0 306.90988 416.79828"
+ xml:space="preserve"
+ inkscape:version="0.48.4 r9939"
+ width="100%"
+ height="100%"
+ sodipodi:docname="Kid1.svg"><metadata
+ id="metadata185"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs183" /><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="640"
+ inkscape:window-height="480"
+ id="namedview181"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:zoom="0.28032166"
+ inkscape:cx="152.20465"
+ inkscape:cy="184.87264"
+ inkscape:window-x="75"
+ inkscape:window-y="34"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="Layer_1" /><path
+ style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="m 192.28065,343.14765 c 0,0 -4.5,75.5 37.5,71 42,-4.5 11.5,-75.5 11.5,-75.5 l -49,4.5 z"
+ id="path3"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#666666;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="m 229.78065,414.14765 c 24.024,-2.574 24.325,-26.903 20.184,-47.259 l -56.117,5.805 c 3.172,20.614 12.003,44.018 35.933,41.454 z"
+ id="path5"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="m 113.60765,343.14765 c 0,0 -23.087995,69 14,73 37.088,4 48,-73 48,-73 h -62 z"
+ id="path7"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#666666;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="m 169.15465,370.76265 -60.625,-7.118 c -3.745,20.685 -4.689,49.94 19.078,52.503 21.993,2.372 34.78,-23.738 41.547,-45.385 z"
+ id="path9"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="m 117.10565,371.73465 39.895,2.68 c 0,0 -7.065,27.803 -23.798,24.788 -16.733,-3.015 -16.097,-27.468 -16.097,-27.468 z"
+ id="path11"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="m 206.27865,381.65765 35.399,-5.164 c 0,0 2.531,26.234 -10.49,26.241 -18.543,0.011 -24.909,-21.077 -24.909,-21.077 z"
+ id="path13"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="m 121.49165,210.92765 c -24.560995,5.263 -52.631995,5.263 -52.631995,5.263 l 5.354,60.772 25.859995,-5.032 c 0,0 64.328,-18.197 62.574,-34.16 -2.104,-19.138 -16.595,-32.106 -41.156,-26.843 z"
+ id="path15"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#c1272d;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="m 231.14065,228.64765 c -10.143,-34.251 -65.789,-51.053 -65.789,-51.053 0,0 -19.298,28.07 -43.86,33.333 -1.209,0.259 -2.428,0.502 -3.65,0.736 l -20.035995,60.709 2.267995,-0.441 -16.300995,80.54 184.714995,-4.731 c 0,10e-4 -25.566,-79.313 -37.347,-119.093 z"
+ id="path17"
+ inkscape:connector-curvature="0" /><line
+ style="fill:#666666;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ x1="110.72466"
+ y1="311.29565"
+ x2="104.40065"
+ y2="352.30963"
+ id="line19" /><polygon
+ style="fill:#666666;stroke:#000000;stroke-miterlimit:10"
+ points="390.823,464.081 392.798,470.374 237.225,476.667 237.225,468.729 "
+ id="polygon21"
+ transform="translate(-145.43535,-189.01935)" /><linearGradient
+ id="SVGID_1_"
+ gradientUnits="userSpaceOnUse"
+ x1="317.35651"
+ y1="383.66669"
+ x2="317.35651"
+ y2="199.5519"
+ gradientTransform="translate(-145.43535,-189.01935)"><stop
+ offset="0"
+ style="stop-color:#402A04"
+ id="stop24" /><stop
+ offset="1"
+ style="stop-color:#7F5100"
+ id="stop26" /></linearGradient><path
+ style="fill:url(#SVGID_1_);stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="M 172.97165,196.01565 H 63.267655 c 0,0 -54.6040005,-192.1050008 109.704995,-188.5960008 164.309,3.5089998 106.414,188.5960008 106.414,188.5960008 h -106.415 z"
+ id="path28"
+ inkscape:connector-curvature="0" /><linearGradient
+ id="SVGID_2_"
+ gradientUnits="userSpaceOnUse"
+ x1="317.6879"
+ y1="298.16669"
+ x2="317.6879"
+ y2="399.66791"
+ gradientTransform="translate(-145.43535,-189.01935)"><stop
+ offset="0.0019"
+ style="stop-color:#FFCA94"
+ id="stop31" /><stop
+ offset="1"
+ style="stop-color:#E2A380"
+ id="stop33" /></linearGradient><path
+ style="fill:url(#SVGID_2_);stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="m 93.798655,65.647649 c 0,0 -48.605,145.281001 77.692995,145.281001 126.298,0 80.202,-145.281001 80.202,-145.281001 H 93.798655 z"
+ id="path35"
+ inkscape:connector-curvature="0" /><linearGradient
+ id="SVGID_3_"
+ gradientUnits="userSpaceOnUse"
+ x1="316.33801"
+ y1="383.66669"
+ x2="316.33801"
+ y2="199.5519"
+ gradientTransform="translate(-145.43535,-189.01935)"><stop
+ offset="0"
+ style="stop-color:#402A04"
+ id="stop38" /><stop
+ offset="1"
+ style="stop-color:#7F5100"
+ id="stop40" /></linearGradient><path
+ style="fill:url(#SVGID_3_);stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="m 108.82665,29.573649 c 0,0 -37.535995,22.859 -34.316995,82.359001 h 62.920995 c 0,0 7.255,-35.965001 7.255,-31.786001 0,4.179 3.509,31.786001 3.509,31.786001 h 48.246 V 87.833649 l 8.772,24.099001 h 62.281 c 0,0 0,-53.553001 -28.63,-82.359001 -28.072,0.877 -130.037,0 -130.037,0 z"
+ id="path42"
+ inkscape:connector-curvature="0" /><linearGradient
+ id="SVGID_4_"
+ gradientUnits="userSpaceOnUse"
+ x1="195.0862"
+ y1="372.3811"
+ x2="217.894"
+ y2="516.99213"
+ gradientTransform="translate(-145.43535,-189.01935)"><stop
+ offset="0"
+ style="stop-color:#006178"
+ id="stop45" /><stop
+ offset="1"
+ style="stop-color:#00495C"
+ id="stop47" /></linearGradient><path
+ style="fill:url(#SVGID_4_);stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="m 111.64765,331.69965 -70.387995,13.196 c -6.487,1.216 -12.79,-3.096 -14.006,-9.583 L 1.8536545,199.83065 c -1.21599995,-6.487 3.096,-12.79 9.5830005,-14.006 l 70.388,-13.196 c 6.487,-1.216 12.79,3.096 14.006,9.583 l 25.399995,135.482 c 1.217,6.487 -3.096,12.79 -9.583,14.006 z"
+ id="path49"
+ inkscape:connector-curvature="0" /><linearGradient
+ id="SVGID_5_"
+ gradientUnits="userSpaceOnUse"
+ x1="197.17931"
+ y1="393.6586"
+ x2="218.5313"
+ y2="505.75641"><stop
+ offset="0"
+ style="stop-color:#6BBBD0"
+ id="stop52" /><stop
+ offset="1"
+ style="stop-color:#ACCCD4"
+ id="stop54" /></linearGradient><polygon
+ style="fill:url(#SVGID_5_);stroke:#000000;stroke-miterlimit:10"
+ points="179.836,514.167 159.336,399.948 236.336,387.667 254.336,498.167 "
+ id="polygon56"
+ transform="translate(-145.43535,-189.01935)" /><linearGradient
+ id="SVGID_6_"
+ gradientUnits="userSpaceOnUse"
+ x1="207.9456"
+ y1="466.41"
+ x2="201.4173"
+ y2="399.4946"><stop
+ offset="0"
+ style="stop-color:#FBCA51"
+ id="stop59" /><stop
+ offset="1"
+ style="stop-color:#FFECBF"
+ id="stop61" /></linearGradient><ellipse
+ style="fill:url(#SVGID_6_);stroke:#000000;stroke-miterlimit:10"
+ cx="205.43201"
+ cy="440.64801"
+ rx="28.959"
+ ry="28.080999"
+ id="ellipse63"
+ sodipodi:cx="205.43201"
+ sodipodi:cy="440.64801"
+ sodipodi:rx="28.959"
+ sodipodi:ry="28.080999"
+ transform="translate(-145.43535,-189.01935)" /><linearGradient
+ id="SVGID_7_"
+ gradientUnits="userSpaceOnUse"
+ x1="190.9269"
+ y1="427.96649"
+ x2="192.52139"
+ y2="436.33749"><stop
+ offset="0"
+ style="stop-color:#6BBBD0"
+ id="stop66" /><stop
+ offset="1"
+ style="stop-color:#ACCCD4"
+ id="stop68" /></linearGradient><line
+ style="fill:url(#SVGID_7_);stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ x1="44.983662"
+ y1="238.97565"
+ x2="47.595661"
+ y2="247.29865"
+ id="line70" /><linearGradient
+ id="SVGID_8_"
+ gradientUnits="userSpaceOnUse"
+ x1="211.39799"
+ y1="423.5397"
+ x2="213.01559"
+ y2="432.0325"><stop
+ offset="0"
+ style="stop-color:#6BBBD0"
+ id="stop73" /><stop
+ offset="1"
+ style="stop-color:#ACCCD4"
+ id="stop75" /></linearGradient><line
+ style="fill:url(#SVGID_8_);stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ x1="66.200653"
+ y1="234.40564"
+ x2="67.343658"
+ y2="243.13666"
+ id="line77" /><path
+ style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="m 47.595655,260.51865 c 0,0 0.653,13.22 15.831,9.14 15.178,-4.08 11.229,-15.994 11.229,-15.994"
+ id="path79"
+ inkscape:connector-curvature="0" /><linearGradient
+ id="SVGID_9_"
+ gradientUnits="userSpaceOnUse"
+ x1="247.2587"
+ y1="416.16669"
+ x2="247.2587"
+ y2="464.19009"
+ gradientTransform="translate(-145.43535,-189.01935)"><stop
+ offset="0.0019"
+ style="stop-color:#FFCA94"
+ id="stop82" /><stop
+ offset="1"
+ style="stop-color:#E2A380"
+ id="stop84" /></linearGradient><path
+ style="fill:url(#SVGID_9_);stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="m 125.03565,225.81865 c 0,0 -54.844995,-4.046 -54.589995,5.391 0.255,9.437 21.359,3.668 20.849,8.259 -0.51,4.591 -10.173,3.424 -8.763,9.166 1.335,5.436 9.891,0.337 10.485,4.399 0.594,4.062 -7.944,4.738 -7.2,9.587 0.744,4.849 9.437,1.275 9.947,4.081 0.51,2.806 -5.959,4.099 -3.664,7.415 2.296,3.316 25.481995,4.532 36.820995,-0.784 11.531,-5.406 -3.885,-47.514 -3.885,-47.514 z"
+ id="path86"
+ inkscape:connector-curvature="0" /><path
+ style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="m 108.82665,147.75265 c 0,0 0.464,-21.64 21.464,-21.14 21,0.5 22,21.14 22,21.14"
+ id="path88"
+ inkscape:connector-curvature="0" /><path
+ style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="m 194.78965,147.75265 c 0,0 5.5,-23.64 25,-23.64 19.5,0 21.571,21 21.571,21"
+ id="path90"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="M 154.28965,177.59465"
+ id="path92"
+ inkscape:connector-curvature="0" /><path
+ style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="m 154.28965,177.59465 c 0,0 0.715,18.789 16.607,18.421 15.893,-0.368 12.893,-18.421 12.893,-18.421"
+ id="path94"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="m 228.94765,254.13365 c -2.38,6.629 -9.604,20.319 -44.933,26.513 -37.922,6.649 -56.122,-3.11 -56.122,-3.11 l -10.787,-59.329 c 0,0 37.312,5.696 53.792,4.439 16.48,-1.257 33.346,-5.764 33.346,-5.764 0,0 18.324,-4.366 23.904,8.074 5.579,12.439 3.538,21.552 0.8,29.177 z"
+ id="path96"
+ inkscape:connector-curvature="0" /><linearGradient
+ id="SVGID_10_"
+ gradientUnits="userSpaceOnUse"
+ x1="316.32571"
+ y1="383.66669"
+ x2="316.32571"
+ y2="199.5519"><stop
+ offset="0"
+ style="stop-color:#402A04"
+ id="stop99" /><stop
+ offset="1"
+ style="stop-color:#7F5100"
+ id="stop101" /></linearGradient><polygon
+ style="fill:url(#SVGID_10_)"
+ points="408.426,250.667 394.198,218.593 358.725,207.167 262.541,210.167 237.725,225.136 224.225,247.667 "
+ id="polygon103"
+ transform="translate(-145.43535,-189.01935)" /><polygon
+ style="fill:#c1272d;stroke:#000000;stroke-miterlimit:10"
+ points="399.167,290.713 428.616,269.855 394.694,225.136 365.225,225.136 354.191,247.667 404.225,254.667 "
+ id="polygon105"
+ transform="translate(-145.43535,-189.01935)" /><path
+ style="fill:#c1272d;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="m 245.96865,0.64764917 c -6.811,-2.50199997 -41.178,27.49999983 -36.678,36.49999983 4.5,9 39,21.5 39,21.5 l 13.209,-17.558 c -10e-4,0 -4.031,-36.2179998 -15.531,-40.44199983 z"
+ id="path107"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#c1272d;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="m 272.19765,55.502649 -13.408,15.144 c 0,0 9,36.500001 17.5,35.500001 8.5,-1 32,-21.720001 30,-31.408001 -2,-9.688 -34.092,-19.236 -34.092,-19.236 z"
+ id="path109"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#c1272d;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="m 243.78965,58.647649 c 0,0 13.418,-23.5 17.709,-22.5 4.291,1 15.093,16.043 15.791,19.356 0.698,3.313 -13,20.644 -18.5,19.144 -5.5,-1.5 -12.5,-10 -15,-16 z"
+ id="path111"
+ inkscape:connector-curvature="0" /><line
+ style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ x1="215.78966"
+ y1="36.11665"
+ x2="245.96864"
+ y2="54.967648"
+ id="line113" /><line
+ style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ x1="262.99066"
+ y1="73.483643"
+ x2="277.31567"
+ y2="96.647644"
+ id="line115" /><circle
+ style="opacity:0.2;fill:#d86d44"
+ cx="262.54099"
+ cy="357.16699"
+ r="19.249001"
+ id="circle117"
+ sodipodi:cx="262.54099"
+ sodipodi:cy="357.16699"
+ sodipodi:rx="19.249001"
+ sodipodi:ry="19.249001"
+ transform="translate(-145.43535,-189.01935)" /><circle
+ style="opacity:0.2;fill:#d86d44"
+ cx="372.72501"
+ cy="357.16699"
+ r="19.249001"
+ id="circle119"
+ sodipodi:cx="372.72501"
+ sodipodi:cy="357.16699"
+ sodipodi:rx="19.249001"
+ sodipodi:ry="19.249001"
+ transform="translate(-145.43535,-189.01935)" /><path
+ style="fill:#c1272d;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="M 231.14065,228.64765"
+ id="path121"
+ inkscape:connector-curvature="0" /><linearGradient
+ id="SVGID_11_"
+ gradientUnits="userSpaceOnUse"
+ x1="218.8582"
+ y1="511.4505"
+ x2="220.60651"
+ y2="520.62927"
+ gradientTransform="translate(-145.43535,-189.01935)"><stop
+ offset="0"
+ style="stop-color:#006178"
+ id="stop124" /><stop
+ offset="1"
+ style="stop-color:#00495C"
+ id="stop126" /></linearGradient><path
+ style="fill:url(#SVGID_11_);stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="m 61.249655,330.25265 c 0.7,3.23 4.623,2.704 4.623,2.704 0,0 14.196,-2.268 16.856,-2.704 2.66,-0.436 5.413,-2.183 4.802,-5.501 -0.611,-3.318 -2.578,-4.334 -4.977,-4.104 -2.143,0.206 -17.811,4.016 -17.811,4.016 0,0 -4.628,0.35 -3.493,5.589 z"
+ id="path128"
+ inkscape:connector-curvature="0" /><linearGradient
+ id="SVGID_12_"
+ gradientUnits="userSpaceOnUse"
+ x1="196.34351"
+ y1="377.0275"
+ x2="197.17529"
+ y2="381.39441"><stop
+ offset="0"
+ style="stop-color:#006178"
+ id="stop131" /><stop
+ offset="1"
+ style="stop-color:#00495C"
+ id="stop133" /></linearGradient><polygon
+ style="fill:url(#SVGID_12_);stroke:#000000;stroke-miterlimit:10"
+ points="186.437,382.911 186.437,379.506 206.978,374.966 207.302,379.506 "
+ id="polygon135"
+ transform="translate(-145.43535,-189.01935)" /><linearGradient
+ id="SVGID_13_"
+ gradientUnits="userSpaceOnUse"
+ x1="152.85809"
+ y1="432.1676"
+ x2="155.30721"
+ y2="445.02539"
+ gradientTransform="translate(-145.43535,-189.01935)"><stop
+ offset="0.0019"
+ style="stop-color:#FFCA94"
+ id="stop138" /><stop
+ offset="1"
+ style="stop-color:#E2A380"
+ id="stop140" /></linearGradient><path
+ style="fill:url(#SVGID_13_);stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="m 2.5626545,251.52265 c 0.949,7.344 13.9770005,5.843 12.4400005,-2.53 -1.537,-8.373 -13.8540005,-8.404 -12.4400005,2.53 z"
+ id="path142"
+ inkscape:connector-curvature="0" /><linearGradient
+ id="SVGID_14_"
+ gradientUnits="userSpaceOnUse"
+ x1="150.90849"
+ y1="418.1373"
+ x2="153.3576"
+ y2="430.99509"
+ gradientTransform="translate(-145.43535,-189.01935)"><stop
+ offset="0.0019"
+ style="stop-color:#FFCA94"
+ id="stop145" /><stop
+ offset="1"
+ style="stop-color:#E2A380"
+ id="stop147" /></linearGradient><path
+ style="fill:url(#SVGID_14_);stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="m 0.61265455,237.49265 c 0.94899995,7.344 13.97700045,5.843 12.44000045,-2.53 -1.537,-8.373 -13.85300045,-8.404 -12.44000045,2.53 z"
+ id="path149"
+ inkscape:connector-curvature="0" /><linearGradient
+ id="SVGID_15_"
+ gradientUnits="userSpaceOnUse"
+ x1="155.52521"
+ y1="446.19681"
+ x2="157.7338"
+ y2="457.79221"
+ gradientTransform="translate(-145.43535,-189.01935)"><stop
+ offset="0.0019"
+ style="stop-color:#FFCA94"
+ id="stop152" /><stop
+ offset="1"
+ style="stop-color:#E2A380"
+ id="stop154" /></linearGradient><path
+ style="fill:url(#SVGID_15_);stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="m 5.7066545,264.72965 c 0.856,6.623 12.6050005,5.269 11.2180005,-2.281 -1.387,-7.55 -12.4920005,-7.58 -11.2180005,2.281 z"
+ id="path156"
+ inkscape:connector-curvature="0" /><linearGradient
+ id="SVGID_16_"
+ gradientUnits="userSpaceOnUse"
+ x1="153.2876"
+ y1="459.8923"
+ x2="155.15581"
+ y2="469.70059"
+ gradientTransform="translate(-145.43535,-189.01935)"><stop
+ offset="0.0019"
+ style="stop-color:#FFCA94"
+ id="stop159" /><stop
+ offset="1"
+ style="stop-color:#E2A380"
+ id="stop161" /></linearGradient><path
+ style="fill:url(#SVGID_16_);stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ d="m 14.738655,269.81365 c 0,0 -14.45200045,0.644 -13.0900005,6.785 1.362,6.141 15.1240005,2.806 15.1240005,2.806 l -2.034,-9.591 z"
+ id="path163"
+ inkscape:connector-curvature="0" /><line
+ style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ x1="103.19666"
+ y1="134.52966"
+ x2="111.07164"
+ y2="138.28766"
+ id="line165" /><line
+ style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ x1="116.44066"
+ y1="121.91666"
+ x2="119.95366"
+ y2="128.63864"
+ id="line167" /><line
+ style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ x1="231.53465"
+ y1="117.39664"
+ x2="227.96065"
+ y2="125.27765"
+ id="line169" /><line
+ style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ x1="249.61266"
+ y1="127.38266"
+ x2="237.84065"
+ y2="134.52966"
+ id="line171" /><line
+ style="fill:#666666;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ x1="243.72664"
+ y1="311.64764"
+ x2="251.97566"
+ y2="348.16364"
+ id="line173" /><line
+ style="fill:#666666;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ x1="184.01564"
+ y1="310.31366"
+ x2="184.01564"
+ y2="349.90466"
+ id="line175" /><line
+ style="fill:#666666;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ x1="146.45665"
+ y1="311.64764"
+ x2="143.12267"
+ y2="350.95166"
+ id="line177" /><line
+ style="fill:#666666;stroke:#000000;stroke-width:1;stroke-miterlimit:10"
+ x1="218.07564"
+ y1="311.29565"
+ x2="222.05864"
+ y2="348.93066"
+ id="line179" /></svg>
* Dali::Toolkit::ImageView myImageView = ImageView::New( "source-image-url.9.png" );
* @endcode
*
+ * A path to a svg image file:
+ * @code
+ * Dali::Toolkit::ImageView myImageView = ImageView::New( "source-image-url.svg" );
+ * @endcode
+ *
* <h3 class="pg">Loading from a Image handle</h3>
* Dali::Image is an abstract base class with multiple derived classes.
*
+ [Gradient](@ref gradient-renderer)
+ [Image](@ref image-renderer)
+ [N-Patch](@ref n-patch-renderer)
+ + [SVG](@ref svg-renderer)
+ [Border](@ref border-renderer)
Controls can provide properties that allow users to specify the renderer type.
imageUrl : "path-to-image.9.png"
};
~~~
+
+___________________________________________________________________________________________________
+
+## SVG Renderer {#svg-renderer}
+
+Renders a svg image into the control's quad.
+
+<div style="width:300px">
+
+![ ](../assets/img/renderers/svg-renderer.svg)
+
+</div>
+
+<div style="width:300px">
+
+![ ](renderers/svg-renderer.svg)
+
+</div>
+
+### Properties Supported
+
+**RendererType:** "svg"
+
+| Property Name | Type | Required | Description |
+|---------------|:-------:|:--------:|----------------------------------|
+| imageUrl | STRING | Yes | The URL of the SVG image. |
+
+### Usage
+
+~~~{.cpp}
+// C++
+Dali::Toolkit::Control control = Dali::Toolkit::Control::New();
+
+Dali::Property::Map map;
+
+map[ "rendererType" ] = "svg";
+map[ "imageUrl" ] = "path-to-image.svg";
+
+control.SetSize( 200.f, 200.f );
+control.SetProperty( Dali::Toolkit::Control::Property::BACKGROUND, map );
+~~~
+
+~~~{.js}
+// JavaScript
+var control = new dali.Control( "Control" );
+
+control.background =
+{
+ rendererType : "svg",
+ imageUrl : "path-to-image.svg"
+};
+~~~
___________________________________________________________________________________________________
## Border Renderer {#border-renderer}
var pentagonVertexDataArray = new Float32Array(pentagonVertexData.length);
pentagonVertexDataArray.set(pentagonVertexData, 0);
- var pentagonVertices = new dali.PropertyBuffer(pentagonVertexFormat, 5);
- pentagonVertices.setData(pentagonVertexDataArray);
+ var pentagonVertices = new dali.PropertyBuffer(pentagonVertexFormat);
+ pentagonVertices.setData(pentagonVertexDataArray, 5);
var pentacleVertexFormat ={ "aPosition2" : dali.PROPERTY_VECTOR2};
var pentacleVertexDataArray = new Float32Array(pentacleVertexData.length);
pentacleVertexDataArray.set(pentacleVertexData, 0);
- var pentacleVertices = new dali.PropertyBuffer(pentacleVertexFormat, 5);
- pentacleVertices.setData(pentacleVertexDataArray);
+ var pentacleVertices = new dali.PropertyBuffer(pentacleVertexFormat);
+ pentacleVertices.setData(pentacleVertexDataArray, 5);
var indexFormat ={ "indices" : dali.PROPERTY_INTEGER };
var indexDataArray = new Uint32Array(indexData.length);
indexDataArray.set(indexData, 0);
- var indices = new dali.PropertyBuffer(indexFormat, 10);
- indices.setData(indexDataArray);
+ var indices = new dali.PropertyBuffer(indexFormat);
+ indices.setData(indexDataArray, 10);
// Create geometry
var geometry = new dali.Geometry();
var initialPositionVertexDataArray = new Float32Array(initialPositionVertexData.length);
initialPositionVertexDataArray.set(initialPositionVertexData, 0);
- var initialPositionVertices = new dali.PropertyBuffer(initialPositionVertexFormat, 27);
- initialPositionVertices.setData(initialPositionVertexDataArray);
+ var initialPositionVertices = new dali.PropertyBuffer(initialPositionVertexFormat);
+ initialPositionVertices.setData(initialPositionVertexDataArray, 27);
// Create vertex buffer for final positions
var finalPositionVertexFormat = { "aFinalPos" : dali.PROPERTY_VECTOR2 };
var finalPositionVertexDataArray = new Float32Array(finalPositionVertexData.length);
finalPositionVertexDataArray.set(finalPositionVertexData, 0);
- var finalPositionVertices = new dali.PropertyBuffer(finalPositionVertexFormat, 27);
- finalPositionVertices.setData(finalPositionVertexDataArray);
+ var finalPositionVertices = new dali.PropertyBuffer(finalPositionVertexFormat);
+ finalPositionVertices.setData(finalPositionVertexDataArray, 27);
// Create vertex buffer for color
var colorVertexFormat = { "aColor" : dali.PROPERTY_VECTOR3 };
var colorVertexDataArray = new Float32Array(colorVertexData.length);
colorVertexDataArray.set(colorVertexData, 0);
- var colorVertices = new dali.PropertyBuffer(colorVertexFormat, 27);
- colorVertices.setData(colorVertexDataArray);
+ var colorVertices = new dali.PropertyBuffer(colorVertexFormat);
+ colorVertices.setData(colorVertexDataArray, 27);
// Create geometry
var geometry = new dali.Geometry();
var polyhedraVertexDataArray = new Float32Array(polyhedraVertexData.length);
polyhedraVertexDataArray.set(polyhedraVertexData, 0);
- var polyhedraVertices = new dali.PropertyBuffer(polyhedraVertexFormat, numSides);
- polyhedraVertices.setData(polyhedraVertexDataArray);
+ var polyhedraVertices = new dali.PropertyBuffer(polyhedraVertexFormat);
+ polyhedraVertices.setData(polyhedraVertexDataArray, numSides);
// Create geometry
var geometry = new dali.Geometry();
var texturedQuadVertexDataArray = new Float32Array(texturedQuadVertexData.length);
texturedQuadVertexDataArray.set(texturedQuadVertexData, 0);
- var texturedQuadVertices = new dali.PropertyBuffer(texturedQuadVertexFormat, 4);
- texturedQuadVertices.setData(texturedQuadVertexDataArray);
+ var texturedQuadVertices = new dali.PropertyBuffer(texturedQuadVertexFormat);
+ texturedQuadVertices.setData(texturedQuadVertexDataArray, 4);
var indexFormat ={ "indices" : dali.PROPERTY_INTEGER };
var indexDataArray = new Uint32Array(indexData.length);
indexDataArray.set(indexData, 0);
- var indices = new dali.PropertyBuffer(indexFormat, 6);
- indices.setData(indexDataArray);
+ var indices = new dali.PropertyBuffer(indexFormat);
+ indices.setData(indexDataArray, 6);
// Create geometry
var geometry = new dali.Geometry();
Name: dali-toolkit
Summary: The OpenGLES Canvas Core Library Toolkit
-Version: 1.1.26
+Version: 1.1.28
Release: 1
Group: System/Libraries
License: Apache-2.0 and BSD-2-Clause and MIT
struct PropertyBufferParameters
{
PropertyBufferParameters()
- : mSize( 0 )
{
}
PropertyBuffer NewPropertyBuffer()
{
- return PropertyBuffer::New( mBufferFormat,
- mSize);
+ return PropertyBuffer::New( mBufferFormat );
}
Property::Map mBufferFormat;
- std::size_t mSize;
};
} // unnamed space
return PropertyBuffer();
}
- found = false;
- int size = V8Utils::GetIntegerParameter( PARAMETER_1, found, isolate, args, 0);
- if( !found )
- {
- DALI_SCRIPT_EXCEPTION( isolate, "missing buffer size from param 1" );
- return PropertyBuffer();
- }
-
- return PropertyBuffer::New(bufferFormat, static_cast<std::size_t>(size));
+ return PropertyBuffer::New( bufferFormat );
}
/**
* var vertexDataArray = new Float32Array(vertexData.length);
* vertexDataArray.set(vertexData, 0);
*
- * propertyBuffer.setData( vertexDataArray );
+ * propertyBuffer.setData( vertexDataArray, vertexData.length );
*```
*/
void PropertyBufferApi::SetData( const v8::FunctionCallbackInfo< v8::Value >& args )
bool found( false );
void* data = V8Utils::GetArrayBufferViewParameter( PARAMETER_0, found, isolate, args);
- if( !found )
+
+ if( ! found )
{
DALI_SCRIPT_EXCEPTION( isolate, "invalid data parameter" );
}
else
{
- buffer.SetData( data );
+ int size = V8Utils::GetIntegerParameter( PARAMETER_1, found, isolate, args, 0);
+ if( !found )
+ {
+ DALI_SCRIPT_EXCEPTION( isolate, "missing buffer size from param 1" );
+ }
+ else
+ {
+ buffer.SetData( data, size );
+ }
}
}