Ai
1 Star 0 Fork 0

mcpsde/cppnode-imageflip

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
imageflip.cpp 14.22 KB
一键复制 编辑 原始数据 按行查看 历史
mcpsde 提交于 2025-04-09 14:45 +08:00 . 修复了3d转的bug
#include <napi.h>
#include <Magick++.h>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
using namespace Magick;
Napi::Value FlipHorizontal(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
// 参数校验(保持不变)
if (info.Length() < 2) {
Napi::TypeError::New(env, "需要至少两个参数:输入路径和输出路径").ThrowAsJavaScriptException();
return env.Null();
}
string inputPath = info[0].As<Napi::String>();
string outputPath = info[1].As<Napi::String>();
float x = 0.5f;
if (info.Length() >= 3 && !info[2].IsUndefined()) {
x = info[2].As<Napi::Number>().FloatValue();
}
if (fabs(x) > 1.0f) {
Napi::Error::New(env, "参数x必须在[-1, 1]范围内").ThrowAsJavaScriptException();
return env.Null();
}
bool isNegative = x < 0;
x = fabs(x);
try {
InitializeMagick(nullptr);
vector<Image> frames;
readImages(&frames, inputPath);
// 保存原始属性(保持不变)
int iterations = frames.empty() ? 0 : frames.front().attribute("loop").empty() ? 0 : stoi(frames.front().attribute("loop"));
vector<unsigned int> delays;
vector<int> disposes;
for (const auto &frame : frames) {
delays.push_back(frame.animationDelay());
disposes.push_back(frame.attribute("dispose").empty() ? 0 : stoi(frame.attribute("dispose")));
}
if (frames.size() > 1) {
coalesceImages(&frames, frames.begin(), frames.end());
}
for (auto &frame : frames) {
if (isNegative) {
frame.flop();
}
size_t width = frame.columns();
size_t height = frame.rows();
size_t flipWidth = static_cast<size_t>(x * width);
// 计算需要扩展的画布宽度(当x>0.5时)
size_t newWidth = width;
if (x > 0.5f) {
newWidth = width + (flipWidth - (width - flipWidth));
}
// 创建新画布
Image newFrame(Geometry(newWidth, height), Color("transparent"));
newFrame.composite(frame, 0, 0, OverCompositeOp);
// 裁剪并翻转左侧部分
Image flipped = newFrame;
flipped.crop(Geometry(flipWidth, height, 0, 0));
flipped.flop();
// 清除目标区域(关键修改)
newFrame.fillColor(Color("transparent"));
newFrame.draw(DrawableRectangle(flipWidth, 0, newWidth, height));
// 覆盖翻转后的图像
newFrame.composite(flipped, flipWidth, 0, OverCompositeOp);
frame = newFrame;
if (isNegative) {
frame.flop();
}
}
// 恢复动画属性(保持不变)
for (size_t i = 0; i < frames.size(); ++i) {
frames[i].animationDelay(delays[i]);
frames[i].attribute("dispose", to_string(disposes[i]));
}
if (!frames.empty()) {
frames.front().attribute("loop", to_string(iterations));
}
// 输出(保持不变)
if (frames.size() > 1) {
optimizeImageLayers(&frames, frames.begin(), frames.end());
writeImages(frames.begin(), frames.end(), outputPath);
} else {
frames[0].write(outputPath);
}
} catch (exception &err) {
Napi::Error::New(env, "最外层未知错误").ThrowAsJavaScriptException();
Napi::Error::New(env, err.what()).ThrowAsJavaScriptException();
}
return env.Null();
}
Napi::Value FlipVertical(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
// 参数校验
if (info.Length() < 2) {
Napi::TypeError::New(env, "需要至少两个参数:输入路径和输出路径").ThrowAsJavaScriptException();
return env.Null();
}
string inputPath = info[0].As<Napi::String>();
string outputPath = info[1].As<Napi::String>();
float y = 0.5f;
if (info.Length() >= 3 && !info[2].IsUndefined()) {
y = info[2].As<Napi::Number>().FloatValue();
}
bool isNegative = y < 0;
y = fabs(y);
if (y > 1.0f) {
Napi::Error::New(env, "参数y必须在[-1, 1]范围内").ThrowAsJavaScriptException();
return env.Null();
}
try {
InitializeMagick(nullptr);
vector<Image> frames;
readImages(&frames, inputPath);
// 保存原始属性
int iterations = frames.empty() ? 0 : frames.front().attribute("loop").empty() ? 0 : stoi(frames.front().attribute("loop"));
vector<unsigned int> delays;
vector<int> disposes;
for (const auto &frame : frames) {
delays.push_back(frame.animationDelay());
disposes.push_back(frame.attribute("dispose").empty() ? 0 : stoi(frame.attribute("dispose")));
}
if (frames.size() > 1) {
coalesceImages(&frames, frames.begin(), frames.end());
}
for (auto &frame : frames) {
if (isNegative) {
frame.flip();
}
size_t width = frame.columns();
size_t height = frame.rows();
size_t flipHeight = static_cast<size_t>(y * height);
// 计算需要扩展的画布高度
size_t newHeight = height;
if (y > 0.5f) {
newHeight = height + (flipHeight - (height - flipHeight));
}
// 创建新画布
Image newFrame(Geometry(width, newHeight), Color("transparent"));
newFrame.composite(frame, 0, 0, OverCompositeOp);
// 裁剪并翻转顶部部分
Image flipped = newFrame;
flipped.crop(Geometry(width, flipHeight, 0, 0));
flipped.flip();
// 清除目标区域
newFrame.fillColor(Color("transparent"));
newFrame.draw(DrawableRectangle(0, flipHeight, width, newHeight));
// 覆盖翻转后的图像
newFrame.composite(flipped, 0, flipHeight, OverCompositeOp);
frame = newFrame;
if (isNegative) {
frame.flip();
}
}
// 恢复动画属性
for (size_t i = 0; i < frames.size(); ++i) {
frames[i].animationDelay(delays[i]);
frames[i].attribute("dispose", to_string(disposes[i]));
}
if (!frames.empty()) {
frames.front().attribute("loop", to_string(iterations));
}
// 输出
if (frames.size() > 1) {
optimizeImageLayers(&frames, frames.begin(), frames.end());
writeImages(frames.begin(), frames.end(), outputPath);
} else {
frames[0].write(outputPath);
}
} catch (exception &err) {
Napi::Error::New(env, err.what()).ThrowAsJavaScriptException();
}
return env.Null();
}
Napi::Value Create3DRotationGIF(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
// 参数校验
if (info.Length() < 2) {
Napi::TypeError::New(env, "需要至少两个参数:输入路径和输出路径").ThrowAsJavaScriptException();
return env.Null();
}
string inputPath = info[0].As<Napi::String>();
string outputPath = info[1].As<Napi::String>();
try {
InitializeMagick(nullptr);
// ========== 内部参数配置 ==========
const int frameCount = 36; // 动画总帧数
const int cubeSize = 300; // 立方体边长(像素)
const int duration = 50; // 每帧持续时间(毫秒)
const double perspective = 800.0; // 透视强度(值越小透视效果越强)
const double rotationSpeed = 2.0; // 旋转速度系数
const double ambientLight = 0.3; // 环境光强度(0-1)
const double diffuseLight = 0.7; // 漫反射强度(0-1)
const int outputSize = 500; // 输出画布大小
// ========== 3D变换相关函数 ==========
auto project3D = [perspective](double x, double y, double z) {
double factor = perspective / (perspective + z);
return std::make_pair(x * factor, y * factor);
};
// ========== 加载源图像 ==========
vector<Image> frames;
readImages(&frames, inputPath);
Image sourceImage = frames.empty() ? Image(Geometry(cubeSize, cubeSize), Color("white")) : frames[0];
sourceImage.resize(Geometry(cubeSize, cubeSize));
// 创建6个面的图像(带简单光照效果)
auto createFace = [&](double lightFactor) {
Image face = sourceImage;
// 应用光照效果
face.modulate(100, 100, 100 + lightFactor * 30);
return face;
};
// 立方体6个面(前,后,左,右,上,下)
vector<Image> cubeFaces = {
createFace(diffuseLight), // 前(正光)
createFace(ambientLight), // 后(背光)
createFace(diffuseLight*0.7), // 左(侧光)
createFace(diffuseLight*0.7), // 右(侧光)
createFace(diffuseLight*0.5), // 上(弱光)
createFace(diffuseLight*0.5) // 下(弱光)
};
// 立方体顶点(3D坐标)
vector<vector<double>> cubeVertices = {
{-1, -1, -1}, {1, -1, -1}, {1, 1, -1}, {-1, 1, -1}, // 前
{-1, -1, 1}, {1, -1, 1}, {1, 1, 1}, {-1, 1, 1} // 后
};
// 立方体面(顶点索引)
vector<vector<int>> cubeFacesIndices = {
{0, 1, 2, 3}, // 前
{4, 5, 6, 7}, // 后
{0, 3, 7, 4}, // 左
{1, 2, 6, 5}, // 右
{3, 2, 6, 7}, // 上
{0, 1, 5, 4} // 下
};
// 清空frames,准备创建动画帧
frames.clear();
// ========== 创建动画帧 ==========
for (int i = 0; i < frameCount; ++i) {
double angle = (360.0 * i) / frameCount * (M_PI / 180.0) * rotationSpeed;
// 创建透明背景的画布
Image frame(Geometry(outputSize, outputSize), Color("transparent"));
frame.backgroundColor(Color("transparent"));
// 旋转矩阵
double cosA = cos(angle);
double sinA = sin(angle);
// 旋转立方体顶点
vector<vector<double>> rotatedVertices;
for (const auto& v : cubeVertices) {
// 绕Y轴旋转
double x = v[0] * cosA - v[2] * sinA;
double z = v[0] * sinA + v[2] * cosA;
rotatedVertices.push_back({x, v[1], z});
}
// 计算每个面的中心深度(用于排序)
vector<pair<double, int>> faceDepths;
for (size_t f = 0; f < cubeFacesIndices.size(); ++f) {
double avgZ = 0;
for (int vi : cubeFacesIndices[f]) {
avgZ += rotatedVertices[vi][2];
}
avgZ /= cubeFacesIndices[f].size();
faceDepths.emplace_back(avgZ, f);
}
// 按深度排序(从远到近绘制)
sort(faceDepths.begin(), faceDepths.end(),
[](const pair<double, int>& a, const pair<double, int>& b) {
return a.first > b.first;
});
// 绘制每个面
for (const auto& [depth, faceIdx] : faceDepths) {
vector<pair<double, double>> facePoints;
for (int vi : cubeFacesIndices[faceIdx]) {
auto [x, y] = project3D(
rotatedVertices[vi][0] * cubeSize/2,
rotatedVertices[vi][1] * cubeSize/2,
rotatedVertices[vi][2] * cubeSize/2
);
facePoints.emplace_back(x + outputSize/2, y + outputSize/2);
}
// 只绘制朝前的面(简单的背面剔除)
if (depth < 0) continue;
// 创建变形后的面图像
Image face = cubeFaces[faceIdx];
face.virtualPixelMethod(Magick::TransparentVirtualPixelMethod);
// 使用正确的Affine变形参数
std::vector<double> affineArgs = {
facePoints[0].first, facePoints[0].second, 0, 0,
facePoints[1].first, facePoints[1].second, cubeSize, 0,
facePoints[2].first, facePoints[2].second, cubeSize, cubeSize,
facePoints[3].first, facePoints[3].second, 0, cubeSize
};
// 应用透视变形
face.distort(Magick::AffineDistortion, affineArgs.size(), affineArgs.data(), true);
// 合成到帧中
frame.composite(face, 0, 0, Magick::OverCompositeOp);
}
// 设置动画属性
frame.animationDelay(duration / 10); // 转换为1/100秒单位
frame.attribute("dispose", "1"); // 背景处理方式
frames.push_back(frame);
}
// 设置循环属性
if (!frames.empty()) {
frames.front().attribute("loop", "0"); // 无限循环
}
// 输出GIF
if (frames.size() > 1) {
optimizeImageLayers(&frames, frames.begin(), frames.end());
writeImages(frames.begin(), frames.end(), outputPath);
} else {
frames[0].write(outputPath);
}
} catch (exception &err) {
Napi::Error::New(env, "创建3D旋转GIF时出错").ThrowAsJavaScriptException();
Napi::Error::New(env, err.what()).ThrowAsJavaScriptException();
}
return env.Null();
}
Napi::Object Init(Napi::Env env, Napi::Object exports)
{
exports.Set(Napi::String::New(env, "flipHorizontal"),
Napi::Function::New(env, FlipHorizontal));
exports.Set(Napi::String::New(env, "flipVertical"),
Napi::Function::New(env, FlipVertical));
exports.Set(Napi::String::New(env, "create3DRotationGIF"),
Napi::Function::New(env, Create3DRotationGIF));
return exports;
}
NODE_API_MODULE(imageflip, Init)
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C++
1
https://gitee.com/mcpsde/cppnode-imageflip.git
git@gitee.com:mcpsde/cppnode-imageflip.git
mcpsde
cppnode-imageflip
cppnode-imageflip
master

搜索帮助