ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/kgsueme/kgsueme/board.pl
Revision: 1.1
Committed: Sun Jun 22 20:47:12 2003 UTC (20 years, 11 months ago) by pcg
Content type: text/plain
Branch: MAIN
Log Message:
*** empty log message ***

File Contents

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