/* * EEZ Modular Firmware * Copyright (C) 2015-present, Envox d.o.o. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include namespace eez { namespace gui { using namespace display; AnimationState g_animationState; static bool g_animationStateDirection; static Rect g_animationStateSrcRect; static Rect g_animationStateDstRect; void animateOpenCloseCallback(float t, VideoBuffer bufferOld, VideoBuffer bufferNew, VideoBuffer bufferDst) { if (!g_animationStateDirection) { auto bufferTemp = bufferOld; bufferOld = bufferNew; bufferNew = bufferTemp; } auto remapX = g_animationStateDirection ? remapExp : remapOutExp; auto remapY = g_animationStateDirection ? remapExp : remapOutExp; int srcX1; int srcY1; int srcX2; int srcY2; int dstX1; int dstY1; int dstX2; int dstY2; if (g_animationStateDirection) { srcX1 = g_animationStateSrcRect.x; srcY1 = g_animationStateSrcRect.y; srcX2 = g_animationStateSrcRect.x + g_animationStateSrcRect.w; srcY2 = g_animationStateSrcRect.y + g_animationStateSrcRect.h; int dx = MAX(g_animationStateSrcRect.x - g_animationStateDstRect.x, g_animationStateDstRect.x + g_animationStateDstRect.w - (g_animationStateSrcRect.x + g_animationStateSrcRect.w)); int dy = MAX(g_animationStateSrcRect.y - g_animationStateDstRect.y, g_animationStateDstRect.y + g_animationStateDstRect.h - (g_animationStateSrcRect.y + g_animationStateSrcRect.h)); dstX1 = g_animationStateSrcRect.x - dx; dstY1 = g_animationStateSrcRect.y - dy; dstX2 = g_animationStateSrcRect.x + g_animationStateSrcRect.w + dx; dstY2 = g_animationStateSrcRect.y + g_animationStateSrcRect.h + dy; } else { int dx = MAX(g_animationStateDstRect.x - g_animationStateSrcRect.x, g_animationStateSrcRect.x + g_animationStateSrcRect.w - (g_animationStateDstRect.x + g_animationStateDstRect.w)); int dy = MAX(g_animationStateDstRect.y - g_animationStateSrcRect.y, g_animationStateSrcRect.y + g_animationStateSrcRect.h - g_animationStateDstRect.y + g_animationStateDstRect.h); srcX1 = g_animationStateDstRect.x - dx; srcY1 = g_animationStateDstRect.y - dx; srcX2 = g_animationStateDstRect.x + g_animationStateDstRect.w + dx; srcY2 = g_animationStateDstRect.y + g_animationStateDstRect.h + dy; dstX1 = g_animationStateDstRect.x; dstY1 = g_animationStateDstRect.y; dstX2 = g_animationStateDstRect.x + g_animationStateDstRect.w; dstY2 = g_animationStateDstRect.y + g_animationStateDstRect.h; } int x1 = (int)round(remapX((float)t, 0, (float)srcX1, 1, (float)dstX1)); if (g_animationStateDirection) { if (x1 < g_animationStateDstRect.x) { x1 = g_animationStateDstRect.x; } } else { if (x1 < g_animationStateSrcRect.x) { x1 = g_animationStateSrcRect.x; } } int y1 = (int)round(remapY((float)t, 0, (float)srcY1, 1, (float)dstY1)); if (g_animationStateDirection) { if (y1 < g_animationStateDstRect.y) { y1 = g_animationStateDstRect.y; } } else { if (y1 < g_animationStateSrcRect.y) { y1 = g_animationStateSrcRect.y; } } int x2 = (int)round(remapX((float)t, 0, (float)srcX2, 1, (float)dstX2)); if (g_animationStateDirection) { if (x2 > g_animationStateDstRect.x + g_animationStateDstRect.w) { x2 = g_animationStateDstRect.x + g_animationStateDstRect.w; } } else { if (x2 > g_animationStateSrcRect.x + g_animationStateSrcRect.w) { x2 = g_animationStateSrcRect.x + g_animationStateSrcRect.w; } } int y2 = (int)round(remapY((float)t, 0, (float)srcY2, 1, (float)dstY2)); if (g_animationStateDirection) { if (y2 > g_animationStateDstRect.y + g_animationStateDstRect.h) { y2 = g_animationStateDstRect.y + g_animationStateDstRect.h; } } else { if (y2 > g_animationStateSrcRect.y + g_animationStateSrcRect.h) { y2 = g_animationStateSrcRect.y + g_animationStateSrcRect.h; } } bitBlt(bufferOld, bufferDst, 0, 0, getDisplayWidth() - 1, getDisplayHeight() - 1); bitBlt(bufferNew, bufferDst, x1, y1, x2, y2); } void animateOpenClose(const Rect &srcRect, const Rect &dstRect, bool direction) { display::animate(BUFFER_OLD, animateOpenCloseCallback); g_animationStateSrcRect = srcRect; g_animationStateDstRect = dstRect; g_animationStateDirection = direction; } void animateOpen(const Rect &srcRect, const Rect &dstRect) { animateOpenClose(srcRect, dstRect, true); } void animateClose(const Rect &srcRect, const Rect &dstRect) { animateOpenClose(srcRect, dstRect, false); } static Rect g_clipRect; static int g_numRects; AnimRect g_animRects[MAX_ANIM_RECTS]; void animateRectsStep(float t, VideoBuffer bufferOld, VideoBuffer bufferNew, VideoBuffer bufferDst) { bitBlt(g_animationState.startBuffer == BUFFER_OLD ? bufferOld : bufferNew, bufferDst, 0, 0, getDisplayWidth() - 1, getDisplayHeight() - 1); float t1 = g_animationState.easingRects(t, 0, 0, 1, 1); // rects float t2 = g_animationState.easingOpacity(t, 0, 0, 1, 1); // opacity for (int i = 0; i < g_numRects; i++) { AnimRect &animRect = g_animRects[i]; int x, y, w, h; if (animRect.srcRect == animRect.dstRect) { x = animRect.srcRect.x; y = animRect.srcRect.y; w = animRect.srcRect.w; h = animRect.srcRect.h; } else { if (animRect.dstRect.x > animRect.srcRect.x) x = (int)roundf(animRect.srcRect.x + t1 * (animRect.dstRect.x - animRect.srcRect.x)); else x = (int)floorf(animRect.srcRect.x + t1 * (animRect.dstRect.x - animRect.srcRect.x)); if (animRect.dstRect.y > animRect.srcRect.y) y = (int)roundf(animRect.srcRect.y + t1 * (animRect.dstRect.y - animRect.srcRect.y)); else y = (int)floorf(animRect.srcRect.y + t1 * (animRect.dstRect.y - animRect.srcRect.y)); if (animRect.dstRect.w > animRect.srcRect.w) w = (int)ceilf(animRect.srcRect.w + t1 * (animRect.dstRect.w - animRect.srcRect.w)); else w = (int)floorf(animRect.srcRect.w + t1 * (animRect.dstRect.w - animRect.srcRect.w)); if (animRect.dstRect.h > animRect.srcRect.h) h = (int)ceilf(animRect.srcRect.h + t1 * (animRect.dstRect.h - animRect.srcRect.h)); else h = (int)floorf(animRect.srcRect.h + t1 * (animRect.dstRect.h - animRect.srcRect.h)); } uint8_t opacity; if (animRect.opacity == OPACITY_FADE_IN) { opacity = (uint8_t)roundf(clamp(roundf(t2 * 255), 0, 255)); } else if (animRect.opacity == OPACITY_FADE_OUT) { opacity = (uint8_t)roundf(clamp((1 - t2) * 255, 0, 255)); } else { opacity = 255; } if (animRect.buffer == BUFFER_SOLID_COLOR) { auto savedOpacity = setOpacity(opacity); setColor(animRect.color); // clip if (x < g_clipRect.x) { w -= g_clipRect.x - x; x = g_clipRect.x; } if (x + w > g_clipRect.x + g_clipRect.w) { w -= (x + w) - (g_clipRect.x + g_clipRect.w); } if (y < g_clipRect.y) { h -= g_clipRect.y - y; y = g_clipRect.y; } if (y + h > g_clipRect.y + g_clipRect.h) { h -= (y + h) - (g_clipRect.y + g_clipRect.y); } fillRect(bufferDst, x, y, x + w - 1, y + h - 1); setOpacity(savedOpacity); } else { void *buffer = animRect.buffer == BUFFER_OLD ? bufferOld : bufferNew; Rect &srcRect = animRect.buffer == BUFFER_OLD ? animRect.srcRect : animRect.dstRect; int sx; int sy; int sw; int sh; int dx; int dy; if (animRect.position == POSITION_TOP_LEFT || animRect.position == POSITION_LEFT || animRect.position == POSITION_BOTTOM_LEFT) { sx = srcRect.x; sw = MIN(srcRect.w, w); dx = x; } else if (animRect.position == POSITION_TOP || animRect.position == POSITION_CENTER || animRect.position == POSITION_BOTTOM) { if (srcRect.w < w) { sx = srcRect.x; sw = srcRect.w; dx = x + (w - srcRect.w) / 2; } else if (srcRect.w > w) { sx = srcRect.x + (srcRect.w - w) / 2; sw = w; dx = x; } else { sx = srcRect.x; sw = srcRect.w; dx = x; } } else { sw = MIN(srcRect.w, w); sx = srcRect.x + srcRect.w - sw; dx = x + w - sw; } if (animRect.position == POSITION_TOP_LEFT || animRect.position == POSITION_TOP || animRect.position == POSITION_TOP_RIGHT) { sy = srcRect.y; sh = MIN(srcRect.h, h); dy = y; } else if (animRect.position == POSITION_LEFT || animRect.position == POSITION_CENTER || animRect.position == POSITION_RIGHT) { if (srcRect.h < h) { sy = srcRect.y; sh = srcRect.h; dy = y + (h - srcRect.h) / 2; } else if (srcRect.h > h) { sy = srcRect.y + (srcRect.h - h) / 2; sh = h; dy = y; } else { sy = srcRect.y; sh = srcRect.h; dy = y; } } else { sh = MIN(srcRect.h, h); sy = srcRect.y + srcRect.h - sh; dy = y + h - sh; } // clip if (sx < g_clipRect.x) { sw -= g_clipRect.x - sx; dx += g_clipRect.x - sx; sx = g_clipRect.x; } if (dx < g_clipRect.x) { sw -= g_clipRect.x - dx; sx += g_clipRect.x - dx; dx = g_clipRect.x; } if (sx + sw > g_clipRect.x + g_clipRect.w) { sw -= (sx + sw) - (g_clipRect.x + g_clipRect.w); } if (dx + sw > g_clipRect.x + g_clipRect.w) { sw -= (dx + sw) - (g_clipRect.x + g_clipRect.w); } if (sy < g_clipRect.y) { sh -= g_clipRect.y - sy; dy += g_clipRect.y - sy; sy = g_clipRect.y; } if (dy < g_clipRect.y) { sh -= g_clipRect.y - dy; sy += g_clipRect.y - dy; dy = g_clipRect.y; } if (dy + sh > g_clipRect.y + g_clipRect.h) { sh -= (dy + sh) - (g_clipRect.y + g_clipRect.h); } if (sy + sh > g_clipRect.y + g_clipRect.h) { sy -= (sy + sh) - (g_clipRect.y + g_clipRect.h); } bitBlt(buffer, bufferDst, sx, sy, sw, sh, dx, dy, opacity); } } } void prepareRect(AppContext *appContext, Rect &rect) { if (appContext->rect.x > 0) { rect.x += appContext->rect.x; } if (appContext->rect.y > 0) { rect.y += appContext->rect.y; } } void animateRects(AppContext *appContext, Buffer startBuffer, int numRects, float duration, const Rect *clipRect) { display::animate(startBuffer, animateRectsStep, duration); g_numRects = numRects; if (clipRect) { g_clipRect.x = clipRect->x; g_clipRect.y = clipRect->y; g_clipRect.w = clipRect->w; g_clipRect.h = clipRect->h; prepareRect(appContext, g_clipRect); } else { g_clipRect.x = appContext->rect.x; g_clipRect.y = appContext->rect.y; g_clipRect.w = appContext->rect.w; g_clipRect.h = appContext->rect.h; } for (int i = 0; i < numRects; i++) { prepareRect(appContext, g_animRects[i].srcRect); prepareRect(appContext, g_animRects[i].dstRect); } } } // gui } // eez