Example #1
0
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            m_MousePositionCurrent = e.Location;
            if (m_MouseButtonsDown == MouseButtons.None)
            {
                m_ButtonDownMousePosition = e.Location;
            }

            switch (m_ManipulationState)
            {
            case MANIPULATION_STATE.PICK_COLOR:
                Cursor = Cursors.Cross;
                ImageUtility.float2 UV0 = Client2ImageUV(m_ButtonDownMousePosition);
                ImageUtility.float2 UV1 = Client2ImageUV(e.Location);
                m_ColorPickingUpdateDelegate(UV0, UV1);
                Invalidate();
                break;

            default:
                Cursor = Cursors.Default;
                break;
            }
        }
Example #2
0
 /// <summary>
 /// Sets the crop rectangle to a specific value
 /// </summary>
 public void                             SetCropRectangle(ImageUtility.float2 _Center, ImageUtility.float2 _HalfSize, float _Rotation)
 {
     m_CropRectangleIsDefault = false;
     m_CropRectangleCenter    = _Center;
     m_CropRectangleHalfSize  = _HalfSize;
     m_CropRectangleRotation  = _Rotation;
     Invalidate();
 }
Example #3
0
        private PointF  ImageUV2Client(ImageUtility.float2 _Position)
        {
            RectangleF ImageRect = ImageClientRect();

//			return new PointF( ImageRect.X + _Position.x * ImageRect.Width, ImageRect.Y + _Position.y * ImageRect.Height );
//			return new PointF( ImageRect.X + _Position.x * ImageRect.Height, ImageRect.Y + _Position.y * ImageRect.Height );
            return(new PointF(0.5f * (Width - ImageRect.Height) + _Position.x * ImageRect.Height, ImageRect.Y + _Position.y * ImageRect.Height));
        }
Example #4
0
 protected ImageUtility.float2[] BuildClipVertices(ImageUtility.float2 _Center, ImageUtility.float2 _HalfSize, ImageUtility.float2 _AxisX, ImageUtility.float2 _AxisY)
 {
     return(new ImageUtility.float2[4] {
         _Center - _HalfSize.x * _AxisX + _HalfSize.y * _AxisY,
         _Center + _HalfSize.x * _AxisX + _HalfSize.y * _AxisY,
         _Center - _HalfSize.x * _AxisX - _HalfSize.y * _AxisY,
         _Center + _HalfSize.x * _AxisX - _HalfSize.y * _AxisY,
     });
 }
Example #5
0
        /// <summary>
        /// Resets the crop rectangle to the entire image
        /// </summary>
        public void                             ResetCropRectangle()
        {
            m_CropRectangleIsDefault = true;
            if (m_Image == null)
            {
                return;
            }

            m_CropRectangleCenter   = new ImageUtility.float2(0.5f, 0.5f);
            m_CropRectangleHalfSize = new ImageUtility.float2(0.5f * ImageAspectRatio, 0.5f);
            m_CropRectangleRotation = 0.0f;
            Invalidate();
        }
Example #6
0
        private PointF[]        m_ClientCropRectangleVertices = new PointF[4];          // Top-left, top-right, bottom-left, bottom-right
        private void            UpdateCropRectangleVertices()
        {
            m_CropRectangleAxisX = new ImageUtility.float2((float)Math.Cos(m_CropRectangleRotation), -(float)Math.Sin(m_CropRectangleRotation));
            m_CropRectangleAxisY = new ImageUtility.float2(-(float)Math.Sin(m_CropRectangleRotation), -(float)Math.Cos(m_CropRectangleRotation));

            RectangleF ImageRect = ImageClientRect();

            m_ClientCropRectangleCenter = ImageUV2Client(m_CropRectangleCenter);

            ImageUtility.float2[] Vertices = new ImageUtility.float2[4] {
                m_CropRectangleCenter - m_CropRectangleHalfSize.x * m_CropRectangleAxisX + m_CropRectangleHalfSize.y * m_CropRectangleAxisY,
                m_CropRectangleCenter + m_CropRectangleHalfSize.x * m_CropRectangleAxisX + m_CropRectangleHalfSize.y * m_CropRectangleAxisY,
                m_CropRectangleCenter - m_CropRectangleHalfSize.x * m_CropRectangleAxisX - m_CropRectangleHalfSize.y * m_CropRectangleAxisY,
                m_CropRectangleCenter + m_CropRectangleHalfSize.x * m_CropRectangleAxisX - m_CropRectangleHalfSize.y * m_CropRectangleAxisY,
            };

            for (int i = 0; i < 4; i++)
            {
                m_ClientCropRectangleVertices[i] = ImageUV2Client(Vertices[i]);
            }
        }
Example #7
0
        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);

            m_MouseButtonsDown &= ~e.Button;

            // End manipulation
            switch (m_ManipulationState)
            {
            case MANIPULATION_STATE.PICK_COLOR:
                ManipulationState = MANIPULATION_STATE.STOPPED;

                // Notify end
                ImageUtility.float2 UV0 = Client2ImageUV(m_ButtonDownMousePosition);
                ImageUtility.float2 UV1 = Client2ImageUV(e.Location);
                m_ColorPickingEndDelegate(UV0, UV1);
                break;
            }

            Invalidate();
            Capture = false;
            Cursor  = Cursors.Default;
        }
Example #8
0
        /// <summary>
        /// Computes the average color within a rectangle in UV space
        /// </summary>
        /// <param name="_TopLeft">The top left corner (in UV space) of the rectangle to sample</param>
        /// <param name="_BottomRight">The bottom right corner (in UV space) of the rectangle to sample</param>
        /// <returns>The average xyY color</returns>
        public ImageUtility.float3      ComputeAverageSwatchColor(ImageUtility.float2 _TopLeft, ImageUtility.float2 _BottomRight)
        {
            // Average xyY values in the specified rectangle
            int X0 = Math.Max(0, Math.Min(m_Texture.Width - 1, (int)Math.Floor(_TopLeft.x * m_Texture.Width)));
            int Y0 = Math.Max(0, Math.Min(m_Texture.Height - 1, (int)Math.Floor(_TopLeft.y * m_Texture.Height)));
            int X1 = Math.Min(m_Texture.Width, Math.Max(X0 + 1, (int)Math.Floor(_BottomRight.x * m_Texture.Width)));
            int Y1 = Math.Min(m_Texture.Height, Math.Max(Y0 + 1, (int)Math.Floor(_BottomRight.y * m_Texture.Height)));
            int W  = X1 - X0;
            int H  = Y1 - Y0;

            ImageUtility.float4 AverageXYZ = new ImageUtility.float4(0, 0, 0, 0);
            for (int Y = Y0; Y < Y1; Y++)
            {
                for (int X = X0; X < X1; X++)
                {
                    ImageUtility.float4 XYZ = m_Texture.ContentXYZ[X, Y];
                    AverageXYZ += XYZ;
                }
            }
            AverageXYZ = (1.0f / (W * H)) * AverageXYZ;

            ImageUtility.float3 xyY = ImageUtility.ColorProfile.XYZ2xyY((ImageUtility.float3)AverageXYZ);
            return(xyY);
        }
