An Illustration of Perl Objects with C APIs

To illustrate the use of C API's with Perl objects, a toy PPM-format image class is created and then extended (ToyPPM.pm and ExtendedToyPPM.pm). This child class runs just as fast as if it were part of a big C-language extension library, instead of being small and independent, depending only on ToyPPM.pm having provided a C API, and on Inline::C, to make use of it.

The image data is maintained in the PPM string itself, as sequence of (height x width x 3) one-byte integer color samples. These samples are exposed as a seemingly normal perl array, implemented by a Tie::Array/perltie-based helper subclass, created from a simple specification, by a generator of array-like classes. This sample array is then "folded" into an apparently normal 3D array of array of arrays, to provide a very natural interface to the image data (though a quite slow one).

ToyPPM.pm also provides a C API, namely a method which returns a string of C code. It defines macros which obtain a pointer to the string's memory, and provide direct and rapid access to the image bytes. This method was created by the same generator, also from a simple specification.

ExtendedToyPPM.pm takes this string of C code, which provides macros for accessing the image data, and combines it with a reference implementation of a line walker, to create the C code for a line drawer. This is then handed to Inline::C, creating a method draw_line(). Which runs just as fast as if it had been hand coded as part of an image library. Without having to sacrifice object-oriented-ness.

    Up

ToyPPM.pm
ExtendedToyPPM.pm

ToyDrawLine.pm

Utility classes:
 ToyArrayFold.pm
 ToyDefineArrayMethods.pm

Generated code:
 ToyPPM::Samples (clean perl)
 ToyPPM::get_toy_api_...
 draw_line()
 draw_line() after preprocessing

Source code:
 illustration.tar

This

   $image = ToyPPM->new_from_ppm_data("P6 2 2 255 RgbRgbRgbRgb");
creates a 2 by 2 pixel RGB image (a greenish one). The string itself is used to store the pixels.

A ToyPPM object looks both like a string,
and like a normal perl 3D array (ie, an array of arrays of arrays).

  SYNOPSIS
            use ToyPPM;

            $image = ToyPPM->new_from_ppm_data(`cat some.ppm`);
            $image = ToyPPM->new_from_ppm_data("P6 2 2 255 RgbRgbRgbRgb");
            print OUT $image;

            @rows = @{$image};

            # The rest is just normal array manipulation...

            @rgb_of_pixel_0_0 = @{$image->[0][0]};
            @{$image->[0][0]} = (255,0,0); # red

            $lower_right_blue_value = $image->[1][1][2];
            $image->[1][1][2] = 255;

            $pixel01 = $image->[0][1];
            $pixel01->[1] = $new_green;

            $width = @{$image->[0]}; # scalar context
Simple.

It also defines a

            $c_code = $image->get_toy_api_ImageCMacros();
which returns a C api
      #define DECL(image_object)  ...
      #define INIT(image_object)  ...
      #define HEIGHT()            ...
      #define GET_R(x,y)          ...
      #define SET_R(x,y,r)        ...
      #define SET_RGB(x,y, r,g,b) ...
      ...etc...

With this, ExtendedToyPPM.pm extends ToyPPM with a draw_line() method.
Which is written in C, and is very, very fast.

Note that

Punchline: At the cost of dealing with C API's and Inline::C, one can get the performance of a big monolithic C extension, while using only normal object-oriented practice. Objects which are nicely small, orthogonal, mixable, inheritable, and independent. With all the project- and community- level benefits which follow.

Comments encouraged - Mitchell Charity <[email protected]>

 

Notes

The ToyDrawLine api is a C macro. There are more flexible options, but they cost an extra line or three in ExtendedToyPPM. So this was mostly a pedagogical choice. I'm not sure it was the right one.

This toy draw_line() will explode if given off-image coordinates. Normally this is handled by the generated array class, but I yanked it to shorten ToyDefineArrayMethods. I could add it to draw_line(), but it doesn't belong there. So... that is what happened to safety.

This ToyArrayFold utility class was just a quick hack. So this "$image->[0][0] = [255,0,0];" doesn't work. Nor do push(), pop(), etc. If there is interest, I can dust off a more complete one.

Regards the PPM format, here is a manpage, another, an overview, and the minimalist netpbm homepage (netpbm replaced the old pbmplus). The toy doesn't handle two-byte samples, nor all the places comments can occur.

This toy code was created solely for this illustration. It was derived, with substantial changes, from a pre-alpha code base. It has not been tested. It follows that it must have a surfeit of bugs, in addition to the many misfeatures knowingly introduced. Just so you know. Please let me know if there is interest.


Comments encouraged - Mitchell Charity <[email protected]>

Notes:
  Perl source formating was provided by Perltidy.  C formatting by enscript.
  

Doables:

History:
  2002-Apr-09  Online.