HiPi
Perl Modules for Raspberry Pi
Version 0.92 - released 28 March 2024

HiPi::Graphics::DrawingContext

A drawing context provides an extended method of writing to a HiPi::Interface::MonoOLED or a HiPi::Interface::EPaper buffer.

Creation and use

use HiPi qw( :oled :epaper );
use HiPi::Interface::MonoOLED;
my $oled = HiPi::Interface::MonoOLED->new(
    type => SSD1306_128_X_64_I2C
);

my $ctx = $oled->create_context;

..... # draw on context, then

$oled->draw_context( $x, $y, $ctx );

use HiPi::Interface::EPaper;
my $epd = HiPi::Interface::EPaper->new(
    type => EPD_WS_2_13_250_X_122_A
);

my $ctx2 = $epd->create_context;

..... # draw on context, then

$epd->draw_context( $x, $y, $ctx2 );
            

Examples

Methods

The module writes all drawing operations to a local context that you write to the buffer using draw_context. You can clear all pixels set in the context using clear_context.

$ctx->clear_context();

Set invert_pen to set pixels you write using the drawing methods to 'off' rather than on. This is useful to draw items as 'off' over areas you may have previously set 'on'

# draw a filled rectangle
$ctx->draw_rectangle(10,20, 60, 40, 1);
$ctx->invert_pen(1);
# write some text over the rectangle
$ctx->draw_text(12, 25, 'Hello World', 'Sans12');
# restore normal pen
$ctx->invert_pen(0);

Draw an arc.

  • $x,$y is the origin of the arc
  • $rw and $rh are the horizontal ( $rw - width ) and vertical ( $rh - height ) radius to use when drawing the arc. If $rw and $rh are equal, you will draw the arc of a circle
  • $start and $end denote the start and end of the arc in degrees. See the diagram below
  • $join is optional. If specified ( $join == 1 ) lines are drawn from the origin to the end points of the arc
  • $fill is optional. If specified ( $fill == 1 ) the arc is filled.

# draw 20 degree arc of a circle at x = 63, y = 15, 10 pixel radius
$ctx->draw_arc(63, 15, 10, 10, 350, 10);

# same arc, but draw lines from origin to edges
$ctx->draw_arc(63, 15, 10, 10, 350, 10, 1);

# same arc, but filled
$ctx->draw_arc(63, 15, 10, 10, 350, 10, 0, 1);

Draw an array of 1 / 0 values as a bitmap.

  • $x, $y is the origin to draw at
  • $bitarray is a reference to an array defining the points
  • $fill is optional but if specified, bits that are 0 in the array will be explicitly written to the buffer. If fill is not specified, only bits that are 1 are explicitly written. Using $fill is usefull if you want to overwrite an area of the buffer that has previously been written to.

