]> git.imager.perl.org - imager-graph.git/blobdiff - Graph.pm
Updates the MANIFEST
[imager-graph.git] / Graph.pm
index 93e05f8c1a3e4c061f49c6adf0cb26587b8f12ed..e74c00675aae5be98a4b1494f02aef938e399294 100644 (file)
--- a/Graph.pm
+++ b/Graph.pm
@@ -7,10 +7,11 @@ Imager::Graph - Perl extension for producing Graphs using the Imager library.
 
 =head1 SYNOPSIS
 
-  use Imager::Graph::SubClass;
-  my $chart = Imager::Graph::SubClass->new;
+  use Imager::Graph::Sub_class;
+  my $chart = Imager::Graph::Sub_class->new;
   my $img = $chart->draw(data=> \@data, ...)
     or die $chart->error;
+  $img->write(file => 'image.png');
 
 =head1 DESCRIPTION
 
@@ -28,7 +29,7 @@ use vars qw($VERSION);
 use Imager qw(:handy);
 use Imager::Fountain;
 
-$VERSION = '0.06';
+$VERSION = '0.07';
 
 # the maximum recursion depth in determining a color, fill or number
 use constant MAX_DEPTH => 10;
@@ -45,9 +46,137 @@ sub new {
   bless {}, $_[0];
 }
 
+=item set_graph_size($size)
+
+Sets the size of the graph (in pixels) within the image.  The size of the image defaults to 1.5 * $graph_size.
+
+=cut
+
+sub set_graph_size {
+  $_[0]->{'custom_style'}->{'size'} = $_[1];
+}
+
+=item set_image_width($width)
+
+Sets the width of the image in pixels.
+
+=cut
+
+sub set_image_width {
+  $_[0]->{'custom_style'}->{'width'} = $_[1];
+}
+
+=item set_image_height($height)
+
+Sets the height of the image in pixels.
+
+=cut
+
+sub set_image_height {
+  $_[0]->{'custom_style'}->{'height'} = $_[1];
+}
+
+=item add_data_series([8, 6, 7, 5, 3, 0, 9], 'Series Name');
+
+Adds a data series to the graph.  For L<Imager::Graph::Pie>, only one data series can be added.
+
+=cut
+
+sub add_data_series {
+  my $self = shift;
+  my $data_ref = shift;
+  my $series_name = shift;
+
+  my $graph_data = $self->{'graph_data'} || [];
+
+  push @$graph_data, { data => $data_ref, series_name => $series_name };
+  if (defined $series_name) {
+    push @{$self->{'labels'}}, $series_name;
+  }
+
+  $self->{'graph_data'} = $graph_data;
+  return;
+}
+
+sub _get_data_series {
+  my ($self, $opts) = @_;
+
+  # return the data supplied to draw() if any.
+  if ($opts->{data}) {
+    # one or multiple series?
+    my $data = $opts->{data};
+    if (@$data && ref $data->[0] && ref $data->[0] =~ /ARRAY/) {
+      return $data;
+    }
+    else {
+      return [ { data => $data } ];
+    }
+  }
+
+  return $self->{'graph_data'};
+}
+
+=item set_labels(['label1', 'label2' ... ])
+
+Labels the specific data points.  For line/bar graphs, this is the x-axis.  For pie graphs, it is the label for the wedges.
+
+=cut
+
+sub set_labels {
+  $_[0]->{'labels'} = $_[1];
+}
+
+sub _get_labels {
+  my ($self, $opts) = @_;
+
+  $opts->{labels}
+    and return $opts->{labels};
+
+  return $_[0]->{'labels'}
+}
+
+=item set_title($title)
+
+Sets the title of the graph.  Requires setting a font.
+
+=cut
+
+sub set_title {
+  $_[0]->{'custom_style'}->{'title'}->{'text'} = $_[1];
+}
+
+=item set_font($font)
+
+Sets the font to use for text.  Takes an L<Imager::Font> object.
+
+=cut
+
+sub set_font {
+  $_[0]->{'custom_style'}->{'font'} = $_[1];
+}
+
+=item set_style($style_name)
+
+Sets the style to be used for the graph.  Imager::Graph comes with several pre-defined styles: fount_lin (default), fount_rad, mono, primary_red, and primary.
+
+=cut
+
+sub set_style {
+  $_[0]->{'style'} = $_[1];
+}
+
+sub _get_style {
+  my ($self, $opts) = @_;
+
+  $opts->{style}
+    and return $opts->{style};
+
+  return $self->{'style'};
+}
+
 =item error
 
-Returns an error message.  Only value if the draw() method returns false.
+Returns an error message.  Only valid if the draw() method returns false.
 
 =cut
 
