Add: components, progress bar
This commit is contained in:
parent
f29bbf8345
commit
bad142a565
20
components/components.go
Normal file
20
components/components.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package components
|
||||||
|
|
||||||
|
// Some general types
|
||||||
|
|
||||||
|
type rect struct {
|
||||||
|
x int
|
||||||
|
y int
|
||||||
|
w int
|
||||||
|
h int
|
||||||
|
}
|
||||||
|
|
||||||
|
type RectGetter interface {
|
||||||
|
GetPos() (int, int)
|
||||||
|
GetSize() (int, int)
|
||||||
|
}
|
||||||
|
|
||||||
|
type RectSetter interface {
|
||||||
|
SetPos(x, y int)
|
||||||
|
SetSize(w, h int)
|
||||||
|
}
|
117
components/progressbar.go
Normal file
117
components/progressbar.go
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
package components
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"gitea.unprism.ru/yotia/display-test/drawer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type progressBar struct {
|
||||||
|
drawer drawer.Drawer // Drawer with dysplay
|
||||||
|
mask uint32 // Flush mask
|
||||||
|
progress float64
|
||||||
|
|
||||||
|
rect rect // Rect
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProgressBar interface {
|
||||||
|
RectGetter
|
||||||
|
|
||||||
|
SetProgress(coef float64) // Value from 0 to 1
|
||||||
|
Draw() // For any cases
|
||||||
|
Clear() // Clear space of bar
|
||||||
|
Flush() // For those times like after SetPos
|
||||||
|
|
||||||
|
io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creation function
|
||||||
|
// Now the only way to chage shape is to recreate component
|
||||||
|
func NewProgressBar(drawer drawer.Drawer, x, y, w, h int) (ProgressBar, error) {
|
||||||
|
if err := drawer.GetDisplay().IsReady(); err != nil {
|
||||||
|
return nil, fmt.Errorf("display is ready: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for correct sizes
|
||||||
|
if w < 4 {
|
||||||
|
return nil, fmt.Errorf("invalid width: below 4")
|
||||||
|
}
|
||||||
|
if h < 4 {
|
||||||
|
return nil, fmt.Errorf("invalid height: below 4")
|
||||||
|
}
|
||||||
|
pb := progressBar{
|
||||||
|
drawer: drawer,
|
||||||
|
rect: rect{x, y, w, h},
|
||||||
|
}
|
||||||
|
|
||||||
|
pb.updateMask()
|
||||||
|
pb.Draw()
|
||||||
|
pb.Flush()
|
||||||
|
|
||||||
|
return &pb, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pb *progressBar) Flush() {
|
||||||
|
pb.drawer.GetDisplay().FlushByMask(pb.mask)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pb *progressBar) updateMask() {
|
||||||
|
pb.mask = 0
|
||||||
|
|
||||||
|
// Implementation dependent !!!
|
||||||
|
// Now for mt12232
|
||||||
|
y0 := min(3, max(0, int(pb.rect.y/8)))
|
||||||
|
y1 := min(3, max(0, int((pb.rect.y+pb.rect.h-1)/8)))
|
||||||
|
|
||||||
|
x0 := min(1, max(0, int(pb.rect.x/61)))
|
||||||
|
x1 := min(1, max(0, int((pb.rect.x+pb.rect.w-1)/61)))
|
||||||
|
|
||||||
|
//log.Println(x0, y0, x1, y1)
|
||||||
|
for y := y0; y <= y1; y++ {
|
||||||
|
for x := x0; x <= x1; x++ {
|
||||||
|
pb.mask |= (1 << (x*4 + y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pb *progressBar) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pb *progressBar) Clear() {
|
||||||
|
pb.drawer.FillBar(pb.rect.x, pb.rect.y, pb.rect.x+pb.rect.w, pb.rect.y+pb.rect.h, 0x00)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw whole bar
|
||||||
|
func (pb *progressBar) Draw() {
|
||||||
|
// Shape:
|
||||||
|
// Bounds - 1px
|
||||||
|
// Padding - 1px
|
||||||
|
// Progress - the rest space
|
||||||
|
pb.Clear()
|
||||||
|
pb.drawer.PutBar(pb.rect.x, pb.rect.y, pb.rect.x+pb.rect.w, pb.rect.y+pb.rect.h, 0xFF)
|
||||||
|
pb.drawBar()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw only bar(without frames)
|
||||||
|
func (pb *progressBar) drawBar() {
|
||||||
|
w := int(float64(pb.rect.w-4) * pb.progress)
|
||||||
|
|
||||||
|
pb.drawer.FillBar(pb.rect.x+2, pb.rect.y+2, pb.rect.x+2+w, pb.rect.y+pb.rect.h-2, 0xFF)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pb *progressBar) SetProgress(coef float64) {
|
||||||
|
pb.progress = max(0, min(1, coef))
|
||||||
|
|
||||||
|
pb.drawBar()
|
||||||
|
pb.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pb *progressBar) GetPos() (int, int) {
|
||||||
|
return pb.rect.x, pb.rect.y
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pb *progressBar) GetSize() (int, int) {
|
||||||
|
return pb.rect.w, pb.rect.h
|
||||||
|
}
|
@ -9,7 +9,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Display interface {
|
type Display interface {
|
||||||
|
IsReady() error // Nil if it is ready, error with some info otherwise
|
||||||
|
|
||||||
Flush(crystal, page byte)
|
Flush(crystal, page byte)
|
||||||
|
FlushByMask(mask uint32)
|
||||||
|
GetFlushMaskBit(crystal, page byte) uint32
|
||||||
|
|
||||||
// Image functions
|
// Image functions
|
||||||
GetImg() *image.Gray
|
GetImg() *image.Gray
|
||||||
@ -17,7 +21,7 @@ type Display interface {
|
|||||||
UnlockImg()
|
UnlockImg()
|
||||||
GetBounds() image.Rectangle
|
GetBounds() image.Rectangle
|
||||||
|
|
||||||
Test(ctx context.Context) error
|
Test(ctx context.Context) error // DEBUG ONLY
|
||||||
|
|
||||||
io.Closer
|
io.Closer
|
||||||
}
|
}
|
||||||
|
@ -19,11 +19,16 @@ const (
|
|||||||
mt12232aW = 122
|
mt12232aW = 122
|
||||||
mt12232aH = 32
|
mt12232aH = 32
|
||||||
flushChanCap = 24 // Flush channel capacity
|
flushChanCap = 24 // Flush channel capacity
|
||||||
|
|
||||||
|
flushUpdateTimeout = 5 * time.Millisecond
|
||||||
)
|
)
|
||||||
|
|
||||||
type displayMt12232a struct {
|
type displayMt12232a struct {
|
||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
|
|
||||||
|
// Some state flags
|
||||||
|
isTurnedOn bool
|
||||||
|
|
||||||
// Image
|
// Image
|
||||||
img *image.Gray
|
img *image.Gray
|
||||||
imgMutex sync.Mutex
|
imgMutex sync.Mutex
|
||||||
@ -145,6 +150,14 @@ func (d *displayMt12232a) powerOn() error {
|
|||||||
if d.dev.ReadStatus(1)&0x20 != 0 {
|
if d.dev.ReadStatus(1)&0x20 != 0 {
|
||||||
return fmt.Errorf("Right cristal is off")
|
return fmt.Errorf("Right cristal is off")
|
||||||
}
|
}
|
||||||
|
d.isTurnedOn = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *displayMt12232a) IsReady() error {
|
||||||
|
if !d.isTurnedOn {
|
||||||
|
return fmt.Errorf("display is turned off")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,12 +230,22 @@ func (d *displayMt12232a) Flush(crystal, page byte) {
|
|||||||
d.pagesFlushFlags.Store(d.pagesFlushFlags.Load() | bit)
|
d.pagesFlushFlags.Store(d.pagesFlushFlags.Load() | bit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *displayMt12232a) FlushByMask(mask uint32) {
|
||||||
|
// !!! TODO Need to update GO to 1.23 to use .Or !!!
|
||||||
|
d.pagesFlushFlags.Store(d.pagesFlushFlags.Load() | mask)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *displayMt12232a) GetFlushMaskBit(crystal, page byte) uint32 {
|
||||||
|
return uint32(1 << (crystal*4 + page))
|
||||||
|
}
|
||||||
|
|
||||||
func (d *displayMt12232a) Close() error {
|
func (d *displayMt12232a) Close() error {
|
||||||
if d.flushCancel != nil {
|
if d.flushCancel != nil {
|
||||||
d.flushCancel()
|
d.flushCancel()
|
||||||
d.flushCancel = nil
|
d.flushCancel = nil
|
||||||
<-d.flushDone
|
<-d.flushDone
|
||||||
}
|
}
|
||||||
|
d.isTurnedOn = false
|
||||||
return d.dev.Close()
|
return d.dev.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,10 +256,10 @@ func (d *displayMt12232a) flushLoop(ctx context.Context) {
|
|||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
close(d.flushDone)
|
close(d.flushDone)
|
||||||
return
|
return
|
||||||
case <-time.After(time.Millisecond):
|
case <-time.After(flushUpdateTimeout):
|
||||||
forUpdate := d.pagesFlushFlags.Swap(0)
|
forUpdate := d.pagesFlushFlags.Swap(0)
|
||||||
checkBit := uint32(1)
|
checkBit := uint32(1)
|
||||||
st := time.Now()
|
//st := time.Now()
|
||||||
d.LockImg()
|
d.LockImg()
|
||||||
for p := byte(0); p < 4; p++ {
|
for p := byte(0); p < 4; p++ {
|
||||||
if forUpdate&(checkBit) != 0 {
|
if forUpdate&(checkBit) != 0 {
|
||||||
@ -257,7 +280,7 @@ func (d *displayMt12232a) flushLoop(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
checkBit = checkBit << 1
|
checkBit = checkBit << 1
|
||||||
}
|
}
|
||||||
d.logger.Printf("%08b - %s\n", forUpdate, time.Since(st))
|
//d.logger.Printf("%08b - %s\n", forUpdate, time.Since(st))
|
||||||
for p := byte(0); p < 4; p++ {
|
for p := byte(0); p < 4; p++ {
|
||||||
if forUpdate&(checkBit) != 0 {
|
if forUpdate&(checkBit) != 0 {
|
||||||
d.dev.WriteCode(1, (3-p)|0xB8)
|
d.dev.WriteCode(1, (3-p)|0xB8)
|
||||||
|
@ -37,7 +37,8 @@ func newMt12864a(logger *log.Logger) (Display, error) {
|
|||||||
d.test()
|
d.test()
|
||||||
d.logger.Println("Draw finished")
|
d.logger.Println("Draw finished")
|
||||||
|
|
||||||
return &d, nil
|
//return &d, nil
|
||||||
|
return nil, fmt.Errorf("IMPLEMENTATION COMMENTED")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *displayMt12864a) Test(ctx context.Context) error {
|
func (d *displayMt12864a) Test(ctx context.Context) error {
|
||||||
@ -145,6 +146,9 @@ func (d *displayMt12864a) GetBounds() image.Rectangle {
|
|||||||
func (d *displayMt12864a) Flush(crystal, page byte) {
|
func (d *displayMt12864a) Flush(crystal, page byte) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *displayMt12864a) FlushByMask(mask uint32) {
|
||||||
|
}
|
||||||
|
|
||||||
func (d *displayMt12864a) Close() error {
|
func (d *displayMt12864a) Close() error {
|
||||||
return rpio.Close()
|
return rpio.Close()
|
||||||
}
|
}
|
||||||
|
@ -42,11 +42,13 @@ func newSsd1306(logger *log.Logger) (Display, error) {
|
|||||||
return nil, fmt.Errorf("create i2c: %w", err)
|
return nil, fmt.Errorf("create i2c: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &displaySsd1306{
|
_ = displaySsd1306{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
bus: bus,
|
bus: bus,
|
||||||
dev: dev,
|
dev: dev,
|
||||||
}, nil
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("IMPLEMENTATION COMMENTED")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *displaySsd1306) GetBounds() image.Rectangle {
|
func (d *displaySsd1306) GetBounds() image.Rectangle {
|
||||||
@ -61,6 +63,10 @@ func (d *displaySsd1306) Flush(crystal, page byte) {
|
|||||||
//return d.dev.Draw(img.Bounds(), d.img, image.Point{})
|
//return d.dev.Draw(img.Bounds(), d.img, image.Point{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *displaySsd1306) FlushByMask(mask uint32) {
|
||||||
|
//return d.dev.Draw(img.Bounds(), d.img, image.Point{})
|
||||||
|
}
|
||||||
|
|
||||||
func (d *displaySsd1306) Close() error {
|
func (d *displaySsd1306) Close() error {
|
||||||
if err := d.bus.Close(); err != nil {
|
if err := d.bus.Close(); err != nil {
|
||||||
d.logger.Println("ERROR: close i2c bus:", err.Error())
|
d.logger.Println("ERROR: close i2c bus:", err.Error())
|
||||||
|
@ -2,7 +2,7 @@ package drawer
|
|||||||
|
|
||||||
import "math"
|
import "math"
|
||||||
|
|
||||||
func (d *drawer) FillBar(sx, sy, ex, ey, color int) {
|
func (d *drawer) FillBar(sx, sy, ex, ey int, color byte) {
|
||||||
d.dev.LockImg()
|
d.dev.LockImg()
|
||||||
defer d.dev.UnlockImg()
|
defer d.dev.UnlockImg()
|
||||||
|
|
||||||
@ -18,12 +18,12 @@ func (d *drawer) FillBar(sx, sy, ex, ey, color int) {
|
|||||||
addr := lineaddr
|
addr := lineaddr
|
||||||
le := addr + w
|
le := addr + w
|
||||||
for ; addr < le; addr++ {
|
for ; addr < le; addr++ {
|
||||||
d.img.Pix[addr] = 255
|
d.img.Pix[addr] = color
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *drawer) PutBar(sx, sy, ex, ey, color int) {
|
func (d *drawer) PutBar(sx, sy, ex, ey int, color byte) {
|
||||||
d.dev.LockImg()
|
d.dev.LockImg()
|
||||||
defer d.dev.UnlockImg()
|
defer d.dev.UnlockImg()
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ func (d *drawer) PutBar(sx, sy, ex, ey, color int) {
|
|||||||
x1 := int(math.Min(float64(ex), float64(bounds.Dx())))
|
x1 := int(math.Min(float64(ex), float64(bounds.Dx())))
|
||||||
|
|
||||||
for addr := sy*bounds.Dx() + x0; addr < sy*bounds.Dx()+x1; addr++ {
|
for addr := sy*bounds.Dx() + x0; addr < sy*bounds.Dx()+x1; addr++ {
|
||||||
d.img.Pix[addr] = 255
|
d.img.Pix[addr] = color
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Bottom
|
// Bottom
|
||||||
@ -45,7 +45,7 @@ func (d *drawer) PutBar(sx, sy, ex, ey, color int) {
|
|||||||
x1 := int(math.Min(float64(ex), float64(bounds.Dx())))
|
x1 := int(math.Min(float64(ex), float64(bounds.Dx())))
|
||||||
|
|
||||||
for addr := (ey-1)*bounds.Dx() + x0; addr < (ey-1)*bounds.Dx()+x1; addr++ {
|
for addr := (ey-1)*bounds.Dx() + x0; addr < (ey-1)*bounds.Dx()+x1; addr++ {
|
||||||
d.img.Pix[addr] = 255
|
d.img.Pix[addr] = color
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Left
|
// Left
|
||||||
@ -54,7 +54,7 @@ func (d *drawer) PutBar(sx, sy, ex, ey, color int) {
|
|||||||
y1 := int(math.Min(float64(ey), float64(bounds.Dy())))
|
y1 := int(math.Min(float64(ey), float64(bounds.Dy())))
|
||||||
|
|
||||||
for addr := y0*bounds.Dx() + sx; addr < y1*bounds.Dx()+sx; addr += bounds.Dx() {
|
for addr := y0*bounds.Dx() + sx; addr < y1*bounds.Dx()+sx; addr += bounds.Dx() {
|
||||||
d.img.Pix[addr] = 255
|
d.img.Pix[addr] = color
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Right
|
// Right
|
||||||
@ -63,7 +63,7 @@ func (d *drawer) PutBar(sx, sy, ex, ey, color int) {
|
|||||||
y1 := int(math.Min(float64(ey), float64(bounds.Dy())))
|
y1 := int(math.Min(float64(ey), float64(bounds.Dy())))
|
||||||
|
|
||||||
for addr := y0*bounds.Dx() + (ex - 1); addr < y1*bounds.Dx()+(ex-1); addr += bounds.Dx() {
|
for addr := y0*bounds.Dx() + (ex - 1); addr < y1*bounds.Dx()+(ex-1); addr += bounds.Dx() {
|
||||||
d.img.Pix[addr] = 255
|
d.img.Pix[addr] = color
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,13 +15,16 @@ type drawer struct {
|
|||||||
type Drawer interface {
|
type Drawer interface {
|
||||||
GetDisplay() display.Display
|
GetDisplay() display.Display
|
||||||
|
|
||||||
|
W() int
|
||||||
|
H() int
|
||||||
|
|
||||||
Clear()
|
Clear()
|
||||||
Flush()
|
Flush()
|
||||||
|
|
||||||
PutText(x, y int, text string)
|
PutText(x, y int, text string)
|
||||||
|
|
||||||
PutBar(x0, y0, x1, y1, color int)
|
PutBar(x0, y0, x1, y1 int, color byte)
|
||||||
FillBar(x0, y0, x1, y1, color int)
|
FillBar(x0, y0, x1, y1 int, color byte)
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(dev display.Display) Drawer {
|
func New(dev display.Display) Drawer {
|
||||||
@ -37,6 +40,14 @@ func (d *drawer) GetDisplay() display.Display {
|
|||||||
return d.dev
|
return d.dev
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *drawer) W() int {
|
||||||
|
return d.img.Rect.Dx()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *drawer) H() int {
|
||||||
|
return d.img.Rect.Dy()
|
||||||
|
}
|
||||||
|
|
||||||
func (d *drawer) Clear() {
|
func (d *drawer) Clear() {
|
||||||
d.dev.LockImg()
|
d.dev.LockImg()
|
||||||
defer d.dev.UnlockImg()
|
defer d.dev.UnlockImg()
|
||||||
|
@ -11,6 +11,7 @@ const (
|
|||||||
LineGap = 1
|
LineGap = 1
|
||||||
FontCharW = 5
|
FontCharW = 5
|
||||||
FontCharH = 7
|
FontCharH = 7
|
||||||
|
LineH = FontCharH + LineGap
|
||||||
)
|
)
|
||||||
|
|
||||||
// Standard ASCII 5x7 font
|
// Standard ASCII 5x7 font
|
||||||
|
39
main.go
39
main.go
@ -4,12 +4,12 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"math"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"gitea.unprism.ru/yotia/display-test/components"
|
||||||
"gitea.unprism.ru/yotia/display-test/display"
|
"gitea.unprism.ru/yotia/display-test/display"
|
||||||
"gitea.unprism.ru/yotia/display-test/drawer"
|
"gitea.unprism.ru/yotia/display-test/drawer"
|
||||||
|
|
||||||
@ -94,22 +94,35 @@ func Init(ctx context.Context) error {
|
|||||||
|
|
||||||
d.Clear()
|
d.Clear()
|
||||||
//d.FillBar(0, 0, 10, 10, 1)
|
//d.FillBar(0, 0, 10, 10, 1)
|
||||||
stTime := time.Now()
|
d.PutText(2, 0, "12345678901234567890")
|
||||||
for {
|
d.PutText(2, drawer.LineH, "WWWWWWWWWWWWWWWWWWWWWWW")
|
||||||
|
d.PutText(1, drawer.LineH*2, "Progress bar")
|
||||||
|
d.Flush()
|
||||||
|
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
|
pb, err := components.NewProgressBar(d, 0, drawer.LineH*3, d.W(), drawer.LineH)
|
||||||
|
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create progress bar: %w", err)
|
||||||
|
}
|
||||||
|
for x := 0; x < 100; x++ {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return nil
|
return nil
|
||||||
default:
|
case <-time.After(10 * time.Millisecond):
|
||||||
t := float64(time.Since(stTime).Milliseconds()) / 1000
|
pb.SetProgress(float64(x) / 100)
|
||||||
x := math.Sin(t)*10 + 5
|
|
||||||
y := math.Cos(t)*10 + 5
|
}
|
||||||
d.Clear()
|
}
|
||||||
d.PutText(int(x), int(y), "CGSG forever")
|
pb.Clear()
|
||||||
stT := time.Now()
|
pb.Close()
|
||||||
|
d.FillBar(0, drawer.LineH*2, d.W(), drawer.LineH*3, 0)
|
||||||
|
d.PutText(1, drawer.LineH*2, "Done")
|
||||||
d.Flush()
|
d.Flush()
|
||||||
logger.Println("Iteration time:", time.Since(stT))
|
time.Sleep(100 * time.Millisecond)
|
||||||
}
|
|
||||||
}
|
|
||||||
//d.PutText(0, 0, "можно 4 строки впихнуть")
|
//d.PutText(0, 0, "можно 4 строки впихнуть")
|
||||||
//d.PutText(0, drawer.FontCharH+drawer.LineGap, "каждая буква 5x7")
|
//d.PutText(0, drawer.FontCharH+drawer.LineGap, "каждая буква 5x7")
|
||||||
//d.PutText(0, (drawer.FontCharH+drawer.LineGap)*2, "+ русский через жопу")
|
//d.PutText(0, (drawer.FontCharH+drawer.LineGap)*2, "+ русский через жопу")
|
||||||
|
Loading…
Reference in New Issue
Block a user