vmathieu
vmathieu

Reputation: 220

Same algorithm, multiple input and output type possibility in Go?

I'm currently using draw2d lib to render some image. I noticed the core algorithm and method are the same for building SVG, or PNG images.

I do need to render this images as SVG (for web use) and PNG (for PDF use)

The only difference is at the entry type and output.

for PNG rendering I've

as input:

var gc *draw2dimg.GraphicContext
var img *image.RGBA

img = image.NewRGBA(image.Rect(0, 0, xSize, ySize))
gc = draw2dimg.NewGraphicContext(img)

as output:

draw2dimg.SaveToPngFile(FileName, img)

and for SVG I've:

as input:

var gc *draw2dsvg.GraphicContext
var img *draw2dsvg.Svg

img = draw2dsvg.NewSvg()
gc = draw2dsvg.NewGraphicContext(img)

as output:

draw2dsvg.SaveToSvgFile(FileName, img)

between input and output I've the same implementation.

Is there any way in Go to use different input type and get the same implementation without have to duplicate some code?

Upvotes: 0

Views: 155

Answers (2)

putu
putu

Reputation: 6454

As I mentioned in the comment, try to refactor your code by moving the core algorithm part into a function or may be to a different package. To illustrate the idea, bellow is the refactored version of example in https://github.com/llgcode/draw2d README.

package main

import (
    "image"
    "image/color"

    "github.com/llgcode/draw2d"
    "github.com/llgcode/draw2d/draw2dimg"
    "github.com/llgcode/draw2d/draw2dpdf"
    "github.com/llgcode/draw2d/draw2dsvg"
)

func coreDraw(gc draw2d.GraphicContext) {
    // Set some properties
    gc.SetFillColor(color.RGBA{0x44, 0xff, 0x44, 0xff})
    gc.SetStrokeColor(color.RGBA{0x44, 0x44, 0x44, 0xff})
    gc.SetLineWidth(5)

    // Draw a closed shape
    gc.BeginPath()    // Initialize a new path
    gc.MoveTo(10, 10) // Move to a position to start the new path
    gc.LineTo(100, 50)
    gc.QuadCurveTo(100, 10, 10, 10)
    gc.Close()
    gc.FillStroke()
}

func main() {
    format := "svg"

    switch format {
    case "png":
        dest := image.NewRGBA(image.Rect(0, 0, 297, 210.0))
        gc := draw2dimg.NewGraphicContext(dest)
        coreDraw(gc)
        draw2dimg.SaveToPngFile("hello.png", dest)
    case "pdf":
        dest := draw2dpdf.NewPdf("L", "mm", "A4")
        gc := draw2dpdf.NewGraphicContext(dest)
        coreDraw(gc)
        draw2dpdf.SaveToPdfFile("hello.pdf", dest)
    case "svg":
        img := draw2dsvg.NewSvg()
        gc := draw2dsvg.NewGraphicContext(img)
        coreDraw(gc)
        draw2dsvg.SaveToSvgFile("hello.svg", img)
    }
}

Upvotes: 1

dtolnay
dtolnay

Reputation: 11033

The typical way to achieve code sharing in Go would be through the use of interfaces. See if you can break down the steps of your image algorithm into interface methods. Then those methods can be driven in a generic way by a single piece of code that works across all image formats, as in the runAlgorithm function below.

package main

import (
    "image"

    "github.com/llgcode/draw2d/draw2dimg"
    "github.com/llgcode/draw2d/draw2dsvg"
)

// Common algorithm that is implemented for various image formats.
type ImageAlgorithm interface {
    CreateImage(xSize, ySize int)
    CreateGraphicsContext()
    SaveImage(fileName string)
}

type RGBAAlgorithm struct {
    gc  *draw2dimg.GraphicContext
    img *image.RGBA
}

func (alg *RGBAAlgorithm) CreateImage(xSize, ySize int) {
    alg.img = image.NewRGBA(image.Rect(0, 0, xSize, ySize))
}

func (alg *RGBAAlgorithm) CreateGraphicsContext() {
    alg.gc = draw2dimg.NewGraphicContext(alg.img)
}

func (alg *RGBAAlgorithm) SaveImage(fileName string) {
    draw2dimg.SaveToPngFile(fileName, alg.img)
}

type SvgAlgorithm struct {
    gc  *draw2dsvg.GraphicContext
    img *draw2dsvg.Svg
}

func (alg *SvgAlgorithm) CreateImage(xSize, ySize int) {
    alg.img = draw2dsvg.NewSvg()
}

func (alg *SvgAlgorithm) CreateGraphicsContext() {
    alg.gc = draw2dsvg.NewGraphicContext(alg.img)
}

func (alg *SvgAlgorithm) SaveImage(fileName string) {
    draw2dsvg.SaveToSvgFile(fileName, alg.img)
}

func runAlgorithm(alg ImageAlgorithm) {
    // input
    alg.CreateImage(100, 100)
    alg.CreateGraphicsContext()

    // output
    alg.SaveImage("out")
}

func main() {
    runAlgorithm(&RGBAAlgorithm{})
    runAlgorithm(&SvgAlgorithm{})
}

Upvotes: 0

Related Questions