| 1226 | void GLSingleView::EffectKenBurns(void) |
| 1227 | { |
| 1228 | |
| 1229 | float single_image_pct = 0.75; |
| 1230 | float trans_pct = 1.0 - single_image_pct; |
| 1231 | float scale_max, x_loc, y_loc; |
| 1232 | float scale_factor = 0; |
| 1233 | |
| 1234 | //initialize effect |
| 1235 | if (!m_effect_kenBurns_initialized) |
| 1236 | { |
| 1237 | |
| 1238 | m_effect_kenBurns_initialized = !m_effect_kenBurns_initialized; |
| 1239 | m_effect_kenBurns_item = NULL; |
| 1240 | // Need to load images in the background to keep effect smooth |
| 1241 | m_effect_kenBurns_imageLoadThread = new KenBurnsImageLoader(this, m_itemList, m_texSize, m_screenSize); |
| 1242 | //Since total image time is longer/different than effect time, create image timers |
| 1243 | m_effect_kenBurns_image_time[m_texCur ? 0 : 1].restart(); |
| 1244 | // Pan image to a random location |
| 1245 | FindRandXY(m_effect_kenBurns_location_x[0], m_effect_kenBurns_location_y[0]); |
| 1246 | // Since first two images are preloaded, hardcode them to zoom in |
| 1247 | m_effect_kenBurns_projection[0] = 1; |
| 1248 | m_effect_kenBurns_projection[1] = 1; |
| 1249 | m_effect_kenBurns_image_timeout = m_effect_transition_timeout + |
| 1250 | (m_effect_transition_timeout * trans_pct); |
| 1251 | m_effect_kenBurns_face_x = 0.0; |
| 1252 | m_effect_kenBurns_face_y = 0.0; |
| 1253 | } |
| 1254 | |
| 1255 | if (m_effect_frame_time.elapsed() >= m_effect_transition_timeout) |
| 1256 | { |
| 1257 | // Effect timed out, move new image to old image but don't load new image yet... |
| 1258 | m_tex1First = !m_tex1First; |
| 1259 | m_texCur = (m_texCur) ? 0 : 1; |
| 1260 | m_effect_current_frame = 0; |
| 1261 | m_effect_frame_time.restart(); |
| 1262 | |
| 1263 | m_effect_kenBurns_image_ready = false; |
| 1264 | m_effect_kenBurns_face_x = 0.0; |
| 1265 | m_effect_kenBurns_face_y = 0.0; |
| 1266 | |
| 1267 | // Find next image to be loaded |
| 1268 | int oldpos = m_pos; |
| 1269 | |
| 1270 | while (true) |
| 1271 | { |
| 1272 | m_pos = m_slideshow_sequence->next(); |
| 1273 | m_effect_kenBurns_item = m_itemList.at(m_pos); |
| 1274 | if (m_effect_kenBurns_item) |
| 1275 | { |
| 1276 | // Skip movies |
| 1277 | if (QFile::exists(m_effect_kenBurns_item->GetPath()) && !GalleryUtil::IsMovie(m_effect_kenBurns_item->GetPath())) |
| 1278 | { |
| 1279 | break; |
| 1280 | } |
| 1281 | } |
| 1282 | if (m_pos == oldpos) |
| 1283 | { |
| 1284 | // No valid items!!! |
| 1285 | close(); |
| 1286 | } |
| 1287 | } |
| 1288 | m_effect_kenBurns_imageLoadThread->Initialize(m_pos); |
| 1289 | m_effect_kenBurns_imageLoadThread->start(); |
| 1290 | } |
| 1291 | |
| 1292 | float t[2], elapsed[2], s[2], effect_pct; |
| 1293 | elapsed[m_texCur] = m_effect_kenBurns_image_time[m_texCur].elapsed(); |
| 1294 | elapsed[m_texCur ? 0 : 1] = m_effect_kenBurns_image_time[m_texCur ? 0 : 1].elapsed(); |
| 1295 | //progress linearly |
| 1296 | t[m_texCur] = elapsed[m_texCur] / m_effect_kenBurns_image_timeout; |
| 1297 | t[m_texCur ? 0 : 1] = elapsed[m_texCur ? 0 : 1] / m_effect_kenBurns_image_timeout; |
| 1298 | //progress faster initially then slowing down- this is needed to ensure images zoom faster than they pan and |
| 1299 | //therefore stay completely on the screen |
| 1300 | s[m_texCur] = sqrt(elapsed[m_texCur]) / sqrt(m_effect_kenBurns_image_timeout); |
| 1301 | s[m_texCur ? 0 : 1] = sqrt(elapsed[m_texCur ? 0 : 1]) / sqrt(m_effect_kenBurns_image_timeout); |
| 1302 | |
| 1303 | effect_pct = m_effect_frame_time.elapsed() * m_effect_transition_timeout_inv; |
| 1304 | |
| 1305 | // Load new image if its ready |
| 1306 | if (effect_pct > single_image_pct && m_effect_kenBurns_image_ready) |
| 1307 | { |
| 1308 | if (!m_effect_kenBurns_new_image_started) |
| 1309 | { |
| 1310 | if (m_effect_kenBurns_item) //Do not create textures for first two images, since they are preloaded |
| 1311 | { |
| 1312 | m_texItem[!m_tex1First].SetItem(m_effect_kenBurns_item, m_effect_kenBurns_orig_image_size); |
| 1313 | m_texItem[!m_tex1First].ScaleTo(m_screenSize, m_scaleMax); |
| 1314 | m_texItem[!m_tex1First].Init(m_effect_kenBurns_image); |
| 1315 | UpdateLCD(m_effect_kenBurns_item); |
| 1316 | |
| 1317 | // If there is no face in this image |
| 1318 | if ((m_effect_kenBurns_face_x == 0.0) && (m_effect_kenBurns_face_x == 0.0)) |
| 1319 | { |
| 1320 | //choose the location and projection (zoom in or out) randomly |
| 1321 | FindRandXY(m_effect_kenBurns_location_x[m_texCur], m_effect_kenBurns_location_y[m_texCur]); |
| 1322 | m_effect_kenBurns_projection[m_texCur] = 1 + (int)((2.0f * rand() / (RAND_MAX + 1.0f))); |
| 1323 | } |
| 1324 | //else if the face is close to center |
| 1325 | else if ((m_effect_kenBurns_face_x < 0.25) && (m_effect_kenBurns_face_x > -0.25) && |
| 1326 | (m_effect_kenBurns_face_y < 0.25) && (m_effect_kenBurns_face_y > -0.25)) |
| 1327 | { |
| 1328 | //start at random location and zoom out to face in center |
| 1329 | FindRandXY(m_effect_kenBurns_location_x[m_texCur], m_effect_kenBurns_location_y[m_texCur]); |
| 1330 | m_effect_kenBurns_projection[m_texCur] = 0; |
| 1331 | } |
| 1332 | else |
| 1333 | { |
| 1334 | //start in center and zoom in to face random location |
| 1335 | m_effect_kenBurns_location_x[m_texCur] = m_effect_kenBurns_face_x; |
| 1336 | m_effect_kenBurns_location_y[m_texCur] = m_effect_kenBurns_face_y; |
| 1337 | m_effect_kenBurns_projection[m_texCur] = 1; |
| 1338 | } |
| 1339 | } |
| 1340 | else //No item, must be 1 of the first two preloaded items |
| 1341 | { |
| 1342 | //start at random location and zoom out to face in center |
| 1343 | FindRandXY(m_effect_kenBurns_location_x[m_texCur], m_effect_kenBurns_location_y[m_texCur]); |
| 1344 | m_effect_kenBurns_projection[m_texCur] = 1; |
| 1345 | } |
| 1346 | |
| 1347 | m_effect_kenBurns_image_time[m_texCur].restart(); |
| 1348 | m_effect_kenBurns_new_image_started = true; |
| 1349 | } |
| 1350 | if (m_effect_kenBurns_projection[m_texCur] == 1) // Zoom in image |
| 1351 | { |
| 1352 | // Start in center and pan out |
| 1353 | x_loc = m_effect_kenBurns_location_x[m_texCur] * t[m_texCur]; |
| 1354 | y_loc = m_effect_kenBurns_location_y[m_texCur] * t[m_texCur]; |
| 1355 | scale_max = FindMaxScale(x_loc,y_loc); |
| 1356 | scale_factor = 1.0f + (scale_max * s[m_texCur]); |
| 1357 | } |
| 1358 | else // Zoom out image |
| 1359 | { |
| 1360 | // Start at random location and pan to center |
| 1361 | x_loc = m_effect_kenBurns_location_x[m_texCur] - m_effect_kenBurns_location_x[m_texCur] * t[m_texCur]; |
| 1362 | y_loc = m_effect_kenBurns_location_y[m_texCur] - m_effect_kenBurns_location_y[m_texCur] * t[m_texCur]; |
| 1363 | scale_max = FindMaxScale(x_loc,y_loc); |
| 1364 | scale_factor = 1.0f + scale_max - (scale_max * t[m_texCur]); |
| 1365 | } |
| 1366 | |
| 1367 | glMatrixMode(GL_MODELVIEW); |
| 1368 | glLoadIdentity(); |
| 1369 | glTranslatef(x_loc, y_loc, 0.0f); |
| 1370 | |
| 1371 | m_texItem[m_texCur].MakeQuad((effect_pct-single_image_pct)*4, scale_factor); |
| 1372 | } |
| 1373 | |
| 1374 | //Load old picture |
| 1375 | if (m_effect_kenBurns_projection[m_texCur ? 0 : 1] == 1)// Zoom in image |
| 1376 | { |
| 1377 | x_loc = m_effect_kenBurns_location_x[m_texCur ? 0 : 1] * t[m_texCur ? 0 : 1]; |
| 1378 | y_loc = m_effect_kenBurns_location_y[m_texCur ? 0 : 1] * t[m_texCur ? 0 : 1]; |
| 1379 | scale_max = FindMaxScale(x_loc,y_loc); |
| 1380 | scale_factor = 1.0f + (scale_max * s[m_texCur ? 0 : 1]); |
| 1381 | } |
| 1382 | else // Zoom out image |
| 1383 | { |
| 1384 | x_loc = m_effect_kenBurns_location_x[m_texCur ? 0 : 1] - |
| 1385 | m_effect_kenBurns_location_x[m_texCur ? 0 : 1] * t[m_texCur ? 0 : 1]; |
| 1386 | y_loc = m_effect_kenBurns_location_y[m_texCur ? 0 : 1] - |
| 1387 | m_effect_kenBurns_location_y[m_texCur ? 0 : 1] * t[m_texCur ? 0 : 1]; |
| 1388 | scale_max = FindMaxScale(x_loc,y_loc); |
| 1389 | scale_factor = 1.0f + scale_max - (scale_max * t[m_texCur ? 0 : 1]); |
| 1390 | } |
| 1391 | |
| 1392 | glMatrixMode(GL_MODELVIEW); |
| 1393 | glLoadIdentity(); |
| 1394 | glTranslatef(x_loc, y_loc, 0.0f); |
| 1395 | |
| 1396 | if (effect_pct<= single_image_pct) |
| 1397 | { |
| 1398 | m_effect_kenBurns_new_image_started=false; |
| 1399 | m_texItem[m_texCur ? 0 : 1].MakeQuad(1.0f, scale_factor); // |
| 1400 | } |
| 1401 | else // Fade out image |
| 1402 | { |
| 1403 | m_texItem[m_texCur ? 0 : 1].MakeQuad(1.0f - ((effect_pct-single_image_pct)*4), scale_factor); |
| 1404 | |
| 1405 | } |
| 1406 | |
| 1407 | m_effect_current_frame++; |
| 1408 | } |
| 1409 | |
| 1516 | |
| 1517 | void GLSingleView::LoadImage(QImage image, QSize origSize, Face *face) |
| 1518 | { |
| 1519 | m_effect_kenBurns_image = image; |
| 1520 | m_effect_kenBurns_orig_image_size = origSize; |
| 1521 | if (face) |
| 1522 | { |
| 1523 | VERBOSE(VB_IMPORTANT, QString("Found face, set location x '%1' y '%2'").arg(face->getX()).arg(face->getY())); |
| 1524 | m_effect_kenBurns_face_x = face->getX(); |
| 1525 | m_effect_kenBurns_face_y = face->getY(); |
| 1526 | } |
| 1527 | } |
| 1528 | |
| 1529 | float GLSingleView::FindMaxScale(float x_loc, float y_loc) |
| 1530 | { |
| 1531 | // Zoom big enough to keep the entire image on screen when we pan |
| 1532 | if (abs(x_loc) > abs(y_loc)) |
| 1533 | return abs(x_loc) * 2; |
| 1534 | else |
| 1535 | return abs(y_loc) * 2; |
| 1536 | } |
| 1537 | |
| 1538 | void GLSingleView::FindRandXY(float &x_loc, float &y_loc) |
| 1539 | { |
| 1540 | x_loc = (0.5 * rand() / (RAND_MAX + 1.0f)) + 0.25; //Random number between .25 and .75 |
| 1541 | if ((int)(2.0 * rand() / (RAND_MAX + 1.0f)) == 0) |
| 1542 | x_loc = -1 * x_loc; |
| 1543 | y_loc = (0.5 * rand() / (RAND_MAX + 1.0f)) + 0.25; //Random number between .25 and .75 |
| 1544 | if ((int)(2.0 * rand() / (RAND_MAX + 1.0f)) == 0) |
| 1545 | y_loc = -1 * y_loc; |
| 1546 | } |
| 1547 | |
| 1548 | KenBurnsImageLoader::KenBurnsImageLoader(GLSingleView *singleView, ThumbList &itemList, QSize texSize, QSize screenSize) |
| 1549 | { |
| 1550 | m_singleView = singleView; |
| 1551 | m_itemList = itemList; |
| 1552 | m_texSize = texSize; |
| 1553 | m_screenSize = screenSize; |
| 1554 | } |
| 1555 | |
| 1556 | void KenBurnsImageLoader::Initialize(int pos) |
| 1557 | { |
| 1558 | m_pos = pos; |
| 1559 | } |
| 1560 | |
| 1561 | void KenBurnsImageLoader::run() |
| 1562 | { |
| 1563 | ThumbItem *item = m_itemList.at(m_pos); |
| 1564 | if (!item) |
| 1565 | { |
| 1566 | VERBOSE(VB_IMPORTANT, LOC_ERR + "No item at "<<m_pos); |
| 1567 | return; |
| 1568 | } |
| 1569 | QImage image(item->GetPath()); |
| 1570 | if (image.isNull()) |
| 1571 | return; |
| 1572 | |
| 1573 | #ifdef FDLIB_SUPPORT |
| 1574 | |
| 1575 | int i, n, x[256], y[256], size[256], w, h, threshold; |
| 1576 | float loc_x, loc_y; |
| 1577 | unsigned char *bgrdata, *graydata; |
| 1578 | |
| 1579 | w = image.width(); |
| 1580 | h = image.height(); |
| 1581 | bgrdata = image.bits(); |
| 1582 | |
| 1583 | graydata = new unsigned char[w*h]; |
| 1584 | for (i=0; i<w*h; i++) |
| 1585 | graydata[i] = (unsigned char) ((.11*bgrdata[4*i] + .59*bgrdata[4*i+1] + .3*bgrdata[4*i+2])); |
| 1586 | |
| 1587 | |
| 1588 | threshold = 0; |
| 1589 | fdlib_detectfaces(graydata, w, h, threshold); |
| 1590 | delete[] graydata; |
| 1591 | n = fdlib_getndetections(); |
| 1592 | if (n==0) |
| 1593 | { |
| 1594 | VERBOSE(VB_IMPORTANT, QString("%1 face found.").arg(n)); |
| 1595 | m_singleView->LoadImage(QGLWidget::convertToGLFormat(image.scaled(m_texSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)), image.size()); |
| 1596 | } |
| 1597 | else if (n==1) |
| 1598 | { |
| 1599 | VERBOSE(VB_IMPORTANT, QString("%1 faces found.").arg(n)); |
| 1600 | |
| 1601 | fdlib_getdetection(0, x, y, size); |
| 1602 | loc_x = (x[0]-(float)w/2)/((float)w/2)*(-1); |
| 1603 | loc_y = (y[0]-(float)h/2)/((float)h/2)*(-1); |
| 1604 | VERBOSE(VB_IMPORTANT, QString("x:%1 y:%2 size:%3").arg(x[0]).arg(y[0]).arg(size[0])); |
| 1605 | m_singleView->LoadImage(QGLWidget::convertToGLFormat(image.scaled(m_texSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)), image.size(), new Face(loc_x, loc_y)); |
| 1606 | } |
| 1607 | else |
| 1608 | { |
| 1609 | VERBOSE(VB_IMPORTANT, QString("%1 faces found.").arg(n)); |
| 1610 | QList<Face*> faces; |
| 1611 | int faceIndex; |
| 1612 | |
| 1613 | for (i=0; i<n; i++) |
| 1614 | { |
| 1615 | fdlib_getdetection(i, x+i, y+i, size+i); |
| 1616 | loc_x = (x[i]-(float)w/2)/((float)w/2)*(-1); |
| 1617 | loc_y = (y[i]-(float)h/2)/((float)h/2)*(-1); |
| 1618 | VERBOSE(VB_IMPORTANT, QString("x:%1 y:%2 size:%3").arg(x[i]).arg(y[i]).arg(size[i])); |
| 1619 | faces.append(new Face(loc_x, loc_y)); |
| 1620 | } |
| 1621 | if (faces.count() > 1) |
| 1622 | faceIndex = (int) ((faces.count()+1) * rand() / (RAND_MAX + 1.0f)); // |
| 1623 | else |
| 1624 | faceIndex = 0; |
| 1625 | |
| 1626 | m_singleView->LoadImage(QGLWidget::convertToGLFormat(image.scaled(m_texSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)), image.size(), faces.at(faceIndex)); |
| 1627 | } |
| 1628 | |
| 1629 | #else //END FDLIB_SUPPORT |
| 1630 | m_singleView->LoadImage(QGLWidget::convertToGLFormat(image.scaled(m_texSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)), image.size()); |
| 1631 | #endif |
| 1632 | m_singleView->Ready(); |
| 1633 | |
| 1634 | } |
| 1635 | |
| 1636 | Face::Face(float x, float y) |
| 1637 | { |
| 1638 | m_x = x; |
| 1639 | m_y = y; |
| 1640 | } |