Bitmap/PPM conversion through a pipe: Difference between revisions

From Rosetta Code
Content added Content deleted
m (→‎{{header|Wren}}: Changed to Wren S/H)
 
(15 intermediate revisions by 7 users not shown)
Line 3: Line 3:
Using the data storage type defined [[Basic_bitmap_storage|on this page]] for raster images, delegate writing a JPEG file through a '''pipe''' using the <tt>output_ppm</tt> function defined [[Write_ppm_file|on this other page]].
Using the data storage type defined [[Basic_bitmap_storage|on this page]] for raster images, delegate writing a JPEG file through a '''pipe''' using the <tt>output_ppm</tt> function defined [[Write_ppm_file|on this other page]].


There are various utilities that can be used for this task, for example: '''cjpeg''' (package ''"jpeg-progs"'' on Linux), '''ppmtojpeg''' (package ''"netpbm"'' on Linux), '''convert''' (from ''ImageMagick'', multi-platform).
There are various utilities that can be used for this task, for example: '''cjpeg''' (package ''"jpeg-progs"'' on Linux, or ''"media-libs/libjpeg-turbo"'' on Gentoo), '''ppmtojpeg''' (package ''"netpbm"'' on Linux), '''convert''' (from ''ImageMagick'', multi-platform).
=={{header|ATS}}==

I use the <code>magick</code> command from ImageMagick. You need all the source files from [[Bitmap#ATS]], [[Bitmap/Read_a_PPM_file#ATS]], and [[Bitmap/Write_a_PPM_file#ATS]]. (You do ''not'' need the files from [[Grayscale_image#ATS]].)

See also [[Bitmap/Read_an_image_through_a_pipe#ATS]].

<syntaxhighlight lang="ats">
(* This program uses ImageMagick to convert an image, with the target
file specified according to the conventions of ImageMagick. That
allows such things as "gif:foobar.jpg" to mean a GIF named
"foobar.jpg". But, if you leave out the "gif:" prefix, ImageMagick
will make a JPEG. (I notice that one can also insert options to
magick, although this was an unexpected result of my design.) *)

(*

##myatsccdef=\
patscc -std=gnu2x -g -O2 -DATS_MEMALLOC_LIBC \
-o $fname($1) $1 \
bitmap{,_{read,write}_ppm}_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_task.dats"
staload _ = "bitmap_read_ppm_task.dats"
staload _ = "bitmap_write_ppm_task.dats"

(*------------------------------------------------------------------*)
(* There is support for pipe-I/O in libats/libc, but I cannot (at
least when in a hurry) figure out how it is supposed to be
used. So, as elsewhere in the "raster graphics operations"
category, what is not in the prelude itself I implement with the
foreign function interfaces. :) Using FFI is a typical part of ATS
programming, and one should get used to doing it.
Anyway, here is some UNSAFE support for pipe-I/O. *)

typedef charstar = $extype"char *"
typedef FILEstar = $extype"FILE *"

fn {}
fileref_popen_unsafe (command : string,
mode : string)
: Option_vt FILEref =
let
val p = $extfcall (ptr, "popen", $UNSAFE.cast{charstar} command,
$UNSAFE.cast{charstar} mode)
in
if iseqz p then
None_vt ()
else
Some_vt ($UNSAFE.cast{FILEref} p)
end

fn {}
fileref_pclose_unsafe (f : FILEref)
: int = (* Returns the exit status of the command. *)
$extfcall (int, "pclose", $UNSAFE.cast{FILEstar} f)

(*------------------------------------------------------------------*)

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 outf_name = if nargs < 3 then "-" else args[2]
val command = string_append ("magick ppm:- ", outf_name)
val () = free args
val pipe_opt =
(* Temporarily treating a strptr as a string, just to make a
function call of this sort, is not actually unsafe. *)
fileref_popen_unsafe ($UNSAFE.strptr2string command, "w")
val () = free command
in
case+ pipe_opt of
| ~ None_vt () =>
begin
free (pfgc1 | pix1);
println! ("For some reason, I failed to open a pipe ",
"to magick.");
exit 3
end
| ~ Some_vt outf =>
let
val success = pixmap_write_ppm (outf, pix1)
in
ignoret (fileref_pclose_unsafe outf);
free (pfgc1 | pix1);
if ~success then
begin
println! ("For some reason, I failed to pipe the ",
"image to magick.");
exit 2
end
end
end
end
</syntaxhighlight>

{{out}}
Using SIPI test image 5.1.12:
<pre>$ myatscc bitmap_write_through_pipe_task.dats
$ ./bitmap_write_through_pipe_task 5.1.12.ppm bitmap_write_through_pipe_task_ATS.jpg</pre>
[[File:Bitmap write through pipe task ATS.jpg|alt=An alarm clock on a book on a book, and a diptych of family portraits. On a table or chest of drawers or some such. Monochrome photograph.]]


=={{header|C}}==
=={{header|C}}==
Line 9: Line 140:
This one uses the ImageMagick <tt>convert</tt> tool.
This one uses the ImageMagick <tt>convert</tt> tool.


<lang c>/* interface */
<syntaxhighlight lang="c">/* interface */
void print_jpg(image img, int qual);</lang>
void print_jpg(image img, int qual);</syntaxhighlight>


<lang c>#define MAXCMDBUF 100
<syntaxhighlight lang="c">#define MAXCMDBUF 100
void print_jpg(image img, int qual)
void print_jpg(image img, int qual)
{
{
Line 28: Line 159:
pclose(pipe);
pclose(pipe);
}
}
}</lang>
}</syntaxhighlight>


The code that writes to the pipe is the same of [[Write_ppm_file|output_ppm]] of course. A complete example is
The code that writes to the pipe is the same of [[Write_ppm_file|output_ppm]] of course. A complete example is


<lang c>#include "imglib.h"
<syntaxhighlight lang="c">#include "imglib.h"


int main()
int main()
Line 43: Line 174:
print_jpg(img, 75);
print_jpg(img, 75);
free_img(img);
free_img(img);
}</lang>
}</syntaxhighlight>


