Update vala version.
[gmpc.git] / src / Widgets / gmpc-image-async.vala
blobe0a0815237322701b5d162914eacfb208c63f5c2
1 /* Gnome Music Player Client (GMPC)
2 * Copyright (C) 2004-2012 Qball Cow <qball@gmpclient.org>
3 * Project homepage: http://gmpclient.org/
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 using GLib;
21 using Gtk;
22 using Gdk;
24 const string LOG_DOMAIN = "ImageAsync";
25 namespace Gmpc
27 /**
28 * Operations you can do on the image.
29 * The modified pixbuf will be stored in cache.
31 public enum ModificationType
33 NONE = 0, // Add nothing
34 CASING = 1, // Add border and or casing
35 DARKEN = 2, // Darken the image (for backdrop)
36 DECOLOR = 4, // Remove color from image.
37 BORDER = 8
39 public class PixbufLoaderAsync : GLib.Object
41 public string uri = null;
42 public Gdk.Pixbuf pixbuf {set; get; default=null;}
43 private Gtk.TreeRowReference rref = null;
44 private int width=0;
45 private int height=0;
47 public signal void pixbuf_update(Gdk.Pixbuf? pixbuf);
49 public void set_rref(Gtk.TreeRowReference rreference)
51 this.rref = rreference;
54 private void call_row_changed()
56 if(rref != null)
58 var model = rref.get_model();
59 Gtk.TreePath? path = rref.get_path();
60 Gtk.TreeIter iter;
61 if(path != null)
63 if(model.get_iter(out iter, path)) {
64 model.row_changed(path, iter);
70 construct
72 GLib.log(LOG_DOMAIN,GLib.LogLevelFlags.LEVEL_DEBUG,"Create the image loading\n" );
76 private Gdk.Pixbuf? modify_pixbuf(owned Gdk.Pixbuf? pix, int size,ModificationType casing)
78 if(pix == null) return null;
79 if((casing&ModificationType.CASING) == ModificationType.CASING)
81 if(config.get_int_with_default("metaimage", "addcase", 1) == 1)
83 int width = pix.width;
84 int height = pix.height;
85 double spineRatio = 5.0/65.0;
87 var ii = Gtk.IconTheme.get_default().lookup_icon("stylized-cover", size, 0);
88 if(ii != null)
90 var path = ii.get_filename();
91 try
93 var case_image = new Gdk.Pixbuf.from_file_at_scale(path, size, size, true);
95 var tempw = (int)(case_image.width*(1.0-spineRatio));
96 Gdk.Pixbuf pix2;
97 if((case_image.height/(double)height)*width < tempw)
99 pix2 = pix.scale_simple(tempw, (int)((height*tempw)/width), Gdk.InterpType.BILINEAR);
101 else
103 pix2 = pix.scale_simple((int)(width*(case_image.height/(double)height)), case_image.height, Gdk.InterpType.BILINEAR);
105 var blank = new Gdk.Pixbuf(Gdk.Colorspace.RGB, true, 8, case_image.width, case_image.height);
106 blank.fill(0x000000FF);
107 tempw = (tempw >= pix2.width)? pix2.width:tempw;
108 var temph = (case_image.height > pix2.height)?pix2.height:case_image.height;
109 pix2.copy_area(0,0, tempw-1, temph-2, blank, case_image.width-tempw, 1);
110 case_image.composite(blank, 0,0,case_image.width, case_image.height, 0,0,1,1,Gdk.InterpType.BILINEAR, 250);
111 pix = (owned)blank;
113 catch (Error e)
115 GLib.log(LOG_DOMAIN,GLib.LogLevelFlags.LEVEL_WARNING,
116 "Failed to get the stylized-cover image");
121 else
123 Gmpc.Fix.add_border(pix);
126 if ((casing&ModificationType.DARKEN) == ModificationType.DARKEN)
128 Gmpc.Misc.darken_pixbuf(pix, 2);
130 if ((casing&ModificationType.DECOLOR) == ModificationType.DECOLOR)
132 Gmpc.Misc.decolor_pixbuf(pix, pix);
134 if ((casing&ModificationType.BORDER) == ModificationType.BORDER)
136 Gmpc.Misc.border_pixbuf(pix);
139 return pix;
143 private uint loader_timeout = 0;
144 private Gdk.PixbufLoader? loader = null;
145 private uchar[] loader_data = null;
146 private uint loader_data_offset = 0;
147 private uchar[] loader_md5sum = null;
148 private ModificationType loader_border = ModificationType.NONE;
150 ~PixbufLoaderAsync()
152 GLib.log(LOG_DOMAIN,GLib.LogLevelFlags.LEVEL_DEBUG,"Free the image loading");
153 // Cancel previous load.
154 if ( loader_timeout > 0)
156 GLib.Source.remove(loader_timeout);
158 loader_data = null;
159 loader_md5sum = null;
160 loader_timeout = 0;
163 loader.close();
165 catch (Error e)
167 // Ignore this.
169 loader = null;
175 public void set_from_raw(uchar[] data, int req_width, int req_height, ModificationType border, [CCode (array_length = false)]uchar[] md5sum)
177 width = req_width;
178 height = req_height;
180 // Cancel previous load.
181 if ( loader_timeout > 0)
183 GLib.Source.remove(loader_timeout);
185 loader_data = null;
186 loader_md5sum = null;
187 loader_timeout = 0;
190 loader.close();
192 catch (Error e)
194 // Ignore this.
196 loader = null;
200 var pb = Gmpc.PixbufCache.lookup_icon(int.max(width,height), md5sum);
201 if(pb != null)
203 this.pixbuf = pb;
204 pixbuf_update(pixbuf);
205 call_row_changed();
206 return;
209 loader = new Gdk.PixbufLoader();
210 loader.size_prepared.connect(size_prepare);
212 loader_data = data;
213 loader_data_offset = 0;
214 loader_md5sum = md5sum[0:16];
215 loader_border = border;
218 loader_timeout = GLib.Idle.add(loader_idle_callback);
219 // loader_timeout = GLib.Timeout.add(200, loader_idle_callback);
220 return;
223 private bool loader_idle_callback()
228 uint end = uint.min(loader_data_offset+2048, loader_data.length);
229 Gmpc.Fix.write_loader(loader, (string)loader_data[loader_data_offset:end], end-loader_data_offset);
230 loader_data_offset = end;;
232 catch ( Error e)
234 warning("Error trying to load image: %s::%s", e.message,uri);
236 if(loader_data_offset < loader_data.length) return true;
240 loader.close();
242 catch (Error err)
244 debug("Error trying to parse image: %s::%s? query cancelled?", err.message,uri);
245 pixbuf_update(null);
246 call_row_changed();
248 loader_data = null;
249 loader_md5sum = null;
250 loader = null;
251 loader_timeout = 0;
252 return false;
255 Gdk.Pixbuf pix = loader.get_pixbuf();
256 /* Maybe another thread allready fetched it in the mean time, we want to use that... */
257 var final = Gmpc.PixbufCache.lookup_icon(int.max(height, width), loader_md5sum);
258 if(final == null)
260 final = this.modify_pixbuf((owned)pix, int.max(height, width),loader_border);
261 Gmpc.PixbufCache.add_icon(int.max(height, width),loader_md5sum, final);
264 this.pixbuf = final;
265 pixbuf_update(pixbuf);
266 call_row_changed();
268 // Cleanup
269 loader = null;
270 loader_data = null;
271 loader_md5sum = null;
272 loader_timeout = 0;
273 return false;
276 private void size_prepare(Gdk.PixbufLoader loader,int gwidth, int gheight)
278 double dsize = (double)(int.max(width,height));
279 int nwidth = 0, nheight = 0;
280 if(height < 0)
282 double scale = width/(double)gwidth;
283 nwidth = width;
284 nheight = (int)(gheight*scale);
286 else if (width < 0)
288 double scale = height/(double)gheight;
289 nheight = height;
290 nwidth = (int)(gwidth*scale);
292 else
294 nwidth = (gheight >gwidth)? (int)((dsize/gheight)*gwidth): (int)dsize;
295 nheight= (gwidth > gheight )? (int)((dsize/gwidth)*gheight): (int)dsize;
297 loader.set_size(nwidth, nheight);
300 public class MetaImageAsync : Gtk.Image
302 private Gmpc.PixbufLoaderAsync? loader = null;
303 public string uri = null;
305 construct
309 ~MetaImageAsync()
311 GLib.log(LOG_DOMAIN,GLib.LogLevelFlags.LEVEL_DEBUG,"Freeing metaimageasync\n");
313 public new void set_from_raw(uchar[] data, int size, ModificationType border, [CCode (array_length = false)] uchar[] md5sum)
315 if(loader == null)
317 loader = new PixbufLoaderAsync();
318 loader.pixbuf_update.connect((source, pixbuf)=>
320 this.set_from_pixbuf(pixbuf);
323 loader.set_from_raw(data, size,size, border,md5sum);
326 public void clear_now()
328 this.loader = null;
329 this.uri = null;
330 this.clear();
333 public void set_pixbuf(Gdk.Pixbuf? pb)
335 this.loader = null;
336 this.uri = null;
337 if(pb != null)
339 this.set_from_pixbuf(pb);
341 else
343 this.clear();