/// <summary> /// Create a plug-in dialog. /// </summary> /// <remarks> /// Save the last used algorithm configuration to a file. /// </remarks> /// <returns>Gimp plug-in dialog</returns> protected override GimpDialog CreateDialog() { gimp_ui_init("HalftoneLab", true); GimpDialog dialog = DialogNew("Halftone Laboratory", "HalftoneLab", IntPtr.Zero, 0, Gimp.StandardHelpFunc, "HalftoneLab"); Table table = new Table(2, 1, false); initConfigManager(); ConfigPanel<HalftoneAlgorithm> configPanel = new ConfigPanel<HalftoneAlgorithm>(configManager); HalftoneAlgorithmPanel algorithmPanel = new HalftoneAlgorithmPanel(selectedAlgorithm); configPanel.ModuleChanged += delegate { algorithmPanel.Module = configPanel.CurrentModule; }; algorithmPanel.ModuleChanged += delegate { selectedAlgorithm = algorithmPanel.Module; configPanel.CurrentModule = selectedAlgorithm; }; table.Attach(configPanel, 0, 1, 0, 1, AttachOptions.Fill, AttachOptions.Fill | AttachOptions.Expand, 0, 0); table.Attach(algorithmPanel, 0, 1, 1, 2, AttachOptions.Fill, AttachOptions.Fill, 0, 0); dialog.VBox.PackStart(table); dialog.Response += delegate { Console.Out.WriteLine("Algorithm name: {0}", selectedAlgorithm.Name); Console.Out.WriteLine("Description: {0}", selectedAlgorithm.Description); // save the last used algorithm to the config manager selectedAlgorithm.Name = "_LAST"; configManager.saveModule(selectedAlgorithm); }; return dialog; }
/// <summary> /// Fill the config manager with a default configuration. /// </summary> /// <param name="config"></param> public static void makeSampleConfig(ConfigManager config) { if (config == null) { return; } int counter = 1; // number of the preset to preserve the order // ---------------------------------------------- // Thresholding HalftoneAlgorithm halftoneAlgorithm = new HalftoneAlgorithm(); ThresholdHalftoneMethod thresholdHalftoneMethod = new ThresholdHalftoneMethod(); halftoneAlgorithm.Method = thresholdHalftoneMethod; halftoneAlgorithm.Name = String.Format("[{0:d2}] Thresholding", counter); halftoneAlgorithm.Description = "Simple threshold at 50% grey"; config.saveModule(halftoneAlgorithm, false); counter++; // ---------------------------------------------- // Random dither with white noise DynamicMatrixThresholdFilter dynamicThresholdFilter = new DynamicMatrixThresholdFilter(); dynamicThresholdFilter.MatrixTable.addDefinitionRecord( new DynamicMatrixThresholdFilter.ThresholdRecord( 0, ThresholdMatrix.Samples.simpleThreshold, 1.0)); thresholdHalftoneMethod.ThresholdFilter = dynamicThresholdFilter; halftoneAlgorithm.Name = String.Format("[{0:d2}] Random dither", counter); halftoneAlgorithm.Description = "Threshold perturbed with white noise"; config.saveModule(halftoneAlgorithm, false); counter++; // ---------------------------------------------- // Bayer matrix thresholdHalftoneMethod.ThresholdFilter = new MatrixThresholdFilter( ThresholdMatrix.Samples.createBayerDispersedDotMatrix(3)); halftoneAlgorithm.Name = String.Format("[{0:d2}] Ordered dither with Bayer matrix", counter); halftoneAlgorithm.Description = "Recursive tesselation matrix 8x8"; config.saveModule(halftoneAlgorithm, false); counter++; // -------------------- SCREENING METHODS -------------------- // Clustered-dot hand coded threshold matrix thresholdHalftoneMethod.ThresholdFilter = new MatrixThresholdFilter( ThresholdMatrix.Samples.sampleScreenMatrix); halftoneAlgorithm.Name = String.Format("[{0:d2}] Screen - Clustered-dot threshold matrix", counter); halftoneAlgorithm.Description = "Hand-coded threshold matrix 8x8 with 45 angle"; config.saveModule(halftoneAlgorithm, false); counter++; // ---------------------------------------------- // Spot function - Eulid dot thresholdHalftoneMethod.ThresholdFilter = new SpotFunctionThresholdFilter( SpotFunction.Samples.euclidDot); halftoneAlgorithm.Name = String.Format("[{0:d2}] Screen - Spot function", counter); halftoneAlgorithm.Description = "Euclid-dot spot function - computed on-the-fly\nEuclid dot: angle = 45 deg, distance: 10 px"; config.saveModule(halftoneAlgorithm, false); counter++; // ---------------------------------------------- // Patterning thresholdHalftoneMethod.ThresholdFilter = new MatrixThresholdFilter( new ThresholdMatrix(new int[,] { { 1, 2 }, { 3, 4 } })); halftoneAlgorithm.PreResize = new HalftoneAlgorithm.Resize() { Factor = 2.0, Interpolation = HalftoneAlgorithm.Resize.InterpolationType.NearestNeighbour }; halftoneAlgorithm.Name = String.Format("[{0:d2}] Screen - Patterning", counter); halftoneAlgorithm.Description = "Resize 2x via Nearest-neighbour interpolation\n2x2 matrix threshold"; config.saveModule(halftoneAlgorithm, false); counter++; halftoneAlgorithm.PreResize = null; // -------------------- ERROR DIFFUSION METHODS -------------------- // Floyd-Steinberg thresholdHalftoneMethod.ThresholdFilter = new MatrixThresholdFilter(); thresholdHalftoneMethod.ErrorFilter = new MatrixErrorFilter( ErrorMatrix.Samples.floydSteinberg); halftoneAlgorithm.Name = String.Format("[{0:d2}] Error diffusion - Floyd-Steinberg", counter); halftoneAlgorithm.Description = "Floyd-Steinberg matrix, scanline order"; config.saveModule(halftoneAlgorithm, false); counter++; // ---------------------------------------------- // Jarvis-Judice-Ninke thresholdHalftoneMethod.ErrorFilter = new MatrixErrorFilter( ErrorMatrix.Samples.jarvisJudiceNinke); halftoneAlgorithm.Name = String.Format("[{0:d2}] Error diffusion - Jarvis-Judice-Ninke", counter); halftoneAlgorithm.Description = "Jarvis-Judice-Ninke matrix, scanline order"; config.saveModule(halftoneAlgorithm, false); counter++; // ---------------------------------------------- // Stucki thresholdHalftoneMethod.ErrorFilter = new MatrixErrorFilter( ErrorMatrix.Samples.stucki); halftoneAlgorithm.Name = String.Format("[{0:d2}] Error diffusion - Stucki", counter); halftoneAlgorithm.Description = "Stucki matrix, scanline order"; config.saveModule(halftoneAlgorithm, false); counter++; // ---------------------------------------------- // Floyd-Steinberg serpentine thresholdHalftoneMethod.ErrorFilter = new MatrixErrorFilter( ErrorMatrix.Samples.floydSteinberg); thresholdHalftoneMethod.ScanningOrder = new SerpentineScanningOrder(); halftoneAlgorithm.Name = String.Format("[{0:d2}] Error diffusion - Floyd-Steinberg serpentine", counter); halftoneAlgorithm.Description = "Floyd-Steinberg matrix, serpentine order"; config.saveModule(halftoneAlgorithm, false); counter++; thresholdHalftoneMethod.ScanningOrder = new ScanlineScanningOrder(); // Error diffusion + sharpening thresholdHalftoneMethod.ErrorFilter = new MatrixErrorFilter( ErrorMatrix.Samples.floydSteinberg); halftoneAlgorithm.PreSharpen = new HalftoneAlgorithm.Sharpen() { Amount = 0.4 }; halftoneAlgorithm.Name = String.Format("[{0:d2}] Error diffusion + sharpening", counter); halftoneAlgorithm.Description = "Pre-sharpened by 40%\nFloyd-Steinberg matrix, scanline order"; config.saveModule(halftoneAlgorithm, false); counter++; halftoneAlgorithm.PreSharpen = null; // -------------------- SFC METHODS -------------------- // SFC clustering - positioning & adaptive SFCClusteringMethod sfcClusteringMethod = new SFCClusteringMethod(); halftoneAlgorithm.Method = sfcClusteringMethod; sfcClusteringMethod.UseClusterPositioning = true; sfcClusteringMethod.UseAdaptiveClustering = true; halftoneAlgorithm.Name = String.Format("[{0:d2}] SFC clustering - positioned & adaptive", counter); halftoneAlgorithm.Description = "SFC clustering, cell size: " + sfcClusteringMethod.MaxCellSize + ", positioning enabled, adaptive"; config.saveModule(halftoneAlgorithm, false); counter++; // ---------------------------------------------- // SFC clustering - bare sfcClusteringMethod.UseClusterPositioning = false; sfcClusteringMethod.UseAdaptiveClustering = false; halftoneAlgorithm.Name = String.Format("[{0:d2}] SFC clustering - bare", counter); halftoneAlgorithm.Description = "SFC clustering, cell size: " + sfcClusteringMethod.MaxCellSize + ", no positioning, non-adaptive"; config.saveModule(halftoneAlgorithm, false); counter++; // ---------------------------------------------- // SFC clustering - positioning sfcClusteringMethod.UseClusterPositioning = true; halftoneAlgorithm.Name = String.Format("[{0:d2}] SFC clustering - positioned", counter); halftoneAlgorithm.Description = "SFC clustering, cell size: " + sfcClusteringMethod.MaxCellSize + ", positioning enabled, non-adaptive"; config.saveModule(halftoneAlgorithm, false); counter++; // ---------------------------------------------- // SFC clustering - adaptive sfcClusteringMethod.UseClusterPositioning = false; sfcClusteringMethod.UseAdaptiveClustering = true; halftoneAlgorithm.Name = String.Format("[{0:d2}] SFC clustering - adaptive", counter); halftoneAlgorithm.Description = "SFC clustering, cell size: " + sfcClusteringMethod.MaxCellSize + ", no positioning, adaptive"; config.saveModule(halftoneAlgorithm, false); counter++; // ---------------------------------------------- // SFC clustering - positioning & adaptive - larger cell sfcClusteringMethod.UseClusterPositioning = true; sfcClusteringMethod.UseAdaptiveClustering = true; sfcClusteringMethod.MaxCellSize = 30; halftoneAlgorithm.Name = String.Format("[{0:d2}] SFC clustering - larger cell", counter); halftoneAlgorithm.Description = "SFC clustering, cell size: " + sfcClusteringMethod.MaxCellSize + ", positioning enabled, adaptive"; config.saveModule(halftoneAlgorithm, false); counter++; // ---------------------------------------------- halftoneAlgorithm.Method = thresholdHalftoneMethod; thresholdHalftoneMethod.ThresholdFilter = new MatrixThresholdFilter(); thresholdHalftoneMethod.ScanningOrder = new HilbertScanningOrder(); // Riermersma thresholdHalftoneMethod.ErrorFilter = new VectorErrorFilter( ErrorMatrix.Samples.riemersma16); halftoneAlgorithm.Name = String.Format("[{0:d2}] SFC - Riemersma", counter); halftoneAlgorithm.Description = "Error diffusion along Hilbert SFC, error coefficients decrease exponencially\nNumber of coefficients: 16, highest-to-lowest ratio: 16:1"; config.saveModule(halftoneAlgorithm, false); counter++; // ---------------------------------------------- // SFC + simple error diffusion thresholdHalftoneMethod.ErrorFilter = new VectorErrorFilter( ErrorMatrix.Samples.nextPixel); halftoneAlgorithm.Name = String.Format("[{0:d2}] SFC - Simple error diffusion along Hilbert curve", counter); halftoneAlgorithm.Description = "The whole error goes to the next pixel"; config.saveModule(halftoneAlgorithm, false); counter++; // ---------------------------------------------- // SFC + simple error diffusion + pertubed threshold dynamicThresholdFilter.MatrixTable.clearDefinitionRecords(); dynamicThresholdFilter.MatrixTable.addDefinitionRecord( new DynamicMatrixThresholdFilter.ThresholdRecord(0, ThresholdMatrix.Samples.simpleThreshold, 1.0)); thresholdHalftoneMethod.ThresholdFilter = dynamicThresholdFilter; thresholdHalftoneMethod.ErrorFilter = new VectorErrorFilter( ErrorMatrix.Samples.nextPixel); halftoneAlgorithm.Name = String.Format("[{0:d2}] SFC - error diffusion, pertubed threshold", counter); halftoneAlgorithm.Description = "Hilbert SFC, error to the next pixel, threshold perturbation: 1.0\nQuite good blue-noise"; config.saveModule(halftoneAlgorithm, false); counter++; // ---------------------------------------------- // Riermersma + perturbed threshold dynamicThresholdFilter.MatrixTable.clearDefinitionRecords(); dynamicThresholdFilter.MatrixTable.addDefinitionRecord( new DynamicMatrixThresholdFilter.ThresholdRecord(0, ThresholdMatrix.Samples.simpleThreshold, 0.25)); thresholdHalftoneMethod.ErrorFilter = new VectorErrorFilter( ErrorMatrix.Samples.riemersma16); halftoneAlgorithm.Name = String.Format("[{0:d2}] SFC - Riemersma, perturbed threshold", counter); halftoneAlgorithm.Description = "Error diffusion along Hilbert SFC, error coefficients decrease exponencially\nNumber of coefficients: 16, highest-to-lowest ratio: 16:1\nThreshold perturbation: 0.25"; config.saveModule(halftoneAlgorithm, false); counter++; // -------------------- BLUE-NOISE METHODS -------------------- // Randomized error matrix - preserved coefficient count thresholdHalftoneMethod.ThresholdFilter = new MatrixThresholdFilter(); thresholdHalftoneMethod.ErrorFilter = new RandomizedMatrixErrorFilter( ErrorMatrix.Samples.floydSteinberg); thresholdHalftoneMethod.ScanningOrder = new SerpentineScanningOrder(); halftoneAlgorithm.Name = String.Format("[{0:d2}] Randomized error matrix - preserved coefficient count", counter); halftoneAlgorithm.Description = "A template for coefficient positions as proposed by Ulichney"; config.saveModule(halftoneAlgorithm, false); counter++; // ---------------------------------------------- // Randomized error matrix - variable coefficient count thresholdHalftoneMethod.ErrorFilter = new RandomizedMatrixErrorFilter( ErrorMatrix.Samples.floydSteinberg) { RandomizeCoeffCount = true }; halftoneAlgorithm.Name = String.Format("[{0:d2}] Randomized error matrix - variable coefficient count", counter); halftoneAlgorithm.Description = "Floyd-Steinberg matrix as a template for matrix size only\nThe number of coefficient is randomized up to the capacity of the matrix"; config.saveModule(halftoneAlgorithm, false); counter++; // ---------------------------------------------- // Perturbed error matrix thresholdHalftoneMethod.ErrorFilter = new PerturbedErrorFilter( new MatrixErrorFilter(ErrorMatrix.Samples.floydSteinberg)) { PerturbationAmplitude = 0.3 }; halftoneAlgorithm.Name = String.Format("[{0:d2}] Perturbed error matrix", counter); halftoneAlgorithm.Description = "Floyd-Steinberg matrix, perturbation amplitude: 0.3"; config.saveModule(halftoneAlgorithm, false); counter++; // ---------------------------------------------- // Perturbed error matrix + perturbed threshold dynamicThresholdFilter.MatrixTable.clearDefinitionRecords(); dynamicThresholdFilter.MatrixTable.addDefinitionRecord( new DynamicMatrixThresholdFilter.ThresholdRecord(0, ThresholdMatrix.Samples.simpleThreshold, 0.15)); thresholdHalftoneMethod.ThresholdFilter = dynamicThresholdFilter; thresholdHalftoneMethod.ErrorFilter = new PerturbedErrorFilter( new MatrixErrorFilter(ErrorMatrix.Samples.floydSteinberg)) { PerturbationAmplitude = 0.3 }; thresholdHalftoneMethod.ScanningOrder = new SerpentineScanningOrder(); halftoneAlgorithm.Name = String.Format("[{0:d2}] Perturbed error matrix and threshold", counter); halftoneAlgorithm.Description = "A kind of blue-noise generator proposed by Ulichney\nFloyd-Steinberg matrix, perturbation amplitude: 0.3\nPerturbed threshold: 0.15\nSerpentine scanning"; config.saveModule(halftoneAlgorithm, false); counter++; // ---------------------------------------------- // Optimal weights - dynamic error matrix int[,] ostromoukhovOptimalCoeffs = new int[,] { {13, 0, 5}, {13, 0, 5}, {21, 0, 10}, {7, 0, 4}, {8, 0, 5}, {47, 3, 28}, {23, 3, 13}, {15, 3, 8}, {22, 6, 11}, {43, 15, 20}, {7, 3, 3}, {501, 224, 211}, {249, 116, 103}, {165, 80, 67}, {123, 62, 49}, {489, 256, 191}, {81, 44, 31}, {483, 272, 181}, {60, 35, 22}, {53, 32, 19}, {237, 148, 83}, {471, 304, 161}, {3, 2, 1}, {481, 314, 185}, {354, 226, 155}, {1389, 866, 685}, {227, 138, 125}, {267, 158, 163}, {327, 188, 220}, {61, 34, 45}, {627, 338, 505}, {1227, 638, 1075}, {20, 10, 19}, {1937, 1000, 1767}, {977, 520, 855}, {657, 360, 551}, {71, 40, 57}, {2005, 1160, 1539}, {337, 200, 247}, {2039, 1240, 1425}, {257, 160, 171}, {691, 440, 437}, {1045, 680, 627}, {301, 200, 171}, {177, 120, 95}, {2141, 1480, 1083}, {1079, 760, 513}, {725, 520, 323}, {137, 100, 57}, {2209, 1640, 855}, {53, 40, 19}, {2243, 1720, 741}, {565, 440, 171}, {759, 600, 209}, {1147, 920, 285}, {2311, 1880, 513}, {97, 80, 19}, {335, 280, 57}, {1181, 1000, 171}, {793, 680, 95}, {599, 520, 57}, {2413, 2120, 171}, {405, 360, 19}, {2447, 2200, 57}, {11, 10, 0}, {158, 151, 3}, {178, 179, 7}, {1030, 1091, 63}, {248, 277, 21}, {318, 375, 35}, {458, 571, 63}, {878, 1159, 147}, {5, 7, 1}, {172, 181, 37}, {97, 76, 22}, {72, 41, 17}, {119, 47, 29}, {4, 1, 1}, {4, 1, 1}, {4, 1, 1}, {4, 1, 1}, {4, 1, 1}, {4, 1, 1}, {4, 1, 1}, {4, 1, 1}, {4, 1, 1}, {65, 18, 17}, {95, 29, 26}, {185, 62, 53}, {30, 11, 9}, {35, 14, 11}, {85, 37, 28}, {55, 26, 19}, {80, 41, 29}, {155, 86, 59}, {5, 3, 2}, {5, 3, 2}, {5, 3, 2}, {5, 3, 2}, {5, 3, 2}, {5, 3, 2}, {5, 3, 2}, {5, 3, 2}, {5, 3, 2}, {5, 3, 2}, {5, 3, 2}, {5, 3, 2}, {5, 3, 2}, {305, 176, 119}, {155, 86, 59}, {105, 56, 39}, {80, 41, 29}, {65, 32, 23}, {55, 26, 19}, {335, 152, 113}, {85, 37, 28}, {115, 48, 37}, {35, 14, 11}, {355, 136, 109}, {30, 11, 9}, {365, 128, 107}, {185, 62, 53}, {25, 8, 7}, {95, 29, 26}, {385, 112, 103}, {65, 18, 17}, {395, 104, 101}, {4, 1, 1} }; thresholdHalftoneMethod.ThresholdFilter = new MatrixThresholdFilter(); DynamicMatrixErrorFilter dynamicErrorFilter = new DynamicMatrixErrorFilter(); int[,] dynamicErrorCoeffs = new int[2,3]; ErrorMatrix dynamicErrorMatrix = new ErrorMatrix(dynamicErrorCoeffs, 1); for (int i = 0; i < ostromoukhovOptimalCoeffs.GetLength(0); i++) { dynamicErrorCoeffs[0, 2] = ostromoukhovOptimalCoeffs[i, 0]; dynamicErrorCoeffs[1, 0] = ostromoukhovOptimalCoeffs[i, 1]; dynamicErrorCoeffs[1, 1] = ostromoukhovOptimalCoeffs[i, 2]; dynamicErrorMatrix.setDefinitionMatrix(dynamicErrorCoeffs); dynamicErrorFilter.MatrixTable.addDefinitionRecord( new DynamicMatrixErrorFilter.ErrorRecord(i, dynamicErrorMatrix)); } for (int i = ostromoukhovOptimalCoeffs.GetLength(0) - 1; i >= 0; i--) { dynamicErrorCoeffs[0, 2] = ostromoukhovOptimalCoeffs[i, 0]; dynamicErrorCoeffs[1, 0] = ostromoukhovOptimalCoeffs[i, 1]; dynamicErrorCoeffs[1, 1] = ostromoukhovOptimalCoeffs[i, 2]; dynamicErrorMatrix.setDefinitionMatrix(dynamicErrorCoeffs); dynamicErrorFilter.MatrixTable.addDefinitionRecord( new DynamicMatrixErrorFilter.ErrorRecord(255 - i, dynamicErrorMatrix)); } thresholdHalftoneMethod.ErrorFilter = dynamicErrorFilter; thresholdHalftoneMethod.ScanningOrder = new SerpentineScanningOrder(); halftoneAlgorithm.Name = String.Format("[{0:d2}] Blue noise - error diffusion with optimal coefficients", counter); halftoneAlgorithm.Description = "Ostromoukhov"; config.saveModule(halftoneAlgorithm, false); counter++; // ---------------------------------------------- // Optimal weights + perturbed threshold dynamicThresholdFilter.MatrixTable.clearDefinitionRecords(); dynamicThresholdFilter.MatrixTable.addDefinitionRecord( new DynamicMatrixThresholdFilter.ThresholdRecord(0, ThresholdMatrix.Samples.simpleThreshold, 0.35)); thresholdHalftoneMethod.ThresholdFilter = dynamicThresholdFilter; halftoneAlgorithm.Name = String.Format("[{0:d2}] Blue noise - optimal coefficients, perturbed threshold", counter); halftoneAlgorithm.Description = "Ostromoukhov coefficients\n threshold perturbation: 0.35"; config.saveModule(halftoneAlgorithm, false); counter++; // -------------------- ARTISTIC METHODS -------------------- // Veryovka-Buchanan - no error filter thresholdHalftoneMethod.ThresholdFilter = new ImageThresholdFilter() { ImageGenerator = new ImageGenerator() { SpotFunction = SpotFunction.Samples.nullSpot, Effects = { ImageGenerator.Samples.patternEffect } } }; thresholdHalftoneMethod.ErrorFilter = null; halftoneAlgorithm.Name = String.Format("[{0:d2}] Veryovka-Buchanan without error filter", counter); halftoneAlgorithm.Description = "Image filled with a GIMP pattern + histogram equalization"; config.saveModule(halftoneAlgorithm, false); counter++; // ---------------------------------------------- // Veryovka-Buchanan - Ja-Ju-Ni error filter thresholdHalftoneMethod.ErrorFilter = new MatrixErrorFilter( ErrorMatrix.Samples.jarvisJudiceNinke); halftoneAlgorithm.Name = String.Format("[{0:d2}] Veryovka-Buchanan with error filter", counter); halftoneAlgorithm.Description = "Image filled with a GIMP pattern + histogram equalization\nJarvis-Judice-Ninke error matrix"; config.saveModule(halftoneAlgorithm, false); counter++; // ---------------------------------------------- // Supersampling - euclid dot spot function SpotFunction bigDot = SpotFunction.Samples.euclidDot; bigDot.Distance = 30; thresholdHalftoneMethod.ErrorFilter = null; thresholdHalftoneMethod.ThresholdFilter = new SpotFunctionThresholdFilter(bigDot); halftoneAlgorithm.PreResize = new HalftoneAlgorithm.Resize() { Factor = 2.0, Interpolation = HalftoneAlgorithm.Resize.InterpolationType.Bicubic }; halftoneAlgorithm.PostResize = new HalftoneAlgorithm.Resize() { Factor = 2.0, Interpolation = HalftoneAlgorithm.Resize.InterpolationType.Bicubic, Forward = false }; halftoneAlgorithm.Name = String.Format("[{0:d2}] Spot function with supersampling", counter); halftoneAlgorithm.Description = "Euclid-dot spot function\nSmoothened via supersampling"; config.saveModule(halftoneAlgorithm, false); counter++; halftoneAlgorithm.PreResize = null; halftoneAlgorithm.PostResize = null; // ---------------------------------------------- // Smoothening - euclid dot spot function halftoneAlgorithm.PostSmoothen = new HalftoneAlgorithm.Smoothen() { Radius = 5 }; halftoneAlgorithm.Name = String.Format("[{0:d2}] Spot function with smoothening", counter); halftoneAlgorithm.Description = "Euclid-dot spot function\nSmoothened via Gaussian blur & Levels"; config.saveModule(halftoneAlgorithm, false); counter++; halftoneAlgorithm.PostSmoothen = null; // ---------------------------------------------- // Spot function + effects thresholdHalftoneMethod.ThresholdFilter = new ImageThresholdFilter() { ImageGenerator = new ImageGenerator() { SpotFunction = SpotFunction.Samples.euclidDot, Effects = { ImageGenerator.Samples.pixelizeEffect, ImageGenerator.Samples.rippleEffect } } }; halftoneAlgorithm.Name = String.Format("[{0:d2}] Spot function with effects", counter); halftoneAlgorithm.Description = "Euclid dot: angle = 45 deg, distance: 10 px\nEffects: Pixelize, Ripple"; config.saveModule(halftoneAlgorithm, false); counter++; // ---------------------------------------------- // Spot function + effects thresholdHalftoneMethod.ThresholdFilter = new ImageThresholdFilter() { ImageGenerator = new ImageGenerator() { SpotFunction = bigDot, Effects = { ImageGenerator.Samples.canvasEffect } } }; halftoneAlgorithm.Name = String.Format("[{0:d2}] Spot function with effects", counter); halftoneAlgorithm.Description = "Euclid dot: angle = 45 deg, distance: 10 px\nEffects: Canvas"; config.saveModule(halftoneAlgorithm, false); counter++; // -------------------- MISCELANEOUS METHODS -------------------- // Floyd-Steinberg with dot gain compensation thresholdHalftoneMethod.ThresholdFilter = new MatrixThresholdFilter(); thresholdHalftoneMethod.ErrorFilter = new MatrixErrorFilter( ErrorMatrix.Samples.floydSteinberg); halftoneAlgorithm.PreDotGain = new HalftoneAlgorithm.GammaCorrection() { Gamma = 2.2 }; halftoneAlgorithm.Name = String.Format("[{0:d2}] Dot gain compensation", counter); halftoneAlgorithm.Description = "Floyd-Steinberg, dot gain compensation via gamma curve"; config.saveModule(halftoneAlgorithm, false); counter++; halftoneAlgorithm.PreDotGain = null; // Dynamic threshold filter dynamicThresholdFilter.MatrixTable.clearDefinitionRecords(); dynamicThresholdFilter.MatrixTable.addDefinitionRecord( new DynamicMatrixThresholdFilter.ThresholdRecord(0, ThresholdMatrix.Samples.sampleScreenMatrix)); dynamicThresholdFilter.MatrixTable.addDefinitionRecord( new DynamicMatrixThresholdFilter.ThresholdRecord(32, ThresholdMatrix.Samples.createBayerDispersedDotMatrix(3))); dynamicThresholdFilter.MatrixTable.addDefinitionRecord( new DynamicMatrixThresholdFilter.ThresholdRecord(64, ThresholdMatrix.Samples.createBayerDispersedDotMatrix(3), 1.0)); dynamicThresholdFilter.MatrixTable.addDefinitionRecord( new DynamicMatrixThresholdFilter.ThresholdRecord(96, ThresholdMatrix.Samples.createBayerDispersedDotMatrix(3), 0.5)); dynamicThresholdFilter.MatrixTable.addDefinitionRecord( new DynamicMatrixThresholdFilter.ThresholdRecord(112, ThresholdMatrix.Samples.simpleThreshold)); dynamicThresholdFilter.MatrixTable.addDefinitionRecord( new DynamicMatrixThresholdFilter.ThresholdRecord(144, ThresholdMatrix.Samples.createBayerDispersedDotMatrix(1))); dynamicThresholdFilter.MatrixTable.addDefinitionRecord( new DynamicMatrixThresholdFilter.ThresholdRecord(160, ThresholdMatrix.Samples.createBayerDispersedDotMatrix(2))); dynamicThresholdFilter.MatrixTable.addDefinitionRecord( new DynamicMatrixThresholdFilter.ThresholdRecord(192, ThresholdMatrix.Samples.simpleThreshold, 1.0)); dynamicThresholdFilter.MatrixTable.addDefinitionRecord( new DynamicMatrixThresholdFilter.ThresholdRecord(224, ThresholdMatrix.Samples.sampleScreenMatrix)); thresholdHalftoneMethod.ThresholdFilter = dynamicThresholdFilter; thresholdHalftoneMethod.ErrorFilter = null; thresholdHalftoneMethod.ScanningOrder = new ScanlineScanningOrder(); halftoneAlgorithm.Name = String.Format("[{0:d2}] Tone-dependent threshold filter", counter); halftoneAlgorithm.Description = "Screen 4x4 45deg angle, Bayer 8x8, Bayer 8x8 + perturbation 1.0, Bayer 8x8 + perturbation 0.5, threshold 0.5, Bayer 2x2, Bayer 4x4, white noise, screen"; config.saveModule(halftoneAlgorithm, false); counter++; // ---------------------------------------------- // Dynamic error filter thresholdHalftoneMethod.ThresholdFilter = new MatrixThresholdFilter(); dynamicErrorFilter.MatrixTable.clearDefinitionRecords(); dynamicErrorFilter.MatrixTable.addDefinitionRecord( new DynamicMatrixErrorFilter.ErrorRecord(0, ErrorMatrix.Samples.floydSteinberg)); dynamicErrorFilter.MatrixTable.addDefinitionRecord( new DynamicMatrixErrorFilter.ErrorRecord(64, ErrorMatrix.Samples.jarvisJudiceNinke)); dynamicErrorFilter.MatrixTable.addDefinitionRecord( new DynamicMatrixErrorFilter.ErrorRecord(128, ErrorMatrix.Samples.stucki)); dynamicErrorFilter.MatrixTable.addDefinitionRecord( new DynamicMatrixErrorFilter.ErrorRecord(192, ErrorMatrix.Samples.sierra)); thresholdHalftoneMethod.ErrorFilter = dynamicErrorFilter; thresholdHalftoneMethod.ScanningOrder = new ScanlineScanningOrder(); halftoneAlgorithm.Name = String.Format("[{0:d2}] Tone-dependent error filter", counter); halftoneAlgorithm.Description = "Floyd-Steinberg, Jarvis-Judice-Ninke, Stucki, Sierra"; config.saveModule(halftoneAlgorithm, false); counter++; // ---------------------------------------------- config.save(); // DEBUG: //foreach (Module module in config.listAllModules()) { // Console.Out.WriteLine("type: {0}, name: {1}, desc: {2}", // module.GetType(), module.Name, module.Description); //} }
/// <summary> /// Create a new halftone algorithm configuration panel using an /// existing halftone algorithm. /// </summary> /// <param name="existingModule"></param> public HalftoneAlgorithmPanel(HalftoneAlgorithm existingModule) { BorderWidth = 3; VBox preProcessingVBox = new VBox(); Frame preResizeFrame = new Frame("Resize") { BorderWidth = 3 }; Frame preDotGainFrame = new Frame("Dot gain correction") { BorderWidth = 3 }; Frame preSharpenFrame = new Frame("Sharpen") { BorderWidth = 3 }; Frame halftoneMethodFrame = new Frame("Halftone method") { BorderWidth = 3 }; VBox postProcessingVBox = new VBox(); Frame postResizeFrame = new Frame("Resize") { BorderWidth = 3 }; Frame postSmoothenFrame = new Frame("Smoothen") { BorderWidth = 3 }; // -------- pre-processing -------- // ---- resize ---- preResizePanel = new ResizePanel(); preResizePanel.ModuleChanged += delegate { Module.PreResize = preResizePanel.Module; if (ModuleChanged != null) { ModuleChanged(this, new EventArgs()); } }; preResizeFrame.Add(preResizePanel); preProcessingVBox.PackStart(preResizeFrame); // ---- dot gain ---- preDotGainEnabledCheckButton = new CheckButton("Enabled"); preDotGainEnabledCheckButton.Toggled += delegate { bool enabled = preDotGainEnabledCheckButton.Active; if (!enabled) { Module.PreDotGain = null; } else if (Module.PreDotGain == null) { Module.PreDotGain = new HalftoneAlgorithm.GammaCorrection(); } if (Module.PreDotGain != null) { preDotGainGammaSpinButton.Value = ((HalftoneAlgorithm.GammaCorrection) Module.PreDotGain).Gamma; } preDotGainGammaSpinButton.Sensitive = enabled; if (ModuleChanged != null) { ModuleChanged(this, new EventArgs()); } }; preDotGainGammaSpinButton = new SpinButton(0.1, 10, 0.1); preDotGainGammaSpinButton.Changed += delegate { ((HalftoneAlgorithm.GammaCorrection)Module.PreDotGain).Gamma = preDotGainGammaSpinButton.Value; if (ModuleChanged != null) { ModuleChanged(this, new EventArgs()); } }; Table preDotGainTable = new Table(2, 2, false) { ColumnSpacing = 3, RowSpacing = 3, BorderWidth = 5 }; preDotGainTable.Attach(preDotGainEnabledCheckButton, 0, 2, 0, 1, AttachOptions.Fill | AttachOptions.Expand, AttachOptions.Shrink, 0, 0); preDotGainTable.Attach(new Label("Gamma:") { Xalign = 0.0f }, 0, 1, 1, 2, AttachOptions.Fill, AttachOptions.Shrink, 0, 0); preDotGainTable.Attach(preDotGainGammaSpinButton, 1, 2, 1, 2, AttachOptions.Fill, AttachOptions.Shrink, 0, 0); preDotGainFrame.Add(preDotGainTable); preProcessingVBox.PackStart(preDotGainFrame); // ---- sharpen ---- preSharpenEnabledCheckButton = new CheckButton("Enabled"); preSharpenEnabledCheckButton.Toggled += delegate { bool enabled = preSharpenEnabledCheckButton.Active; if (!enabled) { Module.PreSharpen = null; } else if (Module.PreSharpen == null) { Module.PreSharpen = new HalftoneAlgorithm.Sharpen(); } if (Module.PreSharpen != null) { preSharpenAmountHScale.Value = Module.PreSharpen.Amount; } preSharpenAmountHScale.Sensitive = enabled; if (ModuleChanged != null) { ModuleChanged(this, new EventArgs()); } }; preSharpenAmountHScale = new HScale(0, 1, 0.01); preSharpenAmountHScale.ChangeValue += delegate { Module.PreSharpen.Amount = preSharpenAmountHScale.Value; if (ModuleChanged != null) { ModuleChanged(this, new EventArgs()); } }; Table preSharpenTable = new Table(2, 2, false) { ColumnSpacing = 3, RowSpacing = 3, BorderWidth = 5 }; preSharpenTable.Attach(preSharpenEnabledCheckButton, 0, 2, 0, 1, AttachOptions.Fill | AttachOptions.Expand, AttachOptions.Shrink, 0, 0); preSharpenTable.Attach(new Label("Amount:") { Xalign = 0.0f }, 0, 1, 1, 2, AttachOptions.Fill, AttachOptions.Shrink, 0, 0); preSharpenTable.Attach(preSharpenAmountHScale, 1, 2, 1, 2, AttachOptions.Fill, AttachOptions.Shrink, 0, 0); preSharpenFrame.Add(preSharpenTable); preProcessingVBox.PackStart(preSharpenFrame); AppendPage(preProcessingVBox, new Label("Pre-processing")); // -------- halftone method -------- halftoneMethodSelector = new SubmoduleSelector<HalftoneMethod>(HalftoneMethod.createDefault()) { BorderWidth = 5 }; halftoneMethodSelector.ModuleChanged += delegate { if (Module != null) { Module.Method = halftoneMethodSelector.Module; if (ModuleChanged != null) { ModuleChanged(this, new EventArgs()); } } }; halftoneMethodFrame.Add(halftoneMethodSelector); AppendPage(halftoneMethodFrame, new Label("Halftone method")); // -------- post-processing -------- // ---- resize ---- postResizePanel = new ResizePanel(); postResizePanel.ModuleChanged += delegate { Module.PostResize = postResizePanel.Module; if (ModuleChanged != null) { ModuleChanged(this, new EventArgs()); } }; supersamplingCheckButton = new CheckButton("Supersampling"); supersamplingCheckButton.Toggled += delegate { Module.SupersamplingEnabled = supersamplingCheckButton.Active; postResizePanel.Sensitive = !supersamplingCheckButton.Active; if (ModuleChanged != null) { ModuleChanged(this, new EventArgs()); } }; Table postResizeTable = new Table(2, 1, false); postResizeTable.Attach(supersamplingCheckButton, 0, 1, 0, 1, AttachOptions.Fill | AttachOptions.Expand, AttachOptions.Shrink, 0, 0); postResizeTable.Attach(postResizePanel, 0, 1, 1, 2, AttachOptions.Fill | AttachOptions.Expand, AttachOptions.Fill | AttachOptions.Expand, 0, 0); postResizeFrame.Add(postResizeTable); postProcessingVBox.PackStart(postResizeFrame); // ---- smoothen ---- postSmoothenEnabledCheckButton = new CheckButton("Enabled"); postSmoothenEnabledCheckButton.Toggled += delegate { bool enabled = postSmoothenEnabledCheckButton.Active; if (!enabled) { Module.PostSmoothen = null; } else if (Module.PostSmoothen == null) { Module.PostSmoothen = new HalftoneAlgorithm.Smoothen(); } if (Module.PostSmoothen != null) { postSmoothenRadiusSpinButton.Value = Module.PostSmoothen.Radius; } postSmoothenRadiusSpinButton.Sensitive = enabled; if (ModuleChanged != null) { ModuleChanged(this, new EventArgs()); } }; postSmoothenRadiusSpinButton = new SpinButton(1, 20, 0.5); postSmoothenRadiusSpinButton.Changed += delegate { Module.PostSmoothen.Radius = postSmoothenRadiusSpinButton.Value; if (ModuleChanged != null) { ModuleChanged(this, new EventArgs()); } }; Table postSmoothenTable = new Table(2, 2, false) { ColumnSpacing = 3, RowSpacing = 3, BorderWidth = 5 }; postSmoothenTable.Attach(postSmoothenEnabledCheckButton, 0, 2, 0, 1, AttachOptions.Fill | AttachOptions.Expand, AttachOptions.Shrink, 0, 0); postSmoothenTable.Attach(new Label("Radius:") { Xalign = 0.0f }, 0, 1, 1, 2, AttachOptions.Fill, AttachOptions.Shrink, 0, 0); postSmoothenTable.Attach(postSmoothenRadiusSpinButton, 1, 2, 1, 2, AttachOptions.Fill, AttachOptions.Shrink, 0, 0); postSmoothenFrame.Add(postSmoothenTable); postProcessingVBox.PackStart(postSmoothenFrame); AppendPage(postProcessingVBox, new Label("Post-processing")); Module = existingModule; ShowAll(); Page = 1; }
private int interpolationTypeIndex( HalftoneAlgorithm.Resize.InterpolationType interpolationType) { return (from result in (interpolationTypes.Select( (type, index) => new { Index = index, Ok = type.type == interpolationType } )) where result.Ok == true select result.Index).First(); }
public ResizePanel(HalftoneAlgorithm.Resize existingModule) : base(3, 2, false) { ColumnSpacing = RowSpacing = 3; BorderWidth = 5; enabledCheckButton = new CheckButton("Enabled"); enabledCheckButton.Toggled += delegate { bool enabled = enabledCheckButton.Active; if (enabled && (Module == null)) { Module = new HalftoneAlgorithm.Resize(); } if (!enabled && (Module != null)) { Module = null; } if (module != null) { resizeFactorSpinButton.Value = Module.Factor; interpolationTypeComboBox.Active = interpolationTypeIndex(Module.Interpolation); } resizeFactorSpinButton.Sensitive = enabled; interpolationTypeComboBox.Sensitive = enabled; }; resizeFactorSpinButton = new SpinButton(0.1, 10, 0.01); resizeFactorSpinButton.Changed += delegate { Module.Factor = resizeFactorSpinButton.Value; if (ModuleChanged != null) { ModuleChanged(this, new EventArgs()); } }; interpolationTypes = new InterpolationTypeRecord[] { new InterpolationTypeRecord() { name = "Nearest neighbour", type = HalftoneAlgorithm.Resize.InterpolationType.NearestNeighbour}, new InterpolationTypeRecord() { name = "Bilinear", type = HalftoneAlgorithm.Resize.InterpolationType.Bilinear}, new InterpolationTypeRecord() { name = "Bicubic", type = HalftoneAlgorithm.Resize.InterpolationType.Bicubic}, new InterpolationTypeRecord() { name = "Lanczos", type = HalftoneAlgorithm.Resize.InterpolationType.Lanczos}, }; var interpolationTypeNames = from type in interpolationTypes select type.name; interpolationTypeComboBox = new ComboBox( new List<string>(interpolationTypeNames).ToArray()); interpolationTypeComboBox.Changed += delegate { HalftoneAlgorithm.Resize.InterpolationType value = interpolationTypes[interpolationTypeComboBox.Active].type; Module.Interpolation = value; if (ModuleChanged != null) { ModuleChanged(this, new EventArgs()); } }; Module = existingModule; Attach(enabledCheckButton, 0, 2, 0, 1, AttachOptions.Fill | AttachOptions.Expand, AttachOptions.Shrink, 0, 0); Attach(new Label("Resize factor:") { Xalign = 0.0f }, 0, 1, 1, 2, AttachOptions.Fill, AttachOptions.Shrink, 0, 0); Attach(resizeFactorSpinButton, 1, 2, 1, 2, AttachOptions.Fill, AttachOptions.Shrink, 0, 0); Attach(new Label("Interpolation type:") { Xalign = 0.0f }, 0, 1, 2, 3, AttachOptions.Fill, AttachOptions.Shrink, 0, 0); Attach(interpolationTypeComboBox, 1, 2, 2, 3, AttachOptions.Fill, AttachOptions.Shrink, 0, 0); ShowAll(); }
/// <summary> /// Initialize the config manager. /// </summary> /// <remarks> /// Load the configurations from file, if possible, and get the last /// used algorithm. /// </remarks> private void initConfigManager() { configManager = new ConfigManager() { ConfigFileName = System.IO.Path.Combine(Environment.GetFolderPath( Environment.SpecialFolder.ApplicationData), "halftonelab.cfg") }; configManager.load(); // load the last used algorithm selectedAlgorithm = configManager.getModule<HalftoneAlgorithm>( "_LAST"); if (selectedAlgorithm == null) { selectedAlgorithm = new HalftoneAlgorithm(); } }