@@ -59,7 +188,8 @@ sub error {
 
 Creates a new image, draws the chart onto that image and returns it.
 
-Typically you will need to supply a C<data> parameter in the format
+Optionally, instead of using the api methods to configure your chart,
+you can supply a C<data> parameter in the format
 required by that particular graph, and if your graph will use any
 text, a C<font> parameter
 
@@ -141,11 +271,418 @@ the data and a linear blue to green fill for the background.
 
 =back
 
+=head1 Style API
+
+To set or override styles, you can use the following methods:
+
+=over 4
+
+=item set_image_background
+
+=cut
+
+sub set_image_background {
+  $_[0]->{'custom_style'}->{'back'} = $_[1];
+}
+
+=item set_channels
+
+=cut
+
+sub set_channels {
+  $_[0]->{'custom_style'}->{'channels'} = $_[1];
+}
+
+=item set_line_color
+
+=cut
+
+sub set_line_color {
+  $_[0]->{'custom_style'}->{'line'} = $_[1];
+}
+
+=item set_title_font_size
+
+=cut
+
+sub set_title_font_size {
+  $_[0]->{'custom_style'}->{'title'}->{'size'} = $_[1];
+}
+
+=item set_title_font_color
+
+=cut
+
+sub set_title_font_color {
+  $_[0]->{'custom_style'}->{'title'}->{'color'} = $_[1];
+}
+
+=item set_title_horizontal_align
+
+=cut
+
+sub set_title_horizontal_align {
+  $_[0]->{'custom_style'}->{'title'}->{'halign'} = $_[1];
+}
+
+=item set_title_vertical_align
+
+=cut
+
+sub set_title_vertical_align {
+  $_[0]->{'custom_style'}->{'title'}->{'valign'} = $_[1];
+}
+
+=item set_text_font_color
+
+=cut
+
+sub set_text_font_color {
+  $_[0]->{'custom_style'}->{'text'}->{'color'} = $_[1];
+}
+
+=item set_text_font_size
+
+=cut
+
+sub set_text_font_size {
+  $_[0]->{'custom_style'}->{'text'}->{'size'} = $_[1];
+}
+
+=item set_graph_background_color
+
+=cut
+
+sub set_graph_background_color {
+  $_[0]->{'custom_style'}->{'bg'} = $_[1];
+}
+
+=item set_graph_foreground_color
+
+=cut
+
+sub set_graph_foreground_color {
+  $_[0]->{'custom_style'}->{'fg'} = $_[1];
+}
+
+=item set_legend_font_color
+
+=cut
+
+sub set_legend_font_color {
+  $_[0]->{'custom_style'}->{'legend'}->{'color'} = $_[1];
+}
+
+=item set_legend_font
+
+=cut
+
+sub set_legend_font {
+  $_[0]->{'custom_style'}->{'legend'}->{'font'} = $_[1];
+}
+
+=item set_legend_font_size
+
+=cut
+
+sub set_legend_font_size {
+  $_[0]->{'custom_style'}->{'legend'}->{'size'} = $_[1];
+}
+
+=item set_legend_patch_size
+
+=cut
+
+sub set_legend_patch_size {
+  $_[0]->{'custom_style'}->{'legend'}->{'patchsize'} = $_[1];
+}
+
+=item set_legend_patch_gap
+
+=cut
+
+sub set_legend_patch_gap {
+  $_[0]->{'custom_style'}->{'legend'}->{'patchgap'} = $_[1];
+}
+
+=item set_legend_horizontal_align
+
+=cut
+
+sub set_legend_horizontal_align {
+  $_[0]->{'custom_style'}->{'legend'}->{'halign'} = $_[1];
+}
+
+=item set_legend_vertical_align
+
+=cut
+
+sub set_legend_vertical_align {
+  $_[0]->{'custom_style'}->{'legend'}->{'valign'} = $_[1];
+}
+
+=item set_legend_padding
+
+=cut
+
+sub set_legend_padding {
+  $_[0]->{'custom_style'}->{'legend'}->{'padding'} = $_[1];
+}
+
+=item set_legend_outside_padding
+
+=cut
+
+sub set_legend_outside_padding {
+  $_[0]->{'custom_style'}->{'legend'}->{'outsidepadding'} = $_[1];
+}
+
+=item set_legend_fill
+
+=cut
+
+sub set_legend_fill {
+  $_[0]->{'custom_style'}->{'legend'}->{'fill'} = $_[1];
+}
+
+=item set_legend_border
+
+=cut
+
+sub set_legend_border {
+  $_[0]->{'custom_style'}->{'legend'}->{'border'} = $_[1];
+}
+
+=item set_legend_orientation
+
+=cut
+
+sub set_legend_orientation {
+  $_[0]->{'custom_style'}->{'legend'}->{'orientation'} = $_[1];
+}
+
+=item set_callout_font_color
+
+=cut
+
+sub set_callout_font_color {
+  $_[0]->{'custom_style'}->{'callout'}->{'color'} = $_[1];
+}
+
+=item set_callout_font
+
+=cut
+
+sub set_callout_font {
+  $_[0]->{'custom_style'}->{'callout'}->{'font'} = $_[1];
+}
+
+=item set_callout_font_size
+
+=cut
+
+sub set_callout_font_size {
+  $_[0]->{'custom_style'}->{'callout'}->{'size'} = $_[1];
+}
+
+=item set_callout_line_color
+
+=cut
+
+sub set_callout_line_color {
+  $_[0]->{'custom_style'}->{'callout'}->{'line'} = $_[1];
+}
+
+=item set_callout_leader_inside_length
+
+=cut
+
+sub set_callout_leader_inside_length {
+  $_[0]->{'custom_style'}->{'callout'}->{'inside'} = $_[1];
+}
+
+=item set_callout_leader_outside_length
+
+=cut
+
+sub set_callout_leader_outside_length {
+  $_[0]->{'custom_style'}->{'callout'}->{'outside'} = $_[1];
+}
+
+=item set_callout_leader_length
+
+=cut
+
+sub set_callout_leader_length {
+  $_[0]->{'custom_style'}->{'callout'}->{'leadlen'} = $_[1];
+}
+
+=item set_callout_gap
+
+=cut
+
+sub set_callout_gap {
+  $_[0]->{'custom_style'}->{'callout'}->{'gap'} = $_[1];
+}
+
+=item set_label_font_color
+
+=cut
+
+sub set_label_font_color {
+  $_[0]->{'custom_style'}->{'label'}->{'color'} = $_[1];
+}
+
+=item set_label_font
+
+=cut
+
+sub set_label_font {
+  $_[0]->{'custom_style'}->{'label'}->{'font'} = $_[1];
+}
+
+=item set_label_font_size
+
+=cut
+
+sub set_label_font_size {
+  $_[0]->{'custom_style'}->{'label'}->{'size'} = $_[1];
+}
+
+=item set_drop_shadow_fill_color
+
+=cut
+
+sub set_drop_shadow_fill_color {
+  $_[0]->{'custom_style'}->{'dropshadow'}->{'fill'} = $_[1];
+}
+
+=item set_drop_shadow_offset
+
+=cut
+
+sub set_drop_shadow_offset {
+  $_[0]->{'custom_style'}->{'dropshadow'}->{'off'} = $_[1];
+}
+
+=item set_drop_shadowXOffset
+
+=cut
+
+sub set_drop_shadowXOffset {
+  $_[0]->{'custom_style'}->{'dropshadow'}->{'offx'} = $_[1];
+}
+
+=item set_drop_shadowYOffset
+
+=cut
+
+sub set_drop_shadowYOffset {
+  $_[0]->{'custom_style'}->{'dropshadow'}->{'offy'} = $_[1];
+}
+
+=item set_drop_shadow_filter
+
+=cut
+
+sub set_drop_shadow_filter {
+  $_[0]->{'custom_style'}->{'dropshadow'}->{'filter'} = $_[1];
+}
+
+=item set_outline_color
+
+=cut
+
+sub set_outline_color {
+  $_[0]->{'custom_style'}->{'outline'}->{'line'} = $_[1];
+}
+
+=item set_data_area_fills
+
+=cut
+
+sub set_data_area_fills {
+  $_[0]->{'custom_style'}->{'fills'} = $_[1];
+}
+
+=item set_data_line_colors
+
+=cut
+
+sub set_data_line_colors {
+  $_[0]->{'custom_style'}->{'colors'} = $_[1];
+}
+
+=back
+
 =head1 FEATURES
 
 Each graph type has a number of features.  These are used to add
 various items that are displayed in the graph area.  Some common
-features are:
+methods are:
+
+=over
+
+=item show_legend()
+
+adds a box containing boxes filled with the data filess, with
+the labels provided to the draw method.  The legend will only be
+displayed if both the legend feature is enabled and labels are
+supplied.
+
+=cut
+
+sub show_legend {
+    $_[0]->{'custom_style'}->{'features'}->{'legend'} = 1;
+}
+
+=item show_outline()
+
+draws a border around the data areas.
+
+=cut
+
+sub show_outline {
+    $_[0]->{'custom_style'}->{'features'}->{'outline'} = 1;
+}
+
+=item show_labels()
+
+labels each data fill, usually by including text inside the data fill.
+If the text does not fit in the fill, they could be displayed in some
+other form, eg. as callouts in a pie graph.  There usually isn't much
+point in including both labels and a legend.
+
+=cut
+
+sub show_labels {
+    $_[0]->{'custom_style'}->{'features'}->{'labels'} = 1;
+}
+
+=item show_drop_shadow()
+
+a simple drop shadow is shown behind some of the graph elements.
+
+=cut
+
+sub show_drop_shadow {
+    $_[0]->{'custom_style'}->{'features'}->{'dropshadow'} = 1;
+}
+
+=item reset_features()
+
+Unsets all of the features
+
+=cut
+
+sub reset_features {
+    $_[0]->{'custom_style'}->{'features'} = {};
+    $_[0]->{'custom_style'}->{'features'}->{'reset'} = 1;
+}
+
+=back
+
+Additionally, features can be set by passing them into the draw() method:
 
 =over
 
@@ -542,11 +1079,11 @@ relationships between the colors of various elements, for example the
 default style information contains:
 
    text=>{
-         color=>'lookup(fg)',
+          color=>'lookup(fg)',
           ...
          },
    legend =>{
-            color=>'lookup(text.color)',
+             color=>'lookup(text.color)',
              ...
             },
 
@@ -618,50 +1155,58 @@ my %style_defs =
   (
    back=> 'lookup(bg)',
    line=> 'lookup(fg)',
+   aa => 1,
    text=>{
-         color => 'lookup(fg)',
+          color => 'lookup(fg)',
           font  => 'lookup(font)',
-         size  => 14,
-        },
+          size  => 14,
+         aa    => 'lookup(aa)',
+         },
    title=>{ 
-          color  => 'lookup(text.color)', 
+           color  => 'lookup(text.color)', 
            font   => 'lookup(text.font)',
-          halign => 'center', 
-          valign => 'top',
-          size   => 'scale(text.size,2.0)',
-         },
+           halign => 'center', 
+           valign => 'top',
+           size   => 'scale(text.size,2.0)',
+          aa     => 'lookup(text.aa)',
+          },
    legend =>{
-            color          => 'lookup(text.color)',
+             color          => 'lookup(text.color)',
              font           => 'lookup(text.font)',
-            size           => 'lookup(text.size)',
-            patchsize      => 'scale(legend.size,0.9)',
-            patchgap       => 'scale(legend.patchsize,0.3)',
-            patchborder    => 'lookup(line)',
-            halign         => 'right',
-            valign         => 'top',
-            padding        => 'scale(legend.size,0.3)',
-            outsidepadding => 'scale(legend.padding,0.4)',
-           },
+            aa             => 'lookup(text.aa)',
+             size           => 'lookup(text.size)',
+             patchsize      => 'scale(legend.size,0.9)',
+             patchgap       => 'scale(legend.patchsize,0.3)',
+             patchborder    => 'lookup(line)',
+             halign         => 'right',
+             valign         => 'top',
+             padding        => 'scale(legend.size,0.3)',
+             outsidepadding => 'scale(legend.padding,0.4)',
+            },
    callout => {
-              color    => 'lookup(text.color)',
+               color    => 'lookup(text.color)',
                font     => 'lookup(text.font)',
-              size     => 'lookup(text.size)',
-              line     => 'lookup(line)',
-              inside   => 'lookup(callout.size)',
-              outside  => 'lookup(callout.size)',
-              leadlen  => 'scale(0.8,callout.size)',
-              gap      => 'scale(callout.size,0.3)',
-             },
+               size     => 'lookup(text.size)',
+               line     => 'lookup(line)',
+               inside   => 'lookup(callout.size)',
+               outside  => 'lookup(callout.size)',
+               leadlen  => 'scale(0.8,callout.size)',
+               gap      => 'scale(callout.size,0.3)',
+              aa       => 'lookup(text.aa)',
+              lineaa   => 'lookup(lineaa)',
+              },
    label => {
              font          => 'lookup(text.font)',
-            size          => 'lookup(text.size)',
-            color         => 'lookup(text.color)',
+             size          => 'lookup(text.size)',
+             color         => 'lookup(text.color)',
              hpad          => 'lookup(label.pad)',
              vpad          => 'lookup(label.pad)',
              pad           => 'scale(label.size,0.2)',
              pcformat      => sub { sprintf "%s (%.0f%%)", $_[0], $_[1] },
              pconlyformat  => sub { sprintf "%.1f%%", $_[0] },
-            },
+            aa            => 'lookup(text.aa)',
+            lineaa        => 'lookup(lineaa)',
+             },
    dropshadow => {
                   fill    => { solid => Imager::Color->new(0, 0, 0, 96) },
                   off     => 'scale(0.4,text.size)',
@@ -674,10 +1219,18 @@ my %style_defs =
                  },
    outline => {
                line =>'lookup(line)',
+              lineaa => 'lookup(lineaa)',
               },
    size=>256,
    width=>'scale(1.5,size)',
    height=>'lookup(size)',