Example #9
0
        protected override void OnMouseMove( MouseEventArgs e )
        {
            base.OnMouseMove( e );

            m_MousePositionCurrent = e.Location;
            if ( m_MouseButtonsDown == MouseButtons.None )
            {
                m_ButtonDownMousePosition = e.Location;
                m_ButtonDownCropRectangleCenter = m_CropRectangleCenter;
                m_ButtonDownCropRectangleHalfSize = m_CropRectangleHalfSize;
                m_ButtonDownCropRectangleRotation = m_CropRectangleRotation;
                m_ButtonDownCropRectangleAxisX = m_CropRectangleAxisX;
                m_ButtonDownCropRectangleAxisY = m_CropRectangleAxisY;

                m_ButtonDownClientCropRectangleCenter = m_ClientCropRectangleCenter;
                m_ButtonDownClientCropRectangleVertices = new PointF[4] {
                    m_ClientCropRectangleVertices[0],
                    m_ClientCropRectangleVertices[1],
                    m_ClientCropRectangleVertices[2],
                    m_ClientCropRectangleVertices[3],
                };
            }

            switch ( m_ManipulationState )
            {
                case MANIPULATION_STATE.CROP_RECTANGLE:
                {	// Take care of crop rectangle manipulation
                    if ( !m_CropRectangleManipulationStarted )
                    {	// Update the cursor based on the hovered manipulation spot
                        m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.NONE;

                        const float	TOLERANCE = 8.0f;

                        PointF	P = e.Location;

                        PointF	TopLeftCorner2UV = new PointF( P.X - m_ClientCropRectangleVertices[0].X, P.Y - m_ClientCropRectangleVertices[0].Y );
                        PointF	BottomRightCorner2UV = new PointF( P.X - m_ClientCropRectangleVertices[3].X, P.Y - m_ClientCropRectangleVertices[3].Y );

                        float	Distance2Left = -TopLeftCorner2UV.X * m_CropRectangleAxisX.x - TopLeftCorner2UV.Y * m_CropRectangleAxisX.y;
                        float	Distance2Top = TopLeftCorner2UV.X * m_CropRectangleAxisY.x + TopLeftCorner2UV.Y * m_CropRectangleAxisY.y;
                        float	Distance2Right = BottomRightCorner2UV.X * m_CropRectangleAxisX.x + BottomRightCorner2UV.Y * m_CropRectangleAxisX.y;
                        float	Distance2Bottom = -BottomRightCorner2UV.X * m_CropRectangleAxisY.x - BottomRightCorner2UV.Y * m_CropRectangleAxisY.y;

                        // Stretch
                        if ( Math.Abs( Distance2Left ) < TOLERANCE )
                        {
                            if ( Math.Abs( Distance2Top ) < TOLERANCE )
                                m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.CORNER_TOP_LEFT;
                            else if ( Math.Abs( Distance2Bottom ) < TOLERANCE )
                                m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.CORNER_BOTTOM_LEFT;
                            else
                                m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.LEFT;
                        }
                        else if ( Math.Abs( Distance2Right ) < TOLERANCE )
                        {
                            if ( Math.Abs( Distance2Top ) < TOLERANCE )
                                m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.CORNER_TOP_RIGHT;
                            else if ( Math.Abs( Distance2Bottom ) < TOLERANCE )
                                m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.CORNER_BOTTOM_RIGHT;
                            else
                                m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.RIGHT;
                        }
                        else if ( Math.Abs( Distance2Top ) < TOLERANCE )
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.TOP;
                        else if ( Math.Abs( Distance2Bottom ) < TOLERANCE )
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.BOTTOM;
                        // Rotate
                        else if ( Distance2Top > TOLERANCE && Distance2Right > TOLERANCE )
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.ROTATE_TOP_RIGHT;
                        else if ( Distance2Top > TOLERANCE && Distance2Left > TOLERANCE )
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.ROTATE_TOP_LEFT;
                        else if ( Distance2Bottom > TOLERANCE && Distance2Right > TOLERANCE )
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.ROTATE_BOTTOM_RIGHT;
                        else if ( Distance2Bottom > TOLERANCE && Distance2Left > TOLERANCE )
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.ROTATE_BOTTOM_LEFT;
                        else if ( Distance2Left < 0.0f && Distance2Right < 0.0f && Distance2Bottom < 0.0f && Distance2Top < 0.0f )
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.CENTER;

                        // Update cursor accordingly
                        switch ( m_CropRectangleManipulatedSpot  )
                        {
                            case CROP_RECTANGLE_SPOT.NONE: Cursor = Cursors.Default; break;
                            case CROP_RECTANGLE_SPOT.LEFT: Cursor = Cursors.SizeWE; break;
                            case CROP_RECTANGLE_SPOT.RIGHT: Cursor = Cursors.SizeWE; break;
                            case CROP_RECTANGLE_SPOT.TOP: Cursor = Cursors.SizeNS; break;
                            case CROP_RECTANGLE_SPOT.BOTTOM: Cursor = Cursors.SizeNS; break;
                            case CROP_RECTANGLE_SPOT.CORNER_TOP_LEFT: Cursor = Cursors.SizeNWSE; break;
                            case CROP_RECTANGLE_SPOT.CORNER_BOTTOM_RIGHT: Cursor = Cursors.SizeNWSE; break;
                            case CROP_RECTANGLE_SPOT.CORNER_TOP_RIGHT: Cursor = Cursors.SizeNESW; break;
                            case CROP_RECTANGLE_SPOT.CORNER_BOTTOM_LEFT: Cursor = Cursors.SizeNESW; break;
                            case CROP_RECTANGLE_SPOT.ROTATE_TOP_LEFT:
                            case CROP_RECTANGLE_SPOT.ROTATE_TOP_RIGHT:
                            case CROP_RECTANGLE_SPOT.ROTATE_BOTTOM_LEFT:
                            case CROP_RECTANGLE_SPOT.ROTATE_BOTTOM_RIGHT:
                                Cursor = Cursors.Hand;
                                break;
                            case CROP_RECTANGLE_SPOT.CENTER:
                                Cursor = Cursors.Hand;
                                break;
                        }
                    }
                    else
                    {	// Handle actual manipulation
                        ImageUtility.float2		CurClientPos = new ImageUtility.float2( e.X, e.Y );
                        ImageUtility.float2		OldClientPos = new ImageUtility.float2( m_ButtonDownMousePosition.X, m_ButtonDownMousePosition.Y );
                        ImageUtility.float2		OldClientCenter = new ImageUtility.float2( m_ButtonDownClientCropRectangleCenter.X, m_ButtonDownClientCropRectangleCenter.Y );

                        ImageUtility.float2		OldCenter = m_ButtonDownCropRectangleCenter;
                        ImageUtility.float2		OldHalfSize = m_ButtonDownCropRectangleHalfSize;
                        ImageUtility.float2		OldAxisX = m_ButtonDownCropRectangleAxisX;
                        ImageUtility.float2		OldAxisY = m_ButtonDownCropRectangleAxisY;

                        ImageUtility.float2[]	OldVertices = new ImageUtility.float2[4] {
                            OldCenter - OldHalfSize.x * OldAxisX + OldHalfSize.y * OldAxisY,
                            OldCenter + OldHalfSize.x * OldAxisX + OldHalfSize.y * OldAxisY,
                            OldCenter - OldHalfSize.x * OldAxisX - OldHalfSize.y * OldAxisY,
                            OldCenter + OldHalfSize.x * OldAxisX - OldHalfSize.y * OldAxisY,
                        };

                        ImageUtility.float2		CurCenter = OldCenter;
                        ImageUtility.float2		CurHalfSize = OldHalfSize;
                        ImageUtility.float2		CurAxisX = OldAxisX;
                        ImageUtility.float2		CurAxisY = OldAxisY;

                        RectangleF	ImageRect = ImageClientRect();	// The image's rectangle in client space

                        switch ( m_CropRectangleManipulatedSpot )
                        {
                            case CROP_RECTANGLE_SPOT.LEFT:
                            case CROP_RECTANGLE_SPOT.RIGHT:
                            {
                                ImageUtility.float2	Center2OldPos = OldClientPos - OldClientCenter;
                                float	OldDistance2Edge = Center2OldPos.Dot( OldAxisX );
                                ImageUtility.float2	Center2CurPos = CurClientPos - OldClientCenter;
                                float	CurDistance2Edge = Center2CurPos.Dot( OldAxisX );

                                float	Delta = CurDistance2Edge - OldDistance2Edge;	// This is the amount (in client space) we need to move the left/right border
                                float	DeltaUV = Delta / ImageRect.Height;				// This is the same amount in UV space
                                DeltaUV *= 0.5f;	// We're dealing with halves all along

                                // Increase width of that amount
                                CurHalfSize.x = OldHalfSize.x + (m_CropRectangleManipulatedSpot == CROP_RECTANGLE_SPOT.LEFT ? -1 : 1) * DeltaUV;

                                // Move center along the X axis
                                if ( m_CropRectangleManipulatedSpot == CROP_RECTANGLE_SPOT.LEFT )
                                {	// Keep right fixed, move to the left
                                    ImageUtility.float2	Right = OldCenter + OldHalfSize.x * OldAxisX;
                                    ImageUtility.float2	Left = Right - 2.0f * CurHalfSize.x * OldAxisX;
                                    CurCenter = 0.5f * (Right + Left);
                                }
                                else
                                {	// Keep left fixed, move to the right
                                    ImageUtility.float2	Left = OldCenter - OldHalfSize.x * OldAxisX;
                                    ImageUtility.float2	Right = Left + 2.0f * CurHalfSize.x * OldAxisX;
                                    CurCenter = 0.5f * (Right + Left);
                                }
                                break;
                            }

                            case CROP_RECTANGLE_SPOT.TOP:
                            case CROP_RECTANGLE_SPOT.BOTTOM:
                            {
                                ImageUtility.float2	Center2OldPos = OldClientPos - OldClientCenter;
                                float	OldDistance2Edge = Center2OldPos.Dot( OldAxisY );
                                ImageUtility.float2	Center2CurPos = CurClientPos - OldClientCenter;
                                float	CurDistance2Edge = Center2CurPos.Dot( OldAxisY );

                                float	Delta = CurDistance2Edge - OldDistance2Edge;	// This is the amount (in client space) we need to move the left/right border
                                float	DeltaUV = Delta / ImageRect.Height;				// This is the same amount in UV space
                                DeltaUV *= 0.5f;	// We're dealing with halves all along

                                // Increase height of that amount
                                CurHalfSize.y = OldHalfSize.y + (m_CropRectangleManipulatedSpot == CROP_RECTANGLE_SPOT.TOP ? 1 : -1) * DeltaUV;

                                // Move center along the X axis
                                if ( m_CropRectangleManipulatedSpot == CROP_RECTANGLE_SPOT.TOP )
                                {	// Keep bottom fixed, move up
                                    ImageUtility.float2	Bottom = OldCenter - OldHalfSize.y * OldAxisY;
                                    ImageUtility.float2	Top = Bottom + 2.0f * CurHalfSize.y * OldAxisY;
                                    CurCenter = 0.5f * (Bottom + Top);
                                }
                                else
                                {	// Keep top fixed, move down
                                    ImageUtility.float2	Top = OldCenter + OldHalfSize.y * OldAxisY;
                                    ImageUtility.float2	Bottom = Top - 2.0f * CurHalfSize.y * OldAxisY;
                                    CurCenter = 0.5f * (Bottom + Top);
                                }
                                break;
                            }

                            case CROP_RECTANGLE_SPOT.CORNER_TOP_LEFT:
                            case CROP_RECTANGLE_SPOT.CORNER_TOP_RIGHT:
                            case CROP_RECTANGLE_SPOT.CORNER_BOTTOM_LEFT:
                            case CROP_RECTANGLE_SPOT.CORNER_BOTTOM_RIGHT:
                            {
                                ImageUtility.float2	Delta = CurClientPos - OldClientPos;
                                ImageUtility.float2	DeltaUV = (1.0f / ImageRect.Height) * Delta;

                                ImageUtility.float2	Corner = new ImageUtility.float2(), OppositeCorner = new ImageUtility.float2();
                                switch ( m_CropRectangleManipulatedSpot )
                                {
                                    case CROP_RECTANGLE_SPOT.CORNER_TOP_LEFT: Corner = OldVertices[0]; OppositeCorner = OldVertices[3]; break;		// Keep bottom right fixed
                                    case CROP_RECTANGLE_SPOT.CORNER_TOP_RIGHT: Corner = OldVertices[1]; OppositeCorner = OldVertices[2]; break;		// Keep bottom left fixed
                                    case CROP_RECTANGLE_SPOT.CORNER_BOTTOM_LEFT: Corner = OldVertices[2]; OppositeCorner = OldVertices[1]; break;		// Keep top right fixed
                                    case CROP_RECTANGLE_SPOT.CORNER_BOTTOM_RIGHT: Corner = OldVertices[3]; OppositeCorner = OldVertices[0]; break;	// Keep top left fixed
                                }

                                // Move corner
                                Corner += DeltaUV;

                                // Compute new center
                                CurCenter = new ImageUtility.float2( 0.5f * (Corner.x + OppositeCorner.x), 0.5f * (Corner.y + OppositeCorner.y) );

                                // Compute new size
                                CurHalfSize = new ImageUtility.float2( Math.Abs( (Corner - CurCenter).Dot( OldAxisX ) ), Math.Abs( (Corner - CurCenter).Dot( OldAxisY ) ) );

                                break;
                            }

                            case CROP_RECTANGLE_SPOT.ROTATE_TOP_LEFT:
                            case CROP_RECTANGLE_SPOT.ROTATE_TOP_RIGHT:
                            case CROP_RECTANGLE_SPOT.ROTATE_BOTTOM_LEFT:
                            case CROP_RECTANGLE_SPOT.ROTATE_BOTTOM_RIGHT:
                            {
                                ImageUtility.float2	Center2OldPos = OldClientPos - OldClientCenter;
                                ImageUtility.float2	Center2CurPos = CurClientPos - OldClientCenter;

                                float	OldAngle = (float) Math.Atan2( -Center2OldPos.y, Center2OldPos.x );
                                float	CurAngle = (float) Math.Atan2( -Center2CurPos.y, Center2CurPos.x );

                                m_CropRectangleRotation = m_ButtonDownCropRectangleRotation + CurAngle - OldAngle;

                                CurAxisX = new ImageUtility.float2( (float) Math.Cos( m_CropRectangleRotation ), -(float) Math.Sin( m_CropRectangleRotation ) );
                                CurAxisY = new ImageUtility.float2( -(float) Math.Sin( m_CropRectangleRotation ), -(float) Math.Cos( m_CropRectangleRotation ) );
                                break;
                            }

                            case CROP_RECTANGLE_SPOT.CENTER:
                            {
                                ImageUtility.float2	Delta = CurClientPos - OldClientPos;
                                ImageUtility.float2	DeltaUV = (1.0f / ImageRect.Height) * Delta;

                                CurCenter = OldCenter + DeltaUV;
                                break;
                            }
                        }

                        CurHalfSize.x = Math.Max( 1e-3f, CurHalfSize.x );
                        CurHalfSize.y = Math.Max( 1e-3f, CurHalfSize.y );

                        // Constrain vertices to the image
                        ImageUtility.float2[]	Vertices = BuildClipVertices( CurCenter, CurHalfSize, CurAxisX, CurAxisY );

                        float	MinX = 0.5f * (1.0f - ImageAspectRatio);
                        float	MaxX = 0.5f * (1.0f + ImageAspectRatio);

                        for ( int i=0; i < 4; i++ )
                        {
                            bool	Rebuild = false;
                            if ( Vertices[i].x < MinX )
                            {
                                Vertices[i].x = MinX;
                                Rebuild = true;
                            }
                            if ( Vertices[i].x > MaxX )
                            {
                                Vertices[i].x = MaxX;
                                Rebuild = true;
                            }
                            if ( Vertices[i].y < 0.0f )
                            {
                                Vertices[i].y = 0.0f;
                                Rebuild = true;
                            }
                            if ( Vertices[i].y > 1.0f )
                            {
                                Vertices[i].y = 1.0f;
                                Rebuild = true;
                            }
                            if ( !Rebuild )
                                continue;

                            ImageUtility.float2	OppositeVertex = Vertices[3-i];	// This one is fixed

                            // Recompute center & half size
                            CurCenter = 0.5f * (OppositeVertex + Vertices[i]);
                            ImageUtility.float2	Delta = Vertices[i] - OppositeVertex;
                            CurHalfSize = 0.5f * new ImageUtility.float2( Math.Abs( Delta.Dot( CurAxisX ) ), Math.Abs( Delta.Dot( CurAxisY ) ) );

                            // Rebuild new vertices
                            Vertices = BuildClipVertices( CurCenter, CurHalfSize, CurAxisX, CurAxisY );
                        }

                        m_CropRectangleCenter = CurCenter;
                        m_CropRectangleHalfSize = CurHalfSize;

                        // The crop rectangle has changed!
                        m_CropRectangleIsDefault = false;

                        // Repaint to update the crop rectangle
                        Invalidate();
                    }
                    break;
                }

                case MANIPULATION_STATE.CALIBRATION_TARGET:
                {	// Take care of calibration manipulation
                    Cursor = Cursors.Cross;
                    switch ( m_CalibrationStage )
                    {
                        case CALIBRATION_STAGE.PICK_CENTER:
                            m_CalibrationCenter = Client2ImageUV_NoSquareAspectRatio( e.Location );
                            Invalidate();
                            break;

                        case CALIBRATION_STAGE.SET_RADIUS:
                            {
                                ImageUtility.float2	Temp = Client2ImageUV_NoSquareAspectRatio( e.Location );
                                m_CalibrationRadius = (float) Math.Sqrt( (Temp.x - m_CalibrationCenter.x)*(Temp.x - m_CalibrationCenter.x) + (Temp.y - m_CalibrationCenter.y)*(Temp.y - m_CalibrationCenter.y) );
                                Invalidate();
                            }
                            break;
                    }
                    break;
                }

                default:
                    Cursor = Cursors.Default;
                    break;
            }
        }
