Ren'Py galery dilemma

recreation

pure evil!
Respected User
Game Developer
Jun 10, 2018
6,327
22,777
I'm currently working on a image galery, but looking at how the renpy galery code is setup, I have the feeling that it's not really optimal, at least not for what I want to achieve. Sadly I've never been too deep into programming stuff, so I have some ideas, but don't know how to implement them.

Using the as reference:
g.condition("persistent.unlock_something" unlocks the image/s, this is nice if you want to unlock images after the ending, but it also means that you have to set a persistant for every image or every image within a label you want to unlock earlier.
I know renpy has a function to detect if an image has been seen in game ( ) which would be perfect for my scenario, but the way the galery is set up, it only accepts expressions for conditions.
Now I could use imagebuttons with instead, but by doing so, I'd loose the ability to show the different states (locked and unlocked) + I'm not sure if I can use expressions for the images, which would mean I'd also have to include buttons for both, the male and the female MC variant of the images.
I'd also have to create a new screen for every image to show, I 'm sure this can be achieved on the fly, but I'd probably take ages until I figure it out...

So I'd like to know what the guys with more experience than me think about it and how you would crack the problem here, do you even see a problem at all? (I might just be too stupid to see the simple way again xD).
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,979
16,236
Now I could use imagebuttons with instead, but by doing so, I'd loose the ability to show the different states (locked and unlocked) + I'm not sure if I can use expressions for the images, which would mean I'd also have to include buttons for both, the male and the female MC variant of the images.
For a gallery-like part of an oncoming, I solved the problem by using the (now renamed) . For your gallery, it could looks like this :
Python:
init python:    
    def isUnlocked( imgID ):
        # Whatever tell if the image is unlocked or not.
        # Return True is unlocked, False else.

    def galleryThumb( imgID ):
        imgID = str( imgID )
        if isUnlocked( imgID ):
            return Frame( "images/gallery/{}.png".format( nameByID[imgID] ), size=( galXSize, galYSize ) )
        else:
            return LiveComposite( ( galXSize, galYSize ), 
                                  ( 0, 0 ), Frame( "images/gallery/{}.png".format( nameByID[imgID] ), size=( galXSize, galYSize ) ),
                                  # Add some opacity to not show too much of the image.
                                  #  Note: This is untested, not remember is /Solid/ accept the /size/ 
                                  # property. If not, put the solid in a /Frame/.
                                  ( 0, 0 ), Solid( "#00000055", size=( galXSize, galYSize ) ) )
                                  # Put something on top of the image to mark that it's locked.
                                  ( 0, 0 ), Frame( "images/gallery/locked.png", size=( galXSize, galYSize ) ) )


#  Dict letting you find the name of the image by it's ID
define nameByID = { "0": "thisImage", "1": "thatOne", [...] }

# Size of the thumbnails
define galXSize = 200
define galYSize = 200


screen gallery:

    # dirty screen to show how it works
    for i in range( 0, 5 ): # row 
        for j in range( 0, 5 ): # collumn
            imagebutton:
                # ID of the actual image is ( row * 5 ) + collumn
                idle galleryThumb( ( i*5 ) + j )
                xsize galXSize ysize galYSize
                #  /Function( showSelectedImage )/ is whatever you want to happen
                # if the image is unlocked.
                action ( [ Function( showSelectedImage ) ] if isUnlocked( imgID ) is True else NullAction() )
In the end, you'll have something that will looks like this ; except that for you the locked images will be darker :
04 phone - stats legacy1.jpg

As for the other problem, well, there's so many way to do it. You talked yourself about renpy.seen_image, and it's probably part of the solution. But instead of using it, you use your own smaller "seen_image":

Code:
init python:    
    def isUnlocked( imgID ):
        # True it the ID is in the set, False else.
        return imgID in unlocked

default unlocked = set( [] )

label start:
    [...]
    show imageX
    $ unlocked.add( "1" )

label whatever:
    [...]
    show imageY
    $ unlocked.add( "52" )
 
  • Like
Reactions: recreation

the66

