-
Notifications
You must be signed in to change notification settings - Fork 2
/
bitmap_to_surface.py
113 lines (105 loc) · 4.49 KB
/
bitmap_to_surface.py
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
# FT_Bitmap to CAIRO_SURFACE_TYPE_IMAGE module
# ============================================
#
# Copyright 2017 Hin-Tak Leung
#
# FreeType is under FTL (BSD license with an advertising clause) or GPLv2+.
# Cairo is under LGPLv2 or MPLv1.1.
#
# This is a heavily modified copy of a few routines from Lawrence D'Oliveiro[1],
# adjusted for freetype-py, and bugfix/workaround for mono-rendering [2].
#
# The bugfix/workaround requires libtiff on small-endian platforms.
#
# TODO: Look into using FreeType's FT_Bitmap_Convert() instead. However, libtiff
# is common enough, and probably not important.
#
#[1] https://github.com/ldo/python_freetype
# https://github.com/ldo/python_freetype_examples
#
#[2] https://github.com/ldo/python_freetype/issues/1
# https://github.com/ldo/python_freetype_examples/issues/1
#
'''
FT_Bitmap to CAIRO_SURFACE_TYPE_IMAGE module
============================================
Converting from Freetype's FT_Bitmap to Cairo's CAIRO_SURFACE_TYPE_IMAGE
Usage:
from bitmap_to_surface import make_image_surface
Works with cairocffi too. (Replace "from cairo ..." with "from cairocffi ...")
Limitation: Surface.create_for_data is not in the "python 3, pycairo < 1.11" combo.
'''
from freetype import FT_PIXEL_MODE_MONO, FT_PIXEL_MODE_GRAY, FT_PIXEL_MODE_BGRA, FT_Pointer, FT_Bitmap
#from freetype import get_handle, FT_Bitmap_Init, FT_Bitmap_Convert, FT_Exception
from cairo import ImageSurface, FORMAT_A1, FORMAT_A8, FORMAT_ARGB32
#from cairocffi import ImageSurface, FORMAT_A1, FORMAT_A8, FORMAT_ARGB32
from array import array
from ctypes import cast, memmove, CDLL, c_void_p, c_int, byref
from sys import byteorder
def make_image_surface(bitmap, copy = True) :
if ( type(bitmap) == FT_Bitmap ):
# bare FT_Bitmap
content = bitmap
else:
# wrapped Bitmap instance
content = bitmap._FT_Bitmap
"creates a Cairo ImageSurface containing (a copy of) the Bitmap pixels."
if ( False and ( byteorder == 'little' ) and (content.pixel_mode == FT_PIXEL_MODE_MONO ) ):
try:
libtiff = CDLL("not")
except:
library = get_handle( )
dest = c_void_p()
error = FT_Bitmap_Init( dest )
if error: raise FT_Exception( error )
error = FT_Bitmap_Convert( library, byref(content),
dest, 1 )
if error: raise FT_Exception( error )
content = dest
if content.pixel_mode == FT_PIXEL_MODE_MONO :
cairo_format = FORMAT_A1
elif content.pixel_mode == FT_PIXEL_MODE_GRAY :
cairo_format = FORMAT_A8
elif content.pixel_mode == FT_PIXEL_MODE_BGRA : # small-endian
cairo_format = FORMAT_ARGB32 # platform native
else :
raise NotImplementedError("unsupported bitmap format %d" % content.pixel_mode)
src_pitch = content.pitch
dst_pitch = ImageSurface.format_stride_for_width(cairo_format, content.width)
if not copy and dst_pitch == src_pitch and content.buffer != None :
pixels = content.buffer
else :
pixels = to_array(content, content.pixel_mode, dst_pitch)
result = ImageSurface.create_for_data(
pixels, cairo_format,
content.width, content.rows,
dst_pitch)
return result
def to_array(content, pixel_mode, dst_pitch = None) :
"returns a Python array object containing a copy of the Bitmap pixels."
if dst_pitch == None :
dst_pitch = content.pitch
buffer_size = content.rows * dst_pitch
buffer = array("B", b"0" * buffer_size)
dstaddr = buffer.buffer_info()[0]
srcaddr = cast(content.buffer, FT_Pointer).value
src_pitch = content.pitch
if dst_pitch == src_pitch :
memmove(dstaddr, srcaddr, buffer_size)
else :
# have to copy a row at a time
if src_pitch < 0 or dst_pitch < 0 :
raise NotImplementedError("can't cope with negative bitmap pitch")
assert dst_pitch > src_pitch
for i in range(content.rows) :
memmove(dstaddr, srcaddr, src_pitch)
dstaddr += dst_pitch
srcaddr += src_pitch
# pillow/PIL itself requires libtiff so it is assumed to be around.
# swap the bit-order from freetype's (MSB) to cairo's (host order) if needed
if ( ( byteorder == 'little' ) and (pixel_mode == FT_PIXEL_MODE_MONO ) ):
libtiff = CDLL("libtiff.so.5")
libtiff.TIFFReverseBits.restype = None
libtiff.TIFFReverseBits.argtypes = (c_void_p, c_int)
libtiff.TIFFReverseBits(buffer.buffer_info()[0], buffer_size)
return buffer