Example #10
0
        private PointF  ImageUV2Client_NoSquareAspectRatio(ImageUtility.float2 _Position)
        {
            RectangleF ImageRect = ImageClientRect();

            return(new PointF(ImageRect.X + _Position.x * ImageRect.Width, ImageRect.Y + _Position.y * ImageRect.Height));
        }
Example #11
0
        private PointF  ImageUV2Client(ImageUtility.float2 _Position)
        {
            RectangleF ImageRect = ImageClientRect();

            return(new PointF(_Position.x * ImageRect.Width + ImageRect.X, _Position.y * ImageRect.Height + ImageRect.Y));
        }
Example #12
0
        /// <summary>
        /// Captures the calibrated texture
        /// </summary>
        /// <param name="_Source">The source image to capture</param>
        /// <param name="_Database">Database to perform proper calibration</param>
        /// <param name="_Parms">Parameters for the capture</param>
        public void Capture( ImageUtility.Bitmap _Source, CameraCalibrationDatabase _Database, CaptureParms _Parms )
        {
            if ( _Source == null )
                throw new Exception( "Invalid source bitmap to build texture from!" );
            if ( _Database == null )
                throw new Exception( "Invalid calibration database found in parameters!" );
            if ( _Parms == null )
                throw new Exception( "Invalid calibration parameters!" );
            if ( m_SwatchWidth <= 0 || m_SwatchHeight <= 0 )
                throw new Exception( "Invalid swatch size! Must be > 0!" );

            // Save parameters as they're associated to this texture
            m_CaptureParameters = _Parms;
            m_WhiteReflectanceReference = _Database.WhiteReflectanceReference;
            m_WhiteReflectanceCorrectionFactor = _Database.WhiteReflectanceCorrectionFactor;
            m_SpatialCorrectionEnabled = _Database.WhiteReferenceImage != null;

            //////////////////////////////////////////////////////////////////////////
            // Setup the database to find the most appropriate calibration data for our image infos
            _Database.PrepareCalibrationFor( _Parms.ISOSpeed, _Parms.ShutterSpeed, _Parms.Aperture );

            //////////////////////////////////////////////////////////////////////////
            // Build target texture
            ImageUtility.float4	AvgXYZ = new ImageUtility.float4( 0, 0, 0, 0 );
            //DEBUG
            // float	MinLuminance_Raw = float.MaxValue;
            // float	MaxLuminance_Raw = -float.MaxValue;

            const int	EXTREME_VALUES_COUNT = 100;
            ImageUtility.float3[]	ArrayMin = new ImageUtility.float3[EXTREME_VALUES_COUNT];
            ImageUtility.float3[]	ArrayMax = new ImageUtility.float3[EXTREME_VALUES_COUNT];
            for ( int i=0; i < EXTREME_VALUES_COUNT; i++ )
            {
                ArrayMin[i] = new ImageUtility.float3( 0, 1, 0 );
                ArrayMax[i] = new ImageUtility.float3( 0, 0, 0 );
            }

            if ( _Parms.CropSource )
            {
                float	fImageWidth = 2.0f * _Parms.CropRectangleHalfSize.x * _Source.Height;
                float	fImageHeight = 2.0f * _Parms.CropRectangleHalfSize.y * _Source.Height;
                int		W = (int) Math.Floor( fImageWidth );
                int		H = (int) Math.Floor( fImageHeight );

                ImageUtility.float2	AxisX = new ImageUtility.float2( (float) Math.Cos( _Parms.CropRectangleRotation ), -(float) Math.Sin( _Parms.CropRectangleRotation ) );
                ImageUtility.float2	AxisY = new ImageUtility.float2( (float) Math.Sin( _Parms.CropRectangleRotation ), (float) Math.Cos( _Parms.CropRectangleRotation ) );

                ImageUtility.float2	TopLeftCorner = new ImageUtility.float2( 0.5f * (_Source.Width - _Source.Height) + _Parms.CropRectangleCenter.x * _Source.Height, _Source.Height * _Parms.CropRectangleCenter.y )
                                                  + _Source.Height * (-_Parms.CropRectangleHalfSize.x * AxisX - _Parms.CropRectangleHalfSize.y * AxisY);

                m_Texture = new ImageUtility.Bitmap( W, H, new ImageUtility.ColorProfile( ImageUtility.ColorProfile.STANDARD_PROFILE.sRGB ) );
                ImageUtility.float4	XYZ;
                ImageUtility.float3	ShortXYZ;
                ImageUtility.float3	xyY;

                ImageUtility.float2	CurrentScanlinePixel = TopLeftCorner + 0.5f * (fImageWidth - W) * AxisX + 0.5f * (fImageHeight - H) * AxisY;
                if ( Math.Abs( _Parms.CropRectangleRotation ) < 1e-6f )
                {	// Use integer pixels to avoid attenuated values due to bilinear filtering
                    CurrentScanlinePixel.x = (float) Math.Floor( CurrentScanlinePixel.x );
                    CurrentScanlinePixel.y = (float) Math.Floor( CurrentScanlinePixel.y );
                }
                for ( int Y=0; Y < H; Y++ )
                {
                    ImageUtility.float2	CurrentPixel = CurrentScanlinePixel;
                    for ( int X=0; X < W; X++ )
                    {
                        float	U = CurrentPixel.x / _Source.Width;
                        float	V = CurrentPixel.y / _Source.Height;

                        XYZ = _Source.BilinearSample( CurrentPixel.x, CurrentPixel.y );

            //DEBUG
            // float	L = XYZ.y * _Database.GetSpatialLuminanceCorrectionFactor( U, V );
            // if ( L < MinLuminance_Raw )
            // 	MinLuminance_Raw = L;
            // if ( L > MaxLuminance_Raw )
            // 	MaxLuminance_Raw = L;
            //DEBUG

                        xyY = ImageUtility.ColorProfile.XYZ2xyY( (ImageUtility.float3) XYZ );
                        xyY = _Database.CalibrateWithSpatialCorrection( U, V, xyY );	// Apply luminance calibration
                        ShortXYZ = ImageUtility.ColorProfile.xyY2XYZ( xyY );
                        XYZ = new ImageUtility.float4( ShortXYZ, XYZ.w );
                        m_Texture.ContentXYZ[X,Y] = XYZ;

                        // Update min/max/avg values
                        InsertMinMax( ShortXYZ, ArrayMin, ArrayMax, EXTREME_VALUES_COUNT );
                        AvgXYZ += XYZ;

                        CurrentPixel += AxisX;
                    }
                    CurrentScanlinePixel += AxisY;
                }
            }
            else
            {	// Simple texture copy, with luminance calibration
                m_Texture = new ImageUtility.Bitmap( _Source.Width, _Source.Height, new ImageUtility.ColorProfile( ImageUtility.ColorProfile.STANDARD_PROFILE.sRGB ) );
                ImageUtility.float4	XYZ;
                ImageUtility.float3	ShortXYZ;
                ImageUtility.float3	xyY;

                int	W = m_Texture.Width;
                int	H = m_Texture.Height;

                int	X0 = 0;
                int	X1 = W;
                int	Y0 = 0;
                int	Y1 = H;

            //DEBUG
            // X0 = 1088; Y0 = 764;
            // X1 = X0 + 1100; Y1 = Y0 + 632;

                for ( int Y=Y0; Y < Y1; Y++ )
                {
                    float	V = (float) Y / H;
                    for ( int X=X0; X < X1; X++ )
                    {
                        float	U = (float) X / W;

                        XYZ = _Source.ContentXYZ[X,Y];

            //DEBUG
            // float	L = XYZ.y * _Database.GetSpatialLuminanceCorrectionFactor( U, V );
            // if ( L < MinLuminance_Raw )
            // 	MinLuminance_Raw = L;
            // if ( L > MaxLuminance_Raw )
            // 	MaxLuminance_Raw = L;
            //DEBUG

                        xyY = ImageUtility.ColorProfile.XYZ2xyY( (ImageUtility.float3) XYZ );
                        xyY = _Database.CalibrateWithSpatialCorrection( U, V, xyY );	// Apply luminance calibration
                        ShortXYZ = ImageUtility.ColorProfile.xyY2XYZ( xyY );
                        XYZ = new ImageUtility.float4( ShortXYZ, XYZ.w );
                        m_Texture.ContentXYZ[X,Y] = XYZ;

                        // Update min/max/avg values
                        InsertMinMax( ShortXYZ, ArrayMin, ArrayMax, EXTREME_VALUES_COUNT );
                        AvgXYZ += XYZ;
                    }
                }
            }

            // Normalize average swatch color
            float	Normalizer = 1.0f / (m_Texture.Width*m_Texture.Height);
            ImageUtility.float3	avgxyY = ImageUtility.ColorProfile.XYZ2xyY( Normalizer * ((ImageUtility.float3) AvgXYZ) );
            m_SwatchAvg.xyY = avgxyY;

            // Compute min & max using statistical norm
             			ImageUtility.float3	BestXYZ_Min;
             			ImageUtility.float3	BestXYZ_Max;

            if ( _Parms.UseModeInsteadOfMean )
            {	// Use mode
                BestXYZ_Min = ComputeMode( ArrayMin );
                BestXYZ_Max = ComputeMode( ArrayMax );
            }
            else
            {	// Use mean
             				BestXYZ_Min = ComputeMean( ArrayMin );
             				BestXYZ_Max = ComputeMean( ArrayMax );
            }
            m_SwatchMin.xyY = ImageUtility.ColorProfile.XYZ2xyY( BestXYZ_Min );
            m_SwatchMax.xyY = ImageUtility.ColorProfile.XYZ2xyY( BestXYZ_Max );

            m_SwatchMin.Texture = BuildSwatch( m_SwatchWidth, m_SwatchHeight, m_SwatchMin.xyY );
            m_SwatchMax.Texture = BuildSwatch( m_SwatchWidth, m_SwatchHeight, m_SwatchMax.xyY );
            m_SwatchAvg.Texture = BuildSwatch( m_SwatchWidth, m_SwatchHeight, m_SwatchAvg.xyY );

            // Rebuild custom swatches
            foreach ( CustomSwatch CS in m_CustomSwatches )
                CS.Texture = BuildSwatch( m_SwatchWidth, m_SwatchHeight, CS.xyY );

            //////////////////////////////////////////////////////////////////////////
            // Feed some purely informational shot infos to the main texture, probably won't be saved anyway...
            m_Texture.HasValidShotInfo = true;
            m_Texture.ISOSpeed = _Parms.ISOSpeed;
            m_Texture.ShutterSpeed = _Parms.ShutterSpeed;
            m_Texture.Aperture = _Parms.Aperture;
        }
        /// <summary>
        /// Prepares the interpolated calibration table to process the pixels in an image shot with the specified shot infos
        /// </summary>
        /// <param name="_ISOSpeed"></param>
        /// <param name="_ShutterSpeed"></param>
        /// <param name="_Aperture"></param>
        public void PrepareCalibrationFor( float _ISOSpeed, float _ShutterSpeed, float _Aperture )
        {
            if ( m_RootNode == null )
                throw new Exception( "Calibration grid hasn't been built: did you provide a valid database path? Does the path contain camera calibration data?" );

            if ( IsPreparedFor( _ISOSpeed, _ShutterSpeed, _Aperture ) )
                return;	// Already prepared!

            //////////////////////////////////////////////////////////////////////////
            // Find the 8 nodes encompassing our values
            // I'm making the delicate assumption that, although the starting node is chosen on the
            //	condition its EV values are strictly inferior to the target we're looking for, all
            //	neighbor nodes should satisfy the condition they're properly placed.
            //
            // This is true for the direct neighbors +X, +Y, +Z that are immediately above target values
            //	but for example, neighbor (+X +Y) may have a very bad aperture value (Z) that may be
            //	above the target aperture...
            //
            // Let's hope the user won't provide too fancy calibrations...
            // (anyway, interpolants are clamped in [0,1] so there's no risk of overshooting)
            //
            ImageUtility.float3	EV;
            GridNode.Convert2EV( _ISOSpeed, _ShutterSpeed, _Aperture, out EV.x, out EV.y, out EV.z );

            // Find the start node
            GridNode		StartNode = FindStartNode( EV.x, EV.y, EV.z );
            m_InterpolationStartNode = StartNode;

            // Build the 8 grid nodes from it
            GridNode[,,]	Grid = new GridNode[2,2,2];
            Grid[0,0,0] = StartNode;
            Grid[1,0,0] = StartNode.m_Neighbors[0][1] != null ? StartNode.m_Neighbors[0][1] : StartNode;		// +X
            Grid[0,1,0] = StartNode.m_Neighbors[1][1] != null ? StartNode.m_Neighbors[1][1] : StartNode;		// +Y
            Grid[0,0,1] = StartNode.m_Neighbors[2][1] != null ? StartNode.m_Neighbors[2][1] : StartNode;		// +Z
            Grid[1,1,0] = Grid[1,0,0].m_Neighbors[1][1] != null ? Grid[1,0,0].m_Neighbors[1][1] : Grid[1,0,0];	// +X +Y
            Grid[0,1,1] = Grid[0,1,0].m_Neighbors[2][1] != null ? Grid[0,1,0].m_Neighbors[2][1] : Grid[0,1,0];	// +Y +Z
            Grid[1,0,1] = Grid[0,0,1].m_Neighbors[0][1] != null ? Grid[0,0,1].m_Neighbors[0][1] : Grid[0,0,1];	// +X +Z
            Grid[1,1,1] = Grid[1,1,0].m_Neighbors[2][1] != null ? Grid[1,1,0].m_Neighbors[2][1] : Grid[1,1,0];	// +X +Y +Z

            //////////////////////////////////////////////////////////////////////////
            // Create the successive interpolants for trilinear interpolation
            //
            // Assume we interpolate on X first (ISO speed), so we need 4 distinct values
            ImageUtility.float4	tX = new ImageUtility.float4(
                    Math.Max( 0.0f, Math.Min( 1.0f, (EV.x - Grid[0,0,0].m_EV_ISOSpeed) / Math.Max( 1e-6f, Grid[1,0,0].m_EV_ISOSpeed - Grid[0,0,0].m_EV_ISOSpeed) ) ),	// Y=0 Z=0
                    Math.Max( 0.0f, Math.Min( 1.0f, (EV.x - Grid[0,1,0].m_EV_ISOSpeed) / Math.Max( 1e-6f, Grid[1,1,0].m_EV_ISOSpeed - Grid[0,1,0].m_EV_ISOSpeed) ) ),	// Y=1 Z=0
                    Math.Max( 0.0f, Math.Min( 1.0f, (EV.x - Grid[0,0,1].m_EV_ISOSpeed) / Math.Max( 1e-6f, Grid[1,0,1].m_EV_ISOSpeed - Grid[0,0,1].m_EV_ISOSpeed) ) ),	// Y=0 Z=1
                    Math.Max( 0.0f, Math.Min( 1.0f, (EV.x - Grid[0,1,1].m_EV_ISOSpeed) / Math.Max( 1e-6f, Grid[1,1,1].m_EV_ISOSpeed - Grid[0,1,1].m_EV_ISOSpeed) ) )	// Y=1 Z=1
                );
            ImageUtility.float4	rX = new ImageUtility.float4( 1.0f - tX.x, 1.0f - tX.y, 1.0f - tX.z, 1.0f - tX.w );

                // Compute the 4 interpolated shutter speeds & apertures
            ImageUtility.float4	ShutterSpeedsX = new ImageUtility.float4(
                    rX.x * Grid[0,0,0].m_EV_ShutterSpeed + tX.x * Grid[1,0,0].m_EV_ShutterSpeed,	// Y=0 Z=0
                    rX.y * Grid[0,1,0].m_EV_ShutterSpeed + tX.y * Grid[1,1,0].m_EV_ShutterSpeed,	// Y=1 Z=0
                    rX.z * Grid[0,0,1].m_EV_ShutterSpeed + tX.z * Grid[1,0,1].m_EV_ShutterSpeed,	// Y=0 Z=1
                    rX.w * Grid[0,1,1].m_EV_ShutterSpeed + tX.w * Grid[1,1,1].m_EV_ShutterSpeed		// Y=1 Z=1
                );
            ImageUtility.float4	AperturesX = new ImageUtility.float4(
                    rX.x * Grid[0,0,0].m_EV_Aperture + tX.x * Grid[1,0,0].m_EV_Aperture,			// Y=0 Z=0
                    rX.y * Grid[0,1,0].m_EV_Aperture + tX.y * Grid[1,1,0].m_EV_Aperture,			// Y=1 Z=0
                    rX.z * Grid[0,0,1].m_EV_Aperture + tX.z * Grid[1,0,1].m_EV_Aperture,			// Y=0 Z=1
                    rX.w * Grid[0,1,1].m_EV_Aperture + tX.w * Grid[1,1,1].m_EV_Aperture				// Y=1 Z=1
                );

            // Next we interpolate on Y (Shutter speed), so we need 2 distinct values
            ImageUtility.float2	tY = new ImageUtility.float2(
                    Math.Max( 0.0f, Math.Min( 1.0f, (EV.y - ShutterSpeedsX.x) / Math.Max( 1e-6f, ShutterSpeedsX.y - ShutterSpeedsX.x) ) ),	// Z=0
                    Math.Max( 0.0f, Math.Min( 1.0f, (EV.y - ShutterSpeedsX.z) / Math.Max( 1e-6f, ShutterSpeedsX.w - ShutterSpeedsX.z) ) )	// Z=1
                );
            ImageUtility.float2	rY = new ImageUtility.float2( 1.0f - tY.x, 1.0f - tY.y );

                // Compute the 2 apertures
            ImageUtility.float2	AperturesY = new ImageUtility.float2(
                    rY.x * AperturesX.x + tY.x * AperturesX.y,
                    rY.y * AperturesX.z + tY.y * AperturesX.w
                );

            // Finally, we interpolate on Z (Aperture), we need only 1 single value
            float	tZ = Math.Max( 0.0f, Math.Min( 1.0f, (EV.z - AperturesY.x) / Math.Max( 1e-6f, AperturesY.y - AperturesY.x) ) );
            float	rZ = 1.0f - tZ;

            //////////////////////////////////////////////////////////////////////////
            // Create the special camera calibration that is the result of the interpolation of the 8 nearest ones in the grid
            m_InterpolatedCalibration = new CameraCalibration();
            m_InterpolatedCalibration.m_CameraShotInfos.m_ISOSpeed = _ISOSpeed;
            m_InterpolatedCalibration.m_CameraShotInfos.m_ShutterSpeed = _ShutterSpeed;
            m_InterpolatedCalibration.m_CameraShotInfos.m_Aperture = _Aperture;

            for ( int ProbeIndex=0; ProbeIndex < REQUIRED_PROBES_COUNT; ProbeIndex++ )
            {
                CameraCalibration.Probe TargetProbe = m_InterpolatedCalibration.m_Reflectances[ProbeIndex];

                float	L000 = Grid[0,0,0].m_CameraCalibration.m_Reflectances[ProbeIndex].m_LuminanceMeasured;
                float	L100 = Grid[1,0,0].m_CameraCalibration.m_Reflectances[ProbeIndex].m_LuminanceMeasured;
                float	L010 = Grid[0,1,0].m_CameraCalibration.m_Reflectances[ProbeIndex].m_LuminanceMeasured;
                float	L110 = Grid[1,1,0].m_CameraCalibration.m_Reflectances[ProbeIndex].m_LuminanceMeasured;
                float	L001 = Grid[0,0,1].m_CameraCalibration.m_Reflectances[ProbeIndex].m_LuminanceMeasured;
                float	L101 = Grid[1,0,1].m_CameraCalibration.m_Reflectances[ProbeIndex].m_LuminanceMeasured;
                float	L011 = Grid[0,1,1].m_CameraCalibration.m_Reflectances[ProbeIndex].m_LuminanceMeasured;
                float	L111 = Grid[1,1,1].m_CameraCalibration.m_Reflectances[ProbeIndex].m_LuminanceMeasured;

                // Interpolate on X (ISO speed)
                float	L00 = rX.x * L000 + tX.x * L100;
                float	L10 = rX.y * L010 + tX.y * L110;
                float	L01 = rX.z * L001 + tX.z * L101;
                float	L11 = rX.w * L011 + tX.w * L111;

                // Interpolate on Y (shutter speed)
                float	L0 = rY.x * L00 + tY.x * L10;
                float	L1 = rY.y * L01 + tY.y * L11;

                // Interpolate on Z (aperture)
                float	L = rZ * L0 + tZ * L1;

                TargetProbe.m_IsAvailable = true;
                TargetProbe.m_LuminanceMeasured = L;
            }

            // Fill missing values
            m_InterpolatedCalibration.UpdateAllLuminances();

            // Reset white reflectance reference because it was set for another setup
            WhiteReflectanceReference = new ImageUtility.float3( 0, 0, -1 );
        }
