如何用QTransform将任意四边形转为指定尺寸矩形?代码调试求助
QTransform实现任意四边形转指定矩形的问题排查
问题背景
在Android平台可通过SkMatrix的setPolyToPoly方法实现任意四边形到指定矩形的图像变换,但QTransform没有原生类似方法,因此将SkMatrix的相关源码改写为Qt/C++版本,已考虑两者矩阵常量的位置差异,但应用生成的QTransform后屏幕显示结果异常,需排查代码或应用逻辑问题。
原实现代码
// SkMatrix Constants static constexpr int kMScaleX = 0; //!< horizontal scale factor static constexpr int kMSkewX = 1; //!< horizontal skew factor static constexpr int kMTransX = 2; //!< horizontal translation static constexpr int kMSkewY = 3; //!< vertical skew factor static constexpr int kMScaleY = 4; //!< vertical scale factor static constexpr int kMTransY = 5; //!< vertical translation static constexpr int kMPersp0 = 6; //!< input x perspective factor static constexpr int kMPersp1 = 7; //!< input y perspective factor static constexpr int kMPersp2 = 8; //!< perspective bias // Constants for Qt static constexpr int qtScaleX = 0; // 11 // scale static constexpr int qtSkewY = 1; // 12 // shear static constexpr int qtPerspY = 2; // 13 // влияет rotate вокгуг YAxias / = Persp1 static constexpr int qtSkewX = 3; // 21 // shear static constexpr int qtScaleY = 4; // 22 // scale static constexpr int qtPerspX = 5; // 23 // влияет rotate вокгуг XAxias / = Persp0 static constexpr int qtTransX = 6; // 31 // translate static constexpr int qtTransY = 7; // 32 // translate static constexpr int qtPerspZ = 8; // 33 // не удалось повлиять на значение / = Persp2 /* ScaleX SkewY PerspY SkewX ScaleY PerspX TransX TransY PerspZ */ void convertSkToQt(const QTransform& skSrc, QTransform& qtDst) { QList<qreal> skLine(9, 0); skLine[kMScaleX ] = skSrc.m11(); skLine[kMSkewX ] = skSrc.m21(); skLine[kMTransX ] = skSrc.m31(); skLine[kMSkewY ] = skSrc.m12(); skLine[kMScaleY ] = skSrc.m22(); skLine[kMTransY ] = skSrc.m32(); skLine[kMPersp0 ] = skSrc.m23(); skLine[kMPersp1 ] = skSrc.m13(); skLine[kMPersp2 ] = skSrc.m33(); /* ScaleX SkewY PerspY SkewX ScaleY PerspX TransX TransY PerspZ */ qtDst.setMatrix( skLine[kMScaleX ], skLine[kMSkewY ], skLine[kMPersp1 ] , skLine[kMSkewX ], skLine[kMScaleY], skLine[kMPersp0 ] , skLine[kMTransX ], skLine[kMTransY], skLine[kMPersp2 ] ); } bool poly4Proc(const QList<QPointF>& srcPt, QTransform& dst) { qreal a1, a2; qreal x0, y0, x1, y1, x2, y2; x0 = srcPt[2].x() - srcPt[0].x(); y0 = srcPt[2].y() - srcPt[0].y(); x1 = srcPt[2].x() - srcPt[1].x(); y1 = srcPt[2].y() - srcPt[1].y(); x2 = srcPt[2].x() - srcPt[3].x(); y2 = srcPt[2].y() - srcPt[3].y(); /* check if abs(x2) > abs(y2) */ if ( x2 > 0 ? y2 > 0 ? x2 > y2 : x2 > -y2 : y2 > 0 ? -x2 > y2 : x2 < y2) { float denom = sk_ieee_float_divide(x1 * y2, x2) - y1; if (checkForZero(denom)) { return false; } a1 = (((x0 - x1) * y2 / x2) - y0 + y1) / denom; } else { float denom = x1 - sk_ieee_float_divide(y1 * x2, y2); if (checkForZero(denom)) { return false; } a1 = (x0 - x1 - sk_ieee_float_divide((y0 - y1) * x2, y2)) / denom; } /* check if abs(x1) > abs(y1) */ if ( x1 > 0 ? y1 > 0 ? x1 > y1 : x1 > -y1 : y1 > 0 ? -x1 > y1 : x1 < y1) { float denom = y2 - sk_ieee_float_divide(x2 * y1, x1); if (checkForZero(denom)) { return false; } a2 = (y0 - y2 - sk_ieee_float_divide((x0 - x2) * y1, x1)) / denom; } else { float denom = sk_ieee_float_divide(y2 * x1, y1) - x2; if (checkForZero(denom)) { return false; } a2 = (sk_ieee_float_divide((y0 - y2) * x1, y1) - x0 + x2) / denom; } QList<qreal> lineMatrix(9, 0); lineMatrix[kMScaleX] = a2 * srcPt[3].x() + srcPt[3].x() - srcPt[0].x(); lineMatrix[kMSkewY] = a2 * srcPt[3].y() + srcPt[3].y() - srcPt[0].y(); lineMatrix[kMPersp0] = a2; lineMatrix[kMSkewX] = a1 * srcPt[1].x() + srcPt[1].x() - srcPt[0].x(); lineMatrix[kMScaleY] = a1 * srcPt[1].y() + srcPt[1].y() - srcPt[0].y(); lineMatrix[kMPersp1] = a1; lineMatrix[kMTransX] = srcPt[0].x(); lineMatrix[kMTransY] = srcPt[0].y(); lineMatrix[kMPersp2] = 1; dst.setMatrix( lineMatrix[0], lineMatrix[1], lineMatrix[2] , lineMatrix[3], lineMatrix[4], lineMatrix[5] , lineMatrix[6], lineMatrix[7], lineMatrix[8] ); return true; } bool setPolyToPoly(QTransform& matrix, const QList<QPointF>& src, const QList<QPointF>& dst) { QTransform tempMap, result; if (!poly4Proc(src, tempMap)) return false; bool done = false; result = tempMap.inverted(&done); if (!done) { qDebug().noquote() << "Can't invert matrix:" << tempMap; return false; } if (!poly4Proc(dst, tempMap)) return false; QTransform matrixRes0 = tempMap * result; convertSkToQt(matrixRes0, matrix); return true; } void test() { painter->save(); QTransform polyToPolymatrix; int deform = 20; QList<QPointF> src(4, QPointF(0, 0)); src[0] = QPointF(0, 0); src[1] = QPointF(200, 0); src[2] = QPointF(200, 200); src[3] = QPointF(0, 200); QList<QPointF> dst(4, QPointF(0, 0)); dst[0] = QPointF(0, 0); dst[1] = QPointF(200-deform, deform); dst[2] = QPointF(200-deform, 200-deform); dst[3] = QPointF(0, 200); setPolyToPoly(polyToPolymatrix, src, dst); painter->setTransform(polyToPolymatrix); painter->drawPixmap(QPointF(0, 0), m_pixmap, QRectF(0, 0, 200, 200)); painter->restore(); }
排查关键点与修复方案
1. 矩阵乘法顺序错误
SkMatrix与QTransform的乘法顺序逻辑相反:
- SkMatrix中
A * B表示先应用变换B,再应用变换A - QTransform中
A * B表示先应用变换A,再应用变换B
原代码中矩阵乘法顺序不符合Qt逻辑,需修改为:
QTransform matrixRes0 = result * tempMap;
2. 矩阵元素对应冗余
原convertSkToQt函数中通过中间列表映射元素存在冗余,直接基于QTransform的mXX方法对应SkMatrix元素即可,简化后:
void convertSkToQt(const QTransform& skSrc, QTransform& qtDst) { qtDst.setMatrix( skSrc.m11(), skSrc.m12(), skSrc.m13(), // m11=ScaleX, m12=SkewY, m13=PerspY skSrc.m21(), skSrc.m22(), skSrc.m23(), // m21=SkewX, m22=ScaleY, m23=PerspX skSrc.m31(), skSrc.m32(), skSrc.m33() // m31=TransX, m32=TransY, m33=PerspZ ); }
3. 浮点类型不统一
代码中混合qreal和float导致精度损失,统一替换为qreal,并使用Qt原生方法处理零值判断:
bool checkForZero(qreal value) { return qFuzzyIsNull(value); } qreal sk_ieee_float_divide(qreal numerator, qreal denominator) { return numerator / denominator; }
4. 变换方向逻辑错误
Qt的setTransform是对画布进行变换,相当于将绘制内容从源坐标映射到目标坐标。若显示异常,可尝试交换setPolyToPoly的src和dst传入参数:
setPolyToPoly(polyToPolymatrix, dst, src);
5. 透视参数验证
QTransform的透视参数(m13、m23)对绘制效果影响较大,可通过打印矩阵元素值,验证poly4Proc中计算的a1、a2是否符合预期。
内容的提问来源于stack exchange,提问作者pier_nasos
相关产品推荐
相关产品推荐

