Esempio n. 1
0
//
/// Maps the points of the printer DC to the screen DC. Sets the screen window
/// extent equal to the maximum logical pointer of the printer DC.
///
/// It is assumed that the viewport extents of the screen DC
/// are already set and represent the full previewed page. 
/// The window extents are set to the page size in logical units
/// as defined by the printer DC.
//
void
TPrintPreviewDC::ReScale()
{
  // Get the extents of the screen viewport in device units (pixels).
  // This should represent the whole previewed page on the screen.

  TSize ve; ::GetViewportExtEx(GetHDC(), &ve);

  // Calculate the size of the previewed page in logical units.

  TSize page = GetPageSizeInPixels(PrnDC);
  TSize pve = PrnDC.GetViewportExt();
  TSize pwe = PrnDC.GetWindowExt();
  TSize we(MulDiv(page.cx, pwe.cx, pve.cx), MulDiv(page.cy, pwe.cy, pve.cy));

  // Set the mapping mode and scale.

  ::SetMapMode(GetHDC(), MM_ANISOTROPIC);
  ::SetWindowExtEx(GetHDC(), we.cx, we.cy, 0);
  ::SetViewportExtEx(GetHDC(), ve.cx, ve.cy, 0);

  // Set the origin for logical units.

  ReOrg();
}
Esempio n. 2
0
    static void mouseWheel(QWindow* window, QObject* item, Qt::MouseButtons buttons,
                                Qt::KeyboardModifiers stateKey,
                                QPointF _pos, int xDelta, int yDelta, int delay = -1)
    {
        QTEST_ASSERT(window);
        QTEST_ASSERT(item);
        if (delay == -1 || delay < QTest::defaultMouseDelay())
            delay = QTest::defaultMouseDelay();
        if (delay > 0)
            QTest::qWait(delay);

        QPoint pos;
        QQuickItem *sgitem = qobject_cast<QQuickItem *>(item);
        if (sgitem)
            pos = sgitem->mapToScene(_pos).toPoint();

        QTEST_ASSERT(buttons == Qt::NoButton || buttons & Qt::MouseButtonMask);
        QTEST_ASSERT(stateKey == 0 || stateKey & Qt::KeyboardModifierMask);

        stateKey &= static_cast<unsigned int>(Qt::KeyboardModifierMask);
        QWheelEvent we(pos, window->mapToGlobal(pos), QPoint(0, 0), QPoint(xDelta, yDelta), 0, Qt::Vertical, buttons, stateKey);

        QSpontaneKeyEvent::setSpontaneous(&we); // hmmmm
        if (!qApp->notify(window, &we))
            QTest::qWarn("Wheel event not accepted by receiving window");
    }
Esempio n. 3
0
int CWindowIterator::Callback_Iterator(HWND hWnd)
{
    if (m_hWnd && GetParent(hWnd) != m_hWnd)
        return 0;
    CWindowEntry &we(mWindows.GetAt(mWindows.Add(hWnd)));
    we.UpdateDesc(mProcessName);
    return mCalllback ? mCalllback(we) : 0;
}
Esempio n. 4
0
	void testEncodeQP_RFC2047()
	{
		// When Quoted-Printable is used, it should be RFC-2047 QP encoding
		vmime::wordEncoder we(
			"buffer\xc3\xa0 foo_bar",
			vmime::charset("utf-8"),
			vmime::wordEncoder::ENCODING_AUTO);

		VASSERT_EQ("1", "buffer=C3=A0_foo=5Fbar", we.getNextChunk(100));
	}
Esempio n. 5
0
	void testGetNextChunk_integral()
	{
		// An integral number of characters should be encoded
		vmime::wordEncoder we(
			"buffer\xc3\xa0plop",
			vmime::charset("utf-8"),
			vmime::wordEncoder::ENCODING_AUTO);

		VASSERT_EQ("1", "buffer=C3=A0", we.getNextChunk(7));
		VASSERT_EQ("2", "plop", we.getNextChunk(10));
	}