Example #14
0
        /// <summary>
        /// Captures the calibrated texture
        /// </summary>
        /// <param name="_Source">The source image to capture</param>
        /// <param name="_Database">Database to perform proper calibration</param>
        /// <param name="_Parms">Parameters for the capture</param>
        public void             Capture(ImageUtility.Bitmap _Source, CameraCalibrationDatabase _Database, CaptureParms _Parms)
        {
            if (_Source == null)
            {
                throw new Exception("Invalid source bitmap to build texture from!");
            }
            if (_Database == null)
            {
                throw new Exception("Invalid calibration database found in parameters!");
            }
            if (_Parms == null)
            {
                throw new Exception("Invalid calibration parameters!");
            }
            if (m_SwatchWidth <= 0 || m_SwatchHeight <= 0)
            {
                throw new Exception("Invalid swatch size! Must be > 0!");
            }

            // Save parameters as they're associated to this texture
            m_CaptureParameters                = _Parms;
            m_WhiteReflectanceReference        = _Database.WhiteReflectanceReference;
            m_WhiteReflectanceCorrectionFactor = _Database.WhiteReflectanceCorrectionFactor;
            m_SpatialCorrectionEnabled         = _Database.WhiteReferenceImage != null;

            //////////////////////////////////////////////////////////////////////////
            // Setup the database to find the most appropriate calibration data for our image infos
            _Database.PrepareCalibrationFor(_Parms.ISOSpeed, _Parms.ShutterSpeed, _Parms.Aperture);


            //////////////////////////////////////////////////////////////////////////
            // Build target texture
            ImageUtility.float4 AvgXYZ = new ImageUtility.float4(0, 0, 0, 0);
//DEBUG
// float	MinLuminance_Raw = float.MaxValue;
// float	MaxLuminance_Raw = -float.MaxValue;

            const int EXTREME_VALUES_COUNT = 100;

            ImageUtility.float3[] ArrayMin = new ImageUtility.float3[EXTREME_VALUES_COUNT];
            ImageUtility.float3[] ArrayMax = new ImageUtility.float3[EXTREME_VALUES_COUNT];
            for (int i = 0; i < EXTREME_VALUES_COUNT; i++)
            {
                ArrayMin[i] = new ImageUtility.float3(0, 1, 0);
                ArrayMax[i] = new ImageUtility.float3(0, 0, 0);
            }

            if (_Parms.CropSource)
            {
                float fImageWidth  = 2.0f * _Parms.CropRectangleHalfSize.x * _Source.Height;
                float fImageHeight = 2.0f * _Parms.CropRectangleHalfSize.y * _Source.Height;
                int   W            = (int)Math.Floor(fImageWidth);
                int   H            = (int)Math.Floor(fImageHeight);

                ImageUtility.float2 AxisX = new ImageUtility.float2((float)Math.Cos(_Parms.CropRectangleRotation), -(float)Math.Sin(_Parms.CropRectangleRotation));
                ImageUtility.float2 AxisY = new ImageUtility.float2((float)Math.Sin(_Parms.CropRectangleRotation), (float)Math.Cos(_Parms.CropRectangleRotation));

                ImageUtility.float2 TopLeftCorner = new ImageUtility.float2(0.5f * (_Source.Width - _Source.Height) + _Parms.CropRectangleCenter.x * _Source.Height, _Source.Height * _Parms.CropRectangleCenter.y)
                                                    + _Source.Height * (-_Parms.CropRectangleHalfSize.x * AxisX - _Parms.CropRectangleHalfSize.y * AxisY);

                m_Texture = new ImageUtility.Bitmap(W, H, new ImageUtility.ColorProfile(ImageUtility.ColorProfile.STANDARD_PROFILE.sRGB));
                ImageUtility.float4 XYZ;
                ImageUtility.float3 ShortXYZ;
                ImageUtility.float3 xyY;

                ImageUtility.float2 CurrentScanlinePixel = TopLeftCorner + 0.5f * (fImageWidth - W) * AxisX + 0.5f * (fImageHeight - H) * AxisY;
                if (Math.Abs(_Parms.CropRectangleRotation) < 1e-6f)
                {                       // Use integer pixels to avoid attenuated values due to bilinear filtering
                    CurrentScanlinePixel.x = (float)Math.Floor(CurrentScanlinePixel.x);
                    CurrentScanlinePixel.y = (float)Math.Floor(CurrentScanlinePixel.y);
                }
                for (int Y = 0; Y < H; Y++)
                {
                    ImageUtility.float2 CurrentPixel = CurrentScanlinePixel;
                    for (int X = 0; X < W; X++)
                    {
                        float U = CurrentPixel.x / _Source.Width;
                        float V = CurrentPixel.y / _Source.Height;

                        XYZ = _Source.BilinearSample(CurrentPixel.x, CurrentPixel.y);

//DEBUG
// float	L = XYZ.y * _Database.GetSpatialLuminanceCorrectionFactor( U, V );
// if ( L < MinLuminance_Raw )
//  MinLuminance_Raw = L;
// if ( L > MaxLuminance_Raw )
//  MaxLuminance_Raw = L;
//DEBUG

                        xyY      = ImageUtility.ColorProfile.XYZ2xyY((ImageUtility.float3)XYZ);
                        xyY      = _Database.CalibrateWithSpatialCorrection(U, V, xyY);                         // Apply luminance calibration
                        ShortXYZ = ImageUtility.ColorProfile.xyY2XYZ(xyY);
                        XYZ      = new ImageUtility.float4(ShortXYZ, XYZ.w);
                        m_Texture.ContentXYZ[X, Y] = XYZ;

                        // Update min/max/avg values
                        InsertMinMax(ShortXYZ, ArrayMin, ArrayMax, EXTREME_VALUES_COUNT);
                        AvgXYZ += XYZ;

                        CurrentPixel += AxisX;
                    }
                    CurrentScanlinePixel += AxisY;
                }
            }
            else
            {                   // Simple texture copy, with luminance calibration
                m_Texture = new ImageUtility.Bitmap(_Source.Width, _Source.Height, new ImageUtility.ColorProfile(ImageUtility.ColorProfile.STANDARD_PROFILE.sRGB));
                ImageUtility.float4 XYZ;
                ImageUtility.float3 ShortXYZ;
                ImageUtility.float3 xyY;

                int W = m_Texture.Width;
                int H = m_Texture.Height;

                int X0 = 0;
                int X1 = W;
                int Y0 = 0;
                int Y1 = H;

//DEBUG
// X0 = 1088; Y0 = 764;
// X1 = X0 + 1100; Y1 = Y0 + 632;

                for (int Y = Y0; Y < Y1; Y++)
                {
                    float V = (float)Y / H;
                    for (int X = X0; X < X1; X++)
                    {
                        float U = (float)X / W;

                        XYZ = _Source.ContentXYZ[X, Y];

//DEBUG
// float	L = XYZ.y * _Database.GetSpatialLuminanceCorrectionFactor( U, V );
// if ( L < MinLuminance_Raw )
//  MinLuminance_Raw = L;
// if ( L > MaxLuminance_Raw )
//  MaxLuminance_Raw = L;
//DEBUG

                        xyY      = ImageUtility.ColorProfile.XYZ2xyY((ImageUtility.float3)XYZ);
                        xyY      = _Database.CalibrateWithSpatialCorrection(U, V, xyY);                         // Apply luminance calibration
                        ShortXYZ = ImageUtility.ColorProfile.xyY2XYZ(xyY);
                        XYZ      = new ImageUtility.float4(ShortXYZ, XYZ.w);
                        m_Texture.ContentXYZ[X, Y] = XYZ;

                        // Update min/max/avg values
                        InsertMinMax(ShortXYZ, ArrayMin, ArrayMax, EXTREME_VALUES_COUNT);
                        AvgXYZ += XYZ;
                    }
                }
            }

            // Normalize average swatch color
            float Normalizer = 1.0f / (m_Texture.Width * m_Texture.Height);

            ImageUtility.float3 avgxyY = ImageUtility.ColorProfile.XYZ2xyY(Normalizer * ((ImageUtility.float3)AvgXYZ));
            m_SwatchAvg.xyY = avgxyY;

            // Compute min & max using statistical norm
            ImageUtility.float3 BestXYZ_Min;
            ImageUtility.float3 BestXYZ_Max;

            if (_Parms.UseModeInsteadOfMean)
            {                   // Use mode
                BestXYZ_Min = ComputeMode(ArrayMin);
                BestXYZ_Max = ComputeMode(ArrayMax);
            }
            else
            {                   // Use mean
                BestXYZ_Min = ComputeMean(ArrayMin);
                BestXYZ_Max = ComputeMean(ArrayMax);
            }
            m_SwatchMin.xyY = ImageUtility.ColorProfile.XYZ2xyY(BestXYZ_Min);
            m_SwatchMax.xyY = ImageUtility.ColorProfile.XYZ2xyY(BestXYZ_Max);

            m_SwatchMin.Texture = BuildSwatch(m_SwatchWidth, m_SwatchHeight, m_SwatchMin.xyY);
            m_SwatchMax.Texture = BuildSwatch(m_SwatchWidth, m_SwatchHeight, m_SwatchMax.xyY);
            m_SwatchAvg.Texture = BuildSwatch(m_SwatchWidth, m_SwatchHeight, m_SwatchAvg.xyY);

            // Rebuild custom swatches
            foreach (CustomSwatch CS in m_CustomSwatches)
            {
                CS.Texture = BuildSwatch(m_SwatchWidth, m_SwatchHeight, CS.xyY);
            }

            //////////////////////////////////////////////////////////////////////////
            // Feed some purely informational shot infos to the main texture, probably won't be saved anyway...
            m_Texture.HasValidShotInfo = true;
            m_Texture.ISOSpeed         = _Parms.ISOSpeed;
            m_Texture.ShutterSpeed     = _Parms.ShutterSpeed;
            m_Texture.Aperture         = _Parms.Aperture;
        }
