diff options
Diffstat (limited to 'apps/plugins/pictureflow.c')
-rw-r--r-- | apps/plugins/pictureflow.c | 154 |
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 | ||
237 | static pix_t *buffer; /* for now it always points to the lcd framebuffer */ | 240 | static pix_t *buffer; /* for now it always points to the lcd framebuffer */ |
238 | static PFreal rays[LCD_WIDTH]; | ||
239 | static uint16_t reflect_table[REFLECT_HEIGHT]; | 241 | static uint16_t reflect_table[REFLECT_HEIGHT]; |
240 | static struct slide_data center_slide; | 242 | static struct slide_data center_slide; |
241 | static struct slide_data left_slides[MAX_SLIDES_COUNT]; | 243 | static 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 | */ | ||
347 | static 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 | */ |
1197 | void recalc_table(void) | 1217 | void 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 | */ | ||
1251 | void render_slide(struct slide_data *slide, const int alpha) | 1288 | void 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 | ¢er_margin, | 1735 | ¢er_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]; |