Esempio n. 6
0
/*************************************************************************
	Change the currently open MenuItem PopupMenu
*************************************************************************/
void MenuBase::changePopupMenuItem(MenuItem* item)
{
	if (!d_allowMultiplePopups&&d_popupItem==item)
		return;

	if (!d_allowMultiplePopups&&d_popupItem!=0)
	{
		d_popupItem->closePopupMenu(false);
		WindowEventArgs we(d_popupItem->getPopupMenu());
		d_popupItem = 0;
		onPopupClosed(we);
	}

	if (item)
	{
		d_popupItem = item;
		d_popupItem->openPopupMenu(false);
		WindowEventArgs we(d_popupItem->getPopupMenu());
		onPopupOpened(we);
	}

}
Esempio n. 7
0
void QQuickMouseArea::wheelEvent(QWheelEvent *event)
{
    Q_D(QQuickMouseArea);
    if (!d->enabled || (!isScrollGestureEnabled() && event->source() != Qt::MouseEventNotSynthesized)) {
        QQuickItem::wheelEvent(event);
        return;
    }

    QQuickWheelEvent we(event->posF().x(), event->posF().y(), event->angleDelta(),
                        event->pixelDelta(), event->buttons(), event->modifiers());
    we.setAccepted(d->isWheelConnected());
    emit wheel(&we);
    if (!we.isAccepted())
        QQuickItem::wheelEvent(event);
}
Esempio n. 8
0
	VMIME_TEST_LIST_END


	void testGetNextChunk()
	{
		// An integral number of characters should be encoded
		vmime::wordEncoder we(
			"bufferfoobarbaz",
			vmime::charset("utf-8"),
			vmime::wordEncoder::ENCODING_AUTO);

		VASSERT_EQ("1", "buffer", we.getNextChunk(6));
		VASSERT_EQ("2", "foo", we.getNextChunk(3));
		VASSERT_EQ("3", "barbaz", we.getNextChunk(10));
	}
