Using the data storage type definedon this page for raster images, delegate writing a JPEG file through apipe using theoutput_ppm function definedon 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 (fromImageMagick, multi-platform).
I use themagick
command from ImageMagick. You need all the source files fromBitmap#ATS,Bitmap/Read_a_PPM_file#ATS, andBitmap/Write_a_PPM_file#ATS. (You donot need the files fromGrayscale_image#ATS.)
See alsoBitmap/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) endfn {}fileref_pclose_unsafe (f : FILEref) : int = (* Returns the exit status of the command. *) $extfcall (int, "pclose", $UNSAFE.cast{FILEstar} f)(*------------------------------------------------------------------*)implementmain0 (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
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
This one uses the ImageMagickconvert tool.
/* interface */voidprint_jpg(imageimg,intqual);
#define MAXCMDBUF 100voidprint_jpg(imageimg,intqual){charbuf[MAXCMDBUF];unsignedintn;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 ofoutput_ppm of course. A complete example is
#include"imglib.h"intmain(){imageimg;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 codeshere andhere
Constancho=400Constalto=300DimAsIntegeri,x,yDimAsUlongkolor' Set up graphicsScreenresancho,alto,32Windowtitle"Pattern Generator"' A little extravagant, this draws a design of dots and lines' Fill background with color Rgb(&h40, &h80, &hc0)Line(0,0)-(ancho-1,alto-1),Rgb(64,128,192),BF' Draw random dots Rgb(&h20, &h40, &h80)Fori=1To2000Pset(Rnd*(ancho-1),Rnd*(alto-1)),Rgb(32,64,128)Next' Draw horizontal linesForx=0Toancho-1Fory=240To244Pset(x,y),Rgb(32,64,128)NextFory=260To264Pset(x,y),Rgb(32,64,128)NextNext' Draw vertical linesFory=0Toalto-1Forx=80To84Pset(x,y),Rgb(32,64,128)NextForx=95To99Pset(x,y),Rgb(32,64,128)NextNext' Open PPM file to writeDimAsIntegerff=FreefileOpen"noutput.ppm"ForBinaryAs#ffIfErr>0ThenPrint"Error opening output file":End' Write PPM headerPut#ff,,"P6"&Chr(10)Put#ff,,Str(ancho)&" "&Str(alto)&Chr(10)Put#ff,,"255"&Chr(10)' Write pixel dataFory=0Toalto-1Forx=0Toancho-1kolor=Point(x,y)Put#ff,,Cbyte(kolorShr16)' BluePut#ff,,Cbyte(kolorShr8)' GreenPut#ff,,Cbyte(kolor)' RedNextNextClose#ff' Convert to JPG using ImageMagick (pipe logic)Shell"magick.exe noutput.ppm noutput.jpg"Sleep
(Go 1 should be close)
Using cjpeg:
packagemain// Files required to build supporting package raster are found in:// * Bitmap// * Write a PPM fileimport("fmt""math/rand""os/exec""raster")funcmain(){b:=raster.NewBitmap(400,300)// a little extravagant, this draws a design of dots and linesb.FillRgb(0xc08040)fori:=0;i<2000;i++{b.SetPxRgb(rand.Intn(400),rand.Intn(300),0x804020)}forx:=0;x<400;x++{fory:=240;y<245;y++{b.SetPxRgb(x,y,0x804020)}fory:=260;y<265;y++{b.SetPxRgb(x,y,0x804020)}}fory:=0;y<300;y++{forx:=80;x<85;x++{b.SetPxRgb(x,y,0x804020)}forx:=95;x<100;x++{b.SetPxRgb(x,y,0x804020)}}// pipe logicc:=exec.Command("cjpeg","-outfile","pipeout.jpg")pipe,err:=c.StdinPipe()iferr!=nil{fmt.Println(err)return}err=c.Start()iferr!=nil{fmt.Println(err)return}err=b.WritePpmTo(pipe)iferr!=nil{fmt.Println(err)return}err=pipe.Close()iferr!=nil{fmt.Println(err)}}
usingImages,FileIOppmimg=load("data/bitmapInputTest.ppm")save("data/bitmapOutputTest.jpg",ppmimg)
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.40importjava.awt.Colorimportjava.awt.Graphicsimportjava.awt.image.BufferedImageclassBasicBitmapStorage(width:Int,height:Int){valimage=BufferedImage(width,height,BufferedImage.TYPE_3BYTE_BGR)funfill(c:Color){valg=image.graphicsg.color=cg.fillRect(0,0,image.width,image.height)}funsetPixel(x:Int,y:Int,c:Color)=image.setRGB(x,y,c.getRGB())fungetPixel(x:Int,y:Int)=Color(image.getRGB(x,y))}funmain(args:Array<String>){// create BasicBitmapStorage objectvalwidth=640valheight=640valbbs=BasicBitmapStorage(width,height)for(yin0untilheight){for(xin0untilwidth){valc=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 diskvalpb=ProcessBuilder("convert","-","output_piped.jpg")pb.directory(null)pb.redirectInput(ProcessBuilder.Redirect.PIPE)valbuffer=ByteArray(width*3)// write one line at a timevalproc=pb.start()valpStdIn=proc.outputStreampStdIn.use{valheader="P6\n$width $height\n255\n".toByteArray()with(it){write(header)for(yin0untilheight){for(xin0untilwidth){valc=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)}}}}
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[]"];];
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.
importbitmapimportppm_writeimportosproc# Build an image.varimage=newImage(100,50)image.fill(color(255,0,0))forrowin10..20:forcolin0..<image.w:image[col,row]=color(0,255,0)forrowin30..40:forcolin0..<image.w:image[col,row]=color(0,0,255)# Launch ImageMagick "convert".# Input is taken from stdin and result written in "output1.jpeg".varp=startProcess("convert",args=["ppm:-","output1.jpeg"],options={poUsePath})varstream=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()
letprint_jpeg~img?(quality=96)()=letcmd=Printf.sprintf"cjpeg -quality %d"qualityin(* let cmd = Printf.sprintf "ppmtojpeg -quality %d" quality in let cmd = Printf.sprintf "convert ppm:- -quality %d jpg:-" quality in *)letic,oc=Unix.open_processcmdinoutput_ppm~img~oc;trywhiletruedoletc=input_charicinprint_charcdonewithEnd_of_file->();;
# 20211224 Perl programming solutionusestrict;usewarnings;useImager;useImager::Test'test_image_raw';my$img=test_image_raw();my$IO=Imager::io_new_bufchain();Imager::i_writeppm_wiol($img,$IO)ordie;my$raw=Imager::io_slurp($IO)ordie;openmy$fh,'|-','/usr/local/bin/convert - -compress none output.jpg'ordie;binmode$fh;syswrite$fh,$rawordie;close$fh;
file output.jpgoutput.jpg: JPEG image data, JFIF standard 1.01, comment: "CREATOR: Imager"magick identify output.jpgoutput.jpg JPEG 150x150 150x150+0+0 8-bit sRGB 3952B 0.000u 0:00.012
Uses the provided demo\rosetta\viewppm.exw utility to accomplish this task.
-- demo\rosetta\Bitmap_PPM_conversion_through_a_pipe.exwwithoutjs-- file i/o, system_exec(), pipes[!!]includebuiltins\pipeio.eincludebuiltins\serialize.eincludeppm.e-- read_ppm()sequencepipes=repeat(0,3)pipes[PIPEIN]=create_pipe(INHERIT_WRITE)-- Create the child process, with replacement stdin.stringcmd=sprintf("%s viewppm -save test.jpg",{get_interpreter(true)})atomhProc=system_exec(cmd,12,pipes),hPipe=pipes[PIPEIN][WRITE_PIPE]sequenceimg=serialize(read_ppm("Lena.ppm",bFlat:=true))ifnotwrite_to_pipe(hPipe,img)thencrash("error")endif-- 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()
# 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"))
"""Adapted from https://stackoverflow.com/questions/26937143/ppm-to-jpeg-jpg-conversion-for-python-3-4-1Requires 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/"""fromPILimportImageim=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.
(define(ppm->jpegbitmap[jpg-file"output"][quality75])(definecommand(format"convert ppm:- -quality ~a jpg:~a.jpg"qualityjpg-file))(match-define(listinoutpiderrctrl)(processcommand))(bitmap->ppmbitmapout)(close-input-portin)(close-output-portout))(ppm->jpegbm)
(formerly Perl 6)
# Reference:# https://rosettacode.org/wiki/Bitmap/Write_a_PPM_file#Rakuusev6;classPixel {hasuint8 ($.R,$.G,$.B) }classBitmap {hasUInt ($.width,$.height);hasPixel@!data;methodfill(Pixel$p) {@!data =$p.clonexx ($!width*$!height) }methodpixel($iwhere ^$!width,$jwhere ^$!height -->Pixel )isrw {@!data[$i*$!height +$j] }methoddata {@!data }}rolePPM {methodP6returnsBlob {"P6\n{self.width} {self.height}\n255\n".encode('ascii') ~Blob.new:flatmap { .R, .G, .B },self.data }}myBitmap$b =Bitmap.new(width =>125,height =>125)butPPM;forflat ^$b.heightX ^$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;
file output_piped.jpgoutput_piped.jpg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 125x125, frames 3
ExtendsWrite ppm file#Ruby. Uses the ImageMagickconvert
tool.Additionally, for debugging, allow writing in pixmap P3 (ascii) format.
classPixmapPIXMAP_FORMATS=["P3","P6"]# implemented output formatsPIXMAP_BINARY_FORMATS=["P6"]# implemented output formats which are binarydefwrite_ppm(ios,format="P6")ifnotPIXMAP_FORMATS.include?(format)raiseNotImplementedError,"pixmap format#{format} has not been implemented"endios.putsformat,"#{@width}#{@height}","255"ios.binmodeifPIXMAP_BINARY_FORMATS.include?(format)@height.timesdo|y|@width.timesdo|x|caseformatwhen"P3"thenios.print@data[x][y].values.join(" "),"\n"when"P6"thenios.print@data[x][y].values.pack('C3')endendendenddefsave(filename,opts={:format=>"P6"})File.open(filename,'w')do|f|write_ppm(f,opts[:format])endenddefprint(opts={:format=>"P6"})write_ppm($stdout,opts[:format])enddefsave_as_jpeg(filename,quality=75)pipe=IO.popen("convert ppm:- -quality#{quality} jpg:#{filename}",'w')write_ppm(pipe)pipe.closeendendimage=Pixmap.open('file.ppm')image.save_as_jpeg('file.jpg')
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_ppmend ;
output, after compilation to 'demo'
shell$ demoshell$ 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
Referring toWrite ppm file#Tcl andBasic bitmap storage#Tcl
packagerequireTkprocoutput_jpeg{imagefilename{quality75}}{setf[open|[listconvertppm:--quality$qualityjpg:->$filename]w]fconfigure$f-translationbinaryputs-nonewline$f[$imagedata-formatppm]close$f}
However, it is more normal to do this directly with the
which is bundled with many Tcl distributions.
packagerequireTkpackagerequireimg::jpegprocoutput_jpeg{imagefilename}{$imagewrite$filename-formatjpeg}setimg[imagecreatephoto-filenamefilename.ppm]output_jpeg$imgfilename.jpg
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"staticDOME_API_v0*core;staticWREN_API_v0*wren;staticconstchar*source="""class PipeConv {\n""foreign static convert(from, to)\n""}\n";voidC_convert(WrenVM*vm){constchar*from=wren->getSlotString(vm,1);constchar*to=wren->getSlotString(vm,2);charcommand[strlen(from)+strlen(to)+10];strcpy(command,"convert ");strcat(command,from);strcat(command," ");strcat(command,to);intres=system(command);}DOME_EXPORTDOME_ResultPLUGIN_onInit(DOME_getAPIFunctionDOME_getAPI,DOME_Contextctx){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);returnDOME_RESULT_SUCCESS;}DOME_EXPORTDOME_ResultPLUGIN_preUpdate(DOME_Contextctx){returnDOME_RESULT_SUCCESS;}DOME_EXPORTDOME_ResultPLUGIN_postUpdate(DOME_Contextctx){returnDOME_RESULT_SUCCESS;}DOME_EXPORTDOME_ResultPLUGIN_preDraw(DOME_Contextctx){returnDOME_RESULT_SUCCESS;}DOME_EXPORTDOME_ResultPLUGIN_postDraw(DOME_Contextctx){returnDOME_RESULT_SUCCESS;}DOME_EXPORTDOME_ResultPLUGIN_onShutdown(DOME_Contextctx){returnDOME_RESULT_SUCCESS;}
This assumes that thedome.h header file is copied to aninclude sub-directory of the current one and that the resultingpipeconv.so shared library file is created in the latter.
We can now use this plug-in in the following script which callsImageMagick to convert theoutput.ppm file to ajpg file and then loads the latter and displays it.
import"graphics"forCanvas,ImageDataimport"dome"forWindowimport"plugin"forPluginPlugin.load("pipeconv")import"pipeconv"forPipeConvclassConvertPPM{constructnew(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 pipePipeConv.convert(fileName,fileName2)// load and display .jpg filevarimage=ImageData.loadFromFile(fileName2)image.draw(0,0)}init(){}update(){}draw(alpha){}}varGame=ConvertPPM.new("output.ppm","output_piped.jpg",350,350)
Uses the PPM class fromhttp://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();