#include "yacreader_flow_gl.h" #include #include #ifdef Q_OS_MAC #include #else #include #endif #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(); //TODO add antialiasing //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.setPixelSize(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) { float pixelRatio = devicePixelRatio(); fontSize = (width + height) * 0.010 * pixelRatio; 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 = devicePixelRatio(); glViewport(0, 0, width*pixelRatio, height*pixelRatio); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(20.0, GLdouble(width) / (float)height, 1.0, 200.0); 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 & currentVector,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-2){ images[count].index--; count++; } images.removeAt(item); if(texture != defaultTexture) delete(texture); numObjects--; } /*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,false); //marks = QVector(n,false); //worker->start(); } void YACReaderFlowGL::reset() { makeCurrent(); startAnimationTimer(); currentSelected = 0; loaded.clear(); for(int i = 0;iwidth(); int height = this->height(); glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); //float sideX = ((float(width)/height)/2)*1.5; //float sideY = 0.5*1.5; gluPerspective(zoom, (float)width / (float)height, 1.0, 200.0); //glOrtho(-sideX, sideX, -sideY+0.2, +sideY+0.2, 4, 11.0); 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, 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) { makeCurrent(); if(event->button() == Qt::LeftButton) { float x,y; float pixelRatio = devicePixelRatio(); x = event->x()*pixelRatio; y = event->y()*pixelRatio; GLint viewport[4]; GLdouble modelview[16]; GLdouble projection[16]; GLfloat winX, winY, winZ; GLdouble posX, posY, posZ; glGetDoublev( GL_MODELVIEW_MATRIX, modelview ); glGetDoublev( GL_PROJECTION_MATRIX, projection ); glGetIntegerv( GL_VIEWPORT, viewport ); winX = (float)x; winY = (float)viewport[3] - (float)y; glReadPixels(winX, int(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ ); gluUnProject(winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ); if((posX >= 0.5 && !flowRightToLeft) || (posX <=-0.5 && flowRightToLeft)) { //int index = currentSelected+1; //while((cfImages[index].current.x-cfImages[index].width/(2.0*config.rotation)) < posX) // index++; //setCurrentIndex(index-1); showNext(); } else if((posX <=-0.5 && !flowRightToLeft) || (posX >= 0.5 && flowRightToLeft) ) showPrevious(); } else QOpenGLWidget::mousePressEvent(event); doneCurrent(); } void YACReaderFlowGL::mouseDoubleClickEvent(QMouseEvent* event) { makeCurrent(); float x,y; float pixelRatio = devicePixelRatio(); x = event->x()*pixelRatio; y = event->y()*pixelRatio; GLint viewport[4]; GLdouble modelview[16]; GLdouble projection[16]; GLfloat winX, winY, winZ; GLdouble posX, posY, posZ; glGetDoublev( GL_MODELVIEW_MATRIX, modelview ); glGetDoublev( GL_PROJECTION_MATRIX, projection ); glGetIntegerv( GL_VIEWPORT, viewport ); winX = (float)x; winY = (float)viewport[3] - (float)y; glReadPixels( x, int(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ ); gluUnProject( winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ); if(posX <= 0.5 && posX >= -0.5) { emit selected(centerIndex()); event->accept(); } doneCurrent(); } 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 = 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(!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::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) { 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); //worker->deleteLater(); rawImages.clear(); //TODO: remove checking for a valid context //checking is needed because of this bug this bug: https://bugreports.qt.io/browse/QTBUG-60148 if (this->context() != nullptr && this->context()->isValid()) { for(int i = 0; ibusy()) 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; bool result = image.load(fileName); 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 } if(!result) return QImage(); 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; bool result = image.loadFromData(raw); 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; } if(!result) return QImage(); 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; }