]> git.mxchange.org Git - flightgear.git/blob - src/Canvas/elements/CanvasImage.cxx
Canvas: CSS like property value inheritance.
[flightgear.git] / src / Canvas / elements / CanvasImage.cxx
1 // An image on the canvas
2 //
3 // Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18
19 #include "CanvasImage.hxx"
20
21 #include <osgDB/ReadFile>
22
23 #include <Canvas/canvas.hxx>
24 #include <Canvas/canvas_mgr.hxx>
25 #include <Canvas/property_helper.hxx>
26 #include <osg/Array>
27 #include <osg/Geometry>
28 #include <osg/PrimitiveSet>
29
30 #include <Main/globals.hxx>
31 #include <Main/fg_props.hxx>
32
33 #include <boost/algorithm/string/predicate.hpp>
34
35 /**
36  * Callback to enable/disable rendering of canvas displayed inside windows or
37  * other canvases.
38  */
39 class CullCallback:
40   public osg::Drawable::CullCallback
41 {
42   public:
43     CullCallback(Canvas::CameraCullCallback* camera_cull);
44
45   private:
46     Canvas::CameraCullCallback *_camera_cull;
47
48     virtual bool cull( osg::NodeVisitor* nv,
49                        osg::Drawable* drawable,
50                        osg::RenderInfo* renderInfo ) const;
51 };
52
53 //------------------------------------------------------------------------------
54 CullCallback::CullCallback(Canvas::CameraCullCallback* camera_cull):
55   _camera_cull( camera_cull )
56 {
57
58 }
59
60 //------------------------------------------------------------------------------
61 bool CullCallback::cull( osg::NodeVisitor* nv,
62                          osg::Drawable* drawable,
63                          osg::RenderInfo* renderInfo ) const
64 {
65   _camera_cull->enableRendering();
66
67   // TODO check if window/image should be culled
68   return false;
69 }
70
71 namespace canvas
72 {
73   //----------------------------------------------------------------------------
74   Image::Image(SGPropertyNode_ptr node, const Style& parent_style):
75     Element(node, parent_style, BOUNDING_BOX),
76     _texture(new osg::Texture2D),
77     _node_src_rect( node->getNode("source", 0, true) )
78   {
79     _geom = new osg::Geometry;
80     _geom->setUseDisplayList(false);
81
82     osg::StateSet *stateSet = _geom->getOrCreateStateSet();
83     stateSet->setTextureAttributeAndModes(0, _texture.get());
84     stateSet->setDataVariance(osg::Object::STATIC);
85
86     // allocate arrays for the image
87     _vertices = new osg::Vec3Array(4);
88     _vertices->setDataVariance(osg::Object::STATIC);
89     _geom->setVertexArray(_vertices);
90
91     _texCoords = new osg::Vec2Array(4);
92     _texCoords->setDataVariance(osg::Object::STATIC);
93     _geom->setTexCoordArray(0, _texCoords);
94
95     _colors = new osg::Vec4Array(4);
96     _colors->setDataVariance(osg::Object::STATIC);
97     _geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
98     _geom->setColorArray(_colors);
99
100     osg::DrawArrays* prim = new osg::DrawArrays(osg::PrimitiveSet::QUADS);
101     prim->set(osg::PrimitiveSet::QUADS, 0, 4);
102     prim->setDataVariance(osg::Object::STATIC);
103     _geom->addPrimitiveSet(prim);
104
105     setDrawable(_geom);
106
107     addStyle("fill", &Image::setFill, this);
108     setFill("#ffffff"); // TODO how should we handle default values?
109
110     setupStyle();
111   }
112
113   //----------------------------------------------------------------------------
114   Image::~Image()
115   {
116
117   }
118
119   //----------------------------------------------------------------------------
120   void Image::update(double dt)
121   {
122     Element::update(dt);
123
124     if( _attributes_dirty & DEST_SIZE )
125     {
126       (*_vertices)[0].set(_region.l(), _region.t(), 0);
127       (*_vertices)[1].set(_region.r(), _region.t(), 0);
128       (*_vertices)[2].set(_region.r(), _region.b(), 0);
129       (*_vertices)[3].set(_region.l(), _region.b(), 0);
130       _vertices->dirty();
131
132       _attributes_dirty &= ~DEST_SIZE;
133       _geom->dirtyBound();
134     }
135
136     if( _attributes_dirty & SRC_RECT )
137     {
138       double u0 = _src_rect.l(),
139              u1 = _src_rect.r(),
140              v0 = _src_rect.b(),
141              v1 = _src_rect.t();
142
143       if( !_node_src_rect->getBoolValue("normalized", true) )
144       {
145         const Rect<int>& tex_dim = getTextureDimensions();
146
147         u0 /= tex_dim.width();
148         u1 /= tex_dim.width();
149         v0 /= tex_dim.height();
150         v1 /= tex_dim.height();
151       }
152
153       (*_texCoords)[0].set(u0, v0);
154       (*_texCoords)[1].set(u1, v0);
155       (*_texCoords)[2].set(u1, v1);
156       (*_texCoords)[3].set(u0, v1);
157       _texCoords->dirty();
158
159       _attributes_dirty &= ~SRC_RECT;
160     }
161   }
162
163   //----------------------------------------------------------------------------
164   void Image::setCanvas(CanvasPtr canvas)
165   {
166     _canvas = canvas;
167     _geom->getOrCreateStateSet()
168          ->setTextureAttribute(0, canvas ? canvas->getTexture() : 0);
169     _geom->setCullCallback(
170       canvas ? new CullCallback(canvas->getCameraCullCallback()) : 0
171     );
172
173     if( !_canvas.expired() )
174       setupDefaultDimensions();
175   }
176
177   //----------------------------------------------------------------------------
178   CanvasWeakPtr Image::getCanvas() const
179   {
180     return _canvas;
181   }
182
183   //----------------------------------------------------------------------------
184   void Image::setImage(osg::Image *img)
185   {
186     // remove canvas...
187     setCanvas( CanvasPtr() );
188
189     _texture->setImage(img);
190     _geom->getOrCreateStateSet()
191          ->setTextureAttributeAndModes(0, _texture);
192
193     if( img )
194       setupDefaultDimensions();
195   }
196
197   //----------------------------------------------------------------------------
198   void Image::setFill(const std::string& fill)
199   {
200     osg::Vec4 color = parseColor(fill);
201     for( int i = 0; i < 4; ++i )
202       (*_colors)[i] = color;
203     _colors->dirty();
204   }
205
206   //----------------------------------------------------------------------------
207   const Rect<float>& Image::getRegion() const
208   {
209     return _region;
210   }
211
212   //----------------------------------------------------------------------------
213   void Image::childChanged(SGPropertyNode* child)
214   {
215     const std::string& name = child->getNameString();
216
217     if( child->getParent() == _node_src_rect )
218     {
219       _attributes_dirty |= SRC_RECT;
220
221       if(      name == "left" )
222         _src_rect.setLeft( child->getFloatValue() );
223       else if( name == "right" )
224         _src_rect.setRight( child->getFloatValue() );
225       else if( name == "top" )
226         _src_rect.setTop( child->getFloatValue() );
227       else if( name == "bottom" )
228         _src_rect.setBottom( child->getFloatValue() );
229
230       return;
231     }
232     else if( child->getParent() != _node )
233       return;
234
235     if( name == "x" )
236     {
237       _region.setX( child->getFloatValue() );
238       _attributes_dirty |= DEST_SIZE;
239     }
240     else if( name == "y" )
241     {
242       _region.setY( child->getFloatValue() );
243       _attributes_dirty |= DEST_SIZE;
244     }
245     else if( name == "size" )
246     {
247       if( child->getIndex() == 0 )
248         _region.setWidth( child->getFloatValue() );
249       else
250         _region.setHeight( child->getFloatValue() );
251
252       _attributes_dirty |= DEST_SIZE;
253     }
254     else if( name == "file" )
255     {
256       static const std::string CANVAS_PROTOCOL = "canvas://";
257       const std::string& path = child->getStringValue();
258
259       if( boost::starts_with(path, CANVAS_PROTOCOL) )
260       {
261         CanvasMgr* canvas_mgr =
262           dynamic_cast<CanvasMgr*>(globals->get_subsystem("Canvas"));
263         if( !canvas_mgr )
264         {
265           SG_LOG(SG_GL, SG_ALERT, "canvas::Image: Failed to get CanvasMgr");
266           return;
267         }
268
269         const SGPropertyNode* canvas_node =
270           canvas_mgr->getPropertyRoot()
271                     ->getParent()
272                     ->getNode( path.substr(CANVAS_PROTOCOL.size()) );
273         if( !canvas_node )
274         {
275           SG_LOG(SG_GL, SG_ALERT, "canvas::Image: No such canvas: " << path);
276           return;
277         }
278
279         // TODO add support for other means of addressing canvases (eg. by
280         // name)
281         CanvasPtr canvas = canvas_mgr->getCanvas( canvas_node->getIndex() );
282         if( !canvas )
283         {
284           SG_LOG(SG_GL, SG_ALERT, "canvas::Image: Invalid canvas: " << path);
285           return;
286         }
287
288         setCanvas(canvas);
289       }
290       else
291       {
292         SGPath tpath = globals->resolve_ressource_path(path);
293         if( tpath.isNull() || !tpath.exists() )
294         {
295           SG_LOG(SG_GL, SG_ALERT, "canvas::Image: No such image: " << path);
296           return;
297         }
298
299         setImage( osgDB::readImageFile(tpath.c_str()) );
300       }
301     }
302   }
303
304   //----------------------------------------------------------------------------
305   void Image::setupDefaultDimensions()
306   {
307     if( !_src_rect.width() || !_src_rect.height() )
308     {
309       const Rect<int>& tex_dim = getTextureDimensions();
310
311       _node_src_rect->setBoolValue("normalized", false);
312       _node_src_rect->setFloatValue("right", tex_dim.width());
313       _node_src_rect->setFloatValue("bottom", tex_dim.height());
314     }
315
316     if( !_region.width() || !_region.height() )
317     {
318       _node->setFloatValue("size[0]", _src_rect.width());
319       _node->setFloatValue("size[1]", _src_rect.height());
320     }
321   }
322
323   //----------------------------------------------------------------------------
324   Rect<int> Image::getTextureDimensions() const
325   {
326     osg::Texture2D *texture = !_canvas.expired()
327                               ? _canvas.lock()->getTexture()
328                               : _texture.get();
329
330     if( !texture )
331       return Rect<int>();
332
333     return Rect<int>
334     (
335       0,0,
336       texture->getTextureWidth(),
337       texture->getTextureHeight()
338     );
339   }
340
341 } // namespace canvas