diff --git a/superpaper/data.py b/superpaper/data.py index b19e9a9..4396e54 100644 --- a/superpaper/data.py +++ b/superpaper/data.py @@ -245,6 +245,7 @@ def __init__(self, profile_file): self.file = profile_file self.name = "default_profile" self.spanmode = "single" # single / multi + self.fit = None self.spangroups = None self.slideshow = True self.delay_list = [600] @@ -288,6 +289,13 @@ def parse_profile(self, parse_file): else: sp_logging.G_LOGGER.info("Exception: unknown spanmode: %s \ in profile: %s", words[1], self.name) + elif words[0] == "fit": + wrd1 = words[1].strip().lower() + if wrd1 == "fill" or wrd1 == "fit" or wrd1 == "stretch": + self.fit = wrd1 + else: + sp_logging.G_LOGGER.info("Exception: unknown fit: %s \ + in profile: %s", words[1], self.name) elif words[0] == "spangroups": self.spangroups = [] groups = words[1].strip().split(",") @@ -536,7 +544,7 @@ def next_wallpaper_files(self): next_image = iterable.__next__() if os.path.isfile(next_image): files.append(next_image) - else: + elif next_image is not None and next_image != "": # reload all files by initializing if sp_logging.DEBUG: sp_logging.G_LOGGER.info("Ran into an invalid file, reinitializing..") @@ -557,16 +565,18 @@ def __iter__(self): return self def __next__(self): - if self.counter < len(self.files): + if len(self.files) > 0 and self.counter < len(self.files): image = self.files[self.counter] self.counter += 1 return image else: self.counter = 0 self.arrange_list() - image = self.files[self.counter] - self.counter += 1 - return image + if len(self.files) > 0 and self.counter < len(self.files): + image = self.files[self.counter] + self.counter += 1 + return image + return "" def arrange_list(self): """Reorders the image list as requested. Mostly for reoccuring shuffling.""" @@ -596,7 +606,7 @@ class CLIProfileData(ProfileData): the images given as input. """ - def __init__(self, files, ppiarr=None, inches=None, + def __init__(self, files, fit=None, ppiarr=None, inches=None, bezels=None, offsets=None, perspective=None): self.name = "cli" self.spanmode = "" # single / multi @@ -604,6 +614,7 @@ def __init__(self, files, ppiarr=None, inches=None, self.spanmode = "single" else: self.spanmode = "multi" + self.fit = fit self.ppimode = None if ppiarr is None and inches is None: @@ -654,6 +665,7 @@ class TempProfileData(object): def __init__(self): self.name = None self.spanmode = None + self.fit = "fill" self.spangroups = None self.slideshow = None self.delay = None @@ -686,6 +698,8 @@ def save(self): tpfile.write("delay=" + str(self.delay) + "\n") if self.sortmode: tpfile.write("sortmode=" + str(self.sortmode) + "\n") + if self.fit: + tpfile.write("fit=" + str(self.fit) + "\n") if self.inches: tpfile.write("diagonal_inches=" + str(self.inches) + "\n") if self.manual_offsets: diff --git a/superpaper/gui.py b/superpaper/gui.py index bdc8d56..193c8ff 100644 --- a/superpaper/gui.py +++ b/superpaper/gui.py @@ -181,6 +181,9 @@ def create_sizer_settings_left(self): self.ch_sshow_sort = wx.Choice(statbox_parent_sshow, -1, name="SortChoice", # size=(self.tc_width*0.7, -1), choices=["Shuffle", "Alphabetical"]) + self.st_sshow_fit = wx.StaticText(statbox_parent_sshow, -1, "Fit:") + self.ch_sshow_fit = wx.Choice(statbox_parent_sshow, -1, name="FitChoice", + choices=["Fill", "Fit", "Stretch"]) # ch_sort_size = self.ch_sshow_sort.GetClientSize() self.st_sshow_delay = wx.StaticText(statbox_parent_sshow, -1, "Delay (minutes):") self.tc_sshow_delay = wx.TextCtrl( @@ -191,15 +194,19 @@ def create_sizer_settings_left(self): ) self.cb_slideshow = wx.CheckBox(statbox_parent_sshow, -1, "Slideshow") self.st_sshow_sort.Disable() + self.st_sshow_fit.Disable() self.st_sshow_delay.Disable() self.tc_sshow_delay.Disable() self.ch_sshow_sort.Disable() + self.ch_sshow_fit.Disable() self.cb_slideshow.Bind(wx.EVT_CHECKBOX, self.onCheckboxSlideshow) self.sizer_setting_slideshow.Add(self.cb_slideshow, 0, wx.ALIGN_LEFT|wx.ALL, 5) sizer_sshow_subsettings.Add(self.st_sshow_delay, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 0) sizer_sshow_subsettings.Add(self.tc_sshow_delay, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 0) sizer_sshow_subsettings.Add(self.st_sshow_sort, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 0) sizer_sshow_subsettings.Add(self.ch_sshow_sort, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 0) + sizer_sshow_subsettings.Add(self.st_sshow_fit, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 0) + sizer_sshow_subsettings.Add(self.ch_sshow_fit, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 0) self.sizer_setting_slideshow.Add(sizer_sshow_subsettings, 0, wx.ALIGN_LEFT|wx.LEFT|wx.BOTTOM, 10) # self.sizer_setting_slideshow.AddSpacer(5) @@ -495,11 +502,20 @@ def populate_fields(self, profile): self.ch_sshow_sort.SetSelection(1) else: self.ch_sshow_sort.SetSelection(wx.NOT_FOUND) + if profile.fit == "fill": + self.ch_sshow_fit.SetSelection(0) + elif profile.fit == "fit": + self.ch_sshow_fit.SetSelection(1) + elif profile.fit == "stretch": + self.ch_sshow_fit.SetSelection(2) + else: + self.ch_sshow_fit.SetSelection(wx.NOT_FOUND) else: self.cb_slideshow.SetValue(False) wx.PostEvent(self.cb_slideshow, wx.CommandEvent(commandEventType=wx.EVT_CHECKBOX.typeId)) self.tc_sshow_delay.Clear() self.ch_sshow_sort.SetSelection(wx.NOT_FOUND) + self.ch_sshow_fit.SetSelection(wx.NOT_FOUND) if profile.hk_binding: self.cb_hotkey.SetValue(True) @@ -558,6 +574,7 @@ def populate_fields(self, profile): display_data = self.display_sys.get_disp_list(False) self.wpprev_pnl.preview_wallpaper( profile.next_wallpaper_files(), + profile.fit, self.show_advanced_settings, self.use_multi_image, display_data, @@ -774,9 +791,14 @@ def onIdle(self, event): leftdown = wx.GetMouseState().LeftIsDown() update = bool(self.resized and not leftdown) if update: + fit = self.ch_sshow_fit.GetString(self.ch_sshow_fit.GetSelection()).lower() + if fit is None or fit == "": + fit = "fill" + self.wpprev_pnl.full_refresh_preview(update, self.show_advanced_settings, self.use_multi_image, + fit, spangroups=self.read_spangroups(True)) self.resized = False else: @@ -812,8 +834,14 @@ def onSpanRadio(self, event): spangroups = None if self.cb_spangroups.GetValue(): spangroups = self.read_spangroups(True) + + fit = self.ch_sshow_fit.GetString(self.ch_sshow_fit.GetSelection()).lower() + if fit is None or fit == "": + fit = "fill" + self.wpprev_pnl.update_display_data( display_data, + fit, self.show_advanced_settings, self.use_multi_image, spangroups=spangroups @@ -864,8 +892,14 @@ def onCheckboxDiaginch(self, event): for tc, diag in zip(self.tc_list_diaginch, diags): tc.ChangeValue(diag) display_data = self.display_sys.get_disp_list(self.show_advanced_settings) + + fit = self.ch_sshow_fit.GetString(self.ch_sshow_fit.GetSelection()).lower() + if fit is None or fit == "": + fit = "fill" + self.wpprev_pnl.update_display_data( display_data, + fit, self.show_advanced_settings, self.use_multi_image ) @@ -945,8 +979,14 @@ def onSaveDiagInch(self, event): self.display_sys.update_display_diags(inches) self.display_sys.save_system() display_data = self.display_sys.get_disp_list(self.show_advanced_settings) + + fit = self.ch_sshow_fit.GetString(self.ch_sshow_fit.GetSelection()).lower() + if fit is None or fit == "": + fit = "fill" + self.wpprev_pnl.update_display_data( display_data, + fit, self.show_advanced_settings, self.use_multi_image ) @@ -1040,6 +1080,7 @@ def onSave(self, event): if tmp_profile.slideshow: tmp_profile.delay = str(60*float(self.tc_sshow_delay.GetLineText(0))) # save delay as seconds for compatibility! tmp_profile.sortmode = self.ch_sshow_sort.GetString(self.ch_sshow_sort.GetSelection()).lower() + tmp_profile.fit = self.ch_sshow_fit.GetString(self.ch_sshow_fit.GetSelection()).lower() if self.cb_hotkey.GetValue(): tmp_profile.hk_binding = self.tc_hotkey_bind.GetLineText(0) @@ -1133,6 +1174,7 @@ def onSave(self, event): display_data = self.display_sys.get_disp_list(False) self.wpprev_pnl.preview_wallpaper( saved_profile.next_wallpaper_files(), + saved_profile.fit, self.show_advanced_settings, self.use_multi_image, display_data, @@ -1162,6 +1204,7 @@ def onCreateNewProfile(self, event): self.cb_slideshow.SetValue(False) self.tc_sshow_delay.ChangeValue("") self.ch_sshow_sort.SetSelection(wx.NOT_FOUND) + self.ch_sshow_fit.SetSelection(wx.NOT_FOUND) self.onCheckboxSlideshow(None) self.cb_offsets.SetValue(False) @@ -1331,10 +1374,11 @@ class WallpaperPreviewPanel(wx.Panel): configuration. Method looks up saved setups to see if one exists that matches the given resolutions, offsets and sizes. """ - def __init__(self, parent, display_sys, image_list = None, use_ppi_px = False, use_multi_image = False): + def __init__(self, parent, display_sys, image_list=None, fit="fill", use_ppi_px=False, use_multi_image=False): self.preview_size = (1080,400) wx.Panel.__init__(self, parent, size=self.preview_size) self.frame = parent + self.fit = fit # Buttons self.config_mode = False @@ -1510,13 +1554,14 @@ def refresh_preview(self, use_ppi_px=False, force_refresh=False): self.move_buttons() self.move_bezel_buttons() - def full_refresh_preview(self, is_resized, use_ppi_px, use_multi_image, spangroups=None): + def full_refresh_preview(self, is_resized, use_ppi_px, use_multi_image, fit, spangroups=None): self.use_multi_image = use_multi_image + self.fit = fit if is_resized and not self.config_mode: dtop_canvas_relsz, dtop_canvas_pos, scaling_fac = self.fit_canvas_wrkarea(self.dtop_canvas_px) # if (self.current_preview_images and dtop_canvas_relsz is not self.dtop_canvas_relsz): - if (self.current_preview_images): - self.preview_wallpaper(self.current_preview_images, use_ppi_px, use_multi_image, spangroups=spangroups) + if self.current_preview_images: + self.preview_wallpaper(self.current_preview_images, fit, use_ppi_px, use_multi_image, spangroups=spangroups) self.move_bezel_buttons() # self.st_bmp_canvas.Hide() else: @@ -1528,6 +1573,7 @@ def full_refresh_preview(self, is_resized, use_ppi_px, use_multi_image, spangrou def preview_wallpaper(self, image_list, + fit, use_ppi_px=False, use_multi_image=False, display_data=None, @@ -1543,7 +1589,7 @@ def preview_wallpaper(self, image_list, image_list.append(image_list[0]) for img_nm, st_bmp in zip(image_list, self.preview_img_list): prev_sz = st_bmp.GetSize() - st_bmp.SetBitmap(self.resize_and_bitmap(img_nm, prev_sz)) + st_bmp.SetBitmap(self.resize_and_bitmap(img_nm, prev_sz, fit)) elif use_ppi_px and spangroups: self.use_multi_image = True # disables canvas drawing # for each group of displays, run span wallpaper preview @@ -1556,7 +1602,7 @@ def preview_wallpaper(self, image_list, canv_sz, canvas_pos = self.canvas_display_group(display_rel_sizes, (0, 0)) print(canv_sz, canvas_pos) - bmp_clr, bmp_bw = self.resize_and_bitmap(img_nm, canv_sz, True) + bmp_clr, bmp_bw = self.resize_and_bitmap(img_nm, canv_sz, fit, True) for (disp, img_sz, bez_szs, @@ -1576,7 +1622,7 @@ def preview_wallpaper(self, image_list, # and crop pieces to show on monitor previews unaltered. # With use_ppi_px any provided bezels will be drawn. canv_sz = self.st_bmp_canvas.GetSize() - bmp_clr, bmp_bw = self.resize_and_bitmap(img, canv_sz, True) + bmp_clr, bmp_bw = self.resize_and_bitmap(img, canv_sz, fit, True) self.st_bmp_canvas.SetBitmap(bmp_bw) # self.st_bmp_canvas.Show() @@ -1594,12 +1640,12 @@ def preview_wallpaper(self, image_list, crop_w_bez = self.bezels_to_bitmap(crop, sz, bez_szs) st_bmp.SetBitmap(crop_w_bez) # st_bmp.Show() - else: + elif len(image_list) > 0: img = image_list[0] # set canvas to fit with keeping aspect the image, with dim/blur # and crop pieces to show on monitor previews unaltered. canv_sz = self.st_bmp_canvas.GetSize() - bmp_clr, bmp_bw = self.resize_and_bitmap(img, canv_sz, True) + bmp_clr, bmp_bw = self.resize_and_bitmap(img, canv_sz, fit, True) self.st_bmp_canvas.SetBitmap(bmp_bw) # self.st_bmp_canvas.Show() @@ -1613,10 +1659,10 @@ def preview_wallpaper(self, image_list, self.draw_monitor_numbers(use_ppi_px) self.Refresh() - def resize_and_bitmap(self, fname, size, enhance_color=False): + def resize_and_bitmap(self, fname, size, fit, enhance_color=False): """Take filename of an image and resize and center crop it to size.""" try: - pil = resize_to_fill(Image.open(fname), size, quality="fast") + pil = resize_to_fill(Image.open(fname), size, fit, quality="fast") except UnidentifiedImageError: msg = ("Opening image '%s' failed with PIL.UnidentifiedImageError." "It could be corrupted or is of foreign type.") % fname @@ -1669,10 +1715,10 @@ def bezels_to_bitmap(self, bmp, disp_sz, bez_rects): return img_out.ConvertToBitmap() - def update_display_data(self, display_data, use_ppi_px, use_multi_image, spangroups=None): + def update_display_data(self, display_data, fit, use_ppi_px, use_multi_image, spangroups=None): self.display_data = display_data self.refresh_preview() - self.full_refresh_preview(True, use_ppi_px, use_multi_image, spangroups=spangroups) + self.full_refresh_preview(True, use_ppi_px, use_multi_image, fit, spangroups=spangroups) # @@ -1905,7 +1951,7 @@ def onSave(self, evt): display_data = self.display_sys.get_disp_list(use_ppi_norm=True) # Full redraw of preview with new offset data if self.current_preview_images: - self.preview_wallpaper(self.current_preview_images, True, False, + self.preview_wallpaper(self.current_preview_images, self.fit, True, False, display_data=display_data) else: self.display_data = display_data @@ -1953,7 +1999,7 @@ def onCancel(self, evt): # redraw preview with restored data self.display_data = self.display_sys.get_disp_list(True) self.refresh_preview() - self.full_refresh_preview(True, True, False) + self.full_refresh_preview(True, True, False, self.fit) # self.show_staticbmps(True) self.frame.toggle_radio_and_profile_choice(True) self.frame.toggle_bezel_buttons(False, True) @@ -2262,7 +2308,7 @@ def bezel_config_save(self): # Show preview positioning config button self.toggle_buttons(True, False) # self.draggable_shapes = [] # Destroys DragShapes / manually drawn previews - self.full_refresh_preview(True, True, False) + self.full_refresh_preview(True, True, False, fit=self.fit) # self.show_staticbmps(True) self.Refresh() # trigger a DisplaySystem save. @@ -2281,7 +2327,7 @@ def bezel_config_cancel(self): pops[1].set_bezel_value(bez_mms[1]) self.display_data = self.display_sys.get_disp_list(True) # self.draggable_shapes = [] # Destroys DragShapes / manually drawn previews - self.full_refresh_preview(True, True, False) + self.full_refresh_preview(True, True, False, fit=self.fit) # self.show_staticbmps(True) self.Refresh() diff --git a/superpaper/profiles-win/example.profile b/superpaper/profiles-win/example.profile index 0136ae5..d307642 100644 --- a/superpaper/profiles-win/example.profile +++ b/superpaper/profiles-win/example.profile @@ -1,5 +1,6 @@ name=example spanmode=single +fit=fill slideshow=true delay=120 sortmode=shuffle diff --git a/superpaper/profiles-win/example_multi.profile b/superpaper/profiles-win/example_multi.profile index 04f1b45..812f40b 100644 --- a/superpaper/profiles-win/example_multi.profile +++ b/superpaper/profiles-win/example_multi.profile @@ -1,5 +1,6 @@ name=example_multi spanmode=multi +fit=fill slideshow=true delay=120 sortmode=shuffle diff --git a/superpaper/profiles/example.profile b/superpaper/profiles/example.profile index 7da71db..59ccfc2 100644 --- a/superpaper/profiles/example.profile +++ b/superpaper/profiles/example.profile @@ -1,5 +1,6 @@ name=example spanmode=single +fit=fill slideshow=true delay=120 sortmode=shuffle diff --git a/superpaper/profiles/example_multi.profile b/superpaper/profiles/example_multi.profile index 0c522bf..36dcd02 100644 --- a/superpaper/profiles/example_multi.profile +++ b/superpaper/profiles/example_multi.profile @@ -1,5 +1,6 @@ name=example_multi spanmode=multi +fit=fill slideshow=true delay=120 sortmode=shuffle diff --git a/superpaper/wallpaper_processing.py b/superpaper/wallpaper_processing.py index a54623b..15045b4 100644 --- a/superpaper/wallpaper_processing.py +++ b/superpaper/wallpaper_processing.py @@ -16,7 +16,7 @@ from operator import itemgetter from threading import Lock, Thread, Timer -from PIL import Image, UnidentifiedImageError +from PIL import Image, ImageOps, UnidentifiedImageError from screeninfo import get_monitors import superpaper.perspective as persp @@ -918,7 +918,7 @@ def compute_ppi_corrected_res_array(res_array, ppi_list_rel_density): # resize image to fill given rectangle and do a centered crop to size. # Return output image. -def resize_to_fill(img, res, quality=Image.LANCZOS): +def resize_to_fill(img, res, fit, quality=Image.LANCZOS): """Resize image to fill given rectangle and do a centered crop to size.""" if quality == "fast": quality = Image.HAMMING @@ -936,70 +936,109 @@ def resize_to_fill(img, res, quality=Image.LANCZOS): return img image_ratio = image_size[0] / image_size[1] target_ratio = res[0] / res[1] - # resize along the shorter edge to get an image that is at least of the - # target size on the shorter edge. - if image_ratio < target_ratio: # img not wide enough / is too tall - resize_multiplier = res[0] / image_size[0] - new_size = ( - round(resize_multiplier * image_size[0]), - round(resize_multiplier * image_size[1])) - img = img.resize(new_size, resample=quality, reducing_gap=reducing_gap) - # crop vertically to target height - extra_height = new_size[1] - res[1] - if extra_height < 0: - sp_logging.G_LOGGER.info( - "Error with cropping vertically, resized image \ - wasn't taller than target size.") - return -1 - if extra_height == 0: - # image is already at right height, no cropping needed. + + if fit == "fit": + if image_ratio < target_ratio: # img not wide enough / is too tall + resize_multiplier = res[1] / image_size[1] + new_size = ( + round(resize_multiplier * image_size[0]), + round(resize_multiplier * image_size[1]) + ) + + img = img.resize(new_size, resample=quality, reducing_gap=reducing_gap) + if new_size == res: + return img + + pad = round((res[0] - new_size[0]) / 2) + img = ImageOps.expand(img, (pad, 0), (0, 0, 0)) return img - # (left edge, half of extra height from top, - # right edge, bottom = top + res[1]) : force correct height - crop_tuple = ( - 0, - round(extra_height/2), - new_size[0], - round(extra_height/2) + res[1]) - cropped_res = img.crop(crop_tuple) - if cropped_res.size == res: - return cropped_res else: - sp_logging.G_LOGGER.info( - "Error: result image not of correct size. crp:%s, res:%s", - cropped_res.size, res) - return -1 - elif image_ratio >= target_ratio: # img not tall enough / is too wide - resize_multiplier = res[1] / image_size[1] - new_size = ( - round(resize_multiplier * image_size[0]), - round(resize_multiplier * image_size[1])) - img = img.resize(new_size, resample=quality, reducing_gap=reducing_gap) - # crop horizontally to target width - extra_width = new_size[0] - res[0] - if extra_width < 0: - sp_logging.G_LOGGER.info( - "Error with cropping horizontally, resized image \ - wasn't wider than target size.") - return -1 - if extra_width == 0: - # image is already at right width, no cropping needed. + resize_multiplier = res[0] / image_size[0] + new_size = ( + round(resize_multiplier * image_size[0]), + round(resize_multiplier * image_size[1]) + ) + + img = img.resize(new_size, resample=quality, reducing_gap=reducing_gap) + if new_size == res: + return img + + pad = round((res[1] - new_size[1]) / 2) + img = ImageOps.expand(img, (0, pad), (0, 0, 0)) return img - # (half of extra from left edge, top edge, - # right = left + desired width, bottom) : force correct width - crop_tuple = ( - round(extra_width/2), - 0, - round(extra_width/2) + res[0], - new_size[1]) - cropped_res = img.crop(crop_tuple) - if cropped_res.size == res: - return cropped_res - else: - sp_logging.G_LOGGER.info( - "Error: result image not of correct size. crp:%s, res:%s", - cropped_res.size, res) - return -1 + elif fit == "stretch": + new_size = ( + round(res[0]), + round(res[1]) + ) + return img.resize(new_size, resample=quality, reducing_gap=reducing_gap) + else: # default fill + # resize along the shorter edge to get an image that is at least of the + # target size on the shorter edge. + if image_ratio < target_ratio: # img not wide enough / is too tall + resize_multiplier = res[0] / image_size[0] + new_size = ( + round(resize_multiplier * image_size[0]), + round(resize_multiplier * image_size[1])) + img = img.resize(new_size, resample=quality, reducing_gap=reducing_gap) + # crop vertically to target height + extra_height = new_size[1] - res[1] + if extra_height < 0: + sp_logging.G_LOGGER.info( + "Error with cropping vertically, resized image \ + wasn't taller than target size.") + return -1 + if extra_height == 0: + # image is already at right height, no cropping needed. + return img + # (left edge, half of extra height from top, + # right edge, bottom = top + res[1]) : force correct height + crop_tuple = ( + 0, + round(extra_height/2), + new_size[0], + round(extra_height/2) + res[1]) + cropped_res = img.crop(crop_tuple) + # we rounded 4 times, so 4 in the worst case. probably within 1 + if abs(cropped_res.size[0] - res[0]) <= 4 or abs(cropped_res.size[1] - res[1]) <= 4: + return cropped_res + else: + sp_logging.G_LOGGER.info( + "Error: result image not of correct size. crp:%s, res:%s", + cropped_res.size, res) + return -1 + else: # img not tall enough / is too wide + resize_multiplier = res[1] / image_size[1] + new_size = ( + round(resize_multiplier * image_size[0]), + round(resize_multiplier * image_size[1])) + img = img.resize(new_size, resample=quality, reducing_gap=reducing_gap) + # crop horizontally to target width + extra_width = new_size[0] - res[0] + if extra_width < 0: + sp_logging.G_LOGGER.info( + "Error with cropping horizontally, resized image \ + wasn't wider than target size.") + return -1 + if extra_width == 0: + # image is already at right width, no cropping needed. + return img + # (half of extra from left edge, top edge, + # right = left + desired width, bottom) : force correct width + crop_tuple = ( + round(extra_width/2), + 0, + round(extra_width/2) + res[0], + new_size[1]) + cropped_res = img.crop(crop_tuple) + # we rounded 4 times, so 4 in the worst case. probably within 1 + if abs(cropped_res.size[0] - res[0]) <= 4 or abs(cropped_res.size[1] - res[1]) <= 4: + return cropped_res + else: + sp_logging.G_LOGGER.info( + "Error: result image not of correct size. crp:%s, res:%s", + cropped_res.size, res) + return -1 def get_center(res): @@ -1144,8 +1183,10 @@ def span_single_image_simple(profile): except UnidentifiedImageError: sp_logging.G_LOGGER.info(("Opening image '%s' failed with PIL.UnidentifiedImageError." "It could be corrupted or is of foreign type."), file) + return + canvas_tuple = tuple(compute_canvas(RESOLUTION_ARRAY, DISPLAY_OFFSET_ARRAY)) - img_resize = resize_to_fill(img, canvas_tuple) + img_resize = resize_to_fill(img, canvas_tuple, profile.fit) outputfile, outputfile_old = alternating_outputfile(profile.name) img_resize.save(outputfile, quality=95) # set quality if jpg is used, png unaffected @@ -1242,7 +1283,7 @@ def span_single_image_advanced(profile): # Canvas containing ppi normalized displays canvas_tuple_trgt = tuple(compute_working_canvas(grp_crops)) sp_logging.G_LOGGER.info("Back-projected canvas size: %s", canvas_tuple_proj) - img_workingsize = resize_to_fill(img, canvas_tuple_proj) + img_workingsize = resize_to_fill(img, canvas_tuple_proj, profile.fit) for crop_tup, coeffs, ppin_crop, (i_res, res) in zip(proj_plane_crops, persp_coeffs, grp_crops, @@ -1269,7 +1310,7 @@ def span_single_image_advanced(profile): # Image is now the height of the eff tallest display + possible manual # offsets and the width of the combined eff widths + possible manual # offsets. - img_workingsize = resize_to_fill(img, canvas_tuple_eff) + img_workingsize = resize_to_fill(img, canvas_tuple_eff, profile.fit) # Simultaneously make crops at working size and then resize down to actual # resolution from RESOLUTION_ARRAY as needed. for crop_tup, (i_res, res) in zip(grp_crops, enumerate(grp_res_arr)): @@ -1320,7 +1361,7 @@ def set_multi_image_wallpaper(profile): except UnidentifiedImageError: sp_logging.G_LOGGER.info(("Opening image '%s' failed with PIL.UnidentifiedImageError." "It could be corrupted or is of foreign type."), file) - img_resized.append(resize_to_fill(image, res)) + img_resized.append(resize_to_fill(image, res, profile.fit)) canvas_tuple = tuple(compute_canvas(RESOLUTION_ARRAY, DISPLAY_OFFSET_ARRAY)) combined_image = Image.new("RGB", canvas_tuple, color=0) combined_image.load()