+
+   # yes, the handling of fill and line AA is inconsistent, lack of
+   # forethought, unfortunately
+   fill => {
+           aa => 'lookup(aa)',
+          },
+   lineaa => 'lookup(aa)',
   );
 
 =item _error($message)
@@ -731,6 +1284,7 @@ my %styles =
      qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
     ],
     fg=>'000000',
+    negative_bg=>'EEEEEE',
     bg=>'E0E0E0',
     legend=>
     {
@@ -744,6 +1298,7 @@ my %styles =
      qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
     ],
     fg=>'000000',
+    negative_bg=>'EEEEEE',
     bg=>'C08080',
     legend=>
     {
@@ -766,10 +1321,12 @@ my %styles =
     channels=>1,
     bg=>'FFFFFF',
     fg=>'000000',
+    negative_bg=>'EEEEEE',
     features=>{ outline=>1 },
     pie =>{
            blur=>undef,
           },
+    aa => 0,
    },
    fount_lin =>
    {
@@ -778,34 +1335,45 @@ my %styles =
      { fountain=>'linear',
        xa_ratio=>0.13, ya_ratio=>0.13, xb_ratio=>0.87, yb_ratio=>0.87,
        segments => Imager::Fountain->simple(positions=>[0, 1],
-                                           colors=>[ NC('FFC0C0'), NC('FF0000') ]),
+                                            colors=>[ NC('FFC0C0'), NC('FF0000') ]),
      },
      { fountain=>'linear',
        xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
        segments => Imager::Fountain->simple(positions=>[0, 1],
-                                           colors=>[ NC('C0FFC0'), NC('00FF00') ]),
+                                            colors=>[ NC('C0FFC0'), NC('00FF00') ]),
      },
      { fountain=>'linear',
        xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
        segments => Imager::Fountain->simple(positions=>[0, 1],
-                                           colors=>[ NC('C0C0FF'), NC('0000FF') ]),
+                                            colors=>[ NC('C0C0FF'), NC('0000FF') ]),
      },
      { fountain=>'linear',
        xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
        segments => Imager::Fountain->simple(positions=>[0, 1],
-                                           colors=>[ NC('FFFFC0'), NC('FFFF00') ]),
+                                            colors=>[ NC('FFFFC0'), NC('FFFF00') ]),
      },
      { fountain=>'linear',
        xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
        segments => Imager::Fountain->simple(positions=>[0, 1],
-                                           colors=>[ NC('C0FFFF'), NC('00FFFF') ]),
+                                            colors=>[ NC('C0FFFF'), NC('00FFFF') ]),
      },
      { fountain=>'linear',
        xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
        segments => Imager::Fountain->simple(positions=>[0, 1],
-                                           colors=>[ NC('FFC0FF'), NC('FF00FF') ]),
+                                            colors=>[ NC('FFC0FF'), NC('FF00FF') ]),
      },
     ],