Esempio n. 9
0
void QQuickMouseArea::wheelEvent(QWheelEvent *event)
{
    Q_D(QQuickMouseArea);
    if (!d->enabled) {
        QQuickItem::wheelEvent(event);
        return;
    }

    QQuickWheelEvent we(event->posF().x(), event->posF().y(), event->angleDelta(),
                        event->pixelDelta(), event->buttons(), event->modifiers());
    we.setAccepted(d->isWheelConnected());
    emit wheel(&we);
    if (!we.isAccepted())
        QQuickItem::wheelEvent(event);
}
Esempio n. 10
0
void MouseController::handleWheel(WheelEvent & e)
{//滚动是忽略capture
    View * target = e.target();
    if (target == root_)
        target = getTargetByPos(e.getLoc(), true);
    if (!target)
        return;

    WheelEvent we(e.type(), target, e.dx(), e.dy(),
        target->mapToLocal(e.getLoc()), e.getLoc(),
        e.getFlags());
    EventDispatcher::Push(we);

    //暂时先这么处理 因为滚动的时候可能离开控件所以 需要改变鼠标样式之类
    MouseEvent me(kET_MOUSE_MOVE, kMB_NONE, root_, root_->mapToLocal(last_mouse_point_), last_mouse_point_, 0);
    handleEvent(me);
    //if (!capture_)
    //{//如果没有capture 可能滚动到新的view里 需要shiftOver
    //    shiftIfNecessary();
    //}
}
Esempio n. 11
0
/*************************************************************************
    Handler for mouse button release events
*************************************************************************/
void MenuItem::onMouseButtonUp(MouseEventArgs& e)
{
    // default processing
    ItemEntry::onMouseButtonUp(e);

    if (e.button == LeftButton)
    {
        releaseInput();

        // was the button released over this window?
        // (use mouse position, as e.position in args has been unprojected)
        if (!d_popupWasClosed &&
                getGUIContext().getRootWindow()->getTargetChildAtPosition(
                    getGUIContext().getMouseCursor().getPosition()) == this)
        {
            WindowEventArgs we(this);
            onClicked(we);
        }

        // event was handled by us.
        ++e.handled;
    }

}
Esempio n. 12
0
      // this is the real one
   void AtmosphericDrag::doCompute(UTCTime utc, EarthBody& rb, Spacecraft& sc)
   {
      // To consist with STK
      double omega_e = 7.292115E-05;  // IERS 1996 conventions
      //double omega_e = rb.getSpinRate(utc);

      Vector<double> r = sc.R();   // satellite position in m
      Vector<double> v = sc.V();   // satellite velocity in m/s

      const double cd = sc.getDragCoeff();
      const double area = sc.getDragArea();
      const double mass = sc.getDryMass();

      double rmag = norm(r);
      double beta = cd * area / mass;  // [m^2/kg]

      // compute the atmospheric density
      double rho = computeDensity(utc, rb, r, v);   // [kg/m^3]

      // debuging...
      //rho  = 6.3097802844338E-12;
      
      // compute the relative velocity vector and magnitude
      Vector<double> we(3,0.0);
      we(2)= omega_e;

      Vector<double> wxr = cross(we,r);
      Vector<double> vr = v - wxr;
      double vrmag = norm(vr);
      
      // form -1/2 (Cd*A/m) rho
      double coeff = -0.5 * beta * rho;
      double coeff2 = coeff * vrmag;

      // compute the acceleration in ECI frame (km/s^2)
      a = vr * coeff2;                                  ///////// a

      // Partial reference: Montenbruck,P248

      // form partial of drag wrt v  
      // da_dv = -0.5*Cd*(A/M)*p*(vr*transpose(vr)/vr+vr1)
      Matrix<double> tr(3,1,0.0);
      tr(0,0)=vr(0);
      tr(1,0)=vr(1);
      tr(2,0)=vr(2);

      Matrix<double> vrvrt = tr*transpose(tr); 
      vrvrt = vrvrt / vrmag;
      
      double eye3[3*3] = {1,0,0,0,1,0,0,0,1};
      Matrix<double> vrm(3,3,0.0);
      vrm = eye3;

      vrm = vrm * vrmag;
      da_dv = (vrvrt + vrm) * coeff;               //////// da_dv

      // da_dr
      // da_dr = -0.5*Cd*(A/M)*vr*dp_dr-da_dv*X(w)
      da_dr.resize(3,3,0.0);

      Matrix<double> X(3,3,0.0);
      X(0,1) = -we(2);      // -wz
      X(0,2) = +we(1);      //  wy
      X(1,0) = +we(2);      // +wz
      X(1,2) = -we(0);      // -wx
      X(2,0) = -we(1);      // -wy
      X(2,1) = +we(0);      // +wx
      
      Matrix<double> part1(3,3,0.0);
      Matrix<double> part2(3,3,0.0);
      
   
      // Get the J2000 to TOD transformation
      Matrix<double> N = ReferenceFrames::J2kToTODMatrix(utc);

      // Transform r from J2000 to TOD
      Vector<double> r_tod = N * r;
      Position geoidPos(r_tod(0),r_tod(1),r_tod(3));
      
      // Satellite height
      double height = geoidPos.getAltitude()/1000.0;              //  convert to [km]
      
      const int n = CIRA_SIZE; ;

      int bracket = 0;

      if (height >= h0[n-1]) 
      {
         bracket = n - 1;
      }
      else 
      {
         for (int i = 0; i < (n-1); i++) 
         {
            if ((height >= h0[i]) && (height < h0[i+1]))
            {
               bracket = i;
            }
         }
      }  // End 'if (height >= h0[n-1]) '
      
      double Hh = H[bracket];
      double coeff4 = -1.0 / (Hh * rmag);

      Vector<double> drhodr = r*coeff4;
      
      Matrix<double> tr2(3,1,0.0);
      tr2(0,0) = drhodr(0);
      tr2(1,0) = drhodr(1);
      tr2(2,0) = drhodr(2);

      part1 = tr*transpose(tr2);      // //Matrix part1 = vr.outerProduct(drhodr);
      part1 = part1*coeff2;

      //part1 = dp_dr*a/rho;
      part2 =-da_dv*X;
      da_dr = part1-part2;

      // form partial of drag wrt cd
      double coeff3 = coeff2 / cd;
      this->dadcd = vr*coeff3;                        ////////   da_dcd

      this->da_dcd(0,0) = dadcd(0);
      this->da_dcd(1,0) = dadcd(1);
      this->da_dcd(2,0) = dadcd(2);

   }  // End of method 'AtmosphericDrag::doCompute()'
