aseprite/src/app/thumbnail_generator.cpp
David Capello e5ba8e0922 Encapsulate Image implementation (private members, accessors, iterators, etc.)
- Rename _rgba/_graya to raster::rgba()/graya()
- Add raster::color_t type (alias for uint32_t)
- Rename raster::GfxObj to Object. And GfxObj::getType() to Object::type()
- Move conversion from raster::Image/Palette to Allegro BITMAP/RGB
  to raster/conversion_alleg.h file
- Add raster/color_scales.h
- Rename image_* functions to raster/primitives.h
- Reimplement ink processing with templates instead of macros
2013-11-09 19:59:05 -03:00

231 lines
6.4 KiB
C++

/* Aseprite
* Copyright (C) 2001-2013 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/thumbnail_generator.h"
#include "app/app.h"
#include "app/document.h"
#include "app/file/file.h"
#include "app/file_system.h"
#include "base/bind.h"
#include "base/scoped_lock.h"
#include "base/thread.h"
#include "raster/conversion_alleg.h"
#include "raster/image.h"
#include "raster/palette.h"
#include "raster/primitives.h"
#include "raster/rotate.h"
#include "raster/sprite.h"
#include <allegro.h>
#define MAX_THUMBNAIL_SIZE 128
namespace app {
class ThumbnailGenerator::Worker {
public:
Worker(FileOp* fop, IFileItem* fileitem)
: m_fop(fop)
, m_fileitem(fileitem)
, m_thumbnail(NULL)
, m_palette(NULL)
, m_thread(Bind<void>(&Worker::loadBgThread, this)) {
}
~Worker() {
fop_stop(m_fop);
m_thread.join();
fop_free(m_fop);
}
IFileItem* getFileItem() { return m_fileitem; }
bool isDone() const { return fop_is_done(m_fop); }
double getProgress() const { return fop_get_progress(m_fop); }
private:
void loadBgThread() {
try {
fop_operate(m_fop, NULL);
// Post load
fop_post_load(m_fop);
// Convert the loaded document into the Allegro bitmap "m_thumbnail".
const Sprite* sprite = (m_fop->document && m_fop->document->getSprite()) ? m_fop->document->getSprite():
NULL;
if (!fop_is_stop(m_fop) && sprite) {
// The palette to convert the Image to a BITMAP
m_palette.reset(new Palette(*sprite->getPalette(FrameNumber(0))));
// Render the 'sprite' in one plain 'image'
base::UniquePtr<Image> image(Image::create(sprite->getPixelFormat(),
sprite->getWidth(),
sprite->getHeight()));
sprite->render(image, 0, 0, FrameNumber(0));
// Calculate the thumbnail size
int thumb_w = MAX_THUMBNAIL_SIZE * image->getWidth() / MAX(image->getWidth(), image->getHeight());
int thumb_h = MAX_THUMBNAIL_SIZE * image->getHeight() / MAX(image->getWidth(), image->getHeight());
if (MAX(thumb_w, thumb_h) > MAX(image->getWidth(), image->getHeight())) {
thumb_w = image->getWidth();
thumb_h = image->getHeight();
}
thumb_w = MID(1, thumb_w, MAX_THUMBNAIL_SIZE);
thumb_h = MID(1, thumb_h, MAX_THUMBNAIL_SIZE);
// Stretch the 'image'
m_thumbnail.reset(Image::create(image->getPixelFormat(), thumb_w, thumb_h));
clear_image(m_thumbnail, 0);
image_scale(m_thumbnail, image, 0, 0, thumb_w, thumb_h);
}
delete m_fop->document;
// Set the thumbnail of the file-item.
if (m_thumbnail) {
BITMAP* bmp = create_bitmap_ex(16, m_thumbnail->getWidth(), m_thumbnail->getHeight());
convert_image_to_allegro(m_thumbnail, bmp, 0, 0, m_palette);
m_fileitem->setThumbnail(bmp);
}
}
catch (const std::exception& e) {
fop_error(m_fop, "Error loading file:\n%s", e.what());
}
fop_done(m_fop);
}
FileOp* m_fop;
IFileItem* m_fileitem;
base::UniquePtr<Image> m_thumbnail;
base::UniquePtr<Palette> m_palette;
base::thread m_thread;
};
static void delete_singleton(ThumbnailGenerator* singleton)
{
delete singleton;
}
ThumbnailGenerator* ThumbnailGenerator::instance()
{
static ThumbnailGenerator* singleton = NULL;
if (singleton == NULL) {
singleton = new ThumbnailGenerator();
App::instance()->Exit.connect(Bind<void>(&delete_singleton, singleton));
}
return singleton;
}
ThumbnailGenerator::WorkerStatus ThumbnailGenerator::getWorkerStatus(IFileItem* fileitem, double& progress)
{
base::scoped_lock hold(m_workersAccess);
for (WorkerList::iterator
it=m_workers.begin(), end=m_workers.end(); it!=end; ++it) {
Worker* worker = *it;
if (worker->getFileItem() == fileitem) {
if (worker->isDone())
return ThumbnailIsDone;
else {
progress = worker->getProgress();
return WorkingOnThumbnail;
}
}
}
return WithoutWorker;
}
bool ThumbnailGenerator::checkWorkers()
{
base::scoped_lock hold(m_workersAccess);
bool doingWork = !m_workers.empty();
for (WorkerList::iterator
it=m_workers.begin(); it != m_workers.end(); ) {
if ((*it)->isDone()) {
delete *it;
it = m_workers.erase(it);
}
else {
++it;
}
}
return doingWork;
}
void ThumbnailGenerator::addWorkerToGenerateThumbnail(IFileItem* fileitem)
{
double progress;
if (fileitem->isBrowsable() ||
fileitem->getThumbnail() != NULL ||
getWorkerStatus(fileitem, progress) != WithoutWorker)
return;
FileOp* fop = fop_to_load_document(fileitem->getFileName().c_str(),
FILE_LOAD_SEQUENCE_NONE |
FILE_LOAD_ONE_FRAME);
if (!fop)
return;
if (fop->has_error()) {
fop_free(fop);
}
else {
Worker* worker = new Worker(fop, fileitem);
try {
base::scoped_lock hold(m_workersAccess);
m_workers.push_back(worker);
}
catch (...) {
delete worker;
throw;
}
}
}
void ThumbnailGenerator::stopAllWorkers()
{
base::thread* ptr = new base::thread(Bind<void>(&ThumbnailGenerator::stopAllWorkersBackground, this));
m_stopThread.reset(ptr);
}
void ThumbnailGenerator::stopAllWorkersBackground()
{
WorkerList workersCopy;
{
base::scoped_lock hold(m_workersAccess);
workersCopy = m_workers;
m_workers.clear();
}
for (WorkerList::iterator
it=workersCopy.begin(), end=workersCopy.end(); it!=end; ++it) {
delete *it;
}
}
} // namespace app