using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Drawing.Imaging; using System.Windows.Forms; namespace TraceOutline { class CTraceOuline { public int color_threshold = 200; public bool use_red = true; public bool use_green = true; public bool use_blue = true; //for argb color pixel format private int CoordsToIndex(int x, int y, int stride) { return (stride * y) + (x * 4); } public int GetGrayScaleColor(Color c) { int numcolorplane = 3; numcolorplane = (!use_blue) ? numcolorplane - 1 : numcolorplane; numcolorplane = (!use_green) ? numcolorplane - 1 : numcolorplane; numcolorplane = (!use_red) ? numcolorplane - 1 : numcolorplane; if (numcolorplane == 0) return (((int)c.B + (int)c.G + (int)c.R) / 3); int accvalue = 0; accvalue = use_blue ? accvalue + (int)c.B : accvalue; accvalue = use_green ? accvalue + (int)c.G : accvalue; accvalue = use_red ? accvalue + (int)c.R : accvalue; return (int)(accvalue / numcolorplane); } private int GetMonoColor(Color c) { int i = GetGrayScaleColor(c); if (i < color_threshold) return 0; else return 1; } public Point[] StringOutline2Polygon(string outline) { string[] s = outline.Split(';'); if (s.Length < 5) return null; Point[] p = new Point[s.Length - 1]; string[] s1 = s[0].Split(','); for (int i = 0; i < s.Length - 1; i++) { s1 = s[i].Split(','); p[i].X = int.Parse(s1[0]); p[i].Y = int.Parse(s1[1]); } return p; } public string TraceOutlineN(Bitmap bm, int x0, int y0, int probe_width, Color fg, Color bg, bool bauto_threshold, int n) { string s = ""; int x = 0, y = y0; int x1 = 0, y1 = 0; Color c1, c2; int start_direction = 0, current_direction = 0; int gc1 = 0; int gc2 = 0; bool hitborder = false; bool hitstart = false; int max_width = bm.Width, max_height = bm.Height; //direct bit manipulation Rectangle rect = new Rectangle(0, 0, bm.Width, bm.Height); BitmapData bmpData = bm.LockBits(rect, ImageLockMode.ReadOnly, bm.PixelFormat); IntPtr ptr = bmpData.Scan0; int bytes = bm.Width * bm.Height * 4; byte[] rgbValues = new byte[bytes]; // Copy the RGB values into the array. System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes); bm.UnlockBits(bmpData); if (bauto_threshold) { //get max pix value diff int maxpixdiff = 0; int maxpixvalue = 0; for (int i = 0; i < (probe_width * 2); i++) { try { if (i % 2 == 1) x = x0 + i / 2; else x = x0 - i / 2; if (x < 0) continue; if (x >= max_width) continue; int index = CoordsToIndex(x, y0, bmpData.Stride); if (index < 0 || index > (bytes - 1)) break; c1 = Color.FromArgb(rgbValues[index + 2], rgbValues[index + 1], rgbValues[index]); gc1 = GetGrayScaleColor(c1); index = CoordsToIndex(x + 1, y0, bmpData.Stride); if (index < 0 || index > (bytes - 1)) break; c2 = Color.FromArgb(rgbValues[index + 2], rgbValues[index + 1], rgbValues[index]); gc2 = GetGrayScaleColor(c2); if (maxpixdiff < Math.Abs(gc1 - gc2)) maxpixdiff = Math.Abs(gc1 - gc2); if (gc1 > maxpixvalue) maxpixvalue = gc1; if (gc2 > maxpixvalue) maxpixvalue = gc2; } catch (Exception) { break; } } if (maxpixdiff > 0) color_threshold = maxpixvalue - (int)(0.3 * maxpixdiff); if (color_threshold < 0) color_threshold = 0; } int gfg = GetMonoColor(fg); int gbg = GetMonoColor(bg); for (int i = 0; i < (probe_width * 2); i++) { try { if (i % 2 == 1) x = x0 + i / 2; else x = x0 - i / 2; if (x < 0) continue; if (x >= max_width) continue; int index = CoordsToIndex(x, y0, bmpData.Stride); if (index < 0 || index > (bytes - 1)) break; c1 = Color.FromArgb(rgbValues[index + 2], rgbValues[index + 1], rgbValues[index]); gc1 = GetMonoColor(c1); index = CoordsToIndex(x + 1, y0, bmpData.Stride); if (index < 0 || index > (bytes - 1)) break; c2 = Color.FromArgb(rgbValues[index + 2], rgbValues[index + 1], rgbValues[index]); gc2 = GetMonoColor(c2); if ((gc1 == gfg && gc2 == gbg) || (gc1 == gbg && gc2 == gfg)) { if (gc1 == gfg && gc2 == gbg) start_direction = 4; if (gc1 == gbg && gc2 == gfg) start_direction = 0; hitborder = true; x1 = x; y1 = y; break; } } catch (Exception) { break; } } if (!hitborder) { return ""; } Color[] cn = new Color[8 * n]; int count = 0; int countlimit = 10000; x = x1; y = y1; int diffx = 0, diffy = 0; int index1 = 0; while (!hitstart) { count++; //fallback to prevent infinite loop if (count > countlimit) { return ""; } //getting all the neighbours' pixel color try { //processing top neighbours left to right for (int i = 0; i <= 2 * n; i++) { diffx = i - n; index1 = CoordsToIndex(x + diffx, y - n, bmpData.Stride); cn[i] = ((x + diffx) >= 0 && (x + diffx) < max_width && (y - n) >= 0 && (y - n) < max_height) ? Color.FromArgb(rgbValues[index1 + 2], rgbValues[index1 + 1], rgbValues[index1]) : Color.Empty; } //processing right neighbours top to bottom for (int i = 2 * n + 1; i < 4 * n; i++) { diffy = i - 3 * n; index1 = CoordsToIndex(x + n, y + diffy, bmpData.Stride); cn[i] = ((x + n) >= 0 && (x + n) < max_width && (y + diffy) >= 0 && (y + diffy) < max_height) ? Color.FromArgb(rgbValues[index1 + 2], rgbValues[index1 + 1], rgbValues[index1]) : Color.Empty; } //processing bottom neighbours right to left for (int i = 4 * n; i <= 6 * n; i++) { diffx = i - 5 * n; index1 = CoordsToIndex(x - diffx, y + n, bmpData.Stride); cn[i] = ((x - diffx) >= 0 && (x - diffx) < max_width && (y + n) >= 0 && (y + n) < max_height) ? Color.FromArgb(rgbValues[index1 + 2], rgbValues[index1 + 1], rgbValues[index1]) : Color.Empty; } //processing left neighbours bottom to top for (int i = 6 * n + 1; i < 8 * n; i++) { diffy = i - 7 * n; index1 = CoordsToIndex(x - n, y - diffy, bmpData.Stride); cn[i] = ((x - n) >= 0 && (x - n) < max_width && (y - diffy) >= 0 && (y - diffy) < max_height) ? Color.FromArgb(rgbValues[index1 + 2], rgbValues[index1 + 1], rgbValues[index1]) : Color.Empty; } } catch (Exception e) { MessageBox.Show(e.ToString()); return ""; } int index = 0; string tests = ""; bool dir_found = false; //find the first valid foreground pixel for (int i = start_direction; i < start_direction + (8 * n); i++) { index = i % (8 * n); if (!cn[index].Equals(Color.Empty)) if (GetMonoColor(cn[index]) == gfg) { current_direction = index; dir_found = true; break; } } //if no foreground pixel found, just find the next valid pixel if (!dir_found) for (int i = start_direction; i < start_direction + (8 * n); i++) { index = i % (8 * n); if (!cn[index].Equals(Color.Empty)) { current_direction = index; dir_found = true; break; } } // find the next direction to look for foreground pixels if ((index >= 0) && (index <= 2 * n)) { diffx = index - n; x += diffx; y -= n; } if ((index > 2 * n) && (index < 4 * n)) { diffy = index - 3 * n; x += n; y += diffy; } if ((index >= 4 * n) && (index <= 6 * n)) { diffx = index - 5 * n; x -= diffx; y += n; } if ((index > 6 * n) && (index < 8 * n)) { diffy = index - 7 * n; x -= n; y -= diffy; } //store the found outline tests = x + "," + y + ";"; s = s + tests; start_direction = (current_direction + (4 * n + 1)) % (8 * n); //adaptive stop condition bool bMinCountOK = (n > 1) ? (count > (max_height / 5)) : (count > 10); if (bMinCountOK && (Math.Abs(x - x1) < (n + 1) && (Math.Abs(y - y1) < (n + 1)))) hitstart = true; } return s; } } }