/* * This file is a part of Winix * and is distributed under the 2-Clause BSD licence. * Author: Tomasz Sowa */ /* * Copyright (c) 2010-2021, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ #include #include "image.h" #include "utf8/utf8.h" #include "system.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 { Winix::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 & out) { pt::wide_to_utf8(in, add_tempa); out << add_tempa; } void Image::EscapePath(const std::string & path, TextStream & out, bool clear_stream) { if( clear_stream ) out.Clear(); out << '"'; for(size_t i=0 ; i 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.item_content.file_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::wide_to_utf8(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::wide_to_utf8(stream_tmp_path.Str(), tmp_file_name); } // second thread (objects are not locked) bool Image::CreateCommand() { Winix::Lock lock_object(synchro); // iq.SetAll(true, false); // iq.WhereId(item_work.file_id); morm::Finder finder(model_connector); file_work = finder.select().where().eq(L"id", item_work.file_id).get(); // the file could have been changed especially when there is a long queue of files if( !file_work.found() ) 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.item_content.file_has_thumb ) { file_work.item_content.file_has_thumb = true; file_work.propagate_connector(); file_work.item_content.update(false); //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() { Winix::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); morm::Finder finder(model_connector); file_work = finder.select().where().eq(L"id", item_work.file_id).get(); // the file could have been changed especially when there is a long queue of files if( !file_work.found() ) return; //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, config->upload_group_int) ) { if( RenameFile(stream_tmp_path.Str(), dst_path) ) { // it doesn't matter for us if there is an error when chmod/chown on a file // the admin (root) will correct it SetPriv(dst_path, config->upload_files_chmod, config->upload_group_int); 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 { log << log3 << "Image: some problems with creating an image" << ", 'convert' process returned: " << res << logend; } } log << logsave; } // 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::wide_to_utf8(item_work.source, sourcea); pt::wide_to_utf8(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