Example #15
0
        /// <summary>
        /// Prepares the interpolated calibration table to process the pixels in an image shot with the specified shot infos
        /// </summary>
        /// <param name="_ISOSpeed"></param>
        /// <param name="_ShutterSpeed"></param>
        /// <param name="_Aperture"></param>
        public void     PrepareCalibrationFor(float _ISOSpeed, float _ShutterSpeed, float _Aperture)
        {
            if (m_RootNode == null)
            {
                throw new Exception("Calibration grid hasn't been built: did you provide a valid database path? Does the path contain camera calibration data?");
            }

            if (IsPreparedFor(_ISOSpeed, _ShutterSpeed, _Aperture))
            {
                return;                 // Already prepared!
            }
            //////////////////////////////////////////////////////////////////////////
            // Find the 8 nodes encompassing our values
            // I'm making the delicate assumption that, although the starting node is chosen on the
            //	condition its EV values are strictly inferior to the target we're looking for, all
            //	neighbor nodes should satisfy the condition they're properly placed.
            //
            // This is true for the direct neighbors +X, +Y, +Z that are immediately above target values
            //	but for example, neighbor (+X +Y) may have a very bad aperture value (Z) that may be
            //	above the target aperture...
            //
            // Let's hope the user won't provide too fancy calibrations...
            // (anyway, interpolants are clamped in [0,1] so there's no risk of overshooting)
            //
            ImageUtility.float3 EV;
            GridNode.Convert2EV(_ISOSpeed, _ShutterSpeed, _Aperture, out EV.x, out EV.y, out EV.z);

            // Find the start node
            GridNode StartNode = FindStartNode(EV.x, EV.y, EV.z);

            m_InterpolationStartNode = StartNode;

            // Build the 8 grid nodes from it
            GridNode[,,]    Grid = new GridNode[2, 2, 2];
            Grid[0, 0, 0]        = StartNode;
            Grid[1, 0, 0]        = StartNode.m_Neighbors[0][1] != null ? StartNode.m_Neighbors[0][1] : StartNode;               // +X
            Grid[0, 1, 0]        = StartNode.m_Neighbors[1][1] != null ? StartNode.m_Neighbors[1][1] : StartNode;               // +Y
            Grid[0, 0, 1]        = StartNode.m_Neighbors[2][1] != null ? StartNode.m_Neighbors[2][1] : StartNode;               // +Z
            Grid[1, 1, 0]        = Grid[1, 0, 0].m_Neighbors[1][1] != null ? Grid[1, 0, 0].m_Neighbors[1][1] : Grid[1, 0, 0];   // +X +Y
            Grid[0, 1, 1]        = Grid[0, 1, 0].m_Neighbors[2][1] != null ? Grid[0, 1, 0].m_Neighbors[2][1] : Grid[0, 1, 0];   // +Y +Z
            Grid[1, 0, 1]        = Grid[0, 0, 1].m_Neighbors[0][1] != null ? Grid[0, 0, 1].m_Neighbors[0][1] : Grid[0, 0, 1];   // +X +Z
            Grid[1, 1, 1]        = Grid[1, 1, 0].m_Neighbors[2][1] != null ? Grid[1, 1, 0].m_Neighbors[2][1] : Grid[1, 1, 0];   // +X +Y +Z

            //////////////////////////////////////////////////////////////////////////
            // Create the successive interpolants for trilinear interpolation
            //
            // Assume we interpolate on X first (ISO speed), so we need 4 distinct values
            ImageUtility.float4 tX = new ImageUtility.float4(
                Math.Max(0.0f, Math.Min(1.0f, (EV.x - Grid[0, 0, 0].m_EV_ISOSpeed) / Math.Max(1e-6f, Grid[1, 0, 0].m_EV_ISOSpeed - Grid[0, 0, 0].m_EV_ISOSpeed))),                              // Y=0 Z=0
                Math.Max(0.0f, Math.Min(1.0f, (EV.x - Grid[0, 1, 0].m_EV_ISOSpeed) / Math.Max(1e-6f, Grid[1, 1, 0].m_EV_ISOSpeed - Grid[0, 1, 0].m_EV_ISOSpeed))),                              // Y=1 Z=0
                Math.Max(0.0f, Math.Min(1.0f, (EV.x - Grid[0, 0, 1].m_EV_ISOSpeed) / Math.Max(1e-6f, Grid[1, 0, 1].m_EV_ISOSpeed - Grid[0, 0, 1].m_EV_ISOSpeed))),                              // Y=0 Z=1
                Math.Max(0.0f, Math.Min(1.0f, (EV.x - Grid[0, 1, 1].m_EV_ISOSpeed) / Math.Max(1e-6f, Grid[1, 1, 1].m_EV_ISOSpeed - Grid[0, 1, 1].m_EV_ISOSpeed)))                               // Y=1 Z=1
                );
            ImageUtility.float4 rX = new ImageUtility.float4(1.0f - tX.x, 1.0f - tX.y, 1.0f - tX.z, 1.0f - tX.w);

            // Compute the 4 interpolated shutter speeds & apertures
            ImageUtility.float4 ShutterSpeedsX = new ImageUtility.float4(
                rX.x * Grid[0, 0, 0].m_EV_ShutterSpeed + tX.x * Grid[1, 0, 0].m_EV_ShutterSpeed,                        // Y=0 Z=0
                rX.y * Grid[0, 1, 0].m_EV_ShutterSpeed + tX.y * Grid[1, 1, 0].m_EV_ShutterSpeed,                        // Y=1 Z=0
                rX.z * Grid[0, 0, 1].m_EV_ShutterSpeed + tX.z * Grid[1, 0, 1].m_EV_ShutterSpeed,                        // Y=0 Z=1
                rX.w * Grid[0, 1, 1].m_EV_ShutterSpeed + tX.w * Grid[1, 1, 1].m_EV_ShutterSpeed                         // Y=1 Z=1
                );
            ImageUtility.float4 AperturesX = new ImageUtility.float4(
                rX.x * Grid[0, 0, 0].m_EV_Aperture + tX.x * Grid[1, 0, 0].m_EV_Aperture,                                        // Y=0 Z=0
                rX.y * Grid[0, 1, 0].m_EV_Aperture + tX.y * Grid[1, 1, 0].m_EV_Aperture,                                        // Y=1 Z=0
                rX.z * Grid[0, 0, 1].m_EV_Aperture + tX.z * Grid[1, 0, 1].m_EV_Aperture,                                        // Y=0 Z=1
                rX.w * Grid[0, 1, 1].m_EV_Aperture + tX.w * Grid[1, 1, 1].m_EV_Aperture                                         // Y=1 Z=1
                );

            // Next we interpolate on Y (Shutter speed), so we need 2 distinct values
            ImageUtility.float2 tY = new ImageUtility.float2(
                Math.Max(0.0f, Math.Min(1.0f, (EV.y - ShutterSpeedsX.x) / Math.Max(1e-6f, ShutterSpeedsX.y - ShutterSpeedsX.x))),                               // Z=0
                Math.Max(0.0f, Math.Min(1.0f, (EV.y - ShutterSpeedsX.z) / Math.Max(1e-6f, ShutterSpeedsX.w - ShutterSpeedsX.z)))                                // Z=1
                );
            ImageUtility.float2 rY = new ImageUtility.float2(1.0f - tY.x, 1.0f - tY.y);

            // Compute the 2 apertures
            ImageUtility.float2 AperturesY = new ImageUtility.float2(
                rY.x * AperturesX.x + tY.x * AperturesX.y,
                rY.y * AperturesX.z + tY.y * AperturesX.w
                );

            // Finally, we interpolate on Z (Aperture), we need only 1 single value
            float tZ = Math.Max(0.0f, Math.Min(1.0f, (EV.z - AperturesY.x) / Math.Max(1e-6f, AperturesY.y - AperturesY.x)));
            float rZ = 1.0f - tZ;


            //////////////////////////////////////////////////////////////////////////
            // Create the special camera calibration that is the result of the interpolation of the 8 nearest ones in the grid
            m_InterpolatedCalibration = new CameraCalibration();
            m_InterpolatedCalibration.m_CameraShotInfos.m_ISOSpeed     = _ISOSpeed;
            m_InterpolatedCalibration.m_CameraShotInfos.m_ShutterSpeed = _ShutterSpeed;
            m_InterpolatedCalibration.m_CameraShotInfos.m_Aperture     = _Aperture;

            for (int ProbeIndex = 0; ProbeIndex < REQUIRED_PROBES_COUNT; ProbeIndex++)
            {
                CameraCalibration.Probe TargetProbe = m_InterpolatedCalibration.m_Reflectances[ProbeIndex];

                float L000 = Grid[0, 0, 0].m_CameraCalibration.m_Reflectances[ProbeIndex].m_LuminanceMeasured;
                float L100 = Grid[1, 0, 0].m_CameraCalibration.m_Reflectances[ProbeIndex].m_LuminanceMeasured;
                float L010 = Grid[0, 1, 0].m_CameraCalibration.m_Reflectances[ProbeIndex].m_LuminanceMeasured;
                float L110 = Grid[1, 1, 0].m_CameraCalibration.m_Reflectances[ProbeIndex].m_LuminanceMeasured;
                float L001 = Grid[0, 0, 1].m_CameraCalibration.m_Reflectances[ProbeIndex].m_LuminanceMeasured;
                float L101 = Grid[1, 0, 1].m_CameraCalibration.m_Reflectances[ProbeIndex].m_LuminanceMeasured;
                float L011 = Grid[0, 1, 1].m_CameraCalibration.m_Reflectances[ProbeIndex].m_LuminanceMeasured;
                float L111 = Grid[1, 1, 1].m_CameraCalibration.m_Reflectances[ProbeIndex].m_LuminanceMeasured;

                // Interpolate on X (ISO speed)
                float L00 = rX.x * L000 + tX.x * L100;
                float L10 = rX.y * L010 + tX.y * L110;
                float L01 = rX.z * L001 + tX.z * L101;
                float L11 = rX.w * L011 + tX.w * L111;

                // Interpolate on Y (shutter speed)
                float L0 = rY.x * L00 + tY.x * L10;
                float L1 = rY.y * L01 + tY.y * L11;

                // Interpolate on Z (aperture)
                float L = rZ * L0 + tZ * L1;

                TargetProbe.m_IsAvailable       = true;
                TargetProbe.m_LuminanceMeasured = L;
            }

            // Fill missing values
            m_InterpolatedCalibration.UpdateAllLuminances();

            // Reset white reflectance reference because it was set for another setup
            WhiteReflectanceReference = new ImageUtility.float3(0, 0, -1);
        }
