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
|
"""
Common tools for drawing compatible frames in a Component's frameRender()
"""
from PyQt6 import QtGui
from PIL import Image
from PIL.ImageQt import ImageQt
from PyQt6 import QtCore
import sys
import os
import math
import logging
from .. import core
log = logging.getLogger("AVP.Toolkit.Frame")
class FramePainter(QtGui.QPainter):
"""
A QPainter for a blank frame, which can be converted into a
Pillow image with finalize()
"""
def __init__(self, width, height):
image = BlankFrame(width, height)
log.debug("Creating QImage from PIL image object")
self.image = ImageQt(image)
super().__init__(self.image)
def setPen(self, penStyle):
if type(penStyle) is tuple:
super().setPen(PaintColor(*penStyle))
else:
super().setPen(penStyle)
def finalize(self):
log.verbose("Finalizing FramePainter")
buffer = QtCore.QBuffer()
buffer.open(QtCore.QBuffer.OpenModeFlag.ReadWrite)
self.image.save(buffer, "PNG")
import io
frame = Image.open(io.BytesIO(buffer.data()))
buffer.close()
self.end()
return frame
imBytes = self.image.bits().asstring(self.image.byteCount())
frame = Image.frombytes(
"RGBA", (self.image.width(), self.image.height()), imBytes
)
self.end()
return frame
class PaintColor(QtGui.QColor):
"""
Subclass of QtGui.QColor with an added scale() method
Previously this class reversed the painter colour to solve
hardware issues related to endianness,
but Qt appears to deal with this itself nowadays
"""
def __init__(self, r, g, b, a=255):
super().__init__(r, g, b, a)
def scale(scalePercent, width, height, returntype=None):
width = (float(width) / 100.0) * float(scalePercent)
height = (float(height) / 100.0) * float(scalePercent)
if returntype == str:
return (str(math.ceil(width)), str(math.ceil(height)))
elif returntype == int:
return (math.ceil(width), math.ceil(height))
else:
return (width, height)
def defaultSize(framefunc):
"""Makes width/height arguments optional"""
def decorator(*args):
if len(args) < 2:
newArgs = list(args)
if len(args) == 0 or len(args) == 1:
height = int(core.Core.settings.value("outputHeight"))
newArgs.append(height)
if len(args) == 0:
width = int(core.Core.settings.value("outputWidth"))
newArgs.insert(0, width)
args = tuple(newArgs)
return framefunc(*args)
return decorator
def FloodFrame(width, height, RgbaTuple):
return Image.new("RGBA", (width, height), RgbaTuple)
@defaultSize
def BlankFrame(width, height):
"""The base frame used by each component to start drawing."""
return FloodFrame(width, height, (0, 0, 0, 0))
@defaultSize
def Checkerboard(width, height):
"""
A checkerboard to represent transparency to the user.
"""
# TODO: Would be cool to generate this image with numpy instead.
log.debug("Creating new %s*%s checkerboard" % (width, height))
image = FloodFrame(1920, 1080, (0, 0, 0, 0))
image.paste(Image.open(os.path.join(core.Core.wd, "gui", "background.png")), (0, 0))
image = image.resize((width, height))
return image
|