+    colors  => [
+     qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
+    ],
+    line_markers =>[
+      { shape => 'circle',   radius => 4 },
+      { shape => 'square',   radius => 4 },
+      { shape => 'diamond',  radius => 4 },
+      { shape => 'triangle', radius => 4 },
+      { shape => 'x',        radius => 4 },
+      { shape => 'plus',     radius => 4 },
+    ],
     back=>{ fountain=>'linear',
             xa_ratio=>0, ya_ratio=>0,
             xb_ratio=>1.0, yb_ratio=>1.0,
@@ -813,6 +1381,7 @@ my %styles =
             ( positions=>[0, 1],
               colors=>[ NC('6060FF'), NC('60FF60') ]) },
     fg=>'000000',
+    negative_bg=>'EEEEEE',
     bg=>'FFFFFF',
     features=>{ dropshadow=>1 },
    },
@@ -823,34 +1392,37 @@ my %styles =
      { fountain=>'radial',
        xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
        segments => Imager::Fountain->simple(positions=>[0, 1],
-                                           colors=>[ NC('FF8080'), NC('FF0000') ]),
+                                            colors=>[ NC('FF8080'), NC('FF0000') ]),
      },
      { fountain=>'radial',
        xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
        segments => Imager::Fountain->simple(positions=>[0, 1],
-                                           colors=>[ NC('80FF80'), NC('00FF00') ]),
+                                            colors=>[ NC('80FF80'), NC('00FF00') ]),
      },
      { fountain=>'radial',
        xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
        segments => Imager::Fountain->simple(positions=>[0, 1],
-                                           colors=>[ NC('808080FF'), NC('0000FF') ]),
+                                            colors=>[ NC('808080FF'), NC('0000FF') ]),
      },
      { fountain=>'radial',
        xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
        segments => Imager::Fountain->simple(positions=>[0, 1],
-                                           colors=>[ NC('FFFF80'), NC('FFFF00') ]),
+                                            colors=>[ NC('FFFF80'), NC('FFFF00') ]),
      },
      { fountain=>'radial',
        xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
        segments => Imager::Fountain->simple(positions=>[0, 1],
-                                           colors=>[ NC('80FFFF'), NC('00FFFF') ]),
+                                            colors=>[ NC('80FFFF'), NC('00FFFF') ]),
      },
      { fountain=>'radial',
        xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
        segments => Imager::Fountain->simple(positions=>[0, 1],
-                                           colors=>[ NC('FF80FF'), NC('FF00FF') ]),
+                                            colors=>[ NC('FF80FF'), NC('FF00FF') ]),
      },
     ],
