Bitmap/Read a PPM file: Difference between revisions

Added FreeBASIC
m (Automated syntax highlighting fixup (second round - minor fixes))
(Added FreeBASIC)
 
(11 intermediate revisions by 3 users not shown)
Line 361:
Close (F2);
end;</syntaxhighlight>
=={{header|ATS}}==
For this you will need the static and dynamic ATS source files of [[Bitmap#ATS]], [[Grayscale_image#ATS]], and [[Bitmap/Write_a_PPM_file#ATS]]. (You do ''not'' need libnetpbm, although one could easily use it with ATS.)
 
There are three files here: a static file for the interface to <code>pixmap_read_ppm</code>, a dynamic file for the implementation of <code>pixmap_read_ppm</code>, and a file for the program that converts an image to grayscale. (The last is a dynamic file, but we will call it the program file.)
 
With <code>pixmap_read_ppm<rgb24></code> you should be able to read any valid PPM, whether raw or plain, and with any valid Maxval. The result is a <code>pixmap1(rgb24)</code> with implicit Maxval of 255. The reader tries to be ''very'' permissive, although there seems not much I can do about the strange way comments work in PPM.
 
===The ATS static file===
This file should be called <code>bitmap_read_ppm_task.sats</code>.
<syntaxhighlight lang="ats">
#define ATS_PACKNAME "Rosetta_Code.bitmap_read_ppm_task"
 
staload "bitmap_task.sats"
 
fn {a : t@ype}
pixmap_read_ppm :
(* On failure to read, the return is None_vt(). I do not currently
provide any indication of why the attempt failed, although in
practice you probably would wish to add that. *)
FILEref ->
Option_vt ([w, h : pos] [p : addr | null < p]
@(mfree_gc_v p | pixmap (a, w, h, p)))
</syntaxhighlight>
 
===The ATS dynamic file===
This file should be called <code>bitmap_read_ppm_task.dats</code>.
<syntaxhighlight lang="ats">
(*------------------------------------------------------------------*)
 
#define ATS_DYNLOADFLAG 0
#define ATS_PACKNAME "Rosetta_Code.bitmap_read_ppm_task"
 
#include "share/atspre_staload.hats"
 
staload "bitmap_task.sats"
 
(* You need to staload bitmap_task.dats, so the ATS compiler will have
access to its implementations of templates. But we staload it
anonymously, so the programmer will not have access. *)
staload _ = "bitmap_task.dats"
 
staload "bitmap_read_ppm_task.sats"
 
(*------------------------------------------------------------------*)
 
datavtype magic_number_vt =
| Netpbm_magic_number of int
| Unknown_magic_number of ()
 
fn {}
read_magic_number (inpf : FILEref) : magic_number_vt =
let
val i = fileref_getc inpf
in
if i <> char2int0 'P' then
Unknown_magic_number ()
else
let
val i = fileref_getc inpf
in
if i < char2int0 '1' && char2int0 '7' < i then
Unknown_magic_number ()
else
Netpbm_magic_number (i - char2int0 '0')
end
end
 
fn {}
get_next_char (inpf : FILEref) : int =
let
fnx
get_next () : int =
let
val i = fileref_getc inpf
in
if i = char2int0 '#' then
skip_through_newline ()
else
i
end
and
skip_through_newline () : int =
let
val i = fileref_getc inpf
in
if i < 0 then
i
else if i = char2int0 '\n' then
get_next ()
else
skip_through_newline ()
end
in
get_next ()
end
 
(* The only tokens we need to scan for, in P1 through P6, are unsigned
integers. P7 headers (Portable Arbitrary Map) have a completely
different arrangement, but we are not handling that. *)
fn {}
get_next_integer (inpf : FILEref)
(* A negative return value means we have reached the end. We do
not distinguish whitespace characters from anything else that
is not a digit or '#'. (Really I want to use intmax_t here,
rather than llint, but there is no intmax_t support in the
prelude. The ats2-xprelude package has support, but I am
avoiding the dependency. *)
: llint =
let
fnx
look_for_digit () : llint =
let
val i = get_next_char inpf
in
if i < char2int0 '0' || char2int0 '9' < i then
look_for_digit ()
else
read_digits (g0i2i (i - char2int0 '0'))
end
and
read_digits (x : llint) : llint =
let
val i = get_next_char inpf
in
if i < char2int0 '0' || char2int0 '9' < i then
(* I cannot find an "ungetc" in prelude/SATS/filebas.sats,
so I will use the foreign function interface directly. *)
let
typedef FILEstar = $extype"FILE *"
extern castfn FILEref2star : FILEref -<> FILEstar
in
ignoret ($extfcall (int, "ungetc", i, FILEref2star inpf));
x
end
else
let
val digit : llint = g0i2i (i - char2int0 '0')
in
read_digits ((10LL * x) + digit)
end
end
in
look_for_digit ()
end
 
fn {}
read_ppm_header (inpf : FILEref)
: Option_vt @(ullint, ullint, ullint) =
let
val width = get_next_integer inpf
in
if width < 0LL then
None_vt ()
else
let
val height = get_next_integer inpf
in
if height < 0LL then
None_vt ()
else
let
val maxval = get_next_integer inpf
in
if maxval < 0LL then
None_vt ()
else
begin
(* There is supposed to be a whitespace character (or
comments and whitespace character) after the
MAXVAL. We will accept anything, whitespace or
not. *)
ignoret (fileref_getc inpf);
 
Some_vt @(g0i2u width, g0i2u height, g0i2u maxval)
end
end
end
end
 
fn {}
get_next_single_byte (inpf : FILEref) : llint =
let
val i = fileref_getc inpf
in
if i < 0 then
~1LL
else
g0i2i i
end
 
fn {}
get_next_double_byte (inpf : FILEref) : llint =
let
val i1 = fileref_getc inpf
in
if i1 < 0 then
~1LL
else
let
val i0 = fileref_getc inpf
in
if i0 < 0 then
~1LL
else
let
val i1 : llint = g0i2i i1
and i0 : llint = g0i2i i0
in
(i1 * 256LL) + i0
end
end
end
 
(*------------------------------------------------------------------*)
(* Implementation is provided only for rgb24. *)
 
extern castfn ull2sz : {i : int} ullint i -<> size_t i
extern castfn ull2u : {i : int} ullint i -<> uint i
extern castfn ull2u8 : ullint -<> uint8
 
extern fn {}
read_raw_ppm_rgb24 : $d2ctype (pixmap_read_ppm<rgb24>)
 
extern fn {}
read_plain_ppm_rgb24 : $d2ctype (pixmap_read_ppm<rgb24>)
 
extern fn {}
read_general_ppm_rgb24 : $d2ctype (pixmap_read_ppm<rgb24>)
 
extern fn {}
read_general$width () : [i : pos] size_t i
 
extern fn {}
read_general$height () : [i : pos] size_t i
 
extern fn {}
read_general$maxval () : [i : pos | i <= 65535] uint i
 
extern fn {}
read_general$next_value : FILEref -> llint
 
implement
pixmap_read_ppm<rgb24> inpf =
case+ read_magic_number inpf of
| ~ Unknown_magic_number () => None_vt ()
| ~ Netpbm_magic_number num =>
begin
case+ num of
| 6 => read_raw_ppm_rgb24 inpf
| 3 => read_plain_ppm_rgb24 inpf
| _ => None_vt
end
 
implement {}
read_raw_ppm_rgb24 inpf =
case+ read_ppm_header inpf of
| ~ None_vt () => None_vt ()
| ~ Some_vt @(width, height, maxval) =>
let
val width = g1ofg0 width
and height = g1ofg0 height
and maxval = g1ofg0 maxval
in
if (width < 1LLU) + (height < 1LLU) +
(maxval < 1LLU) + (65535LLU < maxval) then
None_vt ()
else
let
val w : Size_t = ull2sz width
val h : Size_t = ull2sz height
val maxval : uInt = ull2u maxval
in
if maxval = 255u then
let
val @(pfgc | pix) = pixmap_make<rgb24> (w, h)
val success =
load<rgb24> (inpf, pix, rgb24_make (255, 0, 0))
in
if ~success then
begin
free (pfgc | pix);
None_vt ()
end
else
Some_vt @(pfgc | pix)
end
else if maxval < 256u then
let
implement read_general$width<> () = w
implement read_general$height<> () = h
implement read_general$maxval<> () = maxval
implement
read_general$next_value<> inpf =
get_next_single_byte inpf
in
read_general_ppm_rgb24<> inpf
end
else
let
implement read_general$width<> () = w
implement read_general$height<> () = h
implement read_general$maxval<> () = maxval
implement
read_general$next_value<> inpf =
get_next_double_byte inpf
in
read_general_ppm_rgb24<> inpf
end
end
end
 
implement {}
read_plain_ppm_rgb24 inpf =
case+ read_ppm_header inpf of
| ~ None_vt () => None_vt ()
| ~ Some_vt @(width, height, maxval) =>
let
val width = g1ofg0 width
and height = g1ofg0 height
and maxval = g1ofg0 maxval
in
if (width < 1LLU) + (height < 1LLU) +
(maxval < 1LLU) + (65535LLU < maxval) then
None_vt ()
else
let
val w : Size_t = ull2sz width
val h : Size_t = ull2sz height
val maxval : uInt = ull2u maxval
implement read_general$width<> () = w
implement read_general$height<> () = h
implement read_general$maxval<> () = maxval
implement
read_general$next_value<> inpf =
get_next_integer inpf
in
read_general_ppm_rgb24<> inpf
end
end
 
implement {}
read_general_ppm_rgb24 inpf =
let
val [w : int] w = read_general$width<> ()
and [h : int] h = read_general$height<> ()
and maxval = read_general$maxval<> ()
 
fn
scale_value (v : ullint) : uint8 =
if maxval = 255u then
ull2u8 v
else
let
val maxval : ullint = g0u2u maxval
val v = 255LLU * v
val v1 = v / maxval
and v0 = v mod maxval
in
if v0 + v0 < maxval then
ull2u8 v1
else if maxval < v0 + v0 then
ull2u8 (succ v1)
else if v1 mod 2LLU = 0LLU then
ull2u8 v1
else
ull2u8 (succ v1)
end
 
(* For easier programming, start with a fully initialized
pixmap. The routine probably is I/O-bound, anyway. *)
val @(pfgc | pix) =
pixmap_make<rgb24> (w, h, rgb24_make (255, 0, 0))
 
macdef between (i, j, v) =
let
val v = ,(v)
in
(,(i) <= v) * (v <= ,(j))
end
 
fun
loop {x, y : nat | x <= w; y <= h}
.<h - y, w - x>.
(pix : !pixmap (rgb24, w, h),
x : size_t x,
y : size_t y)
: bool (* success *) =
if y = h then
true
else if x = w then
loop (pix, i2sz 0, succ y)
else
let
val maxv : llint = g0u2i maxval
val vr = read_general$next_value<> inpf
in
if ~between (0LL, maxv, vr) then
false
else
let
val vg = read_general$next_value<> inpf
in
if ~between (0LL, maxv, vg) then
false
else
let
val vb = read_general$next_value<> inpf
in
if ~between (0LL, maxv, vb) then
false
else
let
val r = scale_value (g0i2u vr)
and g = scale_value (g0i2u vg)
and b = scale_value (g0i2u vb)
in
pix[x, y] := rgb24_make @(r, g, b);
loop (pix, succ x, y)
end
end
end
end
 
val success = loop (pix, i2sz 0, i2sz 0)
in
if ~success then
begin
free (pfgc | pix);
None_vt ()
end
else
Some_vt @(pfgc | pix)
end
 
(*------------------------------------------------------------------*)
 
#ifdef BITMAP_READ_PPM_TASK_TEST #then
 
staload "bitmap_write_ppm_task.sats"
staload _ = "bitmap_write_ppm_task.dats"
 
(* The test program converts a PPM at standard input to a raw PPM with
MAXVAL 255. *)
implement
main0 () =
let
val pix_opt = pixmap_read_ppm<rgb24> stdin_ref
in
case+ pix_opt of
| ~ None_vt () => ()
| ~ Some_vt @(pfgc | pix) =>
begin
ignoret (pixmap_write_ppm (stdout_ref, pix));
free (pfgc | pix)
end
end
 
#endif
 
(*------------------------------------------------------------------*)
</syntaxhighlight>
 
===The ATS program file===
This file should be called <code>bitmap_read_ppm_task_program.dats</code> (though it actually could be called by another name).
[[File:Bitmap read ppm task ATS color.jpg|thumb|alt=A gnarly tree on a rise by the sea, in color.]][[File:Bitmap read ppm task ATS gray.jpg|thumb|alt=A gnarly tree on a rise by the sea, in grayscale.]]
<syntaxhighlight lang="ats">
(* The program should be able to read a PPM in raw or plain format,
with any valid Maxval. The output will be a grayscale raw PPM with
Maxval=255.
 
Compile with "myatscc bitmap_read_ppm_task_program.dats", which
should give you a program named "bitmap_read_ppm_task_program". *)
 
(*
 
##myatsccdef=\
patscc -std=gnu2x -g -O2 -DATS_MEMALLOC_LIBC \
-o $fname($1) $1 \
bitmap{,_{{read,write}_ppm,grayscale}}_task.{s,d}ats
 
*)
 
#include "share/atspre_staload.hats"
 
staload "bitmap_task.sats"
staload "bitmap_read_ppm_task.sats"
staload "bitmap_write_ppm_task.sats"
staload "bitmap_grayscale_task.sats"
 
staload _ = "bitmap_task.dats"
staload _ = "bitmap_read_ppm_task.dats"
staload _ = "bitmap_write_ppm_task.dats"
staload _ = "bitmap_grayscale_task.dats"
 
implement
main0 (argc, argv) =
let
val args = listize_argc_argv (argc, argv)
val nargs = length args
 
val inpf =
if nargs < 2 then
stdin_ref
else if args[1] = "-" then
stdin_ref
else
fileref_open_exn (args[1], file_mode_r)
val pix_opt = pixmap_read_ppm<rgb24> inpf
val () = fileref_close inpf
in
case+ pix_opt of
| ~ None_vt () =>
begin
free args;
println! ("For some reason, I failed to read the image.");
exit 1
end
| ~ Some_vt @(pfgc1 | pix1) =>
let
val @(pfgc2 | pix2) = pixmap_convert<rgb24,gray8> pix1
val () = free (pfgc1 | pix1)
val @(pfgc3 | pix3) = pixmap_convert<gray8,rgb24> pix2
val () = free (pfgc2 | pix2)
 
val outf =
if nargs < 3 then
stdout_ref
else if args[2] = "-" then
stdout_ref
else
fileref_open_exn (args[2], file_mode_w)
val success = pixmap_write_ppm<rgb24> (outf, pix3)
val () = fileref_close outf
 
val () = free (pfgc3 | pix3)
in
free args;
if ~success then
begin
println! ("For some reason, ",
"I failed to write a new image.");
exit 2
end
end
end
</syntaxhighlight>
 
You can compile the program with the shell command
 
<pre>myatscc bitmap_read_ppm_task_program.dats</pre>
 
If compilation is successful, the program will be called <code>bitmap_read_ppm_task_program</code>. You can give up to two arguments (any others will be ignored). The first argument is a file name for the input file, the second is the file name for the output file. Either argument can be "-", meaning to use the respective standard input or output. An argument omitted is equivalent to "-".
 
Shown in the margin are before and after for SIPI test image 4.1.06 (not counting that I have converted the PPM files to JPEGs).
 
=={{header|AutoHotkey}}==
{{works with | AutoHotkey_L}}
Line 422 ⟶ 977:
}
#include bitmap_storage.ahk ; from http://rosettacode.org/wiki/Basic_bitmap_storage/AutoHotkey</syntaxhighlight>
 
=={{header|BBC BASIC}}==
{{works with|BBC BASIC for Windows}}
Line 1,114 ⟶ 1,670:
* doing formatted I/O with Fortran is a pain... And unformatted does not mean ''free''; Fortran2003 has ''streams'', but they are not implemented (yet) in GNU Fortran compiler. Here (as in the write part) I've tried to handle the PPM format through formatted I/O. The tests worked but I have not tried still everything.
* comments after the first line are not handled
 
=={{header|FreeBASIC}}==
{{trans|Yabasic}}
<syntaxhighlight lang="vbnet">Dim As String imagen = "Lena.ppm"
 
Sub readPPM (fs As String)
Dim As Integer x, y, ancho, alto
Dim As String t, kolor
Dim As Ubyte r, g, b
If Len(fs) = 0 Then Print "No PPM file name indicated.": Exit Sub
Dim As Long ff = Freefile
Open fs For Binary As #ff
If Err Then Print "File "; fs; " not found.": Exit Sub
Input #ff, t, ancho, alto, kolor
If t = "P6" Then
Screenres ancho, alto, 32
For y = 0 To alto - 1
For x = 0 To ancho - 1
Get #ff, , r
Get #ff, , g
Get #ff, , b
Pset (x, y), Rgb(r, g, b)
Next x
Next y
Close #ff
Else
Print "File is NOT PPM P6 type."
End If
End Sub
 
readPPM(imagen)
Sleep</syntaxhighlight>
 
=={{header|Go}}==
<syntaxhighlight lang="go">package raster
Line 1,251 ⟶ 1,844:
myimgGray=: toColor toGray myimg
myimgGray writeppm jpath '~temp/myimgGray.ppm'</syntaxhighlight>
 
=={{header|Java}}==
For convenience, the code for the class used in the [[Bitmap]] task here and integrated with the code in the [[Grayscale image]] is included.
<syntaxhighlight lang="java">
 
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
 
import javax.imageio.ImageIO;
 
public class ReadPPMFile {
 
public static void main(String[] aArgs) throws IOException {
// Using the file created in the Bitmap task
String filePath = "output.ppm";
reader = new BufferedInputStream( new FileInputStream(filePath) );
final char header1 = (char) reader.read();
final char header2 = (char) reader.read();
final char header3 = (char) reader.read();
if ( header1 != 'P' || header2 != '6' || header3 != END_OF_LINE) {
reader.close();
throw new IllegalArgumentException("Not a valid P6 PPM file");
}
final int width = processCharacters(SPACE_CHARACTER);
final int height = processCharacters(END_OF_LINE);
final int maxColorValue = processCharacters(END_OF_LINE);
if ( maxColorValue < 0 || maxColorValue > 255 ) {
reader.close();
throw new IllegalArgumentException("Maximum color value is outside the range 0..255");
}
// Remove any comments before reading data
reader.mark(1);
while ( reader.read() == START_OF_COMMENT ) {
while ( reader.read() != END_OF_LINE );
reader.mark(1);
}
reader.reset();
// Read data
BasicBitmapStorage bitmap = new BasicBitmapStorage(width, height);
byte[] buffer = new byte[width * 3];
for ( int y = 0; y < height; y++ ) {
reader.read(buffer, 0, buffer.length);
for ( int x = 0; x < width; x++ ) {
Color color = new Color(Byte.toUnsignedInt(buffer[x * 3]),
Byte.toUnsignedInt(buffer[x * 3 + 1]),
Byte.toUnsignedInt(buffer[x * 3 + 2]));
bitmap.setPixel(x, y, color);
}
}
reader.close();
// Convert to gray scale and save to a file
bitmap.convertToGrayscale();
File grayFile = new File("outputGray.jpg");
ImageIO.write((RenderedImage) bitmap.getImage(), "jpg", grayFile);
}
private static int processCharacters(char aChar) throws IOException {
StringBuilder characters = new StringBuilder();
char ch;
while ( ( ch = (char) reader.read() ) != aChar ) {
if ( ch == START_OF_COMMENT ) {
while ( reader.read() != END_OF_LINE );
continue;
}
characters.append(ch);
}
return Integer.valueOf(characters.toString());
}
private static BufferedInputStream reader;
private static final char START_OF_COMMENT = '#';
private static final char SPACE_CHARACTER = ' ';
private static final char END_OF_LINE = '\n';
}
final class BasicBitmapStorage {
 
public BasicBitmapStorage(int aWidth, int aHeight) {
image = new BufferedImage(aWidth, aHeight, BufferedImage.TYPE_INT_RGB);
}
 
public void fill(Color aColor) {
Graphics graphics = image.getGraphics();
graphics.setColor(aColor);
graphics.fillRect(0, 0, image.getWidth(), image.getHeight());
}
 
public Color getPixel(int aX, int aY) {
return new Color(image.getRGB(aX, aY));
}
public void setPixel(int aX, int aY, Color aColor) {
image.setRGB(aX, aY, aColor.getRGB());
}
public Image getImage() {
return image;
}
public void convertToGrayscale() {
for ( int y = 0; y < image.getHeight(); y++ ) {
for ( int x = 0; x < image.getWidth(); x++ ) {
int color = image.getRGB(x, y);
 
int alpha = ( color >> 24 ) & 255;
int red = ( color >> 16 ) & 255;
int green = ( color >> 8 ) & 255;
int blue = color & 255;
 
final int luminance = (int) ( 0.2126 * red + 0.7152 * green + 0.0722 * blue );
 
alpha = alpha << 24;
red = luminance << 16;
green = luminance << 8;
blue = luminance;
 
color = alpha + red + green + blue;
 
image.setRGB(x, y, color);
}
}
}
private final BufferedImage image;
 
}
</syntaxhighlight>
{{ out }}
[[Media:ColouredJava.png]] & [[Media:GrayscaleJava.png]]
 
=={{header|Julia}}==
{{works with|Julia|0.6}}
Line 2,818 ⟶ 3,557:
{{libheader|DOME}}
This assumes that [https://rosettacode.org/wiki/File:Lenna100.jpg Lenna100.jpg], a 512 x 512 color image of the eponymous lady, has already been converted to Lenna100.ppm using a variation of the 'Write a PPM file' task.
<syntaxhighlight lang="ecmascriptwren">import "graphics" for Canvas, ImageData, Color
import "dome" for Window, Process
import "io" for FileSystem
Line 2,844 ⟶ 3,583:
loadPPMFile(fileName) {
var ppm = FileSystem.load(fileName)
var count = ppm.count // ensure file is fully loaded before proceeding
if (ppm[0..1] != "P6") {
System.print("The loaded file is not a P6 file.")
Line 2,893 ⟶ 3,633:
 
var Game = Bitmap.new("Lenna100.ppm", "Lenna100_gs.jpg", 1048, 512)</syntaxhighlight>
 
=={{header|XPL0}}==
The simplicity of redirecting an input file on the command line doesn't
2,122

edits