Esempio n. 13
0
//----------------------------------------------------------------------------
void mitk::vtkEventProvider::ProcessEvents(vtkObject* object,
                                               unsigned long event,
                                               void* clientData,
                                               void* vtkNotUsed(callData))
{

  vtkEventProvider* self =
    reinterpret_cast<vtkEventProvider *>( clientData );
  vtkRenderWindowInteractor* rwi =
    static_cast<vtkInteractorStyle *>( object )->GetInteractor();

  // base renderer
  mitk::BaseRenderer* baseRenderer = mitk::BaseRenderer::GetInstance(self->GetRenderWindow()->GetVtkRenderWindow());


  switch(event)
  {
    // key press
    case vtkCommand::KeyPressEvent:
    {
      VTKEVENTPROVIDER_DEBUG << "key press event";
      mitk::KeyEvent mke(mitk::VtkEventAdapter::AdaptKeyEvent(baseRenderer,event,rwi));
      self->GetRenderWindow()->keyPressMitkEvent(&mke);
      break;
    }

    // mouse events
    case vtkCommand::MouseMoveEvent:
  {
      VTKEVENTPROVIDER_DEBUG << "mouse move event";
      mitk::MouseEvent me(mitk::VtkEventAdapter::AdaptMouseEvent(baseRenderer,event,rwi));
      self->GetRenderWindow()->mouseMoveMitkEvent(&me);
      break;
    }

  case vtkCommand::LeftButtonPressEvent:
    case vtkCommand::MiddleButtonPressEvent:
    case vtkCommand::RightButtonPressEvent:
  {
      VTKEVENTPROVIDER_DEBUG << "mouse press event";
      mitk::MouseEvent me(mitk::VtkEventAdapter::AdaptMouseEvent(baseRenderer,event,rwi));
      self->GetRenderWindow()->mousePressMitkEvent(&me);
      break;
    }

  case vtkCommand::LeftButtonReleaseEvent:
    case vtkCommand::MiddleButtonReleaseEvent:
    case vtkCommand::RightButtonReleaseEvent:
    {
      VTKEVENTPROVIDER_DEBUG << "mouse release event";
      mitk::MouseEvent me(mitk::VtkEventAdapter::AdaptMouseEvent(baseRenderer,event,rwi));
      self->GetRenderWindow()->mouseReleaseMitkEvent(&me);
      break;
    }

  // mouse WHEEL
    case vtkCommand::MouseWheelForwardEvent:
    case vtkCommand::MouseWheelBackwardEvent:
    {
      VTKEVENTPROVIDER_DEBUG << "mouse wheel event";
      mitk::WheelEvent we(mitk::VtkEventAdapter::AdaptWheelEvent(baseRenderer,event,rwi));
      self->GetRenderWindow()->wheelMitkEvent(&we);
      break;
    }

  default:
      VTKEVENTPROVIDER_INFO << "VTK event not mapped properly.";
    break;
  }

}
Esempio n. 14
0
void PianoView::wheelEvent(QWheelEvent* event)
      {
      int step = event->delta() / 120;
      double xmag = transform().m11();
      double ymag = transform().m22();

      if (event->modifiers() == Qt::ControlModifier) {
            if (step > 0) {
                  for (int i = 0; i < step; ++i) {
                        if (xmag > 10.0)
                              break;
                        scale(1.1, 1.0);
                        xmag *= 1.1;
                        }
                  }
            else {
                  for (int i = 0; i < -step; ++i) {
                        if (xmag < 0.001)
                              break;
                        scale(.9, 1.0);
                        xmag *= .9;
                        }
                  }
            emit magChanged(xmag, ymag);

            int tpix  = (480 * 4) * xmag;
            magStep = -5;
            if (tpix <= 4000)
                  magStep = -4;
            if (tpix <= 2000)
                  magStep = -3;
            if (tpix <= 1000)
                  magStep = -2;
            if (tpix <= 500)
                  magStep = -1;
            if (tpix <= 128)
                  magStep = 0;
            if (tpix <= 64)
                  magStep = 1;
            if (tpix <= 32)
                  magStep = 2;
            if (tpix <= 16)
                  magStep = 3;
            if (tpix <= 8)
                  magStep = 4;
            if (tpix <= 4)
                  magStep = 5;
            if (tpix <= 2)
                  magStep = 6;

            //
            // if xpos <= 0, then the scene is centered
            // there is no scroll bar anymore sending
            // change signals, so we have to do it here:
            //
            double xpos = -(mapFromScene(QPointF()).x());
            if (xpos <= 0)
                  emit xposChanged(xpos);
            }
      else if (event->modifiers() == Qt::ShiftModifier) {
            QWheelEvent we(event->pos(), event->delta(), event->buttons(), 0, Qt::Horizontal);
            QGraphicsView::wheelEvent(&we);
            }
      else if (event->modifiers() == 0) {
            QGraphicsView::wheelEvent(event);
            }
      else if (event->modifiers() == (Qt::ShiftModifier | Qt::ControlModifier)) {
            if (step > 0) {
                  for (int i = 0; i < step; ++i) {
                        if (ymag > 3.0)
                              break;
                        scale(1.0, 1.1);
                        ymag *= 1.1;
                        }
                  }
            else {
                  for (int i = 0; i < -step; ++i) {
                        if (ymag < 0.4)
                              break;
                        scale(1.0, .9);
                        ymag *= .9;
                        }
                  }
            emit magChanged(xmag, ymag);
            }
      }
