1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
  
12
  
13
  
14
  
15
  
16
  
17
  
18
  
19
  
20
  
21
  
22
  
23
  
24
  
25
  
26
  
27
  
28
  
29
  
30
  
31
  
32
  
33
  
34
  
35
  
36
  
37
  
38
  
39
  
40
  
41
  
42
  
43
  
44
  
45
  
46
  
47
  
48
  
49
  
50
  
51
  
52
  
53
  
54
  
55
  
56
  
57
  
58
  
59
  
60
  
61
  
62
  
63
  
64
  
65
  
66
  
67
  
68
  
69
  
70
  
71
  
72
  
73
  
74
  
75
  
76
  
77
  
78
  
79
  
80
  
81
  
82
  
83
  
84
  
85
  
86
  
87
  
88
  
89
  
90
  
91
  
92
  
93
  
94
  
95
  
96
  
97
  
98
  
99
  
100
  
101
  
102
  
103
  
104
  
105
  
106
  
107
  
108
  
109
  
110
  
111
  
112
  
113
  
114
  
115
  
116
  
117
  
118
  
119
  
120
  
121
  
122
  
123
  
124
  
125
  
126
  
127
  
128
  
129
  
130
  
131
  
132
  
133
  
134
  
135
  
136
  
137
  
138
  
139
  
140
  
141
  
142
  
143
  
144
  
145
  
146
  
147
  
148
  
149
  
150
  
151
  
152
  
153
  
154
  
155
  
156
  
157
  
158
  
159
  
160
  
161
  
162
  
163
  
164
  
165
  
166
  
167
  
168
  
169
  
170
  
171
  
172
  
173
  
174
  
175
  
176
  
177
  
178
  
179
  
180
  
181
  
182
  
183
  
184
  
185
  
#pike __REAL_VERSION__ 
 
//! Display a image on the screen. Requires GTK. 
 
#if constant(GTK2.Window) 
// Toplevel compat for GTK2 
constant GTK = GTK2; 
class GDK { constant Pixmap = GTK2.GdkPixmap; } 
#define USE_GTK2 
#endif 
 
inherit GTK.Window; 
 
#ifdef USE_GTK2 
// Compat for old Window functions 
void set_background(GTK2.GdkPixmap p) { get_window()->set_background(p); } 
GTK2.GdkWindow get_gdkwindow() { return get_window(); } 
void set_policy( int a, int b, int c ) { set_resizable(a||b); } 
void set_usize(int w, int h) { set_size_request(w, h); } 
void set_app_paintable(int flag) { (flag? set_flags:unset_flags)(GTK2.APP_PAINTABLE); } 
#endif 
 
typedef Standards.URI|string|Image.Image|Image.Layer|array(Image.Layer) PVImage; 
//! The image types accepted. If the image is a string, it is assumed 
//! to be a filename of a image that can be loaded with Image.load. 
//! This includes URLs. 
 
