232 lines
7.6 KiB
Python
232 lines
7.6 KiB
Python
from PIL import Image, ImageDraw, ImageFont
|
|
import socket
|
|
import binascii
|
|
import io
|
|
import time
|
|
import re
|
|
import numpy as np
|
|
|
|
import PIL.ImageOps
|
|
|
|
|
|
class FlipdotSender(object):
|
|
'''
|
|
classdocs
|
|
'''
|
|
|
|
C_BLACK = 0
|
|
C_WHITE = 255
|
|
|
|
global threadrunning
|
|
threadrunning=False
|
|
|
|
lastimgmap = []
|
|
|
|
def __init__(self, udphost, udpport, img_size=(160,48), font_size=9, font_size_scroll=12,
|
|
font_offset1=(0,0), font_offset2=(0,8),
|
|
#font_family='/usr/share/fonts/gnu-free/FreeMono.ttf',
|
|
#font_family='/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf',
|
|
font_family='freefont/FreeMono.ttf',
|
|
|
|
chars_per_line=11):
|
|
'''
|
|
Constructor
|
|
'''
|
|
|
|
self._udphost = udphost
|
|
if not type(udpport) is int or udpport > 65536:
|
|
raise TypeError('port has to be int and > 65536 !!')
|
|
self._udpport = udpport
|
|
self._img_size = img_size
|
|
self._font_size = font_size
|
|
self._font_size_scroll = font_size_scroll
|
|
self._font_offset1 = font_offset1
|
|
self._font_offset2 = font_offset2
|
|
self._font_family = font_family
|
|
self._chars_per_line = chars_per_line
|
|
|
|
self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
|
|
def stopAnimation(self):
|
|
global threadrunning
|
|
threadrunning=False #tried to stop a running animation
|
|
|
|
|
|
def _list2byte(self, l):
|
|
byte = 0
|
|
i = 0
|
|
for i in range(8):
|
|
byte += 2**(7-i) if l[i] else 0
|
|
return byte
|
|
|
|
def _array2packet(self, a):
|
|
return [self._list2byte(a[i*8:i*8+8]) for i in range(int(len(a)/8))]
|
|
|
|
'''
|
|
def _send(self, image): #old function, backup
|
|
imgmap = []
|
|
for pixel in image.getdata():
|
|
r, g, b, a = pixel
|
|
if r == FlipdotSender.C_WHITE:
|
|
imgmap.append(1)
|
|
else:
|
|
imgmap.append(0)
|
|
|
|
lastimgmap=imgmap
|
|
|
|
packet = self._array2packet(imgmap)
|
|
|
|
self._sock.sendto(bytes(packet), (self._udphost, self._udpport))'''
|
|
|
|
def _send(self, image,fadespeed=0,invert=False): #changes slowly 'fadespeed'-pixels at a time
|
|
global threadrunning
|
|
#if fadespeed=0 -> change instant.
|
|
#time to change= 1280/25*0.2
|
|
imgmap = []
|
|
for pixel in image.getdata():
|
|
r, g, b, a = pixel
|
|
if r >= 127:
|
|
if invert:
|
|
imgmap.append(0)
|
|
else:
|
|
imgmap.append(1)
|
|
else:
|
|
if invert:
|
|
imgmap.append(1)
|
|
else:
|
|
imgmap.append(0)
|
|
|
|
imgmaptmp=FlipdotSender.lastimgmap
|
|
|
|
|
|
if fadespeed>0:
|
|
threadrunning=True
|
|
#diff=np.sum(np.array(imgmaptmp) != np.array(imgmap)) #different pixels
|
|
pixelchangeind=np.arange(self._img_size[0]*self._img_size[1])
|
|
np.random.shuffle(pixelchangeind)
|
|
for _i,ind in enumerate(pixelchangeind):
|
|
if threadrunning==False:
|
|
break #stop this for
|
|
|
|
if ind<len(imgmaptmp): #imgmaptmp is not empty (normally at first run)
|
|
imgmaptmp[ind]=imgmap[ind]
|
|
if _i%fadespeed==0:
|
|
packet = self._array2packet(imgmaptmp)
|
|
self._sock.sendto(bytes(packet), (self._udphost, self._udpport))
|
|
time.sleep(0.2)
|
|
if threadrunning==True: #if animation hasnt been cancelled
|
|
self.sendPacket(imgmap) #send packet and save last-imagemap
|
|
threadrunning=False
|
|
else:
|
|
self.sendPacket(imgmap) #send packet and save last-imagemap
|
|
|
|
|
|
def sendPacket(self, imgmap):
|
|
packet = self._array2packet(imgmap)
|
|
self._sock.sendto(bytes(packet), (self._udphost, self._udpport))
|
|
|
|
FlipdotSender.lastimgmap=imgmap
|
|
|
|
def send_bytes(self, img):
|
|
imgmap = []
|
|
for pixel in img:
|
|
if pixel == "1":
|
|
imgmap.append(1)
|
|
else:
|
|
imgmap.append(0)
|
|
|
|
if len(img) < 1280:
|
|
imgmap = np.hstack((imgmap, np.zeros(1280-len(img), dtype=int)))
|
|
|
|
packet = self._array2packet(imgmap)
|
|
|
|
self._sock.sendto(bytes(packet), (self._udphost, self._udpport))
|
|
|
|
|
|
def send_text(self, text,fadespeed=0):
|
|
image = Image.new("RGBA", self._img_size, FlipdotSender.C_BLACK)
|
|
draw = ImageDraw.Draw(image)
|
|
draw.fontmode = "1" # No AA
|
|
|
|
font = ImageFont.truetype(self._font_family, self._font_size)
|
|
|
|
cut = self._chars_per_line
|
|
|
|
splitted_text = text.split("|")
|
|
|
|
draw.text(self._font_offset1, splitted_text[0], font=font, fill=FlipdotSender.C_WHITE)
|
|
if len(splitted_text)>1:
|
|
draw.text(self._font_offset2, splitted_text[1], font=font, fill=FlipdotSender.C_WHITE)
|
|
|
|
self._send(image,fadespeed)
|
|
|
|
def send_textFull(self, text,fadespeed=0):
|
|
image = Image.new("RGBA", self._img_size, FlipdotSender.C_BLACK)
|
|
draw = ImageDraw.Draw(image)
|
|
draw.fontmode = "1" # No AA
|
|
|
|
l=1000 #init very high
|
|
splitpoint=40 #very high
|
|
currentfontsize=12+1 #init with max font size
|
|
font = ImageFont.truetype(self._font_family, currentfontsize)
|
|
|
|
while(l>80): #while text too long and font not too small
|
|
if currentfontsize>8:
|
|
currentfontsize=currentfontsize-1 #reduce size and try if fits
|
|
else: #if fontsize too small, try cutting sentence (2 lines)
|
|
splitpoint=splitpoint-1
|
|
|
|
font = ImageFont.truetype(self._font_family, currentfontsize)
|
|
l=draw.textsize(text[0:splitpoint], font=font)[0]
|
|
print("Textlength="+str(l)+" split="+str(splitpoint))
|
|
|
|
if splitpoint==40: #not splitted
|
|
draw.text((0,int((16-currentfontsize)/2) ), text, font=font, fill=FlipdotSender.C_WHITE)
|
|
else:
|
|
draw.text((0,-1), text[0:splitpoint], font=font, fill=FlipdotSender.C_WHITE)
|
|
draw.text((0,-1+currentfontsize), text[splitpoint:], font=font, fill=FlipdotSender.C_WHITE)
|
|
|
|
self._send(image,fadespeed)
|
|
|
|
|
|
def send_marquee(self, str, speed=3):
|
|
global threadrunning
|
|
threadrunning=True
|
|
offset = self._img_size[0]
|
|
font = ImageFont.truetype(self._font_family, self._font_size_scroll)
|
|
|
|
while offset >= -font.getsize(str)[0]-speed and threadrunning==True:
|
|
image = Image.new("RGBA", self._img_size, FlipdotSender.C_BLACK)
|
|
draw = ImageDraw.Draw(image)
|
|
draw.fontmode = "1" # No AA
|
|
|
|
draw.text((offset,0), str, font=font, fill=FlipdotSender.C_WHITE)
|
|
self._send(image)
|
|
offset -= speed
|
|
time.sleep(0.15)
|
|
threadrunning=False
|
|
|
|
|
|
|
|
def send_img_from_filename(self, imgfilename,invert=False):
|
|
background = Image.new("RGBA", self._img_size, FlipdotSender.C_BLACK)
|
|
image = Image.open(imgfilename)
|
|
image = image.convert(mode='RGBA')
|
|
image = image.resize(self._img_size)
|
|
image.save('/tmp/send2.png', 'PNG')
|
|
background.paste(image, box=(0,0), mask=None)
|
|
background.save('/tmp/send.png', 'PNG')
|
|
|
|
self._send(background,0,invert)
|
|
|
|
def send_img(self, img):
|
|
background = Image.new("RGBA", self._img_size, FlipdotSender.C_BLACK)
|
|
stream = io.BytesIO(img)
|
|
image = Image.open(stream)
|
|
image = image.convert(mode='RGBA')
|
|
image = image.resize(self._img_size)
|
|
image.save('/tmp/send2.jpeg', 'JPEG')
|
|
background.paste(image, box=(0,0), mask=None)
|
|
background.save('/tmp/send.jpeg', 'JPEG')
|
|
|
|
self._send(background)
|