This is a simple viewer application that scans the microSD card for files with a JPG suffix, and presents a scrolling menu, so you can choose an image. It then loads the JPEG using cmd_loadimage.
Touching the screen highlights circular areas.
(Note that the JPEG load time is about 0.9s - it is not visible in the video because JPEG loading interrupts screenshots.)
#include <EEPROM.h>
#include <SPI.h>
#include <GD2.h>
class scroller {
public:
signed short dragprev;
int vel; // velocity
long base; // screen x coordinate, in 1/16ths pixel
long limit;
void init(uint32_t lim) {
dragprev = -32768;
vel = 0; // velocity
base = 0; // screen x coordinate, in 1/16ths pixel
limit = lim;
}
void run(bool touching, int16_t sx) {
if (touching & (dragprev != -32768)) {
vel = (dragprev - sx) << 4;
} else {
int change = max(1, abs(vel) >> 5);
if (vel < 0)
vel += change;
if (vel > 0)
vel -= change;
}
dragprev = touching ? sx : -32768;
base += vel;
base = max(0, min(base, limit));
}
uint16_t getval() {
return base >> 4;
}
};
void setup()
{
Serial.begin(115200);
GD.begin();
}
#define PROGRESS_Y 136
static void progress(long a, long b)
{
GD.wr32(REG_MACRO_0, VERTEX2II(40 + 399 * a / b, PROGRESS_Y, 0, 0));
}
static void draw_progress()
{
GD.Begin(LINES);
GD.ColorRGB(0xc0c0c0);
GD.LineWidth(16 * 5);
GD.Vertex2ii(40, PROGRESS_Y);
GD.Vertex2ii(440, PROGRESS_Y);
GD.ColorRGB(0x000000);
GD.LineWidth(16 * 3);
GD.Vertex2ii(40, PROGRESS_Y);
GD.Vertex2ii(440, PROGRESS_Y);
GD.ColorRGB(0xffffff);
GD.LineWidth(16 * 2);
GD.Vertex2ii(40, PROGRESS_Y);
GD.Macro(0);
GD.swap();
}
static void paint_alpha()
{
GD.ColorMask(0, 0, 0, 1);
GD.Clear();
}
static void use_alpha()
{
GD.ColorMask(1, 1, 1, 1);
GD.BlendFunc(DST_ALPHA, ONE_MINUS_DST_ALPHA);
}
static char *getdir(char directory[60][8], int i)
{
static char nm[8 + 1 + 3 + 1];
nm[8] = 0;
strncpy(nm, directory[i], 8);
strcat(nm, ".jpg");
return nm;
}
static void showdir0(char directory[60][8], int num_jpgs, int yb, int sel)
{
GD.Tag(0);
GD.SaveContext();
GD.ScissorSize(480, 136);
GD.cmd_gradient(0, 0, 0x3030c0, 0, 136, 0x000000);
GD.ScissorXY(0, 136);
GD.cmd_gradient(0, 136, 0x080800, 0, 272, 0x908880);
GD.RestoreContext();
GD.LineWidth(48);
GD.Begin(RECTS);
GD.ColorRGB(0xffffff);
for (int j = 0; j < num_jpgs; j++) {
int y = 40 * j - yb;
if ((-50 < y) && (y < 272)) {
GD.ColorA((sel == j) ? 0xff : 0xa0);
GD.Vertex2f(16 * (240 - 100), 16 * (y + 6));
GD.Vertex2f(16 * (240 + 100), 16 * (y + 40 - 6));
}
}
}
static void showdir1(char directory[60][8], int num_jpgs, int yb, int sel)
{
GD.ColorA(0xff);
GD.ColorRGB(0x000000);
for (int j = 0; j < num_jpgs; j++) {
int y = 40 * j - yb;
if ((-50 < y) && (y < 272)) {
GD.Tag(128 + j);
GD.cmd_text(240, y + 18, 28, OPT_CENTER, getdir(directory, j));
}
}
}
void loop()
{
char directory[60][8];
int num_jpgs = 0;
int picked;
scroller yscroll;
{
int j = 0;
dirent de;
GD.__end();
do {
GD.SD.rdn((byte*)&de, GD.SD.o_root + j * 32, sizeof(de));
if ((0x20 < de.name[0]) && (de.name[0] < 0x80)) {
if (memcmp(de.ext, "JPG", 3) == 0) {
if (num_jpgs < 60) {
char *pd = directory[num_jpgs++];
byte i;
for (i = 0; i < 8 && de.name[i] != ' '; i++)
*pd++ = tolower(de.name[i]);
if (i != 8)
*pd = 0;
}
}
}
j++;
} while (de.name[0]);
GD.resume();
yscroll.init((40L * (num_jpgs - 4)) << 4);
do {
GD.get_inputs();
byte touching = (GD.inputs.x != -32768) && (GD.inputs.tag < 128);
yscroll.run(touching, GD.inputs.y);
showdir0(directory, num_jpgs, yscroll.base >> 4, -1);
showdir1(directory, num_jpgs, yscroll.base >> 4, -1);
GD.swap();
} while (GD.inputs.tag < 128);
picked = GD.inputs.tag - 128;
}
GD.Clear();
draw_progress();
GD.cmd_loadimage(0, 0);
int ok = GD.load(getdir(directory, picked), progress);
uint32_t m_ptr, m_w, m_h;
GD.cmd_getprops(m_ptr, m_w, m_h);
GD.finish();
uint16_t w = GD.rd16(m_w);
uint16_t h = GD.rd16(m_h);
byte prev_touching = 0;
int cx, cy;
float smooth_r;
GD.BitmapSize(BILINEAR, BORDER, BORDER, w, h);
float zoom = 0.3;
int fadeout = 0;
while (1) {
GD.get_inputs();
byte touching = GD.inputs.x != -32768;
showdir0(directory, num_jpgs, yscroll.base >> 4, picked);
showdir1(directory, num_jpgs, yscroll.base >> 4, picked);
GD.ColorRGB(0xffffff);
int zw = zoom * w / 2;
int zh = zoom * h / 2;
GD.Begin(BITMAPS);
GD.cmd_scale(F16(zoom), F16(zoom));
GD.cmd_setmatrix();
GD.Vertex2ii(240 - zw, 136 - zh, 0, 0);
zoom = min(1.0, zoom * 1.2);
if (touching) {
if (!prev_touching) {
cx = GD.inputs.x;
cy = GD.inputs.y;
smooth_r = 0;
} else {
int dx = cx - GD.inputs.x;
int dy = cy - GD.inputs.y;
float r = sqrt(dx * dx + dy * dy);
smooth_r = .75 * smooth_r + 0.25 * r;
}
fadeout = 15;
}
if (fadeout) {
fadeout--;
smooth_r += 1;
}
if (touching || fadeout) {
int r0 = int(15 * smooth_r);
int r1 = int(16 * smooth_r);
float fade = min(fadeout / 15., smooth_r / 40);
GD.SaveContext();
GD.ClearColorA(128 * fade);
paint_alpha();
GD.Begin(POINTS);
GD.BlendFunc(ZERO, ONE_MINUS_SRC_ALPHA);
GD.PointSize(r1);
GD.Vertex2ii(cx, cy);
use_alpha();
GD.ColorRGB(0x000000);
GD.Begin(RECTS);
GD.Vertex2ii(0, 0);
GD.Vertex2ii(480, 272);
GD.RestoreContext();
paint_alpha();
GD.Begin(POINTS);
GD.ColorA(255 * fadeout / 15);
GD.BlendFunc(ONE, ONE_MINUS_SRC_ALPHA);
GD.PointSize(r1);
GD.Vertex2ii(cx, cy);
GD.ColorA(255);
GD.BlendFunc(ZERO, ONE_MINUS_SRC_ALPHA);
GD.PointSize(r0);
GD.Vertex2ii(cx, cy);
use_alpha();
GD.PointSize(r1);
GD.Vertex2ii(cx, cy);
}
GD.swap();
prev_touching = touching;
}
}