| 1182 | void GLSingleView::EffectKenBurns(void) |
| 1183 | { |
| 1184 | |
| 1185 | float single_image_pct = 0.75; |
| 1186 | float trans_pct = 1.0 - single_image_pct; |
| 1187 | float scale_max, x_loc, y_loc; |
| 1188 | float scale_factor = 0; |
| 1189 | |
| 1190 | //initialize effect |
| 1191 | if (!m_effect_kenBurns_initialized) |
| 1192 | { |
| 1193 | |
| 1194 | m_effect_kenBurns_initialized = !m_effect_kenBurns_initialized; |
| 1195 | m_effect_kenBurns_item = NULL; |
| 1196 | // Need to load images in the background to keep effect smooth |
| 1197 | m_effect_kenBurns_imageLoadThread = new KenBurnsImageLoader(this, m_itemList, m_texSize, m_screenSize); |
| 1198 | //Since total image time is longer/different than effect time, create image timers |
| 1199 | m_effect_kenBurns_image_time[m_texCur ? 0 : 1].restart(); |
| 1200 | // Pan image to a random location |
| 1201 | FindRandXY(m_effect_kenBurns_location_x[0], m_effect_kenBurns_location_y[0]); |
| 1202 | // Since first two images are preloaded, hardcode them to zoom in |
| 1203 | m_effect_kenBurns_projection[0] = 1; |
| 1204 | m_effect_kenBurns_projection[1] = 1; |
| 1205 | m_effect_kenBurns_image_timeout = m_effect_transition_timeout + |
| 1206 | (m_effect_transition_timeout * trans_pct); |
| 1207 | m_effect_kenBurns_face_x = 0.0; |
| 1208 | m_effect_kenBurns_face_y = 0.0; |
| 1209 | } |
| 1210 | |
| 1211 | if (m_effect_frame_time.elapsed() >= m_effect_transition_timeout) |
| 1212 | { |
| 1213 | // Effect timed out, move new image to old image but don't load new image yet... |
| 1214 | m_tex1First = !m_tex1First; |
| 1215 | m_texCur = (m_texCur) ? 0 : 1; |
| 1216 | m_effect_current_frame = 0; |
| 1217 | m_effect_frame_time.restart(); |
| 1218 | |
| 1219 | m_effect_kenBurns_image_ready = false; |
| 1220 | m_effect_kenBurns_face_x = 0.0; |
| 1221 | m_effect_kenBurns_face_y = 0.0; |
| 1222 | |
| 1223 | // Find next image to be loaded |
| 1224 | int oldpos = m_pos; |
| 1225 | |
| 1226 | while (true) |
| 1227 | { |
| 1228 | m_pos = m_slideshow_sequence->next(); |
| 1229 | m_effect_kenBurns_item = m_itemList.at(m_pos); |
| 1230 | if (m_effect_kenBurns_item) |
| 1231 | { |
| 1232 | // Skip movies |
| 1233 | if (QFile::exists(m_effect_kenBurns_item->GetPath()) && !GalleryUtil::isMovie(m_effect_kenBurns_item->GetPath())) |
| 1234 | { |
| 1235 | break; |
| 1236 | } |
| 1237 | } |
| 1238 | if (m_pos == oldpos) |
| 1239 | { |
| 1240 | // No valid items!!! |
| 1241 | close(); |
| 1242 | } |
| 1243 | } |
| 1244 | m_effect_kenBurns_imageLoadThread->Initialize(m_pos); |
| 1245 | m_effect_kenBurns_imageLoadThread->start(); |
| 1246 | } |
| 1247 | |
| 1248 | float t[2], elapsed[2], s[2], effect_pct; |
| 1249 | elapsed[m_texCur] = m_effect_kenBurns_image_time[m_texCur].elapsed(); |
| 1250 | elapsed[m_texCur ? 0 : 1] = m_effect_kenBurns_image_time[m_texCur ? 0 : 1].elapsed(); |
| 1251 | //progress linearly |
| 1252 | t[m_texCur] = elapsed[m_texCur] / m_effect_kenBurns_image_timeout; |
| 1253 | t[m_texCur ? 0 : 1] = elapsed[m_texCur ? 0 : 1] / m_effect_kenBurns_image_timeout; |
| 1254 | //progress faster initially then slowing down- this is needed to ensure images zoom faster than they pan and |
| 1255 | //therefore stay completely on the screen |
| 1256 | s[m_texCur] = sqrt(elapsed[m_texCur]) / sqrt(m_effect_kenBurns_image_timeout); |
| 1257 | s[m_texCur ? 0 : 1] = sqrt(elapsed[m_texCur ? 0 : 1]) / sqrt(m_effect_kenBurns_image_timeout); |
| 1258 | |
| 1259 | effect_pct = m_effect_frame_time.elapsed() * m_effect_transition_timeout_inv; |
| 1260 | |
| 1261 | // Load new image if its ready |
| 1262 | if (effect_pct > single_image_pct && m_effect_kenBurns_image_ready) |
| 1263 | { |
| 1264 | if (!m_effect_kenBurns_new_image_started) |
| 1265 | { |
| 1266 | if (m_effect_kenBurns_item) //Do not create textures for first two images, since they are preloaded |
| 1267 | { |
| 1268 | m_texItem[!m_tex1First].SetItem(m_effect_kenBurns_item, m_effect_kenBurns_orig_image_size); |
| 1269 | m_texItem[!m_tex1First].ScaleTo(m_screenSize, m_scaleMax); |
| 1270 | m_texItem[!m_tex1First].Init(m_effect_kenBurns_image); |
| 1271 | UpdateLCD(m_effect_kenBurns_item); |
| 1272 | |
| 1273 | // If there is no face in this image |
| 1274 | if ((m_effect_kenBurns_face_x == 0.0) && (m_effect_kenBurns_face_x == 0.0)) |
| 1275 | { |
| 1276 | //choose the location and projection (zoom in or out) randomly |
| 1277 | FindRandXY(m_effect_kenBurns_location_x[m_texCur], m_effect_kenBurns_location_y[m_texCur]); |
| 1278 | m_effect_kenBurns_projection[m_texCur] = 1 + (int)((2.0f * rand() / (RAND_MAX + 1.0f))); |
| 1279 | } |
| 1280 | //else if the face is close to center |
| 1281 | else if ((m_effect_kenBurns_face_x < 0.25) && (m_effect_kenBurns_face_x > -0.25) && |
| 1282 | (m_effect_kenBurns_face_y < 0.25) && (m_effect_kenBurns_face_y > -0.25)) |
| 1283 | { |
| 1284 | //start at random location and zoom out to face in center |
| 1285 | FindRandXY(m_effect_kenBurns_location_x[m_texCur], m_effect_kenBurns_location_y[m_texCur]); |
| 1286 | m_effect_kenBurns_projection[m_texCur] = 0; |
| 1287 | } |
| 1288 | else |
| 1289 | { |
| 1290 | //start in center and zoom in to face random location |
| 1291 | m_effect_kenBurns_location_x[m_texCur] = m_effect_kenBurns_face_x; |
| 1292 | m_effect_kenBurns_location_y[m_texCur] = m_effect_kenBurns_face_y; |
| 1293 | m_effect_kenBurns_projection[m_texCur] = 1; |
| 1294 | } |
| 1295 | } |
| 1296 | else //No item, must be 1 of the first two preloaded items |
| 1297 | { |
| 1298 | //start at random location and zoom out to face in center |
| 1299 | FindRandXY(m_effect_kenBurns_location_x[m_texCur], m_effect_kenBurns_location_y[m_texCur]); |
| 1300 | m_effect_kenBurns_projection[m_texCur] = 1; |
| 1301 | } |
| 1302 | |
| 1303 | m_effect_kenBurns_image_time[m_texCur].restart(); |
| 1304 | m_effect_kenBurns_new_image_started = true; |
| 1305 | } |
| 1306 | if (m_effect_kenBurns_projection[m_texCur] == 1) // Zoom in image |
| 1307 | { |
| 1308 | // Start in center and pan out |
| 1309 | x_loc = m_effect_kenBurns_location_x[m_texCur] * t[m_texCur]; |
| 1310 | y_loc = m_effect_kenBurns_location_y[m_texCur] * t[m_texCur]; |
| 1311 | scale_max = FindMaxScale(x_loc,y_loc); |
| 1312 | scale_factor = 1.0f + (scale_max * s[m_texCur]); |
| 1313 | } |
| 1314 | else // Zoom out image |
| 1315 | { |
| 1316 | // Start at random location and pan to center |
| 1317 | x_loc = m_effect_kenBurns_location_x[m_texCur] - m_effect_kenBurns_location_x[m_texCur] * t[m_texCur]; |
| 1318 | y_loc = m_effect_kenBurns_location_y[m_texCur] - m_effect_kenBurns_location_y[m_texCur] * t[m_texCur]; |
| 1319 | scale_max = FindMaxScale(x_loc,y_loc); |
| 1320 | scale_factor = 1.0f + scale_max - (scale_max * t[m_texCur]); |
| 1321 | } |
| 1322 | |
| 1323 | glMatrixMode(GL_MODELVIEW); |
| 1324 | glLoadIdentity(); |
| 1325 | glTranslatef(x_loc, y_loc, 0.0f); |
| 1326 | |
| 1327 | m_texItem[m_texCur].MakeQuad((effect_pct-single_image_pct)*4, scale_factor); |
| 1328 | } |
| 1329 | |
| 1330 | //Load old picture |
| 1331 | if (m_effect_kenBurns_projection[m_texCur ? 0 : 1] == 1)// Zoom in image |
| 1332 | { |
| 1333 | x_loc = m_effect_kenBurns_location_x[m_texCur ? 0 : 1] * t[m_texCur ? 0 : 1]; |
| 1334 | y_loc = m_effect_kenBurns_location_y[m_texCur ? 0 : 1] * t[m_texCur ? 0 : 1]; |
| 1335 | scale_max = FindMaxScale(x_loc,y_loc); |
| 1336 | scale_factor = 1.0f + (scale_max * s[m_texCur ? 0 : 1]); |
| 1337 | } |
| 1338 | else // Zoom out image |
| 1339 | { |
| 1340 | x_loc = m_effect_kenBurns_location_x[m_texCur ? 0 : 1] - |
| 1341 | m_effect_kenBurns_location_x[m_texCur ? 0 : 1] * t[m_texCur ? 0 : 1]; |
| 1342 | y_loc = m_effect_kenBurns_location_y[m_texCur ? 0 : 1] - |
| 1343 | m_effect_kenBurns_location_y[m_texCur ? 0 : 1] * t[m_texCur ? 0 : 1]; |
| 1344 | scale_max = FindMaxScale(x_loc,y_loc); |
| 1345 | scale_factor = 1.0f + scale_max - (scale_max * t[m_texCur ? 0 : 1]); |
| 1346 | } |
| 1347 | |
| 1348 | glMatrixMode(GL_MODELVIEW); |
| 1349 | glLoadIdentity(); |
| 1350 | glTranslatef(x_loc, y_loc, 0.0f); |
| 1351 | |
| 1352 | if (effect_pct<= single_image_pct) |
| 1353 | { |
| 1354 | m_effect_kenBurns_new_image_started=false; |
| 1355 | m_texItem[m_texCur ? 0 : 1].MakeQuad(1.0f, scale_factor); // |
| 1356 | } |
| 1357 | else // Fade out image |
| 1358 | { |
| 1359 | m_texItem[m_texCur ? 0 : 1].MakeQuad(1.0f - ((effect_pct-single_image_pct)*4), scale_factor); |
| 1360 | |
| 1361 | } |
| 1362 | |
| 1363 | m_effect_current_frame++; |
| 1364 | } |
| 1365 | |
| 1469 | |
| 1470 | void GLSingleView::LoadImage(QImage image, QSize origSize, Face *face) |
| 1471 | { |
| 1472 | m_effect_kenBurns_image = image; |
| 1473 | m_effect_kenBurns_orig_image_size = origSize; |
| 1474 | if (face) |
| 1475 | { |
| 1476 | VERBOSE(VB_IMPORTANT, QString("Found face, set location x '%1' y '%2'").arg(face->getX()).arg(face->getY())); |
| 1477 | m_effect_kenBurns_face_x = face->getX(); |
| 1478 | m_effect_kenBurns_face_y = face->getY(); |
| 1479 | } |
| 1480 | } |
| 1481 | |
| 1482 | float GLSingleView::FindMaxScale(float x_loc, float y_loc) |
| 1483 | { |
| 1484 | // Zoom big enough to keep the entire image on screen when we pan |
| 1485 | if (abs(x_loc) > abs(y_loc)) |
| 1486 | return abs(x_loc) * 2; |
| 1487 | else |
| 1488 | return abs(y_loc) * 2; |
| 1489 | } |
| 1490 | |
| 1491 | void GLSingleView::FindRandXY(float &x_loc, float &y_loc) |
| 1492 | { |
| 1493 | x_loc = (0.5 * rand() / (RAND_MAX + 1.0f)) + 0.25; //Random number between .25 and .75 |
| 1494 | if ((int)(2.0 * rand() / (RAND_MAX + 1.0f)) == 0) |
| 1495 | x_loc = -1 * x_loc; |
| 1496 | y_loc = (0.5 * rand() / (RAND_MAX + 1.0f)) + 0.25; //Random number between .25 and .75 |
| 1497 | if ((int)(2.0 * rand() / (RAND_MAX + 1.0f)) == 0) |
| 1498 | y_loc = -1 * y_loc; |
| 1499 | } |
| 1500 | |
| 1501 | KenBurnsImageLoader::KenBurnsImageLoader(GLSingleView *singleView, ThumbList &itemList, QSize texSize, QSize screenSize) |
| 1502 | { |
| 1503 | m_singleView = singleView; |
| 1504 | m_itemList = itemList; |
| 1505 | m_texSize = texSize; |
| 1506 | m_screenSize = screenSize; |
| 1507 | } |
| 1508 | |
| 1509 | void KenBurnsImageLoader::Initialize(int pos) |
| 1510 | { |
| 1511 | m_pos = pos; |
| 1512 | } |
| 1513 | |
| 1514 | void KenBurnsImageLoader::run() |
| 1515 | { |
| 1516 | ThumbItem *item = m_itemList.at(m_pos); |
| 1517 | if (!item) |
| 1518 | { |
| 1519 | VERBOSE(VB_IMPORTANT, LOC_ERR + "No item at "<<m_pos); |
| 1520 | return; |
| 1521 | } |
| 1522 | QImage image(item->GetPath()); |
| 1523 | if (image.isNull()) |
| 1524 | return; |
| 1525 | |
| 1526 | #ifdef FDLIB_SUPPORT |
| 1527 | |
| 1528 | int i, n, x[256], y[256], size[256], w, h, threshold; |
| 1529 | float loc_x, loc_y; |
| 1530 | unsigned char *bgrdata, *graydata; |
| 1531 | |
| 1532 | w = image.width(); |
| 1533 | h = image.height(); |
| 1534 | bgrdata = image.bits(); |
| 1535 | |
| 1536 | graydata = new unsigned char[w*h]; |
| 1537 | for (i=0; i<w*h; i++) |
| 1538 | graydata[i] = (unsigned char) ((.11*bgrdata[4*i] + .59*bgrdata[4*i+1] + .3*bgrdata[4*i+2])); |
| 1539 | |
| 1540 | |
| 1541 | threshold = 0; |
| 1542 | fdlib_detectfaces(graydata, w, h, threshold); |
| 1543 | delete[] graydata; |
| 1544 | n = fdlib_getndetections(); |
| 1545 | if (n==0) |
| 1546 | { |
| 1547 | VERBOSE(VB_IMPORTANT, QString("%1 face found.").arg(n)); |
| 1548 | m_singleView->LoadImage(QGLWidget::convertToGLFormat(image.smoothScale(m_texSize)), image.size()); |
| 1549 | } |
| 1550 | else if (n==1) |
| 1551 | { |
| 1552 | VERBOSE(VB_IMPORTANT, QString("%1 faces found.").arg(n)); |
| 1553 | |
| 1554 | fdlib_getdetection(0, x, y, size); |
| 1555 | loc_x = (x[0]-(float)w/2)/((float)w/2)*(-1); |
| 1556 | loc_y = (y[0]-(float)h/2)/((float)h/2)*(-1); |
| 1557 | VERBOSE(VB_IMPORTANT, QString("x:%1 y:%2 size:%3").arg(x[0]).arg(y[0]).arg(size[0])); |
| 1558 | m_singleView->LoadImage(QGLWidget::convertToGLFormat(image.smoothScale(m_texSize)), image.size(), new Face(loc_x, loc_y)); |
| 1559 | } |
| 1560 | else |
| 1561 | { |
| 1562 | VERBOSE(VB_IMPORTANT, QString("%1 faces found.").arg(n)); |
| 1563 | QPtrList<Face> faces; |
| 1564 | int faceIndex; |
| 1565 | |
| 1566 | for (i=0; i<n; i++) |
| 1567 | { |
| 1568 | fdlib_getdetection(i, x+i, y+i, size+i); |
| 1569 | loc_x = (x[i]-(float)w/2)/((float)w/2)*(-1); |
| 1570 | loc_y = (y[i]-(float)h/2)/((float)h/2)*(-1); |
| 1571 | VERBOSE(VB_IMPORTANT, QString("x:%1 y:%2 size:%3").arg(x[i]).arg(y[i]).arg(size[i])); |
| 1572 | faces.append(new Face(loc_x, loc_y)); |
| 1573 | } |
| 1574 | if (faces.count() > 1) |
| 1575 | faceIndex = (int) ((faces.count()+1) * rand() / (RAND_MAX + 1.0f)); // |
| 1576 | else |
| 1577 | faceIndex = 0; |
| 1578 | |
| 1579 | m_singleView->LoadImage(QGLWidget::convertToGLFormat(image.smoothScale(m_texSize)), image.size(), faces.at(faceIndex)); |
| 1580 | } |
| 1581 | |
| 1582 | #else //END FDLIB_SUPPORT |
| 1583 | m_singleView->LoadImage(QGLWidget::convertToGLFormat(image.smoothScale(m_texSize)), image.size()); |
| 1584 | #endif |
| 1585 | m_singleView->Ready(); |
| 1586 | |
| 1587 | } |
| 1588 | |
| 1589 | Face::Face(float x, float y) |
| 1590 | { |
| 1591 | m_x = x; |
| 1592 | m_y = y; |
| 1593 | } |