You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
digikam/digikam/libs/thumbbar/thumbnailjob.cpp

319 lines
6.9 KiB

/* ============================================================
*
* This file is a part of digiKam project
* http://www.digikam.org
*
* Date : 2003-10-14
* Description : digiKam KIO thumbnails generator interface
*
* Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu>
* Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
*
* 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.
*
* ============================================================ */
// C Ansi includes.
extern "C"
{
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <fcntl.h>
#include <unistd.h>
}
// TQt includes.
#include <tqstring.h>
#include <tqdir.h>
#include <tqfileinfo.h>
#include <tqimage.h>
#include <tqpixmap.h>
#include <tqpainter.h>
#include <tqcolor.h>
#include <tqdatastream.h>
// KDE includes.
#include <kglobal.h>
// Local includes.
#include "ddebug.h"
#include "thumbnailjob.h"
#include "thumbnailjob.moc"
namespace Digikam
{
class ThumbnailJobPriv
{
public:
bool highlight;
bool exifRotate;
bool running;
int size;
// Shared memory segment Id. The segment is allocated to a size
// of extent x extent x 4 (32 bit image) on first need.
int shmid;
// And the data area
uchar *shmaddr;
KURL curr_url;
KURL next_url;
KURL::List urlList;
};
ThumbnailJob::ThumbnailJob(const KURL& url, int size,
bool highlight, bool exifRotate)
: KIO::Job(false)
{
d = new ThumbnailJobPriv;
d->urlList.append(url);
d->size = size;
d->highlight = highlight;
d->exifRotate = exifRotate;
d->curr_url = d->urlList.first();
d->next_url = d->curr_url;
d->running = false;
d->shmid = -1;
d->shmaddr = 0;
processNext();
}
ThumbnailJob::ThumbnailJob(const KURL::List& urlList, int size,
bool highlight, bool exifRotate)
: KIO::Job(false)
{
d = new ThumbnailJobPriv;
d->urlList = urlList;
d->size = size;
d->highlight = highlight;
d->running = false;
d->exifRotate = exifRotate;
d->curr_url = d->urlList.first();
d->next_url = d->curr_url;
d->shmid = -1;
d->shmaddr = 0;
processNext();
}
ThumbnailJob::~ThumbnailJob()
{
if (d->shmaddr)
{
shmdt((char*)d->shmaddr);
shmctl(d->shmid, IPC_RMID, 0);
}
delete d;
}
void ThumbnailJob::addItem(const KURL& url)
{
d->urlList.append(url);
if (!d->running && subjobs.isEmpty())
processNext();
}
void ThumbnailJob::addItems(const KURL::List& urlList)
{
for (KURL::List::const_iterator it = urlList.begin();
it != urlList.end(); ++it)
{
d->urlList.append(*it);
}
if (!d->running && subjobs.isEmpty())
processNext();
}
bool ThumbnailJob::setNextItemToLoad(const KURL& url)
{
KURL::List::const_iterator it = d->urlList.find(url);
if (it != d->urlList.end())
{
d->next_url = *it;
return true;
}
return false;
}
void ThumbnailJob::removeItem(const KURL& url)
{
d->urlList.remove(url);
}
void ThumbnailJob::processNext()
{
if (d->urlList.isEmpty())
{
d->running = false;
emit signalCompleted();
return;
}
KURL::List::iterator it = d->urlList.find(d->next_url);
if (it == d->urlList.end())
{
it = d->urlList.begin();
}
d->curr_url = *it;
it = d->urlList.remove(it);
if (it != d->urlList.end())
{
d->next_url = *it;
}
else
{
d->next_url = KURL();
}
KURL url(d->curr_url);
url.setProtocol("digikamthumbnail");
KIO::TransferJob *job = KIO::get(url, false, false);
job->addMetaData("size", TQString::number(d->size));
createShmSeg();
if (d->shmid != -1)
job->addMetaData("shmid", TQString::number(d->shmid));
// Rotate thumbnail accordindly with Exif rotation tag if necessary.
if (d->exifRotate)
job->addMetaData("exif", "yes");
connect(job, TQT_SIGNAL(data(KIO::Job *, const TQByteArray &)),
this, TQT_SLOT(slotThumbData(KIO::Job *, const TQByteArray &)));
addSubjob(job);
d->running = true;
}
void ThumbnailJob::slotResult(KIO::Job *job)
{
subjobs.remove(job);
Q_ASSERT( subjobs.isEmpty() );
if (job->error())
{
emit signalFailed(d->curr_url);
}
d->running = false;
processNext();
}
void ThumbnailJob::createShmSeg()
{
if (d->shmid == -1)
{
if (d->shmaddr)
{
shmdt((char*)d->shmaddr);
shmctl(d->shmid, IPC_RMID, 0);
}
d->shmid = shmget(IPC_PRIVATE, 256 * 256 * 4, IPC_CREAT|0600);
if (d->shmid != -1)
{
d->shmaddr = static_cast<uchar *>(shmat(d->shmid, 0, SHM_RDONLY));
if (d->shmaddr == (uchar *)-1)
{
shmctl(d->shmid, IPC_RMID, 0);
d->shmaddr = 0;
d->shmid = -1;
}
}
else
d->shmaddr = 0;
}
}
void ThumbnailJob::slotThumbData(KIO::Job*, const TQByteArray &data)
{
if (data.isEmpty())
return;
TQImage thumb;
TQDataStream stream(data, IO_ReadOnly);
if (d->shmaddr)
{
int width, height, depth;
stream >> width >> height >> depth;
thumb = TQImage(d->shmaddr, width, height, depth,
0, 0, TQImage::IgnoreEndian);
// The buffer supplied to the TQImage constructor above must remain valid
// throughout the lifetime of the object.
// This is not true, the shared memory will be freed or reused.
// If we pass the object around, we must do a deep copy.
thumb = thumb.copy();
}
else
{
stream >> thumb;
}
if (thumb.isNull())
{
DWarning() << k_funcinfo << "thumbnail is null" << endl;
emit signalFailed(d->curr_url);
return;
}
emitThumbnail(thumb);
}
void ThumbnailJob::emitThumbnail(TQImage& thumb)
{
if (thumb.isNull())
{
return;
}
TQPixmap pix(thumb);
int w = pix.width();
int h = pix.height();
// highlight only when requested and when thumbnail
// width and height are greater than 10
if (d->highlight && (w >= 10 && h >= 10))
{
TQPainter p(&pix);
p.setPen(TQPen(TQColor(0,0,0),1));
p.drawRect(0,0,w,h);
p.end();
}
emit signalThumbnail(d->curr_url, pix);
}
} // namespace Digikam