1import threading 2import traceback 3import weakref 4 5import fsui 6from .Constants import Constants 7from ..launcher_signal import LauncherSignal 8 9# FIXME: Remove dependency on arcade package (move stuff into fsgs instead) 10from arcade.glui.imageloader import get_file_for_sha1_cached 11 12 13class ImageLoader(object): 14 def __init__(self): 15 self.stop_flag = False 16 self.requests = [] 17 self.requests_lock = threading.Lock() 18 self.requests_condition = threading.Condition(self.requests_lock) 19 threading.Thread( 20 target=self.image_loader_thread, name="ImageLoaderThread" 21 ).start() 22 LauncherSignal.add_listener("quit", self) 23 24 def stop(self): 25 print("[IMAGES] ImageLoader.stop") 26 with self.requests_lock: 27 self.stop_flag = True 28 self.requests_condition.notify() 29 30 def on_quit_signal(self): 31 print("[IMAGES] ImageLoader.on_quit_signal") 32 self.stop_flag = True 33 34 def image_loader_thread(self): 35 try: 36 self._image_loader_thread() 37 except Exception: 38 traceback.print_exc() 39 40 def load_image(self, path="", sha1="", size=None, on_load=None, **kwargs): 41 request = ImageLoadRequest() 42 request.path = path 43 request.sha1 = sha1 44 request.image = None 45 request.size = size 46 request.on_load = on_load 47 request.args = kwargs 48 with self.requests_lock: 49 self.requests.append(weakref.ref(request)) 50 self.requests_condition.notify() 51 return request 52 53 def _image_loader_thread(self): 54 while True: 55 request = None 56 with self.requests_lock: 57 if self.stop_flag: 58 break 59 while len(self.requests) > 0: 60 request = self.requests.pop(0)() 61 if request is not None: 62 break 63 if request: 64 self.fill_request(request) 65 request.notify() 66 else: 67 with self.requests_lock: 68 if self.stop_flag: 69 break 70 self.requests_condition.wait() 71 72 def fill_request(self, request): 73 try: 74 self._fill_request(request) 75 except Exception: 76 traceback.print_exc() 77 78 @staticmethod 79 def get_cache_path_for_sha1(request, sha1): 80 cover = request.args.get("is_cover", False) 81 if cover: 82 size_arg = "?w={0}&h={1}&t=lbcover".format( 83 Constants.COVER_SIZE[0], Constants.COVER_SIZE[1] 84 ) 85 cache_ext = "_{0}x{1}_lbcover.png".format( 86 Constants.COVER_SIZE[0], Constants.COVER_SIZE[1] 87 ) 88 elif request.size: 89 size_arg = "?s=1x" 90 cache_ext = "_1x.png" 91 else: 92 size_arg = "" 93 cache_ext = "" 94 return get_file_for_sha1_cached(sha1, size_arg, cache_ext) 95 96 def _fill_request(self, request): 97 if request.path is None: 98 return 99 cover = request.args.get("is_cover", False) 100 if request.path.startswith("sha1:"): 101 path = self.get_cache_path_for_sha1(request, request.path[5:]) 102 else: 103 path = request.path 104 if not path: 105 return 106 print("[IMAGES] Loading", request.path) 107 image = fsui.Image(path) 108 print(image.size, request.size) 109 if request.size is not None: 110 dest_size = request.size 111 else: 112 dest_size = image.size 113 if image.size == dest_size: 114 request.image = image 115 return 116 if cover: 117 try: 118 ratio = image.size[0] / image.size[1] 119 except Exception: 120 ratio = 1.0 121 if 0.85 < ratio < 1.20: 122 min_length = min(request.size) 123 dest_size = (min_length, min_length) 124 double_size = False 125 else: 126 double_size = True 127 128 if double_size and image.size[0] < 400: 129 image.resize( 130 (image.size[0] * 2, image.size[1] * 2), fsui.Image.NEAREST 131 ) 132 image.resize(dest_size) 133 request.image = image 134 135 136class ImageLoadRequest(object): 137 def __init__(self): 138 self.on_load = self._dummy_on_load_function 139 self.size = None 140 self.args = {} 141 142 def notify(self): 143 def on_load_function(): 144 self.on_load(self) 145 146 fsui.call_after(on_load_function) 147 148 def _dummy_on_load_function(self, obj): 149 pass 150