/* * This file is subject to the terms of the GFX License. If a copy of * the license was not distributed with this file, you can obtain one at: * * http://ugfx.org/license.html */ /** * @file src/gwin/gimage.c * @brief GWIN sub-system image code. */ #include "gfx.h" #if GFX_USE_GWIN && GWIN_NEED_IMAGE #include "gwin/class_gwin.h" #define widget(gh) ((GImageObject *)gh) static void _destroy(GWindowObject *gh) { if (gdispImageIsOpen(&widget(gh)->image)) gdispImageClose(&widget(gh)->image); } #if GWIN_NEED_IMAGE_ANIMATION static void _redraw(GHandle gh); static void _timer(void *gh) { // We need to re-test the visibility in case it has been made invisible since the last frame. if ((((GHandle)gh)->flags & GWIN_FLG_VISIBLE)) _redraw((GHandle)gh); } #endif static void _redraw(GHandle gh) { coord_t x, y, w, h, dx, dy; color_t bg; #if GWIN_NEED_IMAGE_ANIMATION delaytime_t delay; #endif // The default display area dx = 0; dy = 0; x = gh->x; y = gh->y; w = gh->width; h = gh->height; bg = gwinGetDefaultBgColor(); // If the image isn't open just clear the area if (!gdispImageIsOpen(&widget(gh)->image)) { gdispGFillArea(gh->display, x, y, w, h, bg); return; } // Center horizontally if the area is larger than the image if (widget(gh)->image.width < w) { w = widget(gh)->image.width; dx = (gh->width-w)/2; x += dx; if (dx) gdispGFillArea(gh->display, gh->x, y, dx, h, bg); gdispGFillArea(gh->display, x+w, y, gh->width-dx-w, h, bg); dx = 0; } // Center image horizontally if the area is smaller than the image else if (widget(gh)->image.width > w) { dx = (widget(gh)->image.width - w)/2; } // Center vertically if the area is larger than the image if (widget(gh)->image.height < h) { h = widget(gh)->image.height; dy = (gh->height-h)/2; y += dy; if (dy) gdispGFillArea(gh->display, x, gh->y, w, dy, bg); gdispGFillArea(gh->display, x, y+h, w, gh->height-dy-h, bg); dy = 0; } // Center image vertically if the area is smaller than the image else if (widget(gh)->image.height > h) { dy = (widget(gh)->image.height - h)/2; } // Reset the background color in case it has changed gdispImageSetBgColor(&widget(gh)->image, bg); // Display the image gdispGImageDraw(gh->display, &widget(gh)->image, x, y, w, h, dx, dy); #if GWIN_NEED_IMAGE_ANIMATION // read the delay for the next frame delay = gdispImageNext(&widget((GHandle)gh)->image); // Wait for that delay if required switch(delay) { case TIME_INFINITE: // Everything is done break; case TIME_IMMEDIATE: // We can't allow a continuous loop here as it would lock the system up so we delay for the minimum period delay = 1; // Fall through default: // Start the timer to draw the next frame of the animation gtimerStart(&widget((GHandle)gh)->timer, _timer, (void*)gh, FALSE, delay); break; } #endif } static const gwinVMT imageVMT = { "Image", // The class name sizeof(GImageObject), // The object size _destroy, // The destroy routine _redraw, // The redraw routine 0, // The after-clear routine }; GHandle gwinGImageCreate(GDisplay *g, GImageObject *gobj, GWindowInit *pInit) { if (!(gobj = (GImageObject *)_gwindowCreate(g, &gobj->g, pInit, &imageVMT, 0))) return 0; // Ensure the gdispImageIsOpen() gives valid results gobj->image.type = 0; // Initialise the timer #if GWIN_NEED_IMAGE_ANIMATION gtimerInit(&gobj->timer); #endif gwinSetVisible((GHandle)gobj, pInit->show); return (GHandle)gobj; } bool_t gwinImageOpenMemory(GHandle gh, const void* memory) { if (gdispImageIsOpen(&widget(gh)->image)) gdispImageClose(&widget(gh)->image); if (!gdispImageSetMemoryReader(&widget(gh)->image, memory)) return FALSE; if (gdispImageOpen(&widget(gh)->image) != GDISP_IMAGE_ERR_OK) return FALSE; if ((gh->flags & GWIN_FLG_VISIBLE)) { // Setting the clip here shouldn't be necessary if the redraw doesn't overdraw // but we put it in for safety anyway #if GDISP_NEED_CLIP gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); #endif _redraw(gh); } return TRUE; } #if defined(WIN32) || GFX_USE_OS_WIN32 || GFX_USE_OS_LINUX || GFX_USE_OS_OSX || defined(__DOXYGEN__) bool_t gwinImageOpenFile(GHandle gh, const char* filename) { if (gdispImageIsOpen(&widget(gh)->image)) gdispImageClose(&widget(gh)->image); if (!gdispImageSetFileReader(&widget(gh)->image, filename)) return FALSE; if (gdispImageOpen(&widget(gh)->image) != GDISP_IMAGE_ERR_OK) return FALSE; if ((gh->flags & GWIN_FLG_VISIBLE)) { // Setting the clip here shouldn't be necessary if the redraw doesn't overdraw // but we put it in for safety anyway #if GDISP_NEED_CLIP gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); #endif _redraw(gh); } return TRUE; } #endif #if GFX_USE_OS_CHIBIOS || defined(__DOXYGEN__) bool_t gwinImageOpenStream(GHandle gh, void *streamPtr) { if (gdispImageIsOpen(&widget(gh)->image)) gdispImageClose(&widget(gh)->image); if (!gdispImageSetBaseFileStreamReader(&widget(gh)->image, streamPtr)) return FALSE; if (gdispImageOpen(&widget(gh)->image) != GDISP_IMAGE_ERR_OK) return FALSE; if ((gh->flags & GWIN_FLG_VISIBLE)) { // Setting the clip here shouldn't be necessary if the redraw doesn't overdraw // but we put it in for safety anyway #if GDISP_NEED_CLIP gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); #endif _redraw(gh); } return TRUE; } #endif gdispImageError gwinImageCache(GHandle gh) { return gdispImageCache(&widget(gh)->image); } #endif // GFX_USE_GWIN && GWIN_NEED_IMAGE /** @} */