//! The alpha combination modes. 
//! 
//! Use @[set_alpha_mode()] to change the mode. 
enum AlphaMode { 
  Squares,  //! Checkerboard pattern (default). 
  Solid,    //! Solid color. 
  None,             //! Ignore alpha. 
  AlphaOnly,        //! Only show the alpha channel (if any). 
}; 
 
 
#define STEP 30 
protected { 
 GDK.Pixmap pixmap; 
 PVImage old_image; 
 AlphaMode alpha_mode; 
 Image.Color.Color alpha_color1 = Image.Color.grey, 
                         alpha_color2 = Image.Color.darkgrey; 
 
 Image.Image get_alpha_squares() 
 { 
   Image.Image alpha_sq = Image.Image( STEP*2, STEP*2, alpha_color1 ); 
   alpha_sq->setcolor( @alpha_color2->rgb() ); 
   alpha_sq->box( 0, 0, STEP-1, STEP-1 ); 
   alpha_sq->box( STEP, STEP, STEP+STEP-1, STEP+STEP-1 ); 
   return alpha_sq; 
 } 
 
 Image.Image do_tile_paste( Image.Image bi, Image.Image i, Image.Image a ) 
 { 
   Image.Image q = i->copy(); 
   for( int y = 0; y<i->ysize(); y+=bi->xsize() ) 
     for( int x = 0; x<i->xsize(); x+=bi->xsize() ) 
       q->paste( bi, x, y ); 
   return q->paste_mask( i, a ); 
 } 
 
 Image.Image combine_alpha( Image.Image i, Image.Image a ) 
 { 
   if(!a) 
     return alpha_mode == AlphaOnly ? i->copy()->clear(255,255,255) : i; 
 
   switch( alpha_mode ) 
   { 
     case Squares:    return do_tile_paste( get_alpha_squares(), i, a ); 
     case Solid:      return i->clear( alpha_color1)->paste_mask( i, a ); 
     case None:       return i; 
     case AlphaOnly:  return a; 
   } 
 } 
 
 string _sprintf(int t) 
 { 
   return t=='O' && sprintf( "%O(%O)", this_program, old_image ); 
 } 
 
 void create( PVImage i ) 
 { 
   catch(GTK.setup_gtk()); 
   ::create( GTK.WindowToplevel ); 
   set_policy( 0,0,1 ); 
   show_now(); 
   if( i ) 
     set_image( i ); 
 } 
 
 float scale_factor = 1.0; 
 int oxs, oys; 
} 
 
void set_alpha_mode( AlphaMode m ) 
//! Set the alpha combining mode. @[m] is one of @[Squares], @[Solid], 
//! @[None] and @[AlphaOnly]. 
{ 
  alpha_mode = m; 
  set_image( old_image ); 
} 
 
void set_alpha_colors( Image.Color.Color c1, Image.Color.Color|void c2 ) 
//! Set the colors used for the alpha combination. @[c2] is only used 
//! for the @[Squares] alpha mode. 
//! 
//! @seealso 
//!   @[set_alpha_mode()] 
{ 
  if( c1 ) alpha_color1 = c1; 
  if( c2 ) alpha_color2 = c2; 
  set_image( old_image ); 
} 
 
Image.Image get_as_image( PVImage i ) 
//! Return the current image as a Image object, with the alpha 
//! combining done. 
{ 
  if( arrayp( i ) ) 
    i = Image.lay( i ); 
 
  if( i->alpha ) 
    i = combine_alpha( i->image(), i->alpha() ); 
  return i; 
} 
 
void set_image( PVImage i ) 
//! Change the image. 
{ 
  if( !i ) return; 
  int is_uri; 
  if( stringp( i ) || 
      (is_uri = (objectp(i) && object_program( i ) >= Standards.URI) ) ) 
  { 
    if( is_uri ) 
      i = Image.decode_layers( Protocols.HTTP.get_url_data( i ) ); 
    else 
      i = Image.load_layers( i ); 
  } 
 
  old_image = i; 
  i = get_as_image( i ); 
  if( scale_factor != 1.0 ) 
    i = i->scale( scale_factor ); 
 
  int xs = i->xsize(), ys = i->ysize(); 
  if( (xs != oxs) || (ys != oys) ) 
  { 
    set_app_paintable( 1 ); 
    set_background( pixmap = GDK.Pixmap( i ) ); 
    set_usize( xs, ys ); 
    oxs = xs; 
    oys = ys; 
  } 
  else 
    pixmap->set( i ); 
  GTK.flush(); 
  while( GTK.main_iteration_do( 0 ) ); 
  get_gdkwindow()->clear(); 
  GTK.flush(); 
  while( GTK.main_iteration_do( 0 ) ); 
} 
 
void scale( float factor ) 
//! Scale the image before display with the specified factor. 
{ 
  scale_factor = factor; 
  set_image( old_image ); 
} 
 
void save( string filename, string|void format ) 
//! Write the image to a file. If no format is specified, PNG is used. 
//! The alpha combination is done on the image before it's saved. 
{ 
  Image.Image i = get_as_image( old_image ); 
  if( scale_factor != 1.0 ) 
    i = i->scale( scale_factor ); 
  Stdio.write_file( filename, 
                    Image[upper_case(format)||"PNG"]["encode"]( i ) ); 
}