beware, the germans are cumming
Modder
Donor
Respected User
Jan 27, 2017
7,810
24,389
i stongly advise to use dedicated thumbnail images. resizing is a slow process. if your gallery is displayed with a viewport/vpgrid and has not only 6 images, you might wait a decent time on a slower machine, before your screen is displayed.
 
  • Like
Reactions: recreation

recreation

pure evil!
Respected User
Game Developer
Jun 10, 2018
6,327
22,777
i stongly advise to use dedicated thumbnail images. resizing is a slow process. if your gallery is displayed with a viewport/vpgrid and has not only 6 images, you might wait a decent time on a slower machine, before your screen is displayed.
I'm using a two by two grid instead with zoom on imagebuttons (using the original images instead of thumbnails).
 

the66

beware, the germans are cumming
Modder
Donor
Respected User
Jan 27, 2017
7,810
24,389
i made a android port lately of a game with a gallery. the gallery was displayed as vpgrid and had 36 images (2410x3000px). the thumbnails were generated at runtime. the 1st creation of the screen (displayables not cached) took roughly 30sec, with dedicated thumbnails almost instant.
with your 4 images you will probably see no difference.
edit: ok, the images were not only resized but also blurred. you have to judge what is more important, the slightly bigger release size or the gain in screen creation speed.
 
Last edited:

recreation

pure evil!
Respected User
Game Developer
Jun 10, 2018
6,327
22,777
okay, thx guys, I fiddled a bit and this is what I got:
Python:
init python:
    def galeryImg(imgID):
        imgID = str(imgID)
        if renpy.seen_image(imgID):
            return imgID
        else:
            return "nope.jpg"
            
define nameByID = ["2walk close", ...]

imagebutton:
    idle galeryImg(nameByID[0])
    hover LiveComposite(
        (1920, 1080),
        (0, 0), galeryImg(nameByID[0]),
        (0, 0), im.Alpha("h.jpg",0.3)
        )
    action [ Show("g_image_full", my_image=nameByID[0]), SensitiveIf(renpy.seen_image(nameByID[0])) ]
    at btnZoom
imagebutton:
    ...
            
screen g_image_full(my_image):
    add my_image
    textbutton "Return" action Hide("g_image_full") xalign 0.5 yalign .99
it works quite well. The only downside is that I'd have to add every button myself, but I'm gonna change that later, have to go to work now^^
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,979
16,236
edit: ok, the images were not only resized but also blurred. you have to judge what is more important, the slightly bigger release size or the gain in screen creation speed.
I think that it's the blur that effectively slowed the process, especially if it's done before the resizing. The algorithm need to use the surrounding pixels, which lead to at least 9 operations for each pixel, more if you want to do it well and look deeper in the neighborhood.
This while resizing is relatively smooth and fast, and rely on :
Code:
  for x from 0 to width step widthFactor:
     for y from 0 to height with step heightFactor:
         copy point in x,y
The smaller is the resulting image, the faster it go. And if it was really impacting, there wouldn't had a zoom feature since more than 12 years (it was already part of the 6.0 version).

But as you said, it also depend of the number of images that will be displayed at once.
 

recreation

pure evil!
Respected User
Game Developer
Jun 10, 2018
6,327
22,777
as an addition to my previous code, if anybody is interested.
Python:
screen g_images():
    style_prefix "gallery"
    tag menu
    add "mm_anim" at zoomEffect # do you remember this one @anne O'nymous ? ;)
    default gridSize = 9
    default page = 0
    grid 3 3:
        for i in range(page*gridSize,(page+1)*gridSize):
            imagebutton:
                idle galeryImg(nameByID[i])
                hover LiveComposite(
                    (1920, 1080), # I'm using the default resolution of the images, so it get's scaled correctly
                    (0, 0), galeryImg(nameByID[i]),
                    (0, 0), im.Alpha("h.jpg",0.3) # I'm using an almost white image as overlay, you could also use a color
                    )
                action [ Show("g_image_full", my_image=nameByID[i]), SensitiveIf(renpy.seen_image(nameByID[i])) ]
                at btnZoom
    if len(nameByID) > (page+1)*gridSize:
        textbutton ">>" action SetScreenVariable("page", page+1) xalign 0.99 yalign .5
    if page > 0:
        textbutton "<<" action SetScreenVariable("page", page-1) xalign 0.01 yalign .5
    textbutton "Return" action ShowMenu("gallery") xalign 0.5 yalign .99
    