Example #16
0
        private void UpdateCropRectangleVertices()
        {
            m_CropRectangleAxisX = new ImageUtility.float2( (float) Math.Cos( m_CropRectangleRotation ), -(float) Math.Sin( m_CropRectangleRotation ) );
            m_CropRectangleAxisY = new ImageUtility.float2( -(float) Math.Sin( m_CropRectangleRotation ), -(float) Math.Cos( m_CropRectangleRotation ) );

            RectangleF	ImageRect = ImageClientRect();

            m_ClientCropRectangleCenter = ImageUV2Client( m_CropRectangleCenter );

            ImageUtility.float2[]	Vertices = new ImageUtility.float2[4] {
                m_CropRectangleCenter - m_CropRectangleHalfSize.x * m_CropRectangleAxisX + m_CropRectangleHalfSize.y * m_CropRectangleAxisY,
                m_CropRectangleCenter + m_CropRectangleHalfSize.x * m_CropRectangleAxisX + m_CropRectangleHalfSize.y * m_CropRectangleAxisY,
                m_CropRectangleCenter - m_CropRectangleHalfSize.x * m_CropRectangleAxisX - m_CropRectangleHalfSize.y * m_CropRectangleAxisY,
                m_CropRectangleCenter + m_CropRectangleHalfSize.x * m_CropRectangleAxisX - m_CropRectangleHalfSize.y * m_CropRectangleAxisY,
            };

            for ( int i=0; i < 4; i++ )
                m_ClientCropRectangleVertices[i] = ImageUV2Client( Vertices[i] );
        }
