File indexing completed on 2024-05-12 15:58:49
0001 /* 0002 * SPDX-FileCopyrightText: 2020 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "KisBezierGradientMesh.h" 0008 0009 #include "kis_grid_interpolation_tools.h" 0010 #include "kis_debug.h" 0011 #include "kis_dom_utils.h" 0012 0013 namespace KisBezierGradientMeshDetail { 0014 0015 struct QImageGradientOp 0016 { 0017 QImageGradientOp(const std::array<QColor, 4> &colors, QImage &dstImage, 0018 const QPointF &dstImageOffset) 0019 : m_colors(colors), m_dstImage(dstImage), 0020 m_dstImageOffset(dstImageOffset), 0021 m_dstImageRect(m_dstImage.rect()) 0022 { 0023 } 0024 0025 void operator() (const QPolygonF &srcPolygon, const QPolygonF &dstPolygon) { 0026 this->operator() (srcPolygon, dstPolygon, dstPolygon); 0027 } 0028 0029 void operator() (const QPolygonF &srcPolygon, const QPolygonF &dstPolygon, const QPolygonF &clipDstPolygon) { 0030 QRect boundRect = clipDstPolygon.boundingRect().toAlignedRect(); 0031 KisFourPointInterpolatorBackward interp(srcPolygon, dstPolygon); 0032 0033 for (int y = boundRect.top(); y <= boundRect.bottom(); y++) { 0034 interp.setY(y); 0035 for (int x = boundRect.left(); x <= boundRect.right(); x++) { 0036 0037 QPointF srcPoint(x, y); 0038 if (clipDstPolygon.containsPoint(srcPoint, Qt::OddEvenFill)) { 0039 0040 interp.setX(srcPoint.x()); 0041 QPointF dstPoint = interp.getValue(); 0042 0043 // about srcPoint/dstPoint hell please see a 0044 // comment in PaintDevicePolygonOp::operator() () 0045 0046 srcPoint -= m_dstImageOffset; 0047 0048 QPoint srcPointI = srcPoint.toPoint(); 0049 0050 if (!m_dstImageRect.contains(srcPointI)) continue; 0051 0052 // TODO: move vertical calculation into the upper loop 0053 const QColor c1 = lerp(m_colors[0], m_colors[1], qBound(0.0, dstPoint.x(), 1.0)); 0054 const QColor c2 = lerp(m_colors[2], m_colors[3], qBound(0.0, dstPoint.x(), 1.0)); 0055 0056 m_dstImage.setPixelColor(srcPointI, lerp(c1, c2, qBound(0.0, dstPoint.y(), 1.0))); 0057 } 0058 } 0059 } 0060 } 0061 0062 const std::array<QColor, 4> &m_colors; 0063 QImage &m_dstImage; 0064 QPointF m_dstImageOffset; 0065 QRect m_dstImageRect; 0066 }; 0067 0068 void saveValue(QDomElement *parent, const QString &tag, const GradientMeshNode &node) 0069 { 0070 QDomDocument doc = parent->ownerDocument(); 0071 QDomElement e = doc.createElement(tag); 0072 parent->appendChild(e); 0073 0074 e.setAttribute("type", "gradient-mesh-node"); 0075 KisDomUtils::saveValue(&e, "color", node.color); 0076 KisDomUtils::saveValue(&e, "node", node.node); 0077 KisDomUtils::saveValue(&e, "left-control", node.leftControl); 0078 KisDomUtils::saveValue(&e, "right-control", node.rightControl); 0079 KisDomUtils::saveValue(&e, "top-control", node.topControl); 0080 KisDomUtils::saveValue(&e, "bottom-control", node.bottomControl); 0081 0082 } 0083 0084 bool loadValue(const QDomElement &parent, GradientMeshNode *node) 0085 { 0086 if (!KisDomUtils::Private::checkType(parent, "gradient-mesh-node")) return false; 0087 0088 KisDomUtils::loadValue(parent, "node", &node->node); 0089 KisDomUtils::loadValue(parent, "left-control", &node->leftControl); 0090 KisDomUtils::loadValue(parent, "right-control", &node->rightControl); 0091 KisDomUtils::loadValue(parent, "top-control", &node->topControl); 0092 KisDomUtils::loadValue(parent, "bottom-control", &node->bottomControl); 0093 0094 return true; 0095 } 0096 0097 void saveValue(QDomElement *parent, const QString &tag, const KisBezierGradientMesh &mesh) 0098 { 0099 QDomDocument doc = parent->ownerDocument(); 0100 QDomElement e = doc.createElement(tag); 0101 parent->appendChild(e); 0102 0103 e.setAttribute("type", "gradient-mesh"); 0104 0105 KisDomUtils::saveValue(&e, "size", mesh.m_size); 0106 KisDomUtils::saveValue(&e, "srcRect", mesh.m_originalRect); 0107 KisDomUtils::saveValue(&e, "columns", mesh.m_columns); 0108 KisDomUtils::saveValue(&e, "rows", mesh.m_rows); 0109 KisDomUtils::saveValue(&e, "nodes", mesh.m_nodes); 0110 } 0111 0112 bool loadValue(const QDomElement &parent, const QString &tag, KisBezierGradientMesh *mesh) 0113 { 0114 QDomElement e; 0115 if (!KisDomUtils::findOnlyElement(parent, tag, &e)) return false; 0116 0117 if (!KisDomUtils::Private::checkType(e, "gradient-mesh")) return false; 0118 0119 mesh->m_columns.clear(); 0120 mesh->m_rows.clear(); 0121 mesh->m_nodes.clear(); 0122 0123 KisDomUtils::loadValue(e, "size", &mesh->m_size); 0124 KisDomUtils::loadValue(e, "srcRect", &mesh->m_originalRect); 0125 KisDomUtils::loadValue(e, "columns", &mesh->m_columns); 0126 KisDomUtils::loadValue(e, "rows", &mesh->m_rows); 0127 KisDomUtils::loadValue(e, "nodes", &mesh->m_nodes); 0128 0129 return true; 0130 } 0131 0132 } 0133 0134 KisBezierGradientMesh::PatchIndex KisBezierGradientMesh::hitTestPatch(const QPointF &pt, QPointF *localPointResult) const { 0135 auto result = endPatches(); 0136 0137 const QRectF unitRect(0, 0, 1, 1); 0138 0139 for (auto it = beginPatches(); it != endPatches(); ++it) { 0140 Patch patch = *it; 0141 0142 if (patch.dstBoundingRect().contains(pt)) { 0143 const QPointF localPos = KisBezierUtils::calculateLocalPosSVG2(patch.points, pt); 0144 0145 if (unitRect.contains(localPos)) { 0146 0147 if (localPointResult) { 0148 *localPointResult = localPos; 0149 } 0150 0151 result = it; 0152 break; 0153 } 0154 } 0155 } 0156 0157 return result.patchIndex(); 0158 } 0159 0160 void KisBezierGradientMesh::renderPatch(const KisBezierGradientMeshDetail::GradientMeshPatch &patch, 0161 const QPoint &dstQImageOffset, 0162 QImage *dstImage) 0163 { 0164 QVector<QPointF> originalPointsLocal; 0165 QVector<QPointF> transformedPointsLocal; 0166 QSize gridSize; 0167 0168 patch.sampleRegularGridSVG2(gridSize, originalPointsLocal, transformedPointsLocal, QPointF(8,8)); 0169 0170 const QRect dstBoundsI = patch.dstBoundingRect().toAlignedRect(); 0171 const QRect imageSize = QRect(dstQImageOffset, dstImage->size()); 0172 KIS_SAFE_ASSERT_RECOVER_NOOP(imageSize.contains(dstBoundsI)); 0173 0174 { 0175 QImageGradientOp polygonOp(patch.colors, *dstImage, dstQImageOffset); 0176 0177 0178 GridIterationTools::RegularGridIndexesOp indexesOp(gridSize); 0179 GridIterationTools::iterateThroughGrid 0180 <GridIterationTools::AlwaysCompletePolygonPolicy>(polygonOp, indexesOp, 0181 gridSize, 0182 originalPointsLocal, 0183 transformedPointsLocal); 0184 0185 } 0186 } 0187 0188 void KisBezierGradientMesh::renderMesh(const QPoint &dstQImageOffset, 0189 QImage *dstImage) const 0190 { 0191 for (auto it = beginPatches(); it != endPatches(); ++it) { 0192 renderPatch(*it, dstQImageOffset, dstImage); 0193 } 0194 }