| 60 | } // namespace |
| 61 | |
| 62 | std::shared_ptr<openshot::Frame> |
| 63 | SphericalProjection::GetFrame(std::shared_ptr<openshot::Frame> frame, |
| 64 | int64_t frame_number) { |
| 65 | auto img = frame->GetImage(); |
| 66 | if (img->format() != QImage::Format_ARGB32) |
| 67 | *img = img->convertToFormat(QImage::Format_ARGB32); |
| 68 | |
| 69 | int W = img->width(), H = img->height(); |
| 70 | int bpl = img->bytesPerLine(); |
| 71 | uchar *src = img->bits(); |
| 72 | |
| 73 | QImage output(W, H, QImage::Format_ARGB32); |
| 74 | output.fill(Qt::black); |
| 75 | uchar *dst = output.bits(); |
| 76 | int dst_bpl = output.bytesPerLine(); |
| 77 | |
| 78 | // Keyframes / angles |
| 79 | const double DEG = M_PI / 180.0; |
| 80 | double yaw_r = -yaw.GetValue(frame_number) * DEG; // drag right -> look right |
| 81 | double pitch_r = pitch.GetValue(frame_number) * DEG; // drag up -> look up |
| 82 | double roll_r = -roll.GetValue(frame_number) * DEG; // positive slider -> clockwise on screen |
| 83 | double in_fov_r = in_fov.GetValue(frame_number) * DEG; |
| 84 | double out_fov_r= fov.GetValue(frame_number) * DEG; |
| 85 | |
| 86 | // Apply invert as a 180° yaw for equirect inputs (camera-centric; no mirroring) |
| 87 | if (input_model == INPUT_EQUIRECT && invert == INVERT_BACK) { |
| 88 | yaw_r += M_PI; |
| 89 | } |
| 90 | |
| 91 | // Rotation R = Ry(yaw) * Rx(pitch). (Roll applied in screen space.) |
| 92 | double sy = sin(yaw_r), cy = cos(yaw_r); |
| 93 | double sp = sin(pitch_r),cp = cos(pitch_r); |
| 94 | |
| 95 | double r00 = cy; |
| 96 | double r01 = sy * sp; |
| 97 | double r02 = sy * cp; |
| 98 | |
| 99 | double r10 = 0.0; |
| 100 | double r11 = cp; |
| 101 | double r12 = -sp; |
| 102 | |
| 103 | double r20 = -sy; |
| 104 | double r21 = cy * sp; |
| 105 | double r22 = cy * cp; |
| 106 | |
| 107 | // Keep roll clockwise on screen regardless of facing direction |
| 108 | double roll_sign = (r22 >= 0.0) ? 1.0 : -1.0; |
| 109 | |
| 110 | // Perspective scalars (rectilinear) |
| 111 | double hx = tan(out_fov_r * 0.5); |
| 112 | double vy = hx * double(H) / W; |
| 113 | |
| 114 | auto q = [](double a) { return std::llround(a * 1e6); }; |
| 115 | bool recompute = uv_map.empty() || W != cached_width || H != cached_height || |
| 116 | q(yaw_r) != q(cached_yaw) || |
| 117 | q(pitch_r) != q(cached_pitch) || |
| 118 | q(roll_r) != q(cached_roll) || |
| 119 | q(in_fov_r) != q(cached_in_fov) || |
nothing calls this directly
no test coverage detected