How to set specific height for text in golang? (SDL2 TTF) - go

So, I finally understood how to set text to screen using SDL2 ttf, but I also discovered, that text covers the entire screen, while I need text to be smaller, but I haven't found anything here https://pkg.go.dev/github.com/veandco/go-sdl2/ttf
Is there a way to make text smaller?
Here's my code:
func WriteChoices() {
font, _ := ttf.OpenFont("arial.ttf", 1920)
font.SetOutline(21)
surface, _ := font.RenderUTF8Solid("Player Computer", sdl.Color{0, 0, 50, 255})
texture, _ := renderer.CreateTextureFromSurface(surface)
renderer.Copy(texture, nil, nil)
font.Close()
surface.Free()
texture.Destroy()
}

You probably can just reduce font size by loading the font like so.
func WriteChoices() {
font, _ := ttf.OpenFont("arial.ttf", 20)
font.SetOutline(21)
surface, _ := font.RenderUTF8Solid("Player Computer", sdl.Color{0, 0, 50, 255})
texture, _ := renderer.CreateTextureFromSurface(surface)
renderer.Copy(texture, nil, sdl.{W:surface.W,H:surface.H})
font.Close()
surface.Free()
texture.Destroy()
}
This may work but i cant test right now, Sorry if i just wasted your time

Related

Publishing changes to window with shiny driver

I need to show a sequence of image.RGBA frames generated by a simulation I'm running, and I'm trying to use shiny to do so.
While getting familiar with the package, I tried running a simple example, very similar to one of those provided, but I do not understand the logic behind Window.Publish, and when the changes are shown to the screen.
According to the docs of Upload and Fill,
When filling a Window, there will not be any visible effect until
Publish is called.
However, when I call w.Publish after Uploading a buffer or Filling the window, I see only a black window.
Strangely, if I use the same lines to upload or fill the window inside the event loop, the window is updated: I see the background color changing when moving the mouse and the square is drawn when resizing the window, even if I do not call w.Publish inside the event loop.
// create a Window
winSize := image.Point{900, 600}
w, err := s.NewWindow(&screen.NewWindowOptions{
Width: winSize.X,
Height: winSize.Y,
Title: "Viewer",
})
if err != nil {
log.Fatalf("s.NewWindow: %+v", err)
}
defer w.Release()
// create a Buffer
size0 := image.Point{30, 30}
b, err := s.NewBuffer(size0)
if err != nil {
log.Fatalf("s.NewBuffer: %+v", err)
}
defer b.Release()
// fill the buffer with a color
white := color.RGBA{255, 255, 255, 255}
draw.Draw(b.RGBA(), b.RGBA().Bounds(), &image.Uniform{white}, image.Point{0, 0}, draw.Src)
// draw the buffer on the window
w.Upload(image.Point{40, 40}, b, b.Bounds())
// publish the changes
w.Publish()
// at this point I see a black window
var sz size.Event
for {
e := w.NextEvent()
switch e := e.(type) {
default:
case key.Event:
if e.Code == key.CodeEscape {
return
}
case mouse.Event:
// this works, the background changes and the square is drawn
v := uint8(rand.Intn(255))
w.Fill(image.Rectangle{image.Point{}, winSize}, color.RGBA{v, v, v, 255}, draw.Src)
w.Upload(image.Point{30, 30}, b, b.Bounds())
case size.Event:
// this works, the background changes and the square is drawn
sz = e
w.Fill(sz.Bounds(), color.RGBA{80, 80, 80, 255}, draw.Src)
w.Upload(image.Point{30, 30}, b, b.Bounds())
case paint.Event:
log.Printf("paint.Event: %T %+v", e, e)
}
}
Why is the window not updated when calling publish?
Why is the window updated inside the loop, even without calling publish?
Why does a size event generate a paint event? That log line I left is printed after a size event, but not a mouse event.
The same thing happens if a use a texture, uploading the buffer to the texture and then copying the texture to the window.
I only need a minimal UI with fast drawing and basic event handling and this package seemed pretty straightforward to use, but if there are other easy options I'm ok with that.
Edit:
As I am running this on Ubuntu 20.04 I believe the driver used is X11, so I looked into that implementation of
Publish and there is a comment which might explain why the call to publish is not needed to actually draw, but is useful for synchronization:
func (w *windowImpl) Publish() screen.PublishResult {
// This sync isn't needed to flush the outgoing X11 requests. Instead, it
// acts as a form of flow control. Outgoing requests can be quite small on
// the wire, e.g. draw this texture ID (an integer) to this rectangle (four
// more integers), but much more expensive on the server (blending a
// million source and destination pixels). Without this sync, the Go X11
// client could easily end up sending work at a faster rate than the X11
// server can serve.
w.s.xc.Sync()
return screen.PublishResult{}
}
But I still do not understand why drawing outside the loop does not work even with the explicit publish call.
Cheers!

