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

/*
* 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