+    colors  => [
+     qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
+    ],
     back=>{ fountain=>'linear',
             xa_ratio=>0, ya_ratio=>0,
             xb_ratio=>1.0, yb_ratio=>1.0,
@@ -858,10 +1430,89 @@ my %styles =
             ( positions=>[0, 1],
               colors=>[ NC('6060FF'), NC('60FF60') ]) },
     fg=>'000000',
+    negative_bg=>'EEEEEE',
     bg=>'FFFFFF',
    }
   );
 
+$styles{'ocean'} = {
+    fills  => [
+             {
+              fountain =>'linear',
+              xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
+              segments => Imager::Fountain->simple(
+                                                    positions=>[0, 1],
+                                                    colors=>[ NC('EFEDCF'), NC('E6E2AF') ]),
+            },
+             {
+              fountain =>'linear',
+              xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
+              segments => Imager::Fountain->simple(
+                                                    positions=>[0, 1],
+                                                    colors=>[ NC('DCD7AB'), NC('A7A37E') ]),
+            },
+             {
+              fountain =>'linear',
+              xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
+              segments => Imager::Fountain->simple(
+                                                    positions=>[0, 1],
+                                                    colors=>[ NC('B2E5D4'), NC('80B4A2') ]),
+            },
+            {
+              fountain =>'linear',
+              xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
+              segments => Imager::Fountain->simple(
+                                                    positions=>[0, 1],
+                                                    colors=>[ NC('7aaab9'), NC('046380') ]),
+            },
+            {
+              fountain =>'linear',
+              xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
+              segments => Imager::Fountain->simple(
+                                                    positions=>[0, 1],
+                                                    colors=>[ NC('c3b8e9'), NC('877EA7') ]),
+            },
+            {
+              fountain =>'linear',
+              xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
+              segments => Imager::Fountain->simple(
+                                                    positions=>[0, 1],
+                                                    colors=>[ NC('A3DF9A'), NC('67A35E') ]),
+            },
+            {
+              fountain =>'linear',
+              xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
+              segments => Imager::Fountain->simple(
+                                                    positions=>[0, 1],
+                                                    colors=>[ NC('E19C98'), NC('B4726F') ]),
+            },
+    ],
+    colors  => [
+     qw(E6E2AF A7A37E 80B4A2 046380 877EA7 67A35E B4726F)
+    ],
+    fg=>'000000',
+    negative_bg=>'EEEEEE',
+    bg=>'FFFFFF',
+    features=>{ dropshadow=>1 },
+
+};
+
+$styles{'ocean_flat'} = {
+    fills=>
+    [
+     qw(E6E2AF A7A37E 80B4A2 046380 877EA7 67A35E B4726F)
+    ],
+    colors  => [
+     qw(E6E2AF A7A37E 80B4A2 046380 877EA7 67A35E B4726F)
+    ],
+    fg=>'000000',
+    negative_bg=>'EEEEEE',
+    bg=>'FFFFFF',
+    features=>{ dropshadow=>1 },
+
+};
+
+
 =item $self->_style_setup(\%opts)
 
 Uses the values from %opts to build a customized hash describing the
