Bitmap/Read a PPM file: Difference between revisions

Added FreeBASIC
(→‎{{header|Perl 6}}: Add perl 6 example)
(Added FreeBASIC)
 
(35 intermediate revisions by 15 users not shown)
Line 1:
{{task|Raster graphics operations}}
[[Category:Input Output]]
[[Category:E examples needing attention]]
{{task|Raster graphics operations}}
 
Using the data storage type defined [[Basic_bitmap_storage|on this page]] for raster images, read an image from a PPM file (binary P6 prefered).
Line 6 ⟶ 7:
 
'''Task''': Use [[write ppm file]] solution and [[grayscale image]] solution with this one in order to convert a color image to grayscale one.
=={{header|11l}}==
{{trans|Python}}
 
<syntaxhighlight lang="11l">T Colour
Byte r, g, b
 
F (r, g, b)
.r = r
.g = g
.b = b
 
F ==(other)
R .r == other.r & .g == other.g & .b == other.b
 
V black = Colour(0, 0, 0)
V white = Colour(255, 255, 255)
 
T Bitmap
Int width, height
Colour background
[[Colour]] map
 
F (width = 40, height = 40, background = white)
assert(width > 0 & height > 0)
.width = width
.height = height
.background = background
.map = (0 .< height).map(h -> (0 .< @width).map(w -> @@background))
 
F fillrect(x, y, width, height, colour = black)
assert(x >= 0 & y >= 0 & width > 0 & height > 0)
L(h) 0 .< height
L(w) 0 .< width
.map[y + h][x + w] = colour
 
F set(x, y, colour = black)
.map[y][x] = colour
 
F get(x, y)
R .map[y][x]
 
F togreyscale()
L(h) 0 .< .height
L(w) 0 .< .width
V (r, g, b) = .get(w, h)
V l = Int(0.2126 * r + 0.7152 * g + 0.0722 * b)
.set(w, h, Colour(l, l, l))
 
F writeppmp3()
V magic = "P3\n"
V comment = "# generated from Bitmap.writeppmp3\n"
V s = magic‘’comment‘’("#. #.\n#.\n".format(.width, .height, 255))
L(h) (.height - 1 .< -1).step(-1)
L(w) 0 .< .width
V (r, g, b) = .get(w, h)
s ‘’= ‘ #3 #3 #3’.format(r, g, b)
s ‘’= "\n"
R s
 
F tokenize(fstr)
[String] tokens
L(line) fstr.split("\n")
I !line.starts_with(‘#’)
L(t) line.split(‘ ’, group_delimiters' 1B)
tokens.append(t)
R tokens
 
F ppmp3tobitmap(fstr)
V tokens = tokenize(fstr)
V tokeni = -1
F nexttoken()
@tokeni++
R @tokens[@tokeni]
assert(‘P3’ == nexttoken(), ‘Wrong filetype’)
V width = Int(nexttoken())
V height = Int(nexttoken())
V maxval = Int(nexttoken())
V bitmap = Bitmap(width, height, Colour(0, 0, 0))
L(h) (height - 1 .< -1).step(-1)
L(w) 0 .< width
V r = Int(nexttoken())
V g = Int(nexttoken())
V b = Int(nexttoken())
bitmap.set(w, h, Colour(r, g, b))
 
R bitmap
 
V ppmtxt = |‘P3
# feep.ppm
4 4
15
0 0 0 0 0 0 0 0 0 15 0 15
0 0 0 0 15 7 0 0 0 0 0 0
0 0 0 0 0 0 0 15 7 0 0 0
15 0 15 0 0 0 0 0 0 0 0 0
 
V bitmap = ppmp3tobitmap(ppmtxt)
print(‘Grey PPM:’)
bitmap.togreyscale()
print(bitmap.writeppmp3())</syntaxhighlight>
 
{{out}}
<pre>
Grey PPM:
P3
# generated from Bitmap.writeppmp3
4 4
255
0 0 0 0 0 0 0 0 0 4 4 4
0 0 0 11 11 11 0 0 0 0 0 0
0 0 0 0 0 0 11 11 11 0 0 0
4 4 4 0 0 0 0 0 0 0 0 0
</pre>
=={{header|Action!}}==
Part of the task responsible for conversion from RGB color image into a grayscale image can be found in the module [http://www.rosettacode.org/wiki/Category:Action!_Bitmap_tools#RGB2GRAY.ACT RGB2GRAY.ACT]. File D:PPM6.PPM can be generated by task [http://www.rosettacode.org/wiki/Bitmap/Write_a_PPM_file#Action.21 Bitmap/Write a PPM file].
{{libheader|Action! Bitmap tools}}
{{libheader|Action! Tool Kit}}
<syntaxhighlight lang="action!">INCLUDE "H6:RGB2GRAY.ACT" ;from task Grayscale image
 
PROC DecodeSize(CHAR ARRAY s BYTE POINTER width,height)
BYTE i
 
width^=ValB(s)
i=1
WHILE i<=s(0) AND s(i)#32
DO
s(i)=32
i==+1
OD
height^=ValB(s)
RETURN
 
PROC LoadHeader(RgbImage POINTER img
CHAR ARRAY format BYTE dev)
CHAR ARRAY line(255)
BYTE header,size,max,width,height
 
header=0 size=0 max=0
WHILE max=0
DO
InputSD(dev,line)
IF line(0)>0 AND line(1)#'# THEN
IF header=0 THEN
IF SCompare(format,format)#0 THEN
Break()
FI
header=1
ELSEIF size=0 THEN
DecodeSize(line,@width,@height)
IF width=0 OR height=0 THEN
Break()
FI
img.w=width img.h=height
size=1
ELSEIF max=0 THEN
max=ValB(line)
IF max#255 THEN
Break()
FI
FI
FI
OD
RETURN
 
PROC LoadPPM6(RgbImage POINTER img CHAR ARRAY path)
BYTE dev=[1],x,y
RGB c
 
Close(dev)
Open(dev,path,4)
LoadHeader(img,"P6",dev)
FOR y=0 TO img.h-1
DO
FOR x=0 TO img.w-1
DO
c.r=GetD(dev)
c.g=GetD(dev)
c.b=GetD(dev)
SetRgbPixel(img,x,y,c)
OD
OD
Close(dev)
RETURN
 
PROC SaveHeader(GrayImage POINTER img
CHAR ARRAY format BYTE dev)
 
PrintDE(dev,format)
PrintBD(dev,img.w)
PutD(dev,32)
PrintBDE(dev,img.h)
PrintBDE(dev,255)
RETURN
 
PROC SavePPM2(RgbImage POINTER img CHAR ARRAY path)
BYTE dev=[1],x,y,c
 
Close(dev)
Open(dev,path,8)
SaveHeader(img,"P2",dev)
FOR y=0 TO img.h-1
DO
FOR x=0 TO img.w-1
DO
c=GetGrayPixel(img,x,y)
PrintBD(dev,c)
IF x=img.w-1 THEN
PutDE(dev)
ELSE
PutD(dev,32)
FI
OD
OD
Close(dev)
RETURN
 
PROC Load(CHAR ARRAY path)
CHAR ARRAY line(255)
BYTE dev=[1]
 
Close(dev)
Open(dev,path,4)
WHILE Eof(dev)=0
DO
InputSD(dev,line)
PrintE(line)
OD
Close(dev)
RETURN
 
PROC Main()
BYTE ARRAY rgbdata(300),graydata(100)
RgbImage rgbimg
GrayImage grayimg
CHAR ARRAY path2="D:PPM2.PPM"
CHAR ARRAY path6="D:PPM6.PPM"
 
Put(125) PutE() ;clear the screen
InitRgbImage(rgbimg,0,0,rgbdata)
InitRgbToGray()
PrintF("Loading %S...%E%E",path6)
LoadPPM6(rgbimg,path6)
 
PrintF("Converting RGB to grayscale...%E%E")
InitGrayImage(grayimg,rgbimg.w,rgbimg.h,graydata)
RgbToGray(rgbimg,grayimg)
 
PrintF("Saving %S...%E%E",path2)
SavePPM2(grayimg,path2)
PrintF("Loading %S...%E%E",path2)
Load(path2)
RETURN</syntaxhighlight>
{{out}}
[https://gitlab.com/amarok8bit/action-rosetta-code/-/raw/master/images/Read_a_PPM_file.png Screenshot from Atari 8-bit computer]
<pre>
Loading D:PPM6.PPM...
 
Converting RGB to grayscale...
 
Saving D:PPM2.PPM...
 
Loading D:PPM2.PPM...
 
P2
3 4
255
0 18 182
54 201 73
237 255 61
45 54 74
</pre>
=={{header|Ada}}==
<langsyntaxhighlight lang="ada">with Ada.Characters.Latin_1; use Ada.Characters.Latin_1;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Streams.Stream_IO; use Ada.Streams.Stream_IO;
Line 77 ⟶ 350:
return Result;
end;
end Get_PPM;</langsyntaxhighlight>
The implementation propagates Data_Error when the file format is incorrect. End_Error is propagated when the file end is prematurely met. The following example illustrates conversion of a color file to grayscale.
<langsyntaxhighlight lang="ada">declare
F1, F2 : File_Type;
begin
Line 87 ⟶ 360:
Close (F1);
Close (F2);
end;</langsyntaxhighlight>
=={{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}}
Only ppm6 files supported.
 
<langsyntaxhighlight AutoHotkeylang="autohotkey">img := ppm_read("lena50.ppm") ;
x := img[4,4] ; get pixel(4,4)
y := img[24,24] ; get pixel(24,24)
Line 148 ⟶ 976:
return bitmap
}
#include bitmap_storage.ahk ; from http://rosettacode.org/wiki/Basic_bitmap_storage/AutoHotkey</langsyntaxhighlight>
 
=={{header|BBC BASIC}}==
{{works with|BBC BASIC for Windows}}
<langsyntaxhighlight lang="bbcbasic"> f% = OPENIN("c:\lena.ppm")
IF f%=0 ERROR 100, "Failed to open input file"
Line 181 ⟶ 1,009:
GCOL 1
LINE x%*2,y%*2,x%*2,y%*2
ENDPROC</langsyntaxhighlight>
 
=={{header|C}}==
It is up to the caller to open the file and pass the handler to the function. So this code can be used in
Line 189 ⟶ 1,016:
Interface:
 
<syntaxhighlight lang ="c">image get_ppm(FILE *pf);</langsyntaxhighlight>
 
Implementation:
 
<langsyntaxhighlight lang="c">#include "imglib.h"
 
#define PPMREADBUFLEN 256
Line 230 ⟶ 1,057:
return img;
}
}</langsyntaxhighlight>
 
The following acts as a filter to convert a PPM file read from standard input into a PPM gray image, and it outputs the converted image to standard output (see [[Grayscale image]], [[Write ppm file]], and [[Raster graphics operations]] in general):
 
<langsyntaxhighlight lang="c">#include <stdio.h>
#include "imglib.h"
 
Line 249 ⟶ 1,076:
free_img(source); free_img((image)idest);
return 0;
}</langsyntaxhighlight>
 
