You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
369 lines
13 KiB
369 lines
13 KiB
/* |
|
* 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 <http://www.gnu.org/licenses/>. |
|
*/ |
|
|
|
#include <eez/gui/gui.h> |
|
|
|
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
|
|
|