1use strict;
2use warnings;
3
4use Test::More;
5use Prima::sys::Test;
6use Prima::Application;
7
8my $dbm = Prima::DeviceBitmap->new( size => [5, 5], monochrome => 1);
9my $r;
10
11sub try($$)
12{
13	my ( $name, $expected ) = @_;
14	$dbm->region(undef);
15	$dbm->clear;
16	$dbm->region($r);
17	$dbm->bar(0,0,$dbm->size);
18
19	my $i = $dbm->image;
20	$i->type(im::Byte);
21	$i->mirror(1);
22	my $data = $i->data;
23	$data =~ s/(.{5}).{3}/$1/gs;
24	$data =~ s/[^\x00]/ /g;
25	$data =~ s/\x00/*/g;
26	is( $data, $expected, $name);
27}
28
29# empty
30try('empty undef',"*" x 25);
31$r = Prima::Region->new;
32ok($r, 'empty rgn');
33ok($r->is_empty, 'is empty');
34try('empty def'," " x 25);
35is_deeply([$r->box], [0,0,0,0], 'empty/box');
36
37# rect
38$r = Prima::Region->new( box => [0, 0, 1, 1]);
39ok(!$r->is_empty, 'not empty');
40try('box 1x1',
41	"     ".
42	"     ".
43	"     ".
44	"     ".
45	"*    "
46);
47is_deeply([$r->box], [0,0,1,1], 'box 1x1/box');
48$r->offset(2,2);
49try('box 1x1 + 2.2',
50	"     ".
51	"     ".
52	"  *  ".
53	"     ".
54	"     "
55);
56is_deeply([$r->box], [2,2,1,1], 'box 1x1/box + 2.2');
57$r->offset(-4,-4);
58is_deeply([$r->box], [-2,-2,1,1], 'box 1x1/box - 2.2');
59
60$r = Prima::Region->new( box => [1, 1, 3, 3]);
61try('box 3x3',
62	"     ".
63	" *** ".
64	" *** ".
65	" *** ".
66	"     "
67);
68is_deeply([$r->box], [1,1,3,3], 'box 3x3/box');
69
70my $r2 = Prima::Region->new( rect => [1, 1, 4, 4]);
71ok( $r->equals($r2), 'equals');
72
73my @star = (0, 0, 2, 5, 5, 0, 0, 3, 5, 3);
74$r = Prima::Region->new(polygon => \@star, fillMode => fm::Alternate);
75my @box = $r->box;
76ok($box[0] < $box[2] && $box[1] < $box[3], 'star 1');
77
78$r2 = Prima::Region->new(polygon => \@star, fillMode => fm::Winding);
79@box = $r->box;
80ok($box[0] < $box[2] && $box[1] < $box[3], 'star 2');
81ok( !$r-> equals($r2), 'poly winding');
82
83my $image = Prima::Image->new(
84	size => [4,4],
85	type => im::Byte,
86	data =>
87		"\xff\xff\xff\x00". # y=0
88		"\xff\xff\xff\x00".
89		"\xff\xff\xff\x00".
90		"\x00\x00\x00\x00"  # y=3
91);
92$r = Prima::Region->new(image => $image);
93is_deeply([$r->box], [0,0,3,3], 'image');
94
95ok( $r-> point_inside(0,0), "point inside 0,0");
96ok( $r-> point_inside(2,2), "point inside 2,2");
97ok( !$r-> point_inside(2,3), "point inside 2,3");
98ok( !$r-> point_inside(3,2), "point inside 3,2");
99
100is( $r-> rect_inside(0,0,0,0), rgn::Inside, "rect inside 0,0,0,0");
101is( $r-> rect_inside(1,1,2,2), rgn::Inside, "rect inside 1,1,2,2");
102is( $r-> rect_inside(2,2,3,3), rgn::Partially, "rect partially inside 2,2,3,3");
103is( $r-> rect_inside(3,3,3,3), rgn::Outside, "rect outside 3,3,3,3");
104
105$dbm->region($image);
106@box = $dbm->clipRect;
107is_deeply(\@box, [0,0,2,2], 'region clip rect');
108
109my $dbm2 = Prima::DeviceBitmap->new( size => [5,5], type => dbt::Pixmap );
110$dbm2->region($image);
111@box = $dbm->clipRect;
112is_deeply(\@box, [0,0,2,2], 'region clip rect on pixmap');
113
114if ( $::application->get_system_value(sv::LayeredWidgets)) {
115	$dbm2 = Prima::DeviceBitmap->new( size => [5,5], type => dbt::Layered );
116	$dbm2->region($image);
117	@box = $dbm->clipRect;
118	is_deeply(\@box, [0,0,2,2], 'region box on layered');
119}
120
121$r2 = $dbm->region;
122is_deeply([$r2->box], [0,0,3,3], 'region reused');
123$r = $r2;
124try('reused check 2',
125	"     ".
126	"     ".
127	"***  ".
128	"***  ".
129	"***  "
130);
131
132
133$dbm->region( undef );
134@box = $dbm->clipRect;
135is_deeply(\@box, [0,0,4,4], 'empty clip rect');
136
137$r2 = Prima::Region->new( box => [1, 1, 2, 2]);
138$r = $r2->dup;
139try('rgnop::Copy',
140	"     ".
141	"     ".
142	" **  ".
143	" **  ".
144	"     "
145);
146
147my $r3 = Prima::Region->new( box => [2, 2, 2, 2]);
148$r = $r2->dup;
149$r->combine( $r3, rgnop::Union);
150try('rgnop::Union',
151	"     ".
152	"  ** ".
153	" *** ".
154	" **  ".
155	"     "
156);
157
158$r = $r2->dup;
159$r->combine( $r3, rgnop::Intersect);
160try('rgnop::Intersect',
161	"     ".
162	"     ".
163	"  *  ".
164	"     ".
165	"     "
166);
167
168$r = $r2->dup;
169$r->combine( $r3, rgnop::Xor);
170try('rgnop::Xor',
171	"     ".
172	"  ** ".
173	" * * ".
174	" **  ".
175	"     "
176);
177
178$r = $r2->dup;
179$r->combine( $r3, rgnop::Diff);
180try('rgnop::Diff',
181	"     ".
182	"     ".
183	" *   ".
184	" **  ".
185	"     "
186);
187
188$r = $dbm->region;
189try('region re-reuse',
190	"     ".
191	"     ".
192	" *   ".
193	" **  ".
194	"     "
195);
196
197sub bits  { join ':', map { sprintf "%08b", ord } split '', shift }
198sub bytes { unpack('H*', shift ) }
199
200sub is_bytes
201{
202	my ( $bytes_actual, $bytes_expected, $name ) = @_;
203	my $ok = $bytes_actual eq $bytes_expected;
204	ok( $ok, $name );
205	warn "#   " . bytes($bytes_actual) . " (actual)\n#   " . bytes($bytes_expected) . " (expected)\n" unless $ok;
206}
207
208sub sort_boxes($)
209{
210	my @b = @{$_[0]};
211	my (@x,@r);
212	push @r, [@x] while @x = splice(@b, 0, 4);
213	@r = sort { "@$a" cmp "@$b" } @r;
214	return [ map { @$_ } @r];
215}
216
217sub is_rects
218{
219	my ($r, $rects, $name) = @_;
220	my $boxes = sort_boxes $r->get_boxes;
221	$rects = sort_boxes $rects;
222	my $ok = is_deeply( $boxes, $rects, $name);
223	warn "# (@$boxes) vs (@$rects)\n" unless $ok;
224	return $ok;
225}
226
227$r = Prima::Region->new;
228$r = Prima::Region->new( rect => [0,1,2,3,4,5,6,7]);
229is_rects($r, [0,1,2,2,4,5,2,2], "two simple rects");
230ok( $r->equals( Prima::Region->new(box => $r->get_boxes)), "is equal (1)");
231$r = Prima::Region->new( box => [0,1,2,3,4,5,6,7]);
232is_rects($r, [0,1,2,3,4,5,6,7], "two simple boxes");
233ok( $r->equals( Prima::Region->new(box => $r->get_boxes)), "is equal (2)");
234$r = Prima::Region->new( polygon => [0,0,0,5,5,5,5,0], fillMode => fm::Overlay | fm::Winding);
235is_rects($r, [0,0,6,6], "simple polygon");
236ok( $r->equals( Prima::Region->new(box => $r->get_boxes)), "is equal (3)");
237
238my $b = Prima::Image->new(
239	size => [5,5],
240	type => im::Byte,
241);
242
243sub render
244{
245	my $rx = shift;
246	$b->region(undef);
247	$b->color(cl::Black);
248	$b->bar(0,0,$b->size);
249	$b->color(cl::White);
250	$b->region($rx);
251	$b->bar(0,0,$b->size);
252}
253
254$r = Prima::Region->new( polygon => [0,0,0,5,5,5,5,0, 0,0,0,2,2,2,2,0], fillMode => fm::Winding|fm::Overlay);
255render($r);
256is( $b->sum, 25 * 255, "polygon with winding");
257ok( $r->equals( Prima::Region->new(box => $r->get_boxes)), "is equal (4)");
258
259$r = Prima::Region->new( polygon => [0,0,0,5,5,5,5,0, 0,0,0,2,2,2,2,0], fillMode => fm::Alternate|fm::Overlay);
260render($r);
261is( $b->sum, 24 * 255, "polygon without winding");
262ok( $r->equals( Prima::Region->new(box => $r->get_boxes)), "is equal (5)");
263
264ok(
265	$b->pixel(0,0) != 0 &&
266	$b->pixel(1,0) != 0 &&
267	$b->pixel(0,1) != 0 &&
268	$b->pixel(1,1) == 0,
269	"pixels are in correct position"
270);
271my $d = $b->data;
272render($b->to_region);
273is_bytes($d, $b->data, "region to image and back is okay");
274
275$b->size(4,4);
276$r = Prima::Region->new( box => [-1,-1,3,3, 2,2,3,3]);
277render($r);
278is_bytes($b->data, ("\xff\xff\x00\x00" x 2).("\x00\x00\xff\xff" x 2), "region outside the box");
279$b->color(0x808080);
280$b->bar(1,1,2,2);
281is_bytes($b->data,
282	"\xff\xff\x00\x00".
283	"\xff\x80\x00\x00".
284	"\x00\x00\x80\xff".
285	"\x00\x00\xff\xff",
286	"bar inside region"
287);
288$b->translate(0,0);
289render(undef);
290$b->color(cl::Black);
291$b->region($r);
292$b->translate(1,1);
293$b->bar(0,0,$b->size);
294is_bytes($b->data,
295	"\xff\xff\xff\xff".
296	"\xff\x00\xff\xff".
297	"\xff\xff\x00\x00".
298	"\xff\xff\x00\x00",
299	"region plot with offset 1"
300);
301
302$b->translate(0,0);
303render(undef);
304$b->translate(2,2);
305$b->color(cl::Black);
306$b->region($r);
307$b->bar(0,0,$b->size);
308is_bytes($b->data,
309	"\xff\xff\xff\xff".
310	"\xff\xff\xff\xff".
311	"\xff\xff\x00\x00".
312	"\xff\xff\x00\x00",
313	"region plot with offset 2"
314);
315
316$b->translate(0,0);
317render(undef);
318$b->translate(5,5);
319$b->color(cl::Black);
320$b->region($r);
321$b->bar(0,0,$b->size);
322is( $b->sum, 16 * 255, "region outside left");
323$b->translate(-5,-5);
324is( $b->sum, 16 * 255, "region outside right");
325
326my $i = Prima::Image->new( size => [32, 32], type => im::Byte);
327$i->color(0);
328$i->bar(0,0,$i->size);
329my $j = $i->dup;
330$j->color(cl::White);
331$j->bar(0,0,$j->size);
332$r = Prima::Region->new( polygon => [0, 0, 10, 25, 25, 0, 0, 15, 25, 15]);
333$i->region($r);
334$i->put_image(0,0,$j);
335
336my $xr = $r->image(type => im::Byte, size => [32, 32], backColor => 0, color => cl::White);
337is( $i->data, $xr->data, 'put_image(region) == region.image');
338
339$i = Prima::Icon->new( size => [32, 32], type => im::Byte, maskType => im::bpp8, autoMasking => am::None);
340$i->color(0);
341$i->bar(0,0,$i->size);
342$j = Prima::Icon->new( size => [32, 32], type => im::Byte, maskType => im::bpp8, autoMasking => am::None);
343$j->color(cl::White);
344$j->bar(0,0,$j->size);
345$j->mask( "\xff" x length($i->mask));
346$i->region($r);
347$i->put_image(0,0,$j,rop::SrcOver);
348is( $i->data, $xr->data, 'put_image(region).rgb == region.image');
349is( $i->mask, $xr->data, 'put_image(region).a8  == region.image');
350
351done_testing;
352