~eliasnaur/gio

5 2

Display subimage

András Belicza
Details
Message ID
<CAP-HBqk66Aa4M-eL9M3fmJx=v8SAFw57oAs3sATeKof7soOWGw@mail.gmail.com>
DKIM signature
missing
Download raw message
I've created a paint.ImageOp of a large image. I would like to display
a subimage of this without having to create a new ImageOp (which would
discard the cached image).

I thought a transform and clip sequence would do it, but I only have
partial success with it.

Let's say I want to display a (x1, y1, x2,y2) subimage at (x, y)
coordinates. I believe this should do it:

op.TransformOp{}.Offset(f32.Point{X: -x1 + x, Y: -y1 + y}).Add(gtx.Ops)
clip.Rect{Rect: f32.Rectangle{
    Min: f32.Point{X: x1, Y: y1},
    Max: f32.Point{X: x2, Y: y2},
}}.Op(gtx.Ops).Add(gtx.Ops)
// now display image as usual

But somehow the image that gets displayed has smaller width than
x2-x1. If I go on and resize the window with mouse (enlarge),
the image's width increases, and at some point it becomes x2-x1,
exactly what I want.

Is there another clipping in the background (e.g. window size or
anything) that prevents the above transformations to not have the
desired effect?
Note: x2-x1 is smaller than the window's width.

Thanks,
Andras
Details
Message ID
<C0KCZENNAOHK.R9QQ68ZLCO5Q@testmac>
In-Reply-To
<CAP-HBqk66Aa4M-eL9M3fmJx=v8SAFw57oAs3sATeKof7soOWGw@mail.gmail.com> (view parent)
DKIM signature
missing
Download raw message
On Wed Feb 12, 2020 at 6:19 PM, András Belicza wrote:
> I've created a paint.ImageOp of a large image. I would like to display
> a subimage of this without having to create a new ImageOp (which would
> discard the cached image).
>
> 
> I thought a transform and clip sequence would do it, but I only have
> partial success with it.
>
> 
> Let's say I want to display a (x1, y1, x2,y2) subimage at (x, y)
> coordinates. I believe this should do it:
>
> 
> op.TransformOp{}.Offset(f32.Point{X: -x1 + x, Y: -y1 + y}).Add(gtx.Ops)
> clip.Rect{Rect: f32.Rectangle{
> Min: f32.Point{X: x1, Y: y1},
> Max: f32.Point{X: x2, Y: y2},
> }}.Op(gtx.Ops).Add(gtx.Ops)
> // now display image as usual
>
> 
> But somehow the image that gets displayed has smaller width than
> x2-x1. If I go on and resize the window with mouse (enlarge),
> the image's width increases, and at some point it becomes x2-x1,
> exactly what I want.
>
> 
> Is there another clipping in the background (e.g. window size or
> anything) that prevents the above transformations to not have the
> desired effect?
> Note: x2-x1 is smaller than the window's width.
>

Do you have a complete example so I can take a look? How does your
PaintOp look like? Are you drawing anything else that may affect the
clipping? Note that clip areas intersect, and you'll need a StackOp
to undo the effect of previous clips.

-- elias
András Belicza
Details
Message ID
<CAP-HBqmVbK-zSP704VvKRhNGKcxo3Dtz_D0yh3vMYaJkE2X02g@mail.gmail.com>
In-Reply-To
<C0KCZENNAOHK.R9QQ68ZLCO5Q@testmac> (view parent)
DKIM signature
missing
Download raw message
Yes, I draw other widgets too, but I do not set clips prior to this.

And yes, I use StackOp to undo it. But still.

I try to create a minimal example that reproduces the issue.

