summaryrefslogtreecommitdiff
path: root/apps/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins')
-rw-r--r--apps/plugins/pictureflow.c154
1 files changed, 98 insertions, 56 deletions
diff --git a/apps/plugins/pictureflow.c b/apps/plugins/pictureflow.c
index 0e48a64e2d..bd51a88b36 100644
--- a/apps/plugins/pictureflow.c
+++ b/apps/plugins/pictureflow.c
@@ -111,6 +111,9 @@ typedef fb_data pix_t;
111#define REFLECT_SC ((0x10000U * 3 + (REFLECT_HEIGHT * 5 - 1)) / \ 111#define REFLECT_SC ((0x10000U * 3 + (REFLECT_HEIGHT * 5 - 1)) / \
112 (REFLECT_HEIGHT * 5)) 112 (REFLECT_HEIGHT * 5))
113#define DISPLAY_OFFS ((LCD_HEIGHT / 2) - REFLECT_HEIGHT) 113#define DISPLAY_OFFS ((LCD_HEIGHT / 2) - REFLECT_HEIGHT)
114#define CAM_DIST MAX(MIN(LCD_HEIGHT,LCD_WIDTH),120)
115#define CAM_DIST_R (CAM_DIST << PFREAL_SHIFT)
116#define DISPLAY_LEFT_R (PFREAL_HALF - LCD_WIDTH * PFREAL_ONE / 2)
114 117
115#define SLIDE_CACHE_SIZE 100 118#define SLIDE_CACHE_SIZE 100
116 119
@@ -235,7 +238,6 @@ static struct configdata config[] =
235/** below we allocate the memory we want to use **/ 238/** below we allocate the memory we want to use **/
236 239
237static pix_t *buffer; /* for now it always points to the lcd framebuffer */ 240static pix_t *buffer; /* for now it always points to the lcd framebuffer */
238static PFreal rays[LCD_WIDTH];
239static uint16_t reflect_table[REFLECT_HEIGHT]; 241static uint16_t reflect_table[REFLECT_HEIGHT];
240static struct slide_data center_slide; 242static struct slide_data center_slide;
241static struct slide_data left_slides[MAX_SLIDES_COUNT]; 243static struct slide_data left_slides[MAX_SLIDES_COUNT];
@@ -337,6 +339,16 @@ static inline PFreal fmul(PFreal a, PFreal b)
337 return (a*b) >> PFREAL_SHIFT; 339 return (a*b) >> PFREAL_SHIFT;
338} 340}
339 341
342/**
343 * This version preshifts each operand, which is useful when we know how many
344 * of the least significant bits will be empty, or are worried about overflow
345 * in a particular calculation
346 */
347static inline PFreal fmuln(PFreal a, PFreal b, int ps1, int ps2)
348{
349 return ((a >> ps1) * (b >> ps2)) >> (PFREAL_SHIFT - ps1 - ps2);
350}
351
340/* ARMv5+ has a clz instruction equivalent to our function. 352/* ARMv5+ has a clz instruction equivalent to our function.
341 */ 353 */
342#if (defined(CPU_ARM) && (ARM_ARCH > 4)) 354#if (defined(CPU_ARM) && (ARM_ARCH > 4))
@@ -1193,23 +1205,28 @@ void reset_slides(void)
1193/** 1205/**
1194 Updates look-up table and other stuff necessary for the rendering. 1206 Updates look-up table and other stuff necessary for the rendering.
1195 Call this when the viewport size or slide dimension is changed. 1207 Call this when the viewport size or slide dimension is changed.
1208 *
1209 * To calculate the offset that will provide the proper margin, we use the same
1210 * projection used to render the slides. Assuming zo == 0, the solution for xc,
1211 * the slide center, is:
1212 * xp * xs * sin(r)
1213 * xc = xp - xs * cos(r) + ────────────────
1214 * z
1215 * TODO: support moving the side slides toward or away from the camera
1196 */ 1216 */
1197void recalc_table(void) 1217void recalc_offsets(void)
1198{ 1218{
1199 int w = (LCD_WIDTH + 1) / 2; 1219 PFreal xs = PFREAL_HALF - DISPLAY_WIDTH * PFREAL_HALF;
1200 int h = (LCD_HEIGHT + 1) / 2; 1220 PFreal xp = DISPLAY_WIDTH * PFREAL_HALF - PFREAL_HALF + center_margin *
1201 int i; 1221 PFREAL_ONE;
1202 for (i = 0; i < w; i++) { 1222 PFreal cosr, sinr;
1203 PFreal gg = (PFREAL_HALF + i * PFREAL_ONE) / (2 * h);
1204 rays[w - i - 1] = -gg;
1205 rays[w + i] = gg;
1206 }
1207 1223
1208 itilt = 70 * IANGLE_MAX / 360; /* approx. 70 degrees tilted */ 1224 itilt = 70 * IANGLE_MAX / 360; /* approx. 70 degrees tilted */
1209 1225 cosr = fcos(-itilt);
1210 offsetX = DISPLAY_WIDTH / 2 * (fsin(itilt) + PFREAL_ONE); 1226 sinr = fsin(-itilt);
1227 offsetX = xp - fmul(xs, cosr) + fmuln(xp,
1228 fmuln(xs, sinr, PFREAL_SHIFT - 2, 0), PFREAL_SHIFT - 2, 0)/CAM_DIST;
1211 offsetY = DISPLAY_WIDTH / 2 * (fsin(itilt) + PFREAL_ONE / 2); 1229 offsetY = DISPLAY_WIDTH / 2 * (fsin(itilt) + PFREAL_ONE / 2);
1212 offsetX += center_margin << PFREAL_SHIFT;
1213} 1230}
1214 1231
1215 1232
@@ -1246,68 +1263,83 @@ static inline pix_t fade_color(pix_t c, unsigned int a)
1246#endif 1263#endif
1247 1264
1248/** 1265/**
1249 Render a single slide 1266 * Render a single slide
1250*/ 1267 * Where xc is the slide's horizontal offset from center, xs is the horizontal
1268 * on the slide from its center, zo is the slide's depth offset from the plane
1269 * of the display, r is the angle at which the slide is tilted, and xp is the
1270 * point on the display corresponding to xs on the slide, the projection
1271 * formulas are:
1272 *
1273 * z * (xc + xs * cos(r))
1274 * xp = ──────────────────────
1275 * z + zo + xs * sin(r)
1276 *
1277 * z * (xc - xp) - xp * zo
1278 * xs = ────────────────────────
1279 * xp * sin(r) - z * cos(r)
1280 *
1281 * We use the xp projection once, to find the left edge of the slide on the
1282 * display. From there, we use the xs reverse projection to find the horizontal
1283 * offset from the slide center of each column on the screen, until we reach
1284 * the right edge of the slide, or the screen. The reverse projection can be
1285 * optimized by saving the numerator and denominator of the fraction, which can
1286 * then be incremented by (z + zo) and sin(r) respectively.
1287 */
1251void render_slide(struct slide_data *slide, const int alpha) 1288void render_slide(struct slide_data *slide, const int alpha)
1252{ 1289{
1253 struct bitmap *bmp = surface(slide->slide_index); 1290 struct bitmap *bmp = surface(slide->slide_index);
1254 if (!bmp) { 1291 if (!bmp) {
1255 return; 1292 return;
1256 } 1293 }
1294 if (slide->angle > 255 || slide->angle < -255)
1295 return;
1257 pix_t *src = (pix_t *)bmp->data; 1296 pix_t *src = (pix_t *)bmp->data;
1258 1297
1259 const int sw = bmp->width; 1298 const int sw = bmp->width;
1260 const int sh = bmp->height; 1299 const int sh = bmp->height;
1300 const PFreal slide_left = -sw * PFREAL_HALF + PFREAL_HALF;
1261 1301
1262 const int h = LCD_HEIGHT; 1302 const int h = LCD_HEIGHT;
1263 const int w = LCD_WIDTH; 1303 const int w = LCD_WIDTH;
1264 1304
1265 1305
1266 int distance = (h + slide->distance) * 100 / zoom; 1306 PFreal zo = (CAM_DIST_R + PFREAL_ONE * slide->distance) * 100 / zoom -
1267 if (distance < 100 ) distance = 100; /* clamp distances */ 1307 CAM_DIST_R;
1268 PFreal dist = distance * PFREAL_ONE; 1308 PFreal cosr = fcos(slide->angle);
1269 PFreal sdx = fcos(slide->angle); 1309 PFreal sinr = fsin(slide->angle);
1270 PFreal sdy = fsin(slide->angle); 1310 PFreal xs = slide_left, xsnum, xsnumi, xsden, xsdeni;
1271 PFreal xs = slide->cx - sw * fdiv(sdx, dist) / 2; 1311 PFreal xp = fdiv(CAM_DIST * (slide->cx + fmul(xs, cosr)),
1272 1312 (CAM_DIST_R + zo + fmul(xs,sinr)));
1273 int xi = fmax(0, xs) >> PFREAL_SHIFT; 1313
1314 /* Since we're finding the screen position of the left edge of the slide,
1315 * we round up.
1316 */
1317 int xi = (fmax(DISPLAY_LEFT_R, xp) - DISPLAY_LEFT_R + PFREAL_ONE - 1)
1318 >> PFREAL_SHIFT;
1319 xp = DISPLAY_LEFT_R + xi * PFREAL_ONE;
1274 if (xi >= w) { 1320 if (xi >= w) {
1275 return; 1321 return;
1276 } 1322 }
1277 1323 xsnum = CAM_DIST * (slide->cx - xp) - fmuln(xp, zo, PFREAL_SHIFT - 2, 0);
1324 xsden = fmuln(xp, sinr, PFREAL_SHIFT - 2, 0) - CAM_DIST * cosr;
1325 xs = fdiv(xsnum, xsden);
1326
1327 xsnumi = -CAM_DIST_R - zo;
1328 xsdeni = sinr;
1278 int x; 1329 int x;
1279 for (x = fmax(xi, 0); x < w; x++) { 1330 int dy = PFREAL_ONE;
1280 PFreal hity = 0; 1331 for (x = xi; x < w; x++) {
1281 PFreal fk = rays[x]; 1332 int column = (xs - slide_left) / PFREAL_ONE;
1282 if (sdy) {
1283 fk = fk - fdiv(sdx, sdy);
1284 if (fk)
1285 hity = -fdiv(( rays[x] * distance
1286 - slide->cx
1287 + slide->cy * sdx / sdy), fk);
1288 }
1289
1290 dist = distance * PFREAL_ONE + hity;
1291 if (dist < 0)
1292 continue;
1293
1294 PFreal hitx = fmul(dist, rays[x]);
1295
1296 PFreal hitdist = fdiv(hitx - slide->cx, sdx);
1297
1298 const int column = (sw >> 1) + (hitdist >> PFREAL_SHIFT);
1299 if (column >= sw) 1333 if (column >= sw)
1300 break; 1334 break;
1301 if (column < 0) 1335 if (zo || slide->angle)
1302 continue; 1336 dy = (CAM_DIST_R + zo + fmul(xs, sinr)) / CAM_DIST;
1303
1304 int y1 = (LCD_HEIGHT / 2) - 1; 1337 int y1 = (LCD_HEIGHT / 2) - 1;
1305 int y2 = y1 + 1; 1338 int y2 = y1 + 1;
1306 pix_t *pixel1 = &buffer[y1 * BUFFER_WIDTH + x]; 1339 pix_t *pixel1 = &buffer[y1 * BUFFER_WIDTH + x];
1307 pix_t *pixel2 = &buffer[y2 * BUFFER_WIDTH + x]; 1340 pix_t *pixel2 = pixel1 + BUFFER_WIDTH;
1308 const int pixelstep = pixel2 - pixel1; 1341 const int pixelstep = BUFFER_WIDTH;
1309 1342
1310 int dy = dist / h;
1311 int p1 = (bmp->height - 1 - (DISPLAY_OFFS)) * PFREAL_ONE; 1343 int p1 = (bmp->height - 1 - (DISPLAY_OFFS)) * PFREAL_ONE;
1312 int p2 = p1 + dy; 1344 int p2 = p1 + dy;
1313 const pix_t *ptr = &src[column * bmp->height]; 1345 const pix_t *ptr = &src[column * bmp->height];
@@ -1340,6 +1372,7 @@ void render_slide(struct slide_data *slide, const int alpha)
1340 } 1372 }
1341 } 1373 }
1342 else 1374 else
1375 {
1343 while ((y1 >= 0) && (p1 >= 0)) 1376 while ((y1 >= 0) && (p1 >= 0))
1344 { 1377 {
1345 *pixel1 = fade_color(ptr[p1 >> PFREAL_SHIFT],alpha); 1378 *pixel1 = fade_color(ptr[p1 >> PFREAL_SHIFT],alpha);
@@ -1364,6 +1397,15 @@ void render_slide(struct slide_data *slide, const int alpha)
1364 y2++; 1397 y2++;
1365 pixel2 += pixelstep; 1398 pixel2 += pixelstep;
1366 } 1399 }
1400 }
1401 if (zo || slide->angle)
1402 {
1403 xsnum += xsnumi;
1404 xsden += xsdeni;
1405 xs = fdiv(xsnum, xsden);
1406 } else
1407 xs += PFREAL_ONE;
1408
1367 } 1409 }
1368 /* let the music play... */ 1410 /* let the music play... */
1369 rb->yield(); 1411 rb->yield();
@@ -1684,7 +1726,7 @@ int settings_menu(void)
1684 rb->set_int("Spacing between slides", "", 1, 1726 rb->set_int("Spacing between slides", "", 1,
1685 &slide_spacing, 1727 &slide_spacing,
1686 NULL, 1, 0, 100, NULL ); 1728 NULL, 1, 0, 100, NULL );
1687 recalc_table(); 1729 recalc_offsets();
1688 reset_slides(); 1730 reset_slides();
1689 break; 1731 break;
1690 1732
@@ -1692,27 +1734,27 @@ int settings_menu(void)
1692 rb->set_int("Center margin", "", 1, 1734 rb->set_int("Center margin", "", 1,
1693 &center_margin, 1735 &center_margin,
1694 NULL, 1, 0, 80, NULL ); 1736 NULL, 1, 0, 80, NULL );
1695 recalc_table(); 1737 recalc_offsets();
1696 reset_slides(); 1738 reset_slides();
1697 break; 1739 break;
1698 1740
1699 case 3: 1741 case 3:
1700 rb->set_int("Number of slides", "", 1, &num_slides, 1742 rb->set_int("Number of slides", "", 1, &num_slides,
1701 NULL, 1, 1, MAX_SLIDES_COUNT, NULL ); 1743 NULL, 1, 1, MAX_SLIDES_COUNT, NULL );
1702 recalc_table(); 1744 recalc_offsets();
1703 reset_slides(); 1745 reset_slides();
1704 break; 1746 break;
1705 1747
1706 case 4: 1748 case 4:
1707 rb->set_int("Zoom", "", 1, &zoom, 1749 rb->set_int("Zoom", "", 1, &zoom,
1708 NULL, 1, 10, 300, NULL ); 1750 NULL, 1, 10, 300, NULL );
1709 recalc_table(); 1751 recalc_offsets();
1710 reset_slides(); 1752 reset_slides();
1711 break; 1753 break;
1712 case 5: 1754 case 5:
1713 album_name_menu(); 1755 album_name_menu();
1714 reset_track_list(); 1756 reset_track_list();
1715 recalc_table(); 1757 recalc_offsets();
1716 reset_slides(); 1758 reset_slides();
1717 break; 1759 break;
1718 case 6: 1760 case 6:
@@ -2076,7 +2118,7 @@ int main(void)
2076 target = 0; 2118 target = 0;
2077 fade = 256; 2119 fade = 256;
2078 2120
2079 recalc_table(); 2121 recalc_offsets();
2080 reset_slides(); 2122 reset_slides();
2081 2123
2082 char fpstxt[10]; 2124 char fpstxt[10];