dpntq48842 2019-08-23 08:32
浏览 157
已采纳

使用“浮点数”绘制图像

Currently, I'm trying to transform an existing C# project to GoLang. The project takes an XML file which contains a bunch of coordinates and draws them on an image.

In C# the code to draw a rectangle on an image is the following:

public void DrawRectangle(Graphics graphics, RectangleShape rectangle)
{
    using (var drawingPen = new Pen(Color.Black))
    {
        graphics.DrawRectangle(
            drawingPen,
            rectangle.StartX,
            rectangle.StartY,
            rectangle.Width,
            rectangle.Height);
    }
}

A rectangle is defined by the following class:

internal sealed class RectangleShape
{
    internal RectangleShape(float startX, float startY, float width, float height)
    {
        this.StartX = startX;
        this.StartY = startY;
        this.Width = width;
        this.Height = height;
    }

    internal float StartX { get; }

    internal float StartY { get; }

    internal float Width { get; }

    internal float Height { get; }
}

It means that C# is able to draw a rectangle on an image using coordinates that are defined as float.

Now, I'm trying to transform the code to GoLang where I'm drawing a rectangle with the following code:

// DrawRect draws a rectangle with the given dimensions on the given image.
func DrawRect(img *image.RGBA, rect Rectangle) {
    endX := rect.X + rect.Width
    endY := rect.Y + rect.Height

    drawHLine(img, rect.X, rect.Y, endX)
    drawHLine(img, rect.Y, endY, endX)
    drawVLine(img, rect.Y, rect.X, endY)
    drawVLine(img, rect.Y, endX, endY)
}

// PRIVATE: drawHLine draws a horizontal line with the given coordinates on the given image.
func drawHLine(img *image.RGBA, startX, y, endX float32) {
    col := color.RGBA{0x00, 0x00, 0x00, 0xff}

    for ; startX <= endX; startX++ {
        img.Set(startX, y, col)
    }
}

// PRIVATE: drawVLine draws a vertical line with the given coordinates on the given image.
func drawVLine(img *image.RGBA, startY, x, endY float32) {
    col := color.RGBA{0x00, 0x00, 0x00, 0xff}

    for ; startY <= endY; startY++ {
        img.Set(x, startY, col)
    }
}

The rectangle is defined with the following struct:

// Rectangle represents a rectangular shape.
type Rectangle struct {
    X      float32
    Y      float32
    Width  float32
    Height float32
}

The sample in Go does not work because the Set function on an image has the following structure:

func (p *RGBA) Set(x, y int, c color.Color) {

Is there any way how Go can work with float parameters to draw a rectangle on an image?

  • 写回答

1条回答 默认 最新

  • dprh34164 2019-08-23 08:44
    关注

    The image.Image type is an interface which defines an image to have pixels with integer coordinates, accessible via the Image.At() method:

    At(x, y int) color.Color
    

    The concrete implementation you're using image.RGBA allows changing the pixels, again, using integer coordinates with the RGBA.Set() method:

    func (p *RGBA) Set(x, y int, c color.Color)
    

    The simplest solution is to convert the float coordinates to integer coordinates. Simply converting floating point to integer is truncation, so rather you should use rounding. A simple rounding is adding 0.5 before the conversion. For details, see Golang Round to Nearest 0.05.

    And best is to do it on the "start" coordinates, so the loop can work with integer values, e.g.:

    func drawHLine(img *image.RGBA, startX, y, endX float32) {
        col := color.RGBA{0x00, 0x00, 0x00, 0xff}
    
        x1, x2 := int(startX + 0.5), int(endX + 0.5)
        y1 := int(y + 0.5)
        for x := x1; x <= x2; x++ {
            img.Set(x, y1, col)
        }
    }
    

    Do note that however this "simplest" solution to convert float coordinates to integer might not be the "best". For example if you need to draw a horizontal line at the x = 0.1 coordinate, the above solution will draw a line at x = 0. A different solution may be to draw a "stronger" line at x = 0, and a "weeker" line at x = 1, giving the effect that the line is in fact at x = 0.1 if viewed from a distance. This aliasing technique gives certainly better results if lines are not horizontal and vertical, but requires more computation (and thus are slower).

    If you do need floating precision at drawing, you may use 3rd party libs like github.com/fogleman/gg which allows you to pass float64 coordiates, and it uses antialiasing to achieve nice results.

    For example drawing a rectangle with github.com/fogleman/gg onto an image.RGBA image is like this:

    var img *image.RGBA = // create your RGBA image
    
    ctx := gg.NewContextForRGBA(img)
    
    // Set drawing color:
    ctx.SetColor(color.RGBA{0x00, 0x00, 0x00, 0xff})
    // Or simply: ctx.SetRGB(0, 0, 0)
    
    // Draw rectangle: x, y, w, h are all float64 parameters
    ctx.DrawRectangle(x, y, w, h)
    

    Also see related: Draw a rectangle in Golang?

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 各位请问平行检验趋势图这样要怎么调整?说标准差差异太大了
  • ¥15 delphi webbrowser组件网页下拉菜单自动选择问题
  • ¥15 wpf界面一直接收PLC给过来的信号,导致UI界面操作起来会卡顿
  • ¥15 init i2c:2 freq:100000[MAIXPY]: find ov2640[MAIXPY]: find ov sensor是main文件哪里有问题吗
  • ¥15 运动想象脑电信号数据集.vhdr
  • ¥15 三因素重复测量数据R语句编写,不存在交互作用
  • ¥15 微信会员卡等级和折扣规则
  • ¥15 微信公众平台自制会员卡可以通过收款码收款码收款进行自动积分吗
  • ¥15 随身WiFi网络灯亮但是没有网络,如何解决?
  • ¥15 gdf格式的脑电数据如何处理matlab