PDF图像中心旋转输出异常:C++实现问题问询
图像绕中心旋转输出PDF失败问题解决
问题背景
项目基于C++实现图像输出到PDF,坐标系原点为左上角,需要实现图像绕自身中心旋转,但当前使用变换矩阵后,图像始终绕左上角旋转,无法达到预期效果。
现有代码片段
变换矩阵构建代码
MatrixTransform aTrans; aTrans.translate(-halfwidth, -halfheight); aTrans.rotate(0.7853981634); aTrans.translate(it->m_dLeft+halfwidth, it->m_dTop+halfheight); DrawImage(sImageName, 0, 0, imageWidth, imageHeight);
MatrixTransform类核心实现
class MatrixTransform { public: MatrixTransform(): m_matrix({0}){ m_matrix[0][0] = 1.0f; m_matrix[1][1] = 1.0f; m_matrix[2][2] = 1.0f; } void translate(float tx, float ty) { m_matrix[0][2] += tx; m_matrix[1][2] += ty; } void rotate(float angle) { float cosAngle = std::cos(angle); float sinAngle = std::sin(angle); float rotationMatrix[3][3] = { {cosAngle, -sinAngle, 0.0f}, {sinAngle, cosAngle, 0.0f}, {0.0f, 0.0f, 1.0f} }; multiplyMatrix(rotationMatrix); } public: float m_matrix[3][3]; void multiplyMatrix(float matrix[3][3]) { float resultMatrix[3][3] = {0}; for (int row = 0; row < 3; ++row) { for (int col = 0; col < 3; ++col) { for (int i = 0; i < 3; ++i) { resultMatrix[row][col] += m_matrix[row][i] * matrix[i][col]; } } } memcpy(m_matrix, resultMatrix, sizeof(m_matrix)); } };
DrawImage及相关页面变换代码
void DrawImage(...) { m_pPage->Save(); // 原变换参数调整 m_pPage->SetTransform( aTrans.m_matrix[0][0], -aTrans.m_matrix[0][1], -aTrans.m_matrix[1][0], aTrans.m_matrix[1][1], aTrans.m_matrix[0][2] + aTrans.m_matrix[1][0] * PageHeight, PageHeight - PageHeight * aTrans.m_matrix[1][1] - aTrans.m_matrix[1][2]); m_pPage->Draw(sImageName, dX, m_dPageHeight - dY - dH, dW, dH); m_pPage->Restore(); } void Page::SetTransform(double dM11, double dM12, double dM21, double dM22, double dX, double dY) { Matrix oInverse = m_pState->m_Matrix.Inverse(); Matrix oResult; oResult.m11 = dM11 * oInverse.m11 + dM12 * oInverse.m21; oResult.m12 = dM11 * oInverse.m12 + dM12 * oInverse.m22; oResult.m21 = dM21 * oInverse.m11 + dM22 * oInverse.m21; oResult.m22 = dM21 * oInverse.m12 + dM22 * oInverse.m22; oResult.x = dX * oInverse.m11 + dY * oInverse.m21 + oInverse.x; oResult.y = dX * oInverse.m12 + dY * oInverse.m22 + oInverse.y; Concat(oResult.m11, oResult.m12, oResult.m21, oResult.m22, oResult.x, oResult.y); } void Page::Draw(const char* sImageName, double dX, double dY, double dWidth, double dHeight) { Save(); Concat(dWidth, 0, 0, dHeight, dX, dY); m_pStream->WriteEscapeName(sImageName); m_pStream->WriteStr(" Do\012"); Restore(); }
关键问题分析
坐标系差异处理冲突
PDF原生坐标系原点在左下角,而项目使用左上角原点,原代码通过m_dPageHeight - dY - dH和SetTransform中的Y轴符号调整做转换,但这种转换与自定义MatrixTransform的变换逻辑叠加后,破坏了绕中心旋转的坐标计算。SetTransform函数的设计逻辑误解
SetTransform内部使用当前状态矩阵的逆矩阵,其作用是设置绝对变换(重置当前变换状态为目标矩阵),而非追加变换。但用户的MatrixTransform是按“平移中心→旋转→平移到目标位置”的追加顺序计算的,两者逻辑不匹配,导致变换顺序错乱。矩阵乘法顺序的隐含规则
矩阵乘法是右乘逻辑,即先定义的变换会后执行。用户的MatrixTransform中translate(-half,-half)→rotate→translate(pos+half, pos+half)的顺序是正确的绕中心旋转逻辑,但SetTransform的逆矩阵操作打乱了这个顺序的实际效果。
解决方案
方案1:直接使用Page::Concat追加变换(推荐)
绕过SetTransform的逆矩阵干扰,直接按顺序追加变换,同时兼容PDF坐标系:
// 计算图像在项目坐标系中的中心位置 double centerX = it->m_dLeft + imageWidth / 2.0; double centerY = it->m_dTop + imageHeight / 2.0; // 转换为PDF坐标系(Y轴翻转) double pdfCenterY = m_dPageHeight - centerY; m_pPage->Save(); // 步骤1:将原点平移到PDF坐标系中的图像中心 m_pPage->Concat(1, 0, 0, 1, centerX, pdfCenterY); // 步骤2:绕当前原点(图像中心)旋转45度 double angle = 0.7853981634; double cosA = cos(angle); double sinA = sin(angle); // PDF旋转矩阵与常规左上角坐标系的区别:sin符号调整 m_pPage->Concat(cosA, sinA, -sinA, cosA, 0, 0); // 步骤3:将原点平移回图像左上角(相对于中心) m_pPage->Concat(1, 0, 0, 1, -imageWidth/2.0, -imageHeight/2.0); // 绘制图像(此时图像左上角对应变换后的原点,实现绕中心旋转) m_pPage->Draw(sImageName, 0, 0, imageWidth, imageHeight); m_pPage->Restore();
方案2:修改MatrixTransform以适配PDF坐标系
如果必须使用自定义MatrixTransform,需先处理Y轴翻转,再执行绕中心旋转逻辑,最后直接用Concat追加变换:
MatrixTransform aTrans; // 先添加PDF坐标系的Y轴翻转变换 aTrans.translate(0, m_dPageHeight); aTrans.scale(1, -1); // 需给MatrixTransform添加scale方法,实现缩放矩阵乘法 // 执行绕中心旋转的常规步骤 aTrans.translate(it->m_dLeft + halfwidth, it->m_dTop + halfheight); aTrans.rotate(0.7853981634); aTrans.translate(-halfwidth, -halfheight); m_pPage->Save(); // 直接用Concat追加变换,跳过SetTransform的逆矩阵操作 m_pPage->Concat( aTrans.m_matrix[0][0], aTrans.m_matrix[0][1], aTrans.m_matrix[1][0], aTrans.m_matrix[1][1], aTrans.m_matrix[0][2], aTrans.m_matrix[1][2] ); m_pPage->Draw(sImageName, 0, 0, imageWidth, imageHeight); m_pPage->Restore();
原代码疑问解答
- DrawImage中
m_dPageHeight - dY - dH的作用:将项目左上角坐标系的图像右下角Y坐标,转换为PDF左下角坐标系的图像左下角Y坐标,完成Y轴翻转。 - SetTransform中逆矩阵的作用:该函数设计为设置绝对变换,通过逆矩阵抵消当前已有的变换,再叠加目标矩阵,最终将页面变换状态重置为目标状态。若需叠加多个变换(如平移+旋转),应使用Concat函数。
内容的提问来源于stack exchange,提问作者renxyz123
相关产品推荐
相关产品推荐

