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