Golang Iris Web - Serve 1x1 pixel

I'm new to Golang and trying to use a framework called iris.
My problem is how to serve 1x1 gif pixel, not using c.HTML context, but the way the browser title becomes 1x1 image gif. The image will be used for tracking.
Any help will be deeply appreciated.
Attention: I'm not really familiar with iris so the solution maybe not idiomatic.
package main
import (
"github.com/kataras/iris"
"image"
"image/color"
"image/gif"
)
func main() {
iris.Get("/", func(ctx *iris.Context) {
img := image.NewRGBA(image.Rect(0, 0, 1, 1)) //We create a new image of size 1x1 pixels
img.Set(0, 0, color.RGBA{255, 0, 0, 255}) //set the first and only pixel to some color, in this case red
err := gif.Encode(ctx.Response.BodyWriter(), img, nil) //encode the rgba image to gif, using gif.encode and write it to the response
if err != nil {
panic(err) //if we encounter some problems panic
}
ctx.SetContentType("image/gif") //set the content type so the browser can identify that our response is actually an gif image
})
iris.Listen(":8080")
}
Links for better understanding:
https://golang.org/pkg/image/
https://golang.org/pkg/image/gif
https://golang.org/pkg/image/color
https://golang.org/pkg/image/
https://godoc.org/github.com/valyala/fasthttp#Response

Best way to remove borders / crop image

