From 6c26a1b74fdf55758582305f115e2fd4d845305b Mon Sep 17 00:00:00 2001 From: Sameer Rahmani Date: Tue, 27 May 2025 11:58:12 +0100 Subject: [PATCH] Setup the basic structure of libraw integration --- flake.lock | 61 ++++ flake.nix | 30 ++ libvips/foreign/foreign.c | 19 +- libvips/foreign/librawload.c | 624 +++++++++++++++++++++++++++++++++ libvips/foreign/meson.build | 8 + libvips/include/vips/foreign.h | 4 + meson.build | 8 + meson_options.txt | 5 + 8 files changed, 752 insertions(+), 7 deletions(-) create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 libvips/foreign/librawload.c diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000000..b31a753765 --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1743550720, + "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "c621e8422220273271f52058f618c94e405bb0f5", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1748026106, + "narHash": "sha256-6m1Y3/4pVw1RWTsrkAK2VMYSzG4MMIj7sqUy7o8th1o=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "063f43f2dbdef86376cc29ad646c45c46e93234c", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1743296961, + "narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000000..6d7987c265 --- /dev/null +++ b/flake.nix @@ -0,0 +1,30 @@ +{ + description = "libvips is a demand-driven, horizontally threaded image processing library."; + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-parts.url = "github:hercules-ci/flake-parts"; + }; + + outputs = inputs@{ flake-parts, ... }: + flake-parts.lib.mkFlake { inherit inputs; } ({ lib, ... }: { + systems = [ + "x86_64-linux" + ]; + + perSystem = { pkgs, ... }: + let + vips' = pkgs.vips.overrideAttrs (drv: { + src = ./.; + mesonFlags = drv.mesonFlags ++ [ + (lib.mesonEnable "libraw" true) + ]; + buildInputs = drv.buildInputs ++ [ + pkgs.libraw + ]; + }); + in + { + packages.default = vips'; + }; + }); +} diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index a7d90489ea..1a7c5556d3 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -1467,8 +1467,7 @@ vips_foreign_apply_saveable(VipsImage *in, VipsImage **ready, * source. */ if (saveable & VIPS_FOREIGN_SAVEABLE_RGB) { - interpretation = sixteenbit ? - VIPS_INTERPRETATION_RGB16 : VIPS_INTERPRETATION_sRGB; + interpretation = sixteenbit ? VIPS_INTERPRETATION_RGB16 : VIPS_INTERPRETATION_sRGB; if (vips_colourspace(in, &out, interpretation, NULL)) { g_object_unref(in); @@ -1486,9 +1485,9 @@ vips_foreign_apply_saveable(VipsImage *in, VipsImage **ready, */ if (saveable & VIPS_FOREIGN_SAVEABLE_CMYK) { if (vips_icc_export(in, &out, - "output-profile", "cmyk", - "depth", sixteenbit ? 16 : 8, - NULL)) { + "output-profile", "cmyk", + "depth", sixteenbit ? 16 : 8, + NULL)) { g_object_unref(in); return -1; } @@ -1503,8 +1502,7 @@ vips_foreign_apply_saveable(VipsImage *in, VipsImage **ready, * source. */ if (saveable & VIPS_FOREIGN_SAVEABLE_MONO) { - interpretation = sixteenbit ? - VIPS_INTERPRETATION_GREY16 : VIPS_INTERPRETATION_B_W; + interpretation = sixteenbit ? VIPS_INTERPRETATION_GREY16 : VIPS_INTERPRETATION_B_W; if (vips_colourspace(in, &out, interpretation, NULL)) { g_object_unref(in); @@ -3052,6 +3050,9 @@ vips_foreign_operation_init(void) extern GType vips_foreign_load_nsgif_buffer_get_type(void); extern GType vips_foreign_load_nsgif_source_get_type(void); + extern GType vips_foreign_load_libraw_file_get_type(void); + extern GType vips_foreign_load_libraw_source_get_type(void); + extern GType vips_foreign_save_cgif_file_get_type(void); extern GType vips_foreign_save_cgif_buffer_get_type(void); extern GType vips_foreign_save_cgif_target_get_type(void); @@ -3143,6 +3144,10 @@ vips_foreign_operation_init(void) vips_foreign_load_nsgif_source_get_type(); #endif /*HAVE_NSGIF*/ +#ifdef HAVE_LIBRAW + vips_foreign_load_libraw_file_get_type(); +#endif /*HAVE_LIBRAW*/ + #ifdef HAVE_CGIF vips_foreign_save_cgif_file_get_type(); vips_foreign_save_cgif_buffer_get_type(); diff --git a/libvips/foreign/librawload.c b/libvips/foreign/librawload.c new file mode 100644 index 0000000000..62ce913363 --- /dev/null +++ b/libvips/foreign/librawload.c @@ -0,0 +1,624 @@ +/* load a RAW with libraw + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define VERBOSE +#define VIPS_DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#ifdef HAVE_LIBRAW + +#include +#include +#include + +#include +#include +#include + +#include + +static const char *vips_foreign_libraw_suffs[] = { + ".arw", ".cr2", ".cr3", ".crw", ".dng", ".nef", ".nrw", + ".orf", ".pef", ".raf", ".raw", ".rw2", ".srw", ".x3f", + ".erf", ".kdc", ".mdc", ".mos", ".pxn", ".srf", + NULL +}; + +typedef struct _VipsForeignLoadLibRaw { + VipsForeignLoad parent_object; + + /* Filename for load. + */ + char *filename; + + /* LibRaw processor. + */ + libraw_data_t *raw_processor; + + /* Load from this source (set by subclasses). + */ + VipsSource *source; + + /* Output params we use. + */ + gboolean auto_brightness; + gboolean use_camera_wb; + gboolean use_auto_wb; + int output_bps; + gboolean half_size; + int user_flip; + int interpolation_quality; + gboolean no_auto_scale; + gboolean output_tiff; +} VipsForeignLoadLibRaw; + +typedef VipsForeignLoadClass VipsForeignLoadLibRawClass; + +G_DEFINE_TYPE(VipsForeignLoadLibRaw, vips_foreign_load_libraw, + VIPS_TYPE_FOREIGN_LOAD); + +typedef struct _VipsForeignLoadLibRawFile { + VipsForeignLoadLibRaw parent_object; + + /* Filename for load. + */ + char *filename; + +} VipsForeignLoadLibRawFile; + +typedef VipsForeignLoadLibRawClass VipsForeignLoadLibRawFileClass; + +G_DEFINE_TYPE(VipsForeignLoadLibRawFile, vips_foreign_load_libraw_file, + vips_foreign_load_libraw_get_type()); + +typedef struct _VipsForeignLoadLibRawSource { + VipsForeignLoadLibRaw parent_object; + + /* Load from a source. + */ + VipsSource *source; + +} VipsForeignLoadLibRawSource; + +typedef VipsForeignLoadClass VipsForeignLoadLibRawSourceClass; + +G_DEFINE_TYPE(VipsForeignLoadLibRawSource, vips_foreign_load_libraw_source, + vips_foreign_load_libraw_get_type()); + +static void +vips_foreign_load_libraw_dispose(GObject *gobject) +{ + VipsForeignLoadLibRaw *raw = (VipsForeignLoadLibRaw *) gobject; + + if (raw->raw_processor) { + libraw_close(raw->raw_processor); + raw->raw_processor = NULL; + } + + G_OBJECT_CLASS(vips_foreign_load_libraw_parent_class)->dispose(gobject); +} + +static int +vips_foreign_load_libraw_build(VipsObject *object) +{ + VipsForeignLoadLibRaw *raw = (VipsForeignLoadLibRaw *) object; + VipsForeignLoadLibRawFile *file = (VipsForeignLoadLibRawFile *) object; + + if (file->filename) + if (!(raw->source = vips_source_new_from_file(file->filename))) + return -1; + + if (VIPS_OBJECT_CLASS(vips_foreign_load_libraw_parent_class)->build(object)) + return -1; + + return 0; +} + +static VipsForeignFlags +vips_foreign_load_libraw_get_flags_filename(const char *filename) +{ + /* RAW files are not sequential. + */ + return 0; +} + +static VipsForeignFlags +vips_foreign_load_libraw_get_flags(VipsForeignLoad *load) +{ + return 0; +} + +static int +vips_foreign_load_libraw_header(VipsForeignLoad *load) +{ + VipsForeignLoadLibRaw *raw = (VipsForeignLoadLibRaw *) load; + VipsImage *out = load->out; + int result; + + raw->raw_processor = libraw_init(0); + if (!raw->raw_processor) { + vips_error("librawload", "%s", _("unable to initialize libraw")); + return -1; + } + + /* Open and unpack the RAW file. + */ + result = libraw_open_file(raw->raw_processor, raw->filename); + if (result != LIBRAW_SUCCESS) { + vips_error("librawload", _("unable to open file \"%s\": %s"), + raw->filename, libraw_strerror(result)); + return -1; + } + + result = libraw_unpack(raw->raw_processor); + if (result != LIBRAW_SUCCESS) { + vips_error("librawload", _("unable to unpack file \"%s\": %s"), + raw->filename, libraw_strerror(result)); + return -1; + } + + /* Set processing parameters. + */ + raw->raw_processor->params.output_bps = raw->output_bps; + raw->raw_processor->params.half_size = raw->half_size; + raw->raw_processor->params.user_flip = raw->user_flip; + raw->raw_processor->params.use_camera_wb = raw->use_camera_wb; + raw->raw_processor->params.use_auto_wb = raw->use_auto_wb; + raw->raw_processor->params.no_auto_bright = !raw->auto_brightness; + raw->raw_processor->params.no_auto_scale = raw->no_auto_scale; + raw->raw_processor->params.output_tiff = raw->output_tiff; + + /* Set interpolation quality. + * 0=linear, 1=VNG, 2=PPG, 3=AHD, 4=DCB + * 11=DHT, 12=AAHD + */ + raw->raw_processor->params.user_qual = raw->interpolation_quality; + + /* Process the image. + */ + result = libraw_dcraw_process(raw->raw_processor); + if (result != LIBRAW_SUCCESS) { + vips_error("librawload", _("processing failed for \"%s\": %s"), + raw->filename, libraw_strerror(result)); + return -1; + } + + libraw_processed_image_t *processed = libraw_dcraw_make_mem_image( + raw->raw_processor, &result); + if (!processed) { + vips_error("librawload", _("unable to create image for \"%s\": %s"), + raw->filename, libraw_strerror(result)); + return -1; + } + + /* Set VIPS image header based on processed image. + */ + int width = processed->width; + int height = processed->height; + int bands = processed->colors; + VipsBandFormat format = processed->bits == 16 ? VIPS_FORMAT_USHORT : VIPS_FORMAT_UCHAR; + + vips_image_init_fields(out, + width, height, bands, + format, VIPS_CODING_NONE, + bands == 3 ? VIPS_INTERPRETATION_sRGB : VIPS_INTERPRETATION_B_W, + 1.0, 1.0); + + /* Set metadata. + */ + vips_image_set_string(out, "raw-make", + raw->raw_processor->idata.make); + vips_image_set_string(out, "raw-model", + raw->raw_processor->idata.model); + vips_image_set_int(out, "raw-iso", + raw->raw_processor->other.iso_speed); + vips_image_set_double(out, "raw-shutter", + raw->raw_processor->other.shutter); + vips_image_set_double(out, "raw-aperture", + raw->raw_processor->other.aperture); + vips_image_set_double(out, "raw-focal-length", + raw->raw_processor->other.focal_len); + + if (raw->raw_processor->other.timestamp) + vips_image_set_int(out, "raw-timestamp", + raw->raw_processor->other.timestamp); + + libraw_dcraw_clear_mem(processed); + + return 0; +} + +static int +vips_foreign_load_libraw_load(VipsForeignLoad *load) +{ + VipsForeignLoadLibRaw *raw = (VipsForeignLoadLibRaw *) load; + VipsImage **t = (VipsImage **) vips_object_local_array(VIPS_OBJECT(load), 3); + int result; + + /* Get the processed image again. + */ + libraw_processed_image_t *processed = libraw_dcraw_make_mem_image( + raw->raw_processor, &result); + if (!processed) { + vips_error("librawload", _("unable to create image for \"%s\": %s"), + raw->filename, libraw_strerror(result)); + return -1; + } + + /* Wrap the libraw buffer in a VipsImage. + */ + t[0] = vips_image_new_from_memory( + processed->data, + processed->data_size, + processed->width, + processed->height, + processed->colors, + processed->bits == 16 ? VIPS_FORMAT_USHORT : VIPS_FORMAT_UCHAR); + + if (!t[0]) { + libraw_dcraw_clear_mem(processed); + return -1; + } + + /* /\* Copy to output and free the processed image. */ + /* *\/ */ + /* if (vips_image_write(t[0], load->real) || */ + /* vips_sequential(load->real, &t[1], */ + /* "tile_height", vips_foreign_load_get_tile_height(load), */ + /* NULL) || */ + /* vips_image_write(t[1], load->out)) { */ + /* libraw_dcraw_clear_mem(processed); */ + /* return -1; */ + /* } */ + + libraw_dcraw_clear_mem(processed); + + return 0; +} + +static gboolean +vips_foreign_load_libraw_is_a(const char *filename) +{ + libraw_data_t *raw = libraw_init(0); + gboolean is_raw = FALSE; + + if (raw) { + int result = libraw_open_file(raw, filename); + is_raw = (result == LIBRAW_SUCCESS); + libraw_close(raw); + } + + return is_raw; +} + +static void +vips_foreign_load_libraw_class_init(VipsForeignLoadLibRawClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(class); + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignClass *foreign_class = (VipsForeignClass *) class; + VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class; + + gobject_class->dispose = vips_foreign_load_libraw_dispose; + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "librawload_base"; + object_class->description = _("load raw camera files"); + object_class->build = vips_foreign_load_libraw_build; + + /* High priority, so that we handle vipsheader etc. + */ + foreign_class->priority = 50; + + foreign_class->suffs = vips_foreign_libraw_suffs; + + load_class->is_a = vips_foreign_load_libraw_is_a; + load_class->get_flags_filename = vips_foreign_load_libraw_get_flags_filename; + load_class->get_flags = vips_foreign_load_libraw_get_flags; + load_class->header = vips_foreign_load_libraw_header; + load_class->load = vips_foreign_load_libraw_load; + + VIPS_ARG_STRING(class, "filename", 1, + _("Filename"), + _("Filename to load from"), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET(VipsForeignLoadLibRaw, filename), + NULL); + + VIPS_ARG_BOOL(class, "auto-brightness", 10, + _("Auto brightness"), + _("Enable automatic brightness adjustment"), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET(VipsForeignLoadLibRaw, auto_brightness), + TRUE); + + VIPS_ARG_BOOL(class, "use-camera-wb", 11, + _("Use camera WB"), + _("Use camera white balance"), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET(VipsForeignLoadLibRaw, use_camera_wb), + TRUE); + + VIPS_ARG_BOOL(class, "use-auto-wb", 12, + _("Use auto WB"), + _("Use automatic white balance"), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET(VipsForeignLoadLibRaw, use_auto_wb), + FALSE); + + VIPS_ARG_INT(class, "output-bps", 13, + _("Output BPS"), + _("Output bits per sample (8 or 16)"), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET(VipsForeignLoadLibRaw, output_bps), + 8, 16, 8); + + VIPS_ARG_BOOL(class, "half-size", 14, + _("Half size"), + _("Output half-size image"), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET(VipsForeignLoadLibRaw, half_size), + FALSE); + + VIPS_ARG_INT(class, "user-flip", 15, + _("User flip"), + _("User flip (0=none, 3=180, 5=90CCW, 6=90CW)"), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET(VipsForeignLoadLibRaw, user_flip), + 0, 6, 0); + + VIPS_ARG_INT(class, "interpolation-quality", 16, + _("Interpolation quality"), + _("Demosaic algorithm (0=linear, 1=VNG, 2=PPG, 3=AHD)"), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET(VipsForeignLoadLibRaw, interpolation_quality), + 0, 12, 3); + + VIPS_ARG_BOOL(class, "no-auto-scale", 17, + _("No auto scale"), + _("Disable automatic scaling"), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET(VipsForeignLoadLibRaw, no_auto_scale), + FALSE); +} + +static void +vips_foreign_load_libraw_init(VipsForeignLoadLibRaw *raw) +{ + raw->auto_brightness = TRUE; + raw->use_camera_wb = TRUE; + raw->use_auto_wb = FALSE; + raw->output_bps = 8; + raw->half_size = FALSE; + raw->user_flip = 0; + raw->interpolation_quality = 3; /* AHD */ + raw->no_auto_scale = FALSE; + raw->output_tiff = FALSE; +} + +static int +vips_foreign_load_libraw_file_build(VipsObject *object) +{ + VipsForeignLoadLibRaw *raw = (VipsForeignLoadLibRaw *) object; + VipsForeignLoadLibRawFile *file = (VipsForeignLoadLibRawFile *) object; + + if (file->filename) + if (!(raw->source = vips_source_new_from_file(file->filename))) + return -1; + + if (VIPS_OBJECT_CLASS(vips_foreign_load_libraw_file_parent_class) + ->build(object)) + return -1; + + return 0; +} + +static gboolean +vips_foreign_load_libraw_is_a_source(VipsSource *source) +{ + const unsigned char *data; + + /* if ((data = vips_source_sniff(source, 4)) && */ + /* data[0] == 'G' && */ + /* data[1] == 'I' && */ + /* data[2] == 'F' && */ + /* data[3] == '8') */ + /* return TRUE; */ + + return FALSE; +} + +static gboolean +vips_foreign_load_libraw_file_is_a(const char *filename) +{ + VipsSource *source; + gboolean result; + + if (!(source = vips_source_new_from_file(filename))) + return FALSE; + result = vips_foreign_load_libraw_is_a_source(source); + VIPS_UNREF(source); + + return result; +} + +static void +vips_foreign_load_libraw_file_class_init(VipsForeignLoadLibRawFileClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(class); + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignClass *foreign_class = (VipsForeignClass *) class; + VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class; + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "libraw_load"; + object_class->description = _("load RAW with libraw"); + object_class->build = vips_foreign_load_libraw_file_build; + + foreign_class->suffs = vips_foreign_libraw_suffs; + + load_class->is_a = vips_foreign_load_libraw_file_is_a; + + VIPS_ARG_STRING(class, "filename", 1, + _("Filename"), + _("Filename to load from"), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET(VipsForeignLoadLibRawFile, filename), + NULL); +} + +static void +vips_foreign_load_libraw_file_init(VipsForeignLoadLibRawFile *file) +{ +} + +static int +vips_foreign_load_libraw_source_build(VipsObject *object) +{ + VipsForeignLoadLibRaw *raw = (VipsForeignLoadLibRaw *) object; + VipsForeignLoadLibRawSource *source = + (VipsForeignLoadLibRawSource *) object; + + if (source->source) { + raw->source = source->source; + g_object_ref(raw->source); + } + + if (VIPS_OBJECT_CLASS(vips_foreign_load_libraw_source_parent_class) + ->build(object)) + return -1; + + return 0; +} + +static void +vips_foreign_load_libraw_source_class_init( + VipsForeignLoadLibRawSourceClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(class); + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS(class); + VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class; + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "libraw_source"; + object_class->description = _("load libraw from source"); + object_class->build = vips_foreign_load_libraw_source_build; + + operation_class->flags |= VIPS_OPERATION_NOCACHE; + + load_class->is_a_source = vips_foreign_load_libraw_is_a_source; + + VIPS_ARG_OBJECT(class, "source", 1, + _("Source"), + _("Source to load from"), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET(VipsForeignLoadLibRawSource, source), + VIPS_TYPE_SOURCE); +} + +static void +vips_foreign_load_libraw_source_init(VipsForeignLoadLibRawSource *source) +{ +} + +#endif /*HAVE_LIBRAW*/ + +/** + * vips_rawload: + * @filename: file to load + * @out: (out): output image + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * * @auto_brightness: %gboolean, enable automatic brightness + * * @use_camera_wb: %gboolean, use camera white balance + * * @use_auto_wb: %gboolean, use automatic white balance + * * @output_bps: %gint, output bits per sample (8 or 16) + * * @half_size: %gboolean, output half-size image + * * @user_flip: %gint, flip/rotate (0=none, 3=180, 5=90CCW, 6=90CW) + * * @interpolation_quality: %gint, demosaic algorithm + * * @no_auto_scale: %gboolean, disable automatic scaling + * + * Read a RAW camera file using libraw. + * + * This loader supports most RAW formats including: + * ARW, CR2, CR3, CRW, DNG, NEF, NRW, ORF, PEF, RAF, RAW, RW2, SRW, X3F + * + * The loader applies demosaicing and basic processing to produce an RGB or + * grayscale image suitable for further processing. + * + * Use @output_bps to control output bit depth (8 or 16 bits per sample). + * Use @half_size for faster loading at reduced resolution. + * Use @interpolation_quality to control the demosaicing algorithm: + * * 0 = linear interpolation (fastest, lowest quality) + * * 1 = VNG (Variable Number of Gradients) + * * 2 = PPG (Patterned Pixel Grouping) + * * 3 = AHD (Adaptive Homogeneity-Directed) - default + * * 4 = DCB (DCB interpolation) + * * 11 = DHT (improved AHD) + * * 12 = AAHD (modified AHD) + * + * Example: + * |[ + * VipsImage *image; + * if (vips_rawload("photo.cr2", &image, + * "output_bps", 16, + * "use_camera_wb", TRUE, + * NULL)) + * error_handling(); + * ]| + * + * Returns: 0 on success, -1 on error. + */ +int +vips_librawload(const char *filename, VipsImage **out, ...) +{ + va_list ap; + int result; + + va_start(ap, out); + result = vips_call_split("librawload", ap, filename, out); + va_end(ap); + + return result; +} diff --git a/libvips/foreign/meson.build b/libvips/foreign/meson.build index 71d55c882d..744147b758 100644 --- a/libvips/foreign/meson.build +++ b/libvips/foreign/meson.build @@ -123,6 +123,14 @@ if not openslide_module foreign_sources += openslide_module_sources endif +libraw_sources = files( + 'librawload.c', +) + +if get_option('libraw') + foreign_sources += libraw_sources +endif + libvips_sources += foreign_sources foreign_lib = static_library('foreign', diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index 4a7ebbe2f2..4f69104032 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -872,6 +872,10 @@ VIPS_API int vips_gifsave_target(VipsImage *in, VipsTarget *target, ...) G_GNUC_NULL_TERMINATED; +VIPS_API +int vips_librawload(const char *filename, VipsImage **out, ...) + G_GNUC_NULL_TERMINATED; + VIPS_API int vips_heifload(const char *filename, VipsImage **out, ...) G_GNUC_NULL_TERMINATED; diff --git a/meson.build b/meson.build index 9e689682d0..9ef2954123 100644 --- a/meson.build +++ b/meson.build @@ -429,6 +429,12 @@ if openexr_dep.found() cfg_var.set('HAVE_OPENEXR', true) endif +libraw_dep = dependency('libraw', required: get_option('libraw')) +if libraw_dep.found() + external_deps += libraw_dep + cfg_var.set('HAVE_LIBRAW', true) +endif + # 2.4 is the first one to have working threading and tiling libopenjp2_dep = dependency('libopenjp2', version: '>=2.4', required: get_option('openjpeg')) if libopenjp2_dep.found() @@ -659,6 +665,7 @@ build_summary = { 'enable Analyze7 load': [get_option('analyze')], 'enable PPM load/save': [get_option('ppm')], 'enable GIF load': [get_option('nsgif')], + 'enable LBRAW load': [get_option('libraw')], }, } build_features = { @@ -689,6 +696,7 @@ build_features = { 'NIfTI load/save': ['libnifti', libnifti_found ? libnifti_dep : disabler()], 'FITS load/save': ['cfitsio', cfitsio_dep], 'GIF save': ['cgif', cgif_dep], + 'RAW load': ['libraw', libraw_dep], 'Magick @0@'.format('/'.join(get_option('magick-features'))): [get_option('magick-package'), magick_found ? magick_dep : disabler(), magick_module], }, } diff --git a/meson_options.txt b/meson_options.txt index 175a46c5e6..0793ed61e3 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -236,6 +236,11 @@ option('nsgif', value: true, description: 'Build with nsgif') +option('libraw', + type: 'boolean', + value: true, + description: 'Build with libraw') + option('ppm', type: 'boolean', value: true,