aboutsummaryrefslogtreecommitdiff
path: root/vendor/golang.org/x/exp/shiny/driver/windriver/texture.go
blob: 0155a662fac8774a6c444a9f2d28a791e22c051e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build windows

package windriver

import (
	"errors"
	"image"
	"image/color"
	"image/draw"
	"sync"
	"syscall"
	"unsafe"

	"golang.org/x/exp/shiny/driver/internal/win32"
	"golang.org/x/exp/shiny/screen"
)

type textureImpl struct {
	size   image.Point
	dc     syscall.Handle
	bitmap syscall.Handle

	mu       sync.Mutex
	released bool
}

type handleCreateTextureParams struct {
	size   image.Point
	dc     syscall.Handle
	bitmap syscall.Handle
	err    error
}

var msgCreateTexture = win32.AddScreenMsg(handleCreateTexture)

func newTexture(size image.Point) (screen.Texture, error) {
	p := handleCreateTextureParams{size: size}
	win32.SendScreenMessage(msgCreateTexture, 0, uintptr(unsafe.Pointer(&p)))
	if p.err != nil {
		return nil, p.err
	}
	return &textureImpl{
		size:   size,
		dc:     p.dc,
		bitmap: p.bitmap,
	}, nil
}

func handleCreateTexture(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) {
	// This code needs to run on Windows message pump thread.
	// Firstly, it calls GetDC(nil) and, according to Windows documentation
	// (https://msdn.microsoft.com/en-us/library/windows/desktop/dd144871(v=vs.85).aspx),
	// has to be released on the same thread.
	// Secondly, according to Windows documentation
	// (https://msdn.microsoft.com/en-us/library/windows/desktop/dd183489(v=vs.85).aspx),
	// ... thread that calls CreateCompatibleDC owns the HDC that is created.
	// When this thread is destroyed, the HDC is no longer valid. ...
	// So making Windows message pump thread own returned HDC makes DC
	// live as long as we want to.
	p := (*handleCreateTextureParams)(unsafe.Pointer(lParam))

	screenDC, err := win32.GetDC(0)
	if err != nil {
		p.err = err
		return
	}
	defer win32.ReleaseDC(0, screenDC)

	dc, err := _CreateCompatibleDC(screenDC)
	if err != nil {
		p.err = err
		return
	}
	bitmap, err := _CreateCompatibleBitmap(screenDC, int32(p.size.X), int32(p.size.Y))
	if err != nil {
		_DeleteDC(dc)
		p.err = err
		return
	}
	p.dc = dc
	p.bitmap = bitmap
}

func (t *textureImpl) Bounds() image.Rectangle {
	return image.Rectangle{Max: t.size}
}

func (t *textureImpl) Fill(r image.Rectangle, c color.Color, op draw.Op) {
	err := t.update(func(dc syscall.Handle) error {
		return fill(dc, r, c, op)
	})
	if err != nil {
		panic(err) // TODO handle error
	}
}

func (t *textureImpl) Release() {
	if err := t.release(); err != nil {
		panic(err) // TODO handle error
	}
}

func (t *textureImpl) release() error {
	t.mu.Lock()
	defer t.mu.Unlock()

	if t.released {
		return nil
	}
	t.released = true

	err := _DeleteObject(t.bitmap)
	if err != nil {
		return err
	}
	return _DeleteDC(t.dc)
}

func (t *textureImpl) Size() image.Point {
	return t.size
}

func (t *textureImpl) Upload(dp image.Point, src screen.Buffer, sr image.Rectangle) {
	err := t.update(func(dc syscall.Handle) error {
		return src.(*bufferImpl).blitToDC(dc, dp, sr)
	})
	if err != nil {
		panic(err) // TODO handle error
	}
}

// update prepares texture t for update and executes f over texture device
// context dc in a safe manner.
func (t *textureImpl) update(f func(dc syscall.Handle) error) (retErr error) {
	t.mu.Lock()
	defer t.mu.Unlock()

	if t.released {
		return errors.New("windriver: Texture.Upload called after Texture.Release")
	}

	// Select t.bitmap into t.dc, so our drawing gets recorded
	// into t.bitmap and not into 1x1 default bitmap created
	// during CreateCompatibleDC call.
	prev, err := _SelectObject(t.dc, t.bitmap)
	if err != nil {
		return err
	}
	defer func() {
		_, err2 := _SelectObject(t.dc, prev)
		if retErr == nil {
			retErr = err2
		}
	}()

	return f(t.dc)
}