1 /* GdkPixbuf library - Scaling and compositing demo
2  *
3  * Copyright (C) 2002 The gtkmm Development Team
4  *
5  * Authors: Federico Mena-Quintero <federico@gimp.org>
6  * gtkmm port: Daniel Elstner <daniel.kitta@gmail.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23 
24 #include <cmath>
25 #include <cstdlib>
26 #include <algorithm>
27 #include <iostream>
28 #include <iterator>
29 #include <vector>
30 
31 #include <sigc++/sigc++.h>
32 #include <glibmm/main.h>
33 #include <gdkmm/pixbuf.h>
34 #include <gdkmm/rectangle.h>
35 #include <gtkmm/drawingarea.h>
36 #include <gtkmm/main.h>
37 #include <gtkmm/window.h>
38 #include <gdkmm/general.h>
39 
40 namespace
41 {
42 
43 /*
44  * These values control the smoothness and speed of the animation.
45  * For instance, FRAME_DELAY=25 and CYCLE_LEN=90 looks much better
46  * to me (and also wastes more CPU cycles, of course).
47  */
48 enum
49 {
50   FRAME_DELAY = 48,
51   CYCLE_LEN   = 64
52 };
53 
54 const char * const background_name =
55   "gtk-demo/background.jpg";
56 
57 const char * const image_names[] =
58 {
59   "gtk-demo/apple-red.png",
60   "gtk-demo/gnome-applets.png",
61   "gtk-demo/gnome-calendar.png",
62   "gtk-demo/gnome-foot.png",
63   "gtk-demo/gnome-gmush.png",
64   "gtk-demo/gnome-gimp.png",
65   "gtk-demo/gnome-gsame.png",
66   "gtk-demo/gnu-keys.png"
67 };
68 
69 class DemoRenderArea : public Gtk::DrawingArea
70 {
71 public:
72   DemoRenderArea();
73   virtual ~DemoRenderArea();
74 
75 protected:
76   virtual bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr);
77 
78 private:
79   Glib::RefPtr<const Gdk::Pixbuf>                 background_;
80   std::vector< Glib::RefPtr<const Gdk::Pixbuf> >  images_;
81   Glib::RefPtr<Gdk::Pixbuf>                       current_frame_;
82   unsigned int                                    frame_num_;
83 
84   void generate_next_frame();
85 };
86 
create_pixbuf(const std::string & name)87 Glib::RefPtr<Gdk::Pixbuf> create_pixbuf(const std::string& name)
88 {
89   return Gdk::Pixbuf::create_from_file(name);
90 }
91 
92 /*
93  * Load all image files, create an empty buffer for storing the current frame,
94  * and install a timeout handler that will be called periodically.  The show
95  * will start as soon as Gtk::Main::run() is invoked.
96  */
DemoRenderArea()97 DemoRenderArea::DemoRenderArea()
98 :
99   frame_num_ (0)
100 {
101   background_ = Gdk::Pixbuf::create_from_file(background_name);
102   std::transform(
103       &image_names[0], &image_names[G_N_ELEMENTS(image_names)],
104       std::back_inserter(images_),
105       &create_pixbuf);
106 
107   const int back_width  = background_->get_width();
108   const int back_height = background_->get_height();
109 
110   current_frame_ = Gdk::Pixbuf::create(
111       Gdk::COLORSPACE_RGB, false, 8, back_width, back_height);
112 
113   set_size_request(back_width, back_height);
114   add_events(Gdk::EXPOSURE_MASK);
115 
116   Glib::signal_timeout().connect(
117       sigc::bind_return(sigc::mem_fun(*this, &DemoRenderArea::generate_next_frame), true),
118       FRAME_DELAY);
119 }
120 
~DemoRenderArea()121 DemoRenderArea::~DemoRenderArea()
122 {}
123 
124 /*
125  * Draw handler of the widget.  Just fill the exposed
126  * area with the corresponding pixmap data from current_frame_.
127  */
on_draw(const Cairo::RefPtr<Cairo::Context> & cr)128 bool DemoRenderArea::on_draw(const Cairo::RefPtr<Cairo::Context>& cr)
129 {
130   Gdk::Cairo::set_source_pixbuf(cr, current_frame_);
131   cr->paint();
132 
133   return true; // stop signal emission
134 }
135 
136 /*
137  * Generate the next frame of the animation and store it into current_frame_.
138  * The draw handler accesses that buffer to do the actual drawing.
139  */
generate_next_frame()140 void DemoRenderArea::generate_next_frame()
141 {
142   const int back_width  = background_->get_width();
143   const int back_height = background_->get_height();
144 
145   background_->copy_area(0, 0, back_width, back_height, current_frame_, 0, 0);
146 
147   const double xmid = back_width  / 2.0;
148   const double ymid = back_height / 2.0;
149 
150   const double f = 2.0 * G_PI * double(frame_num_) / CYCLE_LEN;
151 
152   double r = std::min(xmid, ymid) / 2.0;
153   r += (r / 3.0) * std::sin(f);
154 
155   for(unsigned i = 0; i < images_.size(); ++i)
156   {
157     const double ang = 2.0 * G_PI * double(i) / images_.size() - f;
158 
159     const double iw = images_[i]->get_width();
160     const double ih = images_[i]->get_height();
161 
162     const int xpos = int(std::floor(xmid + r * std::cos(ang) - iw / 2.0 + 0.5));
163     const int ypos = int(std::floor(ymid + r * std::sin(ang) - ih / 2.0 + 0.5));
164 
165     const double depth = ((i % 2) != 0) ? std::sin(f) : std::cos(f);
166     const double scale = std::max(0.25, 2.0 * depth * depth);
167 
168     Gdk::Rectangle rect (xpos, ypos, int(iw * scale), int(ih * scale));
169     rect.intersect(Gdk::Rectangle(0, 0, back_width, back_height));
170 
171     if(!rect.has_zero_area())
172     {
173       const int overall_alpha = std::max(127, int(std::abs(255.0 * depth)));
174 
175       images_[i]->composite(
176           current_frame_,
177           rect.get_x(), rect.get_y(), rect.get_width(), rect.get_height(),
178           xpos, ypos, scale, scale,
179           Gdk::INTERP_NEAREST,
180           overall_alpha);
181     }
182   }
183   frame_num_ = (frame_num_ + 1) % CYCLE_LEN;
184 
185   // Tell GTK+ the widget should be redrawn soon.  This will trigger the
186   // draw signal if the widget is actually mapped on the screen.
187   queue_draw();
188 }
189 
190 } // anonymous namespace
191 
main(int argc,char ** argv)192 int main(int argc, char** argv)
193 {
194   try
195   {
196     Gtk::Main main_instance (&argc, &argv);
197 
198     Gtk::Window window;
199     window.add(*Gtk::manage(new DemoRenderArea()));
200 
201     window.set_resizable(false);
202     window.show_all();
203 
204     Gtk::Main::run(window);
205   }
206   catch(const Glib::Error& error)
207   {
208     std::cerr << error.what() << std::endl;
209     return EXIT_FAILURE;
210   }
211   return EXIT_SUCCESS;
212 }
213