fogleman / gg

Go Graphics - 2D rendering in Go with a simple API.
https://godoc.org/github.com/fogleman/gg
MIT License
4.34k stars 352 forks source link

Watermark with "fogleman/gg" #183

Open WillianBR opened 1 year ago

WillianBR commented 1 year ago

Watermark with "fogleman/gg"

I stumbled into "fogleman/gg" yesterday and immediately seen it as a nice way to add watermark into my images. But I couldn't make ir work.

I started using "fogleman/gg/blob/master/examples/rotated-text.go" as boilerplate, but it seeans the dc.Rotate() do it with all the canvas and I couldn't center the text!

If course the idea is draw a diagonal text over the hypotenuse of the canvas. Exactly into the center.

When I draw the texto with 0 degrees rotarion it is on center. But if I only rotate 45 degrees, it's a mess.

Does anybody can help with that?

The intended steps are:

  1. Get the image bounds and compute hypotenuse, and fit the most large text possible without get clipped out of canvas;

  2. Create a canvas with transparent backgroud and draw the rotate (45º) text;

  3. Save the new picture with a another name;

The step 2 is the tricky one at the moment!

@fogleman ?

Canvas rotated at 45 degress:

rotated-text 45-degrees

Canvas at 0 degress:

rotated-text 0-degrees

Draft souce code:

//  https://github.com/fogleman/gg/blob/master/examples/rotated-text.go
//  go build -v -ldflags "-s -w" -o rotated-text.exe rotated-text.go
//  
package main

import (
    "flag"
    "fmt"
    "os"
    "os/exec"
    "path/filepath"

    //
    // External packages:
    //
    "github.com/fogleman/gg"
    "github.com/golang/freetype/truetype"
    "golang.org/x/image/font/gofont/goregular"
)

// Execute a external process and return the process object, error object and full executable path
func Start(args ...string) (p *os.Process, err error, executable string) {
    if args[0], err = exec.LookPath(args[0]); err == nil {
        var procAttr os.ProcAttr
        procAttr.Files = []*os.File{os.Stdin,
            os.Stdout, os.Stderr}
        p, err := os.StartProcess(args[0], args, &procAttr)
        if err == nil {
            return p, nil, args[0]
        }
    }
    return nil, err, args[0]
}

func main() {

    xPtr := flag.Int("x", -150, "Valor de X")
    yPtr := flag.Int("y", 160, "Valor de Y")
    rotatePtr := flag.Int("rotate", -45, "Angulo de rotação em graus")
    fontSizePtr := flag.Int("fontSize", 32, "Tamanho da fonte")
    sizePtr := flag.Int("size", 500, "Tamanho do Canvas (size x size)")
    txtPtr := flag.String("text", "Willian & Simone", "Texto para imprimir")
    viewerPtr := flag.String("viewer", "mspaint.exe", "Visualizador de imagem.")
    imageFilePtr := flag.String("file", "rotated-text.go.png", "Arquivo para salvar a imagem")
    bWaitViewer := flag.Bool("WaitViewer", false, "Wait the viewer to finish")
    flag.Parse()

    var S = *sizePtr
    dc := gg.NewContext(S, S)
    dc.SetRGB(1, 1, 1)
    dc.Clear()
    dc.SetRGB(0, 0, 0)
    font, err := truetype.Parse(goregular.TTF)
    if err != nil {
        panic("truetype.Parse()")
    }
    face := truetype.NewFace(font, &truetype.Options{
        Size: float64(*fontSizePtr),
    })
    dc.SetFontFace(face)

    dc.Rotate(gg.Radians(float64(*rotatePtr))) // Left to Right and Up

    var tx, ty float64
    tx, ty = float64(*xPtr), float64(*yPtr)
    text := *txtPtr
    w, h := dc.MeasureString(text)
    fmt.Printf("DEBUG> Text width is %v and height is %v.\n", w, h)

    dc.DrawRectangle(0, 0, float64(S), float64(S))

    dc.SetRGB(0, 0, 0)
    dc.DrawRectangle(tx, ty, w, h)
    dc.Stroke()
    dc.DrawString(text, tx, ty-4+h)

    dc.SavePNG(*imageFilePtr)

    if len(*viewerPtr) > 0 {
        process, err, executable := Start(*viewerPtr, *imageFilePtr)
        if err != nil {
            fmt.Printf("ERROR Unable to run \"%s %s\": %s\n", *viewerPtr, *imageFilePtr, err.Error())
        } else {
            imageFullPath, _ := filepath.Abs(*imageFilePtr)
            fmt.Printf("Running as pid %d with \"%s %s\"\n", process.Pid, executable, imageFullPath)
            if *bWaitViewer {
                process.Wait()
            }
        }
    }
}

/*
rotated-text.exe -rotate 0 -text "Willian & Simone" -size 500 -x 126 -y 234
    Text width is 247 and height is 32.
    x = 500 - 247 / 2
    y = 500 -  32 / 2

rotated-text.exe -rotate -45 -text "Willian & Simone" -size 500 -x 126 -y 234
*/
splace commented 1 year ago

you need to translate to the centre of the rotation

(below works, i think, but has changes other than just bug fix.)

package main

import (
    "flag"
    //"fmt"
    "github.com/fogleman/gg"
    "github.com/golang/freetype/truetype"
    "golang.org/x/image/font/gofont/goregular"
)

func main() {

    xPtr := flag.Int("x", 250, "Valor de X")
    yPtr := flag.Int("y", 250, "Valor de Y")
    rotatePtr := flag.Int("rotate", -45, "Angulo de rotação em graus")
    fontSizePtr := flag.Int("fontSize", 32, "Tamanho da fonte")
    sizePtr := flag.Int("size", 500, "Tamanho do Canvas (size x size)")
    txtPtr := flag.String("text", "Willian & Simone", "Texto para imprimir")
    imageFilePtr := flag.String("file", "rotated-text.go.png", "Arquivo para salvar a imagem")
    flag.Parse()

    dc := gg.NewContext(*sizePtr, *sizePtr)
    dc.SetRGB(1, 1, 1)
    dc.Clear()

    font, err := truetype.Parse(goregular.TTF)
    if err != nil {
        panic("truetype.Parse()")
    }
    face := truetype.NewFace(font, &truetype.Options{
        Size: float64(*fontSizePtr),
    })
    dc.SetFontFace(face)

    dc.SetRGB(0, 0, 0)
       // border inset 10px
    dc.DrawRectangle(10, 10, float64(*sizePtr)-20, float64(*sizePtr)-20)
    dc.Stroke()
    dc.Translate(float64(*xPtr), float64(*yPtr))
    dc.Rotate(gg.Radians(float64(*rotatePtr))) // Left to Right and Up
        // does its own centring 
    dc.DrawStringAnchored(*txtPtr, 0, 0,.5,.5)

    w, h := dc.MeasureString(*txtPtr)
       // added border
    dc.DrawRectangle(-w/2-10,-h/2, w+20, h+10)
    dc.Stroke()

    dc.SavePNG(*imageFilePtr)

}
splace commented 1 year ago

also use alpha color... dc.SetRGBA(1, 1, 1,100) ... for a transparent watermark

WillianBR commented 1 year ago

I'll check it later, after I get home!

Thank you!

WillianBR commented 1 year ago

Hi @splace,

I tested it and work!

I looks like a missed the detail in the documentation about the function DrawStringAnchored().

I started some test and now I'll look for a function group to save the canvas status (colors, etc.) and restore it after.

And ofcourse, add the code to read a existing image and apply the watermark at it center without compromised the original image & text.