Example of a failed Haskell FFI binding
Example of a failed Haskell FFI binding Table of Contents
- 1 Guide
- 2 code
- 3 conclusion
1
Introduction
Developing my Emacs plug-in
You need a small tool that can crop the transparent part of PNG,
Unfortunately, there is no small program that can automatically perform this activity. So, hackage
I found a graphics library, the GD package.
,
The libgd is used to flip the underlying layer.
, The advantage of this library is that it is simple, just a few interfaces, other graphics libraries are too complicated, that is beyond
I don't like the size of programs that can be written without turning over documents.
2
Code
OK. You only need a few lines of code. Using Haskell as a script for such fine-grained work is actually quite advantageous, with a theoretical speed
It is much faster than the script. After all, it is a static compilation language, and the number of lines of code is quite streamlined. After all, there is a high-order function, which can be like a pipeline.
Splicing function. In terms of beauty, there is nothing more to say, the Code itself can be very aesthetic.
But ...... here I hit the wall .......
The code is here:
This Haskell code can be compiled at a slower speed .....
Corresponding C version code:
# Include <GD. h> <br/> # include <stdio. h> <br/> int cutoffwhiteregion (const char * filename, const int edge) <br/>{< br/> file * src = fopen (filename, "rb "); <br/> If (src = NULL) {<br/> fprintf (stderr, "can't read from % S. /n ", filename); <br/> return 1; <br/>}< br/> gdimageptr in = gdimagecreatefrompng (SRC); <br/> fclose (SRC ); </P> <p> If (! Gdimagetruecolor (in) {<br/> fprintf (stderr, "% s is not a true color PNG. /n ", filename); <br/> gdimagedestroy (in); <br/> return 1; <br/>}</P> <p> const int width = gdimagesx (in); <br/> const int Height = gdimagesy (in ); </P> <p> int min_w = width; <br/> int max_w = 0; <br/> int min_h = height; <br/> int max_h = 0; </P> <p> for (int w = 0; W <width; ++ W) {<br/> for (INT h = 0; H <peight; ++ h) {<br/> Int c = gdimagegetpixel (in, W, H); <br/> If (gdimagered (in, c )! = 255 & gdimagegreen (in, c )! = 255 & <br/> gdimageblue (in, c )! = 255) {<br/> min_w = min_w <W? Min_w: W; <br/> max_w = max_w> W? Max_w: W; <br/> min_h = min_h <p? Min_h: H; <br/> max_h = max_h> H? Max_h: H; <br/>}</P> <p> file * res = fopen (filename, "WB "); <br/> If (RES = NULL) {<br/> fprintf (stderr, "can't write to % S. /n ", filename); <br/> return 1; <br/>}< br/>/* gdimageptr out = gdimagecreatetruecolor (max_w-min_w + edge * 3/2, max_h-min_h + edge * 3/2); */<br/> gdimageptr out = gdimagecreate (max_w-min_w + edge * 3/2, max_h-min_h + edge * 3/2 ); <br/> GDI Magecopy (out, in, <br/> edge/2, Edge/2, <br/> min_w-edge/2> 0? Min_w-edge/2: 0, <br/> min_h-edge/2> 0? Min_h-edge/2: 0, <br/> max_w-min_w + edge> max_w? Max_w: max_w-min_w + edge, <br/> max_h-min_h + edge> max_h? Max_h: max_h-min_h + edge <br/>); <br/> gdimagepng (Out, Res); </P> <p> gdimagedestroy (out ); <br/> gdimagedestroy (in); <br/> return 0; <br/>}< br/> int main (INT argc, char * argv []) <br/>{< br/> If (argc = 1) {</P> <p> fprintf (stderr, "Usage: pngcutter inputfile... /n "); <br/> return 1; <br/>}< br/> int edge = 20; <br/> for (INT I = 1; I <argc; ++ I) <br/>{< br/> cutoffwhiteregion (argv [I], edge); <br/>}< br/> return 0; <br/>}< br/>
Well, the same job can be completed in an instant in C. For Haskell, a small icon takes more than 10 seconds...
The culprit is looking at the source code of the library.
Found:
3
Conclusion
The problem is that each time you simply query a pixel value, the results will have to exchange control between the two runtime,
If an image has tens of thousands of pixels, the latency is naturally significant. In fact, I personally think this type of library should be FFI,
There are only two ways:
- For high-level function interfaces, only a few small parameters are passed in each call. Of course, this database is actually written in this way, but it is very bad on the $ getpixel $ function. In fact, you can change the interface to a region for each query, you can also extract all the information, store it in a C array, and then convert it to the Haskell data structure during the Haskell runtime. In addition, the macro interface provided by the C library is never used during the call, that is, the interface is not changed when the other version is upgraded. In fact, since the other party provides a macro, it means that the corresponding function interface or data structure is unstable. In this case, it is better to write a C wrapper function for forwarding.
- Directly manipulate the underlying data, and completely simulate the data structure in C in Haskell. Each sending operation directly submits the raw memory block in Haskell for the C runtime. During the extraction, you can directly operate the bare memory block of C. This requires a good understanding of the bottom-layer details of the other party. However, if the data structure is stable, it is worth a try.
Note: My little plug-in is almost finished. The effect is as above. You can roughly restore the pretty appearance in Emacs (the ugly look in the csdn code box is not allowed !!), However, comment does not have a satisfactory solution.
Makefile of C code:
Cxx = g ++ <br/> cxxflags =-wall-O3-lxpm-lx11-ljpeg-lfontconfig-lfreetype-lpng-LZ-lm-LGD <br/> All: pngcutter <br/> pngcutter: pngcutter. c <br/> $ (cxx) $ (cxxflags)-O pngcutter. c
Haskell version code:
Module main where <br/> import system. filepath <br/> import system. environment <br/> Import graphics. GD. bytestring <br/> import control. monad <br/> main: Io () <br/> main = <br/> DO <br/> FP <-head 'libtm 'getargs <br/> pngimage <-loadpngfile FP <br/> (width, height) <-imagesize pngimage </P> <p> let Ws = [0 .. width-1] <br/> HS = [0 .. height-1] <br/> (points, iocolor) = unzip <br/> [(W, h) <br/>, getpixel (W, h) pngimage) <br/> | W <-WS, h <-Hs] </P> <p> colors <-sequence iocolor <br/> Print $ length colors <br/> let (base, ext) = splitextension FP <br/> newname = base ++ "_ new" ++ <br/> ext <br/> pointsnottransparnt = <br/> FST $ unzip <br/> $ filter (/(_, alpha)-> alpha/= 127) <br/> $ zip Points <br/> $ map (/(_, alpha)-> alpha ). torgba) <br/> colors </P> <p> (widths, heights) = unzip pointsnottransparnt <br/> (x1, x2) = (minimum widths, maximum widths) <br/> (Y1, Y2) = (minimum heights, maximum heights) <br/> startpoint = (x1, Y1) <br/> size = (x2-x1, y2-y1) </P> <p> newpng <-newimage size <br/> copyregion startpoint size pngimage (1, 1) newpng <br/> savepngfile newname newpng <br/>
Author: boyun Tang
Date: 17:31:27 CST
HTML generated by org-mode 7.4 In Emacs 23