On Wed, Feb 12, 2020 at 6:26 PM Elias Naur <mail@eliasnaur.com> wrote:
>
> On Wed Feb 12, 2020 at 6:19 PM, András Belicza wrote:
> > I've created a paint.ImageOp of a large image. I would like to display
> > a subimage of this without having to create a new ImageOp (which would
> > discard the cached image).
> >
> >
> > I thought a transform and clip sequence would do it, but I only have
> > partial success with it.
> >
> >
> > Let's say I want to display a (x1, y1, x2,y2) subimage at (x, y)
> > coordinates. I believe this should do it:
> >
> >
> > op.TransformOp{}.Offset(f32.Point{X: -x1 + x, Y: -y1 + y}).Add(gtx.Ops)
> > clip.Rect{Rect: f32.Rectangle{
> > Min: f32.Point{X: x1, Y: y1},
> > Max: f32.Point{X: x2, Y: y2},
> > }}.Op(gtx.Ops).Add(gtx.Ops)
> > // now display image as usual
> >
> >
> > But somehow the image that gets displayed has smaller width than
> > x2-x1. If I go on and resize the window with mouse (enlarge),
> > the image's width increases, and at some point it becomes x2-x1,
> > exactly what I want.
> >
> >
> > Is there another clipping in the background (e.g. window size or
> > anything) that prevents the above transformations to not have the
> > desired effect?
> > Note: x2-x1 is smaller than the window's width.
> >
>
> Do you have a complete example so I can take a look? How does your
> PaintOp look like? Are you drawing anything else that may affect the
> clipping? Note that clip areas intersect, and you'll need a StackOp
> to undo the effect of previous clips.
>
> -- elias
András Belicza
Details
Message ID
<CAP-HBqnwyv3cUTtL6=pMCkRxEVxXBRP_wtyebw1unVFw4T1uyQ@mail.gmail.com>
In-Reply-To
<CAP-HBqmVbK-zSP704VvKRhNGKcxo3Dtz_D0yh3vMYaJkE2X02g@mail.gmail.com> (view parent)
DKIM signature
missing
Download raw message
Ok, here's a small, complete app that reproduces the behavior.

Start the app. Everything seems OK. If you resize the window (increase
its width with mouse), still everything is OK. If you try to shrink
it, then after a certain point, the displayed image starts to shrink
below the expected size.

Here's the source:

package main

import (
"fmt"
"image"
"image/color"

"gioui.org/app"
"gioui.org/f32"
"gioui.org/font/gofont"
"gioui.org/io/system"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/op/clip"
"gioui.org/op/paint"
"gioui.org/unit"
"gioui.org/widget/material"
)

func main() {
go func() {
w := app.NewWindow(app.Title("Gio test"), app.Size(unit.Px(700), unit.Px(700)))
loop(w)
}()

app.Main()
}

func loop(w *app.Window) {
gofont.Register()
th := material.NewTheme()
gtx := layout.NewContext(w.Queue())

img := image.NewRGBA(image.Rect(0, 0, 1000, 1000))
for x := 0; x < 1000; x++ {
for y := 0; y < 1000; y++ {
img.Set(x, y, color.RGBA{R: 50 + uint8(x/5), A: 255})
}
}

iop := paint.NewImageOp(img)

for e := range w.Events() {
switch e := e.(type) {
case system.FrameEvent:
gtx.Reset(e.Config, e.Size)

displayWidth := 400
var x1, y1, x2, y2 float32 = 300, 300, 300 + float32(displayWidth), 700
var x, y float32 = 0, 50

off := f32.Point{
X: -x1 + x + float32(e.Size.X-displayWidth)/2, // kind of like centering
Y: -y1 + y,
}
cl := f32.Rectangle{
Min: f32.Point{X: x1, Y: y1},
Max: f32.Point{X: x2, Y: y2},
}

op.TransformOp{}.Offset(off).Add(gtx.Ops)
clip.Rect{Rect: cl}.Op(gtx.Ops).Add(gtx.Ops)
fmt.Println("offset:", off, "clip:", cl)

img := th.Image(iop)
img.Scale = 1
img.Layout(gtx)

e.Frame(gtx.Ops)
}
}
}

