fully implemented apng character support
This commit is contained in:
parent
27fa0104a4
commit
82cdb793d8
82
gameview.py
82
gameview.py
@ -5,6 +5,8 @@ from os.path import exists
|
||||
from functools import partial
|
||||
from ConfigParser import ConfigParser
|
||||
|
||||
import images
|
||||
|
||||
AOpath = "base/"
|
||||
#AOpath = 'I:/aovanilla1.7.5/client/base/'
|
||||
|
||||
@ -120,7 +122,7 @@ class ChatLogs(QtGui.QTextEdit):
|
||||
self.type = logtype
|
||||
self.savelog = ini.read_ini_bool(AOpath+"AO2XP.ini", "General", "save logs")
|
||||
self.combinelog = ini.read_ini_bool(AOpath+"AO2XP.ini", "General", "combined logs")
|
||||
if not os.path.exists("chatlogs"):
|
||||
if not exists("chatlogs"):
|
||||
os.mkdir("chatlogs")
|
||||
|
||||
if self.savelog:
|
||||
@ -164,8 +166,10 @@ class ChatLogs(QtGui.QTextEdit):
|
||||
|
||||
class AOCharMovie(QtGui.QLabel):
|
||||
done = QtCore.pyqtSignal()
|
||||
use_pillow = False
|
||||
use_pillow = 0
|
||||
pillow_frames = []
|
||||
pillow_frame = 0
|
||||
pillow_speed = 0
|
||||
|
||||
def __init__(self, parent):
|
||||
QtGui.QLabel.__init__(self, parent)
|
||||
@ -183,6 +187,7 @@ class AOCharMovie(QtGui.QLabel):
|
||||
self.pillow_timer.setSingleShot(True)
|
||||
|
||||
self.preanim_timer.timeout.connect(self.timer_done)
|
||||
self.pillow_timer.timeout.connect(self.pillow_frame_change)
|
||||
self.m_movie.frameChanged.connect(self.frame_change)
|
||||
|
||||
def set_flipped(self, flip):
|
||||
@ -200,6 +205,7 @@ class AOCharMovie(QtGui.QLabel):
|
||||
p_emote = emote
|
||||
|
||||
self.pillow_frames = []
|
||||
self.pillow_frame = 0
|
||||
|
||||
original_path = AOpath+"characters/"+p_char+"/"+emote_prefix+p_emote+".gif"
|
||||
alt_path = AOpath+"characters/"+p_char+"/"+p_emote+".png"
|
||||
@ -210,7 +216,7 @@ class AOCharMovie(QtGui.QLabel):
|
||||
|
||||
if exists(apng_path):
|
||||
gif_path = apng_path
|
||||
self.use_pillow = True
|
||||
self.use_pillow = 1
|
||||
else:
|
||||
if ini.read_ini_bool(AOpath+"AO2XP.ini", "General", "download characters"):
|
||||
url = "http://s3.wasabisys.com/webao/base/characters/"+p_char.lower()+"/"+emote_prefix+p_emote.lower()+".apng"
|
||||
@ -221,7 +227,7 @@ class AOCharMovie(QtGui.QLabel):
|
||||
|
||||
if exists(webp_path):
|
||||
gif_path = webp_path
|
||||
self.use_pillow = True
|
||||
self.use_pillow = 2
|
||||
else:
|
||||
if ini.read_ini_bool(AOpath+"AO2XP.ini", "General", "download characters"):
|
||||
url = "http://s3.wasabisys.com/webao/base/characters/"+p_char.lower()+"/"+p_emote.lower()+".webp"
|
||||
@ -232,7 +238,7 @@ class AOCharMovie(QtGui.QLabel):
|
||||
|
||||
if exists(original_path):
|
||||
gif_path = original_path
|
||||
self.use_pillow = False
|
||||
self.use_pillow = 0
|
||||
else:
|
||||
if ini.read_ini_bool(AOpath+"AO2XP.ini", "General", "download characters"):
|
||||
url = "http://s3.wasabisys.com/webao/base/characters/"+p_char.lower()+"/"+emote_prefix+p_emote.lower()+".gif"
|
||||
@ -243,7 +249,7 @@ class AOCharMovie(QtGui.QLabel):
|
||||
|
||||
if exists(alt_path):
|
||||
gif_path = alt_path
|
||||
self.use_pillow = False
|
||||
self.use_pillow = 0
|
||||
else:
|
||||
if ini.read_ini_bool(AOpath+"AO2XP.ini", "General", "download characters"):
|
||||
url = "http://s3.wasabisys.com/webao/base/characters/"+p_char.lower()+"/"+emote_prefix+p_emote.lower()+".png"
|
||||
@ -256,27 +262,47 @@ class AOCharMovie(QtGui.QLabel):
|
||||
gif_path = placeholder_path
|
||||
else:
|
||||
gif_path = ""
|
||||
self.use_pillow = False
|
||||
self.use_pillow = 0
|
||||
|
||||
if not self.use_pillow:
|
||||
self.m_movie.stop()
|
||||
self.m_movie.setFileName(gif_path)
|
||||
self.m_movie.start()
|
||||
|
||||
elif self.use_pillow == 1: # apng
|
||||
self.pillow_frames = images.load_apng(apng_path)
|
||||
#print apng_path, self.pillow_frames[0], int(self.pillow_frames[0][1] * self.pillow_speed) if len(self.pillow_frames[0]) > 1 else 0
|
||||
if len(self.pillow_frames) > 1: self.pillow_timer.start(int(self.pillow_frames[0][1] * self.pillow_speed))
|
||||
self.set_pillow_frame()
|
||||
|
||||
elif self.use_pillow == 2: # webp
|
||||
self.pillow_frames = images.load_webp(webp_path)
|
||||
if len(self.pillow_frames) > 1: self.pillow_timer.start(int(self.pillow_frames[0][1] * self.pillow_speed))
|
||||
self.set_pillow_frame()
|
||||
|
||||
self.show()
|
||||
self.m_movie.start()
|
||||
|
||||
def play_pre(self, p_char, p_emote, duration):
|
||||
gif_path = AOpath+"characters/"+p_char+"/"+p_emote+".gif"
|
||||
|
||||
self.m_movie.stop()
|
||||
self.clear()
|
||||
self.m_movie.setFileName(gif_path)
|
||||
self.m_movie.jumpToFrame(0)
|
||||
apng_path = AOpath+"characters/"+p_char+"/"+p_emote+".apng"
|
||||
webp_path = AOpath+"characters/"+p_char+"/"+p_emote+".webp"
|
||||
|
||||
full_duration = duration * self.time_mod
|
||||
real_duration = 0
|
||||
|
||||
self.play_once = False
|
||||
self.m_movie.stop()
|
||||
self.clear()
|
||||
|
||||
if exists(apng_path):
|
||||
real_duration = images.get_apng_duration(apng_path)
|
||||
|
||||
elif exists(webp_path):
|
||||
real_duration = images.get_webp_duration(webp_path)
|
||||
|
||||
elif exists(gif_path):
|
||||
self.m_movie.setFileName(gif_path)
|
||||
self.m_movie.jumpToFrame(0)
|
||||
for n_frame in range(self.m_movie.frameCount()):
|
||||
real_duration += self.m_movie.nextFrameDelay()
|
||||
self.m_movie.jumpToFrame(n_frame + 1)
|
||||
@ -290,6 +316,7 @@ class AOCharMovie(QtGui.QLabel):
|
||||
if percentage_modifier > 100.0 or percentage_modifier < 0.0:
|
||||
percentage_modifier = 100.0
|
||||
|
||||
self.pillow_fullduration = full_duration
|
||||
if full_duration == 0 or full_duration >= real_duration:
|
||||
self.play_once = True
|
||||
else:
|
||||
@ -298,6 +325,7 @@ class AOCharMovie(QtGui.QLabel):
|
||||
self.preanim_timer.start(full_duration)
|
||||
|
||||
self.m_movie.setSpeed(int(percentage_modifier))
|
||||
self.pillow_speed = percentage_modifier / 100.
|
||||
self.play(p_char, p_emote, "")
|
||||
|
||||
def play_talking(self, p_char, p_emote):
|
||||
@ -310,6 +338,7 @@ class AOCharMovie(QtGui.QLabel):
|
||||
|
||||
self.play_once = False
|
||||
self.m_movie.setSpeed(100)
|
||||
self.pillow_speed = 1
|
||||
self.play(p_char, p_emote, '(b)')
|
||||
|
||||
def play_idle(self, p_char, p_emote):
|
||||
@ -322,6 +351,7 @@ class AOCharMovie(QtGui.QLabel):
|
||||
|
||||
self.play_once = False
|
||||
self.m_movie.setSpeed(100)
|
||||
self.pillow_speed = 1
|
||||
self.play(p_char, p_emote, '(a)')
|
||||
|
||||
def stop(self):
|
||||
@ -341,6 +371,28 @@ class AOCharMovie(QtGui.QLabel):
|
||||
if self.m_movie.frameCount() - 1 == n_frame and self.play_once:
|
||||
self.preanim_timer.start(self.m_movie.nextFrameDelay())
|
||||
|
||||
@QtCore.pyqtSlot()
|
||||
def pillow_frame_change(self):
|
||||
if len(self.pillow_frames)-1 == self.pillow_frame:
|
||||
if self.play_once:
|
||||
self.preanim_timer.start(int(self.pillow_frames[self.pillow_frame][1] * self.pillow_speed))
|
||||
elif len(self.pillow_frames) > 1:
|
||||
self.pillow_frame = 0
|
||||
self.pillow_timer.start(int(self.pillow_frames[self.pillow_frame][1] * self.pillow_speed))
|
||||
else:
|
||||
self.pillow_frame += 1
|
||||
self.pillow_timer.start(int(self.pillow_frames[self.pillow_frame][1] * self.pillow_speed))
|
||||
|
||||
self.set_pillow_frame()
|
||||
|
||||
def set_pillow_frame(self):
|
||||
f_img = self.pillow_frames[self.pillow_frame][0].mirrored(self.m_flipped, False)
|
||||
if f_img.size().width() != 256 or f_img.size().height() != 192:
|
||||
f_img = f_img.scaled(256, 192, transformMode=QtCore.Qt.SmoothTransformation)
|
||||
|
||||
f_pixmap = QtGui.QPixmap.fromImage(f_img)
|
||||
self.setPixmap(f_pixmap)
|
||||
|
||||
@QtCore.pyqtSlot()
|
||||
def timer_done(self):
|
||||
self.done.emit()
|
||||
@ -1655,7 +1707,9 @@ class gui(QtGui.QWidget):
|
||||
preanim_duration = ao2_duration
|
||||
|
||||
anim_to_find = AOpath+"characters/"+f_char+"/"+f_preanim+".gif"
|
||||
if not exists(anim_to_find) or preanim_duration < 0:
|
||||
apng_to_find = AOpath+"characters/"+f_char+"/"+f_preanim+".apng"
|
||||
webp_to_find = AOpath+"characters/"+f_char+"/"+f_preanim+".webp"
|
||||
if (not exists(anim_to_find) and not exists(apng_to_find) and not exists(webp_to_find)) or preanim_duration < 0:
|
||||
if noninterrupting:
|
||||
self.anim_state = 4
|
||||
else:
|
||||
|
39
images.py
39
images.py
@ -8,6 +8,8 @@ APNG_DISPOSE_OP_BACKGROUND = 1
|
||||
APNG_DISPOSE_OP_PREVIOUS = 2
|
||||
APNG_BLEND_OP_SOURCE = 0
|
||||
APNG_BLEND_OP_OVER = 1
|
||||
disposes = ["APNG_DISPOSE_OP_NONE", "APNG_DISPOSE_OP_BACKGROUND", "APNG_DISPOSE_OP_PREVIOUS"]
|
||||
blends = ["APNG_BLEND_OP_SOURCE", "APNG_BLEND_OP_OVER"]
|
||||
|
||||
# layman's terms of above apng constants so i can easily wrap my head around it:
|
||||
#
|
||||
@ -18,42 +20,49 @@ APNG_BLEND_OP_OVER = 1
|
||||
# APNG_BLEND_OP_SOURCE: replace frame region
|
||||
# APNG_BLEND_OP_OVER: blend with frame
|
||||
|
||||
def load_apng(file):
|
||||
def load_apng(file): # this one was hell to implement compared to the three funcs below
|
||||
img = APNG.open(file)
|
||||
frames = []
|
||||
pilframes = []
|
||||
dispose_op = 0
|
||||
|
||||
width, height = img.frames[0][0].width, img.frames[0][0].height
|
||||
outputbuf = Image.new("RGBA", (width, height), (255,255,255,0))
|
||||
prev_frame = None
|
||||
|
||||
for frame, frame_info in img.frames:
|
||||
i = img.frames.index((frame, frame_info))
|
||||
pilframe = Image.open(io.BytesIO(frame.to_bytes())).convert("RGBA")
|
||||
|
||||
if dispose_op == APNG_DISPOSE_OP_BACKGROUND:
|
||||
pilframe2 = Image.new("RGBA", (width, height), (255,255,255,0))
|
||||
pilframe2.paste(pilframe, (frame_info.x_offset, frame_info.y_offset), pilframe.convert("RGBA"))
|
||||
#print str(i)+"\t"+disposes[dispose_op]+"\t\t"+blends[frame_info.blend_op]
|
||||
|
||||
if dispose_op == APNG_DISPOSE_OP_BACKGROUND or (i == 0 and dispose_op == APNG_DISPOSE_OP_PREVIOUS):
|
||||
prev_frame = outputbuf.copy()
|
||||
emptyrect = Image.new("RGBA", (img.frames[i-1][0].width, img.frames[i-1][0].height), (255,255,255,0))
|
||||
outputbuf.paste(emptyrect, (img.frames[i-1][1].x_offset, img.frames[i-1][1].y_offset))
|
||||
|
||||
elif dispose_op == APNG_DISPOSE_OP_PREVIOUS:
|
||||
pilframe2 = pilframes[-2].copy()
|
||||
pilframe2.paste(pilframe, (frame_info.x_offset, frame_info.y_offset), pilframe.convert("RGBA"))
|
||||
outputbuf = prev_frame.copy()
|
||||
|
||||
else:
|
||||
if i == 0:
|
||||
pilframe2 = pilframe
|
||||
else:
|
||||
pilframe2 = pilframes[-1].copy()
|
||||
pilframe2.paste(pilframe, (frame_info.x_offset, frame_info.y_offset), pilframe.convert("RGBA") if frame_info.blend_op == APNG_BLEND_OP_OVER else None)
|
||||
prev_frame = outputbuf.copy()
|
||||
|
||||
pilframes.append(pilframe2)
|
||||
if frame_info:
|
||||
frames.append([pilframe2.toqimage(), frame_info.delay*10]) # convert delay from centiseconds to milliseconds
|
||||
outputbuf.paste(pilframe, (frame_info.x_offset, frame_info.y_offset), pilframe.convert("RGBA") if frame_info.blend_op == APNG_BLEND_OP_OVER else None)
|
||||
else:
|
||||
outputbuf.paste(pilframe, (0, 0))
|
||||
|
||||
final_frame = outputbuf.copy()
|
||||
pilframes.append(final_frame)
|
||||
if frame_info:
|
||||
frames.append([final_frame.toqimage(), frame_info.delay*10]) # convert delay from centiseconds to milliseconds
|
||||
dispose_op = frame_info.depose_op
|
||||
else:
|
||||
frames.append([pilframe2.toqimage(), 0])
|
||||
|
||||
frames.append([final_frame.toqimage(), 0])
|
||||
|
||||
for frame in pilframes: frame.close()
|
||||
return frames
|
||||
#return pilframes
|
||||
|
||||
def load_webp(file):
|
||||
img = Image.open(file)
|
||||
|
Loading…
Reference in New Issue
Block a user