yacreader/common/gl/yacreader_flow_gl.cpp
2024-01-27 10:27:06 +01:00

1608 lines
44 KiB
C++

#include "yacreader_flow_gl.h"
#include <QtGui>
#include <QMatrix4x4>
#include <cmath>
/*** 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 &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 - 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<bool>(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<YACReader::YACReaderComicReadStatus> 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<int> newOrder)
{
worker->lock();
worker->reset(); // is this necesary?
startAnimationTimer();
QList<QString> pathsNew;
QVector<bool> loadedNew;
QVector<YACReaderComicReadStatus> marksNew;
QVector<YACReader3DImage> 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<bool>(n, false);
rawImages = QVector<QByteArray>(n);
imagesSetted = QVector<bool>(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;
}