@@ -873,10 +1524,14 @@ sub _style_setup {
   my ($self, $opts) = @_;
   my $style_defs = $self->_style_defs;
   my $style;
-  $style = $styles{$opts->{style}} if $opts->{style};
+
+  my $pre_def_style = $self->_get_style($opts);
+  my $api_style = $self->{'custom_style'} || {};
+  $style = $styles{$pre_def_style} if $pre_def_style;
+
   $style ||= $styles{$def_style};
 
-  my @search_list = ( $style_defs, $style, $opts);
+  my @search_list = ( $style_defs, $style, $api_style, $opts);
   my %work;
 
   my @composite = $self->_composite();
@@ -897,7 +1552,8 @@ sub _style_setup {
         }
       }
       else {
-       $work{$key} = $src->{$key};
+        $work{$key} = $src->{$key}
+         if defined $src->{$key}; # $opts with pmichauds new accessor handling
       }
     }
   }
@@ -1011,23 +1667,23 @@ sub _get_number {
   else {
     if ($what =~ /^lookup\(([\w.]+)\)$/) {
       @depth < MAX_DEPTH
-       or return $self->_error("too many levels of recursion in lookup (@depth)");
+        or return $self->_error("too many levels of recursion in lookup (@depth)");
       return $self->_get_number($1, @depth);
     }
     elsif ($what =~ /^scale\(
-                   ((?:[a-z][\w.]*)|$NUM_RE)
+                    ((?:[a-z][\w.]*)|$NUM_RE)
                     ,
-                   ((?:[a-z][\w.]*)|$NUM_RE)\)$/x) {
+                    ((?:[a-z][\w.]*)|$NUM_RE)\)$/x) {
       my ($left, $right) = ($1, $2);
       unless ($left =~ /^$NUM_RE$/) {
-       @depth < MAX_DEPTH 
-         or return $self->_error("too many levels of recursion in scale (@depth)");
-       $left = $self->_get_number($left, @depth);
+        @depth < MAX_DEPTH 
+          or return $self->_error("too many levels of recursion in scale (@depth)");
+        $left = $self->_get_number($left, @depth);
       }
       unless ($right =~ /^$NUM_RE$/) {
-       @depth < MAX_DEPTH 
-         or return $self->_error("too many levels of recursion in scale (@depth)");
-       $right = $self->_get_number($right, @depth);
+        @depth < MAX_DEPTH 
+          or return $self->_error("too many levels of recursion in scale (@depth)");
+        $right = $self->_get_number($right, @depth);
       }
       return $left * $right;
     }
@@ -1085,7 +1741,7 @@ sub _get_color {
   unless (ref $what) {
     if ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
       @depth < MAX_DEPTH or
-       return $self->_error("too many levels of recursion in lookup (@depth)");
+        return $self->_error("too many levels of recursion in lookup (@depth)");
 
       return $self->_get_color($1, @depth);
     }
@@ -1121,33 +1777,33 @@ sub _translate_fill {
       # default to normal combine mode
       my %work = ( combine => 'normal', %$what );
       if ($what->{hatch}) {
-       if (!$work{fg}) {
-         $work{fg} = $self->_get_color('fg')
-           or return;
-       }
-       if (!$work{bg}) {
-         $work{bg} = $self->_get_color('bg')
-           or return;
-       }
-       return ( fill=>\%work );
+        if (!$work{fg}) {
+          $work{fg} = $self->_get_color('fg')
+            or return;
+        }
+        if (!$work{bg}) {
+          $work{bg} = $self->_get_color('bg')
+            or return;
+        }
+        return ( fill=>\%work );
       }
       elsif ($what->{fountain}) {
-       for my $key (qw(xa ya xb yb)) {
-         if (exists $work{"${key}_ratio"}) {
-           if ($key =~ /^x/) {
-             $work{$key} = $box->[0] + $work{"${key}_ratio"} 
-               * ($box->[2] - $box->[0]);
-           }
-           else {
-             $work{$key} = $box->[1] + $work{"${key}_ratio"} 
-               * ($box->[3] - $box->[1]);
-           }
-         }
-       }
-       return ( fill=>\%work );
+        for my $key (qw(xa ya xb yb)) {
+          if (exists $work{"${key}_ratio"}) {
+            if ($key =~ /^x/) {
+              $work{$key} = $box->[0] + $work{"${key}_ratio"} 
+                * ($box->[2] - $box->[0]);
+            }
+            else {
+              $work{$key} = $box->[1] + $work{"${key}_ratio"} 
+                * ($box->[3] - $box->[1]);
+            }
+          }
+        }
+        return ( fill=>\%work );
       }
       else {
-       return ( fill=> \%work );
+        return ( fill=> \%work );
       }
     }
   }
@@ -1176,6 +1832,25 @@ sub _data_fill {
                                 "data.$index");
 }
 
