• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

fyne-io / fyne / 18807176232

25 Oct 2025 06:34PM UTC coverage: 61.057% (-0.004%) from 61.061%
18807176232

Pull #5989

github

Jacalz
Fix TODO regarding comparable map key
Pull Request #5989: RFC: Proof of concept for upgrading Go to 1.24

155 of 188 new or added lines in 62 files covered. (82.45%)

27 existing lines in 6 files now uncovered.

25609 of 41943 relevant lines covered (61.06%)

692.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

3.9
/internal/painter/gl/draw.go
1
package gl
2

3
import (
4
        "image/color"
5
        "math"
6

7
        "fyne.io/fyne/v2"
8
        "fyne.io/fyne/v2/canvas"
9
        paint "fyne.io/fyne/v2/internal/painter"
10
)
11

12
const edgeSoftness = 1.0
13

14
func (p *painter) createBuffer(size int) Buffer {
×
15
        vbo := p.ctx.CreateBuffer()
×
16
        p.logError()
×
17
        p.ctx.BindBuffer(arrayBuffer, vbo)
×
18
        p.logError()
×
19
        p.ctx.BufferData(arrayBuffer, make([]float32, size), staticDraw)
×
20
        p.logError()
×
21
        return vbo
×
22
}
×
23

24
func (p *painter) updateBuffer(vbo Buffer, points []float32) {
×
25
        p.ctx.BindBuffer(arrayBuffer, vbo)
×
26
        p.logError()
×
27
        p.ctx.BufferSubData(arrayBuffer, points)
×
28
        p.logError()
×
29
}
×
30

31
func (p *painter) drawCircle(circle *canvas.Circle, pos fyne.Position, frame fyne.Size) {
×
32
        radius := paint.GetMaximumRadius(circle.Size())
×
33
        program := p.roundRectangleProgram
×
34

×
35
        // Vertex: BEG
×
36
        bounds, points := p.vecSquareCoords(pos, circle, frame)
×
37
        p.ctx.UseProgram(program.ref)
×
38
        p.updateBuffer(program.buff, points)
×
39
        p.UpdateVertexArray(program, "vert", 2, 4, 0)
×
40
        p.UpdateVertexArray(program, "normal", 2, 4, 2)
×
41

×
42
        p.ctx.BlendFunc(srcAlpha, oneMinusSrcAlpha)
×
43
        p.logError()
×
44
        // Vertex: END
×
45

×
46
        // Fragment: BEG
×
47
        frameWidthScaled, frameHeightScaled := p.scaleFrameSize(frame)
×
48
        p.SetUniform2f(program, "frame_size", frameWidthScaled, frameHeightScaled)
×
49

×
50
        x1Scaled, x2Scaled, y1Scaled, y2Scaled := p.scaleRectCoords(bounds[0], bounds[2], bounds[1], bounds[3])
×
51
        p.SetUniform4f(program, "rect_coords", x1Scaled, x2Scaled, y1Scaled, y2Scaled)
×
52

×
53
        strokeWidthScaled := roundToPixel(circle.StrokeWidth*p.pixScale, 1.0)
×
54
        p.SetUniform1f(program, "stroke_width_half", strokeWidthScaled*0.5)
×
55

×
56
        rectSizeWidthScaled := x2Scaled - x1Scaled - strokeWidthScaled
×
57
        rectSizeHeightScaled := y2Scaled - y1Scaled - strokeWidthScaled
×
58
        p.SetUniform2f(program, "rect_size_half", rectSizeWidthScaled*0.5, rectSizeHeightScaled*0.5)
×
59

×
60
        radiusScaled := roundToPixel(radius*p.pixScale, 1.0)
×
61
        p.SetUniform4f(program, "radius", radiusScaled, radiusScaled, radiusScaled, radiusScaled)
×
62

×
63
        r, g, b, a := getFragmentColor(circle.FillColor)
×
64
        p.SetUniform4f(program, "fill_color", r, g, b, a)
×
65

×
66
        strokeColor := circle.StrokeColor
×
67
        if strokeColor == nil {
×
68
                strokeColor = color.Transparent
×
69
        }
×
70
        r, g, b, a = getFragmentColor(strokeColor)
×
71
        p.SetUniform4f(program, "stroke_color", r, g, b, a)
×
72

×
73
        edgeSoftnessScaled := roundToPixel(edgeSoftness*p.pixScale, 1.0)
×
74
        p.SetUniform1f(program, "edge_softness", edgeSoftnessScaled)
×
75
        p.logError()
×
76
        // Fragment: END
×
77

×
78
        p.ctx.DrawArrays(triangleStrip, 0, 4)
×
79
        p.logError()
×
80
}
81

