From dfe45029ab612bbb0df9ae09e4d2b3d7ed5b4b60 Mon Sep 17 00:00:00 2001 From: Nimetu Date: Sun, 4 Jul 2021 16:55:50 +0300 Subject: [PATCH] Add CSSLength, CSSBackground, CSSBackgroundRenderer classes --- nel/include/nel/gui/css_background.h | 83 +++ nel/include/nel/gui/css_background_renderer.h | 193 ++++++ nel/include/nel/gui/css_length.h | 76 +++ nel/include/nel/gui/css_types.h | 38 ++ nel/src/gui/css_background.cpp | 427 ++++++++++++ nel/src/gui/css_background_renderer.cpp | 607 ++++++++++++++++++ nel/src/gui/css_length.cpp | 222 +++++++ 7 files changed, 1646 insertions(+) create mode 100644 nel/include/nel/gui/css_background.h create mode 100644 nel/include/nel/gui/css_background_renderer.h create mode 100644 nel/include/nel/gui/css_length.h create mode 100644 nel/src/gui/css_background.cpp create mode 100644 nel/src/gui/css_background_renderer.cpp create mode 100644 nel/src/gui/css_length.cpp diff --git a/nel/include/nel/gui/css_background.h b/nel/include/nel/gui/css_background.h new file mode 100644 index 000000000..87ce87d0e --- /dev/null +++ b/nel/include/nel/gui/css_background.h @@ -0,0 +1,83 @@ +// Ryzom - MMORPG Framework +// 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 . + +#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 &parts); + void positionFromTwo(const std::vector &parts); + void positionFromThree(const std::vector &parts); + void positionFromFour(const std::vector &parts); + }; + +}//namespace + +#endif // CL_CSS_BACKGROUND_H + + diff --git a/nel/include/nel/gui/css_background_renderer.h b/nel/include/nel/gui/css_background_renderer.h new file mode 100644 index 000000000..86b31cb3d --- /dev/null +++ b/nel/include/nel/gui/css_background_renderer.h @@ -0,0 +1,193 @@ +// Ryzom - MMORPG Framework +// 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 . + + + +#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, 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 m_DrawQueue; + + const sint8 m_RenderLayer; + bool m_ModulateGlobalColor; + // if true, painting area returns area at least the size of viewport (ie, 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 + + diff --git a/nel/include/nel/gui/css_length.h b/nel/include/nel/gui/css_length.h new file mode 100644 index 000000000..d7b8c657b --- /dev/null +++ b/nel/include/nel/gui/css_length.h @@ -0,0 +1,76 @@ +// Ryzom - MMORPG Framework +// 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 . + +#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 + + diff --git a/nel/include/nel/gui/css_types.h b/nel/include/nel/gui/css_types.h index dd80e308f..ac59af422 100644 --- a/nel/include/nel/gui/css_types.h +++ b/nel/include/nel/gui/css_types.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 diff --git a/nel/src/gui/css_background.cpp b/nel/src/gui/css_background.cpp new file mode 100644 index 000000000..4665a0c42 --- /dev/null +++ b/nel/src/gui/css_background.cpp @@ -0,0 +1,427 @@ +// Ryzom - MMORPG Framework +// 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 . + +#include "stdpch.h" + +#include +#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 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 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 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 &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 &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 &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 &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 + diff --git a/nel/src/gui/css_background_renderer.cpp b/nel/src/gui/css_background_renderer.cpp new file mode 100644 index 000000000..4d7df021e --- /dev/null +++ b/nel/src/gui/css_background_renderer.cpp @@ -0,0 +1,607 @@ +// Ryzom - MMORPG Framework +// 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 . + + +#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 + diff --git a/nel/src/gui/css_length.cpp b/nel/src/gui/css_length.cpp new file mode 100644 index 000000000..fde47b644 --- /dev/null +++ b/nel/src/gui/css_length.cpp @@ -0,0 +1,222 @@ +// Ryzom - MMORPG Framework +// 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 . + +#include "stdpch.h" + +#include +#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 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 +