#include "yacreader_flow_gl.h" #include #include #include /*** Animation Settings ***/ /*** Position Configuration ***/ int YACReaderFlowGL::updateInterval = 16; struct Preset defaultYACReaderFlowConfig = { 0.08f, // Animation_step sets the speed of the animation 1.5f, // Animation_speedup sets the acceleration of the animation 0.1f, // Animation_step_max sets the maximum speed of the animation 3.f, // Animation_Fade_out_dis sets the distance of view 1.5f, // pre_rotation sets the rotation increasion 3.f, // View_rotate_light_strenght sets the light strenght on rotation 0.01f, // View_rotate_add sets the speed of the rotation 0.02f, // View_rotate_sub sets the speed of reversing the rotation 20.f, // View_angle sets the maximum view angle 0.f, // CF_X the X Position of the Coverflow 0.f, // CF_Y the Y Position of the Coverflow -8.f, // CF_Z the Z Position of the Coverflow 15.f, // CF_RX the X Rotation of the Coverflow 0.f, // CF_RY the Y Rotation of the Coverflow 0.f, // CF_RZ the Z Rotation of the Coverflow -50.f, // Rotation sets the rotation of each cover 0.18f, // X_Distance sets the distance between the covers 1.f, // Center_Distance sets the distance between the centered and the non centered covers 0.1f, // Z_Distance sets the pushback amount 0.0f, // Y_Distance sets the elevation amount 30.f // zoom level }; struct Preset presetYACReaderFlowClassicConfig = { 0.08f, // Animation_step sets the speed of the animation 1.5f, // Animation_speedup sets the acceleration of the animation 0.1f, // Animation_step_max sets the maximum speed of the animation 2.f, // Animation_Fade_out_dis sets the distance of view 1.5f, // pre_rotation sets the rotation increasion 3.f, // View_rotate_light_strenght sets the light strenght on rotation 0.08f, // View_rotate_add sets the speed of the rotation 0.08f, // View_rotate_sub sets the speed of reversing the rotation 30.f, // View_angle sets the maximum view angle 0.f, // CF_X the X Position of the Coverflow -0.2f, // CF_Y the Y Position of the Coverflow -7.f, // CF_Z the Z Position of the Coverflow 0.f, // CF_RX the X Rotation of the Coverflow 0.f, // CF_RY the Y Rotation of the Coverflow 0.f, // CF_RZ the Z Rotation of the Coverflow -40.f, // Rotation sets the rotation of each cover 0.18f, // X_Distance sets the distance between the covers 1.f, // Center_Distance sets the distance between the centered and the non centered covers 0.1f, // Z_Distance sets the pushback amount 0.0f, // Y_Distance sets the elevation amount 22.f // zoom level }; struct Preset presetYACReaderFlowStripeConfig = { 0.08f, // Animation_step sets the speed of the animation 1.5f, // Animation_speedup sets the acceleration of the animation 0.1f, // Animation_step_max sets the maximum speed of the animation 6.f, // Animation_Fade_out_dis sets the distance of view 1.5f, // pre_rotation sets the rotation increasion 4.f, // View_rotate_light_strenght sets the light strenght on rotation 0.08f, // View_rotate_add sets the speed of the rotation 0.08f, // View_rotate_sub sets the speed of reversing the rotation 30.f, // View_angle sets the maximum view angle 0.f, // CF_X the X Position of the Coverflow -0.2f, // CF_Y the Y Position of the Coverflow -7.f, // CF_Z the Z Position of the Coverflow 0.f, // CF_RX the X Rotation of the Coverflow 0.f, // CF_RY the Y Rotation of the Coverflow 0.f, // CF_RZ the Z Rotation of the Coverflow 0.f, // Rotation sets the rotation of each cover 1.1f, // X_Distance sets the distance between the covers 0.2f, // Center_Distance sets the distance between the centered and the non centered covers 0.01f, // Z_Distance sets the pushback amount 0.0f, // Y_Distance sets the elevation amount 22.f // zoom level }; struct Preset presetYACReaderFlowOverlappedStripeConfig = { 0.08f, // Animation_step sets the speed of the animation 1.5f, // Animation_speedup sets the acceleration of the animation 0.1f, // Animation_step_max sets the maximum speed of the animation 2.f, // Animation_Fade_out_dis sets the distance of view 1.5f, // pre_rotation sets the rotation increasion 3.f, // View_rotate_light_strenght sets the light strenght on rotation 0.08f, // View_rotate_add sets the speed of the rotation 0.08f, // View_rotate_sub sets the speed of reversing the rotation 30.f, // View_angle sets the maximum view angle 0.f, // CF_X the X Position of the Coverflow -0.2f, // CF_Y the Y Position of the Coverflow -7.f, // CF_Z the Z Position of the Coverflow 0.f, // CF_RX the X Rotation of the Coverflow 0.f, // CF_RY the Y Rotation of the Coverflow 0.f, // CF_RZ the Z Rotation of the Coverflow 0.f, // Rotation sets the rotation of each cover 0.18f, // X_Distance sets the distance between the covers 1.f, // Center_Distance sets the distance between the centered and the non centered covers 0.1f, // Z_Distance sets the pushback amount 0.0f, // Y_Distance sets the elevation amount 22.f // zoom level }; struct Preset pressetYACReaderFlowUpConfig = { 0.08f, // Animation_step sets the speed of the animation 1.5f, // Animation_speedup sets the acceleration of the animation 0.1f, // Animation_step_max sets the maximum speed of the animation 2.5f, // Animation_Fade_out_dis sets the distance of view 1.5f, // pre_rotation sets the rotation increasion 3.f, // View_rotate_light_strenght sets the light strenght on rotation 0.08f, // View_rotate_add sets the speed of the rotation 0.08f, // View_rotate_sub sets the speed of reversing the rotation 5.f, // View_angle sets the maximum view angle 0.f, // CF_X the X Position of the Coverflow -0.2f, // CF_Y the Y Position of the Coverflow -7.f, // CF_Z the Z Position of the Coverflow 0.f, // CF_RX the X Rotation of the Coverflow 0.f, // CF_RY the Y Rotation of the Coverflow 0.f, // CF_RZ the Z Rotation of the Coverflow -50.f, // Rotation sets the rotation of each cover 0.18f, // X_Distance sets the distance between the covers 1.f, // Center_Distance sets the distance between the centered and the non centered covers 0.1f, // Z_Distance sets the pushback amount -0.1f, // Y_Distance sets the elevation amount 22.f // zoom level }; struct Preset pressetYACReaderFlowDownConfig = { 0.08f, // Animation_step sets the speed of the animation 1.5f, // Animation_speedup sets the acceleration of the animation 0.1f, // Animation_step_max sets the maximum speed of the animation 2.5f, // Animation_Fade_out_dis sets the distance of view 1.5f, // pre_rotation sets the rotation increasion 3.f, // View_rotate_light_strenght sets the light strenght on rotation 0.08f, // View_rotate_add sets the speed of the rotation 0.08f, // View_rotate_sub sets the speed of reversing the rotation 5.f, // View_angle sets the maximum view angle 0.f, // CF_X the X Position of the Coverflow -0.2f, // CF_Y the Y Position of the Coverflow -7.f, // CF_Z the Z Position of the Coverflow 0.f, // CF_RX the X Rotation of the Coverflow 0.f, // CF_RY the Y Rotation of the Coverflow 0.f, // CF_RZ the Z Rotation of the Coverflow -50.f, // Rotation sets the rotation of each cover 0.18f, // X_Distance sets the distance between the covers 1.f, // Center_Distance sets the distance between the centered and the non centered covers 0.1f, // Z_Distance sets the pushback amount 0.1f, // Y_Distance sets the elevation amount 22.f // zoom level }; /*Constructor*/ YACReaderFlowGL::YACReaderFlowGL(QWidget *parent, struct Preset p) : QOpenGLWidget(/*QOpenGLWidget migration QGLFormat(QGL::SampleBuffers),*/ parent), numObjects(0), lazyPopulateObjects(-1), hasBeenInitialized(false), bUseVSync(false), flowRightToLeft(false) { updateCount = 0; config = p; currentSelected = 0; centerPos.x = 0.f; centerPos.y = 0.f; centerPos.z = 1.f; centerPos.rot = 0.f; /*** Style ***/ shadingTop = 0.8f; shadingBottom = 0.02f; reflectionUp = 0.f; reflectionBottom = 0.6f; /*** System variables ***/ numObjects = 0; // CFImage Dummy; viewRotate = 0.f; viewRotateActive = 0; stepBackup = config.animationStep / config.animationSpeedUp; /*QTimer * timer = new QTimer(); connect(timer, SIGNAL(timeout()), this, SLOT(updateImageData())); timer->start(70); */ /*loader = new WidgetLoader(0,this); loader->flow = this; QThread * loaderThread = new QThread(parent); loader->moveToThread(loaderThread); loaderThread->start();*/ QSurfaceFormat f = format(); f.setSamples(4); f.setVersion(2, 1); f.setSwapInterval(0); setFormat(f); timerId = startTimer(updateInterval); } void YACReaderFlowGL::timerEvent(QTimerEvent *event) { if (timerId == event->timerId()) update(); // if(!worker->isRunning()) // worker->start(); } void YACReaderFlowGL::startAnimationTimer() { if (timerId == -1) timerId = startTimer(updateInterval); } void YACReaderFlowGL::stopAnimationTimer() { if (timerId != -1) { killTimer(timerId); timerId = -1; } } YACReaderFlowGL::~YACReaderFlowGL() { } QSize YACReaderFlowGL::minimumSizeHint() const { return QSize(320, 200); } /*QSize YACReaderFlowGL::sizeHint() const { return QSize(320, 200); }*/ void YACReaderFlowGL::initializeGL() { glShadeModel(GL_SMOOTH); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); defaultTexture = new QOpenGLTexture(QImage(":/images/defaultCover.png")); defaultTexture->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::LinearMipMapLinear); #ifdef YACREADER_LIBRARY markTexture = new QOpenGLTexture(QImage(":/images/readRibbon.png")); markTexture->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::LinearMipMapLinear); readingTexture = new QOpenGLTexture(QImage(":/images/readingRibbon.png")); readingTexture->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::LinearMipMapLinear); #endif if (lazyPopulateObjects != -1) populate(lazyPopulateObjects); hasBeenInitialized = true; } void YACReaderFlowGL::paintGL() { QPainter painter; painter.begin(this); painter.beginNativePainting(); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glEnable(GL_COLOR_MATERIAL); glEnable(GL_BLEND); glEnable(GL_MULTISAMPLE); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if (numObjects > 0) { updatePositions(); udpatePerspective(width(), height()); draw(); } glDisable(GL_MULTISAMPLE); glDisable(GL_BLEND); glDisable(GL_COLOR_MATERIAL); glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); painter.endNativePainting(); QFont font = painter.font(); font.setFamily("Arial"); font.setPointSize(fontSize); painter.setFont(font); painter.setPen(QColor(76, 76, 76)); painter.drawText(10, fontSize + 10, QString("%1/%2").arg(currentSelected + 1).arg(numObjects)); painter.end(); } void YACReaderFlowGL::resizeGL(int width, int height) { fontSize = (width + height) * 0.010; if (fontSize < 10) fontSize = 10; // int side = qMin(width, height); udpatePerspective(width, height); if (numObjects > 0) updatePositions(); } void YACReaderFlowGL::udpatePerspective(int width, int height) { float pixelRatio = devicePixelRatioF(); glViewport(0, 0, width * pixelRatio, height * pixelRatio); glMatrixMode(GL_PROJECTION); QMatrix4x4 perspectiveMatrix; perspectiveMatrix.setToIdentity(); perspectiveMatrix.perspective(20.0, GLdouble(width) / (float)height, 1.0, 200.0); glLoadMatrixf(perspectiveMatrix.constData()); glMatrixMode(GL_MODELVIEW); } //----------------------------------------------------------------------------- /*Private*/ void YACReaderFlowGL::calcPos(YACReader3DImage &image, int pos) { if (flowRightToLeft) { pos = pos * -1; } if (pos == 0) { image.current = centerPos; } else { if (pos > 0) { image.current.x = (config.centerDistance) + (config.xDistance * pos); image.current.y = config.yDistance * pos * -1; image.current.z = config.zDistance * pos * -1; image.current.rot = config.rotation; } else { image.current.x = (config.centerDistance) * -1 + (config.xDistance * pos); image.current.y = config.yDistance * pos; image.current.z = config.zDistance * pos; image.current.rot = config.rotation * -1; } } } void YACReaderFlowGL::calcVector(YACReader3DVector &vector, int pos) { calcPos(dummy, pos); vector.x = dummy.current.x; vector.y = dummy.current.y; vector.z = dummy.current.z; vector.rot = dummy.current.rot; } bool YACReaderFlowGL::animate(YACReader3DVector ¤tVector, YACReader3DVector &toVector) { float rotDiff = toVector.rot - currentVector.rot; float xDiff = toVector.x - currentVector.x; float yDiff = toVector.y - currentVector.y; float zDiff = toVector.z - currentVector.z; if (fabs(rotDiff) < 0.01 && fabs(xDiff) < 0.001 && fabs(yDiff) < 0.001 && fabs(zDiff) < 0.001) return true; // calculate and apply positions currentVector.x = currentVector.x + (xDiff)*config.animationStep; currentVector.y = currentVector.y + (yDiff)*config.animationStep; currentVector.z = currentVector.z + (zDiff)*config.animationStep; if (fabs(rotDiff) > 0.01) { currentVector.rot = currentVector.rot + (rotDiff) * (config.animationStep * config.preRotation); } else { viewRotateActive = 0; } return false; } void YACReaderFlowGL::drawCover(const YACReader3DImage &image) { float w = image.width; float h = image.height; // fadeout float opacity = 1 - 1 / (config.animationFadeOutDist + config.viewRotateLightStrenght * fabs(viewRotate)) * fabs(0 - image.current.x); glLoadIdentity(); glTranslatef(config.cfX, config.cfY, config.cfZ); glRotatef(config.cfRX, 1, 0, 0); glRotatef(viewRotate * config.viewAngle + config.cfRY, 0, 1, 0); glRotatef(config.cfRZ, 0, 0, 1); glTranslatef(image.current.x, image.current.y, image.current.z); glPushMatrix(); glRotatef(image.current.rot, 0, 1, 0); glEnable(GL_TEXTURE_2D); image.texture->bind(); // calculate shading float LShading = ((config.rotation != 0) ? ((image.current.rot < 0) ? 1 - 1 / config.rotation * image.current.rot : 1) : 1); float RShading = ((config.rotation != 0) ? ((image.current.rot > 0) ? 1 - 1 / (config.rotation * -1) * image.current.rot : 1) : 1); float LUP = shadingTop + (1 - shadingTop) * LShading; float LDOWN = shadingBottom + (1 - shadingBottom) * LShading; float RUP = shadingTop + (1 - shadingTop) * RShading; float RDOWN = shadingBottom + (1 - shadingBottom) * RShading; ; // DrawCover glBegin(GL_QUADS); // esquina inferior izquierda glColor4f(LDOWN * opacity, LDOWN * opacity, LDOWN * opacity, 1); glTexCoord2f(0.0f, 1.0f); glVertex3f(w / 2.f * -1.f, -0.5f, 0.f); // esquina inferior derecha glColor4f(RDOWN * opacity, RDOWN * opacity, RDOWN * opacity, 1); glTexCoord2f(1.0f, 1.0f); glVertex3f(w / 2.f, -0.5f, 0.f); // esquina superior derecha glColor4f(RUP * opacity, RUP * opacity, RUP * opacity, 1); glTexCoord2f(1.0f, 0.0f); glVertex3f(w / 2.f, -0.5f + h, 0.f); // esquina superior izquierda glColor4f(LUP * opacity, LUP * opacity, LUP * opacity, 1); glTexCoord2f(0.0f, 0.0f); glVertex3f(w / 2.f * -1.f, -0.5f + h, 0.f); glEnd(); // Draw reflection glBegin(GL_QUADS); // esquina inferior izquierda glColor4f(LUP * opacity * reflectionUp / 2, LUP * opacity * reflectionUp / 2, LUP * opacity * reflectionUp / 2, 1); glTexCoord2f(0.0f, 0.0f); glVertex3f(w / 2.f * -1.f, -0.5f - h, 0.f); // esquina inferior derecha glColor4f(RUP * opacity * reflectionUp / 2, RUP * opacity * reflectionUp / 2, RUP * opacity * reflectionUp / 2, 1); glTexCoord2f(1.0f, 0.0f); glVertex3f(w / 2.f, -0.5f - h, 0.f); // esquina superior derecha glColor4f(RDOWN * opacity / 3, RDOWN * opacity / 3, RDOWN * opacity / 3, 1); glTexCoord2f(1.0f, 1.0f); glVertex3f(w / 2.f, -0.5f, 0.f); // esquina superior izquierda glColor4f(LDOWN * opacity / 3, LDOWN * opacity / 3, LDOWN * opacity / 3, 1); glTexCoord2f(0.0f, 1.0f); glVertex3f(w / 2.f * -1.f, -0.5f, 0.f); glEnd(); glDisable(GL_TEXTURE_2D); if (showMarks && loaded[image.index] && marks[image.index] != Unread) { glEnable(GL_TEXTURE_2D); if (marks[image.index] == Read) markTexture->bind(); else readingTexture->bind(); glBegin(GL_QUADS); // esquina inferior izquierda glColor4f(RUP * opacity, RUP * opacity, RUP * opacity, 1); glTexCoord2f(0.0f, 1.0f); glVertex3f(w / 2.f - 0.2, -0.688f + h, 0.001f); // esquina inferior derecha glColor4f(RUP * opacity, RUP * opacity, RUP * opacity, 1); glTexCoord2f(1.0f, 1.0f); glVertex3f(w / 2.f - 0.05, -0.688f + h, 0.001f); // esquina superior derecha glColor4f(RUP * opacity, RUP * opacity, RUP * opacity, 1); glTexCoord2f(1.0f, 0.0f); glVertex3f(w / 2.f - 0.05, -0.488f + h, 0.001f); // esquina superior izquierda glColor4f(RUP * opacity, RUP * opacity, RUP * opacity, 1); glTexCoord2f(0.0f, 0.0f); glVertex3f(w / 2.f - 0.2, -0.488f + h, 0.001f); glEnd(); glDisable(GL_TEXTURE_2D); } glPopMatrix(); } /*Public*/ void YACReaderFlowGL::cleanupAnimation() { config.animationStep = stepBackup; viewRotateActive = 0; } void YACReaderFlowGL::draw() { int CS = currentSelected; int count; // Draw right Covers for (count = numObjects - 1; count > -1; count--) { if (count > CS) { drawCover(images[count]); } } // Draw left Covers for (count = 0; count < numObjects - 1; count++) { if (count < CS) { drawCover(images[count]); } } // Draw Center Cover drawCover(images[CS]); } void YACReaderFlowGL::showPrevious() { startAnimationTimer(); if (currentSelected > 0) { currentSelected--; emit centerIndexChanged(currentSelected); config.animationStep *= config.animationSpeedUp; if (config.animationStep > config.animationStepMax) { config.animationStep = config.animationStepMax; } if (viewRotateActive && viewRotate > -1) { viewRotate -= config.viewRotateAdd; } viewRotateActive = 1; } } void YACReaderFlowGL::showNext() { startAnimationTimer(); if (currentSelected < numObjects - 1) { currentSelected++; emit centerIndexChanged(currentSelected); config.animationStep *= config.animationSpeedUp; if (config.animationStep > config.animationStepMax) { config.animationStep = config.animationStepMax; } if (viewRotateActive && viewRotate < 1) { viewRotate += config.viewRotateAdd; } viewRotateActive = 1; } } void YACReaderFlowGL::setCurrentIndex(int pos) { if (!(pos >= 0 && pos < images.length() && images.length() > 0)) return; if (pos >= images.length() && images.length() > 0) pos = images.length() - 1; startAnimationTimer(); currentSelected = pos; config.animationStep *= config.animationSpeedUp; if (config.animationStep > config.animationStepMax) { config.animationStep = config.animationStepMax; } if (viewRotateActive && viewRotate < 1) { viewRotate += config.viewRotateAdd; } viewRotateActive = 1; } void YACReaderFlowGL::updatePositions() { int count; bool stopAnimation = true; for (count = numObjects - 1; count > -1; count--) { calcVector(images[count].animEnd, count - currentSelected); if (!animate(images[count].current, images[count].animEnd)) stopAnimation = false; } // slowly reset view angle if (!viewRotateActive) { viewRotate += (0 - viewRotate) * config.viewRotateSub; } if (fabs(images[currentSelected].current.x - images[currentSelected].animEnd.x) < 1) // viewRotate < 0.2) { cleanupAnimation(); if (updateCount >= 0) // TODO parametrizar { updateCount = 0; updateImageData(); } else updateCount++; } else updateCount++; if (stopAnimation) stopAnimationTimer(); } void YACReaderFlowGL::insert(char *name, QOpenGLTexture *texture, float x, float y, int item) { startAnimationTimer(); Q_UNUSED(name) // set a new entry if (item == -1) { images.push_back(YACReader3DImage()); item = numObjects; numObjects++; calcVector(images[item].current, item); images[item].current.z = images[item].current.z - 1; } images[item].texture = texture; images[item].width = x; images[item].height = y; images[item].index = item; // strcpy(cfImages[item].name,name); } void YACReaderFlowGL::remove(int item) { if (item < 0 || item >= images.size()) return; startAnimationTimer(); loaded.remove(item); marks.remove(item); // reposition current selection if (item <= currentSelected && currentSelected != 0) { currentSelected--; } QOpenGLTexture *texture = images[item].texture; int count = item; while (count <= numObjects - 1) { images[count].index--; count++; } images.removeAt(item); if (texture != defaultTexture) delete (texture); numObjects--; } void YACReaderFlowGL::add(int item) { float x = 1; float y = 1 * (700.f / 480.0f); QString s = "cover"; images.insert(item, YACReader3DImage()); loaded.insert(item, false); marks.insert(item, Unread); numObjects++; for (int i = item + 1; i < numObjects; i++) { images[i].index++; } insert(s.toLocal8Bit().data(), defaultTexture, x, y, item); } /*Info*/ YACReader3DImage YACReaderFlowGL::getCurrentSelected() { return images[currentSelected]; } void YACReaderFlowGL::replace(char *name, QOpenGLTexture *texture, float x, float y, int item) { startAnimationTimer(); Q_UNUSED(name) if (images[item].index == item) { images[item].texture = texture; images[item].width = x; images[item].height = y; loaded[item] = true; } else loaded[item] = false; } void YACReaderFlowGL::populate(int n) { emit centerIndexChanged(0); float x = 1; float y = 1 * (700.f / 480.0f); int i; for (i = 0; i < n; i++) { QString s = "cover"; insert(s.toLocal8Bit().data(), defaultTexture, x, y); } loaded = QVector(n, false); } void YACReaderFlowGL::reset() { makeCurrent(); startAnimationTimer(); currentSelected = 0; loaded.clear(); for (int i = 0; i < numObjects; i++) { if (images[i].texture != defaultTexture) delete (images[i].texture); } numObjects = 0; images.clear(); if (!hasBeenInitialized) lazyPopulateObjects = -1; doneCurrent(); } void YACReaderFlowGL::reload() { startAnimationTimer(); int n = numObjects; reset(); populate(n); } // slots void YACReaderFlowGL::setCF_RX(int value) { startAnimationTimer(); config.cfRX = value; } void YACReaderFlowGL::setCF_RY(int value) { startAnimationTimer(); config.cfRY = value; } void YACReaderFlowGL::setCF_RZ(int value) { startAnimationTimer(); config.cfRZ = value; } void YACReaderFlowGL::setZoom(int zoom) { startAnimationTimer(); int width = this->width(); int height = this->height(); glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); QMatrix4x4 zoomMatrix; zoomMatrix.setToIdentity(); zoomMatrix.perspective(zoom, (float)width / (float)height, 1.0, 200.0); glLoadMatrixf(zoomMatrix.constData()); glMatrixMode(GL_MODELVIEW); } void YACReaderFlowGL::setRotation(int angle) { startAnimationTimer(); config.rotation = -angle; } // sets the distance between the covers void YACReaderFlowGL::setX_Distance(int distance) { startAnimationTimer(); config.xDistance = distance / 100.0; } // sets the distance between the centered and the non centered covers void YACReaderFlowGL::setCenter_Distance(int distance) { startAnimationTimer(); config.centerDistance = distance / 100.0; } // sets the pushback amount void YACReaderFlowGL::setZ_Distance(int distance) { startAnimationTimer(); config.zDistance = distance / 100.0; } void YACReaderFlowGL::setCF_Y(int value) { startAnimationTimer(); config.cfY = value / 100.0; } void YACReaderFlowGL::setCF_Z(int value) { startAnimationTimer(); config.cfZ = value; } void YACReaderFlowGL::setY_Distance(int value) { startAnimationTimer(); config.yDistance = value / 100.0; } void YACReaderFlowGL::setFadeOutDist(int value) { startAnimationTimer(); config.animationFadeOutDist = value; } void YACReaderFlowGL::setLightStrenght(int value) { startAnimationTimer(); config.viewRotateLightStrenght = value; } void YACReaderFlowGL::setMaxAngle(int value) { startAnimationTimer(); config.viewAngle = value; } void YACReaderFlowGL::setPreset(const Preset &p) { startAnimationTimer(); config = p; } void YACReaderFlowGL::setPerformance(Performance performance) { if (this->performance != performance) { startAnimationTimer(); this->performance = performance; reload(); } } void YACReaderFlowGL::useVSync(bool b) { if (bUseVSync != b) { bUseVSync = b; if (b) { QSurfaceFormat f = format(); f.setVersion(2, 1); f.setSwapInterval(1); setFormat(f); } else { QSurfaceFormat f = format(); f.setVersion(2, 1); f.setSwapInterval(0); setFormat(f); } reset(); } } void YACReaderFlowGL::setShowMarks(bool value) { startAnimationTimer(); showMarks = value; } void YACReaderFlowGL::setMarks(QVector marks) { startAnimationTimer(); this->marks = marks; } void YACReaderFlowGL::setMarkImage(QImage &image) { Q_UNUSED(image); // qué pasa la primera vez?? // deleteTexture(markTexture); // markTexture = bindTexture(image,GL_TEXTURE_2D,GL_RGBA,QGLContext::LinearFilteringBindOption | QGLContext::MipmapBindOption); } void YACReaderFlowGL::markSlide(int index, YACReader::YACReaderComicReadStatus status) { startAnimationTimer(); marks[index] = status; } void YACReaderFlowGL::unmarkSlide(int index) { startAnimationTimer(); marks[index] = YACReader::Unread; } void YACReaderFlowGL::setSlideSize(QSize size) { Q_UNUSED(size); // TODO calcular el tamaño del widget } void YACReaderFlowGL::clear() { reset(); } void YACReaderFlowGL::setCenterIndex(unsigned int index) { setCurrentIndex(index); } void YACReaderFlowGL::showSlide(int index) { setCurrentIndex(index); } int YACReaderFlowGL::centerIndex() { return currentSelected; } void YACReaderFlowGL::updateMarks() { // do nothing } /*void YACReaderFlowGL::setFlowType(FlowType flowType) { //TODO esperar a que se reimplemente flowtype }*/ void YACReaderFlowGL::render() { // do nothing } void YACReaderFlowGL::setFlowRightToLeft(bool b) { flowRightToLeft = b; } // EVENTOS void YACReaderFlowGL::wheelEvent(QWheelEvent *event) { Movement m = getMovement(event); switch (m) { case None: return; case Forward: showNext(); break; case Backward: showPrevious(); break; default: break; } } void YACReaderFlowGL::keyPressEvent(QKeyEvent *event) { if ((event->key() == Qt::Key_Left && !flowRightToLeft) || (event->key() == Qt::Key_Right && flowRightToLeft)) { if (event->modifiers() == Qt::ControlModifier) setCurrentIndex((currentSelected - 10 < 0) ? 0 : currentSelected - 10); else showPrevious(); event->accept(); return; } if ((event->key() == Qt::Key_Right && !flowRightToLeft) || (event->key() == Qt::Key_Left && flowRightToLeft)) { if (event->modifiers() == Qt::ControlModifier) setCurrentIndex((currentSelected + 10 >= numObjects) ? numObjects - 1 : currentSelected + 10); else showNext(); event->accept(); return; } if (event->key() == Qt::Key_Up) { // emit selected(centerIndex()); return; } event->ignore(); } void YACReaderFlowGL::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { QVector3D intersection = getPlaneIntersection(event->x(), event->y(), images[currentSelected]); if ((intersection.x() > 0.5 && !flowRightToLeft) || (intersection.x() < -0.5 && flowRightToLeft)) { showNext(); } else if ((intersection.x() < -0.5 && !flowRightToLeft) || (intersection.x() > 0.5 && flowRightToLeft)) { showPrevious(); } } else { QOpenGLWidget::mousePressEvent(event); } doneCurrent(); } void YACReaderFlowGL::mouseDoubleClickEvent(QMouseEvent *event) { QVector3D intersection = getPlaneIntersection(event->x(), event->y(), images[currentSelected]); if (intersection.x() < 0.5 && intersection.x() > -0.5) { emit selected(centerIndex()); event->accept(); } } QVector3D YACReaderFlowGL::getPlaneIntersection(int x, int y, YACReader3DImage plane) { // get viewport and matrices // TODO: these should be cached!!! GLint viewport[4]; QMatrix4x4 m_modelview; QMatrix4x4 m_projection; makeCurrent(); glGetIntegerv(GL_VIEWPORT, viewport); glGetFloatv(GL_MODELVIEW_MATRIX, m_modelview.data()); glGetFloatv(GL_PROJECTION_MATRIX, m_projection.data()); doneCurrent(); // create the picking ray QVector3D ray_origin(x * devicePixelRatioF(), y * devicePixelRatioF(), 0); QVector3D ray_end(x * devicePixelRatioF(), y * devicePixelRatioF(), 1.0); // TODO: These should be cached in the class ray_origin = ray_origin.unproject(m_modelview, m_projection, QRect(viewport[0], viewport[1], viewport[2], viewport[3])); ray_end = ray_end.unproject(m_modelview, m_projection, QRect(viewport[0], viewport[1], viewport[2], viewport[3])); QVector3D ray_vector = ray_end - ray_origin; // calculate the plane vectors QVector3D plane_origin((plane.width / 2) * -1, -0.5, 0); QVector3D plane_vektor_1 = QVector3D(plane.width / 2, -0.5, 0) - plane_origin; QVector3D plane_vektor_2 = QVector3D((plane.width / 2) * -1, -0.5 * plane.height, 0) - plane_origin; // get the intersection using Cramer's rule. We only x for the line, not the plane double intersection_LES_determinant = ((plane_vektor_1.x() * plane_vektor_2.y() * (-1) * ray_vector.z()) + (plane_vektor_2.x() * (-1) * ray_vector.y() * plane_vektor_1.z()) + ((-1) * ray_vector.x() * plane_vektor_1.y() * plane_vektor_2.z()) - ((-1) * ray_vector.x() * plane_vektor_2.y() * plane_vektor_1.z()) - (plane_vektor_1.x() * (-1) * ray_vector.y() * plane_vektor_2.z()) - (plane_vektor_2.x() * plane_vektor_1.y() * (-1) * ray_vector.z())); QVector3D det = ray_origin - plane_origin; double intersection_ray_determinant = ((plane_vektor_1.x() * plane_vektor_2.y() * det.z()) + (plane_vektor_2.x() * det.y() * plane_vektor_1.z()) + (det.x() * plane_vektor_1.y() * plane_vektor_2.z()) - (det.x() * plane_vektor_2.y() * plane_vektor_1.z()) - (plane_vektor_1.x() * det.y() * plane_vektor_2.z()) - (plane_vektor_2.x() * plane_vektor_1.y() * det.z())); // return the intersection point return ray_origin + ray_vector * (intersection_ray_determinant / intersection_LES_determinant); } YACReaderComicFlowGL::YACReaderComicFlowGL(QWidget *parent, struct Preset p) : YACReaderFlowGL(parent, p) { worker = new ImageLoaderGL(this); worker->flow = this; } void YACReaderComicFlowGL::setImagePaths(QStringList paths) { worker->reset(); reset(); numObjects = 0; if (lazyPopulateObjects != -1 || hasBeenInitialized) YACReaderFlowGL::populate(paths.size()); lazyPopulateObjects = paths.size(); this->paths = paths; // numObjects = paths.size(); } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void YACReaderComicFlowGL::updateImageData() { // can't do anything, wait for the next possibility if (worker->busy()) return; // set image of last one int idx = worker->index(); if (idx >= 0 && !worker->result().isNull()) { if (!loaded[idx]) { float x = 1; QImage img = worker->result(); QOpenGLTexture *texture = new QOpenGLTexture(img); if (performance == high || performance == ultraHigh) { texture->setAutoMipMapGenerationEnabled(true); texture->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::LinearMipMapLinear); } else { texture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear); } float y = 1 * (float(img.height()) / img.width()); QString s = "cover"; replace(s.toLocal8Bit().data(), texture, x, y, idx); } } // try to load only few images on the left and right side // i.e. all visible ones plus some extra int count = 8; switch (performance) { case low: count = 8; break; case medium: count = 10; break; case high: count = 12; break; case ultraHigh: count = 16; break; } int *indexes = new int[2 * count + 1]; int center = currentSelected; indexes[0] = center; for (int j = 0; j < count; j++) { indexes[j * 2 + 1] = center + j + 1; indexes[j * 2 + 2] = center - j - 1; } for (int c = 0; c < 2 * count + 1; c++) { int i = indexes[c]; if ((i >= 0) && (i < numObjects)) if (!loaded[i]) // slide(i).isNull()) { // loader->loadTexture(i); // loaded[i]=true; // schedule thumbnail generation if (paths.size() > 0) { QString fname = paths.at(i); // loaded[i]=true; worker->generate(i, fname); } delete[] indexes; return; } } delete[] indexes; } void YACReaderComicFlowGL::remove(int item) { worker->lock(); worker->reset(); YACReaderFlowGL::remove(item); if (item >= 0 && item < paths.size()) { paths.removeAt(item); } worker->unlock(); } void YACReaderComicFlowGL::add(const QString &path, int index) { worker->lock(); worker->reset(); paths.insert(index, path); YACReaderFlowGL::add(index); worker->unlock(); } void YACReaderComicFlowGL::resortCovers(QList newOrder) { worker->lock(); worker->reset(); // is this necesary? startAnimationTimer(); QList pathsNew; QVector loadedNew; QVector marksNew; QVector imagesNew; int index = 0; foreach (int i, newOrder) { if (i < 0 || i >= images.size()) { continue; } pathsNew << paths.at(i); loadedNew << loaded.at(i); marksNew << marks.at(i); imagesNew << images.at(i); imagesNew.last().index = index++; } paths = pathsNew; loaded = loadedNew; marks = marksNew; images = imagesNew; worker->unlock(); } YACReaderPageFlowGL::YACReaderPageFlowGL(QWidget *parent, struct Preset p) : YACReaderFlowGL(parent, p) { worker = new ImageLoaderByteArrayGL(this); worker->flow = this; } YACReaderPageFlowGL::~YACReaderPageFlowGL() { this->killTimer(timerId); rawImages.clear(); makeCurrent(); for (auto &image : images) { if (image.texture != defaultTexture) { if (image.texture->isCreated()) { image.texture->destroy(); } delete image.texture; } } if (defaultTexture != nullptr) { if (defaultTexture->isCreated()) { defaultTexture->destroy(); } delete defaultTexture; } doneCurrent(); } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void YACReaderPageFlowGL::updateImageData() { // can't do anything, wait for the next possibility if (worker->busy()) return; // set image of last one int idx = worker->index(); if (idx >= 0 && !worker->result().isNull()) { if (!loaded[idx]) { float x = 1; QImage img = worker->result(); QOpenGLTexture *texture = new QOpenGLTexture(img); if (performance == high || performance == ultraHigh) { texture->setAutoMipMapGenerationEnabled(true); texture->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::LinearMipMapLinear); } else { texture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear); } float y = 1 * (float(img.height()) / img.width()); QString s = "cover"; replace(s.toLocal8Bit().data(), texture, x, y, idx); loaded[idx] = true; } } // try to load only few images on the left and right side // i.e. all visible ones plus some extra int count = 8; switch (performance) { case low: count = 8; break; case medium: count = 10; break; case high: count = 12; break; case ultraHigh: count = 14; break; } int *indexes = new int[2 * count + 1]; int center = currentSelected; indexes[0] = center; for (int j = 0; j < count; j++) { indexes[j * 2 + 1] = center + j + 1; indexes[j * 2 + 2] = center - j - 1; } for (int c = 0; c < 2 * count + 1; c++) { int i = indexes[c]; if ((i >= 0) && (i < numObjects)) if (rawImages.size() > 0) if (!loaded[i] && imagesReady[i]) // slide(i).isNull()) { worker->generate(i, rawImages.at(i)); delete[] indexes; return; } } delete[] indexes; } void YACReaderPageFlowGL::populate(int n) { worker->reset(); if (lazyPopulateObjects != -1 || hasBeenInitialized) YACReaderFlowGL::populate(n); lazyPopulateObjects = n; imagesReady = QVector(n, false); rawImages = QVector(n); imagesSetted = QVector(n, false); // puede sobrar } //----------------------------------------------------------------------------- // ImageLoader //----------------------------------------------------------------------------- QImage ImageLoaderGL::loadImage(const QString &fileName) { QImage image; if (!image.load(fileName)) { return QImage(); } switch (flow->performance) { case low: image = image.scaledToWidth(200, Qt::SmoothTransformation); break; case medium: image = image.scaledToWidth(256, Qt::SmoothTransformation); break; case high: image = image.scaledToWidth(320, Qt::SmoothTransformation); break; case ultraHigh: break; // no scaling in ultraHigh } return image; } ImageLoaderGL::ImageLoaderGL(YACReaderFlowGL *flow) : QThread(), flow(flow), restart(false), working(false), idx(-1) { } ImageLoaderGL::~ImageLoaderGL() { mutex.lock(); condition.wakeOne(); mutex.unlock(); wait(); } bool ImageLoaderGL::busy() const { return isRunning() ? working : false; } void ImageLoaderGL::generate(int index, const QString &fileName) { mutex.lock(); this->idx = index; this->fileName = fileName; this->size = size; this->img = QImage(); mutex.unlock(); if (!isRunning()) start(); else { // already running, wake up whenever ready restart = true; condition.wakeOne(); } } void ImageLoaderGL::lock() { mutex.lock(); } void ImageLoaderGL::unlock() { mutex.unlock(); } void ImageLoaderGL::run() { for (;;) { // copy necessary data mutex.lock(); this->working = true; QString fileName = this->fileName; mutex.unlock(); QImage image = loadImage(fileName); // let everyone knows it is ready mutex.lock(); this->working = false; this->img = image; mutex.unlock(); // put to sleep mutex.lock(); if (!this->restart) condition.wait(&mutex); restart = false; mutex.unlock(); } } QImage ImageLoaderGL::result() { return img; } //----------------------------------------------------------------------------- // ImageLoader //----------------------------------------------------------------------------- QImage ImageLoaderByteArrayGL::loadImage(const QByteArray &raw) { QImage image; if (!image.loadFromData(raw)) { return QImage(); } switch (flow->performance) { case low: image = image.scaledToWidth(128, Qt::SmoothTransformation); break; case medium: image = image.scaledToWidth(196, Qt::SmoothTransformation); break; case high: image = image.scaledToWidth(256, Qt::SmoothTransformation); break; case ultraHigh: image = image.scaledToWidth(320, Qt::SmoothTransformation); break; } return image; } ImageLoaderByteArrayGL::ImageLoaderByteArrayGL(YACReaderFlowGL *flow) : QThread(), flow(flow), restart(false), working(false), idx(-1) { } ImageLoaderByteArrayGL::~ImageLoaderByteArrayGL() { mutex.lock(); condition.wakeOne(); mutex.unlock(); wait(); } bool ImageLoaderByteArrayGL::busy() const { return isRunning() ? working : false; } void ImageLoaderByteArrayGL::generate(int index, const QByteArray &raw) { mutex.lock(); this->idx = index; this->rawData = raw; this->size = size; this->img = QImage(); mutex.unlock(); if (!isRunning()) start(); else { // already running, wake up whenever ready restart = true; condition.wakeOne(); } } void ImageLoaderByteArrayGL::run() { for (;;) { // copy necessary data mutex.lock(); this->working = true; QByteArray raw = this->rawData; mutex.unlock(); QImage image = loadImage(raw); // let everyone knows it is ready mutex.lock(); this->working = false; this->img = image; mutex.unlock(); // put to sleep mutex.lock(); if (!this->restart) condition.wait(&mutex); restart = false; mutex.unlock(); } } QImage ImageLoaderByteArrayGL::result() { return img; }