// imagemagick plugins const std = @import("std"); const images = @import("image.zig"); const log = std.log.scoped(.scritcher_magick); const Image = images.Image; const mc = @cImport({ @cInclude("wand/magick_wand.h"); }); pub const MagickContext = struct { wand: *mc.MagickWand, pub fn init() !MagickContext { mc.InitializeMagick(null); var wand = mc.NewMagickWand(); if (wand == null) return error.WandCreateFail; return MagickContext{ .wand = wand.?, }; } pub fn deinit(self: *MagickContext) void { _ = mc.DestroyMagickWand(self.wand); mc.DestroyMagick(); } pub fn doErr(self: *MagickContext) !void { _ = self; return error.WandError; } }; fn magickLoad(image: *Image) !MagickContext { var mctx = try MagickContext.init(); errdefer mctx.deinit(); var curpath = try std.cstr.addNullByte(image.allocator, image.curpath); defer image.allocator.free(curpath); log.debug("loading '{s}'", .{curpath}); if (mc.MagickReadImage(mctx.wand, curpath.ptr) != 1) return error.MagickReadFail; return mctx; } fn magickSave(image: *Image, wand: *mc.MagickWand) !void { const allocator = image.allocator; var tmpnam = try images.temporaryName(allocator); var c_tmpnam = try std.cstr.addNullByte(allocator, tmpnam); defer allocator.free(c_tmpnam); log.debug("\tmagick: saving to '{s}'..", .{c_tmpnam}); if (mc.MagickWriteImage(wand, c_tmpnam.ptr) != 1) return error.MagickWriteFail; log.debug("OK", .{}); try image.reopen(tmpnam); } /// Rotate the given image. /// bgfill must point to a null-terminated string. pub fn runRotate(image: *Image, deg: f32, bgfill: []const u8) !void { var mctx = try magickLoad(image); defer mctx.deinit(); var bg = mc.NewPixelWand(); defer mc.DestroyPixelWand(bg); if (mc.PixelSetColor(bg, bgfill.ptr) != 1) return error.PixelSetColorFail; if (mc.MagickRotateImage(mctx.wand, bg, deg) != 1) return error.RotateFail; try magickSave(image, mctx.wand); }