diff --git a/options/processing_options.go b/options/processing_options.go index e7f91a597c..523b81808a 100644 --- a/options/processing_options.go +++ b/options/processing_options.go @@ -87,6 +87,7 @@ type DitherOptions struct { OptionsSet04 bool OptionsSet05 bool OptionsSet06 bool + OptionsSet07 bool OptionsSetCam16 bool OptionsSetHpminde bool OptionsSetScam bool @@ -740,6 +741,11 @@ func applyPixelateOption(po *ProcessingOptions, args []string) error { return nil } +// The 9 colors the 10" panel measures, each as name:L:a:b. The six Spectra +// colors are r, g, bl, y, w and bk; the rest are or (orange), br (brownish 2) +// and y2 (softer yellow 2). +const opts07PaletteTokens = 9 * 4 + func applyDitherOption(po *ProcessingOptions, args []string) error { switch args[0] { case "fs": @@ -816,6 +822,22 @@ func applyDitherOption(po *ProcessingOptions, args []string) error { po.Dither.MeasuredPalette = strings.Join(args[idx+2:idx+26], ":") po.Dither.OptionsSet06 = true idx += 25 + case "opts07": + // as opts06, but carrying the 9 color palette measured on the 10" panel. ex: + // opts07:0.65:bk:12.79:5.21:-12.53:y:63.41:-12.92:68.53:bl:28.63:5.38:-39.49:r:26.32:38.07:26.51:g:30.21:-22:5.54:w:66.16:-3.5:-1.98:or:36.06:36.04:36.13:br:46.37:21.06:46.76:y2:68.07:-13.74:39.3 + if len(args) < idx+2+opts07PaletteTokens { + return fmt.Errorf("opts07 requires %d params got %d: %s", idx+2+opts07PaletteTokens, len(args), args[0]) + } + swapYellowProbStr := args[idx+1] + floatSwapYellowProb, err := strconv.ParseFloat(swapYellowProbStr, 64) + if err != nil || math.IsNaN(floatSwapYellowProb) || math.IsInf(floatSwapYellowProb, 0) { + return fmt.Errorf("Invalid swap yellow probability: %s", swapYellowProbStr) + } + + po.Dither.SwapYellowProb = floatSwapYellowProb + po.Dither.MeasuredPalette = strings.Join(args[idx+2:idx+2+opts07PaletteTokens], ":") + po.Dither.OptionsSet07 = true + idx += 1 + opts07PaletteTokens case "optscam16": po.Dither.OptionsSetCam16 = true case "optshpminde": diff --git a/options/processing_options_test.go b/options/processing_options_test.go index 4bb4673896..3c7b2a3d04 100644 --- a/options/processing_options_test.go +++ b/options/processing_options_test.go @@ -597,6 +597,58 @@ func (s *ProcessingOptionsTestSuite) TestParseBase64URLOnlyPresets() { require.Equal(s.T(), originURL, imageURL) } +const nineColorPalette = "bk:12.79:5.21:-12.53:y:63.41:-12.92:68.53:bl:28.63:5.38:-39.49:" + + "r:26.32:38.07:26.51:g:30.21:-22:5.54:w:66.16:-3.5:-1.98:" + + "or:36.06:36.04:36.13:br:46.37:21.06:46.76:y2:68.07:-13.74:39.3" + +const sixColorPalette = "bk:8.88:9.08:-12.38:y:60.02:-10.87:64.97:w:61.55:-3.11:-0.93:" + + "bl:26.91:3.06:-34.11:r:23.76:38.93:28.27:g:31:-18.73:9.4" + +func (s *ProcessingOptionsTestSuite) TestParseDitherOpts07() { + originURL := "http://images.dev/lorem/ipsum.jpg" + path := fmt.Sprintf("/dither:fs:opts07:0.65:%s/plain/%s", nineColorPalette, originURL) + po, imageURL, err := ParsePath(path, make(http.Header)) + + require.Nil(s.T(), err) + require.Equal(s.T(), originURL, imageURL) + require.True(s.T(), po.Dither.OptionsSet07) + require.False(s.T(), po.Dither.OptionsSet06) + require.Equal(s.T(), 0.65, po.Dither.SwapYellowProb) + require.Equal(s.T(), nineColorPalette, po.Dither.MeasuredPalette) +} + +func (s *ProcessingOptionsTestSuite) TestParseDitherOpts07WithTrailingOption() { + originURL := "http://images.dev/lorem/ipsum.jpg" + // applyDitherOption's loop never visits the last argument, so co is there to + // give sp a position the loop actually reaches + path := fmt.Sprintf("/dither:fs:opts07:0.65:%s:sp:co/plain/%s", nineColorPalette, originURL) + po, _, err := ParsePath(path, make(http.Header)) + + require.Nil(s.T(), err) + require.True(s.T(), po.Dither.OptionsSet07) + require.Equal(s.T(), nineColorPalette, po.Dither.MeasuredPalette) + require.True(s.T(), po.Dither.SoftProof, "the palette must not swallow the option after it") +} + +func (s *ProcessingOptionsTestSuite) TestParseDitherOpts07RejectsSixColorPalette() { + originURL := "http://images.dev/lorem/ipsum.jpg" + path := fmt.Sprintf("/dither:fs:opts07:0.65:%s/plain/%s", sixColorPalette, originURL) + _, _, err := ParsePath(path, make(http.Header)) + + require.Error(s.T(), err, "opts07 carries 9 colors; a 6 color palette belongs in opts06") +} + +func (s *ProcessingOptionsTestSuite) TestParseDitherOpts06StillTakesSixColors() { + originURL := "http://images.dev/lorem/ipsum.jpg" + path := fmt.Sprintf("/dither:fs:opts06:0.65:%s/plain/%s", sixColorPalette, originURL) + po, _, err := ParsePath(path, make(http.Header)) + + require.Nil(s.T(), err) + require.True(s.T(), po.Dither.OptionsSet06) + require.False(s.T(), po.Dither.OptionsSet07) + require.Equal(s.T(), sixColorPalette, po.Dither.MeasuredPalette) +} + func TestProcessingOptions(t *testing.T) { suite.Run(t, new(ProcessingOptionsTestSuite)) } diff --git a/plugins/pushd-dither b/plugins/pushd-dither index 6d49bc9dd7..4545f0245c 160000 --- a/plugins/pushd-dither +++ b/plugins/pushd-dither @@ -1 +1 @@ -Subproject commit 6d49bc9dd7ed76f8f0da2e3dc3f3e193752cbbdc +Subproject commit 4545f0245cb2477ab3b87ceada2d2a9e1bdfdaa0 diff --git a/processing/dither.go b/processing/dither.go index e34b803158..3b938380c2 100644 --- a/processing/dither.go +++ b/processing/dither.go @@ -283,7 +283,9 @@ func shellOutDither(inFile string, po *options.ProcessingOptions) error { cmdArgs = append(cmdArgs, "--pal-auto-expand", "2.0") cmdArgs = append(cmdArgs, "--inflate-color-space", "jzazbz") cmdArgs = append(cmdArgs, "--pal-str", po.Dither.MeasuredPalette) - case po.Dither.OptionsSet06: + // opts07 is opts06 over a nine ink palette: the dither tool reads the extra + // inks straight off --pal-str, so the option set is the same. + case po.Dither.OptionsSet06, po.Dither.OptionsSet07: cmdArgs = append(cmdArgs, "--jzazbz") cmdArgs = append(cmdArgs, "--hull-project") cmdArgs = append(cmdArgs, "--chroma-lightness")