| 1223 | void GLSingleView::EffectKenBurns(void) |
| 1224 | { |
| 1225 | |
| 1226 | float single_image_pct = 0.75; |
| 1227 | float trans_pct = 1.0 - single_image_pct; |
| 1228 | float scale_max, x_loc, y_loc; |
| 1229 | float scale_factor = 0; |
| 1230 | |
| 1231 | //initialize effect |
| 1232 | if (!m_effect_kenBurns_initialized) |
| 1233 | { |
| 1234 | |
| 1235 | m_effect_kenBurns_initialized = !m_effect_kenBurns_initialized; |
| 1236 | m_effect_kenBurns_item = NULL; |
| 1237 | // Need to load images in the background to keep effect smooth |
| 1238 | m_effect_kenBurns_imageLoadThread = new KenBurnsImageLoader(this, m_itemList, m_texSize, m_screenSize); |
| 1239 | //Since total image time is longer/different than effect time, create image timers |
| 1240 | m_effect_kenBurns_image_time[m_texCur ? 0 : 1].restart(); |
| 1241 | // Pan image to a random location |
| 1242 | FindRandXY(m_effect_kenBurns_location_x[0], m_effect_kenBurns_location_y[0]); |
| 1243 | // Since first two images are preloaded, hardcode them to zoom in |
| 1244 | m_effect_kenBurns_projection[0] = 1; |
| 1245 | m_effect_kenBurns_projection[1] = 1; |
| 1246 | m_effect_kenBurns_image_timeout = m_effect_transition_timeout + |
| 1247 | (m_effect_transition_timeout * trans_pct); |
| 1248 | } |
| 1249 | |
| 1250 | if (m_effect_frame_time.elapsed() >= m_effect_transition_timeout) |
| 1251 | { |
| 1252 | // Effect timed out, move new image to old image but don't load new image yet... |
| 1253 | m_tex1First = !m_tex1First; |
| 1254 | m_texCur = (m_texCur) ? 0 : 1; |
| 1255 | m_effect_current_frame = 0; |
| 1256 | m_effect_frame_time.restart(); |
| 1257 | |
| 1258 | m_effect_kenBurns_image_ready = false; |
| 1259 | |
| 1260 | // Find next image to be loaded |
| 1261 | int oldpos = m_pos; |
| 1262 | |
| 1263 | while (true) |
| 1264 | { |
| 1265 | m_pos = m_slideshow_sequence->next(); |
| 1266 | m_effect_kenBurns_item = m_itemList.at(m_pos); |
| 1267 | if (m_effect_kenBurns_item) |
| 1268 | { |
| 1269 | // Skip movies |
| 1270 | if (QFile::exists(m_effect_kenBurns_item->GetPath()) && !GalleryUtil::IsMovie(m_effect_kenBurns_item->GetPath())) |
| 1271 | { |
| 1272 | break; |
| 1273 | } |
| 1274 | } |
| 1275 | if (m_pos == oldpos) |
| 1276 | { |
| 1277 | // No valid items!!! |
| 1278 | close(); |
| 1279 | } |
| 1280 | } |
| 1281 | m_effect_kenBurns_imageLoadThread->Initialize(m_pos); |
| 1282 | m_effect_kenBurns_imageLoadThread->start(); |
| 1283 | } |
| 1284 | |
| 1285 | float t[2], elapsed[2], s[2], effect_pct; |
| 1286 | elapsed[m_texCur] = m_effect_kenBurns_image_time[m_texCur].elapsed(); |
| 1287 | elapsed[m_texCur ? 0 : 1] = m_effect_kenBurns_image_time[m_texCur ? 0 : 1].elapsed(); |
| 1288 | //progress linearly |
| 1289 | t[m_texCur] = elapsed[m_texCur] / m_effect_kenBurns_image_timeout; |
| 1290 | t[m_texCur ? 0 : 1] = elapsed[m_texCur ? 0 : 1] / m_effect_kenBurns_image_timeout; |
| 1291 | //progress faster initially then slowing down- this is needed to ensure images zoom faster than they pan and |
| 1292 | //therefore stay completely on the screen |
| 1293 | s[m_texCur] = sqrt(elapsed[m_texCur]) / sqrt(m_effect_kenBurns_image_timeout); |
| 1294 | s[m_texCur ? 0 : 1] = sqrt(elapsed[m_texCur ? 0 : 1]) / sqrt(m_effect_kenBurns_image_timeout); |
| 1295 | |
| 1296 | effect_pct = m_effect_frame_time.elapsed() * m_effect_transition_timeout_inv; |
| 1297 | |
| 1298 | // Load new image if its ready |
| 1299 | if (effect_pct > single_image_pct && m_effect_kenBurns_image_ready) |
| 1300 | { |
| 1301 | if (!m_effect_kenBurns_new_image_started) |
| 1302 | { |
| 1303 | if (m_effect_kenBurns_item) //Do not create textures for first two images, since they are preloaded |
| 1304 | { |
| 1305 | m_texItem[!m_tex1First].SetItem(m_effect_kenBurns_item, m_effect_kenBurns_orig_image_size); |
| 1306 | m_texItem[!m_tex1First].ScaleTo(m_screenSize, m_scaleMax); |
| 1307 | m_texItem[!m_tex1First].Init(m_effect_kenBurns_image); |
| 1308 | UpdateLCD(m_effect_kenBurns_item); |
| 1309 | |
| 1310 | //choose the location and projection (zoom in or out) randomly |
| 1311 | FindRandXY(m_effect_kenBurns_location_x[m_texCur], m_effect_kenBurns_location_y[m_texCur]); |
| 1312 | m_effect_kenBurns_projection[m_texCur] = 1 + (int)((2.0f * rand() / (RAND_MAX + 1.0f))); |
| 1313 | |
| 1314 | } |
| 1315 | else //No item, must be 1 of the first two preloaded items |
| 1316 | { |
| 1317 | //start at random location and zoom out to face in center |
| 1318 | FindRandXY(m_effect_kenBurns_location_x[m_texCur], m_effect_kenBurns_location_y[m_texCur]); |
| 1319 | m_effect_kenBurns_projection[m_texCur] = 1; |
| 1320 | } |
| 1321 | |
| 1322 | m_effect_kenBurns_image_time[m_texCur].restart(); |
| 1323 | m_effect_kenBurns_new_image_started = true; |
| 1324 | } |
| 1325 | if (m_effect_kenBurns_projection[m_texCur] == 1) // Zoom in image |
| 1326 | { |
| 1327 | // Start in center and pan out |
| 1328 | x_loc = m_effect_kenBurns_location_x[m_texCur] * t[m_texCur]; |
| 1329 | y_loc = m_effect_kenBurns_location_y[m_texCur] * t[m_texCur]; |
| 1330 | scale_max = FindMaxScale(x_loc,y_loc); |
| 1331 | scale_factor = 1.0f + (scale_max * s[m_texCur]); |
| 1332 | } |
| 1333 | else // Zoom out image |
| 1334 | { |
| 1335 | // Start at random location and pan to center |
| 1336 | x_loc = m_effect_kenBurns_location_x[m_texCur] - m_effect_kenBurns_location_x[m_texCur] * t[m_texCur]; |
| 1337 | y_loc = m_effect_kenBurns_location_y[m_texCur] - m_effect_kenBurns_location_y[m_texCur] * t[m_texCur]; |
| 1338 | scale_max = FindMaxScale(x_loc,y_loc); |
| 1339 | scale_factor = 1.0f + scale_max - (scale_max * t[m_texCur]); |
| 1340 | } |
| 1341 | |
| 1342 | glMatrixMode(GL_MODELVIEW); |
| 1343 | glLoadIdentity(); |
| 1344 | glTranslatef(x_loc, y_loc, 0.0f); |
| 1345 | |
| 1346 | m_texItem[m_texCur].MakeQuad((effect_pct-single_image_pct)*4, scale_factor); |
| 1347 | } |
| 1348 | |
| 1349 | //Load old picture |
| 1350 | if (m_effect_kenBurns_projection[m_texCur ? 0 : 1] == 1)// Zoom in image |
| 1351 | { |
| 1352 | x_loc = m_effect_kenBurns_location_x[m_texCur ? 0 : 1] * t[m_texCur ? 0 : 1]; |
| 1353 | y_loc = m_effect_kenBurns_location_y[m_texCur ? 0 : 1] * t[m_texCur ? 0 : 1]; |
| 1354 | scale_max = FindMaxScale(x_loc,y_loc); |
| 1355 | scale_factor = 1.0f + (scale_max * s[m_texCur ? 0 : 1]); |
| 1356 | } |
| 1357 | else // Zoom out image |
| 1358 | { |
| 1359 | x_loc = m_effect_kenBurns_location_x[m_texCur ? 0 : 1] - |
| 1360 | m_effect_kenBurns_location_x[m_texCur ? 0 : 1] * t[m_texCur ? 0 : 1]; |
| 1361 | y_loc = m_effect_kenBurns_location_y[m_texCur ? 0 : 1] - |
| 1362 | m_effect_kenBurns_location_y[m_texCur ? 0 : 1] * t[m_texCur ? 0 : 1]; |
| 1363 | scale_max = FindMaxScale(x_loc,y_loc); |
| 1364 | scale_factor = 1.0f + scale_max - (scale_max * t[m_texCur ? 0 : 1]); |
| 1365 | } |
| 1366 | |
| 1367 | glMatrixMode(GL_MODELVIEW); |
| 1368 | glLoadIdentity(); |
| 1369 | glTranslatef(x_loc, y_loc, 0.0f); |
| 1370 | |
| 1371 | if (effect_pct<= single_image_pct) |
| 1372 | { |
| 1373 | m_effect_kenBurns_new_image_started=false; |
| 1374 | m_texItem[m_texCur ? 0 : 1].MakeQuad(1.0f, scale_factor); // |
| 1375 | } |
| 1376 | else // Fade out image |
| 1377 | { |
| 1378 | m_texItem[m_texCur ? 0 : 1].MakeQuad(1.0f - ((effect_pct-single_image_pct)*4), scale_factor); |
| 1379 | |
| 1380 | } |
| 1381 | |
| 1382 | m_effect_current_frame++; |
| 1383 | } |
| 1384 | |
| 1491 | |
| 1492 | void GLSingleView::LoadImage(QImage image, QSize origSize) |
| 1493 | { |
| 1494 | m_effect_kenBurns_image = image; |
| 1495 | m_effect_kenBurns_orig_image_size = origSize; |
| 1496 | } |
| 1497 | |
| 1498 | float GLSingleView::FindMaxScale(float x_loc, float y_loc) |
| 1499 | { |
| 1500 | // Zoom big enough to keep the entire image on screen when we pan |
| 1501 | if (abs(x_loc) > abs(y_loc)) |
| 1502 | return abs(x_loc) * 2; |
| 1503 | else |
| 1504 | return abs(y_loc) * 2; |
| 1505 | } |
| 1506 | |
| 1507 | void GLSingleView::FindRandXY(float &x_loc, float &y_loc) |
| 1508 | { |
| 1509 | x_loc = (0.5 * rand() / (RAND_MAX + 1.0f)) + 0.25; //Random number between .25 and .75 |
| 1510 | if ((int)(2.0 * rand() / (RAND_MAX + 1.0f)) == 0) |
| 1511 | x_loc = -1 * x_loc; |
| 1512 | y_loc = (0.5 * rand() / (RAND_MAX + 1.0f)) + 0.25; //Random number between .25 and .75 |
| 1513 | if ((int)(2.0 * rand() / (RAND_MAX + 1.0f)) == 0) |
| 1514 | y_loc = -1 * y_loc; |
| 1515 | } |
| 1516 | |
| 1517 | KenBurnsImageLoader::KenBurnsImageLoader(GLSingleView *singleView, ThumbList &itemList, QSize texSize, QSize screenSize) |
| 1518 | { |
| 1519 | m_singleView = singleView; |
| 1520 | m_itemList = itemList; |
| 1521 | m_texSize = texSize; |
| 1522 | m_screenSize = screenSize; |
| 1523 | } |
| 1524 | |
| 1525 | void KenBurnsImageLoader::Initialize(int pos) |
| 1526 | { |
| 1527 | m_pos = pos; |
| 1528 | } |
| 1529 | |
| 1530 | void KenBurnsImageLoader::run() |
| 1531 | { |
| 1532 | ThumbItem *item = m_itemList.at(m_pos); |
| 1533 | if (!item) |
| 1534 | { |
| 1535 | VERBOSE(VB_IMPORTANT, LOC_ERR + "No item at "<<m_pos); |
| 1536 | return; |
| 1537 | } |
| 1538 | QImage image(item->GetPath()); |
| 1539 | if (image.isNull()) |
| 1540 | return; |
| 1541 | |
| 1542 | m_singleView->LoadImage(QGLWidget::convertToGLFormat(image.scaled(m_texSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)), image.size()); |
| 1543 | m_singleView->Ready(); |
| 1544 | |
| 1545 | } |