From 3441dbdbf5622daeb05408b8aa352c7544e3956f Mon Sep 17 00:00:00 2001 From: lazysnake Date: Sat, 3 Jul 2021 12:42:12 +0200 Subject: [PATCH] cg button scaling, code cleanup --- game/screens.rpy | 57 +---------------------- game/src/cg_gallery.rpy | 101 ++++++++++++++++++++++++++++++++++------ 2 files changed, 87 insertions(+), 71 deletions(-) diff --git a/game/screens.rpy b/game/screens.rpy index b25b6c0..db549d2 100644 --- a/game/screens.rpy +++ b/game/screens.rpy @@ -609,46 +609,6 @@ style main_menu_text: style main_menu_title: properties gui.text_properties("title") -## Gallery screen ################################################################ -## -## This screen holds the Gallery. -## - -screen gallery(): - - tag menu - ## This use statement includes the game_menu screen inside this one. The - ## vbox child is then included inside the viewport inside the game_menu - ## screen. - style_prefix "main_menu" - - add gui.main_menu_background - add gui.extras_submenu_panel - - frame: - pass - - vbox: - xpos 500 - yalign 0.2 - textbutton "Back to Extras" action ShowMenu("extras") - - vbox: - viewport id "gallery": - xpos 700 - ypos 1000 - style_prefix "gallery" - use images - text _("") ## Not sure why, but this keeps the vbox below working ... Renpy quirk? Does it kill scrolling in the viewport? Tune in when we have the gallery populated in the images screen. - - - - vbox: - xpos 1942 - # xalign 1.0 - yalign 0.95 - use extrasnavigation - style main_menu_frame is empty style main_menu_vbox is vbox style main_menu_text is gui_text @@ -675,21 +635,6 @@ style main_menu_title: properties gui.text_properties("title") -## Images Screen ################################################################ -## -## This is the screen that actually houses the images of the gallery -screen images(): - tag menu - style_prefix "main_menu" - - frame: - pass - vbox: - xpos 600 - ypos 600 - text _("Placeholder for gallery.") - - ## Updates screen ################################################################ ## @@ -1247,7 +1192,7 @@ screen extrasnavigation(): imagebutton auto "gui/button/menubuttons/helpbutton_%s.png" action ShowMenu("help") imagebutton auto "gui/button/menubuttons/aboutbutton_%s.png" action ShowMenu("about") imagebutton auto "gui/button/menubuttons/updatesbutton_%s.png" action ShowMenu("updates") - imagebutton auto "gui/button/menubuttons/gallerybutton_%s.png" action ShowMenu("gallery") + imagebutton auto "gui/button/menubuttons/gallerybutton_%s.png" action ShowMenu("cg_gallery") imagebutton auto "gui/button/menubuttons/returnbutton_%s.png" action ShowMenu("main_menu") diff --git a/game/src/cg_gallery.rpy b/game/src/cg_gallery.rpy index f252af9..b45d7fd 100644 --- a/game/src/cg_gallery.rpy +++ b/game/src/cg_gallery.rpy @@ -9,35 +9,47 @@ init python: # CONST PARAMS GALLERY_COLS = 3 GALLERY_CGS_PER_PAGE = 6 - NOT_UNLOCKED_COVER = im.FactorScale("gui/gallery/unlocked_cg_button_cover.png", 0.225, 0.225) + PREFERRED_WIDTH = 432 #px (1920 * 0.225) + PREFERRED_HEIGHT = 243 #px (1080 * 0.225) + DEFAULT_WIDTH_SCALE_RATIO = round(float(PREFERRED_WIDTH) / float(1920), 5) + DEFAULT_HEIGHT_SCALE_RATIO = round(float(PREFERRED_HEIGHT) / float(1080), 5) + NOT_UNLOCKED_COVER = im.FactorScale("gui/gallery/unlocked_cg_button_cover.png", DEFAULT_WIDTH_SCALE_RATIO, DEFAULT_HEIGHT_SCALE_RATIO) + ACCEPTED_EXTENSIONS = ["jpg", "png"] # GALLERY ITEMS # Data structure that holds the data for each cg and button # item is the key in the Gallery - # { item: string; cg: Displayable; }[] + # ext is the file extension + # { item: string; cg: Displayable; ext: string }[] galleryItems = [] # Which page of the gallery is shown galleryPage = 1 # Global function to unlock a cg + # fname should be a key used in galleryItems + # (fname: string): None def unlockCg(fname): unlocked = fname in persistent.cggallery if not unlocked: persistent.cggallery.append(fname) renpy.persistent.save() + for i in range(0, len(galleryItems) - 1): + # TODO: raise Exception(galleryItems[i]) + # Make a scaled cg button - # (cg: string, unlocked?: boolean): Displayable - def cg(fname, unlocked = False): + # (cg: string; ext: string; w: float; h: float; unlocked?: boolean): Displayable + def cg(fname, ext, w, h, unlocked = False): if not unlocked: return NOT_UNLOCKED_COVER - return im.FactorScale("images/cgs/" + fname + ".png", 0.225, 0.225) + scaleFactor = getBoxNormalizerRatio(w, h) + return im.FactorScale("images/cgs/" + fname + "." + ext, scaleFactor["x"], scaleFactor["y"]) # Create an object in g:Gallery, add to galleryItems - # (imageName: string; unlocked?: boolean): None - def addGalleryItem(imageName, unlocked = False): + # (imageName: string; ext: string; w: float; h: float; unlocked?: boolean): None + def addGalleryItem(imageName, ext, w, h, unlocked = False): g.button(imageName) g.image(imageName) @@ -49,7 +61,7 @@ init python: galleryItems.append({ "item": imageName, - "cg": cg(imageName, unlocked) + "cg": cg(imageName, ext, w, h, unlocked) }) # Reads /images/cgs dir for all .png files @@ -64,24 +76,83 @@ init python: workingDirPath = getcwd().replace("\\", "/") cgDirPath = workingDirPath + "/game/" + cgPath - # Add each .png to the gallery - # TODO: make case insensitive + # Add each image to the gallery galleryItems = [] for cgFile in listdir(cgDirPath): - if isfile(join(cgDirPath, cgFile)): - if (cgFile[-4:] == '.png'): + filePath = join(cgDirPath, cgFile) + if isfile(filePath): + ext = cgFile[-3:].lower() + if (ext in ACCEPTED_EXTENSIONS): + attr = getImageFileAttributes(filePath) fname = str(cgFile[0:-4]) unlocked = fname in persistent.cggallery - addGalleryItem(fname, unlocked) + addGalleryItem(fname, ext, attr["w"], attr["h"], unlocked) # Add empty items to fill grid after last cg button extraSpaces = GALLERY_COLS - (len(galleryItems) % GALLERY_COLS) for i in range(0, extraSpaces): galleryItems.append({ "item": None, - "cg": None + "cg": None, + "ext": None }) + # Get width/height of image by absolute path + # based on https://stackoverflow.com/questions/8032642/how-to-obtain-image-size-using-standard-python-class-without-using-external-lib + # (filePath: string) -> { w: int; h: int } | None + def getImageFileAttributes(filePath): + try: + from struct import unpack + from imghdr import what + + w, h = 0, 0 + + with open(filePath, "rb") as file: + ext = what(filePath) + + header = file.read(24) + if (len(header) != 24): + raise Exception("File header invalid for " + filePath) + + if (ext == 'png'): + checksum = unpack('>i', header[4:8])[0] + if (checksum != 0x0d0a1a0a): + raise Exception("File checksum invalid for " + filePath) + + w, h = unpack('>ii', head[16:24]) + + if (ext == 'jpeg' or ext == 'jpg'): + file.seek(0) + size = 2 + ftype = 0 + while not 0xc0 <= ftype <= 0xcf: + file.seek(size, 1) + byte = file.read(1) + + while ord(byte) == 0xff: + byte = file.read(1) + + ftype = ord(byte) + size = unpack('>H', file.read(2))[0] - 2 + + file.seek(1, 1) + h, w = unpack('>HH', file.read(4)) + + return { "w": w, "h": h } + except Exception: + #TODO: log error + return None + + # Returns what params to call im.FactorScale with for cg button size + # Basically the delta diff dimensions + # (w: int; h: int) -> { x: float; y: float } + def getBoxNormalizerRatio(w, h): + x = round(float(PREFERRED_WIDTH) / float(w), 4) + y = round(float(PREFERRED_HEIGHT) / float(h), 4) + + return { "x": x, "y": y } + + # Returns the pageth slice from galleryItems # (page: int): Partial def getGalleryPage(page): @@ -98,7 +169,7 @@ init python: ## A screen that shows the image gallery screen cg_gallery(): tag menu - use game_menu(_("Gallery")): + use game_menu(_("Gallery"), scroll="viewport"): fixed: $ pageItems = getGalleryPage(galleryPage)