2016-04-10 15 views

答えて

-1

これは実用的な解決策で、拡散値は画像サイズによって異なります。この時点では8bppビットマップ出力はありません。

Sample-Output..

public class DistanceField 
{ 
    public int Spread = 8; 
    public int WhiteColor = Color.White.ToArgb(); 

    public DistanceField() 
    {} 

    /** 
    * Caclulate the squared distance between two points 
    * 
    * @param x1 The x coordinate of the first point 
    * @param y1 The y coordiante of the first point 
    * @param x2 The x coordinate of the second point 
    * @param y2 The y coordinate of the second point 
    * @return The squared distance between the two points 
    */ 
    private int squareDist(int x1, int y1, int x2, int y2) 
    { 
     int dx = x1 - x2; 
     int dy = y1 - y2; 
     return dx * dx + dy * dy; 
    } 

    /** 
    * Process the image into a distance field. 
    * 
    * The input image should be binary (black/white), but if not, see {@link #isInside(int)}. 
    * 
    * The returned image is a factor of {@code upscale} smaller than {@code inImage}. 
    * Opaque pixels more than {@link #spread} away in the output image from white remain opaque; 
    * transparent pixels more than {@link #spread} away in the output image from black remain transparent. 
    * In between, we get a smooth transition from opaque to transparent, with an alpha value of 0.5 
    * when we are exactly on the edge. 
    * 
    * @param inImage the image to process. 
    * @return the distance field image 
    */ 
    public Bitmap GenerateDistanceField(Bitmap inImage) 
    { 
     Bitmap outImage = new Bitmap(inImage.Width, inImage.Height, PixelFormat.Format32bppArgb); 

     // Note: coordinates reversed to mimic storage of BufferedImage, for memory locality 
     bool[,] bitmap = new bool[inImage.Height, inImage.Width]; 
     for(int y = 0; y < inImage.Height; ++y) 
     { 
      for(int x = 0; x < inImage.Width; ++x) 
      { 
       bitmap[y, x] = isInside(inImage.GetPixel(x, y).ToArgb()); 
      } 
     } 

     for (int y = 0; y < inImage.Height; ++y) 
     { 
      for (int x = 0; x < inImage.Width; ++x) 
      { 
       float signedDistance = findSignedDistance(x, y, ref bitmap); 
       outImage.SetPixel(x, y, Color.FromArgb(distanceToRGB(signedDistance))); 
      } 
     } 

     return outImage; 
    } 

    public Bitmap ConvertToGrayScale(Bitmap oldbmp) 
    { 
     using (var ms = new MemoryStream()) 
     { 
      oldbmp.Save(ms, ImageFormat.Gif); 
      ms.Position = 0; 
      return (Bitmap)Image.FromStream(ms); 
     } 
    } 

    /** 
    * Returns {@code true} if the color is considered as the "inside" of the image, 
    * {@code false} if considered "outside". 
    * 
    * <p> Any color with one of its color channels at least 128 
    * <em>and</em> its alpha channel at least 128 is considered "inside". 
    */ 
    private bool isInside(int rgb) 
    { 
     return (rgb & 0x808080) != 0 && (rgb & 0x80000000) != 0; 
    } 


    /** 
    * Returns the signed distance for a given point. 
    * 
    * For points "inside", this is the distance to the closest "outside" pixel. 
    * For points "outside", this is the <em>negative</em> distance to the closest "inside" pixel. 
    * If no pixel of different color is found within a radius of {@code spread}, returns 
    * the {@code -spread} or {@code spread}, respectively. 
    * 
    * @param centerX the x coordinate of the center point 
    * @param centerY the y coordinate of the center point 
    * @param bitmap the array representation of an image, {@code true} representing "inside" 
    * @return the signed distance 
    */ 
    private float findSignedDistance(int centerX, int centerY, ref bool[,] bitmap) 
    { 
     int width = bitmap.GetLength(1); 
     int height = bitmap.GetLength(0); 
     bool baseVal = bitmap[centerY, centerX]; 

     int delta = this.Spread; 
     int startX = Math.Max(0, centerX - delta); 
     int endX = Math.Min(width - 1, centerX + delta); 
     int startY = Math.Max(0, centerY - delta); 
     int endY = Math.Min(height - 1, centerY + delta); 

     int closestSquareDist = delta * delta; 

     for (int y = startY; y <= endY; ++y) 
     { 
      for (int x = startX; x <= endX; ++x) 
      { 
       if (baseVal != bitmap[y, x]) 
       { 
        int sqDist = squareDist(centerX, centerY, x, y); 
        if (sqDist < closestSquareDist) 
        { 
         closestSquareDist = sqDist; 
        } 
       } 
      } 
     } 

     float closestDist = (float)Math.Sqrt(closestSquareDist); 
     return (baseVal ? 1 : -1) * Math.Min(closestDist, this.Spread); 
    } 

    /** 
    * For a distance as returned by {@link #findSignedDistance}, returns the corresponding "RGB" (really ARGB) color value. 
    * 
    * @param signedDistance the signed distance of a pixel 
    * @return an ARGB color value suitable for {@link BufferedImage#setRGB}. 
    */ 
    private int distanceToRGB(float signedDistance) 
    { 
     float alpha = 0.5f + 0.5f * (signedDistance/this.Spread); 
     alpha = Math.Min(1, Math.Max(0, alpha)); // compensate for rounding errors 
     int alphaByte = (int)(alpha * 0xFF); // no unsigned byte in Java :(
     return (alphaByte << 24) | (this.WhiteColor & 0xFFFFFF); 
    } 
}