1 /*
2 * This source file is part of libRocket, the HTML/CSS Interface Middleware
3 *
4 * For the latest information, see http://www.librocket.com
5 *
6 * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
25 *
26 */
27
28 #include "precompiled.h"
29 #include "ElementImage.h"
30 #include "../../Include/Rocket/Core.h"
31 #include "TextureDatabase.h"
32 #include "TextureResource.h"
33
34 namespace Rocket {
35 namespace Core {
36
37 // Constructs a new ElementImage.
ElementImage(const String & tag)38 ElementImage::ElementImage(const String& tag) : Element(tag), dimensions(-1, -1), geometry(this)
39 {
40 ResetCoords();
41 geometry_dirty = false;
42 texture_dirty = true;
43 }
44
~ElementImage()45 ElementImage::~ElementImage()
46 {
47 }
48
49 // Sizes the box to the element's inherent size.
GetIntrinsicDimensions(Vector2f & _dimensions)50 bool ElementImage::GetIntrinsicDimensions(Vector2f& _dimensions)
51 {
52 // Check if we need to reload the texture.
53 if (texture_dirty)
54 LoadTexture();
55
56 // Calculate the x dimension.
57 if (HasAttribute("width"))
58 dimensions.x = GetAttribute< float >("width", -1);
59 else if (using_coords)
60 dimensions.x = (float) (coords[2] - coords[0]);
61 else
62 dimensions.x = (float) texture.GetDimensions(GetRenderInterface()).x;
63
64 // Calculate the y dimension.
65 if (HasAttribute("height"))
66 dimensions.y = GetAttribute< float >("height", -1);
67 else if (using_coords)
68 dimensions.y = (float) (coords[3] - coords[1]);
69 else
70 dimensions.y = (float) texture.GetDimensions(GetRenderInterface()).y;
71
72 // Return the calculated dimensions. If this changes the size of the element, it will result in
73 // a 'resize' event which is caught below and will regenerate the geometry.
74 _dimensions = dimensions;
75 return true;
76 }
77
78 // Renders the element.
OnRender()79 void ElementImage::OnRender()
80 {
81 // Regenerate the geometry if required (this will be set if 'coords' changes but does not
82 // result in a resize).
83 if (geometry_dirty)
84 GenerateGeometry();
85
86 // Render the geometry beginning at this element's content region.
87 geometry.Render(GetAbsoluteOffset(Rocket::Core::Box::CONTENT));
88 }
89
90 // Called when attributes on the element are changed.
OnAttributeChange(const Rocket::Core::AttributeNameList & changed_attributes)91 void ElementImage::OnAttributeChange(const Rocket::Core::AttributeNameList& changed_attributes)
92 {
93 // Call through to the base element's OnAttributeChange().
94 Rocket::Core::Element::OnAttributeChange(changed_attributes);
95
96 float dirty_layout = false;
97
98 // Check for a changed 'src' attribute. If this changes, the old texture handle is released,
99 // forcing a reload when the layout is regenerated.
100 if (changed_attributes.find("src") != changed_attributes.end())
101 {
102 texture_dirty = true;
103 dirty_layout = true;
104 }
105
106 // Check for a changed 'width' attribute. If this changes, a layout is forced which will
107 // recalculate the dimensions.
108 if (changed_attributes.find("width") != changed_attributes.end() ||
109 changed_attributes.find("height") != changed_attributes.end())
110 {
111 dirty_layout = true;
112 }
113
114 // Check for a change to the 'coords' attribute. If this changes, the coordinates are
115 // recomputed and a layout forced.
116 if (changed_attributes.find("coords") != changed_attributes.end())
117 {
118 if (HasAttribute("coords"))
119 {
120 StringList coords_list;
121 StringUtilities::ExpandString(coords_list, GetAttribute< String >("coords", ""));
122
123 if (coords_list.size() != 4)
124 {
125 Rocket::Core::Log::Message(Log::LT_WARNING, "Element '%s' has an invalid 'coords' attribute; coords requires 4 values, found %d.", GetAddress().CString(), coords_list.size());
126 ResetCoords();
127 }
128 else
129 {
130 for (size_t i = 0; i < 4; ++i)
131 coords[i] = atoi(coords_list[i].CString());
132
133 // Check for the validity of the coordinates.
134 if (coords[0] < 0 || coords[2] < coords[0] ||
135 coords[1] < 0 || coords[3] < coords[1])
136 {
137 Rocket::Core::Log::Message(Log::LT_WARNING, "Element '%s' has an invalid 'coords' attribute; invalid coordinate values specified.", GetAddress().CString());
138 ResetCoords();
139 }
140 else
141 {
142 // We have new, valid coordinates; force the geometry to be regenerated.
143 geometry_dirty = true;
144 using_coords = true;
145 }
146 }
147 }
148 else
149 ResetCoords();
150
151 // Coordinates have changes; this will most likely result in a size change, so we need to force a layout.
152 dirty_layout = true;
153 }
154
155 if (dirty_layout)
156 DirtyLayout();
157 }
158
159 // Regenerates the element's geometry.
ProcessEvent(Rocket::Core::Event & event)160 void ElementImage::ProcessEvent(Rocket::Core::Event& event)
161 {
162 Element::ProcessEvent(event);
163
164 if (event.GetTargetElement() == this &&
165 event == RESIZE)
166 {
167 GenerateGeometry();
168 }
169 }
170
GenerateGeometry()171 void ElementImage::GenerateGeometry()
172 {
173 // Release the old geometry before specifying the new vertices.
174 geometry.Release(true);
175
176 std::vector< Rocket::Core::Vertex >& vertices = geometry.GetVertices();
177 std::vector< int >& indices = geometry.GetIndices();
178
179 vertices.resize(4);
180 indices.resize(6);
181
182 // Generate the texture coordinates.
183 Vector2f texcoords[2];
184 if (using_coords)
185 {
186 Vector2f texture_dimensions((float) texture.GetDimensions(GetRenderInterface()).x, (float) texture.GetDimensions(GetRenderInterface()).y);
187 if (texture_dimensions.x == 0)
188 texture_dimensions.x = 1;
189 if (texture_dimensions.y == 0)
190 texture_dimensions.y = 1;
191
192 texcoords[0].x = (float) coords[0] / texture_dimensions.x;
193 texcoords[0].y = (float) coords[1] / texture_dimensions.y;
194
195 texcoords[1].x = (float) coords[2] / texture_dimensions.x;
196 texcoords[1].y = (float) coords[3] / texture_dimensions.y;
197 }
198 else
199 {
200 texcoords[0] = Vector2f(0, 0);
201 texcoords[1] = Vector2f(1, 1);
202 }
203
204 Rocket::Core::GeometryUtilities::GenerateQuad(&vertices[0], // vertices to write to
205 &indices[0], // indices to write to
206 Vector2f(0, 0), // origin of the quad
207 GetBox().GetSize(Rocket::Core::Box::CONTENT), // size of the quad
208 Colourb(255, 255, 255, 255), // colour of the vertices
209 texcoords[0], // top-left texture coordinate
210 texcoords[1]); // top-right texture coordinate
211
212 geometry_dirty = false;
213 }
214
LoadTexture()215 bool ElementImage::LoadTexture()
216 {
217 texture_dirty = false;
218
219 // Get the source URL for the image.
220 String image_source = GetAttribute< String >("src", "");
221 if (image_source.Empty())
222 return false;
223
224 geometry_dirty = true;
225
226 Rocket::Core::ElementDocument* document = GetOwnerDocument();
227 URL source_url(document == NULL ? "" : document->GetSourceURL());
228
229 if (!texture.Load(image_source, source_url.GetPath()))
230 {
231 geometry.SetTexture(NULL);
232 return false;
233 }
234
235 // Set the texture onto our geometry object.
236 geometry.SetTexture(&texture);
237 return true;
238 }
239
ResetCoords()240 void ElementImage::ResetCoords()
241 {
242 using_coords = false;
243
244 for (int i = 0; i < 4; ++i)
245 coords[i] = -1;
246 }
247
248 }
249 }
250