yacreader/YACReader/render.cpp
Felix Kauselmann 599f7ffcf3 Add a manga reading mode which displays image in reverse order in double page mode.
The mode is triggered by selecting the "21" double page icon next to the double page icon.
2014-06-29 21:38:00 +02:00

1104 lines
29 KiB
C++
Raw Blame History

#include "render.h"
#include <cmath>
#include <QList>
#include <algorithm>
#include <QByteArray>
#include <QPixmap>
#include <QApplication>
#include <QImage>
#include <typeinfo>
#include "comic_db.h"
#include "yacreader_global.h"
template<class T>
inline const T& kClamp( const T& x, const T& low, const T& high )
{
if ( x < low ) return low;
else if ( high < x ) return high;
else return x;
}
inline
int changeBrightness( int value, int brightness )
{
return kClamp( value + brightness * 255 / 100, 0, 255 );
}
inline
int changeContrast( int value, int contrast )
{
return kClamp((( value - 127 ) * contrast / 100 ) + 127, 0, 255 );
}
inline
int changeGamma( int value, int gamma )
{
return kClamp( int( pow( value / 255.0, 100.0 / gamma ) * 255 ), 0, 255 );
}
inline
int changeUsingTable( int value, const int table[] )
{
return table[ value ];
}
template< int operation( int, int ) >
static
QImage changeImage( const QImage& image, int value )
{
QImage im = image;
im.detach();
if( im.colorCount() == 0 ) /* truecolor */
{
if( im.format() != QImage::Format_RGB32 ) /* just in case */
im = im.convertToFormat( QImage::Format_RGB32 );
int table[ 256 ];
for( int i = 0;
i < 256;
++i )
table[ i ] = operation( i, value );
if( im.hasAlphaChannel() )
{
for( int y = 0;
y < im.height();
++y )
{
QRgb* line = reinterpret_cast< QRgb* >( im.scanLine( y ));
for( int x = 0;
x < im.width();
++x )
line[ x ] = qRgba( changeUsingTable( qRed( line[ x ] ), table ),
changeUsingTable( qGreen( line[ x ] ), table ),
changeUsingTable( qBlue( line[ x ] ), table ),
changeUsingTable( qAlpha( line[ x ] ), table ));
}
}
else
{
for( int y = 0;
y < im.height();
++y )
{
QRgb* line = reinterpret_cast< QRgb* >( im.scanLine( y ));
for( int x = 0;
x < im.width();
++x )
line[ x ] = qRgb( changeUsingTable( qRed( line[ x ] ), table ),
changeUsingTable( qGreen( line[ x ] ), table ),
changeUsingTable( qBlue( line[ x ] ), table ));
}
}
}
else
{
QVector<QRgb> colors = im.colorTable();
for( int i = 0;
i < im.colorCount();
++i )
colors[ i ] = qRgb( operation( qRed( colors[ i ] ), value ),
operation( qGreen( colors[ i ] ), value ),
operation( qBlue( colors[ i ] ), value ));
}
return im;
}
// brightness is multiplied by 100 in order to avoid floating point numbers
QImage changeBrightness( const QImage& image, int brightness )
{
if( brightness == 0 ) // no change
return image;
return changeImage< changeBrightness >( image, brightness );
}
// contrast is multiplied by 100 in order to avoid floating point numbers
QImage changeContrast( const QImage& image, int contrast )
{
if( contrast == 100 ) // no change
return image;
return changeImage< changeContrast >( image, contrast );
}
// gamma is multiplied by 100 in order to avoid floating point numbers
QImage changeGamma( const QImage& image, int gamma )
{
if( gamma == 100 ) // no change
return image;
return changeImage< changeGamma >( image, gamma );
}
//-----------------------------------------------------------------------------
// MeanNoiseReductionFilter
//-----------------------------------------------------------------------------
MeanNoiseReductionFilter::MeanNoiseReductionFilter(enum NeighborghoodSize ns)
:neighborghoodSize(ns)
{
}
QImage MeanNoiseReductionFilter::setFilter(const QImage & image)
{
int width = image.width();
int height = image.height();
QImage result(width,height,image.format());
int filterSize = sqrt((float)neighborghoodSize);
int bound = filterSize/2;
QRgb pix;
int r,g,b;
for(int j=bound;j<height-bound;j++){
for(int i=bound;i<width-bound;i++){
r=g=b=0;
for(int y=j-bound;y<=j+bound;y++)
{
for(int x=i-bound;x<=i+bound;x++)
{
pix = image.pixel(x,y);
r += qRed(pix);
g += qGreen(pix);
b += qBlue(pix);
}
}
result.setPixel(i,j,QColor(r/neighborghoodSize,g/neighborghoodSize,b/neighborghoodSize).rgb());
//qDebug((QString::number(redChannel.at(4))+" "+QString::number(greenChannel.at(4))+" "+QString::number(blueChannel.at(4))).toAscii());
//qDebug((QString::number(redChannel.size())+" "+QString::number(greenChannel.size())+" "+QString::number(blueChannel.size())).toAscii());
}
}
return result;
}
//-----------------------------------------------------------------------------
// MedianNoiseReductionFilter
//-----------------------------------------------------------------------------
MedianNoiseReductionFilter::MedianNoiseReductionFilter(enum NeighborghoodSize ns)
:neighborghoodSize(ns)
{
}
QImage MedianNoiseReductionFilter::setFilter(const QImage & image)
{
int width = image.width();
int height = image.height();
QImage result(width,height,image.format());
int filterSize = sqrt((float)neighborghoodSize);
int bound = filterSize/2;
QRgb pix;
QList<int> redChannel;
QList<int> greenChannel;
QList<int> blueChannel;
for(int j=bound;j<height-bound;j++){
for(int i=bound;i<width-bound;i++){
redChannel.clear();
greenChannel.clear();
blueChannel.clear();
for(int y=j-bound;y<=j+bound;y++)
{
for(int x=i-bound;x<=i+bound;x++)
{
pix = image.pixel(x,y);
redChannel.push_back(qRed(pix));
greenChannel.push_back(qGreen(pix));
blueChannel.push_back(qBlue(pix));
}
}
std::sort(redChannel.begin(),redChannel.end());
std::sort(greenChannel.begin(),greenChannel.end());
std::sort(blueChannel.begin(),blueChannel.end());
result.setPixel(i,j,QColor(redChannel.at(4),greenChannel.at(4),blueChannel.at(4)).rgb());
}
}
return result;
}
//-----------------------------------------------------------------------------
// BrightnessFilter
//-----------------------------------------------------------------------------
BrightnessFilter::BrightnessFilter(int l)
:ImageFilter()
{
level = l;
}
QImage BrightnessFilter::setFilter(const QImage & image)
{
/*int width = image.width();
int height = image.height();
QImage result(width,height,image.format());
for(int j=0;j<height;j++){
for(int i=0;i<width;i++){
result.setPixel(i,j,QColor(image.pixel(i,j)).light(level).rgb());
}
}
return result;*/
if(level ==-1)
{
QSettings settings(YACReader::getSettingsPath()+"/YACReader.ini",QSettings::IniFormat);
return changeBrightness(image,settings.value(BRIGHTNESS,0).toInt());
}
else
{
return changeBrightness(image,level);
}
}
//-----------------------------------------------------------------------------
// ContrastFilter
//-----------------------------------------------------------------------------
ContrastFilter::ContrastFilter(int l)
:ImageFilter()
{
level = l;
}
QImage ContrastFilter::setFilter(const QImage & image)
{
/*int width = image.width();
int height = image.height();
QImage result(width,height,image.format());
int min,max,v;
min = 0;
max = 255;
int sum = 0;
QVector<int> hist(256,0);
for(int j=0;j<height;j++){
for(int i=0;i<width;i++){
hist[QColor(image.pixel(i,j)).lightness()]++;
sum++;
}
}
long double count = sum;
long double new_count = 0.0;
long double percentage,next_percentage;
for (int i = 0; i < 254; i++)
{
new_count += hist[i];
percentage = new_count/count;
next_percentage = (new_count+hist[i+1])/count;
if(fabs (percentage - 0.006) < fabs (next_percentage - 0.006))
{
min = i+1;
break;
}
}
new_count=0.0;
for (int i = 255; i > 1; i--)
{
new_count += hist[i];
percentage = new_count/count;
next_percentage = (new_count+hist[i-1])/count;
if(fabs (percentage - 0.006) < fabs (next_percentage - 0.006))
{
max = i-1;
break;
}
}
QColor c;
int range = max - min;
for(int j=0;j<height;j++){
for(int i=0;i<width;i++){
c = QColor(image.pixel(i,j));
result.setPixel(i,j,c.light(((c.lightness()-min)/range*1.0)*255).rgb());
}
}
return result;*/
if(level ==-1)
{
QSettings settings(YACReader::getSettingsPath()+"/YACReader.ini",QSettings::IniFormat);
return changeContrast(image,settings.value(CONTRAST,100).toInt());
}
else
{
return changeContrast(image,level);
}
}
//-----------------------------------------------------------------------------
// ContrastFilter
//-----------------------------------------------------------------------------
GammaFilter::GammaFilter(int l)
:ImageFilter()
{
level = l;
}
QImage GammaFilter::setFilter(const QImage & image)
{
if(level ==-1)
{
QSettings settings(YACReader::getSettingsPath()+"/YACReader.ini",QSettings::IniFormat);
return changeGamma(image,settings.value(GAMMA,100).toInt());
}
else
{
return changeGamma(image,level);
}
}
//-----------------------------------------------------------------------------
// PageRender
//-----------------------------------------------------------------------------
PageRender::PageRender()
:QThread()
{
}
PageRender::PageRender(Render * r,int np, const QByteArray & rd, QImage * p,unsigned int d, QVector<ImageFilter *> f)
:QThread(),
render(r),
numPage(np),
data(rd),
page(p),
degrees(d),
filters(f)
{
}
void PageRender::run()
{
QMutexLocker locker(&(render->mutex));
QImage img;
img.loadFromData(data);
if(degrees > 0)
{
QMatrix m;
m.rotate(degrees);
img = img.transformed(m,Qt::SmoothTransformation);
}
for(int i=0;i<filters.size();i++)
{
img = filters[i]->setFilter(img);
}
*page = img;
emit pageReady(numPage);
}
//-----------------------------------------------------------------------------
// DoublePageRender
//-----------------------------------------------------------------------------
DoublePageRender::DoublePageRender(Render * r, int np, const QByteArray & rd, const QByteArray & rd2, QImage * p,unsigned int d, QVector<ImageFilter *> f)
:PageRender(),
render(r),
numPage(np),
data(rd),
data2(rd2),
page(p),
degrees(d),
filters(f)
{
}
void DoublePageRender::run()
{
//QImage result;
QMutexLocker locker(&(render->mutex));
QImage img, img2;
if(!data.isEmpty())
img.loadFromData(data);
if(!data2.isEmpty())
img2.loadFromData(data2);
/*if(img.isNull())
img = QPixmap(img2.width(),img2.height());
if(img2.isNull())
img2 = QPixmap(img.width(),img.height());*/
if(img.isNull() && !img2.isNull())
{
img = img2;
img2 = QImage();
}
int totalWidth,totalHeight;
//x = img.width()+img2.width();
totalHeight = qMax(img.height(),img2.height());
//widths fiting the normalized height
int width1, width2;
//altura normalizada
if(!img2.isNull())
{
if(img.height()!=totalHeight)
totalWidth = (width1 = ((img.width() * totalHeight) / img.height())) + (width2 = img2.width());
else
totalWidth = (width1 = img.width()) + (width2 = ((img2.width() * totalHeight) / img2.height()));
}
else
totalWidth = width1 = img.width();
QImage auxImg(totalWidth,totalHeight,QImage::Format_RGB32);
QPainter painter(&auxImg);
if (render->doubleMangaPage) {
qDebug() << "we are in the double Manga Page tree" << render->doubleMangaPage;
painter.drawImage(QRect(width2,0,width1,totalHeight),img);
if(!img2.isNull())
painter.drawImage(QRect(0,0,width2,totalHeight),img2);
painter.end();
}
else {
qDebug() << "no double Manga Page" << render->doubleMangaPage;
painter.drawImage(QRect(0,0,width1,totalHeight),img);
if(!img2.isNull())
painter.drawImage(QRect(width1,0,width2,totalHeight),img2);
painter.end();
}
if(degrees > 0)
{
QMatrix m;
m.rotate(degrees);
auxImg = auxImg.transformed(m,Qt::SmoothTransformation);
}
for(int i=0;i<filters.size();i++)
{
auxImg = filters[i]->setFilter(auxImg);
}
*page = auxImg;
emit pageReady(numPage);
}
//-----------------------------------------------------------------------------
// Render
//-----------------------------------------------------------------------------
Render::Render()
:currentIndex(0),doublePage(false),doubleMangaPage(false),comic(0),loadedComic(false),imageRotation(0),numLeftPages(2),numRightPages(2)
{
int size = numLeftPages+numRightPages+1;
currentPageBufferedIndex = numLeftPages;
for(int i = 0; i<size; i++)
{
buffer.push_back(new QImage());
pageRenders.push_back(0);
}
filters.push_back(new BrightnessFilter());
filters.push_back(new ContrastFilter());
filters.push_back(new GammaFilter());
}
Render::~Render()
{
if(comic!=0)
{
comic->moveToThread(QApplication::instance()->thread());
comic->deleteLater();
}
foreach(ImageFilter * filter, filters)
delete filter;
foreach(PageRender * pr,pageRenders)
if(pr !=0)
{
if(pr->wait())
delete pr;
}
}
//Este m<>todo se encarga de forzar el renderizado de las p<>ginas.
//Actualiza el buffer seg<65>n es necesario.
//si la pagina actual no est<73> renderizada, se lanza un hilo que la renderize (double or single page mode) y se emite una se<73>al que indica que se est<73> renderizando.
void Render::render()
{
updateBuffer();
if(buffer[currentPageBufferedIndex]->isNull())
{
if(pagesReady.size()>0)
{
if(doublePage)
{
if(pagesReady[currentIndex] && pagesReady[qMin(currentIndex+1,(int)comic->numPages()-1)])
if(currentIndex+1 > (int)comic->numPages()-1)
pageRenders[currentPageBufferedIndex] = new DoublePageRender(this,currentIndex,comic->getRawData()->at(currentIndex),QByteArray(),buffer[currentPageBufferedIndex],imageRotation,filters);
else
pageRenders[currentPageBufferedIndex] = new DoublePageRender(this,currentIndex,comic->getRawData()->at(currentIndex),comic->getRawData()->at(currentIndex+1),buffer[currentPageBufferedIndex],imageRotation,filters);
else
//las p<>ginas no est<73>n listas, y se est<73>n cargando en el c<>mic
emit processingPage(); //para evitar confusiones esta se<73>al deber<65>a llamarse de otra forma
}
else
if(pagesReady[currentIndex])
pageRenders[currentPageBufferedIndex] = new PageRender(this,currentIndex,comic->getRawData()->at(currentIndex),buffer[currentPageBufferedIndex],imageRotation,filters);
else
//las p<>ginas no est<73>n listas, y se est<73>n cargando en el c<>mic
emit processingPage(); //para evitar confusiones esta se<73>al deber<65>a llamarse de otra forma
//si se ha creado un hilo para renderizar la p<>gina actual, se arranca
if(pageRenders[currentPageBufferedIndex]!=0)
{
//se conecta la se<73>al pageReady del hilo, con el SLOT prepareAvailablePage
connect(pageRenders[currentPageBufferedIndex],SIGNAL(pageReady(int)),this,SLOT(prepareAvailablePage(int)));
//se emite la se<73>al de procesando, debido a que los hilos se arrancan aqu<71>
if(doublePage || filters.size()>0)
emit processingPage();
pageRenders[currentPageBufferedIndex]->start();
pageRenders[currentPageBufferedIndex]->setPriority(QThread::TimeCriticalPriority);
}
else
//en qu<71> caso ser<65>a necesario hacer esto??? //TODO: IMPORTANTE, puede que no sea necesario.
emit processingPage();
}
else
//no hay ninguna p<>gina lista para ser renderizada, es necesario esperar.
emit processingPage();
}
else
// la p<>gina actual est<73> lista
emit currentPageReady();
//se renderizan las p<>ginas restantes para llenar el buffer.
if(doublePage)
fillBufferDoublePage();
else
fillBuffer();
}
QPixmap * Render::getCurrentPage()
{
QPixmap * page = new QPixmap();
*page = page->fromImage(*buffer[currentPageBufferedIndex]);
return page;
}
void Render::setRotation(int degrees)
{
Q_UNUSED(degrees)
}
void Render::setComic(Comic * c)
{
if(comic !=0)
{
comic->moveToThread(QApplication::instance()->thread());
comic->disconnect();
comic->deleteLater();
}
comic = c;
}
void Render::prepareAvailablePage(int page)
{
if(currentIndex == page)
emit currentPageReady();
}
void Render::update()
{
render();
}
//-----------------------------------------------------------------------------
// Comic interface
//-----------------------------------------------------------------------------
void Render::load(const QString & path, int atPage)
{
createComic(path);
loadComic(path,atPage);
startLoad();
}
//-----------------------------------------------------------------------------
void Render::load(const QString & path, const ComicDB & comicDB)
{
//TODO prepare filters
for(int i = 0; i < filters.count(); i++)
{
if(typeid(*filters[i]) == typeid(BrightnessFilter))
if(comicDB.info.brightness == -1)
filters[i]->setLevel(0);
else
filters[i]->setLevel(comicDB.info.brightness);
if(typeid(*filters[i]) == typeid(ContrastFilter))
if(comicDB.info.contrast == -1)
filters[i]->setLevel(100);
else
filters[i]->setLevel(comicDB.info.contrast);
if(typeid(*filters[i]) == typeid(GammaFilter))
if(comicDB.info.gamma == -1)
filters[i]->setLevel(100);
else
filters[i]->setLevel(comicDB.info.gamma);
}
createComic(path);
loadComic(path,comicDB);
startLoad();
}
void Render::createComic(const QString & path)
{
if(comic!=0)
{
//comic->moveToThread(QApplication::instance()->thread());
comic->disconnect();
comic->deleteLater();
}
//comic->moveToThread(QApplication::instance()->thread());
comic = FactoryComic::newComic(path);
if(comic == NULL)//archivo no encontrado o no v<>lido
{
emit errorOpening();
reset();
return;
}
previousIndex = currentIndex = 0;
connect(comic,SIGNAL(errorOpening()),this,SIGNAL(errorOpening()));
connect(comic,SIGNAL(errorOpening(QString)),this,SIGNAL(errorOpening(QString)));
connect(comic,SIGNAL(crcErrorFound(QString)),this,SIGNAL(crcError(QString)));
connect(comic,SIGNAL(errorOpening()),this,SLOT(reset()));
connect(comic,SIGNAL(imageLoaded(int)),this,SIGNAL(imageLoaded(int)));
connect(comic,SIGNAL(imageLoaded(int)),this,SLOT(pageRawDataReady(int)));
connect(comic,SIGNAL(openAt(int)),this,SLOT(renderAt(int)));
connect(comic,SIGNAL(numPages(unsigned int)),this,SIGNAL(numPages(unsigned int)));
connect(comic,SIGNAL(numPages(unsigned int)),this,SLOT(setNumPages(unsigned int)));
connect(comic,SIGNAL(imageLoaded(int,QByteArray)),this,SIGNAL(imageLoaded(int,QByteArray)));
connect(comic,SIGNAL(isBookmark(bool)),this,SIGNAL(currentPageIsBookmark(bool)));
connect(comic,SIGNAL(isBookmark(bool)),this,SLOT(pageIsBookmark(bool)));
connect(comic,SIGNAL(bookmarksUpdated()),this,SIGNAL(bookmarksUpdated()));
//connect(comic,SIGNAL(isLast()),this,SIGNAL(isLast()));
//connect(comic,SIGNAL(isCover()),this,SIGNAL(isCover()));
pagesReady.clear();
}
void Render::loadComic(const QString & path,const ComicDB & comicDB)
{
comic->load(path,comicDB);
}
void Render::loadComic(const QString & path, int atPage)
{
comic->load(path,atPage);
}
void Render::startLoad()
{
QThread * thread = NULL;
thread = new QThread();
comic->moveToThread(thread);
connect(thread, SIGNAL(started()), comic, SLOT(process()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
if(thread != NULL)
thread->start();
invalidate();
loadedComic = true;
update();
}
void Render::renderAt(int page)
{
previousIndex = currentIndex = page;
emit pageChanged(page);
}
void Render::reset()
{
loadedComic = false;
invalidate();
}
//si se solicita la siguiente p<>gina, se calcula cu<63>l debe ser en funci<63>n de si se lee en modo a doble p<>gina o no.
//la p<>gina s<>lo se renderiza, si realmente ha cambiado.
void Render::nextPage()
{
int nextPage; //indica cu<63>l ser<65> la pr<70>xima p<>gina
if(doublePage)
{
nextPage = currentIndex;
if(currentIndex+2<(int)comic->numPages())
{
nextPage = currentIndex+2;
if(currentIndex != nextPage)
comic->setIndex(nextPage);
}
}
else
{
nextPage = comic->nextPage();
}
//se fuerza renderizado si la p<>gina ha cambiado
if(currentIndex != nextPage)
{
previousIndex = currentIndex;
currentIndex = nextPage;
update();
emit pageChanged(currentIndex);
}
else
emit isLast();
}
//si se solicita la p<>gina anterior, se calcula cu<63>l debe ser en funci<63>n de si se lee en modo a doble p<>gina o no.
//la p<>gina s<>lo se renderiza, si realmente ha cambiado.
void Render::previousPage()
{
int previousPage; //indica cu<63>l ser<65> la pr<70>xima p<>gina
if(doublePage)
{
if(currentIndex == 1)
invalidate();
previousPage = qMax(currentIndex-2,0);
if(currentIndex != previousPage)
{
comic->setIndex(previousPage);
}
}
else
{
previousPage = comic->previousPage();
}
//se fuerza renderizado si la p<>gina ha cambiado
if(currentIndex != previousPage)
{
previousIndex = currentIndex;
currentIndex = previousPage;
update();
emit pageChanged(currentIndex);
}
else
emit isCover();
}
unsigned int Render::getIndex()
{
return comic->getIndex();
}
unsigned int Render::numPages()
{
return comic->numPages();
}
bool Render::hasLoadedComic()
{
if(comic!=0)
return comic->loaded();
return false;
}
void Render::setNumPages(unsigned int numPages)
{
pagesReady.fill(false,numPages);
}
void Render::pageRawDataReady(int page)
{
pagesEmited.push_back(page);
if(pageRenders.size()>0)
{
for(int i=0;i<pagesEmited.size();i++)
{
pagesReady[pagesEmited.at(i)] = true;
if(pagesEmited.at(i) == currentIndex)
update();
if(doublePage)
{
if(pagesEmited.at(i)==currentIndex+1)
update();
if ( ((pagesEmited.at(i) < currentIndex) && (pagesEmited.at(i) > currentIndex-2*numLeftPages)) ||
((pagesEmited.at(i) > currentIndex+1) && (pagesEmited.at(i) < currentIndex+1+2*numRightPages)) )
{
fillBufferDoublePage();
}
}
else
{
if ( ((pagesEmited.at(i) < currentIndex) && (pagesEmited.at(i) > currentIndex-numLeftPages)) ||
((pagesEmited.at(i) > currentIndex) && (pagesEmited.at(i) < currentIndex+numRightPages)) )
{
fillBuffer();
}
}
}
pagesEmited.clear();
}
}
//s<>lo se renderiza la p<>gina, si ha habido un cambio de p<>gina
void Render::goTo(int index)
{
if(currentIndex != index)
{
comic->setIndex(index);
previousIndex = currentIndex;
currentIndex = index;
//si cambia la paridad de las p<>gina en modo a doble p<>gina, se rellena el buffer.
//esto solo deber<65>a orcurrir al llegar al principio o al final
if(doublePage && ((previousIndex - index) % 2)!=0)
invalidate();
update();
emit pageChanged(currentIndex);
}
}
void Render::rotateRight()
{
imageRotation = (imageRotation+90) % 360;
reload();
}
void Render::rotateLeft()
{
if(imageRotation == 0)
imageRotation = 270;
else
imageRotation = imageRotation - 90;
reload();
}
//Actualiza el buffer, a<>adiendo las im<69>genes (vac<61>as) necesarias para su posterior renderizado y
//eliminado aquellas que ya no sean necesarias. Tambi<62>n libera los hilos (no estoy seguro de que sea responsabilidad suya)
//Calcula el n<>mero de nuevas p<>ginas que hay que buferear y si debe hacerlo por la izquierda o la derecha (seg<65>n sea el sentido de la lectura)
void Render::updateBuffer()
{
QMutexLocker locker(&mutex);
int windowSize = currentIndex - previousIndex;
if(doublePage)
{
windowSize = windowSize/2;
if(currentIndex == 0 && windowSize == 0 && previousIndex == 1)
windowSize = -1;
}
if(windowSize > 0)//add pages to right pages and remove on the left
{
windowSize = qMin(windowSize,buffer.size());
for(int i = 0; i < windowSize; i++)
{
//renders
PageRender * pr = pageRenders.front();
pageRenders.pop_front();
if(pr !=0)
{
if(pr->wait())
delete pr;
}
pageRenders.push_back(0);
//images
if(buffer.front()!=0)
delete buffer.front();
buffer.pop_front();
buffer.push_back(new QImage());
}
}
else //add pages to left pages and remove on the right
if(windowSize<0)
{
windowSize = -windowSize;
windowSize = qMin(windowSize,buffer.size());
for(int i = 0; i < windowSize; i++)
{
//renders
PageRender * pr = pageRenders.back();
pageRenders.pop_back();
if(pr !=0)
{
if(pr->wait())
delete pr;
}
pageRenders.push_front(0);
//images
buffer.push_front(new QImage());
QImage * p = buffer.back();
if(p!=0)
delete p;
buffer.pop_back();
}
}
previousIndex = currentIndex;
}
void Render::fillBuffer()
{
for(int i = 1; i <= qMax(numLeftPages,numRightPages); i++)
{
if ((currentIndex+i < (int)comic->numPages()) &&
buffer[currentPageBufferedIndex+i]->isNull() &&
i <= numRightPages &&
pageRenders[currentPageBufferedIndex+i]==0 &&
pagesReady[currentIndex+1]) //preload next pages
{
pageRenders[currentPageBufferedIndex+i] = new PageRender(this,currentIndex+i,comic->getRawData()->at(currentIndex+i),buffer[currentPageBufferedIndex+i],imageRotation,filters);
connect(pageRenders[currentPageBufferedIndex],SIGNAL(pageReady(int)),this,SLOT(prepareAvailablePage(int)));
pageRenders[currentPageBufferedIndex+i]->start();
}
if ((currentIndex-i > 0) &&
buffer[currentPageBufferedIndex-i]->isNull() &&
i <= numLeftPages &&
pageRenders[currentPageBufferedIndex-i]==0 &&
pagesReady[currentIndex-1]) //preload previous pages
{
pageRenders[currentPageBufferedIndex-i] = new PageRender(this,currentIndex-i,comic->getRawData()->at(currentIndex-i),buffer[currentPageBufferedIndex-i],imageRotation,filters);
connect(pageRenders[currentPageBufferedIndex],SIGNAL(pageReady(int)),this,SLOT(prepareAvailablePage(int)));
pageRenders[currentPageBufferedIndex-i]->start();
}
}
}
void Render::fillBufferDoublePage()
{
for(int i = 1; i <= qMax(numLeftPages,numRightPages); i++)
{
if ((currentIndex+2*i < (int)comic->numPages()) &&
buffer[currentPageBufferedIndex+i]->isNull() &&
i <= numRightPages &&
pageRenders[currentPageBufferedIndex+i]==0 &&
(pagesReady[currentIndex+2*i] && pagesReady[qMin(currentIndex+(2*i)+1,(int)comic->numPages()-1)])) //preload next pages
{
if(currentIndex+(2*i)+1 > (int)comic->numPages()-1)
pageRenders[currentPageBufferedIndex+i] = new DoublePageRender(this,currentIndex+2*i,comic->getRawData()->at(currentIndex+(2*i)),QByteArray(),buffer[currentPageBufferedIndex+i],imageRotation,filters);
else
pageRenders[currentPageBufferedIndex+i] = new DoublePageRender(this,currentIndex+2*i,comic->getRawData()->at(currentIndex+(2*i)),comic->getRawData()->at(currentIndex+(2*i)+1),buffer[currentPageBufferedIndex+i],imageRotation,filters);
connect(pageRenders[currentPageBufferedIndex],SIGNAL(pageReady(int)),this,SLOT(prepareAvailablePage(int)));
pageRenders[currentPageBufferedIndex+i]->start();
}
if ((currentIndex-2*i >= -1) &&
buffer[currentPageBufferedIndex-i]->isNull() &&
i <= numLeftPages &&
pageRenders[currentPageBufferedIndex-i]==0 &&
(pagesReady[qMax(currentIndex-2*i,0)] && pagesReady[qMin(currentIndex-(2*i)+1,(int)comic->numPages()-1)])) //preload previous pages
{
if(currentIndex-2*i == -1)
pageRenders[currentPageBufferedIndex-i] = new DoublePageRender(this,0,QByteArray(),comic->getRawData()->at(0),buffer[currentPageBufferedIndex-i],imageRotation,filters);
else
pageRenders[currentPageBufferedIndex-i] = new DoublePageRender(this,currentIndex-2*i,comic->getRawData()->at(currentIndex-(2*i)),comic->getRawData()->at(currentIndex-(2*i)+1),buffer[currentPageBufferedIndex-i],imageRotation,filters);
connect(pageRenders[currentPageBufferedIndex],SIGNAL(pageReady(int)),this,SLOT(prepareAvailablePage(int)));
pageRenders[currentPageBufferedIndex-i]->start();
}
}
}
//M<>todo que debe ser llamado cada vez que la estructura del buffer se vuelve inconsistente con el modo de lectura actual.
//se terminan todos los hilos en ejecuci<63>n y se libera la memoria (de hilos e im<69>genes)
void Render::invalidate()
{
for(int i=0;i<pageRenders.size();i++)
{
if(pageRenders[i]!=0)
{
pageRenders[i]->wait();
delete pageRenders[i];
pageRenders[i] = 0;
}
}
for(int i=0;i<buffer.size();i++)
{
delete buffer[i];
buffer[i] = new QImage();
}
}
void Render::doublePageSwitch()
{
doublePage = !doublePage;
if(comic)
{
invalidate();
update();
}
}
void Render::doubleMangaPageSwitch()
{
doubleMangaPage = !doubleMangaPage;
if(comic&&doublePage)
{
invalidate();
update();
}
}
QString Render::getCurrentPagesInformation()
{
QString s = QString::number(currentIndex+1);
if (doublePage && (currentIndex+1 < (int)comic->numPages()))
s += "-"+QString::number(currentIndex+2);
s += "/"+QString::number(comic->numPages());
return s;
}
void Render::setBookmark()
{
comic->setBookmark();
}
void Render::removeBookmark()
{
comic->removeBookmark();
}
void Render::save()
{
comic->saveBookmarks();
}
Bookmarks * Render::getBookmarks()
{
return comic->bm;
}
void Render::reload()
{
if(comic)
{
invalidate();
update();
}
}
void Render::updateFilters(int brightness, int contrast, int gamma)
{
for(int i = 0; i < filters.count(); i++)
{
if(typeid(*filters[i]) == typeid(BrightnessFilter))
filters[i]->setLevel(brightness);
if(typeid(*filters[i]) == typeid(ContrastFilter))
filters[i]->setLevel(contrast);
if(typeid(*filters[i]) == typeid(GammaFilter))
filters[i]->setLevel(gamma);
}
reload();
}