+sub _data_color {
+  my ($self, $index) = @_;
+
+  my $colors = $self->{'_style'}{'colors'} || [];
+  my $fills  = $self->{'_style'}{'fills'} || [];
+
+  # Try to just use a fill, so non-fountain styles don't need
+  # to have a duplicated set of fills and colors
+  my $fill = $fills->[$index % @$fills];
+  if (!ref $fill) {
+    return $fill;
+  }
+
+  if (@$colors) {
+    return $colors->[$index % @$colors] || '000000';
+  }
+  return '000000';
+}
+
 =item _get_fill($index, $box)
 
 Retrieves fill parameters for a named fill.
@@ -1223,6 +1898,15 @@ sub _make_img {
   $img;
 }
 
+sub _get_image {
+  my $self = shift;
+
+  if (!$self->{'_image'}) {
+    $self->{'_image'} = $self->_make_img();
+  }
+  return $self->{'_image'};
+}
+
 =item _text_style($name)
 
 Returns parameters suitable for calls to Imager::Font's bounding_box()
@@ -1230,6 +1914,9 @@ and draw() methods intended for use in defining text styles.
 
 Returns an empty list on failure.
 
+Returns the following attributes: font, color, size, aa, sizew
+(optionally)
+
 =cut
 
 sub _text_style {
@@ -1257,6 +1944,7 @@ sub _text_style {
   $work{size} = $self->_get_number("$name.size");
   $work{sizew} = $self->_get_number("$name.sizew")
     if $work{sizew};
+  $work{aa} = $self->_get_number("$name.aa");
 
   %work;
 }
@@ -1276,11 +1964,33 @@ sub _text_bbox {
     or return;
 
   my @bbox = $text_info{font}->bounding_box(%text_info, string=>$text,
-                                           canon=>1);
+                                            canon=>1);
 
   return @bbox[0..3];
 }
 