On Wed, Feb 12, 2020 at 6:39 PM András Belicza <iczaaa@gmail.com> wrote:
>
> Yes, I draw other widgets too, but I do not set clips prior to this.
>
> And yes, I use StackOp to undo it. But still.
>
> I try to create a minimal example that reproduces the issue.
>
> On Wed, Feb 12, 2020 at 6:26 PM Elias Naur <mail@eliasnaur.com> wrote:
> >
> > On Wed Feb 12, 2020 at 6:19 PM, András Belicza wrote:
> > > I've created a paint.ImageOp of a large image. I would like to display
> > > a subimage of this without having to create a new ImageOp (which would
> > > discard the cached image).
> > >
> > >
> > > I thought a transform and clip sequence would do it, but I only have
> > > partial success with it.
> > >
> > >
> > > Let's say I want to display a (x1, y1, x2,y2) subimage at (x, y)
> > > coordinates. I believe this should do it:
> > >
> > >
> > > op.TransformOp{}.Offset(f32.Point{X: -x1 + x, Y: -y1 + y}).Add(gtx.Ops)
> > > clip.Rect{Rect: f32.Rectangle{
> > > Min: f32.Point{X: x1, Y: y1},
> > > Max: f32.Point{X: x2, Y: y2},
> > > }}.Op(gtx.Ops).Add(gtx.Ops)
> > > // now display image as usual
> > >
> > >
> > > But somehow the image that gets displayed has smaller width than
> > > x2-x1. If I go on and resize the window with mouse (enlarge),
> > > the image's width increases, and at some point it becomes x2-x1,
> > > exactly what I want.
> > >
> > >
> > > Is there another clipping in the background (e.g. window size or
> > > anything) that prevents the above transformations to not have the
> > > desired effect?
> > > Note: x2-x1 is smaller than the window's width.
> > >
> >
> > Do you have a complete example so I can take a look? How does your
> > PaintOp look like? Are you drawing anything else that may affect the
> > clipping? Note that clip areas intersect, and you'll need a StackOp
> > to undo the effect of previous clips.
> >
> > -- elias
Details
Message ID
<C0L0YBYFWAY1.1G35HOF8FTV84@toolbox>
In-Reply-To
<CAP-HBqnwyv3cUTtL6=pMCkRxEVxXBRP_wtyebw1unVFw4T1uyQ@mail.gmail.com> (view parent)
DKIM signature
missing
Download raw message
On Wed Feb 12, 2020 at 21:10, András Belicza wrote:
> Ok, here's a small, complete app that reproduces the behavior.
>
> Start the app. Everything seems OK. If you resize the window (increase
> its width with mouse), still everything is OK. If you try to shrink
> it, then after a certain point, the displayed image starts to shrink
> below the expected size.
>
> Here's the source:
>
> ...

Thank you. I believe the culprit is that th.Image clips to the gtx.Constraints
bounds. Because you're low-level drawing, you're better off just drawing directly:


	gtx.Reset(e.Config, e.Size)

	displayWidth := 400
	var x1, y1, x2, y2 float32 = 300, 300, 300 + float32(displayWidth), 700
	var x, y float32 = 0, 50

	off := f32.Point{
		X: -x1 + x + float32(e.Size.X-displayWidth)/2, // kind of like centering
		Y: -y1 + y,
	}
	cl := f32.Rectangle{
		Min: f32.Point{X: x1, Y: y1},
		Max: f32.Point{X: x2, Y: y2},
	}

	op.TransformOp{}.Offset(off).Add(gtx.Ops)
	clip.Rect{Rect: cl}.Op(gtx.Ops).Add(gtx.Ops)
	iop.Add(gtx.Ops)
	sz := img.Bounds().Size()
	paint.PaintOp{Rect: f32.Rectangle{Max: f32.Point{X: float32(sz.X), Y: float32(sz.Y)}}}.Add(gtx.Ops)

	fmt.Println("offset:", off, "clip:", cl)

	e.Frame(gtx.Ops)