I am using a Go package (Go binding to ImageMagick's MagickWand C API) to ImageMagick where I'm removing borders from images (cropping). The way I am using the trim function can be found below.
Now the problem is the fuzzy factor. For example, if I set the value to 2000, the image (here is the source) still has some white images like these:
fuzz factor value 2000 --> result
fuzz factor value 10000 --> result
I have created a small html which illustrates the problem best. It contains both images: https://dl.dropboxusercontent.com/u/15684927/image-trim-problem.html
As you can see the source has some pixels on the bottom right corner which are causing the trouble. If I set the factor to 10000, I'm afraid that I will loose pixels on other pictures. If I set it on 2000, the trimming isn't done right in pictures like these.
So my actual question is: what is the best way to "crop" / "trim" images?
package main
import "gopkg.in/gographics/imagick.v1/imagick"
func main() {
imagick.Initialize()
defer imagick.Terminate()
inputFile := "tshirt-original.jpg"
outputFile := "trimmed.jpg"
mw := imagick.NewMagickWand()
// Schedule cleanup
defer mw.Destroy()
// read image
err := mw.ReadImage(inputFile)
if err != nil {
panic(err)
}
// first trim original image
// fuzz: by default target must match a particular pixel color exactly.
// However, in many cases two colors may differ by a small amount. The fuzz
// member of image defines how much tolerance is acceptable to consider two
// colors as the same. For example, set fuzz to 10 and the color red at
// intensities of 100 and 102 respectively are now interpreted as the same
// color for the purposes of the floodfill.
mw.TrimImage(10000)
// Set the compression quality to 95 (high quality = low compression)
err = mw.SetImageCompressionQuality(95)
if err != nil {
panic(err)
}
// save
err = mw.WriteImage(outputFile)
if err != nil {
panic(err)
}
}
Basically, your problem is that you have a high-frequency, high-amplitude artifact at the edge of your image. Or, put differently, a sharp, high peak at the edge, which, if you want to use trim, forces you to use such a high a fuzz-value to overcome this, that the algorithm also considers the 'actual content' as equal to the 'background' (border).
One solution here is to use a multi-step approach, whereby you first smooth out the edge artifacts and then apply trim to the resulting image. By smoothing it out, you get rid of the high peak and smear it out into a nice rolling hill. Rolling hills, in turn, can be easily trimmed with low fuzz values. This then provides you with the desired geometry which you can use to crop the original.
Specifically, let's take the original image:
Now, let's smooth out that ridge on the edge using a blur with a radius of 10 and a sigma of 10 through convert original.jpg -blur 10x10 10x10.jpg, which yields:
Now, you might notice that the artifacts on the edge have now pretty much disappeared.
We can now do a 'virtual' trim and ask ImageMagick what the result of the trim would be through convert 10x10.jpg -fuzz 2000 -format %# info:, which, according to the documentation gives you the "trim bounding box (without actually trimming)": 1326x1560+357+578%
Taking these values (except for the percentage sign) and using them for crop geometry, gives you the convert with crop command convert original.jpg -crop 1326x1560+357+578 cropped.jpg, which gives you:
Edit:
Now, since you want this as code, using imagick, here's the solution in code. It assumes you have the file stored as './data/original.jpg' and will store it as './data/trimmed.jpg'
package main
import (
"fmt"
"gopkg.in/gographics/imagick.v2/imagick"
)
func init() {
imagick.Initialize()
}
const originalImageFilename = "data/original.jpg"
func main() {
mw := imagick.NewMagickWand()
err := mw.ReadImage(originalImageFilename)
if err != nil {
fmt.Sprint(err.Error())
return
}
// Use a clone to determine what will happen
mw2 := mw.Clone()
mw2.BlurImage(10, 10)
mw2.TrimImage(2000)
_, _, xOffset, yOffset, err := mw2.GetImagePage()
if err != nil {
fmt.Sprint(err.Error())
return
}
trimmedWidth := mw2.GetImageWidth()
trimmedHeight := mw2.GetImageHeight()
mw2.Destroy()
mw.CropImage(trimmedWidth, trimmedHeight, xOffset, yOffset)
mw.WriteImage("data/trimmed.jpg")
mw.Destroy()
}

How to convert image.RGBA (image.Image) to image.Paletted?

I'm trying to create an animated GIF from a series of arbitrary non-paletted images. In order to create a paletted image, I need to come up with a palette somehow.
// RGBA, etc. images from somewhere else
var frames []image.Image
outGif := &gif.GIF{}
for _, simage := range frames {
// TODO: Convert image to paletted image
// bounds := simage.Bounds()
// palettedImage := image.NewPaletted(bounds, ...)
// Add new frame to animated GIF
outGif.Image = append(outGif.Image, palettedImage)
outGif.Delay = append(outGif.Delay, 0)
}
gif.EncodeAll(w, outGif)
Is there an easy way in golang stdlib to accomplish this?
It seems an automatic way of intelligently generating palettes is missing from the golang stdlib (correct me if I'm wrong here). But there seems to be a stub for providing your own Quantizer, which led me to the gogif project. (Which was the apparent source of image.Gif.)
I was able to borrow the MedianCutQuantizer from that project, defined here:
https://github.com/andybons/gogif/blob/master/mediancut.go
Which results in the following:
var subimages []image.Image // RGBA, etc. images from somewhere else
outGif := &gif.GIF{}
for _, simage := range subimages {
bounds := simage.Bounds()
palettedImage := image.NewPaletted(bounds, nil)
quantizer := gogif.MedianCutQuantizer{NumColor: 64}
quantizer.Quantize(palettedImage, bounds, simage, image.ZP)
// Add new frame to animated GIF
outGif.Image = append(outGif.Image, palettedImage)
outGif.Delay = append(outGif.Delay, 0)
}
gif.EncodeAll(w, outGif)
Instead of generating your own palette, you can also use on of the predefined (https://golang.org/pkg/image/color/palette/)
...
palettedImage := image.NewPaletted(bounds, palette.Plan9)
draw.Draw(palettedImage, palettedImage.Rect, simage, bounds.Min, draw.Over)
...

Using the Set() method of an image.Image or *image.RGBA

I am doing some practice with the Go image package with my free time this summer.
package main
import (
"os"
"image"
"image/png"
"image/color"
"log"
"fmt"
"reflect"
)
func main(){
file , err := os.OpenFile("C:/Sources/go3x3.png", os.O_RDWR, os.FileMode(0777))
if err != nil {
log.Fatal(err)
}
img , err := png.Decode(file)
if err != nil {
log.Fatal(err)
}
img.At(0,0).RGBA()
fmt.Println("type:", reflect.TypeOf(img))
m := image.NewRGBA(image.Rect(0, 0, 640, 480))
fmt.Println("type:", reflect.TypeOf(m))
m.Set(5, 5, color.RGBA{255, 0, 0, 255})
img.Set(0, 0, color.RGBA{136, 0, 21, 255})
}
The problem here is when I run it with the img.Set commented out I get this result
type: *image.RGBA
type: *image.RGBA
but when it's uncommented I get an error saying
img.Set undefined (type image.Image has no field or method Set)
I'm assuming I'm using reflect wrong, I'm still fully grasping the whole interface and type definitions in Go.
To expand on
a previous answer
answer:
png.Decode may create one of several different underlying image types (*image.Gray, *image.RGBA, *image.Paletted, *image.NRGBA, etc).
It returns whatever image it created as an image.Image interface which provides read only access to the data.
However, all (most?) of the actual image types it returns do implement the Set method for simple write access.
The way you can safely test for and use this method is via the existing draw.Image interface from the image/draw package. It's just this:
// From image/draw:
// Image is an image.Image with a Set method to change a single pixel.
type Image interface {
image.Image
Set(x, y int, c color.Color)
}
So you could do something like:
func drawablePNGImage(r io.Reader) (draw.Image, error) {
img, err := png.Decode(r)
if err != nil {
return nil, err
}
dimg, ok := img.(draw.Image)
if !ok {
return nil, fmt.Errorf("%T is not a drawable image type", img)
}
return dimg, nil
}
Playground (shows an example calling all the image.Image methods as well as Set).
Edit for Go1.17+:
Note that Go1.17 added draw.RGBA64Image with a SetRGBA64 method. As with draw.Image, all of the standard image types implement this. The advantage of this method is that the color values are not boxed in the color.Color interface type so doing many pixel operations can be faster.
Also note that Go1.18 added optimisations to the draw.Draw and draw.DrawMask fallback implementations for images that implement the optional draw.RGBA64Image and image.RGBA64Image interfaces.
reflect.TypeOf(img) gives you the reflection type of the value in the interface img, if its an interface. In this case, img is an interface, an image.Image which contains an *image.RGBA.
You can fix your code by converting img to an *image.RGBA, or more robustly, define an interface type with the right Set method, and convert img to that (the draw.Image interface in "image/draw" works perfectly for this, as noted by #DaveC). The interface type is preferable if you aren't sure that png.Decode will always give you an *image.RGBA for the .png files you have.
img.(*image.RGBA).Set(0, 0, color.RGBA{136, 0, 21, 255})
or
type Setter interface {
Set(x, y int, c color.Color)
}
img.(Setter).Set(0, 0, color.RGBA{136, 0, 21, 255})
or (probably best):
import "image/draw"
...
img.(draw.Image).Set(0, 0, color.RGBA{136, 0, 21, 255})
The compiler is correct, the image.Image type is an interface that does not include the Set() function.
I am not an expert at the image library but my cursory look at the types seems to suggest you can take an Image type and use the Bounds() method to get a image.Rectangle to create a new RGBA type as done previously in your example code.
// Your current image manipulation
m := image.NewRGBA(image.Rect(0, 0, 640, 480))
fmt.Println("type:", reflect.TypeOf(m))
m.Set(5, 5, color.RGBA{255, 0, 0, 255})
// You can create a image.RGBA type by passing the image.Rectangle
// returned from image.Image.Bounds()
m = image.NewRGBA(img.Bounds())
m.Set(0, 0, color.RGBA{136, 0, 21, 255})
This is a strict answer to your type issue but I don't have any gurantee it accomplishes your end goals.

Resources