1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2019 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "nel/gui/css_background_renderer.h"
20 #include "nel/gui/css_border_renderer.h"
21 #include "nel/gui/view_renderer.h"
22 #include "nel/gui/widget_manager.h"
23 #include "nel/gui/view_bitmap.h"
26 using namespace NLMISC
;
34 // ----------------------------------------------------------------------------
35 CSSBackgroundRenderer::CSSBackgroundRenderer()
36 : CurrentAlpha(255), TextureId(-1),
37 m_BorderX(0), m_BorderY(0), m_BorderW(0), m_BorderH(0),
38 m_PaddingX(0), m_PaddingY(0), m_PaddingW(0), m_PaddingH(0),
39 m_ContentX(0), m_ContentY(0), m_ContentW(0), m_ContentH(0),
40 m_RootFontSize(16.f
), m_FontSize(16.f
), m_Viewport(NULL
),
41 m_RenderLayer(0), m_ModulateGlobalColor(false), m_FillViewport(false),
46 // ----------------------------------------------------------------------------
47 CSSBackgroundRenderer::~CSSBackgroundRenderer()
50 CViewRenderer::getInstance()->deleteTexture(TextureId
);
53 // ----------------------------------------------------------------------------
54 void CSSBackgroundRenderer::clear()
59 CViewRenderer::getInstance()->deleteTexture(TextureId
);
62 m_Background
.image
.clear();
63 m_Background
.color
.A
= 0;
66 // ----------------------------------------------------------------------------
67 void CSSBackgroundRenderer::setBackground(const CSSBackground
&bg
)
70 // TODO: CSSBackground should keep track of TextureId
71 CViewRenderer
&rVR
= *CViewRenderer::getInstance();
72 if (bg
.image
!= m_Background
.image
&& TextureId
!= -1)
73 rVR
.deleteTexture(TextureId
);
76 // TODO: does not accept urls
77 if (TextureId
== -1 && !bg
.image
.empty())
79 // TODO: make CViewRenderer accept urls
80 if (bg
.image
.find("://") != std::string::npos
)
81 TextureId
= rVR
.createTexture(bg
.image
, 0, 0, -1, -1, false);
85 // ----------------------------------------------------------------------------
86 void CSSBackgroundRenderer::setImage(const std::string
&bgtex
)
89 // TODO: CSSBackground should keep track of TextureId
90 CViewRenderer
&rVR
= *CViewRenderer::getInstance();
91 if (bgtex
!= m_Background
.image
&& TextureId
!= -1)
93 rVR
.deleteTexture(TextureId
);
96 m_Background
.image
= bgtex
;
98 if (TextureId
== -1 && !bgtex
.empty())
100 // TODO: make CViewRenderer accept urls
101 if (bgtex
.find("://") != std::string::npos
)
102 TextureId
= rVR
.createTexture(bgtex
, 0, 0, -1, -1, false);
106 // ----------------------------------------------------------------------------
107 void CSSBackgroundRenderer::setImageRepeat(bool b
)
109 m_Background
.repeatX
= b
? CSS_VALUE_REPEAT
: CSS_VALUE_NOREPEAT
;
110 m_Background
.repeatY
= b
? CSS_VALUE_REPEAT
: CSS_VALUE_NOREPEAT
;
113 // ----------------------------------------------------------------------------
114 void CSSBackgroundRenderer::setImageCover(bool b
)
116 m_Background
.size
= b
? CSS_VALUE_COVER
: CSS_VALUE_AUTO
;
119 // ----------------------------------------------------------------------------
120 void CSSBackgroundRenderer::updateCoords()
125 // TODO: color from last background layer
126 buildColorQuads(m_Background
);
128 // -------------------------------------------------------------------
130 buildImageQuads(m_Background
, TextureId
);
133 // ----------------------------------------------------------------------------
134 void CSSBackgroundRenderer::draw() {
135 if (m_Dirty
) updateCoords();
136 if (m_DrawQueue
.empty()) return;
138 CViewRenderer
&rVR
= *CViewRenderer::getInstance();
140 // flush draw cache to ensure correct draw order
143 // TODO: no need for widget manager, if global color is set from parent
145 if (m_ModulateGlobalColor
)
146 globalColor
= CWidgetManager::getInstance()->getGlobalColor();
148 // TODO: there might be issue on draw order IF using multiple textures
149 // and second (top) texture is created before first one.
150 for(uint i
= 0; i
< m_DrawQueue
.size(); ++i
)
152 CRGBA color
= m_DrawQueue
[i
].Color
;
153 if (m_ModulateGlobalColor
)
154 color
.modulateFromColor (color
, globalColor
);
156 color
.A
= (uint8
) (((uint16
) CurrentAlpha
* (uint16
) color
.A
) >> 8);
157 rVR
.drawQuad(m_RenderLayer
, m_DrawQueue
[i
].Quad
, m_DrawQueue
[i
].TextureId
, color
, false);
160 // flush draw cache to ensure correct draw order
164 // ----------------------------------------------------------------------------
165 void CSSBackgroundRenderer::getPositioningArea(const CSSBackground
&bg
, sint32
&areaX
, sint32
&areaY
, sint32
&areaW
, sint32
&areaH
) const
169 case CSS_VALUE_PADDING_BOX
:
175 case CSS_VALUE_CONTENT_BOX
:
181 case CSS_VALUE_BORDER_BOX
:
192 // ----------------------------------------------------------------------------
193 void CSSBackgroundRenderer::getPaintingArea(const CSSBackground
&bg
, sint32
&areaX
, sint32
&areaY
, sint32
&areaW
, sint32
&areaH
) const
197 case CSS_VALUE_PADDING_BOX
:
203 case CSS_VALUE_CONTENT_BOX
:
209 case CSS_VALUE_BORDER_BOX
:
219 if (m_FillViewport
&& m_Viewport
)
221 sint32 newX
= std::min(areaX
, m_Viewport
->getXReal());
222 sint32 newY
= std::min(areaY
, m_Viewport
->getYReal());
223 areaW
= std::max(areaX
+ areaW
, m_Viewport
->getXReal() + m_Viewport
->getWReal()) - newX
;
224 areaH
= std::max(areaY
+ areaH
, m_Viewport
->getYReal() + m_Viewport
->getHReal()) - newY
;
230 // ----------------------------------------------------------------------------
231 void CSSBackgroundRenderer::calculateSize(const CSSBackground
&bg
, sint32
&texW
, sint32
&texH
) const
233 sint32 areaX
, areaY
, areaW
, areaH
;
234 getPositioningArea(bg
, areaX
, areaY
, areaW
, areaH
);
240 vpW
= m_Viewport
->getWReal();
241 vpH
= m_Viewport
->getHReal();
244 float whRatio
= (float)texW
/ (float)texH
;
247 case CSS_VALUE_LENGTH
:
249 if (bg
.width
.isAuto() && bg
.height
.isAuto())
253 else if (bg
.width
.isAuto())
255 texH
= bg
.height
.calculate(areaH
, m_FontSize
, m_RootFontSize
, vpW
, vpH
);
256 texW
= texH
* whRatio
;
258 else if (bg
.height
.isAuto())
261 texW
= bg
.width
.calculate(areaW
, m_FontSize
, m_RootFontSize
, vpW
, vpH
);
262 texH
= texW
/ whRatio
;
266 texW
= bg
.width
.calculate(areaW
, m_FontSize
, m_RootFontSize
, vpW
, vpH
);
267 texH
= bg
.height
.calculate(areaH
, m_FontSize
, m_RootFontSize
, vpW
, vpH
);
276 case CSS_VALUE_COVER
:
278 float canvasRatio
= (float)areaW
/ (float)areaH
;
279 if (whRatio
< canvasRatio
)
282 texH
= areaW
/ whRatio
;
284 texW
= areaH
* whRatio
;
289 case CSS_VALUE_CONTAIN
:
291 // same as covert, but ratio check is reversed
292 float canvasRatio
= (float)areaW
/ (float)areaH
;
293 if (whRatio
> canvasRatio
)
296 texH
= areaW
/ whRatio
;
298 texW
= areaH
* whRatio
;
306 // ----------------------------------------------------------------------------
307 void CSSBackgroundRenderer::calculatePosition(const CSSBackground
&bg
, sint32
&texX
, sint32
&texY
, sint32
&texW
, sint32
&texH
) const
309 sint32 areaX
, areaY
, areaW
, areaH
;
310 getPositioningArea(bg
, areaX
, areaY
, areaW
, areaH
);
316 vpW
= m_Viewport
->getWReal();
317 vpH
= m_Viewport
->getHReal();
320 float ofsX
= bg
.xPosition
.calculate(1, m_FontSize
, m_RootFontSize
, vpW
, vpH
);
321 float ofsY
= bg
.yPosition
.calculate(1, m_FontSize
, m_RootFontSize
, vpW
, vpH
);
323 if (bg
.xPosition
.isPercent() || bg
.xAnchor
== CSS_VALUE_CENTER
)
325 if (bg
.xAnchor
== CSS_VALUE_RIGHT
)
327 else if (bg
.xAnchor
== CSS_VALUE_CENTER
)
330 ofsX
= (float)(areaW
- texW
) * ofsX
;
332 else if (bg
.xAnchor
== CSS_VALUE_RIGHT
)
334 ofsX
= areaW
- texW
- ofsX
;
337 // areaY is bottom edge, areaY+areaH is top edge
338 if (bg
.yPosition
.isPercent() || bg
.yAnchor
== CSS_VALUE_CENTER
)
340 if (bg
.yAnchor
== CSS_VALUE_TOP
)
342 else if (bg
.yAnchor
== CSS_VALUE_CENTER
)
345 ofsY
= (float)(areaH
- texH
) * ofsY
;
347 else if (bg
.yAnchor
== CSS_VALUE_TOP
)
349 ofsY
= areaH
- texH
- ofsY
;
356 // ----------------------------------------------------------------------------
357 void CSSBackgroundRenderer::getImageTile(sint32
&tilePos
, sint32
&tileSize
, sint32
&spacing
, sint32
&tiles
, sint32 areaPos
, sint32 areaSize
, CSSValueType repeat
) const
361 case CSS_VALUE_NOREPEAT
:
367 case CSS_VALUE_SPACE
:
369 // if no space for 2+ tiles, then show single one on calculated tilePos
370 if (tileSize
* 2 > areaSize
)
372 // set spacing large enough to only display single tile
378 // available for middle tiles
379 sint32 midSize
= (areaSize
- tileSize
*2);
380 // number of middle tiles
381 sint32 midTiles
= midSize
/ tileSize
;
383 tiles
= 2 + midTiles
;
385 // int div for floor()
386 spacing
= ( midSize
- tileSize
* midTiles
) / (midTiles
+ 1);
390 case CSS_VALUE_ROUND
:
391 // fall-thru - size is already calculated
392 case CSS_VALUE_REPEAT
:
396 tilePos
-= std::ceil(abs(tilePos
- areaPos
)/(float)tileSize
)*tileSize
;
397 tiles
= std::ceil((std::abs(areaPos
- tilePos
) + areaSize
) / (float)tileSize
);
404 // ----------------------------------------------------------------------------
405 void CSSBackgroundRenderer::calculateTiles(const CSSBackground
&bg
, sint32
&texX
, sint32
&texY
, sint32
&texW
, sint32
&texH
, sint32
&tilesX
, sint32
&tilesY
, sint32
&spacingX
, sint32
&spacingY
) const
407 sint32 areaX
, areaY
, areaW
, areaH
;
408 getPositioningArea(bg
, areaX
, areaY
, areaW
, areaH
);
410 // texX,texY is for position area (ie content-box), but painting area can be bigger (ie border-box)
411 sint32 paintX
, paintY
, paintW
, paintH
;
412 getPaintingArea(bg
, paintX
, paintY
, paintW
, paintH
);
414 areaX
-= std::ceil((areaX
- paintX
) / (float)texW
) * texW
;
415 if ((paintX
+ paintW
) > (areaX
+ areaW
))
416 areaW
+= std::ceil(((paintX
+ paintW
) - (areaX
+ areaW
)) / (float)texW
) * texW
;
418 areaY
-= std::ceil((areaY
- paintY
) / (float)texH
) * texH
;
419 if ((paintY
+ paintH
) > (areaY
+ areaH
))
420 areaH
+= std::ceil(((paintY
+ paintH
) - (areaY
+ areaH
)) / (float)texH
) * texH
;
422 if (texW
<= 0 || texH
<= 0 || areaW
<= 0 || areaH
<= 0)
425 spacingX
= spacingY
= 0;
429 if (bg
.repeatX
== CSS_VALUE_ROUND
)
431 sint numTiles
= std::max(1, (sint
)std::round((float)areaW
/ texW
));
432 texW
= areaW
/ numTiles
;
433 if (bg
.height
.isAuto() && bg
.repeatY
!= CSS_VALUE_ROUND
)
435 float aspect
= (float)areaW
/ (numTiles
* texW
);
436 texH
= texW
* aspect
;
440 if (bg
.repeatY
== CSS_VALUE_ROUND
)
442 sint numTiles
= std::max(1, (sint
)std::round((float)areaH
/ texH
));
443 texH
= areaH
/ numTiles
;
444 if (bg
.width
.isAuto() && bg
.repeatX
!= CSS_VALUE_ROUND
)
446 float aspect
= (float)areaH
/ (numTiles
* texH
);
447 texW
= texH
* aspect
;
451 getImageTile(texX
, texW
, spacingX
, tilesX
, areaX
, areaW
, bg
.repeatX
);
452 getImageTile(texY
, texH
, spacingY
, tilesY
, areaY
, areaH
, bg
.repeatY
);
455 // ----------------------------------------------------------------------------
456 void CSSBackgroundRenderer::buildColorQuads(const CSSBackground
&bg
)
461 // painting area defined with background-clip
463 getPaintingArea(bg
, x
, y
, w
, h
);
465 if (w
<= 0 || h
<= 0)
468 CViewRenderer
&rVR
= *CViewRenderer::getInstance();
471 shape
.Quad
.Uv0
.set(0.f
, 1.f
);
472 shape
.Quad
.Uv1
.set(1.f
, 1.f
);
473 shape
.Quad
.Uv2
.set(1.f
, 0.f
);
474 shape
.Quad
.Uv3
.set(0.f
, 0.f
);
476 shape
.Quad
.V0
.set(x
, y
, 0);
477 shape
.Quad
.V1
.set(x
+w
, y
, 0);
478 shape
.Quad
.V2
.set(x
+w
, y
+h
, 0);
479 shape
.Quad
.V3
.set(x
, y
+h
, 0);
481 shape
.Color
= bg
.color
;
482 shape
.TextureId
= rVR
.getBlankTextureId();
484 m_DrawQueue
.push_back(shape
);
487 // ----------------------------------------------------------------------------
488 void CSSBackgroundRenderer::buildImageQuads(const CSSBackground
&bg
, sint32 textureId
)
490 // TODO: m_Background should have textureId and that should be "reserved" in CViewRenderer
491 // even before download is finished
495 CViewRenderer
&rVR
= *CViewRenderer::getInstance();
499 rVR
.getTextureSizeFromId(textureId
, texW
, texH
);
500 if (texW
<= 0 || texH
<= 0)
503 // get requested texture size
504 calculateSize(m_Background
, texW
, texH
);
505 if(texW
<= 0 || texH
<= 0)
508 // get texture left/top corner
510 calculatePosition(m_Background
, texX
, texY
, texW
, texH
);
512 sint32 tilesX
, tilesY
;
513 sint32 spacingX
, spacingY
;
514 calculateTiles(m_Background
, texX
, texY
, texW
, texH
, tilesX
, tilesY
, spacingX
, spacingY
);
516 sint32 clipL
, clipB
, clipR
, clipT
;
517 getPaintingArea(m_Background
, clipL
, clipB
, clipR
, clipT
);
521 m_DrawQueue
.reserve(tilesX
* tilesY
+ m_DrawQueue
.size());
522 for(sint32 tileX
= 0; tileX
< tilesX
; tileX
++)
524 for(sint32 tileY
= 0; tileY
< tilesY
; tileY
++)
526 sint32 tileL
= texX
+ tileX
* (texW
+ spacingX
);
527 sint32 tileB
= texY
+ tileY
* (texH
+ spacingY
);
528 sint32 tileR
= tileL
+ texW
;
529 sint32 tileT
= tileB
+ texH
;
531 // tile is outside clip area
532 if (tileT
<= clipB
|| tileB
>= clipT
|| tileR
<= clipL
|| tileL
>= clipR
)
540 // clip if tile not totally inside clip area
541 if (!(tileL
>= clipL
&& tileR
<= clipR
&& tileB
>= clipB
&& tileT
<= clipT
))
546 ratio
= ((float)(clipL
- tileL
))/((float)(tileR
- tileL
));
548 uv0
.U
+= ratio
*(uv1
.U
-uv0
.U
);
549 uv3
.U
+= ratio
*(uv2
.U
-uv3
.U
);
554 ratio
= ((float)(clipB
- tileB
))/((float)(tileT
- tileB
));
556 uv0
.V
+= ratio
*(uv3
.V
-uv0
.V
);
557 uv1
.V
+= ratio
*(uv2
.V
-uv1
.V
);
562 ratio
= ((float)(clipR
- tileR
))/((float)(tileL
- tileR
));
564 uv2
.U
+= ratio
*(uv3
.U
-uv2
.U
);
565 uv1
.U
+= ratio
*(uv0
.U
-uv1
.U
);
570 ratio
= ((float)(clipT
- tileT
))/((float)(tileB
- tileT
));
572 uv2
.V
+= ratio
*(uv1
.V
-uv2
.V
);
573 uv3
.V
+= ratio
*(uv0
.V
-uv3
.V
);
578 shape
.Quad
.Uv0
= uv0
;
579 shape
.Quad
.Uv1
= uv1
;
580 shape
.Quad
.Uv2
= uv2
;
581 shape
.Quad
.Uv3
= uv3
;
583 shape
.Color
= CRGBA::White
;
584 shape
.TextureId
= textureId
;
586 shape
.Quad
.V0
.set(tileL
, tileB
, 0);
587 shape
.Quad
.V1
.set(tileR
, tileB
, 0);
588 shape
.Quad
.V2
.set(tileR
, tileT
, 0);
589 shape
.Quad
.V3
.set(tileL
, tileT
, 0);
591 m_DrawQueue
.push_back(shape
);