# Draw a 'raspberry' starting at x = 10, y = 15
my $bitarray =  [ 
    [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
    [ 0, 0, 0, 1, 1, 1, 1, 0, 0, 0,   0, 0, 0, 1, 1, 1, 1, 0, 0, 0 ],
    [ 0, 0, 1, 0, 0, 0, 0, 1, 0, 0,   0, 0, 1, 0, 0, 0, 0, 1, 0, 0 ],
    [ 0, 0, 1, 0, 1, 1, 0, 0, 1, 0,   0, 1, 0, 0, 1, 1, 0, 1, 0, 0 ],
    [ 0, 0, 1, 0, 0, 0, 1, 0, 1, 0,   0, 1, 0, 1, 0, 0, 0, 1, 0, 0 ],
    
    [ 0, 0, 0, 1, 0, 0, 0, 0, 0, 1,   1, 0, 0, 0, 0, 0, 1, 0, 0, 0 ],
    [ 0, 0, 0, 0, 1, 1, 1, 0, 0, 0,   0, 0, 0, 1, 1, 1, 0, 0, 0, 0 ],
    [ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,   0, 0, 0, 1, 0, 0, 0, 0, 0, 0 ],
    [ 0, 0, 0, 0, 1, 1, 0, 0, 0, 1,   1, 0, 0, 0, 1, 1, 0, 0, 0, 0 ],
    [ 0, 0, 0, 1, 0, 0, 0, 0, 1, 0,   0, 1, 0, 0, 0, 0, 1, 0, 0, 0 ],
    
    [ 0, 0, 1, 0, 0, 1, 1, 0, 0, 1,   1, 0, 0, 1, 1, 0, 0, 1, 0, 0 ],
    [ 0, 0, 1, 0, 1, 0, 0, 1, 0, 0,   0, 0, 1, 0, 0, 1, 0, 1, 0, 0 ],
    [ 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,   0, 0, 1, 0, 0, 1, 0, 0, 1, 0 ],
    [ 0, 1, 0, 0, 0, 1, 1, 0, 0, 1,   1, 0, 0, 1, 1, 0, 0, 0, 1, 0 ],
    [ 0, 1, 0, 0, 0, 0, 0, 0, 1, 0,   0, 1, 0, 0, 0, 0, 0, 0, 1, 0 ],
    
    [ 0, 1, 0, 0, 1, 1, 0, 0, 1, 0,   0, 1, 0, 0, 1, 1, 0, 0, 1, 0 ],
    [ 0, 0, 1, 0, 1, 0, 1, 0, 0, 1,   1, 0, 0, 1, 0, 1, 0, 1, 0, 0 ],
    [ 0, 0, 1, 0, 0, 1, 1, 0, 0, 0,   0, 0, 0, 1, 1, 0, 0, 1, 0, 0 ],
    [ 0, 0, 0, 1, 0, 0, 0, 0, 0, 1,   1, 0, 0, 0, 0, 0, 1, 0, 0, 0 ],
    [ 0, 0, 0, 0, 1, 1, 0, 0, 1, 0,   0, 1, 0, 0, 1, 1, 0, 0, 0, 0 ],
    
    [ 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,   0, 0, 1, 1, 0, 0, 0, 0, 0, 0 ],
    [ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,   1, 1, 0, 0, 0, 0, 0, 0, 0, 0 ],
    [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
];

$ctx->draw_bit_array( 10, 15, $bitarray );

Draw a circle at origin $x $y using radius $r. If $fill is specified, fill the circle

$ctx->draw_circle( 15, 15, 10 );

Draw an ellipse.

  • $x,$y is the origin of the ellipse
  • $rw and $rh are the horizontal ( $rw - width ) and vertical ( $rh - height ) radius to use when drawing the $ellipse. If $rw and $rh are equal, you will draw a circle
  • $fill is optional. If specified ( $fill == 1 ) the ellipse is filled.

$ctx->draw_ellipse(20, 15, 10, 7);

Draw a line between $x1,$y1 and $x2,$y2. $endpoint denotes whether the pixel at $x2,$y2 should be written. The default value for $endpoint is 1 ( true ).

$ctx->draw_line( 0, 0, 127, 31);

Set a single pixel to 1 (on) or 0 (off).

$x, $y define the pixel to write. $on can be 1 or 0 ( default 1 )

# set pixel at 0,0 to 'on'
$ctx->draw_pixel( 0, 0 );
            
# set same pixel to 'off'
$ctx->draw_pixel( 0, 0, 0 );

Draw a polygon

  • $vertices is a reference to an array of points representing the vertices of the polygon. If the final point is not equal to the first point, the polygon will be closed anyway.
  • $fill may be optionally set to fill the polygon.
$ctx->draw_polygon( [ [41,32],[63,60],[84,32],[78,53],[46,53] ] );
# draw it filled
$ctx->draw_polygon( [ [41,32],[63,60],[84,32],[78,53],[46,53] ], 1 );

Draw a rectangle

  • $x1, $y1 is the top left of the rectangle
  • $x2, $y2 is the bottom right of the rectangle
  • $fill can be optionally be specified ( 1 ) to fill the rectangle

$ctx->draw_rectangle( 10, 15, 30, 20 );

Draw a rectangle with rounded corners

  • $x1, $y1 is the top left of the rectangle
  • $x2, $y2 is the bottom right of the rectangle
  • $r is the radius to use for the arc that will form the rounded corners. The default value is 4.
  • $fill can be optionally be specified ( 1 ) to fill the rectangle

$ctx->draw_rounded_rectangle( 10, 15, 30, 20, 5 );

Draw text to the context

  • $x, $y is the origin for drawing the text. The origin is in the top left corner of the text.
  • $text is the text string to be written
  • $font can either be a string naming one of the builtin fonts, or a reference to a font object. The default font is Mono10

The method returns the width and height of the text as written to the context

my $y = 0;
my( $w, $h ) = $ctx->draw_text(0, $y, 'Hello World', 'Sans12');
$y += $h;
( $w, $h ) = $ctx->draw_text(0, $y, 'Raspberry Pi', 'Sans12');
$y += $h;
( $w, $h ) = $ctx->draw_text(0, $y, 'HiPi Perl', 'Sans12');

Several builtin fonts are provided.

The standard fonts include the printable ascii characters and the 'degree' character ( U+00B0 )

  • Sans10, Sans12, Sans14, Sans15, Sans19, Sans20, Sans26, Sans33
  • Mono10, Mono12, Mono14, Mono15, Mono19, Mono20, Mono26, Mono33
  • Serif10, Serif12, Serif14, Serif15, Serif19, Serif20, Serif26, Serif33

There are also extended fonts that include all the Latin-1 Supplement characters

  • SansExtended11, SansExtended13, SansExtended15, SansExtended17, SansExtended21, SansExtended23, SansExtended30, SansExtended38
  • MonoExtended11, MonoExtended13, MonoExtended15, MonoExtended17, MonoExtended21, MonoExtended23, MonoExtended30, MonoExtended38
  • SerifExtended11, SerifExtended13, SerifExtended15, SerifExtended17, SerifExtended21, SerifExtended23, SerifExtended30, SerifExtended38

EPD fonts created at a higher DPI are also included intended for use with E-Paper displays but you may use any of the bitmap fonts with both OLED and EPaper displays. THE EPD fonts include all the Latin-1 Supplement characters.

  • SansEPD15, SansEPD19, SansEPD23, SansEPD28, SansEPD31, SansEPD38, SansEPD50, SansEPD76, SansEPD102
  • MonoEPD15, MonoEPD19, MonoEPD23, MonoEPD28, MonoEPD31, MonoEPD38, MonoEPD50, MonoEPD76, MonoEPD102
  • SerifEPD15, SerifEPD19, SerifEPD23, SerifEPD28, SerifEPD31, SerifEPD38, SerifEPD50, SerifEPD76, SansEPD102

The fonts are derived from the Bitstream Vera family.

It is expected that many users will need characters outside the builtin range. HiPi includes tools to create your own bitmapped fonts in the same way the builtins were created.

See: Creating Bitmap Fonts

Get the width and height of the text, without writing to the context. This can be helpful to work out where to position the text.

# Write the text to the centre of the OLED
my( $w, $h ) = $ctx->get_text_extents( 'Raspberry Pi', 'Sans12' );
# centre of text
my $x = int(0.5 + ($oled->cols - $w) / 2);
my $y = int(0.5 + ($oled->rows - $h) / 2);
$ctx->draw_text( $x, $y, 'Raspberry Pi', 'Sans12' ); 

Rotates all of the points in the context about $x, $y by $deg degrees. Rotation direction is as shown in the graphic below

The rotate method returns the rotated context. It also rotates the context in place.

To return a new rotated context leaving the existing context unchanged, use rotated_context

Note that results for rotations that are not multiples of 90 degrees may vary.

$ctx->rotate( 90, 5, 5 );

Parameters and results are the same as for 'rotate' except that rotated_context returns a new context leaving the existing context unchanged.

$oled->draw_context(17,12, $ctx->rotated_context( 90, 10, 10 ) );

Examples

Rotated Text

use HiPi qw( :oled :rpi );
use HiPi::Interface::MonoOLED;

my $oled = HiPi::Interface::MonoOLED->new(
    type      => SH1106_128_X_64_SPI,
    reset_pin => RPI_PIN_40,
    dc_pin    => RPI_PIN_38,
    flipped   => 1,
);
    
$oled->clear_buffer;

my $ctx = $oled->create_context;

my( $w, $h) = $ctx->draw_text(0,0,'Raspberry Pi', 'Sans14');

# Draw all raspberrys & perls in centre

# centre of text
my $cx = int( 0.5 + $w / 2);
my $cy = int( 0.5 + $h / 2);

# draw top line centered
{
    my $x = int(0.5 + ($oled->cols - $w) / 2);
    my $y = 0;
    $oled->draw_context( $x, $y, $ctx->rotated_context( 0, 0, 0) );
}

# draw bottom line rotated through 180 about its centre
{
    my $x = int(0.5 + ($oled->cols - $w) / 2);
    my $y = $oled->rows - $h -1;
    $oled->draw_context( $x, $y, $ctx->rotated_context( 180, $cx, $cy) );
}

$ctx->clear_context;

( $w, $h) = $ctx->draw_text(0,0,'Perl', 'Sans14');
$cx = int( 0.5 + $w / 2);
$cy = int( 0.5 + $h / 2);

# Perl right
{
    my $x = $oled->cols -1;
    my $y = int( 0.5 + ($oled->rows - $w) / 2);
    $oled->draw_context( $x, $y, $ctx->rotated_context( 90, 0, 0 ) );
}

# Perl left
{
    my $x = 0;
    my $y = int( 0.5 + ($w + $oled->rows) / 2);
    $oled->draw_context( $x, $y, $ctx->rotated_context( -90, 0, 0 ) );
}