#include "render.h" #include #include #include #include #include #include #include #include #include "comic_db.h" #include "yacreader_global_gui.h" template 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 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 )); im.setColorTable(colors); } 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 redChannel; QList greenChannel; QList blueChannel; for(int j=bound;j hist(256,0); for(int j=0;j 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 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;isetFilter(img); } *page = img; emit pageReady(numPage); } //----------------------------------------------------------------------------- // Render //----------------------------------------------------------------------------- Render::Render() :currentIndex(0),doublePage(false),doubleMangaPage(false),comic(0),loadedComic(false),imageRotation(0),numLeftPages(4),numRightPages(4) { int size = numLeftPages+numRightPages+1; currentPageBufferedIndex = numLeftPages; for(int i = 0; imoveToThread(QApplication::instance()->thread()); comic->deleteLater(); } foreach(PageRender * pr,pageRenders) if(pr !=0) { if(pr->wait()) delete pr; } //TODO move to share_ptr foreach(ImageFilter * filter, filters) delete filter; } //Este método se encarga de forzar el renderizado de las páginas. //Actualiza el buffer según es necesario. //si la pagina actual no está renderizada, se lanza un hilo que la renderize (double or single page mode) y se emite una señal que indica que se está renderizando. void Render::render() { updateBuffer(); if(buffer[currentPageBufferedIndex]->isNull()) { if(pagesReady.size()>0) { if(pagesReady[currentIndex]) { pageRenders[currentPageBufferedIndex] = new PageRender(this,currentIndex,comic->getRawData()->at(currentIndex),buffer[currentPageBufferedIndex],imageRotation,filters); } else //las páginas no están listas, y se están cargando en el cómic emit processingPage(); //para evitar confusiones esta señal deberí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ñal pageReady del hilo, con el SLOT prepareAvailablePage connect(pageRenders[currentPageBufferedIndex],SIGNAL(pageReady(int)),this,SLOT(prepareAvailablePage(int))); //se emite la señal de procesando, debido a que los hilos se arrancan aquí if(filters.size()>0) emit processingPage(); pageRenders[currentPageBufferedIndex]->start(); pageRenders[currentPageBufferedIndex]->setPriority(QThread::TimeCriticalPriority); } else //en qué caso serí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á lista { //emit currentPageReady(); //make prepareAvailablePage the only function that emits currentPageReady() prepareAvailablePage(currentIndex); } fillBuffer(); } QPixmap * Render::getCurrentPage() { QPixmap * page = new QPixmap(); *page = page->fromImage(*buffer[currentPageBufferedIndex]); return page; } QPixmap * Render::getCurrentDoublePage() { if (currentPageIsDoublePage()) { QPoint leftpage(0,0); QPoint rightpage(0,0); QSize leftsize = buffer[currentPageBufferedIndex]->size(); QSize rightsize = buffer[currentPageBufferedIndex+1]->size(); int totalWidth,totalHeight; switch (imageRotation) { case 0: totalHeight = qMax(leftsize.rheight(),rightsize.rheight()); leftsize.scale(leftsize.rwidth(), totalHeight, Qt::KeepAspectRatioByExpanding); rightsize.scale(rightsize.rwidth(), totalHeight, Qt::KeepAspectRatioByExpanding); totalWidth = leftsize.rwidth() + rightsize.rwidth(); rightpage.setX(leftsize.rwidth()); break; case 90: totalWidth = qMax(leftsize.rwidth(), rightsize.rwidth()); leftsize.scale(totalWidth, leftsize.rheight(), Qt::KeepAspectRatioByExpanding); rightsize.scale(totalWidth, rightsize.rheight(), Qt::KeepAspectRatioByExpanding); totalHeight = leftsize.rheight() + rightsize.rheight(); rightpage.setY(leftsize.rheight()); break; case 180: totalHeight = qMax(leftsize.rheight(),rightsize.rheight()); leftsize.scale(leftsize.rwidth(), totalHeight, Qt::KeepAspectRatioByExpanding); rightsize.scale(rightsize.rwidth(), totalHeight, Qt::KeepAspectRatioByExpanding); totalWidth = leftsize.rwidth() + rightsize.rwidth(); leftpage.setX(rightsize.rwidth()); break; case 270: totalWidth = qMax(leftsize.rwidth(), rightsize.rwidth()); leftsize.scale(totalWidth, leftsize.rheight(), Qt::KeepAspectRatioByExpanding); rightsize.scale(totalWidth, rightsize.rheight(), Qt::KeepAspectRatioByExpanding); totalHeight = leftsize.rheight() + rightsize.rheight(); leftpage.setY(rightsize.rheight()); break; default: return NULL; } QPixmap * page = new QPixmap(totalWidth, totalHeight); QPainter painter(page); painter.drawImage(QRect(leftpage,leftsize), *buffer[currentPageBufferedIndex]); painter.drawImage(QRect(rightpage,rightsize), *buffer[currentPageBufferedIndex+1]); return page; } else { return NULL; } } QPixmap * Render::getCurrentDoubleMangaPage() { if (currentPageIsDoublePage()) { QPoint leftpage(0,0); QPoint rightpage(0,0); QSize leftsize = buffer[currentPageBufferedIndex+1]->size(); QSize rightsize = buffer[currentPageBufferedIndex]->size(); int totalWidth,totalHeight; switch (imageRotation) { case 0: totalHeight = qMax(leftsize.rheight(),rightsize.rheight()); leftsize.scale(leftsize.rwidth(), totalHeight, Qt::KeepAspectRatioByExpanding); rightsize.scale(rightsize.rwidth(), totalHeight, Qt::KeepAspectRatioByExpanding); totalWidth = leftsize.rwidth() + rightsize.rwidth(); rightpage.setX(leftsize.rwidth()); break; case 90: totalWidth = qMax(leftsize.rwidth(), rightsize.rwidth()); leftsize.scale(totalWidth, leftsize.rheight(), Qt::KeepAspectRatioByExpanding); rightsize.scale(totalWidth, rightsize.rheight(), Qt::KeepAspectRatioByExpanding); totalHeight = leftsize.rheight() + rightsize.rheight(); rightpage.setY(leftsize.rheight()); break; case 180: totalHeight = qMax(leftsize.rheight(),rightsize.rheight()); leftsize.scale(leftsize.rwidth(), totalHeight, Qt::KeepAspectRatioByExpanding); rightsize.scale(rightsize.rwidth(), totalHeight, Qt::KeepAspectRatioByExpanding); totalWidth = leftsize.rwidth() + rightsize.rwidth(); leftpage.setX(rightsize.rwidth()); break; case 270: totalWidth = qMax(leftsize.rwidth(), rightsize.rwidth()); leftsize.scale(totalWidth, leftsize.rheight(), Qt::KeepAspectRatioByExpanding); rightsize.scale(totalWidth, rightsize.rheight(), Qt::KeepAspectRatioByExpanding); totalHeight = leftsize.rheight() + rightsize.rheight(); leftpage.setY(rightsize.rheight()); break; default: return NULL; } QPixmap * page = new QPixmap(totalWidth, totalHeight); QPainter painter(page); painter.drawImage(QRect(rightpage, rightsize), *buffer[currentPageBufferedIndex]); painter.drawImage(QRect(leftpage, leftsize), *buffer[currentPageBufferedIndex+1]); return page; } else { return NULL; } } bool Render::currentPageIsDoublePage() { if (buffer[currentPageBufferedIndex]->isNull() || buffer[currentPageBufferedIndex+1]->isNull()) { return false; } if (imageRotation == 0 || imageRotation == 180) { if (buffer[currentPageBufferedIndex]->height() > buffer[currentPageBufferedIndex]->width() && buffer[currentPageBufferedIndex+1]->height() > buffer[currentPageBufferedIndex+1]->width()) { return true; } } else if (imageRotation == 90 || imageRotation == 270) { if (buffer[currentPageBufferedIndex]->width() > buffer[currentPageBufferedIndex]->height() && buffer[currentPageBufferedIndex+1]->width() > buffer[currentPageBufferedIndex+1]->height()) { return true; } } return false; } bool Render::nextPageIsDoublePage() { //this function is not used right now if (buffer[currentPageBufferedIndex+2]->isNull() || buffer[currentPageBufferedIndex+3]->isNull()) { return false; } if (imageRotation == 0 || imageRotation == 180) { if (buffer[currentPageBufferedIndex+2]->height() > buffer[currentPageBufferedIndex+2]->width() && buffer[currentPageBufferedIndex+3]->height() > buffer[currentPageBufferedIndex+3]->width()) { return true; } } else if (imageRotation == 90 || imageRotation == 270) { if (buffer[currentPageBufferedIndex]->width() > buffer[currentPageBufferedIndex]->height() && buffer[currentPageBufferedIndex+1]->width() > buffer[currentPageBufferedIndex+1]->height()) { return true; } } return false; } bool Render::previousPageIsDoublePage() { if (buffer[currentPageBufferedIndex-1]->isNull() || buffer[currentPageBufferedIndex-2]->isNull()) { return false; } if (imageRotation == 0 || imageRotation == 180) { if (buffer[currentPageBufferedIndex-1]->height() > buffer[currentPageBufferedIndex-1]->width() && buffer[currentPageBufferedIndex-2]->height() > buffer[currentPageBufferedIndex-2]->width()) { return true; } } else if (imageRotation == 90 || imageRotation == 270) { if (buffer[currentPageBufferedIndex-1]->width() > buffer[currentPageBufferedIndex-1]->height() && buffer[currentPageBufferedIndex-2]->width() > buffer[currentPageBufferedIndex-2]->height()) { return true; } } return false; } 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(!doublePage) { if (currentIndex == page) { emit currentPageReady(); } } else { //check for last page in double page mode if ((currentIndex == page) && (currentIndex + 1) >= (int)comic->numPages()) { emit currentPageReady(); } else if ((currentIndex == page && !buffer[currentPageBufferedIndex+1]->isNull()) || (currentIndex+1 == page && !buffer[currentPageBufferedIndex]->isNull())) { emit currentPageReady(); } } } void Render::update() { render(); } //----------------------------------------------------------------------------- // Comic interface //----------------------------------------------------------------------------- void Render::load(const QString & path, int atPage) { createComic(path); if (comic !=0) { 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); if (comic!=0) { loadComic(path,comicDB); startLoad(); } } void Render::createComic(const QString & path) { previousIndex = currentIndex = 0; pagesEmited.clear(); if(comic!=0) { //comic->moveToThread(QApplication::instance()->thread()); comic->invalidate(); 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; } connect(comic,SIGNAL(errorOpening()),this,SIGNAL(errorOpening()), Qt::QueuedConnection); connect(comic,SIGNAL(errorOpening(QString)),this,SIGNAL(errorOpening(QString)), Qt::QueuedConnection); connect(comic,SIGNAL(crcErrorFound(QString)),this,SIGNAL(crcError(QString)), Qt::QueuedConnection); connect(comic,SIGNAL(errorOpening()),this,SLOT(reset()), Qt::QueuedConnection); connect(comic,SIGNAL(imageLoaded(int)),this,SLOT(pageRawDataReady(int)), Qt::QueuedConnection); connect(comic,SIGNAL(imageLoaded(int)),this,SIGNAL(imageLoaded(int)), Qt::QueuedConnection); connect(comic,SIGNAL(openAt(int)),this,SLOT(renderAt(int)), Qt::QueuedConnection); connect(comic,SIGNAL(numPages(unsigned int)),this,SIGNAL(numPages(unsigned int)), Qt::QueuedConnection); connect(comic,SIGNAL(numPages(unsigned int)),this,SLOT(setNumPages(unsigned int)), Qt::QueuedConnection); connect(comic,SIGNAL(imageLoaded(int,QByteArray)),this,SIGNAL(imageLoaded(int,QByteArray)), Qt::QueuedConnection); connect(comic,SIGNAL(isBookmark(bool)),this,SIGNAL(currentPageIsBookmark(bool)), Qt::QueuedConnection); connect(comic,SIGNAL(bookmarksUpdated()),this,SIGNAL(bookmarksUpdated()), Qt::QueuedConnection); //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 = nullptr; thread = new QThread(); comic->moveToThread(thread); connect(comic, SIGNAL(errorOpening()), thread, SLOT(quit()), Qt::QueuedConnection); connect(comic, SIGNAL(errorOpening(QString)), thread, SLOT(quit()), Qt::QueuedConnection); connect(comic, SIGNAL(imagesLoaded()), thread, SLOT(quit()), Qt::QueuedConnection); connect(comic, SIGNAL(destroyed()), thread, SLOT(quit()), Qt::QueuedConnection); connect(comic, SIGNAL(invalidated()), thread, SLOT(quit()), Qt::QueuedConnection); connect(thread, SIGNAL(started()), comic, SLOT(process())); connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); if(thread != nullptr) 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ál debe ser en funció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ál será la próxima página nextPage = comic->nextPage(); //se fuerza renderizado si la página ha cambiado if(currentIndex != nextPage) { previousIndex = currentIndex; currentIndex = nextPage; update(); emit pageChanged(currentIndex); } else if (hasLoadedComic() && (currentIndex == numPages()-1)) { emit isLast(); } } void Render::nextDoublePage() { int nextPage; if (currentIndex +2 < (int)comic->numPages()) { nextPage = currentIndex+2; } else { nextPage = currentIndex; } if(currentIndex != nextPage) { comic->setIndex(nextPage); previousIndex = currentIndex; currentIndex = nextPage; update(); emit pageChanged(currentIndex); } else if (hasLoadedComic() && ((unsigned int)currentIndex >= numPages()-2)) { emit isLast(); } } //si se solicita la página anterior, se calcula cuál debe ser en funció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ál será la próxima página previousPage = comic->previousPage(); //se fuerza renderizado si la página ha cambiado if(currentIndex != previousPage) { previousIndex = currentIndex; currentIndex = previousPage; update(); emit pageChanged(currentIndex); } else if (hasLoadedComic() && (currentIndex == 0)) { emit isCover(); } } void Render::previousDoublePage() { int previousPage; //indica cuál será la próxima página previousPage = qMax(currentIndex-2,0); if(currentIndex != previousPage) { comic->setIndex(previousPage); previousIndex = currentIndex; currentIndex = previousPage; update(); emit pageChanged(currentIndex); } } 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) { if (!hasLoadedComic()) return; pagesEmited.push_back(page); if(pageRenders.size()>0) { for(int i=0;i= pagesReady.size()) { pagesEmited.clear(); return; //Oooops, something went wrong } pagesReady[pagesEmited.at(i)] = true; if(pagesEmited.at(i) == currentIndex) update(); 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; 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ágenes (vacías) necesarias para su posterior renderizado y //eliminado aquellas que ya no sean necesarias. Tambié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ún sea el sentido de la lectura) void Render::updateBuffer() { QMutexLocker locker(&mutex); int windowSize = currentIndex - previousIndex; 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() { if (pagesReady.size() < 1) { return; } 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+i]) //preload next pages { pageRenders[currentPageBufferedIndex+i] = new PageRender(this,currentIndex+i,comic->getRawData()->at(currentIndex+i),buffer[currentPageBufferedIndex+i],imageRotation,filters); connect(pageRenders[currentPageBufferedIndex+i],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-i]) //preload previous pages { pageRenders[currentPageBufferedIndex-i] = new PageRender(this,currentIndex-i,comic->getRawData()->at(currentIndex-i),buffer[currentPageBufferedIndex-i],imageRotation,filters); connect(pageRenders[currentPageBufferedIndex-i],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ón y se libera la memoria (de hilos e imágenes) void Render::invalidate() { for(int i=0;iwait(); delete pageRenders[i]; pageRenders[i] = 0; } } for(int i=0;inumPages())) { if (currentPageIsDoublePage()) { if (doubleMangaPage) s = QString::number(currentIndex+2) + "-" + s; else 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(); }