Add CSSLength, CSSBackground, CSSBackgroundRenderer classes

develop
Nimetu 3 years ago
parent 65edd9f95d
commit dfe45029ab

@ -0,0 +1,83 @@
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010-2019 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef CL_CSS_BACKGROUND_H
#define CL_CSS_BACKGROUND_H
#include "nel/misc/types_nl.h"
#include "nel/misc/rgba.h"
#include "nel/gui/css_types.h"
#include "nel/gui/css_length.h"
namespace NLGUI
{
/**
* \brief CSS background info
* \date 2021-07-02 11:36 GMT
* \author Meelis Mägi (Nimetu)
*/
class CSSBackground
{
public:
CSSBackground()
:color(NLMISC::CRGBA::Transparent),
repeatX(CSS_VALUE_REPEAT), repeatY(CSS_VALUE_REPEAT), attachment(CSS_VALUE_SCROLL),
xAnchor(CSS_VALUE_LEFT), yAnchor(CSS_VALUE_TOP),
clip(CSS_VALUE_BORDER_BOX), origin(CSS_VALUE_PADDING_BOX), size(CSS_VALUE_AUTO)
{}
void setImage(const std::string &value);
void setPosition(const std::string &value);
void setSize(const std::string &value);
void setRepeat(const std::string &value);
void setOrigin(const std::string &value);
void setClip(const std::string &value);
void setAttachment(const std::string &value);
void setColor(const std::string &value);
public:
// TODO: only final layer has color
NLMISC::CRGBA color;
std::string image;
CSSValueType repeatX;
CSSValueType repeatY;
CSSValueType attachment;
CSSValueType xAnchor;
CSSValueType yAnchor;
CSSLength xPosition;
CSSLength yPosition;
CSSValueType clip;
CSSValueType origin;
CSSValueType size;
CSSLength width;
CSSLength height;
private:
void positionFromOne(const std::vector<std::string> &parts);
void positionFromTwo(const std::vector<std::string> &parts);
void positionFromThree(const std::vector<std::string> &parts);
void positionFromFour(const std::vector<std::string> &parts);
};
}//namespace
#endif // CL_CSS_BACKGROUND_H

@ -0,0 +1,193 @@
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010-2019 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef NL_CSS_BACKGROUND_RENDERER_H
#define NL_CSS_BACKGROUND_RENDERER_H
#include "nel/misc/types_nl.h"
#include "nel/misc/rgba.h"
#include "nel/misc/geom_ext.h"
#include "nel/gui/css_types.h"
#include "nel/gui/css_background.h"
namespace NLGUI
{
class CInterfaceElement;
/**
* \brief Border renderer for GUI classes
* \date 2021-06-29 15:17 GMT
* \author Meelis Mägi (Nimetu)
*/
class CSSBackgroundRenderer
{
public:
// alpha value from parent
uint8 CurrentAlpha;
// TODO: should be moved to CSSBackground
sint32 TextureId;
public:
CSSBackgroundRenderer();
~CSSBackgroundRenderer();
// return true if no background is set
bool isEmpty() const
{
return m_Background.image.empty() && m_Background.color.A == 0;
}
void setModulateGlobalColor(bool m) { m_ModulateGlobalColor = m; }
void updateCoords();
void invalidateCoords() { m_Dirty = true; }
void invalidateContent() { m_Dirty = true; };
void clear();
void setBackground(const CSSBackground &bg);
// helper function to change background image
void setImage(const std::string &bgtex);
void setImageRepeat(bool b);
void setImageCover(bool b);
// helper function to change background color
void setColor(const NLMISC::CRGBA &color)
{
m_Dirty = true;
m_Background.color = color;
}
NLMISC::CRGBA getColor() const
{
return m_Background.color;
}
// override painting area to be at least the size of viewport (ie, <html> background)
void setFillViewport(bool b) {
m_Dirty = true;
m_FillViewport = b;
}
void setViewport(CInterfaceElement *root)
{
m_Dirty = true;
m_Viewport = root;
}
void setBorderArea(sint32 x, sint32 y, sint32 w, sint32 h)
{
m_Dirty = true;
m_BorderX = x;
m_BorderY = y;
m_BorderW = w;
m_BorderH = h;
}
void setPaddingArea(sint32 x, sint32 y, sint32 w, sint32 h)
{
m_Dirty = true;
m_PaddingX = x;
m_PaddingY = y;
m_PaddingW = w;
m_PaddingH = h;
}
void setContentArea(sint32 x, sint32 y, sint32 w, sint32 h)
{
m_Dirty = true;
m_ContentX = x;
m_ContentY = y;
m_ContentW = w;
m_ContentH = h;
}
// sizes for em, rem units
void setFontSize(float rootFontSize, float fontSize)
{
m_Dirty = true;
m_RootFontSize = rootFontSize;
m_FontSize = fontSize;
}
void draw();
private:
sint32 m_BorderX, m_BorderY, m_BorderW, m_BorderH;
sint32 m_PaddingX, m_PaddingY, m_PaddingW, m_PaddingH;
sint32 m_ContentX, m_ContentY, m_ContentW, m_ContentH;
// font size for 'rem'
float m_RootFontSize;
// font size for 'em'
float m_FontSize;
// viewport element for vw,wh,vmin,vmax
CInterfaceElement* m_Viewport;
struct SDrawQueue
{
sint32 TextureId;
NLMISC::CQuadUV Quad;
NLMISC::CRGBA Color;
};
std::vector<SDrawQueue> m_DrawQueue;
const sint8 m_RenderLayer;
bool m_ModulateGlobalColor;
// if true, painting area returns area at least the size of viewport (ie, <html> background)
bool m_FillViewport;
// if true, then updateCoords() is called from draw()
bool m_Dirty;
CSSBackground m_Background;
// get clip area based on background-clip
void getPaintingArea(const CSSBackground &bg, sint32 &areaX, sint32 &areaY, sint32 &areaW, sint32 &areaH) const;
// get positioning area based on background-origin
void getPositioningArea(const CSSBackground &bg, sint32 &areaX, sint32 &areaY, sint32 &areaW, sint32 &areaH) const;
// calculate image size based on background-size
void calculateSize(const CSSBackground &bg, sint32 &texW, sint32 &texH) const;
// calculate image position based on background-position
void calculatePosition(const CSSBackground &bg, sint32 &texX, sint32 &texY, sint32 &texW, sint32 &texH) const;
// calculate image tile position, size, count, and spacing based on background-repeat
void calculateTiles(const CSSBackground &bg, sint32 &texX, sint32 &texY, sint32 &texW, sint32 &texH, sint32 &tilesX, sint32 &tilesY, sint32 &spacingX, sint32 &spacingY) const;
// position, size, and count for first tile to cover an area
void getImageTile(sint32 &tilePos, sint32 &tileSize, sint32 &spacing, sint32 &tiles, sint32 areaPos, sint32 areaSize, CSSValueType repeat) const;
// push background color to draw queue
void buildColorQuads(const CSSBackground &bg);
// push background image to draw quque
void buildImageQuads(const CSSBackground &bg, sint32 textureId);
}; // CSSBackgroundRenderer
}//namespace
#endif // NL_CSS_BACKGROUND_RENDERER_H

