diff options
Diffstat (limited to 'src/gui/painting')
29 files changed, 3173 insertions, 1615 deletions
diff --git a/src/gui/painting/WEBGRADIENTS_LICENSE.txt b/src/gui/painting/WEBGRADIENTS_LICENSE.txt new file mode 100644 index 0000000000..1a4d527a4d --- /dev/null +++ b/src/gui/painting/WEBGRADIENTS_LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 itmeo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/gui/painting/painting.pri b/src/gui/painting/painting.pri index 29bef14f0c..749cc221ff 100644 --- a/src/gui/painting/painting.pri +++ b/src/gui/painting/painting.pri @@ -99,8 +99,13 @@ SOURCES += \ painting/qplatformbackingstore.cpp \ painting/qpathsimplifier.cpp +webgradients.files = painting/webgradients.binaryjson +webgradients.prefix = qgradient +webgradients.base = painting + RESOURCES += \ - painting/qpdf.qrc + painting/qpdf.qrc \ + webgradients darwin { HEADERS += painting/qcoregraphics_p.h diff --git a/src/gui/painting/qblendfunctions.cpp b/src/gui/painting/qblendfunctions.cpp index a4a091a29f..2dd5144e40 100644 --- a/src/gui/painting/qblendfunctions.cpp +++ b/src/gui/painting/qblendfunctions.cpp @@ -430,28 +430,28 @@ struct Blend_RGB32_on_RGB32_ConstAlpha { }; struct Blend_ARGB32_on_ARGB32_SourceAlpha { - inline void write(quint32 *dst, quint32 src) { - *dst = src + BYTE_MUL(*dst, qAlpha(~src)); + inline void write(quint32 *dst, quint32 src) + { + blend_pixel(*dst, src); } inline void flush(void *) {} }; struct Blend_ARGB32_on_ARGB32_SourceAndConstAlpha { - inline Blend_ARGB32_on_ARGB32_SourceAndConstAlpha(quint32 alpha) { + inline Blend_ARGB32_on_ARGB32_SourceAndConstAlpha(quint32 alpha) + { m_alpha = (alpha * 255) >> 8; - m_ialpha = 255 - m_alpha; } - inline void write(quint32 *dst, quint32 src) { - src = BYTE_MUL(src, m_alpha); - *dst = src + BYTE_MUL(*dst, qAlpha(~src)); + inline void write(quint32 *dst, quint32 src) + { + blend_pixel(*dst, src, m_alpha); } inline void flush(void *) {} quint32 m_alpha; - quint32 m_ialpha; }; void qt_scale_image_rgb32_on_rgb32(uchar *destPixels, int dbpl, diff --git a/src/gui/painting/qbrush.cpp b/src/gui/painting/qbrush.cpp index 5c13308d94..c667454cd2 100644 --- a/src/gui/painting/qbrush.cpp +++ b/src/gui/painting/qbrush.cpp @@ -46,9 +46,13 @@ #include "qvariant.h" #include "qline.h" #include "qdebug.h" +#include <QtCore/qjsondocument.h> +#include <QtCore/qjsonarray.h> #include <QtCore/qcoreapplication.h> #include "private/qhexstring_p.h" #include <QtCore/qnumeric.h> +#include <QtCore/qfile.h> +#include <QtCore/qmutex.h> QT_BEGIN_NAMESPACE @@ -1330,6 +1334,72 @@ QGradient::QGradient() { } +/*! + \enum QGradient::Preset + \since 5.12 + + This enum specifies a set of predefined presets for QGradient, + based on the gradients from https://webgradients.com/. +*/ + +/*! + \fn QGradient::QGradient(QGradient::Preset preset) + \since 5.12 + + Constructs a gradient based on a predefined \a preset. + + The coordinate mode of the resulting gradient is + QGradient::ObjectBoundingMode, allowing the preset + to be applied to arbitrary object sizes. +*/ +QGradient::QGradient(Preset preset) + : QGradient() +{ + static QHash<int, QGradient> cachedPresets; + static QMutex cacheMutex; + QMutexLocker locker(&cacheMutex); + if (cachedPresets.contains(preset)) { + const QGradient &cachedPreset = cachedPresets.value(preset); + m_type = cachedPreset.m_type; + m_data = cachedPreset.m_data; + m_stops = cachedPreset.m_stops; + m_spread = cachedPreset.m_spread; + dummy = cachedPreset.dummy; + } else { + static QJsonDocument jsonPresets = []() { + QFile webGradients(QLatin1String(":/qgradient/webgradients.binaryjson")); + webGradients.open(QFile::ReadOnly); + return QJsonDocument::fromBinaryData(webGradients.readAll()); + }(); + + const QJsonValue presetData = jsonPresets[preset - 1]; + if (!presetData.isObject()) + return; + + m_type = LinearGradient; + setCoordinateMode(ObjectBoundingMode); + setSpread(PadSpread); + + const QJsonValue start = presetData[QLatin1Literal("start")]; + const QJsonValue end = presetData[QLatin1Literal("end")]; + m_data.linear.x1 = start[QLatin1Literal("x")].toDouble(); + m_data.linear.y1 = start[QLatin1Literal("y")].toDouble(); + m_data.linear.x2 = end[QLatin1Literal("x")].toDouble(); + m_data.linear.y2 = end[QLatin1Literal("y")].toDouble(); + + for (const QJsonValue &stop : presetData[QLatin1String("stops")].toArray()) { + setColorAt(stop[QLatin1Literal("stop")].toDouble(), + QColor(QRgb(stop[QLatin1Literal("color")].toInt()))); + } + + cachedPresets.insert(preset, *this); + } +} + +QT_END_NAMESPACE +static void initGradientPresets() { Q_INIT_RESOURCE(qmake_webgradients); } +Q_CONSTRUCTOR_FUNCTION(initGradientPresets); +QT_BEGIN_NAMESPACE /*! \enum QGradient::Type diff --git a/src/gui/painting/qbrush.h b/src/gui/painting/qbrush.h index e5cff9cc9b..9023d2f43d 100644 --- a/src/gui/painting/qbrush.h +++ b/src/gui/painting/qbrush.h @@ -203,7 +203,181 @@ public: ComponentInterpolation }; + enum Preset { + WarmFlame = 1, + NightFade = 2, + SpringWarmth = 3, + JuicyPeach = 4, + YoungPassion = 5, + LadyLips = 6, + SunnyMorning = 7, + RainyAshville = 8, + FrozenDreams = 9, + WinterNeva = 10, + DustyGrass = 11, + TemptingAzure = 12, + HeavyRain = 13, + AmyCrisp = 14, + MeanFruit = 15, + DeepBlue = 16, + RipeMalinka = 17, + CloudyKnoxville = 18, + MalibuBeach = 19, + NewLife = 20, + TrueSunset = 21, + MorpheusDen = 22, + RareWind = 23, + NearMoon = 24, + WildApple = 25, + SaintPetersburg = 26, + AriellesSmile = 27, + PlumPlate = 28, + EverlastingSky = 29, + HappyFisher = 30, + Blessing = 31, + SharpeyeEagle = 32, + LadogaBottom = 33, + LemonGate = 34, + ItmeoBranding = 35, + ZeusMiracle = 36, + OldHat = 37, + StarWine = 38, + HappyAcid = 41, + AwesomePine = 42, + NewYork = 43, + ShyRainbow = 44, + MixedHopes = 46, + FlyHigh = 47, + StrongBliss = 48, + FreshMilk = 49, + SnowAgain = 50, + FebruaryInk = 51, + KindSteel = 52, + SoftGrass = 53, + GrownEarly = 54, + SharpBlues = 55, + ShadyWater = 56, + DirtyBeauty = 57, + GreatWhale = 58, + TeenNotebook = 59, + PoliteRumors = 60, + SweetPeriod = 61, + WideMatrix = 62, + SoftCherish = 63, + RedSalvation = 64, + BurningSpring = 65, + NightParty = 66, + SkyGlider = 67, + HeavenPeach = 68, + PurpleDivision = 69, + AquaSplash = 70, + SpikyNaga = 72, + LoveKiss = 73, + CleanMirror = 75, + PremiumDark = 76, + ColdEvening = 77, + CochitiLake = 78, + SummerGames = 79, + PassionateBed = 80, + MountainRock = 81, + DesertHump = 82, + JungleDay = 83, + PhoenixStart = 84, + OctoberSilence = 85, + FarawayRiver = 86, + AlchemistLab = 87, + OverSun = 88, + PremiumWhite = 89, + MarsParty = 90, + EternalConstance = 91, + JapanBlush = 92, + SmilingRain = 93, + CloudyApple = 94, + BigMango = 95, + HealthyWater = 96, + AmourAmour = 97, + RiskyConcrete = 98, + StrongStick = 99, + ViciousStance = 100, + PaloAlto = 101, + HappyMemories = 102, + MidnightBloom = 103, + Crystalline = 104, + PartyBliss = 106, + ConfidentCloud = 107, + LeCocktail = 108, + RiverCity = 109, + FrozenBerry = 110, + ChildCare = 112, + FlyingLemon = 113, + NewRetrowave = 114, + HiddenJaguar = 115, + AboveTheSky = 116, + Nega = 117, + DenseWater = 118, + Seashore = 120, + MarbleWall = 121, + CheerfulCaramel = 122, + NightSky = 123, + MagicLake = 124, + YoungGrass = 125, + ColorfulPeach = 126, + GentleCare = 127, + PlumBath = 128, + HappyUnicorn = 129, + AfricanField = 131, + SolidStone = 132, + OrangeJuice = 133, + GlassWater = 134, + NorthMiracle = 136, + FruitBlend = 137, + MillenniumPine = 138, + HighFlight = 139, + MoleHall = 140, + SpaceShift = 142, + ForestInei = 143, + RoyalGarden = 144, + RichMetal = 145, + JuicyCake = 146, + SmartIndigo = 147, + SandStrike = 148, + NorseBeauty = 149, + AquaGuidance = 150, + SunVeggie = 151, + SeaLord = 152, + BlackSea = 153, + GrassShampoo = 154, + LandingAircraft = 155, + WitchDance = 156, + SleeplessNight = 157, + AngelCare = 158, + CrystalRiver = 159, + SoftLipstick = 160, + SaltMountain = 161, + PerfectWhite = 162, + FreshOasis = 163, + StrictNovember = 164, + MorningSalad = 165, + DeepRelief = 166, + SeaStrike = 167, + NightCall = 168, + SupremeSky = 169, + LightBlue = 170, + MindCrawl = 171, + LilyMeadow = 172, + SugarLollipop = 173, + SweetDessert = 174, + MagicRay = 175, + TeenParty = 176, + FrozenHeat = 177, + GagarinView = 178, + FabledSunset = 179, + PerfectBlue = 180 + }; + Q_ENUM(Preset) + QGradient(); + QGradient(Preset); Type type() const { return m_type; } diff --git a/src/gui/painting/qcolor.cpp b/src/gui/painting/qcolor.cpp index c55bcb12c9..1d7375d1df 100644 --- a/src/gui/painting/qcolor.cpp +++ b/src/gui/painting/qcolor.cpp @@ -304,11 +304,6 @@ static const int rgbTblSize = sizeof(rgbTbl) / sizeof(RGBData); #undef rgb -#if defined(Q_CC_MSVC) && _MSC_VER < 1600 -inline bool operator<(const RGBData &data1, const RGBData &data2) -{ return qstrcmp(data1.name, data2.name) < 0; } -#endif - inline bool operator<(const char *name, const RGBData &data) { return qstrcmp(name, data.name) < 0; } inline bool operator<(const RGBData &data, const char *name) diff --git a/src/gui/painting/qcompositionfunctions.cpp b/src/gui/painting/qcompositionfunctions.cpp index 339a9749b8..027bf23115 100644 --- a/src/gui/painting/qcompositionfunctions.cpp +++ b/src/gui/painting/qcompositionfunctions.cpp @@ -64,125 +64,354 @@ QT_BEGIN_NAMESPACE where the source is an array of pixels. */ -#if defined __SSE2__ -# define LOAD(ptr) _mm_loadl_epi64(reinterpret_cast<const __m128i *>(ptr)) +struct Argb32OperationsC +{ + typedef QRgb Type; + typedef quint8 Scalar; + typedef QRgb OptimalType; + typedef quint8 OptimalScalar; + + static const Type clear; + static bool isOpaque(Type val) + { return qAlpha(val) == 255; } + static bool isTransparent(Type val) + { return qAlpha(val) == 0; } + static Scalar scalarFrom8bit(uint8_t a) + { return a; } + static void memfill(Type *ptr, Type value, qsizetype len) + { qt_memfill32(ptr, value, len); } + static void memcpy(Type *Q_DECL_RESTRICT dest, const Type *Q_DECL_RESTRICT src, qsizetype len) + { ::memcpy(dest, src, len * sizeof(Type)); } + + static OptimalType load(const Type *ptr) + { return *ptr; } + static OptimalType convert(const Type &val) + { return val; } + static void store(Type *ptr, OptimalType value) + { *ptr = value; } + static OptimalType add(OptimalType a, OptimalType b) + { return a + b; } + static OptimalScalar add(OptimalScalar a, OptimalScalar b) + { return a + b; } + static OptimalType plus(OptimalType a, OptimalType b) + { return comp_func_Plus_one_pixel(a, b); } + static OptimalScalar alpha(OptimalType val) + { return qAlpha(val); } + static OptimalScalar invAlpha(OptimalScalar c) + { return 255 - c; } + static OptimalScalar invAlpha(OptimalType val) + { return alpha(~val); } + static OptimalScalar scalar(Scalar v) + { return v; } + static OptimalType multiplyAlpha8bit(OptimalType val, uint8_t a) + { return BYTE_MUL(val, a); } + static OptimalType interpolate8bit(OptimalType x, uint8_t a1, OptimalType y, uint8_t a2) + { return INTERPOLATE_PIXEL_255(x, a1, y, a2); } + static OptimalType multiplyAlpha(OptimalType val, OptimalScalar a) + { return BYTE_MUL(val, a); } + static OptimalScalar multiplyAlpha8bit(OptimalScalar val, uint8_t a) + { return qt_div_255(val * a); } + static OptimalType interpolate(OptimalType x, OptimalScalar a1, OptimalType y, OptimalScalar a2) + { return INTERPOLATE_PIXEL_255(x, a1, y, a2); } +}; + +const Argb32OperationsC::Type Argb32OperationsC::clear = 0; + +struct Rgba64OperationsBase +{ + typedef QRgba64 Type; + typedef quint16 Scalar; + + static const Type clear; + + static bool isOpaque(Type val) + { return val.isOpaque(); } + static bool isTransparent(Type val) + { return val.isTransparent(); } + static Scalar scalarFrom8bit(uint8_t a) + { return a * 257; } + + static void memfill(Type *ptr, Type value, qsizetype len) + { qt_memfill64((quint64*)ptr, value, len); } + static void memcpy(Type *Q_DECL_RESTRICT dest, const Type *Q_DECL_RESTRICT src, qsizetype len) + { ::memcpy(dest, src, len * sizeof(Type)); } +}; + +const Rgba64OperationsBase::Type Rgba64OperationsBase::clear = QRgba64::fromRgba64(0); + +struct Rgba64OperationsC : public Rgba64OperationsBase +{ + typedef QRgba64 OptimalType; + typedef quint16 OptimalScalar; + + static OptimalType load(const Type *ptr) + { return *ptr; } + static OptimalType convert(const Type &val) + { return val; } + static void store(Type *ptr, OptimalType value) + { *ptr = value; } + static OptimalType add(OptimalType a, OptimalType b) + { return QRgba64::fromRgba64((quint64)a + (quint64)b); } + static OptimalScalar add(OptimalScalar a, OptimalScalar b) + { return a + b; } + static OptimalType plus(OptimalType a, OptimalType b) + { return addWithSaturation(a, b); } + static OptimalScalar alpha(OptimalType val) + { return val.alpha(); } + static OptimalScalar invAlpha(Scalar c) + { return 65535 - c; } + static OptimalScalar invAlpha(OptimalType val) + { return 65535 - alpha(val); } + static OptimalScalar scalar(Scalar v) + { return v; } + static OptimalType multiplyAlpha8bit(OptimalType val, uint8_t a) + { return multiplyAlpha255(val, a); } + static OptimalScalar multiplyAlpha8bit(OptimalScalar val, uint8_t a) + { return qt_div_255(val * a); } + static OptimalType interpolate8bit(OptimalType x, uint8_t a1, OptimalType y, uint8_t a2) + { return interpolate255(x, a1, y, a2); } + static OptimalType multiplyAlpha(OptimalType val, OptimalScalar a) + { return multiplyAlpha65535(val, a); } + static OptimalType interpolate(OptimalType x, OptimalScalar a1, OptimalType y, OptimalScalar a2) + { return interpolate65535(x, a1, y, a2); } +}; + +#if defined(__SSE2__) +struct Rgba64OperationsSSE2 : public Rgba64OperationsBase +{ + typedef __m128i OptimalType; + typedef __m128i OptimalScalar; + + static OptimalType load(const Type *ptr) + { + return _mm_loadl_epi64(reinterpret_cast<const __m128i *>(ptr)); + } + static OptimalType convert(const Type &value) + { #ifdef Q_PROCESSOR_X86_64 -# define CONVERT(value) _mm_cvtsi64_si128(value) + return _mm_cvtsi64_si128(value); #else -# define CONVERT(value) LOAD(&value) + return load(&value); #endif -# define STORE(ptr, value) _mm_storel_epi64(reinterpret_cast<__m128i *>(ptr), value) -# define ADD(p, q) _mm_add_epi32(p, q) -# define ALPHA(c) _mm_shufflelo_epi16(c, _MM_SHUFFLE(3, 3, 3, 3)) -# define CONST(n) _mm_shufflelo_epi16(_mm_cvtsi32_si128(n), _MM_SHUFFLE(0, 0, 0, 0)) -# define INVALPHA(c) _mm_sub_epi32(CONST(65535), ALPHA(c)) -#elif defined __ARM_NEON__ -# define LOAD(ptr) vreinterpret_u16_u64(vld1_u64(reinterpret_cast<const uint64_t *>(ptr))) -# define CONVERT(value) vreinterpret_u16_u64(vmov_n_u64(value)) -# define STORE(ptr, value) vst1_u64(reinterpret_cast<uint64_t *>(ptr), vreinterpret_u64_u16(value)) -# define ADD(p, q) vadd_u16(p, q) -# define ALPHA(c) vdup_lane_u16(c, 3) -# define CONST(n) vdup_n_u16(n) -# define INVALPHA(c) vmvn_u16(ALPHA(c)) -#else -# define LOAD(ptr) *ptr -# define CONVERT(value) value -# define STORE(ptr, value) *ptr = value -# define ADD(p, q) (p + q) -# define ALPHA(c) (c).alpha() -# define CONST(n) n -# define INVALPHA(c) (65535 - ALPHA(c)) + } + static void store(Type *ptr, OptimalType value) + { + _mm_storel_epi64(reinterpret_cast<__m128i *>(ptr), value); + } + static OptimalType add(OptimalType a, OptimalType b) + { + return _mm_add_epi16(a, b); + } +// same as above: +// static OptimalScalar add(OptimalScalar a, OptimalScalar b) + static OptimalType plus(OptimalType a, OptimalType b) + { + return _mm_adds_epu16(a, b); + } + static OptimalScalar alpha(OptimalType c) + { + return _mm_shufflelo_epi16(c, _MM_SHUFFLE(3, 3, 3, 3)); + } + static OptimalScalar invAlpha(Scalar c) + { + return scalar(65535 - c); + } + static OptimalScalar invAlpha(OptimalType c) + { + return _mm_xor_si128(_mm_set1_epi16(-1), alpha(c)); + } + static OptimalScalar scalar(Scalar n) + { + return _mm_shufflelo_epi16(_mm_cvtsi32_si128(n), _MM_SHUFFLE(0, 0, 0, 0)); + } + static OptimalType multiplyAlpha8bit(OptimalType val, uint8_t a) + { + return multiplyAlpha255(val, a); + } +// same as above: +// static OptimalScalar multiplyAlpha8bit(OptimalScalar a, uint8_t a) + static OptimalType interpolate8bit(OptimalType x, uint8_t a1, OptimalType y, uint8_t a2) + { + return interpolate255(x, a1, y, a2); + } + static OptimalType multiplyAlpha(OptimalType val, OptimalScalar a) + { + return multiplyAlpha65535(val, a); + } + // a2 is const-ref because otherwise MSVC2015@x86 complains that it can't 16-byte align the argument. + static OptimalType interpolate(OptimalType x, OptimalScalar a1, OptimalType y, const OptimalScalar &a2) + { + return interpolate65535(x, a1, y, a2); + } +}; #endif +#if defined(__ARM_NEON__) +struct Rgba64OperationsNEON : public Rgba64OperationsBase +{ + typedef uint16x4_t OptimalType; + typedef uint16x4_t OptimalScalar; + + static OptimalType load(const Type *ptr) + { + return vreinterpret_u16_u64(vld1_u64(reinterpret_cast<const uint64_t *>(ptr))); + } + static OptimalType convert(const Type &val) + { + return vreinterpret_u16_u64(vmov_n_u64(val)); + } + static void store(Type *ptr, OptimalType value) + { + vst1_u64(reinterpret_cast<uint64_t *>(ptr), vreinterpret_u64_u16(value)); + } + static OptimalType add(OptimalType a, OptimalType b) + { + return vadd_u16(a, b); + } +// same as above: +// static OptimalScalar add(OptimalScalar a, OptimalScalar b) + static OptimalType plus(OptimalType a, OptimalType b) + { + return vqadd_u16(a, b); + } + static OptimalScalar alpha(OptimalType c) + { + return vdup_lane_u16(c, 3); + } + static OptimalScalar invAlpha(Scalar c) + { + return scalar(65535 - c); + } + static OptimalScalar invAlpha(OptimalType c) + { + return vmvn_u16(alpha(c)); + } + static OptimalScalar scalar(Scalar n) + { + return vdup_n_u16(n); + } + static OptimalType multiplyAlpha8bit(OptimalType val, uint8_t a) + { + return multiplyAlpha255(val, a); + } +// same as above: +// static OptimalScalar multiplyAlpha8bit(OptimalScalar a, uint8_t a) + static OptimalType interpolate8bit(OptimalType x, uint8_t a1, OptimalType y, uint8_t a2) + { + return interpolate255(x, a1, y, a2); + } + static OptimalType multiplyAlpha(OptimalType val, OptimalScalar a) + { + return multiplyAlpha65535(val, a); + } + static OptimalType interpolate(OptimalType x, OptimalScalar a1, OptimalType y, OptimalScalar a2) + { + return interpolate65535(x, a1, y, a2); + } +}; + +#endif + +typedef Argb32OperationsC Argb32Operations; +#if defined(__SSE2__) +typedef Rgba64OperationsSSE2 Rgba64Operations; +#elif defined(__ARM_NEON__) +typedef Rgba64OperationsNEON Rgba64Operations; +#else +typedef Rgba64OperationsC Rgba64Operations; +#endif /* result = 0 d = d * cia */ -void QT_FASTCALL comp_func_solid_Clear(uint *dest, int length, uint, uint const_alpha) +template<class Ops> +inline static void comp_func_Clear_template(typename Ops::Type *dest, int length, uint const_alpha) { - if (const_alpha == 255) { - qt_memfill32(dest, 0, length); - } else { + if (const_alpha == 255) + Ops::memfill(dest, Ops::clear, length); + else { uint ialpha = 255 - const_alpha; - for (int i = 0; i < length; ++i) - dest[i] = BYTE_MUL(dest[i], ialpha); + for (int i = 0; i < length; ++i) { + Ops::store(&dest[i], Ops::multiplyAlpha8bit(Ops::load(&dest[i]), ialpha)); + } } } +void QT_FASTCALL comp_func_solid_Clear(uint *dest, int length, uint, uint const_alpha) +{ + comp_func_Clear_template<Argb32Operations>(dest, length, const_alpha); +} + void QT_FASTCALL comp_func_solid_Clear_rgb64(QRgba64 *dest, int length, QRgba64, uint const_alpha) { - if (const_alpha == 255) { - qt_memfill64((quint64*)dest, 0, length); - } else { - uint ialpha = 255 - const_alpha; - for (int i = 0; i < length; ++i) - STORE(&dest[i], multiplyAlpha255(LOAD(&dest[i]), ialpha)); - } + comp_func_Clear_template<Rgba64Operations>(dest, length, const_alpha); } void QT_FASTCALL comp_func_Clear(uint *dest, const uint *, int length, uint const_alpha) { - comp_func_solid_Clear(dest, length, 0, const_alpha); + comp_func_Clear_template<Argb32Operations>(dest, length, const_alpha); } void QT_FASTCALL comp_func_Clear_rgb64(QRgba64 *dest, const QRgba64 *, int length, uint const_alpha) { - comp_func_solid_Clear_rgb64(dest, length, QRgba64(), const_alpha); + comp_func_Clear_template<Rgba64Operations>(dest, length, const_alpha); } /* result = s dest = s * ca + d * cia */ -void QT_FASTCALL comp_func_solid_Source(uint *dest, int length, uint color, uint const_alpha) +template<class Ops> +inline static void comp_func_solid_Source_template(typename Ops::Type *dest, int length, typename Ops::Type color, uint const_alpha) { - if (const_alpha == 255) { - qt_memfill32(dest, color, length); - } else { - uint ialpha = 255 - const_alpha; - color = BYTE_MUL(color, const_alpha); + if (const_alpha == 255) + Ops::memfill(dest, color, length); + else { + const uint ialpha = 255 - const_alpha; + auto s = Ops::multiplyAlpha8bit(Ops::convert(color), const_alpha); for (int i = 0; i < length; ++i) { - dest[i] = color + BYTE_MUL(dest[i], ialpha); + auto d = Ops::multiplyAlpha8bit(Ops::load(&dest[i]), ialpha); + Ops::store(&dest[i], Ops::add(s, d)); } } } -void QT_FASTCALL comp_func_solid_Source_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +template<class Ops> +inline static void comp_func_Source_template(typename Ops::Type *Q_DECL_RESTRICT dest, + const typename Ops::Type *Q_DECL_RESTRICT src, + int length, uint const_alpha) { if (const_alpha == 255) - qt_memfill64((quint64*)dest, color, length); + Ops::memcpy(dest, src, length); else { - uint ialpha = 255 - const_alpha; - auto c = multiplyAlpha255(CONVERT(color), const_alpha); + const uint ialpha = 255 - const_alpha; for (int i = 0; i < length; ++i) { - STORE(&dest[i], ADD(c, multiplyAlpha255(LOAD(&dest[i]), ialpha))); + auto s = Ops::load(src + i); + auto d = Ops::load(dest + i); + Ops::store(&dest[i], Ops::interpolate8bit(s, const_alpha, d, ialpha)); } } } +void QT_FASTCALL comp_func_solid_Source(uint *dest, int length, uint color, uint const_alpha) +{ + comp_func_solid_Source_template<Argb32Operations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_solid_Source_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +{ + comp_func_solid_Source_template<Rgba64Operations>(dest, length, color, const_alpha); +} + void QT_FASTCALL comp_func_Source(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - if (const_alpha == 255) { - ::memcpy(dest, src, size_t(length) * sizeof(uint)); - } else { - uint ialpha = 255 - const_alpha; - for (int i = 0; i < length; ++i) { - dest[i] = INTERPOLATE_PIXEL_255(src[i], const_alpha, dest[i], ialpha); - } - } + comp_func_Source_template<Argb32Operations>(dest, src, length, const_alpha); } void QT_FASTCALL comp_func_Source_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { - if (const_alpha == 255) - ::memcpy(dest, src, size_t(length) * sizeof(quint64)); - else { - uint ialpha = 255 - const_alpha; - for (int i = 0; i < length; ++i) { - STORE(&dest[i], interpolate255(LOAD(&src[i]), const_alpha, LOAD(&dest[i]), ialpha)); - } - } + comp_func_Source_template<Rgba64Operations>(dest, src, length, const_alpha); } void QT_FASTCALL comp_func_solid_Destination(uint *, int, uint, uint) @@ -207,68 +436,66 @@ void QT_FASTCALL comp_func_Destination_rgb64(QRgba64 *, const QRgba64 *, int, ui = s * ca + d * (sia * ca + cia) = s * ca + d * (1 - sa*ca) */ -void QT_FASTCALL comp_func_solid_SourceOver(uint *dest, int length, uint color, uint const_alpha) +template<class Ops> +inline static void comp_func_solid_SourceOver_template(typename Ops::Type *dest, int length, typename Ops::Type color, uint const_alpha) { - if ((const_alpha & qAlpha(color)) == 255) { - qt_memfill32(dest, color, length); - } else { + if (const_alpha == 255 && Ops::isOpaque(color)) + Ops::memfill(dest, color, length); + else { + auto c = Ops::convert(color); if (const_alpha != 255) - color = BYTE_MUL(color, const_alpha); + c = Ops::multiplyAlpha8bit(c, const_alpha); + auto cAlpha = Ops::invAlpha(c); for (int i = 0; i < length; ++i) { - dest[i] = color + BYTE_MUL(dest[i], qAlpha(~color)); + auto d = Ops::multiplyAlpha(Ops::load(&dest[i]), cAlpha); + Ops::store(&dest[i], Ops::add(c, d)); } } } -void QT_FASTCALL comp_func_solid_SourceOver_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +template<class Ops> +inline static void comp_func_SourceOver_template(typename Ops::Type *Q_DECL_RESTRICT dest, + const typename Ops::Type *Q_DECL_RESTRICT src, + int length, uint const_alpha) { - if (const_alpha == 255 && color.isOpaque()) { - qt_memfill64((quint64*)dest, color, length); + if (const_alpha == 255) { + for (int i = 0; i < length; ++i) { + auto c = src[i]; + if (Ops::isOpaque(c)) + Ops::store(&dest[i], Ops::convert(c)); + else if (!Ops::isTransparent(c)) { + auto s = Ops::convert(c); + auto d = Ops::multiplyAlpha(Ops::load(&dest[i]), Ops::invAlpha(s)); + Ops::store(&dest[i], Ops::add(s, d)); + } + } } else { - auto c = CONVERT(color); - if (const_alpha != 255) - c = multiplyAlpha255(c, const_alpha); - auto cAlpha = INVALPHA(c); for (int i = 0; i < length; ++i) { - STORE(&dest[i], ADD(c, multiplyAlpha65535(LOAD(&dest[i]), cAlpha))); + auto s = Ops::multiplyAlpha8bit(Ops::load(&src[i]), const_alpha); + auto d = Ops::multiplyAlpha(Ops::load(&dest[i]), Ops::invAlpha(s)); + Ops::store(&dest[i], Ops::add(s, d)); } } } +void QT_FASTCALL comp_func_solid_SourceOver(uint *dest, int length, uint color, uint const_alpha) +{ + comp_func_solid_SourceOver_template<Argb32Operations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_solid_SourceOver_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +{ + comp_func_solid_SourceOver_template<Rgba64Operations>(dest, length, color, const_alpha); +} + void QT_FASTCALL comp_func_SourceOver(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - if (const_alpha == 255) { - for (int i = 0; i < length; ++i) { - uint s = src[i]; - if (s >= 0xff000000) - dest[i] = s; - else if (s != 0) - dest[i] = s + BYTE_MUL(dest[i], qAlpha(~s)); - } - } else { - for (int i = 0; i < length; ++i) { - uint s = BYTE_MUL(src[i], const_alpha); - dest[i] = s + BYTE_MUL(dest[i], qAlpha(~s)); - } - } + comp_func_SourceOver_template<Argb32Operations>(dest, src, length, const_alpha); } void QT_FASTCALL comp_func_SourceOver_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { - if (const_alpha == 255) { - for (int i = 0; i < length; ++i) { - QRgba64 s = src[i]; - if (s.isOpaque()) - dest[i] = s; - else if (!s.isTransparent()) - STORE(&dest[i], ADD(CONVERT(s), multiplyAlpha65535(LOAD(&dest[i]), 65535 - s.alpha()))); - } - } else { - for (int i = 0; i < length; ++i) { - auto s = multiplyAlpha255(LOAD(&src[i]), const_alpha); - STORE(&dest[i], ADD(s, multiplyAlpha65535(LOAD(&dest[i]), INVALPHA(s)))); - } - } + comp_func_SourceOver_template<Rgba64Operations>(dest, src, length, const_alpha); } /* @@ -276,128 +503,122 @@ void QT_FASTCALL comp_func_SourceOver_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const dest = (d + s * dia) * ca + d * cia = d + s * dia * ca */ -void QT_FASTCALL comp_func_solid_DestinationOver(uint *dest, int length, uint color, uint const_alpha) -{ - if (const_alpha != 255) - color = BYTE_MUL(color, const_alpha); - for (int i = 0; i < length; ++i) { - uint d = dest[i]; - dest[i] = d + BYTE_MUL(color, qAlpha(~d)); - } -} - -void QT_FASTCALL comp_func_solid_DestinationOver_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +template<class Ops> +inline static void comp_func_solid_DestinationOver_template(typename Ops::Type *dest, int length, typename Ops::Type color, uint const_alpha) { - auto c = CONVERT(color); + auto c = Ops::convert(color); if (const_alpha != 255) - c = multiplyAlpha255(c, const_alpha); + c = Ops::multiplyAlpha8bit(c, const_alpha); for (int i = 0; i < length; ++i) { - auto d = LOAD(&dest[i]); - STORE(&dest[i], ADD(d, multiplyAlpha65535(c, INVALPHA(d)))); + auto d = Ops::load(&dest[i]); + auto s = Ops::multiplyAlpha(c, Ops::invAlpha(d)); + Ops::store(&dest[i], Ops::add(s, d)); } } -void QT_FASTCALL comp_func_DestinationOver(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +template<class Ops> +inline static void comp_func_DestinationOver_template(typename Ops::Type *Q_DECL_RESTRICT dest, + const typename Ops::Type *Q_DECL_RESTRICT src, + int length, uint const_alpha) { if (const_alpha == 255) { for (int i = 0; i < length; ++i) { - uint d = dest[i]; - dest[i] = d + BYTE_MUL(src[i], qAlpha(~d)); + auto d = Ops::load(&dest[i]); + auto s = Ops::multiplyAlpha(Ops::load(&src[i]), Ops::invAlpha(d)); + Ops::store(&dest[i], Ops::add(s, d)); } } else { for (int i = 0; i < length; ++i) { - uint d = dest[i]; - uint s = BYTE_MUL(src[i], const_alpha); - dest[i] = d + BYTE_MUL(s, qAlpha(~d)); + auto d = Ops::load(&dest[i]); + auto s = Ops::multiplyAlpha8bit(Ops::load(&src[i]), const_alpha); + s = Ops::multiplyAlpha(s, Ops::invAlpha(d)); + Ops::store(&dest[i], Ops::add(s, d)); } } } +void QT_FASTCALL comp_func_solid_DestinationOver(uint *dest, int length, uint color, uint const_alpha) +{ + comp_func_solid_DestinationOver_template<Argb32Operations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_solid_DestinationOver_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +{ + comp_func_solid_DestinationOver_template<Rgba64Operations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_DestinationOver(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + comp_func_DestinationOver_template<Argb32Operations>(dest, src, length, const_alpha); +} + void QT_FASTCALL comp_func_DestinationOver_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { - if (const_alpha == 255) { - for (int i = 0; i < length; ++i) { - auto d = LOAD(&dest[i]); - STORE(&dest[i], ADD(d, multiplyAlpha65535(LOAD(&src[i]), INVALPHA(d)))); - } - } else { - for (int i = 0; i < length; ++i) { - auto d = LOAD(&dest[i]); - auto s = multiplyAlpha255(LOAD(&src[i]), const_alpha); - STORE(&dest[i], ADD(d, multiplyAlpha65535(s, INVALPHA(d)))); - } - } + comp_func_DestinationOver_template<Rgba64Operations>(dest, src, length, const_alpha); } /* result = s * da dest = s * da * ca + d * cia */ -void QT_FASTCALL comp_func_solid_SourceIn(uint *dest, int length, uint color, uint const_alpha) +template<class Ops> +inline static void comp_func_solid_SourceIn_template(typename Ops::Type *dest, int length, typename Ops::Type color, uint const_alpha) { if (const_alpha == 255) { + auto c = Ops::convert(color); for (int i = 0; i < length; ++i) { - dest[i] = BYTE_MUL(color, qAlpha(dest[i])); + Ops::store(&dest[i], Ops::multiplyAlpha(c, Ops::alpha(Ops::load(&dest[i])))); } } else { - color = BYTE_MUL(color, const_alpha); - uint cia = 255 - const_alpha; + auto c = Ops::multiplyAlpha8bit(Ops::convert(color), const_alpha); + auto cia = Ops::invAlpha(Ops::scalarFrom8bit(const_alpha)); for (int i = 0; i < length; ++i) { - uint d = dest[i]; - dest[i] = INTERPOLATE_PIXEL_255(color, qAlpha(d), d, cia); + auto d = Ops::load(&dest[i]); + Ops::store(&dest[i], Ops::interpolate(c, Ops::alpha(d), d, cia)); } } } -void QT_FASTCALL comp_func_solid_SourceIn_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +template<class Ops> +inline static void comp_func_SourceIn_template(typename Ops::Type *Q_DECL_RESTRICT dest, + const typename Ops::Type *Q_DECL_RESTRICT src, + int length, uint const_alpha) { if (const_alpha == 255) { - auto c = CONVERT(color); for (int i = 0; i < length; ++i) { - STORE(&dest[i], multiplyAlpha65535(c, dest[i].alpha())); + auto s = Ops::load(&src[i]); + Ops::store(&dest[i], Ops::multiplyAlpha(s, Ops::alpha(Ops::load(&dest[i])))); } } else { - uint ca = const_alpha * 257; - auto cia = CONST(65535 - ca); - auto c = multiplyAlpha65535(CONVERT(color), ca); + auto ca = Ops::scalarFrom8bit(const_alpha); + auto cia = Ops::invAlpha(ca); + auto cav = Ops::scalar(ca); for (int i = 0; i < length; ++i) { - auto d = LOAD(&dest[i]); - STORE(&dest[i], interpolate65535(c, ALPHA(d), d, cia)); + auto d = Ops::load(&dest[i]); + auto s = Ops::multiplyAlpha(Ops::load(&src[i]), cav); + Ops::store(&dest[i], Ops::interpolate(s, Ops::alpha(d), d, cia)); } } } +void QT_FASTCALL comp_func_solid_SourceIn(uint *dest, int length, uint color, uint const_alpha) +{ + comp_func_solid_SourceIn_template<Argb32Operations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_solid_SourceIn_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +{ + comp_func_solid_SourceIn_template<Rgba64Operations>(dest, length, color, const_alpha); +} + void QT_FASTCALL comp_func_SourceIn(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - if (const_alpha == 255) { - for (int i = 0; i < length; ++i) { - dest[i] = BYTE_MUL(src[i], qAlpha(dest[i])); - } - } else { - uint cia = 255 - const_alpha; - for (int i = 0; i < length; ++i) { - uint d = dest[i]; - uint s = BYTE_MUL(src[i], const_alpha); - dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(d), d, cia); - } - } + comp_func_SourceIn_template<Argb32Operations>(dest, src, length, const_alpha); } void QT_FASTCALL comp_func_SourceIn_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { - if (const_alpha == 255) { - for (int i = 0; i < length; ++i) { - STORE(&dest[i], multiplyAlpha65535(LOAD(&src[i]), dest[i].alpha())); - } - } else { - uint ca = const_alpha * 257; - auto cia = CONST(65535 - ca); - for (int i = 0; i < length; ++i) { - auto d = LOAD(&dest[i]); - auto s = multiplyAlpha65535(LOAD(&src[i]), ca); - STORE(&dest[i], interpolate65535(s, ALPHA(d), d, cia)); - } - } + comp_func_SourceIn_template<Rgba64Operations>(dest, src, length, const_alpha); } /* @@ -405,128 +626,120 @@ void QT_FASTCALL comp_func_SourceIn_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const Q dest = d * sa * ca + d * cia = d * (sa * ca + cia) */ -void QT_FASTCALL comp_func_solid_DestinationIn(uint *dest, int length, uint color, uint const_alpha) +template<class Ops> +inline static void comp_func_solid_DestinationIn_template(typename Ops::Type *dest, int length, typename Ops::Type color, uint const_alpha) { - uint a = qAlpha(color); + auto sa = Ops::alpha(Ops::convert(color)); if (const_alpha != 255) { - a = BYTE_MUL(a, const_alpha) + 255 - const_alpha; - } - for (int i = 0; i < length; ++i) { - dest[i] = BYTE_MUL(dest[i], a); + sa = Ops::multiplyAlpha8bit(sa, const_alpha); + sa = Ops::add(sa, Ops::invAlpha(Ops::scalarFrom8bit(const_alpha))); } -} -void QT_FASTCALL comp_func_solid_DestinationIn_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) -{ - uint a = color.alpha(); - uint ca64k = const_alpha * 257; - if (const_alpha != 255) - a = qt_div_65535(a * ca64k) + 65535 - ca64k; for (int i = 0; i < length; ++i) { - STORE(&dest[i], multiplyAlpha65535(LOAD(&dest[i]), a)); + Ops::store(&dest[i], Ops::multiplyAlpha(Ops::load(&dest[i]), sa)); } } -void QT_FASTCALL comp_func_DestinationIn(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +template<class Ops> +inline static void comp_func_DestinationIn_template(typename Ops::Type *Q_DECL_RESTRICT dest, + const typename Ops::Type *Q_DECL_RESTRICT src, + int length, uint const_alpha) { if (const_alpha == 255) { for (int i = 0; i < length; ++i) { - dest[i] = BYTE_MUL(dest[i], qAlpha(src[i])); + auto a = Ops::alpha(Ops::load(&src[i])); + Ops::store(&dest[i], Ops::multiplyAlpha(Ops::load(&dest[i]), a)); } } else { - uint cia = 255 - const_alpha; + auto cia = Ops::invAlpha(Ops::scalarFrom8bit(const_alpha)); for (int i = 0; i < length; ++i) { - uint a = BYTE_MUL(qAlpha(src[i]), const_alpha) + cia; - dest[i] = BYTE_MUL(dest[i], a); + auto sa = Ops::multiplyAlpha8bit(Ops::alpha(Ops::load(&src[i])), const_alpha); + sa = Ops::add(sa, cia); + Ops::store(&dest[i], Ops::multiplyAlpha(Ops::load(&dest[i]), sa)); } } } +void QT_FASTCALL comp_func_solid_DestinationIn(uint *dest, int length, uint color, uint const_alpha) +{ + comp_func_solid_DestinationIn_template<Argb32Operations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_solid_DestinationIn_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +{ + comp_func_solid_DestinationIn_template<Rgba64Operations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_DestinationIn(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + comp_func_DestinationIn_template<Argb32Operations>(dest, src, length, const_alpha); +} + void QT_FASTCALL comp_func_DestinationIn_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { - if (const_alpha == 255) { - for (int i = 0; i < length; ++i) { - STORE(&dest[i], multiplyAlpha65535(LOAD(&dest[i]), src[i].alpha())); - } - } else { - uint ca = const_alpha * 257; - uint cia = 65535 - ca; - for (int i = 0; i < length; ++i) { - uint a = qt_div_65535(src[i].alpha() * ca) + cia; - STORE(&dest[i], multiplyAlpha65535(LOAD(&dest[i]), a)); - } - } + comp_func_DestinationIn_template<Rgba64Operations>(dest, src, length, const_alpha); } /* result = s * dia dest = s * dia * ca + d * cia */ - -void QT_FASTCALL comp_func_solid_SourceOut(uint *dest, int length, uint color, uint const_alpha) +template<class Ops> +inline static void comp_func_solid_SourceOut_template(typename Ops::Type *dest, int length, typename Ops::Type color, uint const_alpha) { + auto c = Ops::convert(color); if (const_alpha == 255) { - for (int i = 0; i < length; ++i) { - dest[i] = BYTE_MUL(color, qAlpha(~dest[i])); - } + for (int i = 0; i < length; ++i) + Ops::store(&dest[i], Ops::multiplyAlpha(c, Ops::invAlpha(Ops::load(&dest[i])))); } else { - color = BYTE_MUL(color, const_alpha); - uint cia = 255 - const_alpha; + auto cia = Ops::invAlpha(Ops::scalarFrom8bit(const_alpha)); + c = Ops::multiplyAlpha8bit(c, const_alpha); for (int i = 0; i < length; ++i) { - uint d = dest[i]; - dest[i] = INTERPOLATE_PIXEL_255(color, qAlpha(~d), d, cia); + auto d = Ops::load(&dest[i]); + Ops::store(&dest[i], Ops::interpolate(c, Ops::invAlpha(d), d, cia)); } } } -void QT_FASTCALL comp_func_solid_SourceOut_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +template<class Ops> +inline static void comp_func_SourceOut_template(typename Ops::Type *Q_DECL_RESTRICT dest, + const typename Ops::Type *Q_DECL_RESTRICT src, + int length, uint const_alpha) { if (const_alpha == 255) { for (int i = 0; i < length; ++i) { - dest[i] = multiplyAlpha65535(color, 65535 - dest[i].alpha()); + auto s = Ops::load(&src[i]); + auto d = Ops::load(&dest[i]); + Ops::store(&dest[i], Ops::multiplyAlpha(s, Ops::invAlpha(d))); } } else { - uint ca = const_alpha * 257; - uint cia = 65535 - ca; - color = multiplyAlpha65535(color, ca); + auto cia = Ops::invAlpha(Ops::scalarFrom8bit(const_alpha)); for (int i = 0; i < length; ++i) { - QRgba64 d = dest[i]; - dest[i] = interpolate65535(color, 65535 - d.alpha(), d, cia); + auto s = Ops::multiplyAlpha8bit(Ops::load(&src[i]), const_alpha); + auto d = Ops::load(&dest[i]); + Ops::store(&dest[i], Ops::interpolate(s, Ops::invAlpha(d), d, cia)); } } } +void QT_FASTCALL comp_func_solid_SourceOut(uint *dest, int length, uint color, uint const_alpha) +{ + comp_func_solid_SourceOut_template<Argb32Operations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_solid_SourceOut_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +{ + comp_func_solid_SourceOut_template<Rgba64Operations>(dest, length, color, const_alpha); +} + void QT_FASTCALL comp_func_SourceOut(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - if (const_alpha == 255) { - for (int i = 0; i < length; ++i) { - dest[i] = BYTE_MUL(src[i], qAlpha(~dest[i])); - } - } else { - uint cia = 255 - const_alpha; - for (int i = 0; i < length; ++i) { - uint s = BYTE_MUL(src[i], const_alpha); - uint d = dest[i]; - dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(~d), d, cia); - } - } + comp_func_SourceOut_template<Argb32Operations>(dest, src, length, const_alpha); } void QT_FASTCALL comp_func_SourceOut_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { - if (const_alpha == 255) { - for (int i = 0; i < length; ++i) { - dest[i] = multiplyAlpha65535(src[i], 65535 - dest[i].alpha()); - } - } else { - uint ca = const_alpha * 257; - uint cia = 65535 - ca; - for (int i = 0; i < length; ++i) { - QRgba64 d = dest[i]; - QRgba64 s = multiplyAlpha65535(src[i], ca); - dest[i] = interpolate65535(s, 65535 - d.alpha(), d, cia); - } - } + comp_func_SourceOut_template<Rgba64Operations>(dest, src, length, const_alpha); } /* @@ -534,56 +747,58 @@ void QT_FASTCALL comp_func_SourceOut_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const dest = d * sia * ca + d * cia = d * (sia * ca + cia) */ -void QT_FASTCALL comp_func_solid_DestinationOut(uint *dest, int length, uint color, uint const_alpha) +template<class Ops> +inline static void comp_func_solid_DestinationOut_template(typename Ops::Type *dest, int length, typename Ops::Type color, uint const_alpha) { - uint a = qAlpha(~color); - if (const_alpha != 255) - a = BYTE_MUL(a, const_alpha) + 255 - const_alpha; - for (int i = 0; i < length; ++i) { - dest[i] = BYTE_MUL(dest[i], a); + auto sai = Ops::invAlpha(Ops::convert(color)); + if (const_alpha != 255) { + sai = Ops::multiplyAlpha8bit(sai, const_alpha); + sai = Ops::add(sai, Ops::invAlpha(Ops::scalarFrom8bit(const_alpha))); } -} -void QT_FASTCALL comp_func_solid_DestinationOut_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) -{ - uint a = 65535 - color.alpha(); - uint ca64k = const_alpha * 257; - if (const_alpha != 255) - a = qt_div_65535(a * ca64k) + 65535 - ca64k; for (int i = 0; i < length; ++i) { - dest[i] = multiplyAlpha65535(dest[i], a); + Ops::store(&dest[i], Ops::multiplyAlpha(Ops::load(&dest[i]), sai)); } } -void QT_FASTCALL comp_func_DestinationOut(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +template<class Ops> +inline static void comp_func_DestinationOut_template(typename Ops::Type *Q_DECL_RESTRICT dest, + const typename Ops::Type *Q_DECL_RESTRICT src, + int length, uint const_alpha) { if (const_alpha == 255) { for (int i = 0; i < length; ++i) { - dest[i] = BYTE_MUL(dest[i], qAlpha(~src[i])); + auto sia = Ops::invAlpha(Ops::load(&src[i])); + Ops::store(&dest[i], Ops::multiplyAlpha(Ops::load(&dest[i]), sia)); } } else { - uint cia = 255 - const_alpha; + auto cia = Ops::invAlpha(Ops::scalarFrom8bit(const_alpha)); for (int i = 0; i < length; ++i) { - uint sia = BYTE_MUL(qAlpha(~src[i]), const_alpha) + cia; - dest[i] = BYTE_MUL(dest[i], sia); + auto sia = Ops::multiplyAlpha8bit(Ops::invAlpha(Ops::load(&src[i])), const_alpha); + sia = Ops::add(sia, cia); + Ops::store(&dest[i], Ops::multiplyAlpha(Ops::load(&dest[i]), sia)); } } } +void QT_FASTCALL comp_func_solid_DestinationOut(uint *dest, int length, uint color, uint const_alpha) +{ + comp_func_solid_DestinationOut_template<Argb32Operations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_solid_DestinationOut_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +{ + comp_func_solid_DestinationOut_template<Rgba64Operations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_DestinationOut(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + comp_func_DestinationOut_template<Argb32Operations>(dest, src, length, const_alpha); +} + void QT_FASTCALL comp_func_DestinationOut_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { - if (const_alpha == 255) { - for (int i = 0; i < length; ++i) { - dest[i] = multiplyAlpha65535(dest[i], 65535 - src[i].alpha()); - } - } else { - uint ca = const_alpha * 257; - uint cia = 65535 - ca; - for (int i = 0; i < length; ++i) { - uint a = qt_div_65535((65535 - src[i].alpha()) * ca) + cia; - dest[i] = multiplyAlpha65535(dest[i], a); - } - } + comp_func_DestinationOut_template<Rgba64Operations>(dest, src, length, const_alpha); } /* @@ -592,59 +807,57 @@ void QT_FASTCALL comp_func_DestinationOut_rgb64(QRgba64 *Q_DECL_RESTRICT dest, c = s*ca * da + d * (sia*ca + cia) = s*ca * da + d * (1 - sa*ca) */ -void QT_FASTCALL comp_func_solid_SourceAtop(uint *dest, int length, uint color, uint const_alpha) -{ - if (const_alpha != 255) { - color = BYTE_MUL(color, const_alpha); - } - uint sia = qAlpha(~color); - for (int i = 0; i < length; ++i) { - dest[i] = INTERPOLATE_PIXEL_255(color, qAlpha(dest[i]), dest[i], sia); - } -} - -void QT_FASTCALL comp_func_solid_SourceAtop_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +template<class Ops> +inline static void comp_func_solid_SourceAtop_template(typename Ops::Type *dest, int length, typename Ops::Type color, uint const_alpha) { + auto c = Ops::convert(color); if (const_alpha != 255) - color = multiplyAlpha255(color, const_alpha); - uint sia = 65535 - color.alpha(); + c = Ops::multiplyAlpha8bit(c, const_alpha); + auto sia = Ops::invAlpha(c); for (int i = 0; i < length; ++i) { - dest[i] = interpolate65535(color, dest[i].alpha(), dest[i], sia); + auto d = Ops::load(&dest[i]); + Ops::store(&dest[i], Ops::interpolate(c, Ops::alpha(d), d, sia)); } } -void QT_FASTCALL comp_func_SourceAtop(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +template<class Ops> +inline static void comp_func_SourceAtop_template(typename Ops::Type *Q_DECL_RESTRICT dest, + const typename Ops::Type *Q_DECL_RESTRICT src, + int length, uint const_alpha) { if (const_alpha == 255) { for (int i = 0; i < length; ++i) { - uint s = src[i]; - uint d = dest[i]; - dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(d), d, qAlpha(~s)); + auto s = Ops::load(&src[i]); + auto d = Ops::load(&dest[i]); + Ops::store(&dest[i], Ops::interpolate(s, Ops::alpha(d), d, Ops::invAlpha(s))); } } else { for (int i = 0; i < length; ++i) { - uint s = BYTE_MUL(src[i], const_alpha); - uint d = dest[i]; - dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(d), d, qAlpha(~s)); + auto s = Ops::multiplyAlpha8bit(Ops::load(&src[i]), const_alpha); + auto d = Ops::load(&dest[i]); + Ops::store(&dest[i], Ops::interpolate(s, Ops::alpha(d), d, Ops::invAlpha(s))); } } } +void QT_FASTCALL comp_func_solid_SourceAtop(uint *dest, int length, uint color, uint const_alpha) +{ + comp_func_solid_SourceAtop_template<Argb32Operations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_solid_SourceAtop_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +{ + comp_func_solid_SourceAtop_template<Rgba64Operations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_SourceAtop(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + comp_func_SourceAtop_template<Argb32Operations>(dest, src, length, const_alpha); +} + void QT_FASTCALL comp_func_SourceAtop_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { - if (const_alpha == 255) { - for (int i = 0; i < length; ++i) { - QRgba64 s = src[i]; - QRgba64 d = dest[i]; - dest[i] = interpolate65535(s, d.alpha(), d, 65535 - s.alpha()); - } - } else { - for (int i = 0; i < length; ++i) { - QRgba64 s = multiplyAlpha255(src[i], const_alpha); - QRgba64 d = dest[i]; - dest[i] = interpolate65535(s, d.alpha(), d, 65535 - s.alpha()); - } - } + comp_func_SourceAtop_template<Rgba64Operations>(dest, src, length, const_alpha); } /* @@ -652,69 +865,63 @@ void QT_FASTCALL comp_func_SourceAtop_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const dest = d*sa*ca + s*dia*ca + d *cia = s*ca * dia + d * (sa*ca + cia) */ -void QT_FASTCALL comp_func_solid_DestinationAtop(uint *dest, int length, uint color, uint const_alpha) +template<class Ops> +inline static void comp_func_solid_DestinationAtop_template(typename Ops::Type *dest, int length, typename Ops::Type color, uint const_alpha) { - uint a = qAlpha(color); + auto c = Ops::convert(color); + auto sa = Ops::alpha(c); if (const_alpha != 255) { - color = BYTE_MUL(color, const_alpha); - a = qAlpha(color) + 255 - const_alpha; - } - for (int i = 0; i < length; ++i) { - uint d = dest[i]; - dest[i] = INTERPOLATE_PIXEL_255(d, a, color, qAlpha(~d)); + c = Ops::multiplyAlpha8bit(c, const_alpha); + auto cia = Ops::invAlpha(Ops::scalarFrom8bit(const_alpha)); + sa = Ops::add(Ops::alpha(c), cia); } -} -void QT_FASTCALL comp_func_solid_DestinationAtop_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) -{ - uint a = color.alpha(); - if (const_alpha != 255) { - color = multiplyAlpha255(color, const_alpha); - a = color.alpha() + 65535 - (const_alpha * 257); - } for (int i = 0; i < length; ++i) { - QRgba64 d = dest[i]; - dest[i] = interpolate65535(d, a, color, 65535 - d.alpha()); + auto d = Ops::load(&dest[i]); + Ops::store(&dest[i], Ops::interpolate(c, Ops::invAlpha(d), d, sa)); } } -void QT_FASTCALL comp_func_DestinationAtop(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +template<class Ops> +inline static void comp_func_DestinationAtop_template(typename Ops::Type *Q_DECL_RESTRICT dest, + const typename Ops::Type *Q_DECL_RESTRICT src, + int length, uint const_alpha) { if (const_alpha == 255) { for (int i = 0; i < length; ++i) { - uint s = src[i]; - uint d = dest[i]; - dest[i] = INTERPOLATE_PIXEL_255(d, qAlpha(s), s, qAlpha(~d)); + auto s = Ops::load(&src[i]); + auto d = Ops::load(&dest[i]); + Ops::store(&dest[i], Ops::interpolate(s, Ops::invAlpha(d), d, Ops::alpha(s))); } } else { - uint cia = 255 - const_alpha; + auto cia = Ops::invAlpha(Ops::scalarFrom8bit(const_alpha)); for (int i = 0; i < length; ++i) { - uint s = BYTE_MUL(src[i], const_alpha); - uint d = dest[i]; - uint a = qAlpha(s) + cia; - dest[i] = INTERPOLATE_PIXEL_255(d, a, s, qAlpha(~d)); + auto s = Ops::multiplyAlpha8bit(Ops::load(&src[i]), const_alpha); + auto d = Ops::load(&dest[i]); + auto sa = Ops::add(Ops::alpha(s), cia); + Ops::store(&dest[i], Ops::interpolate(s, Ops::invAlpha(d), d, sa)); } } } +void QT_FASTCALL comp_func_solid_DestinationAtop(uint *dest, int length, uint color, uint const_alpha) +{ + comp_func_solid_DestinationAtop_template<Argb32Operations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_solid_DestinationAtop_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +{ + comp_func_solid_DestinationAtop_template<Rgba64Operations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_DestinationAtop(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + comp_func_DestinationAtop_template<Argb32Operations>(dest, src, length, const_alpha); +} + void QT_FASTCALL comp_func_DestinationAtop_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { - if (const_alpha == 255) { - for (int i = 0; i < length; ++i) { - QRgba64 s = src[i]; - QRgba64 d = dest[i]; - dest[i] = interpolate65535(d, s.alpha(), s, 65535 - d.alpha()); - } - } else { - uint ca = const_alpha * 257; - uint cia = 65535 - ca; - for (int i = 0; i < length; ++i) { - QRgba64 s = multiplyAlpha65535(src[i], ca); - QRgba64 d = dest[i]; - uint a = s.alpha() + cia; - dest[i] = interpolate65535(d, a, s, 65535 - d.alpha()); - } - } + comp_func_DestinationAtop_template<Rgba64Operations>(dest, src, length, const_alpha); } /* @@ -723,62 +930,58 @@ void QT_FASTCALL comp_func_DestinationAtop_rgb64(QRgba64 *Q_DECL_RESTRICT dest, = s*ca * dia + d * (sia*ca + cia) = s*ca * dia + d * (1 - sa*ca) */ -void QT_FASTCALL comp_func_solid_XOR(uint *dest, int length, uint color, uint const_alpha) +template<class Ops> +inline static void comp_func_solid_XOR_template(typename Ops::Type *dest, int length, typename Ops::Type color, uint const_alpha) { + auto c = Ops::convert(color); if (const_alpha != 255) - color = BYTE_MUL(color, const_alpha); - uint sia = qAlpha(~color); + c = Ops::multiplyAlpha8bit(c, const_alpha); + auto sia = Ops::invAlpha(c); for (int i = 0; i < length; ++i) { - uint d = dest[i]; - dest[i] = INTERPOLATE_PIXEL_255(color, qAlpha(~d), d, sia); + auto d = Ops::load(&dest[i]); + Ops::store(&dest[i], Ops::interpolate(c, Ops::invAlpha(d), d, sia)); } } -void QT_FASTCALL comp_func_solid_XOR_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) -{ - if (const_alpha != 255) - color = multiplyAlpha255(color, const_alpha); - auto s = CONVERT(color); - auto sia = CONST(65535 - color.alpha()); - for (int i = 0; i < length; ++i) { - auto d = LOAD(&dest[i]); - STORE(&dest[i], interpolate65535(s, INVALPHA(d), d, sia)); - } -} - -void QT_FASTCALL comp_func_XOR(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +template<class Ops> +inline static void comp_func_XOR_template(typename Ops::Type *Q_DECL_RESTRICT dest, + const typename Ops::Type *Q_DECL_RESTRICT src, + int length, uint const_alpha) { if (const_alpha == 255) { for (int i = 0; i < length; ++i) { - uint d = dest[i]; - uint s = src[i]; - dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(~d), d, qAlpha(~s)); + auto d = Ops::load(&dest[i]); + auto s = Ops::load(&src[i]); + Ops::store(&dest[i], Ops::interpolate(s, Ops::invAlpha(d), d, Ops::invAlpha(s))); } } else { for (int i = 0; i < length; ++i) { - uint d = dest[i]; - uint s = BYTE_MUL(src[i], const_alpha); - dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(~d), d, qAlpha(~s)); + auto d = Ops::load(&dest[i]); + auto s = Ops::multiplyAlpha8bit(Ops::load(&src[i]), const_alpha); + Ops::store(&dest[i], Ops::interpolate(s, Ops::invAlpha(d), d, Ops::invAlpha(s))); } } } +void QT_FASTCALL comp_func_solid_XOR(uint *dest, int length, uint color, uint const_alpha) +{ + comp_func_solid_XOR_template<Argb32Operations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_solid_XOR_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +{ + comp_func_solid_XOR_template<Rgba64Operations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_XOR(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + comp_func_XOR_template<Argb32Operations>(dest, src, length, const_alpha); +} + void QT_FASTCALL comp_func_XOR_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { - if (const_alpha == 255) { - for (int i = 0; i < length; ++i) { - auto d = LOAD(&dest[i]); - auto s = LOAD(&src[i]); - STORE(&dest[i], interpolate65535(s, INVALPHA(d), d, INVALPHA(s))); - } - } else { - for (int i = 0; i < length; ++i) { - auto d = LOAD(&dest[i]); - auto s = multiplyAlpha255(LOAD(&src[i]), const_alpha); - STORE(&dest[i], interpolate65535(s, INVALPHA(d), d, INVALPHA(s))); - } - } + comp_func_XOR_template<Rgba64Operations>(dest, src, length, const_alpha); } struct QFullCoverage { @@ -827,84 +1030,67 @@ static inline uint mix_alpha_rgb64(uint da, uint sa) Dca' = Sca.Da + Dca.Sa + Sca.(1 - Da) + Dca.(1 - Sa) = Sca + Dca */ -template <typename T> -Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Plus_impl(uint *dest, int length, uint color, const T &coverage) +template<class Ops> +inline static void comp_func_solid_Plus_template(typename Ops::Type *dest, int length, typename Ops::Type color, uint const_alpha) { - uint s = color; - - for (int i = 0; i < length; ++i) { - uint d = dest[i]; - d = comp_func_Plus_one_pixel(d, s); - coverage.store(&dest[i], d); + auto c = Ops::convert(color); + if (const_alpha == 255) { + for (int i = 0; i < length; ++i) { + auto d = Ops::load(&dest[i]); + d = Ops::plus(d, c); + Ops::store(&dest[i], d); + } + } else { + uint ia = 255 - const_alpha; + for (int i = 0; i < length; ++i) { + auto d = Ops::load(&dest[i]); + d = Ops::interpolate8bit(Ops::plus(d, c), const_alpha, d, ia); + Ops::store(&dest[i], d); + } } } -void QT_FASTCALL comp_func_solid_Plus(uint *dest, int length, uint color, uint const_alpha) +template<class Ops> +inline static void comp_func_Plus_template(typename Ops::Type *Q_DECL_RESTRICT dest, + const typename Ops::Type *Q_DECL_RESTRICT src, + int length, uint const_alpha) { - if (const_alpha == 255) - comp_func_solid_Plus_impl(dest, length, color, QFullCoverage()); - else - comp_func_solid_Plus_impl(dest, length, color, QPartialCoverage(const_alpha)); -} - -void QT_FASTCALL comp_func_solid_Plus_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) -{ - auto b = CONVERT(color); if (const_alpha == 255) { for (int i = 0; i < length; ++i) { - auto a = LOAD(&dest[i]); - a = addWithSaturation(a, b); - STORE(&dest[i], a); + auto d = Ops::load(&dest[i]); + auto s = Ops::load(&src[i]); + d = Ops::plus(d, s); + Ops::store(&dest[i], d); } } else { + uint ia = 255 - const_alpha; for (int i = 0; i < length; ++i) { - auto a = LOAD(&dest[i]); - auto d = addWithSaturation(a, b); - a = interpolate255(d, const_alpha, a, 255 - const_alpha); - STORE(&dest[i], a); + auto d = Ops::load(&dest[i]); + auto s = Ops::load(&src[i]); + d = Ops::interpolate8bit(Ops::plus(d, s), const_alpha, d, ia); + Ops::store(&dest[i], d); } } } -template <typename T> -Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Plus_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) +void QT_FASTCALL comp_func_solid_Plus(uint *dest, int length, uint color, uint const_alpha) { - for (int i = 0; i < length; ++i) { - uint d = dest[i]; - uint s = src[i]; - - d = comp_func_Plus_one_pixel(d, s); + comp_func_solid_Plus_template<Argb32Operations>(dest, length, color, const_alpha); +} - coverage.store(&dest[i], d); - } +void QT_FASTCALL comp_func_solid_Plus_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +{ + comp_func_solid_Plus_template<Rgba64Operations>(dest, length, color, const_alpha); } void QT_FASTCALL comp_func_Plus(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha) { - if (const_alpha == 255) - comp_func_Plus_impl(dest, src, length, QFullCoverage()); - else - comp_func_Plus_impl(dest, src, length, QPartialCoverage(const_alpha)); + comp_func_Plus_template<Argb32Operations>(dest, src, length, const_alpha); } void QT_FASTCALL comp_func_Plus_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha) { - if (const_alpha == 255) { - for (int i = 0; i < length; ++i) { - auto a = LOAD(&dest[i]); - auto b = LOAD(&src[i]); - a = addWithSaturation(a, b); - STORE(&dest[i], a); - } - } else { - for (int i = 0; i < length; ++i) { - auto a = LOAD(&dest[i]); - auto b = LOAD(&src[i]); - auto d = addWithSaturation(a, b); - a = interpolate255(d, const_alpha, a, 255 - const_alpha); - STORE(&dest[i], a); - } - } + comp_func_Plus_template<Rgba64Operations>(dest, src, length, const_alpha); } /* diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index 34847daf55..ff37cb6cd0 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Copyright (C) 2016 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** @@ -73,9 +73,6 @@ enum { half_point = 1 << 15 }; -// must be multiple of 4 for easier SIMD implementations -static const int buffer_size = 2048; - template<QImage::Format> Q_DECL_CONSTEXPR uint redWidth(); template<QImage::Format> Q_DECL_CONSTEXPR uint redShift(); template<QImage::Format> Q_DECL_CONSTEXPR uint greenWidth(); @@ -94,6 +91,10 @@ template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_ARGB4444_Premultiplied> template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_ARGB8555_Premultiplied>() { return 5; } template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_ARGB8565_Premultiplied>() { return 5; } template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_ARGB6666_Premultiplied>() { return 6; } +template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_RGBX8888>() { return 8; } +template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_RGBA8888>() { return 8; } +template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_RGBA8888_Premultiplied>() { return 8; } + template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGB16>() { return 11; } template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGB444>() { return 8; } template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGB555>() { return 10; } @@ -103,6 +104,15 @@ template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_ARGB4444_Premultiplied> template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_ARGB8555_Premultiplied>() { return 18; } template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_ARGB8565_Premultiplied>() { return 19; } template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_ARGB6666_Premultiplied>() { return 12; } +#if Q_BYTE_ORDER == Q_BIG_ENDIAN +template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGBX8888>() { return 24; } +template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGBA8888>() { return 24; } +template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGBA8888_Premultiplied>() { return 24; } +#else +template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGBX8888>() { return 0; } +template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGBA8888>() { return 0; } +template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGBA8888_Premultiplied>() { return 0; } +#endif template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGB16>() { return 6; } template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGB444>() { return 4; } template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGB555>() { return 5; } @@ -112,6 +122,10 @@ template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_ARGB4444_Premultiplie template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_ARGB8555_Premultiplied>() { return 5; } template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_ARGB8565_Premultiplied>() { return 6; } template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_ARGB6666_Premultiplied>() { return 6; } +template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGBX8888>() { return 8; } +template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGBA8888>() { return 8; } +template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGBA8888_Premultiplied>() { return 8; } + template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGB16>() { return 5; } template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGB444>() { return 4; } template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGB555>() { return 5; } @@ -121,6 +135,15 @@ template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_ARGB4444_Premultiplie template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_ARGB8555_Premultiplied>() { return 13; } template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_ARGB8565_Premultiplied>() { return 13; } template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_ARGB6666_Premultiplied>() { return 6; } +#if Q_BYTE_ORDER == Q_BIG_ENDIAN +template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGBX8888>() { return 16; } +template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGBA8888>() { return 16; } +template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGBA8888_Premultiplied>() { return 16; } +#else +template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGBX8888>() { return 8; } +template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGBA8888>() { return 8; } +template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGBA8888_Premultiplied>() { return 8; } +#endif template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGB16>() { return 5; } template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGB444>() { return 4; } template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGB555>() { return 5; } @@ -130,6 +153,10 @@ template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_ARGB4444_Premultiplied template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_ARGB8555_Premultiplied>() { return 5; } template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_ARGB8565_Premultiplied>() { return 5; } template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_ARGB6666_Premultiplied>() { return 6; } +template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGBX8888>() { return 8; } +template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGBA8888>() { return 8; } +template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGBA8888_Premultiplied>() { return 8; } + template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGB16>() { return 0; } template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGB444>() { return 0; } template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGB555>() { return 0; } @@ -139,6 +166,15 @@ template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_ARGB4444_Premultiplied template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_ARGB8555_Premultiplied>() { return 8; } template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_ARGB8565_Premultiplied>() { return 8; } template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_ARGB6666_Premultiplied>() { return 0; } +#if Q_BYTE_ORDER == Q_BIG_ENDIAN +template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGBX8888>() { return 8; } +template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGBA8888>() { return 8; } +template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGBA8888_Premultiplied>() { return 8; } +#else +template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGBX8888>() { return 16; } +template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGBA8888>() { return 16; } +template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGBA8888_Premultiplied>() { return 16; } +#endif template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_RGB16>() { return 0; } template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_RGB444>() { return 0; } template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_RGB555>() { return 0; } @@ -148,6 +184,10 @@ template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_ARGB4444_Premultiplie template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_ARGB8555_Premultiplied>() { return 8; } template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_ARGB8565_Premultiplied>() { return 8; } template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_ARGB6666_Premultiplied>() { return 6; } +template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_RGBX8888>() { return 0; } +template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_RGBA8888>() { return 8; } +template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_RGBA8888_Premultiplied>() { return 8; } + template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGB16>() { return 0; } template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGB444>() { return 0; } template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGB555>() { return 0; } @@ -157,54 +197,129 @@ template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_ARGB4444_Premultiplie template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_ARGB8555_Premultiplied>() { return 0; } template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_ARGB8565_Premultiplied>() { return 0; } template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_ARGB6666_Premultiplied>() { return 18; } +#if Q_BYTE_ORDER == Q_BIG_ENDIAN +template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGBX8888>() { return 0; } +template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGBA8888>() { return 0; } +template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGBA8888_Premultiplied>() { return 0; } +#else +template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGBX8888>() { return 24; } +template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGBA8888>() { return 24; } +template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGBA8888_Premultiplied>() { return 24; } +#endif -template<QImage::Format> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel(); -template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB16>() { return QPixelLayout::BPP16; } -template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB444>() { return QPixelLayout::BPP16; } -template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB555>() { return QPixelLayout::BPP16; } -template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB666>() { return QPixelLayout::BPP24; } -template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB888>() { return QPixelLayout::BPP24; } -template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB4444_Premultiplied>() { return QPixelLayout::BPP16; } -template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB8555_Premultiplied>() { return QPixelLayout::BPP24; } -template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB8565_Premultiplied>() { return QPixelLayout::BPP24; } -template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB6666_Premultiplied>() { return QPixelLayout::BPP24; } +template<QImage::Format> constexpr QPixelLayout::BPP bitsPerPixel(); +template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB16>() { return QPixelLayout::BPP16; } +template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB444>() { return QPixelLayout::BPP16; } +template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB555>() { return QPixelLayout::BPP16; } +template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB666>() { return QPixelLayout::BPP24; } +template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB888>() { return QPixelLayout::BPP24; } +template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB4444_Premultiplied>() { return QPixelLayout::BPP16; } +template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB8555_Premultiplied>() { return QPixelLayout::BPP24; } +template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB8565_Premultiplied>() { return QPixelLayout::BPP24; } +template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB6666_Premultiplied>() { return QPixelLayout::BPP24; } +template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGBX8888>() { return QPixelLayout::BPP32; } +template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGBA8888>() { return QPixelLayout::BPP32; } +template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGBA8888_Premultiplied>() { return QPixelLayout::BPP32; } -template<QImage::Format Format> -static const uint *QT_FASTCALL convertToRGB32(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +typedef const uint *(QT_FASTCALL *FetchPixelsFunc)(uint *buffer, const uchar *src, int index, int count); + +template <QPixelLayout::BPP bpp> static +uint QT_FASTCALL fetchPixel(const uchar *, int) { - auto conversion = [](uint s) { - // MSVC needs these constexpr defined in here otherwise it will create a capture. - Q_CONSTEXPR uint redMask = ((1 << redWidth<Format>()) - 1); - Q_CONSTEXPR uint greenMask = ((1 << greenWidth<Format>()) - 1); - Q_CONSTEXPR uint blueMask = ((1 << blueWidth<Format>()) - 1); + Q_UNREACHABLE(); + return 0; +} - Q_CONSTEXPR uchar redLeftShift = 8 - redWidth<Format>(); - Q_CONSTEXPR uchar greenLeftShift = 8 - greenWidth<Format>(); - Q_CONSTEXPR uchar blueLeftShift = 8 - blueWidth<Format>(); +template <> +inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP1LSB>(const uchar *src, int index) +{ + return (src[index >> 3] >> (index & 7)) & 1; +} - Q_CONSTEXPR uchar redRightShift = 2 * redWidth<Format>() - 8; - Q_CONSTEXPR uchar greenRightShift = 2 * greenWidth<Format>() - 8; - Q_CONSTEXPR uchar blueRightShift = 2 * blueWidth<Format>() - 8; +template <> +inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP1MSB>(const uchar *src, int index) +{ + return (src[index >> 3] >> (~index & 7)) & 1; +} - uint red = (s >> redShift<Format>()) & redMask; - uint green = (s >> greenShift<Format>()) & greenMask; - uint blue = (s >> blueShift<Format>()) & blueMask; +template <> +inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP8>(const uchar *src, int index) +{ + return src[index]; +} - red = ((red << redLeftShift) | (red >> redRightShift)) << 16; - green = ((green << greenLeftShift) | (green >> greenRightShift)) << 8; - blue = (blue << blueLeftShift) | (blue >> blueRightShift); - return 0xff000000 | red | green | blue; - }; +template <> +inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP16>(const uchar *src, int index) +{ + return reinterpret_cast<const quint16 *>(src)[index]; +} + +template <> +inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP24>(const uchar *src, int index) +{ + return reinterpret_cast<const quint24 *>(src)[index]; +} + +template <> +inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP32>(const uchar *src, int index) +{ + return reinterpret_cast<const uint *>(src)[index]; +} - UNALIASED_CONVERSION_LOOP(buffer, src, count, conversion); +template <QPixelLayout::BPP bpp> +inline const uint *QT_FASTCALL fetchPixels(uint *buffer, const uchar *src, int index, int count) +{ + for (int i = 0; i < count; ++i) + buffer[i] = fetchPixel<bpp>(src, index + i); return buffer; } +template <> +inline const uint *QT_FASTCALL fetchPixels<QPixelLayout::BPP32>(uint *, const uchar *src, int index, int) +{ + return reinterpret_cast<const uint *>(src) + index; +} + +template <QPixelLayout::BPP width> static +void QT_FASTCALL storePixel(uchar *dest, int index, uint pixel); + +template <> +inline void QT_FASTCALL storePixel<QPixelLayout::BPP16>(uchar *dest, int index, uint pixel) +{ + reinterpret_cast<quint16 *>(dest)[index] = quint16(pixel); +} + +template <> +inline void QT_FASTCALL storePixel<QPixelLayout::BPP24>(uchar *dest, int index, uint pixel) +{ + reinterpret_cast<quint24 *>(dest)[index] = quint24(pixel); +} + +static FetchPixelsFunc qFetchPixels[QPixelLayout::BPPCount] = { + 0, // BPPNone + fetchPixels<QPixelLayout::BPP1MSB>, // BPP1MSB + fetchPixels<QPixelLayout::BPP1LSB>, // BPP1LSB + fetchPixels<QPixelLayout::BPP8>, // BPP8 + fetchPixels<QPixelLayout::BPP16>, // BPP16 + fetchPixels<QPixelLayout::BPP24>, // BPP24 + fetchPixels<QPixelLayout::BPP32> // BPP32 +}; + +typedef uint (QT_FASTCALL *FetchPixelFunc)(const uchar *src, int index); + +static const FetchPixelFunc qFetchPixel[QPixelLayout::BPPCount] = { + 0, // BPPNone + fetchPixel<QPixelLayout::BPP1MSB>, // BPP1MSB + fetchPixel<QPixelLayout::BPP1LSB>, // BPP1LSB + fetchPixel<QPixelLayout::BPP8>, // BPP8 + fetchPixel<QPixelLayout::BPP16>, // BPP16 + fetchPixel<QPixelLayout::BPP24>, // BPP24 + fetchPixel<QPixelLayout::BPP32> // BPP32 +}; + template<QImage::Format Format> -static const QRgba64 *QT_FASTCALL convertToRGB64(QRgba64 *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +static Q_ALWAYS_INLINE uint convertPixelToRGB32(uint s) { Q_CONSTEXPR uint redMask = ((1 << redWidth<Format>()) - 1); Q_CONSTEXPR uint greenMask = ((1 << greenWidth<Format>()) - 1); @@ -218,23 +333,54 @@ static const QRgba64 *QT_FASTCALL convertToRGB64(QRgba64 *buffer, const uint *sr Q_CONSTEXPR uchar greenRightShift = 2 * greenWidth<Format>() - 8; Q_CONSTEXPR uchar blueRightShift = 2 * blueWidth<Format>() - 8; - for (int i = 0; i < count; ++i) { - uint red = (src[i] >> redShift<Format>()) & redMask; - uint green = (src[i] >> greenShift<Format>()) & greenMask; - uint blue = (src[i] >> blueShift<Format>()) & blueMask; + uint red = (s >> redShift<Format>()) & redMask; + uint green = (s >> greenShift<Format>()) & greenMask; + uint blue = (s >> blueShift<Format>()) & blueMask; + + red = ((red << redLeftShift) | (red >> redRightShift)) << 16; + green = ((green << greenLeftShift) | (green >> greenRightShift)) << 8; + blue = (blue << blueLeftShift) | (blue >> blueRightShift); + return 0xff000000 | red | green | blue; +} + +template<QImage::Format Format> +static void QT_FASTCALL convertToRGB32(uint *buffer, int count, const QVector<QRgb> *) +{ + for (int i = 0; i < count; ++i) + buffer[i] = convertPixelToRGB32<Format>(buffer[i]); +} - red = ((red << redLeftShift) | (red >> redRightShift)); - green = ((green << greenLeftShift) | (green >> greenRightShift)); - blue = (blue << blueLeftShift) | (blue >> blueRightShift); - buffer[i] = QRgba64::fromRgba(red, green, blue, 255); +template<QImage::Format Format> +static const uint *QT_FASTCALL fetchRGBToRGB32(uint *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + constexpr QPixelLayout::BPP BPP = bitsPerPixel<Format>(); +#if defined(__SSE2__) && !defined(__SSSE3__) + if (BPP == QPixelLayout::BPP24) { + // With SSE2 can convertToRGB32 be vectorized, but it takes SSSE3 + // to vectorize the deforested version below. + qFetchPixels[BPP](buffer, src, index, count); + convertToRGB32<Format>(buffer, count, nullptr); + return buffer; } +#endif + for (int i = 0; i < count; ++i) + buffer[i] = convertPixelToRGB32<Format>(fetchPixel<BPP>(src, index + i)); + return buffer; +} + +template<QImage::Format Format> +static const QRgba64 *QT_FASTCALL convertToRGB64(QRgba64 *buffer, const uint *src, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + for (int i = 0; i < count; ++i) + buffer[i] = QRgba64::fromArgb32(convertPixelToRGB32<Format>(src[i])); return buffer; } template<QImage::Format Format> -static const uint *QT_FASTCALL convertARGBPMToARGB32PM(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +static Q_ALWAYS_INLINE uint convertPixelToARGB32PM(uint s) { Q_CONSTEXPR uint alphaMask = ((1 << alphaWidth<Format>()) - 1); Q_CONSTEXPR uint redMask = ((1 << redWidth<Format>()) - 1); @@ -255,34 +401,48 @@ static const uint *QT_FASTCALL convertARGBPMToARGB32PM(uint *buffer, const uint (alphaWidth<Format>() != greenWidth<Format>()) || (alphaWidth<Format>() != blueWidth<Format>()); + uint alpha = (s >> alphaShift<Format>()) & alphaMask; + uint red = (s >> redShift<Format>()) & redMask; + uint green = (s >> greenShift<Format>()) & greenMask; + uint blue = (s >> blueShift<Format>()) & blueMask; + + alpha = (alpha << alphaLeftShift) | (alpha >> alphaRightShift); + red = (red << redLeftShift) | (red >> redRightShift); + green = (green << greenLeftShift) | (green >> greenRightShift); + blue = (blue << blueLeftShift) | (blue >> blueRightShift); + if (mustMin) { - for (int i = 0; i < count; ++i) { - uint alpha = (src[i] >> alphaShift<Format>()) & alphaMask; - uint red = (src[i] >> redShift<Format>()) & redMask; - uint green = (src[i] >> greenShift<Format>()) & greenMask; - uint blue = (src[i] >> blueShift<Format>()) & blueMask; - - alpha = (alpha << alphaLeftShift) | (alpha >> alphaRightShift); - red = qMin(alpha, (red << redLeftShift) | (red >> redRightShift)); - green = qMin(alpha, (green << greenLeftShift) | (green >> greenRightShift)); - blue = qMin(alpha, (blue << blueLeftShift) | (blue >> blueRightShift)); - buffer[i] = (alpha << 24) | (red << 16) | (green << 8) | blue; - } - } else { - for (int i = 0; i < count; ++i) { - uint alpha = (src[i] >> alphaShift<Format>()) & alphaMask; - uint red = (src[i] >> redShift<Format>()) & redMask; - uint green = (src[i] >> greenShift<Format>()) & greenMask; - uint blue = (src[i] >> blueShift<Format>()) & blueMask; - - alpha = ((alpha << alphaLeftShift) | (alpha >> alphaRightShift)) << 24; - red = ((red << redLeftShift) | (red >> redRightShift)) << 16; - green = ((green << greenLeftShift) | (green >> greenRightShift)) << 8; - blue = (blue << blueLeftShift) | (blue >> blueRightShift); - buffer[i] = alpha | red | green | blue; - } + red = qMin(alpha, red); + green = qMin(alpha, green); + blue = qMin(alpha, blue); } + return (alpha << 24) | (red << 16) | (green << 8) | blue; +} + +template<QImage::Format Format> +static void QT_FASTCALL convertARGBPMToARGB32PM(uint *buffer, int count, const QVector<QRgb> *) +{ + for (int i = 0; i < count; ++i) + buffer[i] = convertPixelToARGB32PM<Format>(buffer[i]); +} + +template<QImage::Format Format> +static const uint *QT_FASTCALL fetchARGBPMToARGB32PM(uint *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + constexpr QPixelLayout::BPP BPP = bitsPerPixel<Format>(); +#if defined(__SSE2__) && !defined(__SSSE3__) + if (BPP == QPixelLayout::BPP24) { + // With SSE2 can convertToRGB32 be vectorized, but it takes SSSE3 + // to vectorize the deforested version below. + qFetchPixels[BPP](buffer, src, index, count); + convertARGBPMToARGB32PM<Format>(buffer, count, nullptr); + return buffer; + } +#endif + for (int i = 0; i < count; ++i) + buffer[i] = convertPixelToARGB32PM<Format>(fetchPixel<BPP>(src, index + i)); return buffer; } @@ -290,81 +450,37 @@ template<QImage::Format Format> static const QRgba64 *QT_FASTCALL convertARGBPMToARGB64PM(QRgba64 *buffer, const uint *src, int count, const QVector<QRgb> *, QDitherInfo *) { - Q_CONSTEXPR uint alphaMask = ((1 << alphaWidth<Format>()) - 1); - Q_CONSTEXPR uint redMask = ((1 << redWidth<Format>()) - 1); - Q_CONSTEXPR uint greenMask = ((1 << greenWidth<Format>()) - 1); - Q_CONSTEXPR uint blueMask = ((1 << blueWidth<Format>()) - 1); - - Q_CONSTEXPR uchar alphaLeftShift = 8 - alphaWidth<Format>(); - Q_CONSTEXPR uchar redLeftShift = 8 - redWidth<Format>(); - Q_CONSTEXPR uchar greenLeftShift = 8 - greenWidth<Format>(); - Q_CONSTEXPR uchar blueLeftShift = 8 - blueWidth<Format>(); - - Q_CONSTEXPR uchar alphaRightShift = 2 * alphaWidth<Format>() - 8; - Q_CONSTEXPR uchar redRightShift = 2 * redWidth<Format>() - 8; - Q_CONSTEXPR uchar greenRightShift = 2 * greenWidth<Format>() - 8; - Q_CONSTEXPR uchar blueRightShift = 2 * blueWidth<Format>() - 8; - - Q_CONSTEXPR bool mustMin = (alphaWidth<Format>() != redWidth<Format>()) || - (alphaWidth<Format>() != greenWidth<Format>()) || - (alphaWidth<Format>() != blueWidth<Format>()); - - if (mustMin) { - for (int i = 0; i < count; ++i) { - uint alpha = (src[i] >> alphaShift<Format>()) & alphaMask; - uint red = (src[i] >> redShift<Format>()) & redMask; - uint green = (src[i] >> greenShift<Format>()) & greenMask; - uint blue = (src[i] >> blueShift<Format>()) & blueMask; - - alpha = (alpha << alphaLeftShift) | (alpha >> alphaRightShift); - red = qMin(alpha, (red << redLeftShift) | (red >> redRightShift)); - green = qMin(alpha, (green << greenLeftShift) | (green >> greenRightShift)); - blue = qMin(alpha, (blue << blueLeftShift) | (blue >> blueRightShift)); - buffer[i] = QRgba64::fromRgba(red, green, blue, alpha); - } - } else { - for (int i = 0; i < count; ++i) { - uint alpha = (src[i] >> alphaShift<Format>()) & alphaMask; - uint red = (src[i] >> redShift<Format>()) & redMask; - uint green = (src[i] >> greenShift<Format>()) & greenMask; - uint blue = (src[i] >> blueShift<Format>()) & blueMask; - - alpha = (alpha << alphaLeftShift) | (alpha >> alphaRightShift); - red = (red << redLeftShift) | (red >> redRightShift); - green = (green << greenLeftShift) | (green >> greenRightShift); - blue = (blue << blueLeftShift) | (blue >> blueRightShift); - buffer[i] = QRgba64::fromRgba(red, green, blue, alpha); - } - } + for (int i = 0; i < count; ++i) + buffer[i] = QRgba64::fromArgb32(convertPixelToARGB32PM<Format>(src[i])); return buffer; } template<QImage::Format Format, bool fromRGB> -static const uint *QT_FASTCALL convertRGBFromARGB32PM(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *dither) +static void QT_FASTCALL storeRGBFromARGB32PM(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *dither) { Q_CONSTEXPR uchar rWidth = redWidth<Format>(); Q_CONSTEXPR uchar gWidth = greenWidth<Format>(); Q_CONSTEXPR uchar bWidth = blueWidth<Format>(); + constexpr QPixelLayout::BPP BPP = bitsPerPixel<Format>(); // RGB32 -> RGB888 is not a precision loss. if (!dither || (rWidth == 8 && gWidth == 8 && bWidth == 8)) { - auto conversion = [](uint s) { - const uint c = fromRGB ? s : qUnpremultiply(s); - Q_CONSTEXPR uint rMask = (1 << redWidth<Format>()) - 1; - Q_CONSTEXPR uint gMask = (1 << greenWidth<Format>()) - 1; - Q_CONSTEXPR uint bMask = (1 << blueWidth<Format>()) - 1; - Q_CONSTEXPR uchar rRightShift = 24 - redWidth<Format>(); - Q_CONSTEXPR uchar gRightShift = 16 - greenWidth<Format>(); - Q_CONSTEXPR uchar bRightShift = 8 - blueWidth<Format>(); + Q_CONSTEXPR uint rMask = (1 << redWidth<Format>()) - 1; + Q_CONSTEXPR uint gMask = (1 << greenWidth<Format>()) - 1; + Q_CONSTEXPR uint bMask = (1 << blueWidth<Format>()) - 1; + Q_CONSTEXPR uchar rRightShift = 24 - redWidth<Format>(); + Q_CONSTEXPR uchar gRightShift = 16 - greenWidth<Format>(); + Q_CONSTEXPR uchar bRightShift = 8 - blueWidth<Format>(); + for (int i = 0; i < count; ++i) { + const uint c = fromRGB ? src[i] : qUnpremultiply(src[i]); const uint r = ((c >> rRightShift) & rMask) << redShift<Format>(); const uint g = ((c >> gRightShift) & gMask) << greenShift<Format>(); const uint b = ((c >> bRightShift) & bMask) << blueShift<Format>(); - return r | g | b; + storePixel<BPP>(dest, index + i, r | g | b); }; - UNALIASED_CONVERSION_LOOP(buffer, src, count, conversion); } else { // We do ordered dither by using a rounding conversion, but instead of // adding half of input precision, we add the adjusted result from the @@ -384,38 +500,39 @@ static const uint *QT_FASTCALL convertRGBFromARGB32PM(uint *buffer, const uint * r = (r + ((dr - r) >> rWidth) + 1) >> (8 - rWidth); g = (g + ((dg - g) >> gWidth) + 1) >> (8 - gWidth); b = (b + ((db - b) >> bWidth) + 1) >> (8 - bWidth); - buffer[i] = (r << redShift<Format>()) - | (g << greenShift<Format>()) - | (b << blueShift<Format>()); + const uint s = (r << redShift<Format>()) + | (g << greenShift<Format>()) + | (b << blueShift<Format>()); + storePixel<BPP>(dest, index + i, s); } } - return buffer; } template<QImage::Format Format, bool fromRGB> -static const uint *QT_FASTCALL convertARGBPMFromARGB32PM(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *dither) +static void QT_FASTCALL storeARGBPMFromARGB32PM(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *dither) { + constexpr QPixelLayout::BPP BPP = bitsPerPixel<Format>(); if (!dither) { - auto conversion = [](uint c) { - Q_CONSTEXPR uint aMask = (1 << alphaWidth<Format>()) - 1; - Q_CONSTEXPR uint rMask = (1 << redWidth<Format>()) - 1; - Q_CONSTEXPR uint gMask = (1 << greenWidth<Format>()) - 1; - Q_CONSTEXPR uint bMask = (1 << blueWidth<Format>()) - 1; - - Q_CONSTEXPR uchar aRightShift = 32 - alphaWidth<Format>(); - Q_CONSTEXPR uchar rRightShift = 24 - redWidth<Format>(); - Q_CONSTEXPR uchar gRightShift = 16 - greenWidth<Format>(); - Q_CONSTEXPR uchar bRightShift = 8 - blueWidth<Format>(); - - Q_CONSTEXPR uint aOpaque = aMask << alphaShift<Format>(); + Q_CONSTEXPR uint aMask = (1 << alphaWidth<Format>()) - 1; + Q_CONSTEXPR uint rMask = (1 << redWidth<Format>()) - 1; + Q_CONSTEXPR uint gMask = (1 << greenWidth<Format>()) - 1; + Q_CONSTEXPR uint bMask = (1 << blueWidth<Format>()) - 1; + + Q_CONSTEXPR uchar aRightShift = 32 - alphaWidth<Format>(); + Q_CONSTEXPR uchar rRightShift = 24 - redWidth<Format>(); + Q_CONSTEXPR uchar gRightShift = 16 - greenWidth<Format>(); + Q_CONSTEXPR uchar bRightShift = 8 - blueWidth<Format>(); + + Q_CONSTEXPR uint aOpaque = aMask << alphaShift<Format>(); + for (int i = 0; i < count; ++i) { + const uint c = src[i]; const uint a = fromRGB ? aOpaque : (((c >> aRightShift) & aMask) << alphaShift<Format>()); const uint r = ((c >> rRightShift) & rMask) << redShift<Format>(); const uint g = ((c >> gRightShift) & gMask) << greenShift<Format>(); const uint b = ((c >> bRightShift) & bMask) << blueShift<Format>(); - return a | r | g | b; + storePixel<BPP>(dest, index + i, a | r | g | b); }; - UNALIASED_CONVERSION_LOOP(buffer, src, count, conversion); } else { Q_CONSTEXPR uchar aWidth = alphaWidth<Format>(); Q_CONSTEXPR uchar rWidth = redWidth<Format>(); @@ -441,55 +558,117 @@ static const uint *QT_FASTCALL convertARGBPMFromARGB32PM(uint *buffer, const uin r = (r + ((dr - r) >> rWidth) + 1) >> (8 - rWidth); g = (g + ((dg - g) >> gWidth) + 1) >> (8 - gWidth); b = (b + ((db - b) >> bWidth) + 1) >> (8 - bWidth); - buffer[i] = (a << alphaShift<Format>()) - | (r << redShift<Format>()) - | (g << greenShift<Format>()) - | (b << blueShift<Format>()); + uint s = (a << alphaShift<Format>()) + | (r << redShift<Format>()) + | (g << greenShift<Format>()) + | (b << blueShift<Format>()); + storePixel<BPP>(dest, index + i, s); } } - return buffer; } +template<QImage::Format Format> +static void QT_FASTCALL rbSwap(uchar *dst, const uchar *src, int count) +{ + Q_CONSTEXPR uchar aWidth = alphaWidth<Format>(); + Q_CONSTEXPR uchar aShift = alphaShift<Format>(); + Q_CONSTEXPR uchar rWidth = redWidth<Format>(); + Q_CONSTEXPR uchar rShift = redShift<Format>(); + Q_CONSTEXPR uchar gWidth = greenWidth<Format>(); + Q_CONSTEXPR uchar gShift = greenShift<Format>(); + Q_CONSTEXPR uchar bWidth = blueWidth<Format>(); + Q_CONSTEXPR uchar bShift = blueShift<Format>(); #ifdef Q_COMPILER_CONSTEXPR + Q_STATIC_ASSERT(rWidth == bWidth); +#endif + Q_CONSTEXPR uint redBlueMask = (1 << rWidth) - 1; + Q_CONSTEXPR uint alphaGreenMask = (((1 << aWidth) - 1) << aShift) + | (((1 << gWidth) - 1) << gShift); + constexpr QPixelLayout::BPP bpp = bitsPerPixel<Format>(); + + for (int i = 0; i < count; ++i) { + const uint c = fetchPixel<bpp>(src, i); + const uint r = (c >> rShift) & redBlueMask; + const uint b = (c >> bShift) & redBlueMask; + const uint t = (c & alphaGreenMask) + | (r << bShift) + | (b << rShift); + storePixel<bpp>(dst, i, t); + } +} + +static void QT_FASTCALL rbSwap_rgb32(uchar *d, const uchar *s, int count) +{ + const uint *src = reinterpret_cast<const uint *>(s); + uint *dest = reinterpret_cast<uint *>(d); + for (int i = 0; i < count; ++i) { + const uint c = src[i]; + const uint ag = c & 0xff00ff00; + const uint rb = c & 0x00ff00ff; + dest[i] = ag | (rb << 16) | (rb >> 16); + } +} + +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN +template<> +void QT_FASTCALL rbSwap<QImage::Format_RGBA8888>(uchar *d, const uchar *s, int count) +{ + return rbSwap_rgb32(d, s, count); +} +#endif + +static void QT_FASTCALL rbSwap_rgb30(uchar *d, const uchar *s, int count) +{ + const uint *src = reinterpret_cast<const uint *>(s); + uint *dest = reinterpret_cast<uint *>(d); + for (int i = 0; i < count; ++i) + dest[i] = qRgbSwapRgb30(src[i]); +} template<QImage::Format Format> Q_DECL_CONSTEXPR static inline QPixelLayout pixelLayoutRGB() { return QPixelLayout{ - uchar(redWidth<Format>()), uchar(redShift<Format>()), - uchar(greenWidth<Format>()), uchar(greenShift<Format>()), - uchar(blueWidth<Format>()), uchar(blueShift<Format>()), - 0, 0, - false, bitsPerPixel<Format>(), + false, + false, + bitsPerPixel<Format>(), + rbSwap<Format>, convertToRGB32<Format>, - convertRGBFromARGB32PM<Format, false>, - convertRGBFromARGB32PM<Format, true>, - convertToRGB64<Format> + convertToRGB64<Format>, + fetchRGBToRGB32<Format>, + storeRGBFromARGB32PM<Format, false>, + storeRGBFromARGB32PM<Format, true> }; } template<QImage::Format Format> Q_DECL_CONSTEXPR static inline QPixelLayout pixelLayoutARGBPM() { return QPixelLayout{ - uchar(redWidth<Format>()), uchar(redShift<Format>()), - uchar(greenWidth<Format>()), uchar(greenShift<Format>()), - uchar(blueWidth<Format>()), uchar(blueShift<Format>()), - uchar(alphaWidth<Format>()), uchar(alphaShift<Format>()), - true, bitsPerPixel<Format>(), + true, + true, + bitsPerPixel<Format>(), + rbSwap<Format>, convertARGBPMToARGB32PM<Format>, - convertARGBPMFromARGB32PM<Format, false>, - convertARGBPMFromARGB32PM<Format, true>, - convertARGBPMToARGB64PM<Format> + convertARGBPMToARGB64PM<Format>, + fetchARGBPMToARGB32PM<Format>, + storeARGBPMFromARGB32PM<Format, false>, + storeARGBPMFromARGB32PM<Format, true> }; } -#endif - -// To convert in place, let 'dest' and 'src' be the same. -static const uint *QT_FASTCALL convertIndexedToARGB32PM(uint *buffer, const uint *src, int count, - const QVector<QRgb> *clut, QDitherInfo *) +static void QT_FASTCALL convertIndexedToARGB32PM(uint *buffer, int count, const QVector<QRgb> *clut) { for (int i = 0; i < count; ++i) - buffer[i] = qPremultiply(clut->at(src[i])); + buffer[i] = qPremultiply(clut->at(buffer[i])); +} + +template<QPixelLayout::BPP BPP> +static const uint *QT_FASTCALL fetchIndexedToARGB32PM(uint *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *clut, QDitherInfo *) +{ + for (int i = 0; i < count; ++i) { + const uint s = fetchPixel<BPP>(src, index + i); + buffer[i] = qPremultiply(clut->at(s)); + } return buffer; } @@ -501,44 +680,71 @@ static const QRgba64 *QT_FASTCALL convertIndexedToARGB64PM(QRgba64 *buffer, cons return buffer; } -static const uint *QT_FASTCALL convertPassThrough(uint *, const uint *src, int, - const QVector<QRgb> *, QDitherInfo *) +static void QT_FASTCALL convertPassThrough(uint *, int, const QVector<QRgb> *) { - return src; } -static const uint *QT_FASTCALL convertARGB32ToARGB32PM(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +static const uint *QT_FASTCALL fetchPassThrough(uint *, const uchar *src, int index, int, + const QVector<QRgb> *, QDitherInfo *) { - return qt_convertARGB32ToARGB32PM(buffer, src, count); + return reinterpret_cast<const uint *>(src) + index; } -static const uint *QT_FASTCALL convertRGBA8888PMToARGB32PM(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +static void QT_FASTCALL storePassThrough(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) { - UNALIASED_CONVERSION_LOOP(buffer, src, count, RGBA2ARGB); - return buffer; + uint *d = reinterpret_cast<uint *>(dest) + index; + if (d != src) + memcpy(d, src, count * sizeof(uint)); } -static const uint *QT_FASTCALL convertRGBA8888ToARGB32PM(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +static void QT_FASTCALL convertARGB32ToARGB32PM(uint *buffer, int count, const QVector<QRgb> *) { - return qt_convertRGBA8888ToARGB32PM(buffer, src, count); + qt_convertARGB32ToARGB32PM(buffer, buffer, count); } -static const uint *QT_FASTCALL convertAlpha8ToRGB32(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +static const uint *QT_FASTCALL fetchARGB32ToARGB32PM(uint *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + return qt_convertARGB32ToARGB32PM(buffer, reinterpret_cast<const uint *>(src) + index, count); +} + +static void QT_FASTCALL convertRGBA8888PMToARGB32PM(uint *buffer, int count, const QVector<QRgb> *) { for (int i = 0; i < count; ++i) - buffer[i] = qRgba(0, 0, 0, src[i]); + buffer[i] = RGBA2ARGB(buffer[i]); +} + +static const uint *QT_FASTCALL fetchRGBA8888PMToARGB32PM(uint *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + const uint *s = reinterpret_cast<const uint *>(src) + index; + UNALIASED_CONVERSION_LOOP(buffer, s, count, RGBA2ARGB); return buffer; } -static const uint *QT_FASTCALL convertGrayscale8ToRGB32(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +static void QT_FASTCALL convertRGBA8888ToARGB32PM(uint *buffer, int count, const QVector<QRgb> *) +{ + qt_convertRGBA8888ToARGB32PM(buffer, buffer, count); +} + +static const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM(uint *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + return qt_convertRGBA8888ToARGB32PM(buffer, reinterpret_cast<const uint *>(src) + index, count); +} + +static void QT_FASTCALL convertAlpha8ToRGB32(uint *buffer, int count, const QVector<QRgb> *) { for (int i = 0; i < count; ++i) - buffer[i] = qRgb(src[i], src[i], src[i]); + buffer[i] = qRgba(0, 0, 0, buffer[i]); +} + +static const uint *QT_FASTCALL fetchAlpha8ToRGB32(uint *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + for (int i = 0; i < count; ++i) + buffer[i] = qRgba(0, 0, 0, src[index + i]); return buffer; } @@ -550,6 +756,24 @@ static const QRgba64 *QT_FASTCALL convertAlpha8ToRGB64(QRgba64 *buffer, const ui return buffer; } +static void QT_FASTCALL convertGrayscale8ToRGB32(uint *buffer, int count, const QVector<QRgb> *) +{ + for (int i = 0; i < count; ++i) { + const uint s = buffer[i]; + buffer[i] = qRgb(s, s, s); + } +} + +static const uint *QT_FASTCALL fetchGrayscale8ToRGB32(uint *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + for (int i = 0; i < count; ++i) { + const uint s = src[index + i]; + buffer[i] = qRgb(s, s, s); + } + return buffer; +} + static const QRgba64 *QT_FASTCALL convertGrayscale8ToRGB64(QRgba64 *buffer, const uint *src, int count, const QVector<QRgb> *, QDitherInfo *) { @@ -558,19 +782,18 @@ static const QRgba64 *QT_FASTCALL convertGrayscale8ToRGB64(QRgba64 *buffer, cons return buffer; } -static const uint *QT_FASTCALL convertARGB32FromARGB32PM(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +static void QT_FASTCALL storeARGB32FromARGB32PM(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) { - for (int i = 0; i < count; ++i) - buffer[i] = qUnpremultiply(src[i]); - return buffer; + uint *d = reinterpret_cast<uint *>(dest) + index; + UNALIASED_CONVERSION_LOOP(d, src, count, [](uint c) { return qUnpremultiply(c); }); } -static const uint *QT_FASTCALL convertRGBA8888PMFromARGB32PM(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +static void QT_FASTCALL storeRGBA8888PMFromARGB32PM(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) { - UNALIASED_CONVERSION_LOOP(buffer, src, count, ARGB2RGBA); - return buffer; + uint *d = reinterpret_cast<uint *>(dest) + index; + UNALIASED_CONVERSION_LOOP(d, src, count, ARGB2RGBA); } #ifdef __SSE2__ @@ -684,33 +907,39 @@ static const QRgba64 *QT_FASTCALL convertRGBA8888PMToARGB64PM(QRgba64 *buffer, c return buffer; } -static const uint *QT_FASTCALL convertRGBA8888FromARGB32PM(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +static void QT_FASTCALL storeRGBA8888FromARGB32PM(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) { - for (int i = 0; i < count; ++i) - buffer[i] = ARGB2RGBA(qUnpremultiply(src[i])); - return buffer; + uint *d = reinterpret_cast<uint *>(dest) + index; + UNALIASED_CONVERSION_LOOP(d, src, count, [](uint c) { return ARGB2RGBA(qUnpremultiply(c)); }); } -static const uint *QT_FASTCALL convertRGBXFromRGB32(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +static void QT_FASTCALL storeRGBXFromRGB32(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) { - UNALIASED_CONVERSION_LOOP(buffer, src, count, [](uint c) { return ARGB2RGBA(0xff000000 | c); }); - return buffer; + uint *d = reinterpret_cast<uint *>(dest) + index; + UNALIASED_CONVERSION_LOOP(d, src, count, [](uint c) { return ARGB2RGBA(0xff000000 | c); }); } -static const uint *QT_FASTCALL convertRGBXFromARGB32PM(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +static void QT_FASTCALL storeRGBXFromARGB32PM(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + uint *d = reinterpret_cast<uint *>(dest) + index; + UNALIASED_CONVERSION_LOOP(d, src, count, [](uint c) { return ARGB2RGBA(0xff000000 | qUnpremultiply(c)); }); +} + +template<QtPixelOrder PixelOrder> +static void QT_FASTCALL convertA2RGB30PMToARGB32PM(uint *buffer, int count, const QVector<QRgb> *) { for (int i = 0; i < count; ++i) - buffer[i] = ARGB2RGBA(0xff000000 | qUnpremultiply(src[i])); - return buffer; + buffer[i] = qConvertA2rgb30ToArgb32<PixelOrder>(buffer[i]); } template<QtPixelOrder PixelOrder> -static const uint *QT_FASTCALL convertA2RGB30PMToARGB32PM(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *dither) +static const uint *QT_FASTCALL fetchA2RGB30PMToARGB32PM(uint *buffer, const uchar *s, int index, int count, + const QVector<QRgb> *, QDitherInfo *dither) { + const uint *src = reinterpret_cast<const uint *>(s) + index; if (!dither) { UNALIASED_CONVERSION_LOOP(buffer, src, count, qConvertA2rgb30ToArgb32<PixelOrder>); } else { @@ -791,178 +1020,71 @@ static const QRgba64 *QT_FASTCALL convertA2RGB30PMToARGB64PM(QRgba64 *buffer, co } template<QtPixelOrder PixelOrder> -static const uint *QT_FASTCALL convertA2RGB30PMFromARGB32PM(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +static void QT_FASTCALL storeA2RGB30PMFromARGB32PM(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) { - UNALIASED_CONVERSION_LOOP(buffer, src, count, qConvertArgb32ToA2rgb30<PixelOrder>); - return buffer; + uint *d = reinterpret_cast<uint *>(dest) + index; + UNALIASED_CONVERSION_LOOP(d, src, count, qConvertArgb32ToA2rgb30<PixelOrder>); } template<QtPixelOrder PixelOrder> -static const uint *QT_FASTCALL convertRGB30FromRGB32(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +static void QT_FASTCALL storeRGB30FromRGB32(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) { - for (int i = 0; i < count; ++i) - buffer[i] = qConvertRgb32ToRgb30<PixelOrder>(src[i]); - return buffer; + uint *d = reinterpret_cast<uint *>(dest) + index; + UNALIASED_CONVERSION_LOOP(d, src, count, qConvertRgb32ToRgb30<PixelOrder>); } template<QtPixelOrder PixelOrder> -static const uint *QT_FASTCALL convertRGB30FromARGB32PM(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) -{ - UNALIASED_CONVERSION_LOOP(buffer, src, count, qConvertRgb32ToRgb30<PixelOrder>); - return buffer; -} - -static const uint *QT_FASTCALL convertAlpha8FromARGB32PM(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) -{ - for (int i = 0; i < count; ++i) - buffer[i] = qAlpha(src[i]); - return buffer; -} - -static const uint *QT_FASTCALL convertGrayscale8FromRGB32(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +static void QT_FASTCALL storeRGB30FromARGB32PM(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) { - for (int i = 0; i < count; ++i) - buffer[i] = qGray(src[i]); - return buffer; + uint *d = reinterpret_cast<uint *>(dest) + index; + UNALIASED_CONVERSION_LOOP(d, src, count, qConvertRgb32ToRgb30<PixelOrder>); } -static const uint *QT_FASTCALL convertGrayscale8FromARGB32PM(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +static void QT_FASTCALL storeAlpha8FromARGB32PM(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) { for (int i = 0; i < count; ++i) - buffer[i] = qGray(qUnpremultiply(src[i])); - return buffer; + dest[index + i] = qAlpha(src[i]); } -template <QPixelLayout::BPP bpp> static -uint QT_FASTCALL fetchPixel(const uchar *, int) -{ - Q_UNREACHABLE(); - return 0; -} - -template <> -inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP1LSB>(const uchar *src, int index) -{ - return (src[index >> 3] >> (index & 7)) & 1; -} - -template <> -inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP1MSB>(const uchar *src, int index) -{ - return (src[index >> 3] >> (~index & 7)) & 1; -} - -template <> -inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP8>(const uchar *src, int index) -{ - return src[index]; -} - -template <> -inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP16>(const uchar *src, int index) -{ - return reinterpret_cast<const quint16 *>(src)[index]; -} - -template <> -inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP24>(const uchar *src, int index) -{ - return reinterpret_cast<const quint24 *>(src)[index]; -} - -template <> -inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP32>(const uchar *src, int index) -{ - return reinterpret_cast<const uint *>(src)[index]; -} - -template <QPixelLayout::BPP bpp> -inline const uint *QT_FASTCALL fetchPixels(uint *buffer, const uchar *src, int index, int count) +static void QT_FASTCALL storeGrayscale8FromRGB32(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) { for (int i = 0; i < count; ++i) - buffer[i] = fetchPixel<bpp>(src, index + i); - return buffer; + dest[index + i] = qGray(src[i]); } -template <> -inline const uint *QT_FASTCALL fetchPixels<QPixelLayout::BPP32>(uint *, const uchar *src, int index, int) -{ - return reinterpret_cast<const uint *>(src) + index; -} - -template <QPixelLayout::BPP width> static -void QT_FASTCALL storePixel(uchar *dest, int index, uint pixel); - -template <> -inline void QT_FASTCALL storePixel<QPixelLayout::BPP1LSB>(uchar *dest, int index, uint pixel) -{ - if (pixel) - dest[index >> 3] |= 1 << (index & 7); - else - dest[index >> 3] &= ~(1 << (index & 7)); -} - -template <> -inline void QT_FASTCALL storePixel<QPixelLayout::BPP1MSB>(uchar *dest, int index, uint pixel) -{ - if (pixel) - dest[index >> 3] |= 1 << (~index & 7); - else - dest[index >> 3] &= ~(1 << (~index & 7)); -} - -template <> -inline void QT_FASTCALL storePixel<QPixelLayout::BPP8>(uchar *dest, int index, uint pixel) -{ - dest[index] = uchar(pixel); -} - -template <> -inline void QT_FASTCALL storePixel<QPixelLayout::BPP16>(uchar *dest, int index, uint pixel) -{ - reinterpret_cast<quint16 *>(dest)[index] = quint16(pixel); -} - -template <> -inline void QT_FASTCALL storePixel<QPixelLayout::BPP24>(uchar *dest, int index, uint pixel) -{ - reinterpret_cast<quint24 *>(dest)[index] = quint24(pixel); -} - -template <QPixelLayout::BPP width> -inline void QT_FASTCALL storePixels(uchar *dest, const uint *src, int index, int count) +static void QT_FASTCALL storeGrayscale8FromARGB32PM(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) { for (int i = 0; i < count; ++i) - storePixel<width>(dest, index + i, src[i]); + dest[index + i] = qGray(qUnpremultiply(src[i])); } -template <> -inline void QT_FASTCALL storePixels<QPixelLayout::BPP32>(uchar *dest, const uint *src, int index, int count) -{ - memcpy(reinterpret_cast<uint *>(dest) + index, src, count * sizeof(uint)); -} // Note: // convertToArgb32() assumes that no color channel is less than 4 bits. -// convertFromArgb32() assumes that no color channel is more than 8 bits. +// storeRGBFromARGB32PM() assumes that no color channel is more than 8 bits. // QImage::rgbSwapped() assumes that the red and blue color channels have the same number of bits. QPixelLayout qPixelLayouts[QImage::NImageFormats] = { - { 0, 0, 0, 0, 0, 0, 0, 0, false, QPixelLayout::BPPNone, 0, 0, 0, 0 }, // Format_Invalid - { 0, 0, 0, 0, 0, 0, 0, 0, false, QPixelLayout::BPP1MSB, convertIndexedToARGB32PM, 0, 0, convertIndexedToARGB64PM }, // Format_Mono - { 0, 0, 0, 0, 0, 0, 0, 0, false, QPixelLayout::BPP1LSB, convertIndexedToARGB32PM, 0, 0, convertIndexedToARGB64PM }, // Format_MonoLSB - { 0, 0, 0, 0, 0, 0, 0, 0, false, QPixelLayout::BPP8, convertIndexedToARGB32PM, 0, 0, convertIndexedToARGB64PM }, // Format_Indexed8 + { false, false, QPixelLayout::BPPNone, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }, // Format_Invalid + { false, false, QPixelLayout::BPP1MSB, nullptr, convertIndexedToARGB32PM, + convertIndexedToARGB64PM, fetchIndexedToARGB32PM<QPixelLayout::BPP1MSB>, nullptr, nullptr }, // Format_Mono + { false, false, QPixelLayout::BPP1LSB, nullptr, convertIndexedToARGB32PM, + convertIndexedToARGB64PM, fetchIndexedToARGB32PM<QPixelLayout::BPP1LSB>, nullptr, nullptr }, // Format_MonoLSB + { false, false, QPixelLayout::BPP8, nullptr, convertIndexedToARGB32PM, + convertIndexedToARGB64PM, fetchIndexedToARGB32PM<QPixelLayout::BPP8>, nullptr, nullptr }, // Format_Indexed8 // Technically using convertPassThrough to convert from ARGB32PM to RGB32 is wrong, // but everywhere this generic conversion would be wrong is currently overloaded. - { 8, 16, 8, 8, 8, 0, 0, 0, false, QPixelLayout::BPP32, convertPassThrough, convertPassThrough, convertPassThrough, convertRGB32ToRGB64 }, // Format_RGB32 - { 8, 16, 8, 8, 8, 0, 8, 24, false, QPixelLayout::BPP32, convertARGB32ToARGB32PM, convertARGB32FromARGB32PM, convertPassThrough, convertARGB32ToARGB64PM }, // Format_ARGB32 - { 8, 16, 8, 8, 8, 0, 8, 24, true, QPixelLayout::BPP32, convertPassThrough, convertPassThrough, convertPassThrough, convertARGB32PMToARGB64PM }, // Format_ARGB32_Premultiplied -#ifdef Q_COMPILER_CONSTEXPR + { false, false, QPixelLayout::BPP32, rbSwap_rgb32, convertPassThrough, + convertRGB32ToRGB64, fetchPassThrough, storePassThrough, storePassThrough }, // Format_RGB32 + { true, false, QPixelLayout::BPP32, rbSwap_rgb32, convertARGB32ToARGB32PM, + convertARGB32ToARGB64PM, fetchARGB32ToARGB32PM, storeARGB32FromARGB32PM, storePassThrough }, // Format_ARGB32 + { true, true, QPixelLayout::BPP32, rbSwap_rgb32, convertPassThrough, + convertARGB32PMToARGB64PM, fetchPassThrough, storePassThrough, storePassThrough }, // Format_ARGB32_Premultiplied pixelLayoutRGB<QImage::Format_RGB16>(), pixelLayoutARGBPM<QImage::Format_ARGB8565_Premultiplied>(), pixelLayoutRGB<QImage::Format_RGB666>(), @@ -972,109 +1094,44 @@ QPixelLayout qPixelLayouts[QImage::NImageFormats] = { pixelLayoutRGB<QImage::Format_RGB888>(), pixelLayoutRGB<QImage::Format_RGB444>(), pixelLayoutARGBPM<QImage::Format_ARGB4444_Premultiplied>(), -#else - { 5, 11, 6, 5, 5, 0, 0, 0, false, QPixelLayout::BPP16, - convertToRGB32<QImage::Format_RGB16>, - convertRGBFromARGB32PM<QImage::Format_RGB16, false>, - convertRGBFromARGB32PM<QImage::Format_RGB16, true>, - convertToRGB64<QImage::Format_RGB16>, - }, - { 5, 19, 6, 13, 5, 8, 8, 0, true, QPixelLayout::BPP24, - convertARGBPMToARGB32PM<QImage::Format_ARGB8565_Premultiplied>, - convertARGBPMFromARGB32PM<QImage::Format_ARGB8565_Premultiplied, false>, - convertARGBPMFromARGB32PM<QImage::Format_ARGB8565_Premultiplied, true>, - convertARGBPMToARGB64PM<QImage::Format_ARGB8565_Premultiplied>, - }, - { 6, 12, 6, 6, 6, 0, 0, 0, false, QPixelLayout::BPP24, - convertToRGB32<QImage::Format_RGB666>, - convertRGBFromARGB32PM<QImage::Format_RGB666, false>, - convertRGBFromARGB32PM<QImage::Format_RGB666, true>, - convertToRGB64<QImage::Format_RGB666>, - }, - { 6, 12, 6, 6, 6, 0, 6, 18, true, QPixelLayout::BPP24, - convertARGBPMToARGB32PM<QImage::Format_ARGB6666_Premultiplied>, - convertARGBPMFromARGB32PM<QImage::Format_ARGB6666_Premultiplied, false>, - convertARGBPMFromARGB32PM<QImage::Format_ARGB6666_Premultiplied, true>, - convertARGBPMToARGB64PM<QImage::Format_ARGB6666_Premultiplied>, - }, - { 5, 10, 5, 5, 5, 0, 0, 0, false, QPixelLayout::BPP16, - convertToRGB32<QImage::Format_RGB555>, - convertRGBFromARGB32PM<QImage::Format_RGB555, false>, - convertRGBFromARGB32PM<QImage::Format_RGB555, true>, - convertToRGB64<QImage::Format_RGB555>, - }, - { 5, 18, 5, 13, 5, 8, 8, 0, true, QPixelLayout::BPP24, - convertARGBPMToARGB32PM<QImage::Format_ARGB8555_Premultiplied>, - convertARGBPMFromARGB32PM<QImage::Format_ARGB8555_Premultiplied, false>, - convertARGBPMFromARGB32PM<QImage::Format_ARGB8555_Premultiplied, true>, - convertARGBPMToARGB64PM<QImage::Format_ARGB8555_Premultiplied>, - }, - { 8, 16, 8, 8, 8, 0, 0, 0, false, QPixelLayout::BPP24, - convertToRGB32<QImage::Format_RGB888>, - convertRGBFromARGB32PM<QImage::Format_RGB888, false>, - convertRGBFromARGB32PM<QImage::Format_RGB888, true>, - convertToRGB64<QImage::Format_RGB888>, - }, - { 4, 8, 4, 4, 4, 0, 0, 0, false, QPixelLayout::BPP16, - convertToRGB32<QImage::Format_RGB444>, - convertRGBFromARGB32PM<QImage::Format_RGB444, false>, - convertRGBFromARGB32PM<QImage::Format_RGB444, true>, - convertToRGB64<QImage::Format_RGB444>, - }, - { 4, 8, 4, 4, 4, 0, 4, 12, true, QPixelLayout::BPP16, - convertARGBPMToARGB32PM<QImage::Format_ARGB4444_Premultiplied>, - convertARGBPMFromARGB32PM<QImage::Format_ARGB4444_Premultiplied, false>, - convertARGBPMFromARGB32PM<QImage::Format_ARGB4444_Premultiplied, true>, - convertARGBPMToARGB64PM<QImage::Format_ARGB4444_Premultiplied>, - }, -#endif -#if Q_BYTE_ORDER == Q_BIG_ENDIAN - { 8, 24, 8, 16, 8, 8, 0, 0, false, QPixelLayout::BPP32, convertRGBA8888PMToARGB32PM, convertRGBXFromARGB32PM, convertRGBXFromRGB32, convertRGBA8888PMToARGB64PM }, // Format_RGBX8888 - { 8, 24, 8, 16, 8, 8, 8, 0, false, QPixelLayout::BPP32, convertRGBA8888ToARGB32PM, convertRGBA8888FromARGB32PM, convertRGBXFromRGB32, convertRGBA8888ToARGB64PM }, // Format_RGBA8888 - { 8, 24, 8, 16, 8, 8, 8, 0, true, QPixelLayout::BPP32, convertRGBA8888PMToARGB32PM, convertRGBA8888PMFromARGB32PM, convertRGBXFromRGB32, convertRGBA8888PMToARGB64PM}, // Format_RGBA8888_Premultiplied -#else - { 8, 0, 8, 8, 8, 16, 0, 24, false, QPixelLayout::BPP32, convertRGBA8888PMToARGB32PM, convertRGBXFromARGB32PM, convertRGBXFromRGB32, convertRGBA8888PMToARGB64PM }, // Format_RGBX8888 - { 8, 0, 8, 8, 8, 16, 8, 24, false, QPixelLayout::BPP32, convertRGBA8888ToARGB32PM, convertRGBA8888FromARGB32PM, convertRGBXFromRGB32, convertRGBA8888ToARGB64PM }, // Format_RGBA8888 (ABGR32) - { 8, 0, 8, 8, 8, 16, 8, 24, true, QPixelLayout::BPP32, convertRGBA8888PMToARGB32PM, convertRGBA8888PMFromARGB32PM, convertRGBXFromRGB32, convertRGBA8888PMToARGB64PM }, // Format_RGBA8888_Premultiplied -#endif - { 10, 20, 10, 10, 10, 0, 0, 30, false, QPixelLayout::BPP32, convertA2RGB30PMToARGB32PM<PixelOrderBGR>, convertRGB30FromARGB32PM<PixelOrderBGR>, convertRGB30FromRGB32<PixelOrderBGR>, convertA2RGB30PMToARGB64PM<PixelOrderBGR> }, // Format_BGR30 - { 10, 20, 10, 10, 10, 0, 2, 30, true, QPixelLayout::BPP32, convertA2RGB30PMToARGB32PM<PixelOrderBGR>, convertA2RGB30PMFromARGB32PM<PixelOrderBGR>, convertRGB30FromRGB32<PixelOrderBGR>, convertA2RGB30PMToARGB64PM<PixelOrderBGR> }, // Format_A2BGR30_Premultiplied - { 10, 0, 10, 10, 10, 20, 0, 30, false, QPixelLayout::BPP32, convertA2RGB30PMToARGB32PM<PixelOrderRGB>, convertRGB30FromARGB32PM<PixelOrderRGB>, convertRGB30FromRGB32<PixelOrderRGB>, convertA2RGB30PMToARGB64PM<PixelOrderRGB> }, // Format_RGB30 - { 10, 0, 10, 10, 10, 20, 2, 30, true, QPixelLayout::BPP32, convertA2RGB30PMToARGB32PM<PixelOrderRGB>, convertA2RGB30PMFromARGB32PM<PixelOrderRGB>, convertRGB30FromRGB32<PixelOrderRGB>, convertA2RGB30PMToARGB64PM<PixelOrderRGB> }, // Format_A2RGB30_Premultiplied - { 0, 0, 0, 0, 0, 0, 8, 0, true, QPixelLayout::BPP8, convertAlpha8ToRGB32, convertAlpha8FromARGB32PM, 0, convertAlpha8ToRGB64 }, // Format_Alpha8 - { 0, 0, 0, 0, 0, 0, 0, 0, false, QPixelLayout::BPP8, convertGrayscale8ToRGB32, convertGrayscale8FromARGB32PM, convertGrayscale8FromRGB32, convertGrayscale8ToRGB64 } // Format_Grayscale8 -}; - -const FetchPixelsFunc qFetchPixels[QPixelLayout::BPPCount] = { - 0, // BPPNone - fetchPixels<QPixelLayout::BPP1MSB>, // BPP1MSB - fetchPixels<QPixelLayout::BPP1LSB>, // BPP1LSB - fetchPixels<QPixelLayout::BPP8>, // BPP8 - fetchPixels<QPixelLayout::BPP16>, // BPP16 - fetchPixels<QPixelLayout::BPP24>, // BPP24 - fetchPixels<QPixelLayout::BPP32> // BPP32 -}; - -StorePixelsFunc qStorePixels[QPixelLayout::BPPCount] = { - 0, // BPPNone - storePixels<QPixelLayout::BPP1MSB>, // BPP1MSB - storePixels<QPixelLayout::BPP1LSB>, // BPP1LSB - storePixels<QPixelLayout::BPP8>, // BPP8 - storePixels<QPixelLayout::BPP16>, // BPP16 - storePixels<QPixelLayout::BPP24>, // BPP24 - storePixels<QPixelLayout::BPP32> // BPP32 -}; - -typedef uint (QT_FASTCALL *FetchPixelFunc)(const uchar *src, int index); - -static const FetchPixelFunc qFetchPixel[QPixelLayout::BPPCount] = { - 0, // BPPNone - fetchPixel<QPixelLayout::BPP1MSB>, // BPP1MSB - fetchPixel<QPixelLayout::BPP1LSB>, // BPP1LSB - fetchPixel<QPixelLayout::BPP8>, // BPP8 - fetchPixel<QPixelLayout::BPP16>, // BPP16 - fetchPixel<QPixelLayout::BPP24>, // BPP24 - fetchPixel<QPixelLayout::BPP32> // BPP32 + { false, false, QPixelLayout::BPP32, rbSwap<QImage::Format_RGBA8888>, convertRGBA8888PMToARGB32PM, + convertRGBA8888PMToARGB64PM, fetchRGBA8888PMToARGB32PM, storeRGBXFromARGB32PM, storeRGBXFromRGB32 }, // Format_RGBX8888 + { true, false, QPixelLayout::BPP32, rbSwap<QImage::Format_RGBA8888>, convertRGBA8888ToARGB32PM, + convertRGBA8888ToARGB64PM, fetchRGBA8888ToARGB32PM, storeRGBA8888FromARGB32PM, storeRGBXFromRGB32 }, // Format_RGBA8888 + { true, true, QPixelLayout::BPP32, rbSwap<QImage::Format_RGBA8888>, convertRGBA8888PMToARGB32PM, + convertRGBA8888PMToARGB64PM, fetchRGBA8888PMToARGB32PM, storeRGBA8888PMFromARGB32PM, storeRGBXFromRGB32 }, // Format_RGBA8888_Premultiplied + { false, false, QPixelLayout::BPP32, rbSwap_rgb30, + convertA2RGB30PMToARGB32PM<PixelOrderBGR>, + convertA2RGB30PMToARGB64PM<PixelOrderBGR>, + fetchA2RGB30PMToARGB32PM<PixelOrderBGR>, + storeRGB30FromARGB32PM<PixelOrderBGR>, + storeRGB30FromRGB32<PixelOrderBGR> + }, // Format_BGR30 + { true, true, QPixelLayout::BPP32, rbSwap_rgb30, + convertA2RGB30PMToARGB32PM<PixelOrderBGR>, + convertA2RGB30PMToARGB64PM<PixelOrderBGR>, + fetchA2RGB30PMToARGB32PM<PixelOrderBGR>, + storeA2RGB30PMFromARGB32PM<PixelOrderBGR>, + storeRGB30FromRGB32<PixelOrderBGR> + }, // Format_A2BGR30_Premultiplied + { false, false, QPixelLayout::BPP32, rbSwap_rgb30, + convertA2RGB30PMToARGB32PM<PixelOrderRGB>, + convertA2RGB30PMToARGB64PM<PixelOrderRGB>, + fetchA2RGB30PMToARGB32PM<PixelOrderRGB>, + storeRGB30FromARGB32PM<PixelOrderRGB>, + storeRGB30FromRGB32<PixelOrderRGB> + }, // Format_RGB30 + { true, true, QPixelLayout::BPP32, rbSwap_rgb30, + convertA2RGB30PMToARGB32PM<PixelOrderRGB>, + convertA2RGB30PMToARGB64PM<PixelOrderRGB>, + fetchA2RGB30PMToARGB32PM<PixelOrderRGB>, + storeA2RGB30PMFromARGB32PM<PixelOrderRGB>, + storeRGB30FromRGB32<PixelOrderRGB> + }, // Format_A2RGB30_Premultiplied + { true, true, QPixelLayout::BPP8, nullptr, convertAlpha8ToRGB32, + convertAlpha8ToRGB64, fetchAlpha8ToRGB32, storeAlpha8FromARGB32PM, nullptr }, // Format_Alpha8 + { false, false, QPixelLayout::BPP8, nullptr, convertGrayscale8ToRGB32, + convertGrayscale8ToRGB64, fetchGrayscale8ToRGB32, storeGrayscale8FromARGB32PM, storeGrayscale8FromRGB32 } // Format_Grayscale8 }; /* @@ -1124,14 +1181,13 @@ static uint * QT_FASTCALL destFetchRGB16(uint *buffer, QRasterBuffer *rasterBuff static uint *QT_FASTCALL destFetch(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length) { const QPixelLayout *layout = &qPixelLayouts[rasterBuffer->format]; - const uint *ptr = qFetchPixels[layout->bpp](buffer, rasterBuffer->scanLine(y), x, length); - return const_cast<uint *>(layout->convertToARGB32PM(buffer, ptr, length, 0, 0)); + return const_cast<uint *>(layout->fetchToARGB32PM(buffer, rasterBuffer->scanLine(y), x, length, nullptr, nullptr)); } static QRgba64 *QT_FASTCALL destFetch64(QRgba64 *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length) { const QPixelLayout *layout = &qPixelLayouts[rasterBuffer->format]; - uint buffer32[buffer_size]; + uint buffer32[BufferSize]; const uint *ptr = qFetchPixels[layout->bpp](buffer32, rasterBuffer->scanLine(y), x, length); return const_cast<QRgba64 *>(layout->convertToARGB64PM(buffer, ptr, length, 0, 0)); } @@ -1304,22 +1360,12 @@ static void QT_FASTCALL destStoreRGB16(QRasterBuffer *rasterBuffer, int x, int y static void QT_FASTCALL destStore(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length) { - uint buf[buffer_size]; const QPixelLayout *layout = &qPixelLayouts[rasterBuffer->format]; - StorePixelsFunc store = qStorePixels[layout->bpp]; + ConvertAndStorePixelsFunc store = layout->storeFromARGB32PM; + if (!layout->premultiplied && !layout->hasAlphaChannel) + store = layout->storeFromRGB32; uchar *dest = rasterBuffer->scanLine(y); - while (length) { - int l = qMin(length, buffer_size); - const uint *ptr = 0; - if (!layout->premultiplied && !layout->alphaWidth) - ptr = layout->convertFromRGB32(buf, buffer, l, 0, 0); - else - ptr = layout->convertFromARGB32PM(buf, buffer, l, 0, 0); - store(dest, ptr, x, l); - length -= l; - buffer += l; - x += l; - } + store(dest, buffer, x, length, nullptr, nullptr); } static void QT_FASTCALL convertFromRgb64(uint *dest, const QRgba64 *src, int length) @@ -1331,19 +1377,16 @@ static void QT_FASTCALL convertFromRgb64(uint *dest, const QRgba64 *src, int len static void QT_FASTCALL destStore64(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length) { - uint buf[buffer_size]; + uint buf[BufferSize]; const QPixelLayout *layout = &qPixelLayouts[rasterBuffer->format]; - StorePixelsFunc store = qStorePixels[layout->bpp]; + ConvertAndStorePixelsFunc store = layout->storeFromARGB32PM; + if (!layout->premultiplied && !layout->hasAlphaChannel) + store = layout->storeFromRGB32; uchar *dest = rasterBuffer->scanLine(y); while (length) { - int l = qMin(length, buffer_size); - const uint *ptr = 0; + int l = qMin(length, BufferSize); convertFromRgb64(buf, buffer, l); - if (!layout->premultiplied && !layout->alphaWidth) - ptr = layout->convertFromRGB32(buf, buf, l, 0, 0); - else - ptr = layout->convertFromARGB32PM(buf, buf, l, 0, 0); - store(dest, ptr, x, l); + store(dest, buf, x, l, nullptr, nullptr); length -= l; buffer += l; x += l; @@ -1528,8 +1571,7 @@ static const uint *QT_FASTCALL fetchUntransformed(uint *buffer, const Operator * const QSpanData *data, int y, int x, int length) { const QPixelLayout *layout = &qPixelLayouts[data->texture.format]; - const uint *ptr = qFetchPixels[layout->bpp](buffer, data->texture.scanLine(y), x, length); - return layout->convertToARGB32PM(buffer, ptr, length, data->texture.colorTable, 0); + return layout->fetchToARGB32PM(buffer, data->texture.scanLine(y), x, length, data->texture.colorTable, nullptr); } static const uint *QT_FASTCALL fetchUntransformedARGB32PM(uint *, const Operator *, @@ -1554,7 +1596,7 @@ static const QRgba64 *QT_FASTCALL fetchUntransformed64(QRgba64 *buffer, const Op { const QPixelLayout *layout = &qPixelLayouts[data->texture.format]; if (layout->bpp != QPixelLayout::BPP32) { - uint buffer32[buffer_size]; + uint buffer32[BufferSize]; const uint *ptr = qFetchPixels[layout->bpp](buffer32, data->texture.scanLine(y), x, length); return layout->convertToARGB64PM(buffer, ptr, length, data->texture.colorTable, 0); } else { @@ -1652,7 +1694,8 @@ static const uint *QT_FASTCALL fetchTransformed(uint *buffer, const Operator *, ++b; } } - return layout->convertToARGB32PM(buffer, buffer, length, data->texture.colorTable, 0); + layout->convertToARGB32PM(buffer, length, data->texture.colorTable); + return buffer; } template<TextureBlendType blendType> /* either BlendTransformed or BlendTransformedTiled */ @@ -1673,7 +1716,7 @@ static const QRgba64 *QT_FASTCALL fetchTransformed64(QRgba64 *buffer, const Oper FetchPixelFunc fetch = qFetchPixel[layout->bpp]; const QVector<QRgb> *clut = data->texture.colorTable; - uint buffer32[buffer_size]; + uint buffer32[BufferSize]; QRgba64 *b = buffer; if (data->fast_matrix) { // The increment pr x in the scanline @@ -1687,9 +1730,9 @@ static const QRgba64 *QT_FASTCALL fetchTransformed64(QRgba64 *buffer, const Oper int i = 0, j = 0; while (i < length) { - if (j == buffer_size) { - layout->convertToARGB64PM(b, buffer32, buffer_size, clut, 0); - b += buffer_size; + if (j == BufferSize) { + layout->convertToARGB64PM(b, buffer32, BufferSize, clut, 0); + b += BufferSize; j = 0; } int px = fx >> 16; @@ -1725,9 +1768,9 @@ static const QRgba64 *QT_FASTCALL fetchTransformed64(QRgba64 *buffer, const Oper int i = 0, j = 0; while (i < length) { - if (j == buffer_size) { - layout->convertToARGB64PM(b, buffer32, buffer_size, clut, 0); - b += buffer_size; + if (j == BufferSize) { + layout->convertToARGB64PM(b, buffer32, BufferSize, clut, 0); + b += BufferSize; j = 0; } const qreal iw = fw == 0 ? 1 : 1 / fw; @@ -1921,7 +1964,7 @@ inline void fetchTransformedBilinear_pixelBounds<BlendTransformedBilinear>(int, } enum FastTransformTypes { - SimpleUpscaleTransform, + SimpleScaleTransform, UpscaleTransform, DownscaleTransform, RotateTransform, @@ -1929,11 +1972,38 @@ enum FastTransformTypes { NFastTransformTypes }; +// Completes the partial interpolation stored in IntermediateBuffer. +// by performing the x-axis interpolation and joining the RB and AG buffers. +static void QT_FASTCALL intermediate_adder(uint *b, uint *end, const IntermediateBuffer &intermediate, int offset, int &fx, int fdx) +{ +#if defined(QT_COMPILER_SUPPORTS_AVX2) + extern void QT_FASTCALL intermediate_adder_avx2(uint *b, uint *end, const IntermediateBuffer &intermediate, int offset, int &fx, int fdx); + if (qCpuHasFeature(AVX2)) + return intermediate_adder_avx2(b, end, intermediate, offset, fx, fdx); +#endif + + // Switch to intermediate buffer coordinates + fx -= offset * fixed_scale; + + while (b < end) { + const int x = (fx >> 16); + + const uint distx = (fx & 0x0000ffff) >> 8; + const uint idistx = 256 - distx; + const uint rb = (intermediate.buffer_rb[x] * idistx + intermediate.buffer_rb[x + 1] * distx) & 0xff00ff00; + const uint ag = (intermediate.buffer_ag[x] * idistx + intermediate.buffer_ag[x + 1] * distx) & 0xff00ff00; + *b = (rb >> 8) | ag; + b++; + fx += fdx; + } + fx += offset * fixed_scale; +} + typedef void (QT_FASTCALL *BilinearFastTransformHelper)(uint *b, uint *end, const QTextureData &image, int &fx, int &fy, int fdx, int fdy); template<TextureBlendType blendType> -static void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper(uint *b, uint *end, const QTextureData &image, - int &fx, int &fy, int fdx, int /*fdy*/) +static void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_scale_helper(uint *b, uint *end, const QTextureData &image, + int &fx, int &fy, int fdx, int /*fdy*/) { int y1 = (fy >> 16); int y2; @@ -1950,16 +2020,12 @@ static void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper(u const int offset = (fx + adjust) >> 16; int x = offset; - // The idea is first to do the interpolation between the row s1 and the row s2 - // into an intermediate buffer, then we interpolate between two pixel of this buffer. - - // intermediate_buffer[0] is a buffer of red-blue component of the pixel, in the form 0x00RR00BB - // intermediate_buffer[1] is the alpha-green component of the pixel, in the form 0x00AA00GG - // +1 for the last pixel to interpolate with, and +1 for rounding errors. - quint32 intermediate_buffer[2][buffer_size + 2]; - // count is the size used in the intermediate_buffer. + IntermediateBuffer intermediate; + // count is the size used in the intermediate.buffer. int count = (qint64(length) * qAbs(fdx) + fixed_scale - 1) / fixed_scale + 2; - Q_ASSERT(count <= buffer_size + 2); //length is supposed to be <= buffer_size and data->m11 < 1 in this case + // length is supposed to be <= BufferSize either because data->m11 < 1 or + // data->m11 < 2, and any larger buffers split + Q_ASSERT(count <= BufferSize + 2); int f = 0; int lim = count; if (blendType == BlendTransformedBilinearTiled) { @@ -1974,8 +2040,8 @@ static void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper(u quint32 rb = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff; quint32 ag = ((((t>>8) & 0xff00ff) * idisty + ((b>>8) & 0xff00ff) * disty) >> 8) & 0xff00ff; do { - intermediate_buffer[0][f] = rb; - intermediate_buffer[1][f] = ag; + intermediate.buffer_rb[f] = rb; + intermediate.buffer_ag[f] = ag; f++; x++; } while (x < image.x1 && f < lim); @@ -2008,10 +2074,10 @@ static void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper(u // Add the values, and shift to only keep 8 significant bits per colors __m128i rAG =_mm_add_epi16(topAG, bottomAG); rAG = _mm_srli_epi16(rAG, 8); - _mm_storeu_si128((__m128i*)(&intermediate_buffer[1][f]), rAG); + _mm_storeu_si128((__m128i*)(&intermediate.buffer_ag[f]), rAG); __m128i rRB =_mm_add_epi16(topRB, bottomRB); rRB = _mm_srli_epi16(rRB, 8); - _mm_storeu_si128((__m128i*)(&intermediate_buffer[0][f]), rRB); + _mm_storeu_si128((__m128i*)(&intermediate.buffer_rb[f]), rRB); } #elif defined(__ARM_NEON__) const int16x8_t disty_ = vdupq_n_s16(disty); @@ -2038,10 +2104,10 @@ static void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper(u // Add the values, and shift to only keep 8 significant bits per colors int16x8_t rAG = vaddq_s16(topAG, bottomAG); rAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(rAG), 8)); - vst1q_s16((int16_t*)(&intermediate_buffer[1][f]), rAG); + vst1q_s16((int16_t*)(&intermediate.buffer_ag[f]), rAG); int16x8_t rRB = vaddq_s16(topRB, bottomRB); rRB = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(rRB), 8)); - vst1q_s16((int16_t*)(&intermediate_buffer[0][f]), rRB); + vst1q_s16((int16_t*)(&intermediate.buffer_rb[f]), rRB); } #endif } @@ -2055,28 +2121,13 @@ static void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper(u uint t = s1[x]; uint b = s2[x]; - intermediate_buffer[0][f] = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff; - intermediate_buffer[1][f] = ((((t>>8) & 0xff00ff) * idisty + ((b>>8) & 0xff00ff) * disty) >> 8) & 0xff00ff; + intermediate.buffer_rb[f] = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff; + intermediate.buffer_ag[f] = ((((t>>8) & 0xff00ff) * idisty + ((b>>8) & 0xff00ff) * disty) >> 8) & 0xff00ff; x++; } - // Now interpolate the values from the intermediate_buffer to get the final result. - fx -= offset * fixed_scale; // Switch to intermediate buffer coordinates - - while (b < end) { - int x1 = (fx >> 16); - int x2 = x1 + 1; - Q_ASSERT(x1 >= 0); - Q_ASSERT(x2 < count); - - int distx = (fx & 0x0000ffff) >> 8; - int idistx = 256 - distx; - int rb = ((intermediate_buffer[0][x1] * idistx + intermediate_buffer[0][x2] * distx) >> 8) & 0xff00ff; - int ag = (intermediate_buffer[1][x1] * idistx + intermediate_buffer[1][x2] * distx) & 0xff00ff00; - *b = rb | ag; - b++; - fx += fdx; - } + // Now interpolate the values from the intermediate.buffer to get the final result. + intermediate_adder(b, end, intermediate, offset, fx, fdx); } template<TextureBlendType blendType> @@ -2542,14 +2593,14 @@ static void QT_FASTCALL fetchTransformedBilinearARGB32PM_fast_rotate_helper(uint static BilinearFastTransformHelper bilinearFastTransformHelperARGB32PM[2][NFastTransformTypes] = { { - fetchTransformedBilinearARGB32PM_simple_upscale_helper<BlendTransformedBilinear>, + fetchTransformedBilinearARGB32PM_simple_scale_helper<BlendTransformedBilinear>, fetchTransformedBilinearARGB32PM_upscale_helper<BlendTransformedBilinear>, fetchTransformedBilinearARGB32PM_downscale_helper<BlendTransformedBilinear>, fetchTransformedBilinearARGB32PM_rotate_helper<BlendTransformedBilinear>, fetchTransformedBilinearARGB32PM_fast_rotate_helper<BlendTransformedBilinear> }, { - fetchTransformedBilinearARGB32PM_simple_upscale_helper<BlendTransformedBilinearTiled>, + fetchTransformedBilinearARGB32PM_simple_scale_helper<BlendTransformedBilinearTiled>, fetchTransformedBilinearARGB32PM_upscale_helper<BlendTransformedBilinearTiled>, fetchTransformedBilinearARGB32PM_downscale_helper<BlendTransformedBilinearTiled>, fetchTransformedBilinearARGB32PM_rotate_helper<BlendTransformedBilinearTiled>, @@ -2584,7 +2635,13 @@ static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, c if (fdy == 0) { // simple scale, no rotation or shear if (qAbs(fdx) <= fixed_scale) { // simple scale up on X - bilinearFastTransformHelperARGB32PM[tiled][SimpleUpscaleTransform](b, end, data->texture, fx, fy, fdx, fdy); + bilinearFastTransformHelperARGB32PM[tiled][SimpleScaleTransform](b, end, data->texture, fx, fy, fdx, fdy); + } else if (qAbs(fdx) <= 2 * fixed_scale) { + // simple scale down on X, less than 2x + const int mid = (length * 2 < BufferSize) ? length : ((length + 1) / 2); + bilinearFastTransformHelperARGB32PM[tiled][SimpleScaleTransform](buffer, buffer + mid, data->texture, fx, fy, fdx, fdy); + if (mid != length) + bilinearFastTransformHelperARGB32PM[tiled][SimpleScaleTransform](buffer + mid, buffer + length, data->texture, fx, fy, fdx, fdy); } else if (qAbs(data->m22) < qreal(1./8.)) { // scale up more than 8x (on Y) bilinearFastTransformHelperARGB32PM[tiled][UpscaleTransform](b, end, data->texture, fx, fy, fdx, fdy); @@ -2652,16 +2709,13 @@ static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, c return buffer; } -template<TextureBlendType blendType, QPixelLayout::BPP bpp> -static void QT_FASTCALL fetchTransformedBilinear_simple_upscale_helper(uint *b, uint *end, const QTextureData &image, - int &fx, int &fy, int fdx, int /*fdy*/) +template<TextureBlendType blendType> +static void QT_FASTCALL fetchTransformedBilinear_simple_scale_helper(uint *b, uint *end, const QTextureData &image, + int &fx, int &fy, int fdx, int /*fdy*/) { const QPixelLayout *layout = &qPixelLayouts[image.format]; const QVector<QRgb> *clut = image.colorTable; - Q_ASSERT(bpp == QPixelLayout::BPPNone || bpp == layout->bpp); - // When templated 'fetch' should be inlined at compile time: - const FetchPixelsFunc fetch = (bpp == QPixelLayout::BPPNone) ? qFetchPixels[layout->bpp] : fetchPixels<bpp>; - const ConvertFunc convertToARGB32PM = layout->convertToARGB32PM; + const FetchAndConvertPixelsFunc fetch = layout->fetchToARGB32PM; int y1 = (fy >> 16); int y2; @@ -2678,16 +2732,14 @@ static void QT_FASTCALL fetchTransformedBilinear_simple_upscale_helper(uint *b, const int offset = (fx + adjust) >> 16; int x = offset; - // The idea is first to do the interpolation between the row s1 and the row s2 - // into an intermediate buffer, then we interpolate between two pixel of this buffer. - // +1 for the last pixel to interpolate with, and +1 for rounding errors. - uint buf1[buffer_size + 2]; - uint buf2[buffer_size + 2]; + IntermediateBuffer intermediate; + uint *buf1 = intermediate.buffer_rb; + uint *buf2 = intermediate.buffer_ag; const uint *ptr1; const uint *ptr2; int count = (qint64(length) * qAbs(fdx) + fixed_scale - 1) / fixed_scale + 2; - Q_ASSERT(count <= buffer_size + 2); //length is supposed to be <= buffer_size and data->m11 < 1 in this case + Q_ASSERT(count <= BufferSize + 2); if (blendType == BlendTransformedBilinearTiled) { x %= image.width; @@ -2696,10 +2748,8 @@ static void QT_FASTCALL fetchTransformedBilinear_simple_upscale_helper(uint *b, int len1 = qMin(count, image.width - x); int len2 = qMin(x, count - len1); - ptr1 = fetch(buf1, s1, x, len1); - ptr1 = convertToARGB32PM(buf1, ptr1, len1, clut, 0); - ptr2 = fetch(buf2, s2, x, len1); - ptr2 = convertToARGB32PM(buf2, ptr2, len1, clut, 0); + ptr1 = fetch(buf1, s1, x, len1, clut, nullptr); + ptr2 = fetch(buf2, s2, x, len1, clut, nullptr); for (int i = 0; i < len1; ++i) { uint t = ptr1[i]; uint b = ptr2[i]; @@ -2708,10 +2758,8 @@ static void QT_FASTCALL fetchTransformedBilinear_simple_upscale_helper(uint *b, } if (len2) { - ptr1 = fetch(buf1 + len1, s1, 0, len2); - ptr1 = convertToARGB32PM(buf1 + len1, ptr1, len2, clut, 0); - ptr2 = fetch(buf2 + len1, s2, 0, len2); - ptr2 = convertToARGB32PM(buf2 + len1, ptr2, len2, clut, 0); + ptr1 = fetch(buf1 + len1, s1, 0, len2, clut, nullptr); + ptr2 = fetch(buf2 + len1, s2, 0, len2, clut, nullptr); for (int i = 0; i < len2; ++i) { uint t = ptr1[i]; uint b = ptr2[i]; @@ -2719,6 +2767,7 @@ static void QT_FASTCALL fetchTransformedBilinear_simple_upscale_helper(uint *b, buf2[i + len1] = ((((t >> 8) & 0xff00ff) * idisty + ((b >> 8) & 0xff00ff) * disty) >> 8) & 0xff00ff; } } + // Generate the rest by repeatedly repeating the previous set of pixels for (int i = image.width; i < count; ++i) { buf1[i] = buf1[i - image.width]; buf2[i] = buf2[i - image.width]; @@ -2729,10 +2778,8 @@ static void QT_FASTCALL fetchTransformedBilinear_simple_upscale_helper(uint *b, int len = qMax(1, end - start); int leading = start - x; - ptr1 = fetch(buf1 + leading, s1, start, len); - ptr1 = convertToARGB32PM(buf1 + leading, ptr1, len, clut, 0); - ptr2 = fetch(buf2 + leading, s2, start, len); - ptr2 = convertToARGB32PM(buf2 + leading, ptr2, len, clut, 0); + ptr1 = fetch(buf1 + leading, s1, start, len, clut, nullptr); + ptr2 = fetch(buf2 + leading, s2, start, len, clut, nullptr); for (int i = 0; i < len; ++i) { uint t = ptr1[i]; @@ -2751,22 +2798,8 @@ static void QT_FASTCALL fetchTransformedBilinear_simple_upscale_helper(uint *b, } } - // Now interpolate the values from the intermediate_buffer to get the final result. - fx -= offset * fixed_scale; // Switch to intermediate buffer coordinates - - while (b < end) { - int x1 = (fx >> 16); - int x2 = x1 + 1; - Q_ASSERT(x1 >= 0); - Q_ASSERT(x2 < count); - - int distx = (fx & 0x0000ffff) >> 8; - int idistx = 256 - distx; - int rb = ((buf1[x1] * idistx + buf1[x2] * distx) >> 8) & 0xff00ff; - int ag = (buf2[x1] * idistx + buf2[x2] * distx) & 0xff00ff00; - *b++ = rb | ag; - fx += fdx; - } + // Now interpolate the values from the intermediate.buffer to get the final result. + intermediate_adder(b, end, intermediate, offset, fx, fdx); } @@ -2916,18 +2949,23 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper if (fdy == 0) { // simple scale, no rotation or shear if (qAbs(fdx) <= fixed_scale) { // scale up on X - fetchTransformedBilinear_simple_upscale_helper<blendType, bpp>(buffer, buffer + length, data->texture, fx, fy, fdx, fdy); + fetchTransformedBilinear_simple_scale_helper<blendType>(buffer, buffer + length, data->texture, fx, fy, fdx, fdy); + } else if (qAbs(fdx) <= 2 * fixed_scale) { // scale down on X less than 2x + const int mid = (length * 2 < BufferSize) ? length : ((length + 1) / 2); + fetchTransformedBilinear_simple_scale_helper<blendType>(buffer, buffer + mid, data->texture, fx, fy, fdx, fdy); + if (mid != length) + fetchTransformedBilinear_simple_scale_helper<blendType>(buffer + mid, buffer + length, data->texture, fx, fy, fdx, fdy); } else { const BilinearFastTransformFetcher fetcher = fetchTransformedBilinear_fetcher<blendType,bpp>; - uint buf1[buffer_size]; - uint buf2[buffer_size]; + uint buf1[BufferSize]; + uint buf2[BufferSize]; uint *b = buffer; while (length) { - int len = qMin(length, buffer_size / 2); + int len = qMin(length, BufferSize / 2); fetcher(buf1, buf2, len, data->texture, fx, fy, fdx, 0); - layout->convertToARGB32PM(buf1, buf1, len * 2, clut, 0); - layout->convertToARGB32PM(buf2, buf2, len * 2, clut, 0); + layout->convertToARGB32PM(buf1, len * 2, clut); + layout->convertToARGB32PM(buf2, len * 2, clut); if (hasFastInterpolate4() || qAbs(data->m22) < qreal(1./8.)) { // scale up more than 8x (on Y) int disty = (fy & 0x0000ffff) >> 8; @@ -2955,14 +2993,14 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper } else { // rotation or shear const BilinearFastTransformFetcher fetcher = fetchTransformedBilinear_fetcher<blendType,bpp>; - uint buf1[buffer_size]; - uint buf2[buffer_size]; + uint buf1[BufferSize]; + uint buf2[BufferSize]; uint *b = buffer; while (length) { - int len = qMin(length, buffer_size / 2); + int len = qMin(length, BufferSize / 2); fetcher(buf1, buf2, len, data->texture, fx, fy, fdx, fdy); - layout->convertToARGB32PM(buf1, buf1, len * 2, clut, 0); - layout->convertToARGB32PM(buf2, buf2, len * 2, clut, 0); + layout->convertToARGB32PM(buf1, len * 2, clut); + layout->convertToARGB32PM(buf2, len * 2, clut); if (hasFastInterpolate4() || qAbs(data->m11) < qreal(1./8.) || qAbs(data->m22) < qreal(1./8.)) { // If we are zooming more than 8 times, we use 8bit precision for the position. @@ -3009,15 +3047,15 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper qreal fy = data->m22 * cy + data->m12 * cx + data->dy; qreal fw = data->m23 * cy + data->m13 * cx + data->m33; - uint buf1[buffer_size]; - uint buf2[buffer_size]; + uint buf1[BufferSize]; + uint buf2[BufferSize]; uint *b = buffer; - int distxs[buffer_size / 2]; - int distys[buffer_size / 2]; + int distxs[BufferSize / 2]; + int distys[BufferSize / 2]; while (length) { - int len = qMin(length, buffer_size / 2); + int len = qMin(length, BufferSize / 2); for (int i = 0; i < len; ++i) { const qreal iw = fw == 0 ? 1 : 1 / fw; const qreal px = fx * iw - qreal(0.5); @@ -3049,8 +3087,8 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper fw += fdw; } - layout->convertToARGB32PM(buf1, buf1, len * 2, clut, 0); - layout->convertToARGB32PM(buf2, buf2, len * 2, clut, 0); + layout->convertToARGB32PM(buf1, len * 2, clut); + layout->convertToARGB32PM(buf2, len * 2, clut); for (int i = 0; i < len; ++i) { int distx = distxs[i]; @@ -3094,13 +3132,13 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co if (fdy == 0) { //simple scale, no rotation - uint sbuf1[buffer_size]; - uint sbuf2[buffer_size]; - quint64 buf1[buffer_size]; - quint64 buf2[buffer_size]; + uint sbuf1[BufferSize]; + uint sbuf2[BufferSize]; + quint64 buf1[BufferSize]; + quint64 buf2[BufferSize]; QRgba64 *b = buffer; while (length) { - int len = qMin(length, buffer_size / 2); + int len = qMin(length, BufferSize / 2); int disty = (fy & 0x0000ffff); #if defined(__SSE2__) const __m128i vdy = _mm_set1_epi16(disty); @@ -3138,15 +3176,15 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co b += len; } } else { //rotation - uint sbuf1[buffer_size]; - uint sbuf2[buffer_size]; - quint64 buf1[buffer_size]; - quint64 buf2[buffer_size]; + uint sbuf1[BufferSize]; + uint sbuf2[BufferSize]; + quint64 buf1[BufferSize]; + quint64 buf2[BufferSize]; QRgba64 *end = buffer + length; QRgba64 *b = buffer; while (b < end) { - int len = qMin(length, buffer_size / 2); + int len = qMin(length, BufferSize / 2); fetcher(sbuf1, sbuf2, len, data->texture, fx, fy, fdx, fdy); @@ -3177,17 +3215,17 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co qreal fw = data->m23 * cy + data->m13 * cx + data->m33; FetchPixelFunc fetch = qFetchPixel[layout->bpp]; - uint sbuf1[buffer_size]; - uint sbuf2[buffer_size]; - quint64 buf1[buffer_size]; - quint64 buf2[buffer_size]; + uint sbuf1[BufferSize]; + uint sbuf2[BufferSize]; + quint64 buf1[BufferSize]; + quint64 buf2[BufferSize]; QRgba64 *b = buffer; - int distxs[buffer_size / 2]; - int distys[buffer_size / 2]; + int distxs[BufferSize / 2]; + int distys[BufferSize / 2]; while (length) { - int len = qMin(length, buffer_size / 2); + int len = qMin(length, BufferSize / 2); for (int i = 0; i < len; ++i) { const qreal iw = fw == 0 ? 1 : 1 / fw; const qreal px = fx * iw - qreal(0.5); @@ -3292,6 +3330,15 @@ static SourceFetchProc sourceFetchARGB32PM[NBlendTypes] = { fetchTransformedBilinearARGB32PM<BlendTransformedBilinearTiled> // BilinearTiled }; +static SourceFetchProc sourceFetchAny16[NBlendTypes] = { + fetchUntransformed, // Untransformed + fetchUntransformed, // Tiled + fetchTransformed<BlendTransformed, QPixelLayout::BPP16>, // Transformed + fetchTransformed<BlendTransformedTiled, QPixelLayout::BPP16>, // TransformedTiled + fetchTransformedBilinear<BlendTransformedBilinear, QPixelLayout::BPP16>, // TransformedBilinear + fetchTransformedBilinear<BlendTransformedBilinearTiled, QPixelLayout::BPP16> // TransformedBilinearTiled +}; + static SourceFetchProc sourceFetchAny32[NBlendTypes] = { fetchUntransformed, // Untransformed fetchUntransformed, // Tiled @@ -3316,6 +3363,8 @@ static inline SourceFetchProc getSourceFetch(TextureBlendType blendType, QImage: return sourceFetchARGB32PM[blendType]; if (blendType == BlendUntransformed || blendType == BlendTiled) return sourceFetchUntransformed[format]; + if (qPixelLayouts[format].bpp == QPixelLayout::BPP16) + return sourceFetchAny16[blendType]; if (qPixelLayouts[format].bpp == QPixelLayout::BPP32) return sourceFetchAny32[blendType]; return sourceFetchGeneric[blendType]; @@ -3717,7 +3766,7 @@ static void blend_color_generic(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); - uint buffer[buffer_size]; + uint buffer[BufferSize]; Operator op = getOperator(data, spans, count); const uint color = data->solid.color.toArgb32(); @@ -3725,7 +3774,7 @@ void blend_color_generic(int count, const QSpan *spans, void *userData) int x = spans->x; int length = spans->len; while (length) { - int l = qMin(buffer_size, length); + int l = qMin(BufferSize, length); uint *dest = op.destFetch ? op.destFetch(buffer, data->rasterBuffer, x, spans->y, l) : buffer; op.funcSolid(dest, l, color, spans->coverage); if (op.destStore) @@ -3777,14 +3826,27 @@ void blend_color_generic_rgb64(int count, const QSpan *spans, void *userData) return blend_color_generic(count, spans, userData); } - quint64 buffer[buffer_size]; + quint64 buffer[BufferSize]; const QRgba64 color = data->solid.color; + bool solidFill = data->rasterBuffer->compositionMode == QPainter::CompositionMode_Source + || (data->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver && color.isOpaque()); + bool isBpp32 = qPixelLayouts[data->rasterBuffer->format].bpp == QPixelLayout::BPP32; while (count--) { int x = spans->x; int length = spans->len; + if (solidFill && isBpp32 && spans->coverage == 255) { + // If dest doesn't matter we don't need to bother with blending or converting all the identical pixels + if (length > 0) { + op.destStore64(data->rasterBuffer, x, spans->y, &color, 1); + uint *dest = (uint*)data->rasterBuffer->scanLine(spans->y) + x; + qt_memfill32(dest + 1, dest[0], length - 1); + length = 0; + } + } + while (length) { - int l = qMin(buffer_size, length); + int l = qMin(BufferSize, length); QRgba64 *dest = op.destFetch64((QRgba64 *)buffer, data->rasterBuffer, x, spans->y, l); op.funcSolid64(dest, l, color, spans->coverage); op.destStore64(data->rasterBuffer, x, spans->y, dest, l); @@ -3889,7 +3951,7 @@ void handleSpans(int count, const QSpan *spans, const QSpanData *data, T &handle int length = right - x; while (length) { - int l = qMin(buffer_size, length); + int l = qMin(BufferSize, length); length -= l; int process_length = l; @@ -3936,8 +3998,8 @@ struct QBlendBase BlendType *dest; - BlendType buffer[buffer_size]; - BlendType src_buffer[buffer_size]; + BlendType buffer[BufferSize]; + BlendType src_buffer[BufferSize]; }; class BlendSrcGeneric : public QBlendBase<uint> @@ -4021,8 +4083,8 @@ static void blend_untransformed_generic(int count, const QSpan *spans, void *use { QSpanData *data = reinterpret_cast<QSpanData *>(userData); - uint buffer[buffer_size]; - uint src_buffer[buffer_size]; + uint buffer[BufferSize]; + uint src_buffer[BufferSize]; Operator op = getOperator(data, spans, count); const int image_width = data->texture.width; @@ -4046,7 +4108,7 @@ static void blend_untransformed_generic(int count, const QSpan *spans, void *use if (length > 0) { const int coverage = (spans->coverage * data->texture.const_alpha) >> 8; while (length) { - int l = qMin(buffer_size, length); + int l = qMin(BufferSize, length); const uint *src = op.srcFetch(src_buffer, &op, data, sy, sx, l); uint *dest = op.destFetch ? op.destFetch(buffer, data->rasterBuffer, x, spans->y, l) : buffer; op.func(dest, src, l, coverage); @@ -4071,8 +4133,8 @@ static void blend_untransformed_generic_rgb64(int count, const QSpan *spans, voi qCDebug(lcQtGuiDrawHelper, "blend_untransformed_generic_rgb64: unsupported 64-bit blend attempted, falling back to 32-bit"); return blend_untransformed_generic(count, spans, userData); } - quint64 buffer[buffer_size]; - quint64 src_buffer[buffer_size]; + quint64 buffer[BufferSize]; + quint64 src_buffer[BufferSize]; const int image_width = data->texture.width; const int image_height = data->texture.height; @@ -4095,7 +4157,7 @@ static void blend_untransformed_generic_rgb64(int count, const QSpan *spans, voi if (length > 0) { const int coverage = (spans->coverage * data->texture.const_alpha) >> 8; while (length) { - int l = qMin(buffer_size, length); + int l = qMin(BufferSize, length); const QRgba64 *src = op.srcFetch64((QRgba64 *)src_buffer, &op, data, sy, sx, l); QRgba64 *dest = op.destFetch64((QRgba64 *)buffer, data->rasterBuffer, x, spans->y, l); op.func64(dest, src, l, coverage); @@ -4259,8 +4321,8 @@ static void blend_tiled_generic(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); - uint buffer[buffer_size]; - uint src_buffer[buffer_size]; + uint buffer[BufferSize]; + uint src_buffer[BufferSize]; Operator op = getOperator(data, spans, count); const int image_width = data->texture.width; @@ -4286,8 +4348,8 @@ static void blend_tiled_generic(int count, const QSpan *spans, void *userData) const int coverage = (spans->coverage * data->texture.const_alpha) >> 8; while (length) { int l = qMin(image_width - sx, length); - if (buffer_size < l) - l = buffer_size; + if (BufferSize < l) + l = BufferSize; const uint *src = op.srcFetch(src_buffer, &op, data, sy, sx, l); uint *dest = op.destFetch ? op.destFetch(buffer, data->rasterBuffer, x, spans->y, l) : buffer; op.func(dest, src, l, coverage); @@ -4312,8 +4374,8 @@ static void blend_tiled_generic_rgb64(int count, const QSpan *spans, void *userD qCDebug(lcQtGuiDrawHelper, "blend_tiled_generic_rgb64: unsupported 64-bit blend attempted, falling back to 32-bit"); return blend_tiled_generic(count, spans, userData); } - quint64 buffer[buffer_size]; - quint64 src_buffer[buffer_size]; + quint64 buffer[BufferSize]; + quint64 src_buffer[BufferSize]; const int image_width = data->texture.width; const int image_height = data->texture.height; @@ -4325,6 +4387,50 @@ static void blend_tiled_generic_rgb64(int count, const QSpan *spans, void *userD if (yoff < 0) yoff += image_height; + bool isBpp32 = qPixelLayouts[data->rasterBuffer->format].bpp == QPixelLayout::BPP32; + if (op.destFetch64 == destFetch64Undefined && image_width <= BufferSize && isBpp32) { + // If destination isn't blended into the result, we can do the tiling directly on destination pixels. + while (count--) { + int x = spans->x; + int y = spans->y; + int length = spans->len; + int sx = (xoff + spans->x) % image_width; + int sy = (spans->y + yoff) % image_height; + if (sx < 0) + sx += image_width; + if (sy < 0) + sy += image_height; + + int sl = qMin(image_width, length); + if (sx > 0 && sl > 0) { + int l = qMin(image_width - sx, sl); + const QRgba64 *src = op.srcFetch64((QRgba64 *)src_buffer, &op, data, sy, sx, l); + op.destStore64(data->rasterBuffer, x, y, src, l); + x += l; + sx += l; + sl -= l; + if (sx >= image_width) + sx = 0; + } + if (sl > 0) { + Q_ASSERT(sx == 0); + const QRgba64 *src = op.srcFetch64((QRgba64 *)src_buffer, &op, data, sy, sx, sl); + op.destStore64(data->rasterBuffer, x, y, src, sl); + x += sl; + sx += sl; + sl -= sl; + if (sx >= image_width) + sx = 0; + } + uint *dest = (uint*)data->rasterBuffer->scanLine(y) + x - image_width; + for (int i = image_width; i < length; ++i) { + dest[i] = dest[i - image_width]; + } + ++spans; + } + return; + } + while (count--) { int x = spans->x; int length = spans->len; @@ -4338,8 +4444,8 @@ static void blend_tiled_generic_rgb64(int count, const QSpan *spans, void *userD const int coverage = (spans->coverage * data->texture.const_alpha) >> 8; while (length) { int l = qMin(image_width - sx, length); - if (buffer_size < l) - l = buffer_size; + if (BufferSize < l) + l = BufferSize; const QRgba64 *src = op.srcFetch64((QRgba64 *)src_buffer, &op, data, sy, sx, l); QRgba64 *dest = op.destFetch64((QRgba64 *)buffer, data->rasterBuffer, x, spans->y, l); op.func64(dest, src, l, coverage); @@ -4388,8 +4494,8 @@ static void blend_tiled_argb(int count, const QSpan *spans, void *userData) const int coverage = (spans->coverage * data->texture.const_alpha) >> 8; while (length) { int l = qMin(image_width - sx, length); - if (buffer_size < l) - l = buffer_size; + if (BufferSize < l) + l = BufferSize; const uint *src = (const uint *)data->texture.scanLine(sy) + sx; uint *dest = ((uint *)data->rasterBuffer->scanLine(spans->y)) + x; op.func(dest, src, l, coverage); @@ -4448,8 +4554,8 @@ static void blend_tiled_rgb565(int count, const QSpan *spans, void *userData) int tx = x; while (length) { int l = qMin(image_width - sx, length); - if (buffer_size < l) - l = buffer_size; + if (BufferSize < l) + l = BufferSize; quint16 *dest = ((quint16 *)data->rasterBuffer->scanLine(spans->y)) + tx; const quint16 *src = (const quint16 *)data->texture.scanLine(sy) + sx; memcpy(dest, src, l * sizeof(quint16)); @@ -4484,8 +4590,8 @@ static void blend_tiled_rgb565(int count, const QSpan *spans, void *userData) if (alpha > 0) { while (length) { int l = qMin(image_width - sx, length); - if (buffer_size < l) - l = buffer_size; + if (BufferSize < l) + l = BufferSize; quint16 *dest = ((quint16 *)data->rasterBuffer->scanLine(spans->y)) + x; const quint16 *src = (const quint16 *)data->texture.scanLine(sy) + sx; blend_sourceOver_rgb16_rgb16(dest, src, l, alpha, ialpha); @@ -4501,199 +4607,6 @@ static void blend_tiled_rgb565(int count, const QSpan *spans, void *userData) } } -static void blend_transformed_bilinear_rgb565(int count, const QSpan *spans, void *userData) -{ - QSpanData *data = reinterpret_cast<QSpanData*>(userData); - QPainter::CompositionMode mode = data->rasterBuffer->compositionMode; - - if (data->texture.format != QImage::Format_RGB16 - || (mode != QPainter::CompositionMode_SourceOver - && mode != QPainter::CompositionMode_Source)) - { - blend_src_generic(count, spans, userData); - return; - } - - quint16 buffer[buffer_size]; - - const int src_minx = data->texture.x1; - const int src_miny = data->texture.y1; - const int src_maxx = data->texture.x2 - 1; - const int src_maxy = data->texture.y2 - 1; - - if (data->fast_matrix) { - // The increment pr x in the scanline - const int fdx = (int)(data->m11 * fixed_scale); - const int fdy = (int)(data->m12 * fixed_scale); - - while (count--) { - const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8; - const quint8 alpha = (coverage + 1) >> 3; - const quint8 ialpha = 0x20 - alpha; - if (alpha == 0) { - ++spans; - continue; - } - - quint16 *dest = (quint16 *)data->rasterBuffer->scanLine(spans->y) + spans->x; - const qreal cx = spans->x + qreal(0.5); - const qreal cy = spans->y + qreal(0.5); - int x = int((data->m21 * cy - + data->m11 * cx + data->dx) * fixed_scale) - half_point; - int y = int((data->m22 * cy - + data->m12 * cx + data->dy) * fixed_scale) - half_point; - int length = spans->len; - - while (length) { - int l; - quint16 *b; - if (ialpha == 0) { - l = length; - b = dest; - } else { - l = qMin(length, buffer_size); - b = buffer; - } - const quint16 *end = b + l; - - while (b < end) { - int x1 = (x >> 16); - int x2; - int y1 = (y >> 16); - int y2; - - fetchTransformedBilinear_pixelBounds<BlendTransformedBilinear>(0, src_minx, src_maxx, x1, x2); - fetchTransformedBilinear_pixelBounds<BlendTransformedBilinear>(0, src_miny, src_maxy, y1, y2); - - const quint16 *src1 = (const quint16*)data->texture.scanLine(y1); - const quint16 *src2 = (const quint16*)data->texture.scanLine(y2); - quint16 tl = src1[x1]; - const quint16 tr = src1[x2]; - quint16 bl = src2[x1]; - const quint16 br = src2[x2]; - - const uint distxsl8 = x & 0xff00; - const uint distysl8 = y & 0xff00; - const uint distx = distxsl8 >> 8; - const uint disty = distysl8 >> 8; - const uint distxy = distx * disty; - - const uint tlw = 0x10000 - distxsl8 - distysl8 + distxy; // (256 - distx) * (256 - disty) - const uint trw = distxsl8 - distxy; // distx * (256 - disty) - const uint blw = distysl8 - distxy; // (256 - distx) * disty - const uint brw = distxy; // distx * disty - uint red = ((tl & 0xf800) * tlw + (tr & 0xf800) * trw - + (bl & 0xf800) * blw + (br & 0xf800) * brw) & 0xf8000000; - uint green = ((tl & 0x07e0) * tlw + (tr & 0x07e0) * trw - + (bl & 0x07e0) * blw + (br & 0x07e0) * brw) & 0x07e00000; - uint blue = ((tl & 0x001f) * tlw + (tr & 0x001f) * trw - + (bl & 0x001f) * blw + (br & 0x001f) * brw); - *b = quint16((red | green | blue) >> 16); - - ++b; - x += fdx; - y += fdy; - } - - if (ialpha != 0) - blend_sourceOver_rgb16_rgb16(dest, buffer, l, alpha, ialpha); - - dest += l; - length -= l; - } - ++spans; - } - } else { - const qreal fdx = data->m11; - const qreal fdy = data->m12; - const qreal fdw = data->m13; - - while (count--) { - const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8; - const quint8 alpha = (coverage + 1) >> 3; - const quint8 ialpha = 0x20 - alpha; - if (alpha == 0) { - ++spans; - continue; - } - - quint16 *dest = (quint16 *)data->rasterBuffer->scanLine(spans->y) + spans->x; - - const qreal cx = spans->x + qreal(0.5); - const qreal cy = spans->y + qreal(0.5); - - qreal x = data->m21 * cy + data->m11 * cx + data->dx; - qreal y = data->m22 * cy + data->m12 * cx + data->dy; - qreal w = data->m23 * cy + data->m13 * cx + data->m33; - - int length = spans->len; - while (length) { - int l; - quint16 *b; - if (ialpha == 0) { - l = length; - b = dest; - } else { - l = qMin(length, buffer_size); - b = buffer; - } - const quint16 *end = b + l; - - while (b < end) { - const qreal iw = w == 0 ? 1 : 1 / w; - const qreal px = x * iw - qreal(0.5); - const qreal py = y * iw - qreal(0.5); - - int x1 = int(px) - (px < 0); - int x2; - int y1 = int(py) - (py < 0); - int y2; - - fetchTransformedBilinear_pixelBounds<BlendTransformedBilinear>(0, src_minx, src_maxx, x1, x2); - fetchTransformedBilinear_pixelBounds<BlendTransformedBilinear>(0, src_miny, src_maxy, y1, y2); - - const quint16 *src1 = (const quint16 *)data->texture.scanLine(y1); - const quint16 *src2 = (const quint16 *)data->texture.scanLine(y2); - quint16 tl = src1[x1]; - const quint16 tr = src1[x2]; - quint16 bl = src2[x1]; - const quint16 br = src2[x2]; - - const uint distx = uint((px - x1) * 256); - const uint disty = uint((py - y1) * 256); - const uint distxsl8 = distx << 8; - const uint distysl8 = disty << 8; - const uint distxy = distx * disty; - - const uint tlw = 0x10000 - distxsl8 - distysl8 + distxy; // (256 - distx) * (256 - disty) - const uint trw = distxsl8 - distxy; // distx * (256 - disty) - const uint blw = distysl8 - distxy; // (256 - distx) * disty - const uint brw = distxy; // distx * disty - uint red = ((tl & 0xf800) * tlw + (tr & 0xf800) * trw - + (bl & 0xf800) * blw + (br & 0xf800) * brw) & 0xf8000000; - uint green = ((tl & 0x07e0) * tlw + (tr & 0x07e0) * trw - + (bl & 0x07e0) * blw + (br & 0x07e0) * brw) & 0x07e00000; - uint blue = ((tl & 0x001f) * tlw + (tr & 0x001f) * trw - + (bl & 0x001f) * blw + (br & 0x001f) * brw); - *b = quint16((red | green | blue) >> 16); - - ++b; - x += fdx; - y += fdy; - w += fdw; - } - - if (ialpha != 0) - blend_sourceOver_rgb16_rgb16(dest, buffer, l, alpha, ialpha); - - dest += l; - length -= l; - } - ++spans; - } - } -} - static void blend_transformed_argb(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); @@ -4704,7 +4617,7 @@ static void blend_transformed_argb(int count, const QSpan *spans, void *userData } CompositionFunction func = functionForMode[data->rasterBuffer->compositionMode]; - uint buffer[buffer_size]; + uint buffer[BufferSize]; quint32 mask = (data->texture.format == QImage::Format_RGB32) ? 0xff000000 : 0; const int image_x1 = data->texture.x1; @@ -4733,7 +4646,7 @@ static void blend_transformed_argb(int count, const QSpan *spans, void *userData int length = spans->len; const int coverage = (spans->coverage * data->texture.const_alpha) >> 8; while (length) { - int l = qMin(length, buffer_size); + int l = qMin(length, BufferSize); const uint *end = buffer + l; uint *b = buffer; while (b < end) { @@ -4770,7 +4683,7 @@ static void blend_transformed_argb(int count, const QSpan *spans, void *userData int length = spans->len; const int coverage = (spans->coverage * data->texture.const_alpha) >> 8; while (length) { - int l = qMin(length, buffer_size); + int l = qMin(length, BufferSize); const uint *end = buffer + l; uint *b = buffer; while (b < end) { @@ -4809,7 +4722,7 @@ static void blend_transformed_rgb565(int count, const QSpan *spans, void *userDa return; } - quint16 buffer[buffer_size]; + quint16 buffer[BufferSize]; const int image_x1 = data->texture.x1; const int image_y1 = data->texture.y1; const int image_x2 = data->texture.x2 - 1; @@ -4845,7 +4758,7 @@ static void blend_transformed_rgb565(int count, const QSpan *spans, void *userDa l = length; b = dest; } else { - l = qMin(length, buffer_size); + l = qMin(length, BufferSize); b = buffer; } const quint16 *end = b + l; @@ -4900,7 +4813,7 @@ static void blend_transformed_rgb565(int count, const QSpan *spans, void *userDa l = length; b = dest; } else { - l = qMin(length, buffer_size); + l = qMin(length, BufferSize); b = buffer; } const quint16 *end = b + l; @@ -4942,7 +4855,7 @@ static void blend_transformed_tiled_argb(int count, const QSpan *spans, void *us } CompositionFunction func = functionForMode[data->rasterBuffer->compositionMode]; - uint buffer[buffer_size]; + uint buffer[BufferSize]; int image_width = data->texture.width; int image_height = data->texture.height; @@ -4970,7 +4883,7 @@ static void blend_transformed_tiled_argb(int count, const QSpan *spans, void *us const int coverage = (spans->coverage * data->texture.const_alpha) >> 8; int length = spans->len; while (length) { - int l = qMin(length, buffer_size); + int l = qMin(length, BufferSize); const uint *end = buffer + l; uint *b = buffer; int px16 = x % (image_width << 16); @@ -5024,7 +4937,7 @@ static void blend_transformed_tiled_argb(int count, const QSpan *spans, void *us const int coverage = (spans->coverage * data->texture.const_alpha) >> 8; int length = spans->len; while (length) { - int l = qMin(length, buffer_size); + int l = qMin(length, BufferSize); const uint *end = buffer + l; uint *b = buffer; while (b < end) { @@ -5075,7 +4988,7 @@ static void blend_transformed_tiled_rgb565(int count, const QSpan *spans, void * return; } - quint16 buffer[buffer_size]; + quint16 buffer[BufferSize]; const int image_width = data->texture.width; const int image_height = data->texture.height; @@ -5109,7 +5022,7 @@ static void blend_transformed_tiled_rgb565(int count, const QSpan *spans, void * l = length; b = dest; } else { - l = qMin(length, buffer_size); + l = qMin(length, BufferSize); b = buffer; } const quint16 *end = b + l; @@ -5169,7 +5082,7 @@ static void blend_transformed_tiled_rgb565(int count, const QSpan *spans, void * l = length; b = dest; } else { - l = qMin(length, buffer_size); + l = qMin(length, BufferSize); b = buffer; } const quint16 *end = b + l; @@ -5227,7 +5140,7 @@ static const ProcessSpans processTextureSpansRGB16[NBlendTypes] = { blend_tiled_rgb565, // Tiled blend_transformed_rgb565, // Transformed blend_transformed_tiled_rgb565, // TransformedTiled - blend_transformed_bilinear_rgb565, // TransformedBilinear + blend_src_generic, // TransformedBilinear blend_src_generic // TransformedBilinearTiled }; @@ -5523,7 +5436,7 @@ static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer, srcColor = colorProfile->toLinear(srcColor.unpremultiplied()).premultiplied(); } - quint64 buffer[buffer_size]; + quint64 buffer[BufferSize]; const DestFetchProc64 destFetch64 = destFetchProc64[rasterBuffer->format]; const DestStoreProc64 destStore64 = destStoreProc64[rasterBuffer->format]; @@ -5532,7 +5445,7 @@ static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer, int i = x; int length = mapWidth; while (length > 0) { - int l = qMin(buffer_size, length); + int l = qMin(BufferSize, length); QRgba64 *dest = destFetch64((QRgba64*)buffer, rasterBuffer, i, y + ly, l); for (int j=0; j < l; ++j) { const int coverage = map[j + (i - x)]; @@ -5561,7 +5474,7 @@ static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer, int end = qMin<int>(x + mapWidth, clip.x + clip.len); if (end <= start) continue; - Q_ASSERT(end - start <= buffer_size); + Q_ASSERT(end - start <= BufferSize); QRgba64 *dest = destFetch64((QRgba64*)buffer, rasterBuffer, start, clip.y, end - start); for (int xp=start; xp<end; ++xp) { @@ -5800,7 +5713,7 @@ static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer, srcColor = colorProfile->toLinear(srcColor.unpremultiplied()).premultiplied(); } - quint64 buffer[buffer_size]; + quint64 buffer[BufferSize]; const DestFetchProc64 destFetch64 = destFetchProc64[rasterBuffer->format]; const DestStoreProc64 destStore64 = destStoreProc64[rasterBuffer->format]; @@ -5809,7 +5722,7 @@ static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer, int i = x; int length = mapWidth; while (length > 0) { - int l = qMin(buffer_size, length); + int l = qMin(BufferSize, length); QRgba64 *dest = destFetch64((QRgba64*)buffer, rasterBuffer, i, y + ly, l); for (int j=0; j < l; ++j) { const uint coverage = src[j + (i - x)]; @@ -5838,7 +5751,7 @@ static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer, int end = qMin<int>(x + mapWidth, clip.x + clip.len); if (end <= start) continue; - Q_ASSERT(end - start <= buffer_size); + Q_ASSERT(end - start <= BufferSize); QRgba64 *dest = destFetch64((QRgba64*)buffer, rasterBuffer, start, clip.y, end - start); for (int xp=start; xp<end; ++xp) { @@ -5928,8 +5841,24 @@ static void qt_rectfill_quint16(QRasterBuffer *rasterBuffer, int x, int y, int width, int height, const QRgba64 &color) { + const QPixelLayout &layout = qPixelLayouts[rasterBuffer->format]; + quint32 c32 = color.toArgb32(); + quint16 c16; + layout.storeFromARGB32PM(reinterpret_cast<uchar *>(&c16), &c32, 0, 1, nullptr, nullptr); qt_rectfill<quint16>(reinterpret_cast<quint16 *>(rasterBuffer->buffer()), - color.toRgb16(), x, y, width, height, rasterBuffer->bytesPerLine()); + c16, x, y, width, height, rasterBuffer->bytesPerLine()); +} + +static void qt_rectfill_quint24(QRasterBuffer *rasterBuffer, + int x, int y, int width, int height, + const QRgba64 &color) +{ + const QPixelLayout &layout = qPixelLayouts[rasterBuffer->format]; + quint32 c32 = color.toArgb32(); + quint24 c24; + layout.storeFromARGB32PM(reinterpret_cast<uchar *>(&c24), &c32, 0, 1, nullptr, nullptr); + qt_rectfill<quint24>(reinterpret_cast<quint24 *>(rasterBuffer->buffer()), + c24, x, y, width, height, rasterBuffer->bytesPerLine()); } static void qt_rectfill_nonpremul_argb32(QRasterBuffer *rasterBuffer, @@ -6049,7 +5978,7 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = 0, qt_alphamapblit_generic, qt_alphargbblit_generic, - 0 + qt_rectfill_quint24 }, // Format_RGB666 { @@ -6058,7 +5987,7 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = 0, qt_alphamapblit_generic, qt_alphargbblit_generic, - 0 + qt_rectfill_quint24 }, // Format_ARGB6666_Premultiplied { @@ -6067,7 +5996,7 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = 0, qt_alphamapblit_generic, qt_alphargbblit_generic, - 0 + qt_rectfill_quint24 }, // Format_RGB555 { @@ -6076,7 +6005,7 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = 0, qt_alphamapblit_generic, qt_alphargbblit_generic, - 0 + qt_rectfill_quint16 }, // Format_ARGB8555_Premultiplied { @@ -6085,7 +6014,7 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = 0, qt_alphamapblit_generic, qt_alphargbblit_generic, - 0 + qt_rectfill_quint24 }, // Format_RGB888 { @@ -6094,7 +6023,7 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = 0, qt_alphamapblit_generic, qt_alphargbblit_generic, - 0 + qt_rectfill_quint24 }, // Format_RGB444 { @@ -6103,7 +6032,7 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = 0, qt_alphamapblit_generic, qt_alphargbblit_generic, - 0 + qt_rectfill_quint16 }, // Format_ARGB4444_Premultiplied { @@ -6112,7 +6041,7 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = 0, qt_alphamapblit_generic, qt_alphargbblit_generic, - 0 + qt_rectfill_quint16 }, // Format_RGBX8888 { @@ -6267,7 +6196,7 @@ void qt_memfill32(quint32 *dest, quint32 color, int count) #endif #ifdef QT_COMPILER_SUPPORTS_SSE4_1 -template<QtPixelOrder> const uint *QT_FASTCALL convertA2RGB30PMFromARGB32PM_sse4(uint *buffer, const uint *src, int count, const QVector<QRgb> *, QDitherInfo *); +template<QtPixelOrder> void QT_FASTCALL storeA2RGB30PMFromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count, const QVector<QRgb> *, QDitherInfo *); #endif extern void qInitBlendFunctions(); @@ -6336,37 +6265,47 @@ static void qInitDrawhelperFunctions() int w, int h, int const_alpha); - extern void QT_FASTCALL storePixelsBPP24_ssse3(uchar *dest, const uint *src, int index, int count); extern const uint * QT_FASTCALL qt_fetchUntransformed_888_ssse3(uint *buffer, const Operator *, const QSpanData *data, int y, int x, int length); qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_ssse3; qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_ssse3; qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_ssse3; qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_ssse3; - qStorePixels[QPixelLayout::BPP24] = storePixelsBPP24_ssse3; sourceFetchUntransformed[QImage::Format_RGB888] = qt_fetchUntransformed_888_ssse3; +#ifndef __SSSE3__ + extern const uint * QT_FASTCALL fetchPixelsBPP24_ssse3(uint *dest, const uchar*src, int index, int count); + qFetchPixels[QPixelLayout::BPP24] = fetchPixelsBPP24_ssse3; +#endif } #endif // SSSE3 #if defined(QT_COMPILER_SUPPORTS_SSE4_1) if (qCpuHasFeature(SSE4_1)) { - extern const uint *QT_FASTCALL convertARGB32ToARGB32PM_sse4(uint *buffer, const uint *src, int count, + extern void QT_FASTCALL convertARGB32ToARGB32PM_sse4(uint *buffer, int count, const QVector<QRgb> *); + extern void QT_FASTCALL convertRGBA8888ToARGB32PM_sse4(uint *buffer, int count, const QVector<QRgb> *); + extern const uint *QT_FASTCALL fetchARGB32ToARGB32PM_sse4(uint *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *); + extern const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_sse4(uint *buffer, const uchar *src, int index, int count, const QVector<QRgb> *, QDitherInfo *); - extern const uint *QT_FASTCALL convertRGBA8888ToARGB32PM_sse4(uint *buffer, const uint *src, int count, + extern void QT_FASTCALL storeARGB32FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count, const QVector<QRgb> *, QDitherInfo *); - extern const uint *QT_FASTCALL convertARGB32FromARGB32PM_sse4(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *); - extern const uint *QT_FASTCALL convertRGBA8888FromARGB32PM_sse4(uint *buffer, const uint *src, int count, + extern void QT_FASTCALL storeRGBA8888FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count, const QVector<QRgb> *, QDitherInfo *); - extern const uint *QT_FASTCALL convertRGBXFromARGB32PM_sse4(uint *buffer, const uint *src, int count, + extern void QT_FASTCALL storeRGBXFromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count, const QVector<QRgb> *, QDitherInfo *); + extern void QT_FASTCALL destStore64ARGB32_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length); + extern void QT_FASTCALL destStore64RGBA8888_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length); + qPixelLayouts[QImage::Format_ARGB32].fetchToARGB32PM = fetchARGB32ToARGB32PM_sse4; qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_sse4; + qPixelLayouts[QImage::Format_RGBA8888].fetchToARGB32PM = fetchRGBA8888ToARGB32PM_sse4; qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_sse4; - qPixelLayouts[QImage::Format_ARGB32].convertFromARGB32PM = convertARGB32FromARGB32PM_sse4; - qPixelLayouts[QImage::Format_RGBA8888].convertFromARGB32PM = convertRGBA8888FromARGB32PM_sse4; - qPixelLayouts[QImage::Format_RGBX8888].convertFromARGB32PM = convertRGBXFromARGB32PM_sse4; - qPixelLayouts[QImage::Format_A2BGR30_Premultiplied].convertFromARGB32PM = convertA2RGB30PMFromARGB32PM_sse4<PixelOrderBGR>; - qPixelLayouts[QImage::Format_A2RGB30_Premultiplied].convertFromARGB32PM = convertA2RGB30PMFromARGB32PM_sse4<PixelOrderRGB>; + qPixelLayouts[QImage::Format_ARGB32].storeFromARGB32PM = storeARGB32FromARGB32PM_sse4; + qPixelLayouts[QImage::Format_RGBA8888].storeFromARGB32PM = storeRGBA8888FromARGB32PM_sse4; + qPixelLayouts[QImage::Format_RGBX8888].storeFromARGB32PM = storeRGBXFromARGB32PM_sse4; + qPixelLayouts[QImage::Format_A2BGR30_Premultiplied].storeFromARGB32PM = storeA2RGB30PMFromARGB32PM_sse4<PixelOrderBGR>; + qPixelLayouts[QImage::Format_A2RGB30_Premultiplied].storeFromARGB32PM = storeA2RGB30PMFromARGB32PM_sse4<PixelOrderRGB>; + destStoreProc64[QImage::Format_ARGB32] = destStore64ARGB32_sse4; + destStoreProc64[QImage::Format_RGBA8888] = destStore64RGBA8888_sse4; } #endif @@ -6401,14 +6340,14 @@ static void qInitDrawhelperFunctions() qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_avx2; qt_functionForModeSolid64_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_rgb64_avx2; - extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper_avx2(uint *b, uint *end, const QTextureData &image, - int &fx, int &fy, int fdx, int /*fdy*/); + extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_scale_helper_avx2(uint *b, uint *end, const QTextureData &image, + int &fx, int &fy, int fdx, int /*fdy*/); extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_downscale_helper_avx2(uint *b, uint *end, const QTextureData &image, int &fx, int &fy, int fdx, int /*fdy*/); extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_fast_rotate_helper_avx2(uint *b, uint *end, const QTextureData &image, int &fx, int &fy, int fdx, int fdy); - bilinearFastTransformHelperARGB32PM[0][SimpleUpscaleTransform] = fetchTransformedBilinearARGB32PM_simple_upscale_helper_avx2; + bilinearFastTransformHelperARGB32PM[0][SimpleScaleTransform] = fetchTransformedBilinearARGB32PM_simple_scale_helper_avx2; bilinearFastTransformHelperARGB32PM[0][DownscaleTransform] = fetchTransformedBilinearARGB32PM_downscale_helper_avx2; bilinearFastTransformHelperARGB32PM[0][FastRotateTransform] = fetchTransformedBilinearARGB32PM_fast_rotate_helper_avx2; } @@ -6440,10 +6379,8 @@ static void qInitDrawhelperFunctions() sourceFetchUntransformed[QImage::Format_RGB888] = qt_fetchUntransformed_888_neon; #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - extern const uint *QT_FASTCALL convertARGB32ToARGB32PM_neon(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *); - extern const uint *QT_FASTCALL convertRGBA8888ToARGB32PM_neon(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *); + extern void QT_FASTCALL convertARGB32ToARGB32PM_neon(uint *buffer, int count, const QVector<QRgb> *); + extern void QT_FASTCALL convertRGBA8888ToARGB32PM_neon(uint *buffer, int count, const QVector<QRgb> *); qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_neon; qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_neon; #endif diff --git a/src/gui/painting/qdrawhelper_avx2.cpp b/src/gui/painting/qdrawhelper_avx2.cpp index 3a70524a9d..ec6643deed 100644 --- a/src/gui/painting/qdrawhelper_avx2.cpp +++ b/src/gui/painting/qdrawhelper_avx2.cpp @@ -45,8 +45,6 @@ QT_BEGIN_NAMESPACE -static Q_CONSTEXPR int BufferSize = 2048; - enum { FixedScale = 1 << 16, HalfPoint = 1 << 15 @@ -576,8 +574,10 @@ inline void fetchTransformedBilinear_pixelBounds(int, int l1, int l2, int &v1, i Q_ASSERT(v2 >= l1 && v2 <= l2); } -void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper_avx2(uint *b, uint *end, const QTextureData &image, - int &fx, int &fy, int fdx, int /*fdy*/) +void QT_FASTCALL intermediate_adder_avx2(uint *b, uint *end, const IntermediateBuffer &intermediate, int offset, int &fx, int fdx); + +void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_scale_helper_avx2(uint *b, uint *end, const QTextureData &image, + int &fx, int &fy, int fdx, int /*fdy*/) { int y1 = (fy >> 16); int y2; @@ -594,16 +594,12 @@ void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper_avx2(uin const int offset = (fx + adjust) >> 16; int x = offset; - // The idea is first to do the interpolation between the row s1 and the row s2 - // into an intermediate buffer, then we interpolate between two pixel of this buffer. - - // intermediate_buffer[0] is a buffer of red-blue component of the pixel, in the form 0x00RR00BB - // intermediate_buffer[1] is the alpha-green component of the pixel, in the form 0x00AA00GG - // +1 for the last pixel to interpolate with, and +1 for rounding errors. - quint32 intermediate_buffer[2][BufferSize + 2]; + IntermediateBuffer intermediate; // count is the size used in the intermediate_buffer. int count = (qint64(length) * qAbs(fdx) + FixedScale - 1) / FixedScale + 2; - Q_ASSERT(count <= BufferSize + 2); //length is supposed to be <= buffer_size and data->m11 < 1 in this case + // length is supposed to be <= BufferSize either because data->m11 < 1 or + // data->m11 < 2, and any larger buffers split + Q_ASSERT(count <= BufferSize + 2); int f = 0; int lim = qMin(count, image.x2 - x); if (x < image.x1) { @@ -613,8 +609,8 @@ void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper_avx2(uin quint32 rb = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff; quint32 ag = ((((t>>8) & 0xff00ff) * idisty + ((b>>8) & 0xff00ff) * disty) >> 8) & 0xff00ff; do { - intermediate_buffer[0][f] = rb; - intermediate_buffer[1][f] = ag; + intermediate.buffer_rb[f] = rb; + intermediate.buffer_ag[f] = ag; f++; x++; } while (x < image.x1 && f < lim); @@ -644,10 +640,10 @@ void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper_avx2(uin // Add the values, and shift to only keep 8 significant bits per colors __m256i rAG =_mm256_add_epi16(topAG, bottomAG); rAG = _mm256_srli_epi16(rAG, 8); - _mm256_storeu_si256((__m256i*)(&intermediate_buffer[1][f]), rAG); + _mm256_storeu_si256((__m256i*)(&intermediate.buffer_ag[f]), rAG); __m256i rRB =_mm256_add_epi16(topRB, bottomRB); rRB = _mm256_srli_epi16(rRB, 8); - _mm256_storeu_si256((__m256i*)(&intermediate_buffer[0][f]), rRB); + _mm256_storeu_si256((__m256i*)(&intermediate.buffer_rb[f]), rRB); } for (; f < count; f++) { // Same as above but without simd @@ -656,30 +652,37 @@ void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper_avx2(uin uint t = s1[x]; uint b = s2[x]; - intermediate_buffer[0][f] = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff; - intermediate_buffer[1][f] = ((((t>>8) & 0xff00ff) * idisty + ((b>>8) & 0xff00ff) * disty) >> 8) & 0xff00ff; + intermediate.buffer_rb[f] = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff; + intermediate.buffer_ag[f] = ((((t>>8) & 0xff00ff) * idisty + ((b>>8) & 0xff00ff) * disty) >> 8) & 0xff00ff; x++; } + // Now interpolate the values from the intermediate_buffer to get the final result. + intermediate_adder_avx2(b, end, intermediate, offset, fx, fdx); +} + +void QT_FASTCALL intermediate_adder_avx2(uint *b, uint *end, const IntermediateBuffer &intermediate, int offset, int &fx, int fdx) +{ fx -= offset * FixedScale; const __m128i v_fdx = _mm_set1_epi32(fdx * 4); const __m128i v_blend = _mm_set1_epi32(0x00800080); + const __m128i vdx_shuffle = _mm_set_epi8(char(0x80), 13, char(0x80), 13, char(0x80), 9, char(0x80), 9, + char(0x80), 5, char(0x80), 5, char(0x80), 1, char(0x80), 1); __m128i v_fx = _mm_setr_epi32(fx, fx + fdx, fx + fdx + fdx, fx + fdx + fdx + fdx); while (b < end - 3) { const __m128i offset = _mm_srli_epi32(v_fx, 16); - __m256i vrb = _mm256_i32gather_epi64((const long long *)intermediate_buffer[0], offset, 4); - __m256i vag = _mm256_i32gather_epi64((const long long *)intermediate_buffer[1], offset, 4); + __m256i vrb = _mm256_i32gather_epi64((const long long *)intermediate.buffer_rb, offset, 4); + __m256i vag = _mm256_i32gather_epi64((const long long *)intermediate.buffer_ag, offset, 4); - __m128i vdx = _mm_and_si128(v_fx, _mm_set1_epi32(0x0000ffff)); - vdx = _mm_srli_epi16(vdx, 8); - __m128i vidx = _mm_sub_epi32(_mm_set1_epi32(256), vdx); + __m128i vdx = _mm_shuffle_epi8(v_fx, vdx_shuffle); + __m128i vidx = _mm_sub_epi16(_mm_set1_epi16(256), vdx); __m256i vmulx = _mm256_castsi128_si256(_mm_unpacklo_epi32(vidx, vdx)); vmulx = _mm256_inserti128_si256(vmulx, _mm_unpackhi_epi32(vidx, vdx), 1); - vrb = _mm256_mullo_epi32(vrb, vmulx); - vag = _mm256_mullo_epi32(vag, vmulx); + vrb = _mm256_mullo_epi16(vrb, vmulx); + vag = _mm256_mullo_epi16(vag, vmulx); __m256i vrbag = _mm256_hadd_epi32(vrb, vag); vrbag = _mm256_permute4x64_epi64(vrbag, _MM_SHUFFLE(3, 1, 2, 0)); @@ -691,21 +694,21 @@ void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_upscale_helper_avx2(uin _mm_storeu_si128((__m128i*)b, _mm_blendv_epi8(ag, rb, v_blend)); b += 4; - fx += 4 * fdx; v_fx = _mm_add_epi32(v_fx, v_fdx); } + fx = _mm_cvtsi128_si32(v_fx); while (b < end) { - int x = (fx >> 16); - - uint distx = (fx & 0x0000ffff) >> 8; - uint idistx = 256 - distx; + const int x = (fx >> 16); - uint rb = ((intermediate_buffer[0][x] * idistx + intermediate_buffer[0][x + 1] * distx) >> 8) & 0xff00ff; - uint ag = (intermediate_buffer[1][x] * idistx + intermediate_buffer[1][x + 1] * distx) & 0xff00ff00; - *b = rb | ag; + const uint distx = (fx & 0x0000ffff) >> 8; + const uint idistx = 256 - distx; + const uint rb = (intermediate.buffer_rb[x] * idistx + intermediate.buffer_rb[x + 1] * distx) & 0xff00ff00; + const uint ag = (intermediate.buffer_ag[x] * idistx + intermediate.buffer_ag[x + 1] * distx) & 0xff00ff00; + *b = (rb >> 8) | ag; b++; fx += fdx; } + fx += offset * FixedScale; } void QT_FASTCALL fetchTransformedBilinearARGB32PM_downscale_helper_avx2(uint *b, uint *end, const QTextureData &image, diff --git a/src/gui/painting/qdrawhelper_neon.cpp b/src/gui/painting/qdrawhelper_neon.cpp index e126f4b670..44d4037c0d 100644 --- a/src/gui/painting/qdrawhelper_neon.cpp +++ b/src/gui/painting/qdrawhelper_neon.cpp @@ -1153,18 +1153,14 @@ static inline void convertARGBToARGB32PM_neon(uint *buffer, const uint *src, int } } -const uint *QT_FASTCALL convertARGB32ToARGB32PM_neon(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +void QT_FASTCALL convertARGB32ToARGB32PM_neon(uint *buffer, int count, const QVector<QRgb> *) { - convertARGBToARGB32PM_neon<false>(buffer, src, count); - return buffer; + convertARGBToARGB32PM_neon<false>(buffer, buffer, count); } -const uint *QT_FASTCALL convertRGBA8888ToARGB32PM_neon(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +void QT_FASTCALL convertRGBA8888ToARGB32PM_neon(uint *buffer, int count, const QVector<QRgb> *) { - convertARGBToARGB32PM_neon<true>(buffer, src, count); - return buffer; + convertARGBToARGB32PM_neon<true>(buffer, buffer, count); } #endif // Q_BYTE_ORDER == Q_LITTLE_ENDIAN diff --git a/src/gui/painting/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h index 6f3c92ca64..bddd6899e3 100644 --- a/src/gui/painting/qdrawhelper_p.h +++ b/src/gui/painting/qdrawhelper_p.h @@ -798,6 +798,7 @@ static Q_ALWAYS_INLINE uint qAlphaRgb30(uint c) } struct quint24 { + quint24() = default; quint24(uint value); operator uint() const; uchar data[3]; @@ -1205,15 +1206,37 @@ inline uint comp_func_Plus_one_pixel(uint d, const uint s) #undef MIX #undef AMIX +// must be multiple of 4 for easier SIMD implementations +static Q_CONSTEXPR int BufferSize = 2048; + +// A buffer of intermediate results used by simple bilinear scaling. +struct IntermediateBuffer +{ + // The idea is first to do the interpolation between the row s1 and the row s2 + // into this intermediate buffer, then later interpolate between two pixel of this buffer. + // + // buffer_rb is a buffer of red-blue component of the pixel, in the form 0x00RR00BB + // buffer_ag is the alpha-green component of the pixel, in the form 0x00AA00GG + // +1 for the last pixel to interpolate with, and +1 for rounding errors. + quint32 buffer_rb[BufferSize+2]; + quint32 buffer_ag[BufferSize+2]; +}; + struct QDitherInfo { int x; int y; }; -typedef const uint *(QT_FASTCALL *ConvertFunc)(uint *buffer, const uint *src, int count, - const QVector<QRgb> *clut, QDitherInfo *dither); +typedef const uint *(QT_FASTCALL *FetchAndConvertPixelsFunc)(uint *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *clut, QDitherInfo *dither); +typedef void (QT_FASTCALL *ConvertAndStorePixelsFunc)(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *clut, QDitherInfo *dither); + +typedef void (QT_FASTCALL *ConvertFunc)(uint *buffer, int count, const QVector<QRgb> *clut); typedef const QRgba64 *(QT_FASTCALL *ConvertFunc64)(QRgba64 *buffer, const uint *src, int count, const QVector<QRgb> *clut, QDitherInfo *dither); +typedef void (QT_FASTCALL *RbSwapFunc)(uchar *dst, const uchar *src, int count); + struct QPixelLayout { @@ -1229,29 +1252,18 @@ struct QPixelLayout BPPCount }; - // All numbers in bits. - uchar redWidth; - uchar redShift; - uchar greenWidth; - uchar greenShift; - uchar blueWidth; - uchar blueShift; - uchar alphaWidth; - uchar alphaShift; + bool hasAlphaChannel; bool premultiplied; BPP bpp; + RbSwapFunc rbSwap; ConvertFunc convertToARGB32PM; - ConvertFunc convertFromARGB32PM; - ConvertFunc convertFromRGB32; ConvertFunc64 convertToARGB64PM; + FetchAndConvertPixelsFunc fetchToARGB32PM; + ConvertAndStorePixelsFunc storeFromARGB32PM; + ConvertAndStorePixelsFunc storeFromRGB32; }; -typedef const uint *(QT_FASTCALL *FetchPixelsFunc)(uint *buffer, const uchar *src, int index, int count); -typedef void (QT_FASTCALL *StorePixelsFunc)(uchar *dest, const uint *src, int index, int count); - extern QPixelLayout qPixelLayouts[QImage::NImageFormats]; -extern const FetchPixelsFunc qFetchPixels[QPixelLayout::BPPCount]; -extern StorePixelsFunc qStorePixels[QPixelLayout::BPPCount]; extern MemRotateFunc qMemRotateFunctions[QPixelLayout::BPPCount][3]; diff --git a/src/gui/painting/qdrawhelper_sse4.cpp b/src/gui/painting/qdrawhelper_sse4.cpp index 14bfaabf09..0b6f963168 100644 --- a/src/gui/painting/qdrawhelper_sse4.cpp +++ b/src/gui/painting/qdrawhelper_sse4.cpp @@ -39,13 +39,14 @@ #include <private/qdrawhelper_p.h> #include <private/qdrawingprimitive_sse2_p.h> +#include <private/qpaintengine_raster_p.h> #if defined(QT_COMPILER_SUPPORTS_SSE4_1) QT_BEGIN_NAMESPACE template<bool RGBA> -static inline void convertARGBToARGB32PM_sse4(uint *buffer, const uint *src, int count) +static void convertARGBToARGB32PM_sse4(uint *buffer, const uint *src, int count) { int i = 0; const __m128i alphaMask = _mm_set1_epi32(0xff000000); @@ -83,7 +84,7 @@ static inline void convertARGBToARGB32PM_sse4(uint *buffer, const uint *src, int _mm_storeu_si128((__m128i *)&buffer[i], srcVector); } } else { - _mm_storeu_si128((__m128i *)&buffer[i], _mm_setzero_si128()); + _mm_storeu_si128((__m128i *)&buffer[i], zero); } } @@ -93,59 +94,250 @@ static inline void convertARGBToARGB32PM_sse4(uint *buffer, const uint *src, int } } -const uint *QT_FASTCALL convertARGB32ToARGB32PM_sse4(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +static inline __m128 reciprocal_mul_ps(__m128 a, float mul) { - convertARGBToARGB32PM_sse4<false>(buffer, src, count); - return buffer; + __m128 ia = _mm_rcp_ps(a); // Approximate 1/a + // Improve precision of ia using Newton-Raphson + ia = _mm_sub_ps(_mm_add_ps(ia, ia), _mm_mul_ps(ia, _mm_mul_ps(ia, a))); + ia = _mm_mul_ps(ia, _mm_set1_ps(mul)); + return ia; } -const uint *QT_FASTCALL convertRGBA8888ToARGB32PM_sse4(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +template<bool RGBA, bool RGBx> +static inline void convertARGBFromARGB32PM_sse4(uint *buffer, const uint *src, int count) { - convertARGBToARGB32PM_sse4<true>(buffer, src, count); - return buffer; + int i = 0; + const __m128i alphaMask = _mm_set1_epi32(0xff000000); + const __m128i rgbaMask = _mm_setr_epi8(2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15); + const __m128i zero = _mm_setzero_si128(); + + for (; i < count - 3; i += 4) { + __m128i srcVector = _mm_loadu_si128((const __m128i *)&src[i]); + if (!_mm_testz_si128(srcVector, alphaMask)) { + if (!_mm_testc_si128(srcVector, alphaMask)) { + __m128i srcVectorAlpha = _mm_srli_epi32(srcVector, 24); + if (RGBA) + srcVector = _mm_shuffle_epi8(srcVector, rgbaMask); + const __m128 a = _mm_cvtepi32_ps(srcVectorAlpha); + const __m128 ia = reciprocal_mul_ps(a, 255.0f); + __m128i src1 = _mm_unpacklo_epi8(srcVector, zero); + __m128i src3 = _mm_unpackhi_epi8(srcVector, zero); + __m128i src2 = _mm_unpackhi_epi16(src1, zero); + __m128i src4 = _mm_unpackhi_epi16(src3, zero); + src1 = _mm_unpacklo_epi16(src1, zero); + src3 = _mm_unpacklo_epi16(src3, zero); + __m128 ia1 = _mm_shuffle_ps(ia, ia, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 ia2 = _mm_shuffle_ps(ia, ia, _MM_SHUFFLE(1, 1, 1, 1)); + __m128 ia3 = _mm_shuffle_ps(ia, ia, _MM_SHUFFLE(2, 2, 2, 2)); + __m128 ia4 = _mm_shuffle_ps(ia, ia, _MM_SHUFFLE(3, 3, 3, 3)); + src1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(src1), ia1)); + src2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(src2), ia2)); + src3 = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(src3), ia3)); + src4 = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(src4), ia4)); + src1 = _mm_packus_epi32(src1, src2); + src3 = _mm_packus_epi32(src3, src4); + src1 = _mm_packus_epi16(src1, src3); + // Handle potential alpha == 0 values: + __m128i srcVectorAlphaMask = _mm_cmpeq_epi32(srcVectorAlpha, zero); + src1 = _mm_andnot_si128(srcVectorAlphaMask, src1); + // Fixup alpha values: + if (RGBx) + srcVector = _mm_or_si128(src1, alphaMask); + else + srcVector = _mm_blendv_epi8(src1, srcVector, alphaMask); + _mm_storeu_si128((__m128i *)&buffer[i], srcVector); + } else { + if (RGBA) + _mm_storeu_si128((__m128i *)&buffer[i], _mm_shuffle_epi8(srcVector, rgbaMask)); + else if (buffer != src) + _mm_storeu_si128((__m128i *)&buffer[i], srcVector); + } + } else { + if (RGBx) + _mm_storeu_si128((__m128i *)&buffer[i], alphaMask); + else + _mm_storeu_si128((__m128i *)&buffer[i], zero); + } + } + + SIMD_EPILOGUE(i, count, 3) { + uint v = qUnpremultiply_sse4(src[i]); + if (RGBx) + v = 0xff000000 | v; + if (RGBA) + v = ARGB2RGBA(v); + buffer[i] = v; + } } -const uint *QT_FASTCALL convertARGB32FromARGB32PM_sse4(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +template<bool RGBA> +static inline void convertARGBFromRGBA64PM_sse4(uint *buffer, const QRgba64 *src, int count) { - for (int i = 0; i < count; ++i) - buffer[i] = qUnpremultiply_sse4(src[i]); - return buffer; + int i = 0; + const __m128i alphaMask = _mm_set1_epi64x(Q_UINT64_C(0xffff) << 48); + const __m128i alphaMask32 = _mm_set1_epi32(0xff000000); + const __m128i rgbaMask = _mm_setr_epi8(2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15); + const __m128i zero = _mm_setzero_si128(); + + for (; i < count - 3; i += 4) { + __m128i srcVector1 = _mm_loadu_si128((const __m128i *)&src[i]); + __m128i srcVector2 = _mm_loadu_si128((const __m128i *)&src[i + 2]); + bool transparent1 = _mm_testz_si128(srcVector1, alphaMask); + bool opaque1 = _mm_testc_si128(srcVector1, alphaMask); + bool transparent2 = _mm_testz_si128(srcVector2, alphaMask); + bool opaque2 = _mm_testc_si128(srcVector2, alphaMask); + + if (!(transparent1 && transparent2)) { + if (!(opaque1 && opaque2)) { + __m128i srcVector1Alpha = _mm_srli_epi64(srcVector1, 48); + __m128i srcVector2Alpha = _mm_srli_epi64(srcVector2, 48); + __m128i srcVectorAlpha = _mm_packus_epi32(srcVector1Alpha, srcVector2Alpha); + const __m128 a = _mm_cvtepi32_ps(srcVectorAlpha); + // Convert srcVectorAlpha to final 8-bit alpha channel + srcVectorAlpha = _mm_add_epi32(srcVectorAlpha, _mm_set1_epi32(128)); + srcVectorAlpha = _mm_sub_epi32(srcVectorAlpha, _mm_srli_epi32(srcVectorAlpha, 8)); + srcVectorAlpha = _mm_srli_epi32(srcVectorAlpha, 8); + srcVectorAlpha = _mm_slli_epi32(srcVectorAlpha, 24); + const __m128 ia = reciprocal_mul_ps(a, 255.0f); + __m128i src1 = _mm_unpacklo_epi16(srcVector1, zero); + __m128i src2 = _mm_unpackhi_epi16(srcVector1, zero); + __m128i src3 = _mm_unpacklo_epi16(srcVector2, zero); + __m128i src4 = _mm_unpackhi_epi16(srcVector2, zero); + __m128 ia1 = _mm_shuffle_ps(ia, ia, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 ia2 = _mm_shuffle_ps(ia, ia, _MM_SHUFFLE(1, 1, 1, 1)); + __m128 ia3 = _mm_shuffle_ps(ia, ia, _MM_SHUFFLE(2, 2, 2, 2)); + __m128 ia4 = _mm_shuffle_ps(ia, ia, _MM_SHUFFLE(3, 3, 3, 3)); + src1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(src1), ia1)); + src2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(src2), ia2)); + src3 = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(src3), ia3)); + src4 = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(src4), ia4)); + src1 = _mm_packus_epi32(src1, src2); + src3 = _mm_packus_epi32(src3, src4); + // Handle potential alpha == 0 values: + __m128i srcVector1AlphaMask = _mm_cmpeq_epi64(srcVector1Alpha, zero); + __m128i srcVector2AlphaMask = _mm_cmpeq_epi64(srcVector2Alpha, zero); + src1 = _mm_andnot_si128(srcVector1AlphaMask, src1); + src3 = _mm_andnot_si128(srcVector2AlphaMask, src3); + src1 = _mm_packus_epi16(src1, src3); + // Fixup alpha values: + src1 = _mm_blendv_epi8(src1, srcVectorAlpha, alphaMask32); + // Fix RGB order + if (!RGBA) + src1 = _mm_shuffle_epi8(src1, rgbaMask); + _mm_storeu_si128((__m128i *)&buffer[i], src1); + } else { + __m128i src1 = _mm_unpacklo_epi16(srcVector1, zero); + __m128i src2 = _mm_unpackhi_epi16(srcVector1, zero); + __m128i src3 = _mm_unpacklo_epi16(srcVector2, zero); + __m128i src4 = _mm_unpackhi_epi16(srcVector2, zero); + src1 = _mm_add_epi32(src1, _mm_set1_epi32(128)); + src2 = _mm_add_epi32(src2, _mm_set1_epi32(128)); + src3 = _mm_add_epi32(src3, _mm_set1_epi32(128)); + src4 = _mm_add_epi32(src4, _mm_set1_epi32(128)); + src1 = _mm_sub_epi32(src1, _mm_srli_epi32(src1, 8)); + src2 = _mm_sub_epi32(src2, _mm_srli_epi32(src2, 8)); + src3 = _mm_sub_epi32(src3, _mm_srli_epi32(src3, 8)); + src4 = _mm_sub_epi32(src4, _mm_srli_epi32(src4, 8)); + src1 = _mm_srli_epi32(src1, 8); + src2 = _mm_srli_epi32(src2, 8); + src3 = _mm_srli_epi32(src3, 8); + src4 = _mm_srli_epi32(src4, 8); + src1 = _mm_packus_epi32(src1, src2); + src3 = _mm_packus_epi32(src3, src4); + src1 = _mm_packus_epi16(src1, src3); + if (!RGBA) + src1 = _mm_shuffle_epi8(src1, rgbaMask); + _mm_storeu_si128((__m128i *)&buffer[i], src1); + } + } else { + _mm_storeu_si128((__m128i *)&buffer[i], zero); + } + } + + SIMD_EPILOGUE(i, count, 3) { + buffer[i] = qConvertRgba64ToRgb32_sse4<RGBA ? PixelOrderRGB : PixelOrderBGR>(src[i]); + } } -const uint *QT_FASTCALL convertRGBA8888FromARGB32PM_sse4(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +void QT_FASTCALL convertARGB32ToARGB32PM_sse4(uint *buffer, int count, const QVector<QRgb> *) { - for (int i = 0; i < count; ++i) - buffer[i] = ARGB2RGBA(qUnpremultiply_sse4(src[i])); + convertARGBToARGB32PM_sse4<false>(buffer, buffer, count); +} + +void QT_FASTCALL convertRGBA8888ToARGB32PM_sse4(uint *buffer, int count, const QVector<QRgb> *) +{ + convertARGBToARGB32PM_sse4<true>(buffer, buffer, count); +} + +const uint *QT_FASTCALL fetchARGB32ToARGB32PM_sse4(uint *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + convertARGBToARGB32PM_sse4<false>(buffer, reinterpret_cast<const uint *>(src) + index, count); return buffer; } -const uint *QT_FASTCALL convertRGBXFromARGB32PM_sse4(uint *buffer, const uint *src, int count, +const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_sse4(uint *buffer, const uchar *src, int index, int count, const QVector<QRgb> *, QDitherInfo *) { - for (int i = 0; i < count; ++i) - buffer[i] = ARGB2RGBA(0xff000000 | qUnpremultiply_sse4(src[i])); + convertARGBToARGB32PM_sse4<true>(buffer, reinterpret_cast<const uint *>(src) + index, count); return buffer; } +void QT_FASTCALL storeRGB32FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + uint *d = reinterpret_cast<uint *>(dest) + index; + convertARGBFromARGB32PM_sse4<false,true>(d, src, count); +} + +void QT_FASTCALL storeARGB32FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + uint *d = reinterpret_cast<uint *>(dest) + index; + convertARGBFromARGB32PM_sse4<false,false>(d, src, count); +} + +void QT_FASTCALL storeRGBA8888FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + uint *d = reinterpret_cast<uint *>(dest) + index; + convertARGBFromARGB32PM_sse4<true,false>(d, src, count); +} + +void QT_FASTCALL storeRGBXFromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + uint *d = reinterpret_cast<uint *>(dest) + index; + convertARGBFromARGB32PM_sse4<true,true>(d, src, count); +} + template<QtPixelOrder PixelOrder> -const uint *QT_FASTCALL convertA2RGB30PMFromARGB32PM_sse4(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *) +void QT_FASTCALL storeA2RGB30PMFromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) { + uint *d = reinterpret_cast<uint *>(dest) + index; for (int i = 0; i < count; ++i) - buffer[i] = qConvertArgb32ToA2rgb30_sse4<PixelOrder>(src[i]); - return buffer; + d[i] = qConvertArgb32ToA2rgb30_sse4<PixelOrder>(src[i]); +} + +void QT_FASTCALL destStore64ARGB32_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length) +{ + uint *dest = (uint*)rasterBuffer->scanLine(y) + x; + convertARGBFromRGBA64PM_sse4<false>(dest, buffer, length); +} + +void QT_FASTCALL destStore64RGBA8888_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length) +{ + uint *dest = (uint*)rasterBuffer->scanLine(y) + x; + convertARGBFromRGBA64PM_sse4<true>(dest, buffer, length); } template -const uint *QT_FASTCALL convertA2RGB30PMFromARGB32PM_sse4<PixelOrderBGR>(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *); +void QT_FASTCALL storeA2RGB30PMFromARGB32PM_sse4<PixelOrderBGR>(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *); template -const uint *QT_FASTCALL convertA2RGB30PMFromARGB32PM_sse4<PixelOrderRGB>(uint *buffer, const uint *src, int count, - const QVector<QRgb> *, QDitherInfo *); +void QT_FASTCALL storeA2RGB30PMFromARGB32PM_sse4<PixelOrderRGB>(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *); QT_END_NAMESPACE diff --git a/src/gui/painting/qdrawhelper_ssse3.cpp b/src/gui/painting/qdrawhelper_ssse3.cpp index 45ecc8b422..42d760d5cc 100644 --- a/src/gui/painting/qdrawhelper_ssse3.cpp +++ b/src/gui/painting/qdrawhelper_ssse3.cpp @@ -167,61 +167,12 @@ void qt_blend_argb32_on_argb32_ssse3(uchar *destPixels, int dbpl, } } -static inline void store_uint24_ssse3(uchar *dst, const uint *src, int len) +const uint *QT_FASTCALL fetchPixelsBPP24_ssse3(uint *buffer, const uchar *src, int index, int count) { - int i = 0; - - quint24 *dst24 = reinterpret_cast<quint24*>(dst); - // Align dst on 16 bytes - for (; i < len && (reinterpret_cast<quintptr>(dst24) & 0xf); ++i) - *dst24++ = quint24(*src++); - - // Shuffle masks for first and second half of every output, all outputs are aligned so the shuffled ends are not used. - const __m128i shuffleMask1 = _mm_setr_epi8(char(0x80), char(0x80), char(0x80), char(0x80), 2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12); - const __m128i shuffleMask2 = _mm_setr_epi8(2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12, char(0x80), char(0x80), char(0x80), char(0x80)); - - const __m128i *inVectorPtr = (const __m128i *)src; - __m128i *dstVectorPtr = (__m128i *)dst24; - - for (; i < (len - 15); i += 16) { - // Load four vectors, store three. - // Create each output vector by combining two shuffled input vectors. - __m128i srcVector1 = _mm_loadu_si128(inVectorPtr); - ++inVectorPtr; - __m128i srcVector2 = _mm_loadu_si128(inVectorPtr); - ++inVectorPtr; - __m128i outputVector1 = _mm_shuffle_epi8(srcVector1, shuffleMask1); - __m128i outputVector2 = _mm_shuffle_epi8(srcVector2, shuffleMask2); - __m128i outputVector = _mm_alignr_epi8(outputVector2, outputVector1, 4); - _mm_store_si128(dstVectorPtr, outputVector); - ++dstVectorPtr; - - srcVector1 = _mm_loadu_si128(inVectorPtr); - ++inVectorPtr; - outputVector1 = _mm_shuffle_epi8(srcVector2, shuffleMask1); - outputVector2 = _mm_shuffle_epi8(srcVector1, shuffleMask2); - outputVector = _mm_alignr_epi8(outputVector2, outputVector1, 8); - _mm_store_si128(dstVectorPtr, outputVector); - ++dstVectorPtr; - - srcVector2 = _mm_loadu_si128(inVectorPtr); - ++inVectorPtr; - outputVector1 = _mm_shuffle_epi8(srcVector1, shuffleMask1); - outputVector2 = _mm_shuffle_epi8(srcVector2, shuffleMask2); - outputVector = _mm_alignr_epi8(outputVector2, outputVector1, 12); - _mm_store_si128(dstVectorPtr, outputVector); - ++dstVectorPtr; - } - dst24 = reinterpret_cast<quint24*>(dstVectorPtr); - src = reinterpret_cast<const uint*>(inVectorPtr); - - SIMD_EPILOGUE(i, len, 15) - *dst24++ = quint24(*src++); -} - -void QT_FASTCALL storePixelsBPP24_ssse3(uchar *dest, const uint *src, int index, int count) -{ - store_uint24_ssse3(dest + index * 3, src, count); + const quint24 *s = reinterpret_cast<const quint24 *>(src); + for (int i = 0; i < count; ++i) + buffer[i] = s[index + i]; + return buffer; } extern void QT_FASTCALL qt_convert_rgb888_to_rgb32_ssse3(quint32 *dst, const uchar *src, int len); diff --git a/src/gui/painting/qdrawingprimitive_sse2_p.h b/src/gui/painting/qdrawingprimitive_sse2_p.h index 93e4b9f572..b237ea1611 100644 --- a/src/gui/painting/qdrawingprimitive_sse2_p.h +++ b/src/gui/painting/qdrawingprimitive_sse2_p.h @@ -43,6 +43,7 @@ #include <QtGui/private/qtguiglobal_p.h> #include <private/qsimd_p.h> #include "qdrawhelper_p.h" +#include "qrgba64_p.h" #ifdef __SSE2__ @@ -230,21 +231,31 @@ QT_END_NAMESPACE QT_BEGIN_NAMESPACE #if QT_COMPILER_SUPPORTS_HERE(SSE4_1) +QT_FUNCTION_TARGET(SSE2) +Q_ALWAYS_INLINE void reciprocal_mul_ss(__m128 &ia, const __m128 a, float mul) +{ + ia = _mm_rcp_ss(a); // Approximate 1/a + // Improve precision of ia using Newton-Raphson + ia = _mm_sub_ss(_mm_add_ss(ia, ia), _mm_mul_ss(ia, _mm_mul_ss(ia, a))); + ia = _mm_mul_ss(ia, _mm_set_ss(mul)); + ia = _mm_shuffle_ps(ia, ia, _MM_SHUFFLE(0,0,0,0)); +} + QT_FUNCTION_TARGET(SSE4_1) inline QRgb qUnpremultiply_sse4(QRgb p) { const uint alpha = qAlpha(p); - if (alpha == 255 || alpha == 0) + if (alpha == 255) return p; - const uint invAlpha = qt_inv_premul_factor[alpha]; - const __m128i via = _mm_set1_epi32(invAlpha); - const __m128i vr = _mm_set1_epi32(0x8000); + if (alpha == 0) + return 0; + const __m128 va = _mm_set1_ps(alpha); + __m128 via; + reciprocal_mul_ss(via, va, 255.0f); // Approximate 1/a __m128i vl = _mm_cvtepu8_epi32(_mm_cvtsi32_si128(p)); - vl = _mm_mullo_epi32(vl, via); - vl = _mm_add_epi32(vl, vr); - vl = _mm_srai_epi32(vl, 16); - vl = _mm_insert_epi32(vl, alpha, 3); + vl = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(vl), via)); vl = _mm_packus_epi32(vl, vl); + vl = _mm_insert_epi16(vl, alpha, 3); vl = _mm_packus_epi16(vl, vl); return _mm_cvtsi128_si32(vl); } @@ -258,21 +269,14 @@ inline uint qConvertArgb32ToA2rgb30_sse4(QRgb p) return qConvertRgb32ToRgb30<PixelOrder>(p); if (alpha == 0) return 0; - Q_CONSTEXPR uint mult = 255 / (255 >> 6); - const uint invAlpha = qt_inv_premul_factor[alpha]; + Q_CONSTEXPR float mult = 1023.0f / (255 >> 6); const uint newalpha = (alpha >> 6); - const __m128i via = _mm_set1_epi32(invAlpha); - const __m128i vna = _mm_set1_epi32(mult * newalpha); - const __m128i vr1 = _mm_set1_epi32(0x1000); - const __m128i vr2 = _mm_set1_epi32(0x80); - __m128i vl = _mm_cvtepu8_epi32(_mm_cvtsi32_si128(p)); - vl = _mm_mullo_epi32(vl, via); - vl = _mm_add_epi32(vl, vr1); - vl = _mm_srli_epi32(vl, 14); - vl = _mm_mullo_epi32(vl, vna); - vl = _mm_add_epi32(vl, _mm_srli_epi32(vl, 8)); - vl = _mm_add_epi32(vl, vr2); - vl = _mm_srli_epi32(vl, 8); + const __m128 va = _mm_set1_ps(alpha); + __m128 via; + reciprocal_mul_ss(via, va, mult * newalpha); + __m128i vl = _mm_cvtsi32_si128(p); + vl = _mm_cvtepu8_epi32(vl); + vl = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(vl), via)); vl = _mm_packus_epi32(vl, vl); uint rgb30 = (newalpha << 30); rgb30 |= ((uint)_mm_extract_epi16(vl, 1)) << 10; @@ -285,6 +289,27 @@ inline uint qConvertArgb32ToA2rgb30_sse4(QRgb p) } return rgb30; } + +template<enum QtPixelOrder PixelOrder> +QT_FUNCTION_TARGET(SSE4_1) +inline uint qConvertRgba64ToRgb32_sse4(QRgba64 p) +{ + if (p.isTransparent()) + return 0; + __m128i vl = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(&p)); + if (!p.isOpaque()) { + const __m128 va = _mm_set1_ps(p.alpha()); + __m128 via; + reciprocal_mul_ss(via, va, 65535.0f); + vl = _mm_unpacklo_epi16(vl, _mm_setzero_si128()); + vl = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(vl) , via)); + vl = _mm_packus_epi32(vl, vl); + vl = _mm_insert_epi16(vl, p.alpha(), 3); + } + if (PixelOrder == PixelOrderBGR) + vl = _mm_shufflelo_epi16(vl, _MM_SHUFFLE(3, 0, 1, 2)); + return toArgb32(vl); +} #endif QT_END_NAMESPACE diff --git a/src/gui/painting/qemulationpaintengine.cpp b/src/gui/painting/qemulationpaintengine.cpp index e6686e3721..49ecd3b318 100644 --- a/src/gui/painting/qemulationpaintengine.cpp +++ b/src/gui/painting/qemulationpaintengine.cpp @@ -101,6 +101,14 @@ void QEmulationPaintEngine::fill(const QVectorPath &path, const QBrush &brush) real_engine->fill(path, copy); return; } + } else if (style == Qt::TexturePattern) { + qreal dpr = qHasPixmapTexture(brush) ? brush.texture().devicePixelRatioF() : brush.textureImage().devicePixelRatioF(); + if (!qFuzzyCompare(dpr, 1.0)) { + QBrush copy = brush; + combineXForm(©, QRectF(0, 0, 1.0/dpr, 1.0/dpr)); + real_engine->fill(path, copy); + return; + } } real_engine->fill(path, brush); diff --git a/src/gui/painting/qpagedpaintdevice.cpp b/src/gui/painting/qpagedpaintdevice.cpp index 1c7d6471b6..613a686848 100644 --- a/src/gui/painting/qpagedpaintdevice.cpp +++ b/src/gui/painting/qpagedpaintdevice.cpp @@ -42,6 +42,41 @@ QT_BEGIN_NAMESPACE +class QDummyPagedPaintDevicePrivate : public QPagedPaintDevicePrivate +{ + bool setPageLayout(const QPageLayout &newPageLayout) override + { + m_pageLayout = newPageLayout; + return m_pageLayout.isEquivalentTo(newPageLayout); + } + + bool setPageSize(const QPageSize &pageSize) override + { + m_pageLayout.setPageSize(pageSize); + return m_pageLayout.pageSize().isEquivalentTo(pageSize); + } + + bool setPageOrientation(QPageLayout::Orientation orientation) override + { + m_pageLayout.setOrientation(orientation); + return m_pageLayout.orientation() == orientation; + } + + bool setPageMargins(const QMarginsF &margins, QPageLayout::Unit units) override + { + m_pageLayout.setUnits(units); + m_pageLayout.setMargins(margins); + return m_pageLayout.margins() == margins && m_pageLayout.units() == units; + } + + QPageLayout pageLayout() const override + { + return m_pageLayout; + } + + QPageLayout m_pageLayout; +}; + QPagedPaintDevicePrivate::~QPagedPaintDevicePrivate() { } @@ -61,9 +96,11 @@ QPagedPaintDevicePrivate::~QPagedPaintDevicePrivate() /*! Constructs a new paged paint device. + + \deprecated */ QPagedPaintDevice::QPagedPaintDevice() - : d(new QPagedPaintDevicePrivate) + : d(new QDummyPagedPaintDevicePrivate) { } @@ -263,7 +300,7 @@ QPagedPaintDevicePrivate *QPagedPaintDevice::dd() */ void QPagedPaintDevice::setPageSize(PageSize size) { - d->m_pageLayout.setPageSize(QPageSize(QPageSize::PageSizeId(size))); + d->setPageSize(QPageSize(QPageSize::PageSizeId(size))); } /*! @@ -271,7 +308,7 @@ void QPagedPaintDevice::setPageSize(PageSize size) */ QPagedPaintDevice::PageSize QPagedPaintDevice::pageSize() const { - return PageSize(d->m_pageLayout.pageSize().id()); + return PageSize(d->pageLayout().pageSize().id()); } /*! @@ -282,7 +319,7 @@ QPagedPaintDevice::PageSize QPagedPaintDevice::pageSize() const */ void QPagedPaintDevice::setPageSizeMM(const QSizeF &size) { - d->m_pageLayout.setPageSize(QPageSize(size, QPageSize::Millimeter)); + d->setPageSize(QPageSize(size, QPageSize::Millimeter)); } /*! @@ -290,7 +327,7 @@ void QPagedPaintDevice::setPageSizeMM(const QSizeF &size) */ QSizeF QPagedPaintDevice::pageSizeMM() const { - return d->m_pageLayout.pageSize().size(QPageSize::Millimeter); + return d->pageLayout().pageSize().size(QPageSize::Millimeter); } /*! @@ -305,8 +342,7 @@ QSizeF QPagedPaintDevice::pageSizeMM() const */ void QPagedPaintDevice::setMargins(const Margins &margins) { - d->m_pageLayout.setUnits(QPageLayout::Millimeter); - d->m_pageLayout.setMargins(QMarginsF(margins.left, margins.top, margins.right, margins.bottom)); + d->setPageMargins(QMarginsF(margins.left, margins.top, margins.right, margins.bottom), QPageLayout::Millimeter); } /*! @@ -318,7 +354,7 @@ void QPagedPaintDevice::setMargins(const Margins &margins) */ QPagedPaintDevice::Margins QPagedPaintDevice::margins() const { - QMarginsF margins = d->m_pageLayout.margins(QPageLayout::Millimeter); + QMarginsF margins = d->pageLayout().margins(QPageLayout::Millimeter); Margins result; result.left = margins.left(); result.top = margins.top(); @@ -413,7 +449,7 @@ bool QPagedPaintDevice::setPageOrientation(QPageLayout::Orientation orientation) bool QPagedPaintDevice::setPageMargins(const QMarginsF &margins) { - return d->setPageMargins(margins); + return setPageMargins(margins, pageLayout().units()); } /*! @@ -458,23 +494,30 @@ QPageLayout QPagedPaintDevice::pageLayout() const /*! \internal + \deprecated + Returns the internal device page layout. */ QPageLayout QPagedPaintDevice::devicePageLayout() const { - return d->m_pageLayout; + qWarning("QPagedPaintDevice::devicePageLayout() is deprecated, just use QPagedPaintDevice::pageLayout()"); + return d->pageLayout(); } /*! \internal + \deprecated + Returns the internal device page layout. */ QPageLayout &QPagedPaintDevice::devicePageLayout() { - return d->m_pageLayout; + qWarning("QPagedPaintDevice::devicePageLayout() is deprecated, you shouldn't be using this at all."); + static QPageLayout dummy; + return dummy; } QT_END_NAMESPACE diff --git a/src/gui/painting/qpagedpaintdevice.h b/src/gui/painting/qpagedpaintdevice.h index 66dd6fa8cf..c8957edab8 100644 --- a/src/gui/painting/qpagedpaintdevice.h +++ b/src/gui/painting/qpagedpaintdevice.h @@ -55,7 +55,7 @@ class QPagedPaintDevicePrivate; class Q_GUI_EXPORT QPagedPaintDevice : public QPaintDevice { public: - QPagedPaintDevice(); + QT_DEPRECATED QPagedPaintDevice(); ~QPagedPaintDevice(); virtual bool newPage() = 0; @@ -243,8 +243,8 @@ public: protected: QPagedPaintDevice(QPagedPaintDevicePrivate *dd); QPagedPaintDevicePrivate *dd(); - QPageLayout devicePageLayout() const; - QPageLayout &devicePageLayout(); + QT_DEPRECATED QPageLayout devicePageLayout() const; + QT_DEPRECATED QPageLayout &devicePageLayout(); friend class QPagedPaintDevicePrivate; QPagedPaintDevicePrivate *d; }; diff --git a/src/gui/painting/qpagedpaintdevice_p.h b/src/gui/painting/qpagedpaintdevice_p.h index a993ea4cac..3a43bd7828 100644 --- a/src/gui/painting/qpagedpaintdevice_p.h +++ b/src/gui/painting/qpagedpaintdevice_p.h @@ -60,8 +60,7 @@ class Q_GUI_EXPORT QPagedPaintDevicePrivate { public: QPagedPaintDevicePrivate() - : m_pageLayout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF(0, 0, 0, 0)), - fromPage(0), + : fromPage(0), toPage(0), pageOrderAscending(true), printSelectionOnly(false) @@ -70,46 +69,19 @@ public: virtual ~QPagedPaintDevicePrivate(); - // ### Qt6 Remove these and make public class methods virtual - virtual bool setPageLayout(const QPageLayout &newPageLayout) - { - m_pageLayout = newPageLayout; - return m_pageLayout.isEquivalentTo(newPageLayout);; - } - virtual bool setPageSize(const QPageSize &pageSize) - { - m_pageLayout.setPageSize(pageSize); - return m_pageLayout.pageSize().isEquivalentTo(pageSize); - } + virtual bool setPageLayout(const QPageLayout &newPageLayout) = 0; - virtual bool setPageOrientation(QPageLayout::Orientation orientation) - { - m_pageLayout.setOrientation(orientation); - return m_pageLayout.orientation() == orientation; - } + virtual bool setPageSize(const QPageSize &pageSize) = 0; - virtual bool setPageMargins(const QMarginsF &margins) - { - return setPageMargins(margins, m_pageLayout.units()); - } + virtual bool setPageOrientation(QPageLayout::Orientation orientation) = 0; - virtual bool setPageMargins(const QMarginsF &margins, QPageLayout::Unit units) - { - m_pageLayout.setUnits(units); - m_pageLayout.setMargins(margins); - return m_pageLayout.margins() == margins && m_pageLayout.units() == units; - } + virtual bool setPageMargins(const QMarginsF &margins, QPageLayout::Unit units) = 0; - virtual QPageLayout pageLayout() const - { - return m_pageLayout; - } + virtual QPageLayout pageLayout() const = 0; static inline QPagedPaintDevicePrivate *get(QPagedPaintDevice *pd) { return pd->d; } - QPageLayout m_pageLayout; - // These are currently required to keep QPrinter functionality working in QTextDocument::print() int fromPage; int toPage; diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index 6336b2943e..e0dd789dfd 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -2502,10 +2502,13 @@ void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, if (image.depth() == 1) image = d->rasterBuffer->colorizeBitmap(image, s->pen.color()); - if (s->matrix.type() > QTransform::TxTranslate) { + const qreal pixmapDevicePixelRatio = pixmap.devicePixelRatio(); + if (s->matrix.type() > QTransform::TxTranslate || pixmapDevicePixelRatio > qreal(1.0)) { QTransform copy = s->matrix; copy.translate(r.x(), r.y()); copy.translate(-sr.x(), -sr.y()); + const qreal inverseDpr = qreal(1.0) / pixmapDevicePixelRatio; + copy.scale(inverseDpr, inverseDpr); d->image_filler_xform.clip = d->clip(); d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled); if (!d->image_filler_xform.blend) diff --git a/src/gui/painting/qpaintengineex.cpp b/src/gui/painting/qpaintengineex.cpp index 0643a7cbb6..9f07af92e4 100644 --- a/src/gui/painting/qpaintengineex.cpp +++ b/src/gui/painting/qpaintengineex.cpp @@ -948,6 +948,8 @@ void QPaintEngineEx::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, con { QBrush brush(state()->pen.color(), pixmap); QTransform xform = QTransform::fromTranslate(r.x() - s.x(), r.y() - s.y()); + if (!qFuzzyCompare(pixmap.devicePixelRatioF(), 1.0)) + xform.scale(1.0/pixmap.devicePixelRatioF(), 1.0/pixmap.devicePixelRatioF()); brush.setTransform(xform); qreal pts[] = { r.x(), r.y(), diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index b992e8f55d..4a18df899e 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -190,6 +190,13 @@ void QPainterPrivate::checkEmulation() if (pg && pg->coordinateMode() > QGradient::LogicalMode) doEmulation = true; + if (state->brush.style() == Qt::TexturePattern) { + if (qHasPixmapTexture(state->brush)) + doEmulation |= !qFuzzyCompare(state->brush.texture().devicePixelRatioF(), 1.0); + else + doEmulation |= !qFuzzyCompare(state->brush.textureImage().devicePixelRatioF(), 1.0); + } + if (doEmulation && extended->flags() & QPaintEngineEx::DoNotEmulate) return; @@ -6657,6 +6664,16 @@ QRectF QPainter::boundingRect(const QRectF &r, const QString &text, const QTextO potentially much more efficient depending on the underlying window system. + drawTiledPixmap() will produce the same visual tiling pattern on + high-dpi displays (with devicePixelRatio > 1), compared to normal- + dpi displays. Set the devicePixelRatio on the \a pixmap to control + the tile size. For example, setting it to 2 halves the tile width + and height (on both 1x and 2x displays), and produces high-resolution + output on 2x displays. + + The \a position offset is always in the painter coordinate system, + indepentent of display devicePixelRatio. + \sa drawPixmap() */ void QPainter::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sp) @@ -7068,6 +7085,37 @@ void QPainter::fillRect(const QRectF &r, const QColor &color) */ /*! + \fn void QPainter::fillRect(int x, int y, int width, int height, QGradient::Preset preset) + + \overload + + Fills the rectangle beginning at (\a{x}, \a{y}) with the given \a + width and \a height, using the given gradient \a preset. + + \since 5.12 +*/ + +/*! + \fn void QPainter::fillRect(const QRect &rectangle, QGradient::Preset preset); + + \overload + + Fills the given \a rectangle with the specified gradient \a preset. + + \since 5.12 +*/ + +/*! + \fn void QPainter::fillRect(const QRectF &rectangle, QGradient::Preset preset); + + \overload + + Fills the given \a rectangle with the specified gradient \a preset. + + \since 5.12 +*/ + +/*! Sets the given render \a hint on the painter if \a on is true; otherwise clears the render hint. diff --git a/src/gui/painting/qpainter.h b/src/gui/painting/qpainter.h index 12a9c720a8..482f5fb63d 100644 --- a/src/gui/painting/qpainter.h +++ b/src/gui/painting/qpainter.h @@ -448,6 +448,10 @@ public: inline void fillRect(const QRect &r, Qt::BrushStyle style); inline void fillRect(const QRectF &r, Qt::BrushStyle style); + inline void fillRect(int x, int y, int w, int h, QGradient::Preset preset); + inline void fillRect(const QRect &r, QGradient::Preset preset); + inline void fillRect(const QRectF &r, QGradient::Preset preset); + void eraseRect(const QRectF &); inline void eraseRect(int x, int y, int w, int h); inline void eraseRect(const QRect &); @@ -746,6 +750,20 @@ inline void QPainter::fillRect(const QRectF &r, Qt::BrushStyle style) fillRect(r, QBrush(style)); } +inline void QPainter::fillRect(int x, int y, int w, int h, QGradient::Preset p) +{ + fillRect(QRect(x, y, w, h), QGradient(p)); +} + +inline void QPainter::fillRect(const QRect &r, QGradient::Preset p) +{ + fillRect(r, QGradient(p)); +} + +inline void QPainter::fillRect(const QRectF &r, QGradient::Preset p) +{ + fillRect(r, QGradient(p)); +} inline void QPainter::setBrushOrigin(int x, int y) { diff --git a/src/gui/painting/qpdf.cpp b/src/gui/painting/qpdf.cpp index e58f9cee4c..4fd0d3c8fe 100644 --- a/src/gui/painting/qpdf.cpp +++ b/src/gui/painting/qpdf.cpp @@ -1109,8 +1109,9 @@ void QPdfEngine::updateState(const QPaintEngineState &state) d->hasPen = d->pen.style() != Qt::NoPen; d->stroker.setPen(d->pen, state.renderHints()); QBrush penBrush = d->pen.brush(); + bool cosmeticPen = qt_pen_is_cosmetic(d->pen, state.renderHints()); bool oldSimple = d->simplePen; - d->simplePen = (d->hasPen && (penBrush.style() == Qt::SolidPattern) && penBrush.isOpaque() && d->opacity == 1.0); + d->simplePen = (d->hasPen && !cosmeticPen && (penBrush.style() == Qt::SolidPattern) && penBrush.isOpaque() && d->opacity == 1.0); if (oldSimple != d->simplePen) flags |= DirtyTransform; } else if (flags & DirtyHints) { diff --git a/src/gui/painting/qpdfwriter.cpp b/src/gui/painting/qpdfwriter.cpp index 2f24c7efcb..e6c5cabd7f 100644 --- a/src/gui/painting/qpdfwriter.cpp +++ b/src/gui/painting/qpdfwriter.cpp @@ -83,41 +83,28 @@ public: { // Try to set the paint engine page layout pd->engine->setPageLayout(newPageLayout); - // Set QPagedPaintDevice layout to match the current paint engine layout - m_pageLayout = pd->engine->pageLayout(); - return m_pageLayout.isEquivalentTo(newPageLayout); + return pageLayout().isEquivalentTo(newPageLayout); } bool setPageSize(const QPageSize &pageSize) override { // Try to set the paint engine page size pd->engine->setPageSize(pageSize); - // Set QPagedPaintDevice layout to match the current paint engine layout - m_pageLayout = pd->engine->pageLayout(); - return m_pageLayout.pageSize().isEquivalentTo(pageSize); + return pageLayout().pageSize().isEquivalentTo(pageSize); } bool setPageOrientation(QPageLayout::Orientation orientation) override { // Set the print engine value pd->engine->setPageOrientation(orientation); - // Set QPagedPaintDevice layout to match the current paint engine layout - m_pageLayout = pd->engine->pageLayout(); - return m_pageLayout.orientation() == orientation; - } - - bool setPageMargins(const QMarginsF &margins) override - { - return setPageMargins(margins, pageLayout().units()); + return pageLayout().orientation() == orientation; } bool setPageMargins(const QMarginsF &margins, QPageLayout::Unit units) override { // Try to set engine margins pd->engine->setPageMargins(margins, units); - // Set QPagedPaintDevice layout to match the current paint engine layout - m_pageLayout = pd->engine->pageLayout(); - return m_pageLayout.margins() == margins && m_pageLayout.units() == units; + return pageLayout().margins() == margins && pageLayout().units() == units; } QPageLayout pageLayout() const override @@ -150,9 +137,6 @@ QPdfWriter::QPdfWriter(const QString &filename) Q_D(QPdfWriter); d->engine->setOutputFilename(filename); - - // Set QPagedPaintDevice layout to match the current paint engine layout - devicePageLayout() = d->engine->pageLayout(); } /*! @@ -165,9 +149,6 @@ QPdfWriter::QPdfWriter(QIODevice *device) Q_D(QPdfWriter); d->engine->d_func()->outDevice = device; - - // Set QPagedPaintDevice layout to match the current paint engine layout - devicePageLayout() = d->engine->pageLayout(); } /*! diff --git a/src/gui/painting/qrgba64.h b/src/gui/painting/qrgba64.h index 2c8f8fa8c4..0e5344cacb 100644 --- a/src/gui/painting/qrgba64.h +++ b/src/gui/painting/qrgba64.h @@ -127,6 +127,10 @@ public: Q_DECL_RELAXED_CONSTEXPR QRgba64 premultiplied() const { + if (isOpaque()) + return *this; + if (isTransparent()) + return QRgba64::fromRgba64(0); const quint32 a = alpha(); const quint16 r = div_65535(red() * a); const quint16 g = div_65535(green() * a); diff --git a/src/gui/painting/qrgba64_p.h b/src/gui/painting/qrgba64_p.h index adceda2210..1ed0e82182 100644 --- a/src/gui/painting/qrgba64_p.h +++ b/src/gui/painting/qrgba64_p.h @@ -51,10 +51,11 @@ // We mean it. // +#include "qrgba64.h" +#include "qdrawhelper_p.h" + +#include <QtCore/private/qsimd_p.h> #include <QtGui/private/qtguiglobal_p.h> -#include <QtGui/qrgba64.h> -#include <QtGui/private/qdrawhelper_p.h> -#include <private/qsimd_p.h> QT_BEGIN_NAMESPACE @@ -159,7 +160,7 @@ Q_ALWAYS_INLINE __m128i interpolate65535(__m128i x, uint alpha1, __m128i y, uint { return _mm_add_epi32(multiplyAlpha65535(x, alpha1), multiplyAlpha65535(y, alpha2)); } -// alpha2 below is const-ref because otherwise MSVC2013 complains that it can't 16-byte align the argument. +// alpha2 below is const-ref because otherwise MSVC2015 complains that it can't 16-byte align the argument. Q_ALWAYS_INLINE __m128i interpolate65535(__m128i x, __m128i alpha1, __m128i y, const __m128i &alpha2) { return _mm_add_epi32(multiplyAlpha65535(x, alpha1), multiplyAlpha65535(y, alpha2)); @@ -185,7 +186,8 @@ inline QRgba64 addWithSaturation(QRgba64 a, QRgba64 b) qMin(a.alpha() + b.alpha(), 65535)); } -#if defined __SSE2__ +#if QT_COMPILER_SUPPORTS_HERE(SSE2) +QT_FUNCTION_TARGET(SSE2) Q_ALWAYS_INLINE uint toArgb32(__m128i v) { v = _mm_unpacklo_epi16(v, _mm_setzero_si128()); @@ -239,20 +241,6 @@ inline uint toRgba8888(QRgba64 rgba64) #endif } -#if defined(__SSE2__) -Q_ALWAYS_INLINE __m128i addWithSaturation(__m128i a, __m128i b) -{ - return _mm_adds_epu16(a, b); -} -#endif - -#if defined(__ARM_NEON__) -Q_ALWAYS_INLINE uint16x4_t addWithSaturation(uint16x4_t a, uint16x4_t b) -{ - return vqmovn_u32(vaddl_u16(a, b)); -} -#endif - inline QRgba64 rgbBlend(QRgba64 d, QRgba64 s, uint rgbAlpha) { QRgba64 blend; diff --git a/src/gui/painting/qt_attribution.json b/src/gui/painting/qt_attribution.json index 06a62d9d66..6aed115d01 100644 --- a/src/gui/painting/qt_attribution.json +++ b/src/gui/painting/qt_attribution.json @@ -27,5 +27,19 @@ "Copyright": "Copyright (C) 2004, 2005 Daniel M. Duley. (C) Carsten Haitzler and various contributors. (C) Willem Monsuwe <willem@stack.nl>" - } + }, + { + "Id": "webgradients", + "Name": "WebGradients", + "QDocModule": "qtgui", + "QtUsage": "Used in Qt GUI to provide presets for QGradient.", + "Files": "webgradients.css", + + "Description": "WebGradients is a free collection of 180 linear gradients.", + "Homepage": "https://webgradients.com/", + "License": "MIT License", + "LicenseId": "MIT", + "LicenseFile": "WEBGRADIENTS_LICENSE.txt", + "Copyright": "Copyright (c) 2017 itmeo" + }, ] diff --git a/src/gui/painting/webgradients.binaryjson b/src/gui/painting/webgradients.binaryjson Binary files differnew file mode 100644 index 0000000000..15b798f7ba --- /dev/null +++ b/src/gui/painting/webgradients.binaryjson diff --git a/src/gui/painting/webgradients.css b/src/gui/painting/webgradients.css new file mode 100644 index 0000000000..870866bb46 --- /dev/null +++ b/src/gui/painting/webgradients.css @@ -0,0 +1,909 @@ +/*001 Warm Flame*/ +.warm_flame{ + background-image: linear-gradient(45deg, #ff9a9e 0%, #fad0c4 99%, #fad0c4 100%); +} + +/*002 Night Fade*/ +.night_fade{ + background-image: linear-gradient(to top, #a18cd1 0%, #fbc2eb 100%); +} + +/*003 Spring Warmth*/ +.spring_warmth{ + background-image: linear-gradient(to top, #fad0c4 0%, #fad0c4 1%, #ffd1ff 100%); +} + +/*004 Juicy Peach*/ +.juicy_peach{ + background-image: linear-gradient(to right, #ffecd2 0%, #fcb69f 100%); +} + +/*005 Young Passion*/ +.young_passion{ + background-image: linear-gradient(to right, #ff8177 0%, #ff867a 0%, #ff8c7f 21%, #f99185 52%, #cf556c 78%, #b12a5b 100%); +} + +/*006 Lady Lips*/ +.lady_lips{ + background-image: linear-gradient(to top, #ff9a9e 0%, #fecfef 99%, #fecfef 100%); +} + +/*007 Sunny Morning*/ +.sunny_morning{ + background-image: linear-gradient(120deg, #f6d365 0%, #fda085 100%); +} + +/*008 Rainy Ashville*/ +.rainy_ashville{ + background-image: linear-gradient(to top, #fbc2eb 0%, #a6c1ee 100%); +} + +/*009 Frozen Dreams*/ +.frozen_dreams{ + background-image: linear-gradient(to top, #fdcbf1 0%, #fdcbf1 1%, #e6dee9 100%); +} + +/*010 Winter Neva*/ +.winter_neva{ + background-image: linear-gradient(120deg, #a1c4fd 0%, #c2e9fb 100%); +} + +/*011 Dusty Grass*/ +.dusty_grass{ + background-image: linear-gradient(120deg, #d4fc79 0%, #96e6a1 100%); +} + +/*012 Tempting Azure*/ +.tempting_azure{ + background-image: linear-gradient(120deg, #84fab0 0%, #8fd3f4 100%); +} + +/*013 Heavy Rain*/ +.heavy_rain{ + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); +} + +/*014 Amy Crisp*/ +.amy_crisp{ + background-image: linear-gradient(120deg, #a6c0fe 0%, #f68084 100%); +} + +/*015 Mean Fruit*/ +.mean_fruit{ + background-image: linear-gradient(120deg, #fccb90 0%, #d57eeb 100%); +} + +/*016 Deep Blue*/ +.deep_blue{ + background-image: linear-gradient(120deg, #e0c3fc 0%, #8ec5fc 100%); +} + +/*017 Ripe Malinka*/ +.ripe_malinka{ + background-image: linear-gradient(120deg, #f093fb 0%, #f5576c 100%); +} + +/*018 Cloudy Knoxville*/ +.cloudy_knoxville{ + background-image: linear-gradient(120deg, #fdfbfb 0%, #ebedee 100%); +} + +/*019 Malibu Beach*/ +.malibu_beach{ + background-image: linear-gradient(to right, #4facfe 0%, #00f2fe 100%); +} + +/*020 New Life*/ +.new_life{ + background-image: linear-gradient(to right, #43e97b 0%, #38f9d7 100%); +} + +/*021 True Sunset*/ +.true_sunset{ + background-image: linear-gradient(to right, #fa709a 0%, #fee140 100%); +} + +/*022 Morpheus Den*/ +.morpheus_den{ + background-image: linear-gradient(to top, #30cfd0 0%, #330867 100%); +} + +/*023 Rare Wind*/ +.rare_wind{ + background-image: linear-gradient(to top, #a8edea 0%, #fed6e3 100%); +} + +/*024 Near Moon*/ +.near_moon{ + background-image: linear-gradient(to top, #5ee7df 0%, #b490ca 100%); +} + +/*025 Wild Apple*/ +.wild_apple{ + background-image: linear-gradient(to top, #d299c2 0%, #fef9d7 100%); +} + +/*026 Saint Petersburg*/ +.saint_petersburg{ + background-image: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); +} + +/*027 Arielle's Smile*/ +.arielles_smile{ + background-image: radial-gradient(circle 248px at center, #16d9e3 0%, #30c7ec 47%, #46aef7 100%); +} + +/*028 Plum Plate*/ +.plum_plate{ + background-image: linear-gradient(135deg, #667eea 0%, #764ba2 100%); +} + +/*029 Everlasting Sky*/ +.everlasting_sky{ + background-image: linear-gradient(135deg, #fdfcfb 0%, #e2d1c3 100%); +} + +/*030 Happy Fisher*/ +.happy_fisher{ + background-image: linear-gradient(120deg, #89f7fe 0%, #66a6ff 100%); +} + +/*031 Blessing*/ +.blessing{ + background-image: linear-gradient(to top, #fddb92 0%, #d1fdff 100%); +} + +/*032 Sharpeye Eagle*/ +.sharpeye_eagle{ + background-image: linear-gradient(to top, #9890e3 0%, #b1f4cf 100%); +} + +/*033 Ladoga Bottom*/ +.ladoga_bottom{ + background-image: linear-gradient(to top, #ebc0fd 0%, #d9ded8 100%); +} + +/*034 Lemon Gate*/ +.lemon_gate{ + background-image: linear-gradient(to top, #96fbc4 0%, #f9f586 100%); +} + +/*035 Itmeo Branding*/ +.itmeo_branding{ + background-image: linear-gradient(180deg, #2af598 0%, #009efd 100%); +} + +/*036 Zeus Miracle*/ +.zeus_miracle{ + background-image: linear-gradient(to top, #cd9cf2 0%, #f6f3ff 100%); +} + +/*037 Old Hat*/ +.old_hat{ + background-image: linear-gradient(to right, #e4afcb 0%, #b8cbb8 0%, #b8cbb8 0%, #e2c58b 30%, #c2ce9c 64%, #7edbdc 100%); +} + +/*038 Star Wine*/ +.star_wine{ + background-image: linear-gradient(to right, #b8cbb8 0%, #b8cbb8 0%, #b465da 0%, #cf6cc9 33%, #ee609c 66%, #ee609c 100%); +} + +/*039 Deep Blue*/ +.deep_blue{ + background-image: linear-gradient(to right, #6a11cb 0%, #2575fc 100%); +} + +/*040 Coup de Grace*/ +.coup_de_grace{ + background: #DCD9D4 linear-gradient(to bottom, rgba(255, 255, 255, 0.50) 0%, rgba(0, 0, 0, 0.50) 100%), radial-gradient(at 50% 0%, rgba(255, 255, 255, 0.10) 0%, rgba(0, 0, 0, 0.50) 50%); + background-blend-mode: soft-light,screen; +} + +/*041 Happy Acid*/ +.happy_acid{ + background-image: linear-gradient(to top, #37ecba 0%, #72afd3 100%); +} + +/*042 Awesome Pine*/ +.awesome_pine{ + background-image: linear-gradient(to top, #ebbba7 0%, #cfc7f8 100%); +} + +/*043 New York*/ +.new_york{ + background-image: linear-gradient(to top, #fff1eb 0%, #ace0f9 100%); +} + +/*044 Shy Rainbow*/ +.shy_rainbow{ + background-image: linear-gradient(to right, #eea2a2 0%, #bbc1bf 19%, #57c6e1 42%, #b49fda 79%, #7ac5d8 100%); +} + +/*045 Loon Crest*/ +.loon_crest{ + background: linear-gradient(to bottom, rgba(255,255,255,0.15) 0%, rgba(0,0,0,0.15) 100%), radial-gradient(at top center, rgba(255,255,255,0.40) 0%, rgba(0,0,0,0.40) 120%) #989898; + background-blend-mode: multiply,multiply; +} + +/*046 Mixed Hopes*/ +.mixed_hopes{ + background-image: linear-gradient(to top, #c471f5 0%, #fa71cd 100%); +} + +/*047 Fly High*/ +.fly_high{ + background-image: linear-gradient(to top, #48c6ef 0%, #6f86d6 100%); +} + +/*048 Strong Bliss*/ +.strong_bliss{ + background-image: linear-gradient(to right, #f78ca0 0%, #f9748f 19%, #fd868c 60%, #fe9a8b 100%); +} + +/*049 Fresh Milk*/ +.fresh_milk{ + background-image: linear-gradient(to top, #feada6 0%, #f5efef 100%); +} + +/*050 Snow Again*/ +.snow_again{ + background-image: linear-gradient(to top, #e6e9f0 0%, #eef1f5 100%); +} + +/*051 February Ink*/ +.february_ink{ + background-image: linear-gradient(to top, #accbee 0%, #e7f0fd 100%); +} + +/*052 Kind Steel*/ +.kind_steel{ + background-image: linear-gradient(-20deg, #e9defa 0%, #fbfcdb 100%); +} + +/*053 Soft Grass*/ +.soft_grass{ + background-image: linear-gradient(to top, #c1dfc4 0%, #deecdd 100%); +} + +/*054 Grown Early*/ +.grown_early{ + background-image: linear-gradient(to top, #0ba360 0%, #3cba92 100%); +} + +/*055 Sharp Blues*/ +.sharp_blues{ + background-image: linear-gradient(to top, #00c6fb 0%, #005bea 100%); +} + +/*056 Shady Water*/ +.shady_water{ + background-image: linear-gradient(to right, #74ebd5 0%, #9face6 100%); +} + +/*057 Dirty Beauty*/ +.dirty_beauty{ + background-image: linear-gradient(to top, #6a85b6 0%, #bac8e0 100%); +} + +/*058 Great Whale*/ +.great_whale{ + background-image: linear-gradient(to top, #a3bded 0%, #6991c7 100%); +} + +/*059 Teen Notebook*/ +.teen_notebook{ + background-image: linear-gradient(to top, #9795f0 0%, #fbc8d4 100%); +} + +/*060 Polite Rumors*/ +.polite_rumors{ + background-image: linear-gradient(to top, #a7a6cb 0%, #8989ba 52%, #8989ba 100%); +} + +/*061 Sweet Period*/ +.sweet_period{ + background-image: linear-gradient(to top, #3f51b1 0%, #5a55ae 13%, #7b5fac 25%, #8f6aae 38%, #a86aa4 50%, #cc6b8e 62%, #f18271 75%, #f3a469 87%, #f7c978 100%); +} + +/*062 Wide Matrix*/ +.wide_matrix{ + background-image: linear-gradient(to top, #fcc5e4 0%, #fda34b 15%, #ff7882 35%, #c8699e 52%, #7046aa 71%, #0c1db8 87%, #020f75 100%); +} + +/*063 Soft Cherish*/ +.soft_cherish{ + background-image: linear-gradient(to top, #dbdcd7 0%, #dddcd7 24%, #e2c9cc 30%, #e7627d 46%, #b8235a 59%, #801357 71%, #3d1635 84%, #1c1a27 100%); +} + +/*064 Red Salvation*/ +.red_salvation{ + background-image: linear-gradient(to top, #f43b47 0%, #453a94 100%); +} + +/*065 Burning Spring*/ +.burning_spring{ + background-image: linear-gradient(to top, #4fb576 0%, #44c489 30%, #28a9ae 46%, #28a2b7 59%, #4c7788 71%, #6c4f63 86%, #432c39 100%); +} + +/*066 Night Party*/ +.night_party{ + background-image: linear-gradient(to top, #0250c5 0%, #d43f8d 100%); +} + +/*067 Sky Glider*/ +.sky_glider{ + background-image: linear-gradient(to top, #88d3ce 0%, #6e45e2 100%); +} + +/*068 Heaven Peach*/ +.heaven_peach{ + background-image: linear-gradient(to top, #d9afd9 0%, #97d9e1 100%); +} + +/*069 Purple Division*/ +.purple_division{ + background-image: linear-gradient(to top, #7028e4 0%, #e5b2ca 100%); +} + +/*070 Aqua Splash*/ +.aqua_splash{ + background-image: linear-gradient(15deg, #13547a 0%, #80d0c7 100%); +} + +/*071 Above Clouds*/ +.above_clouds{ + background-image: linear-gradient(to left, #BDBBBE 0%, #9D9EA3 100%), radial-gradient(88% 271%, rgba(255, 255, 255, 0.25) 0%, rgba(254, 254, 254, 0.25) 1%, rgba(0, 0, 0, 0.25) 100%), radial-gradient(50% 100%, rgba(255, 255, 255, 0.30) 0%, rgba(0, 0, 0, 0.30) 100%); + background-blend-mode: normal, lighten, soft-light; +} + +/*072 Spiky Naga*/ +.spiky_naga{ + background-image: linear-gradient(to top, #505285 0%, #585e92 12%, #65689f 25%, #7474b0 37%, #7e7ebb 50%, #8389c7 62%, #9795d4 75%, #a2a1dc 87%, #b5aee4 100%); +} + +/*073 Love Kiss*/ +.love_kiss{ + background-image: linear-gradient(to top, #ff0844 0%, #ffb199 100%); +} + +/*074 Sharp Glass*/ +.sharp_glass{ + background: #C9CCD3 linear-gradient(-180deg, rgba(255, 255, 255, 0.50) 0%, rgba(0, 0, 0, 0.50) 100%); + background-blend-mode: lighten; +} + +/*075 Clean Mirror*/ +.clean_mirror{ + background-image: linear-gradient(45deg, #93a5cf 0%, #e4efe9 100%); +} + +/*076 Premium Dark*/ +.premium_dark{ + background-image: linear-gradient(to right, #434343 0%, black 100%); +} + +/*077 Cold Evening*/ +.cold_evening{ + background-image: linear-gradient(to top, #0c3483 0%, #a2b6df 100%, #6b8cce 100%, #a2b6df 100%); +} + +/*078 Cochiti Lake*/ +.cochiti_lake{ + background-image: linear-gradient(45deg, #93a5cf 0%, #e4efe9 100%); +} + +/*079 Summer Games*/ +.summer_games{ + background-image: linear-gradient(to right, #92fe9d 0%, #00c9ff 100%); +} + +/*080 Passionate Bed*/ +.passionate_bed{ + background-image: linear-gradient(to right, #ff758c 0%, #ff7eb3 100%); +} + +/*081 Mountain Rock*/ +.mountain_rock{ + background-image: linear-gradient(to right, #868f96 0%, #596164 100%); +} + +/*082 Desert Hump*/ +.desert_hump{ + background-image: linear-gradient(to top, #c79081 0%, #dfa579 100%); +} + +/*083 Jungle Day*/ +.jungle_day{ + background-image: linear-gradient(45deg, #8baaaa 0%, #ae8b9c 100%); +} + +/*084 Phoenix Start*/ +.phoenix_start{ + background-image: linear-gradient(to right, #f83600 0%, #f9d423 100%); +} + +/*085 October Silence*/ +.october_silence{ + background-image: linear-gradient(-20deg, #b721ff 0%, #21d4fd 100%); +} + +/*086 Faraway River*/ +.faraway_river{ + background-image: linear-gradient(-20deg, #6e45e2 0%, #88d3ce 100%); +} + +/*087 Alchemist Lab*/ +.alchemist_lab{ + background-image: linear-gradient(-20deg, #d558c8 0%, #24d292 100%); +} + +/*088 Over Sun*/ +.over_sun{ + background-image: linear-gradient(60deg, #abecd6 0%, #fbed96 100%); +} + +/*089 Premium White*/ +.premium_white{ + background-image: linear-gradient(to top, #d5d4d0 0%, #d5d4d0 1%, #eeeeec 31%, #efeeec 75%, #e9e9e7 100%); +} + +/*090 Mars Party*/ +.mars_party{ + background-image: linear-gradient(to top, #5f72bd 0%, #9b23ea 100%); +} + +/*091 Eternal Constance*/ +.eternal_constance{ + background-image: linear-gradient(to top, #09203f 0%, #537895 100%); +} + +/*092 Japan Blush*/ +.japan_blush{ + background-image: linear-gradient(-20deg, #ddd6f3 0%, #faaca8 100%, #faaca8 100%); +} + +/*093 Smiling Rain*/ +.smiling_rain{ + background-image: linear-gradient(-20deg, #dcb0ed 0%, #99c99c 100%); +} + +/*094 Cloudy Apple*/ +.cloudy_apple{ + background-image: linear-gradient(to top, #f3e7e9 0%, #e3eeff 99%, #e3eeff 100%); +} + +/*095 Big Mango*/ +.big_mango{ + background-image: linear-gradient(to top, #c71d6f 0%, #d09693 100%); +} + +/*096 Healthy Water*/ +.healthy_water{ + background-image: linear-gradient(60deg, #96deda 0%, #50c9c3 100%); +} + +/*097 Amour Amour*/ +.amour_amour{ + background-image: linear-gradient(to top, #f77062 0%, #fe5196 100%); +} + +/*098 Risky Concrete*/ +.risky_concrete{ + background-image: linear-gradient(to top, #c4c5c7 0%, #dcdddf 52%, #ebebeb 100%); +} + +/*099 Strong Stick*/ +.strong_stick{ + background-image: linear-gradient(to right, #a8caba 0%, #5d4157 100%); +} + +/*100 Vicious Stance*/ +.vicious_stance{ + background-image: linear-gradient(60deg, #29323c 0%, #485563 100%); +} + +/*101 Palo Alto*/ +.palo_alto{ + background-image: linear-gradient(-60deg, #16a085 0%, #f4d03f 100%); +} + +/*102 Happy Memories*/ +.happy_memories{ + background-image: linear-gradient(-60deg, #ff5858 0%, #f09819 100%); +} + +/*103 Midnight Bloom*/ +.midnight_bloom{ + background-image: linear-gradient(-20deg, #2b5876 0%, #4e4376 100%); +} + +/*104 Crystalline*/ +.crystalline{ + background-image: linear-gradient(-20deg, #00cdac 0%, #8ddad5 100%); +} + +/*105 Raccoon Back*/ +.raccoon_back{ + background: linear-gradient(-180deg, #BCC5CE 0%, #929EAD 98%), radial-gradient(at top left, rgba(255,255,255,0.30) 0%, rgba(0,0,0,0.30) 100%); + background-blend-mode: screen; +} + +/*106 Party Bliss*/ +.party_bliss{ + background-image: linear-gradient(to top, #4481eb 0%, #04befe 100%); +} + +/*107 Confident Cloud*/ +.confident_cloud{ + background-image: linear-gradient(to top, #dad4ec 0%, #dad4ec 1%, #f3e7e9 100%); +} + +/*108 Le Cocktail*/ +.le_cocktail{ + background-image: linear-gradient(45deg, #874da2 0%, #c43a30 100%); +} + +/*109 River City*/ +.river_city{ + background-image: linear-gradient(to top, #4481eb 0%, #04befe 100%); +} + +/*110 Frozen Berry*/ +.frozen_berry{ + background-image: linear-gradient(to top, #e8198b 0%, #c7eafd 100%); +} + +/*111 Elegance*/ +.elegance{ + background-image: radial-gradient(73% 147%, #EADFDF 59%, #ECE2DF 100%), radial-gradient(91% 146%, rgba(255,255,255,0.50) 47%, rgba(0,0,0,0.50) 100%); + background-blend-mode: screen; +} + +/*112 Child Care*/ +.child_care{ + background-image: linear-gradient(-20deg, #f794a4 0%, #fdd6bd 100%); +} + +/*113 Flying Lemon*/ +.flying_lemon{ + background-image: linear-gradient(60deg, #64b3f4 0%, #c2e59c 100%); +} + +/*114 New Retrowave*/ +.new_retrowave{ + background-image: linear-gradient(to top, #3b41c5 0%, #a981bb 49%, #ffc8a9 100%); +} + +/*115 Hidden Jaguar*/ +.hidden_jaguar{ + background-image: linear-gradient(to top, #0fd850 0%, #f9f047 100%); +} + +/*116 Above The Sky*/ +.above_the_sky{ + background-image: linear-gradient(to top, lightgrey 0%, lightgrey 1%, #e0e0e0 26%, #efefef 48%, #d9d9d9 75%, #bcbcbc 100%); +} + +/*117 Nega*/ +.nega{ + background-image: linear-gradient(45deg, #ee9ca7 0%, #ffdde1 100%); +} + +/*118 Dense Water*/ +.dense_water{ + background-image: linear-gradient(to right, #3ab5b0 0%, #3d99be 31%, #56317a 100%); +} + +/*119 Chemic Aqua*/ +.chemic_aqua{ + background: #CDDCDC radial-gradient(at 50% 100%, rgba(255, 255, 255, 0.50) 0%, rgba(0, 0, 0, 0.50) 100%), linear-gradient(to bottom, rgba(255, 255, 255, 0.25) 0%, rgba(0, 0, 0, 0.25) 100%); + background-blend-mode: screen, overlay; +} + +/*120 Seashore*/ +.seashore{ + background-image: linear-gradient(to top, #209cff 0%, #68e0cf 100%); +} + +/*121 Marble Wall*/ +.marble_wall{ + background-image: linear-gradient(to top, #bdc2e8 0%, #bdc2e8 1%, #e6dee9 100%); +} + +/*122 Cheerful Caramel*/ +.cheerful_caramel{ + background-image: linear-gradient(to top, #e6b980 0%, #eacda3 100%); +} + +/*123 Night Sky*/ +.night_sky{ + background-image: linear-gradient(to top, #1e3c72 0%, #1e3c72 1%, #2a5298 100%); +} + +/*124 Magic Lake*/ +.magic_lake{ + background-image: linear-gradient(to top, #d5dee7 0%, #ffafbd 0%, #c9ffbf 100%); +} + +/*125 Young Grass*/ +.young_grass{ + background-image: linear-gradient(to top, #9be15d 0%, #00e3ae 100%); +} + +/*126 Colorful Peach*/ +.colorful_peach{ + background-image: linear-gradient(to right, #ed6ea0 0%, #ec8c69 100%); +} + +/*127 Gentle Care*/ +.gentle_care{ + background-image: linear-gradient(to right, #ffc3a0 0%, #ffafbd 100%); +} + +/*128 Plum Bath*/ +.plum_bath{ + background-image: linear-gradient(to top, #cc208e 0%, #6713d2 100%); +} + +/*129 Happy Unicorn*/ +.happy_unicorn{ + background-image: linear-gradient(to top, #b3ffab 0%, #12fff7 100%); +} + +/*130 Full Metal*/ +.full_metal{ + background: linear-gradient(to bottom, #D5DEE7 0%, #E8EBF2 50%, #E2E7ED 100%), linear-gradient(to bottom, rgba(0,0,0,0.02) 50%, rgba(255,255,255,0.02) 61%, rgba(0,0,0,0.02) 73%), linear-gradient(33deg, rgba(255,255,255,0.20) 0%, rgba(0,0,0,0.20) 100%); + background-blend-mode: normal,color-burn; +} + +/*131 African Field*/ +.african_field{ + background-image: linear-gradient(to top, #65bd60 0%, #5ac1a8 25%, #3ec6ed 50%, #b7ddb7 75%, #fef381 100%); +} + +/*132 Solid Stone*/ +.solid_stone{ + background-image: linear-gradient(to right, #243949 0%, #517fa4 100%); +} + +/*133 Orange Juice*/ +.orange_juice{ + background-image: linear-gradient(-20deg, #fc6076 0%, #ff9a44 100%); +} + +/*134 Glass Water*/ +.glass_water{ + background-image: linear-gradient(to top, #dfe9f3 0%, white 100%); +} + +/*135 Slick Carbon*/ +.slick_carbon{ + background: linear-gradient(to bottom, #323232 0%, #3F3F3F 40%, #1C1C1C 150%), linear-gradient(to top, rgba(255,255,255,0.40) 0%, rgba(0,0,0,0.25) 200%); + background-blend-mode: multiply; +} + +/*136 North Miracle*/ +.north_miracle{ + background-image: linear-gradient(to right, #00dbde 0%, #fc00ff 100%); +} + +/*137 Fruit Blend*/ +.fruit_blend{ + background-image: linear-gradient(to right, #f9d423 0%, #ff4e50 100%); +} + +/*138 Millennium Pine*/ +.millennium_pine{ + background-image: linear-gradient(to top, #50cc7f 0%, #f5d100 100%); +} + +/*139 High Flight*/ +.high_flight{ + background-image: linear-gradient(to right, #0acffe 0%, #495aff 100%); +} + +/*140 Mole Hall*/ +.mole_hall{ + background-image: linear-gradient(-20deg, #616161 0%, #9bc5c3 100%); +} + +/*141 Earl Gray*/ +.earl_gray{ + background: #E4E4E1 radial-gradient(at top center, rgba(255, 255, 255, 0.03) 0%, rgba(0, 0, 0, 0.03) 100%), linear-gradient(to top, rgba(255, 255, 255, 0.1) 0%, rgba(143, 152, 157, 0.60) 100%); + background-blend-mode: normal, multiply; +} + +/*142 Space Shift*/ +.space_shift{ + background-image: linear-gradient(60deg, #3d3393 0%, #2b76b9 37%, #2cacd1 65%, #35eb93 100%); +} + +/*143 Forest Inei*/ +.forest_inei{ + background-image: linear-gradient(to top, #df89b5 0%, #bfd9fe 100%); +} + +/*144 Royal Garden*/ +.royal_garden{ + background-image: linear-gradient(to right, #ed6ea0 0%, #ec8c69 100%); +} + +/*145 Rich Metal*/ +.rich_metal{ + background-image: linear-gradient(to right, #d7d2cc 0%, #304352 100%); +} + +/*146 Juicy Cake*/ +.juicy_cake{ + background-image: linear-gradient(to top, #e14fad 0%, #f9d423 100%); +} + +/*147 Smart Indigo*/ +.smart_indigo{ + background-image: linear-gradient(to top, #b224ef 0%, #7579ff 100%); +} + +/*148 Sand Strike*/ +.sand_strike{ + background-image: linear-gradient(to right, #c1c161 0%, #c1c161 0%, #d4d4b1 100%); +} + +/*149 Norse Beauty*/ +.norse_beauty{ + background-image: linear-gradient(to right, #ec77ab 0%, #7873f5 100%); +} + +/*150 Aqua Guidance*/ +.aqua_guidance{ + background-image: linear-gradient(to top, #007adf 0%, #00ecbc 100%); +} + +/*151 Sun Veggie*/ +.sun_veggie{ + background-image: linear-gradient(-225deg, #20E2D7 0%, #F9FEA5 100%); +} + +/*152 Sea Lord*/ +.sea_lord{ + background-image: linear-gradient(-225deg, #2CD8D5 0%, #C5C1FF 56%, #FFBAC3 100%); +} + +/*153 Black Sea*/ +.black_sea{ + background-image: linear-gradient(-225deg, #2CD8D5 0%, #6B8DD6 48%, #8E37D7 100%); +} + +/*154 Grass Shampoo*/ +.grass_shampoo{ + background-image: linear-gradient(-225deg, #DFFFCD 0%, #90F9C4 48%, #39F3BB 100%); +} + +/*155 Landing Aircraft*/ +.landing_aircraft{ + background-image: linear-gradient(-225deg, #5D9FFF 0%, #B8DCFF 48%, #6BBBFF 100%); +} + +/*156 Witch Dance*/ +.witch_dance{ + background-image: linear-gradient(-225deg, #A8BFFF 0%, #884D80 100%); +} + +/*157 Sleepless Night*/ +.sleepless_night{ + background-image: linear-gradient(-225deg, #5271C4 0%, #B19FFF 48%, #ECA1FE 100%); +} + +/*158 Angel Care*/ +.angel_care{ + background-image: linear-gradient(-225deg, #FFE29F 0%, #FFA99F 48%, #FF719A 100%); +} + +/*159 Crystal River*/ +.crystal_river{ + background-image: linear-gradient(-225deg, #22E1FF 0%, #1D8FE1 48%, #625EB1 100%); +} + +/*160 Soft Lipstick*/ +.soft_lipstick{ + background-image: linear-gradient(-225deg, #B6CEE8 0%, #F578DC 100%); +} + +/*161 Salt Mountain*/ +.salt_mountain{ + background-image: linear-gradient(-225deg, #FFFEFF 0%, #D7FFFE 100%); +} + +/*162 Perfect White*/ +.perfect_white{ + background-image: linear-gradient(-225deg, #E3FDF5 0%, #FFE6FA 100%); +} + +/*163 Fresh Oasis*/ +.fresh_oasis{ + background-image: linear-gradient(-225deg, #7DE2FC 0%, #B9B6E5 100%); +} + +/*164 Strict November*/ +.strict_november{ + background-image: linear-gradient(-225deg, #CBBACC 0%, #2580B3 100%); +} + +/*165 Morning Salad*/ +.morning_salad{ + background-image: linear-gradient(-225deg, #B7F8DB 0%, #50A7C2 100%); +} + +/*166 Deep Relief*/ +.deep_relief{ + background-image: linear-gradient(-225deg, #7085B6 0%, #87A7D9 50%, #DEF3F8 100%); +} + +/*167 Sea Strike*/ +.sea_strike{ + background-image: linear-gradient(-225deg, #77FFD2 0%, #6297DB 48%, #1EECFF 100%); +} + +/*168 Night Call*/ +.night_call{ + background-image: linear-gradient(-225deg, #AC32E4 0%, #7918F2 48%, #4801FF 100%); +} + +/*169 Supreme Sky*/ +.supreme_sky{ + background-image: linear-gradient(-225deg, #D4FFEC 0%, #57F2CC 48%, #4596FB 100%); +} + +/*170 Light Blue*/ +.light_blue{ + background-image: linear-gradient(-225deg, #9EFBD3 0%, #57E9F2 48%, #45D4FB 100%); +} + +/*171 Mind Crawl*/ +.mind_crawl{ + background-image: linear-gradient(-225deg, #473B7B 0%, #3584A7 51%, #30D2BE 100%); +} + +/*172 Lily Meadow*/ +.lily_meadow{ + background-image: linear-gradient(-225deg, #65379B 0%, #886AEA 53%, #6457C6 100%); +} + +/*173 Sugar Lollipop*/ +.sugar_lollipop{ + background-image: linear-gradient(-225deg, #A445B2 0%, #D41872 52%, #FF0066 100%); +} + +/*174 Sweet Dessert*/ +.sweet_dessert{ + background-image: linear-gradient(-225deg, #7742B2 0%, #F180FF 52%, #FD8BD9 100%); +} + +/*175 Magic Ray*/ +.magic_ray{ + background-image: linear-gradient(-225deg, #FF3CAC 0%, #562B7C 52%, #2B86C5 100%); +} + +/*176 Teen Party*/ +.teen_party{ + background-image: linear-gradient(-225deg, #FF057C 0%, #8D0B93 50%, #321575 100%); +} + +/*177 Frozen Heat*/ +.frozen_heat{ + background-image: linear-gradient(-225deg, #FF057C 0%, #7C64D5 48%, #4CC3FF 100%); +} + +/*178 Gagarin View*/ +.gagarin_view{ + background-image: linear-gradient(-225deg, #69EACB 0%, #EACCF8 48%, #6654F1 100%); +} + +/*179 Fabled Sunset*/ +.fabled_sunset{ + background-image: linear-gradient(-225deg, #231557 0%, #44107A 29%, #FF1361 67%, #FFF800 100%); +} + +/*180 Perfect Blue*/ +.perfect_blue{ + background-image: linear-gradient(-225deg, #3D4E81 0%, #5753C9 48%, #6E7FF3 100%); +} |