However, I still think the above is too hard and convoluted. I added a ImageOp.Rect
field so your program reduces to just

	func loop(w *app.Window) {
		gofont.Register()
		th := material.NewTheme()
		gtx := layout.NewContext(w.Queue())

		img := image.NewRGBA(image.Rect(0, 0, 1000, 1000))
		for x := 0; x < 1000; x++ {
			for y := 0; y < 1000; y++ {
				img.Set(x, y, color.RGBA{R: 50 + uint8(x/5), A: 255})
			}
		}

		iop := paint.NewImageOp(img)
		// Subimage.
		iop.Rect = image.Rectangle{
			Min: image.Point{X: 500, Y: 500},
			Max: image.Point{X: 600, Y: 600},
		}

		for e := range w.Events() {
			switch e := e.(type) {
			case system.FrameEvent:
				gtx.Reset(e.Config, e.Size)

				layout.Center.Layout(gtx, func() {
					img := th.Image(iop)
					img.Scale = 1
					img.Layout(gtx)
				})

				e.Frame(gtx.Ops)
			}
		}
	}

-- elias
András Belicza
Details
Message ID
<CAP-HBqnwG=S4Jm7DKTBRwL0z9-+hXxZ+4LskxUgsTi54GFrLWA@mail.gmail.com>
In-Reply-To
<C0L0YBYFWAY1.1G35HOF8FTV84@toolbox> (view parent)
DKIM signature
missing
Download raw message
Awesome. Works like a charm. Thanks for the addition too.

On Thu, Feb 13, 2020 at 1:17 PM Elias Naur <mail@eliasnaur.com> wrote:
>
> On Wed Feb 12, 2020 at 21:10, András Belicza wrote:
> > Ok, here's a small, complete app that reproduces the behavior.
> >
> > Start the app. Everything seems OK. If you resize the window (increase
> > its width with mouse), still everything is OK. If you try to shrink
> > it, then after a certain point, the displayed image starts to shrink
> > below the expected size.
> >
> > Here's the source:
> >
> > ...
>
> Thank you. I believe the culprit is that th.Image clips to the gtx.Constraints
> bounds. Because you're low-level drawing, you're better off just drawing directly:
>
>
>         gtx.Reset(e.Config, e.Size)
>
>         displayWidth := 400
>         var x1, y1, x2, y2 float32 = 300, 300, 300 + float32(displayWidth), 700
>         var x, y float32 = 0, 50
>
>         off := f32.Point{
>                 X: -x1 + x + float32(e.Size.X-displayWidth)/2, // kind of like centering
>                 Y: -y1 + y,
>         }
>         cl := f32.Rectangle{
>                 Min: f32.Point{X: x1, Y: y1},
>                 Max: f32.Point{X: x2, Y: y2},
>         }
>
>         op.TransformOp{}.Offset(off).Add(gtx.Ops)
>         clip.Rect{Rect: cl}.Op(gtx.Ops).Add(gtx.Ops)
>         iop.Add(gtx.Ops)
>         sz := img.Bounds().Size()
>         paint.PaintOp{Rect: f32.Rectangle{Max: f32.Point{X: float32(sz.X), Y: float32(sz.Y)}}}.Add(gtx.Ops)
>
>         fmt.Println("offset:", off, "clip:", cl)
>
>         e.Frame(gtx.Ops)
>
> However, I still think the above is too hard and convoluted. I added a ImageOp.Rect
> field so your program reduces to just
>
>         func loop(w *app.Window) {
>                 gofont.Register()
>                 th := material.NewTheme()
>                 gtx := layout.NewContext(w.Queue())
>
>                 img := image.NewRGBA(image.Rect(0, 0, 1000, 1000))
>                 for x := 0; x < 1000; x++ {
>                         for y := 0; y < 1000; y++ {
>                                 img.Set(x, y, color.RGBA{R: 50 + uint8(x/5), A: 255})
>                         }
>                 }
>
>                 iop := paint.NewImageOp(img)
>                 // Subimage.
>                 iop.Rect = image.Rectangle{
>                         Min: image.Point{X: 500, Y: 500},
>                         Max: image.Point{X: 600, Y: 600},
>                 }
>
>                 for e := range w.Events() {
>                         switch e := e.(type) {
>                         case system.FrameEvent:
>                                 gtx.Reset(e.Config, e.Size)
>
>                                 layout.Center.Layout(gtx, func() {
>                                         img := th.Image(iop)
>                                         img.Scale = 1
>                                         img.Layout(gtx)
>                                 })
>
>                                 e.Frame(gtx.Ops)
>                         }
>                 }
>         }
>
> -- elias