@ -0,0 +1,76 @@
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010-2021 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef CL_CSS_LENGTH_H
#define CL_CSS_LENGTH_H
#include "nel/misc/types_nl.h"
#include "nel/gui/css_types.h"
namespace NLGUI
{
/**
* \brief CSS types used in GUI classes
* \date 2021-07-02 11:36 GMT
* \author Meelis Mägi (Nimetu)
*/
class CSSLength
{
public:
enum Kind {
Auto, Relative, Fixed
};
CSSLength(float value = 0, CSSUnitType unit = CSS_UNIT_NONE, Kind kind = Auto)
: m_Value(value), m_Unit(unit), m_Kind(Auto)
{}
void setAuto() { m_Kind = Auto; }
bool parseValue(const std::string &value, bool allowPercent = true, bool allowNegative = false);
void setFloatValue(float f, const std::string &unit);
float getValue() const;
float getFloat() const { return m_Value; }
bool isPercent() const { return m_Unit == CSS_UNIT_PERCENT; }
bool isAuto() const { return m_Kind == Auto; }
bool isRelative() const { return m_Kind == Relative; }
// % uses relValue
// em uses emSize
// rem uses remSize
// vw,vh,vi,vb,vmin,vmax uses vwSize/vhSize
float calculate(uint32 relValue, uint32 emSize, uint32 remSize, uint32 vwSize, uint32 whSize) const;
CSSUnitType getUnit() const { return m_Unit; }
std::string toString() const;
private:
void setUnit(const std::string &unit);
float m_Value;
CSSUnitType m_Unit;
Kind m_Kind;
};
}//namespace
#endif // CL_CSS_LENGTH_H

@ -47,6 +47,44 @@ namespace NLGUI
CSS_LINE_WIDTH_THICK = 5
};
enum CSSUnitType {
CSS_UNIT_NONE = 0,
CSS_UNIT_EM,
CSS_UNIT_REM,
CSS_UNIT_PERCENT,
CSS_UNIT_PX,
CSS_UNIT_PT,
CSS_UNIT_VW,
CSS_UNIT_VH,
CSS_UNIT_VI,
CSS_UNIT_VB,
CSS_UNIT_VMIN,
CSS_UNIT_VMAX
};
enum CSSValueType {
CSS_VALUE_NONE = 0,
CSS_VALUE_REPEAT,
CSS_VALUE_SPACE,
CSS_VALUE_ROUND,
CSS_VALUE_NOREPEAT,
CSS_VALUE_FIXED,
CSS_VALUE_LOCAL,
CSS_VALUE_SCROLL,
CSS_VALUE_LEFT,
CSS_VALUE_CENTER,
CSS_VALUE_RIGHT,
CSS_VALUE_TOP,
CSS_VALUE_BOTTOM,
CSS_VALUE_BORDER_BOX,
CSS_VALUE_PADDING_BOX,
CSS_VALUE_CONTENT_BOX,
CSS_VALUE_LENGTH,
CSS_VALUE_AUTO,
CSS_VALUE_COVER,
CSS_VALUE_CONTAIN
};
}//namespace
#endif // CL_CSS_TYPES_H

