31     init_effect_details();
 
   38   : yaw(new_yaw), pitch(new_pitch), roll(new_roll)
 
   39   , fov(new_fov), projection_mode(0), invert(0), interpolation(0)
 
   41     init_effect_details();
 
   44 void SphericalProjection::init_effect_details()
 
   49     info.
description = 
"Flatten and reproject 360° video with yaw, pitch, roll, and fov (sphere, hemisphere, fisheye modes)";
 
   55     std::shared_ptr<openshot::Frame> frame,
 
   58     auto img = frame->GetImage();
 
   59     if (img->format() != QImage::Format_ARGB32)
 
   60         *img = img->convertToFormat(QImage::Format_ARGB32);
 
   62     int W = img->width(), H = img->height();
 
   63     int bpl = img->bytesPerLine();
 
   64     uchar* src = img->bits();
 
   66     QImage output(W, H, QImage::Format_ARGB32);
 
   67     output.fill(Qt::black);
 
   68     uchar* dst = output.bits();
 
   69     int dst_bpl = output.bytesPerLine();
 
   72     double yaw_r   = 
yaw.
GetValue(frame_number)   * M_PI/180.0;
 
   74     double roll_r  = -
roll.
GetValue(frame_number) * M_PI/180.0 + M_PI;
 
   75     double fov_r   = 
fov.
GetValue(frame_number)   * M_PI/180.0;
 
   78     double sy = sin(yaw_r),  cy = cos(yaw_r);
 
   79     double sp = sin(pitch_r), cp = cos(pitch_r);
 
   80     double sr = sin(roll_r), cr = cos(roll_r);
 
   82     double r00 = cy*cr + sy*sp*sr, r01 = -cy*sr + sy*sp*cr, r02 = sy*cp;
 
   83     double r10 = cp*sr,            r11 = cp*cr,            r12 = -sp;
 
   84     double r20 = -sy*cr + cy*sp*sr, r21 = sy*sr + cy*sp*cr, r22 = cy*cp;
 
   87     double hx = tan(fov_r*0.5);
 
   88     double vy = hx * double(H)/W;
 
   90 #pragma omp parallel for schedule(static) 
   91     for (
int yy = 0; yy < H; yy++) {
 
   92         uchar* dst_row = dst + yy * dst_bpl;
 
   93         double ndc_y = (2.0*(yy + 0.5)/H - 1.0) * vy;
 
   95         for (
int xx = 0; xx < W; xx++) {
 
   97             double ndc_x = (2.0*(xx + 0.5)/W - 1.0) * hx;
 
   98             double vx = ndc_x, vy2 = -ndc_y, vz = -1.0;
 
   99             double inv = 1.0/sqrt(vx*vx + vy2*vy2 + vz*vz);
 
  100             vx *= inv; vy2 *= inv; vz *= inv;
 
  103             double dx = r00*vx + r01*vy2 + r02*vz;
 
  104             double dy = r10*vx + r11*vy2 + r12*vz;
 
  105             double dz = r20*vx + r21*vy2 + r22*vz;
 
  117                 double ax = 0.0, ay = 0.0, az = 
invert ? -1.0 : 1.0;
 
  118                 double cos_t = dx*ax + dy*ay + dz*az;
 
  119                 double theta = acos(cos_t);
 
  120                 double rpx = (theta / fov_r) * (W/2.0);
 
  121                 double phi = atan2(dy, dx);
 
  122                 uf = W*0.5 + rpx*cos(phi);
 
  123                 vf = H*0.5 + rpx*sin(phi);
 
  127                 double lon = atan2(dx, dz);
 
  128                 double lat = asin(dy);
 
  130                     lon = std::clamp(lon, -M_PI/2.0, M_PI/2.0);
 
  133                 vf = (lat + M_PI/2.0)/M_PI * H;
 
  136             uchar* d = dst_row + xx*4;
 
  140                 int x0 = std::clamp(
int(std::floor(uf)), 0, W-1);
 
  141                 int y0 = std::clamp(
int(std::floor(vf)), 0, H-1);
 
  142                 uchar* s = src + y0*bpl + x0*4;
 
  143                 d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; d[3] = s[3];
 
  147                 int x0 = std::clamp(
int(std::floor(uf)), 0, W-1);
 
  148                 int y0 = std::clamp(
int(std::floor(vf)), 0, H-1);
 
  149                 int x1 = std::clamp(x0 + 1, 0, W-1);
 
  150                 int y1 = std::clamp(y0 + 1, 0, H-1);
 
  151                 double dxr = uf - x0, dyr = vf - y0;
 
  152                 uchar* p00 = src + y0*bpl + x0*4;
 
  153                 uchar* p10 = src + y0*bpl + x1*4;
 
  154                 uchar* p01 = src + y1*bpl + x0*4;
 
  155                 uchar* p11 = src + y1*bpl + x1*4;
 
  156                 for (
int c = 0; c < 4; c++) {
 
  157                     double v0 = p00[c]*(1-dxr) + p10[c]*dxr;
 
  158                     double v1 = p01[c]*(1-dxr) + p11[c]*dxr;
 
  159                     d[c] = uchar(v0*(1-dyr) + v1*dyr + 0.5);
 
  195         throw InvalidJSON(
"Invalid JSON for SphericalProjection");
 
  206     if (!root[
"projection_mode"].isNull()) 
projection_mode = root[
"projection_mode"].asInt();
 
  207     if (!root[
"invert"].isNull())          
invert          = root[
"invert"].asInt();
 
  208     if (!root[
"interpolation"].isNull())   
interpolation   = root[
"interpolation"].asInt();
 
  219                                       false, requested_frame);
 
  224                                       false, requested_frame);
 
  229                                       false, requested_frame);
 
  234                                       false, requested_frame);
 
  240                                                 false, requested_frame);
 
  249                                        false, requested_frame);
 
  257                                               false, requested_frame);
 
  261     return root.toStyledString();