(*------------------------------------------------------------------*)
#define ATS_DYNLOADFLAG 0
#define ATS_PACKNAME "Rosetta_Code.bitmap_task"
#include "share/atspre_staload.hats"
staload "bitmap_task.sats"
(*------------------------------------------------------------------*)
(* The actual type, normally not seen by the user, is a boxed
record. *)
datavtype _pixmap (a : t@ype, w : int, h : int, p : addr) =
| _pixmap of
@{
pf = array_v (a, p, w * h) |
w = size_t w,
h = size_t h,
p = ptr p
}
(* Here is one of the ways to tie an abstract type to its
implementation: *)
assume pixmap (a, w, h, p) = _pixmap (a, w, h, p)
(* Another way is to use casts. *)
(*------------------------------------------------------------------*)
implement {}
pixmap_width pix =
case+ pix of _pixmap record => record.w
implement {}
pixmap_height pix =
case+ pix of _pixmap record => record.h
implement {a}
pixmap_make_array (pf | w, h, p) =
_pixmap @{pf = pf | w = w, h = h, p = p}
implement {a}
pixmap_unmake pix =
case+ pix of
| ~ _pixmap @{pf = pf | w = w, h = h, p = p} => @(pf | w, h, p)
primplement
pixmap_prove_index_bounds {w, h} {x, y} () =
let
prval () = mul_gte_gte_gte {y, w} ()
prval () = mul_gte_gte_gte {h - (y + 1), w} ()
in
end
implement {a}
pixmap_make_uninitized {w, h} (w, h) =
let
prval () = lemma_g1uint_param w (* Proves w >= 0. *)
prval () = lemma_g1uint_param h (* Proves h >= 0. *)
prval () = mul_gte_gte_gte {w, h} () (* Proves w*h >= 0. *)
val @(pf, pfgc | p) = array_ptr_alloc (w * h)
val pix = pixmap_make (pf | w, h, p)
in
@(pfgc | pix)
end
implement {a}
pixmap_make_elt (w, h, elt) =
let
val @(pfgc | pix) = pixmap_make (w, h)
in
fill (pix, elt);
@(pfgc | pix)
end
implement {}
pixmap_free_storage_return pix =
case+ pix of
| ~ _pixmap record => @(record.pf | record.p)
implement {}
pixmap_free_storage_free (pfgc | pix) =
let
val @(pf | p) = pixmap_free pix
in
array_ptr_free (pf, pfgc | p)
end
implement {a}
pixmap_fill_elt {w, h} {p} (pix, elt) =
case+ pix of
| @ _pixmap record =>
let
prval () = lemma_g1uint_param (record.w)
prval () = lemma_g1uint_param (record.h)
prval () = mul_gte_gte_gte {w, h} ()
stadef n = w * h
val n : size_t n = record.w * record.h
and p : ptr p = record.p
fun
loop {i : nat | i <= n}
..
(pf_lft : array_v (a, p, i),
pf_rgt : array_v (a?, p + (i * sizeof a), n - i) |
i : size_t i)
: @(array_v (a, p, n) | ) =
if i = n then
let
prval () = array_v_unnil pf_rgt
in
@(pf_lft | )
end
else
let
prval @(pf_elt, pf_rgt) = array_v_uncons pf_rgt
val () = ptr_set (pf_elt | ptr_add (p, i), elt)
prval pf_lft = array_v_extend (pf_lft, pf_elt)
in
loop (pf_lft, pf_rgt | succ i)
end
val @(pf | ) = loop (array_v_nil (), record.pf | i2sz 0)
prval () = record.pf := pf
prval () = fold@ pix
in
end
implement {a} {tk}
pixmap_set_at_guint {w, h} {x, y} (pix, x, y, elt) =
case+ pix of
| @ _pixmap record =>
let
prval () = lemma_g1uint_param x
prval () = lemma_g1uint_param y
stadef n = w * h
stadef i = x + (y * w)
prval () = pixmap_prove_index_bounds {w, h} {x, y} ()
prval () = prop_verify {0 <= i && i < n} ()
(* I purposely store the data in an order such that you can
write something such as a PPM without looping separately
over x and y. Also, even if you did do an outer loop over y
and an inner loop over x, you would get the advantage of
data locality. *)
val i : size_t i = g1u2u x + (g1u2u y * record.w)
macdef pixels = !(record.p)
val () = pixels[i] := elt
prval () = fold@ pix
in
end
implement {a} {tk}
pixmap_set_at_gint (pix, x, y, elt) =
pixmap_set_at_guint (pix, g1i2u x, g1i2u y, elt)
implement {a} {tk}
pixmap_get_at_guint {w, h} {x, y} (pix, x, y) =
case+ pix of
| @ _pixmap record =>
let
prval () = lemma_g1uint_param x
prval () = lemma_g1uint_param y
stadef n = w * h
stadef i = x + (y * w)
prval () = pixmap_prove_index_bounds {w, h} {x, y} ()
prval () = prop_verify {0 <= i && i < n} ()
val i : size_t i = g1u2u x + (g1u2u y * record.w)
macdef pixels = !(record.p)
val elt = pixels[i]
prval () = fold@ pix
in
elt
end
implement {a} {tk}
pixmap_get_at_gint (pix, x, y) =
pixmap_get_at_guint (pix, g1i2u x, g1i2u y)
implement {a}
pixmap_dump (outf, pix) =
case+ pix of
| @ _pixmap record =>
let
macdef pixels = !(record.p)
val n = record.w * record.h
val success = pixmap$pixels_dump (outf, pixels, n)
prval () = fold@ pix
in
success
end
implement {a}
pixmap_load (inpf, pix, elt) =
case+ pix of
| @ _pixmap record =>
let
macdef pixels = !(record.p)
val n = record.w * record.h
val success = pixmap$pixels_load (inpf, pixels, n, elt)
prval () = fold@ pix
in
success
end
(*------------------------------------------------------------------*)
typedef FILEstar = $extype"FILE *"
extern castfn FILEref2star : FILEref -<> FILEstar
implement
pixmap$pixels_dump (outf, pixels, n) =
let
val num_written =
$extfcall (size_t, "fwrite", addr@ pixels, sizeof, n,
FILEref2star outf)
in
num_written = n
end
implement
pixmap$pixels_load (inpf, pixels, n, elt) =
let
prval [n : int] EQINT () = eqint_make_guint n
val num_read =
$extfcall (size_t, "fread", addr@ pixels, sizeof, n,
FILEref2star inpf)
in
if num_read = n then
let
prval () = $UNSAFE.castvwtp2void{@[rgb24][n]} pixels
in
true
end
else
begin
array_initize_elt (pixels, n, elt);
false
end
end
(*------------------------------------------------------------------*)
assume rgb24 = @(uint8, uint8, uint8)
implement {tk}
rgb24_make_uint_uint_uint (r, g, b) =
let
(* The prelude tends to miss implementations for type conversions
to uint8, so let us at least implement conversion from uint to
uint8. (I do not wish to use a general unsafe cast, because
that sort of code has caused me bugs before. C does not always
know how to do a type conversion correctly.) The ats2-xprelude
package has a much more complete set of implementations
(generated en masse by m4 macros), but for this task I am
avoiding such dependencies. *)
implement
g0uint2uint i =
let
extern castfn g0uint2uint_uint_uint8 : uint -<> uint8
in
g0uint2uint_uint_uint8 i
end
in
rgb24_make_tuple @(g0u2u r, g0u2u g, g0u2u b)
end
implement {tk}
rgb24_make_int_int_int (r, g, b) =
let
(* See the comment in rgb24_make_uint_uint_uint. *)
implement
g0int2uint i =
let
extern castfn g0int2uint_int_uint8 : int -<> uint8
in
g0int2uint_int_uint8 i
end
in
rgb24_make @(g0i2u r, g0i2u g, g0i2u b)
end
implement {}
rgb24_make_tuple tup = tup
implement {}
rgb24_values rgb = rgb
(*------------------------------------------------------------------*)
#ifdef BITMAP_TASK_TEST #then
%{^
#include
%}
fn
test_sizeof_rgb24 () : void =
(* We want to be sure rgb24 takes up exactly 24 bits. Our dump and
load implementations depend on that. (If it prove not the case on
some platform, one can write, for that unanticipated platform,
special implementations of dump and load.) *)
let
val- true = sizeof = i2sz 3
val- true = sizeof * $extval (size_t, "CHAR_BIT") = i2sz 24
in
end
fn
test_pixel_load_copy_dump () : void =
(* Test loading, copying, and dumping of raw 24-bit RGB data from
SIPI image "Peppers", 4.2.07.tiff:
https://sipi.usc.edu/database/database.php?volume=misc&image=13#top
I have the data stored as "4.2.07.raw". *)
let
val failure_color = rgb24_make (0xFF, 0x00, 0x00)
val @(pfgc1 | pix1) = pixmap_make (i2sz 512, i2sz 512)
val inpf = fileref_open_exn ("4.2.07.raw", file_mode_r)
val success = load (inpf, pix1, failure_color)
val () = fileref_close inpf
val- true = success
val @(pfgc2 | pix2) = pixmap_make (i2sz 512, i2sz 512,
failure_color)
fun
copy_pixels {x, y : nat | x <= 512; y <= 512}
.<512 - x, 512 - y>.
(pix1 : !pixmap (rgb24, 512, 512),
pix2 : !pixmap (rgb24, 512, 512),
x : int x,
y : int y) : void =
if x = 512 then
()
else if y = 512 then
copy_pixels (pix1, pix2, succ x, 0)
else
begin
pix2[x, y] := pix1[x, y];
copy_pixels (pix1, pix2, x, succ y)
end
val () = copy_pixels (pix1, pix2, 0, 0)
val outf = fileref_open_exn ("4.2.07.raw.dumped", file_mode_w)
val success = dump (outf, pix2)
val () = fileref_close outf
val- true = success
val status = $extfcall (int, "system",
"cmp 4.2.07.raw 4.2.07.raw.dumped")
val- true = status = 0
in
free (pfgc1 | pix1);
free (pfgc2 | pix2)
end
implement
main0 () =
begin
test_sizeof_rgb24 ();
test_pixel_load_copy_dump ()
end
#endif
(*------------------------------------------------------------------*)