Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Commit c655e2ad authored by Dmitry Kazakov's avatar Dmitry Kazakov

Added an optimized version of Alpha Darken composite op

It gives 1.58...1.74 times better result of the composition
on Sandy Bridge. Other architectures are to be tested.
parent e4c8ec20
......@@ -306,6 +306,26 @@ if(LCMS2_FOUND)
set(HAVE_LCMS2 TRUE)
endif(LCMS2_FOUND)
##
## Test for Vc
##
set(OLD_CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} )
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules )
macro_optional_find_package(Vc)
macro_log_feature(Vc_FOUND "Vc" "Portable, zero-overhead SIMD library for C++" "http://code.compeng.uni-frankfurt.de/projects/vc" FALSE "" "Required by the Krita for vectorization")
macro_bool_to_01(Vc_FOUND HAVE_VC)
configure_file(config-vc.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-vc.h )
if(HAVE_VC)
message(STATUS "Vc found!")
SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${Vc_CMAKE_MODULES_DIR}")
include (OptimizeForArchitecture)
OptimizeForArchitecture()
endif(HAVE_VC)
set(CMAKE_MODULE_PATH ${OLD_CMAKE_MODULE_PATH} )
##
## Test for Nepomuk
##
......
......@@ -12,24 +12,6 @@ if(MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj")
endif(MSVC)
set(OLD_CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} )
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules )
macro_optional_find_package(Vc)
macro_log_feature(Vc_FOUND "Vc" "Portable, zero-overhead SIMD library for C++" "http://code.compeng.uni-frankfurt.de/projects/vc" FALSE "" "Required by the Krita for vectorization")
macro_bool_to_01(Vc_FOUND HAVE_VC)
configure_file(config-vc.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-vc.h )
if(HAVE_VC)
message(STATUS "Vc found!")
SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${Vc_CMAKE_MODULES_DIR}")
include (OptimizeForArchitecture)
OptimizeForArchitecture()
endif(HAVE_VC)
set(CMAKE_MODULE_PATH ${OLD_CMAKE_MODULE_PATH} )
include(CheckFunctionExists)
......
set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} )
include_directories( ${KOMAIN_INCLUDES} ${CMAKE_SOURCE_DIR}/krita/image/tiles3 )
set(LINK_VC_LIB)
if(HAVE_VC)
include_directories(${Vc_INCLUDE_DIR})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Vc_DEFINITIONS}")
set(LINK_VC_LIB ${Vc_LIBRARIES})
endif(HAVE_VC)
add_definitions(-DFILES_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data/")
......@@ -26,6 +29,7 @@ set(kis_floodfill_benchmark_SRCS kis_floodfill_benchmark.cpp)
set(kis_gradient_benchmark_SRCS kis_gradient_benchmark.cpp)
set(kis_mask_generator_benchmark_SRCS kis_mask_generator_benchmark.cpp)
set(kis_low_memory_benchmark_SRCS kis_low_memory_benchmark.cpp)
set(kis_composition_benchmark_SRCS kis_composition_benchmark.cpp)
calligra_add_benchmark(KisDatamanagerBenchmark TESTNAME krita-benchmarks-KisDataManager ${kis_datamanager_benchmark_SRCS})
calligra_add_benchmark(KisHLineIteratorBenchmark TESTNAME krita-benchmarks-KisHLineIterator ${kis_hiterator_benchmark_SRCS})
......@@ -41,6 +45,7 @@ calligra_add_benchmark(KisFloodfillBenchmark TESTNAME krita-benchmarks-KisFloodF
calligra_add_benchmark(KisGradientBenchmark TESTNAME krita-benchmarks-KisGradientFill ${kis_gradient_benchmark_SRCS})
calligra_add_benchmark(KisMaskGeneratorBenchmark TESTNAME krita-benchmarks-KisMaskGenerator ${kis_mask_generator_benchmark_SRCS})
calligra_add_benchmark(KisLowMemoryBenchmark TESTNAME krita-benchmarks-KisLowMemory ${kis_low_memory_benchmark_SRCS})
calligra_add_benchmark(KisCompositionBenchmark TESTNAME krita-benchmarks-KisComposition ${kis_composition_benchmark_SRCS})
target_link_libraries(KisDatamanagerBenchmark ${KDE4_KDEUI_LIBS} kritaimage ${QT_QTTEST_LIBRARY})
target_link_libraries(KisHLineIteratorBenchmark ${KDE4_KDEUI_LIBS} kritaimage ${QT_QTTEST_LIBRARY})
......@@ -55,8 +60,5 @@ target_link_libraries(KisFastMathBenchmark ${KDE4_KDEUI_LIBS} kritaimage ${QT_QT
target_link_libraries(KisFloodfillBenchmark ${KDE4_KDEUI_LIBS} kritaimage ${QT_QTTEST_LIBRARY})
target_link_libraries(KisGradientBenchmark ${KDE4_KDEUI_LIBS} kritaimage ${QT_QTTEST_LIBRARY})
target_link_libraries(KisLowMemoryBenchmark ${KDE4_KDEUI_LIBS} kritaimage ${QT_QTTEST_LIBRARY})
target_link_libraries(KisMaskGeneratorBenchmark ${KDE4_KDEUI_LIBS} kritaimage ${QT_QTTEST_LIBRARY})
if(HAVE_VC)
target_link_libraries(KisMaskGeneratorBenchmark ${Vc_LIBRARIES})
endif(HAVE_VC)
\ No newline at end of file
target_link_libraries(KisCompositionBenchmark ${KDE4_KDEUI_LIBS} kritaimage ${QT_QTTEST_LIBRARY} ${LINK_VC_LIB})
target_link_libraries(KisMaskGeneratorBenchmark ${KDE4_KDEUI_LIBS} kritaimage ${QT_QTTEST_LIBRARY} ${LINK_VC_LIB})
/*
* Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.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_composition_benchmark.h"
#include <qtest_kde.h>
#include <KoColorSpace.h>
#include <KoCompositeOp.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorSpaceTraits.h>
#include <KoCompositeOpAlphaDarken.h>
#include "KoOptimizedCompositeOpFactory.h"
// for calculation of the needed alignment
#ifdef HAVE_VC
#include <Vc/Vc>
#include <Vc/IO>
#endif
// for memalign()
#include <malloc.h>
const int alpha_pos = 3;
void generateDataLine(uint seed, int numPixels, quint8 *srcPixels, quint8 *dstPixels, quint8 *mask)
{
Q_ASSERT(numPixels >= 4);
for (int i = 0; i < 4; i++) {
srcPixels[4*i] = i * 10 + 30;
srcPixels[4*i+1] = i * 10 + 30;
srcPixels[4*i+2] = i * 10 + 30;
srcPixels[4*i+3] = i * 10 + 35;
dstPixels[4*i] = i * 10 + 160;
dstPixels[4*i+1] = i * 10 + 160;
dstPixels[4*i+2] = i * 10 + 160;
dstPixels[4*i+3] = i * 10 + 165;
mask[i] = i * 10 + 225;
}
qsrand(seed);
numPixels -= 4;
srcPixels += 4 * 4;
dstPixels += 4 * 4;
mask += 4;
for (int i = 0; i < numPixels; i++) {
for (int j = 0; j < 4; j++) {
*(srcPixels++) = 50 + qrand() % 205;
*(dstPixels++) = 50 + qrand() % 205;
}
*(mask++) = 50 + qrand() % 205;
}
}
void printData(int numPixels, quint8 *srcPixels, quint8 *dstPixels, quint8 *mask)
{
for (int i = 0; i < numPixels; i++) {
qDebug() << "Src: "
<< srcPixels[i*4] << "\t"
<< srcPixels[i*4+1] << "\t"
<< srcPixels[i*4+2] << "\t"
<< srcPixels[i*4+3] << "\t"
<< "Msk:" << mask[i];
qDebug() << "Dst: "
<< dstPixels[i*4] << "\t"
<< dstPixels[i*4+1] << "\t"
<< dstPixels[i*4+2] << "\t"
<< dstPixels[i*4+3];
}
}
const int rowStride = 64;
const int totalRows = 64;
const QRect processRect(0,0,64,64);
const int numPixels = rowStride * totalRows;
const int numTiles = 1024;
struct Tile {
quint8 *src;
quint8 *dst;
quint8 *mask;
};
void benchmarkCompositeOp(const KoCompositeOp *op,
bool haveMask,
const int srcAlignmentShift,
const int dstAlignmentShift)
{
QVector<Tile> tiles(numTiles);
#ifdef HAVE_VC
const int vecSize = Vc::float_v::Size;
#else
const int vecSize = 1;
#endif
for (int i = 0; i < numTiles; i++) {
tiles[i].src = (quint8*)memalign(vecSize * 4, numPixels * 4 + srcAlignmentShift) + srcAlignmentShift;
tiles[i].dst = (quint8*)memalign(vecSize * 4, numPixels * 4 + dstAlignmentShift) + dstAlignmentShift;
tiles[i].mask = (quint8*)memalign(vecSize, numPixels);
generateDataLine(1, numPixels, tiles[i].src, tiles[i].dst, tiles[i].mask);
}
// qDebug() << "Initial values:";
// printData(8, tiles[0].src, tiles[0].dst, tiles[0].mask);
const int tileOffset = 4 * (processRect.y() * rowStride + processRect.x());
KoCompositeOp::ParameterInfo params;
params.dstRowStride = 4 * rowStride;
params.srcRowStride = 4 * rowStride;
params.maskRowStride = rowStride;
params.rows = processRect.height();
params.cols = processRect.width();
params.opacity = 0.5*1.0f;
params.flow = 0.3*1.0f;
params.channelFlags = QBitArray();
QBENCHMARK_ONCE {
foreach (const Tile &tile, tiles) {
params.dstRowStart = tile.dst + tileOffset;
params.srcRowStart = tile.src + tileOffset;
params.maskRowStart = haveMask ? tile.mask : 0;
op->composite(params);
}
}
// qDebug() << "Final values:";
// printData(8, tiles[0].src, tiles[0].dst, tiles[0].mask);
foreach (const Tile &tile, tiles) {
free(tile.src - srcAlignmentShift);
free(tile.dst - dstAlignmentShift);
free(tile.mask);
}
}
void KisCompositionBenchmark::testRgb8CompositeAlphaDarkenLegacy_Aligned()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *op = new KoCompositeOpAlphaDarken<KoBgrU8Traits>(cs);
benchmarkCompositeOp(op, true, 0, 0);
delete op;
}
// Unaligned versions of the Legacy version give the same results
void KisCompositionBenchmark::testRgb8CompositeAlphaDarkenOptimized_Aligned()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *op = KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs);
benchmarkCompositeOp(op, true, 0, 0);
delete op;
}
void KisCompositionBenchmark::testRgb8CompositeAlphaDarkenOptimized_SrcUnaligned()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *op = KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs);
benchmarkCompositeOp(op, true, 8, 0);
delete op;
}
void KisCompositionBenchmark::testRgb8CompositeAlphaDarkenOptimized_DstUnaligned()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *op = KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs);
benchmarkCompositeOp(op, true, 0, 8);
delete op;
}
void KisCompositionBenchmark::testRgb8CompositeAlphaDarkenOptimized_Unaligned()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *op = KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs);
benchmarkCompositeOp(op, true, 4, 8);
delete op;
}
void KisCompositionBenchmark::testRgb8CompositeAlphaDarkenLegacy_Aligned_NoMask()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *op = new KoCompositeOpAlphaDarken<KoBgrU8Traits>(cs);
benchmarkCompositeOp(op, false, 0, 0);
delete op;
}
void KisCompositionBenchmark::testRgb8CompositeAlphaDarkenOptimized_Aligned_NoMask()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *op = KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs);
benchmarkCompositeOp(op, false, 0, 0);
delete op;
}
void KisCompositionBenchmark::testRgb8CompositeAlphaDarkenReal_Aligned()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
const KoCompositeOp *op = cs->compositeOp(COMPOSITE_ALPHA_DARKEN);
benchmarkCompositeOp(op, true, 0, 0);
}
QTEST_KDEMAIN(KisCompositionBenchmark, GUI)
/*
* Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.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_COMPOSITION_BENCHMARK_H
#define __KIS_COMPOSITION_BENCHMARK_H
#include <QtTest/QtTest>
class KisCompositionBenchmark : public QObject
{
Q_OBJECT
private slots:
void testRgb8CompositeAlphaDarkenLegacy_Aligned();
void testRgb8CompositeAlphaDarkenOptimized_Aligned();
void testRgb8CompositeAlphaDarkenOptimized_SrcUnaligned();
void testRgb8CompositeAlphaDarkenOptimized_DstUnaligned();
void testRgb8CompositeAlphaDarkenOptimized_Unaligned();
void testRgb8CompositeAlphaDarkenLegacy_Aligned_NoMask();
void testRgb8CompositeAlphaDarkenOptimized_Aligned_NoMask();
void testRgb8CompositeAlphaDarkenReal_Aligned();
};
#endif /* __KIS_COMPOSITION_BENCHMARK_H */
......@@ -24,8 +24,6 @@ inline double drand48() {
}
#endif
#include <KoCompositeOps.h>
#include "hairy_brush.h"
#include "trajectory.h"
......
......@@ -10,6 +10,13 @@ if(OPENEXR_FOUND)
add_definitions(${OPENEXR_DEFINITIONS})
endif(OPENEXR_FOUND)
set(LINK_VC_LIB)
if(HAVE_VC)
include_directories(${Vc_INCLUDE_DIR})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Vc_DEFINITIONS}")
set(LINK_VC_LIB ${Vc_LIBRARIES})
endif(HAVE_VC)
add_subdirectory(tests)
add_subdirectory(benchmarks)
......@@ -44,6 +51,7 @@ set(pigmentcms_SRCS
colorspaces/KoRgbU16ColorSpace.cpp
colorspaces/KoRgbU8ColorSpace.cpp
colorspaces/KoSimpleColorSpaceEngine.cpp
compositeops/KoOptimizedCompositeOpFactory.cpp
colorprofiles/KoDummyColorProfile.cpp
resources/KoAbstractGradient.cpp
resources/KoColorSet.cpp
......@@ -91,7 +99,7 @@ set(PIGMENT_INSTALL_FILES
KoHistogramProducer.h
)
set (EXTRA_LIBRARIES ${LINK_OPENEXR_LIB})
set (EXTRA_LIBRARIES ${LINK_OPENEXR_LIB} ${LINK_VC_LIB})
if(MSVC)
# avoid "cannot open file 'LIBC.lib'" error
......
......@@ -30,9 +30,6 @@
#include "KoChannelInfo.h"
#include "KoID.h"
#include "KoIntegerMaths.h"
#include "KoCompositeOpOver.h"
#include "KoCompositeOpErase.h"
#include "KoCompositeOpAlphaDarken.h"
KoRgbU16ColorSpace::KoRgbU16ColorSpace() :
......
......@@ -29,12 +29,8 @@
#include "KoChannelInfo.h"
#include "KoID.h"
#include "KoIntegerMaths.h"
#include "KoCompositeOpOver.h"
#include "KoCompositeOpErase.h"
#include "KoCompositeOpAlphaDarken.h"
#include "compositeops/KoCompositeOps.h"
#include "compositeops/KoCompositeOpAdd.h"
#include "compositeops/KoCompositeOpSubtract.h"
KoRgbU8ColorSpace::KoRgbU8ColorSpace() :
......
......@@ -49,6 +49,8 @@
#include "compositeops/KoCompositeOpBehind.h"
#include "KoOptimizedCompositeOpFactory.h"
namespace _Private {
template<class Traits, bool flag>
......@@ -57,6 +59,38 @@ struct AddGeneralOps
static void add(KoColorSpace* cs) { Q_UNUSED(cs); }
};
template<class Traits>
struct OptimizedOpsSelector
{
static KoCompositeOp* createAlphaDarkenOp(const KoColorSpace *cs) {
return new KoCompositeOpAlphaDarken<Traits>(cs);
}
};
template<>
struct OptimizedOpsSelector<KoRgbU8Traits>
{
static KoCompositeOp* createAlphaDarkenOp(const KoColorSpace *cs) {
return KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs);
}
};
template<>
struct OptimizedOpsSelector<KoBgrU8Traits>
{
static KoCompositeOp* createAlphaDarkenOp(const KoColorSpace *cs) {
return KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs);
}
};
template<>
struct OptimizedOpsSelector<KoLabU8Traits>
{
static KoCompositeOp* createAlphaDarkenOp(const KoColorSpace *cs) {
return KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs);
}
};
template<class Traits>
struct AddGeneralOps<Traits, true>
{
......@@ -71,7 +105,7 @@ struct AddGeneralOps<Traits, true>
static void add(KoColorSpace* cs) {
cs->addCompositeOp(new KoCompositeOpOver<Traits>(cs));
cs->addCompositeOp(new KoCompositeOpAlphaDarken<Traits>(cs));
cs->addCompositeOp(OptimizedOpsSelector<Traits>::createAlphaDarkenOp(cs));
cs->addCompositeOp(new KoCompositeOpCopy2<Traits>(cs));
cs->addCompositeOp(new KoCompositeOpErase<Traits>(cs));
cs->addCompositeOp(new KoCompositeOpBehind<Traits>(cs));
......
/*
* Copyright (c) 2006 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2011 Silvio Heinrich <plassy@web.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOOPTIMIZEDCOMPOSITEOPALPHADARKEN32_H_
#define KOOPTIMIZEDCOMPOSITEOPALPHADARKEN32_H_
#include "KoCompositeOpFunctions.h"
#include "KoCompositeOpBase.h"
#include "KoStreamedMath.h"
template<typename channels_type, typename pixel_type>
struct AlphaDarkenCompositor32 {
/**
* This is a vector equivalent of compositeOnePixel(). It is considered
* to process Vc::float_v::Size pixels in a single pass.
*
* o the \p haveMask parameter points whether the real (non-null) mask
* pointer is passed to the function.
* o the \p src pointer may be aligned to vector boundary or may be
* not. In case not, it must be pointed with a special parameter
* \p src_aligned.
* o the \p dst pointer must always(!) be aligned to the boundary
* of a streaming vector. Unaligned writes are really expensive.
* o This function is *never* used if HAVE_VC is not present
*/
#ifdef HAVE_VC
template<bool haveMask, bool src_aligned>
static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, float flow)
{
Vc::float_v src_alpha;
Vc::float_v dst_alpha;
Vc::float_v opacity_vec(255.0 * opacity * flow);
Vc::float_v flow_norm_vec(flow);
Vc::float_v uint8MaxRec2((float)1.0 / (255.0 * 255.0));
Vc::float_v uint8MaxRec1((float)1.0 / 255.0);
Vc::float_v msk_norm_alpha;
src_alpha = KoStreamedMath::fetch_alpha_32<src_aligned>(src);
if (haveMask) {
Vc::float_v mask_vec = KoStreamedMath::fetch_mask_8(mask);
msk_norm_alpha = src_alpha * mask_vec * uint8MaxRec2;
} else {
msk_norm_alpha = src_alpha * uint8MaxRec1;
}
dst_alpha = KoStreamedMath::fetch_alpha_32<true>(dst);
src_alpha = msk_norm_alpha * opacity_vec;
Vc::float_v src_c1;
Vc::float_v src_c2;
Vc::float_v src_c3;
Vc::float_v dst_c1;
Vc::float_v dst_c2;
Vc::float_v dst_c3;
KoStreamedMath::fetch_colors_32<src_aligned>(src, src_c1, src_c2, src_c3);
Vc::float_v dst_blend = src_alpha * uint8MaxRec1;
KoStreamedMath::fetch_colors_32<true>(dst, dst_c1, dst_c2, dst_c3);
Vc::float_v alpha1 = src_alpha + dst_alpha -
dst_blend * dst_alpha;
// TODO: if (dstAlpha == 0) dstC = srcC;
dst_c1 = dst_blend * (src_c1 - dst_c1) + dst_c1;
dst_c2 = dst_blend * (src_c2 - dst_c2) + dst_c2;
dst_c3 = dst_blend * (src_c3 - dst_c3) + dst_c3;
Vc::float_m alpha2_mask = opacity > dst_alpha;
Vc::float_v opt1 = (opacity_vec - dst_alpha) * msk_norm_alpha + dst_alpha;
Vc::float_v alpha2;
alpha2(!alpha2_mask) = dst_alpha;
alpha2(alpha2_mask) = opt1;
dst_alpha = (alpha2 - alpha1) * flow_norm_vec + alpha1;
KoStreamedMath::write_channels_32(dst, dst_alpha, dst_c1, dst_c2, dst_c3);
}
#endif /* HAVE_VC */
/**
* Composes one pixel of the source into the destination
*/
template <bool haveMask>
static ALWAYS_INLINE void compositeOnePixel(const channels_type *src, channels_type *dst, const quint8 *mask, channels_type opacity, channels_type flow)
{
using namespace Arithmetic;
const qint32 alpha_pos = 3;
channels_type srcAlpha = src[alpha_pos];
channels_type dstAlpha = dst[alpha_pos];
channels_type mskAlpha = haveMask ? mul(scale<channels_type>(*mask), srcAlpha) : srcAlpha;
srcAlpha = mul(mskAlpha, opacity);
if(dstAlpha != zeroValue<channels_type>()) {
dst[0] = lerp(dst[0], src[0], srcAlpha);
dst[1] = lerp(dst[1], src[1], srcAlpha);
dst[2] = lerp(dst[2], src[2], srcAlpha);
}
else {
const pixel_type *s = reinterpret_cast<const pixel_type*>(src);
pixel_type *d = reinterpret_cast<pixel_type*>(dst);
*d = *s;
}
channels_type alpha1 = unionShapeOpacity(srcAlpha, dstAlpha); // alpha with 0% flow
channels_type alpha2 = (opacity > dstAlpha) ? lerp(dstAlpha, opacity, mskAlpha) : dstAlpha; // alpha with 100% flow
dst[alpha_pos] = lerp(alpha1, alpha2, flow);
}
};
/**
* An optimized version of a composite op for the use in 4 byte
* colorspaces with alpha channel placed at the last byte of
* the pixel: C1_C2_C3_A.
*/
class KoOptimizedCompositeOpAlphaDarken32 : public KoCompositeOp
{
public:
KoOptimizedCompositeOpAlphaDarken32(const KoColorSpace* cs)
: KoCompositeOp(cs, COMPOSITE_ALPHA_DARKEN, i18n("Alpha darken"), KoCompositeOp::categoryMix()) {}
using KoCompositeOp::composite;
virtual void composite(const KoCompositeOp::ParameterInfo& params) const
{
if(params.maskRowStart) {
KoStreamedMath::genericComposite32<true, true, AlphaDarkenCompositor32<quint8, quint32> >(params);
} else {
KoStreamedMath::genericComposite32<false, true, AlphaDarkenCompositor32<quint8, quint32> >(params);
}
}
};
#endif // KOOPTIMIZEDCOMPOSITEOPALPHADARKEN32_H_
/*
* Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.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.