1 /* Gnome Music Player Client (GMPC)
2 * Copyright (C) 2004-2011 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.
24 const string LOG_DOMAIN
= "ImageAsync";
28 * Operations you can do on the image.
29 * The modified pixbuf will be stored in cache.
31 public enum ModificationType
{
32 NONE
= 0, // Add nothing
33 CASING
= 1, // Add border and or casing
34 DARKEN
= 2, // Darken the image (for backdrop)
35 DECOLOR
= 4, // Remove color from image.
38 public class PixbufLoaderAsync
: GLib
.Object
40 private weak GLib
.Cancellable? pcancel
= null;
41 public string uri
= null;
42 public Gdk
.Pixbuf pixbuf
{set;get;default=null;}
43 private Gtk
.TreeRowReference rref
= null;
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()
57 var model
= rref
.get_model();
58 var path
= rref
.get_path();
60 if(model
.get_iter(out iter
, path
))
62 model
.row_changed(path
, iter
);
68 GLib
.log(LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
,"Create the image loading\n" );
71 ~PixbufLoaderAsync() {
72 GLib
.log(LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
,"Free the image loading");
73 if(this
.pcancel
!= null) pcancel
.cancel();
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);
89 var path
= ii
.get_filename();
91 var case_image
= new Gdk
.Pixbuf
.from_file_at_scale(path
, size
, size
, true);
93 var tempw
= (int)(case_image
.width
*(1.0-spineRatio
));
95 if((case_image
.height
/(double)height
)*width
< tempw
) {
96 pix2
= pix
.scale_simple(tempw
, (int)((height
*tempw
)/width
), Gdk
.InterpType
.BILINEAR
);
98 pix2
= pix
.scale_simple((int)(width
*(case_image
.height
/(double)height
)), case_image
.height
, Gdk
.InterpType
.BILINEAR
);
100 var blank
= new Gdk
.Pixbuf(Gdk
.Colorspace
.RGB
, true, 8, case_image
.width
, case_image
.height
);
101 blank
.fill(0x000000FF);
102 tempw
= (tempw
>= pix2
.width
)? pix2
.width
:tempw
;
103 var temph
= (case_image
.height
> pix2
.height
)?pix2
.height
:case_image
.height
;
104 pix2
.copy_area(0,0, tempw
-1, temph
-2, blank
, case_image
.width
-tempw
, 1);
105 case_image
.composite(blank
, 0,0,case_image
.width
, case_image
.height
, 0,0,1,1,Gdk
.InterpType
.BILINEAR
, 250);
108 GLib
.log(LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_WARNING
,
109 "Failed to get the stylized-cover image");
114 Gmpc
.Fix
.add_border(pix
);
117 if ((casing
&ModificationType
.DARKEN
) == ModificationType
.DARKEN
)
119 Gmpc
.Misc
.darken_pixbuf(pix
, 2);
121 if ((casing
&ModificationType
.DECOLOR
) == ModificationType
.DECOLOR
)
123 Gmpc
.Misc
.decolor_pixbuf(pix
, pix
);
125 if ((casing
&ModificationType
.BORDER
) == ModificationType
.BORDER
)
127 Gmpc
.Misc
.border_pixbuf(pix
);
134 public void set_from_raw(uchar[] data
, int req_width
, int req_height
, ModificationType border
)
138 /* If running cancel the current action. */
141 GLib
.Cancellable cancel
= new GLib
.Cancellable();
142 this
.pcancel
= cancel
;
143 /* var pb = Gmpc.PixbufCache.lookup_icon(int.max(width,height), uri);
147 pixbuf_update(pixbuf);
152 Gdk
.PixbufLoader loader
= new Gdk
.PixbufLoader();
153 loader
.size_prepared
.connect(size_prepare
);
154 loader
.area_prepared
.connect((source
) => {
155 var apix
= loader
.get_pixbuf();
156 var afinal
= this
.modify_pixbuf((owned
)apix
, int.max(height
, width
),border
);
159 pixbuf_update(pixbuf
);
163 Gmpc
.Fix
.write_loader(loader
, (string)data
, data
.length
);
165 warning("Error trying to fetch image: %s::%s", e
.message
,uri
);
170 debug("Error trying to parse image: %s::%s? query cancelled?", err
.message
,uri
);
175 /* Failed to load the image */
179 if(cancel
.is_cancelled())
181 GLib
.log(LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
,"Cancelled loading of image");
189 Gdk
.Pixbuf pix
= loader
.get_pixbuf();
190 /* Maybe another thread allready fetched it in the mean time, we want to use that... */
192 var final = Gmpc.PixbufCache.lookup_icon(int.max(height, width), uri);
195 Gmpc.PixbufCache.add_icon(int.max(height, width),uri, final);
198 var final
= this
.modify_pixbuf((owned
)pix
, int.max(height
, width
),border
);
200 pixbuf_update(pixbuf
);
205 public void set_from_file(string uri
, int req_width
, int req_height
, ModificationType border
)
209 /* If running cancel the current action. */
215 var pb
= Gmpc
.PixbufCache
.lookup_icon(int.max(width
,height
), uri
);
219 pixbuf_update(pixbuf
);
223 GLib
.Cancellable cancel
= new GLib
.Cancellable();
224 this
.pcancel
= cancel
;
225 this
.load_from_file_async(uri
, width
,height
, cancel
, border
);
229 GLib
.log(LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
,"Cancel the image loading");
230 if(this
.pcancel
!= null) {
231 this
.pcancel
.cancel();
234 private void size_prepare(Gdk
.PixbufLoader loader
,int gwidth
, int gheight
)
236 double dsize
= (double)(int.max(width
,height
));
237 int nwidth
= 0, nheight
= 0;
239 double scale
= width
/(double)gwidth
;
241 nheight
= (int)(gheight
*scale
);
242 } else if (width
< 0) {
243 double scale
= height
/(double)gheight
;
245 nwidth
= (int)(gwidth
*scale
);
247 nwidth
= (gheight
>gwidth
)?
(int)((dsize
/gheight
)*gwidth
): (int)dsize
;
248 nheight
= (gwidth
> gheight
)?
(int)((dsize
/gwidth
)*gheight
): (int)dsize
;
250 loader
.set_size(nwidth
, nheight
);
252 private async
void load_from_file_async(string uri
, int req_width
, int req_height
, GLib
.Cancellable cancel
, ModificationType border
)
256 GLib
.File file
= GLib
.File
.new_for_path(uri
);
258 Gdk
.PixbufLoader loader
= new Gdk
.PixbufLoader();
259 loader
.size_prepared
.connect(size_prepare
);
261 loader.area_prepared.connect((source) => {
262 var apix = loader.get_pixbuf();
263 var afinal = this.modify_pixbuf((owned)apix, int.max(height, width),border);
266 pixbuf_update(pixbuf);
270 var stream
= yield file
.read_async(0, cancel
);
271 if(!cancel
.is_cancelled() && stream
!= null )
276 result
= yield stream
.read_async(data
,0, cancel
);
277 Gmpc
.Fix
.write_loader(loader
,(string)data
, result
);
278 }catch ( Error erro
) {
279 warning("Error trying to fetch image: %s::%s", erro
.message
,uri
);
282 }while(!cancel
.is_cancelled() && result
> 0);
285 warning("Error trying to fetch image: %s::%s", e
.message
,uri
);
290 debug("Error trying to parse image: %s::%s? query cancelled?", err
.message
,uri
);
295 /* Failed to load the image */
299 if(cancel
.is_cancelled())
301 GLib
.log(LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
,"Cancelled loading of image");
309 Gdk
.Pixbuf pix
= loader
.get_pixbuf();
310 /* Maybe another thread allready fetched it in the mean time, we want to use that... */
311 var final
= Gmpc
.PixbufCache
.lookup_icon(int.max(height
, width
), uri
);
314 final
= this
.modify_pixbuf((owned
)pix
, int.max(height
, width
),border
);
315 Gmpc
.PixbufCache
.add_icon(int.max(height
, width
),uri
, final
);
318 pixbuf_update(pixbuf
);
325 public class MetaImageAsync
: Gtk
.Image
327 private Gmpc
.PixbufLoaderAsync? loader
= null;
328 public string uri
= null;
334 GLib
.log(LOG_DOMAIN
,GLib
.LogLevelFlags
.LEVEL_DEBUG
,"Freeing metaimageasync\n");
336 public new
void set_from_raw(uchar[] data
, int size
, ModificationType border
)
339 loader
= new
PixbufLoaderAsync();
340 loader
.pixbuf_update
.connect((source
, pixbuf
)=>{
341 this
.set_from_pixbuf(pixbuf
);
344 loader
.set_from_raw(data
, size
,size
, border
);
346 Gdk.PixbufLoader loader = new Gdk.PixbufLoader();
347 Gmpc.Fix.write_loader(loader,(string)data, data.length);
349 Gdk.Pixbuf pix = loader.get_pixbuf();
350 this.set_from_pixbuf(pix);
355 public new
void set_from_file(string uri
, int size
, ModificationType border
)
359 loader
= new
PixbufLoaderAsync();
360 loader
.pixbuf_update
.connect((source
, pixbuf
)=>{
361 this
.set_from_pixbuf(pixbuf
);
364 loader
.set_from_file(uri
, size
,size
, border
);
366 public new
void set_from_file_at_size(string uri
, int width
,int height
, ModificationType border
)
370 loader
= new
PixbufLoaderAsync();
371 loader
.pixbuf_update
.connect((source
, pixbuf
)=>{
372 this
.set_from_pixbuf(pixbuf
);
375 loader
.set_from_file(uri
, width
,height
, border
);
377 public void clear_now()
384 public void set_pixbuf(Gdk
.Pixbuf? pb
)
389 this
.set_from_pixbuf(pb
);