82
func (p *painter) drawGradient(o fyne.CanvasObject, texCreator func(fyne.CanvasObject) Texture, pos fyne.Position, frame fyne.Size) {
×
83
        p.drawTextureWithDetails(o, texCreator, pos, o.Size(), frame, canvas.ImageFillStretch, 1.0, 0)
×
84
}
×
85

86
func (p *painter) drawImage(img *canvas.Image, pos fyne.Position, frame fyne.Size) {
×
87
        p.drawTextureWithDetails(img, p.newGlImageTexture, pos, img.Size(), frame, img.FillMode, float32(img.Alpha()), 0)
×
88
}
×
89

90
func (p *painter) drawLine(line *canvas.Line, pos fyne.Position, frame fyne.Size) {
×
91
        if line.StrokeColor == color.Transparent || line.StrokeColor == nil || line.StrokeWidth == 0 {
×
92
                return
×
93
        }
×
94
        points, halfWidth, feather := p.lineCoords(pos, line.Position1, line.Position2, line.StrokeWidth, 0.5, frame)
×
95
        p.ctx.UseProgram(p.lineProgram.ref)
×
96
        p.updateBuffer(p.lineProgram.buff, points)
×
97
        p.UpdateVertexArray(p.lineProgram, "vert", 2, 4, 0)
×
98
        p.UpdateVertexArray(p.lineProgram, "normal", 2, 4, 2)
×
99

×
100
        p.ctx.BlendFunc(srcAlpha, oneMinusSrcAlpha)
×
101
        p.logError()
×
102

×
103
        r, g, b, a := getFragmentColor(line.StrokeColor)
×
104
        p.SetUniform4f(p.lineProgram, "color", r, g, b, a)
×
105

×
106
        p.SetUniform1f(p.lineProgram, "lineWidth", halfWidth)
×
107

×
108
        p.SetUniform1f(p.lineProgram, "feather", feather)
×
109

×
110
        p.ctx.DrawArrays(triangles, 0, 6)
×
111
        p.logError()
×
112
}
113

114
func (p *painter) drawObject(o fyne.CanvasObject, pos fyne.Position, frame fyne.Size) {
×
115
        switch obj := o.(type) {
×
116
        case *canvas.Circle:
×
117
                p.drawCircle(obj, pos, frame)
×
118
        case *canvas.Line:
×
119
                p.drawLine(obj, pos, frame)
×
120
        case *canvas.Image:
×
121
                p.drawImage(obj, pos, frame)
×
122
        case *canvas.Raster:
×
123
                p.drawRaster(obj, pos, frame)
×
124
        case *canvas.Rectangle:
×
125
                p.drawRectangle(obj, pos, frame)
×
126
        case *canvas.Text:
×
127
                p.drawText(obj, pos, frame)
×
128
        case *canvas.LinearGradient:
×
129
                p.drawGradient(obj, p.newGlLinearGradientTexture, pos, frame)
×
130
        case *canvas.RadialGradient:
×
131
                p.drawGradient(obj, p.newGlRadialGradientTexture, pos, frame)
×
132
        case *canvas.Polygon:
×
133
                p.drawPolygon(obj, pos, frame)
×
134
        case *canvas.Arc:
×
135
                p.drawArc(obj, pos, frame)
×
136
        }
137
}
138

139
func (p *painter) drawRaster(img *canvas.Raster, pos fyne.Position, frame fyne.Size) {
×
140
        p.drawTextureWithDetails(img, p.newGlRasterTexture, pos, img.Size(), frame, canvas.ImageFillStretch, float32(img.Alpha()), 0)
×
141
}
×
142

143
func (p *painter) drawRectangle(rect *canvas.Rectangle, pos fyne.Position, frame fyne.Size) {
×
144
        topRightRadius := paint.GetCornerRadius(rect.TopRightCornerRadius, rect.CornerRadius)
×
145
        topLeftRadius := paint.GetCornerRadius(rect.TopLeftCornerRadius, rect.CornerRadius)
×
146
        bottomRightRadius := paint.GetCornerRadius(rect.BottomRightCornerRadius, rect.CornerRadius)
×
147
        bottomLeftRadius := paint.GetCornerRadius(rect.BottomLeftCornerRadius, rect.CornerRadius)
×
148
        p.drawOblong(rect, rect.FillColor, rect.StrokeColor, rect.StrokeWidth, topRightRadius, topLeftRadius, bottomRightRadius, bottomLeftRadius, rect.Aspect, pos, frame)
×
149
}
×
150