In order to make it working, you must link it with the raster image functions given by the codes [[Bresenham's_line_algorithm#C|here]] and [[Basic_bitmap_storage#C|here]]
In order to make it working, you must link it with the raster image functions given by the codes [[Bresenham's_line_algorithm#C|here]] and [[Basic_bitmap_storage#C|here]]
Line 50: Line 181:
{{works with|Go weekly.2011-12-14}} (Go 1 should be close)
{{works with|Go weekly.2011-12-14}} (Go 1 should be close)
Using cjpeg:
Using cjpeg:
<lang go>package main
<syntaxhighlight lang="go">package main


// Files required to build supporting package raster are found in:
// Files required to build supporting package raster are found in:
Line 108: Line 239:
fmt.Println(err)
fmt.Println(err)
}
}
}</lang>
}</syntaxhighlight>

=={{header|Julia}}==
=={{header|Julia}}==
{{works with|Julia|0.6}}
{{works with|Julia|0.6}}


<lang julia>using Images, FileIO
<syntaxhighlight lang="julia">using Images, FileIO


ppmimg = load("data/bitmapInputTest.ppm")
ppmimg = load("data/bitmapInputTest.ppm")
save("data/bitmapOutputTest.jpg", ppmimg)</lang>
save("data/bitmapOutputTest.jpg", ppmimg)</syntaxhighlight>

=={{header|Kotlin}}==
=={{header|Kotlin}}==
{{works with|Ubuntu 16.04}}
{{works with|Ubuntu 16.04}}
In order to provide a complete runnable example, we repeat bits of code from other relevant tasks and add code which pipes .ppm data to ImageMagick's 'convert' tool which then writes the corresponding .jpg file to disk.
In order to provide a complete runnable example, we repeat bits of code from other relevant tasks and add code which pipes .ppm data to ImageMagick's 'convert' tool which then writes the corresponding .jpg file to disk.
<lang scala>// Version 1.2.40
<syntaxhighlight lang="scala">// Version 1.2.40


import java.awt.Color
import java.awt.Color
Line 176: Line 305:
}
}
}
}
}</lang>
}</syntaxhighlight>
=={{header|Mathematica}}/{{header|Wolfram Language}}==

