/* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2005-12-17 * Description : image file IO threaded interface. * * Copyright (C) 2005-2007 by Marcel Wiesweg * Copyright (C) 2005-2007 by Gilles Caulier * * 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, 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. * * ============================================================ */ // Local includes. #include "ddebug.h" #include "dmetadata.h" #include "loadsavethread.h" #include "managedloadsavethread.h" #include "sharedloadsavethread.h" #include "loadsavetask.h" namespace Digikam { class LoadSaveThreadPriv { public: LoadSaveThreadPriv() { running = true; blockNotification = false; lastTask = 0; } bool running; bool blockNotification; LoadSaveTask *lastTask; TQTime notificationTime; }; //--------------------------------------------------------------------------------------------------- LoadSaveThread::LoadSaveThread() { d = new LoadSaveThreadPriv; m_currentTask = 0; m_notificationPolicy = NotificationPolicyTimeLimited; start(); } LoadSaveThread::~LoadSaveThread() { d->running = false; { TQMutexLocker lock(&m_mutex); m_condVar.wakeAll(); } wait(); if (d->lastTask) delete d->lastTask; delete d; } void LoadSaveThread::load(LoadingDescription description) { TQMutexLocker lock(&m_mutex); m_todo.append(new LoadingTask(this, description)); m_condVar.wakeAll(); } void LoadSaveThread::save(DImg &image, const TQString& filePath, const TQString &format) { TQMutexLocker lock(&m_mutex); m_todo.append(new SavingTask(this, image, filePath, format)); m_condVar.wakeAll(); } void LoadSaveThread::run() { while (d->running) { { TQMutexLocker lock(&m_mutex); if (d->lastTask) { delete d->lastTask; d->lastTask = 0; } m_currentTask = m_todo.getFirst(); if (m_currentTask) { m_todo.removeFirst(); if (m_notificationPolicy == NotificationPolicyTimeLimited) { // set timing values so that first event is sent only // after an initial time span. d->notificationTime = TQTime::currentTime(); d->blockNotification = true; } } else m_condVar.wait(&m_mutex, 1000); } if (m_currentTask) m_currentTask->execute(); } } void LoadSaveThread::taskHasFinished() { // This function is called by the tasks before they send their final message. // This is to guarantee the user of the API that at least the final message // is sent after load() has been called. This might not been the case // if m_currentTask is currently loading the same image and a race condition // between the return from execute and the next run of the loop above occurs. TQMutexLocker lock(&m_mutex); d->lastTask = m_currentTask; m_currentTask = 0; } void LoadSaveThread::customEvent(TQCustomEvent *event) { if (event->type() == NotifyEvent::notifyEventId()) { switch (m_notificationPolicy) { case NotificationPolicyDirect: d->blockNotification = false; break; case NotificationPolicyTimeLimited: break; } ((NotifyEvent *)event)->notify(this); } } void LoadSaveThread::setNotificationPolicy(NotificationPolicy notificationPolicy) { m_notificationPolicy = notificationPolicy; d->blockNotification = false; } bool LoadSaveThread::querySendNotifyEvent() { // This function is called from the thread to ask for permission to send a notify event. switch (m_notificationPolicy) { case NotificationPolicyDirect: // Note that m_blockNotification is not protected by a mutex. However, if there is a // race condition, the worst case is that one event is not sent, which is no problem. if (d->blockNotification) return false; else { d->blockNotification = true; return true; } break; case NotificationPolicyTimeLimited: // Current default time value: 100 millisecs. if (d->blockNotification) d->blockNotification = d->notificationTime.msecsTo(TQTime::currentTime()) < 100; if (d->blockNotification) return false; else { d->notificationTime = TQTime::currentTime(); d->blockNotification = true; return true; } break; } return false; } // This is a hack needed to prevent hanging when a TDEProcess-based loader (raw loader) // is waiting for the process to finish, but the main thread is waiting // for the thread to finish and no TDEProcess events are delivered. // Remove when porting to TQt4. bool LoadSaveThread::isShuttingDown() { // the condition is met after d->running is set to false in the destructor return running() && !d->running; } bool LoadSaveThread::exifRotate(DImg &image, const TQString& filePath) { TQVariant attribute(image.attribute("exifRotated")); if (attribute.isValid() && attribute.toBool()) return false; // Raw files are already rotated properlly by dcraw. Only perform auto-rotation with JPEG/PNG/TIFF file. // We don't have a feedback from dcraw about auto-rotated RAW file during decoding. Return true anyway. attribute = image.attribute("fromRawEmbeddedPreview"); if (DImg::fileFormat(filePath) == DImg::RAW && !(attribute.isValid() && attribute.toBool()) ) { return true; } // Rotate thumbnail based on metadata orientation information DMetadata metadata(filePath); DMetadata::ImageOrientation orientation = metadata.getImageOrientation(); bool rotatedOrFlipped = false; if(orientation != DMetadata::ORIENTATION_NORMAL) { switch (orientation) { case DMetadata::ORIENTATION_NORMAL: case DMetadata::ORIENTATION_UNSPECIFIED: break; case DMetadata::ORIENTATION_HFLIP: image.flip(DImg::HORIZONTAL); rotatedOrFlipped = true; break; case DMetadata::ORIENTATION_ROT_180: image.rotate(DImg::ROT180); rotatedOrFlipped = true; break; case DMetadata::ORIENTATION_VFLIP: image.flip(DImg::VERTICAL); rotatedOrFlipped = true; break; case DMetadata::ORIENTATION_ROT_90_HFLIP: image.rotate(DImg::ROT90); image.flip(DImg::HORIZONTAL); rotatedOrFlipped = true; break; case DMetadata::ORIENTATION_ROT_90: image.rotate(DImg::ROT90); rotatedOrFlipped = true; break; case DMetadata::ORIENTATION_ROT_90_VFLIP: image.rotate(DImg::ROT90); image.flip(DImg::VERTICAL); rotatedOrFlipped = true; break; case DMetadata::ORIENTATION_ROT_270: image.rotate(DImg::ROT270); rotatedOrFlipped = true; break; } } image.setAttribute("exifRotated", true); return rotatedOrFlipped; /* if (orientation == DMetadata::ORIENTATION_NORMAL || orientation == DMetadata::ORIENTATION_UNSPECIFIED) return; TQWMatrix matrix; switch (orientation) { case DMetadata::ORIENTATION_NORMAL: case DMetadata::ORIENTATION_UNSPECIFIED: break; case DMetadata::ORIENTATION_HFLIP: matrix.scale(-1, 1); break; case DMetadata::ORIENTATION_ROT_180: matrix.rotate(180); break; case DMetadata::ORIENTATION_VFLIP: matrix.scale(1, -1); break; case DMetadata::ORIENTATION_ROT_90_HFLIP: matrix.scale(-1, 1); matrix.rotate(90); break; case DMetadata::ORIENTATION_ROT_90: matrix.rotate(90); break; case DMetadata::ORIENTATION_ROT_90_VFLIP: matrix.scale(1, -1); matrix.rotate(90); break; case DMetadata::ORIENTATION_ROT_270: matrix.rotate(270); break; } // transform accordingly thumb = thumb.xForm( matrix ); */ } } // namespace Digikam #include "loadsavethread.moc"