1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "GUI/BsGUITexture.h"
4#include "2D/BsImageSprite.h"
5#include "GUI/BsGUISkin.h"
6#include "Image/BsSpriteTexture.h"
7#include "GUI/BsGUIDimensions.h"
8
9namespace bs
10{
11 const String& GUITexture::getGUITypeName()
12 {
13 static String name = "Texture";
14 return name;
15 }
16
17 GUITexture::GUITexture(const String& styleName, const HSpriteTexture& texture,
18 TextureScaleMode scale, bool transparent, const GUIDimensions& dimensions)
19 :GUIElement(styleName, dimensions), mScaleMode(scale), mTransparent(transparent), mUsingStyleTexture(false)
20 {
21 mImageSprite = bs_new<ImageSprite>();
22 mDesc.animationStartTime = gTime().getTime();
23
24 if(texture != nullptr)
25 {
26 mActiveTexture = texture;
27 mUsingStyleTexture = false;
28 }
29 else
30 {
31 mActiveTexture = _getStyle()->normal.texture;
32 mUsingStyleTexture = true;
33 }
34
35 bool isTexLoaded = SpriteTexture::checkIsLoaded(mActiveTexture);
36 mActiveTextureWidth = isTexLoaded ? mActiveTexture->getFrameWidth() : 0;
37 mActiveTextureHeight = isTexLoaded ? mActiveTexture->getFrameHeight() : 0;
38 }
39
40 GUITexture::~GUITexture()
41 {
42 bs_delete(mImageSprite);
43 }
44
45 GUITexture* GUITexture::create(const HSpriteTexture& texture, TextureScaleMode scale, bool transparent,
46 const GUIOptions& options, const String& styleName)
47 {
48 return new (bs_alloc<GUITexture>()) GUITexture(getStyleName<GUITexture>(styleName),
49 texture, scale, transparent, GUIDimensions::create(options));
50 }
51
52 GUITexture* GUITexture::create(const HSpriteTexture& texture, TextureScaleMode scale, bool transparent,
53 const String& styleName)
54 {
55 return new (bs_alloc<GUITexture>()) GUITexture(getStyleName<GUITexture>(styleName),
56 texture, scale, transparent, GUIDimensions::create());
57 }
58
59 GUITexture* GUITexture::create(const HSpriteTexture& texture, TextureScaleMode scale,
60 const GUIOptions& options, const String& styleName)
61 {
62 return new (bs_alloc<GUITexture>()) GUITexture(getStyleName<GUITexture>(styleName),
63 texture, scale, true, GUIDimensions::create(options));
64 }
65
66 GUITexture* GUITexture::create(const HSpriteTexture& texture, TextureScaleMode scale,
67 const String& styleName)
68 {
69 return new (bs_alloc<GUITexture>()) GUITexture(getStyleName<GUITexture>(styleName),
70 texture, scale, true, GUIDimensions::create());
71 }
72
73 GUITexture* GUITexture::create(const HSpriteTexture& texture,
74 const GUIOptions& options, const String& styleName)
75 {
76 return new (bs_alloc<GUITexture>()) GUITexture(getStyleName<GUITexture>(styleName),
77 texture, TextureScaleMode::StretchToFit, true, GUIDimensions::create(options));
78 }
79
80 GUITexture* GUITexture::create(const HSpriteTexture& texture, const String& styleName)
81 {
82 return new (bs_alloc<GUITexture>()) GUITexture(getStyleName<GUITexture>(styleName),
83 texture, TextureScaleMode::StretchToFit, true, GUIDimensions::create());
84 }
85
86 GUITexture* GUITexture::create(TextureScaleMode scale, const GUIOptions& options, const String& styleName)
87 {
88 return new (bs_alloc<GUITexture>()) GUITexture(getStyleName<GUITexture>(styleName),
89 HSpriteTexture(), scale, true, GUIDimensions::create(options));
90 }
91
92 GUITexture* GUITexture::create(TextureScaleMode scale, const String& styleName)
93 {
94 return new (bs_alloc<GUITexture>()) GUITexture(getStyleName<GUITexture>(styleName),
95 HSpriteTexture(), scale, true, GUIDimensions::create());
96 }
97
98 GUITexture* GUITexture::create(const GUIOptions& options, const String& styleName)
99 {
100 return new (bs_alloc<GUITexture>()) GUITexture(getStyleName<GUITexture>(styleName),
101 HSpriteTexture(), TextureScaleMode::StretchToFit, true, GUIDimensions::create(options));
102 }
103
104 GUITexture* GUITexture::create(const String& styleName)
105 {
106 return new (bs_alloc<GUITexture>()) GUITexture(getStyleName<GUITexture>(styleName),
107 HSpriteTexture(), TextureScaleMode::StretchToFit, true, GUIDimensions::create());
108 }
109
110 void GUITexture::setTexture(const HSpriteTexture& texture)
111 {
112 Vector2I origSize = mDimensions.calculateSizeRange(_getOptimalSize()).optimal;
113
114 mActiveTexture = texture;
115
116 bool isTexLoaded = SpriteTexture::checkIsLoaded(mActiveTexture);
117 mActiveTextureWidth = isTexLoaded ? mActiveTexture->getFrameWidth() : 0;
118 mActiveTextureHeight = isTexLoaded ? mActiveTexture->getFrameHeight() : 0;
119
120 mUsingStyleTexture = false;
121 mDesc.animationStartTime = gTime().getTime();
122
123 Vector2I newSize = mDimensions.calculateSizeRange(_getOptimalSize()).optimal;
124 if (origSize != newSize)
125 _markLayoutAsDirty();
126 else
127 _markContentAsDirty();
128 }
129
130 UINT32 GUITexture::_getNumRenderElements() const
131 {
132 return mImageSprite->getNumRenderElements();
133 }
134
135 const SpriteMaterialInfo& GUITexture::_getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const
136 {
137 *material = mImageSprite->getMaterial(renderElementIdx);
138 return mImageSprite->getMaterialInfo(renderElementIdx);
139 }
140
141 void GUITexture::_getMeshInfo(UINT32 renderElementIdx, UINT32& numVertices, UINT32& numIndices, GUIMeshType& type) const
142 {
143 UINT32 numQuads = mImageSprite->getNumQuads(renderElementIdx);
144 numVertices = numQuads * 4;
145 numIndices = numQuads * 6;
146 type = GUIMeshType::Triangle;
147 }
148
149 void GUITexture::updateRenderElementsInternal()
150 {
151 Vector2I textureSize;
152 if (SpriteTexture::checkIsLoaded(mActiveTexture))
153 {
154 mDesc.texture = mActiveTexture;
155 textureSize.x = mDesc.texture->getFrameWidth();
156 textureSize.y = mDesc.texture->getFrameHeight();
157 }
158 Vector2I destSize(mLayoutData.area.width, mLayoutData.area.height);
159
160 // ScaleToFit is the only scaling mode that might result in the GUITexture area not being completely covered by
161 // the sprite. We need the actual sprite size and offsets to center it.
162 if(mScaleMode == TextureScaleMode::ScaleToFit)
163 {
164 if(destSize.x != 0 && destSize.y != 0)
165 {
166 float aspectX = textureSize.x / (float)destSize.x;
167 float aspectY = textureSize.y / (float)destSize.y;
168
169 if (aspectY > aspectX)
170 {
171 destSize.x = Math::roundToPosInt(textureSize.x / aspectY);
172 destSize.y = Math::roundToPosInt(textureSize.y / aspectY);
173 }
174 else
175 {
176 destSize.x = Math::roundToPosInt(textureSize.x / aspectX);
177 destSize.y = Math::roundToPosInt(textureSize.y / aspectX);
178 }
179 }
180
181 mImageSpriteOffset = Vector2I(
182 ((INT32)mLayoutData.area.width - destSize.x) / 2,
183 ((INT32)mLayoutData.area.height - destSize.y) / 2
184 );
185 }
186 else
187 mImageSpriteOffset = Vector2I();
188
189 mDesc.width = (UINT32)destSize.x;
190 mDesc.height = (UINT32)destSize.y;
191
192 mDesc.borderLeft = _getStyle()->border.left;
193 mDesc.borderRight = _getStyle()->border.right;
194 mDesc.borderTop = _getStyle()->border.top;
195 mDesc.borderBottom = _getStyle()->border.bottom;
196 mDesc.transparent = mTransparent;
197 mDesc.color = getTint();
198
199 if(mScaleMode != TextureScaleMode::ScaleToFit)
200 mDesc.uvScale = ImageSprite::getTextureUVScale(textureSize, destSize, mScaleMode);
201 else
202 mDesc.uvScale = Vector2::ONE;
203
204 mImageSprite->update(mDesc, (UINT64)_getParentWidget());
205
206 GUIElement::updateRenderElementsInternal();
207 }
208
209 void GUITexture::styleUpdated()
210 {
211 if (mUsingStyleTexture)
212 {
213 mActiveTexture = _getStyle()->normal.texture;
214 mDesc.animationStartTime = gTime().getTime();
215
216 bool isTexLoaded = SpriteTexture::checkIsLoaded(mActiveTexture);
217 mActiveTextureWidth = isTexLoaded ? mActiveTexture->getFrameWidth() : 0;
218 mActiveTextureHeight = isTexLoaded ? mActiveTexture->getFrameHeight() : 0;
219 }
220 }
221
222 Vector2I GUITexture::_getOptimalSize() const
223 {
224 // TODO - Accounting for style dimensions might be redundant here, I'm pretty sure we do that on higher level anyway
225 Vector2I optimalSize;
226
227 // Note: We use cached texture size here. This is because we use this method for checking we a layout update is
228 // needed (size change is detected). Sprite texture could change without us knowing and by storing the size we can
229 // safely detect this. (In short, don't do mActiveTexture->getFrameWidth/Height() here)
230
231 if(_getDimensions().fixedWidth())
232 optimalSize.x = _getDimensions().minWidth;
233 else
234 {
235 if (SpriteTexture::checkIsLoaded(mActiveTexture))
236 optimalSize.x = mActiveTextureWidth;
237 else
238 optimalSize.x = _getDimensions().maxWidth;
239 }
240
241 if(_getDimensions().fixedHeight())
242 optimalSize.y = _getDimensions().minHeight;
243 else
244 {
245 if (SpriteTexture::checkIsLoaded(mActiveTexture))
246 optimalSize.y = mActiveTextureHeight;
247 else
248 optimalSize.y = _getDimensions().maxHeight;
249 }
250
251 return optimalSize;
252 }
253
254 void GUITexture::_fillBuffer(UINT8* vertices, UINT32* indices, UINT32 vertexOffset, UINT32 indexOffset,
255 UINT32 maxNumVerts, UINT32 maxNumIndices, UINT32 renderElementIdx) const
256 {
257 UINT8* uvs = vertices + sizeof(Vector2);
258 UINT32 vertexStride = sizeof(Vector2) * 2;
259 UINT32 indexStride = sizeof(UINT32);
260
261 Vector2I offset(mLayoutData.area.x, mLayoutData.area.y);
262 offset += mImageSpriteOffset;
263 mImageSprite->fillBuffer(vertices, uvs, indices, vertexOffset, indexOffset, maxNumVerts, maxNumIndices,
264 vertexStride, indexStride, renderElementIdx, offset, mLayoutData.getLocalClipRect());
265 }
266}