Esempio n. 15
0
 * Print the DRAM routing info for one base/limit pair.
 *
 * Show base, limit, dest node, dest link on that node, read and write
 * enable, and interleave information.
 *
 * @param level Printing level
 * @param which Register number
 * @param base Base register
 * @param lim Limit register
 */
static void showdram(int level, u8 which, u32 base, u32 lim)
{
	printk(level, "DRAM(%02x)%010llx-%010llx, ->(%d), %s, %s, %s, %d\n",
	       which, (((u64) base & 0xffff0000) << 8),
	       (((u64) lim & 0xffff0000) << 8) + 0xffffff,
	       r_node(lim), re(base), we(base), ileave(base), (lim >> 8) & 3);
}

/**
 * Print the config routing info for a config register.
 *
 * Show base, limit, dest node, dest link on that node, read and write
 * enable, and device number compare enable
 *
 * @param level Printing level
 * @param which Register number
 * @param reg Config register
 */
static void showconfig(int level, u8 which, u32 reg)
{
	/* Don't use r_node() and r_link() here. */
Esempio n. 16
0
void BoolView::attach(Propagator *p, int pos, int eflags) {
	WatchElem we(p->prop_id, pos);
	if (eflags & EVENT_L) sat.watches[2*v+s].push(we);
	if (eflags & EVENT_U) sat.watches[2*v+(1-s)].push(we);
}
    /**
     * The predicted sharp edge gradient ∇I^s is used as a spatial prior to guide
     * the recovery of a coarse version of the latent image.
     * Objective function: E(I) = ||I ⊗ k - B||² + λ||∇I - ∇I^s||²
     * 
     * @param blurred        blurred grayvalue image (B)
     * @param kernel         energy presserving kernel (k)
     * @param selectionGrads gradients of selected edges (x and y direction) (∇I^s)
     * @param latent         resulting image (I)
     * @param weight         λ, default is 2.0e-3 (weight from paper)
     */
    void coarseImageEstimation(Mat blurred, const Mat& kernel,
                               const array<Mat,2>& selectionGrads, Mat& latent,
                               const float weight = 2.0e-3) {

        assert(kernel.type() == CV_32F && "works with energy preserving float kernel");
        assert(blurred.type() == CV_8U && "works with gray valued blurred image");

        // convert grayvalue image to float and normalize it to [0,1]
        blurred.convertTo(blurred, CV_32F);
        blurred /= 255.0;

        // fill kernel with zeros to get to the blurred image size
        // it's important to use BORDER_ISOLATED flag if the kernel is an ROI of a greater image!
        Mat pkernel;
        copyMakeBorder(kernel, pkernel, 0,
                       blurred.rows - kernel.rows, 0,
                       blurred.cols - kernel.cols,
                       BORDER_CONSTANT, Scalar::all(0));

        // using sobel filter as gradients dx and dy
        Mat sobelx = Mat::zeros(blurred.size(), CV_32F);
        sobelx.at<float>(0,0) = -1;
        sobelx.at<float>(0,1) = 1;
        Mat sobely = Mat::zeros(blurred.size(), CV_32F);
        sobely.at<float>(0,0) = -1;
        sobely.at<float>(1,0) = 1;


        //                ____               ______                ______
        //             (  F(k) * F(B) + λ * (F(∂_x) * F(∂_x I^s) + F(∂_y) * F(∂_y I^s)) )
        // I = F^-1 * ( ---------------------------------------------------------------  )
        //            (     ____               ______            ______                  )
        //             (    F(k) * F(k) + λ * (F(∂_x) * F(∂_x) + F(∂_y) * F(∂_y))       )
        // where * is pointwise multiplication
        // 
        // here: F(k)       = k
        //       F(∂_x I^s) = xS
        //       F(∂_y I^s) = yS
        //       F(∂_x)     = dx
        //       F(∂_y)     = dy
        //       F(B)       = B

        // compute DFT (withoud padding)
        // the result are stored as 2 channel matrices: Re(FFT(I)), Im(FFT(I))
        Mat K, xS, yS, B, Dx, Dy;
        deblur::dft(pkernel, K);
        deblur::dft(selectionGrads[0], xS);
        deblur::dft(selectionGrads[1], yS);
        deblur::dft(blurred, B);
        deblur::dft(sobelx, Dx);
        deblur::dft(sobely, Dy);

        // weight from paper
        complex<float> we(weight, 0.0);

        // latent image in fourier domain
        Mat I = Mat::zeros(xS.size(), xS.type());

        // pointwise computation of I
        for (int x = 0; x < xS.cols; x++) {
            for (int y = 0; y < xS.rows; y++) {
                // complex entries at the current position
                complex<float> b(B.at<Vec2f>(y, x)[0], B.at<Vec2f>(y, x)[1]);
                complex<float> k(K.at<Vec2f>(y, x)[0], K.at<Vec2f>(y, x)[1]);

                complex<float> xs(xS.at<Vec2f>(y, x)[0], xS.at<Vec2f>(y, x)[1]);
                complex<float> ys(yS.at<Vec2f>(y, x)[0], yS.at<Vec2f>(y, x)[1]);

                complex<float> dx(Dx.at<Vec2f>(y, x)[0], Dx.at<Vec2f>(y, x)[1]);
                complex<float> dy(Dy.at<Vec2f>(y, x)[0], Dy.at<Vec2f>(y, x)[1]);

                // compute current point of latent image in fourier domain
                complex<float> i = (conj(k) * b + we * (conj(dx) * xs + conj(dy) * ys)) /
                                   (conj(k) * k + we * (conj(dx) * dx + conj(dy) * dy));
                
                I.at<Vec2f>(y, x) = { real(i), imag(i) };
            }
        }

        // compute inverse DFT of the latent image
        dft(I, latent, DFT_INVERSE | DFT_REAL_OUTPUT);


        // threshold the result because it has large negative and positive values
        // which would result in a very grayish image
        threshold(latent, latent, 0.0, -1, THRESH_TOZERO);


        // swap slices of the result
        // because the image is shifted to the upper-left corner (why??)
        int x = latent.cols;
        int y = latent.rows;
        int hs1 = (kernel.cols - 1) / 2;
        int hs2 = (kernel.rows - 1) / 2;

        // create rects per image slice
        //  __________
        // |      |   |
        // |   0  | 1 |
        // |      |   |
        // |------|---|
        // |   2  | 3 |
        // |______|___|
        // 
        // rect gets the coordinates of the top-left corner, width and height
        Mat q0(latent, Rect(0, 0, x - hs1, y - hs2));      // Top-Left
        Mat q1(latent, Rect(x - hs1, 0, hs1, y - hs2));    // Top-Right
        Mat q2(latent, Rect(0, y - hs2, x - hs1, hs2));    // Bottom-Left
        Mat q3(latent, Rect(x - hs1, y - hs2, hs1, hs2));  // Bottom-Right

        Mat latentSwap;
        cv::hconcat(q3, q2, latentSwap);
        Mat tmp;
        cv::hconcat(q1, q0, tmp);
        cv::vconcat(latentSwap, tmp, latentSwap);


        // convert result to uchar image
        convertFloatToUchar(latentSwap, latent);

        assert(blurred.size() == latent.size()
               && "Something went wrong - latent and blurred size has to be equal");
    }
    /**
     * With the critical edge selection, initial kernel erstimation can be accomplished quickly.
     * Objective function: E(k) = ||∇I^s ⊗ k - ∇B||² + γ||k||²
     * 
     * @param selectionGrads  array of x and y gradients of final selected edges (∇I^s) [-1, 1]
     * @param blurredGrads    array of x and y gradients of blurred image (∇B) [-1, 1]
     * @param kernel          energy preserving kernel (k)
     */
    void fastKernelEstimation(const array<Mat,2>& selectionGrads,
                              const array<Mat,2>& blurredGrads, Mat& kernel,
                              const float weight = 1e-2) {

        assert(selectionGrads[0].rows == blurredGrads[0].rows && "matrixes have to be of same size!");
        assert(selectionGrads[0].cols == blurredGrads[0].cols && "matrixes have to be of same size!");

        // based on Perseval's theorem, perform FFT
        //                __________              __________
        //             (  F(∂_x I^s) * F(∂_x B) + F(∂_y I^s) * F(∂_y B) )
        // k = F^-1 * ( ----------------------------------------------   )
        //             (         F(∂_x I^s)² + F(∂_y I^s)² + γ          )
        // where * is pointwise multiplication
        //                   __________
        // and F(∂_x I^s)² = F(∂_x I^s) * F(∂_x I^s) ! because they mean the norm
        // 
        // here: F(∂_x I^s) = xS
        //       F(∂_x B)   = xB
        //       F(∂_y I^s) = yS
        //       F(∂_y B)   = yB
        
        // compute FFTs
        // the result are stored as 2 channel matrices: Re(FFT(I)), Im(FFT(I))
        Mat xS, xB, yS, yB;
        deblur::dft(selectionGrads[0], xS);
        deblur::dft(blurredGrads[0], xB);
        deblur::dft(selectionGrads[1], yS);
        deblur::dft(blurredGrads[1], yB);

        complex<float> we(weight, 0.0);

        // kernel in Fourier domain
        Mat K = Mat::zeros(xS.size(), xS.type());

        // pixelwise computation of kernel
        for (int y = 0; y < K.rows; y++) {
            for (int x = 0; x < K.cols; x++) { 
                // complex entries at the current position
                complex<float> xs(xS.at<Vec2f>(y, x)[0], xS.at<Vec2f>(y, x)[1]);
                complex<float> ys(yS.at<Vec2f>(y, x)[0], yS.at<Vec2f>(y, x)[1]);

                complex<float> xb(xB.at<Vec2f>(y, x)[0], xB.at<Vec2f>(y, x)[1]);
                complex<float> yb(yB.at<Vec2f>(y, x)[0], yB.at<Vec2f>(y, x)[1]);


                // kernel entry in the Fourier space
                complex<float> k = (conj(xs) * xb + conj(ys) * yb) /
                                   (conj(xs) * xs + conj(ys) * ys + we);
                                   // (abs(xs) * abs(xs) + abs(ys) * abs(ys) + we); // equivalent
                
                K.at<Vec2f>(y, x) = { real(k), imag(k) };
            }
        }

        // only use the real part of the complex output
        Mat kernelBig;
        dft(K, kernelBig, DFT_INVERSE | DFT_REAL_OUTPUT);

        // FIXME: find kernel inside image (kind of bounding box) instead of force user to
        // approximate a correct kernel-width (otherwise some information are lost)

        // cut of kernel in middle of the temporary kernel
        int x = kernelBig.cols / 2 - kernel.cols / 2;
        int y = kernelBig.rows / 2 - kernel.rows / 2;
        swapQuadrants(kernelBig);
        Mat kernelROI = kernelBig(Rect(x, y, kernel.cols, kernel.rows));

        // copy the ROI to the kernel to avoid that some OpenCV functions accidently
        // uses the information outside of the ROI (like copyMakeBorder())
        kernelROI.copyTo(kernel);

        // threshold kernel to erease negative values
        threshold(kernel, kernel, 0.0, -1, THRESH_TOZERO);

        // // kernel has to be energy preserving
        // // this means: sum(kernel) = 1
        kernel /= sum(kernel)[0];
    }