/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.java.sip.communicator.plugin.desktoputil.presence.avatar.imagepicker;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
import net.java.sip.communicator.plugin.desktoputil.*;
/**
* Component allowing the user to easily crop an image
*
* @author Damien Roth
* @author Damian Minkov
*/
public class ImageClipper
extends JComponent
implements MouseListener,
MouseMotionListener
{
/**
* Border of the image.
*/
private static final Color IMAGE_BORDER_COLOR
= new Color(174, 189, 215);
/**
* Image overlay color.
*/
private static final Color IMAGE_OVERLAY_COLOR
= new Color(1.0f, 1.0f, 1.0f, 0.4f);
/**
* The last remembered component width, to see when component is resized.
*/
private int lastComponentWidth = 0;
/**
* The last remembered component height, to see when component is resized.
*/
private int lastComponentHeight = 0;
/**
* The image that we will crop.
*/
private BufferedImage image = null;
/**
* The rectangle in which we are currently drawing the image.
*/
private Rectangle imageRect = new Rectangle();
/**
* The zone that we will crop later from the image.
*/
private Rectangle cropZoneRect;
/**
* Used for mouse dragging.
* This is every time the initial X coordinate of the mouse
* and the coordinates are according the image.
*/
private int mouseStartX;
/**
* Used for mouse dragging.
* This is every time the initial Y coordinate of the mouse
* and the coordinates are according the image.
*/
private int mouseStartY;
/**
* Construct an new image cropper
*
* @param cropZoneWidth the width of the crop zone
* @param cropZoneHeight the height of the crop zone
*/
public ImageClipper(int cropZoneWidth, int cropZoneHeight)
{
this.cropZoneRect = new Rectangle(cropZoneWidth, cropZoneHeight);
updateCropZone();
Dimension d = new Dimension(320, 240);
this.setSize(d);
this.setMinimumSize(d);
this.setPreferredSize(d);
this.addMouseListener(this);
this.addMouseMotionListener(this);
}
/**
* Compute static values of the cropping zone
*/
private void updateCropZone()
{
this.cropZoneRect.x =
(this.getWidth() / 2) - (this.cropZoneRect.width / 2);
this.cropZoneRect.y =
(this.getHeight() / 2) - (this.cropZoneRect.height / 2);
}
/**
* Defines the image to be cropped
*
* @param image the image to be cropped
*/
public void setImage(BufferedImage image)
{
this.image = image;
this.imageRect.width = image.getWidth(this);
this.imageRect.height = image.getHeight(this);
// put the image in the center
this.imageRect.x = (this.getWidth() - this.imageRect.width)/2;
this.imageRect.y = (this.getHeight() - this.imageRect.height)/2;
// set the initial values
this.lastComponentHeight = this.getHeight();
this.lastComponentWidth = this.getWidth();
updateImagePoints();
this.repaint();
}
/**
* Update image points if needed, when component is resized.
*/
private void updateImagePoints()
{
if(lastComponentWidth != this.getWidth())
{
this.imageRect.x += (this.getWidth() - lastComponentWidth)/2;
lastComponentWidth = this.getWidth();
}
if(lastComponentHeight != this.getHeight())
{
this.imageRect.y += (this.getHeight() - lastComponentHeight)/2;
lastComponentHeight = this.getHeight();
}
}
/**
* Returns the cropped area of the image
*
* @return the cropped area
*/
public Rectangle getCroppedArea()
{
Rectangle croppedArea = new Rectangle();
croppedArea.setSize(this.cropZoneRect.getSize());
croppedArea.x = this.cropZoneRect.x - this.imageRect.x;
croppedArea.y = this.cropZoneRect.y - this.imageRect.y;
return croppedArea;
}
/**
* Paint the component with the image we have and the settings
* we have for it.
* @param g the graphics to draw.
*/
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g = g.create();
AntialiasingManager.activateAntialiasing(g);
// Draw image
updateImagePoints();
g.drawImage(this.image, this.imageRect.x, this.imageRect.y,
this.imageRect.width, this.imageRect.height, this);
// Select rect
updateCropZone();
g.setColor(Color.BLACK);
g.drawRect(this.cropZoneRect.x, this.cropZoneRect.y,
this.cropZoneRect.width, this.cropZoneRect.height);
// Image overlay
drawImageOverlay(g);
// Image border
g.setColor(IMAGE_BORDER_COLOR);
g.drawRoundRect(this.imageRect.x-2, this.imageRect.y-2,
this.imageRect.width+3, this.imageRect.height+3, 2, 2);
g.drawRoundRect(this.imageRect.x-1, this.imageRect.y-1,
this.imageRect.width+1, this.imageRect.height+1, 2, 2);
}
/**
* Draw an overlay over the parts of the images
* which are not in the crop zone
*
* @param g the Graphics used to draw
*/
private void drawImageOverlay(Graphics g)
{
int width, height;
g.setColor(IMAGE_OVERLAY_COLOR);
// left vertical non cropped part
width = this.cropZoneRect.x - this.imageRect.x;
if (width > 0)
{
g.fillRect(this.imageRect.x, this.imageRect.y,
width, this.imageRect.height);
}
// right vertical non cropped
width = this.imageRect.x + this.imageRect.width
- (this.cropZoneRect.x + this.cropZoneRect.width);
if (width > 0)
{
g.fillRect(
this.cropZoneRect.x + this.cropZoneRect.width,
this.imageRect.y,
width,
this.imageRect.height);
}
// Top horizontal non croppped part
height = this.cropZoneRect.y - this.imageRect.y;
if (height > 0)
{
g.fillRect(this.cropZoneRect.x, this.imageRect.y,
this.cropZoneRect.width, height);
}
// Bottom horizontal non croppped part
height = (this.imageRect.y + this.imageRect.height)
- (this.cropZoneRect.y + this.cropZoneRect.height);
if (height > 0)
{
g.fillRect(
this.cropZoneRect.x,
this.cropZoneRect.y + this.cropZoneRect.height,
this.cropZoneRect.width,
height);
}
}
/**
* Start image cropping action.
* @param e the mouse event, initial clicking.
*/
public void mousePressed(MouseEvent e)
{
// Init the dragging
mouseStartX = e.getX();
mouseStartY = e.getY();
}
/**
* Event that user is dragging the mouse.
* @param e the mouse event.
*/
public void mouseDragged(MouseEvent e)
{
// New position of the image
int newXpos = this.imageRect.x + e.getX() - mouseStartX;
int newYpos = this.imageRect.y + e.getY() - mouseStartY;
if(newXpos <= cropZoneRect.x
&& newXpos + imageRect.width
>= cropZoneRect.x + cropZoneRect.width)
{
this.imageRect.x = newXpos;
mouseStartX = e.getX();
}
if(newYpos < cropZoneRect.y
&& newYpos + imageRect.height
>= cropZoneRect.y + cropZoneRect.height)
{
this.imageRect.y = newYpos;
mouseStartY = e.getY();
}
this.repaint();
}
/**
* Not used.
* @param e
*/
public void mouseClicked(MouseEvent e) {}
/**
* Not used.
* @param e
*/
public void mouseEntered(MouseEvent e) {}
/**
* Not used.
* @param e
*/
public void mouseExited(MouseEvent e) {}
/**
* Not used.
* @param e
*/
public void mouseReleased(MouseEvent e) {}
/**
* Not used.
* @param e
*/
public void mouseMoved(MouseEvent e) {}
}