=={{header|C sharp|C#}}==
Tested with [[Write ppm file#C.23|this solution.]]
 
<langsyntaxhighlight lang="csharp">using System.IO;
class PPMReader
{
Line 286 ⟶ 1,112:
return bitmap;
}
}</langsyntaxhighlight>
 
=={{header|Common Lisp}}==
 
The function read-ppm-image reads either a P6 or P3 file depending on the file contents. The package description assumes that you have the [[Basic bitmap storage#Common Lisp]] package.
 
<langsyntaxhighlight lang="lisp">
(in-package #:rgb-pixel-buffer)
 
Line 364 ⟶ 1,189:
 
(export 'read-ppm-image)
</syntaxhighlight>
</lang>
To read the feep.ppm file as shown on the description page for the ppm format use:
<langsyntaxhighlight lang="lisp">
(read-ppm-image "feep.ppm")
</syntaxhighlight>
</lang>
 
=={{header|D}}==
The Image module contains a loadPPM6 function to load binary PPM images.
=={{header|Delphi}}==
Class helper for read and write Bitmap's and Ppm's
{{Trans|C#}}
<syntaxhighlight lang="delphi">
program BtmAndPpm;
 
{$APPTYPE CONSOLE}
 
{$R *.res}
 
uses
System.SysUtils,
System.Classes,
Winapi.Windows,
Vcl.Graphics;
 
type
TBitmapHelper = class helper for TBitmap
private
public
procedure SaveAsPPM(FileName: TFileName; useGrayScale: Boolean = False);
procedure LoadFromPPM(FileName: TFileName; useGrayScale: Boolean = False);
end;
 
function ColorToGray(Color: TColor): TColor;
var
L: Byte;
begin
L := round(0.2126 * GetRValue(Color) + 0.7152 * GetGValue(Color) + 0.0722 *
GetBValue(Color));
Result := RGB(L, L, L);
end;
 
{ TBitmapHelper }
 
procedure TBitmapHelper.SaveAsPPM(FileName: TFileName; useGrayScale: Boolean = False);
var
i, j, color: Integer;
Header: AnsiString;
ppm: TMemoryStream;
begin
ppm := TMemoryStream.Create;
try
Header := Format('P6'#10'%d %d'#10'255'#10, [Self.Width, Self.Height]);
writeln(Header);
ppm.Write(Tbytes(Header), Length(Header));
 
for i := 0 to Self.Height - 1 do
for j := 0 to Self.Width - 1 do
begin
if useGrayScale then
color := ColorToGray(ColorToRGB(Self.Canvas.Pixels[i, j]))
else
color := ColorToRGB(Self.Canvas.Pixels[i, j]);
ppm.Write(color, 3);
end;
ppm.SaveToFile(FileName);
finally
ppm.Free;
end;
end;
 
procedure TBitmapHelper.LoadFromPPM(FileName: TFileName; useGrayScale: Boolean = False);
var
p: Integer;
ppm: TMemoryStream;
sW, sH: string;
temp: AnsiChar;
W, H: Integer;
Color: TColor;
 
function ReadChar: AnsiChar;
begin
ppm.Read(Result, 1);
end;
 
begin
ppm := TMemoryStream.Create;
ppm.LoadFromFile(FileName);
if ReadChar + ReadChar <> 'P6' then
exit;
 
repeat
temp := ReadChar;
if temp in ['0'..'9'] then
sW := sW + temp;
until temp = ' ';
 
repeat
temp := ReadChar;
if temp in ['0'..'9'] then
sH := sH + temp;
until temp = #10;
 
W := StrToInt(sW);
H := StrToInt(sH);
 
if ReadChar + ReadChar + ReadChar <> '255' then
exit;
 
ReadChar(); //skip newLine
 
SetSize(W, H);
p := 0;
while ppm.Read(Color, 3) > 0 do
begin
if useGrayScale then
Color := ColorToGray(Color);
Canvas.Pixels[p mod W, p div W] := Color;
inc(p);
end;
ppm.Free;
end;
 
begin
with TBitmap.Create do
begin
// Load bmp
LoadFromFile('Input.bmp');
// Save as ppm
SaveAsPPM('Output.ppm');
 
// Load as ppm and convert in grayscale
LoadFromPPM('Output.ppm', True);
 
// Save as bmp
SaveToFile('Output.bmp');
 
Free;
end;
end.
</syntaxhighlight>
=={{header|E}}==
 
<langsyntaxhighlight lang="e">def chr := <import:java.lang.makeCharacter>.asChar
 
def readPPM(inputStream) {
Line 429 ⟶ 1,384:
image.replace(data.snapshot())
return image
}</langsyntaxhighlight>
 
[[Category:E examples needing attention]]Note: As of this writing the [[grayscale image]] task has not been implemented, so the task code (below) won't actually run yet. But readPPM above has been tested separately.
 
<langsyntaxhighlight lang="e">def readPPMTask(inputFile, outputFile) {
makeGrayscale \
.fromColor(readPPM(<import:java.io.makeFileInputStream>(inputFile))) \
.toColor() \
.writePPM(<import:java.io.makeFileOutputStream>(outputFile))
}</langsyntaxhighlight>
=={{header|Erlang}}==
 
<syntaxhighlight lang="erlang">
% This module provides basic operations on ppm files:
% Read from file, create ppm in memory (from generic bitmap) and save to file.
% Writing PPM files was introduced in roseta code task 'Bitmap/Write a PPM file'
% but the same code is included here to provide whole set of operations on ppm
% needed for purposes of this task.
 
-module(ppm).
 
-export([ppm/1, write/2, read/1]).
 
% constants for writing ppm file
-define(WHITESPACE, <<10>>).
-define(SPACE, <<32>>).
 
% constants for reading ppm file
-define(WHITESPACES, [9, 10, 13, 32]).
-define(PPM_HEADER, "P6").
 
% data structure introduced in task Bitmap (module ros_bitmap.erl)
-record(bitmap, {
mode = rgb,
pixels = nil,
shape = {0, 0}
}).
 
%%%%%%%%% API %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
% read ppm file from file
read(Filename) ->
{ok, File} = file:read_file(Filename),
parse(File).
 
% create ppm image from bitmap record
ppm(Bitmap) ->
{Width, Height} = Bitmap#bitmap.shape,
Pixels = ppm_pixels(Bitmap),
Maxval = 255, % original ppm format maximum
list_to_binary([
header(), width_and_height(Width, Height), maxval(Maxval), Pixels]).
 
% write bitmap as ppm file
write(Bitmap, Filename) ->
Ppm = ppm(Bitmap),
{ok, File} = file:open(Filename, [binary, write]),
file:write(File, Ppm),
file:close(File).
 
%%%%%%%%% Reading PPM %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
parse(Binary) ->
{?PPM_HEADER, Data} = get_next_token(Binary),
{Width, HeightAndRest} = get_next_token(Data),
{Height, MaxValAndRest} = get_next_token(HeightAndRest),
{_MaxVal, RawPixels} = get_next_token(MaxValAndRest),
Shape = {list_to_integer(Width), list_to_integer(Height)},
Pixels = load_pixels(RawPixels),
#bitmap{pixels=Pixels, shape=Shape}.
 
% load binary as a list of RGB triplets
load_pixels(Binary) when is_binary(Binary)->
load_pixels([], Binary).
load_pixels(Acc, <<>>) ->
array:from_list(lists:reverse(Acc));
load_pixels(Acc, <<R, G, B, Rest/binary>>) ->
load_pixels([<<R,G,B>>|Acc], Rest).
 
is_whitespace(Byte) ->
lists:member(Byte, ?WHITESPACES).
 
% get next part of PPM file, skip whitespaces, and return the rest of a binary
get_next_token(Binary) ->
get_next_token("", true, Binary).
get_next_token(CurrentToken, false, <<Byte, Rest/binary>>) ->
case is_whitespace(Byte) of
true ->
{lists:reverse(CurrentToken), Rest};
false ->
get_next_token([Byte | CurrentToken], false, Rest)
end;
get_next_token(CurrentToken, true, <<Byte, Rest/binary>>) ->
case is_whitespace(Byte) of
true ->
get_next_token(CurrentToken, true, Rest);
false ->
get_next_token([Byte | CurrentToken], false, Rest)
end.
 
%%%%%%%%% Writing PPM %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
header() ->
[<<"P6">>, ?WHITESPACE].
 
width_and_height(Width, Height) ->
[encode_decimal(Width), ?SPACE, encode_decimal(Height), ?WHITESPACE].
 
maxval(Maxval) ->
[encode_decimal(Maxval), ?WHITESPACE].
 
ppm_pixels(Bitmap) ->
% 24 bit color depth
array:to_list(Bitmap#bitmap.pixels).
 
encode_decimal(Number) ->
integer_to_list(Number).
</syntaxhighlight>
 
Usage in accordance with Grayscale Task:
<syntaxhighlight lang="erlang">
Colorful = ppm:read("colorful.ppm"),
Gray = ros_bitmap:convert(ros_bitmap:convert(Colorful, grey), rgb),
ppm:write(Gray, "gray.ppm"),
</syntaxhighlight>
=={{header|Euphoria}}==
<langsyntaxhighlight lang="euphoria">include get.e
 
function get2(integer fn)
Line 481 ⟶ 1,548:
close(fn)
return image
end function</langsyntaxhighlight>
 
Converting an image to grayscale:
<langsyntaxhighlight lang="euphoria">sequence image
image = read_ppm("image.ppm")
image = to_gray(image)
image = to_color(image)
write_ppm("image_gray.ppm",image)</langsyntaxhighlight>
 
=={{header|FBSL}}==
Read a colored PPM file, convert it to grayscale and write back to disk under a different name. Sanity checks are omitted for brevity.
Line 495 ⟶ 1,561:
'''24-bpp P6 PPM solution:'''
[[File:FBSLLena.png|right]]
<langsyntaxhighlight lang="qbasic">#ESCAPECHARS ON
 
DIM colored = ".\\Lena.ppm", grayscale = ".\\LenaGry.ppm"
Line 512 ⟶ 1,578:
NEXT
 
FILEPUT(FILEOPEN(grayscale, BINARY_NEW), FILEGET): FILECLOSE(FILEOPEN) ' Save buffer</langsyntaxhighlight>
 
=={{header|Forth}}==
<langsyntaxhighlight lang="forth">: read-ppm { fid -- bmp }
pad dup 80 fid read-line throw 0= abort" Partial line"
s" P6" compare abort" Only P6 supported."
Line 552 ⟶ 1,617:
: bsize ( bmp -- len ) bdim * pixels bdata ;
 
test dup bsize test2 dup bsize compare . \ 0 if identical</langsyntaxhighlight>
 
=={{header|Fortran}}==
{{works with|Fortran|90 and later}}
Line 559 ⟶ 1,623:
(This function is part of module RCImageIO, see [[Write ppm file#Fortran|Write ppm file]])
 
<langsyntaxhighlight lang="fortran">subroutine read_ppm(u, img)
integer, intent(in) :: u
type(rgbimage), intent(out) :: img
Line 600 ⟶ 1,664:
end if
 
end subroutine read_ppm</langsyntaxhighlight>
 
'''Notes''':
Line 606 ⟶ 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}}==
<langsyntaxhighlight lang="go">package raster
 
import (
Line 674 ⟶ 1,775:
}
return b, f.Close()
}</langsyntaxhighlight>
Demonstration program, also demonstrating functions from task [[Grayscale image]]:
<langsyntaxhighlight lang="go">package main
 
// Files required to build supporting package raster are found in:
Line 703 ⟶ 1,804:
fmt.Println(err)
}
}</langsyntaxhighlight>
 
=={{header|Haskell}}==
The definition of <tt>Bitmap.Netpbm.readNetpbm</tt> is given [[Write ppm file|here]].
<langsyntaxhighlight lang="haskell">import Bitmap
import Bitmap.RGB
import Bitmap.Gray
Line 718 ⟶ 1,818:
(readNetpbm "original.ppm" :: IO (Image RealWorld RGB)) >>=
stToIO . toGrayImage >>=
writeNetpbm "new.pgm"</langsyntaxhighlight>
The above writes a PGM, not a PPM, since the image being output is in grayscale. If you actually want a gray PPM, convert the <tt>Image RealWorld Gray</tt> back to an <tt>Image RealWorld RGB</tt> first:
<langsyntaxhighlight lang="haskell">main =
(readNetpbm "original.ppm" :: IO (Image RealWorld RGB)) >>=
stToIO . (toRGBImage <=< toGrayImage) >>=
writeNetpbm "new.ppm"</langsyntaxhighlight>
 
=={{header|J}}==
'''Solution:'''<br>
Uses <tt>makeRGB</tt> from [[Basic bitmap storage#J|Basic bitmap storage]].
<langsyntaxhighlight lang="j">require 'files'
 
readppm=: monad define
Line 737 ⟶ 1,836:
if. (_99 0 +./@e. wbyh,maxval) +. 'P6' -.@-: 2{.t do. _1 return. end.
(a. i. dat) makeRGB |.wbyh NB. convert to basic bitmap format
)</langsyntaxhighlight>
 
'''Example:'''<br>
Using utilities and file from [[Grayscale image#J|Grayscale image]] and [[Write ppm file#J|Write ppm file]].<br>
Writes a gray PPM file (a color format) which is bigger than necessary. A PGM file would be more appropriate.
<langsyntaxhighlight lang="j">myimg=: readppm jpath '~temp/myimg.ppm'
myimgGray=: toColor toGray myimg
myimgGray writeppm jpath '~temp/myimgGray.ppm'</langsyntaxhighlight>
 
=={{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}}==
<lang{{works with|Julia>|0.6}}
using Color, Images, FixedPointNumbers
 
<syntaxhighlight lang="julia">using Images, FileIO, Netpbm
const M_RGB_Y = reshape(Color.M_RGB_XYZ[2,:], 3)
 
rgbimg = load("data/bitmapInputTest.ppm")
function rgb2gray(img::Image)
greyimg = Gray.(rgbimg)
g = red(img)*M_RGB_Y[1] + green(img)*M_RGB_Y[2] + blue(img)*M_RGB_Y[3]
save("data/bitmapOutputTest.ppm", greyimg)</syntaxhighlight>
g = clamp(g, 0.0, 1.0)
=={{header|Kotlin}}==
return grayim(g)
For convenience, we repeat the code for the class used in the [[Bitmap]] task here and integrate the code in the [[Grayscale image]] task within it.
end
<syntaxhighlight lang="scala">// Version 1.2.40
 
import java.awt.Color
ima = imread("bitmap_read_ppm_in.ppm")
import java.awt.Graphics
imb = convert(Image{Gray{Ufixed8}}, ima)
import java.awt.image.BufferedImage
imwrite(imb, "bitmap_read_ppm_out.png")
import java.io.FileInputStream
</lang>
import java.io.PushbackInputStream
import java.io.File
import javax.imageio.ImageIO
 
class BasicBitmapStorage(width: Int, height: Int) {
{{out}}
val image = BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR)
[https://raw.githubusercontent.com/MichaeLeroy/rosetta-code/master/julia/support/color_chips.png Input] and [https://raw.githubusercontent.com/MichaeLeroy/rosetta-code/master/julia/completed/bitmap_read_ppm_out.png output] images in <tt>PNG</tt> format. The <tt>PPM</tt> input file was created using a variant of the Julia PPM/Write [https://github.com/MichaeLeroy/rosetta-code/blob/master/julia/support/color_chips.jl solution] to provide something reasonably colorful.
 
fun fill(c: Color) {
=={{header|Mathematica}}/ {{header|Wolfram Language}}==
val g = image.graphics
<lang Mathematica>Import["file.ppm","PPM"]
g.color = c
</lang>
g.fillRect(0, 0, image.width, image.height)
}
 
fun setPixel(x: Int, y: Int, c: Color) = image.setRGB(x, y, c.getRGB())
 
fun getPixel(x: Int, y: Int) = Color(image.getRGB(x, y))
 
fun toGrayScale() {
for (x in 0 until image.width) {
for (y in 0 until image.height) {
var rgb = image.getRGB(x, y)
val red = (rgb shr 16) and 0xFF
val green = (rgb shr 8) and 0xFF
val blue = rgb and 0xFF
val lumin = (0.2126 * red + 0.7152 * green + 0.0722 * blue).toInt()
rgb = (lumin shl 16) or (lumin shl 8) or lumin
image.setRGB(x, y, rgb)
}
}
}
}
 
fun PushbackInputStream.skipComment() {
while (read().toChar() != '\n') {}
}
 
fun PushbackInputStream.skipComment(buffer: ByteArray) {
var nl: Int
while (true) {
nl = buffer.indexOf(10) // look for newline at end of comment
if (nl != -1) break
read(buffer) // read another buffer full if newline not yet found
}
val len = buffer.size
if (nl < len - 1) unread(buffer, nl + 1, len - nl - 1)
}
 
fun Byte.toUInt() = if (this < 0) 256 + this else this.toInt()
 
fun main(args: Array<String>) {
// use file, output.ppm, created in the Bitmap/Write a PPM file task
val pbis = PushbackInputStream(FileInputStream("output.ppm"), 80)
pbis.use {
with (it) {
val h1 = read().toChar()
val h2 = read().toChar()
val h3 = read().toChar()
if (h1 != 'P' || h2 != '6' || h3 != '\n') {
println("Not a P6 PPM file")
System.exit(1)
}
val sb = StringBuilder()
while (true) {
val r = read().toChar()
if (r == '#') { skipComment(); continue }
if (r == ' ') break // read until space reached
sb.append(r.toChar())
}
val width = sb.toString().toInt()
sb.setLength(0)
while (true) {
val r = read().toChar()
if (r == '#') { skipComment(); continue }
if (r == '\n') break // read until new line reached
sb.append(r.toChar())
}
val height = sb.toString().toInt()
sb.setLength(0)
while (true) {
val r = read().toChar()
if (r == '#') { skipComment(); continue }
if (r == '\n') break // read until new line reached
sb.append(r.toChar())
}
val maxCol = sb.toString().toInt()
if (maxCol !in 0..255) {
println("Maximum color value is outside the range 0..255")
System.exit(1)
}
var buffer = ByteArray(80)
// get rid of any more opening comments before reading data
while (true) {
read(buffer)
if (buffer[0].toChar() == '#') {
skipComment(buffer)
}
else {
unread(buffer)
break
}
}
// read data
val bbs = BasicBitmapStorage(width, height)
buffer = ByteArray(width * 3)
var y = 0
while (y < height) {
read(buffer)
for (x in 0 until width) {
val c = Color(
buffer[x * 3].toUInt(),
buffer[x * 3 + 1].toUInt(),
buffer[x * 3 + 2].toUInt()
)
bbs.setPixel(x, y, c)
}
y++
}
// convert to grayscale and save to a file
bbs.toGrayScale()
val grayFile = File("output_gray.jpg")
ImageIO.write(bbs.image, "jpg", grayFile)
}
}
}</syntaxhighlight>
=={{header|Lua}}==
<langsyntaxhighlight lang="lua">function Read_PPM( filename )
local fp = io.open( filename, "rb" )
if fp == nil then return nil end
Line 804 ⟶ 2,164:
return image
end</langsyntaxhighlight>
=={{header|M2000 Interpreter}}==
Now function Bitmap has double signature. With two numbers make a bitmap,with all pixels white. With one number, expect that it is a file number and read file, and then return the bitmap.
 
<syntaxhighlight lang="m2000 interpreter">
Module Checkit {
Function Bitmap {
If match("NN") then {
Read x as long, y as long
} else.if Match("N") Then {
\\ is a file?
Read f
if not Eof(f) then {
Line Input #f, p3$
If p3$="P3" Then {
Line Input #f, Comment$
if left$(Comment$,1)="#" then {
Line Input #f, Dimension$
} else Dimension$=Comment$
long x=Val(piece$(Dimension$," ")(0))
long y=Val(piece$(Dimension$," ")(1))
do {
Line Input #f, P255$
} until left$(P255$, 1)<>"#"
If not P255$="255" then Error "Not proper ppm format"
}
}
} else Error "No proper arguments"
if x<1 or y<1 then Error "Wrong dimensions"
structure rgb {
red as byte
green as byte
blue as byte
}
m=len(rgb)*x mod 4
if m>0 then m=4-m ' add some bytes to raster line
m+=len(rgb) *x
Structure rasterline {
{
pad as byte*m
}
\\ union pad+hline
hline as rgb*x
}
Structure Raster {
magic as integer*4
w as integer*4
h as integer*4
lines as rasterline*y
}
Buffer Clear Image1 as Raster
\\ 24 chars as header to be used from bitmap render build in functions
Return Image1, 0!magic:="cDIB", 0!w:=Hex$(x,2), 0!h:=Hex$(y, 2)
\\ fill white (all 255)
\\ Str$(string) convert to ascii, so we get all characters from words width to byte width
if not valid(f) then Return Image1, 0!lines:=Str$(String$(chrcode$(255), Len(rasterline)*y))
Buffer Clear Pad as Byte*4
SetPixel=Lambda Image1, Pad,aLines=Len(Raster)-Len(Rasterline), blines=-Len(Rasterline) (x, y, c) ->{
where=alines+3*x+blines*y
if c>0 then c=color(c)
c-!
Return Pad, 0:=c as long
Return Image1, 0!where:=Eval(Pad, 2) as byte, 0!where+1:=Eval(Pad, 1) as byte, 0!where+2:=Eval(Pad, 0) as byte
}
GetPixel=Lambda Image1,aLines=Len(Raster)-Len(Rasterline), blines=-Len(Rasterline) (x,y) ->{
where=alines+3*x+blines*y
=color(Eval(image1, where+2 as byte), Eval(image1, where+1 as byte), Eval(image1, where as byte))
}
StrDib$=Lambda$ Image1, Raster -> {
=Eval$(Image1, 0, Len(Raster))
}
CopyImage=Lambda Image1 (image$) -> {
if left$(image$,12)=Eval$(Image1, 0, 24 ) Then {
Return Image1, 0:=Image$
} Else Error "Can't Copy Image"
}
Export2File=Lambda Image1, x, y (f) -> {
\\ use this between open and close
Print #f, "P3"
Print #f,"# Created using M2000 Interpreter"
Print #f, x;" ";y
Print #f, 255
x2=x-1
where=24
For y1= 0 to y-1 {
a$=""
For x1=0 to x2 {
Print #f, a$;Eval(Image1, where+2 as byte);" ";
Print #f, Eval(Image1, where+1 as byte);" ";
Print #f, Eval(Image1, where as byte);
where+=3
a$=" "
}
Print #f
m=where mod 4
if m<>0 then where+=4-m
}
}
if valid(F) then {
'load RGB values form file
x0=x-1
where=24
For y1=y-1 to 0 {
do {
Line Input #f, aline$
} until left$(aline$,1)<>"#"
flush ' empty stack
Stack aline$ ' place all values to stack as FIFO
For x1=0 to x0 {
\\ now read from stack using Number
Return Image1, 0!where+2:=Number as byte, 0!where+1:=Number as byte, 0!where:=Number as byte
where+=3
}
m=where mod 4
if m<>0 then where+=4-m
}
}
Group Bitmap {
SetPixel=SetPixel
GetPixel=GetPixel
Image$=StrDib$
Copy=CopyImage
ToFile=Export2File
}
=Bitmap
}
A=Bitmap(10, 10)
Call A.SetPixel(5,5, color(128,0,255))
Open "A.PPM" for Output as #F
Call A.ToFile(F)
Close #f
Open "A.PPM" for Input as #F
Try {
C=Bitmap(f)
Copy 400*twipsx,200*twipsy use C.Image$()
}
Close #f
' is the same as this one
Open "A.PPM" for Input as #F
Line Input #f, p3$
If p3$="P3" Then {
Line Input #f, Comment$
if left$(Comment$,1)="#" then {
Line Input #f, Dimension$
} else Dimension$=Comment$
Long x=Val(piece$(Dimension$," ")(0))
Long y=Val(piece$(Dimension$," ")(1))
do {
Line Input #f, P255$
} until left$(P255$, 1)<>"#"
If not P255$="255" then Error "Not proper ppm format"
B=Bitmap(x, y)
x0=x-1
For y1=y-1 to 0 {
do {
Line Input #f, aline$
} until left$(aline$,1)<>"#"
flush ' empty stack
Stack aline$ ' place all values to stack as FIFO
For x1=0 to x0 {
\\ now read from stack
Read red, green, blue
Call B.setpixel(x1, y1, Color(red, green, blue))
}
}
}
Close #f
If valid("B") then Copy 200*twipsx,200*twipsy use B.Image$()
}
Checkit
 
</syntaxhighlight>
=={{header|Mathematica}}/ {{header|Wolfram Language}}==
<syntaxhighlight lang="mathematica">Import["file.ppm","PPM"]
</syntaxhighlight>
=={{header|Nim}}==
<langsyntaxhighlight lang="nim">import strutils
import bitmap
import streams
 
type FormatError = object of CatchableError
 
# States used to parse the header.
type State = enum waitingMagic, waitingWidth, waitingHeight, waitingColors
 
#---------------------------------------------------------------------------------------------------
 
iterator tokens(f: Stream): tuple[value: string, lastInLine: bool] =
## Yield the tokens in the header.
for line in f.lines:
if not line.startsWith('#'):
let fields = line.splitWhitespace()
for i, t in fields:
yield (t, i == fields.high)
 
#---------------------------------------------------------------------------------------------------
 
proc getInt(s: string): int {.inline.} =
## Try to parse an int. Raise an exception if not an integer.
try:
result = s.parseInt()
except ValueError:
raise newException(FormatError, "Invalid value")
 
#---------------------------------------------------------------------------------------------------
proc readPPM(f: TFile): Image =
if f.readLine != "P6":
raise newException(E_base, "Invalid file format")
 
proc header(f: Stream): tuple[width, height: Index] =
var line = ""
## Read the header and retrun the image width and height.
while f.readLine(line):
var state = waitingMagic
if line[0] != '#':
for (token, lastInLine) in f.tokens:
case state
of waitingMagic:
if token != "P6":
raise newException(FormatError, "Invalid file header")
of waitingWidth:
result.width = token.getInt()
of waitingHeight:
result.height = token.getInt()
of waitingColors:
if token.getInt() != 255:
raise newException(FormatError, "Invalid number of colors")
if not lastInLine:
raise newException(FormatError, "Invalid data after number of colors")
break
state = succ(state)
 
#---------------------------------------------------------------------------------------------------
var parts = line.split(" ")
result = img(parseInt parts[0], parseInt parts[1])
 
proc readPPM*(f: Stream): Image =
if f.readLine != "255":
## Read a PPM file from a stream into an image.
raise newException(E_base, "Invalid file format")
 
let header = f.header()
result = newImage(header.width, header.height)
 
var
arr: array[256, int8]
read = f.readBytesreadData(addr(arr, 0), 256)
pos = 0
 
while read != 0:
for i in 0 .. < read:
case pos mod 3
of 0: result.pixels[pos div 3].r = arr[i].uint8
Line 836 ⟶ 2,414:
of 2: result.pixels[pos div 3].b = arr[i].uint8
else: discard
 
inc pos
 
read = f.readBytesreadData(addr(arr, 0), 256)</lang>
 
if pos != 3 * result.w * result.h:
raise newException(FormatError, "Truncated file")
 
#---------------------------------------------------------------------------------------------------
 
proc readPPM*(filename: string): Image =
## Load a PPM file into an image.
 
var file = openFileStream(filename, fmRead)
result = file.readPPM()
file.close()
 
#———————————————————————————————————————————————————————————————————————————————————————————————————
 
when isMainModule:
let image = readPPM("output.ppm")
echo image.h, " ", image.w</syntaxhighlight>
=={{header|OCaml}}==
 
<langsyntaxhighlight lang="ocaml">let read_ppm ~filename =
let ic = open_in filename in
let line = input_line ic in
Line 880 ⟶ 2,474:
r_channel,
g_channel,
b_channel)</langsyntaxhighlight>
 
and converting a given color file to grayscale:
<langsyntaxhighlight lang="ocaml">let () =
let img = read_ppm ~filename:"logo.ppm" in
let img = to_color(to_grayscale ~img) in
output_ppm ~oc:stdout ~img;
;;</langsyntaxhighlight>
sending the result to <tt>stdout</tt> allows to see the result without creating a temporary file sending it through a pipe to the '''display''' utility of ''ImageMagick'':
ocaml script.ml | display -
 
=={{header|Oz}}==
The read function in module <code>"BitmapIO.oz"</code>:
<langsyntaxhighlight lang="oz">functor
import
Bitmap
Line 976 ⟶ 2,569:
 
%% Omitted: Write
end</langsyntaxhighlight>
 
The actual task:
<langsyntaxhighlight lang="oz">declare
[BitmapIO Grayscale] = {Module.link ['BitmapIO.ozf' 'Grayscale.ozf']}
 
Line 985 ⟶ 2,578:
G = {Grayscale.toGraymap B}
in
{BitmapIO.write {Grayscale.fromGraymap G} "greyimage.ppm"}</langsyntaxhighlight>
 
=={{header|Perl}}==
 
{{libheader|Imlib2}}
 
<langsyntaxhighlight lang="perl">#! /usr/bin/perl
 
use strict;
Line 1,004 ⟶ 2,596:
$img->save("out1.png");
 
exit 0;</langsyntaxhighlight>
=={{header|Phix}}==
Based on [[Bitmap/Read_a_PPM_file#Euphoria|Euphoria]], requires write_ppm() from [[Bitmap/Write_a_PPM_file#Phix|Write_a_PPM_file]], to_grey from [[Grayscale_image#Phix|Grayscale_image]]<br>
Note that demo\rosetta\Bitmap_read_ppm.exw is just the last 3 lines with the include ppm.e since that contains the read_ppm() (abeit with a few more options and other tweaks) also used by several other examples, and covers the above requirements. Results may be verified with demo\rosetta\viewppm.exw
<syntaxhighlight lang="phix">-- demo\rosetta\Bitmap_read_ppm.exw (runnable version)
 
function read_ppm(string filename)
 
=={{header|Perl 6}}==
{{works with|Rakudo|2017.09}}
Uses pieces from [[Bitmap#Perl_6| Bitmap]], [[Bitmap/Write_a_PPM_file#Perl_6| Write a PPM file]] and [[Grayscale_image#Perl_6| Grayscale image]] tasks. Included here to make a complete, runnable program.
 
<lang perl6>class Pixel { has UInt ($.R, $.G, $.B) }
class Bitmap {
has UInt ($.width, $.height);
has Pixel @.data;
}
 
role PGM {
has @.GS;
method P5 returns Blob {
"P5\n{self.width} {self.height}\n255\n".encode('ascii')
~ Blob.new: self.GS
}
}
 
sub load-ppm ( $ppm ) {
my $fh = $ppm.IO.open( :enc('ISO-8859-1') );
my $type = $fh.get;
my ($width, $height) = $fh.get.split: ' ';
my $depth = $fh.get;
Bitmap.new( width => $width.Int, height => $height.Int,
data => ( $fh.slurp.ords.rotor(3).map:
{ Pixel.new(R => $_[0], G => $_[1], B => $_[2]) } )
)
}
 
sub grayscale ( Bitmap $bmp ) {
$bmp.GS = map { (.R*0.2126 + .G*0.7152 + .B*0.0722).round(1) min 255 }, $bmp.data;
}
 
my $filename = './camelia.ppm';
 
my Bitmap $b = load-ppm( $filename ) but PGM;
 
grayscale($b);
 
'./camelia-gs.pgm'.IO.open(:bin, :w).write: $b.P5;</lang>
 
See [https://github.com/thundergnat/rc/blob/master/img/camelia.png camelia], and [https://github.com/thundergnat/rc/blob/master/img/camelia-gs.png camelia-gs] images. (converted to .png as .ppm format is not widely supported).
 
=={{header|Phix}}==
Based on [[Bitmap/Read_a_PPM_file#Euphoria|Euphoria]], requires write_ppm() from [[Bitmap/Write_a_PPM_file#Phix|Write_a_PPM_file]], to_gray from [[Grayscale_image#Phix|Grayscale_image]]
<lang Phix>function read_ppm(sequence filename)
sequence image, line
integer dimx, dimy, maxcolor
Line 1,076 ⟶ 2,626:
return image
end function
 
--include ppm.e -- read_ppm(), write_ppm(), to_grey() (as distributed, instead of the above)
sequence img = read_ppm("Lena.ppm")
img = to_grayto_grey(img)
write_ppm("LenaGray.ppm",img)</langsyntaxhighlight>
 
=={{header|PicoLisp}}==
<langsyntaxhighlight PicoLisplang="picolisp">(de ppmRead (File)
(in File
(unless (and `(hex "5036") (rd 2)) # P6
Line 1,099 ⟶ 2,650:
(map
'((X) (set X (list (rd 1) (rd 1) (rd 1))))
Y ) ) ) ) ) )</langsyntaxhighlight>
Read a color image "img.ppm", convert and write to "img.pgm":
<langsyntaxhighlight PicoLisplang="picolisp">(pgmWrite (ppm->pgm (ppmRead "img.ppm")) "img.pgm")</langsyntaxhighlight>
 
=={{header|PL/I}}==
<syntaxhighlight lang="pl/i">
<lang PL/I>
/* BITMAP FILE: read in a file in PPM format, P6 (binary). 14/5/2010 */
test: procedure options (main);
Line 1,168 ⟶ 2,718:
return (index('0123456789', ch) > 0);
end is_digit;
end test;</langsyntaxhighlight>
 
=={{header|PureBasic}}==
<langsyntaxhighlight PureBasiclang="purebasic">Structure PPMColor
r.c
g.c
Line 1,228 ⟶ 2,777:
EndIf
EndIf
EndProcedure</langsyntaxhighlight>
 
To complete the task, the following code should be added to the above fragment and to the PureBasic solutions for [[Grayscale_image#PureBasic|Grayscale image]] and [[Bitmap/Write_a_PPM_file#PureBasic|Write a PPM file]]
<langsyntaxhighlight PureBasiclang="purebasic">Define file.s, file2.s, image = 3
file = OpenFileRequester("Select source image file", "", "PPM image (*.ppm)|*.ppm", 0)
If file And LCase(GetExtensionPart(file)) = "ppm"
Line 1,238 ⟶ 2,787:
file2 = Left(file, Len(file) - Len(GetExtensionPart(file))) + "_grayscale." + GetExtensionPart(file)
SaveImageAsPPM(image, file2, 1)
EndIf</langsyntaxhighlight>
 
=={{header|Python}}==
{{works with|Python|3.1}}
 
Extending the example given [[Basic_bitmap_storage#Alternative_version|here]]
<langsyntaxhighlight lang="python"># With help from http://netpbm.sourceforge.net/doc/ppm.html
 
# String masquerading as ppm file (version P3)
Line 1,312 ⟶ 2,860:
4 4 4 0 0 0 0 0 0 0 0 0
 
'''</langsyntaxhighlight>
 
=={{header|Racket}}==
<langsyntaxhighlight lang="racket">
#lang racket
(require racket/draw)
Line 1,338 ⟶ 2,885:
(send dc draw-point x y)))
bm))
</syntaxhighlight>
</lang>
=={{header|Raku}}==
(formerly Perl 6)
{{works with|Rakudo|2017.09}}
Uses pieces from [[Bitmap#Raku| Bitmap]], [[Bitmap/Write_a_PPM_file#Raku| Write a PPM file]] and [[Grayscale_image#Raku| Grayscale image]] tasks. Included here to make a complete, runnable program.
 
<syntaxhighlight lang="raku" line>class Pixel { has UInt ($.R, $.G, $.B) }
class Bitmap {
has UInt ($.width, $.height);
has Pixel @.data;
}
 
role PGM {
has @.GS;
method P5 returns Blob {
"P5\n{self.width} {self.height}\n255\n".encode('ascii')
~ Blob.new: self.GS
}
}
 
sub load-ppm ( $ppm ) {
my $fh = $ppm.IO.open( :enc('ISO-8859-1') );
my $type = $fh.get;
my ($width, $height) = $fh.get.split: ' ';
my $depth = $fh.get;
Bitmap.new( width => $width.Int, height => $height.Int,
data => ( $fh.slurp.ords.rotor(3).map:
{ Pixel.new(R => $_[0], G => $_[1], B => $_[2]) } )
)
}
 
sub grayscale ( Bitmap $bmp ) {
$bmp.GS = map { (.R*0.2126 + .G*0.7152 + .B*0.0722).round(1) min 255 }, $bmp.data;
}
 
my $filename = './camelia.ppm';
 
my Bitmap $b = load-ppm( $filename ) but PGM;
 
grayscale($b);
 
'./camelia-gs.pgm'.IO.open(:bin, :w).write: $b.P5;</syntaxhighlight>
 
See [https://github.com/thundergnat/rc/blob/master/img/camelia.png camelia], and [https://github.com/thundergnat/rc/blob/master/img/camelia-gs.png camelia-gs] images. (converted to .png as .ppm format is not widely supported).
=={{header|REXX}}==
The input file &nbsp; '''Lenna50.ppm''' &nbsp; is a '''PPM''' format of
<br>the input file &nbsp;&nbsp; '''Lenna50.jpg''' &nbsp;&nbsp; used elsewhere on Rosetta Code.
 
This REXX program handles alternative delimiters as well as comments within the PPM header.
<syntaxhighlight lang="rexx">/*REXX program reads a PPM formatted image file, and creates a gray─scale image of it. */
parse arg iFN oFN /*obtain optional argument from the CL.*/
if iFN=='' | iFN=="," then iFN= 'Lenna50' /*Not specified? Then use the default.*/
if oFN=='' | oFN=="," then oFN= 'greyscale' /* " " " " " " */
iFID= iFN'.ppm'; oFID= oFN'.ppm' /*complete the input and output FIDs.*/
call charin iFID, 1, 0 /*set the position of the input file. */
y=charin(iFID, , copies(9, digits() ) ) /*read the entire input file ───► X */
parse var y id 3 c 4 3 width height # pixels /*extract header info from the PPM hdr.*/
LF= 'a'x /*define a comment separator (in hdr).*/ /* ◄─── LF delimiters & comments*/
if c==LF then do; commentEND=pos(LF, y, 4) /*point to the last char in the comment*/ /* ◄─── LF delimiters & comments*/
parse var y =(commentEND) +1 width height # pixels /* ◄─── LF delimiters & comments*/
end /* ◄─── LF delimiters & comments*/
/* [↓] has an alternative delimiter? */ /* ◄─── LF delimiters & comments*/
z=pos(LF, height); if z\==0 then parse var height height =(z) +1 # pixels /* ◄─── LF delimiters & comments*/
z=pos(LF, # ); if z\==0 then parse var # # =(z) +1 pixels /* ◄─── LF delimiters & comments*/
chunk=4000 /*chunk size to be written at one time.*/
LenPixels= length(pixels)
 
do j=0 for 256; _=d2c(j); @._=j; @@.j=_ /*build two tables for fast conversions*/
end /*j*/
 
call charout oFID, , 1 /*set the position of the output file. */
call charout oFID, id || width height #' ' /*write the header followed by a blank.*/
!=1
do until !>=LenPixels; $= /*$: partial output string so far.*/
do !=! by 3 for chunk /*chunk: # pixels converted at 1 time.*/
parse var pixels =(!) r +1 g +1 b +1 /*obtain the next RGB of a PPM pixel.*/
if r=='' then leave /*has the end─of─string been reached? */
_=(.2126*@.r + .7152*@.g + .0722*@.b )%1 /*an integer RGB greyscale of a pixel. */
$=$ || @@._ || @@._ || @@._ /*lump (grey) R G B pixels together. */
end /*!*/ /* [↑] D2C converts decimal ───► char*/
call charout oFID, $ /*write the next bunch of pixels. */
end /*until*/
 
call charout oFID /*close the output file just to be safe*/
say 'File ' oFID " was created." /*stick a fork in it, we're all done. */</syntaxhighlight>
{{out|output}}
<pre>
File greyscale.ppm was created.
</pre>
=={{header|Ruby}}==
Extending [[Basic_bitmap_storage#Ruby]]
<langsyntaxhighlight lang="ruby">class Pixmap
# 'open' is a class method
def self.open(filename)
Line 1,375 ⟶ 3,008:
 
# then, convert to grayscale
Pixmap.open('testcross.ppm').to_grayscale!.save('testgray.ppm')</langsyntaxhighlight>
=={{header|Rust}}==
<syntaxhighlight lang="rust">
parser.rs:
use super::{Color, ImageFormat};
use std::str::from_utf8;
use std::str::FromStr;
 
pub fn parse_version(input: &[u8]) -> nom::IResult<&[u8], ImageFormat> {
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::character::complete::line_ending;
use nom::combinator::map;
use nom::sequence::terminated;
 
// starts with P3/P6 ends with a CR/LF
terminated(
alt((
map(tag("P3".as_bytes()), |_| ImageFormat::P3),
map(tag("P6".as_bytes()), |_| ImageFormat::P6),
)),
line_ending,
)(input)
}
 
pub fn parse_image_attributes(input: &[u8]) -> nom::IResult<&[u8], (usize, usize, usize)> {
use nom::character::complete::line_ending;
use nom::character::complete::{digit1, space1};
use nom::sequence::terminated;
use nom::sequence::tuple;
 
// 3 numbers separated by spaces ends with a CR/LF
terminated(tuple((digit1, space1, digit1, space1, digit1)), line_ending)(input).map(
|(next_input, result)| {
(
next_input,
(
usize::from_str_radix(from_utf8(result.0).unwrap(), 10).unwrap(),
usize::from_str_radix(from_utf8(result.2).unwrap(), 10).unwrap(),
usize::from_str_radix(from_utf8(result.4).unwrap(), 10).unwrap(),
),
)
},
)
}
 
pub fn parse_color_binary(input: &[u8]) -> nom::IResult<&[u8], Color> {
use nom::number::complete::u8 as nom_u8;
use nom::sequence::tuple;
 
tuple((nom_u8, nom_u8, nom_u8))(input).map(|(next_input, res)| {
(
next_input,
Color {
red: res.0,
green: res.1,
blue: res.2,
},
)
})
}
 
pub fn parse_data_binary(input: &[u8]) -> nom::IResult<&[u8], Vec<Color>> {
use nom::multi::many0;
many0(parse_color_binary)(input)
}
 
pub fn parse_color_ascii(input: &[u8]) -> nom::IResult<&[u8], Color> {
use nom::character::complete::{digit1, space0, space1};
use nom::sequence::tuple;
 
tuple((digit1, space1, digit1, space1, digit1, space0))(input).map(|(next_input, res)| {
(
next_input,
Color {
red: u8::from_str(from_utf8(res.0).unwrap()).unwrap(),
green: u8::from_str(from_utf8(res.2).unwrap()).unwrap(),
blue: u8::from_str(from_utf8(res.4).unwrap()).unwrap(),
},
)
})
}
 
pub fn parse_data_ascii(input: &[u8]) -> nom::IResult<&[u8], Vec<Color>> {
use nom::multi::many0;
many0(parse_color_ascii)(input)
}
 
 
lib.rs:
extern crate nom;
extern crate thiserror;
mod parser;
 
use std::default::Default;
use std::fmt;
use std::io::{BufWriter, Error, Write};
use std::ops::{Index, IndexMut};
use std::{fs::File, io::Read};
use thiserror::Error;
 
#[derive(Copy, Clone, Default, PartialEq, Debug)]
pub struct Color {
pub red: u8,
pub green: u8,
pub blue: u8,
}
 
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum ImageFormat {
P3,
P6,
}
 
impl From<&str> for ImageFormat {
fn from(i: &str) -> Self {
match i.to_lowercase().as_str() {
"p3" => ImageFormat::P3,
"p6" => ImageFormat::P6,
_ => unimplemented!("no other formats supported"),
}
}
}
 
impl fmt::Display for ImageFormat {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ImageFormat::P3 => {
write!(f, "P3")
}
ImageFormat::P6 => {
write!(f, "P6")
}
}
}
}
 
#[derive(Error, Debug)]
pub enum ImageError {
#[error("File not found")]
FileNotFound,
#[error("File not readable")]
FileNotReadable,
#[error("Invalid header information")]
InvalidHeader,
#[error("Invalid information in the data block")]
InvalidData,
#[error("Invalid max color information")]
InvalidMaxColor,
#[error("File is incomplete")]
IncompleteFile,
#[error("unknown data store error")]
Unknown,
}
pub struct Image {
pub format: ImageFormat,
pub width: usize,
pub height: usize,
pub data: Vec<Color>,
}
 
impl Image {
#[must_use]
pub fn new(width: usize, height: usize) -> Self {
Self {
format: ImageFormat::P6,
width,
height,
data: vec![Color::default(); width * height],
}
}
 
pub fn fill(&mut self, color: Color) {
for elem in &mut self.data {
*elem = color;
}
}
 
/// # Errors
///
/// Will return `Error` if `filename` does not exist or the user does not have
/// permission to write to it, or the write operation fails.
pub fn write_ppm(&self, filename: &str) -> Result<(), Error> {
let file = File::create(filename)?;
let mut writer = BufWriter::new(file);
writeln!(&mut writer, "{}", self.format.to_string())?;
writeln!(&mut writer, "{} {} 255", self.width, self.height)?;
match self.format {
ImageFormat::P3 => {
writer.write_all(
&self
.data
.iter()
.flat_map(|color| {
vec![
color.red.to_string(),
color.green.to_string(),
color.blue.to_string(),
]
})
.collect::<Vec<String>>()
.join(" ")
.as_bytes(),
)?;
}
ImageFormat::P6 => {
writer.write_all(
&self
.data
.iter()
.flat_map(|color| vec![color.red, color.green, color.blue])
.collect::<Vec<u8>>(),
)?;
}
}
Ok(())
}
 
/// # Panics
///
/// Panics if the format is not P6 or P3 PPM
/// # Errors
///
/// Will return `Error` if `filename` does not exist or the user does not have
/// permission to read it or the read operation fails, or the file format does not
/// match the specification
pub fn read_ppm(filename: &str) -> Result<Image, ImageError> {
let mut file = File::open(filename).map_err(|_| ImageError::FileNotFound)?;
let mut data: Vec<u8> = Vec::new();
file.read_to_end(&mut data)
.map_err(|_| ImageError::FileNotReadable)?;
 
let (i, format) = parser::parse_version(&data).map_err(|_| ImageError::InvalidHeader)?;
let (i, (width, height, max_color)) =
parser::parse_image_attributes(i).map_err(|_| ImageError::InvalidHeader)?;
 
if max_color != 255 {
return Err(ImageError::InvalidMaxColor);
}
 
let (_, data) = match format {
ImageFormat::P3 => parser::parse_data_ascii(i).map_err(|_| ImageError::InvalidData)?,
ImageFormat::P6 => parser::parse_data_binary(i).map_err(|_| ImageError::InvalidData)?,
};
 
if data.len() != height * width {
return Err(ImageError::IncompleteFile);
};
 
Ok(Image {
format,
width,
height,
data,
})
}
}
 
impl Index<(usize, usize)> for Image {
type Output = Color;
 
fn index(&self, (x, y): (usize, usize)) -> &Color {
&self.data[x + y * self.width]
}
}
 
impl IndexMut<(usize, usize)> for Image {
fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut Color {
&mut self.data[x + y * self.width]
}
}
 
 
use bitmap::Image;
 
// see read_ppm implementation in the bitmap library
 
pub fn main() {
// read a PPM image, which was produced by the write-a-ppm-file task
let image = Image::read_ppm("./test_image.ppm").unwrap();
 
println!("Read using nom parsing:");
println!("Format: {:?}", image.format);
println!("Dimensions: {} x {}", image.height, image.width);
}
</syntaxhighlight>
=={{header|Scala}}==
Uses the [[Basic_bitmap_storage#Scala|Basic Bitmap Storage]] and [[Grayscale_image#Scala|Grayscale Bitmap]] classes.
Line 1,382 ⟶ 3,299:
See also Task [[Write_ppm_file#Scala|Write a PPM File]] for save code.
 
<langsyntaxhighlight lang="scala">import scala.io._
import scala.swing._
import java.io._
Line 1,427 ⟶ 3,344:
out
}
}</langsyntaxhighlight>
 
Usage:
<langsyntaxhighlight lang="scala">object PixmapTest {
def main(args: Array[String]): Unit = {
val img=Pixmap.load("image.ppm").get
Line 1,444 ⟶ 3,361:
}
}
}</langsyntaxhighlight>
 
=={{header|Seed7}}==
<langsyntaxhighlight lang="seed7">$ include "seed7_05.s7i";
include "draw.s7i";
include "color.s7i";
Line 1,482 ⟶ 3,398:
close(ppmFile);
end if;
end func;</langsyntaxhighlight>
 
=={{header|Tcl}}==
{{libheader|Tk}}
The actual PPM reader is built into the photo image engine:
<langsyntaxhighlight lang="tcl">package require Tk
 
proc readPPM {image file} {
$image read $file -format ppm
}</langsyntaxhighlight>
Thus, to read a PPM, convert it to grayscale, and write it back out again becomes this (which requires Tcl 8.6 for <code>try</code>/<code>finally</code>); the PPM reader and writer are inlined because they are trivial at the script level:
<langsyntaxhighlight lang="tcl">package require Tk
 
proc grayscaleFile {filename {newFilename ""}} {
Line 1,513 ⟶ 3,428:
image delete $buffer
}
}</langsyntaxhighlight>
 
However, the Tk library also has built-in the ability to convert code to grayscale directly during the saving of an image to a file, leading to this minimal solution:
<langsyntaxhighlight lang="tcl">package require Tk
 
proc grayscaleFile {filename {newFilename ""}} {
Line 1,527 ⟶ 3,442:
image delete $buffer
}
}</langsyntaxhighlight>
 
=={{header|UNIX Shell}}==
{{works with|ksh93}}
Line 1,534 ⟶ 3,448:
 
Add the following functions to the <tt>RGBColor_t</tt> type
<langsyntaxhighlight lang="bash"> function setrgb {
_.r=$1
_.g=$2
Line 1,544 ⟶ 3,458:
_.g=$x
_.b=$x
}</langsyntaxhighlight>
 
Add the following function to the <tt>Bitmap_t</tt> type
<langsyntaxhighlight lang="bash"> function grayscale {
RGBColor_t c
for ((y=0; y<_.height; y++)); do
Line 1,585 ⟶ 3,499:
fi
exec 4<&-
}</langsyntaxhighlight>
 
Now we can:
<langsyntaxhighlight lang="bash">Bitmap_t c
c.read "$HOME/tmp/bitmap.ppm"
c.to_s
Line 1,600 ⟶ 3,514:
c.grayscale
c.to_s
c.write "$HOME/tmp/bitmap_g.ppm"</langsyntaxhighlight>
 
=={{header|Vedit macro language}}==
<langsyntaxhighlight lang="vedit">// Load a PPM file
// @10 = filename
// On return:
Line 1,619 ⟶ 3,532:
Search("|X", ADVANCE) // skip maxval (assume 255)
Del_Block(0,CP) // remove the header
Return</langsyntaxhighlight>
 
Example of usage. In addition to LOAD_PPM routine above, you need routine RGB_TO_GRAYSCALE from [[Grayscale image]] and routine SAVE_PPM from [[Write ppm file]].
<langsyntaxhighlight lang="vedit">// Load RGB image
Reg_Set(10, "|(USER_MACRO)\example.ppm")
Call("LOAD_PPM")
Line 1,640 ⟶ 3,553:
// Cleanup and exit
Buf_Switch(#20) Buf_Quit(OK)
return</langsyntaxhighlight>
=={{header|Wren}}==
{{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="wren">import "graphics" for Canvas, ImageData, Color
import "dome" for Window, Process
import "io" for FileSystem
 
class Bitmap {
construct new(fileName, fileName2, width, height) {
Window.title = "Bitmap - read PPM file"
Window.resize(width, height)
Canvas.resize(width, height)
_w = width
_h = height
_fn2 = fileName2
loadPPMFile(fileName)
}
 
init() {
toGrayScale()
// display images side by side
_bmp.draw(0, 0)
_bmp2.draw(536, 0)
// save gray scale image to file
_bmp2.saveToFile(_fn2)
}
 
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.")
Process.exit()
}
var lines = ppm.split("\n")
if (Num.fromString(lines[2]) > 255) {
System.print("The maximum color value can't exceed 255.")
Process.exit()
}
var wh = lines[1].split(" ")
var w = Num.fromString(wh[0])
var h = Num.fromString(wh[1])
_bmp = ImageData.create(fileName, w, h)
var bytes = ppm.bytes
var i = bytes.count - 3 * w * h
for (y in 0...h) {
for (x in 0...w) {
var r = bytes[i]
var g = bytes[i+1]
var b = bytes[i+2]
var c = Color.rgb(r, g, b)
pset(x, y, c)
i = i + 3
}
}
}
 
toGrayScale() {
_bmp2 = ImageData.create("gray scale", _bmp.width, _bmp.height)
for (x in 0..._bmp.width) {
for (y in 0..._bmp.height) {
var c1 = _bmp.pget(x, y)
var lumin = (0.2126 * c1.r + 0.7152 * c1.g + 0.0722 * c1.b).floor
var c2 = Color.rgb(lumin, lumin,lumin, c1.a)
_bmp2.pset(x, y, c2)
}
}
}
 
pset(x, y, col) { _bmp.pset(x, y, col) }
 
pget(x, y) { _bmp.pget(x, y) }
 
update() {}
 
draw(alpha) {}
}
 
var Game = Bitmap.new("Lenna100.ppm", "Lenna100_gs.jpg", 1048, 512)</syntaxhighlight>
 
=={{header|XPL0}}==
Line 1,647 ⟶ 3,639:
files larger than 1280x1024 are clipped to the screen dimensions.
 
<langsyntaxhighlight XPL0lang="xpl0">include c:\cxpl\codes; \intrinsic 'code' declarations
 
func OpenInFile; \Open for input the file typed on command line
Line 1,691 ⟶ 3,683:
X:= ChIn(1); \wait for keystroke
SetVid(3); \restore normal text display
]</langsyntaxhighlight>
=={{header|Yabasic}}==
<syntaxhighlight lang="yabasic">sub readPPM(f$)
local ff, x, y, t$, dcol$, wid, hei
 
if f$ = "" print "No PPM file name indicate." : return false
 
ff = open (f$, "rb")
if not ff print "File ", f$, " not found." : return false
 
input #ff t$, wid, hei, dcol$
 
if t$ = "P6" then
open window wid, hei
for x = 0 to hei - 1
for y = 0 to wid - 1
color peek(#ff), peek(#ff), peek(#ff)
dot y, x
next y
next x
close #ff
else
print "File is NOT PPM P6 type." : return false
end if
return true
end sub</syntaxhighlight>
=={{header|zkl}}==
{{trans|FBSL}}
Line 1,699 ⟶ 3,718:
I used a slightly different image from what is shown, but the results are the same.
[[File:FBSLLena.png|right]]
<langsyntaxhighlight lang="zkl">//24-bpp P6 PPM solution:
image:=File("lena.ppm","rb").read();
start:=image.find("\n255\n")+5; // Get sizeof PPM header
Line 1,709 ⟶ 3,728:
}
 
File("lenaGrey.ppm","wb").write(image);</langsyntaxhighlight>
 
 
 
{{omit from|AWK}}
{{omit from|Lotus 123 Macro Scripting}}
2,122

edits