510 lines
15 KiB
Python
510 lines
15 KiB
Python
#--------------------------------------------------------------------
|
|
#
|
|
# PyGUI - Win32 - GDI Plus
|
|
#
|
|
#--------------------------------------------------------------------
|
|
|
|
from ctypes import *
|
|
from ctypes.wintypes import BOOL
|
|
try:
|
|
from numpy import ndarray, float32
|
|
except ImportError:
|
|
class ndarray(object):
|
|
pass
|
|
|
|
#wg = windll.gdiplus
|
|
wg = oledll.gdiplus
|
|
|
|
#--------------------------------------------------------------------
|
|
|
|
# enum Unit
|
|
|
|
UnitWorld = 0
|
|
UnitDisplay = 1
|
|
UnitPixel = 2
|
|
UnitPoint = 3
|
|
|
|
# enum FillMode
|
|
|
|
FillModeAlternate = 0
|
|
|
|
# enum CombineMode
|
|
|
|
CombineModeIntersect = 1
|
|
|
|
# enum MatrixOrder
|
|
|
|
MatrixOrderPrepend = 0
|
|
MatrixOrderAppend = 1
|
|
|
|
# Pixel Formats
|
|
|
|
# In-memory pixel data formats:
|
|
# bits 0-7 = format index
|
|
# bits 8-15 = pixel size (in bits)
|
|
# bits 16-23 = flags
|
|
# bits 24-31 = reserved
|
|
|
|
PixelFormatIndexed = 0x00010000 # Indexes into a palette
|
|
PixelFormatGDI = 0x00020000 # Is a GDI-supported format
|
|
PixelFormatAlpha = 0x00040000 # Has an alpha component
|
|
PixelFormatPAlpha = 0x00080000 # Pre-multiplied alpha
|
|
PixelFormatExtended = 0x00100000 # Extended color 16 bits/channel
|
|
PixelFormatCanonical = 0x00200000
|
|
|
|
PixelFormat1bppIndexed = (1 | ( 1 << 8) | PixelFormatIndexed | PixelFormatGDI)
|
|
PixelFormat4bppIndexed = (2 | ( 4 << 8) | PixelFormatIndexed | PixelFormatGDI)
|
|
PixelFormat8bppIndexed = (3 | ( 8 << 8) | PixelFormatIndexed | PixelFormatGDI)
|
|
PixelFormat16bppGrayScale = (4 | (16 << 8) | PixelFormatExtended)
|
|
PixelFormat16bppRGB555 = (5 | (16 << 8) | PixelFormatGDI)
|
|
PixelFormat16bppRGB565 = (6 | (16 << 8) | PixelFormatGDI)
|
|
PixelFormat16bppARGB1555 = (7 | (16 << 8) | PixelFormatAlpha | PixelFormatGDI)
|
|
PixelFormat24bppRGB = (8 | (24 << 8) | PixelFormatGDI)
|
|
PixelFormat32bppRGB = (9 | (32 << 8) | PixelFormatGDI)
|
|
PixelFormat32bppARGB = (10 | (32 << 8) | PixelFormatAlpha | PixelFormatGDI | PixelFormatCanonical)
|
|
PixelFormat32bppPARGB = (11 | (32 << 8) | PixelFormatAlpha | PixelFormatPAlpha | PixelFormatGDI)
|
|
PixelFormat48bppRGB = (12 | (48 << 8) | PixelFormatExtended)
|
|
PixelFormat64bppARGB = (13 | (64 << 8) | PixelFormatAlpha | PixelFormatCanonical | PixelFormatExtended)
|
|
PixelFormat64bppPARGB = (14 | (64 << 8) | PixelFormatAlpha | PixelFormatPAlpha | PixelFormatExtended)
|
|
|
|
# enum FontStyle
|
|
|
|
FontStyleBold = 1
|
|
FontStyleItalic = 2
|
|
FontStyleUnderline = 4
|
|
FontStyleStrikeout = 8
|
|
|
|
class PointF(Structure):
|
|
_fields_ = [("x", c_float), ("y", c_float)]
|
|
|
|
class RectF(Structure):
|
|
_fields_ = [
|
|
("x", c_float), ("y", c_float),
|
|
("width", c_float), ("height", c_float)]
|
|
|
|
#--------------------------------------------------------------------
|
|
|
|
def rect_args(rect):
|
|
l, t, r, b = rect
|
|
return c_float(l), c_float(t), c_float(r - l), c_float(b - t)
|
|
|
|
def points_args(points):
|
|
if isinstance(points, ndarray) and points.flags['C_CONTIGUOUS'] and points.dtype == float32:
|
|
#print "GDIPlus.points_args: using ndarray" ###
|
|
n = points.size // 2
|
|
buf = points.ctypes.data
|
|
else:
|
|
n = len(points)
|
|
buf = (PointF * n)()
|
|
for i, p in enumerate(points):
|
|
buf[i].x, buf[i].y = p
|
|
return buf, n
|
|
|
|
def arc_args(c, r, a0, a1):
|
|
x, y = c
|
|
d = c_float(2 * r)
|
|
return c_float(x - r), c_float(y - r), d, d, \
|
|
c_float(a0), c_float((a1 - a0) % 360.0)
|
|
|
|
#--------------------------------------------------------------------
|
|
|
|
class GdiplusStartupInput(Structure):
|
|
|
|
_fields_ = [
|
|
('GdiplusVersion', c_uint),
|
|
('DebugEventCallback', c_void_p),
|
|
('SuppressBackgroundThread', BOOL),
|
|
('SuppressExternalCodecs', BOOL),
|
|
]
|
|
|
|
def __init__(self):
|
|
Structure.__init__(self)
|
|
self.GdiplusVersion = 1
|
|
self.DebugEventCallback = None
|
|
self.SuppressBackgroundThread = 0
|
|
self.SuppressExternalCodecs = 0
|
|
|
|
StartupInput = GdiplusStartupInput()
|
|
token = c_ulong()
|
|
wg.GdiplusStartup(pointer(token), pointer(StartupInput), None)
|
|
|
|
#--------------------------------------------------------------------
|
|
|
|
class Pen(object):
|
|
|
|
def __init__(self, argb, size):
|
|
ptr = c_void_p()
|
|
wg.GdipCreatePen1(argb, c_float(size), UnitWorld, byref(ptr))
|
|
self.ptr = ptr
|
|
|
|
def __del__(self, wg = wg):
|
|
wg.GdipDeletePen(self.ptr)
|
|
|
|
#--------------------------------------------------------------------
|
|
|
|
class SolidBrush(object):
|
|
|
|
def __init__(self, argb):
|
|
ptr = c_void_p()
|
|
wg.GdipCreateSolidFill(argb, byref(ptr))
|
|
self.ptr = ptr
|
|
|
|
def __del__(self, wg = wg):
|
|
wg.GdipDeleteBrush(self.ptr)
|
|
|
|
def __str__(self):
|
|
argb = c_ulong()
|
|
wg.GdipGetSolidFillColor(self.ptr, byref(argb))
|
|
return "<SolidBrush argb=0x%08x>" % argb.value
|
|
|
|
#--------------------------------------------------------------------
|
|
|
|
class TextureBrush(object):
|
|
|
|
def __init__(self, image):
|
|
ptr = c_void_p()
|
|
wg.GdipCreateTexture(image.ptr, 4, byref(ptr))
|
|
self.ptr = ptr
|
|
|
|
def __del__(self, wg = wg):
|
|
wg.GdipDeleteBrush(self.ptr)
|
|
|
|
def __str__(self):
|
|
return "<TextureBrush>"
|
|
|
|
#--------------------------------------------------------------------
|
|
|
|
class Font(object):
|
|
|
|
def __init__(self, family, size, style):
|
|
uname = create_unicode_buffer(family)
|
|
fam = c_void_p()
|
|
wg.GdipCreateFontFamilyFromName(uname, None, byref(fam))
|
|
flags = 0
|
|
if 'bold' in style:
|
|
flags |= FontStyleBold
|
|
if 'italic' in style:
|
|
flags |= FontStyleItalic
|
|
ptr = c_void_p()
|
|
wg.GdipCreateFont(fam, c_float(size), flags, UnitWorld, byref(ptr))
|
|
self.ptr = ptr
|
|
wg.GdipDeleteFontFamily(fam)
|
|
|
|
def from_hdc(cls, hdc):
|
|
self = cls.__new__(cls)
|
|
ptr = c_void_p()
|
|
wg.GdipCreateFontFromDC(hdc, byref(ptr))
|
|
self.ptr = ptr
|
|
return self
|
|
|
|
from_hdc = classmethod(from_hdc)
|
|
|
|
def __del__(self, wg = wg):
|
|
wg.GdipDeleteFont(self.ptr)
|
|
|
|
#--------------------------------------------------------------------
|
|
|
|
class Image(object):
|
|
|
|
def __str__(self):
|
|
return "<Image 0x%x>" % self.ptr.value
|
|
|
|
def from_file(cls, path):
|
|
self = cls.__new__(cls)
|
|
ptr = c_void_p()
|
|
upath = create_unicode_buffer(path)
|
|
self._create_from_file(upath, ptr)
|
|
self.ptr = ptr
|
|
return self
|
|
|
|
from_file = classmethod(from_file)
|
|
|
|
def _create_from_file(self, upath, ptr):
|
|
wg.GdipLoadImageFromFile(upath, byref(ptr))
|
|
|
|
def __del__(self, wg = wg):
|
|
wg.GdipDisposeImage(self.ptr)
|
|
|
|
def GetWidth(self):
|
|
uint = c_uint()
|
|
wg.GdipGetImageWidth(self.ptr, byref(uint))
|
|
return uint.value
|
|
|
|
def GetHeight(self):
|
|
uint = c_uint()
|
|
wg.GdipGetImageHeight(self.ptr, byref(uint))
|
|
return uint.value
|
|
|
|
#--------------------------------------------------------------------
|
|
|
|
class Bitmap(Image):
|
|
|
|
def __init__(self, width, height):
|
|
ptr = c_void_p()
|
|
format = PixelFormat32bppARGB
|
|
wg.GdipCreateBitmapFromScan0(width, height, 0, format, None, byref(ptr))
|
|
self.ptr = ptr
|
|
#print "GDIPlus.Bitmap:", (width, height), repr(self), "ptr =", self.ptr ###
|
|
|
|
def _create_from_file(self, upath, ptr):
|
|
wg.GdipCreateBitmapFromFile(upath, byref(ptr))
|
|
|
|
def from_data(cls, width, height, format, data):
|
|
self = cls.__new__(cls)
|
|
ptr = c_void_p()
|
|
bits_per_pixel = (format >> 8) & 0xff
|
|
row_stride = (width * bits_per_pixel) >> 3
|
|
wg.GdipCreateBitmapFromScan0(width, height, row_stride, format, data, byref(ptr))
|
|
self.ptr = ptr
|
|
return self
|
|
|
|
from_data = classmethod(from_data)
|
|
|
|
def __str__(self):
|
|
return "<Bitmap 0x%x>" % self.ptr.value
|
|
|
|
def GetHICON(self):
|
|
hicon = c_ulong()
|
|
wg.GdipCreateHICONFromBitmap(self.ptr, byref(hicon))
|
|
return hicon.value
|
|
|
|
def GetPixel(self, x, y):
|
|
c = c_ulong()
|
|
wg.GdipBitmapGetPixel(self.ptr, x, y, byref(c))
|
|
return c.value
|
|
|
|
def SetPixel(self, x, y, c):
|
|
wg.GdipBitmapSetPixel(self.ptr, x, y, c)
|
|
|
|
#--------------------------------------------------------------------
|
|
|
|
class GraphicsPath(object):
|
|
|
|
def __init__(self):
|
|
ptr = c_void_p()
|
|
wg.GdipCreatePath(FillModeAlternate, byref(ptr))
|
|
self.ptr = ptr
|
|
|
|
def __del__(self, wg = wg):
|
|
wg.GdipDeletePath(self.ptr)
|
|
|
|
def Reset(self):
|
|
wg.GdipResetPath(self.ptr)
|
|
|
|
def StartFigure(self):
|
|
wg.GdipStartPathFigure(self.ptr)
|
|
|
|
def AddLine_4f(self, x0, y0, x1, y1):
|
|
wg.GdipAddPathLine(self.ptr,
|
|
c_float(x0), c_float(y0), c_float(x1), c_float(y1))
|
|
|
|
def AddBezier_4p(self, p0, p1, p2, p3):
|
|
x0, y0 = p0
|
|
x1, y1 = p1
|
|
x2, y2 = p2
|
|
x3, y3 = p3
|
|
wg.GdipAddPathBezier(self.ptr,
|
|
c_float(x0), c_float(y0), c_float(x1), c_float(y1),
|
|
c_float(x2), c_float(y2), c_float(x3), c_float(y3))
|
|
|
|
def AddBeziers_pv(self, points):
|
|
wg.GdipAddPathBeziers(self.ptr, *points_args(points))
|
|
|
|
def AddRectangle_r(self, rect):
|
|
wg.GdipAddPathRectangle(self.ptr, *rect_args(rect))
|
|
|
|
def AddEllipse_r(self, rect):
|
|
wg.GdipAddPathEllipse(self.ptr, *rect_args(rect))
|
|
|
|
def AddArc_p3f(self, c, r, a0, a1):
|
|
wg.GdipAddPathArc(self.ptr, *arc_args(c, r, a0, a1))
|
|
|
|
def AddPie_p3f(self, c, r, a0, a1):
|
|
wg.GdipAddPathPie(self.ptr, *arc_args(c, r, a0, a1))
|
|
|
|
def AddLines_pv(self, points):
|
|
wg.GdipAddPathLine2(self.ptr, *points_args(points))
|
|
|
|
def AddPolygon_pv(self, points):
|
|
wg.GdipAddPathPolygon(self.ptr, *points_args(points))
|
|
|
|
def CloseFigure(self):
|
|
wg.GdipClosePathFigure(self.ptr)
|
|
|
|
def GetLastPoint(self):
|
|
p = PointF()
|
|
wg.GdipGetPathLastPoint(self.ptr, byref(p))
|
|
return p.x, p.y
|
|
|
|
#--------------------------------------------------------------------
|
|
|
|
class Graphics(object):
|
|
|
|
def from_hdc(cls, hdc):
|
|
self = cls.__new__(cls)
|
|
ptr = c_void_p()
|
|
wg.GdipCreateFromHDC(c_ulong(hdc), byref(ptr))
|
|
self.ptr = ptr
|
|
return self
|
|
|
|
from_hdc = classmethod(from_hdc)
|
|
|
|
def from_dc(cls, dc):
|
|
return cls.from_hdc(dc.GetSafeHdc())
|
|
|
|
from_dc = classmethod(from_dc)
|
|
|
|
def from_image(cls, image):
|
|
#print "Graphics.from_image:", repr(image) ###
|
|
#print "...", image ###
|
|
self = cls.__new__(cls)
|
|
ptr = c_void_p()
|
|
wg.GdipGetImageGraphicsContext(image.ptr, byref(ptr))
|
|
self.ptr = ptr
|
|
return self
|
|
|
|
from_image = classmethod(from_image)
|
|
|
|
def __del__(self, wg = wg):
|
|
wg.GdipDeleteGraphics(self.ptr)
|
|
|
|
def __str__(self):
|
|
return "<Graphics 0x%x>" % self.ptr.value
|
|
|
|
def GetHDC(self):
|
|
hdc = c_long()
|
|
wg.GdipGetDC(self.ptr, byref(hdc))
|
|
return hdc.value
|
|
|
|
def ReleaseHDC(self, hdc):
|
|
wg.GdipReleaseDC(self.ptr, hdc)
|
|
|
|
def GetDpiX(self):
|
|
result = c_float()
|
|
wg.GdipGetDpiX(self.ptr, byref(result))
|
|
return result.value
|
|
|
|
def GetDpiY(self):
|
|
result = c_float()
|
|
wg.GdipGetDpiY(self.ptr, byref(result))
|
|
return result.value
|
|
|
|
def SetPageUnit(self, unit):
|
|
self.unit = unit
|
|
wg.GdipSetPageUnit(self.ptr, unit)
|
|
|
|
def GetClipBounds(self):
|
|
r = RectF()
|
|
wg.GdipGetClipBounds(self.ptr, byref(r))
|
|
return (r.x, r.y, r.x + r.width, r.y + r.height)
|
|
|
|
def Save(self):
|
|
state = c_uint()
|
|
wg.GdipSaveGraphics(self.ptr, byref(state))
|
|
return state.value
|
|
|
|
def Restore(self, state):
|
|
wg.GdipRestoreGraphics(self.ptr, state)
|
|
|
|
def DrawImage_rr(self, image, dst_rect, src_rect):
|
|
sl, st, sr, sb = src_rect
|
|
dl, dt, dr, db = dst_rect
|
|
wg.GdipDrawImageRectRect(self.ptr, image.ptr,
|
|
c_float(dl), c_float(dt), c_float(dr - dl), c_float(db - dt),
|
|
c_float(sl), c_float(st), c_float(sr - sl), c_float(sb - st),
|
|
UnitPixel, None, None, None)
|
|
|
|
def DrawPath(self, pen, path):
|
|
wg.GdipDrawPath(self.ptr, pen.ptr, path.ptr)
|
|
|
|
def FillPath(self, brush, path):
|
|
wg.GdipFillPath(self.ptr, brush.ptr, path.ptr)
|
|
|
|
def DrawAndMeasureStringWidth_2f(self, text, font, x, y, brush):
|
|
wtext = unicode(text)
|
|
n = len(text)
|
|
pos = PointF(x, y)
|
|
flags = 5 # DriverStringOptions CmapLookup+RealizedAdvance
|
|
b = RectF()
|
|
wg.GdipDrawDriverString(self.ptr, wtext, n, font.ptr, brush.ptr,
|
|
byref(pos), flags, None)
|
|
wg.GdipMeasureDriverString(self.ptr, wtext, n, font.ptr, byref(pos),
|
|
flags, None, byref(b))
|
|
return b.width
|
|
|
|
def MeasureStringWidth(self, text, font):
|
|
wtext = unicode(text)
|
|
n = len(text)
|
|
pos = PointF(0, 0)
|
|
flags = 5 # DriverStringOptions CmapLookup+RealizedAdvance
|
|
b = RectF()
|
|
wg.GdipMeasureDriverString(self.ptr, wtext, n, font.ptr, byref(pos),
|
|
flags, None, byref(b))
|
|
return b.width
|
|
|
|
def SetClip_PI(self, path):
|
|
wg.GdipSetClipPath(self.ptr, path.ptr, CombineModeIntersect)
|
|
|
|
def SetClip_rI(self, rect):
|
|
x, y, w, h = rect_args(rect)
|
|
wg.GdipSetClipRect(self.ptr, x, y, w, h, CombineModeIntersect)
|
|
|
|
def DrawRectangle_r(self, pen, rect):
|
|
wg.GdipDrawRectangle(self.ptr, pen.ptr, *rect_args(rect))
|
|
|
|
def FillRectangle_r(self, brush, rect):
|
|
#print "Graphics.FillRectangle_r:", self, brush, rect ###
|
|
#print "... clip bounds =", self.GetClipBounds() ###
|
|
wg.GdipFillRectangle(self.ptr, brush.ptr, *rect_args(rect))
|
|
|
|
def DrawEllipse_r(self, pen, rect):
|
|
wg.GdipDrawEllipse(self.ptr, pen.ptr, *rect_args(rect))
|
|
|
|
def FillEllipse_r(self, brush, rect):
|
|
wg.GdipFillEllipse(self.ptr, brush.ptr, *rect_args(rect))
|
|
|
|
def DrawArc_3pf(self, pen, c, r, a0, a1):
|
|
wg.GdipDrawArc(self.ptr, pen.ptr, *arc_args(c, r, a0, a1))
|
|
|
|
def DrawPie_p3f(self, pen, c, r, a0, a1):
|
|
wg.GdipDrawPie(self.ptr, pen.ptr, *arc_args(c, r, a0, a1))
|
|
|
|
def FillPie_p3f(self, brush, c, r, a0, a1):
|
|
wg.GdipFillPie(self.ptr, brush.ptr, *arc_args(c, r, a0, a1))
|
|
|
|
def DrawPolygon_pv(self, pen, points):
|
|
wg.GdipDrawPolygon(self.ptr, pen.ptr, *points_args(points))
|
|
|
|
def FillPolygon_pv(self, brush, points):
|
|
buf, n = points_args(points)
|
|
wg.GdipFillPolygon(self.ptr, brush.ptr, buf, n, FillModeAlternate)
|
|
|
|
def DrawBeziers_pv(self, pen, points):
|
|
wg.GdipDrawBeziers(self.ptr, pen.ptr, *points_args(points))
|
|
|
|
def DrawLines_pv(self, pen, points):
|
|
wg.GdipDrawLines(self.ptr, pen.ptr, *points_args(points))
|
|
|
|
def Translate_2f(self, dx, dy):
|
|
wg.GdipTranslateWorldTransform(self.ptr, c_float(dx), c_float(dy),
|
|
MatrixOrderAppend)
|
|
|
|
def Scale_2f(self, sx, sy):
|
|
wg.GdipScaleWorldTransform(self.ptr, c_float(sx), c_float(sy),
|
|
MatrixOrderAppend)
|
|
|
|
def Rotate_1f(self, r):
|
|
wg.GdipRotateWorldTransform(self.ptr, c_float(r),
|
|
MatrixOrderAppend)
|
|
|
|
|
|
|
|
def GetTransform(self):
|
|
matrix = c_void_p()
|
|
elems = (c_float * 6)()
|
|
wg.GdipCreateMatrix(byref(matrix))
|
|
wg.GdipGetWorldTransform(self.ptr, matrix)
|
|
wg.GdipGetMatrixElements(matrix, elems)
|
|
wg.GdipDeleteMatrix(matrix)
|
|
return list(elems)
|