diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -38,6 +38,7 @@ set(kis_low_memory_benchmark_SRCS kis_low_memory_benchmark.cpp) set(kis_filter_selections_benchmark_SRCS kis_filter_selections_benchmark.cpp) set(kis_composition_benchmark_SRCS kis_composition_benchmark.cpp) +set(kis_thumbnail_benchmark_SRCS kis_thumbnail_benchmark.cpp) krita_add_benchmark(KisDatamanagerBenchmark TESTNAME krita-benchmarks-KisDataManager ${kis_datamanager_benchmark_SRCS}) krita_add_benchmark(KisHLineIteratorBenchmark TESTNAME krita-benchmarks-KisHLineIterator ${kis_hiterator_benchmark_SRCS}) @@ -56,6 +57,7 @@ krita_add_benchmark(KisLowMemoryBenchmark TESTNAME krita-benchmarks-KisLowMemory ${kis_low_memory_benchmark_SRCS}) krita_add_benchmark(KisFilterSelectionsBenchmark TESTNAME krita-image-KisFilterSelectionsBenchmark ${kis_filter_selections_benchmark_SRCS}) krita_add_benchmark(KisCompositionBenchmark TESTNAME krita-benchmarks-KisComposition ${kis_composition_benchmark_SRCS}) +krita_add_benchmark(KisThumbnailBenchmark TESTNAME krita-benchmarks-KisThumbnail ${kis_thumbnail_benchmark_SRCS}) target_link_libraries(KisDatamanagerBenchmark kritaimage Qt5::Test) target_link_libraries(KisHLineIteratorBenchmark kritaimage Qt5::Test) @@ -77,3 +79,5 @@ set_property(TARGET KisCompositionBenchmark APPEND PROPERTY COMPILE_OPTIONS "${Vc_ARCHITECTURE_FLAGS}") endif() target_link_libraries(KisMaskGeneratorBenchmark kritaimage Qt5::Test) +target_link_libraries(KisThumbnailBenchmark kritaimage Qt5::Test) + diff --git a/benchmarks/kis_thumbnail_benchmark.h b/benchmarks/kis_thumbnail_benchmark.h new file mode 100644 --- /dev/null +++ b/benchmarks/kis_thumbnail_benchmark.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2016 Eugene Ingerman geneing at gmail dot com + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KIS_THUMBNAIL_BENCHMARK_H +#define KIS_THUMBNAIL_BENCHMARK_H + +#include +#include "kis_paint_device.h" + +class KoColor; +class KoColorSpace; + +class KisThumbnailBenchmark : public QObject +{ + Q_OBJECT + +private: + const KoColorSpace * m_colorSpace; + KisPaintDeviceSP m_dev; + QVector m_thumbnails; + QSize m_thumbnailSizeLimit; + int m_oversampleRatio; + int m_skipCount; + +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + + void benchmarkCreateThumbnail(); + void benchmarkCreateThumbnailCached(); + void benchmarkCreateThumbnailHiQ(); + + void benchmarkCreateThumbnailHiQcreateThumbOversample2x(); + void benchmarkCreateThumbnailHiQcreateThumbOversample3x(); + void benchmarkCreateThumbnailHiQcreateThumbOversample4x(); + +}; + + +#endif diff --git a/benchmarks/kis_thumbnail_benchmark.cpp b/benchmarks/kis_thumbnail_benchmark.cpp new file mode 100644 --- /dev/null +++ b/benchmarks/kis_thumbnail_benchmark.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2016 Eugene Ingerman geneing at gmail dot com + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kis_thumbnail_benchmark.h" +#include "kis_benchmark_values.h" + +#include +#include +#include "kis_iterator_ng.h" + +#include "kis_paint_device.h" +#include "KoColorSpace.h" +#include "KoColorSpaceRegistry.h" +#include "KoCompositeOpRegistry.h" +#include "KoColor.h" + +#include "kis_image.h" +#include "kis_painter.h" +#include "kis_types.h" +#include "kis_sequential_iterator.h" +#include "kis_transform_worker.h" + + +#include + +#define SAVE_OUTPUT +const int THUMBNAIL_WIDTH = 640; +const int THUMBNAIL_HEIGHT = 640; +const int IMAGE_WIDTH = 8000; +const int IMAGE_HEIGHT = 6000; +const int OVERSAMPLE = 4; + +void KisThumbnailBenchmark::initTestCase() +{ + m_colorSpace = KoColorSpaceRegistry::instance()->rgb8(); + + m_dev = new KisPaintDevice(m_colorSpace); + KoColor color(m_colorSpace); + color.fromQColor(Qt::white); + + m_dev->clear(); + m_dev->fill(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, color.data()); + + color.fromQColor(Qt::black); + + KisPainter painter(m_dev); + painter.setPaintColor(color); + float radius = std::min(IMAGE_WIDTH, IMAGE_HEIGHT); + const float angle = 2 * 3.1415926 / 360.; + const float endWidth = 30; + + for (float i = 0; i < 90; i += 5) { + painter.drawThickLine(QPointF(0, 0), QPointF(radius * std::sin(angle * i), radius * std::cos(angle * i)), 1, endWidth); + painter.drawThickLine(QPointF(IMAGE_WIDTH, IMAGE_HEIGHT), + QPointF(IMAGE_WIDTH - radius * std::sin(angle * i), IMAGE_HEIGHT - radius * std::cos(angle * i)), 1, endWidth); + } +#ifdef SAVE_OUTPUT + m_dev->convertToQImage(m_colorSpace->profile()).save("ThumbFullImage.png"); +#endif +} + +void KisThumbnailBenchmark::cleanupTestCase() +{ +} + +void KisThumbnailBenchmark::benchmarkCreateThumbnail() +{ + QImage image; + + QBENCHMARK{ + image = m_dev->createThumbnail(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, QRect() ); + //m_dev->setDirty(); + } + + image.save("createThumbnail.png"); +} + +void KisThumbnailBenchmark::benchmarkCreateThumbnailCached() +{ + QImage image; + + QBENCHMARK{ + image = m_dev->createThumbnail(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, 2. ); + } +} + +void KisThumbnailBenchmark::benchmarkCreateThumbnailHiQ() +{ + QImage image; + + QBENCHMARK{ + image = m_dev->createThumbnail(OVERSAMPLE * THUMBNAIL_WIDTH, OVERSAMPLE * THUMBNAIL_HEIGHT); + image = image.scaled(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, Qt::KeepAspectRatio, Qt::SmoothTransformation); + m_dev->setDirty(); + } + + image.save("createThumbnailHiQ.png"); +} + +void KisThumbnailBenchmark::benchmarkCreateThumbnailHiQcreateThumbOversample2x() +{ + QImage image; + + QBENCHMARK{ + image = m_dev->createThumbnail(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, QRect(), 2, + KoColorConversionTransformation::internalRenderingIntent(), + KoColorConversionTransformation::internalConversionFlags()); + m_dev->setDirty(); + } + + image.save("createThumbnailHiQcreateThumbOversample2x.png"); +} + +void KisThumbnailBenchmark::benchmarkCreateThumbnailHiQcreateThumbOversample3x() +{ + QImage image; + + QBENCHMARK{ + image = m_dev->createThumbnail(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, QRect(), 3, + KoColorConversionTransformation::internalRenderingIntent(), + KoColorConversionTransformation::internalConversionFlags()); + m_dev->setDirty(); + } + + image.save("createThumbnailHiQcreateThumbOversample3x.png"); +} + +void KisThumbnailBenchmark::benchmarkCreateThumbnailHiQcreateThumbOversample4x() +{ + QImage image; + + QBENCHMARK{ + image = m_dev->createThumbnail(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, QRect(), 4, + KoColorConversionTransformation::internalRenderingIntent(), + KoColorConversionTransformation::internalConversionFlags()); + m_dev->setDirty(); + } + + image.save("createThumbnailHiQcreateThumbOversample4x.png"); +} + + +QTEST_MAIN(KisThumbnailBenchmark) diff --git a/libs/image/kis_layer.cc b/libs/image/kis_layer.cc --- a/libs/image/kis_layer.cc +++ b/libs/image/kis_layer.cc @@ -788,7 +788,7 @@ KisPaintDeviceSP originalDevice = original(); return originalDevice ? - originalDevice->createThumbnail(w, h, + originalDevice->createThumbnail(w, h, 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) : QImage(); } diff --git a/libs/image/kis_mask.cc b/libs/image/kis_mask.cc --- a/libs/image/kis_mask.cc +++ b/libs/image/kis_mask.cc @@ -341,7 +341,7 @@ selection() ? selection()->projection() : 0; return originalDevice ? - originalDevice->createThumbnail(w, h, + originalDevice->createThumbnail(w, h, 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) : QImage(); } diff --git a/libs/image/kis_paint_device.h b/libs/image/kis_paint_device.h --- a/libs/image/kis_paint_device.h +++ b/libs/image/kis_paint_device.h @@ -526,7 +526,7 @@ * @param rect: only this rect will be used for the thumbnail * */ - KisPaintDeviceSP createThumbnailDevice(qint32 w, qint32 h, QRect rect = QRect()) const; + KisPaintDeviceSP createThumbnailDevice(qint32 w, qint32 h, QRect rect = QRect(), qreal oversample=1.) const; /** * Creates a thumbnail of the paint device, retaining the aspect ratio. @@ -536,15 +536,16 @@ * @param maxw: maximum width * @param maxh: maximum height * @param rect: only this rect will be used for the thumbnail + * @param oversample: ratio used for antialiasing */ - QImage createThumbnail(qint32 maxw, qint32 maxh, QRect rect, + QImage createThumbnail(qint32 maxw, qint32 maxh, QRect rect, qreal oversample=1, KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::internalConversionFlags()); /** * Cached version of createThumbnail(qint32 maxw, qint32 maxh, const KisSelection *selection, QRect rect) */ - QImage createThumbnail(qint32 maxw, qint32 maxh, + QImage createThumbnail(qint32 maxw, qint32 maxh, qreal oversample=1, KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::internalConversionFlags()); diff --git a/libs/image/kis_paint_device.cc b/libs/image/kis_paint_device.cc --- a/libs/image/kis_paint_device.cc +++ b/libs/image/kis_paint_device.cc @@ -37,7 +37,7 @@ #include #include #include - +#include #include "kis_image.h" #include "kis_random_sub_accessor.h" @@ -64,6 +64,8 @@ #include "kis_paint_device_data.h" #include "kis_paint_device_frames_interface.h" +#include "kis_transform_worker.h" +#include "kis_filter_strategy.h" struct KisPaintDevice::Private { @@ -1396,7 +1398,19 @@ return image; } -KisPaintDeviceSP KisPaintDevice::createThumbnailDevice(qint32 w, qint32 h, QRect rect) const +inline bool moveBy(KisSequentialConstIterator& iter, int numPixels ) +{ + int pos = 0; + while(pos srcWidth) { w = srcWidth; h = qint32(double(srcWidth) / w * h); @@ -1419,6 +1439,8 @@ else if (srcHeight > srcWidth) w = qint32(double(srcWidth) / srcHeight * h); + oversample *= (qreal)h / hstart; //readjusting oversample ratio, given that we had to readjust thumbnail size + const qint32 pixelSize = this->pixelSize(); KisRandomConstAccessorSP iter = createRandomConstAccessorNG(0, 0); @@ -1433,20 +1455,27 @@ memcpy(dstIter->rawData(), iter->rawDataConst(), pixelSize); } } - return thumbnail; + if( oversample!=1. ){ + QPointer updater = new KoDummyUpdater(); + KisTransformWorker worker(thumbnail, 1/oversample, 1/oversample, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + updater, KisFilterStrategyRegistry::instance()->value("Bilinear")); + worker.run(); + } + + return thumbnail; } -QImage KisPaintDevice::createThumbnail(qint32 w, qint32 h, QRect rect, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) +QImage KisPaintDevice::createThumbnail(qint32 w, qint32 h, QRect rect, qreal oversample, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { - KisPaintDeviceSP dev = createThumbnailDevice(w, h, rect); + KisPaintDeviceSP dev = createThumbnailDevice(w, h, rect, oversample); QImage thumbnail = dev->convertToQImage(KoColorSpaceRegistry::instance()->rgb8()->profile(), 0, 0, w, h, renderingIntent, conversionFlags); return thumbnail; } -QImage KisPaintDevice::createThumbnail(qint32 w, qint32 h, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) +QImage KisPaintDevice::createThumbnail(qint32 w, qint32 h, qreal oversample, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { - return m_d->cache()->createThumbnail(w, h, renderingIntent, conversionFlags); + return m_d->cache()->createThumbnail(w, h, oversample, renderingIntent, conversionFlags); } KisHLineIteratorSP KisPaintDevice::createHLineIteratorNG(qint32 x, qint32 y, qint32 w) diff --git a/libs/image/kis_paint_device_cache.h b/libs/image/kis_paint_device_cache.h --- a/libs/image/kis_paint_device_cache.h +++ b/libs/image/kis_paint_device_cache.h @@ -82,11 +82,11 @@ return m_regionCache.getValue(); } - QImage createThumbnail(qint32 w, qint32 h, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { + QImage createThumbnail(qint32 w, qint32 h, qreal oversample, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { QImage thumbnail; if(m_thumbnailsValid) { - thumbnail = findThumbnail(w, h); + thumbnail = findThumbnail(w, h, oversample); } else { m_thumbnails.clear(); @@ -94,8 +94,8 @@ } if(thumbnail.isNull()) { - thumbnail = m_paintDevice->createThumbnail(w, h, QRect(), renderingIntent, conversionFlags); - cacheThumbnail(w, h, thumbnail); + thumbnail = m_paintDevice->createThumbnail(w, h, QRect(), oversample, renderingIntent, conversionFlags); + cacheThumbnail(w, h, oversample, thumbnail); } Q_ASSERT(!thumbnail.isNull() || m_paintDevice->extent().isEmpty()); @@ -103,16 +103,16 @@ } private: - inline QImage findThumbnail(qint32 w, qint32 h) { + inline QImage findThumbnail(qint32 w, qint32 h, qreal oversample) { QImage resultImage; - if (m_thumbnails.contains(w) && m_thumbnails[w].contains(h)) { - resultImage = m_thumbnails[w][h]; + if (m_thumbnails.contains(w) && m_thumbnails[w].contains(h) && m_thumbnails[w][h].contains(oversample)) { + resultImage = m_thumbnails[w][h][oversample]; } return resultImage; } - inline void cacheThumbnail(qint32 w, qint32 h, QImage image) { - m_thumbnails[w][h] = image; + inline void cacheThumbnail(qint32 w, qint32 h, qreal oversample, QImage image) { + m_thumbnails[w][h][oversample] = image; } private: @@ -153,7 +153,7 @@ RegionCache m_regionCache; bool m_thumbnailsValid; - QMap > m_thumbnails; + QMap > > m_thumbnails; }; #endif /* __KIS_PAINT_DEVICE_CACHE_H */ diff --git a/libs/image/kis_selection_based_layer.cpp b/libs/image/kis_selection_based_layer.cpp --- a/libs/image/kis_selection_based_layer.cpp +++ b/libs/image/kis_selection_based_layer.cpp @@ -285,7 +285,7 @@ KisPaintDeviceSP originalDevice = original(); return originalDevice && originalSelection ? - originalDevice->createThumbnail(w, h, + originalDevice->createThumbnail(w, h, 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) : QImage(); diff --git a/libs/ui/kis_custom_pattern.cc b/libs/ui/kis_custom_pattern.cc --- a/libs/ui/kis_custom_pattern.cc +++ b/libs/ui/kis_custom_pattern.cc @@ -167,7 +167,7 @@ } QString dir = KoResourceServerProvider::instance()->patternServer()->saveLocation(); - m_pattern = new KoPattern(dev->createThumbnail(size.width(), size.height(), rc, + m_pattern = new KoPattern(dev->createThumbnail(size.width(), size.height(), rc, /*oversample*/ 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()), name, dir); } diff --git a/plugins/dockers/advancedcolorselector/kis_common_colors.cpp b/plugins/dockers/advancedcolorselector/kis_common_colors.cpp --- a/plugins/dockers/advancedcolorselector/kis_common_colors.cpp +++ b/plugins/dockers/advancedcolorselector/kis_common_colors.cpp @@ -131,7 +131,7 @@ KisImageWSP kisImage = m_canvas->image(); - QImage image = kisImage->projection()->createThumbnail(1024, 1024, kisImage->bounds(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); + QImage image = kisImage->projection()->createThumbnail(1024, 1024, kisImage->bounds(), 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); KisCommonColorsRecalculationRunner* runner = new KisCommonColorsRecalculationRunner(image, patchCount(), this); QThreadPool::globalInstance()->start(runner); diff --git a/plugins/dockers/historydocker/KisUndoModel.cpp b/plugins/dockers/historydocker/KisUndoModel.cpp --- a/plugins/dockers/historydocker/KisUndoModel.cpp +++ b/plugins/dockers/historydocker/KisUndoModel.cpp @@ -254,7 +254,7 @@ if (m_stack->count() == idx && !m_imageMap.contains(currentCommand)) { KisImageWSP historyImage = m_canvas->viewManager()->image(); KisPaintDeviceSP paintDevice = historyImage->projection(); - QImage image = paintDevice->createThumbnail(32, 32, + QImage image = paintDevice->createThumbnail(32, 32, 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); m_imageMap[currentCommand] = image; diff --git a/plugins/dockers/overview/overviewwidget.cc b/plugins/dockers/overview/overviewwidget.cc --- a/plugins/dockers/overview/overviewwidget.cc +++ b/plugins/dockers/overview/overviewwidget.cc @@ -110,7 +110,10 @@ if (isVisible() && previewSize.isValid()) { QImage img = image->projection()-> - createThumbnail(previewSize.width(), previewSize.height(), image->bounds()); + createThumbnail(previewSize.width(), previewSize.height(), image->bounds(), /*oversample=*/ 2, + KoColorConversionTransformation::internalRenderingIntent(), + KoColorConversionTransformation::internalConversionFlags() + ); m_pixmap = QPixmap::fromImage(img); } diff --git a/plugins/tools/tool_transform2/kis_tool_transform.cc b/plugins/tools/tool_transform2/kis_tool_transform.cc --- a/plugins/tools/tool_transform2/kis_tool_transform.cc +++ b/plugins/tools/tool_transform2/kis_tool_transform.cc @@ -769,7 +769,7 @@ origImg = m_selectedPortionCache-> createThumbnail(thumbRect.width(), thumbRect.height(), - srcRect, + srcRect, 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); thumbToImageTransform = scaleTransform.inverted();