151
func (p *painter) drawOblong(obj fyne.CanvasObject, fill, stroke color.Color, strokeWidth, topRightRadius, topLeftRadius, bottomRightRadius, bottomLeftRadius, aspect float32, pos fyne.Position, frame fyne.Size) {
×
152
        if (fill == color.Transparent || fill == nil) && (stroke == color.Transparent || stroke == nil || strokeWidth == 0) {
×
153
                return
×
154
        }
×
155

156
        roundedCorners := topRightRadius != 0 || topLeftRadius != 0 || bottomRightRadius != 0 || bottomLeftRadius != 0
×
157
        var program ProgramState
×
158
        if roundedCorners {
×
159
                program = p.roundRectangleProgram
×
160
        } else {
×
161
                program = p.rectangleProgram
×
162
        }
×
163

164
        // Vertex: BEG
165
        bounds, points := p.vecRectCoords(pos, obj, frame, aspect)
×
166
        p.ctx.UseProgram(program.ref)
×
167
        p.updateBuffer(program.buff, points)
×
168
        p.UpdateVertexArray(program, "vert", 2, 4, 0)
×
169
        p.UpdateVertexArray(program, "normal", 2, 4, 2)
×
170

×
171
        p.ctx.BlendFunc(srcAlpha, oneMinusSrcAlpha)
×
172
        p.logError()
×
173
        // Vertex: END
×
174

×
175
        // Fragment: BEG
×
176
        frameWidthScaled, frameHeightScaled := p.scaleFrameSize(frame)
×
177
        p.SetUniform2f(program, "frame_size", frameWidthScaled, frameHeightScaled)
×
178

×
179
        x1Scaled, x2Scaled, y1Scaled, y2Scaled := p.scaleRectCoords(bounds[0], bounds[2], bounds[1], bounds[3])
×
180
        p.SetUniform4f(program, "rect_coords", x1Scaled, x2Scaled, y1Scaled, y2Scaled)
×
181

×
182
        strokeWidthScaled := roundToPixel(strokeWidth*p.pixScale, 1.0)
×
183
        if roundedCorners {
×
184
                p.SetUniform1f(program, "stroke_width_half", strokeWidthScaled*0.5)
×
185

×
186
                rectSizeWidthScaled := x2Scaled - x1Scaled - strokeWidthScaled
×
187
                rectSizeHeightScaled := y2Scaled - y1Scaled - strokeWidthScaled
×
188
                p.SetUniform2f(program, "rect_size_half", rectSizeWidthScaled*0.5, rectSizeHeightScaled*0.5)
×
189

×
190
                // the maximum possible corner radii for a circular shape, calculated taking into account the rect coords with aspect ratio
×
191
                size := fyne.NewSize(bounds[2]-bounds[0], bounds[3]-bounds[1])
×
192
                topRightRadiusScaled := roundToPixel(
×
193
                        paint.GetMaximumCornerRadius(topRightRadius, topLeftRadius, bottomRightRadius, size)*p.pixScale,
×
194
                        1.0,
×
195
                )
×
196
                topLeftRadiusScaled := roundToPixel(
×
197
                        paint.GetMaximumCornerRadius(topLeftRadius, topRightRadius, bottomLeftRadius, size)*p.pixScale,
×
198
                        1.0,
×
199
                )
×
200
                bottomRightRadiusScaled := roundToPixel(
×
201
                        paint.GetMaximumCornerRadius(bottomRightRadius, bottomLeftRadius, topRightRadius, size)*p.pixScale,
×
202
                        1.0,
×
203
                )
×
204
                bottomLeftRadiusScaled := roundToPixel(
×
205
                        paint.GetMaximumCornerRadius(bottomLeftRadius, bottomRightRadius, topLeftRadius, size)*p.pixScale,
×
206
                        1.0,
×
207
                )
×
208
                p.SetUniform4f(program, "radius", topRightRadiusScaled, bottomRightRadiusScaled, topLeftRadiusScaled, bottomLeftRadiusScaled)
×
209

×
210
                edgeSoftnessScaled := roundToPixel(edgeSoftness*p.pixScale, 1.0)
×
211
                p.SetUniform1f(program, "edge_softness", edgeSoftnessScaled)
×
212
        } else {
×
213
                p.SetUniform1f(program, "stroke_width", strokeWidthScaled)
×
214
        }
×
215

216
        r, g, b, a := getFragmentColor(fill)
×
217
        p.SetUniform4f(program, "fill_color", r, g, b, a)
×
218

×
219
        strokeColor := stroke
×
220
        if strokeColor == nil {
×
221
                strokeColor = color.Transparent
×
222
        }
×
223
        r, g, b, a = getFragmentColor(strokeColor)
×
224
        p.SetUniform4f(program, "stroke_color", r, g, b, a)
×
225
        p.logError()
×
226
        // Fragment: END
×
227

×
228
        p.ctx.DrawArrays(triangleStrip, 0, 4)
×
229
        p.logError()
×
230
}
231

