a litte more internals documentation
[imager-graph.git] / Graph.pm
CommitLineData
35574351 1package Imager::Graph;
54ada35d 2require 5.005;
35574351
TC
3
4=head1 NAME
5
6Imager::Graph - Perl extension for producing Graphs using the Imager library.
7
8=head1 SYNOPSIS
9
10 use Imager::Graph::SubClass;
11 my $chart = Imager::Graph::SubClass->new;
81453d28 12 my $img = $chart->draw(data=> \@data, ...)
35574351
TC
13 or die $chart->error;
14
15=head1 DESCRIPTION
16
17Imager::Graph provides style information to its base classes. It
18defines the colors, text display information and fills based on both
19built-in styles and modifications supplied by the user to the draw()
20method.
21
35574351
TC
22=over
23
24=cut
25
26use strict;
81453d28 27use warnings;
35574351
TC
28use vars qw($VERSION);
29use Imager qw(:handy);
bb0de914 30use Imager::Fountain;
35574351 31
68a6c9c6 32$VERSION = '0.06';
35574351 33
35574351
TC
34# the maximum recursion depth in determining a color, fill or number
35use constant MAX_DEPTH => 10;
36
37my $NUM_RE = '(?:[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]\d+?)?)';
38
39=item new
40
41This is a simple constructor. No parameters required.
42
43=cut
44
45sub new {
46 bless {}, $_[0];
47}
48
49=item error
50
51Returns an error message. Only value if the draw() method returns false.
52
53=cut
54
55sub error {
56 $_[0]->{_errstr};
57}
58
59=item draw
60
61Creates a new image, draws the chart onto that image and returns it.
62
63Typically you will need to supply a C<data> parameter in the format
64required by that particular graph, and if your graph will use any
65text, a C<font> parameter
66
67You can also supply many different parameters which control the way
68the graph looks. These are supplied as keyword, value pairs, where
69the value can be a hashref containing sub values.
70
71The C<style> parameter will selects a basic color set, and possibly
72sets other related parameters. See L</"STYLES">.
73
81453d28 74 my $font = Imager::Font->new(file => 'ImUgly.ttf');
75 my $img = $chart->draw(
76 data => \@data,
77 font => $font,
78 title => {
79 text => "Hello, World!",
80 size => 36,
81 color => 'FF0000'
82 }
83 );
35574351
TC
84
85When referring to a single sub-value this documentation will refer to
86'title.color' rather than 'the color element of title'.
87
88Returns the graph image on success, or false on failure.
89
90=back
91
92=head1 STYLES
93
94The currently defined styles are:
95
96=over
97
7b94e723
TC
98=item primary
99
100a light grey background with no outlines. Uses primary colors for the
101data fills.
102
35574351
TC
103=item primary_red
104
105a light red background with no outlines. Uses primary colors for the
7b94e723 106data fills.
35574351
TC
107
108Graphs drawn using this style should save well as a gif, even though
109some graphs may perform a slight blur.
110
7b94e723 111This was the default style, but the red was too loud.
35574351
TC
112
113=item mono
114
115designed for monochrome output, such as most laser printers, this uses
116hatched fills for the data, and no colors. The returned image is a
117one channel image (which can be overridden with the C<channels>
118parameter.)
119
120You can also override the colors used by all components for background
121or drawing by supplying C<fg> and/or C<bg> parameters. ie. if you
122supply C<<fg=>'FF0000', channels=>3>> then the hash fills and anything
123else will be drawn in red. Another use might be to set a transparent
124background, by supplying C<<bg=>'00000000', channels=>4>>.
125
126This style outlines the legend if present and outlines the hashed fills.
127
35574351
TC
128=item fount_lin
129
130designed as a "pretty" style this uses linear fountain fills for the
131background and data fills, and adds a drop shadow.
132
133You can override the value used for text and outlines by setting the
134C<fg> parameter.
135
81453d28 136This is the default style.
137
35574351
TC
138=item fount_rad
139
140also designed as a "pretty" style this uses radial fountain fills for
141the data and a linear blue to green fill for the background.
142
143=back
144
145=head1 FEATURES
146
147Each graph type has a number of features. These are used to add
148various items that are displayed in the graph area. Some common
149features are:
150
151=over
152
153=item legend
154
155adds a box containing boxes filled with the data filess, with
156the labels provided to the draw method. The legend will only be
157displayed if both the legend feature is enabled and labels are
158supplied.
159
160=item labels
161
162labels each data fill, usually by including text inside the data fill.
163If the text does not fit in the fill, they could be displayed in some
164other form, eg. as callouts in a pie graph. There usually isn't much
165point in including both labels and a legend.
166
167=item dropshadow
168
169a simple drop shadow is shown behind some of the graph elements.
170
171=back
172
173Each graph also has features specific to that graph.
174
175=head1 COMMON PARAMETERS
176
177When referring to a single sub-value this documentation will refer to
178'title.color' rather than 'the color element of title'.
179
180Normally, except for the font parameter, these are controlled by
181styles, but these are the style parameters I'd mostly likely expect
182you want to use:
183
184=over
185
186=item font
187
188the Imager font object used to draw text on the chart.
189
190=item back
191
192the background fill for the graph. Default depends on the style.
193
194=item size
195
196the base size of the graph image. Default: 256
197
198=item width
199
200the width of the graph image. Default: 1.5 * size (384)
201
202=item height
203
204the height of the graph image. Default: size (256)
205
206=item channels
207
208the number of channels in the image. Default: 3 (the 'mono' style
209sets this to 1).
210
211=item line
212
213the color used for drawing lines, such as outlines or callouts.
214Default depends on the current style. Set to undef to remove the
215outline from a style.
216
217=item title
218
219the text used for a graph title. Default: no title. Note: this is
220the same as the title=>{ text => ... } field.
221
222=over
223
224=item halign
225
226horizontal alignment of the title in the graph, one of 'left',
227'center' or 'right'. Default: center
228
229=item valign
230
231vertical alignment of the title, one of 'top', 'center' or 'right'.
232Default: top. It's probably a bad idea to set this to 'center' unless
233you have a very short title.
234
235=back
236
237=item text
238
239This contains basic defaults used in drawing text.
240
241=over
242
243=item color
244
245the default color used for all text, defaults to the fg color.
246
247=item size
248
249the base size used for text, also used to scale many graph elements.
250Default: 14.
251
252=back
253
254=back
255
256=head1 BEYOND STYLES
257
258In most cases you will want to use just the styles, but you may want
259to exert more control over the way your chart looks. This section
260describes the options you can use to control the way your chart looks.
261
262Hopefully you don't need to read this.
263
264=over
265
266=item back
267
268The background of the graph.
269
270=item bg
271
272=item fg
273
274Used to define basic background and foreground colors for the graph.
275The bg color may be used for the background of the graph, and is used
276as a default for the background of hatcheed fills. The fg is used as
277the default for line and text colors.
278
279=item font
280
281The default font used by the graph. Normally you should supply this
282if your graph as any text.
283
284=item line
285
286The default line color.
287
288=item text
289
290defaults for drawing text. Other textual graph elements will inherit
291or modify these values.
292
293=over
294
295=item color
296
297default text color, defaults to the I<fg> color.
298
299=item size
300
301default text size. Default: 14. This is used to scale many graph
302elements, including padding and leader sizes. Other text elements
303will either use or scale this value.
304
305=item font
306
307default font object. Inherited from I<font>, which should have been
308supplied by the caller.
309
310=back
311
312=item title
313
314If you supply a scalar value for this element, it will be stored in
315the I<text> field.
316
317Defines the text, font and layout information for the title.
318
319=over
320
321=item color
322
323The color of the title, inherited from I<text.color>.
324
325=item font
326
327The font object used for the title, inherited from I<text.font>.
328
329=item size
330
331size of the title text. Default: double I<text.size>
332
333=item halign
334
335=item valign
336
337The horizontal and vertical alignment of the title.
338
339=back
340
341=item legend
342
343defines attributes of the graph legend, if present.
344
345=over
346
347=item color
348
349=item font
350
351=item size
352
353text attributes for the labels used in the legend.
354
355=item patchsize
356
357the width and height of the color patch in the legend. Defaults to
35890% of the legend text size.
359
360=item patchgap
361
362the minimum gap between patches in pixels. Defaults to 30% of the
363patchsize.
364
365=item patchborder
366
367the color of the border drawn around each patch. Inherited from I<line>.
368
369=item halign
370
371=item valign
372
373the horizontal and vertical alignment of the legend within the graph.
374Defaults to 'right' and 'top'.
375
376=item padding
377
378the gap between the legend patches and text and the outside of it's
379box, or to the legend border, if any.
380
381=item outsidepadding
382
383the gap between the border and the outside of the legend's box. This
384is only used if the I<legend.border> attribute is defined.
385
386=item fill
387
388the background fill for the legend. Default: none
389
390=item border
391
392the border color of the legend. Default: none (no border is drawn
393around the legend.)
394
33a928b7
TC
395=item orientation
396
397The orientation of the legend. If this is C<vertical> the the patches
398and labels are stacked on top of each other. If this is C<horizontal>
399the patchs and labels are word wrapped across the image. Default:
400vertical.
401
35574351
TC
402=back
403
33a928b7
TC
404For example to create a horizontal legend with borderless patches,
405darker than the background, you might do:
406
407 my $im = $chart->draw
408 (...,
409 legend =>
410 {
411 patchborder => undef,
412 orientation => 'horizontal',
413 fill => { solid => Imager::Color->new(0, 0, 0, 32), }
414 },
415 ...);
416
35574351
TC
417=item callout
418
419defines attributes for graph callouts, if any are present. eg. if the
420pie graph cannot fit the label into the pie graph segement it will
421present it as a callout.
422
423=over
424
425=item color
426
427=item font
428
429=item size
430
431the text attributes of the callout label. Inherited from I<text>.
432
433=item line
434
435the color of the callout lines. Inherited from I<line>
436
437=item inside
438
439=item outside
440
441the length of the leader on the inside and the outside of the fill,
442usually at some angle. Both default to the size of the callout text.
443
444=item leadlen
445
446the length of the horizontal portion of the leader. Default:
447I<callout.size>.
448
449=item gap
450
451the gap between the callout leader and the callout text. Defaults to
45230% of the text callout size.
453
454=back
455
456=item label
457
458defines attributes for labels drawn into the data areas of a graph.
459
460=over
461
462=item color
463
464=item font
465
466=item size
467
468The text attributes of the labels. Inherited from I<text>.
469
470=back
471
472=item dropshadow
473
474the attributes of the graph's drop shadow
475
476=over
477
478=item fill
479
480the fill used for the drop shadow. Default: '404040' (dark gray)
481
482=item off
483
484the offset of the drop shadow. A convenience value inherited by offx
485and offy. Default: 40% of I<text.size>.
486
487=item offx
488
489=item offy
490
491the horizontal and vertical offsets of the drop shadow. Both
492inherited from I<dropshadow.off>.
493
494=item filter
495
496the filter description passed to Imager's filter method to blur the
497drop shadow. Default: an 11 element convolution filter.
498
499=back
500
501=item outline
502
503describes the lines drawn around filled data areas, such as the
504segments of a pie chart.
505
506=over
507
508=item line
509
510the line color of the outlines, inherited from I<line>.
511
512=back
513
514=item fills
515
516a reference to an array containing fills for each data item.
517
518You can mix fill types, ie. using a simple color for the first item, a
519hatched fill for the second and a fountain fill for the next.
520
521=back
522
523=head1 HOW VALUES WORK
524
525Internally rather than specifying literal color, fill, or font objects
526or literal sizes for each element, Imager::Graph uses a number of
527special values to inherit or modify values taken from other graph
528element names.
529
530=head2 Specifying colors
531
532You can specify colors by either supplying an Imager::Color object, by
533supplying lookup of another color, or by supplying a single value that
534Imager::Color::new can use as an initializer. The most obvious is
535just a 6 or 8 digit hex value representing the red, green, blue and
536optionally alpha channels of the image.
537
538You can lookup another color by using the lookup() "function", for
539example if you give a color as "lookup(fg)" then Imager::Graph will
540look for the fg element in the current style (or as overridden by
541you.) This is used internally by Imager::Graph to set up the
542relationships between the colors of various elements, for example the
543default style information contains:
544
545 text=>{
546 color=>'lookup(fg)',
547 ...
548 },
549 legend =>{
550 color=>'lookup(text.color)',
551 ...
552 },
553
554So by setting the I<fg> color, you also set the default text color,
555since each text element uses lookup(text.color) as its value.
556
557=head2 Specifying fills
558
559Fills can be used for the graph background color, the background color
560for the legend block and for the fills used for each data element.
561
562You can specify a fill as a L<color value|Specifying colors> or as a
33a928b7 563general fill, see L<Imager::Fill> for details.
35574351
TC
564
565You don't need (or usually want) to call Imager::Fill::new yourself,
566since the various fill functions will call it for you, and
567Imager::Graph provides some hooks to make them more useful.
568
569=over
570
571=item *
572
573with hatched fills, if you don't supply a 'fg' or 'bg' parameter,
574Imager::Graph will supply the current graph fg and bg colors.
575
576=item *
577
578with fountain fill, you can supply the xa_ratio, ya_ratio, xb_ratio
579and yb_ratio parameters, and they will be scaled in the fill area to
580define the fountain fills xa, ya, xb and yb parameters.
581
582=back
583
584As with colors, you can use lookup(name) or lookup(name1.name2) to
585have one element to inherit the fill of another.
586
33a928b7
TC
587Imager::Graph defaults the fill combine value to C<'normal'>. This
588doesn't apply to simple color fills.
589
35574351
TC
590=head2 Specifying numbers
591
592You can specify various numbers, usually representing the size of
593something, commonly text, but sometimes the length of a line or the
594size of a gap.
595
596You can use the same lookup mechanism as with colors and fills, but
597you can also scale values. For example, 'scale(0.5,text.size)' will
598return half the size of the normal text size.
599
600As with colors, this is used internally to scale graph elements based
601on the base text size. If you change the base text size then other
602graph elements will scale as well.
603
604=head2 Specifying other elements
605
606Other elements, such as fonts, or parameters for a filter, can also
607use the lookup(name) mechanism.
608
609=head1 INTERNAL METHODS
610
611Only useful if you need to fix bugs, add features or create a new
612graph class.
613
614=over
615
616=cut
617
618my %style_defs =
619 (
620 back=> 'lookup(bg)',
621 line=> 'lookup(fg)',
622 text=>{
623 color => 'lookup(fg)',
624 font => 'lookup(font)',
625 size => 14,
626 },
627 title=>{
628 color => 'lookup(text.color)',
629 font => 'lookup(text.font)',
630 halign => 'center',
631 valign => 'top',
632 size => 'scale(text.size,2.0)',
633 },
634 legend =>{
635 color => 'lookup(text.color)',
636 font => 'lookup(text.font)',
637 size => 'lookup(text.size)',
638 patchsize => 'scale(legend.size,0.9)',
639 patchgap => 'scale(legend.patchsize,0.3)',
640 patchborder => 'lookup(line)',
641 halign => 'right',
642 valign => 'top',
643 padding => 'scale(legend.size,0.3)',
644 outsidepadding => 'scale(legend.padding,0.4)',
645 },
646 callout => {
647 color => 'lookup(text.color)',
648 font => 'lookup(text.font)',
649 size => 'lookup(text.size)',
650 line => 'lookup(line)',
651 inside => 'lookup(callout.size)',
652 outside => 'lookup(callout.size)',
653 leadlen => 'scale(0.8,callout.size)',
654 gap => 'scale(callout.size,0.3)',
655 },
656 label => {
657 font => 'lookup(text.font)',
658 size => 'lookup(text.size)',
659 color => 'lookup(text.color)',
660 hpad => 'lookup(label.pad)',
661 vpad => 'lookup(label.pad)',
662 pad => 'scale(label.size,0.2)',
663 pcformat => sub { sprintf "%s (%.0f%%)", $_[0], $_[1] },
664 pconlyformat => sub { sprintf "%.1f%%", $_[0] },
665 },
666 dropshadow => {
320f5a49 667 fill => { solid => Imager::Color->new(0, 0, 0, 96) },
35574351
TC
668 off => 'scale(0.4,text.size)',
669 offx => 'lookup(dropshadow.off)',
670 offy => 'lookup(dropshadow.off)',
671 filter => { type=>'conv',
672 # this needs a fairly heavy blur
673 coef=>[0.1, 0.2, 0.4, 0.6, 0.7, 0.9, 1.2,
674 0.9, 0.7, 0.6, 0.4, 0.2, 0.1 ] },
675 },
676 outline => {
677 line =>'lookup(line)',
678 },
679 size=>256,
680 width=>'scale(1.5,size)',
681 height=>'lookup(size)',
682 );
683
684=item _error($message)
685
686Sets the error field of the object and returns an empty list or undef,
687depending on context. Should be used for error handling, since it may
688provide some user hooks at some point.
689
f68db40f
TC
690The intended usage is:
691
692 some action
693 or return $self->_error("error description");
694
695You should almost always return the result of _error() or return
696immediately afterwards.
697
35574351
TC
698=cut
699
700sub _error {
701 my ($self, $error) = @_;
702
703 $self->{_errstr} = $error;
704
705 return;
706}
707
708
709=item _style_defs()
710
711Returns the style defaults, such as the relationships between line
712color and text color.
713
714Intended to be over-ridden by base classes to provide graph specific
715defaults.
716
717=cut
718
719sub _style_defs {
720 \%style_defs;
721}
722
81453d28 723# Let's make the default something that looks really good, so folks will be interested enough to customize the style.
724my $def_style = 'fount_lin';
35574351
TC
725
726my %styles =
727 (
7b94e723
TC
728 primary =>
729 {
730 fills=>
731 [
732 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
733 ],
734 fg=>'000000',
735 bg=>'E0E0E0',
736 legend=>
737 {
738 #patchborder=>'000000'
739 },
740 },
35574351
TC
741 primary_red =>
742 {
743 fills=>
744 [
745 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
746 ],
747 fg=>'000000',
748 bg=>'C08080',
749 legend=>
750 {
751 patchborder=>'000000'
752 },
753 },
754 mono =>
755 {
756 fills=>
757 [
758 { hatch=>'slash2' },
759 { hatch=>'slosh2' },
760 { hatch=>'vline2' },
761 { hatch=>'hline2' },
762 { hatch=>'cross2' },
763 { hatch=>'grid2' },
764 { hatch=>'stipple3' },
765 { hatch=>'stipple2' },
766 ],
767 channels=>1,
768 bg=>'FFFFFF',
769 fg=>'000000',
770 features=>{ outline=>1 },
771 pie =>{
772 blur=>undef,
773 },
774 },
bb0de914 775 fount_lin =>
35574351
TC
776 {
777 fills=>
778 [
779 { fountain=>'linear',
780 xa_ratio=>0.13, ya_ratio=>0.13, xb_ratio=>0.87, yb_ratio=>0.87,
35574351
TC
781 segments => Imager::Fountain->simple(positions=>[0, 1],
782 colors=>[ NC('FFC0C0'), NC('FF0000') ]),
783 },
784 { fountain=>'linear',
785 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
786 segments => Imager::Fountain->simple(positions=>[0, 1],
787 colors=>[ NC('C0FFC0'), NC('00FF00') ]),
788 },
789 { fountain=>'linear',
790 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
791 segments => Imager::Fountain->simple(positions=>[0, 1],
792 colors=>[ NC('C0C0FF'), NC('0000FF') ]),
793 },
794 { fountain=>'linear',
795 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
796 segments => Imager::Fountain->simple(positions=>[0, 1],
797 colors=>[ NC('FFFFC0'), NC('FFFF00') ]),
798 },
799 { fountain=>'linear',
800 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
801 segments => Imager::Fountain->simple(positions=>[0, 1],
802 colors=>[ NC('C0FFFF'), NC('00FFFF') ]),
803 },
804 { fountain=>'linear',
805 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
806 segments => Imager::Fountain->simple(positions=>[0, 1],
807 colors=>[ NC('FFC0FF'), NC('FF00FF') ]),
808 },
809 ],
810 back=>{ fountain=>'linear',
811 xa_ratio=>0, ya_ratio=>0,
812 xb_ratio=>1.0, yb_ratio=>1.0,
813 segments=>Imager::Fountain->simple
814 ( positions=>[0, 1],
815 colors=>[ NC('6060FF'), NC('60FF60') ]) },
816 fg=>'000000',
817 bg=>'FFFFFF',
818 features=>{ dropshadow=>1 },
bb0de914
TC
819 },
820 fount_rad =>
821 {
35574351
TC
822 fills=>
823 [
824 { fountain=>'radial',
825 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
826 segments => Imager::Fountain->simple(positions=>[0, 1],
827 colors=>[ NC('FF8080'), NC('FF0000') ]),
828 },
829 { fountain=>'radial',
830 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
831 segments => Imager::Fountain->simple(positions=>[0, 1],
832 colors=>[ NC('80FF80'), NC('00FF00') ]),
833 },
834 { fountain=>'radial',
835 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
836 segments => Imager::Fountain->simple(positions=>[0, 1],
837 colors=>[ NC('808080FF'), NC('0000FF') ]),
838 },
839 { fountain=>'radial',
840 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
841 segments => Imager::Fountain->simple(positions=>[0, 1],
842 colors=>[ NC('FFFF80'), NC('FFFF00') ]),
843 },
844 { fountain=>'radial',
845 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
846 segments => Imager::Fountain->simple(positions=>[0, 1],
847 colors=>[ NC('80FFFF'), NC('00FFFF') ]),
848 },
849 { fountain=>'radial',
850 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
851 segments => Imager::Fountain->simple(positions=>[0, 1],
852 colors=>[ NC('FF80FF'), NC('FF00FF') ]),
853 },
854 ],
855 back=>{ fountain=>'linear',
856 xa_ratio=>0, ya_ratio=>0,
857 xb_ratio=>1.0, yb_ratio=>1.0,
858 segments=>Imager::Fountain->simple
859 ( positions=>[0, 1],
860 colors=>[ NC('6060FF'), NC('60FF60') ]) },
861 fg=>'000000',
862 bg=>'FFFFFF',
bb0de914
TC
863 }
864 );
35574351
TC
865
866=item $self->_style_setup(\%opts)
867
868Uses the values from %opts to build a customized hash describing the
869way the graph should be drawn.
870
871=cut
872
873sub _style_setup {
874 my ($self, $opts) = @_;
875 my $style_defs = $self->_style_defs;
876 my $style;
877 $style = $styles{$opts->{style}} if $opts->{style};
878 $style ||= $styles{$def_style};
879
880 my @search_list = ( $style_defs, $style, $opts);
881 my %work;
882
883 my @composite = $self->_composite();
884 my %composite;
885 @composite{@composite} = @composite;
886
887 for my $src (@search_list) {
888 for my $key (keys %$src) {
889 if ($composite{$key}) {
890 $work{$key} = {} unless exists $work{$key};
891 if (ref $src->{$key}) {
892 # some keys have sub values, especially text
893 @{$work{$key}}{keys %{$src->{$key}}} = values %{$src->{$key}};
894 }
895 else {
896 # assume it's the text for a title or something
897 $work{$key}{text} = $src->{$key};
898 }
899 }
900 else {
901 $work{$key} = $src->{$key};
902 }
903 }
904 }
905
906 # features are handled specially
907 $work{features} = {};
908 for my $src (@search_list) {
909 if ($src->{features}) {
910 if (ref $src->{features}) {
911 if (ref($src->{features}) =~ /ARRAY/) {
912 # just set those features
913 for my $feature (@{$src->{features}}) {
914 $work{features}{$feature} = 1;
915 }
916 }
917 elsif (ref($src->{features}) =~ /HASH/) {
918 if ($src->{features}{reset}) {
919 $work{features} = {}; # only the ones the user specifies
920 }
921 @{$work{features}}{keys %{$src->{features}}} =
922 values(%{$src->{features}});
923 }
924 }
925 else {
926 # just set that single feature
927 $work{features}{$src->{features}} = 1;
928 }
929 }
930 }
931 #use Data::Dumper;
932 #print Dumper(\%work);
933
934 $self->{_style} = \%work;
935}
936
937=item $self->_get_thing($name)
938
939Retrieve some general 'thing'.
940
941Supports the 'lookup(foo)' mechanism.
942
f68db40f
TC
943Returns an empty list on failure.
944
35574351
TC
945=cut
946
947sub _get_thing {
948 my ($self, $name, @depth) = @_;
949
950 push(@depth, $name);
951 my $what;
952 if ($name =~ /^(\w+)\.(\w+)$/) {
953 $what = $self->{_style}{$1}{$2};
954 }
955 else {
956 $what = $self->{_style}{$name};
957 }
958 defined $what or
959 return;
960 if (ref $what) {
961 return $what;
962 }
963 elsif ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
964 @depth < MAX_DEPTH
965 or return $self->_error("too many levels of recursion in lookup(@depth)");
966 return $self->_get_thing($1, @depth);
967 }
968 else {
969 return $what;
970 }
971}
972
973=item $self->_get_number($name)
974
975Retrieves a number from the style. The value in the style can be the
976number, or one of two functions:
977
978=over
979
980=item lookup(newname)
981
982Recursively looks up I<newname> in the style.
983
984=item scale(value1,value2)
985
33a928b7 986Each value can be a number or a name. Names are recursively looked up
35574351
TC
987in the style and the product is returned.
988
989=back
990
991=cut
bb0de914 992
35574351
TC
993sub _get_number {
994 my ($self, $name, @depth) = @_;
995
996 push(@depth, $name);
997 my $what;
998 if ($name =~ /^(\w+)\.(\w+)$/) {
999 $what = $self->{_style}{$1}{$2};
1000 }
1001 else {
1002 $what = $self->{_style}{$name};
1003 }
1004 defined $what or
1005 return $self->_error("$name is undef (@depth)");
1006
1007 if (ref $what) {
1008 if ($what =~ /CODE/) {
1009 $what = $what->($self, $name);
1010 }
1011 }
1012 else {
1013 if ($what =~ /^lookup\(([\w.]+)\)$/) {
1014 @depth < MAX_DEPTH
1015 or return $self->_error("too many levels of recursion in lookup (@depth)");
1016 return $self->_get_number($1, @depth);
1017 }
1018 elsif ($what =~ /^scale\(
1019 ((?:[a-z][\w.]*)|$NUM_RE)
1020 ,
1021 ((?:[a-z][\w.]*)|$NUM_RE)\)$/x) {
1022 my ($left, $right) = ($1, $2);
1023 unless ($left =~ /^$NUM_RE$/) {
1024 @depth < MAX_DEPTH
1025 or return $self->_error("too many levels of recursion in scale (@depth)");
1026 $left = $self->_get_number($left, @depth);
1027 }
1028 unless ($right =~ /^$NUM_RE$/) {
1029 @depth < MAX_DEPTH
1030 or return $self->_error("too many levels of recursion in scale (@depth)");
1031 $right = $self->_get_number($right, @depth);
1032 }
1033 return $left * $right;
1034 }
1035 else {
1036 return $what+0;
1037 }
1038 }
1039}
1040
379c5b02
TC
1041=item $self->_get_integer($name)
1042
1043Retrieves an integer from the style. This is a simple wrapper around
1044_get_number() that rounds the result to an integer.
1045
f68db40f
TC
1046Returns an empty list on failure.
1047
379c5b02
TC
1048=cut
1049
1050sub _get_integer {
1051 my ($self, $name, @depth) = @_;
1052
1053 my $number = $self->_get_number($name, @depth)
1054 or return;
1055
1056 return sprintf("%.0f", $number);
1057}
1058
35574351
TC
1059=item _get_color($name)
1060
1061Returns a color object of the given name from the style hash.
1062
1063Uses Imager::Color->new to translate normal scalars into color objects.
1064
1065Allows the lookup(name) mechanism.
1066
f68db40f
TC
1067Returns an empty list on failure.
1068
35574351
TC
1069=cut
1070
1071sub _get_color {
1072 my ($self, $name, @depth) = @_;
1073
1074 push(@depth, $name);
1075 my $what;
1076 if ($name =~ /^(\w+)\.(\w+)$/) {
1077 $what = $self->{_style}{$1}{$2};
1078 }
1079 else {
1080 $what = $self->{_style}{$name};
1081 }
1082
1083 defined($what)
1084 or return $self->_error("$name was undefined (@depth)");
1085
1086 unless (ref $what) {
1087 if ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
1088 @depth < MAX_DEPTH or
1089 return $self->_error("too many levels of recursion in lookup (@depth)");
1090
1091 return $self->_get_color($1, @depth);
1092 }
1093 $what = Imager::Color->new($what);
1094 }
1095
1096 $what;
1097}
1098
1099=item _translate_fill($what, $box)
1100
1101Given the value of a fill, either attempts to convert it into a fill
1102list (one of C<<color=>$color_value, filled=>1>> or C<<fill=>{ fill
1103parameters }>>), or to lookup another fill that is referred to with
1104the 'lookup(name)' mechanism.
1105
1106This function does the fg and bg initialization for hatched fills, and
1107translation of *_ratio for fountain fills (using the $box parameter).
1108
f68db40f
TC
1109Returns an empty list on failure.
1110
35574351
TC
1111=cut
1112
1113sub _translate_fill {
1114 my ($self, $what, $box, @depth) = @_;
1115
1116 if (ref $what) {
1117 if (UNIVERSAL::isa($what, "Imager::Color")) {
1118 return ( color=>Imager::Color->new($what), filled=>1 );
1119 }
1120 else {
1121 # a general fill
33a928b7
TC
1122 # default to normal combine mode
1123 my %work = ( combine => 'normal', %$what );
35574351 1124 if ($what->{hatch}) {
35574351
TC
1125 if (!$work{fg}) {
1126 $work{fg} = $self->_get_color('fg')
1127 or return;
1128 }
1129 if (!$work{bg}) {
1130 $work{bg} = $self->_get_color('bg')
1131 or return;
1132 }
1133 return ( fill=>\%work );
1134 }
1135 elsif ($what->{fountain}) {
35574351
TC
1136 for my $key (qw(xa ya xb yb)) {
1137 if (exists $work{"${key}_ratio"}) {
1138 if ($key =~ /^x/) {
1139 $work{$key} = $box->[0] + $work{"${key}_ratio"}
1140 * ($box->[2] - $box->[0]);
1141 }
1142 else {
1143 $work{$key} = $box->[1] + $work{"${key}_ratio"}
1144 * ($box->[3] - $box->[1]);
1145 }
1146 }
1147 }
1148 return ( fill=>\%work );
1149 }
1150 else {
33a928b7 1151 return ( fill=> \%work );
35574351
TC
1152 }
1153 }
1154 }
1155 else {
1156 if ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
1157 return $self->_get_fill($1, $box, @depth);
1158 }
1159 else {
1160 # assumed to be an Imager::Color single value
1161 return ( color=>Imager::Color->new($what), filled=>1 );
1162 }
1163 }
1164}
1165
1166=item _data_fill($index, $box)
1167
1168Retrieves the fill parameters for a data area fill.
1169
1170=cut
1171
1172sub _data_fill {
1173 my ($self, $index, $box) = @_;
1174
1175 my $fills = $self->{_style}{fills};
1176 return $self->_translate_fill($fills->[$index % @$fills], $box,
1177 "data.$index");
1178}
1179
1180=item _get_fill($index, $box)
1181
1182Retrieves fill parameters for a named fill.
1183
1184=cut
1185
1186sub _get_fill {
1187 my ($self, $name, $box, @depth) = @_;
1188
1189 push(@depth, $name);
1190 my $what;
1191 if ($name =~ /^(\w+)\.(\w+)$/) {
1192 $what = $self->{_style}{$1}{$2};
1193 }
1194 else {
1195 $what = $self->{_style}{$name};
1196 }
1197
1198 defined($what)
1199 or return $self->_error("no fill $name found");
1200
1201 return $self->_translate_fill($what, $box, @depth);
1202}
1203
1204=item _make_img()
1205
1206Builds the image object for the graph and fills it with the background
1207fill.
1208
1209=cut
1210
1211sub _make_img {
1212 my ($self) = @_;
35574351 1213
81453d28 1214 my $width = $self->_get_number('width') || 256;
1215 my $height = $self->_get_number('height') || 256;
35574351
TC
1216 my $channels = $self->{_style}{channels};
1217
1218 $channels ||= 3;
1219
1220 my $img = Imager->new(xsize=>$width, ysize=>$height, channels=>$channels);
1221
1222 $img->box($self->_get_fill('back', [ 0, 0, $width-1, $height-1]));
1223
1224 $img;
1225}
1226
f68db40f
TC
1227=item _text_style($name)
1228
1229Returns parameters suitable for calls to Imager::Font's bounding_box()
1230and draw() methods intended for use in defining text styles.
1231
1232Returns an empty list on failure.
1233
1234=cut
1235
35574351
TC
1236sub _text_style {
1237 my ($self, $name) = @_;
1238
1239 my %work;
1240
1241 if ($self->{_style}{$name}) {
1242 %work = %{$self->{_style}{$name}};
1243 }
1244 else {
1245 %work = %{$self->{_style}{text}};
1246 }
1247 $work{font}
5d622bb8 1248 or return $self->_error("$name has no font parameter");
35574351
TC
1249
1250 $work{font} = $self->_get_thing("$name.font")
5d622bb8 1251 or return $self->_error("No $name.font defined, either set $name.font or font to a font");
35574351
TC
1252 UNIVERSAL::isa($work{font}, "Imager::Font")
1253 or return $self->_error("$name.font is not a font");
1254 if ($work{color} && !ref $work{color}) {
1255 $work{color} = $self->_get_color("$name.color")
1256 or return;
1257 }
1258 $work{size} = $self->_get_number("$name.size");
1259 $work{sizew} = $self->_get_number("$name.sizew")
1260 if $work{sizew};
1261
1262 %work;
1263}
1264
f68db40f
TC
1265=item _text_bbox($text, $name)
1266
1267Returns a bounding box for the specified $text as styled by $name.
1268
1269Returns an empty list on failure.
1270
1271=cut
1272
35574351
TC
1273sub _text_bbox {
1274 my ($self, $text, $name) = @_;
1275
5d622bb8
TC
1276 my %text_info = $self->_text_style($name)
1277 or return;
35574351
TC
1278
1279 my @bbox = $text_info{font}->bounding_box(%text_info, string=>$text,
1280 canon=>1);
1281
1282 return @bbox[0..3];
1283}
1284
1285sub _align_box {
1286 my ($self, $box, $chart_box, $name) = @_;
1287
1288 my $halign = $self->{_style}{$name}{halign}
1289 or $self->_error("no halign for $name");
1290 my $valign = $self->{_style}{$name}{valign};
1291
1292 if ($halign eq 'right') {
1293 $box->[0] += $chart_box->[2] - $box->[2];
1294 }
1295 elsif ($halign eq 'left') {
1296 $box->[0] = $chart_box->[0];
1297 }
1298 elsif ($halign eq 'center' || $halign eq 'centre') {
1299 $box->[0] = ($chart_box->[0] + $chart_box->[2] - $box->[2])/2;
1300 }
1301 else {
1302 return $self->_error("invalid halign $halign for $name");
1303 }
1304
1305 if ($valign eq 'top') {
1306 $box->[1] = $chart_box->[1];
1307 }
1308 elsif ($valign eq 'bottom') {
1309 $box->[1] = $chart_box->[3] - $box->[3];
1310 }
1311 elsif ($valign eq 'center' || $valign eq 'centre') {
1312 $box->[1] = ($chart_box->[1] + $chart_box->[3] - $box->[3])/2;
1313 }
1314 else {
1315 return $self->_error("invalid valign $valign for $name");
1316 }
1317 $box->[2] += $box->[0];
1318 $box->[3] += $box->[1];
1319}
1320
1321sub _remove_box {
1322 my ($self, $chart_box, $object_box) = @_;
1323
1324 my $areax;
1325 my $areay;
1326 if ($object_box->[0] - $chart_box->[0]
1327 < $chart_box->[2] - $object_box->[2]) {
1328 $areax = ($object_box->[2] - $chart_box->[0])
1329 * ($chart_box->[3] - $chart_box->[1]);
1330 }
1331 else {
1332 $areax = ($chart_box->[2] - $object_box->[0])
1333 * ($chart_box->[3] - $chart_box->[1]);
1334 }
1335
1336 if ($object_box->[1] - $chart_box->[1]
1337 < $chart_box->[3] - $object_box->[3]) {
1338 $areay = ($object_box->[3] - $chart_box->[1])
1339 * ($chart_box->[2] - $chart_box->[0]);
1340 }
1341 else {
1342 $areay = ($chart_box->[3] - $object_box->[1])
1343 * ($chart_box->[2] - $chart_box->[0]);
1344 }
1345
1346 if ($areay < $areax) {
1347 if ($object_box->[1] - $chart_box->[1]
1348 < $chart_box->[3] - $object_box->[3]) {
1349 $chart_box->[1] = $object_box->[3];
1350 }
1351 else {
1352 $chart_box->[3] = $object_box->[1];
1353 }
1354 }
1355 else {
1356 if ($object_box->[0] - $chart_box->[0]
1357 < $chart_box->[2] - $object_box->[2]) {
1358 $chart_box->[0] = $object_box->[2];
1359 }
1360 else {
1361 $chart_box->[2] = $object_box->[0];
1362 }
1363 }
1364}
1365
1366sub _draw_legend {
1367 my ($self, $img, $labels, $chart_box) = @_;
1368
33a928b7
TC
1369 my $orient = $self->_get_thing('legend.orientation');
1370 defined $orient or $orient = 'vertical';
1371
1372 if ($orient eq 'vertical') {
1373 return $self->_draw_legend_vertical($img, $labels, $chart_box);
1374 }
1375 elsif ($orient eq 'horizontal') {
1376 return $self->_draw_legend_horizontal($img, $labels, $chart_box);
1377 }
1378 else {
1379 return $self->_error("Unknown legend.orientation $orient");
1380 }
1381}
1382
1383sub _draw_legend_horizontal {
1384 my ($self, $img, $labels, $chart_box) = @_;
1385
1386 defined(my $padding = $self->_get_integer('legend.padding'))
1387 or return;
1388 my $patchsize = $self->_get_integer('legend.patchsize')
1389 or return;
1390 defined(my $gap = $self->_get_integer('legend.patchgap'))
1391 or return;
1392
1393 my $minrowsize = $patchsize + $gap;
1394 my ($width, $height) = (0,0);
1395 my $row_height = $minrowsize;
1396 my $pos = 0;
1397 my @sizes;
1398 my @offsets;
1399 for my $label (@$labels) {
5d622bb8
TC
1400 my @text_box = $self->_text_bbox($label, 'legend')
1401 or return;
33a928b7
TC
1402 push(@sizes, \@text_box);
1403 my $entry_width = $patchsize + $gap + $text_box[2];
1404 if ($pos == 0) {
1405 # never re-wrap the first entry
1406 push @offsets, [ 0, $height ];
1407 }
1408 else {
1409 if ($pos + $gap + $entry_width > $chart_box->[2]) {
1410 $pos = 0;
1411 $height += $row_height;
1412 }
1413 push @offsets, [ $pos, $height ];
1414 }
1415 my $entry_right = $pos + $entry_width;
1416 $pos += $gap + $entry_width;
1417 $entry_right > $width and $width = $entry_right;
1418 if ($text_box[3] > $row_height) {
1419 $row_height = $text_box[3];
1420 }
1421 }
1422 $height += $row_height;
1423 my @box = ( 0, 0, $width + $padding * 2, $height + $padding * 2 );
1424 my $outsidepadding = 0;
1425 if ($self->{_style}{legend}{border}) {
1426 defined($outsidepadding = $self->_get_integer('legend.outsidepadding'))
1427 or return;
1428 $box[2] += 2 * $outsidepadding;
1429 $box[3] += 2 * $outsidepadding;
1430 }
1431 $self->_align_box(\@box, $chart_box, 'legend')
1432 or return;
1433 if ($self->{_style}{legend}{fill}) {
1434 $img->box(xmin=>$box[0]+$outsidepadding,
1435 ymin=>$box[1]+$outsidepadding,
1436 xmax=>$box[2]-$outsidepadding,
1437 ymax=>$box[3]-$outsidepadding,
1438 $self->_get_fill('legend.fill', \@box));
1439 }
1440 $box[0] += $outsidepadding;
1441 $box[1] += $outsidepadding;
1442 $box[2] -= $outsidepadding;
1443 $box[3] -= $outsidepadding;
1444 my %text_info = $self->_text_style('legend')
1445 or return;
1446 my $patchborder;
1447 if ($self->{_style}{legend}{patchborder}) {
1448 $patchborder = $self->_get_color('legend.patchborder')
1449 or return;
1450 }
1451
1452 my $dataindex = 0;
1453 for my $label (@$labels) {
1454 my ($left, $top) = @{$offsets[$dataindex]};
1455 $left += $box[0] + $padding;
1456 $top += $box[1] + $padding;
1457 my $textpos = $left + $patchsize + $gap;
1458 my @patchbox = ( $left, $top,
1459 $left + $patchsize, $top + $patchsize );
1460 my @fill = $self->_data_fill($dataindex, \@patchbox)
1461 or return;
1462 $img->box(xmin=>$left, ymin=>$top, xmax=>$left + $patchsize,
1463 ymax=>$top + $patchsize, @fill);
1464 if ($self->{_style}{legend}{patchborder}) {
1465 $img->box(xmin=>$left, ymin=>$top, xmax=>$left + $patchsize,
1466 ymax=>$top + $patchsize,
1467 color=>$patchborder);
1468 }
1469 $img->string(%text_info, x=>$textpos, 'y'=>$top + $patchsize,
1470 text=>$label);
1471
1472 ++$dataindex;
1473 }
1474 if ($self->{_style}{legend}{border}) {
1475 my $border_color = $self->_get_color('legend.border')
1476 or return;
1477 $img->box(xmin=>$box[0], ymin=>$box[1], xmax=>$box[2], ymax=>$box[3],
1478 color=>$border_color);
1479 }
1480 $self->_remove_box($chart_box, \@box);
1481 1;
1482}
1483
1484sub _draw_legend_vertical {
1485 my ($self, $img, $labels, $chart_box) = @_;
1486
379c5b02 1487 defined(my $padding = $self->_get_integer('legend.padding'))
35574351 1488 or return;
379c5b02 1489 my $patchsize = $self->_get_integer('legend.patchsize')
35574351 1490 or return;
379c5b02 1491 defined(my $gap = $self->_get_integer('legend.patchgap'))
35574351
TC
1492 or return;
1493 my $minrowsize = $patchsize + $gap;
1494 my ($width, $height) = (0,0);
1495 my @sizes;
1496 for my $label (@$labels) {
5d622bb8
TC
1497 my @box = $self->_text_bbox($label, 'legend')
1498 or return;
35574351
TC
1499 push(@sizes, \@box);
1500 $width = $box[2] if $box[2] > $width;
1501 if ($minrowsize > $box[3]) {
1502 $height += $minrowsize;
1503 }
1504 else {
1505 $height += $box[3];
1506 }
1507 }
1508 my @box = (0, 0,
1509 $width + $patchsize + $padding * 2 + $gap,
1510 $height + $padding * 2 - $gap);
1511 my $outsidepadding = 0;
1512 if ($self->{_style}{legend}{border}) {
33a928b7 1513 defined($outsidepadding = $self->_get_integer('legend.outsidepadding'))
35574351
TC
1514 or return;
1515 $box[2] += 2 * $outsidepadding;
1516 $box[3] += 2 * $outsidepadding;
1517 }
1518 $self->_align_box(\@box, $chart_box, 'legend')
1519 or return;
1520 if ($self->{_style}{legend}{fill}) {
1521 $img->box(xmin=>$box[0]+$outsidepadding,
1522 ymin=>$box[1]+$outsidepadding,
1523 xmax=>$box[2]-$outsidepadding,
1524 ymax=>$box[3]-$outsidepadding,
1525 $self->_get_fill('legend.fill', \@box));
1526 }
1527 $box[0] += $outsidepadding;
1528 $box[1] += $outsidepadding;
1529 $box[2] -= $outsidepadding;
1530 $box[3] -= $outsidepadding;
1531 my $ypos = $box[1] + $padding;
1532 my $patchpos = $box[0]+$padding;
1533 my $textpos = $patchpos + $patchsize + $gap;
1534 my %text_info = $self->_text_style('legend')
1535 or return;
1536 my $patchborder;
1537 if ($self->{_style}{legend}{patchborder}) {
1538 $patchborder = $self->_get_color('legend.patchborder')
1539 or return;
1540 }
1541 my $dataindex = 0;
1542 for my $label (@$labels) {
1543 my @patchbox = ( $patchpos - $patchsize/2, $ypos - $patchsize/2,
1544 $patchpos + $patchsize * 3 / 2, $ypos + $patchsize*3/2 );
1545 my @fill = $self->_data_fill($dataindex, \@patchbox)
1546 or return;
1547 $img->box(xmin=>$patchpos, ymin=>$ypos, xmax=>$patchpos + $patchsize,
1548 ymax=>$ypos + $patchsize, @fill);
1549 if ($self->{_style}{legend}{patchborder}) {
1550 $img->box(xmin=>$patchpos, ymin=>$ypos, xmax=>$patchpos + $patchsize,
1551 ymax=>$ypos + $patchsize,
1552 color=>$patchborder);
1553 }
1554 $img->string(%text_info, x=>$textpos, 'y'=>$ypos + $patchsize,
1555 text=>$label);
1556
1557 my $step = $patchsize + $gap;
1558 if ($minrowsize < $sizes[$dataindex][3]) {
1559 $ypos += $sizes[$dataindex][3];
1560 }
1561 else {
1562 $ypos += $minrowsize;
1563 }
1564 ++$dataindex;
1565 }
1566 if ($self->{_style}{legend}{border}) {
1567 my $border_color = $self->_get_color('legend.border')
1568 or return;
1569 $img->box(xmin=>$box[0], ymin=>$box[1], xmax=>$box[2], ymax=>$box[3],
1570 color=>$border_color);
1571 }
1572 $self->_remove_box($chart_box, \@box);
1573 1;
1574}
1575
1576sub _draw_title {
1577 my ($self, $img, $chart_box) = @_;
1578
1579 my $title = $self->{_style}{title}{text};
5d622bb8
TC
1580 my @box = $self->_text_bbox($title, 'title')
1581 or return;
35574351
TC
1582 my $yoff = $box[1];
1583 @box[0,1] = (0,0);
1584 $self->_align_box(\@box, $chart_box, 'title');
5d622bb8
TC
1585 my %text_info = $self->_text_style('title')
1586 or return;
35574351
TC
1587 $img->string(%text_info, x=>$box[0], 'y'=>$box[3] + $yoff, text=>$title);
1588 $self->_remove_box($chart_box, \@box);
1589 1;
1590}
1591
1592sub _small_extent {
1593 my ($self, $box) = @_;
1594
1595 if ($box->[2] - $box->[0] > $box->[3] - $box->[1]) {
1596 return $box->[3] - $box->[1];
1597 }
1598 else {
1599 return $box->[2] - $box->[0];
1600 }
1601}
1602
1603=item _composite()
1604
1605Returns a list of style fields that are stored as composites, and
1606should be merged instead of just being replaced.
1607
1608=cut
1609
1610sub _composite {
1611 qw(title legend text label dropshadow outline callout);
1612}
1613
1614sub _filter_region {
1615 my ($self, $img, $left, $top, $right, $bottom, $filter) = @_;
1616
1617 unless (ref $filter) {
1618 my $name = $filter;
1619 $filter = $self->_get_thing($name)
1620 or return;
1621 $filter->{type}
1622 or return $self->_error("no type for filter $name");
1623 }
1624
1625 $left > 0 or $left = 0;
1626 $top > 0 or $top = 0;
1627
1628 # newer versions of Imager let you work on just part of an image
1629 if ($img->can('masked') && !$self->{_style}{features}{_debugblur}) {
1630 my $masked = $img->masked(left=>$left, top=>$top,
1631 right=>$right, bottom=>$bottom);
1632 $masked->filter(%$filter);
1633 }
1634 else {
1635 # for older versions of Imager
1636 my $subset = $img->crop(left=>$left, top=>$top,
1637 right=>$right, bottom=>$bottom);
1638 $subset->filter(%$filter);
1639 $img->paste(left=>$left, top=>$top, img=>$subset);
1640 }
1641}
1642
16431;
1644__END__
1645
1646=back
1647
1648=head1 SEE ALSO
1649
1650Imager::Graph::Pie(3), Imager(3), perl(1).
1651
1652=head1 AUTHOR
1653
1654Tony Cook <tony@develop-help.com>
1655
54ada35d
TC
1656=head1 LICENSE
1657
1658Imager::Graph is licensed under the same terms as perl itself.
1659
35574351
TC
1660=head1 BLAME
1661
1662Addi for producing a cool imaging module. :)
1663
1664=cut