|
|
|
/***************************************************************************
|
|
|
|
copyright : (C) 2003-2006 by Robby Stephenson
|
|
|
|
email : robby@periapsis.org
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
* *
|
|
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
|
|
* it under the terms of version 2 of the GNU General Public License as *
|
|
|
|
* published by the Free Software Foundation; *
|
|
|
|
* *
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
#include "imagefactory.h"
|
|
|
|
#include "image.h"
|
|
|
|
#include "document.h"
|
|
|
|
#include "filehandler.h"
|
|
|
|
#include "tellico_utils.h"
|
|
|
|
#include "tellico_kernel.h"
|
|
|
|
#include "core/tellico_config.h"
|
|
|
|
#include "tellico_debug.h"
|
|
|
|
|
|
|
|
#include <ktempdir.h>
|
|
|
|
#include <kapplication.h>
|
|
|
|
#include <kimageeffect.h>
|
|
|
|
|
|
|
|
#include <tqfile.h>
|
|
|
|
#include <tqdir.h>
|
|
|
|
|
|
|
|
#define RELEASE_IMAGES
|
|
|
|
|
|
|
|
using Tellico::ImageFactory;
|
|
|
|
|
|
|
|
bool ImageFactory::s_needInit = true;
|
|
|
|
const Tellico::Data::Image ImageFactory::s_null;
|
|
|
|
|
|
|
|
TQDict<Tellico::Data::Image> ImageFactory::s_imageDict;
|
|
|
|
// since most images get turned into pixmaps quickly, use 10 megs
|
|
|
|
// for images and 10 megs for pixmaps
|
|
|
|
TQCache<Tellico::Data::Image> ImageFactory::s_imageCache(10 * 1024 * 1024);
|
|
|
|
TQCache<TQPixmap> ImageFactory::s_pixmapCache(10 * 1024 * 1024);
|
|
|
|
// this image info map is just for big images that don't fit
|
|
|
|
// in the cache, so that don't have to be continually reloaded to get info
|
|
|
|
TQMap<TQString, Tellico::Data::ImageInfo> ImageFactory::s_imageInfoMap;
|
|
|
|
Tellico::StringSet ImageFactory::s_imagesInTmpDir;
|
|
|
|
Tellico::StringSet ImageFactory::s_imagesToRelease;
|
|
|
|
KTempDir* ImageFactory::s_tmpDir = 0;
|
|
|
|
TQString ImageFactory::s_localDir;
|
|
|
|
|
|
|
|
void ImageFactory::init() {
|
|
|
|
if(!s_needInit) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
s_imageDict.setAutoDelete(true);
|
|
|
|
s_imageCache.setAutoDelete(true);
|
|
|
|
s_imageCache.setMaxCost(Config::imageCacheSize());
|
|
|
|
s_pixmapCache.setAutoDelete(true);
|
|
|
|
s_needInit = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString ImageFactory::tempDir() {
|
|
|
|
if(!s_tmpDir) {
|
|
|
|
s_tmpDir = new KTempDir();
|
|
|
|
s_tmpDir->setAutoDelete(true);
|
|
|
|
}
|
|
|
|
return s_tmpDir->name();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString ImageFactory::dataDir() {
|
|
|
|
static const TQString dataDir = Tellico::saveLocation(TQString::tqfromLatin1("data/"));
|
|
|
|
return dataDir;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString ImageFactory::localDir() {
|
|
|
|
if(s_localDir.isEmpty()) {
|
|
|
|
return dataDir();
|
|
|
|
}
|
|
|
|
return s_localDir;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString ImageFactory::addImage(const KURL& url_, bool quiet_, const KURL& refer_, bool link_) {
|
|
|
|
return addImageImpl(url_, quiet_, refer_, link_).id();
|
|
|
|
}
|
|
|
|
|
|
|
|
const Tellico::Data::Image& ImageFactory::addImageImpl(const KURL& url_, bool quiet_, const KURL& refer_, bool link_) {
|
|
|
|
if(url_.isEmpty() || !url_.isValid()) {
|
|
|
|
return s_null;
|
|
|
|
}
|
|
|
|
// myLog() << "ImageFactory::addImageImpl(KURL) - " << url_.prettyURL() << endl;
|
|
|
|
Data::Image* img = refer_.isEmpty()
|
|
|
|
? FileHandler::readImageFile(url_, quiet_)
|
|
|
|
: FileHandler::readImageFile(url_, quiet_, refer_);
|
|
|
|
if(!img) {
|
|
|
|
myLog() << "ImageFactory::addImageImpl() - image not found: " << url_.prettyURL() << endl;
|
|
|
|
return s_null;
|
|
|
|
}
|
|
|
|
if(img->isNull()) {
|
|
|
|
delete img;
|
|
|
|
return s_null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(link_) {
|
|
|
|
img->setLinkOnly(true);
|
|
|
|
img->setID(url_.url());
|
|
|
|
}
|
|
|
|
|
|
|
|
if(hasImage(img->id())) {
|
|
|
|
// myDebug() << "### ImageFactory::addImageImpl() - hasImage() is true!" << endl;
|
|
|
|
const Data::Image& img2 = imageById(img->id());
|
|
|
|
if(!img2.isNull()) {
|
|
|
|
delete img;
|
|
|
|
return img2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!link_) {
|
|
|
|
s_imageDict.insert(img->id(), img);
|
|
|
|
}
|
|
|
|
s_imageInfoMap.insert(img->id(), Data::ImageInfo(*img));
|
|
|
|
return *img;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString ImageFactory::addImage(const TQImage& image_, const TQString& format_) {
|
|
|
|
return addImageImpl(image_, format_).id();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString ImageFactory::addImage(const TQPixmap& pix_, const TQString& format_) {
|
|
|
|
return addImageImpl(pix_.convertToImage(), format_).id();
|
|
|
|
}
|
|
|
|
|
|
|
|
const Tellico::Data::Image& ImageFactory::addImageImpl(const TQImage& image_, const TQString& format_) {
|
|
|
|
Data::Image* img = new Data::Image(image_, format_);
|
|
|
|
if(hasImage(img->id())) {
|
|
|
|
const Data::Image& img2 = imageById(img->id());
|
|
|
|
if(!img2.isNull()) {
|
|
|
|
delete img;
|
|
|
|
return img2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(img->isNull()) {
|
|
|
|
delete img;
|
|
|
|
return s_null;
|
|
|
|
}
|
|
|
|
s_imageDict.insert(img->id(), img);
|
|
|
|
s_imageInfoMap.insert(img->id(), Data::ImageInfo(*img));
|
|
|
|
return *img;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString ImageFactory::addImage(const TQByteArray& data_, const TQString& format_, const TQString& id_) {
|
|
|
|
return addImageImpl(data_, format_, id_).id();
|
|
|
|
}
|
|
|
|
|
|
|
|
const Tellico::Data::Image& ImageFactory::addImageImpl(const TQByteArray& data_, const TQString& format_,
|
|
|
|
const TQString& id_) {
|
|
|
|
if(id_.isEmpty()) {
|
|
|
|
return s_null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// do not call imageById(), it causes infinite looping with Document::loadImage()
|
|
|
|
Data::Image* img = s_imageCache.find(id_);
|
|
|
|
if(img) {
|
|
|
|
myLog() << "ImageFactory::addImageImpl(TQByteArray) - already exists in cache: " << id_ << endl;
|
|
|
|
return *img;
|
|
|
|
}
|
|
|
|
|
|
|
|
img = s_imageDict.find(id_);
|
|
|
|
if(img) {
|
|
|
|
myLog() << "ImageFactory::addImageImpl(TQByteArray) - already exists in dict: " << id_ << endl;
|
|
|
|
return *img;
|
|
|
|
}
|
|
|
|
|
|
|
|
img = new Data::Image(data_, format_, id_);
|
|
|
|
if(img->isNull()) {
|
|
|
|
myDebug() << "ImageFactory::addImageImpl(TQByteArray) - NULL IMAGE!!!!!" << endl;
|
|
|
|
delete img;
|
|
|
|
return s_null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// myLog() << "ImageFactory::addImageImpl(TQByteArray) - " << data_.size()
|
|
|
|
// << " bytes, format = " << format_
|
|
|
|
// << ", id = "<< img->id() << endl;
|
|
|
|
|
|
|
|
s_imageDict.insert(img->id(), img);
|
|
|
|
s_imageInfoMap.insert(img->id(), Data::ImageInfo(*img));
|
|
|
|
return *img;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Tellico::Data::Image& ImageFactory::addCachedImageImpl(const TQString& id_, CacheDir dir_) {
|
|
|
|
// myLog() << "ImageFactory::addCachedImageImpl() - dir = " << (dir_ == DataDir ? "DataDir" : "TmpDir" )
|
|
|
|
// << "; id = " << id_ << endl;
|
|
|
|
KURL u;
|
|
|
|
if(dir_ == DataDir) {
|
|
|
|
u.setPath(dataDir() + id_);
|
|
|
|
} else if(dir_ == LocalDir) {
|
|
|
|
u.setPath(localDir() + id_);
|
|
|
|
} else{ // Temp
|
|
|
|
u.setPath(tempDir() + id_);
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString newID = addImage(u, true);
|
|
|
|
if(newID.isEmpty()) {
|
|
|
|
myLog() << "ImageFactory::addCachedImageImpl() - null image loaded" << endl;
|
|
|
|
return s_null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// the id probably got changed, so reset it
|
|
|
|
// addImage() already inserted it in the dict
|
|
|
|
Data::Image* img = s_imageDict.take(newID);
|
|
|
|
if(!img) {
|
|
|
|
kdWarning() << "ImageFactory::addCachedImageImpl() - no image in dict - very bad!" << endl;
|
|
|
|
return s_null;
|
|
|
|
}
|
|
|
|
if(img->isNull()) {
|
|
|
|
kdWarning() << "ImageFactory::addCachedImageImpl() - null image in dict, should never happen!" << endl;
|
|
|
|
delete img;
|
|
|
|
return s_null;
|
|
|
|
}
|
|
|
|
img->setID(id_);
|
|
|
|
s_imageInfoMap.remove(newID);
|
|
|
|
s_imageInfoMap.insert(img->id(), Data::ImageInfo(*img));
|
|
|
|
|
|
|
|
if(s_imageCache.insert(img->id(), img, img->numBytes())) {
|
|
|
|
// myLog() << "ImageFactory::addCachedImageImpl() - removing from dict: " << img->id() << endl;
|
|
|
|
} else {
|
|
|
|
// can't hold it in the cache
|
|
|
|
kdWarning() << "Tellico's image cache is unable to hold the image, it might be too big!" << endl;
|
|
|
|
kdWarning() << "Image name is " << img->id() << endl;
|
|
|
|
kdWarning() << "Image size is " << img->numBytes() << endl;
|
|
|
|
kdWarning() << "Max cache size is " << s_imageCache.maxCost() << endl;
|
|
|
|
|
|
|
|
// add it back to the dict, but add the image to the list of
|
|
|
|
// images to release later. Necessary to avoid a memory leak since new Image()
|
|
|
|
// was called, we need to keep the pointer
|
|
|
|
s_imageDict.insert(img->id(), img);
|
|
|
|
s_imagesToRelease.add(img->id());
|
|
|
|
}
|
|
|
|
return *img;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ImageFactory::writeImage(const TQString& id_, const KURL& targetDir_, bool force_) {
|
|
|
|
// myLog() << "ImageFactory::writeImage() - target = " << targetDir_.url() << id_ << endl;
|
|
|
|
if(targetDir_.isEmpty()) {
|
|
|
|
myDebug() << "ImageFactory::writeImage() - empty target dir!" << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Data::Image& img = imageById(id_);
|
|
|
|
if(img.isNull()) {
|
|
|
|
// myDebug() << "ImageFactory::writeImage() - null image: " << id_ << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(img.linkOnly()) {
|
|
|
|
// myLog() << "ImageFactory::writeImage() - " << id_ << ": link only, not writing!" << endl;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
KURL target = targetDir_;
|
|
|
|
target.addPath(id_);
|
|
|
|
|
|
|
|
return FileHandler::writeDataURL(target, img.byteArray(), force_);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ImageFactory::writeCachedImage(const TQString& id_, CacheDir dir_, bool force_ /*=false*/) {
|
|
|
|
if(id_.isEmpty()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// myLog() << "ImageFactory::writeCachedImage() - dir = " << (dir_ == DataDir ? "DataDir" : "TmpDir" )
|
|
|
|
// << "; id = " << id_ << endl;
|
|
|
|
|
|
|
|
TQString path = ( dir_ == DataDir ? dataDir() : dir_ == TempDir ? tempDir() : localDir() );
|
|
|
|
|
|
|
|
// images in the temp directory are erased every session, so we can track
|
|
|
|
// whether they've already been written with a simple string set.
|
|
|
|
// images in the data directory are persistent, so we have to check the
|
|
|
|
// actual file existence
|
|
|
|
bool exists = ( dir_ == TempDir ? s_imagesInTmpDir.has(id_) : TQFile::exists(path + id_));
|
|
|
|
|
|
|
|
if(!force_ && exists) {
|
|
|
|
// myDebug() << "...writeCachedImage() - exists = true: " << id_ << endl;
|
|
|
|
} else if(!force_ && !exists && dir_ == LocalDir) {
|
|
|
|
TQDir dir(localDir());
|
|
|
|
if(!dir.exists()) {
|
|
|
|
myDebug() << "ImageFactory::writeCachedImage() - creating " << s_localDir << endl;
|
|
|
|
dir.mkdir(localDir());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// myLog() << "ImageFactory::writeCachedImage() - dir = " << (dir_ == DataDir ? "DataDir" : "TmpDir" )
|
|
|
|
// << "; id = " << id_ << endl;
|
|
|
|
}
|
|
|
|
// only write if it doesn't exist
|
|
|
|
bool success = (!force_ && exists) || writeImage(id_, path, true /* force */);
|
|
|
|
|
|
|
|
if(success) {
|
|
|
|
if(dir_ == TempDir) {
|
|
|
|
s_imagesInTmpDir.add(id_);
|
|
|
|
}
|
|
|
|
|
|
|
|
// remove from dict and add to cache
|
|
|
|
// it might not be in dict though
|
|
|
|
Data::Image* img = s_imageDict.take(id_);
|
|
|
|
if(img && s_imageCache.insert(img->id(), img, img->numBytes())) {
|
|
|
|
s_imageInfoMap.remove(id_);
|
|
|
|
} else if(img) {
|
|
|
|
// myLog() << "ImageFactory::writeCachedImage() - failed writing image to cache: " << id_ << endl;
|
|
|
|
// myLog() << "ImageFactory::writeCachedImage() - removed from dict, deleting: " << img->id() << endl;
|
|
|
|
// myLog() << "ImageFactory::writeCachedImage() - current dict size: " << s_imageDict.count() << endl;
|
|
|
|
// can't insert it in the cache, so put it back in the dict
|
|
|
|
// No, it's written to disk now, so we're safe
|
|
|
|
// s_imageDict.insert(img->id(), img);
|
|
|
|
delete img;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Tellico::Data::Image& ImageFactory::imageById(const TQString& id_) {
|
|
|
|
if(id_.isEmpty()) {
|
|
|
|
myDebug() << "ImageFactory::imageById() - empty id" << endl;
|
|
|
|
return s_null;
|
|
|
|
}
|
|
|
|
// myLog() << "ImageFactory::imageById() - " << id_ << endl;
|
|
|
|
|
|
|
|
// can't think of a better place to regularly check for images to release
|
|
|
|
// but don't release image that just got asked for
|
|
|
|
s_imagesToRelease.remove(id_);
|
|
|
|
releaseImages();
|
|
|
|
|
|
|
|
// first check the cache, used for images that are in the data file, or are only temporary
|
|
|
|
// then the dict, used for images downloaded, but not yet saved anywhere
|
|
|
|
Data::Image* img = s_imageCache.find(id_);
|
|
|
|
if(img) {
|
|
|
|
// myLog() << "...imageById() - found in cache" << endl;
|
|
|
|
return *img;
|
|
|
|
}
|
|
|
|
|
|
|
|
img = s_imageDict.find(id_);
|
|
|
|
if(img) {
|
|
|
|
// myLog() << "...imageById() - found in dict" << endl;
|
|
|
|
return *img;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if the image is link only, we need to load it
|
|
|
|
// but can't call imageInfo() since that might recurse into imageById()
|
|
|
|
// also, the image info cache might not have it so check if the
|
|
|
|
// id is a valid absolute url
|
|
|
|
// yeah, it's probably slow
|
|
|
|
if((s_imageInfoMap.contains(id_) && s_imageInfoMap[id_].linkOnly) || !KURL::isRelativeURL(id_)) {
|
|
|
|
KURL u = id_;
|
|
|
|
if(u.isValid()) {
|
|
|
|
return addImageImpl(u, false, KURL(), true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// the document does a delayed loading of the images, sometimes
|
|
|
|
// so an image could be in the tmp dir and not be in the cache
|
|
|
|
// or it could be too big for the cache
|
|
|
|
if(s_imagesInTmpDir.has(id_)) {
|
|
|
|
const Data::Image& img2 = addCachedImageImpl(id_, TempDir);
|
|
|
|
if(!img2.isNull()) {
|
|
|
|
// myLog() << "...imageById() - found in tmp dir" << endl;
|
|
|
|
return img2;
|
|
|
|
} else {
|
|
|
|
myLog() << "ImageFactory::imageById() - img in tmpDir list but not actually there: " << id_ << endl;
|
|
|
|
s_imagesInTmpDir.remove(id_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// try to do a delayed loading of the image
|
|
|
|
if(Data::Document::self()->loadImage(id_)) {
|
|
|
|
// loadImage() could insert in either the cache or the dict!
|
|
|
|
img = s_imageCache.find(id_);
|
|
|
|
if(!img) {
|
|
|
|
img = s_imageDict.find(id_);
|
|
|
|
}
|
|
|
|
if(img) {
|
|
|
|
// myLog() << "...imageById() - found in doc" << endl;
|
|
|
|
// go ahead and write image to disk so we don't have to keep it in memory
|
|
|
|
// calling pixmap() could be loading all the covers, and we don't want one
|
|
|
|
// to get pushed out of the cache yet
|
|
|
|
if(!s_imagesInTmpDir.has(id_)) {
|
|
|
|
writeCachedImage(id_, TempDir);
|
|
|
|
}
|
|
|
|
return *img;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// don't check Config::writeImagesInFile(), someday we might have problems
|
|
|
|
// and the image will exist in the data dir, but the app thinks everything should
|
|
|
|
// be in the zip file instead
|
|
|
|
bool exists = TQFile::exists(dataDir() + id_);
|
|
|
|
if(exists) {
|
|
|
|
// if we're loading from the application data dir, but images are being saved in the
|
|
|
|
// data file instead, then consider the document to be modified since it needs
|
|
|
|
// the image saved
|
|
|
|
if(Config::imageLocation() != Config::ImagesInAppDir) {
|
|
|
|
Data::Document::self()->slotSetModified(true);
|
|
|
|
}
|
|
|
|
const Data::Image& img2 = addCachedImageImpl(id_, DataDir);
|
|
|
|
if(img2.isNull()) {
|
|
|
|
myDebug() << "ImageFactory::imageById() - tried to add from DataDir, but failed: " << id_ << endl;
|
|
|
|
} else {
|
|
|
|
// myLog() << "...imageById() - found in data dir" << endl;
|
|
|
|
return img2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// if localDir() == DataDir(), then there's nothing left to check
|
|
|
|
if(localDir() == dataDir()) {
|
|
|
|
return s_null;
|
|
|
|
}
|
|
|
|
exists = TQFile::exists(localDir() + id_);
|
|
|
|
if(exists) {
|
|
|
|
// if we're loading from the application data dir, but images are being saved in the
|
|
|
|
// data file instead, then consider the document to be modified since it needs
|
|
|
|
// the image saved
|
|
|
|
if(Config::imageLocation() != Config::ImagesInLocalDir) {
|
|
|
|
Data::Document::self()->slotSetModified(true);
|
|
|
|
}
|
|
|
|
const Data::Image& img2 = addCachedImageImpl(id_, LocalDir);
|
|
|
|
if(img2.isNull()) {
|
|
|
|
myDebug() << "ImageFactory::imageById() - tried to add from LocalDir, but failed: " << id_ << endl;
|
|
|
|
} else {
|
|
|
|
// myLog() << "...imageById() - found in data dir" << endl;
|
|
|
|
return img2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
myDebug() << "***ImageFactory::imageById() - not found: " << id_ << endl;
|
|
|
|
return s_null;
|
|
|
|
}
|
|
|
|
|
|
|
|
Tellico::Data::ImageInfo ImageFactory::imageInfo(const TQString& id_) {
|
|
|
|
if(s_imageInfoMap.contains(id_)) {
|
|
|
|
return s_imageInfoMap[id_];
|
|
|
|
}
|
|
|
|
|
|
|
|
const Data::Image& img = imageById(id_);
|
|
|
|
if(img.isNull()) {
|
|
|
|
return Data::ImageInfo();
|
|
|
|
}
|
|
|
|
return Data::ImageInfo(img);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ImageFactory::cacheImageInfo(const Data::ImageInfo& info) {
|
|
|
|
s_imageInfoMap.insert(info.id, info);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ImageFactory::validImage(const TQString& id_) {
|
|
|
|
// don't try s_imageInfoMap[id_] cause it inserts an empty image info
|
|
|
|
return s_imageInfoMap.contains(id_) || hasImage(id_) || !imageById(id_).isNull();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQPixmap ImageFactory::pixmap(const TQString& id_, int width_, int height_) {
|
|
|
|
if(id_.isEmpty()) {
|
|
|
|
return TQPixmap();
|
|
|
|
}
|
|
|
|
|
|
|
|
const TQString key = id_ + '|' + TQString::number(width_) + '|' + TQString::number(height_);
|
|
|
|
TQPixmap* pix = s_pixmapCache.find(key);
|
|
|
|
if(pix) {
|
|
|
|
return *pix;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Data::Image& img = imageById(id_);
|
|
|
|
if(img.isNull()) {
|
|
|
|
return TQPixmap();
|
|
|
|
}
|
|
|
|
|
|
|
|
if(width_ > 0 && height_ > 0) {
|
|
|
|
pix = new TQPixmap(img.convertToPixmap(width_, height_));
|
|
|
|
} else {
|
|
|
|
pix = new TQPixmap(img.convertToPixmap());
|
|
|
|
}
|
|
|
|
|
|
|
|
// pixmap size is w x h x d, divided by 8 bits
|
|
|
|
if(!s_pixmapCache.insert(key, pix, pix->width()*pix->height()*pix->depth()/8)) {
|
|
|
|
kdWarning() << "ImageFactory::pixmap() - can't save in cache: " << id_ << endl;
|
|
|
|
kdWarning() << "### Current pixmap size is " << (pix->width()*pix->height()*pix->depth()/8) << endl;
|
|
|
|
kdWarning() << "### Max pixmap cache size is " << s_pixmapCache.maxCost() << endl;
|
|
|
|
TQPixmap pix2(*pix);
|
|
|
|
delete pix;
|
|
|
|
return pix2;
|
|
|
|
}
|
|
|
|
return *pix;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ImageFactory::clean(bool deleteTempDirectory_) {
|
|
|
|
// the dict and caches all auto-delete
|
|
|
|
s_imagesToRelease.clear();
|
|
|
|
s_imageDict.clear();
|
|
|
|
s_imageInfoMap.clear();
|
|
|
|
s_imageCache.clear();
|
|
|
|
s_pixmapCache.clear();
|
|
|
|
if(deleteTempDirectory_) {
|
|
|
|
s_imagesInTmpDir.clear();
|
|
|
|
delete s_tmpDir;
|
|
|
|
s_tmpDir = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ImageFactory::createStyleImages(const StyleOptions& opt_) {
|
|
|
|
const int collType = Kernel::self()->collectionType();
|
|
|
|
|
|
|
|
const TQColor& baseColor = opt_.baseColor.isValid()
|
|
|
|
? opt_.baseColor
|
|
|
|
: Config::templateBaseColor(collType);
|
|
|
|
const TQColor& highColor = opt_.highlightedBaseColor.isValid()
|
|
|
|
? opt_.highlightedBaseColor
|
|
|
|
: Config::templateHighlightedBaseColor(collType);
|
|
|
|
|
|
|
|
const TQColor& bgc1 = Tellico::blendColors(baseColor, highColor, 30);
|
|
|
|
const TQColor& bgc2 = Tellico::blendColors(baseColor, highColor, 50);
|
|
|
|
|
|
|
|
const TQString bgname = TQString::tqfromLatin1("gradient_bg.png");
|
|
|
|
TQImage bgImage = KImageEffect::gradient(TQSize(400, 1), bgc1, baseColor,
|
|
|
|
KImageEffect::PipeCrossGradient);
|
|
|
|
bgImage = KImageEffect::rotate(bgImage, KImageEffect::Rotate90);
|
|
|
|
|
|
|
|
const TQString hdrname = TQString::tqfromLatin1("gradient_header.png");
|
|
|
|
TQImage hdrImage = KImageEffect::unbalancedGradient(TQSize(1, 10), highColor, bgc2,
|
|
|
|
KImageEffect::VerticalGradient, 100, -100);
|
|
|
|
|
|
|
|
if(opt_.imgDir.isEmpty()) {
|
|
|
|
// write the style images both to the tmp dir and the data dir
|
|
|
|
// doesn't really hurt and lets the user switch back and forth
|
|
|
|
ImageFactory::removeImage(bgname, true /*delete */);
|
|
|
|
ImageFactory::addImageImpl(Data::Image::byteArray(bgImage, "PNG"), TQString::tqfromLatin1("PNG"), bgname);
|
|
|
|
ImageFactory::writeCachedImage(bgname, DataDir, true /*force*/);
|
|
|
|
ImageFactory::writeCachedImage(bgname, TempDir, true /*force*/);
|
|
|
|
|
|
|
|
ImageFactory::removeImage(hdrname, true /*delete */);
|
|
|
|
ImageFactory::addImageImpl(Data::Image::byteArray(hdrImage, "PNG"), TQString::tqfromLatin1("PNG"), hdrname);
|
|
|
|
ImageFactory::writeCachedImage(hdrname, DataDir, true /*force*/);
|
|
|
|
ImageFactory::writeCachedImage(hdrname, TempDir, true /*force*/);
|
|
|
|
} else {
|
|
|
|
bgImage.save(opt_.imgDir + bgname, "PNG");
|
|
|
|
hdrImage.save(opt_.imgDir + hdrname, "PNG");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ImageFactory::removeImage(const TQString& id_, bool deleteImage_) {
|
|
|
|
//be careful using this
|
|
|
|
s_imageDict.remove(id_);
|
|
|
|
s_imageCache.remove(id_);
|
|
|
|
s_imagesInTmpDir.remove(id_);
|
|
|
|
|
|
|
|
if(deleteImage_) {
|
|
|
|
// remove from both data dir and temp dir
|
|
|
|
TQFile::remove(dataDir() + id_);
|
|
|
|
TQFile::remove(tempDir() + id_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Tellico::StringSet ImageFactory::imagesNotInCache() {
|
|
|
|
StringSet set;
|
|
|
|
for(TQDictIterator<Tellico::Data::Image> it(s_imageDict); it.current(); ++it) {
|
|
|
|
if(s_imageCache.find(it.currentKey()) == 0) {
|
|
|
|
set.add(it.currentKey());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return set;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ImageFactory::hasImage(const TQString& id_) {
|
|
|
|
return s_imageCache.find(id_, false) || s_imageDict.find(id_);
|
|
|
|
}
|
|
|
|
|
|
|
|
// the purpose here is to remove images from the dict if they're is on the disk somewhere,
|
|
|
|
// either in tempDir() or in dataDir(). The use for this is for calling pixmap() on an
|
|
|
|
// image too big to stay in the cache. Then it stays in the dict forever.
|
|
|
|
void ImageFactory::releaseImages() {
|
|
|
|
#ifdef RELEASE_IMAGES
|
|
|
|
if(s_imagesToRelease.isEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const TQStringList images = s_imagesToRelease.toList();
|
|
|
|
for(TQStringList::ConstIterator it = images.begin(); it != images.end(); ++it) {
|
|
|
|
s_imagesToRelease.remove(*it);
|
|
|
|
if(!s_imageDict.find(*it)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// myLog() << "ImageFactory::releaseImage() - id = " << *it << endl;
|
|
|
|
if(TQFile::exists(dataDir() + *it)) {
|
|
|
|
// myDebug() << "...exists in dataDir() - removing from dict" << endl;
|
|
|
|
s_imageDict.remove(*it);
|
|
|
|
} else if(TQFile::exists(tempDir() + *it)) {
|
|
|
|
// myDebug() << "...exists in tempDir() - removing from dict" << endl;
|
|
|
|
s_imageDict.remove(*it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void ImageFactory::setLocalDirectory(const KURL& url_) {
|
|
|
|
if(url_.isEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(!url_.isLocalFile()) {
|
|
|
|
myWarning() << "ImageFactory::setLocalDirectory() - Tellico can only save images to local disk" << endl;
|
|
|
|
myWarning() << "unable to save to " << url_ << endl;
|
|
|
|
} else {
|
|
|
|
s_localDir = url_.directory(false);
|
|
|
|
// could have already been set once
|
|
|
|
if(!url_.fileName().contains(TQString::tqfromLatin1("_files"))) {
|
|
|
|
s_localDir += url_.fileName().section('.', 0, 0) + TQString::tqfromLatin1("_files/");
|
|
|
|
}
|
|
|
|
myLog() << "ImageFactory::setLocalDirectory() - local dir = " << s_localDir << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef RELEASE_IMAGES
|