@ -0,0 +1,427 @@
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010-2021 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdpch.h"
#include <string>
#include "nel/gui/libwww.h"
#include "nel/gui/css_length.h"
#include "nel/gui/css_background.h"
using namespace NLMISC;
#ifdef DEBUG_NEW
#define new DEBUG_NEW
#endif
namespace NLGUI
{
void CSSBackground::setImage(const std::string &value)
{
image = value;
}
void CSSBackground::setPosition(const std::string &value)
{
std::vector<std::string> parts;
splitString(toLowerAscii(value), " ", parts);
if (parts.empty() || parts.size() > 4)
return;
switch(parts.size())
{
case 1:
positionFromOne(parts);
break;
case 2:
positionFromTwo(parts);
break;
case 3:
positionFromThree(parts);
break;
case 4:
positionFromFour(parts);
break;
default:
return;
}
}
void CSSBackground::setSize(const std::string &value)
{
std::vector<std::string> parts;
splitString(toLowerAscii(value), " ", parts);
if (parts.size() > 2)
return;
if (parts.size() == 1 && (parts[0] == "cover" || parts[0] == "contain"))
{
if (parts[0] == "cover")
size = CSS_VALUE_COVER;
else
size = CSS_VALUE_CONTAIN;
width.setAuto();
height.setAuto();
return;
}
// height will default to 'auto' if not set
if (parts.size() == 1)
parts.push_back("auto");
if (parts[0] == "auto" && parts[1] == "auto")
{
size = CSS_VALUE_AUTO;
width.setAuto();
height.setAuto();
return;
}
CSSLength newW, newH;
bool success = true;
if (parts[0] == "auto")
{
newW.setAuto();
}
else
{
float fval;
std::string unit;
if (!getCssLength(fval, unit, parts[0]))
{
nlwarning("Failed to parse background-size[0] '%s'", parts[0].c_str());
return;
}
newW.setFloatValue(fval, unit);
}
if (parts[1] == "auto")
{
newH.setAuto();
}
else
{
float fval;
std::string unit;
if (!getCssLength(fval, unit, parts[1]))
{
nlwarning("Failed to parse background-size[1] '%s'", parts[1].c_str());
return;
}
newH.setFloatValue(fval, unit);
}
size = CSS_VALUE_LENGTH;
width = newW;
height = newH;
}
void CSSBackground::setRepeat(const std::string &value)
{
std::vector<std::string> parts;
splitString(toLowerAscii(value), " ", parts);
if (parts.size() == 0 || parts.size() > 2)
return;
if (parts.size() == 1)
{
if (parts[0] == "repeat-x")
parts.push_back("no-repeat");
else if (parts[0] == "repeat-y")
parts.insert(parts.begin(), "no-repeat");
else //repeat, space, round, no-repeat
parts.push_back(parts[0]);
}
if (parts[0] == "repeat") repeatX = CSS_VALUE_REPEAT;
else if (parts[0] == "no-repeat") repeatX = CSS_VALUE_NOREPEAT;
else if (parts[0] == "space") repeatX = CSS_VALUE_SPACE;
else if (parts[0] == "round") repeatX = CSS_VALUE_ROUND;
else repeatX = CSS_VALUE_REPEAT;
if (parts[1] == "repeat") repeatY = CSS_VALUE_REPEAT;
else if (parts[1] == "no-repeat") repeatY = CSS_VALUE_NOREPEAT;
else if (parts[1] == "space") repeatY = CSS_VALUE_SPACE;
else if (parts[1] == "round") repeatY = CSS_VALUE_ROUND;
else repeatY = CSS_VALUE_REPEAT;
}
void CSSBackground::setOrigin(const std::string &value)
{
if (value == "border-box") origin = CSS_VALUE_BORDER_BOX;
else if (value == "padding-box") origin = CSS_VALUE_PADDING_BOX;
else if (value == "content-box") origin = CSS_VALUE_CONTENT_BOX;
else origin = CSS_VALUE_PADDING_BOX;
}
void CSSBackground::setClip(const std::string &value)
{
if (value == "border-box") clip = CSS_VALUE_BORDER_BOX;
else if (value == "padding-box") clip = CSS_VALUE_PADDING_BOX;
else if (value == "content-box") clip = CSS_VALUE_CONTENT_BOX;
//else if (value == "text") clip = CSSValueType::Text;
else clip = CSS_VALUE_PADDING_BOX;
}
void CSSBackground::setAttachment(const std::string &value)
{
if (value == "fixed") attachment = CSS_VALUE_FIXED;
else if (value == "local") attachment = CSS_VALUE_LOCAL;
else if (value == "scroll") attachment = CSS_VALUE_SCROLL;
else attachment = CSS_VALUE_SCROLL;
}
void CSSBackground::setColor(const std::string &value)
{
NLMISC::CRGBA tmp;
if (scanHTMLColor(value.c_str(), tmp))
color = tmp;
}
static bool isHorizontalKeyword(const std::string &val)
{
return val == "left" || val == "right";
}
static bool isVerticalKeyword(const std::string &val)
{
return val == "top" || val == "bottom";
}
void CSSBackground::positionFromOne(const std::vector<std::string> &parts)
{
CSSValueType newH = CSS_VALUE_LEFT;
CSSValueType newV = CSS_VALUE_TOP;
CSSLength newX, newY;
newX.setFloatValue(0, "%");
newY.setFloatValue(0, "%");
uint index = 0;
float fval;
std::string unit;
if (isHorizontalKeyword(parts[index]))
{
newH = parts[index] == "left" ? CSS_VALUE_LEFT : CSS_VALUE_RIGHT;
newV = CSS_VALUE_CENTER;
}
else if (isVerticalKeyword(parts[index]))
{
newH = CSS_VALUE_CENTER;
newV = parts[index] == "top" ? CSS_VALUE_TOP : CSS_VALUE_BOTTOM;
}
else if (parts[index] == "center")
{
newH = CSS_VALUE_CENTER;
newV = CSS_VALUE_CENTER;
}
else if (getCssLength(fval, unit, parts[index], true))
{
newX.setFloatValue(fval, unit);
newV = CSS_VALUE_CENTER;
}
else
{
return;
}
xAnchor = newH;
yAnchor = newV;
xPosition = newX;
yPosition = newY;
}
void CSSBackground::positionFromTwo(const std::vector<std::string> &parts)
{
CSSValueType newH = CSS_VALUE_LEFT;
CSSValueType newV = CSS_VALUE_TOP;
CSSLength newX, newY;
newX.setFloatValue(0, "%");
newY.setFloatValue(0, "%");
float fval;
std::string unit;
uint index = 0;
bool hasCenter = false;
bool hasX = false;
bool hasY = false;
for (uint index = 0; index < parts.size(); index++)
{
if (parts[index] == "center")
{
hasCenter = true;
}
else if (isHorizontalKeyword(parts[index]))
{
if (hasX) return;
hasX = true;
newH = parts[index] == "left" ? CSS_VALUE_LEFT : CSS_VALUE_RIGHT;
}
else if (isVerticalKeyword(parts[index]))
{
if (hasY) return;
hasY = true;
newV = parts[index] == "top" ? CSS_VALUE_TOP : CSS_VALUE_BOTTOM;
}
else if (getCssLength(fval, unit, parts[index], true))
{
// invalid: 'top 50%';
if (hasY) return;
if (!hasX)
{
hasX = true;
newX.setFloatValue(fval, unit);
}
else
{
hasY = true;
newY.setFloatValue(fval, unit);
}
}
else
{
return;
}
}
if (hasCenter)
{
if (!hasX)
newH = CSS_VALUE_CENTER;
if (!hasY)
newV = CSS_VALUE_CENTER;
}
xAnchor = newH;
yAnchor = newV;
xPosition = newX;
yPosition = newY;
}
void CSSBackground::positionFromThree(const std::vector<std::string> &parts)
{
CSSValueType newH = CSS_VALUE_LEFT;
CSSValueType newV = CSS_VALUE_TOP;
CSSLength newX, newY;
newX.setFloatValue(0, "%");
newY.setFloatValue(0, "%");
float fval;
std::string unit;
bool hasCenter = false;
bool hasX = false;
bool hasY = false;
for(uint index = 0; index < 3; index++)
{
if (parts[index] == "center")
{
if (hasCenter) return;
hasCenter = true;
}
else if (isHorizontalKeyword(parts[index]))
{
if (hasX) return;
hasX = true;
newH = parts[index] == "left" ? CSS_VALUE_LEFT : CSS_VALUE_RIGHT;
if ((index+1) < parts.size() && getCssLength(fval, unit, parts[index+1], true))
{
newX.setFloatValue(fval, unit);
index++;
}
}
else if (isVerticalKeyword(parts[index]))
{
if (hasY) return;
hasY = true;
newV = parts[index] == "top" ? CSS_VALUE_TOP : CSS_VALUE_BOTTOM;
if ((index+1) < parts.size() && getCssLength(fval, unit, parts[index+1], true))
{
newY.setFloatValue(fval, unit);
index++;
}
}
else
{
return;
}
}
if (hasCenter)
{
if (hasX && hasY)
return;
if (!hasX)
newH = CSS_VALUE_CENTER;
else
newV = CSS_VALUE_CENTER;
}
xAnchor = newH;
yAnchor = newV;
xPosition = newX;
yPosition = newY;
}
void CSSBackground::positionFromFour(const std::vector<std::string> &parts)
{
CSSValueType newH = CSS_VALUE_LEFT;
CSSValueType newV = CSS_VALUE_TOP;
CSSLength newX, newY;
newX.setFloatValue(0, "%");
newY.setFloatValue(0, "%");
float fval;
std::string unit;
bool hasX = false;
bool hasY = false;
for(uint index = 0; index<4; index+=2)
{
if (parts[index] == "center")
return;
if (isHorizontalKeyword(parts[index]))
{
if (hasX) return;
hasX = true;
if (!getCssLength(fval, unit, parts[index+1], true)) return;
newH = parts[index] == "left" ? CSS_VALUE_LEFT : CSS_VALUE_RIGHT;
newX.setFloatValue(fval, unit);
}
else if (isVerticalKeyword(parts[index]))
{
if (hasY) return;
hasY = true;
if (!getCssLength(fval, unit, parts[index+1], true)) return;
newV = parts[index] == "top" ? CSS_VALUE_TOP : CSS_VALUE_BOTTOM;
newY.setFloatValue(fval, unit);
}
else
{
return;
}
}
xAnchor = newH;
yAnchor = newV;
xPosition = newX;
yPosition = newY;
}
} // namespace

