private static void PaintColorSchemeToBitmapT <T>(this IRaster raster, T noData, Func <int, int, T> getValue, IRasterSymbolizer rasterSymbolizer, Bitmap bitmap, IProgressHandler progressHandler) where T : struct, IEquatable <T>, IComparable <T> { if (raster == null) { throw new ArgumentNullException(nameof(raster)); } if (rasterSymbolizer == null) { throw new ArgumentNullException(nameof(rasterSymbolizer)); } if (bitmap == null) { throw new ArgumentNullException(nameof(bitmap)); } if (rasterSymbolizer.Scheme.Categories == null || rasterSymbolizer.Scheme.Categories.Count == 0) { return; } BitmapData bmpData; var numRows = raster.NumRows; var numColumns = raster.NumColumns; var rect = new Rectangle(0, 0, numColumns, numRows); try { bmpData = bitmap.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); } catch { var ms = new MemoryStream(); bitmap.Save(ms, ImageFormat.MemoryBmp); ms.Position = 0; bmpData = bitmap.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); } // Prepare progress meter var pm = new ProgressMeter(progressHandler, SymbologyMessageStrings.DesktopRasterExt_PaintingColorScheme, numRows); if (numRows * numColumns < 100000) { pm.StepPercent = 50; } if (numRows * numColumns < 500000) { pm.StepPercent = 10; } if (numRows * numColumns < 1000000) { pm.StepPercent = 5; } var sets = GetColorSets <T>(rasterSymbolizer.Scheme.Categories); var noDataColor = Argb.FromColor(rasterSymbolizer.NoDataColor); var alpha = Argb.ByteRange(Convert.ToInt32(rasterSymbolizer.Opacity * 255)); var ptr = bmpData.Scan0; for (var row = 0; row < numRows; row++) { for (var col = 0; col < numColumns; col++) { var val = getValue(row, col); Argb argb; if (val.Equals(noData)) { argb = noDataColor; } else { // Usually values are not random, so check neighboring previous cells for same color int?srcOffset = null; if (col > 0) { if (val.Equals(getValue(row, col - 1))) { srcOffset = Offset(row, col - 1, bmpData.Stride); } } if (srcOffset == null && row > 0) { if (val.Equals(getValue(row - 1, col))) { srcOffset = Offset(row - 1, col, bmpData.Stride); } } if (srcOffset != null) { argb = new Argb(Marshal.ReadByte(ptr, (int)srcOffset + 3), Marshal.ReadByte(ptr, (int)srcOffset + 2), Marshal.ReadByte(ptr, (int)srcOffset + 1), Marshal.ReadByte(ptr, (int)srcOffset)); } else { var color = GetColor(sets, val); argb = new Argb(alpha, color.R, color.G, color.B); } } var offset = Offset(row, col, bmpData.Stride); Marshal.WriteByte(ptr, offset, argb.B); Marshal.WriteByte(ptr, offset + 1, argb.G); Marshal.WriteByte(ptr, offset + 2, argb.R); Marshal.WriteByte(ptr, offset + 3, argb.A); } pm.CurrentValue = row; } pm.Reset(); if (rasterSymbolizer.IsSmoothed) { var mySmoother = new Smoother(bmpData.Stride, bmpData.Width, bmpData.Height, bmpData.Scan0, progressHandler); mySmoother.Smooth(); } bitmap.UnlockBits(bmpData); rasterSymbolizer.ColorSchemeHasUpdated = true; }
private static Argb GetColor <T>(IEnumerable <ColorSet <T> > sets, T value) where T : struct, IComparable <T> { foreach (var set in sets) { if (set.Contains(value)) { if (!set.Gradient) { return(set.Color); } if (set.Min == null || set.Max == null) { return(set.Color); } double lowVal = Convert.ToDouble(set.Min.Value); double range = Math.Abs(Convert.ToDouble(set.Max.Value) - lowVal); double p = 0; // the portion of the range, where 0 is LowValue & 1 is HighValue double ht; double dVal = Convert.ToDouble(value); switch (set.GradientModel) { case GradientModel.Linear: p = (dVal - lowVal) / range; break; case GradientModel.Exponential: ht = dVal; if (ht < 1) { ht = 1.0; } if (range > 1) { p = Math.Pow(ht - lowVal, 2) / Math.Pow(range, 2); } else { return(set.Color); } break; case GradientModel.Logarithmic: ht = dVal; if (ht < 1) { ht = 1.0; } if (range > 1.0 && ht - lowVal > 1.0) { p = Math.Log(ht - lowVal) / Math.Log(range); } else { return(set.Color); } break; } return(new Argb(set.MinA + (int)(set.RangeA * p), set.MinR + (int)(set.RangeR * p), set.MinG + (int)(set.RangeG * p), set.MinB + (int)(set.RangeB * p))); } } return(Argb.FromColor(Color.Transparent)); }
private static List <ColorSet <T> > GetColorSets <T>(IEnumerable <IColorCategory> categories) where T : struct, IComparable <T> { var result = new List <ColorSet <T> >(); foreach (var c in categories) { var cs = new ColorSet <T>(); Color high = c.HighColor; Color low = c.LowColor; cs.Color = Argb.FromColor(low); if (high != low) { cs.GradientModel = c.GradientModel; cs.Gradient = true; cs.MinA = low.A; cs.MinR = low.R; cs.MinG = low.G; cs.MinB = low.B; cs.RangeA = high.A - cs.MinA; cs.RangeR = high.R - cs.MinR; cs.RangeG = high.G - cs.MinG; cs.RangeB = high.B - cs.MinB; } cs.Max = Global.MaximumValue <T>(); var testMax = Convert.ToDouble(cs.Max); cs.Min = Global.MinimumValue <T>(); var testMin = Convert.ToDouble(cs.Min); if (c.Range.Maximum != null && c.Range.Maximum < testMax) { if (c.Range.Maximum < testMin) { cs.Max = cs.Min; } else { cs.Max = (T)Convert.ChangeType(c.Range.Maximum.Value, typeof(T)); } } if (c.Range.Minimum != null && c.Range.Minimum > testMin) { if (c.Range.Minimum > testMax) { cs.Min = Global.MaximumValue <T>(); } else { cs.Min = (T)Convert.ChangeType(c.Range.Minimum.Value, typeof(T)); } } cs.MinInclusive = c.Range.MinIsInclusive; cs.MaxInclusive = c.Range.MaxIsInclusive; result.Add(cs); } // The normal order uses "overwrite" behavior, so that each color is drawn // if it qualifies until all the ranges are tested, overwriting previous. // This can be mimicked by going through the sets in reverse and choosing // the first that qualifies. For lots of color ranges, opting out of // a large portion of the range testing should be faster. result.Reverse(); return(result); }
private static void DrawToBitmapT <T>(IRaster raster, T noData, Func <int, int, T> getValue, Func <int, byte> getByte, Action <int, byte> setByte, IRasterSymbolizer rasterSymbolizer, int stride, ProgressMeter pm) where T : struct, IEquatable <T>, IComparable <T> { if (raster == null) { throw new ArgumentNullException(nameof(raster)); } if (rasterSymbolizer == null) { throw new ArgumentNullException(nameof(rasterSymbolizer)); } if (rasterSymbolizer.Scheme.Categories == null || rasterSymbolizer.Scheme.Categories.Count == 0) { return; } float[][] hillshade = null; if (rasterSymbolizer.ShadedRelief.IsUsed) { pm.BaseMessage = "Calculating Shaded Relief"; hillshade = rasterSymbolizer.HillShade ?? raster.CreateHillShadeT(getValue, rasterSymbolizer.ShadedRelief, pm); } pm.BaseMessage = "Calculating Colors"; var sets = GetColorSets <T>(rasterSymbolizer.Scheme.Categories); var noDataColor = Argb.FromColor(rasterSymbolizer.NoDataColor); for (int row = 0; row < raster.NumRows; row++) { for (int col = 0; col < raster.NumColumns; col++) { var value = getValue(row, col); Argb argb; if (value.Equals(noData)) { argb = noDataColor; } else { // Usually values are not random, so check neighboring previous cells for same color int?srcOffset = null; if (col > 0) { if (value.Equals(getValue(row, col - 1))) { srcOffset = Offset(row, col - 1, stride); } } if (srcOffset == null && row > 0) { if (value.Equals(getValue(row - 1, col))) { srcOffset = Offset(row - 1, col, stride); } } if (srcOffset != null) { argb = new Argb(getByte((int)srcOffset + 3), getByte((int)srcOffset + 2), getByte((int)srcOffset + 1), getByte((int)srcOffset)); } else { argb = GetColor(sets, value); } } if (hillshade != null) { if (hillshade[row][col] == -1 || float.IsNaN(hillshade[row][col])) { argb = new Argb(argb.A, noDataColor.R, noDataColor.G, noDataColor.B); } else { var red = (int)(argb.R * hillshade[row][col]); var green = (int)(argb.G * hillshade[row][col]); var blue = (int)(argb.B * hillshade[row][col]); argb = new Argb(argb.A, red, green, blue); } } var offset = Offset(row, col, stride); setByte(offset, argb.B); setByte(offset + 1, argb.G); setByte(offset + 2, argb.R); setByte(offset + 3, argb.A); } pm.Next(); } }