screen g_image_full(my_image):
    add my_image
    textbutton "Return" action Hide("g_image_full") xalign 0.5 yalign .99
 
  • Like
Reactions: Huitieme

recreation

pure evil!
Respected User
Game Developer
Jun 10, 2018
6,327
22,777
grr... next problem. When I add more than "gridSize" images, I obviously get an "index is out of range". To avoid this I added another loop to add some invisible buttons to fill the grid.
Python:
            if i == len(nameByID) and i < (page+1)*gridSize:
                for n in range(len(nameByID), (page+1)*gridSize):
                    imagebutton:
                        idle im.Alpha("h.jpg",1)
                        hover im.Alpha("h.jpg",1)
                        action NullAction()
                        at btnZoom
but I still get "index is out of range", no matter what I try... and I can't find the error in my calculation :/
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,979
16,236
Python:
    add "mm_anim" at zoomEffect # do you remember this one @anne O'nymous ? ;)
Oh yeah. I keep it preciously in my toolbox folder, and still don't understand why the default zoom is so "unstable".


but I still get "index is out of range", no matter what I try... and I can't find the error in my calculation :/
Mostly the error is that you put it in the wrong place:
Code:
screen g_images():
    [...]
        for i in range(page*gridSize,(page+1)*gridSize):
            showIf len( nameByID ) < i:
                imagebutton:
                    idle galeryImg(nameByID[i])
    [...]
 

recreation

pure evil!
Respected User
Game Developer
Jun 10, 2018
6,327
22,777
Oh yeah. I keep it preciously in my toolbox folder, and still don't understand why the default zoom is so "unstable".




Mostly the error is that you put it in the wrong place:
Code:
screen g_images():
    [...]
        for i in range(page*gridSize,(page+1)*gridSize):
            showIf len( nameByID ) < i:
                imagebutton:
                    idle galeryImg(nameByID[i])
    [...]
I think you misunderstood what I'm trying to do. I have a grid size 3x3, so gridSize = 9 images per page.
Python:
...
    grid 3 3:
        for i in range(page*gridSize,(page+1)*gridSize):
            imagebutton:
                idle galeryImg(nameByID[i])
                hover LiveComposite(
                    (1920, 1080),
                    (0, 0), galeryImg(nameByID[i]),
                    (0, 0), im.Alpha("h.jpg",0.3)
                    )
                action [ Show("g_image_full", my_image=nameByID[i]), SensitiveIf(renpy.seen_image(nameByID[i])) ]
                at btnZoom
but when don't specifiy exactly 9 images (or x9 images) I get the error, so I added:
Python:
...
    grid 3 3:
        for i in range(page*gridSize,(page+1)*gridSize):
            imagebutton:
                ...
            if i == len(nameByID) and i < (page+1)*gridSize:
                for n in range(len(nameByID), (page+1)*gridSize):
                    imagebutton:
                        idle im.Alpha("h.jpg",1)
                        hover im.Alpha("h.jpg",1)
                        action NullAction()
                        at btnZoom
So the grid get's filled with invisible images, but it doesn't work as expected :/
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,979
16,236
I think you misunderstood what I'm trying to do. I have a grid size 3x3, so gridSize = 9 images per page.
My fault. I noted the grid, but was too focused on the misplacing of your test, and I forgot to take care of the grid.

Code:
screen g_images():
    [...]
        for i in range(page*gridSize,(page+1)*gridSize):
            # Either an image, if there's one
            if len( nameByID ) < i:
                imagebutton:
                  idle galeryImg(nameByID[i])
            # Or an invisible image, an empty "frame looking", or whatever to fill.
            else:
                 add Solid( "#00000000" )
    [...]
 

recreation

pure evil!
Respected User
Game Developer
Jun 10, 2018
6,327
22,777
ah, I almost started to doubt my sanity: if len( nameByID ) <= i: works, or if i < len(nameByID):...
Thx again!
 
Last edited: