ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/kgsueme/kgsueme/board.pl
Revision: 1.2
Committed: Mon Jun 23 01:14:21 2003 UTC (20 years, 11 months ago) by pcg
Content type: text/plain
Branch: MAIN
Changes since 1.1: +28 -16 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 pcg 1.2 package Gtk2::GoBoard;
2 pcg 1.1
3     # my try at a real widget, needs the realobjects branch from Glib-CVS
4    
5     use Glib;
6    
7     use KGS::Constants;
8     use KGS::Game::Board;
9    
10     use POSIX qw(ceil);
11    
12     register Glib::Type Gtk2::DrawingArea, __PACKAGE__,
13     signals => {},
14     properties => {};
15    
16     sub new {
17     my ($self, %arg) = @_;
18     $self = Glib::Object::new $self;
19     while (my ($k, $v) = each %arg) {
20     $self->{$k} = $v
21     }
22     $self;
23     }
24    
25     sub INSTANCE_INIT {
26     warn "instance_init @_\n";
27     my $self = shift;
28    
29     $self->double_buffered (0);
30    
31     $self->signal_connect(configure_event => \&configure_event);
32     }
33    
34     sub size_allocate {
35     my ($self, $alloc) = @_;
36    
37     my ($w, $h) = ($alloc->width, $alloc->height);
38    
39     my $s = $self->{width} = $w;
40     warn "ALLOC($w,$h,$s)\n";#d#
41    
42     1;
43     }
44    
45     sub configure_event {
46     my ($self, $event) = @_;
47     warn "configure @_\n";#d#
48    
49     delete $self->{stack};
50    
51     my $s = $self->{width} = $self->allocation->width;
52     $self->{backgroundpb} = $self->draw_board ($s);
53    
54     $self->{backgroundpm} = new Gtk2::Gdk::Pixmap $self->window, $self->{width}, $self->{width}, -1;
55     $self->{backgroundpm}->draw_pixbuf ($self->style->white_gc,
56     $self->{backgroundpb},
57     0, 0, 0, 0, $self->{width}, $self->{width},
58     "normal", 0, 0);
59     $self->window->set_back_pixmap ($self->{backgroundpm}, 0);
60    
61     $self->repaint_board (delete $self->{board}, 0);
62     $self->window->clear_area (0, 0, $self->{width}, $self->{width});
63    
64     1;
65     }
66    
67     sub new_pixbuf {
68     my ($w, $h, $alpha, $fill) = @_;
69    
70     my $pixbuf = new Gtk2::Gdk::Pixbuf 'rgb', $alpha, 8, $w, $h;
71     $pixbuf->fill ($fill) if defined $fill;
72    
73     $pixbuf;
74     }
75    
76     sub scale_pixbuf {
77     my ($src, $w, $h, $mode, $alpha) = @_;
78    
79     my $dst = new_pixbuf $w, $h, $alpha;
80    
81     $src->scale(
82     $dst, 0, 0, $w, $h, 0, 0,
83     $w / $src->get_width, $h / $src->get_height,
84     $mode,
85     );
86    
87     $dst;
88     }
89    
90     # create a stack of stones
91     sub create_stack {
92     my ($self, $mark, $size, $rand) = @_;
93    
94     my $shadow = $size * 0.05;
95    
96     my $c = \$self->{stack}{$mark};
97     unless ($$c) {
98     for my $stone ($mark & (MARK_W | MARK_GRAY_W) ? @::white_img : @::black_img) {
99     my $base = new_pixbuf $size + $shadow, $size + $shadow, 1, 0x00000000;
100    
101     # zeroeth the shadow
102     if ($mark & (MARK_B | MARK_W)) {
103     # the -0.5's are a mystery to me
104     $::shadow_img->composite (
105     $base, $shadow, $shadow, $size, $size, $shadow - 0.5, $shadow - 0.5,
106     $size / $::shadow_img->get_width, $size / $::shadow_img->get_height,
107     $::config->{speed} ? 'tiles' : 'bilinear', 192
108     );
109     }
110    
111     # first the big stones (handicap stones could be different)
112     for ([MARK_B, $mark & MARK_MOVE ? 255 : 255],
113     [MARK_W, $mark & MARK_MOVE ? 255 : 255],
114     [MARK_GRAY_B, 128],
115     [MARK_GRAY_W, 128]) {
116     my ($mask, $alpha) = @$_;
117     if ($mark & $mask) {
118     $stone->composite (
119     $base, 0, 0, $size, $size, 0, 0,
120     $size / $stone->get_width, $size / $stone->get_height,
121     $::config->{speed} ? 'tiles' : 'bilinear', $alpha
122     );
123     }
124     }
125    
126     # then the small stones
127     for ([MARK_SMALL_B, $::black_img[$rand % @::black_img]],
128     [MARK_SMALL_W, $::white_img[$rand % @::white_img]]) {
129     my ($mask, $img) = @$_;
130     if ($mark & $mask) {
131     $img->composite (
132     $base, (int ($size / 4)) x2, (ceil ($size / 2 + 1)) x2, ($size / 4) x2,
133     $size / $img->get_width / 2, $size / $img->get_height / 2,
134     $::config->{speed} ? 'tiles' : 'bilinear', 224
135     );
136     }
137     }
138    
139     # and lastly any markers
140     my $dark_bg = ! ! ($mark & (MARK_B | MARK_GRAY_B));
141    
142     for ([MARK_CIRCLE, $::circle_img[$dark_bg]],
143     [MARK_TRIANGLE, $::triangle_img[$dark_bg]],
144     [MARK_SQUARE, $::square_img[$dark_bg]]) {
145     my ($mask, $img) = @$_;
146     if ($mark & $mask) {
147     $img->composite (
148     $base, 0, 0, $size, $size, 0, 0,
149     $size / $img->get_width, $size / $img->get_height,
150     $::config->{speed} ? 'tiles' : 'bilinear', 192
151     );
152     }
153     }
154    
155     push @$$c, $base;
156     }
157     }
158    
159     $$c->[$rand % @$$c];
160     }
161    
162     sub pixbuf_text {
163 pcg 1.2 my ($self, $pixbuf, $colour, $x, $y, $height, $text) = @_;
164 pcg 1.1
165 pcg 1.2 #my $layout = $self->create_pango_layout ($text);
166     #my ($w, $h) = $layout->get_pixel_size;
167     #print "$w $h\n";#d#
168    
169 pcg 1.1 my @c = grep $_,
170     map $::font[$colour][$::fontmap{$_}],
171     split //, $text;
172    
173     if (@c) {
174     my $spacing = $height * 0.1;
175     my $s = $height / List::Util::max map $_->get_height, @c;
176     my $W = List::Util::sum map $_->get_width, @c;
177    
178     $x -= ($W * $s + $spacing * (@c - 1)) * 0.5;
179     $y -= $height * 0.5;
180    
181     for (@c) {
182     my $w = $_->get_width * $s;
183     # +2 == don't fight the rounding
184     $_->composite ($pixbuf,
185     $x, $y, $w+2, $height+2, $x, $y, $s, $s,
186     $::config->{speed} ? 'tiles' : 'bilinear', 255);
187    
188     $x += $w + $spacing;
189     }
190     }
191     }
192    
193     sub pixbuf_rect {
194     my ($pb, $colour, $x1, $y1, $x2, $y2, $alpha) = @_;
195     # we fake lines by... a horrible method :/
196     my $colour_pb = new_pixbuf 1, 1, 0, $colour;
197     $colour_pb->composite ($pb, $x1, $y1, $x2 - $x1 + 1, $y2 - $y1 + 1, $x1, $y1, $x2 + 1, $y2 + 1,
198     'nearest', $alpha);
199     }
200    
201     sub set_board {
202     my ($self, $board) = @_;
203    
204     $self->repaint_board ($board, 1);
205     }
206    
207     # draw an empty board
208     sub draw_board {
209     my ($self, $s) = @_;
210     my $canvas = $self->{canvas};
211    
212     my $size = $self->{size};
213    
214     # we leave enough space for the shadows.. I like smaller stones, and we
215     # do no need to do the nifty recursive screen updates that goban2 does
216     my $border = int ($s / ($size + 3) * 0.5);
217     my $s2 = $s - $border * 2;
218 pcg 1.2 my $edge = $self->{edge} = int ($s2 / ($size + 1) * 0.975);
219 pcg 1.1 my $ofs = int ($edge / 2);
220    
221     my @k = map int ($s2 * $_ / ($size+1) + $border + 0.5), 0 .. $size;
222    
223     $self->{k} = \@k;
224    
225     my $pixbuf;
226    
227     my ($bw, $bh) = ($::board_img->get_width, $::board_img->get_height);
228    
229     if ($s < $bw && $s < $bh) {
230     $pixbuf = new_pixbuf $s, $s, 0;
231     $::board_img->copy_area (0, 0, $s, $s, $pixbuf, 0, 0);
232     } else {
233     $pixbuf = scale_pixbuf $::board_img, $s, $s, $::config->{speed} ? 'nearest' : 'bilinear', 0;
234     }
235    
236     my $linew = int ($s / 40 / $size);
237    
238     # ornamental border... we have time to waste :/
239     pixbuf_rect $pixbuf, 0xffcc7700, 0, 0, $s-1, $linew, 255;
240     pixbuf_rect $pixbuf, 0xffcc7700, 0, 0, $linew, $s-1, 255;
241     pixbuf_rect $pixbuf, 0xffcc7700, $s-$linew-1, 0, $s-1, $s-1, 255;
242     pixbuf_rect $pixbuf, 0xffcc7700, 0, $s-$linew-1, $s-1, $s-1, 255;
243    
244     for my $i (1 .. $size) {
245     pixbuf_rect $pixbuf, 0x44111100, $k[$i] - $linew, $k[1] - $linew, $k[$i] + $linew, $k[$size] + $linew, 192;
246     pixbuf_rect $pixbuf, 0x44111100, $k[1] - $linew, $k[$i] - $linew, $k[$size] + $linew, $k[$i] + $linew, 192;
247    
248     # 38 max, but we allow a bit more
249     my $label = (qw(- A B C D E F G H J K L M N O P Q R S T U V W X Y Z
250     AA BB CC DD EE FF GG HH JJ KK LL MM NN OO PP QQ RR SS TT UU VV WW XX YY ZZ))[$i];
251    
252 pcg 1.2 pixbuf_text $self, $pixbuf, 0, $k[$i], $border, $ofs, $label;
253     pixbuf_text $self, $pixbuf, 0, $k[$i], $s2 + $border, $ofs, $label;
254     pixbuf_text $self, $pixbuf, 0, $border, $k[$i], $ofs, $size - $i + 1;
255     pixbuf_text $self, $pixbuf, 0, $s2 + $border, $k[$i], $ofs, $size - $i + 1;
256 pcg 1.1
257     $a++;
258     $a++ if $a eq "I"; # not correct, instead of AA AB, we should get HH JJ KK...
259     }
260    
261     # hoshi points
262     my $hoshi = sub {
263     my ($x, $y) = @_;
264     my $hs = int ($edge / 4) | 1;
265     $x = $k[$x] - $hs / 2; $y = $k[$y] - $hs / 2;
266    
267     # we use the shadow mask... not perfect, but I want to finish this
268     $::shadow_img->composite ($pixbuf,
269     $x, $y, $hs + 1, $hs + 1, $x, $y,
270     $hs / $::shadow_img->get_width, $hs / $::shadow_img->get_height,
271     $::config->{speed} ? 'tiles' : 'bilinear', 255);
272     };
273    
274     my $h1 = $size < 10 ? 3 : 4; # corner / edge offset
275     $hoshi->($h1, $h1);
276     $hoshi->($size - $h1 + 1, $h1);
277     $hoshi->($h1, $size - $h1 + 1);
278     $hoshi->($size - $h1 + 1, $size - $h1 + 1);
279    
280     if ($size % 2) { # on odd boards, also the remaining 5
281     my $h2 = ($size + 1) / 2;
282     if ($size > 10) {
283     $hoshi->($h1, $h2);
284     $hoshi->($size - $h1 + 1, $h2);
285     $hoshi->($h2, $size - $h1 + 1);
286     $hoshi->($h2, $h1);
287     }
288     # the tengen
289     $hoshi->($h2, $h2);
290     }
291    
292     $pixbuf;
293     }
294    
295     sub repaint_board {
296     my ($self, $board, $dopaint) = @_;
297    
298 pcg 1.2 my @areas;
299    
300 pcg 1.1 my $old = $self->{board};
301     my $size = $self->{size};
302     my $edge = $self->{edge};
303     my $ofs = int ($edge * 0.5);
304     my $k = $self->{k};
305    
306     # repaint
307     for my $x (1 .. $size) {
308     for my $y (1 .. $size) {
309     my $mark = $board->{board}[$x-1][$y-1];
310     my $old = $old->{board}[$x-1][$y-1];
311    
312     if ($old != $mark) {
313     my $rand = ($x ^ $y ^ 0x5555);
314    
315     my $shadow = $edge * 0.05;
316     my @area = ($k->[$x] - $ofs, $k->[$y] - $ofs,
317     $edge + $shadow, $edge + $shadow);
318    
319     my $pb = new_pixbuf @area[2,3];
320     $self->{backgroundpb}->copy_area (@area, $pb, 0, 0);
321    
322     if ($mark) {
323     my $stack = $self->create_stack($mark, $edge, $rand);
324    
325     $stack->composite ($pb, 0, 0, @area[2,3], 0, 0, 1, 1,
326     'nearest', 255);
327    
328     # labels are handled here because they are quite rare
329     if ($mark & MARK_LABEL) {
330     my $white = $mark & (MARK_W | MARK_GRAY_W) ? 0 : 1;
331    
332     if ($white) {
333     pixbuf_text $pb, 0,
334     $ofs * 0.1, $ofs * 0.1, $ofs * 0.7,
335     $self->{board}{label}[$x-1][$y-1];
336     }
337     pixbuf_text $pb, $white,
338     0, 0, $ofs * 0.7,
339     $self->{board}{label}[$x-1][$y-1];
340     }
341     }
342    
343     # speed none, normal, max
344 pcg 1.2 $self->{backgroundpm}->draw_pixbuf ($self->style->black_gc, $pb,
345 pcg 1.1 0, 0, @area, 'max', 0, 0);
346     # a single full clear_area is way faster than many single calls here
347 pcg 1.2 push @areas, \@area if $dopaint;
348 pcg 1.1 }
349 pcg 1.2 }
350     }
351    
352     if (@areas) {
353     # the "cut-off" point is arbitrary
354     if (@areas > 16) {
355     # update a single rectangle only
356     my $rect = new Gtk2::Gdk::Rectangle @{pop @areas};
357     $rect = $rect->union (new Gtk2::Gdk::Rectangle @$_) for @areas;
358     $self->window->clear_area ($rect->values);
359     } else {
360     # update all the affected rectangles
361     $self->window->clear_area (@$_) for @areas;
362 pcg 1.1 }
363     }
364    
365     $self->{board} = $board;
366     #d# save
367     #Storable::nstore { board => $self->{board}, size => $self->{size}, path => $self->{path}}, "testboard.storable";
368     }
369    
370     sub redraw {
371     my ($self, $area) = @_;
372    
373     if ($area && $self->{pixbuf}) {
374     my ($x, $y, $w, $h) = $area->values;
375    
376     $self->{canvas}->window->draw_pixbuf ($self->{canvas}->style->white_gc, $self->{pixbuf},
377     $x, $y, $x, $y, $w, $h,
378     "normal", 0, 0);
379     $self->{canvas}->window->draw_rectangle ($self->{canvas}->style->black_gc, 0,
380     $x - 1, $y - 1, $w + 2, $h + 2) if $::DEBUG_EXPOSE;
381     }
382     }
383    
384     sub FINALIZE {
385     warn "FINALIZE(@_)\n";#d#
386     my ($self) = @_;
387     $self->{userpanel}[$_] && (delete $self->{userpanel}[$_])->destroy
388     for BLACK, WHITE;
389     $self->SUPER::destroy;
390     delete $appwin::gamelist->{game}{$self->{channel}};
391     }
392    
393     1;
394