Example #17
0
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            m_MousePositionCurrent = e.Location;
            if (m_MouseButtonsDown == MouseButtons.None)
            {
                m_ButtonDownMousePosition         = e.Location;
                m_ButtonDownCropRectangleCenter   = m_CropRectangleCenter;
                m_ButtonDownCropRectangleHalfSize = m_CropRectangleHalfSize;
                m_ButtonDownCropRectangleRotation = m_CropRectangleRotation;
                m_ButtonDownCropRectangleAxisX    = m_CropRectangleAxisX;
                m_ButtonDownCropRectangleAxisY    = m_CropRectangleAxisY;

                m_ButtonDownClientCropRectangleCenter   = m_ClientCropRectangleCenter;
                m_ButtonDownClientCropRectangleVertices = new PointF[4] {
                    m_ClientCropRectangleVertices[0],
                    m_ClientCropRectangleVertices[1],
                    m_ClientCropRectangleVertices[2],
                    m_ClientCropRectangleVertices[3],
                };
            }

            switch (m_ManipulationState)
            {
            case MANIPULATION_STATE.CROP_RECTANGLE:
            {                           // Take care of crop rectangle manipulation
                if (!m_CropRectangleManipulationStarted)
                {                       // Update the cursor based on the hovered manipulation spot
                    m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.NONE;

                    const float TOLERANCE = 8.0f;

                    PointF P = e.Location;

                    PointF TopLeftCorner2UV     = new PointF(P.X - m_ClientCropRectangleVertices[0].X, P.Y - m_ClientCropRectangleVertices[0].Y);
                    PointF BottomRightCorner2UV = new PointF(P.X - m_ClientCropRectangleVertices[3].X, P.Y - m_ClientCropRectangleVertices[3].Y);

                    float Distance2Left   = -TopLeftCorner2UV.X * m_CropRectangleAxisX.x - TopLeftCorner2UV.Y * m_CropRectangleAxisX.y;
                    float Distance2Top    = TopLeftCorner2UV.X * m_CropRectangleAxisY.x + TopLeftCorner2UV.Y * m_CropRectangleAxisY.y;
                    float Distance2Right  = BottomRightCorner2UV.X * m_CropRectangleAxisX.x + BottomRightCorner2UV.Y * m_CropRectangleAxisX.y;
                    float Distance2Bottom = -BottomRightCorner2UV.X * m_CropRectangleAxisY.x - BottomRightCorner2UV.Y * m_CropRectangleAxisY.y;

                    // Stretch
                    if (Math.Abs(Distance2Left) < TOLERANCE)
                    {
                        if (Math.Abs(Distance2Top) < TOLERANCE)
                        {
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.CORNER_TOP_LEFT;
                        }
                        else if (Math.Abs(Distance2Bottom) < TOLERANCE)
                        {
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.CORNER_BOTTOM_LEFT;
                        }
                        else
                        {
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.LEFT;
                        }
                    }
                    else if (Math.Abs(Distance2Right) < TOLERANCE)
                    {
                        if (Math.Abs(Distance2Top) < TOLERANCE)
                        {
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.CORNER_TOP_RIGHT;
                        }
                        else if (Math.Abs(Distance2Bottom) < TOLERANCE)
                        {
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.CORNER_BOTTOM_RIGHT;
                        }
                        else
                        {
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.RIGHT;
                        }
                    }
                    else if (Math.Abs(Distance2Top) < TOLERANCE)
                    {
                        m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.TOP;
                    }
                    else if (Math.Abs(Distance2Bottom) < TOLERANCE)
                    {
                        m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.BOTTOM;
                    }
                    // Rotate
                    else if (Distance2Top > TOLERANCE && Distance2Right > TOLERANCE)
                    {
                        m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.ROTATE_TOP_RIGHT;
                    }
                    else if (Distance2Top > TOLERANCE && Distance2Left > TOLERANCE)
                    {
                        m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.ROTATE_TOP_LEFT;
                    }
                    else if (Distance2Bottom > TOLERANCE && Distance2Right > TOLERANCE)
                    {
                        m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.ROTATE_BOTTOM_RIGHT;
                    }
                    else if (Distance2Bottom > TOLERANCE && Distance2Left > TOLERANCE)
                    {
                        m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.ROTATE_BOTTOM_LEFT;
                    }
                    else if (Distance2Left < 0.0f && Distance2Right < 0.0f && Distance2Bottom < 0.0f && Distance2Top < 0.0f)
                    {
                        m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.CENTER;
                    }

                    // Update cursor accordingly
                    switch (m_CropRectangleManipulatedSpot)
                    {
                    case CROP_RECTANGLE_SPOT.NONE: Cursor = Cursors.Default; break;

                    case CROP_RECTANGLE_SPOT.LEFT: Cursor = Cursors.SizeWE; break;

                    case CROP_RECTANGLE_SPOT.RIGHT: Cursor = Cursors.SizeWE; break;

                    case CROP_RECTANGLE_SPOT.TOP: Cursor = Cursors.SizeNS; break;

                    case CROP_RECTANGLE_SPOT.BOTTOM: Cursor = Cursors.SizeNS; break;

                    case CROP_RECTANGLE_SPOT.CORNER_TOP_LEFT: Cursor = Cursors.SizeNWSE; break;

                    case CROP_RECTANGLE_SPOT.CORNER_BOTTOM_RIGHT: Cursor = Cursors.SizeNWSE; break;

                    case CROP_RECTANGLE_SPOT.CORNER_TOP_RIGHT: Cursor = Cursors.SizeNESW; break;

                    case CROP_RECTANGLE_SPOT.CORNER_BOTTOM_LEFT: Cursor = Cursors.SizeNESW; break;

                    case CROP_RECTANGLE_SPOT.ROTATE_TOP_LEFT:
                    case CROP_RECTANGLE_SPOT.ROTATE_TOP_RIGHT:
                    case CROP_RECTANGLE_SPOT.ROTATE_BOTTOM_LEFT:
                    case CROP_RECTANGLE_SPOT.ROTATE_BOTTOM_RIGHT:
                        Cursor = Cursors.Hand;
                        break;

                    case CROP_RECTANGLE_SPOT.CENTER:
                        Cursor = Cursors.Hand;
                        break;
                    }
                }
                else
                {                               // Handle actual manipulation
                    ImageUtility.float2 CurClientPos    = new ImageUtility.float2(e.X, e.Y);
                    ImageUtility.float2 OldClientPos    = new ImageUtility.float2(m_ButtonDownMousePosition.X, m_ButtonDownMousePosition.Y);
                    ImageUtility.float2 OldClientCenter = new ImageUtility.float2(m_ButtonDownClientCropRectangleCenter.X, m_ButtonDownClientCropRectangleCenter.Y);

                    ImageUtility.float2 OldCenter   = m_ButtonDownCropRectangleCenter;
                    ImageUtility.float2 OldHalfSize = m_ButtonDownCropRectangleHalfSize;
                    ImageUtility.float2 OldAxisX    = m_ButtonDownCropRectangleAxisX;
                    ImageUtility.float2 OldAxisY    = m_ButtonDownCropRectangleAxisY;

                    ImageUtility.float2[] OldVertices = new ImageUtility.float2[4] {
                        OldCenter - OldHalfSize.x * OldAxisX + OldHalfSize.y * OldAxisY,
                        OldCenter + OldHalfSize.x * OldAxisX + OldHalfSize.y * OldAxisY,
                        OldCenter - OldHalfSize.x * OldAxisX - OldHalfSize.y * OldAxisY,
                        OldCenter + OldHalfSize.x * OldAxisX - OldHalfSize.y * OldAxisY,
                    };

                    ImageUtility.float2 CurCenter   = OldCenter;
                    ImageUtility.float2 CurHalfSize = OldHalfSize;
                    ImageUtility.float2 CurAxisX    = OldAxisX;
                    ImageUtility.float2 CurAxisY    = OldAxisY;

                    RectangleF ImageRect = ImageClientRect();                                   // The image's rectangle in client space

                    switch (m_CropRectangleManipulatedSpot)
                    {
                    case CROP_RECTANGLE_SPOT.LEFT:
                    case CROP_RECTANGLE_SPOT.RIGHT:
                    {
                        ImageUtility.float2 Center2OldPos = OldClientPos - OldClientCenter;
                        float OldDistance2Edge            = Center2OldPos.Dot(OldAxisX);
                        ImageUtility.float2 Center2CurPos = CurClientPos - OldClientCenter;
                        float CurDistance2Edge            = Center2CurPos.Dot(OldAxisX);

                        float Delta   = CurDistance2Edge - OldDistance2Edge;            // This is the amount (in client space) we need to move the left/right border
                        float DeltaUV = Delta / ImageRect.Height;                       // This is the same amount in UV space
                        DeltaUV *= 0.5f;                                                // We're dealing with halves all along

                        // Increase width of that amount
                        CurHalfSize.x = OldHalfSize.x + (m_CropRectangleManipulatedSpot == CROP_RECTANGLE_SPOT.LEFT ? -1 : 1) * DeltaUV;

                        // Move center along the X axis
                        if (m_CropRectangleManipulatedSpot == CROP_RECTANGLE_SPOT.LEFT)
                        {                                               // Keep right fixed, move to the left
                            ImageUtility.float2 Right = OldCenter + OldHalfSize.x * OldAxisX;
                            ImageUtility.float2 Left  = Right - 2.0f * CurHalfSize.x * OldAxisX;
                            CurCenter = 0.5f * (Right + Left);
                        }
                        else
                        {                                               // Keep left fixed, move to the right
                            ImageUtility.float2 Left  = OldCenter - OldHalfSize.x * OldAxisX;
                            ImageUtility.float2 Right = Left + 2.0f * CurHalfSize.x * OldAxisX;
                            CurCenter = 0.5f * (Right + Left);
                        }
                        break;
                    }

                    case CROP_RECTANGLE_SPOT.TOP:
                    case CROP_RECTANGLE_SPOT.BOTTOM:
                    {
                        ImageUtility.float2 Center2OldPos = OldClientPos - OldClientCenter;
                        float OldDistance2Edge            = Center2OldPos.Dot(OldAxisY);
                        ImageUtility.float2 Center2CurPos = CurClientPos - OldClientCenter;
                        float CurDistance2Edge            = Center2CurPos.Dot(OldAxisY);

                        float Delta   = CurDistance2Edge - OldDistance2Edge;            // This is the amount (in client space) we need to move the left/right border
                        float DeltaUV = Delta / ImageRect.Height;                       // This is the same amount in UV space
                        DeltaUV *= 0.5f;                                                // We're dealing with halves all along

                        // Increase height of that amount
                        CurHalfSize.y = OldHalfSize.y + (m_CropRectangleManipulatedSpot == CROP_RECTANGLE_SPOT.TOP ? 1 : -1) * DeltaUV;

                        // Move center along the X axis
                        if (m_CropRectangleManipulatedSpot == CROP_RECTANGLE_SPOT.TOP)
                        {                                               // Keep bottom fixed, move up
                            ImageUtility.float2 Bottom = OldCenter - OldHalfSize.y * OldAxisY;
                            ImageUtility.float2 Top    = Bottom + 2.0f * CurHalfSize.y * OldAxisY;
                            CurCenter = 0.5f * (Bottom + Top);
                        }
                        else
                        {                                               // Keep top fixed, move down
                            ImageUtility.float2 Top    = OldCenter + OldHalfSize.y * OldAxisY;
                            ImageUtility.float2 Bottom = Top - 2.0f * CurHalfSize.y * OldAxisY;
                            CurCenter = 0.5f * (Bottom + Top);
                        }
                        break;
                    }

                    case CROP_RECTANGLE_SPOT.CORNER_TOP_LEFT:
                    case CROP_RECTANGLE_SPOT.CORNER_TOP_RIGHT:
                    case CROP_RECTANGLE_SPOT.CORNER_BOTTOM_LEFT:
                    case CROP_RECTANGLE_SPOT.CORNER_BOTTOM_RIGHT:
                    {
                        ImageUtility.float2 Delta   = CurClientPos - OldClientPos;
                        ImageUtility.float2 DeltaUV = (1.0f / ImageRect.Height) * Delta;

                        ImageUtility.float2 Corner = new ImageUtility.float2(), OppositeCorner = new ImageUtility.float2();
                        switch (m_CropRectangleManipulatedSpot)
                        {
                        case CROP_RECTANGLE_SPOT.CORNER_TOP_LEFT: Corner = OldVertices[0]; OppositeCorner = OldVertices[3]; break;                                                      // Keep bottom right fixed

                        case CROP_RECTANGLE_SPOT.CORNER_TOP_RIGHT: Corner = OldVertices[1]; OppositeCorner = OldVertices[2]; break;                                                     // Keep bottom left fixed

                        case CROP_RECTANGLE_SPOT.CORNER_BOTTOM_LEFT: Corner = OldVertices[2]; OppositeCorner = OldVertices[1]; break;                                                   // Keep top right fixed

                        case CROP_RECTANGLE_SPOT.CORNER_BOTTOM_RIGHT: Corner = OldVertices[3]; OppositeCorner = OldVertices[0]; break;                                                  // Keep top left fixed
                        }

                        // Move corner
                        Corner += DeltaUV;

                        // Compute new center
                        CurCenter = new ImageUtility.float2(0.5f * (Corner.x + OppositeCorner.x), 0.5f * (Corner.y + OppositeCorner.y));

                        // Compute new size
                        CurHalfSize = new ImageUtility.float2(Math.Abs((Corner - CurCenter).Dot(OldAxisX)), Math.Abs((Corner - CurCenter).Dot(OldAxisY)));

                        break;
                    }

                    case CROP_RECTANGLE_SPOT.ROTATE_TOP_LEFT:
                    case CROP_RECTANGLE_SPOT.ROTATE_TOP_RIGHT:
                    case CROP_RECTANGLE_SPOT.ROTATE_BOTTOM_LEFT:
                    case CROP_RECTANGLE_SPOT.ROTATE_BOTTOM_RIGHT:
                    {
                        ImageUtility.float2 Center2OldPos = OldClientPos - OldClientCenter;
                        ImageUtility.float2 Center2CurPos = CurClientPos - OldClientCenter;

                        float OldAngle = (float)Math.Atan2(-Center2OldPos.y, Center2OldPos.x);
                        float CurAngle = (float)Math.Atan2(-Center2CurPos.y, Center2CurPos.x);

                        m_CropRectangleRotation = m_ButtonDownCropRectangleRotation + CurAngle - OldAngle;

                        CurAxisX = new ImageUtility.float2((float)Math.Cos(m_CropRectangleRotation), -(float)Math.Sin(m_CropRectangleRotation));
                        CurAxisY = new ImageUtility.float2(-(float)Math.Sin(m_CropRectangleRotation), -(float)Math.Cos(m_CropRectangleRotation));
                        break;
                    }

                    case CROP_RECTANGLE_SPOT.CENTER:
                    {
                        ImageUtility.float2 Delta   = CurClientPos - OldClientPos;
                        ImageUtility.float2 DeltaUV = (1.0f / ImageRect.Height) * Delta;

                        CurCenter = OldCenter + DeltaUV;
                        break;
                    }
                    }

                    CurHalfSize.x = Math.Max(1e-3f, CurHalfSize.x);
                    CurHalfSize.y = Math.Max(1e-3f, CurHalfSize.y);

                    // Constrain vertices to the image
                    ImageUtility.float2[] Vertices = BuildClipVertices(CurCenter, CurHalfSize, CurAxisX, CurAxisY);

                    float MinX = 0.5f * (1.0f - ImageAspectRatio);
                    float MaxX = 0.5f * (1.0f + ImageAspectRatio);

                    for (int i = 0; i < 4; i++)
                    {
                        bool Rebuild = false;
                        if (Vertices[i].x < MinX)
                        {
                            Vertices[i].x = MinX;
                            Rebuild       = true;
                        }
                        if (Vertices[i].x > MaxX)
                        {
                            Vertices[i].x = MaxX;
                            Rebuild       = true;
                        }
                        if (Vertices[i].y < 0.0f)
                        {
                            Vertices[i].y = 0.0f;
                            Rebuild       = true;
                        }
                        if (Vertices[i].y > 1.0f)
                        {
                            Vertices[i].y = 1.0f;
                            Rebuild       = true;
                        }
                        if (!Rebuild)
                        {
                            continue;
                        }

                        ImageUtility.float2 OppositeVertex = Vertices[3 - i];                                   // This one is fixed

                        // Recompute center & half size
                        CurCenter = 0.5f * (OppositeVertex + Vertices[i]);
                        ImageUtility.float2 Delta = Vertices[i] - OppositeVertex;
                        CurHalfSize = 0.5f * new ImageUtility.float2(Math.Abs(Delta.Dot(CurAxisX)), Math.Abs(Delta.Dot(CurAxisY)));

                        // Rebuild new vertices
                        Vertices = BuildClipVertices(CurCenter, CurHalfSize, CurAxisX, CurAxisY);
                    }

                    m_CropRectangleCenter   = CurCenter;
                    m_CropRectangleHalfSize = CurHalfSize;

                    // The crop rectangle has changed!
                    m_CropRectangleIsDefault = false;

                    // Repaint to update the crop rectangle
                    Invalidate();
                }
                break;
            }

            case MANIPULATION_STATE.CALIBRATION_TARGET:
            {                           // Take care of calibration manipulation
                Cursor = Cursors.Cross;
                switch (m_CalibrationStage)
                {
                case CALIBRATION_STAGE.PICK_CENTER:
                    m_CalibrationCenter = Client2ImageUV_NoSquareAspectRatio(e.Location);
                    Invalidate();
                    break;

                case CALIBRATION_STAGE.SET_RADIUS:
                {
                    ImageUtility.float2 Temp = Client2ImageUV_NoSquareAspectRatio(e.Location);
                    m_CalibrationRadius = (float)Math.Sqrt((Temp.x - m_CalibrationCenter.x) * (Temp.x - m_CalibrationCenter.x) + (Temp.y - m_CalibrationCenter.y) * (Temp.y - m_CalibrationCenter.y));
                    Invalidate();
                }
                break;
                }
                break;
            }

            default:
                Cursor = Cursors.Default;
                break;
            }
        }
Example #18
0
 /// <summary>
 /// Sets the crop rectangle to a specific value
 /// </summary>
 public void SetCropRectangle( ImageUtility.float2 _Center, ImageUtility.float2 _HalfSize, float _Rotation )
 {
     m_CropRectangleIsDefault = false;
     m_CropRectangleCenter = _Center;
     m_CropRectangleHalfSize = _HalfSize;
     m_CropRectangleRotation = _Rotation;
     Invalidate();
 }
Example #19
0
        /// <summary>
        /// Resets the crop rectangle to the entire image
        /// </summary>
        public void ResetCropRectangle()
        {
            m_CropRectangleIsDefault = true;
            if ( m_Image == null )
                return;

            m_CropRectangleCenter = new ImageUtility.float2( 0.5f, 0.5f );
            m_CropRectangleHalfSize = new ImageUtility.float2( 0.5f * ImageAspectRatio, 0.5f );
            m_CropRectangleRotation = 0.0f;
            Invalidate();
        }