@ -0,0 +1,607 @@
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010-2019 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdpch.h"
#include "nel/gui/css_background_renderer.h"
#include "nel/gui/css_border_renderer.h"
#include "nel/gui/view_renderer.h"
#include "nel/gui/widget_manager.h"
#include "nel/gui/view_bitmap.h"
using namespace std;
using namespace NLMISC;
#ifdef DEBUG_NEW
#define new DEBUG_NEW
#endif
namespace NLGUI
{
// ----------------------------------------------------------------------------
CSSBackgroundRenderer::CSSBackgroundRenderer()
: CurrentAlpha(255), TextureId(-1),
m_BorderX(0), m_BorderY(0), m_BorderW(0), m_BorderH(0),
m_PaddingX(0), m_PaddingY(0), m_PaddingW(0), m_PaddingH(0),
m_ContentX(0), m_ContentY(0), m_ContentW(0), m_ContentH(0),
m_RootFontSize(16.f), m_FontSize(16.f), m_Viewport(NULL),
m_RenderLayer(0), m_ModulateGlobalColor(false), m_FillViewport(false),
m_Dirty(false)
{
}
// ----------------------------------------------------------------------------
CSSBackgroundRenderer::~CSSBackgroundRenderer()
{
if (TextureId != -1)
CViewRenderer::getInstance()->deleteTexture(TextureId);
}
// ----------------------------------------------------------------------------
void CSSBackgroundRenderer::clear()
{
m_Dirty = true;
if (TextureId != -1)
CViewRenderer::getInstance()->deleteTexture(TextureId);
TextureId = -1;
m_Background.image.clear();
m_Background.color.A = 0;
}
// ----------------------------------------------------------------------------
void CSSBackgroundRenderer::setBackground(const CSSBackground &bg)
{
m_Dirty = true;
// TODO: CSSBackground should keep track of TextureId
CViewRenderer &rVR = *CViewRenderer::getInstance();
if (bg.image != m_Background.image && TextureId != -1)
rVR.deleteTexture(TextureId);
m_Background = bg;
// TODO: does not accept urls
if (TextureId == -1 && !bg.image.empty())
{
// TODO: make CViewRenderer accept urls
if (bg.image.find("://") != std::string::npos)
TextureId = rVR.createTexture(bg.image, 0, 0, -1, -1, false);
}
}
// ----------------------------------------------------------------------------
void CSSBackgroundRenderer::setImage(const std::string &bgtex)
{
m_Dirty = true;
// TODO: CSSBackground should keep track of TextureId
CViewRenderer &rVR = *CViewRenderer::getInstance();
if (bgtex != m_Background.image && TextureId != -1)
{
rVR.deleteTexture(TextureId);
TextureId = -1;
}
m_Background.image = bgtex;
if (TextureId == -1 && !bgtex.empty())
{
// TODO: make CViewRenderer accept urls
if (bgtex.find("://") != std::string::npos)
TextureId = rVR.createTexture(bgtex, 0, 0, -1, -1, false);
}
}
// ----------------------------------------------------------------------------
void CSSBackgroundRenderer::setImageRepeat(bool b)
{
m_Background.repeatX = b ? CSS_VALUE_REPEAT : CSS_VALUE_NOREPEAT;
m_Background.repeatY = b ? CSS_VALUE_REPEAT : CSS_VALUE_NOREPEAT;
}
// ----------------------------------------------------------------------------
void CSSBackgroundRenderer::setImageCover(bool b)
{
m_Background.size = b ? CSS_VALUE_COVER : CSS_VALUE_AUTO;
}
// ----------------------------------------------------------------------------
void CSSBackgroundRenderer::updateCoords()
{
m_Dirty = false;
m_DrawQueue.clear();
// TODO: color from last background layer
buildColorQuads(m_Background);
// -------------------------------------------------------------------
// background-image
buildImageQuads(m_Background, TextureId);
}
// ----------------------------------------------------------------------------
void CSSBackgroundRenderer::draw() {
if (m_Dirty) updateCoords();
if (m_DrawQueue.empty()) return;
CViewRenderer &rVR = *CViewRenderer::getInstance();
// flush draw cache to ensure correct draw order
rVR.flush();
sint32 clipX, clipY, clipW, clipH;
if (m_Viewport)
{
rVR.getClipWindow(clipX, clipY, clipW, clipH);
rVR.setClipWindow(m_Viewport->getXReal(), m_Viewport->getYReal(), m_Viewport->getWReal(), m_Viewport->getHReal());
}
// TODO: no need for widget manager, if global color is set from parent
CRGBA globalColor;
if (m_ModulateGlobalColor)
globalColor = CWidgetManager::getInstance()->getGlobalColor();
// TODO: there might be issue on draw order IF using multiple textures
// and second (top) texture is created before first one.
for(uint i = 0; i < m_DrawQueue.size(); ++i)
{
CRGBA color = m_DrawQueue[i].Color;
if (m_ModulateGlobalColor)
color.modulateFromColor (color, globalColor);
color.A = (uint8) (((uint16) CurrentAlpha * (uint16) color.A) >> 8);
rVR.drawQuad(m_RenderLayer, m_DrawQueue[i].Quad, m_DrawQueue[i].TextureId, color, false);
}
// flush draw cache to ensure correct draw order
rVR.flush();
if (m_Viewport)
rVR.setClipWindow(clipX, clipY, clipW, clipH);
}
// ----------------------------------------------------------------------------
void CSSBackgroundRenderer::getPositioningArea(const CSSBackground &bg, sint32 &areaX, sint32 &areaY, sint32 &areaW, sint32 &areaH) const
{
switch(bg.origin)
{
case CSS_VALUE_PADDING_BOX:
areaX = m_PaddingX;
areaY = m_PaddingY;
areaW = m_PaddingW;
areaH = m_PaddingH;
break;
case CSS_VALUE_CONTENT_BOX:
areaX = m_ContentX;
areaY = m_ContentY;
areaW = m_ContentW;
areaH = m_ContentH;
break;
case CSS_VALUE_BORDER_BOX:
// fall thru
default:
areaX = m_BorderX;
areaY = m_BorderY;
areaW = m_BorderW;
areaH = m_BorderH;
break;
}
}
// ----------------------------------------------------------------------------
void CSSBackgroundRenderer::getPaintingArea(const CSSBackground &bg, sint32 &areaX, sint32 &areaY, sint32 &areaW, sint32 &areaH) const
{
switch(bg.clip)
{
case CSS_VALUE_PADDING_BOX:
areaX = m_PaddingX;
areaY = m_PaddingY;
areaW = m_PaddingW;
areaH = m_PaddingH;
break;
case CSS_VALUE_CONTENT_BOX:
areaX = m_ContentX;
areaY = m_ContentY;
areaW = m_ContentW;
areaH = m_ContentH;
break;
case CSS_VALUE_BORDER_BOX:
// fall thru
default:
areaX = m_BorderX;
areaY = m_BorderY;
areaW = m_BorderW;
areaH = m_BorderH;
break;
}
if (m_FillViewport && m_Viewport)
{
sint32 newX = std::min(areaX, m_Viewport->getXReal());
sint32 newY = std::min(areaY, m_Viewport->getYReal());
areaW = std::max(areaX + areaW, m_Viewport->getXReal() + m_Viewport->getWReal()) - newX;
areaH = std::max(areaY + areaH, m_Viewport->getYReal() + m_Viewport->getHReal()) - newY;
areaX = newX;
areaY = newY;
}
}
// ----------------------------------------------------------------------------
void CSSBackgroundRenderer::calculateSize(const CSSBackground &bg, sint32 &texW, sint32 &texH) const
{
sint32 areaX, areaY, areaW, areaH;
getPositioningArea(bg, areaX, areaY, areaW, areaH);
sint32 vpW=0;
sint32 vpH=0;
if (m_Viewport)
{
vpW = m_Viewport->getWReal();
vpH = m_Viewport->getHReal();
}
float whRatio = (float)texW / (float)texH;
switch(bg.size)
{
case CSS_VALUE_LENGTH:
{
if (bg.width.isAuto() && bg.height.isAuto())
{
// no-op
}
else if (bg.width.isAuto())
{
texH = bg.height.calculate(areaH, m_FontSize, m_RootFontSize, vpW, vpH);
texW = texH * whRatio;
}
else if (bg.height.isAuto())
{
// calculate Height
texW = bg.width.calculate(areaW, m_FontSize, m_RootFontSize, vpW, vpH);
texH = texW / whRatio;
}
else
{
texW = bg.width.calculate(areaW, m_FontSize, m_RootFontSize, vpW, vpH);
texH = bg.height.calculate(areaH, m_FontSize, m_RootFontSize, vpW, vpH);
}
break;
}
case CSS_VALUE_AUTO:
{
// no-op
break;
}
case CSS_VALUE_COVER:
{
float canvasRatio = (float)areaW / (float)areaH;
if (whRatio < canvasRatio)
{
texW = areaW;
texH = areaW / whRatio;
} else {
texW = areaH * whRatio;
texH = areaH;
}
break;
}
case CSS_VALUE_CONTAIN:
{
// same as covert, but ratio check is reversed
float canvasRatio = (float)areaW / (float)areaH;
if (whRatio > canvasRatio)
{
texW = areaW;
texH = areaW / whRatio;
} else {
texW = areaH * whRatio;
texH = areaH;
}
break;
}
}
}
// ----------------------------------------------------------------------------
void CSSBackgroundRenderer::calculatePosition(const CSSBackground &bg, sint32 &texX, sint32 &texY, sint32 &texW, sint32 &texH) const
{
sint32 areaX, areaY, areaW, areaH;
getPositioningArea(bg, areaX, areaY, areaW, areaH);
sint32 vpW=0;
sint32 vpH=0;
if (m_Viewport)
{
vpW = m_Viewport->getWReal();
vpH = m_Viewport->getHReal();
}
float ofsX = bg.xPosition.calculate(1, m_FontSize, m_RootFontSize, vpW, vpH);
float ofsY = bg.yPosition.calculate(1, m_FontSize, m_RootFontSize, vpW, vpH);
if (bg.xPosition.isPercent() || bg.xAnchor == CSS_VALUE_CENTER)
{
if (bg.xAnchor == CSS_VALUE_RIGHT)
ofsX = 1.f - ofsX;
else if (bg.xAnchor == CSS_VALUE_CENTER)
ofsX = 0.5f;
ofsX = (float)(areaW - texW) * ofsX;
}
else if (bg.xAnchor == CSS_VALUE_RIGHT)
{
ofsX = areaW - texW - ofsX;
}
// areaY is bottom edge, areaY+areaH is top edge
if (bg.yPosition.isPercent() || bg.yAnchor == CSS_VALUE_CENTER)
{
if (bg.yAnchor == CSS_VALUE_TOP)
ofsY = 1.f - ofsY;
else if (bg.yAnchor == CSS_VALUE_CENTER)
ofsY = 0.5f;
ofsY = (float)(areaH - texH) * ofsY;
}
else if (bg.yAnchor == CSS_VALUE_TOP)
{
ofsY = areaH - texH - ofsY;
}
texX = areaX + ofsX;
texY = areaY + ofsY;
}
// ----------------------------------------------------------------------------
void CSSBackgroundRenderer::getImageTile(sint32 &tilePos, sint32 &tileSize, sint32 &spacing, sint32 &tiles, sint32 areaPos, sint32 areaSize, CSSValueType repeat) const
{
switch(repeat)
{
case CSS_VALUE_NOREPEAT:
{
tiles = 1;
spacing = 0;
break;
}
case CSS_VALUE_SPACE:
{
// if no space for 2+ tiles, then show single one on calculated tilePos
if (tileSize * 2 > areaSize)
{
// set spacing large enough to only display single tile
tiles = 1;
spacing = areaSize;
}
else
{
// available for middle tiles
sint32 midSize = (areaSize - tileSize*2);
// number of middle tiles
sint32 midTiles = midSize / tileSize;
tiles = 2 + midTiles;
tilePos = areaPos;
// int div for floor()
spacing = ( midSize - tileSize * midTiles) / (midTiles + 1);
}
break;
}
case CSS_VALUE_ROUND:
// fall-thru - size is already calculated
case CSS_VALUE_REPEAT:
// fall-thru
default:
{
tilePos -= std::ceil(abs(tilePos - areaPos)/(float)tileSize)*tileSize;
tiles = std::ceil((std::abs(areaPos - tilePos) + areaSize) / (float)tileSize);
spacing = 0;
break;
}
}
}
// ----------------------------------------------------------------------------
void CSSBackgroundRenderer::calculateTiles(const CSSBackground &bg, sint32 &texX, sint32 &texY, sint32 &texW, sint32 &texH, sint32 &tilesX, sint32 &tilesY, sint32 &spacingX, sint32 &spacingY) const
{
sint32 areaX, areaY, areaW, areaH;
getPositioningArea(bg, areaX, areaY, areaW, areaH);
// texX,texY is for position area (ie content-box), but painting area can be bigger (ie border-box)
sint32 paintX, paintY, paintW, paintH;
getPaintingArea(bg, paintX, paintY, paintW, paintH);
if (paintX < areaX)
areaX -= std::ceil((areaX - paintX) / (float)texW) * texW;
if ((paintX + paintW) > (areaX + areaW))
areaW += std::ceil(((paintX + paintW) - (areaX + areaW)) / (float)texW) * texW;
if (paintY < areaY)
areaY -= std::ceil((areaY - paintY) / (float)texH) * texH;
if ((paintY + paintH) > (areaY + areaH))
areaH += std::ceil(((paintY + paintH) - (areaY + areaH)) / (float)texH) * texH;
if (texW <= 0 || texH <= 0 || areaW <= 0 || areaH <= 0)
{
tilesX = tilesY = 0;
spacingX = spacingY = 0;
return;
}
if (bg.repeatX == CSS_VALUE_ROUND)
{
sint numTiles = std::max(1, (sint)std::round((float)areaW / texW));
texW = areaW / numTiles;
if (bg.height.isAuto() && bg.repeatY != CSS_VALUE_ROUND)
{
float aspect = (float)areaW / (numTiles * texW);
texH = texW * aspect;
}
}
if (bg.repeatY == CSS_VALUE_ROUND)
{
sint numTiles = std::max(1, (sint)std::round((float)areaH / texH));
texH = areaH / numTiles;
if (bg.width.isAuto() && bg.repeatX != CSS_VALUE_ROUND)
{
float aspect = (float)areaH / (numTiles * texH);
texW = texH * aspect;
}
}
getImageTile(texX, texW, spacingX, tilesX, areaX, areaW, bg.repeatX);
getImageTile(texY, texH, spacingY, tilesY, areaY, areaH, bg.repeatY);
}
// ----------------------------------------------------------------------------
void CSSBackgroundRenderer::buildColorQuads(const CSSBackground &bg)
{
if (bg.color.A == 0)
return;
// painting area defined with background-clip
sint32 x, y, w, h;
getPaintingArea(bg, x, y, w, h);
if (w <= 0 || h <= 0)
return;
CViewRenderer &rVR = *CViewRenderer::getInstance();
SDrawQueue shape;
shape.Quad.Uv0.set(0.f, 1.f);
shape.Quad.Uv1.set(1.f, 1.f);
shape.Quad.Uv2.set(1.f, 0.f);
shape.Quad.Uv3.set(0.f, 0.f);
shape.Quad.V0.set(x, y, 0);
shape.Quad.V1.set(x+w, y, 0);
shape.Quad.V2.set(x+w, y+h, 0);
shape.Quad.V3.set(x, y+h, 0);
shape.Color = bg.color;
shape.TextureId = rVR.getBlankTextureId();
m_DrawQueue.push_back(shape);
}
// ----------------------------------------------------------------------------
void CSSBackgroundRenderer::buildImageQuads(const CSSBackground &bg, sint32 textureId)
{
// TODO: m_Background should have textureId and that should be "reserved" in CViewRenderer
// even before download is finished
if (textureId < 0)
return;
CViewRenderer &rVR = *CViewRenderer::getInstance();
sint32 texW = 0;
sint32 texH = 0;
rVR.getTextureSizeFromId(textureId, texW, texH);
if (texW <= 0 || texH <= 0)
return;
// get requested texture size
calculateSize(m_Background, texW, texH);
if(texW <= 0 || texH <= 0)
return;
// get texture left/top corner
sint32 texX, texY;
calculatePosition(m_Background, texX, texY, texW, texH);
sint32 tilesX, tilesY;
sint32 spacingX, spacingY;
calculateTiles(m_Background, texX, texY, texW, texH, tilesX, tilesY, spacingX, spacingY);
sint32 clipL, clipB, clipR, clipT;
getPaintingArea(m_Background, clipL, clipB, clipR, clipT);
clipR += clipL;
clipT += clipB;
m_DrawQueue.reserve(tilesX * tilesY + m_DrawQueue.size());
for(sint32 tileX = 0; tileX < tilesX; tileX++)
{
for(sint32 tileY = 0; tileY < tilesY; tileY++)
{
sint32 tileL = texX + tileX * (texW + spacingX);
sint32 tileB = texY + tileY * (texH + spacingY);
sint32 tileR = tileL + texW;
sint32 tileT = tileB + texH;
// tile is outside clip area
if (tileT <= clipB || tileB >= clipT || tileR <= clipL || tileL >= clipR)
continue;
CUV uv0(0,1);
CUV uv1(1,1);
CUV uv2(1,0);
CUV uv3(0,0);
// clip if tile not totally inside clip area
if (!(tileL >= clipL && tileR <= clipR && tileB >= clipB && tileT <= clipT))
{
float ratio;
if (tileL < clipL)
{
ratio = ((float)(clipL - tileL))/((float)(tileR - tileL));
tileL = clipL;
uv0.U += ratio*(uv1.U-uv0.U);
uv3.U += ratio*(uv2.U-uv3.U);
}
if (tileB < clipB)
{
ratio = ((float)(clipB - tileB))/((float)(tileT - tileB));
tileB = clipB;
uv0.V += ratio*(uv3.V-uv0.V);
uv1.V += ratio*(uv2.V-uv1.V);
}
if (tileR > clipR)
{
ratio = ((float)(clipR - tileR))/((float)(tileL - tileR));
tileR = clipR;
uv2.U += ratio*(uv3.U-uv2.U);
uv1.U += ratio*(uv0.U-uv1.U);
}
if (tileT > clipT)
{
ratio = ((float)(clipT - tileT))/((float)(tileB - tileT));
tileT = clipT;
uv2.V += ratio*(uv1.V-uv2.V);
uv3.V += ratio*(uv0.V-uv3.V);
}
}
SDrawQueue shape;
shape.Quad.Uv0 = uv0;
shape.Quad.Uv1 = uv1;
shape.Quad.Uv2 = uv2;
shape.Quad.Uv3 = uv3;
shape.Color = CRGBA::White;
shape.TextureId = textureId;
shape.Quad.V0.set(tileL, tileB, 0);
shape.Quad.V1.set(tileR, tileB, 0);
shape.Quad.V2.set(tileR, tileT, 0);
shape.Quad.V3.set(tileL, tileT, 0);
m_DrawQueue.push_back(shape);
}
}
}
}//namespace

