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