home *** CD-ROM | disk | FTP | other *** search
/ Acorn User 10 / AU_CD10.iso / Updates / Perl / Non-RPC / !Perl / riscos / RISCOS / Draw.pm < prev    next >
Text File  |  1998-07-13  |  14KB  |  455 lines

  1. package RISCOS::Draw;
  2.  
  3. use RISCOS::SWI;
  4. require Exporter;
  5. use Carp;
  6. use strict;
  7. # use SelfLoader;
  8. use RISCOS::Units 'pack_transform_block';
  9.  
  10. use vars qw (@ISA @EXPORT_OK $VERSION %winding @winding %plot @plot %cap $cap
  11.          %join @join %tagname @tagname @taglength $draw_pp);
  12.  
  13. @ISA = qw(Exporter);
  14. $VERSION = 0.03;
  15. # 0.02 soops up split_path_block to accept an ARRAY reference
  16. # 0.03 path_transform
  17. @EXPORT_OK = qw(path_bbox pack_path_block pack_dash_block split_path_block
  18.         split_dash_block what unwhat path_transform);
  19.  
  20. %winding = ('non-zero' => 0, 'negative' => 1, 'even-odd' => 2, 'positive'=> 3 );
  21. %plot = ('non-boundary exterior' => 4, 'boundary exterior' => 8,
  22.      'boundary interior' => 16, 'non-boundary interior' => 32);
  23. %cap = ('butt' => 0, 'round' => 1,  'square' => 2, 'triangle' => 3);
  24. %join = ('mitred' => 0, 'round' => 1, 'bevelled' => 2);
  25. %tagname = ('end' => 0, 'continue' => 1, 'move' => 2, 'move_same_wind' => 3,
  26.         'close_gap' => 4, 'close' => 5, 'bezier' => 6, 'gap_same_sub' => 7,
  27.         'line' => 8, 'bézier' => 6);
  28. @taglength = (-1,1,2,2,0,0,6,2,2);
  29.  
  30. foreach my $name (qw (winding plot cap join tagname)) {
  31.     no strict 'refs';
  32.     my @pair;
  33.     while (@pair = each %{$name}) {
  34.     # map 'butt' => 0 into $cap[0] = 'butt';
  35.     ${$name}[$pair[1]] = $pair[0];
  36.     }
  37. }
  38.  
  39. $draw_pp = SWINumberFromString('XDraw_ProcessPath');
  40. #__DATA__
  41. # Cool.
  42. # 1st arg is either reference to a GLOB to use that to lookup names/numbers
  43. #         or a string to use RISCOS::Draw::string as the GLOB.
  44. #        (hence lookup stuff in the hash/array pairs defined above.
  45. sub what ($$;@) {
  46.     my $type = shift;
  47.     my $default = shift;
  48.     my @result;
  49.     $type = (ref ($type) ne 'GLOB') ? $RISCOS::Draw::{$type} : $$type;
  50.  
  51.     if ($default =~ tr/0-9//c) {
  52.     # Do this out here as numeric defaults are intentionally not validated
  53.     my $lcname = lc $default;
  54.     $_ = $type->{$lcname};
  55.     croak "Unknown $type default name '$default'" unless defined $_;
  56.     $default = $_
  57.     }
  58.     @result = map {
  59.     # All somewhat hairy - map returns the last thing evaluated
  60.     if (defined $_) {
  61.         # Shotgun.
  62.         local $Carp::CarpLevel = 1;
  63.         if (tr/0-9//c) {
  64.         # Not just alphabetics
  65.         my $lcname = lc $_;
  66.         my $result = $type->{$lcname};
  67.         croak "Unknown $type name '$_'" unless defined $result;
  68.         $result
  69.         } else {
  70.         croak "$type value $_ undefined " unless defined $$type[$_];
  71.         $_;    # This is the return value for map.
  72.         }
  73.     } else {
  74.         $default
  75.     }
  76.     } @_;
  77.     wantarray ? @result : $result[0];
  78. }
  79.  
  80. sub unwhat ($$;@) {
  81.     my $type = shift;
  82.     my $default = shift;
  83.     my @result;
  84.     $type = (ref ($type) ne 'GLOB') ? $RISCOS::Draw::{$type} : $$type;
  85.  
  86.     @result = map {
  87.     # All somewhat hairy - map returns the last thing evaluated
  88.     (defined $_) ? $$type[$_] : $default;
  89.     } @_;
  90.     wantarray ? @result : $result[0];
  91. }
  92.  
  93. # Assumes ints are 32 bit.
  94. sub pack_path_block {
  95.     if (ref $_[0] eq 'ARRAY') {
  96.     @_ = @{$_[0]}
  97.     } elsif (ref $_[0] eq 'SCALAR' or ref $_[0] eq 'LVALUE') {
  98.     return wantarray ? split_path_block (${$_[0]}) : ${$_[0]};
  99.     } elsif (ref $_[0]) {
  100.     croak 'Cannot make a path from a ' . ref ($_[0]) . ' reference';
  101.     }
  102.  
  103.     my @result;
  104.     foreach my $thing (@_) {
  105.     if (ref ($thing) eq 'ARRAY') {
  106.         $thing = pack 'i*', @$thing;
  107.     } elsif (ref $thing) {
  108.         carp 'Cannot make a path element from a ' . ref ($$_[0]) .
  109.          ' reference';
  110.     }
  111.     push @result, $thing;
  112.     }
  113.     # Ensure it finishes with a single end of path
  114.     # Cope with equasor's empty path objects by checking @result is not empty
  115.     pop @result while (@result and $result[$#result] =~ /^\0\0\0\0/);
  116.     push @result, "\0\0\0\0";
  117.  
  118.     wantarray ? @result : join '', @result;
  119. }
  120.  
  121. # Assumes ints are 32 bit.
  122. sub pack_dash_block ($;$) {
  123.     my ($off, $dash) = @_;
  124.  
  125.     if (ref ($off) eq 'ARRAY') {
  126.     $dash = $off;
  127.     $off = shift (@$dash);
  128.     }
  129.     if (ref ($dash) eq 'ARRAY') {
  130.     return $dash = pack 'I*', $off, scalar (@$dash), @$dash;
  131.     }
  132.     $off;    # Assume first argument was already packed
  133. }
  134.  
  135. # Assumes ints are 32 bit.
  136. sub split_dash_block ($) {
  137.     my $dash = $_[0];
  138.     if (ref ($dash) eq 'ARRAY') {
  139.     return @$dash if wantarray;
  140.     my $off = shift (@$dash);
  141.     return pack 'I*', $off, scalar (@$dash), @$dash;
  142.     }
  143.     return $dash unless wantarray;
  144.     return () unless defined $dash;
  145.     # Left with scalar block to unpack
  146.     unpack 'Ix4I*', $dash;
  147. }
  148.  
  149. # Assumes ints are 32 bit.
  150. sub split_path_block ($) {
  151.     my $result = [];
  152.     return wantarray ? () : undef unless defined ($_=$_[0]);
  153.  
  154.     if ('ARRAY' eq ref $_) {
  155.     $result = $_
  156.     } else {
  157.     while (defined (my $tag = unpack 'I', $_)) {
  158.         my $amount = $taglength[$tag];
  159.         croak "Unknown draw tag $tag" unless defined $amount;
  160.         last if $amount == -1;
  161.         push @$result, substr $_, 0, 4 + 4 * $amount;
  162.         $_ = substr $_, 4 + 4 * $amount;
  163.     }
  164.     }
  165.     # Ensure it finishes with an end of path
  166.     # Change of plan. Don't store it with an end of path.
  167.     # push @$result, "\0\0\0\0";
  168.     wantarray ? @$result : $result;
  169. }
  170.  
  171. # Path    Scalar = Block
  172. #    Ref to Array of Scalar = Block to Join
  173. # Transform    Default none
  174. sub path_transform ($;$) {
  175.     my ($path, $trans) = @_;
  176.  
  177.     $path = pack_path_block($path) if (ref $path);
  178.  
  179.     # undefined paths are an error
  180.     return undef unless defined $path;
  181.  
  182.     # empty paths are easy
  183.     return $path unless (length $path > 4 and defined $trans);
  184.  
  185.     my $transblock = pack_transform_block $trans;
  186.  
  187.     # path, fill, transform, flatness, thickness, join/cap, dash, output
  188.     defined kernelswi ($draw_pp, $path, 0, $transblock, 0, 0, 0, 0, 0) ? $path
  189.                                        : undef;
  190. }
  191.  
  192. # Path    Scalar = Block
  193. #    Ref to Array of Scalar = Block to Join
  194. # Winding    Default 0
  195. # Plot        Boundary
  196. # Thickness    Default 0
  197. # Join        Default bevelled, mitre limit defaults to 10
  198. #        Scalar ref => mitre with this limit
  199. # Start cap    Default Butt, Triangle is × 1, × 2
  200. #        Array ref => Triangle, [width, length]
  201. # End cap    as above
  202. # Dash        Scalar = block
  203. #        Array Ref = [Offest, Values]; length will be caclulated
  204. # Flatness    Default 0
  205. # Transform    Default none
  206. sub path_bbox ($;$$$$$$$$$) {
  207.     # A lot of this code will need to be broken out if other subs are written.
  208.     my ($path, $winding, $plot, $thick, $join, $start, $end, $dash, $flat,
  209.     $trans) = @_;
  210.  
  211.     $path = pack_path_block($path) if (ref $path);
  212.  
  213.     return wantarray ? () : undef unless (defined $path and length $path > 4);
  214.     # empty paths do not have bounding boxes.
  215.  
  216.     my ($mitre_limit, $start_w, $start_l, $end_w, $end_l) = (10, 1, 2, 1, 2);
  217.  
  218.     $plot = what ('plot', 0x30, $plot);
  219.     $plot |= what ('winding', 0, $winding) | 0x70000000;
  220.     # Flatten, Thicken, Re-Flattened.
  221.     $thick = 0 unless defined $thick;
  222.     $flat = 0 unless defined $flat;
  223.  
  224.     if (ref ($join) eq 'SCALAR') {
  225.     $mitre_limit = $$join;
  226.     $join = 0;
  227.     } else {
  228.     $join = what ('join', 2, $join);
  229.     }
  230.  
  231.     if (ref ($start) eq 'ARRAY') {
  232.     ($start_w, $start_l) = @$start;
  233.     $start = 3;
  234.     } else {
  235.     $start = what ('cap', 0, $start);
  236.     }
  237.  
  238.     if (ref ($end) eq 'ARRAY') {
  239.     ($end_w, $end_l) = @$end;
  240.     $end = 3;
  241.     } else {
  242.     $end = what ('cap', 0, $end);
  243.     }
  244.  
  245.     $dash = pack_dash_block ($dash);
  246.     $dash = 0 unless defined ($dash) && length $dash;
  247.  
  248.     my $transblock = pack_transform_block $trans if defined $trans;
  249.     my $joinblock = pack 'C3xiS4', $join, $start, $end, ($mitre_limit * 65536),
  250.              ($start_w * 256), ($start_l * 256),
  251.              ($end_w * 256), ($end_l * 256);
  252.  
  253.     my $output = 'x'x16;
  254.     my $outputaddr = unpack 'I', pack 'P16', $output;
  255.     croak sprintf 'RISC OS bug - cannot use a buffer address %X with Draw_ProcessPath - must be less than &80000000', $outputaddr unless $outputaddr < 0x80000000;
  256.     return wantarray ? () : undef
  257.       unless defined kernelswi ($draw_pp, $path, $plot,
  258.                  (defined $transblock ? $transblock : 0),
  259.                  $flat, 0 + $thick, $joinblock, $dash,
  260.                  0x80000000 | $outputaddr);
  261.     my $result = [];
  262.     @$result = unpack 'i4', $output;
  263.     wantarray ? @$result : $result;
  264. }
  265.  
  266. $draw_pp;
  267. __END__
  268.  
  269. =head1 NAME
  270.  
  271. RISCOS::Draw -- perl interface to the Draw module
  272.  
  273. =head1 SYNOPSIS
  274.  
  275.     use RISCOS::Draw qw(what split_path_block path_bbox);
  276.     # Convert join name to number, default to bevelled
  277.     $join = what ('join', 'bevelled', $join);
  278.     # Split scalar containing path into array of path elements
  279.     @path = split_path_block $block;
  280.     $bbox = path_bbox $block, $width;
  281.  
  282. =head1 DESCRIPTION
  283.  
  284. C<RISCOS::Draw> provides an interface to the Draw module, which provides "an
  285. implementation of PostScript type drawing". At present only functions necessary
  286. to calculate path bounding boxes have been implemented, principally for use by
  287. the DrawFile Path object.
  288.  
  289. =head2 Subroutines
  290.  
  291. =over 4
  292.  
  293. =item what <type> <default> <values...>
  294.  
  295. C<what> converts names of options into the appropriate numeric constants.
  296. Arguments passed as numbers are faulted if they do not correspond to a named
  297. option. I<type> can either be a reference to a typeglob (which supplies a hash
  298. to convert from name to number and an array from number to name) or a string to
  299. use lookup tables provided by this package. C<RISCOS::Draw> provides these
  300. tables:
  301.  
  302. =head3 winding
  303.  
  304.     non-zero        0
  305.     negative        1
  306.     even-odd        2
  307.     positive        3
  308.  
  309. =head3 plot
  310.  
  311.     non-boundary exterior    4
  312.     boundary exterior    8
  313.     boundary interior    16
  314.     non-boundary interior    32
  315.  
  316. =head3 cap
  317.  
  318.     butt            0
  319.     round            1
  320.     square            2
  321.     triangle        3
  322.  
  323. =head3 join
  324.  
  325.     mitred            0
  326.     round            1
  327.     bevelled        2
  328.  
  329. =head3 tagname
  330.  
  331.     end            0
  332.     continue        1
  333.     move            2
  334.     move_same_wind        3
  335.     close_gap        4
  336.     close            5
  337.     bezier            6
  338.     gap_same_sub        7
  339.     line            8
  340.  
  341. I<default> is a default value (string or text) to use for any undefined values
  342. passed as arguments. Numeric defaults are deliberately not checked for validity.
  343. In array context C<what> returns an array corresponding to the converted
  344. arguments. In scalar context C<what> returns only the first value.
  345.  
  346. =item unwhat <type> <default> <values...>
  347.  
  348. C<unwhat> provides the reverse conversion to C<what>, converting numeric values
  349. to text. Any undefined values are converted to the (unchecked) supplied default.In scalar context it returns the first conversion only, in array context a list
  350. of conversions.
  351.  
  352. =item split_dash_block <block>
  353.  
  354. Splits a dash block into an array of integers, which should be regarded as a
  355. single value followed by a list. The first value is the offset of the start of
  356. the dash pattern in the list. The list itself gives the length of dash segments
  357. in user units.
  358.  
  359. =item pack_dash_block <packed_block>
  360.  
  361. =item pack_dash_block <array_ref>
  362.  
  363. =item pack_dash_block <start> <array_ref>
  364.  
  365. Packs an array of integers into a block to pass to C<Draw> SWIs or to store in a
  366. DrawFile. If a single array reference is passed it is assumed to point to an
  367. array C<($start, @lengths)> (which is passed to shift). If two arguments are
  368. passed the first is taken to be the start index, the second a reference to an
  369. array of dash lengths. If a single scalar is passed it is assumed to be already
  370. packed and is returned verbatim.
  371.  
  372. =item split_path_block <path_block>
  373.  
  374. splits the scalar containing a series of C<Draw> path elements into an array,
  375. with each element containing the a single move, line or curve. Concatenating the
  376. array with C<join ''> will give the original scalar, except that the terminating
  377. end-of-path marker C<"\0\0\0\0"> (and any trailing garbage) will be absent. If
  378. passed a reference to an array, then this array (or a reference to it) is
  379. returned.
  380.  
  381. In array context returns this array, in scalar context returns a reference to
  382. it.
  383.  
  384. =item pack_path_block <reference>
  385.  
  386. =item pack_path_block path elements...
  387.  
  388. packs the path block supplied. In scalar context returns the path block, in
  389. array context returns the path block as if split by C<split_path_block>.
  390.  
  391. If the first argument is scalar reference it is assumed to point to an already
  392. packed path block and C<split_path_block> is called if necessary.
  393.  
  394. If the first argument is an array reference, it is dereferenced and replaces
  395. the argument list, else the supplied argument list is processed.
  396.  
  397. For each entry in turn, if it is an array reference it is assumed to point to an
  398. array of integer values to be used as path/move type and co-ordinates, which are
  399. C<pack>ed with the template C<'I*'> and added to the output array. Otherwise the
  400. entry is assumed to be already packed and added to the output array verbatim.
  401.  
  402. A final C<"\0\0\0\0"> is added to the output list if necessary.
  403.  
  404. In array context the output array is returned, in scalar context it is
  405. concatenated with C<join ''>.
  406.  
  407. =item path_bbox <path> [<winding> <plot_type> <thickness> <join> <start_cap> <end_cap> <dash> <flatness> <transform>
  408.  
  409. calculates the bounding box of I<path>, returning a reference to an array in
  410. scalar context, or the array itself in array context. C<undef> or an empty list
  411. are returned if an error occurs (including supplying an empty path). All
  412. arguments except I<path> are optional, and will default to sane values if not
  413. supplied.
  414.  
  415. I<path> is either a scalar if it is already packed as a block, or a reference to
  416. be passed to C<pack_path_block>.
  417.  
  418. I<winding> defaults to 0 (non-zero), and is one of the B<four> values in the
  419. C<Draw> winding table (distinct from the B<two> used by C<DrawFile>).
  420.  
  421. I<plot> is the plot type to pass to the SWI, and defaults to 'boundary'.
  422.  
  423. I<thickness> is the line thickness in user units, which defaults to 0 (thin)
  424.  
  425. I<join> is either a scalar to lookup in the join table (default bevelled, mitre
  426. limit defaults to 10), or a scalar reference to select mitred joins with this
  427. as the mitre limit.
  428.  
  429. I<start_cap> and I<end_cap> are each either scalars to lookup in the cap table
  430. (default butt, triangle is width × 1, length × 2) or an array ref to select
  431. triangle caps with C<[width, length]>.
  432.  
  433. I<dash> is either a scalar to be used verbatim as the dash block, or an array
  434. reference of the form C<[$Offset, @Values]> to pass to C<pack_dash_block>. The
  435. default is continuous lines.
  436.  
  437. I<flatness> defaults to 0, which is normally appropriate
  438.  
  439. I<transform> is a reference to the transformation matrix to use, defaulting
  440. to none
  441.  
  442. =back
  443.  
  444. =head1 BUGS
  445.  
  446. Definitely not tested enough yet. Some bits not tested at all, I believe.
  447. Bounding box calculations and sufficient packing/unpacking to manipulate
  448. DrawFiles does work.
  449.  
  450. =head1 AUTHOR
  451.  
  452. Nicholas Clark <F<nick@unfortu.net>>
  453.  
  454. =cut
  455.