winix/core/image.cpp

604 lines
13 KiB
C++
Raw Normal View History

/*
* This file is a part of Winix
* and is not publicly distributed
*
* Copyright (c) 2010-2014, Tomasz Sowa
* All rights reserved.
*
*/
#include <ctime>
#include "image.h"
#include "utf8/utf8.h"
#include "log.h"
#include "system.h"
#include "plugin.h"
#include "lock.h"
namespace Winix
{
void Image::SetDb(Db * pdb)
{
db = pdb;
}
void Image::SetConfig(Config * pconfig)
{
config = pconfig;
}
void Image::SetSystem(System * psystem)
{
system = psystem;
}
// first thread (objects locked)
Image::Scale Image::GetImageScale(long dir_id)
{
Scale scale;
Mount * m = system->mounts.CalcMount(dir_id);
scale.cx = config->image_cx;
scale.cy = config->image_cy;
scale.aspect_mode = config->image_mode;
scale.quality = config->image_quality;
// reading width and height from the mount point (if exists)
int index = system->mounts.MountParImageSize();
if( m && m->param[index].defined && m->param[index].arg.size() == 2 )
{
scale.cx = Tol(m->param[index].arg[0]);
scale.cy = Tol(m->param[index].arg[1]);
}
// reading image mode from the mount point (if exists)
index = system->mounts.MountParImageMode();
if( m && m->param[index].defined && m->param[index].arg.size() == 1 )
scale.aspect_mode = Toi(m->param[index].arg[0]);
// reading image quality from the mount point (if exists)
index = system->mounts.MountParImageQuality();
if( m && m->param[index].defined && m->param[index].arg.size() == 1 )
scale.quality = Toi(m->param[index].arg[0]);
return scale;
}
// first thread (objects locked)
Image::Scale Image::GetThumbScale(long dir_id)
{
Scale scale;
Mount * m = system->mounts.CalcMount(dir_id);
scale.cx = config->thumb_cx;
scale.cy = config->thumb_cy;
scale.aspect_mode = config->thumb_mode;
scale.quality = config->thumb_quality;
// reading width and height from the mount point (if exists)
int index = system->mounts.MountParThumbSize();
if( m && m->param[index].defined && m->param[index].arg.size() == 2 )
{
scale.cx = Tol(m->param[index].arg[0]);
scale.cy = Tol(m->param[index].arg[1]);
}
// reading thumb mode from the mount point (if exists)
index = system->mounts.MountParThumbMode();
if( m && m->param[index].defined && m->param[index].arg.size() == 1 )
scale.aspect_mode = Toi(m->param[index].arg[0]);
// reading image quality from the mount point (if exists)
index = system->mounts.MountParThumbQuality();
if( m && m->param[index].defined && m->param[index].arg.size() == 1 )
scale.quality = Toi(m->param[index].arg[0]);
return scale;
}
// first thread (objects locked)
void Image::Resize(long file_id, size_t cx, size_t cy, int aspect_mode, int quality)
{
item_temp.type = WINIX_IMAGE_TYPE_RESIZE;
item_temp.file_id = file_id;
item_temp.cx = cx;
item_temp.cy = cy;
item_temp.aspect_mode = aspect_mode;
item_temp.quality = quality;
CheckParam(item_temp);
image_tab.insert(image_tab.end(), item_temp);
WakeUpThread();
}
// first thread (objects locked)
void Image::CreateThumb(long file_id, size_t thumb_cx, size_t thumb_cy, int aspect_mode, int quality)
{
item_temp.type = WINIX_IMAGE_TYPE_CREATE_THUMB;
item_temp.file_id = file_id;
item_temp.thumb_cx = thumb_cx;
item_temp.thumb_cy = thumb_cy;
item_temp.aspect_mode = aspect_mode;
item_temp.quality = quality;
CheckParam(item_temp);
image_tab.insert(image_tab.end(), item_temp);
WakeUpThread();
}
// first thread (objects locked)
void Image::Crop(long file_id, size_t xoffset, size_t yoffset, size_t cx, size_t cy, int quality)
{
item_temp.type = WINIX_IMAGE_TYPE_CROP;
item_temp.file_id = file_id;
item_temp.xoffset = xoffset;
item_temp.yoffset = yoffset;
item_temp.cx = cx;
item_temp.cy = cy;
item_temp.quality = quality;
CheckParam(item_temp);
image_tab.insert(image_tab.end(), item_temp);
WakeUpThread();
}
// first thread (objects locked)
void Image::CropThumb(long file_id, size_t xoffset, size_t yoffset, size_t cx, size_t cy, int quality)
{
item_temp.type = WINIX_IMAGE_TYPE_CROP_THUMB;
item_temp.file_id = file_id;
item_temp.xoffset = xoffset;
item_temp.yoffset = yoffset;
item_temp.cx = cx;
item_temp.cy = cy;
item_temp.quality = quality;
CheckParam(item_temp);
image_tab.insert(image_tab.end(), item_temp);
WakeUpThread();
}
// first thread (objects locked)
void Image::CropNewThumb(long file_id, size_t xoffset, size_t yoffset, size_t cx, size_t cy,
size_t thumb_cx, size_t thumb_cy, int aspect_mode, int quality)
{
item_temp.type = WINIX_IMAGE_TYPE_CREATE_CROP_NEW_THUMB;
item_temp.file_id = file_id;
item_temp.xoffset = xoffset;
item_temp.yoffset = yoffset;
item_temp.cx = cx;
item_temp.cy = cy;
item_temp.thumb_cx = thumb_cx;
item_temp.thumb_cy = thumb_cy;
item_temp.aspect_mode = aspect_mode;
item_temp.quality = quality;
CheckParam(item_temp);
image_tab.insert(image_tab.end(), item_temp);
WakeUpThread();
}
void Image::CheckParam(ImageItem & item)
{
SetMinMax(item.aspect_mode, 1, 7);
SetMinMax(item.quality, 0, 100);
SetMinMax(item.cx, 1, 30000);
SetMinMax(item.cy, 1, 30000);
SetMinMax(item.thumb_cx, 1, 30000);
SetMinMax(item.thumb_cy, 1, 30000);
SetMinMax(item.xoffset, 0, 30000);
SetMinMax(item.yoffset, 0, 30000);
}
// second thread (objects locked)
bool Image::SignalReceived()
{
return !image_tab.empty();
}
// second thread (objects not locked)
void Image::Do()
{
ImageTab::iterator i;
bool end;
Lock();
i = image_tab.begin();
Unlock();
do
{
class Lock lock_object(synchro);
if( i != image_tab.end() )
{
item_work = *i;
image_tab.erase(i++);
end = false;
}
else
{
end = true;
}
lock_object.Unlock();
if( !end )
CreateImage();
}
while( !end && !IsExitSignal() );
}
void Image::Add(const std::wstring & in, TextStream<std::string> & out)
{
PT::WideToUTF8(in, add_tempa);
out << add_tempa;
}
void Image::EscapePath(const std::string & path, TextStream<std::string> & out, bool clear_stream)
{
if( clear_stream )
out.Clear();
out << '"';
for(size_t i=0 ; i<path.size() ; ++i)
{
if( path[i] == '"' )
out << '\\';
if( path[i] != 0 )
out << path[i];
}
out << '\"';
}
/*
from: http://www.imagemagick.org/script/command-line-processing.php#geometry
scale% Height and width both scaled by specified percentage.
scale-x%xscale-y% Height and width individually scaled by specified percentages. (Only one % symbol needed.)
width Width given, height automagically selected to preserve aspect ratio.
xheight Height given, width automagically selected to preserve aspect ratio.
widthxheight Maximum values of height and width given, aspect ratio preserved.
widthxheight^ Minimum values of width and height given, aspect ratio preserved.
widthxheight! Width and height emphatically given, original aspect ratio ignored.
widthxheight> Change as per widthxheight but only if an image dimension exceeds a specified dimension.
widthxheight< Change dimensions only if both image dimensions exceed specified dimensions.
*/
void Image::SelectAspect(size_t cx, size_t cy)
{
switch( item_work.aspect_mode )
{
case WINIX_IMAGE_MODE_1:
command << cx;
break;
case WINIX_IMAGE_MODE_3:
command << cx << "x" << cy;
break;
case WINIX_IMAGE_MODE_4:
command << '"' << cx << "x" << cy << "^\"";
break;
case WINIX_IMAGE_MODE_5:
command << '"' << cx << "x" << cy << "!\"";
break;
case WINIX_IMAGE_MODE_6:
command << '"' << cx << "x" << cy << ">\"";
break;
case WINIX_IMAGE_MODE_7:
command << '"' << cx << "x" << cy << "<\"";
break;
case WINIX_IMAGE_MODE_2:
default:
command << "x" << cy;
break;
}
}
// second thread (objects locked)
bool Image::CreateInputFileName()
{
bool thumb = (item_work.type == WINIX_IMAGE_TYPE_CROP_THUMB);
if( thumb && !file_work.has_thumb )
{
log << log1 << "Image: file id: " << file_work.id << ", url: " << file_work.url
<< " doesn't have a thumbnail yet (skipping)" << logend;
return false;
}
if( system->MakeFilePath(file_work, src_path, thumb) )
{
PT::WideToUTF8(src_path, input_file_name);
return true;
}
else
{
log << log1 << "Image: cannot create a source path" << logend;
return false;
}
}
// second thread (objects locked)
void Image::CreateTmpFileName()
{
stream_tmp_path.Clear();
stream_tmp_path << config->upload_dir << L"/tmp/image_" << std::time(0);
PT::WideToUTF8(stream_tmp_path.Str(), tmp_file_name);
}
// second thread (objects are not locked)
bool Image::CreateCommand()
{
class Lock lock_object(synchro);
iq.SetAll(true, false);
iq.WhereId(item_work.file_id);
// the file could have been changed especially when there is a long queue of files
if( db->GetItem(file_work, iq) != WINIX_ERR_OK )
return false;
if( !CreateInputFileName() )
return false;
command.Clear();
Add(config->convert_cmd, command);
command << " ";
EscapePath(input_file_name, command, false);
command << " -quiet -quality " << item_work.quality;
if( item_work.type == WINIX_IMAGE_TYPE_RESIZE )
{
command << " -resize ";
SelectAspect(item_work.cx, item_work.cy);
}
else
if( item_work.type == WINIX_IMAGE_TYPE_CREATE_THUMB )
{
command << " -strip -thumbnail ";
SelectAspect(item_work.thumb_cx, item_work.thumb_cy);
}
else
if( item_work.type == WINIX_IMAGE_TYPE_CROP )
{
command << " -crop " << item_work.cx << "x" << item_work.cy
<< "+" << item_work.xoffset << "+" << item_work.yoffset << " +repage ";
}
else
if( item_work.type == WINIX_IMAGE_TYPE_CROP_THUMB )
{
command << " -strip -crop " << item_work.cx << "x" << item_work.cy
<< "+" << item_work.xoffset << "+" << item_work.yoffset
<< " +repage ";
}
else
if( item_work.type == WINIX_IMAGE_TYPE_CREATE_CROP_NEW_THUMB )
{
command << " -strip -crop " << item_work.cx << "x" << item_work.cy
<< "+" << item_work.xoffset << "+" << item_work.yoffset
<< " +repage -thumbnail ";
SelectAspect(item_work.thumb_cx, item_work.thumb_cy);
}
CreateTmpFileName();
command << " ";
EscapePath(tmp_file_name, command, false);
log << log4 << "Image: running: " << command.Str() << logend;
return true;
}
// second thread (objects are locked)
void Image::ImageSavedCorrectly()
{
if( item_work.type == WINIX_IMAGE_TYPE_CREATE_THUMB )
{
if( !file_work.has_thumb )
{
file_work.has_thumb = true;
db->EditHasThumbById(true, file_work.id);
}
log << log3 << "Image: generated a thumbnail: " << dst_path << logend;
plugin.Call((Session*)0, WINIX_CREATED_THUMB, &file_work);
}
else
if( item_work.type == WINIX_IMAGE_TYPE_RESIZE )
{
log << log3 << "Image: image resized: " << dst_path << logend;
plugin.Call((Session*)0, WINIX_IMAGE_RESIZED, &file_work);
}
else
if( item_work.type == WINIX_IMAGE_TYPE_CROP )
{
log << log3 << "Image: image cropped: " << dst_path << logend;
// !! IMPROVE ME add a correct message
//plugin.Call((Session*)0, WINIX_IMAGE_RESIZED, &file_work);
}
else
if( item_work.type == WINIX_IMAGE_TYPE_CROP_THUMB )
{
log << log3 << "Image: image thumbnail cropped: " << dst_path << logend;
// !! IMPROVE ME add a correct message
//plugin.Call((Session*)0, WINIX_IMAGE_RESIZED, &file_work);
}
else
if( item_work.type == WINIX_IMAGE_TYPE_CREATE_CROP_NEW_THUMB )
{
log << log3 << "Image: a new thumbnail from an original image was cropped: " << dst_path << logend;
// !! IMPROVE ME add a correct message
//plugin.Call((Session*)0, WINIX_IMAGE_RESIZED, &file_work);
}
}
// second thread (objects are not locked)
void Image::SaveImage()
{
class Lock lock_object(synchro);
// the file could have been changed especially when creating the image lasted too long
iq.SetAll(true, false);
iq.WhereId(item_work.file_id);
if( db->GetItem(file_work, iq) == WINIX_ERR_OK )
{
bool thumb = (item_work.type == WINIX_IMAGE_TYPE_CREATE_THUMB ||
item_work.type == WINIX_IMAGE_TYPE_CROP_THUMB ||
item_work.type == WINIX_IMAGE_TYPE_CREATE_CROP_NEW_THUMB );
if( system->MakeFilePath(file_work, dst_path, thumb, true, config->upload_dirs_chmod) )
{
if( RenameFile(stream_tmp_path.Str(), dst_path) )
{
ImageSavedCorrectly();
}
else
{
log << log1 << "Image: cannot move a temporary file: " << stream_tmp_path.Str()
<< ", to: " << dst_path << logend;
Winix::RemoveFile(stream_tmp_path.Str());
}
}
else
{
log << log1 << "Image: cannot create a destination path" << logend;
}
}
}
// second thread (objects are not locked)
void Image::CreateImage()
{
if( CreateCommand() )
{
int res = std::system(command.CStr());
if( res == 0 )
{
SaveImage();
}
else
{
class Lock lock_object(synchro);
log << log3 << "Image: some problems with creating an image"
<< ", 'convert' process returned: " << res << logend;
}
}
}
// second thread (objects are not locked)
// !! there is a problem with GIF files
// Bus error (core dumped)
/*
#include "wand/MagickWand.h"
// compiler options:
// include: -I/usr/local/include/ImageMagick
// link with: `MagickWand-config --ldflags --libs`
void Image::CreateThumbnail()
{
PT::WideToUTF8(item_work.source, sourcea);
PT::WideToUTF8(item_work.dst, dsta);
MagickWandGenesis();
MagickWand * wand = NewMagickWand();
if( MagickReadImage(wand, sourcea.c_str()) )
{
MagickThumbnailImage(wand, item_work.cx, item_work.cy);
if( MagickWriteImage(wand, dsta.c_str()) )
{
Lock();
log << log3 << "Image: created a thumbnail: " << dsta << logend;
Unlock();
}
}
DestroyMagickWand(wand);
MagickWandTerminus();
}
*/
} // namespace Winix