coding UTF-8; // Specify character encoding (to prevent mojibake) // Import libraries import GUI; // Library for GUI components like windows and buttons import Graphics; // Library for handling image data and graphic resources import Graphics2D; // Library for 2D rendering import File; // Library for handling file and folder information import system.Int; // Library providing helper functions for integers // Constant settings for GUI const int DEFAULT_WINDOW_WIDTH = 800; // Initial window width const int DEFAULT_WINDOW_HEIGHT = 700; // Initial window height const int CONTROL_AREA_HEIGHT = 60; // Height of the control section at the bottom of the window const int PLAY_BUTTON_WIDTH = 140; // Width of the play/stop button const string PLAY_TEXT = "PLAY"; // Label text for the play button const string STOP_TEXT = "STOP"; // Label text for the stop button const int BACKGROUND_COLOR[] = { 50, 50, 50, 255 }; // Background color (R,G,B,Alpha) // Constant settings for file paths const string DEFAULT_DIRECTORY_PATH = "./sample"; // Default folder path const string DEFAULT_FILE_NAME = "image"; // Default image file name (excluding number and extension) const string FILE_EXTENSION_SELECT_MESSAGE = "- Select Extension -"; // Message shown when selecting file extension const string FILE_EXTENSIONS[] = { FILE_EXTENSION_SELECT_MESSAGE, ".png", ".jpg" }; // Extension options // Other constant settings const int DEFAULT_WAIT = 30; // Default interval between frames (milliseconds) const int FILE_NUMBER_SEARCH_LIMIT = 1000; // Max number to search for start of sequence // Variables related to file names and paths (set in the configure function) string directoryPath; // Path to the folder containing image files string fileNameHead; // File name prefix (excluding number and extension) string extension; // File extension (including dot) int fileNumberBegin; // Starting number of the sequence int fileNumberEnd; // Ending number of the sequence // Variables related to GUI layout int windowWidth = DEFAULT_WINDOW_WIDTH; // Width of the image display window (can change) int windowHeight = DEFAULT_WINDOW_HEIGHT; // Height of the image display window (can change) int screenWidth; // Width of the display screen (set in the "configure" function) int screenHeight; // Height of the display screen (set in the "configure" function) // Variables for GUI components int screenWindow = NULL; // ID of the image display window int screenImageLabel = NULL; // ID of the image display label int screenGraphics = NULL; // ID of the graphics resource for drawing on the screen int screenRenderer = NULL; // ID of the renderer (drawing engine) for the screen int playButton = NULL; // ID of the play/stop button int seekSlider = NULL; // ID of the seek bar int waitField = NULL; // ID of the field for setting frame interval int seekLabel = NULL; // ID of the label next to the seek bar int waitLabel = NULL; // ID of the label next to the interval input field // Variables related to images int totalNumberOfImages; // Total number of image files string imageFilePaths[]; // Array to store paths of all image files int imageGraphicsBuffers[]; // Array to buffer image contents (stores graphics resource IDs for each image) // Variables for control bool continuesMainLoop = true; // Controls whether the main loop continues (set to false to exit) bool windowSizeChanged = false; // Set to true when the window is resized, triggers re-layout bool playing = true; // True when playing, false when stopped bool bufferingEnabled; // Controls whether image buffering is enabled (set in the configure function) // ============================================================ // Main process - This function runs automatically on startup // ============================================================ void main() { // Perform configuration based on user input configure(); // Initialize drawing-related resources initializeGraphicsResources(); // Initialize GUI components initializeGUI(); // Apply layout settings to the GUI updateLayout(); // Once the image window is open, hide the CUI console window to avoid clutter hide(); // Variable to store the last displayed image index, to avoid redundant redraws int lastImageIndex = -1; // Main loop: updates and redraws the image on each cycle while (continuesMainLoop) { // Get the image index to display, based on the seek bar's position int imageIndex = getComponentInt(seekSlider); // Draw the image on the screen (skip if the image hasn't changed) if (imageIndex != lastImageIndex) { drawScreen(imageIndex); } // Handle window resize if (windowSizeChanged) { // This variable is set to true in "onWindowResize" when a resize is detected updateLayout(); // Reapply GUI layout drawScreen(imageIndex); // Redraw image on screen windowSizeChanged = false; // Reset resize flag } // Store the index of the last drawn image lastImageIndex = imageIndex; // Wait for the specified interval before the next frame (animation delay) int wait = getWaitValue(); sleep(wait); // Advance time if animation is playing if (playing) { // Increment image index; loop back to start if at the end imageIndex++; if (imageIndex == totalNumberOfImages) { imageIndex = 0; } // Update the seek bar to match the new image index setComponentInt(seekSlider, imageIndex); } } // Exit the program when the main loop ends exit(); } // ================================================== // Function for handling configuration via user input // ================================================== void configure() { // Select the folder to load image files from (if skipped, the "sample" folder is used) directoryPath = DEFAULT_DIRECTORY_PATH; if (confirm("Do you want to specify the folder for loading images?", "(If you select 'No', the '" + DEFAULT_DIRECTORY_PATH + "' folder will be used)")) { directoryPath = choose("Select the folder to load images from", "."); } println("Image folder = " + directoryPath); // Enter the file name (excluding number and extension) fileNameHead = input("Enter the image file name (excluding number and extension)", DEFAULT_FILE_NAME); println("Image file name = " + fileNameHead); // Select the file extension extension = select(FILE_EXTENSIONS); while (extension == FILE_EXTENSION_SELECT_MESSAGE) { extension = select(FILE_EXTENSIONS); } println("Extension = " + extension); // Search for image files in the folder and determine the start and end of the sequence scanFileIndex(); // Choose whether to enable image buffering bufferingEnabled = confirm( "Enable image buffering?", "Buffering helps animations play more smoothly.", "It also reduces stress on HDDs, especially on older PCs.", "However, buffering uses more memory, which may cause issues in low-memory environments." ); } // ================================================================================== // Function to search for image files and determine the start and end of the sequence // ================================================================================== void scanFileIndex() { println("Searching for files..."); // Search for the starting number and set it to "fileNumberBegin" fileNumberBegin = 0; while (true) { // Starting from 0, increment fileNumberBegin by 1 and // check if a file with that number exists; // the first one found will be considered the start of the sequence string fileName = fileNameHead + fileNumberBegin + extension; string filePath = getFilePath(fileName, directoryPath); if (exists(filePath)) { println("Start file: " + filePath); break; } if(fileNumberBegin >= FILE_NUMBER_SEARCH_LIMIT) { popup("Searched up to file number " + FILE_NUMBER_SEARCH_LIMIT + " but no sequential file was found."); exit(); } fileNumberBegin++; } println("Start number = " + fileNumberBegin); // Search for the ending number and set it to fileNumberEnd fileNumberEnd = fileNumberBegin; while (true) { // Starting from fileNumberBegin, increment fileNumberEnd by 1 // and check if a file with that number exists; // when no such file is found, set (current number - 1) as the end of the sequence string fileName = fileNameHead + fileNumberEnd + extension; string filePath = getFilePath(fileName, directoryPath); if (!exists(filePath)) { fileNumberEnd--; break; } fileNumberEnd++; } println("End number = " + fileNumberEnd); } // ================================================== // Initialization for rendering-related resources // ================================================== void initializeGraphicsResources() { // Calculate and set the total number of image files totalNumberOfImages = fileNumberEnd - fileNumberBegin + 1; // Allocate arrays for file paths and buffers, using the total image count alloc(imageFilePaths, totalNumberOfImages); alloc(imageGraphicsBuffers, totalNumberOfImages); // Store the full file paths in the array for (int i=0; i= 10000 && wait != lastWait) { if( !confirm("A large value (10 seconds or more) is specified for the interval. Are you sure you want to apply it?") ) { waitText = (string)DEFAULT_WAIT; setComponentText(waitField, waitText); wait = DEFAULT_WAIT; } } // Store the value that triggered a warning, to skip future warnings for the same value lastWait = wait; return wait; } // ============================================================ // Function to handle layout of GUI components on the screen // ============================================================ void updateLayout() { // Get the inner size of the window and calculate the display area size int windowInnnerSize[] = getComponentSize(screenWindow, INNER); screenWidth = windowInnnerSize[0]; screenHeight = windowInnnerSize[1] - CONTROL_AREA_HEIGHT; // Update the renderer and image label with the new screen size setGraphics2DSize(screenRenderer, screenWidth, screenHeight); setComponentSize(screenImageLabel, screenWidth, screenHeight); // Layout settings for the control panel at the bottom of the window // Play/stop button setComponentSize(playButton, PLAY_BUTTON_WIDTH, CONTROL_AREA_HEIGHT); setComponentLocation(playButton, 0, screenHeight); // Seek bar setComponentSize(seekSlider, screenWidth - PLAY_BUTTON_WIDTH - 180, 20); setComponentLocation(seekSlider, PLAY_BUTTON_WIDTH + 160, screenHeight + 10); // Wait time input field setComponentSize(waitField, 100, 20); setComponentLocation(waitField, PLAY_BUTTON_WIDTH + 160, screenHeight + 30); // Info label to the left of the seek bar setComponentSize(seekLabel, 140, 20); setComponentLocation(seekLabel, PLAY_BUTTON_WIDTH + 20, screenHeight + 10); // Description label to the left of the wait time input setComponentSize(waitLabel, 140, 20); setComponentLocation(waitLabel, PLAY_BUTTON_WIDTH + 20, screenHeight + 30); // Repaint the labels paintComponent(seekLabel); paintComponent(waitLabel); } // ================================================== // Function to draw an image on the screen // ================================================== void drawScreen(int i) { // Declare a variable to store the ID of the graphics resource for the image int imageFileGraphics; // If buffering is enabled, use the preloaded graphics resource if (bufferingEnabled) { imageFileGraphics = imageGraphicsBuffers[i]; // If buffering is disabled, load the image from the file path and create a graphics resource } else { imageFileGraphics = newGraphics(imageFilePaths[i]); } // Get the width and height of the image int imageWidth = getGraphicsWidth(imageFileGraphics); int imageHeight = getGraphicsHeight(imageFileGraphics); // Calculate the aspect ratio of the image float imageAspectRatio = (float)imageWidth / (float)imageHeight; // Calculate the width and height to draw the image, fitting it to the screen while preserving aspect ratio int drawWidth = screenHeight * imageAspectRatio; int drawHeight = screenHeight; if (drawWidth > screenWidth) { drawWidth = screenWidth; drawHeight = screenWidth / imageAspectRatio; } // Calculate the top-left corner position to center the image on the screen int drawX = screenWidth/2 - drawWidth/2; int drawY = screenHeight/2 - drawHeight/2; // Fill the screen with the background color setDrawColor(screenRenderer, BACKGROUND_COLOR); drawRectangle(screenRenderer, 0, 0, screenWidth, screenHeight, true); // Draw the image on top of the background drawImage(screenRenderer, drawX, drawY, drawWidth, drawHeight, imageFileGraphics); int imageNumber = getComponentInt(seekSlider) + fileNumberBegin; setComponentString(seekLabel, "Image Number=" + imageNumber); // Repaint the image display label paintComponent(screenImageLabel); // If buffering is disabled, delete the graphics resource after each draw if (!bufferingEnabled) { deleteGraphics(imageFileGraphics); } } // ============================================================ // Function called when a button is clicked (event handler) // ============================================================ void onButtonClick(int id) { // If the play/stop button is clicked if (id == playButton) { // Toggle between playing and stopped states playing = !playing; // Update the button label accordingly if (playing) { setComponentString(playButton, STOP_TEXT); } else { setComponentString(playButton, PLAY_TEXT); } } } // ============================================================ // Function called when the window is resized (event handler) // ============================================================ void onWindowResize(int id, int width, int height) { // If the image display window is resized if (id == screenWindow) { // Set the resize flag to true to trigger layout update in the main loop // (Avoid heavy processing here since resize events may fire repeatedly) windowSizeChanged = true; } } // ============================================================ // Function called when the window is closed (event handler) // ============================================================ void onWindowClose (int id) { // If the image display window is closed if (id == screenWindow) { // Exit the main loop and end the program continuesMainLoop = false; } }