232
func (p *painter) drawPolygon(polygon *canvas.Polygon, pos fyne.Position, frame fyne.Size) {
×
233
        if ((polygon.FillColor == color.Transparent || polygon.FillColor == nil) && (polygon.StrokeColor == color.Transparent || polygon.StrokeColor == nil || polygon.StrokeWidth == 0)) || polygon.Sides < 3 {
×
234
                return
×
235
        }
×
236
        size := polygon.Size()
×
237

×
238
        // Vertex: BEG
×
239
        bounds, points := p.vecRectCoords(pos, polygon, frame, 0.0)
×
240
        program := p.polygonProgram
×
241
        p.ctx.UseProgram(program.ref)
×
242
        p.updateBuffer(program.buff, points)
×
243
        p.UpdateVertexArray(program, "vert", 2, 4, 0)
×
244
        p.UpdateVertexArray(program, "normal", 2, 4, 2)
×
245

×
246
        p.ctx.BlendFunc(srcAlpha, oneMinusSrcAlpha)
×
247
        p.logError()
×
248
        // Vertex: END
×
249

×
250
        // Fragment: BEG
×
251
        frameWidthScaled, frameHeightScaled := p.scaleFrameSize(frame)
×
252
        p.SetUniform2f(program, "frame_size", frameWidthScaled, frameHeightScaled)
×
253

×
254
        x1Scaled, x2Scaled, y1Scaled, y2Scaled := p.scaleRectCoords(bounds[0], bounds[2], bounds[1], bounds[3])
×
255
        p.SetUniform4f(program, "rect_coords", x1Scaled, x2Scaled, y1Scaled, y2Scaled)
×
256

×
257
        edgeSoftnessScaled := roundToPixel(edgeSoftness*p.pixScale, 1.0)
×
258
        p.SetUniform1f(program, "edge_softness", edgeSoftnessScaled)
×
259

×
NEW
260
        outerRadius := min(size.Width, size.Height) / 2
×
261
        outerRadiusScaled := roundToPixel(outerRadius*p.pixScale, 1.0)
×
262
        p.SetUniform1f(program, "outer_radius", outerRadiusScaled)
×
263

×
264
        p.SetUniform1f(program, "angle", polygon.Angle)
×
265
        p.SetUniform1f(program, "sides", float32(polygon.Sides))
×
266

×
NEW
267
        cornerRadius := min(paint.GetMaximumRadius(size), polygon.CornerRadius)
×
268
        cornerRadiusScaled := roundToPixel(cornerRadius*p.pixScale, 1.0)
×
269
        p.SetUniform1f(program, "corner_radius", cornerRadiusScaled)
×
270

×
271
        strokeWidthScaled := roundToPixel(polygon.StrokeWidth*p.pixScale, 1.0)
×
272
        p.SetUniform1f(program, "stroke_width", strokeWidthScaled)
×
273

×
274
        r, g, b, a := getFragmentColor(polygon.FillColor)
×
275
        p.SetUniform4f(program, "fill_color", r, g, b, a)
×
276

×
277
        strokeColor := polygon.StrokeColor
×
278
        if strokeColor == nil {
×
279
                strokeColor = color.Transparent
×
280
        }
×
281
        r, g, b, a = getFragmentColor(strokeColor)
×
282
        p.SetUniform4f(program, "stroke_color", r, g, b, a)
×
283

×
284
        p.logError()
×
285
        // Fragment: END
×
286

×
287
        p.ctx.DrawArrays(triangleStrip, 0, 4)
×
288
        p.logError()
×
289
}
290

