From c6adfe35f2b46a74db7c40ed52706e714fea7a71 Mon Sep 17 00:00:00 2001 From: spillerrec Date: Wed, 19 Jul 2017 21:44:56 +0200 Subject: [PATCH] ADD: Do not modify original image as much as possible ADD: Point to a subarea of Image source, instead of actively cropping it, to avoid extra copying CHANGE: Removing transparency and alpha is moved out of Image, as it is not mask related --- cgCompress.pro | 3 ++ src/FileUtils.cpp | 21 ++++++++++++++ src/FileUtils.hpp | 3 ++ src/Image.cpp | 72 +++++++++++++++++++--------------------------- src/Image.hpp | 32 +++++++++------------ src/MultiImage.hpp | 6 ---- src/SubQImage.hpp | 61 +++++++++++++++++++++++++++++++++++++++ src/main.cpp | 20 ++++++------- 8 files changed, 141 insertions(+), 77 deletions(-) create mode 100644 src/SubQImage.hpp diff --git a/cgCompress.pro b/cgCompress.pro index 2e96610..0daf472 100644 --- a/cgCompress.pro +++ b/cgCompress.pro @@ -20,6 +20,9 @@ CONFIG += c++14 # Generate both debug and release on Linux CONFIG += debug_and_release +#QMAKE_CXXFLAGS_RELEASE -= -O2 +QMAKE_CXXFLAGS_DEBUG += -O3 + # Position of binaries and build files Release:DESTDIR = release Release:UI_DIR = release/.ui diff --git a/src/FileUtils.cpp b/src/FileUtils.cpp index fe53361..da759ef 100644 --- a/src/FileUtils.cpp +++ b/src/FileUtils.cpp @@ -167,3 +167,24 @@ QStringList expandFolders( QStringList paths ){ return files; } + + +/** \return This image where all transparent pixels are set to discard_color */ +QImage discardTransparent( QImage img, QRgb discard_color ){ + QImage output( img.convertToFormat(QImage::Format_ARGB32) ); + + auto size = output.size(); + for( int iy=0; iy Image::segment() const{ } */ -/** \return This image where all transparent pixels are set to #000000 */ -Image Image::discardTransparent() const{ - QImage output( img.convertToFormat(QImage::Format_ARGB32) ); - - auto size = output.size(); - for( int iy=0; iy Image::diff_segment( Image diff ) const{ * \param [in] on_top Image to paint * \return The combined image */ Image Image::combine( Image on_top ) const{ - QPoint tl{ min( pos.x(), on_top.pos.x() ), min( pos.y(), on_top.pos.y() ) }; - int width = max( pos.x()+img.width(), on_top.pos.x()+on_top.img.width() ) - tl.x(); - int height = max( pos.y()+img.height(), on_top.pos.y()+on_top.img.height() ) - tl.y(); + QPoint tl{ min( get_pos().x(), on_top.get_pos().x() ), min( get_pos().y(), on_top.get_pos().y() ) }; + int width = max( get_pos().x()+img.width(), on_top.get_pos().x()+on_top.img.width() ) - tl.x(); + int height = max( get_pos().y()+img.height(), on_top.get_pos().y()+on_top.img.height() ) - tl.y(); QImage output( width, height, QImage::Format_ARGB32 ); output.fill( 0 ); QPainter painter( &output ); - painter.drawImage( pos-tl, img ); - painter.drawImage( on_top.pos-tl, on_top.img ); + painter.drawImage( get_pos()-tl, qimg() ); + painter.drawImage( on_top.get_pos()-tl, on_top.qimg() ); return Image( tl, output ); //TODO: } @@ -185,8 +174,8 @@ Image Image::difference( Image input ) const{ auto w = img.width(); auto mask = make_mask( img.size() ); for( int iy=0; iy best.save_compressed_size( format ) ) - return best.remove_transparent(); + return best; else - return copy.remove_transparent(); + return copy; } int Image::compressed_size( Format format, Format::Precision p ) const{ @@ -414,18 +402,18 @@ int Image::compressed_size( Format format, Format::Precision p ) const{ return saved_data.size(); //Calculate the gradient - return format.file_size( remove_transparent().img, p ); + return format.file_size( remove_transparent(), p ); } int Image::estimate_compressed_size( Format format ) const{ if( mask.isNull() ) - return format.file_size( remove_transparent().qimg(), Format::LOW ); + return format.file_size( remove_transparent(), Format::LOW ); int diffs = 0; auto w = img.width(); for( int iy=0; iy #include "Format.hpp" +#include "SubQImage.hpp" class Image { private: - QPoint pos; - QImage img; + SubQImage img; QImage mask; QByteArray saved_data; @@ -44,8 +44,8 @@ class Image { Image( QString path ) : Image( QImage(path) ) { } private: - Image( QPoint pos, QImage img, QImage mask ) : pos(pos), img(img), mask(mask) { } - Image newMask( QImage mask ) const{ return Image( pos, img, mask ); } + Image( SubQImage img, QImage mask ) : img(img), mask(mask) { } + Image newMask( QImage mask ) const{ return Image( img, mask ); } /* QList segment() const; QList diff_segment( Image diff ) const;*/ @@ -61,26 +61,22 @@ class Image { bool is_valid() const{ return !img.size().isEmpty(); } /** \return The offset of the image */ - QPoint get_pos() const{ return pos; } + QPoint get_pos() const{ return img.offset(); } /** \return The image data */ - QImage qimg() const{ return img; } - - /** Remove alpha */ - void removeAlpha() - { img = img.convertToFormat(QImage::Format_RGB32).convertToFormat(QImage::Format_ARGB32); } + QImage qimg() const{ return img.get(); } /** Save the image to the file system * \param [in] path The location on the file system * \param [in] format The format used for compression * \return true if successful */ - bool save( QString path, Format format ) const{ return format.save( img, path ); } + bool save( QString path, Format format ) const{ return format.save( remove_transparent(), path ); } /** Save the image to a memory buffer * \param [in] format The compression format to use * \return The image in compressed form */ QByteArray to_byte_array( Format format ) const - { return saved_data.size() > 0 ? saved_data : format.to_byte_array( img ); } + { return saved_data.size() > 0 ? saved_data : format.to_byte_array( remove_transparent() ); } Image resize( int size ) const; @@ -94,7 +90,7 @@ class Image { QSize newSize( std::min( x+width, x+mask.width() ) - x , std::min( y+height, y+mask.height() ) - y ); auto newMask = newSize.isNull() ? QImage() : mask.copy( x,y, newSize.width(), newSize.height() ); - return Image( pos+QPoint(x,y), img.copy( x,y, newSize.width(), newSize.height() ), newMask ); + return Image( img.copy( {x,y}, newSize ), newMask ); } Image combine( Image on_top ) const; @@ -108,15 +104,14 @@ class Image { int compressed_size( Format format, Format::Precision p=Format::HIGH ) const; int save_compressed_size( Format format ){ - saved_data = remove_transparent().to_byte_array( format ); + saved_data = to_byte_array( format ); return saved_data.size(); } Image difference( Image img ) const; Image remove_area( Image img ) const; Image clean_alpha( int kernel_size, int threshold ) const; - Image remove_transparent() const; - Image discardTransparent() const; + QImage remove_transparent() const; Image auto_crop() const; Image optimize_filesize( Format format ) const; @@ -124,9 +119,8 @@ class Image { /** \param [in] other Image to compare against * \return true if images are interchangeable */ - bool operator==( const Image& other ) const{ - return pos == other.pos && img == other.img; - } + bool operator==( const Image& other ) const + { return img == other.img && mask == other.mask; } bool mustKeepAlpha() const; static Image fromTransparent( QImage img ); diff --git a/src/MultiImage.hpp b/src/MultiImage.hpp index 9f65efe..316ceb8 100644 --- a/src/MultiImage.hpp +++ b/src/MultiImage.hpp @@ -47,12 +47,6 @@ class MultiImage { bool optimize2( QString name ) const; bool validate( QString file ) const; - - /** Remove alpha from input images */ - void removeAlpha() { for( auto& original : originals ) original.removeAlpha(); } - - /** Remove alpha from input images */ - void discardTransparent() { for( auto& original : originals ) original = original.discardTransparent(); } }; #endif diff --git a/src/SubQImage.hpp b/src/SubQImage.hpp new file mode 100644 index 0000000..168bf4d --- /dev/null +++ b/src/SubQImage.hpp @@ -0,0 +1,61 @@ +/* + This file is part of cgCompress. + + cgCompress 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 3 of the License, or + (at your option) any later version. + + cgCompress 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. + + You should have received a copy of the GNU General Public License + along with cgCompress. If not, see . +*/ + +#ifndef SUB_QIMAGE_HPP +#define SUB_QIMAGE_HPP + +#include + + +/** Provides ReadOnly access to a region of a QImage without copying. + * row() and rowIndex() provides pixel access which is offset and casted correctly */ +class SubQImage{ + private: + QImage img; + QPoint pos; + QSize subsize; + + auto scanLine( int iy ) const + { return img.constScanLine( iy + pos.y() ); } + + SubQImage( QImage img, QPoint pos, QSize subsize ) + : img(img), pos(pos), subsize(subsize) {} + public: + SubQImage( QImage img, QPoint pos={0,0} ) + : img(img), pos(pos), subsize(img.size()) { } + + auto offset() const{ return pos; } + + auto size() const{ return subsize; } + auto width() const{ return size().width(); } + auto height() const{ return size().height(); } + + auto rowIndex( int iy ) const{ return scanLine( iy ) + pos.x(); } + auto row( int iy ) const{ return (const QRgb*)(scanLine( iy )) + pos.x(); } + + auto get() const + { return img.copy( pos.x(), pos.y(), width(), height() ); } + + SubQImage copy( QPoint pos, QSize size ) const + { return { img, offset() + pos, size }; } + + bool operator==( const SubQImage& other ) const + { return img == other.img && pos == other.pos && subsize == other.subsize; } +}; + +#endif + diff --git a/src/main.cpp b/src/main.cpp index 22468a3..9970601 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -123,14 +123,14 @@ int main( int argc, char* argv[] ){ Format format = get_format( options ); - auto doMultiImg = [&]( MultiImage& multi_img, QString output_path ){ + auto convert_img = [&]( QImage img ){ if( options.contains( "--noalpha" ) ) - multi_img.removeAlpha(); + img = withoutAlpha( img ); if( options.contains( "--discard-transparent" ) ) - multi_img.discardTransparent(); + img = discardTransparent( img ); - return optimizeImage( multi_img, output_path ); + return img; }; if( options.contains( "--help" ) ) print_help(); @@ -150,18 +150,18 @@ int main( int argc, char* argv[] ){ MultiImage multi_img( format ); for( auto image : images ) - multi_img.append( Image( image.second ) ); + multi_img.append( Image( convert_img( {image.second} ) ) ); - doMultiImg( multi_img, name ); + optimizeImage( multi_img, name ); } } else if( options.contains( "--combined" ) ){ MultiImage multi_img( format ); for( auto file : files ) for( auto image : extract_files( file ) ) - multi_img.append( Image( image.second ) ); + multi_img.append( Image( convert_img( image.second ) ) ); - doMultiImg( multi_img, QFileInfo(files[0]).completeBaseName() ); + optimizeImage( multi_img, QFileInfo(files[0]).completeBaseName() ); } else{ files = expandFolders( files ); @@ -181,7 +181,7 @@ int main( int argc, char* argv[] ){ QImage last; for( int j=start; j