File indexing completed on 2024-05-12 15:56:42

0001 /*
0002  *  SPDX-FileCopyrightText: 2016 Dmitry Kazakov <dimula73@gmail.com>
0003  *  SPDX-FileCopyrightText: 2020 Sharaf Zaman <sharafzaz121@gmail.com>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 #ifndef KOMESHPATCHESRENDERER_H
0008 #define KOMESHPATCHESRENDERER_H
0009 
0010 #include <QImage>
0011 #include <QPainter>
0012 #include <QPainterPath>
0013 #include <QVector>
0014 #include <QRectF>
0015 
0016 #include <KoColorSpaceRegistry.h>
0017 #include <KoMixColorsOp.h>
0018 #include <SvgMeshArray.h>
0019 #include <SvgMeshGradient.h>
0020 
0021 struct KoMeshPatchesRenderer {
0022 public:
0023 
0024     KoMeshPatchesRenderer()
0025     {}
0026 
0027     void configure(QRectF gradientgRect, const QTransform& painterTransform) {
0028 
0029         // NOTE: This is a necessary step to prevent loss of quality, because painterTransform is scaled.
0030 
0031         // we wish to scale the patch, but not translate, because patch should stay inside
0032         // the boundingRect
0033         QTransform painterTransformShifted = painterTransform *
0034             QTransform::fromTranslate(painterTransform.dx(), painterTransform.dy()).inverted();
0035 
0036         // we are applying transformation on a Unit rect, so we can extract scaling info only
0037         QRectF unitRectScaled = painterTransformShifted.mapRect(QRectF(gradientgRect.topLeft(), QSize(1, 1)));
0038         QTransform scaledTransform = QTransform::fromScale(unitRectScaled.width(), unitRectScaled.height());
0039 
0040         // boundingRect of the scaled version
0041         QRectF scaledGradientRect = scaledTransform.mapRect(gradientgRect);
0042 
0043         m_patch = QImage(scaledGradientRect.size().toSize(), QImage::Format_ARGB32);
0044         m_patch.fill(Qt::transparent);
0045 
0046         m_patchPainter.begin(&m_patch);
0047 
0048         // this ensures that the patch renders inside the boundingRect
0049         m_patchPainter.translate(-scaledGradientRect.topLeft());
0050 
0051         // upscale the patch to the same scaling factor as the painterTransform
0052         m_patchPainter.setTransform(scaledTransform, true);
0053         m_patchPainter.setCompositionMode(QPainter::CompositionMode_Source);
0054     }
0055 
0056     void fillPatch(const SvgMeshPatch *patch,
0057                    SvgMeshGradient::Shading type,
0058                    const SvgMeshArray *mesharray = nullptr,
0059                    const int row = -1,
0060                    const int col = -1) {
0061 
0062         QColor color0 = patch->getStop(SvgMeshPatch::Top).color;
0063         QColor color1 = patch->getStop(SvgMeshPatch::Right).color;
0064         QColor color2 = patch->getStop(SvgMeshPatch::Bottom).color;
0065         QColor color3 = patch->getStop(SvgMeshPatch::Left).color;
0066 
0067         const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
0068 
0069         quint8 c[4][4];
0070         cs->fromQColor(color0, c[0]);
0071         cs->fromQColor(color1, c[1]);
0072         cs->fromQColor(color2, c[2]);
0073         cs->fromQColor(color3, c[3]);
0074 
0075         bool verticalDiv = patch->isDivisbleVertically();
0076         bool horizontalDiv = patch->isDivisibleHorizontally();
0077         bool colorVariationExists = checkColorVariance(c);
0078 
0079         if (colorVariationExists && (verticalDiv || horizontalDiv)) {
0080             QVector<QColor> colors;
0081             if (type == SvgMeshGradient::BICUBIC) {
0082 
0083                 // it is a parent patch, so calculate coefficients aka alpha
0084                 if (mesharray) {
0085                     calculateAlpha(mesharray, row, col, patch);
0086                 }
0087 
0088                 colors = getColorsBicubic(patch);
0089             } else {
0090                 colors = getColorsBilinear(patch);
0091             }
0092 
0093             if (verticalDiv && horizontalDiv) {
0094                 QVector<SvgMeshPatch*> patches;
0095                 patches.reserve(4);
0096 
0097                 patch->subdivide(patches, colors);
0098                 for (const auto& p: patches) {
0099                     fillPatch(p, type);
0100                     delete p;
0101                 }
0102             } else if (verticalDiv) {
0103                 QVector<SvgMeshPatch*> patches;
0104                 patches.reserve(2);
0105 
0106                 patch->subdivideVertically(patches, colors);
0107                 for (const auto& p: patches) {
0108                     fillPatch(p, type);
0109                     delete p;
0110                 }
0111             } else if (horizontalDiv) {
0112                 QVector<SvgMeshPatch*> patches;
0113                 patches.reserve(2);
0114 
0115                 patch->subdivideHorizontally(patches, colors);
0116                 for (const auto& p: patches) {
0117                     fillPatch(p, type);
0118                     delete p;
0119                 }
0120             }
0121 
0122         } else {
0123             const QPainterPath outline = patch->getPath();
0124             const QRectF patchRect = outline.boundingRect();
0125 
0126             quint8 mixed[4];
0127             cs->mixColorsOp()->mixColors(c[0], 4, mixed);
0128 
0129             QColor average;
0130             cs->toQColor(mixed, &average);
0131 
0132             QPen pen(average);
0133             pen.setWidth(0);
0134             m_patchPainter.setPen(pen);
0135 
0136             if (patchRect.width() <= 1 && patchRect.height() <= 1) {
0137                 m_patchPainter.drawPoint(patchRect.topLeft());
0138                 m_patchPainter.fillPath(outline, average);
0139 
0140             } else {
0141                 m_patchPainter.setBrush(average);
0142                 m_patchPainter.drawPath(outline);
0143             }
0144         }
0145     }
0146 
0147     /*
0148      * returns false if the variation is below tolerance.
0149      */
0150     bool checkColorVariance(quint8 c[4][4])
0151     {
0152         const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
0153         const quint8 tolerance = 0;
0154 
0155         for (int i = 0; i < 3; ++i) {
0156             if (cs->difference(c[i], c[i + 1]) > tolerance) {
0157                 return true;
0158             }
0159 
0160             if (c[i][3] != c[i + 1][3] && cs->differenceA(c[i], c[i + 1]) > tolerance) {
0161                 return true;
0162             }
0163         }
0164 
0165         return false;
0166     }
0167 
0168     QVector<qreal> difference(const QVector<qreal>& v1, const QVector<qreal>& v2)
0169     {
0170         QVector<qreal> v3(4, 0);
0171         for (int i = 0; i < 4; ++i) {
0172             v3[i] = v1[i] - v2[i];
0173         }
0174         return v3;
0175     }
0176 
0177 
0178     QVector<qreal> multiply(const QVector<qreal>& v1, qreal n)
0179     {
0180         QVector<qreal> v3(4, 0);
0181         for (int i = 0; i < 4; ++i) {
0182             v3[i] = v1[i] * n;
0183         }
0184         return v3;
0185     }
0186 
0187     QVector<qreal> split(QColor c)
0188     {
0189         return {c.redF(), c.greenF(), c.blueF(), c.alphaF()};
0190     }
0191 
0192     qreal getValue(const QVector<qreal>& alpha, const QPointF p)
0193     {
0194         KIS_ASSERT(alpha.size() == 16);
0195         qreal x = p.x(), y = p.y();
0196         qreal result = 0;
0197 
0198         qreal xx  = x * x;
0199         qreal xxx = xx * x;
0200         qreal yy  = y * y;
0201         qreal yyy = yy * y;
0202 
0203         result += alpha[0];
0204         result += alpha[1] * x;
0205         result += alpha[2] * xx;
0206         result += alpha[3] * xxx;
0207 
0208         result += alpha[4] * y;
0209         result += alpha[5] * y * x;
0210         result += alpha[6] * y * xx;
0211         result += alpha[7] * y * xxx;
0212 
0213         result += alpha[ 8] * yy;
0214         result += alpha[ 9] * yy * x;
0215         result += alpha[10] * yy * xx;
0216         result += alpha[11] * yy * xxx;
0217 
0218         result += alpha[12] * yyy;
0219         result += alpha[13] * yyy * x;
0220         result += alpha[14] * yyy * xx;
0221         result += alpha[15] * yyy * xxx;
0222 
0223         return result;
0224     }
0225 
0226     QColor getColorUsingAlpha(const QVector<QVector<qreal>>& alpha, QPointF p)
0227     {
0228         qreal r = getValue(alpha[0], p);
0229         qreal g = getValue(alpha[1], p);
0230         qreal b = getValue(alpha[2], p);
0231         qreal a = getValue(alpha[3], p);
0232 
0233         // Clamp
0234         r = r > 1.0 ? 1.0 : r;
0235         g = g > 1.0 ? 1.0 : g;
0236         b = b > 1.0 ? 1.0 : b;
0237         a = a > 1.0 ? 1.0 : a;
0238 
0239         r = r < 0.0 ? 0.0 : r;
0240         g = g < 0.0 ? 0.0 : g;
0241         b = b < 0.0 ? 0.0 : b;
0242         a = a < 0.0 ? 0.0 : a;
0243 
0244         QColor result;
0245         result.setRgbF(r, g, b);
0246         result.setAlphaF(a);
0247         return result;
0248     }
0249 
0250     /// Naming convention adopted from: https://en.wikipedia.org/wiki/Bicubic_interpolation#Computation
0251     QVector<qreal> getAlpha(const QVector<qreal>& X)
0252     {
0253         const static qreal A[16][16] = {
0254             { 1, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},
0255             { 0, 0, 0, 0,  1, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},
0256             {-3, 3, 0, 0, -2,-1, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},
0257             { 2,-2, 0, 0,  1, 1, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},
0258             { 0, 0, 0, 0,  0, 0, 0, 0,  1, 0, 0, 0,  0, 0, 0, 0},
0259             { 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  1, 0, 0, 0},
0260             { 0, 0, 0, 0,  0, 0, 0, 0, -3, 3, 0, 0, -2,-1, 0, 0},
0261             { 0, 0, 0, 0,  0, 0, 0, 0,  2,-2, 0, 0,  1, 1, 0, 0},
0262             {-3, 0, 3, 0,  0, 0, 0, 0, -2, 0,-1, 0,  0, 0, 0, 0},
0263             { 0, 0, 0, 0, -3, 0, 3, 0,  0, 0, 0, 0, -2, 0,-1, 0},
0264             { 9,-9,-9, 9,  6, 3,-6,-3,  6,-6, 3,-3,  4, 2, 2, 1},
0265             {-6, 6, 6,-6, -3,-3, 3, 3, -4, 4,-2, 2, -2,-2,-1,-1},
0266             { 2, 0,-2, 0,  0, 0, 0, 0,  1, 0, 1, 0,  0, 0, 0, 0},
0267             { 0, 0, 0, 0,  2, 0,-2, 0,  0, 0, 0, 0,  1, 0, 1, 0},
0268             {-6, 6, 6,-6, -4,-2, 4, 2, -3, 3,-3, 3, -2,-1,-2,-1},
0269             { 4,-4,-4, 4,  2, 2,-2,-2,  2,-2, 2,-2,  1, 1, 1, 1}};
0270 
0271 
0272         QVector<qreal> alpha(16, 0);
0273         for (int i = 0; i < 16; ++i) {
0274             alpha[i] = 0;
0275             for (int j = 0; j < 16; ++j) {
0276                 alpha[i] += A[i][j] * X[j];
0277             }
0278         }
0279 
0280         return alpha;
0281     }
0282 
0283     QVector<qreal> secant(const SvgMeshStop& stop1, const SvgMeshStop& stop2)
0284     {
0285         qreal distance = QLineF(stop1.point, stop2.point).length();
0286 
0287         if (distance == 0.0) {  // NaN
0288             return {0.0, 0.0, 0.0, 0.0};
0289         }
0290 
0291         qreal c1[4] = {stop1.color.redF(), stop1.color.greenF(), stop1.color.blueF(), stop1.color.alphaF()};
0292         qreal c2[4] = {stop2.color.redF(), stop2.color.greenF(), stop2.color.blueF(), stop2.color.alphaF()};
0293 
0294         QVector<qreal> result(4, 0.0);
0295 
0296         for (int i = 0; i < 4; ++i) {
0297             result[i] = (c2[i] - c1[i]) / distance;
0298         }
0299 
0300         return result;
0301     }
0302 
0303     QVector<qreal> derivative(const SvgMeshStop& stop0,
0304                               const SvgMeshStop& stop1,
0305                               const SvgMeshStop& stop2)
0306     {
0307         QVector<qreal> delta0 = secant(stop0, stop1);
0308         QVector<qreal> delta1 = secant(stop1, stop2);
0309 
0310         QVector<qreal> result(4);
0311 
0312         for (int i = 0; i < 4; ++i) {
0313             qreal slope = (delta0[i] + delta1[i]) / 2;
0314 
0315             // if sign changes
0316             if ((delta0[i] * delta1[i]) < 0 && delta0[i] > 0) {
0317                 slope = 0;
0318             } else if (abs(slope) > 3 * abs(delta0[i])) {
0319                 slope = 3 * delta0[i];
0320             } else if (abs(slope) > 3 * abs(delta1[i])) {
0321                 slope = 3 * delta1[i];
0322             }
0323 
0324             result.push_back(slope);
0325         }
0326         return result;
0327     }
0328 
0329     /// Derivative in the X direction, but the patch should not be on an edge
0330     QVector<QVector<qreal>> derivativeX(const SvgMeshPatch* patch0,
0331                                         const SvgMeshPatch* patch1,
0332                                         const SvgMeshPatch* patch2)
0333     {
0334         SvgMeshStop f10 = patch0->getStop(SvgMeshPatch::Top);
0335         SvgMeshStop f20 = patch0->getStop(SvgMeshPatch::Left);
0336 
0337         SvgMeshStop f11 = patch1->getStop(SvgMeshPatch::Top);
0338         SvgMeshStop f12 = patch1->getStop(SvgMeshPatch::Right);
0339         SvgMeshStop f21 = patch1->getStop(SvgMeshPatch::Left);
0340         SvgMeshStop f22 = patch1->getStop(SvgMeshPatch::Bottom);
0341 
0342         SvgMeshStop f13 = patch2->getStop(SvgMeshPatch::Right);
0343         SvgMeshStop f23 = patch2->getStop(SvgMeshPatch::Bottom);
0344 
0345         QVector<qreal> d11 = derivative(f10, f11, f12);
0346         QVector<qreal> d12 = derivative(f11, f12, f13);
0347         QVector<qreal> d21 = derivative(f20, f21, f22);
0348         QVector<qreal> d22 = derivative(f21, f22, f23);
0349 
0350         return {d11, d12, d21, d22};
0351     }
0352 
0353     /// Derivative in the Y direction, but the patch should not be on an edge
0354     QVector<QVector<qreal>> derivativeY(const SvgMeshPatch* patch0,
0355                                         const SvgMeshPatch* patch1,
0356                                         const SvgMeshPatch* patch2)
0357     {
0358         SvgMeshStop f01 = patch0->getStop(SvgMeshPatch::Top);
0359         SvgMeshStop f02 = patch0->getStop(SvgMeshPatch::Right);
0360 
0361         SvgMeshStop f11 = patch1->getStop(SvgMeshPatch::Top);
0362         SvgMeshStop f12 = patch1->getStop(SvgMeshPatch::Right);
0363         SvgMeshStop f21 = patch1->getStop(SvgMeshPatch::Left);
0364         SvgMeshStop f22 = patch1->getStop(SvgMeshPatch::Bottom);
0365 
0366         SvgMeshStop f31 = patch2->getStop(SvgMeshPatch::Left);
0367         SvgMeshStop f32 = patch2->getStop(SvgMeshPatch::Bottom);
0368 
0369         QVector<qreal> d11 = derivative(f01, f11, f21);
0370         QVector<qreal> d12 = derivative(f02, f12, f22);
0371         QVector<qreal> d21 = derivative(f11, f21, f31);
0372         QVector<qreal> d22 = derivative(f12, f22, f32);
0373 
0374         return {d11, d12, d21, d22};
0375     }
0376 
0377     /// Derivative in the X direction. The edge has to be in left-most column.
0378     QVector<QVector<qreal>> derivativeEdgeBeginX(const SvgMeshPatch* patch0,
0379                                                  const SvgMeshPatch* patch1)
0380     {
0381         SvgMeshStop f00 = patch0->getStop(SvgMeshPatch::Top);
0382         SvgMeshStop f01 = patch0->getStop(SvgMeshPatch::Right);
0383         SvgMeshStop f10 = patch0->getStop(SvgMeshPatch::Left);
0384         SvgMeshStop f11 = patch0->getStop(SvgMeshPatch::Bottom);
0385 
0386         SvgMeshStop f02 = patch1->getStop(SvgMeshPatch::Right);
0387         SvgMeshStop f12 = patch1->getStop(SvgMeshPatch::Bottom);
0388 
0389         QVector<qreal> d01 = derivative(f00, f01, f02);
0390         QVector<qreal> d11 = derivative(f10, f11, f12);
0391         QVector<qreal> d00 = difference(multiply(secant(f00, f01), 2), d01);
0392         QVector<qreal> d10 = difference(multiply(secant(f10, f11), 2), d11);
0393 
0394         return {d00, d01, d10, d11};
0395     }
0396 
0397     /// Derivative in the Y direction. The edge has to be in top row
0398     QVector<QVector<qreal>> derivativeEdgeBeginY(const SvgMeshPatch* patch0,
0399                                                  const SvgMeshPatch* patch1)
0400     {
0401         SvgMeshStop f00 = patch0->getStop(SvgMeshPatch::Top);
0402         SvgMeshStop f01 = patch0->getStop(SvgMeshPatch::Right);
0403         SvgMeshStop f10 = patch0->getStop(SvgMeshPatch::Left);
0404         SvgMeshStop f11 = patch0->getStop(SvgMeshPatch::Bottom);
0405 
0406         SvgMeshStop f20 = patch1->getStop(SvgMeshPatch::Left);
0407         SvgMeshStop f21 = patch1->getStop(SvgMeshPatch::Bottom);
0408 
0409         QVector<qreal> d10 = derivative(f00, f10, f20);
0410         QVector<qreal> d11 = derivative(f01, f11, f21);
0411         QVector<qreal> d00 = difference(multiply(secant(f00, f10), 2), d10);
0412         QVector<qreal> d01 = difference(multiply(secant(f01, f11), 2), d11);
0413 
0414         return {d00, d01, d10, d11};
0415     }
0416 
0417     // Derivative in the X direction. The edge has to be in right-most column.
0418     QVector<QVector<qreal>> derivativeEdgeEndX(const SvgMeshPatch* patch1,
0419                                                const SvgMeshPatch* patch0)
0420     {
0421         SvgMeshStop f02 = patch1->getStop(SvgMeshPatch::Top);
0422         SvgMeshStop f03 = patch1->getStop(SvgMeshPatch::Right);
0423         SvgMeshStop f12 = patch1->getStop(SvgMeshPatch::Left);
0424         SvgMeshStop f13 = patch1->getStop(SvgMeshPatch::Bottom);
0425 
0426         SvgMeshStop f01 = patch0->getStop(SvgMeshPatch::Top);
0427         SvgMeshStop f11 = patch0->getStop(SvgMeshPatch::Left);
0428 
0429         QVector<qreal> d02 = derivative(f01, f02, f03);
0430         QVector<qreal> d12 = derivative(f11, f12, f13);
0431         QVector<qreal> d03 = difference(multiply(secant(f02, f03), 2), d02);
0432         QVector<qreal> d13 = difference(multiply(secant(f12, f13), 2), d12);
0433 
0434         return {d02, d03, d12, d13};
0435     }
0436 
0437     // Derivative in the Y direction. The edge has to be the bottom row
0438     QVector<QVector<qreal>> derivativeEdgeEndY(const SvgMeshPatch* patch1,
0439                                                 const SvgMeshPatch* patch0)
0440     {
0441         SvgMeshStop f22 = patch1->getStop(SvgMeshPatch::Top);
0442         SvgMeshStop f23 = patch1->getStop(SvgMeshPatch::Right);
0443         SvgMeshStop f32 = patch1->getStop(SvgMeshPatch::Left);
0444         SvgMeshStop f33 = patch1->getStop(SvgMeshPatch::Bottom);
0445 
0446         SvgMeshStop f12 = patch0->getStop(SvgMeshPatch::Top);
0447         SvgMeshStop f13 = patch0->getStop(SvgMeshPatch::Right);
0448 
0449         QVector<qreal> d22 = derivative(f12, f22, f32);
0450         QVector<qreal> d23 = derivative(f13, f23, f33);
0451         QVector<qreal> d32 = difference(multiply(secant(f22, f32), 2), d22);
0452         QVector<qreal> d33 = difference(multiply(secant(f23, f33), 2), d23);
0453 
0454         return {d22, d23, d32, d33};
0455     }
0456 
0457     // TODO: Make this private
0458     void calculateAlpha(const SvgMeshArray* mesharray, const int row, const int col, const SvgMeshPatch* patch)
0459     {
0460         // This is the convention which is being followed:
0461         //
0462         // f00, f03, f30, f33 are corners and their respective derivatives are represented as
0463         // d00, d03, d30, d33
0464         //   
0465         //   +-----> U/x (row)
0466         // |
0467         // |                 f00-------f01-------f02-------f03
0468         // V                  |         |         |         |
0469         //  V/y (col)         |         |         |         |
0470         //                    |         |         |         |
0471         //                   f10-------f11-------f12-------f13
0472         //                    |         |         |         |
0473         //                    |         |         |         |
0474         //                    |         |         |         |
0475         //                   f20-------f21-------f22-------f23
0476         //                    |         |         |         |
0477         //                    |         |         |         |
0478         //                    |         |         |         |
0479         //                   f30-------f31-------f32-------f33
0480         //
0481 
0482         SvgMeshStop f11 = patch->getStop(SvgMeshPatch::Top);
0483         SvgMeshStop f12 = patch->getStop(SvgMeshPatch::Right);
0484         SvgMeshStop f21 = patch->getStop(SvgMeshPatch::Left);
0485         SvgMeshStop f22 = patch->getStop(SvgMeshPatch::Bottom);
0486 
0487         QVector<QVector<qreal>> dx(4, QVector<qreal>(4, 0));
0488         QVector<QVector<qreal>> dy(4, QVector<qreal>(4, 0));
0489 
0490         // dx
0491         if (!mesharray || mesharray->numColumns() < 2) {
0492 
0493             // NOTE: they're zero here: from trial and error
0494             dx[0] = multiply(secant(f11, f12), 2);
0495             dx[2] = multiply(secant(f21, f22), 2);
0496             dx[1] = dx[3] = {0, 0, 0, 0};
0497 
0498         } else if (col == 0) {
0499             dx = derivativeEdgeBeginX(patch, mesharray->getPatch(row, col + 1));
0500 
0501         } else if (col == mesharray->numColumns() - 1) {
0502             dx = derivativeEdgeEndX(mesharray->getPatch(row, col),
0503                                      mesharray->getPatch(row, col - 1));
0504         } else {
0505             dx = derivativeX(mesharray->getPatch(row, col - 1),
0506                              mesharray->getPatch(row, col),
0507                              mesharray->getPatch(row, col + 1));
0508         }
0509 
0510         // dy
0511         if (!mesharray || mesharray->numRows() < 2) {
0512 
0513             // NOTE: they're zero here: from trial and error
0514             dy[0] = multiply(secant(f11, f21), 2);
0515             dy[1] = multiply(secant(f12, f22), 2);
0516             dy[2] = dy[3] = {0, 0, 0, 0};
0517 
0518         } else if (row == 0) {
0519             dy = derivativeEdgeBeginY(patch, mesharray->getPatch(row + 1, col));
0520 
0521         } else if (row == mesharray->numRows() - 1) {
0522             dy = derivativeEdgeEndY(mesharray->getPatch(row, col),
0523                                      mesharray->getPatch(row - 1, col));
0524         } else {
0525             dy = derivativeY(mesharray->getPatch(row - 1, col),
0526                              mesharray->getPatch(row, col),
0527                              mesharray->getPatch(row + 1, col));
0528         }
0529 
0530         QVector<QVector<qreal>> c = {split(f11.color), split(f12.color), split(f21.color), split(f22.color)};
0531         QVector<QVector<qreal>> alpha(4, QVector<qreal>(16, 0));
0532 
0533         qreal width01 = QLineF(f11.point, f12.point).length();
0534         qreal width23 = QLineF(f21.point, f22.point).length();
0535 
0536         qreal height01 = QLineF(f11.point, f21.point).length();
0537         qreal height23 = QLineF(f12.point, f22.point).length();
0538 
0539         for (int i = 0; i < 4; ++i) {
0540             QVector<qreal> X {
0541                 c[0][i],
0542                 c[1][i],
0543                 c[2][i],
0544                 c[3][i],
0545 
0546                 dx[0][i] * width01,
0547                 dx[1][i] * width01,
0548                 dx[2][i] * width23,
0549                 dx[3][i] * width23,
0550 
0551                 dy[0][i] * height01,
0552                 dy[1][i] * height23,
0553                 dy[2][i] * height01,
0554                 dy[3][i] * height23,
0555 
0556                 // Specs says not to care about cross derivatives
0557                 0,
0558                 0,
0559                 0,
0560                 0};
0561 
0562             alpha[i] = getAlpha(X);
0563         }
0564 
0565         m_alpha = alpha;
0566     }
0567 
0568     QVector<QColor> getColorsBicubic(const SvgMeshPatch* patch)
0569     {
0570         QPointF midTop    = patch->getMidpointParametric(SvgMeshPatch::Top);
0571         QPointF midRight  = patch->getMidpointParametric(SvgMeshPatch::Right);
0572         QPointF midBottom = patch->getMidpointParametric(SvgMeshPatch::Bottom);
0573         QPointF midLeft   = patch->getMidpointParametric(SvgMeshPatch::Left);
0574         QPointF center    = (midTop + midBottom) / 2;
0575 
0576         QVector<QColor> result(5);
0577         result[0] = getColorUsingAlpha(m_alpha, midTop);
0578         result[1] = getColorUsingAlpha(m_alpha, midRight);
0579         result[2] = getColorUsingAlpha(m_alpha, midBottom);
0580         result[3] = getColorUsingAlpha(m_alpha, midLeft);
0581         result[4] = getColorUsingAlpha(m_alpha, center);
0582 
0583         return result;
0584     }
0585 
0586     QColor midPointColor(QColor first, QColor second)
0587     {
0588         qreal a = (first.alphaF() + second.alphaF()) / 2;
0589         qreal r = (first.redF()   + second.redF()) / 2;
0590         qreal g = (first.greenF() + second.greenF()) / 2;
0591         qreal b = (first.blueF()  + second.blueF()) / 2;
0592 
0593         QColor c;
0594         c.setRgbF(r, g, b, a);
0595         return c;
0596     }
0597 
0598     QVector<QColor> getColorsBilinear(const SvgMeshPatch* patch)
0599     {
0600         QVector<QColor> result(5);
0601 
0602         QColor c1 = patch->getStop(SvgMeshPatch::Top).color;
0603         QColor c2 = patch->getStop(SvgMeshPatch::Right).color;
0604         QColor c3 = patch->getStop(SvgMeshPatch::Bottom).color;
0605         QColor c4 = patch->getStop(SvgMeshPatch::Left).color;
0606 
0607         result[0] = midPointColor(c1, c2);
0608         result[1] = midPointColor(c2, c3);
0609         result[2] = midPointColor(c3, c4);
0610         result[3] = midPointColor(c4, c1);
0611         result[4] = midPointColor(midPointColor(result[0], result[2]), midPointColor(result[1], result[3]));
0612 
0613         return result;
0614     }
0615 
0616     QImage* patchImage() {
0617         return &m_patch;
0618     }
0619 
0620 private:
0621     QImage m_patch;
0622     QPainter m_patchPainter;
0623     // TODO: make them local
0624     QVector<QVector<qreal>> m_alpha;
0625 };
0626 
0627 #endif // KOMESHPATCHESRENDERER_H