291
func (p *painter) drawArc(arc *canvas.Arc, pos fyne.Position, frame fyne.Size) {
×
292
        if ((arc.FillColor == color.Transparent || arc.FillColor == nil) && (arc.StrokeColor == color.Transparent || arc.StrokeColor == nil || arc.StrokeWidth == 0)) || arc.StartAngle == arc.EndAngle {
×
293
                return
×
294
        }
×
295

296
        // Vertex: BEG
297
        bounds, points := p.vecRectCoords(pos, arc, frame, 0.0)
×
298
        program := p.arcProgram
×
299
        p.ctx.UseProgram(program.ref)
×
300
        p.updateBuffer(program.buff, points)
×
301
        p.UpdateVertexArray(program, "vert", 2, 4, 0)
×
302
        p.UpdateVertexArray(program, "normal", 2, 4, 2)
×
303

×
304
        p.ctx.BlendFunc(srcAlpha, oneMinusSrcAlpha)
×
305
        p.logError()
×
306
        // Vertex: END
×
307

×
308
        // Fragment: BEG
×
309
        frameWidthScaled, frameHeightScaled := p.scaleFrameSize(frame)
×
310
        p.SetUniform2f(program, "frame_size", frameWidthScaled, frameHeightScaled)
×
311

×
312
        x1Scaled, x2Scaled, y1Scaled, y2Scaled := p.scaleRectCoords(bounds[0], bounds[2], bounds[1], bounds[3])
×
313
        p.SetUniform4f(program, "rect_coords", x1Scaled, x2Scaled, y1Scaled, y2Scaled)
×
314

×
315
        edgeSoftnessScaled := roundToPixel(edgeSoftness*p.pixScale, 1.0)
×
316
        p.SetUniform1f(program, "edge_softness", edgeSoftnessScaled)
×
317

×
NEW
318
        outerRadius := min(arc.Size().Width, arc.Size().Height) / 2
×
319
        outerRadiusScaled := roundToPixel(outerRadius*p.pixScale, 1.0)
×
320
        p.SetUniform1f(program, "outer_radius", outerRadiusScaled)
×
321

×
NEW
322
        innerRadius := outerRadius * min(1.0, max(0.0, arc.CutoutRatio))
×
323
        innerRadiusScaled := roundToPixel(innerRadius*p.pixScale, 1.0)
×
324
        p.SetUniform1f(program, "inner_radius", innerRadiusScaled)
×
325

×
326
        startAngle, endAngle := paint.NormalizeArcAngles(arc.StartAngle, arc.EndAngle)
×
327
        p.SetUniform1f(program, "start_angle", startAngle)
×
328
        p.SetUniform1f(program, "end_angle", endAngle)
×
329

×
NEW
330
        cornerRadius := min(paint.GetMaximumRadiusArc(outerRadius, innerRadius, arc.EndAngle-arc.StartAngle), arc.CornerRadius)
×
331
        cornerRadiusScaled := roundToPixel(cornerRadius*p.pixScale, 1.0)
×
332
        p.SetUniform1f(program, "corner_radius", cornerRadiusScaled)
×
333

×
334
        strokeWidthScaled := roundToPixel(arc.StrokeWidth*p.pixScale, 1.0)
×
335
        p.SetUniform1f(program, "stroke_width", strokeWidthScaled)
×
336

×
337
        r, g, b, a := getFragmentColor(arc.FillColor)
×
338
        p.SetUniform4f(program, "fill_color", r, g, b, a)
×
339

×
340
        strokeColor := arc.StrokeColor
×
341
        if strokeColor == nil {
×
342
                strokeColor = color.Transparent
×
343
        }
×
344
        r, g, b, a = getFragmentColor(strokeColor)
×
345
        p.SetUniform4f(program, "stroke_color", r, g, b, a)
×
346

×
347
        p.logError()
×
348
        // Fragment: END
×
349

×
350
        p.ctx.DrawArrays(triangleStrip, 0, 4)
×
351
        p.logError()
×
352
}
353

354
func (p *painter) drawText(text *canvas.Text, pos fyne.Position, frame fyne.Size) {
×
355
        if text.Text == "" || text.Text == " " {
×
356
                return
×
357
        }
×
358

359
        size := text.MinSize()
×
360
        containerSize := text.Size()
×
361
        switch text.Alignment {
×
362
        case fyne.TextAlignTrailing:
×
363
                pos = fyne.NewPos(pos.X+containerSize.Width-size.Width, pos.Y)
×
364
        case fyne.TextAlignCenter:
×
365
                pos = fyne.NewPos(pos.X+(containerSize.Width-size.Width)/2, pos.Y)
×
366
        }
367

368
        if containerSize.Height > size.Height {
×
369
                pos = fyne.NewPos(pos.X, pos.Y+(containerSize.Height-size.Height)/2)
×
370
        }
×
371

372
        // text size is sensitive to position on screen
373
        size, _ = roundToPixelCoords(size, text.Position(), p.pixScale)
×
374
        size.Width += roundToPixel(paint.VectorPad(text), p.pixScale)
×
375
        p.drawTextureWithDetails(text, p.newGlTextTexture, pos, size, frame, canvas.ImageFillStretch, 1.0, 0)
×
376
}
377

