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