# HG changeset patch
# User Hueburtsonly <Hueburtsonly@users.noreply.github.com>
# Date 1676690599 -39600
#      Sat Feb 18 14:23:19 2023 +1100
# Node ID 7f7a91ee63dcee6a1ae5a2b6a76996d042f1c15d
# Parent  45f8b601c9927214ccc0cc423b52fb974d12b48b
Add Resolution and ResolutionUnit parameters to imwrite.

Compatible with MATLAB's parameters for writing TIFF files, and uses the same params/behavior for PNG, JPEG, and BMP (only Magick++ formats that support density AFAICT.)

This means it is incompatible with MATLAB's inconsistent XResolution and YResolution parameters for writing PNG files.

diff -r 45f8b601c992 -r 7f7a91ee63dc libinterp/corefcn/__magick_read__.cc
--- a/libinterp/corefcn/__magick_read__.cc	Thu Feb 16 15:13:35 2023 -0800
+++ b/libinterp/corefcn/__magick_read__.cc	Sat Feb 18 14:23:19 2023 +1100
@@ -1495,6 +1495,24 @@
 
 #endif
 
+static Magick::ResolutionType
+resolution_unit_to_magick_value (const octave_value& octave)
+{
+  std::string unit = octave.string_value ();
+  if (unit.compare("inch") == 0) {
+    return Magick::PixelsPerInchResolution;
+  }
+  if (unit.compare("centimeter") == 0) {
+    return Magick::PixelsPerCentimeterResolution;
+  }
+  if (unit.compare("unknown") == 0) {
+    return Magick::UndefinedResolution;
+  }
+  // The "meter" shouldn't be seen here, as __imwrite__.m converts it to
+  // centimeters.
+  error ("__magick_write__: unexpected unit '%s'.", unit.c_str ());
+}
+
 DEFUN (__magick_write__, args, ,
        doc: /* -*- texinfo -*-
 @deftypefn {} {} __magick_write__ (@var{fname}, @var{fmt}, @var{img}, @var{map}, @var{options})
@@ -1593,6 +1611,11 @@
       imvec[i].quality (quality);
       imvec[i].animationDelay (delaytime(i));
       imvec[i].gifDisposeMethod (disposal_methods[disposalmethod(i)]);
+      if (options.getfield ("resolution").complex_array_value ().numel() == 2) {
+        imvec[i].resolutionUnits (resolution_unit_to_magick_value(options.getfield ("resolutionunit")));
+        imvec[i].xResolution (options.getfield ("resolution").complex_array_value ().elem (0).real ());
+        imvec[i].yResolution (options.getfield ("resolution").complex_array_value ().elem (1).real ());
+      }
     }
 
   // If writemode is set to append, read the image and append to it.  Even
diff -r 45f8b601c992 -r 7f7a91ee63dc scripts/image/imwrite.m
--- a/scripts/image/imwrite.m	Thu Feb 16 15:13:35 2023 -0800
+++ b/scripts/image/imwrite.m	Sat Feb 18 14:23:19 2023 +1100
@@ -85,6 +85,19 @@
 ## between 0 and 100, with larger values indicating higher visual quality and
 ## lower compression.  Defaults to 75.
 ##
+## @item Resolution
+## Some file formats, such as JPEG, PNG, TIFF and BMP are able to store
+## a resolution (e.g. 72 dpi), which can be specified by this option.
+## If the given value is scalar, it will be used for both X and Y resolutions.
+## Otherwise, it must be a two-element vector containing X and Y resolutions.
+## By default, this resolution is assumed to be given in pixels per inch, but
+## this can be overridden by setting the ResolutionUnit parameter.
+##
+## @item ResolutionUnit
+## The units that Resolution parameter are given in. The value should be the
+## string @qcode{"inch"} (default), @qcode{"centimeter"}, @qcode{"meter"}, or
+## @qcode{"unknown"}.
+##
 ## @item WriteMode
 ## Some file formats, such as TIFF and GIF, are able to store multiple images
 ## in a single file.  This option specifies if @var{img} should be appended
diff -r 45f8b601c992 -r 7f7a91ee63dc scripts/image/private/__imwrite__.m
--- a/scripts/image/private/__imwrite__.m	Thu Feb 16 15:13:35 2023 -0800
+++ b/scripts/image/private/__imwrite__.m	Sat Feb 18 14:23:19 2023 +1100
@@ -53,7 +53,11 @@
                     "delaytime", ones (1, size (img, 4)) *500, # 0.5 seconds
                     "loopcount", 0, ## this is actually Inf
                     "alpha",     cast ([], class (img)),
-                    "compression", "none");
+                    "compression", "none",
+                    "resolution", [],
+                    "resolutionunit", "inch");
+
+  ext_supports_resolution = any (strcmpi (ext, {"bmp", "jpg",  "png", "tif"}));
 
   for idx = 1:2:numel (param_list)
 
@@ -165,6 +169,40 @@
         endif
         options.quality = round (options.quality);
 
+      case "resolution",
+        options.resolution = param_list{idx+1};
+
+        if (! ext_supports_resolution)
+          error ("imwrite: paramater %s may only be set for BMP, JPG, PNG, and TIF formats. ",
+                 param_list{idx} )
+        endif
+
+        if (! isnumeric (options.resolution) || numel (options.resolution) > 2
+            || any(options.resolution <= 0))
+          error ("imwrite: value for %s option must be a positive scalar or two-element vector",
+                 param_list{idx});
+        endif
+
+        if isscalar(options.resolution)
+          options.resolution = [options.resolution, options.resolution];
+        endif
+
+      case "resolutionunit",
+        options.resolutionunit = param_list{idx+1};
+
+        if (! ext_supports_resolution)
+          error ("imwrite: paramater %s may only be set for BMP, JPG, PNG, and TIF formats. ",
+                 param_list{idx} )
+        endif
+
+        if (! ischar (options.resolutionunit)
+            || ! any (strcmpi (options.resolutionunit, {"inch", "centimeter", "meter", "unknown"})))
+          error ('imwrite: value for %s option must be "inch", "centimeter", "meter" or "unknown"',
+                 param_list{idx});
+        endif
+        options.resolutionunit = tolower (options.resolutionunit);
+
+
       case "writemode",
         options.writemode = param_list{idx+1};
         if (! ischar (options.writemode)
@@ -218,6 +256,11 @@
     error ("imwrite: IMG 3rd dimension must be 1, 3, or 4");
   endif
 
+  if (strcmpi (options.resolutionunit, "meter"))
+    options.resolutionunit = "centimeter";
+    options.resolution = options.resolution / 100;
+  endif
+
   __magick_write__ (filename, ext, img, map, options);
 
 endfunction