378
func (p *painter) drawTextureWithDetails(o fyne.CanvasObject, creator func(canvasObject fyne.CanvasObject) Texture,
379
        pos fyne.Position, size, frame fyne.Size, fill canvas.ImageFill, alpha float32, pad float32,
380
) {
×
381
        texture, err := p.getTexture(o, creator)
×
382
        if err != nil {
×
383
                return
×
384
        }
×
385

386
        cornerRadius := float32(0)
×
387
        aspect := float32(0)
×
388
        if img, ok := o.(*canvas.Image); ok {
×
389
                aspect = img.Aspect()
×
390
                if aspect == 0 {
×
391
                        aspect = 1 // fallback, should not occur - normally an image load error
×
392
                }
×
393
                if img.CornerRadius > 0 {
×
394
                        cornerRadius = img.CornerRadius
×
395
                }
×
396
        }
397
        points := p.rectCoords(size, pos, frame, fill, aspect, pad)
×
398
        inner, _ := rectInnerCoords(size, pos, fill, aspect)
×
399

×
400
        p.ctx.UseProgram(p.program.ref)
×
401
        p.updateBuffer(p.program.buff, points)
×
402
        p.UpdateVertexArray(p.program, "vert", 3, 5, 0)
×
403
        p.UpdateVertexArray(p.program, "vertTexCoord", 2, 5, 3)
×
404

×
405
        // Set corner radius and texture size in pixels
×
NEW
406
        cornerRadius = min(paint.GetMaximumRadius(size), cornerRadius)
×
407
        p.SetUniform1f(p.program, "cornerRadius", cornerRadius*p.pixScale)
×
408
        p.SetUniform2f(p.program, "size", inner.Width*p.pixScale, inner.Height*p.pixScale)
×
409

×
410
        p.SetUniform1f(p.program, "alpha", alpha)
×
411

×
412
        p.ctx.BlendFunc(one, oneMinusSrcAlpha)
×
413
        p.logError()
×
414

×
415
        p.ctx.ActiveTexture(texture0)
×
416
        p.ctx.BindTexture(texture2D, texture)
×
417
        p.logError()
×
418

×
419
        p.ctx.DrawArrays(triangleStrip, 0, 4)
×
420
        p.logError()
×
421
}
422

423
func (p *painter) lineCoords(pos, pos1, pos2 fyne.Position, lineWidth, feather float32, frame fyne.Size) ([]float32, float32, float32) {
×
424
        // Shift line coordinates so that they match the target position.
×
NEW
425
        xPosDiff := pos.X - min(pos1.X, pos2.X)
×
NEW
426
        yPosDiff := pos.Y - min(pos1.Y, pos2.Y)
×
427
        pos1.X = roundToPixel(pos1.X+xPosDiff, p.pixScale)
×
428
        pos1.Y = roundToPixel(pos1.Y+yPosDiff, p.pixScale)
×
429
        pos2.X = roundToPixel(pos2.X+xPosDiff, p.pixScale)
×
430
        pos2.Y = roundToPixel(pos2.Y+yPosDiff, p.pixScale)
×
431

×
432
        if lineWidth <= 1 {
×
433
                offset := float32(0.5)                  // adjust location for lines < 1pt on regular display
×
434
                if lineWidth <= 0.5 && p.pixScale > 1 { // and for 1px drawing on HiDPI (width 0.5)
×
435
                        offset = 0.25
×
436
                }
×
437
                if pos1.X == pos2.X {
×
438
                        pos1.X -= offset
×
439
                        pos2.X -= offset
×
440
                }
×
441
                if pos1.Y == pos2.Y {
×
442
                        pos1.Y -= offset
×
443
                        pos2.Y -= offset
×
444
                }
×
445
        }
446

447
        x1Pos := pos1.X / frame.Width
×
448
        x1 := -1 + x1Pos*2
×
449
        y1Pos := pos1.Y / frame.Height
×
450
        y1 := 1 - y1Pos*2
×
451
        x2Pos := pos2.X / frame.Width
×
452
        x2 := -1 + x2Pos*2
×
453
        y2Pos := pos2.Y / frame.Height
×
454
        y2 := 1 - y2Pos*2
×
455

×
456
        normalX := (pos2.Y - pos1.Y) / frame.Width
×
457
        normalY := (pos2.X - pos1.X) / frame.Height
×
458
        dirLength := float32(math.Sqrt(float64(normalX*normalX + normalY*normalY)))
×
459
        normalX /= dirLength
×
460
        normalY /= dirLength
×
461

×
462
        normalObjX := normalX * 0.5 * frame.Width
×
463
        normalObjY := normalY * 0.5 * frame.Height
×
464
        widthMultiplier := float32(math.Sqrt(float64(normalObjX*normalObjX + normalObjY*normalObjY)))
×
465
        halfWidth := (roundToPixel(lineWidth+feather, p.pixScale) * 0.5) / widthMultiplier
×
466
        featherWidth := feather / widthMultiplier
×
467

×
468
        return []float32{
×
469
                // coord x, y normal x, y
×
470
                x1, y1, normalX, normalY,
×
471
                x2, y2, normalX, normalY,
×
472
                x2, y2, -normalX, -normalY,
×
473
                x2, y2, -normalX, -normalY,
×
474
                x1, y1, normalX, normalY,
×
475
                x1, y1, -normalX, -normalY,
×
476
        }, halfWidth, featherWidth
×
477
}
478

