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