@ -0,0 +1,222 @@
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010-2021 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdpch.h"
#include <string>
#include "nel/gui/css_length.h"
using namespace NLMISC;
#ifdef DEBUG_NEW
#define new DEBUG_NEW
#endif
namespace NLGUI
{
bool CSSLength::parseValue(const std::string &value, bool allowPercent, bool allowNegative)
{
static const std::set<std::string> knownUnits = {
"%", "rem", "em", "px", "pt", "vw", "vh", "vi", "vb", "vmin", "vmax"
};
std::string::size_type pos = 0;
std::string::size_type len = value.size();
if (len == 0)
return false;
if (len == 1 && value[0] == '0')
{
m_Value = 0;
m_Kind = Auto;
return true;
}
// +100px; -100px
if (value[0] == '+')
pos++;
else if (allowNegative && value[0] == '-')
pos++;
while(pos < len)
{
bool isNumeric = (value[pos] >= '0' && value[pos] <= '9')
|| (pos == 0 && value[pos] == '.')
|| (pos > 0 && value[pos] == '.' && value[pos-1] >= '0' && value[pos-1] <= '9');
if (!isNumeric)
break;
pos++;
}
std::string unit = toLowerAscii(value.substr(pos));
if (knownUnits.count(unit))
{
std::string tmpstr = value.substr(0, pos);
return fromString(tmpstr, m_Value);
}
return false;
}
float CSSLength::getValue() const
{
if (m_Unit == CSS_UNIT_PERCENT)
return m_Value / 100.f;
return m_Value;
}
void CSSLength::setFloatValue(float f, const std::string &unit)
{
m_Value = f;
setUnit(unit);
}
void CSSLength::setUnit(const std::string &unit)
{
if (unit.empty())
{
m_Unit = CSS_UNIT_NONE;
m_Kind = Fixed;
}
else if (unit == "px")
{
m_Unit = CSS_UNIT_PX;
m_Kind = Fixed;
} else if (unit == "pt")
{
m_Unit = CSS_UNIT_PT;
m_Kind = Fixed;
} else if (unit == "%")
{
m_Unit = CSS_UNIT_PERCENT;
m_Kind = Relative;
} else if (unit == "em")
{
m_Unit = CSS_UNIT_EM;
m_Kind = Relative;
} else if (unit == "rem")
{
m_Unit = CSS_UNIT_REM;
m_Kind = Relative;
} else if (unit == "vw")
{
m_Unit = CSS_UNIT_VW;
m_Kind = Relative;
} else if (unit == "vh")
{
m_Unit = CSS_UNIT_VH;
m_Kind = Relative;
} else if (unit == "vi")
{
m_Unit = CSS_UNIT_VI;
m_Kind = Relative;
} else if (unit == "vb")
{
m_Unit = CSS_UNIT_VB;
m_Kind = Relative;
} else if (unit == "vmin")
{
m_Unit = CSS_UNIT_VMIN;
m_Kind = Relative;
} else if (unit == "vmax")
{
m_Unit = CSS_UNIT_VMAX;
m_Kind = Relative;
} else if (unit == "auto")
{
m_Unit = CSS_UNIT_NONE;
m_Kind = Auto;
} else
{
// fallback to auto
m_Unit = CSS_UNIT_NONE;
m_Kind = Auto;
}
}
float CSSLength::calculate(uint32 relValue, uint32 emSize, uint32 remSize, uint32 vwSize, uint32 vhSize = 0) const
{
float value = getValue();
switch(m_Unit)
{
case CSS_UNIT_EM:
return emSize * value;
case CSS_UNIT_REM:
return remSize * value;
case CSS_UNIT_PERCENT:
return relValue * value;
case CSS_UNIT_PX:
case CSS_UNIT_PT:
return value;
case CSS_UNIT_VW:
case CSS_UNIT_VI:
// Vi for horizontal writing mode only
return (float)vwSize*0.01f;
case CSS_UNIT_VH:
case CSS_UNIT_VB:
// Vb for horizontal writing mode only
return (float)vhSize*0.01f;
case CSS_UNIT_VMIN:
return (float)std::min(vwSize, vhSize)*0.01f;
case CSS_UNIT_VMAX:
return (float)std::max(vwSize, vhSize)*0.01f;
}
nldebug("Unknown CSS unit '%s'", toString().c_str());
return value;
}
std::string CSSLength::toString() const
{
if (m_Kind == Auto)
return "auto";
std::string ret;
ret += NLMISC::toString("%f", m_Value);
size_t pos = ret.find(".");
for( ; pos < ret.size(); ++pos)
{
if (ret[pos] != '0')
break;
}
if (pos == ret.size())
ret = ret.substr(0, ret.find("."));
switch(m_Unit)
{
case CSS_UNIT_NONE: break;
case CSS_UNIT_EM: ret += "em"; break;
case CSS_UNIT_REM: ret += "rem"; break;
case CSS_UNIT_PERCENT: ret += "%"; break;
case CSS_UNIT_PX: ret += "px"; break;
case CSS_UNIT_PT: ret += "pt"; break;
case CSS_UNIT_VW: ret += "vw"; break;
case CSS_UNIT_VH: ret += "vh"; break;
case CSS_UNIT_VI: ret += "vi"; break;
case CSS_UNIT_VB: ret += "vb"; break;
case CSS_UNIT_VMIN: ret += "vmin"; break;
case CSS_UNIT_VMAX: ret += "vmax"; break;
}
return ret;
}
} // namespace
Loading…
Cancel
Save