2011-09-13 08:08:34 +02:00
|
|
|
/*
|
|
|
|
* This file is a part of Winix
|
|
|
|
* and is not publicly distributed
|
|
|
|
*
|
2012-01-12 03:24:08 +01:00
|
|
|
* Copyright (c) 2010-2012, Tomasz Sowa
|
2011-09-13 08:08:34 +02:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <ctime>
|
|
|
|
#include "image.h"
|
2012-01-12 03:24:08 +01:00
|
|
|
#include "utf8/utf8.h"
|
2011-09-13 08:08:34 +02:00
|
|
|
#include "log.h"
|
|
|
|
#include "system.h"
|
|
|
|
#include "plugin.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
void Image::Resize(const Item & item, size_t cx, size_t cy, int aspect_mode, int quality)
|
|
|
|
{
|
|
|
|
item_temp.type = WINIX_IMAGE_TYPE_RESIZE;
|
|
|
|
item_temp.file = item;
|
|
|
|
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(const Item & item, size_t cx, size_t cy, int aspect_mode, int quality)
|
|
|
|
{
|
|
|
|
item_temp.type = WINIX_IMAGE_TYPE_CREATE_THUMB;
|
|
|
|
item_temp.file = item;
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Image::CheckParam(ImageItem & item)
|
|
|
|
{
|
|
|
|
if( item.aspect_mode < 1 )
|
|
|
|
item.aspect_mode = 1;
|
|
|
|
|
|
|
|
if( item.aspect_mode > 7 )
|
|
|
|
item.aspect_mode = 7;
|
|
|
|
|
|
|
|
if( item.quality < 0 )
|
|
|
|
item.quality = 0;
|
|
|
|
|
|
|
|
if( item.quality > 100 )
|
|
|
|
item.quality = 100;
|
|
|
|
|
|
|
|
if( item_temp.cx < 5 )
|
|
|
|
item_temp.cx = 5;
|
|
|
|
|
|
|
|
if( item_temp.cy < 5 )
|
|
|
|
item_temp.cy = 5;
|
|
|
|
|
|
|
|
if( item_temp.cx > 10000 )
|
|
|
|
item_temp.cx = 10000;
|
|
|
|
|
|
|
|
if( item_temp.cy > 10000 )
|
|
|
|
item_temp.cy = 10000;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
{
|
|
|
|
Lock();
|
|
|
|
|
|
|
|
if( i != image_tab.end() )
|
|
|
|
{
|
|
|
|
item_work = *i;
|
|
|
|
image_tab.erase(i++);
|
|
|
|
end = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
end = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Unlock();
|
|
|
|
|
|
|
|
if( !end )
|
|
|
|
CreateImage();
|
|
|
|
|
|
|
|
}
|
|
|
|
while( !end && !IsExitSignal() );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Image::Add(const std::wstring & in, TextStream<std::string> & out)
|
|
|
|
{
|
2012-01-12 03:24:08 +01:00
|
|
|
PT::WideToUTF8(in, add_tempa);
|
2011-09-13 08:08:34 +02:00
|
|
|
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 << '\\';
|
|
|
|
|
|
|
|
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()
|
|
|
|
{
|
|
|
|
switch( item_work.aspect_mode )
|
|
|
|
{
|
|
|
|
case WINIX_IMAGE_MODE_1:
|
|
|
|
command << item_work.cx;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WINIX_IMAGE_MODE_3:
|
|
|
|
command << item_work.cx << "x" << item_work.cy;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WINIX_IMAGE_MODE_4:
|
|
|
|
command << '"' << item_work.cx << "x" << item_work.cy << "^\"";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WINIX_IMAGE_MODE_5:
|
|
|
|
command << '"' << item_work.cx << "x" << item_work.cy << "!\"";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WINIX_IMAGE_MODE_6:
|
|
|
|
command << '"' << item_work.cx << "x" << item_work.cy << ">\"";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WINIX_IMAGE_MODE_7:
|
|
|
|
command << '"' << item_work.cx << "x" << item_work.cy << "<\"";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WINIX_IMAGE_MODE_2:
|
|
|
|
default:
|
|
|
|
command << "x" << item_work.cy;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// second thread (objects locked)
|
|
|
|
bool Image::CreateInputFileName()
|
|
|
|
{
|
|
|
|
if( system->MakeFilePath(item_work.file, src_path) )
|
|
|
|
{
|
2012-01-12 03:24:08 +01:00
|
|
|
PT::WideToUTF8(src_path, input_file_name);
|
2011-09-13 08:08:34 +02:00
|
|
|
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);
|
2012-01-12 03:24:08 +01:00
|
|
|
PT::WideToUTF8(stream_tmp_path.Str(), tmp_file_name);
|
2011-09-13 08:08:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// second thread (objects are not locked)
|
|
|
|
bool Image::CreateCommand()
|
|
|
|
{
|
|
|
|
Lock();
|
|
|
|
|
|
|
|
iq.SetAll(true, false);
|
|
|
|
iq.WhereId(item_work.file.id);
|
|
|
|
|
|
|
|
// !! skoro teraz i tak wczytujemy caly obiekt
|
|
|
|
// to teraz w kolejce wystarczy zapamietywac tylko samo item.id (a nie caly obiekt item)
|
|
|
|
|
|
|
|
// the file could have been changed especially when there is a long queue of files
|
|
|
|
if( db->GetItem(item_work.file, iq) != WINIX_ERR_OK )
|
|
|
|
{
|
|
|
|
Unlock();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !CreateInputFileName() )
|
|
|
|
{
|
|
|
|
Unlock();
|
|
|
|
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 ";
|
|
|
|
else
|
|
|
|
command << " -strip -thumbnail ";
|
|
|
|
|
|
|
|
SelectAspect();
|
|
|
|
CreateTmpFileName();
|
|
|
|
|
|
|
|
command << " ";
|
|
|
|
EscapePath(tmp_file_name, command, false);
|
|
|
|
|
|
|
|
log << log4 << "Image: running: " << command.Str() << logend;
|
|
|
|
|
|
|
|
Unlock();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// second thread (objects are not locked)
|
|
|
|
void Image::SaveImage()
|
|
|
|
{
|
|
|
|
bool moved = false;
|
|
|
|
|
|
|
|
Lock();
|
|
|
|
|
|
|
|
// 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(item_work.file, iq) == WINIX_ERR_OK )
|
|
|
|
{
|
|
|
|
bool thumb = (item_work.type == WINIX_IMAGE_TYPE_CREATE_THUMB);
|
|
|
|
|
|
|
|
if( system->MakeFilePath(item_work.file, dst_path, thumb, true, config->upload_dirs_chmod) )
|
|
|
|
{
|
|
|
|
if( RenameFile(stream_tmp_path.Str(), dst_path) )
|
|
|
|
{
|
|
|
|
if( thumb )
|
|
|
|
{
|
|
|
|
item_work.file.has_thumb = true;
|
|
|
|
db->EditHasThumbById(true, item_work.file.id);
|
|
|
|
log << log3 << "Image: generated a thumbnail: " << dst_path << logend;
|
2012-03-17 06:11:23 +01:00
|
|
|
plugin.Call((Session*)0, WINIX_CREATED_THUMB, &item_work.file);
|
2011-09-13 08:08:34 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
log << log3 << "Image: image resized: " << dst_path << logend;
|
2012-03-17 06:11:23 +01:00
|
|
|
plugin.Call((Session*)0, WINIX_IMAGE_RESIZED, &item_work.file);
|
2011-09-13 08:08:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
moved = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
log << log1 << "Image: cannot move a temporary file: " << stream_tmp_path.Str() << ", to: " << dst_path << logend;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
log << log1 << "Image: cannot create a destination path" << logend;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !moved )
|
|
|
|
::RemoveFile(stream_tmp_path.Str());
|
|
|
|
|
|
|
|
Unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// second thread (objects are not locked)
|
|
|
|
void Image::CreateImage()
|
|
|
|
{
|
|
|
|
if( !CreateCommand() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
int res = std::system(command.CStr());
|
|
|
|
|
|
|
|
if( res == 0 )
|
|
|
|
{
|
|
|
|
SaveImage();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Lock();
|
|
|
|
log << log3 << "Image: some problems with creating an image"
|
|
|
|
<< ", 'convert' process returned: " << res << logend;
|
|
|
|
Unlock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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()
|
|
|
|
{
|
2012-01-12 03:24:08 +01:00
|
|
|
PT::WideToUTF8(item_work.source, sourcea);
|
|
|
|
PT::WideToUTF8(item_work.dst, dsta);
|
2011-09-13 08:08:34 +02:00
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
|