public OpenGLShape GetShape(ref OpenGLShapeInfo info)
        {
            //
            // Hopefully, we already have this shape
            //
            var ss = _shapesByType [(int)info.ShapeType];
            foreach (var s in ss) {
                if (s.Info.Equals (ref info)) {
                    return s;
                }
            }

            //
            // Uh oh, have to make it.
            //
            _needsSave = true;
            var shape = new OpenGLShape (info);
            ss.Add (shape);

            //
            // Find out its texture requirements
            //
            var r = shape.Info.GetTextureRequirements ();

            //
            // Try to find a texture that can meet those requirements
            //
            foreach (var t in _textures) {
                if (t.TryInsert (r)) {
                    break;
                }
            }

            //
            // If we couldn't find a texture that could take this reference,
            // we better get another texture
            //
            if (r.Texture == null) {
                //
                // Discover the largest texture we can handle.
                // It is the smaller of that supported by OpenGL
                // and whatever the renderer can handle.
                //
                var maxSize = 0;
                GL.GetInteger (All.MaxTextureSize, ref maxSize);
                maxSize = Math.Min (MaxTextureSize, maxSize);

                //
                // Create that texture if this shape will fit in it
                //
                if (r.Frame.Width <= maxSize && r.Frame.Height <= maxSize) {
                    var tex = CreateTexture (maxSize, maxSize);

                    tex.NeedsSave = true;
                    _textures.Add (tex);

                    tex.TryInsert (r);
                }
            }

            shape.TextureReference = r;

            return shape;
        }
        public void Open()
        {
            var path = ShapeStorePath;
            if (string.IsNullOrEmpty (path)) return;

            EnsurePathExists (path);

            //
            // Load textures
            //
            var ti = 0;
            var foundTexture = true;
            var textures = new List<OpenGLTexture> ();
            while (foundTexture) {
                try {
                    var t = ReadTexture (ti);
                    foundTexture = t != null;
                    if (foundTexture) {
                        textures.Insert (ti, t);
                    }
                    ti++;
                }
                catch (Exception) {
                    foundTexture = false;
                }
            }

            //
            // Load the shapes
            //
            var shapesByType = CreateShapesByType ();
            using (var r = System.Xml.XmlReader.Create (System.IO.Path.Combine (path, "ShapeStore.xml"))) {
                while (r.Read ()) {
                    if (r.IsStartElement ("Shape")) {
                        try {
                            var s = new OpenGLShape (r.ReadSubtree (), textures);
                            if (s.TextureReference.Texture != null) {
                                shapesByType[(int)s.Info.ShapeType].Add (s);
                            }
                        }
                        catch (Exception) {
                        }
                    }
                }
            }

            //
            // Done!
            //
            _textures = textures;
            _shapesByType = shapesByType;
        }
        void AddShape(OpenGLShape shape, float x, float y)
        {
            var tex = shape.TextureReference;

            if (tex != null && tex.Texture != null && _nextDrawCallIndex < MaxDrawCalls) {

                //
                // Remember which shapes we're drawing so that they
                // can be rendered into their textures
                //
                if (!_frameShapes.ContainsKey (shape)) {
                    _frameShapes.Add (shape, shape);
                }

                //
                // Get a buffer with enough space
                //
                EnsureRoomForVertices (4);
                var b = _buffers[_currentBufferIndex];
                var i = b.Length;

                //
                // The position must be computed such that the point
                // ShapeOffset is aligned to x, y
                //
                var tx = x - tex.ShapeOffset.X;
                var ty = y - tex.ShapeOffset.Y;
                b.Positions[i] = new Vector2 (tx, ty);
                b.Positions[i + 1] = new Vector2 (tx, ty + tex.Height);
                b.Positions[i + 2] = new Vector2 (tx + tex.Width, ty + tex.Height);
                b.Positions[i + 3] = new Vector2 (tx + tex.Width, ty);

                //
                // TexCoords and Color are simple, you should be able to understand them ;-)
                //
                b.TexCoords[i] = new Vector2 (tex.U, tex.V);
                b.TexCoords[i + 1] = new Vector2 (tex.U, tex.V + tex.TexHeight);
                b.TexCoords[i + 2] = new Vector2 (tex.U + tex.TexWidth, tex.V + tex.TexHeight);
                b.TexCoords[i + 3] = new Vector2 (tex.U + tex.TexWidth, tex.V);

                b.Colors[i] = _color;
                b.Colors[i + 1] = _color;
                b.Colors[i + 2] = _color;
                b.Colors[i + 3] = _color;

                b.Length += 4;

                //
                // Record the draw call
                //
                var call = new OpenGLDrawArrayCall {
                    Operation = All.TriangleFan,
                    BufferIndex = (byte)_currentBufferIndex,
                    Offset = (ushort)i,
                    NumVertices = 4,
                    Texture = tex.Texture,
                };
                _calls[_nextDrawCallIndex] = call;
                _nextDrawCallIndex++;
            }
        }