diff --git a/doc/rst/source/gmt.conf.rst b/doc/rst/source/gmt.conf.rst index d96e57e6560..dea442ac8e9 100644 --- a/doc/rst/source/gmt.conf.rst +++ b/doc/rst/source/gmt.conf.rst @@ -1091,8 +1091,13 @@ PostScript Parameters with standard fonts. **PS_COLOR_MODEL** - Determines whether PostScript output should use **RGB**, **HSV**, **CMYK**, - or **GRAY** when specifying color [default is **rgb**]. Note if **HSV** + Determines whether PostScript output should use **RGB**, **RGBnoQUANT**, + **HSV**, **CMYK**, or **GRAY** when specifying color [default is **rgb**]. + **RGBnoQUANT** uses + normal RGB colors but forces direct RGB raster output instead of indexed + images. This may increase file size and is intended for compatibility with + third-party PostScript/PDF editors that have problems with indexed images. + Note if **HSV** is selected it does not apply to images which in that case uses **RGB**. When selecting **GRAY**, all colors will be converted to gray scale using YIQ (television) conversion. diff --git a/src/gmt_defaults.h b/src/gmt_defaults.h index aaf12936f5a..019b9f247a6 100644 --- a/src/gmt_defaults.h +++ b/src/gmt_defaults.h @@ -192,6 +192,7 @@ struct GMT_DEFAULTS { double ps_transparency; /* Later transparency [0] */ double ps_penwidth; /* Current pen width */ unsigned int ps_color_mode; /* Postscript encoding of color [PSL_RGB | PSL_CMYK | PSL_HSV | PSL_GRAY] */ + bool ps_color_no_quantization; /* Skip RGB image quantization and indexed-color output [false] */ unsigned int ps_copies; /* How man copies of each plot [>=1] [GMT4 COMPATIBILITY ONLY] */ int ps_media; /* Default paper media [25(Letter)]; negative if custom size */ unsigned int ps_orientation; /* Orientation of page [PSL_LANDSCAPE (0)] or PSL_PORTRAIT (1) */ diff --git a/src/gmt_init.c b/src/gmt_init.c index 7d7fb9fb4bf..7b2bdc2d297 100644 --- a/src/gmt_init.c +++ b/src/gmt_init.c @@ -6972,6 +6972,7 @@ EXTERN_MSC void gmtinit_conf_classic (struct GMT_CTRL *GMT) { gmtinit_load_encoding (GMT); /* PS_COLOR_MODEL */ GMT->current.setting.ps_color_mode = PSL_RGB; + GMT->current.setting.ps_color_no_quantization = false; /* PS_IMAGE_COMPRESS */ if (GMT->PSL) { /* Only when using PSL in this session */ #ifdef HAVE_ZLIB @@ -11750,14 +11751,26 @@ unsigned int gmtlib_setparameter (struct GMT_CTRL *GMT, const char *keyword, cha gmt_M_compat_translate ("PS_COLOR_MODEL"); break; case GMTCASE_PS_COLOR_MODEL: - if (!strcmp (lower_value, "rgb")) + if (!strcmp (lower_value, "rgb")) { GMT->current.setting.ps_color_mode = PSL_RGB; - else if (!strcmp (lower_value, "cmyk")) + GMT->current.setting.ps_color_no_quantization = false; + } + else if (!strcmp (lower_value, "rgbnoquant")) { + GMT->current.setting.ps_color_mode = PSL_RGB; + GMT->current.setting.ps_color_no_quantization = true; + } + else if (!strcmp (lower_value, "cmyk")) { GMT->current.setting.ps_color_mode = PSL_CMYK; - else if (!strcmp (lower_value, "hsv")) + GMT->current.setting.ps_color_no_quantization = false; + } + else if (!strcmp (lower_value, "hsv")) { GMT->current.setting.ps_color_mode = PSL_HSV; - else if (!strcmp (lower_value, "gray") || !strcmp (lower_value, "grey")) + GMT->current.setting.ps_color_no_quantization = false; + } + else if (!strcmp (lower_value, "gray") || !strcmp (lower_value, "grey")) { GMT->current.setting.ps_color_mode = PSL_GRAY; + GMT->current.setting.ps_color_no_quantization = false; + } else error = true; break; @@ -13261,7 +13274,9 @@ char *gmtlib_getparameter (struct GMT_CTRL *GMT, const char *keyword) { else { error = gmtinit_badvalreport (GMT, keyword); break; } /* Not recognized so give error message */ /* Intentionally fall through */ case GMTCASE_PS_COLOR_MODEL: - if (GMT->current.setting.ps_color_mode == PSL_RGB) + if (GMT->current.setting.ps_color_no_quantization) + strcpy (value, "rgbnoquant"); + else if (GMT->current.setting.ps_color_mode == PSL_RGB) strcpy (value, "rgb"); else if (GMT->current.setting.ps_color_mode == PSL_CMYK) strcpy (value, "cmyk"); diff --git a/src/gmt_keywords.txt b/src/gmt_keywords.txt index e912880dede..3209d3f3da1 100644 --- a/src/gmt_keywords.txt +++ b/src/gmt_keywords.txt @@ -152,7 +152,7 @@ PROJ_SCALE_FACTOR # Scale factor for UTM maps # PS Group: PostScript settings #------------------------------------------------------- PS_CHAR_ENCODING # Character encoding vector to use in PS plots -PS_COLOR_MODEL # Color model in use (RGB, HSV, CMYK, GRAY) +PS_COLOR_MODEL # Color model in use (RGB, RGBnoQUANT, HSV, CMYK, GRAY) PS_DPI # The dots-pr-inch setting [obsolete - not in gmt.conf] PS_IMAGE_COMPRESS # Compression algorithm for images PS_LINE_CAP # Line cap setting @@ -177,4 +177,4 @@ TIME_REPORT # Controls time or elapsed run-time stamp in messages TIME_UNIT # Unit of time measurement TIME_SYSTEM # Shortcut to setting TIME_EPOCH/UNIT [does not go in gmt.conf] TIME_WEEK_START # Day a new week starts on -TIME_Y2K_OFFSET_YEAR # For 2-digit years to separate 1900 and 2000 centuries \ No newline at end of file +TIME_Y2K_OFFSET_YEAR # For 2-digit years to separate 1900 and 2000 centuries diff --git a/src/gmt_plot.c b/src/gmt_plot.c index 9251634fd6f..f0de4bbb305 100644 --- a/src/gmt_plot.c +++ b/src/gmt_plot.c @@ -9319,6 +9319,7 @@ struct PSL_CTRL *gmt_plotinit (struct GMT_CTRL *GMT, struct GMT_OPTION *options) else snprintf (GMT->current.ps.title, GMT_LEN256, "GMT v%s Document from %s", GMT_VERSION, GMT->init.module_name); + PSL->internal.no_quantization = GMT->current.setting.ps_color_no_quantization; PSL_beginplot (PSL, fp, GMT->current.setting.ps_orientation|write_to_mem|switch_charset, O_active, GMT->current.setting.ps_color_mode, GMT->current.ps.origin, GMT->current.setting.map_origin, media_size, GMT->current.ps.title, fno); diff --git a/src/postscriptlight.c b/src/postscriptlight.c index 9b45b63033e..520fea61216 100644 --- a/src/postscriptlight.c +++ b/src/postscriptlight.c @@ -3888,7 +3888,9 @@ int PSL_plotcolorimage (struct PSL_CTRL *PSL, double x, double y, double xsize, /* Colormask or interpolate */ it = nx < 0 ? 1 : (nbits < 0 ? 2 : 0); - if (PSL->internal.color_mode != PSL_GRAY && (image = psl_makecolormap (PSL, buffer, nx, ny, nbits))) { + if (!PSL->internal.no_quantization && + PSL->internal.color_mode != PSL_GRAY && + (image = psl_makecolormap (PSL, buffer, nx, ny, nbits))) { /* Creation of colormap was successful */ nbits = psl_bitreduce (PSL, image->buffer, nx, ny, image->colormap->ncolors); diff --git a/src/postscriptlight.h b/src/postscriptlight.h index 4aae05ddf6d..d4b1b0655a6 100644 --- a/src/postscriptlight.h +++ b/src/postscriptlight.h @@ -45,6 +45,7 @@ extern "C" { /* Declaration modifiers for DLL support (MSC et al) */ #include "declspec.h" +#include #include #define PSL_MaxOpStack_Size 300000 /* As of GhostSCript 9.50; see declaration in gs_init.ps */ @@ -401,6 +402,7 @@ struct PSL_CTRL { int deflate_level; /* Compression level for DEFLATE (1-9, default 0) */ int call_level; /* Level in a series of module calls from GMT */ int color_mode; /* 0 = rgb, 1 = cmyk, 2 = hsv (only 1-2 for images) */ + bool no_quantization; /* Skip RGB image quantization and indexed-color output [false] */ int line_cap; /* 0, 1, or 2 for butt, round, or square [butt] */ int line_join; /* 0, 1, or 2 for miter, arc, or bevel [miter] */ int miter_limit; /* Acute angle threshold 0-180; 0 means PS default [0] */ diff --git a/test/postscriptlight/rgbnoquant.sh b/test/postscriptlight/rgbnoquant.sh new file mode 100644 index 00000000000..efefc02eb32 --- /dev/null +++ b/test/postscriptlight/rgbnoquant.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# +# Purpose: Verify RGBnoQUANT bypasses PSL indexed-color image optimization +# GMT modules: gmt +# +printf 'P6\n4 4\n255\n' > colors.ppm +printf '\377\000\000\000\377\000\000\000\377\377\377\000' >> colors.ppm +printf '\000\377\000\000\000\377\377\377\000\377\000\000' >> colors.ppm +printf '\000\000\377\377\377\000\377\000\000\000\377\000' >> colors.ppm +printf '\377\377\000\377\000\000\000\377\000\000\000\377' >> colors.ppm + +# Default RGB keeps the existing indexed-color and bit-reduction path. +gmt set PS_COLOR_MODEL RGB +[ "$(gmt get PS_COLOR_MODEL)" = "rgb" ] || echo "RGB setting did not round-trip" >> fail +gmt psimage colors.ppm -R0/4/0/4 -JX4c > indexed.ps +grep -Fq "/Indexed /DeviceRGB" indexed.ps || echo "RGB image was not indexed" >> fail +grep -Fq "/Decode [0 3]" indexed.ps || echo "RGB image was not bit-reduced" >> fail + +# Case-insensitive RGBnoQUANT must use the existing direct DeviceRGB path. +gmt set PS_COLOR_MODEL RgBnOqUaNt +[ "$(gmt get PS_COLOR_MODEL)" = "rgbnoquant" ] || echo "RGBnoQUANT setting did not round-trip" >> fail +gmt psimage colors.ppm -R0/4/0/4 -JX4c > direct.ps +grep -Fq "/Indexed /DeviceRGB" direct.ps && echo "RGBnoQUANT image was indexed" >> fail +grep -Fq "/DeviceRGB setcolorspace" direct.ps || echo "RGBnoQUANT image was not direct DeviceRGB" >> fail +grep -Fq "/Decode [0 1 0 1 0 1]" direct.ps || echo "RGBnoQUANT image was not 24 bit" >> fail + +# Switching back to RGB must reset the no-quantization flag. +gmt set PS_COLOR_MODEL rgb +gmt psimage colors.ppm -R0/4/0/4 -JX4c > restored.ps +grep -Fq "/Indexed /DeviceRGB" restored.ps || echo "RGB did not restore indexed output" >> fail