+=item _line_style($name)
+
+Return parameters suitable for calls to Imager's line(), polyline(),
+and box() methods.
+
+For now this returns only color and aa parameters, but future releases
+of Imager may support extra parameters.
+
+=cut
+
+sub _line_style {
+  my ($self, $name) = @_;
+
+  my %line;
+  $line{color} = $self->_get_color("$name.line")
+    or return;
+  $line{aa} = $self->_get_number("$name.lineaa");
+  defined $line{aa} or $line{aa} = $self->_get_number("aa");
+
+  return %line;
+}
+
 sub _align_box {
   my ($self, $box, $chart_box, $name) = @_;
 
@@ -1344,7 +2054,7 @@ sub _remove_box {
 
   if ($areay < $areax) {
     if ($object_box->[1] - $chart_box->[1] 
-       < $chart_box->[3] - $object_box->[3]) {
+        < $chart_box->[3] - $object_box->[3]) {
       $chart_box->[1] = $object_box->[3];
     }
     else {
@@ -1353,7 +2063,7 @@ sub _remove_box {
   }
   else {
     if ($object_box->[0] - $chart_box->[0] 
-       < $chart_box->[2] - $object_box->[2]) {
+        < $chart_box->[2] - $object_box->[2]) {
       $chart_box->[0] = $object_box->[2];
     }
     else {
@@ -1406,8 +2116,8 @@ sub _draw_legend_horizontal {
     }
     else {
       if ($pos + $gap + $entry_width > $chart_box->[2]) {
-       $pos = 0;
-       $height += $row_height;
+        $pos = 0;
+        $height += $row_height;
       }
       push @offsets, [ $pos, $height ];
     }
@@ -1434,7 +2144,7 @@ sub _draw_legend_horizontal {
               ymin=>$box[1]+$outsidepadding, 
               xmax=>$box[2]-$outsidepadding, 
               ymax=>$box[3]-$outsidepadding,
-            $self->_get_fill('legend.fill', \@box));
+             $self->_get_fill('legend.fill', \@box));
   }
   $box[0] += $outsidepadding;
   $box[1] += $outsidepadding;
@@ -1459,11 +2169,11 @@ sub _draw_legend_horizontal {
     my @fill = $self->_data_fill($dataindex, \@patchbox)
       or return;
     $img->box(xmin=>$left, ymin=>$top, xmax=>$left + $patchsize,
-              ymax=>$top + $patchsize, @fill);
+               ymax=>$top + $patchsize, @fill);
     if ($self->{_style}{legend}{patchborder}) {
       $img->box(xmin=>$left, ymin=>$top, xmax=>$left + $patchsize,
-               ymax=>$top + $patchsize,
-               color=>$patchborder);
+                ymax=>$top + $patchsize,
+                color=>$patchborder);
     }
     $img->string(%text_info, x=>$textpos, 'y'=>$top + $patchsize, 
                  text=>$label);
@@ -1474,7 +2184,7 @@ sub _draw_legend_horizontal {
     my $border_color = $self->_get_color('legend.border')
       or return;
     $img->box(xmin=>$box[0], ymin=>$box[1], xmax=>$box[2], ymax=>$box[3],
-             color=>$border_color);
+              color=>$border_color);
   }
   $self->_remove_box($chart_box, \@box);
   1;
@@ -1505,8 +2215,8 @@ sub _draw_legend_vertical {
     }
   }
   my @box = (0, 0, 
-            $width + $patchsize + $padding * 2 + $gap,
-            $height + $padding * 2 - $gap);
+             $width + $patchsize + $padding * 2 + $gap,
+             $height + $padding * 2 - $gap);
   my $outsidepadding = 0;
   if ($self->{_style}{legend}{border}) {
     defined($outsidepadding = $self->_get_integer('legend.outsidepadding'))
@@ -1521,7 +2231,7 @@ sub _draw_legend_vertical {
               ymin=>$box[1]+$outsidepadding, 
               xmax=>$box[2]-$outsidepadding, 
               ymax=>$box[3]-$outsidepadding,
-            $self->_get_fill('legend.fill', \@box));
+             $self->_get_fill('legend.fill', \@box));
   }
   $box[0] += $outsidepadding;
   $box[1] += $outsidepadding;
@@ -1541,14 +2251,21 @@ sub _draw_legend_vertical {
   for my $label (@$labels) {
     my @patchbox = ( $patchpos - $patchsize/2, $ypos - $patchsize/2,
                      $patchpos + $patchsize * 3 / 2, $ypos + $patchsize*3/2 );
-    my @fill = $self->_data_fill($dataindex, \@patchbox)
-      or return;
+
+    my @fill;
+    if ($self->_draw_flat_legend()) {
+      @fill = (color => $self->_data_color($dataindex), filled => 1);
+    }
+    else {
+      @fill = $self->_data_fill($dataindex, \@patchbox)
+        or return;
+    }
     $img->box(xmin=>$patchpos, ymin=>$ypos, xmax=>$patchpos + $patchsize,
-              ymax=>$ypos + $patchsize, @fill);
+               ymax=>$ypos + $patchsize, @fill);
     if ($self->{_style}{legend}{patchborder}) {
       $img->box(xmin=>$patchpos, ymin=>$ypos, xmax=>$patchpos + $patchsize,
-               ymax=>$ypos + $patchsize,
-               color=>$patchborder);
+                ymax=>$ypos + $patchsize,
+                color=>$patchborder);
     }
     $img->string(%text_info, x=>$textpos, 'y'=>$ypos + $patchsize, 
                  text=>$label);
@@ -1566,7 +2283,7 @@ sub _draw_legend_vertical {
     my $border_color = $self->_get_color('legend.border')
       or return;
     $img->box(xmin=>$box[0], ymin=>$box[1], xmax=>$box[2], ymax=>$box[3],
-             color=>$border_color);
+              color=>$border_color);
   }
   $self->_remove_box($chart_box, \@box);
   1;
@@ -1599,6 +2316,10 @@ sub _small_extent {
   }
 }
 
+sub _draw_flat_legend {
+  return 0;
+}
+
 =item _composite()
 
 Returns a list of style fields that are stored as composites, and