479
// rectCoords calculates the openGL coordinate space of a rectangle
480
func (p *painter) rectCoords(size fyne.Size, pos fyne.Position, frame fyne.Size,
481
        fill canvas.ImageFill, aspect float32, pad float32,
482
) []float32 {
×
483
        size, pos = rectInnerCoords(size, pos, fill, aspect)
×
484
        size, pos = roundToPixelCoords(size, pos, p.pixScale)
×
485

×
486
        xPos := (pos.X - pad) / frame.Width
×
487
        x1 := -1 + xPos*2
×
488
        x2Pos := (pos.X + size.Width + pad) / frame.Width
×
489
        x2 := -1 + x2Pos*2
×
490

×
491
        yPos := (pos.Y - pad) / frame.Height
×
492
        y1 := 1 - yPos*2
×
493
        y2Pos := (pos.Y + size.Height + pad) / frame.Height
×
494
        y2 := 1 - y2Pos*2
×
495

×
496
        xInset := float32(0.0)
×
497
        yInset := float32(0.0)
×
498

×
499
        if fill == canvas.ImageFillCover {
×
500
                viewAspect := size.Width / size.Height
×
501

×
502
                if viewAspect > aspect {
×
503
                        newHeight := size.Width / aspect
×
504
                        heightPad := (newHeight - size.Height) / 2
×
505
                        yInset = heightPad / newHeight
×
506
                } else if viewAspect < aspect {
×
507
                        newWidth := size.Height * aspect
×
508
                        widthPad := (newWidth - size.Width) / 2
×
509
                        xInset = widthPad / newWidth
×
510
                }
×
511
        }
512

513
        return []float32{
×
514
                // coord x, y, z texture x, y
×
515
                x1, y2, 0, xInset, 1.0 - yInset, // top left
×
516
                x1, y1, 0, xInset, yInset, // bottom left
×
517
                x2, y2, 0, 1.0 - xInset, 1.0 - yInset, // top right
×
518
                x2, y1, 0, 1.0 - xInset, yInset, // bottom right
×
519
        }
×
520
}
521

522
func rectInnerCoords(size fyne.Size, pos fyne.Position, fill canvas.ImageFill, aspect float32) (fyne.Size, fyne.Position) {
6✔
523
        if fill == canvas.ImageFillContain || fill == canvas.ImageFillOriginal {
10✔
524
                // change pos and size accordingly
4✔
525

4✔
526
                viewAspect := size.Width / size.Height
4✔
527

4✔
528
                newWidth, newHeight := size.Width, size.Height
4✔
529
                widthPad, heightPad := float32(0), float32(0)
4✔
530
                if viewAspect > aspect {
7✔
531
                        newWidth = size.Height * aspect
3✔
532
                        widthPad = (size.Width - newWidth) / 2
3✔
533
                } else if viewAspect < aspect {
4✔
534
                        newHeight = size.Width / aspect
×
535
                        heightPad = (size.Height - newHeight) / 2
×
536
                }
×
537

538
                return fyne.NewSize(newWidth, newHeight), fyne.NewPos(pos.X+widthPad, pos.Y+heightPad)
4✔
539
        }
540

541
        return size, pos
2✔
542
}
543

544
func (p *painter) vecRectCoords(pos fyne.Position, rect fyne.CanvasObject, frame fyne.Size, aspect float32) ([4]float32, []float32) {
×
545
        xPad, yPad := float32(0), float32(0)
×
546

×
547
        if aspect != 0 {
×
548
                inner := rect.Size()
×
549
                frameAspect := inner.Width / inner.Height
×
550

×
551
                if frameAspect > aspect {
×
552
                        newWidth := inner.Height * aspect
×
553
                        xPad = (inner.Width - newWidth) / 2
×
554
                } else if frameAspect < aspect {
×
555
                        newHeight := inner.Width / aspect
×
556
                        yPad = (inner.Height - newHeight) / 2
×
557
                }
×
558
        }
559

560
        return p.vecRectCoordsWithPad(pos, rect, frame, xPad, yPad)
×
561
}
562

