Bitmap

From Rosetta Code
Revision as of 00:55, 6 December 2008 by rosettacode>Blue Prawn (initial version)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Task
Bitmap
You are encouraged to solve this task according to the task description, using any language you may know.

Show a basic storage type to handle a simple RGB raster graphics image, and some primitive associated functions.

If possible provide a function to allocate an uninitialised image, given its width and height. And provide 3 additional functions:

  • one to fill an image with a plain RGB color,
  • one to set a given pixel with a color,
  • one to get the color of a pixel.

(If there are specificities about the storage or the allocation, explain those.)

OCaml

<ocaml>let new_img ~width ~height =

 let all_channels =
   let kind = Bigarray.int8_unsigned
   and layout = Bigarray.c_layout
   in
   Bigarray.Array3.create kind layout 3 width height
 in
 let r_channel = Bigarray.Array3.slice_left_2 all_channels 0
 and g_channel = Bigarray.Array3.slice_left_2 all_channels 1
 and b_channel = Bigarray.Array3.slice_left_2 all_channels 2
 in
 (all_channels,
  r_channel,
  g_channel,
  b_channel)</ocaml>

and here is the type of the raster image this function returns:

type raster =
  (int, Bigarray.int8_unsigned_elt, Bigarray.c_layout) Bigarray.Array3.t *
  (int, Bigarray.int8_unsigned_elt, Bigarray.c_layout) Bigarray.Array2.t *
  (int, Bigarray.int8_unsigned_elt, Bigarray.c_layout) Bigarray.Array2.t *
  (int, Bigarray.int8_unsigned_elt, Bigarray.c_layout) Bigarray.Array2.t

What is particular with this allocation and its associated type is that there is not only one buffer for each RGB channel, but also an additionnal one that handles all the three channels, and what is important here is that it is not additionnal memory, the memory is shared, so there are 2 ways to access the raster buffer: through the separated RGB channels, or through the joint channel (all_channels).

This solution have a lot of advantages across a more naive one: this types is compatible to memory-map a file (a ppm file for instance, where the data is not compressed), the buffer can be shared/exchanged with C (for OpenGL textures for instance), etc.

A more naive form would be this one:

<ocaml>let new_img ~width ~height =

 let r_channel, g_channel, b_channel =
   let kind = Bigarray.int8_unsigned
   and layout = Bigarray.c_layout
   in
   (Bigarray.Array2.create kind layout width height,
    Bigarray.Array2.create kind layout width height,
    Bigarray.Array2.create kind layout width height)
 in
 (r_channel,
  g_channel,
  b_channel)</ocaml>

Here are the functions to fill with a color and to set one given pixel:

<ocaml>let fill_img ~img:(_, r_channel, g_channel, b_channel) ~color:(r,g,b) =

 Bigarray.Array2.fill r_channel r;
 Bigarray.Array2.fill g_channel g;
 Bigarray.Array2.fill b_channel b;
</ocaml>


<ocaml>let put_pixel_unsafe (_, r_channel, g_channel, b_channel) (r,g,b) =

 (fun x y ->
   r_channel.{x,y} <- r;
   g_channel.{x,y} <- g;
   b_channel.{x,y} <- b;
 )

let put_pixel ~img ~color:(r,g,b) ~pt:(x, y) =

 let width = Bigarray.Array2.dim1 r_channel
 and height = Bigarray.Array2.dim2 r_channel in
 if (x < 0) || (x >= width) then invalid_arg "x out of bounds";
 if (y < 0) || (y >= height) then invalid_arg "y out of bounds";
 if (r < 0) || (r > 255) then invalid_arg "red out of bounds";
 if (g < 0) || (g > 255) then invalid_arg "green out of bounds";
 if (b < 0) || (b > 255) then invalid_arg "blue out of bounds";
 put_pixel_unsafe img color x y;
</ocaml>


<ocaml>let get_pixel_unsafe (_, r_channel, g_channel, b_channel) =

 (fun x y ->
   (r_channel.{x,y},
    g_channel.{x,y},
    b_channel.{x,y})
 )

let get_pixel ~img ~pt:(x, y) =

 let width = Bigarray.Array2.dim1 r_channel
 and height = Bigarray.Array2.dim2 r_channel in
 if (x < 0) || (x >= width) then invalid_arg "x out of bounds";
 if (y < 0) || (y >= height) then invalid_arg "y out of bounds";
 get_pixel_unsafe img x y;
</ocaml>