├── .gitignore ├── LICENSE ├── README.md ├── examples ├── liquidrescale.nim └── nim.cfg ├── nimagemagick.nimble └── src ├── nimagemagick.nim └── nimagemagick └── missing.nim /.gitignore: -------------------------------------------------------------------------------- 1 | nimcache/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Zed 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nimagemagick 2 | 3 | nimagemagick is an [ImageMagick 7.0](https://www.imagemagick.org/) wrapper for Nim. 4 | It wraps the [MagickCore](https://imagemagick.org/script/magick-core.php) and 5 | [MagickWand](https://imagemagick.org/script/magick-wand.php) APIs, though at 6 | this point it's mostly a "thin" wrapper. With time more functions will be covered 7 | by the wrapper for a convenient interface. See the current progress in 8 | [src/nimagemagick.nim](https://github.com/zedeus/nimagemagick/blob/master/src/nimagemagick.nim) 9 | 10 | The wrapper gets automatically generated at compile time using the 11 | [nimterop](https://github.com/genotrance/nimterop/) package, which leverages the 12 | [tree-sitter](https://github.com/tree-sitter/tree-sitter) project. 13 | 14 | Requires ImageMagick **7.0** or later. Run `convert -version` to check. 15 | Only tested on Linux. 16 | 17 | Contributions are welcome. 18 | 19 | ### Installation 20 | ```sh 21 | nimble install https://github.com/zedeus/nimagemagick.git 22 | ``` 23 | 24 | ### Example usage 25 | ```nim 26 | import nimagemagick 27 | 28 | proc main = 29 | # create a new wand, and read the built-in logo image 30 | var wand = newWand("logo:") 31 | wand.resizeImage(200, 200) 32 | wand.displayImage() 33 | 34 | # save the image 35 | wand.writeImage("logo.png") 36 | 37 | # genesis must be called to setup the MagickWand environment 38 | genesis() 39 | 40 | main() 41 | 42 | # terminus must be called after the main proc has exited. 43 | # If called inside main, the wands will be destroyed after 44 | # the environment has terminated, leading to a segfault 45 | terminus() 46 | ``` 47 | 48 | See other examples in the examples folder. 49 | 50 | ### Notes 51 | 52 | To use a function from MagickCore/MagickWand that doesn't yet have a 53 | wrapper in wand.nim, `ptr MagickWand` can be accessed via the `impl` 54 | field of any `Wand` object. Here's the example above without the 55 | convenience wrappers: 56 | 57 | ```nim 58 | import nimagemagick 59 | 60 | proc main = 61 | var wand = newWand() 62 | discard MagickReadImage(wand.impl, "logo:") 63 | discard MagickResizeImage(wand.impl, 200, 200, LanczosFilter) 64 | discard MagickDisplayImage(wand.impl, "") 65 | 66 | MagickWandGenesis() 67 | main() 68 | MagickWandTerminus() 69 | ``` 70 | 71 | If you don't want to use the Wand object, replace `newWand()` with 72 | `NewMagickWand()`, and remember to call `DestroyMagickWand(wand)`. 73 | -------------------------------------------------------------------------------- /examples/liquidrescale.nim: -------------------------------------------------------------------------------- 1 | import std/[lenientops, strutils] 2 | 3 | import nimagemagick 4 | 5 | proc main = 6 | var 7 | wand = newWand("logo:") 8 | width = wand.width 9 | height = wand.height 10 | 11 | # rescale down to 50%, then back to original size 12 | wand.liquidRescale(width * 0.5, height * 0.5) 13 | wand.resizeImage(width, height) 14 | 15 | wand.displayImage() 16 | 17 | genesis() 18 | main() 19 | terminus() 20 | 21 | 22 | #[ The equivalent in C style (minus error handling) 23 | proc main = 24 | var wand = NewMagickWand() 25 | discard MagickReadImage(wand, "logo:") 26 | 27 | var 28 | width = MagickGetImageWidth(wand) 29 | height = MagickGetImageHeight(wand) 30 | 31 | discard MagickLiquidRescaleImage(wand, cuint(width * 0.5), cuint(height * 0.5), 1, 1) 32 | discard MagickResizeImage(wand, width, height, LanczosFilter) 33 | 34 | discard MagickDisplayImage(wand, "") 35 | wand = DestroyMagickWand(wand) 36 | 37 | MagickWandGenesis() 38 | main() 39 | MagickWandTerminus() 40 | ]# 41 | -------------------------------------------------------------------------------- /examples/nim.cfg: -------------------------------------------------------------------------------- 1 | path="../" 2 | -------------------------------------------------------------------------------- /nimagemagick.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.1.0" 4 | author = "zedeus" 5 | description = "ImageMagick (MagickCore and MagickWand) wrapper" 6 | license = "MIT" 7 | srcDir = "src" 8 | 9 | # Dependencies 10 | 11 | requires "nim >= 0.19.0", "nimterop >= 0.1.0" 12 | -------------------------------------------------------------------------------- /src/nimagemagick.nim: -------------------------------------------------------------------------------- 1 | import std/strutils 2 | import nimterop/cimport 3 | 4 | const 5 | libs = gorgeEx("MagickWand-config --libs").output 6 | flags = gorgeEx("MagickWand-config --cflags").output 7 | path = flags[2 .. ^1].split(" ")[0] 8 | header = path & "/MagickWand/MagickWand.h" 9 | 10 | {.passL: libs.} 11 | {.passC: flags.} 12 | {.hint[ConvFromXtoItselfNotNeeded]: off.} 13 | 14 | include ./nimagemagick/missing 15 | 16 | cPlugin: 17 | import strutils 18 | 19 | proc onSymbol*(sym: var Symbol) {.exportc, dynlib.} = 20 | sym.name = sym.name.strip(chars={'_'}) 21 | sym.name = sym.name.replace("___", "_") 22 | sym.name = sym.name.replace("__", "_") 23 | 24 | cIncludeDir(path) 25 | cImport(header, recurse=true) 26 | 27 | converter bToM*(b: bool): MagickBooleanType = 28 | if b: MagickTrue else: MagickFalse 29 | 30 | converter mToB*(m: MagickBooleanType): bool = 31 | m == MagickTrue 32 | 33 | type 34 | Wand* = object 35 | impl*: ptr MagickWand 36 | 37 | proc `=destroy`*(wand: var Wand) = 38 | wand.impl = DestroyMagickWand(wand.impl) 39 | 40 | proc wandException*(wand: Wand) = 41 | var 42 | severity: ExceptionType 43 | description = MagickGetException(wand.impl, addr severity) 44 | error = $(severity.int) & ": " & $description 45 | discard MagickRelinquishMemory(description) 46 | raise newException(IOError, error) 47 | 48 | proc genesis* = 49 | MagickWandGenesis() 50 | 51 | proc terminus* = 52 | MagickWandTerminus() 53 | 54 | proc newWand*(): Wand = 55 | result.impl = NewMagickWand() 56 | 57 | proc cloneWand*(wand: Wand): Wand = 58 | result.impl = CloneMagickWand(wand.impl) 59 | 60 | proc readImage*(wand: Wand; image: string) = 61 | if not MagickReadImage(wand.impl, image): 62 | wandException(wand) 63 | 64 | proc newWand*(image: string): Wand = 65 | result = newWand() 66 | if not MagickReadImage(result.impl, image): 67 | wandException(result) 68 | 69 | proc writeImage*(wand: Wand; image: string) = 70 | if not MagickWriteImage(wand.impl, image): 71 | wandException(wand) 72 | 73 | proc displayImage*(wand: Wand; server="") = 74 | if not MagickDisplayImage(wand.impl, server): 75 | wandException(wand) 76 | 77 | proc appendImages*(wand: Wand; stack: bool): Wand = 78 | result.impl = MagickAppendImages(wand.impl, stack) 79 | 80 | proc addImage*(wand, addWand: Wand): bool {.discardable.} = 81 | MagickAddImage(wand.impl, addWand.impl) 82 | 83 | proc setSize*(wand: Wand; width, height: SomeNumber): bool {.discardable.} = 84 | MagickSetSize(wand.impl, width.cuint, height.cuint) 85 | 86 | proc setFirstIterator*(wand: Wand) = 87 | MagickSetFirstIterator(wand.impl) 88 | 89 | proc setLastIterator*(wand: Wand) = 90 | MagickSetLastIterator(wand.impl) 91 | 92 | proc liquidRescale*(wand: Wand; columns, rows: SomeNumber; 93 | deltaX=1.0; rigidity=1.0): bool {.discardable.} = 94 | MagickLiquidRescaleImage(wand.impl, columns.cuint, rows.cuint, 95 | deltaX.cdouble, rigidity.cdouble) 96 | 97 | proc resizeImage*(wand: Wand; columns, rows: SomeNumber; 98 | filter=LanczosFilter): bool {.discardable.} = 99 | MagickResizeImage(wand.impl, columns.cuint, rows.cuint, filter) 100 | 101 | proc width*(wand: Wand): int = 102 | MagickGetImageWidth(wand.impl).int 103 | 104 | proc height*(wand: Wand): int = 105 | MagickGetImageHeight(wand.impl).int 106 | 107 | proc getSize*(wand: Wand): (int, int) = 108 | (wand.width, wand.height) 109 | -------------------------------------------------------------------------------- /src/nimagemagick/missing.nim: -------------------------------------------------------------------------------- 1 | # function types missing due to this tree-sitter issue 2 | # https://github.com/tree-sitter/tree-sitter-cpp/issues/28 3 | 4 | cOverride: 5 | type 6 | ImageView* = object 7 | WandView* = object 8 | PixelWand* = object 9 | 10 | type 11 | DuplexTransferImageViewMethod* = proc(a1: ptr ImageView, a2: ptr ImageView, a3: ptr ImageView, a4: cint, a5: cint, a6: ptr ImageView): bool {.nimcall.} 12 | GetImageViewMethod* = proc(a1: ptr ImageView, a2: cint, a3: cint, a4: ptr ImageView): bool {.nimcall.} 13 | SetImageViewMethod* = proc(a1: ptr ImageView, a2: cint, a3: cint, a4: ptr ImageView): bool {.nimcall.} 14 | TransferImageViewMethod* = proc(a1: ptr ImageView, a2: ptr ImageView, a3: cint, a4: cint, a5: ptr ImageView): bool {.nimcall.} 15 | UpdateImageViewMethod* = proc(a1: ptr ImageView, a2: cint, a3: cint, a4: ptr ImageView): bool {.nimcall.} 16 | DuplexTransferWandViewMethod* = proc(a1: ptr WandView, a2: ptr WandView, a3: ptr WandView, a4: cint, a5: cint, a6: ptr WandView): bool {.nimcall.} 17 | GetWandViewMethod* = proc(a1: ptr WandView, a2: cint, a3: cint, a4: ptr WandView): bool {.nimcall.} 18 | SetWandViewMethod* = proc(a1: ptr WandView, a2: cint, a3: cint, a4: ptr WandView): bool {.nimcall.} 19 | TransferWandViewMethod* = proc(a1: ptr WandView, a2: ptr WandView, a3: cint, a4: cint, a5: ptr WandView): bool {.nimcall.} 20 | UpdateWandViewMethod* = proc(a1: ptr WandView, a2: cint, a3: cint, a4: ptr WandView): bool {.nimcall.} 21 | 22 | proc ClonePixelWand*(a1: ptr PixelWand): ptr PixelWand {.importc: "ClonePixelWand", header: header.} 23 | proc DestroyPixelWand*(a1: ptr PixelWand): ptr PixelWand {.importc: "DestroyPixelWand", header: header.} 24 | proc NewPixelWand*(): ptr PixelWand {.importc: "NewPixelWand", header: header.} 25 | 26 | type 27 | ChannelPerceptualHash* {.importc: "struct _ChannelPerceptualHash", header: header, bycopy.} = object 28 | srgb_hu_phash*: array[9, cdouble] 29 | hclp_hu_phash*: array[9, cdouble] 30 | number_colorspaces*: cuint 31 | colorspace*: array[7, pointer] 32 | phash*: array[7, array[9, cdouble]] 33 | number_channels*: cuint 34 | --------------------------------------------------------------------------------