Image loading Mechanism Changed

Redone the loading and reloading of images.
Now it's using MD5 hashes to check for difference
when reloading. There is a reload function
Also to colors added ability to refresh as well
This commit is contained in:
Jeison Yehuda Amihud (Blender Dumbass) 2021-01-02 23:45:26 +00:00
parent f6cf881283
commit d6c0e4d06a

View file

@ -5,6 +5,7 @@
import os
import math
import hashlib
import threading
# GTK module ( Graphical interface
@ -93,6 +94,16 @@ def roundrect(layer, win, x, y, width, height, r, button=False, icon=False,
if height < r*2:
height = r*2
# I just out of blue decided that I want to have a setting to restrict
# the amount of roundness for every roundrect. If the user want let him
# or her have ugly squares everywhere.
if "Roundness" not in win.settings:
win.settings["Roundness"] = 1.0
settings.write("Roundness", 1.0)
r = r*win.settings["Roundness"]
# actuall drawing
layer.move_to(x,y+r)
layer.arc(x+r, y+r, r, math.pi, 3*math.pi/2)
@ -196,6 +207,201 @@ def blur(surface, win, amount):
else:
return surface
def hash_file(f):
# This function will give me MD5 hashes of various files
try:
BLOCKSIZE = 65536
hasher = hashlib.md5()
with open(f, 'rb') as afile:
buf = afile.read(BLOCKSIZE)
while len(buf) > 0:
hasher.update(buf)
buf = afile.read(BLOCKSIZE)
return str(hasher.hexdigest())
except:
return "FOLDER"
def loadimage(layer, win ,path, x, y, width, height, fit, cell=0):
# So multiple types of the image could be loaded. It could be either
# an image file. Like png or jpeg. Either a video file. Or a blend file
# each with it's own loading problems.
# While cairo can read pngs directly. It's not the best way of doing it
# since it's not capable of reading jpegs and other files. So let's do
# something about it.
foundformat = False
# IMAGEFILES
for f in fileformats.images:
if path.endswith(f):
foundformat = True
# So this parth of the code is for loading all simple images. From
# pngs Jpegs to anything else.
# We are going to use Gtk's GdkPixbuf for this. We will need to
# convert it to cairo surface later
try:
load1 = GdkPixbuf.Pixbuf.new_from_file(path)
except:
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/"+win.settings["Theme"]+"/icons/image.png")
# VIDEOFILES
for f in fileformats.videos:
if path.endswith(f):
foundformat = True
# Now if it's a video. It's going to be a little harder. We will need
# to use totem. It's a video player that exists in many GNU/Linux
# systems. Unfortunatly not everybody could see video previews.
try:
# This is going to require a few steps.
# 1. Making a filename for our totem to output.
part = path.replace("/", "_").replace(" ", "_")
# 2. Calling totem. And telling him that we want a thumbnail.
os.system("totem-video-thumbnailer -s "+str(width)+" "+path\
+" /tmp/vcstudio_thumbnail"+part+".png")
# 3. Loading this thumbnail.
load1 = GdkPixbuf.Pixbuf.new_from_file("/tmp/vcstudio_thumbnail"+part+".png")
# 4. Cleaning the thumbnail from the OS.
try:
os.remove("/tmp/vcstudio_thumbnail"+part+".png")
except:
pass
except:
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/"+win.settings["Theme"]+"/icons/video.png")
# BLEND FILES
for f in ["blend", "blend1"]:
if path.endswith(f):
foundformat = True
# Similarly to the Video. Blends files has their own thumbnailer. This
# time it's inside of the code of VCStudio. But I've copied it from
# the blender's installation folder. So you can find it there too
# it's called blender-thumbnailer.py. Which is a python script. Which
# is cool.
# Because thumbnailer is developed to work regardless of whether blender
# is installed or not. As it's reading the blend file directly. And
# not using some bpy script. We can copy that over to VCStudio and use
# it to give previews to users who do not have blender installed.
try:
# This is going to require a few steps.
# 1. Making a filename for our thumbnailer to output.
part = path.replace("/", "_").replace(" ", "_")
# 2. Calling thumbnailer. And telling him that we want a thumbnail.
os.system("python3 "+os.getcwd()+"/UI/blender-thumbnailer.py "\
+path+" /tmp/vcstudio_thumbnail"+part+".png")
# 3. Loading this thumbnail.
load1 = GdkPixbuf.Pixbuf.new_from_file("/tmp/vcstudio_thumbnail"+part+".png")
# 4. Cleaning the thumbnail from the OS.
try:
os.remove("/tmp/vcstudio_thumbnail"+part+".png")
except:
pass
except:
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/"+win.settings["Theme"]+"/icons/blender.png")
if not foundformat:
# If you can't find any format. Just use the file icon then
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/"+win.settings["Theme"]+"/icons/file.png")
# Then to convert the pixbuf to a cairo surface
Px = load1.get_width()
Py = load1.get_height()
load2 = cairo.ImageSurface(cairo.FORMAT_ARGB32, Px, Py)
imagedraw = cairo.Context(load2)
Gdk.cairo_set_source_pixbuf( imagedraw, load1, 0, 0)
imagedraw.paint()
# If I want to resize the image for an icon or something. There is
# gonna be the folowing algorythm.
if width or height:
dx = 0
dy = 0
imagesurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
imagedraw = cairo.Context(imagesurface)
# Crop effect. Really hard on my brains.
if fit == 'crop':
if height > load2.get_height()\
or width > load2.get_width():
dx = (width/2) -(load2.get_width() /2)
dy = (height/2)-(load2.get_height()/2)
else:
factor = 1
if (load2.get_height()*(width/load2.get_width()))\
< height:
factor = height / load2.get_height()
else:
factor = width / load2.get_width()
#factor = 0.1
imagedraw.scale(factor, factor)
dx = (width/2)/factor -(load2.get_width() /2)
dy = (height/2)/factor -(load2.get_height()/2)
imagedraw.set_source_surface(load2, dx, dy)
imagedraw.paint()
else:
imagesurface = load2
# Saving it into the win.images
win.images[cell][path]["loading"] = False
win.images[cell][path]["image"] = imagesurface
win.images[cell][path]["hash"] = hash_file(path)
win.imageload -= 1
def reload_images(win):
# This function will force the images to refrash. For things like sync
# or simply where a file is changed. Or for any other reason. Now we
# probably don't need to do this on every image. So instead we are going
# to compare the hashes of the current images to the ones of the files
# and if they changed. We are going to update those.
for cell in list(win.images.keys()):
for image in list(win.images[cell].keys()):
if hash_file(image) != win.images[cell][image]["hash"]:
win.images[cell][image]["loading"] = True
pass
def image(layer, win ,path, x, y, width=0, height=0, fit="crop", cell=0):
# This module will handle drawing images to the layers. It's not that hard
@ -219,153 +425,46 @@ def image(layer, win ,path, x, y, width=0, height=0, fit="crop", cell=0):
# If you ran this software you probably noticed that images are loading
# dynamically. I did it using this following crazy algorythm borowed from
# the old organizer. Basically for each image I see it's any image is loading
# at the moment. If yes, wait. If not loading. Meaning it can take the turn
# I create a thread using GLib. Because I'm lazy. Which is loading the image.
# Basically the UI keeps working before all images are loaded.
# the old organizer.
if path not in win.images[cell] or win.images[cell][path] == "LOADING-IMAGE":
# Here I want to load images. But I also want to give myself an ability to
# load refrash images. Without refrashing all the images. For this I'm going
# to use hashes. Specificly MD5 but it doesn't matter really. The idea is
# that on a special redraw call. We are going to check hashes storred with
# those from the file. And only if a file really changed. Then update the image.
if path not in win.images[cell] or win.images[cell][path]["loading"]:
win.images[cell][path] = "LOADING-IMAGE"
# If this is the first time this image is draw we want to create it a data
# structure. So we could load the image.
if win.imageload < 1: # This needs to be fixed. Because multithreading
win.imageload += 1 # gives tons of errors when loading single images.
def loadimage(layer, win ,path, x, y, width, height, fit):
# Loading the image into the cairo.
try:
# It could be not PNG
try:
loadimage = cairo.ImageSurface.create_from_png(path)
except:
# If it's not png it's gonna take few steps
# Let's first of all see if it's a blend file.
part = path.replace("/", "_").replace(" ", "_")
if path.endswith(".blend") or path.endswith(".blend1"):
IF = os.system("python3 "+os.getcwd()+"/UI/blender-thumbnailer.py "\
+path+" /tmp/vcstudio_blender_thumbnail"+part+".png")
if not IF:
load1 = GdkPixbuf.Pixbuf.new_from_file("/tmp/vcstudio_blender_thumbnail"+part+".png")
else:
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/"\
+win.settings["Theme"]+"/icons/blender.png")
os.remove("/tmp/vcstudio_blender_thumbnail"+part+".png")
else:
# if video
video = False
for f in fileformats.videos:
if path.endswith(f):
video = True
if video:
IF = os.system("totem-video-thumbnailer -s "+str(width)+" "+path\
+" /tmp/vcstudio_video_thumbnail"+part+".png")
if not IF:
load1 = GdkPixbuf.Pixbuf.new_from_file("/tmp/vcstudio_video_thumbnail"+part+".png")
else:
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/"\
+win.settings["Theme"]+"/icons/video.png")
os.remove("/tmp/vcstudio_video_thumbnail"+part+".png")
else:
# if image
image = False
for f in fileformats.images:
if path.endswith(f):
image = True
if image:
try:
load1 = GdkPixbuf.Pixbuf.new_from_file(path)
except:
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/"\
+win.settings["Theme"]+"/icons/image.png")
else:
if path.endswith(".progress"):
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/"\
+win.settings["Theme"]+"/icons/checklist.png")
else:
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/"\
+win.settings["Theme"]+"/icons/file.png")
# First we need to make a pixbuf.
Px = load1.get_width()
Py = load1.get_height()
# Then to convert the pixbuf to a cairo surface
loadimage = cairo.ImageSurface(cairo.FORMAT_ARGB32, Px, Py)
imagedraw = cairo.Context(loadimage)
Gdk.cairo_set_source_pixbuf( imagedraw, load1, 0, 0)
imagedraw.paint()
# If I want to resize the image for an icon or something. There is
# gonna be the folowing algorythm.
if width or height:
dx = 0
dy = 0
imagesurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
imagedraw = cairo.Context(imagesurface)
# Crop effect. Really hard on my brains.
if fit == 'crop':
if height > loadimage.get_height()\
or width > loadimage.get_width():
dx = (width/2) -(loadimage.get_width() /2)
dy = (height/2)-(loadimage.get_height()/2)
else:
factor = 1
if (loadimage.get_height()*(width/loadimage.get_width()))\
< height:
factor = height / loadimage.get_height()
else:
factor = width / loadimage.get_width()
#factor = 0.1
imagedraw.scale(factor, factor)
dx = (width/2)/factor -(loadimage.get_width() /2)
dy = (height/2)/factor -(loadimage.get_height()/2)
imagedraw.set_source_surface(loadimage, dx, dy)
imagedraw.paint()
else:
imagesurface = loadimage
except Exception as e:
print(e)
imagesurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 10, 10)
# Saving it into the win.images
win.images[cell][path] = imagesurface
win.imageload -= 1
win.images[cell][path] = {
"loading": True, # Whether the image is currently loading.
"image" : None, # The image data it self.
"hash" : "" # The hash of the image file.
}
#GLib.timeout_add(1, loadimage, layer, win ,path, x, y, width, height, fit)
t = threading.Thread(target=loadimage, args=(layer, win ,path, x, y, width, height, fit))
# Then we want to insure to load only a given amount of images in the same
# time. For this we are going to use the next variable.
MAXLOAD = 10
if win.imageload < MAXLOAD:
win.imageload += 1
# Now we want to load the image. But we are going to do that in a separate thread
# not to freeze the UI while the image is loading.
# See: loadimage() function for the continuation of this.
t = threading.Thread(target=loadimage, args=(layer, win ,path, x, y, width, height, fit, cell))
t.start()
#loading it back
else:
if win.images[cell][path] != "LOADING-IMAGE":
imagesurface = win.images[cell][path]
if win.images[cell][path]["image"]:
imagesurface = win.images[cell][path]["image"]
# Writting the image to the screen
layer.set_source_surface(imagesurface, x, y)