=={{header|Mathematica}}==
The Windows command line does not like quotes in the middle of text, so strings have been turned into character codes.
The Windows command line does not like quotes in the middle of text, so strings have been turned into character codes.
<lang Mathematica>convert[image_,out_]:=Module[{process=StartProcess[{
<syntaxhighlight lang="mathematica">convert[image_,out_]:=Module[{process=StartProcess[{
"wolfram","-noinit","-noprompt","-run",
"wolfram","-noinit","-noprompt","-run",
"Export[FromCharacterCode["~~ToString[ToCharacterCode[out]]~~"],ImportString[StringRiffle[Table[InputString[],{4}],FromCharacterCode[10]],FromCharacterCode[{80,80,77}]]]"
"Export[FromCharacterCode["~~ToString[ToCharacterCode[out]]~~"],ImportString[StringRiffle[Table[InputString[],{4}],FromCharacterCode[10]],FromCharacterCode[{80,80,77}]]]"
Line 186: Line 314:
WriteLine[process,image];
WriteLine[process,image];
WriteLine[process,"Quit[]"];
WriteLine[process,"Quit[]"];
];</lang>
];</syntaxhighlight>

=={{header|Nim}}==
=={{header|Nim}}==
We use "convert" command from ImageMagick and "pnmtojpeg" command from Netpbm. The first command allows to specify the output file name, the second writes to stdout and, so, we have to use a redirection. Thus, the way to launch the process is slightly different.
We use "convert" command from ImageMagick and "pnmtojpeg" command from Netpbm. The first command allows to specify the output file name, the second writes to stdout and, so, we have to use a redirection. Thus, the way to launch the process is slightly different.


<lang Nim>import bitmap
<syntaxhighlight lang="nim">import bitmap
import ppm_write
import ppm_write
import osproc
import osproc


# Build an image.
# Build an image.
var image = initImage(100, 50)
var image = newImage(100, 50)
image.fill(color(255, 0, 0))
image.fill(color(255, 0, 0))
for row in 10..20:
for row in 10..20:
Line 207: Line 334:
# Launch ImageMagick "convert".
# Launch ImageMagick "convert".
# Input is taken from stdin and result written in "output1.jpeg".
# Input is taken from stdin and result written in "output1.jpeg".
var p = startProcess("/usr/bin/convert", args = ["ppm:-", "output1.jpeg"])
var p = startProcess("convert", args = ["ppm:-", "output1.jpeg"], options = {poUsePath})
let stream = p.inputStream()
var stream = p.inputStream()
image.writePPM(stream)
image.writePPM(stream)
p.close()
p.close()
Line 214: Line 341:
# Launch Netpbm "pnmtojpeg".
# Launch Netpbm "pnmtojpeg".
# Input is taken from stdin and output sent to "output2.jpeg".
# Input is taken from stdin and output sent to "output2.jpeg".
p = startProcess("/usr/bin/pnmtojpeg >output2.jpeg", options = {poEvalCommand})
p = startProcess("pnmtojpeg >output2.jpeg", options = {poUsePath, poEvalCommand})
let inStream = p.inputStream()
stream = p.inputStream()
image.writePPM(inStream)
image.writePPM(stream)
p.close()</lang>
p.close()</syntaxhighlight>


== {{Header|OCaml}} ==
=={{Header|OCaml}}==


<lang ocaml>let print_jpeg ~img ?(quality=96) () =
<syntaxhighlight lang="ocaml">let print_jpeg ~img ?(quality=96) () =
let cmd = Printf.sprintf "cjpeg -quality %d" quality in
let cmd = Printf.sprintf "cjpeg -quality %d" quality in
(*
(*
Line 235: Line 362:
done
done
with End_of_file -> ()
with End_of_file -> ()
;;</lang>
;;</syntaxhighlight>
=={{header|Perl}}==
<syntaxhighlight lang="perl"># 20211224 Perl programming solution


use strict;
use warnings;

use Imager;
use Imager::Test 'test_image_raw';

my $img = test_image_raw();
my $IO = Imager::io_new_bufchain();
Imager::i_writeppm_wiol($img, $IO) or die;
my $raw = Imager::io_slurp($IO) or die;

open my $fh, '|-', '/usr/local/bin/convert - -compress none output.jpg' or die;
binmode $fh;
syswrite $fh, $raw or die;
close $fh;</syntaxhighlight>
{{out}}
<pre>file output.jpg
output.jpg: JPEG image data, JFIF standard 1.01, comment: "CREATOR: Imager"
magick identify output.jpg
output.jpg JPEG 150x150 150x150+0+0 8-bit sRGB 3952B 0.000u 0:00.012</pre>
=={{header|Phix}}==
Uses the provided demo\rosetta\viewppm.exw utility to accomplish this task.
<!--<syntaxhighlight lang="phix">(notonline)-->
<span style="color: #000080;font-style:italic;">-- demo\rosetta\Bitmap_PPM_conversion_through_a_pipe.exw</span>
<span style="color: #008080;">without</span> <span style="color: #008080;">js</span> <span style="color: #000080;font-style:italic;">-- file i/o, system_exec(), pipes[!!]</span>
<span style="color: #008080;">include</span> <span style="color: #000000;">builtins</span><span style="color: #0000FF;">\</span><span style="color: #000000;">pipeio</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span>
<span style="color: #008080;">include</span> <span style="color: #000000;">builtins</span><span style="color: #0000FF;">\</span><span style="color: #7060A8;">serialize</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span>
<span style="color: #008080;">include</span> <span style="color: #000000;">ppm</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span> <span style="color: #000080;font-style:italic;">-- read_ppm()</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">pipes</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">pipes</span><span style="color: #0000FF;">[</span><span style="color: #000000;">PIPEIN</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">create_pipe</span><span style="color: #0000FF;">(</span><span style="color: #000000;">INHERIT_WRITE</span><span style="color: #0000FF;">)</span>
<span style="color: #000080;font-style:italic;">-- Create the child process, with replacement stdin. </span>
<span style="color: #004080;">string</span> <span style="color: #000000;">cmd</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"%s viewppm -save test.jpg"</span><span style="color: #0000FF;">,{</span><span style="color: #7060A8;">get_interpreter</span><span style="color: #0000FF;">(</span><span style="color: #004600;">true</span><span style="color: #0000FF;">)})</span>
<span style="color: #004080;">atom</span> <span style="color: #000000;">hProc</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">system_exec</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cmd</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">12</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">pipes</span><span style="color: #0000FF;">),</span>
<span style="color: #000000;">hPipe</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">pipes</span><span style="color: #0000FF;">[</span><span style="color: #000000;">PIPEIN</span><span style="color: #0000FF;">][</span><span style="color: #000000;">WRITE_PIPE</span><span style="color: #0000FF;">]</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">img</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">serialize</span><span style="color: #0000FF;">(</span><span style="color: #000000;">read_ppm</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Lena.ppm"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">bFlat</span><span style="color: #0000FF;">:=</span><span style="color: #004600;">true</span><span style="color: #0000FF;">))</span>
<span style="color: #008080;">if</span> <span style="color: #008080;">not</span> <span style="color: #000000;">write_to_pipe</span><span style="color: #0000FF;">(</span><span style="color: #000000;">hPipe</span><span style="color: #0000FF;">,</span><span style="color: #000000;">img</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span> <span style="color: #7060A8;">crash</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"error"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000080;font-style:italic;">-- Close the pipe handle so the child process stops reading.
--hPipe = close_handles(hPipe)</span>
<span style="color: #000000;">pipes</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">close_handles</span><span style="color: #0000FF;">(</span><span style="color: #000000;">pipes</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- (may as well do the lot)</span>
<span style="color: #0000FF;">?</span><span style="color: #008000;">"done"</span>
<span style="color: #0000FF;">{}</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">wait_key</span><span style="color: #0000FF;">()</span>
<!--</syntaxhighlight>-->
=={{header|PicoLisp}}==
=={{header|PicoLisp}}==
<lang PicoLisp># Create an empty image of 120 x 90 pixels
<syntaxhighlight lang="picolisp"># Create an empty image of 120 x 90 pixels
(setq *Ppm (make (do 90 (link (need 120)))))
(setq *Ppm (make (do 90 (link (need 120)))))


Line 248: Line 424:


# Write to "img.jpg" through a pipe
# Write to "img.jpg" through a pipe
(ppmWrite *Ppm '("convert" "-" "img.jpg"))</lang>
(ppmWrite *Ppm '("convert" "-" "img.jpg"))</syntaxhighlight>

=={{header|Python}}==
=={{header|Python}}==
<syntaxhighlight lang="python">
<lang Python>
"""
"""
Adapted from https://stackoverflow.com/questions/26937143/ppm-to-jpeg-jpg-conversion-for-python-3-4-1
Adapted from https://stackoverflow.com/questions/26937143/ppm-to-jpeg-jpg-conversion-for-python-3-4-1
Line 262: Line 437:
im = Image.open("boxes_1.ppm")
im = Image.open("boxes_1.ppm")
im.save("boxes_1.jpg")
im.save("boxes_1.jpg")
</syntaxhighlight>
</lang>
Does not need to pipe through a conversion utility
Does not need to pipe through a conversion utility
because the Pillow module does the conversion.
because the Pillow module does the conversion.

=={{header|Racket}}==
=={{header|Racket}}==
<lang racket>
<syntaxhighlight lang="racket">
(define (ppm->jpeg bitmap [jpg-file "output"] [quality 75])
(define (ppm->jpeg bitmap [jpg-file "output"] [quality 75])
(define command (format "convert ppm:- -quality ~a jpg:~a.jpg" quality jpg-file))
(define command (format "convert ppm:- -quality ~a jpg:~a.jpg" quality jpg-file))
Line 275: Line 449:
(close-output-port out))
(close-output-port out))


(ppm->jpeg bm)</lang>
(ppm->jpeg bm)</syntaxhighlight>

=={{header|Raku}}==
=={{header|Raku}}==
(formerly Perl 6)
(formerly Perl 6)
<lang perl6># Reference:
<syntaxhighlight lang="raku" line># Reference:
# https://rosettacode.org/wiki/Bitmap/Write_a_PPM_file#Raku
# https://rosettacode.org/wiki/Bitmap/Write_a_PPM_file#Raku


Line 315: Line 488:
my $proc = run '/usr/bin/convert','-','output_piped.jpg', :in;
my $proc = run '/usr/bin/convert','-','output_piped.jpg', :in;
$proc.in.write: $b.P6;
$proc.in.write: $b.P6;
$proc.in.close;</lang>
$proc.in.close;</syntaxhighlight>
{{out}}
{{out}}
<pre>file output_piped.jpg
<pre>file output_piped.jpg
output_piped.jpg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 125x125, frames 3</pre>
output_piped.jpg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 125x125, frames 3</pre>

=={{header|Ruby}}==
=={{header|Ruby}}==
Extends [[Write ppm file#Ruby]]. Uses the ImageMagick <code>convert</code> tool.
Extends [[Write ppm file#Ruby]]. Uses the ImageMagick <code>convert</code> tool.
Additionally, for debugging, allow writing in pixmap P3 (ascii) format.
Additionally, for debugging, allow writing in pixmap P3 (ascii) format.


<lang ruby>class Pixmap
<syntaxhighlight lang="ruby">class Pixmap
PIXMAP_FORMATS = ["P3", "P6"] # implemented output formats
PIXMAP_FORMATS = ["P3", "P6"] # implemented output formats
PIXMAP_BINARY_FORMATS = ["P6"] # implemented output formats which are binary
PIXMAP_BINARY_FORMATS = ["P6"] # implemented output formats which are binary
Line 362: Line 534:


image = Pixmap.open('file.ppm')
image = Pixmap.open('file.ppm')
image.save_as_jpeg('file.jpg')</lang>
image.save_as_jpeg('file.jpg')</syntaxhighlight>

=={{header|Standard ML}}==
=={{header|Standard ML}}==
This function uses convert, and retrieves its output
This function uses convert, and retrieves its output
<syntaxhighlight lang="standard ml">
<lang Standard ML>
val useOSConvert = fn ppm =>
val useOSConvert = fn ppm =>
let
let
Line 394: Line 565:
)
)
end;
end;
</syntaxhighlight>
</lang>
call and return value
call and return value
<syntaxhighlight lang="standard ml">
<lang Standard ML>
useOSConvert "P3 3 2 255 255 0 0 0 255 0 0 0 255 255 255 0 255 255 255 0 0 0" ;
useOSConvert "P3 3 2 255 255 0 0 0 255 0 0 0 255 255 255 0 255 255 255 0 0 0" ;
val it =
val it =
fromList[0wxFF, 0wxD8, 0wxFF, 0wxE0, 0wx0, 0wx10, 0wx4A, 0wx46, 0wx49,
fromList[0wxFF, 0wxD8, 0wxFF, 0wxE0, 0wx0, 0wx10, 0wx4A, 0wx46, 0wx49,
0wx46, ...]: BinIO.vector
0wx46, ...]: BinIO.vector
</syntaxhighlight>
</lang>
This is the fire-and-forget version.
This is the fire-and-forget version.
<syntaxhighlight lang="standard ml">
<lang Standard ML>
val demo = fn () =>
val demo = fn () =>
let
let
Line 437: Line 608:


end ;
end ;
</syntaxhighlight>
</lang>
output, after compilation to 'demo'
output, after compilation to 'demo'
shell$ demo
shell$ demo
shell$ file /tmp/out.jpeg
shell$ file /tmp/out.jpeg
/tmp/out.jpeg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 3x2, frames 3
/tmp/out.jpeg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 3x2, frames 3

=={{header|Tcl}}==
=={{header|Tcl}}==
Referring to [[Write ppm file#Tcl]] and [[Basic bitmap storage#Tcl]]
Referring to [[Write ppm file#Tcl]] and [[Basic bitmap storage#Tcl]]


{{libheader|Tk}}
{{libheader|Tk}}
<lang tcl>package require Tk
<syntaxhighlight lang="tcl">package require Tk


proc output_jpeg {image filename {quality 75}} {
proc output_jpeg {image filename {quality 75}} {
Line 454: Line 624:
puts -nonewline $f [$image data -format ppm]
puts -nonewline $f [$image data -format ppm]
close $f
close $f
}</lang>
}</syntaxhighlight>
However, it is more normal to do this directly with the {{libheader|TkImg}} which is bundled with many Tcl distributions.
However, it is more normal to do this directly with the {{libheader|TkImg}} which is bundled with many Tcl distributions.
<lang tcl>package require Tk
<syntaxhighlight lang="tcl">package require Tk
package require img::jpeg
package require img::jpeg


Line 463: Line 633:
}
}
set img [image create photo -filename filename.ppm]
set img [image create photo -filename filename.ppm]
output_jpeg $img filename.jpg</lang>
output_jpeg $img filename.jpg</syntaxhighlight>
=={{header|Wren}}==
{{libheader|DOME}}
As DOME doesn't have a method for calling an external process (''ImageMagick'' in this case), we first need to create a small plug-in in C to add this functionality.
<syntaxhighlight lang="c">/* gcc -O3 -std=c11 -shared -o pipeconv.so -fPIC -I./include pipeconv.c */

#include <stdlib.h>
#include <string.h>
#include "dome.h"

static DOME_API_v0* core;
static WREN_API_v0* wren;

static const char* source = ""
"class PipeConv {\n"
"foreign static convert(from, to) \n"
"} \n";

void C_convert(WrenVM* vm) {
const char *from = wren->getSlotString(vm, 1);
const char *to = wren->getSlotString(vm, 2);
char command[strlen(from) + strlen(to) + 10];
strcpy(command, "convert ");
strcat(command, from);
strcat(command, " ");
strcat(command, to);
int res = system(command);
}

DOME_EXPORT DOME_Result PLUGIN_onInit(DOME_getAPIFunction DOME_getAPI, DOME_Context ctx) {
core = DOME_getAPI(API_DOME, DOME_API_VERSION);
wren = DOME_getAPI(API_WREN, WREN_API_VERSION);
core->registerModule(ctx, "pipeconv", source);
core->registerClass(ctx, "pipeconv", "PipeConv", NULL, NULL);
core->registerFn(ctx, "pipeconv", "static PipeConv.convert(_,_)", C_convert);
return DOME_RESULT_SUCCESS;
}

DOME_EXPORT DOME_Result PLUGIN_preUpdate(DOME_Context ctx) {
return DOME_RESULT_SUCCESS;
}

DOME_EXPORT DOME_Result PLUGIN_postUpdate(DOME_Context ctx) {
return DOME_RESULT_SUCCESS;
}

DOME_EXPORT DOME_Result PLUGIN_preDraw(DOME_Context ctx) {
return DOME_RESULT_SUCCESS;
}

DOME_EXPORT DOME_Result PLUGIN_postDraw(DOME_Context ctx) {
return DOME_RESULT_SUCCESS;
}

DOME_EXPORT DOME_Result PLUGIN_onShutdown(DOME_Context ctx) {
return DOME_RESULT_SUCCESS;
}</syntaxhighlight>
This assumes that the ''dome.h'' header file is copied to an ''include'' sub-directory of the current one and that the resulting ''pipeconv.so'' shared library file is created in the latter.

We can now use this plug-in in the following script which calls ''ImageMagick'' to convert the ''output.ppm'' file to a ''jpg'' file and then loads the latter and displays it.

<syntaxhighlight lang="wren">import "graphics" for Canvas, ImageData
import "dome" for Window
import "plugin" for Plugin

Plugin.load("pipeconv")

import "pipeconv" for PipeConv

class ConvertPPM {
construct new(fileName, fileName2, width, height) {
Window.resize(width, height)
Canvas.resize(width, height)
Window.title = "Convert PPM file via pipe"
// convert .ppm file to .jpg via a pipe
PipeConv.convert(fileName, fileName2)
// load and display .jpg file
var image = ImageData.loadFromFile(fileName2)
image.draw(0, 0)
}

init() {}

update() {}

draw(alpha) {}
}

var Game = ConvertPPM.new("output.ppm", "output_piped.jpg", 350, 350)</syntaxhighlight>


=={{header|zkl}}==
=={{header|zkl}}==
Line 470: Line 728:


Using the convert utility by ImageMagick:
Using the convert utility by ImageMagick:
<lang zkl>img:=PPM.readPPMFile("fractal.ppm");
<syntaxhighlight lang="zkl">img:=PPM.readPPMFile("fractal.ppm");
p:=System.popen(0'|convert ppm:- jpg:"fractal.jpg"|,"w");
p:=System.popen(0'|convert ppm:- jpg:"fractal.jpg"|,"w");
img.write(p); p.close();</lang>
img.write(p); p.close();</syntaxhighlight>

{{omit from|GUISS}}
{{omit from|GUISS}}
{{omit from|PARI/GP}}
{{omit from|PARI/GP}}
{{omit from|TI-83 BASIC}} {{omit from|TI-89 BASIC}} <!-- Does not have an external OS/command processor. -->
{{omit from|TI-83 BASIC}}
{{omit from|TI-89 BASIC}} <!-- Does not have an external OS/command processor. -->
{{omit from|ZX Spectrum Basic|Does not have pipelining}}
{{omit from|ZX Spectrum Basic|Does not have pipelining}}

Latest revision as of 10:38, 8 November 2023

Task
Bitmap/PPM conversion through a pipe
You are encouraged to solve this task according to the task description, using any language you may know.

Using the data storage type defined on this page for raster images, delegate writing a JPEG file through a pipe using the output_ppm function defined on this other page.

There are various utilities that can be used for this task, for example: cjpeg (package "jpeg-progs" on Linux, or "media-libs/libjpeg-turbo" on Gentoo), ppmtojpeg (package "netpbm" on Linux), convert (from ImageMagick, multi-platform).

ATS

I use the magick command from ImageMagick. You need all the source files from Bitmap#ATS, Bitmap/Read_a_PPM_file#ATS, and Bitmap/Write_a_PPM_file#ATS. (You do not need the files from Grayscale_image#ATS.)

See also Bitmap/Read_an_image_through_a_pipe#ATS.

(* This program uses ImageMagick to convert an image, with the target
   file specified according to the conventions of ImageMagick. That
   allows such things as "gif:foobar.jpg" to mean a GIF named
   "foobar.jpg". But, if you leave out the "gif:" prefix, ImageMagick
   will make a JPEG. (I notice that one can also insert options to
   magick, although this was an unexpected result of my design.) *)

(*

##myatsccdef=\
patscc -std=gnu2x -g -O2 -DATS_MEMALLOC_LIBC \
  -o $fname($1) $1 \
  bitmap{,_{read,write}_ppm}_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_task.dats"
staload _ = "bitmap_read_ppm_task.dats"
staload _ = "bitmap_write_ppm_task.dats"

(*------------------------------------------------------------------*)
(* There is support for pipe-I/O in libats/libc, but I cannot (at
   least when in a hurry) figure out how it is supposed to be
   used. So, as elsewhere in the "raster graphics operations"
   category, what is not in the prelude itself I implement with the
   foreign function interfaces. :) Using FFI is a typical part of ATS
   programming, and one should get used to doing it.
   
   Anyway, here is some UNSAFE support for pipe-I/O. *)

typedef charstar = $extype"char *"
typedef FILEstar = $extype"FILE *"

fn {}
fileref_popen_unsafe (command : string,
                      mode    : string)
    : Option_vt FILEref =
  let
    val p = $extfcall (ptr, "popen", $UNSAFE.cast{charstar} command,
                       $UNSAFE.cast{charstar} mode)
  in
    if iseqz p then
      None_vt ()
    else
      Some_vt ($UNSAFE.cast{FILEref} p)
  end

fn {}
fileref_pclose_unsafe (f : FILEref)
    : int =              (* Returns the exit status of the command. *)
  $extfcall (int, "pclose", $UNSAFE.cast{FILEstar} f)

(*------------------------------------------------------------------*)

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 outf_name = if nargs < 3 then "-" else args[2]
        val command = string_append ("magick ppm:- ", outf_name)
        val () = free args
        val pipe_opt =
          (* Temporarily treating a strptr as a string, just to make a
             function call of this sort, is not actually unsafe. *)
          fileref_popen_unsafe ($UNSAFE.strptr2string command, "w")
        val () = free command
      in
        case+ pipe_opt of
        | ~ None_vt () =>
          begin
            free (pfgc1 | pix1);
            println! ("For some reason, I failed to open a pipe ",
                      "to magick.");
            exit 3
          end
        | ~ Some_vt outf =>
          let
            val success = pixmap_write_ppm (outf, pix1)
          in
            ignoret (fileref_pclose_unsafe outf);
            free (pfgc1 | pix1);
            if ~success then
              begin
                println! ("For some reason, I failed to pipe the ",
                          "image to magick.");
                exit 2
              end
          end
      end
  end
Output:

Using SIPI test image 5.1.12:

$ myatscc bitmap_write_through_pipe_task.dats
$ ./bitmap_write_through_pipe_task 5.1.12.ppm bitmap_write_through_pipe_task_ATS.jpg

An alarm clock on a book on a book, and a diptych of family portraits. On a table or chest of drawers or some such. Monochrome photograph.

C

Works with: POSIX version .1-2001

This one uses the ImageMagick convert tool.

/* interface */
void print_jpg(image img, int qual);
#define MAXCMDBUF 100
void print_jpg(image img, int qual)
{
   char buf[MAXCMDBUF];
   unsigned int n;
   FILE *pipe;
   
   snprintf(buf, MAXCMDBUF, "convert ppm:- -quality %d jpg:-", qual);
   pipe = popen(buf, "w");
   if ( pipe != NULL )
   {
           fprintf(pipe, "P6\n%d %d\n255\n", img->width, img->height);
           n = img->width * img->height;
           fwrite(img->buf, sizeof(pixel), n, pipe);
           pclose(pipe);
   }
}

The code that writes to the pipe is the same of output_ppm of course. A complete example is

#include "imglib.h"

int main()
{
      image img;
      
      img = alloc_img(100,100);
      fill_img(img, 50, 20, 200);
      draw_line(img, 0, 0, 80, 80, 255, 0, 0);
      print_jpg(img, 75);
      free_img(img);
}

In order to make it working, you must link it with the raster image functions given by the codes here and here

Go

(Go 1 should be close)

Using cjpeg:

package main

// Files required to build supporting package raster are found in:
// * Bitmap
// * Write a PPM file

import (
    "fmt"
    "math/rand"
    "os/exec"
    "raster"
)

func main() {
    b := raster.NewBitmap(400, 300)
    // a little extravagant, this draws a design of dots and lines
    b.FillRgb(0xc08040)
    for i := 0; i < 2000; i++ {
        b.SetPxRgb(rand.Intn(400), rand.Intn(300), 0x804020)
    }
    for x := 0; x < 400; x++ {
        for y := 240; y < 245; y++ {
            b.SetPxRgb(x, y, 0x804020)
        }
        for y := 260; y < 265; y++ {
            b.SetPxRgb(x, y, 0x804020)
        }
    }
    for y := 0; y < 300; y++ {
        for x := 80; x < 85; x++ {
            b.SetPxRgb(x, y, 0x804020)
        }
        for x := 95; x < 100; x++ {
            b.SetPxRgb(x, y, 0x804020)
        }
    }

    // pipe logic
    c := exec.Command("cjpeg", "-outfile", "pipeout.jpg")
    pipe, err := c.StdinPipe()
    if err != nil {
        fmt.Println(err)
        return
    }
    err = c.Start()
    if err != nil {
        fmt.Println(err)
        return
    }
    err = b.WritePpmTo(pipe)
    if err != nil {
        fmt.Println(err)
        return
    }
    err = pipe.Close()
    if err != nil {
        fmt.Println(err)
    }
}

Julia

Works with: Julia version 0.6
using Images, FileIO

ppmimg = load("data/bitmapInputTest.ppm")
save("data/bitmapOutputTest.jpg", ppmimg)

Kotlin

Works with: Ubuntu 16.04

In order to provide a complete runnable example, we repeat bits of code from other relevant tasks and add code which pipes .ppm data to ImageMagick's 'convert' tool which then writes the corresponding .jpg file to disk.

// Version 1.2.40

import java.awt.Color
import java.awt.Graphics
import java.awt.image.BufferedImage

class BasicBitmapStorage(width: Int, height: Int) {
    val image = BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR)

    fun fill(c: Color) {
        val g = image.graphics
        g.color = c
        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 main(args: Array<String>) {
    // create BasicBitmapStorage object
    val width = 640
    val height = 640
    val bbs = BasicBitmapStorage(width, height)
    for (y in 0 until height) {
        for (x in 0 until width) {
            val c = Color(x % 256, y % 256, (x * y) % 256)
            bbs.setPixel(x, y, c)
        }
    }

    // now write the object in PPM format to ImageMagick's STDIN via a pipe
    // so it can be converted to a .jpg file and written to disk
    val pb = ProcessBuilder("convert", "-", "output_piped.jpg")
    pb.directory(null)
    pb.redirectInput(ProcessBuilder.Redirect.PIPE)
    val buffer = ByteArray(width * 3) // write one line at a time
    val proc = pb.start()
    val pStdIn = proc.outputStream
    pStdIn.use {
        val header = "P6\n$width $height\n255\n".toByteArray()
        with (it) {
            write(header)
            for (y in 0 until height) {
                for (x in 0 until width) {
                    val c = bbs.getPixel(x, y)
                    buffer[x * 3] = c.red.toByte()
                    buffer[x * 3 + 1] = c.green.toByte()
                    buffer[x * 3 + 2] = c.blue.toByte()
                }
                write(buffer)
            }
        }
    }
}

Mathematica/Wolfram Language

The Windows command line does not like quotes in the middle of text, so strings have been turned into character codes.

convert[image_,out_]:=Module[{process=StartProcess[{
"wolfram","-noinit","-noprompt","-run",
"Export[FromCharacterCode["~~ToString[ToCharacterCode[out]]~~"],ImportString[StringRiffle[Table[InputString[],{4}],FromCharacterCode[10]],FromCharacterCode[{80,80,77}]]]"
}]},
WriteLine[process,image];
WriteLine[process,"Quit[]"];
];

Nim

We use "convert" command from ImageMagick and "pnmtojpeg" command from Netpbm. The first command allows to specify the output file name, the second writes to stdout and, so, we have to use a redirection. Thus, the way to launch the process is slightly different.

import bitmap
import ppm_write
import osproc

# Build an image.
var image = newImage(100, 50)
image.fill(color(255, 0, 0))
for row in 10..20:
  for col in 0..<image.w:
    image[col, row] = color(0, 255, 0)
for row in 30..40:
  for col in 0..<image.w:
    image[col, row] = color(0, 0, 255)

# Launch ImageMagick "convert".
# Input is taken from stdin and result written in "output1.jpeg".
var p = startProcess("convert", args = ["ppm:-", "output1.jpeg"], options = {poUsePath})
var stream = p.inputStream()
image.writePPM(stream)
p.close()

# Launch Netpbm "pnmtojpeg".
# Input is taken from stdin and output sent to "output2.jpeg".
p = startProcess("pnmtojpeg >output2.jpeg", options = {poUsePath, poEvalCommand})
stream = p.inputStream()
image.writePPM(stream)
p.close()

OCaml

let print_jpeg ~img ?(quality=96) () =
  let cmd = Printf.sprintf "cjpeg -quality %d" quality in
  (*
  let cmd = Printf.sprintf "ppmtojpeg -quality %d" quality in
  let cmd = Printf.sprintf "convert ppm:- -quality %d jpg:-" quality in
  *)
  let ic, oc = Unix.open_process cmd in
  output_ppm ~img ~oc;
  try
    while true do
      let c = input_char ic in
      print_char c
    done
  with End_of_file -> ()
;;

Perl

# 20211224 Perl programming solution

use strict;
use warnings;

use Imager;
use Imager::Test 'test_image_raw'; 

my $img = test_image_raw();
my $IO  = Imager::io_new_bufchain();
Imager::i_writeppm_wiol($img, $IO) or die;
my $raw = Imager::io_slurp($IO) or die;

open my $fh, '|-', '/usr/local/bin/convert - -compress none output.jpg' or die;
binmode $fh;
syswrite $fh, $raw or die;
close $fh;
Output:
file output.jpg
output.jpg: JPEG image data, JFIF standard 1.01, comment: "CREATOR: Imager"
magick identify output.jpg
output.jpg JPEG 150x150 150x150+0+0 8-bit sRGB 3952B 0.000u 0:00.012

Phix

Uses the provided demo\rosetta\viewppm.exw utility to accomplish this task.

-- demo\rosetta\Bitmap_PPM_conversion_through_a_pipe.exw
without js -- file i/o, system_exec(), pipes[!!]
include builtins\pipeio.e
include builtins\serialize.e
include ppm.e -- read_ppm()

sequence pipes = repeat(0,3)
pipes[PIPEIN] = create_pipe(INHERIT_WRITE)

-- Create the child process, with replacement stdin. 
string cmd = sprintf("%s viewppm -save test.jpg",{get_interpreter(true)})
atom hProc = system_exec(cmd, 12, pipes),
     hPipe = pipes[PIPEIN][WRITE_PIPE]

sequence img = serialize(read_ppm("Lena.ppm",bFlat:=true))
if not write_to_pipe(hPipe,img) then crash("error") end if

-- Close the pipe handle so the child process stops reading. 
--hPipe = close_handles(hPipe)
pipes = close_handles(pipes) -- (may as well do the lot)

?"done"
{} = wait_key()

PicoLisp

# Create an empty image of 120 x 90 pixels
(setq *Ppm (make (do 90 (link (need 120)))))

# Fill background with green color
(ppmFill *Ppm 0 255 0)

# Draw a diagonal line
(for I 80 (ppmSetPixel *Ppm I I 0 0 0))

# Write to "img.jpg" through a pipe
(ppmWrite *Ppm '("convert" "-" "img.jpg"))

Python

"""
Adapted from https://stackoverflow.com/questions/26937143/ppm-to-jpeg-jpg-conversion-for-python-3-4-1
Requires pillow-5.3.0 with Python 3.7.1 32-bit on Windows.
Sample ppm graphics files from http://www.cs.cornell.edu/courses/cs664/2003fa/images/
"""

from PIL import Image

im = Image.open("boxes_1.ppm")
im.save("boxes_1.jpg")

Does not need to pipe through a conversion utility because the Pillow module does the conversion.

Racket

(define (ppm->jpeg bitmap [jpg-file "output"] [quality 75])
  (define command (format "convert ppm:- -quality ~a jpg:~a.jpg" quality jpg-file))
  (match-define (list in out pid err ctrl)  (process command))
  (bitmap->ppm bitmap out)
  (close-input-port in)
  (close-output-port out))

(ppm->jpeg bm)

Raku

(formerly Perl 6)

# Reference:
# https://rosettacode.org/wiki/Bitmap/Write_a_PPM_file#Raku

use v6;

class Pixel { has uint8 ($.R, $.G, $.B) }
class Bitmap {
    has UInt ($.width, $.height);
    has Pixel @!data;

    method fill(Pixel $p) {
        @!data = $p.clone xx ($!width*$!height)
    }
    method pixel(
          $i where ^$!width,
          $j where ^$!height
          --> Pixel
      ) is rw { @!data[$i*$!height + $j] }

    method data { @!data }
}

role PPM {
    method P6 returns Blob {
        "P6\n{self.width} {self.height}\n255\n".encode('ascii')
        ~ Blob.new: flat map { .R, .G, .B }, self.data
    }
}

my Bitmap $b = Bitmap.new(width => 125, height => 125) but PPM;
for flat ^$b.height X ^$b.width -> $i, $j {
    $b.pixel($i, $j) = Pixel.new: :R($i*2), :G($j*2), :B(255-$i*2);
}

my $proc = run '/usr/bin/convert','-','output_piped.jpg', :in;
$proc.in.write: $b.P6;
$proc.in.close;
Output:
file output_piped.jpg
output_piped.jpg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 125x125, frames 3

Ruby

Extends Write ppm file#Ruby. Uses the ImageMagick convert tool. Additionally, for debugging, allow writing in pixmap P3 (ascii) format.

class Pixmap
  PIXMAP_FORMATS = ["P3", "P6"]   # implemented output formats
  PIXMAP_BINARY_FORMATS = ["P6"]  # implemented output formats which are binary

  def write_ppm(ios, format="P6")
    if not PIXMAP_FORMATS.include?(format)
      raise NotImplementedError, "pixmap format #{format} has not been implemented" 
    end
    ios.puts format, "#{@width} #{@height}", "255"
    ios.binmode if PIXMAP_BINARY_FORMATS.include?(format)
    @height.times do |y|
      @width.times do |x|
        case format
        when "P3" then ios.print @data[x][y].values.join(" "),"\n"
        when "P6" then ios.print @data[x][y].values.pack('C3')
        end
      end
    end
  end

  def save(filename, opts={:format=>"P6"})
    File.open(filename, 'w') do |f|
      write_ppm(f, opts[:format])
    end
  end

  def print(opts={:format=>"P6"})
    write_ppm($stdout, opts[:format])
  end

  def save_as_jpeg(filename, quality=75)
    pipe = IO.popen("convert ppm:- -quality #{quality} jpg:#{filename}", 'w')
    write_ppm(pipe)
    pipe.close
  end
end

image = Pixmap.open('file.ppm')
image.save_as_jpeg('file.jpg')

Standard ML

This function uses convert, and retrieves its output

val useOSConvert =  fn ppm =>
  let
   val img       =   String.translate (fn #"\"" => "\\\""|n=>str n )  ppm ;
   val app        = " convert  -  jpeg:- "
   val fname      = "/tmp/fConv" ^ (String.extract (Time.toString (Posix.ProcEnv.time()),7,NONE) );
   val shellCommand = " echo   \"" ^ img  ^ "\"  | "  ^  app  ;
   val me         = (  Posix.FileSys.mkfifo
                               (fname,
			        Posix.FileSys.S.flags [ Posix.FileSys.S.irusr,Posix.FileSys.S.iwusr ]
			       ) ; 
                       Posix.Process.fork ()
		     ) ;
  in
   if (Option.isSome me) then
     let
        val fin =BinIO.openIn fname
     in
        ( Posix.Process.sleep (Time.fromReal 0.1) ;
          BinIO.inputAll fin  before
	  (BinIO.closeIn fin ; OS.FileSys.remove fname )
	)
     end
   else
     ( OS.Process.system (  shellCommand ^ " > " ^ fname  ^ " 2>&1 "     ) ;                                 
       Word8Vector.fromList [] before OS.Process.exit OS.Process.success
     )
  end;

call and return value

useOSConvert "P3 3 2 255 255   0   0   0 255   0    0   0 255   255 255   0   255 255 255  0   0   0"  ;
val it =
   fromList[0wxFF, 0wxD8, 0wxFF, 0wxE0, 0wx0, 0wx10, 0wx4A, 0wx46, 0wx49,
      0wx46, ...]: BinIO.vector

This is the fire-and-forget version.

val demo = fn () =>
let
 val useOSConvert =  fn ppmf =>
  let
   val appopt     =   ("/usr/local/bin/convert",  ["convert","-",  "/tmp/out.jpeg"])
   val p          =   Posix.IO.pipe () ;
   val me         =   Posix.Process.fork ()
  in
   case  me of SOME cpd  =>
     (   Posix.IO.close (#outfd p);
	 Posix.IO.dup2 {old=(#infd p), new= Posix.FileSys.stdin } ;
	 Posix.IO.close (#infd p);
         Posix.Process.exec appopt
	)
     |   _   =>
     (   Posix.IO.close (#infd p);
         ppmf (#outfd p) ;
         Posix.IO.close (#outfd p) ;
	 OS.Process.exit OS.Process.success 
     )
  end;
  
 fun output_ppm fd =                                                  (* placeholder for the ppm/bitmap functionality *)
      Posix.IO.writeVec ( fd ,
       Word8VectorSlice.full ( Byte.stringToBytes
       "P3 3 2 255 255   0   0    0 255   0   0   0 255  255 255   0 255 255 255   0   0   0 " ) 
      )

in

  useOSConvert output_ppm

end ;

output, after compilation to 'demo'

shell$ demo
shell$ file /tmp/out.jpeg
/tmp/out.jpeg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 3x2, frames 3

Tcl

Referring to Write ppm file#Tcl and Basic bitmap storage#Tcl

Library: Tk
package require Tk

proc output_jpeg {image filename {quality 75}} {
    set f [open |[list convert ppm:- -quality $quality jpg:- > $filename] w]
    fconfigure $f -translation binary
    puts -nonewline $f [$image data -format ppm]
    close $f
}

However, it is more normal to do this directly with the

Library: TkImg

which is bundled with many Tcl distributions.

package require Tk
package require img::jpeg

proc output_jpeg {image filename} {
    $image write $filename -format jpeg
}
set img [image create photo -filename filename.ppm]
output_jpeg $img filename.jpg

Wren

Library: DOME

As DOME doesn't have a method for calling an external process (ImageMagick in this case), we first need to create a small plug-in in C to add this functionality.

/* gcc -O3 -std=c11 -shared -o pipeconv.so -fPIC  -I./include pipeconv.c */

#include <stdlib.h>
#include <string.h>
#include "dome.h"

static DOME_API_v0* core;
static WREN_API_v0* wren;

static const char* source =  ""
"class PipeConv {\n" 
  "foreign static convert(from, to) \n"
"} \n";

void C_convert(WrenVM* vm) {
    const char *from = wren->getSlotString(vm, 1);
    const char *to = wren->getSlotString(vm, 2);
    char command[strlen(from) + strlen(to) + 10];
    strcpy(command, "convert ");
    strcat(command, from);
    strcat(command, " ");
    strcat(command, to);
    int res = system(command);
}

DOME_EXPORT DOME_Result PLUGIN_onInit(DOME_getAPIFunction DOME_getAPI, DOME_Context ctx) {
    core = DOME_getAPI(API_DOME, DOME_API_VERSION);
    wren = DOME_getAPI(API_WREN, WREN_API_VERSION);
    core->registerModule(ctx, "pipeconv", source);
    core->registerClass(ctx, "pipeconv", "PipeConv", NULL, NULL);
    core->registerFn(ctx, "pipeconv", "static PipeConv.convert(_,_)", C_convert);
    return DOME_RESULT_SUCCESS;
}

DOME_EXPORT DOME_Result PLUGIN_preUpdate(DOME_Context ctx) {
    return DOME_RESULT_SUCCESS;
}

DOME_EXPORT DOME_Result PLUGIN_postUpdate(DOME_Context ctx) {
    return DOME_RESULT_SUCCESS;
}

DOME_EXPORT DOME_Result PLUGIN_preDraw(DOME_Context ctx) {
    return DOME_RESULT_SUCCESS;
}

DOME_EXPORT DOME_Result PLUGIN_postDraw(DOME_Context ctx) {
    return DOME_RESULT_SUCCESS;
}

DOME_EXPORT DOME_Result PLUGIN_onShutdown(DOME_Context ctx) {
    return DOME_RESULT_SUCCESS;
}

This assumes that the dome.h header file is copied to an include sub-directory of the current one and that the resulting pipeconv.so shared library file is created in the latter.

We can now use this plug-in in the following script which calls ImageMagick to convert the output.ppm file to a jpg file and then loads the latter and displays it.

import "graphics" for Canvas, ImageData
import "dome" for Window
import "plugin" for Plugin

Plugin.load("pipeconv")

import "pipeconv" for PipeConv

class ConvertPPM {
    construct new(fileName, fileName2, width, height) {
        Window.resize(width, height)
        Canvas.resize(width, height)
        Window.title = "Convert PPM file via pipe"
        // convert .ppm file to .jpg via a pipe
        PipeConv.convert(fileName, fileName2)
        // load and display .jpg file
        var image = ImageData.loadFromFile(fileName2)
        image.draw(0, 0)
    }

    init() {}

    update() {}

    draw(alpha) {}
}

var Game = ConvertPPM.new("output.ppm", "output_piped.jpg", 350, 350)

zkl

Translation of: C

Uses the PPM class from http://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#zkl

Using the convert utility by ImageMagick:

img:=PPM.readPPMFile("fractal.ppm");
p:=System.popen(0'|convert ppm:- jpg:"fractal.jpg"|,"w");
img.write(p); p.close();