6773f9b29e
三段式对比直击 Day4 slides「主线程耗时=冻结」痛点:主线程渲染冻结 / 后台单线程不卡但慢 / 后台多线程不卡且快。 - QThreadPool + QRunnable 分水平条带并行,排队信号槽回传,epoch 版本号防过期覆盖 - wheelEvent 缩放(鼠标为锚)+ mouseEvent 拖拽平移 + paintEvent 画 QImage - 子目录自带独立 CMakeLists.txt,可单独构建/共享(双模式约定同 lineedit_eye_toggle) - 配套 README(概念/走读/双构建/5 踩坑点/练习)+ 设计规格 - 已验证:独立工程 offscreen 运行 exit=0;主工程构建 exit=0(w64devkit g++16 + mingw73 Qt5.14.2) 注:主工程 examples/CMakeLists.txt 的接入(mandelbrot + lineedit_eye_toggle + qdialog + themes 原子接入)与顶层 CMakeLists 本地调试改动未一并提交,待整体确认后单独提交。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
76 lines
3.3 KiB
C++
76 lines
3.3 KiB
C++
// ============================================================================
|
||
// mandelbrot_strip_task —— Day4 Mandelbrot 渲染器的「分块计算 + 调色板」单元
|
||
// 职责:
|
||
// * MandelViewport : 视口参数(复平面中心 / 像素跨度 / 尺寸 / 最大迭代)
|
||
// * MandelPalette : 迭代次数→RGB 的预计算调色板(提升趣味,与知识点无关)
|
||
// * renderStrip() : 纯计算函数,渲染视口内 [y0,y1) 行像素。多线程 task 与
|
||
// 「主线程卡死」演示路径共用它——保证两路径算的是同一份分形,
|
||
// 差异只在「是否阻塞事件循环 / 是否并行」。
|
||
// * MandelbrotStripTask : 一条水平带的 QRunnable 任务。多继承 QObject+QRunnable
|
||
// 以获得信号能力(QRunnable 本身无信号——这是初学者高频困惑,
|
||
// 见 README 踩坑点 2)。
|
||
// 设计要点:task 持 palette/viewport 的【值副本】,自包含、不引用主线程对象,
|
||
// 避免主对象先析构导致的悬垂访问(见 README 踩坑点 4)。
|
||
// ============================================================================
|
||
#ifndef MANDELBROT_STRIP_TASK_H
|
||
#define MANDELBROT_STRIP_TASK_H
|
||
|
||
#include <QImage>
|
||
#include <QObject>
|
||
#include <QRunnable>
|
||
#include <QRgb>
|
||
#include <QVector>
|
||
|
||
// 视口参数:复平面映射。scale = 每像素对应的复平面跨度。
|
||
struct MandelViewport {
|
||
double centerX = -0.5; // 经典 Mandelbrot 主分形居中
|
||
double centerY = 0.0;
|
||
double scale = 3.0 / 600.0;
|
||
int width = 0; // show 后由 widget 尺寸填入
|
||
int height = 0;
|
||
int maxIter = 256;
|
||
};
|
||
|
||
// 调色板:迭代次数 → RGB。集合内部(迭代未逃逸)映射为黑。
|
||
class MandelPalette {
|
||
public:
|
||
explicit MandelPalette(int maxIter = 256);
|
||
QRgb color(int i) const { return table_.at(i); }
|
||
private:
|
||
QVector<QRgb> table_;
|
||
};
|
||
|
||
// 纯计算:渲染视口内 [y0, y1) 行像素,返回 (width × (y1-y0)) 的 RGB32 QImage。
|
||
QImage renderStrip(const MandelViewport& vp, const MandelPalette& pal, int y0, int y1);
|
||
|
||
// 一条水平带的多线程任务。多继承 QObject + QRunnable 以拥有信号。
|
||
// setAutoDelete(true)(默认):run() 结束后由线程池回收;不设 QObject parent,
|
||
// 避免与 QThreadPool 的自动删除发生双重所有权——见 README 踩坑点 2。
|
||
class MandelbrotStripTask : public QObject, public QRunnable {
|
||
Q_OBJECT
|
||
public:
|
||
MandelbrotStripTask(quint64 epoch, int stripIndex,
|
||
const MandelViewport& vp, const MandelPalette& pal,
|
||
int y0, int y1)
|
||
: QObject(nullptr), epoch_(epoch), stripIndex_(stripIndex),
|
||
vp_(vp), pal_(pal), y0_(y0), y1_(y1) { setAutoDelete(true); }
|
||
|
||
void run() override {
|
||
// 算完一条带 → 通过信号把结果「排队」送回主线程(Qt 自动跨线程调度)。
|
||
emit stripReady(epoch_, stripIndex_, renderStrip(vp_, pal_, y0_, y1_));
|
||
}
|
||
|
||
signals:
|
||
void stripReady(quint64 epoch, int stripIndex, QImage strip);
|
||
|
||
private:
|
||
quint64 epoch_;
|
||
int stripIndex_;
|
||
MandelViewport vp_;
|
||
MandelPalette pal_; // 值副本:任务自包含
|
||
int y0_;
|
||
int y1_;
|
||
};
|
||
|
||
#endif // MANDELBROT_STRIP_TASK_H
|