mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-07-14 15:02:50 +03:00
Merge branch 'develop' into feature/grid-layout
This commit is contained in:
@@ -1,57 +0,0 @@
|
||||
#ifndef EE_GRAPHICS_LINEARGRADIENTDRAWABLE_HPP
|
||||
#define EE_GRAPHICS_LINEARGRADIENTDRAWABLE_HPP
|
||||
|
||||
#include <eepp/graphics/drawable.hpp>
|
||||
#include <eepp/system/color.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace EE { namespace Graphics {
|
||||
|
||||
class EE_API LinearGradientDrawable : public Drawable {
|
||||
public:
|
||||
struct ColorStop {
|
||||
Float position{ 0 }; /* 0.0 to 1.0 */
|
||||
Color color;
|
||||
|
||||
ColorStop() : color( Color::White ) {}
|
||||
ColorStop( Float pos, const Color& col ) : position( pos ), color( col ) {}
|
||||
};
|
||||
|
||||
static LinearGradientDrawable* New();
|
||||
static LinearGradientDrawable* NewRepeating();
|
||||
|
||||
LinearGradientDrawable( Drawable::Type drawableType = LINEARGRADIENT );
|
||||
|
||||
virtual Sizef getSize();
|
||||
|
||||
virtual Sizef getPixelsSize();
|
||||
|
||||
virtual void draw();
|
||||
|
||||
virtual void draw( const Vector2f& position );
|
||||
|
||||
virtual void draw( const Vector2f& position, const Sizef& size );
|
||||
|
||||
virtual bool isStateful() { return false; }
|
||||
|
||||
const std::vector<ColorStop>& getColorStops() const;
|
||||
|
||||
void setColorStops( std::vector<ColorStop> stops );
|
||||
|
||||
Float getAngle() const;
|
||||
|
||||
void setAngle( Float angleDegrees );
|
||||
|
||||
void setSize( const Sizef& size );
|
||||
|
||||
bool isRepeating() const;
|
||||
|
||||
protected:
|
||||
std::vector<ColorStop> mColorStops;
|
||||
Float mAngle{ 180.f }; /* CSS angle: 0=to top, 90=to right, 180=to bottom (default) */
|
||||
Sizef mSize;
|
||||
};
|
||||
|
||||
}} // namespace EE::Graphics
|
||||
|
||||
#endif
|
||||
72
include/eepp/ui/lineargradientdrawable.hpp
Normal file
72
include/eepp/ui/lineargradientdrawable.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#ifndef EE_UI_LINEARGRADIENTDRAWABLE_HPP
|
||||
#define EE_UI_LINEARGRADIENTDRAWABLE_HPP
|
||||
|
||||
#include <eepp/graphics/drawable.hpp>
|
||||
#include <eepp/system/color.hpp>
|
||||
#include <eepp/ui/css/stylesheetlength.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace EE { namespace UI {
|
||||
|
||||
class EE_API LinearGradientDrawable : public Graphics::Drawable {
|
||||
public:
|
||||
using Length = CSS::StyleSheetLength;
|
||||
using GradientUnit = CSS::StyleSheetLength::Unit;
|
||||
|
||||
struct ColorStop {
|
||||
Color color;
|
||||
Float value{ 0 };
|
||||
GradientUnit unit{ CSS::StyleSheetLength::Percentage };
|
||||
|
||||
ColorStop() : color( Color::White ) {}
|
||||
ColorStop( Float val, const Color& col, GradientUnit u = CSS::StyleSheetLength::Percentage ) :
|
||||
value( val ), color( col ), unit( u ) {}
|
||||
|
||||
Float getNormalized( Float gradientLineLength ) const {
|
||||
if ( unit == CSS::StyleSheetLength::Percentage )
|
||||
return eemax( 0.f, value ) / 100.f;
|
||||
if ( gradientLineLength > 0.0001f )
|
||||
return eemax( 0.f, value ) / gradientLineLength;
|
||||
return 0.f;
|
||||
}
|
||||
};
|
||||
|
||||
static LinearGradientDrawable* New();
|
||||
static LinearGradientDrawable* NewRepeating();
|
||||
|
||||
LinearGradientDrawable(
|
||||
Graphics::Drawable::Type drawableType = Graphics::Drawable::LINEARGRADIENT );
|
||||
|
||||
virtual Sizef getSize();
|
||||
|
||||
virtual Sizef getPixelsSize();
|
||||
|
||||
virtual void draw();
|
||||
|
||||
virtual void draw( const Vector2f& position );
|
||||
|
||||
virtual void draw( const Vector2f& position, const Sizef& size );
|
||||
|
||||
virtual bool isStateful() { return false; }
|
||||
|
||||
const std::vector<ColorStop>& getColorStops() const;
|
||||
|
||||
void setColorStops( std::vector<ColorStop> stops );
|
||||
|
||||
Float getAngle() const;
|
||||
|
||||
void setAngle( Float angleDegrees );
|
||||
|
||||
void setSize( const Sizef& size );
|
||||
|
||||
bool isRepeating() const;
|
||||
|
||||
protected:
|
||||
std::vector<ColorStop> mColorStops;
|
||||
Float mAngle{ 180.f };
|
||||
Sizef mSize;
|
||||
};
|
||||
|
||||
}} // namespace EE::UI
|
||||
|
||||
#endif
|
||||
@@ -1,20 +1,35 @@
|
||||
#ifndef EE_GRAPHICS_RADIALGRADIENTDRAWABLE_HPP
|
||||
#define EE_GRAPHICS_RADIALGRADIENTDRAWABLE_HPP
|
||||
#ifndef EE_UI_RADIALGRADIENTDRAWABLE_HPP
|
||||
#define EE_UI_RADIALGRADIENTDRAWABLE_HPP
|
||||
|
||||
#include <eepp/graphics/drawable.hpp>
|
||||
#include <eepp/system/color.hpp>
|
||||
#include <eepp/ui/css/stylesheetlength.hpp>
|
||||
#include <eepp/ui/lineargradientdrawable.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace EE { namespace Graphics {
|
||||
namespace EE { namespace UI {
|
||||
|
||||
class EE_API RadialGradientDrawable : public Drawable {
|
||||
class EE_API RadialGradientDrawable : public Graphics::Drawable {
|
||||
public:
|
||||
using Length = CSS::StyleSheetLength;
|
||||
using GradientUnit = CSS::StyleSheetLength::Unit;
|
||||
|
||||
struct ColorStop {
|
||||
Float position{ 0 };
|
||||
Color color;
|
||||
Float value{ 0 };
|
||||
GradientUnit unit{ CSS::StyleSheetLength::Percentage };
|
||||
|
||||
ColorStop() : color( Color::White ) {}
|
||||
ColorStop( Float pos, const Color& col ) : position( pos ), color( col ) {}
|
||||
ColorStop( Float val, const Color& col, GradientUnit u = CSS::StyleSheetLength::Percentage ) :
|
||||
value( val ), color( col ), unit( u ) {}
|
||||
|
||||
Float getNormalized( Float gradientRayLength ) const {
|
||||
if ( unit == CSS::StyleSheetLength::Percentage )
|
||||
return eemax( 0.f, value ) / 100.f;
|
||||
if ( gradientRayLength > 0.0001f )
|
||||
return eemax( 0.f, value ) / gradientRayLength;
|
||||
return 0.f;
|
||||
}
|
||||
};
|
||||
|
||||
enum ShapeType { CIRCLE, ELLIPSE };
|
||||
@@ -23,7 +38,8 @@ class EE_API RadialGradientDrawable : public Drawable {
|
||||
static RadialGradientDrawable* New();
|
||||
static RadialGradientDrawable* NewRepeating();
|
||||
|
||||
RadialGradientDrawable( Drawable::Type drawableType = RADIALGRADIENT );
|
||||
RadialGradientDrawable(
|
||||
Graphics::Drawable::Type drawableType = Graphics::Drawable::RADIALGRADIENT );
|
||||
|
||||
virtual Sizef getSize();
|
||||
|
||||
@@ -65,6 +81,6 @@ class EE_API RadialGradientDrawable : public Drawable {
|
||||
Sizef mSize;
|
||||
};
|
||||
|
||||
}} // namespace EE::Graphics
|
||||
}} // namespace EE::UI
|
||||
|
||||
#endif
|
||||
@@ -3,18 +3,19 @@
|
||||
#include <eepp/graphics/drawable.hpp>
|
||||
#include <eepp/graphics/drawablesearcher.hpp>
|
||||
#include <eepp/graphics/fontmanager.hpp>
|
||||
#include <eepp/graphics/lineargradientdrawable.hpp>
|
||||
#include <eepp/graphics/radialgradientdrawable.hpp>
|
||||
#include <eepp/graphics/rectangledrawable.hpp>
|
||||
#include <eepp/graphics/triangledrawable.hpp>
|
||||
#include <eepp/scene/scenemanager.hpp>
|
||||
#include <eepp/system/log.hpp>
|
||||
#include <eepp/system/luapattern.hpp>
|
||||
#include <eepp/ui/css/drawableimageparser.hpp>
|
||||
#include <eepp/ui/lineargradientdrawable.hpp>
|
||||
#include <eepp/ui/radialgradientdrawable.hpp>
|
||||
#include <eepp/ui/uiiconthememanager.hpp>
|
||||
#include <eepp/ui/uinode.hpp>
|
||||
#include <eepp/ui/uiscenenode.hpp>
|
||||
|
||||
using namespace EE::Graphics;
|
||||
using namespace EE::Scene;
|
||||
|
||||
namespace EE { namespace UI { namespace CSS {
|
||||
@@ -23,30 +24,34 @@ struct ColorStopResult {
|
||||
bool valid{ false };
|
||||
bool isHint{ false };
|
||||
Color color;
|
||||
Float position{ -1.f }; /* -1 = not specified */
|
||||
Float secondPosition{ -1.f }; /* -1 = single-position stop */
|
||||
Float value{ -1.f };
|
||||
StyleSheetLength::Unit unit{ StyleSheetLength::Percentage };
|
||||
Float secondValue{ -1.f };
|
||||
StyleSheetLength::Unit secondUnit{ StyleSheetLength::Percentage };
|
||||
};
|
||||
|
||||
static Float parsePositionValue( const std::string& token ) {
|
||||
static StyleSheetLength parseStyleSheetLength( const std::string& token ) {
|
||||
if ( token.empty() )
|
||||
return -1.f;
|
||||
Float val;
|
||||
return StyleSheetLength( -1.f, StyleSheetLength::Percentage );
|
||||
if ( token.back() == '%' ) {
|
||||
std::string num = token.substr( 0, token.size() - 1 );
|
||||
Float val;
|
||||
if ( String::fromString( val, num ) )
|
||||
return eemax( 0.f, eemin( 100.f, val ) ) / 100.f;
|
||||
return -1.f;
|
||||
return StyleSheetLength( eemax( 0.f, eemin( 100.f, val ) ),
|
||||
StyleSheetLength::Percentage );
|
||||
return StyleSheetLength( -1.f, StyleSheetLength::Percentage );
|
||||
}
|
||||
if ( token.size() > 2 && ( token.substr( token.size() - 2 ) == "px" ||
|
||||
token.substr( token.size() - 2 ) == "dp" ) ) {
|
||||
std::string num = token.substr( 0, token.size() - 2 );
|
||||
if ( String::fromString( val, num ) )
|
||||
return eemax( 0.f, eemin( 100.f, val ) ) / 100.f;
|
||||
return -1.f;
|
||||
// Handle bare numbers as percentages (CSS default for gradient stops)
|
||||
{
|
||||
Float val;
|
||||
if ( String::fromString( val, token ) )
|
||||
return StyleSheetLength( eemax( 0.f, eemin( 100.f, val ) ),
|
||||
StyleSheetLength::Percentage );
|
||||
}
|
||||
if ( String::fromString( val, token ) )
|
||||
return eemax( 0.f, eemin( 100.f, val ) ) / 100.f;
|
||||
return -1.f;
|
||||
StyleSheetLength len = StyleSheetLength::fromString( token );
|
||||
if ( len.getValue() >= 0.f )
|
||||
return len;
|
||||
return StyleSheetLength( -1.f, StyleSheetLength::Percentage );
|
||||
}
|
||||
|
||||
static ColorStopResult parseColorStop( const std::string& stopStr ) {
|
||||
@@ -62,7 +67,8 @@ static ColorStopResult parseColorStop( const std::string& stopStr ) {
|
||||
std::string num = str.substr( 0, str.size() - 1 );
|
||||
Float val;
|
||||
if ( String::fromString( val, num ) ) {
|
||||
result.position = eemax( 0.f, eemin( 100.f, val ) ) / 100.f;
|
||||
result.value = eemax( 0.f, eemin( 100.f, val ) );
|
||||
result.unit = StyleSheetLength::Percentage;
|
||||
result.isHint = true;
|
||||
result.valid = true;
|
||||
return result;
|
||||
@@ -111,11 +117,13 @@ static ColorStopResult parseColorStop( const std::string& stopStr ) {
|
||||
if ( Color::isColorString( altColor ) ) {
|
||||
std::string firstPos = colorPart.substr( innerSpace + 1 );
|
||||
String::trimInPlace( firstPos );
|
||||
Float p1 = parsePositionValue( firstPos );
|
||||
Float p2 = parsePositionValue( posPart );
|
||||
StyleSheetLength p1 = parseStyleSheetLength( firstPos );
|
||||
StyleSheetLength p2 = parseStyleSheetLength( posPart );
|
||||
result.color = Color::fromString( altColor );
|
||||
result.position = p1;
|
||||
result.secondPosition = p2 >= 0.f ? p2 : -1.f;
|
||||
result.value = p1.getValue();
|
||||
result.unit = p1.getUnit();
|
||||
result.secondValue = p2.getValue();
|
||||
result.secondUnit = p2.getUnit();
|
||||
result.valid = true;
|
||||
return result;
|
||||
}
|
||||
@@ -123,20 +131,20 @@ static ColorStopResult parseColorStop( const std::string& stopStr ) {
|
||||
}
|
||||
|
||||
// Try to parse the position part (single position)
|
||||
Float posVal = parsePositionValue( posPart );
|
||||
StyleSheetLength posLen = parseStyleSheetLength( posPart );
|
||||
|
||||
if ( Color::isColorString( colorPart ) ) {
|
||||
result.color = Color::fromString( colorPart );
|
||||
result.position = posVal;
|
||||
result.value = posLen.getValue();
|
||||
result.unit = posLen.getUnit();
|
||||
result.valid = true;
|
||||
} else if ( posVal < 0.f && Color::isColorString( str ) ) {
|
||||
} else if ( posLen.getValue() < 0.f && Color::isColorString( str ) ) {
|
||||
result.color = Color::fromString( str );
|
||||
result.valid = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DrawableImageParser::DrawableImageParser() {
|
||||
registerBaseParsers();
|
||||
}
|
||||
@@ -267,12 +275,13 @@ void DrawableImageParser::registerBaseParsers() {
|
||||
continue;
|
||||
|
||||
gradientStops.push_back( stop );
|
||||
if ( stop.secondPosition >= 0.f ) {
|
||||
if ( stop.secondValue >= 0.f ) {
|
||||
ColorStopResult stop2;
|
||||
stop2.valid = true;
|
||||
stop2.color = stop.color;
|
||||
stop2.position = stop.secondPosition;
|
||||
stop2.secondPosition = -1.f;
|
||||
stop2.value = stop.secondValue;
|
||||
stop2.unit = stop.secondUnit;
|
||||
stop2.secondValue = -1.f;
|
||||
gradientStops.push_back( stop2 );
|
||||
}
|
||||
}
|
||||
@@ -282,22 +291,34 @@ void DrawableImageParser::registerBaseParsers() {
|
||||
// list, set its position to be equal to the largest specified
|
||||
// position of any color-stop before it. This ensures hard
|
||||
// transitions instead of accidental gradients.
|
||||
// We also convert the unit to match the dominating unit of the
|
||||
// gradient, so that mixed px/% stops produce consistent results.
|
||||
{
|
||||
Float maxPos = -1.f;
|
||||
StyleSheetLength::Unit maxUnit = StyleSheetLength::Percentage;
|
||||
bool hasMax = false;
|
||||
for ( auto& s : gradientStops ) {
|
||||
if ( s.isHint )
|
||||
continue;
|
||||
if ( s.position >= 0.f ) {
|
||||
if ( s.position < maxPos )
|
||||
s.position = maxPos;
|
||||
else
|
||||
maxPos = s.position;
|
||||
if ( s.value >= 0.f ) {
|
||||
if ( hasMax && s.value < maxPos ) {
|
||||
s.value = maxPos;
|
||||
s.unit = maxUnit;
|
||||
} else {
|
||||
maxPos = s.value;
|
||||
maxUnit = s.unit;
|
||||
hasMax = true;
|
||||
}
|
||||
}
|
||||
if ( s.secondPosition >= 0.f ) {
|
||||
if ( s.secondPosition < maxPos )
|
||||
s.secondPosition = maxPos;
|
||||
else
|
||||
maxPos = s.secondPosition;
|
||||
if ( s.secondValue >= 0.f ) {
|
||||
if ( hasMax && s.secondValue < maxPos ) {
|
||||
s.secondValue = maxPos;
|
||||
s.secondUnit = maxUnit;
|
||||
} else {
|
||||
maxPos = s.secondValue;
|
||||
maxUnit = s.secondUnit;
|
||||
hasMax = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -317,20 +338,22 @@ void DrawableImageParser::registerBaseParsers() {
|
||||
// irrelevant (the hint has no effect); drop them.
|
||||
std::sort( gradientStops.begin(), gradientStops.end(),
|
||||
[]( const ColorStopResult& a, const ColorStopResult& b ) {
|
||||
return a.position < b.position;
|
||||
return a.value < b.value;
|
||||
} );
|
||||
|
||||
// Set position 0 for the first unpositioned color stop, and position 1
|
||||
// for the last, so interior auto-distribution has proper boundaries.
|
||||
for ( size_t i = 0; i < gradientStops.size(); i++ ) {
|
||||
if ( !gradientStops[i].isHint && gradientStops[i].position < 0.f ) {
|
||||
gradientStops[i].position = 0.f;
|
||||
if ( !gradientStops[i].isHint && gradientStops[i].value < 0.f ) {
|
||||
gradientStops[i].value = 0.f;
|
||||
gradientStops[i].unit = StyleSheetLength::Percentage;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for ( size_t i = gradientStops.size(); i > 0; i-- ) {
|
||||
if ( !gradientStops[i - 1].isHint && gradientStops[i - 1].position < 0.f ) {
|
||||
gradientStops[i - 1].position = 1.f;
|
||||
if ( !gradientStops[i - 1].isHint && gradientStops[i - 1].value < 0.f ) {
|
||||
gradientStops[i - 1].value = 100.f;
|
||||
gradientStops[i - 1].unit = StyleSheetLength::Percentage;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -338,27 +361,27 @@ void DrawableImageParser::registerBaseParsers() {
|
||||
// Fill missing positions on color stops. Hints always have explicit
|
||||
// positions so they are left untouched.
|
||||
for ( size_t i = 0; i < gradientStops.size(); i++ ) {
|
||||
if ( gradientStops[i].isHint || gradientStops[i].position >= 0.f )
|
||||
if ( gradientStops[i].isHint || gradientStops[i].value >= 0.f )
|
||||
continue;
|
||||
size_t prevKnown = i;
|
||||
size_t nextKnown = i;
|
||||
while ( prevKnown > 0 && ( gradientStops[prevKnown - 1].position < 0.f ) )
|
||||
while ( prevKnown > 0 && ( gradientStops[prevKnown - 1].value < 0.f ) )
|
||||
prevKnown--;
|
||||
while ( nextKnown < gradientStops.size() &&
|
||||
( gradientStops[nextKnown].isHint || gradientStops[nextKnown].position < 0.f ) )
|
||||
( gradientStops[nextKnown].isHint || gradientStops[nextKnown].value < 0.f ) )
|
||||
nextKnown++;
|
||||
|
||||
Float startPos = ( prevKnown > 0 && !gradientStops[prevKnown - 1].isHint )
|
||||
? gradientStops[prevKnown - 1].position
|
||||
? gradientStops[prevKnown - 1].value
|
||||
: 0.f;
|
||||
Float endPos =
|
||||
( nextKnown < gradientStops.size() ) ? gradientStops[nextKnown].position : 1.f;
|
||||
( nextKnown < gradientStops.size() ) ? gradientStops[nextKnown].value : 100.f;
|
||||
size_t range = nextKnown - prevKnown + 1;
|
||||
|
||||
for ( size_t j = prevKnown; j < nextKnown; j++ ) {
|
||||
if ( gradientStops[j].isHint )
|
||||
continue;
|
||||
gradientStops[j].position =
|
||||
gradientStops[j].value =
|
||||
startPos +
|
||||
( endPos - startPos ) * ( (Float)( j - prevKnown + 1 ) / (Float)range );
|
||||
}
|
||||
@@ -366,24 +389,6 @@ void DrawableImageParser::registerBaseParsers() {
|
||||
i = nextKnown;
|
||||
}
|
||||
|
||||
// Non-repeating: force first/last color stops to exactly 0/1 so
|
||||
// that the gradient fills the entire box (CSS §4.3). Repeating
|
||||
// mode keeps raw positions for tiling.
|
||||
if ( !repeating ) {
|
||||
for ( size_t i = 0; i < gradientStops.size(); i++ ) {
|
||||
if ( !gradientStops[i].isHint ) {
|
||||
gradientStops[i].position = 0.f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for ( size_t i = gradientStops.size(); i > 0; i-- ) {
|
||||
if ( !gradientStops[i - 1].isHint ) {
|
||||
gradientStops[i - 1].position = 1.f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Expand hinted segments into finely sampled color stops
|
||||
std::vector<LinearGradientDrawable::ColorStop> stops;
|
||||
const int HINT_SAMPLES = 16;
|
||||
@@ -393,8 +398,8 @@ void DrawableImageParser::registerBaseParsers() {
|
||||
continue;
|
||||
|
||||
// Add the current color stop
|
||||
stops.push_back( LinearGradientDrawable::ColorStop( gradientStops[i].position,
|
||||
gradientStops[i].color ) );
|
||||
stops.push_back( LinearGradientDrawable::ColorStop(
|
||||
gradientStops[i].value, gradientStops[i].color, gradientStops[i].unit ) );
|
||||
|
||||
// Find the next color stop and any hints between them
|
||||
size_t nextColor = i + 1;
|
||||
@@ -407,14 +412,14 @@ void DrawableImageParser::registerBaseParsers() {
|
||||
std::vector<Float> hints;
|
||||
for ( size_t h = i + 1; h < nextColor; h++ ) {
|
||||
if ( gradientStops[h].isHint )
|
||||
hints.push_back( gradientStops[h].position );
|
||||
hints.push_back( gradientStops[h].value );
|
||||
}
|
||||
|
||||
if ( hints.empty() )
|
||||
continue;
|
||||
|
||||
Float p0 = gradientStops[i].position;
|
||||
Float p1 = gradientStops[nextColor].position;
|
||||
Float p0 = gradientStops[i].value;
|
||||
Float p1 = gradientStops[nextColor].value;
|
||||
Color c0 = gradientStops[i].color;
|
||||
Color c1 = gradientStops[nextColor].color;
|
||||
|
||||
@@ -500,12 +505,13 @@ void DrawableImageParser::registerBaseParsers() {
|
||||
continue;
|
||||
|
||||
gradientStops.push_back( stop );
|
||||
if ( stop.secondPosition >= 0.f ) {
|
||||
if ( stop.secondValue >= 0.f ) {
|
||||
ColorStopResult stop2;
|
||||
stop2.valid = true;
|
||||
stop2.color = stop.color;
|
||||
stop2.position = stop.secondPosition;
|
||||
stop2.secondPosition = -1.f;
|
||||
stop2.value = stop.secondValue;
|
||||
stop2.unit = stop.secondUnit;
|
||||
stop2.secondValue = -1.f;
|
||||
gradientStops.push_back( stop2 );
|
||||
}
|
||||
}
|
||||
@@ -516,20 +522,30 @@ void DrawableImageParser::registerBaseParsers() {
|
||||
// position of any color-stop before it.
|
||||
{
|
||||
Float maxPos = -1.f;
|
||||
StyleSheetLength::Unit maxUnit = StyleSheetLength::Percentage;
|
||||
bool hasMax = false;
|
||||
for ( auto& s : gradientStops ) {
|
||||
if ( s.isHint )
|
||||
continue;
|
||||
if ( s.position >= 0.f ) {
|
||||
if ( s.position < maxPos )
|
||||
s.position = maxPos;
|
||||
else
|
||||
maxPos = s.position;
|
||||
if ( s.value >= 0.f ) {
|
||||
if ( hasMax && s.value < maxPos ) {
|
||||
s.value = maxPos;
|
||||
s.unit = maxUnit;
|
||||
} else {
|
||||
maxPos = s.value;
|
||||
maxUnit = s.unit;
|
||||
hasMax = true;
|
||||
}
|
||||
}
|
||||
if ( s.secondPosition >= 0.f ) {
|
||||
if ( s.secondPosition < maxPos )
|
||||
s.secondPosition = maxPos;
|
||||
else
|
||||
maxPos = s.secondPosition;
|
||||
if ( s.secondValue >= 0.f ) {
|
||||
if ( hasMax && s.secondValue < maxPos ) {
|
||||
s.secondValue = maxPos;
|
||||
s.secondUnit = maxUnit;
|
||||
} else {
|
||||
maxPos = s.secondValue;
|
||||
maxUnit = s.secondUnit;
|
||||
hasMax = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -546,19 +562,19 @@ void DrawableImageParser::registerBaseParsers() {
|
||||
|
||||
std::sort( gradientStops.begin(), gradientStops.end(),
|
||||
[]( const ColorStopResult& a, const ColorStopResult& b ) {
|
||||
return a.position < b.position;
|
||||
return a.value < b.value;
|
||||
} );
|
||||
|
||||
// Set boundary defaults for unpositioned stops
|
||||
for ( size_t i = 0; i < gradientStops.size(); i++ ) {
|
||||
if ( !gradientStops[i].isHint && gradientStops[i].position < 0.f ) {
|
||||
gradientStops[i].position = 0.f;
|
||||
if ( !gradientStops[i].isHint && gradientStops[i].value < 0.f ) {
|
||||
gradientStops[i].value = 0.f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for ( size_t i = gradientStops.size(); i > 0; i-- ) {
|
||||
if ( !gradientStops[i - 1].isHint && gradientStops[i - 1].position < 0.f ) {
|
||||
gradientStops[i - 1].position = 1.f;
|
||||
if ( !gradientStops[i - 1].isHint && gradientStops[i - 1].value < 0.f ) {
|
||||
gradientStops[i - 1].value = 1.f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -566,27 +582,27 @@ void DrawableImageParser::registerBaseParsers() {
|
||||
// Fill missing positions on color stops. Hints always have explicit
|
||||
// positions so they are left untouched.
|
||||
for ( size_t i = 0; i < gradientStops.size(); i++ ) {
|
||||
if ( gradientStops[i].isHint || gradientStops[i].position >= 0.f )
|
||||
if ( gradientStops[i].isHint || gradientStops[i].value >= 0.f )
|
||||
continue;
|
||||
size_t prevKnown = i;
|
||||
size_t nextKnown = i;
|
||||
while ( prevKnown > 0 && ( gradientStops[prevKnown - 1].position < 0.f ) )
|
||||
while ( prevKnown > 0 && ( gradientStops[prevKnown - 1].value < 0.f ) )
|
||||
prevKnown--;
|
||||
while ( nextKnown < gradientStops.size() &&
|
||||
( gradientStops[nextKnown].isHint || gradientStops[nextKnown].position < 0.f ) )
|
||||
( gradientStops[nextKnown].isHint || gradientStops[nextKnown].value < 0.f ) )
|
||||
nextKnown++;
|
||||
|
||||
Float startPos = ( prevKnown > 0 && !gradientStops[prevKnown - 1].isHint )
|
||||
? gradientStops[prevKnown - 1].position
|
||||
? gradientStops[prevKnown - 1].value
|
||||
: 0.f;
|
||||
Float endPos =
|
||||
( nextKnown < gradientStops.size() ) ? gradientStops[nextKnown].position : 1.f;
|
||||
( nextKnown < gradientStops.size() ) ? gradientStops[nextKnown].value : 100.f;
|
||||
size_t range = nextKnown - prevKnown + 1;
|
||||
|
||||
for ( size_t j = prevKnown; j < nextKnown; j++ ) {
|
||||
if ( gradientStops[j].isHint )
|
||||
continue;
|
||||
gradientStops[j].position =
|
||||
gradientStops[j].value =
|
||||
startPos +
|
||||
( endPos - startPos ) * ( (Float)( j - prevKnown + 1 ) / (Float)range );
|
||||
}
|
||||
@@ -594,22 +610,6 @@ void DrawableImageParser::registerBaseParsers() {
|
||||
i = nextKnown;
|
||||
}
|
||||
|
||||
// Non-repeating: force first/last color stops to exactly 0/1
|
||||
if ( !repeating ) {
|
||||
for ( size_t i = 0; i < gradientStops.size(); i++ ) {
|
||||
if ( !gradientStops[i].isHint ) {
|
||||
gradientStops[i].position = 0.f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for ( size_t i = gradientStops.size(); i > 0; i-- ) {
|
||||
if ( !gradientStops[i - 1].isHint ) {
|
||||
gradientStops[i - 1].position = 1.f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Expand hinted segments into finely sampled color stops
|
||||
std::vector<RadialGradientDrawable::ColorStop> stops;
|
||||
const int HINT_SAMPLES = 16;
|
||||
@@ -619,8 +619,8 @@ void DrawableImageParser::registerBaseParsers() {
|
||||
continue;
|
||||
|
||||
// Add the current color stop
|
||||
stops.push_back( RadialGradientDrawable::ColorStop( gradientStops[i].position,
|
||||
gradientStops[i].color ) );
|
||||
stops.push_back( RadialGradientDrawable::ColorStop(
|
||||
gradientStops[i].value, gradientStops[i].color, gradientStops[i].unit ) );
|
||||
|
||||
// Find the next color stop and any hints between them
|
||||
size_t nextColor = i + 1;
|
||||
@@ -633,14 +633,14 @@ void DrawableImageParser::registerBaseParsers() {
|
||||
std::vector<Float> hints;
|
||||
for ( size_t h = i + 1; h < nextColor; h++ ) {
|
||||
if ( gradientStops[h].isHint )
|
||||
hints.push_back( gradientStops[h].position );
|
||||
hints.push_back( gradientStops[h].value );
|
||||
}
|
||||
|
||||
if ( hints.empty() )
|
||||
continue;
|
||||
|
||||
Float p0 = gradientStops[i].position;
|
||||
Float p1 = gradientStops[nextColor].position;
|
||||
Float p0 = gradientStops[i].value;
|
||||
Float p1 = gradientStops[nextColor].value;
|
||||
Color c0 = gradientStops[i].color;
|
||||
Color c1 = gradientStops[nextColor].color;
|
||||
|
||||
@@ -705,7 +705,7 @@ void DrawableImageParser::registerBaseParsers() {
|
||||
|
||||
const auto& params( functionType.getParameters() );
|
||||
|
||||
CSS::StyleSheetLength length( params[0] );
|
||||
StyleSheetLength length( params[0] );
|
||||
drawable->setRadius( node->convertLength( length, size.getWidth() / 2.f ) );
|
||||
|
||||
if ( params.size() >= 2 ) {
|
||||
@@ -749,7 +749,7 @@ void DrawableImageParser::registerBaseParsers() {
|
||||
std::vector<std::string> parts( String::split( param, ' ' ) );
|
||||
|
||||
if ( parts.size() >= 2 ) {
|
||||
CSS::StyleSheetLength length( parts[1] );
|
||||
StyleSheetLength length( parts[1] );
|
||||
drawable->setLineWidth( node->convertLength( length, size.getWidth() ) );
|
||||
}
|
||||
} else if ( param.find( "º" ) != std::string::npos ) {
|
||||
@@ -822,8 +822,8 @@ void DrawableImageParser::registerBaseParsers() {
|
||||
std::vector<std::string> coords( String::split( vertex[v], ' ' ) );
|
||||
|
||||
if ( coords.size() == 2 ) {
|
||||
CSS::StyleSheetLength posX( coords[0] );
|
||||
CSS::StyleSheetLength posY( coords[1] );
|
||||
StyleSheetLength posX( coords[0] );
|
||||
StyleSheetLength posY( coords[1] );
|
||||
vertices.push_back(
|
||||
Vector2f( node->convertLength( posX, size.getWidth() ),
|
||||
node->convertLength( posY, size.getHeight() ) ) );
|
||||
@@ -891,8 +891,8 @@ void DrawableImageParser::registerBaseParsers() {
|
||||
std::vector<std::string> coords( String::split( vertex[v], ' ' ) );
|
||||
|
||||
if ( coords.size() == 2 ) {
|
||||
CSS::StyleSheetLength posX( coords[0] );
|
||||
CSS::StyleSheetLength posY( coords[1] );
|
||||
StyleSheetLength posX( coords[0] );
|
||||
StyleSheetLength posY( coords[1] );
|
||||
vertices.push_back(
|
||||
Vector2f( node->convertLength( posX, size.getWidth() ),
|
||||
node->convertLength( posY, size.getHeight() ) ) );
|
||||
@@ -946,7 +946,7 @@ void DrawableImageParser::registerBaseParsers() {
|
||||
const auto& params = functionType.getParameters();
|
||||
if ( params.size() < 2 )
|
||||
return nullptr;
|
||||
CSS::StyleSheetLength length( params[1] );
|
||||
StyleSheetLength length( params[1] );
|
||||
return uiScene->findIconDrawable( params[0],
|
||||
node->convertLength( length, size.getWidth() ) );
|
||||
};
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <eepp/graphics/globalbatchrenderer.hpp>
|
||||
#include <eepp/graphics/lineargradientdrawable.hpp>
|
||||
#include <eepp/math/math.hpp>
|
||||
|
||||
namespace EE { namespace Graphics {
|
||||
#include <eepp/graphics/globalbatchrenderer.hpp>
|
||||
#include <eepp/math/math.hpp>
|
||||
#include <eepp/ui/lineargradientdrawable.hpp>
|
||||
|
||||
namespace EE { namespace UI {
|
||||
|
||||
LinearGradientDrawable* LinearGradientDrawable::New() {
|
||||
return eeNew( LinearGradientDrawable, () );
|
||||
}
|
||||
|
||||
LinearGradientDrawable* LinearGradientDrawable::NewRepeating() {
|
||||
return eeNew( LinearGradientDrawable, ( REPEATINGLINEARGRADIENT ) );
|
||||
return eeNew( LinearGradientDrawable, ( Graphics::Drawable::REPEATINGLINEARGRADIENT ) );
|
||||
}
|
||||
|
||||
LinearGradientDrawable::LinearGradientDrawable( Drawable::Type drawableType ) :
|
||||
LinearGradientDrawable::LinearGradientDrawable( Graphics::Drawable::Type drawableType ) :
|
||||
Drawable( drawableType ) {}
|
||||
|
||||
Sizef LinearGradientDrawable::getSize() {
|
||||
@@ -40,16 +41,13 @@ void LinearGradientDrawable::draw( const Vector2f& position, const Sizef& size )
|
||||
Float w = size.getWidth();
|
||||
Float h = size.getHeight();
|
||||
|
||||
// CSS angle: 0° = up, 90° = right, 180° = down (default)
|
||||
Float aRad = mAngle * EE_PI / 180.f;
|
||||
Float dx = std::sin( aRad );
|
||||
Float dy = -std::cos( aRad );
|
||||
|
||||
// Gradient line passes through box center
|
||||
Float cx = w * 0.5f;
|
||||
Float cy = h * 0.5f;
|
||||
|
||||
// Find gradient line intersections with box boundaries
|
||||
Float tVals[4];
|
||||
int validCount = 0;
|
||||
|
||||
@@ -91,7 +89,17 @@ void LinearGradientDrawable::draw( const Vector2f& position, const Sizef& size )
|
||||
|
||||
Float txLen = tMax - tMin;
|
||||
|
||||
// Perpendicular direction (rotated 90° clockwise in screen coords)
|
||||
// Normalize all stops to [0,1] using the gradient-line pixel length
|
||||
std::vector<ColorStop> stops;
|
||||
stops.reserve( mColorStops.size() );
|
||||
for ( const auto& s : mColorStops )
|
||||
stops.push_back(
|
||||
ColorStop( s.getNormalized( txLen ), s.color, CSS::StyleSheetLength::Percentage ) );
|
||||
|
||||
std::sort( stops.begin(), stops.end(),
|
||||
[]( const ColorStop& a, const ColorStop& b ) { return a.value < b.value; } );
|
||||
|
||||
|
||||
Float px = -dy;
|
||||
Float py = dx;
|
||||
|
||||
@@ -101,19 +109,14 @@ void LinearGradientDrawable::draw( const Vector2f& position, const Sizef& size )
|
||||
sBR->quadsBegin();
|
||||
|
||||
if ( isRepeating() ) {
|
||||
Float firstPos = mColorStops.front().position;
|
||||
Float lastPos = mColorStops.back().position;
|
||||
Float firstPos = stops.front().value;
|
||||
Float lastPos = stops.back().value;
|
||||
Float patternLen = lastPos - firstPos;
|
||||
if ( patternLen < 0.0001f ) {
|
||||
sBR->draw();
|
||||
return;
|
||||
}
|
||||
|
||||
// Visible position range mapped to [0, 1] along the gradient-line
|
||||
// segment visible in the box.
|
||||
// Pattern start in position space: firstPos
|
||||
// Repeat interval: patternLen
|
||||
// Visible range: [0, 1]
|
||||
int rStart = (int)std::floor( ( -firstPos ) / patternLen );
|
||||
int rEnd = (int)std::ceil( ( 1.f - lastPos ) / patternLen );
|
||||
const int MAX_REPEATS = 128;
|
||||
@@ -123,9 +126,9 @@ void LinearGradientDrawable::draw( const Vector2f& position, const Sizef& size )
|
||||
for ( int r = rStart; r <= rEnd; r++ ) {
|
||||
Float repeatOff = (Float)r * patternLen;
|
||||
|
||||
for ( size_t i = 0; i + 1 < mColorStops.size(); i++ ) {
|
||||
Float p0 = mColorStops[i].position + repeatOff;
|
||||
Float p1 = mColorStops[i + 1].position + repeatOff;
|
||||
for ( size_t i = 0; i + 1 < stops.size(); i++ ) {
|
||||
Float p0 = stops[i].value + repeatOff;
|
||||
Float p1 = stops[i + 1].value + repeatOff;
|
||||
|
||||
if ( p1 <= 0.f || p0 >= 1.f )
|
||||
continue;
|
||||
@@ -135,20 +138,21 @@ void LinearGradientDrawable::draw( const Vector2f& position, const Sizef& size )
|
||||
if ( clip1 - clip0 < 0.0001f )
|
||||
continue;
|
||||
|
||||
// Colors at clipped band boundaries (interpolate within the stop pair)
|
||||
Float bw = p1 - p0;
|
||||
Float frac0 = ( clip0 - p0 ) / bw;
|
||||
Float frac1 = ( clip1 - p0 ) / bw;
|
||||
const Color& sc0 = mColorStops[i].color;
|
||||
const Color& sc1 = mColorStops[i + 1].color;
|
||||
Color cc0( (Uint8)( (Float)sc0.r + frac0 * (Float)( sc1.r - sc0.r ) ),
|
||||
(Uint8)( (Float)sc0.g + frac0 * (Float)( sc1.g - sc0.g ) ),
|
||||
(Uint8)( (Float)sc0.b + frac0 * (Float)( sc1.b - sc0.b ) ),
|
||||
(Uint8)( (Float)sc0.a + frac0 * (Float)( sc1.a - sc0.a ) ) );
|
||||
Color cc1( (Uint8)( (Float)sc0.r + frac1 * (Float)( sc1.r - sc0.r ) ),
|
||||
(Uint8)( (Float)sc0.g + frac1 * (Float)( sc1.g - sc0.g ) ),
|
||||
(Uint8)( (Float)sc0.b + frac1 * (Float)( sc1.b - sc0.b ) ),
|
||||
(Uint8)( (Float)sc0.a + frac1 * (Float)( sc1.a - sc0.a ) ) );
|
||||
const Color& sc0 = stops[i].color;
|
||||
const Color& sc1 = stops[i + 1].color;
|
||||
Color cc0(
|
||||
(Uint8)( (Float)sc0.r + frac0 * (Float)( sc1.r - sc0.r ) ),
|
||||
(Uint8)( (Float)sc0.g + frac0 * (Float)( sc1.g - sc0.g ) ),
|
||||
(Uint8)( (Float)sc0.b + frac0 * (Float)( sc1.b - sc0.b ) ),
|
||||
(Uint8)( (Float)sc0.a + frac0 * (Float)( sc1.a - sc0.a ) ) );
|
||||
Color cc1(
|
||||
(Uint8)( (Float)sc0.r + frac1 * (Float)( sc1.r - sc0.r ) ),
|
||||
(Uint8)( (Float)sc0.g + frac1 * (Float)( sc1.g - sc0.g ) ),
|
||||
(Uint8)( (Float)sc0.b + frac1 * (Float)( sc1.b - sc0.b ) ),
|
||||
(Uint8)( (Float)sc0.a + frac1 * (Float)( sc1.a - sc0.a ) ) );
|
||||
|
||||
Float tc0 = tMin + clip0 * txLen;
|
||||
Float tc1 = tMin + clip1 * txLen;
|
||||
@@ -157,7 +161,6 @@ void LinearGradientDrawable::draw( const Vector2f& position, const Sizef& size )
|
||||
Float gx1 = cx + tc1 * dx;
|
||||
Float gy1 = cy + tc1 * dy;
|
||||
|
||||
// Perpendicular intersection for point 0
|
||||
Float ptVals0[4];
|
||||
int ptCount0 = 0;
|
||||
auto addPT0 = [&]( Float pt, Float qx, Float qy ) {
|
||||
@@ -190,7 +193,6 @@ void LinearGradientDrawable::draw( const Vector2f& position, const Sizef& size )
|
||||
Vector2f a0( gx0 + ptMin0 * px + position.x, gy0 + ptMin0 * py + position.y );
|
||||
Vector2f b0( gx0 + ptMax0 * px + position.x, gy0 + ptMax0 * py + position.y );
|
||||
|
||||
// Perpendicular intersection for point 1
|
||||
Float ptVals1[4];
|
||||
int ptCount1 = 0;
|
||||
auto addPT1 = [&]( Float pt, Float qx, Float qy ) {
|
||||
@@ -230,24 +232,19 @@ void LinearGradientDrawable::draw( const Vector2f& position, const Sizef& size )
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Pre-compute position on gradient line: center + t * dir
|
||||
// Pre-compute perpendicular intersections for all stops
|
||||
struct PerpSeg {
|
||||
Vector2f a;
|
||||
Vector2f b;
|
||||
Float gradPos;
|
||||
};
|
||||
std::vector<PerpSeg> segments;
|
||||
segments.reserve( mColorStops.size() );
|
||||
segments.reserve( stops.size() );
|
||||
|
||||
for ( size_t i = 0; i < mColorStops.size(); i++ ) {
|
||||
for ( size_t i = 0; i < stops.size(); i++ ) {
|
||||
PerpSeg seg;
|
||||
Float t = tMin + mColorStops[i].position * txLen;
|
||||
seg.gradPos = t;
|
||||
Float t = tMin + stops[i].value * txLen;
|
||||
Float gx = cx + t * dx;
|
||||
Float gy = cy + t * dy;
|
||||
|
||||
// Find perpendicular line intersections with the box
|
||||
Float ptVals[4];
|
||||
int ptCount = 0;
|
||||
|
||||
@@ -289,10 +286,9 @@ void LinearGradientDrawable::draw( const Vector2f& position, const Sizef& size )
|
||||
segments.push_back( seg );
|
||||
}
|
||||
|
||||
// Render quads between consecutive stops
|
||||
for ( size_t i = 0; i < mColorStops.size() - 1; i++ ) {
|
||||
const Color& c0 = mColorStops[i].color;
|
||||
const Color& c1 = mColorStops[i + 1].color;
|
||||
for ( size_t i = 0; i < stops.size() - 1; i++ ) {
|
||||
const Color& c0 = stops[i].color;
|
||||
const Color& c1 = stops[i + 1].color;
|
||||
|
||||
Color fc0 = ( mColor.a == 255 ) ? c0 : Color( c0 ).blendAlpha( mColor.a );
|
||||
Color fc1 = ( mColor.a == 255 ) ? c1 : Color( c1 ).blendAlpha( mColor.a );
|
||||
@@ -316,19 +312,6 @@ LinearGradientDrawable::getColorStops() const {
|
||||
|
||||
void LinearGradientDrawable::setColorStops( std::vector<ColorStop> stops ) {
|
||||
mColorStops = std::move( stops );
|
||||
if ( mColorStops.size() >= 2 ) {
|
||||
std::sort(
|
||||
mColorStops.begin(), mColorStops.end(),
|
||||
[]( const ColorStop& a, const ColorStop& b ) { return a.position < b.position; } );
|
||||
if ( !isRepeating() ) {
|
||||
mColorStops.front().position = 0.f;
|
||||
mColorStops.back().position = 1.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LinearGradientDrawable::isRepeating() const {
|
||||
return mDrawableType == REPEATINGLINEARGRADIENT;
|
||||
}
|
||||
|
||||
Float LinearGradientDrawable::getAngle() const {
|
||||
@@ -343,4 +326,8 @@ void LinearGradientDrawable::setSize( const Sizef& size ) {
|
||||
mSize = size;
|
||||
}
|
||||
|
||||
}} // namespace EE::Graphics
|
||||
bool LinearGradientDrawable::isRepeating() const {
|
||||
return mDrawableType == Graphics::Drawable::REPEATINGLINEARGRADIENT;
|
||||
}
|
||||
|
||||
}} // namespace EE::UI
|
||||
@@ -2,20 +2,20 @@
|
||||
#include <cmath>
|
||||
|
||||
#include <eepp/graphics/globalbatchrenderer.hpp>
|
||||
#include <eepp/graphics/radialgradientdrawable.hpp>
|
||||
#include <eepp/math/math.hpp>
|
||||
#include <eepp/ui/radialgradientdrawable.hpp>
|
||||
|
||||
namespace EE { namespace Graphics {
|
||||
namespace EE { namespace UI {
|
||||
|
||||
RadialGradientDrawable* RadialGradientDrawable::New() {
|
||||
return eeNew( RadialGradientDrawable, () );
|
||||
}
|
||||
|
||||
RadialGradientDrawable* RadialGradientDrawable::NewRepeating() {
|
||||
return eeNew( RadialGradientDrawable, ( REPEATINGRADIALGRADIENT ) );
|
||||
return eeNew( RadialGradientDrawable, ( Graphics::Drawable::REPEATINGRADIALGRADIENT ) );
|
||||
}
|
||||
|
||||
RadialGradientDrawable::RadialGradientDrawable( Drawable::Type drawableType ) :
|
||||
RadialGradientDrawable::RadialGradientDrawable( Graphics::Drawable::Type drawableType ) :
|
||||
Drawable( drawableType ) {}
|
||||
|
||||
Sizef RadialGradientDrawable::getSize() {
|
||||
@@ -65,6 +65,17 @@ void RadialGradientDrawable::draw( const Vector2f& position, const Sizef& size )
|
||||
Float cy = mCenter.y * h;
|
||||
Float maxR = computeRadius( cx, cy, w, h, mExtent );
|
||||
|
||||
// Normalize all stops to [0,1] using the gradient ray pixel length
|
||||
std::vector<ColorStop> stops;
|
||||
stops.reserve( mColorStops.size() );
|
||||
for ( const auto& s : mColorStops )
|
||||
stops.push_back(
|
||||
ColorStop( s.getNormalized( maxR ), s.color, CSS::StyleSheetLength::Percentage ) );
|
||||
|
||||
std::sort( stops.begin(), stops.end(),
|
||||
[]( const ColorStop& a, const ColorStop& b ) { return a.value < b.value; } );
|
||||
|
||||
|
||||
const int SEGMENTS = 48;
|
||||
Float angleStep = 2.f * EE_PI / (Float)SEGMENTS;
|
||||
|
||||
@@ -85,8 +96,8 @@ void RadialGradientDrawable::draw( const Vector2f& position, const Sizef& size )
|
||||
}
|
||||
|
||||
if ( isRepeating() ) {
|
||||
Float firstPos = mColorStops.front().position;
|
||||
Float lastPos = mColorStops.back().position;
|
||||
Float firstPos = stops.front().value;
|
||||
Float lastPos = stops.back().value;
|
||||
Float patternLen = lastPos - firstPos;
|
||||
if ( patternLen < 0.0001f ) {
|
||||
sBR->draw();
|
||||
@@ -102,9 +113,9 @@ void RadialGradientDrawable::draw( const Vector2f& position, const Sizef& size )
|
||||
for ( int r = rStart; r <= rEnd; r++ ) {
|
||||
Float repeatOff = (Float)r * patternLen;
|
||||
|
||||
for ( size_t i = 0; i + 1 < mColorStops.size(); i++ ) {
|
||||
Float p0 = mColorStops[i].position + repeatOff;
|
||||
Float p1 = mColorStops[i + 1].position + repeatOff;
|
||||
for ( size_t i = 0; i + 1 < stops.size(); i++ ) {
|
||||
Float p0 = stops[i].value + repeatOff;
|
||||
Float p1 = stops[i + 1].value + repeatOff;
|
||||
|
||||
if ( p1 <= 0.f || p0 >= 1.f )
|
||||
continue;
|
||||
@@ -120,16 +131,18 @@ void RadialGradientDrawable::draw( const Vector2f& position, const Sizef& size )
|
||||
Float bw = p1 - p0;
|
||||
Float frac0 = ( clip0 - p0 ) / bw;
|
||||
Float frac1 = ( clip1 - p0 ) / bw;
|
||||
const Color& sc0 = mColorStops[i].color;
|
||||
const Color& sc1 = mColorStops[i + 1].color;
|
||||
Color cc0( (Uint8)( (Float)sc0.r + frac0 * (Float)( sc1.r - sc0.r ) ),
|
||||
(Uint8)( (Float)sc0.g + frac0 * (Float)( sc1.g - sc0.g ) ),
|
||||
(Uint8)( (Float)sc0.b + frac0 * (Float)( sc1.b - sc0.b ) ),
|
||||
(Uint8)( (Float)sc0.a + frac0 * (Float)( sc1.a - sc0.a ) ) );
|
||||
Color cc1( (Uint8)( (Float)sc0.r + frac1 * (Float)( sc1.r - sc0.r ) ),
|
||||
(Uint8)( (Float)sc0.g + frac1 * (Float)( sc1.g - sc0.g ) ),
|
||||
(Uint8)( (Float)sc0.b + frac1 * (Float)( sc1.b - sc0.b ) ),
|
||||
(Uint8)( (Float)sc0.a + frac1 * (Float)( sc1.a - sc0.a ) ) );
|
||||
const Color& sc0 = stops[i].color;
|
||||
const Color& sc1 = stops[i + 1].color;
|
||||
Color cc0(
|
||||
(Uint8)( (Float)sc0.r + frac0 * (Float)( sc1.r - sc0.r ) ),
|
||||
(Uint8)( (Float)sc0.g + frac0 * (Float)( sc1.g - sc0.g ) ),
|
||||
(Uint8)( (Float)sc0.b + frac0 * (Float)( sc1.b - sc0.b ) ),
|
||||
(Uint8)( (Float)sc0.a + frac0 * (Float)( sc1.a - sc0.a ) ) );
|
||||
Color cc1(
|
||||
(Uint8)( (Float)sc0.r + frac1 * (Float)( sc1.r - sc0.r ) ),
|
||||
(Uint8)( (Float)sc0.g + frac1 * (Float)( sc1.g - sc0.g ) ),
|
||||
(Uint8)( (Float)sc0.b + frac1 * (Float)( sc1.b - sc0.b ) ),
|
||||
(Uint8)( (Float)sc0.a + frac1 * (Float)( sc1.a - sc0.a ) ) );
|
||||
|
||||
Color fc0 = ( mColor.a == 255 ) ? cc0 : Color( cc0 ).blendAlpha( mColor.a );
|
||||
Color fc1 = ( mColor.a == 255 ) ? cc1 : Color( cc1 ).blendAlpha( mColor.a );
|
||||
@@ -147,12 +160,12 @@ void RadialGradientDrawable::draw( const Vector2f& position, const Sizef& size )
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for ( size_t i = 0; i + 1 < mColorStops.size(); i++ ) {
|
||||
Float r0 = mColorStops[i].position * maxR;
|
||||
Float r1 = mColorStops[i + 1].position * maxR;
|
||||
for ( size_t i = 0; i + 1 < stops.size(); i++ ) {
|
||||
Float r0 = stops[i].value * maxR;
|
||||
Float r1 = stops[i + 1].value * maxR;
|
||||
|
||||
const Color& c0 = mColorStops[i].color;
|
||||
const Color& c1 = mColorStops[i + 1].color;
|
||||
const Color& c0 = stops[i].color;
|
||||
const Color& c1 = stops[i + 1].color;
|
||||
Color fc0 = ( mColor.a == 255 ) ? c0 : Color( c0 ).blendAlpha( mColor.a );
|
||||
Color fc1 = ( mColor.a == 255 ) ? c1 : Color( c1 ).blendAlpha( mColor.a );
|
||||
|
||||
@@ -162,8 +175,9 @@ void RadialGradientDrawable::draw( const Vector2f& position, const Sizef& size )
|
||||
Float cj = cosVals[j], sj = sinVals[j];
|
||||
Float cj1 = cosVals[j + 1], sj1 = sinVals[j + 1];
|
||||
|
||||
sBR->batchQuadFree( cx + r0 * cj + posX, cy + r0 * sj + posY, cx + r1 * cj + posX,
|
||||
cy + r1 * sj + posY, cx + r1 * cj1 + posX, cy + r1 * sj1 + posY,
|
||||
sBR->batchQuadFree( cx + r0 * cj + posX, cy + r0 * sj + posY,
|
||||
cx + r1 * cj + posX, cy + r1 * sj + posY,
|
||||
cx + r1 * cj1 + posX, cy + r1 * sj1 + posY,
|
||||
cx + r0 * cj1 + posX, cy + r0 * sj1 + posY );
|
||||
}
|
||||
}
|
||||
@@ -179,15 +193,6 @@ RadialGradientDrawable::getColorStops() const {
|
||||
|
||||
void RadialGradientDrawable::setColorStops( std::vector<ColorStop> stops ) {
|
||||
mColorStops = std::move( stops );
|
||||
if ( mColorStops.size() >= 2 ) {
|
||||
std::sort(
|
||||
mColorStops.begin(), mColorStops.end(),
|
||||
[]( const ColorStop& a, const ColorStop& b ) { return a.position < b.position; } );
|
||||
if ( !isRepeating() ) {
|
||||
mColorStops.front().position = 0.f;
|
||||
mColorStops.back().position = 1.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RadialGradientDrawable::ShapeType RadialGradientDrawable::getShape() const {
|
||||
@@ -219,7 +224,7 @@ void RadialGradientDrawable::setSize( const Sizef& size ) {
|
||||
}
|
||||
|
||||
bool RadialGradientDrawable::isRepeating() const {
|
||||
return mDrawableType == REPEATINGRADIALGRADIENT;
|
||||
return mDrawableType == Graphics::Drawable::REPEATINGRADIALGRADIENT;
|
||||
}
|
||||
|
||||
}} // namespace EE::Graphics
|
||||
}} // namespace EE::UI
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
#include <eepp/graphics/fontfamily.hpp>
|
||||
#include <eepp/graphics/fonttruetype.hpp>
|
||||
#include <eepp/graphics/lineargradientdrawable.hpp>
|
||||
#include <eepp/graphics/radialgradientdrawable.hpp>
|
||||
#include <eepp/scene/scenemanager.hpp>
|
||||
#include <eepp/system/filesystem.hpp>
|
||||
#include <eepp/system/sys.hpp>
|
||||
#include <eepp/ui/css/drawableimageparser.hpp>
|
||||
#include <eepp/ui/css/stylesheetspecification.hpp>
|
||||
#include <eepp/ui/lineargradientdrawable.hpp>
|
||||
#include <eepp/ui/radialgradientdrawable.hpp>
|
||||
#include <eepp/ui/uiscenenode.hpp>
|
||||
#include <eepp/ui/uithememanager.hpp>
|
||||
#include <eepp/ui/uiwidget.hpp>
|
||||
@@ -64,8 +64,8 @@ UTEST( DrawableImageParser, TwoStopsWithPosition ) {
|
||||
auto* grad = static_cast<LinearGradientDrawable*>( drawable );
|
||||
const auto& stops = grad->getColorStops();
|
||||
ASSERT_EQ( stops.size(), (size_t)2 );
|
||||
EXPECT_EQ( stops[0].position, 0.f );
|
||||
EXPECT_EQ( stops[1].position, 1.f );
|
||||
EXPECT_EQ( stops[0].value, 0.f );
|
||||
EXPECT_EQ( stops[1].value, 100.f );
|
||||
Color expect05 = Color::fromString( "#f5eedd" );
|
||||
Color expectEbe = Color::fromString( "#ebe0c2" );
|
||||
EXPECT_TRUE( stops[0].color == expect05 );
|
||||
@@ -161,7 +161,7 @@ UTEST( DrawableImageParser, ThreeStops ) {
|
||||
auto* grad = static_cast<LinearGradientDrawable*>( drawable );
|
||||
const auto& stops = grad->getColorStops();
|
||||
ASSERT_EQ( stops.size(), (size_t)3 );
|
||||
EXPECT_EQ( stops[0].position, 0.f );
|
||||
EXPECT_EQ( stops[0].value, 0.f );
|
||||
EXPECT_TRUE( stops[0].color == Color::fromString( "red" ) );
|
||||
EXPECT_TRUE( stops[1].color == Color::fromString( "green" ) );
|
||||
EXPECT_TRUE( stops[2].color == Color::fromString( "blue" ) );
|
||||
@@ -191,16 +191,16 @@ UTEST( DrawableImageParser, ColorHint ) {
|
||||
* (stop 0 + 15 internal samples + stop 1 = 17, but setColorStops may
|
||||
* re-clamp, so we just check the boundaries and the hint position). */
|
||||
ASSERT_GE( stops.size(), (size_t)17 );
|
||||
EXPECT_EQ( stops[0].position, 0.f );
|
||||
EXPECT_EQ( stops[0].value, 0.f );
|
||||
EXPECT_TRUE( stops[0].color == Color::fromString( "red" ) );
|
||||
EXPECT_EQ( stops[stops.size() - 1].position, 1.f );
|
||||
EXPECT_EQ( stops[stops.size() - 1].value, 100.f );
|
||||
EXPECT_TRUE( stops[stops.size() - 1].color == Color::fromString( "blue" ) );
|
||||
|
||||
/* At the hint position 25%, t = 0.25^0.5 = 0.5, so the color should
|
||||
* be ~50% red, 50% blue (rgb ~127, 0, 127). */
|
||||
bool foundMidpoint = false;
|
||||
for ( const auto& s : stops ) {
|
||||
if ( std::abs( s.position - 0.25f ) < 0.02f ) {
|
||||
if ( std::abs( s.value - 25.f ) < 2.f ) {
|
||||
EXPECT_GT( s.color.r, (Uint8)100 );
|
||||
EXPECT_GT( s.color.b, (Uint8)100 );
|
||||
foundMidpoint = true;
|
||||
@@ -231,9 +231,9 @@ UTEST( DrawableImageParser, StopsWithoutPositions ) {
|
||||
const auto& stops = grad->getColorStops();
|
||||
ASSERT_EQ( stops.size(), (size_t)3 );
|
||||
/* Should be evenly distributed: 0%, 50%, 100% */
|
||||
EXPECT_EQ( stops[0].position, 0.f );
|
||||
EXPECT_NEAR( stops[1].position, 0.5f, 0.01f );
|
||||
EXPECT_EQ( stops[2].position, 1.f );
|
||||
EXPECT_EQ( stops[0].value, 0.f );
|
||||
EXPECT_NEAR( stops[1].value, 50.f, 0.01f );
|
||||
EXPECT_EQ( stops[2].value, 100.f );
|
||||
|
||||
eeSAFE_DELETE( drawable );
|
||||
Engine::destroySingleton();
|
||||
@@ -281,8 +281,8 @@ UTEST( DrawableImageParser, RepeatingTwoStops ) {
|
||||
EXPECT_TRUE( grad->isRepeating() );
|
||||
const auto& stops = grad->getColorStops();
|
||||
ASSERT_EQ( stops.size(), (size_t)2 );
|
||||
EXPECT_EQ( stops[0].position, 0.f );
|
||||
EXPECT_EQ( stops[1].position, 1.f );
|
||||
EXPECT_EQ( stops[0].value, 0.f );
|
||||
EXPECT_EQ( stops[1].value, 100.f );
|
||||
|
||||
eeSAFE_DELETE( drawable );
|
||||
Engine::destroySingleton();
|
||||
@@ -330,8 +330,8 @@ UTEST( DrawableImageParser, RepeatingWithPositions ) {
|
||||
EXPECT_TRUE( grad->isRepeating() );
|
||||
const auto& stops = grad->getColorStops();
|
||||
ASSERT_EQ( stops.size(), (size_t)2 );
|
||||
EXPECT_EQ( stops[0].position, 0.1f );
|
||||
EXPECT_EQ( stops[1].position, 0.4f );
|
||||
EXPECT_EQ( stops[0].value, 10.f );
|
||||
EXPECT_EQ( stops[1].value, 40.f );
|
||||
|
||||
eeSAFE_DELETE( drawable );
|
||||
Engine::destroySingleton();
|
||||
@@ -356,8 +356,8 @@ UTEST( DrawableImageParser, RadialTwoStops ) {
|
||||
EXPECT_FALSE( grad->isRepeating() );
|
||||
const auto& stops = grad->getColorStops();
|
||||
ASSERT_EQ( stops.size(), (size_t)2 );
|
||||
EXPECT_EQ( stops[0].position, 0.f );
|
||||
EXPECT_EQ( stops[1].position, 1.f );
|
||||
EXPECT_EQ( stops[0].value, 0.f );
|
||||
EXPECT_EQ( stops[1].value, 1.f );
|
||||
|
||||
eeSAFE_DELETE( drawable );
|
||||
Engine::destroySingleton();
|
||||
@@ -402,8 +402,8 @@ UTEST( DrawableImageParser, RadialWithPositions ) {
|
||||
auto* grad = static_cast<RadialGradientDrawable*>( drawable );
|
||||
const auto& stops = grad->getColorStops();
|
||||
ASSERT_EQ( stops.size(), (size_t)2 );
|
||||
EXPECT_EQ( stops[0].position, 0.f );
|
||||
EXPECT_EQ( stops[1].position, 1.f );
|
||||
EXPECT_EQ( stops[0].value, 10.f );
|
||||
EXPECT_EQ( stops[1].value, 80.f );
|
||||
|
||||
eeSAFE_DELETE( drawable );
|
||||
Engine::destroySingleton();
|
||||
@@ -428,8 +428,8 @@ UTEST( DrawableImageParser, RepeatingRadial ) {
|
||||
EXPECT_TRUE( grad->isRepeating() );
|
||||
const auto& stops = grad->getColorStops();
|
||||
ASSERT_EQ( stops.size(), (size_t)2 );
|
||||
EXPECT_EQ( stops[0].position, 0.1f );
|
||||
EXPECT_EQ( stops[1].position, 0.4f );
|
||||
EXPECT_EQ( stops[0].value, 10.f );
|
||||
EXPECT_EQ( stops[1].value, 40.f );
|
||||
|
||||
eeSAFE_DELETE( drawable );
|
||||
Engine::destroySingleton();
|
||||
@@ -452,11 +452,18 @@ UTEST( DrawableImageParser, TwoLengthStopSyntax ) {
|
||||
auto* grad = static_cast<LinearGradientDrawable*>( drawable );
|
||||
EXPECT_TRUE( grad->isRepeating() );
|
||||
const auto& stops = grad->getColorStops();
|
||||
/* red 0 10px → two stops: raw pos 0 and raw pos 10. blue 10px 20px →
|
||||
* two stops: raw pos 10 and raw pos 20. All px positions are stored
|
||||
* as raw pixel values (unit != NORMALIZED=true) and normalized at draw time. */
|
||||
ASSERT_EQ( stops.size(), (size_t)4 );
|
||||
EXPECT_EQ( stops[0].position, 0.f );
|
||||
EXPECT_EQ( stops[1].position, 0.1f );
|
||||
EXPECT_EQ( stops[2].position, 0.1f );
|
||||
EXPECT_EQ( stops[3].position, 0.2f );
|
||||
EXPECT_EQ( stops[0].value, 0.f );
|
||||
EXPECT_EQ( stops[0].unit, EE::UI::CSS::StyleSheetLength::Percentage );
|
||||
EXPECT_EQ( stops[1].value, 10.f );
|
||||
EXPECT_EQ( stops[1].unit, EE::UI::CSS::StyleSheetLength::Px );
|
||||
EXPECT_EQ( stops[2].value, 10.f );
|
||||
EXPECT_EQ( stops[2].unit, EE::UI::CSS::StyleSheetLength::Px );
|
||||
EXPECT_EQ( stops[3].value, 20.f );
|
||||
EXPECT_EQ( stops[3].unit, EE::UI::CSS::StyleSheetLength::Px );
|
||||
|
||||
eeSAFE_DELETE( drawable );
|
||||
Engine::destroySingleton();
|
||||
|
||||
Reference in New Issue
Block a user