563
func (p *painter) vecRectCoordsWithPad(pos fyne.Position, rect fyne.CanvasObject, frame fyne.Size, xPad, yPad float32) ([4]float32, []float32) {
×
564
        size := rect.Size()
×
565
        pos1 := rect.Position()
×
566

×
567
        xPosDiff := pos.X - pos1.X + xPad
×
568
        yPosDiff := pos.Y - pos1.Y + yPad
×
569
        pos1.X = roundToPixel(pos1.X+xPosDiff, p.pixScale)
×
570
        pos1.Y = roundToPixel(pos1.Y+yPosDiff, p.pixScale)
×
571
        size.Width = roundToPixel(size.Width-2*xPad, p.pixScale)
×
572
        size.Height = roundToPixel(size.Height-2*yPad, p.pixScale)
×
573

×
574
        // without edge softness adjustment the rectangle has cropped edges
×
575
        edgeSoftnessScaled := roundToPixel(edgeSoftness*p.pixScale, 1.0)
×
576
        x1Pos := pos1.X
×
577
        x1Norm := -1 + (x1Pos-edgeSoftnessScaled)*2/frame.Width
×
578
        x2Pos := pos1.X + size.Width
×
579
        x2Norm := -1 + (x2Pos+edgeSoftnessScaled)*2/frame.Width
×
580
        y1Pos := pos1.Y
×
581
        y1Norm := 1 - (y1Pos-edgeSoftnessScaled)*2/frame.Height
×
582
        y2Pos := pos1.Y + size.Height
×
583
        y2Norm := 1 - (y2Pos+edgeSoftnessScaled)*2/frame.Height
×
584

×
585
        // output a norm for the fill and the vert is unused, but we pass 0 to avoid optimisation issues
×
586
        coords := []float32{
×
587
                0, 0, x1Norm, y1Norm, // first triangle
×
588
                0, 0, x2Norm, y1Norm, // second triangle
×
589
                0, 0, x1Norm, y2Norm,
×
590
                0, 0, x2Norm, y2Norm,
×
591
        }
×
592

×
593
        return [4]float32{x1Pos, y1Pos, x2Pos, y2Pos}, coords
×
594
}
×
595

596
func (p *painter) vecSquareCoords(pos fyne.Position, rect fyne.CanvasObject, frame fyne.Size) ([4]float32, []float32) {
×
597
        return p.vecRectCoordsWithPad(pos, rect, frame, 0, 0)
×
598
}
×
599

600
func roundToPixel(v float32, pixScale float32) float32 {
×
601
        if pixScale == 1.0 {
×
602
                return float32(math.Round(float64(v)))
×
603
        }
×
604

605
        return float32(math.Round(float64(v*pixScale))) / pixScale
×
606
}
607

608
func roundToPixelCoords(size fyne.Size, pos fyne.Position, pixScale float32) (fyne.Size, fyne.Position) {
×
609
        end := pos.Add(size)
×
610
        end.X = roundToPixel(end.X, pixScale)
×
611
        end.Y = roundToPixel(end.Y, pixScale)
×
612
        pos.X = roundToPixel(pos.X, pixScale)
×
613
        pos.Y = roundToPixel(pos.Y, pixScale)
×
614
        size.Width = end.X - pos.X
×
615
        size.Height = end.Y - pos.Y
×
616

×
617
        return size, pos
×
618
}
×
619

620
// Returns FragmentColor(red,green,blue,alpha) from fyne.Color
621
func getFragmentColor(col color.Color) (float32, float32, float32, float32) {
3✔
622
        if col == nil {
4✔
623
                return 0, 0, 0, 0
1✔
624
        }
1✔
625
        r, g, b, a := col.RGBA()
2✔
626
        if a == 0 {
2✔
627
                return 0, 0, 0, 0
×
628
        }
×
629
        alpha := float32(a)
2✔
630
        return float32(r) / alpha, float32(g) / alpha, float32(b) / alpha, alpha / 0xffff
2✔
631
}
632

633
func (p *painter) scaleFrameSize(frame fyne.Size) (float32, float32) {
×
634
        frameWidthScaled := roundToPixel(frame.Width*p.pixScale, 1.0)
×
635
        frameHeightScaled := roundToPixel(frame.Height*p.pixScale, 1.0)
×
636
        return frameWidthScaled, frameHeightScaled
×
637
}
×
638

639
// Returns scaled RectCoords(x1,x2,y1,y2) in same order
640
func (p *painter) scaleRectCoords(x1, x2, y1, y2 float32) (float32, float32, float32, float32) {
×
641
        x1Scaled := roundToPixel(x1*p.pixScale, 1.0)
×
642
        x2Scaled := roundToPixel(x2*p.pixScale, 1.0)
×
643
        y1Scaled := roundToPixel(y1*p.pixScale, 1.0)
×
644
        y2Scaled := roundToPixel(y2*p.pixScale, 1.0)
×
645
        return x1Scaled, x2Scaled, y1Scaled, y2Scaled
×
646
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc