Вступление
Примеры Словарь Вопросы
Sun Microsystems

Примеры программирования на Java

Ластик
(erase.java)

Описание апплета
Пример работы апплета
//******************************************************************************
//
// ImageErase.java:	Applet - стирание частей изображения, под которым лежит 
//							 другое изображение
//******************************************************************************
import java.applet.*;
import java.awt.*;
import java.awt.image.*;
import java.util.*;

//==============================================================================
// Основной класс - applet ImgErase 
//
//==============================================================================
public class ImgErase extends Applet implements Runnable
{
	//нить загрузки изображений
	private Thread	 m_ImgErase = null;

	//контекст рисования для ускорения перерисовки
	private Graphics m_Graphics;

	//текущее изображение для рисования
	private Image	 m_imgPaint;
	//пикселы фонового изображения
	private int      m_nBackPixels[];
	//пикселы стираемого  изображения
	private int      m_nPaintPixels[];
	
	//размеры изображений
	private int 	 m_nImgWidth  = 0;
	private int 	 m_nImgHeight = 0;

	//флаг загрузки изображений
	private boolean  m_fAllLoaded = false;
	
	//имена изображений для загрузки 
	private String   m_sImgBack = "backgr.jpg";
	private String   m_sImgFore = "foregr.jpg";

	//размеры ластика
	private int	     m_nEraserWidth   = 8;
	private int	     m_nEraserHeight  = 8;

	//сдвиг ластика по отношению к курсору мыши
	private int      m_nEraserOffsetX = 4;
	private int      m_nEraserOffsetY = 4;

	//цвет фона апплета
	private Color    m_clrBk = Color.white;

	//флаг активности ластика
	private boolean  m_fErasing = false;

	//разрешенная область стирания
	Rectangle m_rcEraseArea =  new Rectangle(0,0,0,0);

	//левый верхний угол изображения
	Point m_ptPict      =  new Point(0, 0);

	//текущая позиция ластика
	Point     m_ptEraser    =  new Point(-100, -100);

    // Имена параметров апплета
	//--------------------------------------------------------------------------
	private final String PARAM_BackgrImage = "BackgrImage";
	private final String PARAM_ForegrImage = "ForegrImage";
	private final String PARAM_EraserWidth = "EraserWidth";
	private final String PARAM_EraserHeight = "EraserHeight";
	private final String PARAM_BkColor = "BkColor";

	// ImgErase конструктор
	//--------------------------------------------------------------------------
	public ImgErase()
	{
	}


	//---------------------------------------------------------------------------
	public String[][] getParameterInfo()
	{
		String[][] info =
		{
			{ PARAM_BackgrImage,  "String", "Image in background" },
			{ PARAM_ForegrImage,  "String", "Image in foreground" },
			{ PARAM_EraserWidth,  "int",    "Width of eraser" },
			{ PARAM_EraserHeight, "int",    "Height of eraser" },
			{ PARAM_BkColor,      "String", "Background color"}
		};
		return info;		
	}

	// В методе init() происходит первичная инициализация апплета
	// и получение значений параметров
	//--------------------------------------------------------------------------
	public void init()
	{
		String param;

		//фоновое изображение
		param = getParameter(PARAM_BackgrImage);
		if (param != null)
			m_sImgBack = param;

		//стираемое изображение
		param = getParameter(PARAM_ForegrImage);
		if (param != null)
			m_sImgFore = param;

		//размеры ластика
		param = getParameter(PARAM_EraserWidth);
		if (param != null)
			m_nEraserWidth = Integer.parseInt(param);
		param = getParameter(PARAM_EraserHeight);
		if (param != null)
			m_nEraserHeight = Integer.parseInt(param);

		//цвет фона
		param = getParameter(PARAM_BkColor);
		if (param != null)
		{
			//разбор строки цвета фона в форме "r,g,b" 
			StringTokenizer st = new  StringTokenizer(param, ",.");
			if( st.countTokens() == 3 )
			{
				int nRed   = Integer.parseInt(st.nextToken());
				int nGreen = Integer.parseInt(st.nextToken());
				int nBlue  = Integer.parseInt(st.nextToken());
				m_clrBk    = new Color(nRed, nGreen, nBlue);
			}
		}
		//установить цвет фона апплета
		setBackground(m_clrBk);
    	
	}

	public void destroy()
	{
	}

	//displayImage - отрисовывает текущее изображение в середине апплета 
	//случае если оно загружено
	//----------------------------------------------------------
	private void displayImage(Graphics g)
	{
		if (!m_fAllLoaded)
			return;

		g.drawImage(m_imgPaint, m_ptPict.x, m_ptPict.y, null);
	}

	
	//paint - если загружено изображение, то отрисовывает его в середине апплета, 
	//иначе - выдаем строчку о загрузке 
	//----------------------------------------------------------
	public void paint(Graphics g)
	{
		if (m_fAllLoaded)
			displayImage(g);
		else
			g.drawString("Loading images...", 10, 20);
	}

	// Запуск апплета - запускаем нить (thread) m_ImgErase, которая загружает 
	// изображения 
	//--------------------------------------------------------------------------
	public void start()
	{
		if (m_ImgErase == null)
		{
			m_ImgErase = new Thread(this);
			m_ImgErase.start();
		}
	}
	
	// Метод stop() - останавливаем выполнение нити m_ImgErase
	//--------------------------------------------------------------------------
	public void stop()
	{
		if (m_ImgErase != null)
		{
			m_ImgErase.stop();
			m_ImgErase = null;
		}
	}

	// Нить загрузки изображений и ожидания окончания с помощью MediaTracker. 
	// после загрузки изображений создаем рисуемое изображение и получаем пикселы 
	// стираемого и фонового изображения в виде массивов int
	//--------------------------------------------------------------------------
	public void run()
	{
		// При повторном входе в страницу изображения уже загружены, поэтому
		// не делаем этого опять (m_fAllLoaded == TRUE).
        if (!m_fAllLoaded)
		{
    		//перерисовка чтобы показать статусное сообщение
			repaint();

			//запоминаем контекст рисования
    		m_Graphics = getGraphics();

    		//стираемое и фоновое изображения
			Image imgBack, imgFore;

			//tracker позволяет отслеживать загрузку изображений
			MediaTracker tracker = new MediaTracker(this);

			//загружам изображения
			imgBack = getImage(getDocumentBase(), m_sImgBack);
			imgFore = getImage(getDocumentBase(), m_sImgFore);

            //регистрируем изображения в tracker
			tracker.addImage(imgBack, 0);
			tracker.addImage(imgFore, 0);

    	    boolean fLoaded = false; //флаг ошибки при загрузке
			try
			{
				tracker.waitForAll();
				fLoaded = !tracker.isErrorAny();	
			}
			catch (InterruptedException e)
			{

			}
			
			//если обнаружена ошибка при загрузке - остановить выполнение нити и 
			//вывести сообщение
			if (!fLoaded)
			{
			    m_fAllLoaded = false;
				stop();
			    m_Graphics.drawString("Error loading images!", 10, 40);
			    return;
			}
			
			//получаем размеры изображения 
			//(подразумевается, что изображения одинакового размера)
		    m_nImgWidth    = imgFore.getWidth(this);
		    m_nImgHeight   = imgFore.getHeight(this);

			//инициация массива для пикселов фонового изображеня
			m_nBackPixels  = new int[m_nImgWidth * m_nImgHeight];
			//инициация массива для пикселов отрисовываемого изображения
			m_nPaintPixels = new int[m_nImgWidth * m_nImgHeight];

			//получаем пикселы изображений с помощью обьектов PixelGrabber
			//
			PixelGrabber grabBack = new PixelGrabber(imgBack.getSource(),
			                        0, 0, m_nImgWidth, m_nImgHeight, 
			                        m_nBackPixels, 0, m_nImgWidth);

			//в начале  изображение полностью совпадает со стираемым
			PixelGrabber grabPaint = new PixelGrabber(imgFore.getSource(),
			                         0,0, m_nImgWidth, m_nImgHeight, 
			                         m_nPaintPixels, 0, m_nImgWidth);
			try 
			{ 
				grabBack.grabPixels();
				grabPaint.grabPixels();
			} 
			catch ( Exception e )
			{
	
			}
			//создаем изображение для рисования 
			m_imgPaint = createImage( new MemoryImageSource(m_nImgWidth, m_nImgHeight,
			                          m_nPaintPixels, 0, m_nImgWidth));

			//вычисляем левую верхнюю точку рисования изображения,
			//так, чтобы оно находилсь в середине апплета
			m_ptPict.x = (size().width - m_nImgWidth)   / 2;
			m_ptPict.y = (size().height - m_nImgHeight) / 2;
			
			//область разрешенного стирания -  больше изображения на 
			//ширину ластика с каждой стороны
			m_rcEraseArea.x = m_ptPict.x - m_nEraserWidth;
			m_rcEraseArea.y = m_ptPict.y - m_nEraserHeight;
			m_rcEraseArea.width  = m_nImgWidth  + m_nEraserWidth*2;
			m_rcEraseArea.height = m_nImgHeight + m_nEraserHeight*2;

			//сдвиг ластика по отношению у указателю мыши - половина ширины
			//и высоты ластика
			m_nEraserOffsetX = m_nEraserWidth / 2;
			m_nEraserOffsetY = m_nEraserHeight / 2;

			m_fAllLoaded = true;
		}		

		//перерисовка после загрузки 
		repaint();
	}

	// DrawEraser рисует контуры ластика в XOR режиме.
	// При повторном рисовании в том же месте контур исчезнет. 
	//----------------------------------------------------------------------------
	private void DrawEraser()
	{
	  m_Graphics.setXORMode(Color.white);
	  m_Graphics.drawRect(m_ptEraser.x, m_ptEraser.y,
	                      m_nEraserWidth, m_nEraserHeight );
	  m_Graphics.setPaintMode();
    }

	//ShowEraser первый раз показывает контур ластика в заданной точке
	//cо сдвигом относительно курсора мыши
	//----------------------------------------------------------------------------
	private void ShowEraser(int x, int y)
    {
	  m_ptEraser.move(x - m_nEraserOffsetX, y - m_nEraserOffsetY);
	  DrawEraser();
	}

	//HideEraser убирает контур ластика
	//и перемещает ластик в невидимую отрицательную область
	//----------------------------------------------------------------------------
	private void HideEraser()
    {
      DrawEraser(); 
	  m_ptEraser.move( - (m_nEraserWidth + 2), -(m_nEraserHeight + 2) );		
	}


	//MoveEraser убирает контур ластика в текущей точке, 
	//перемещает ластик в заданную точку со сдвигом относительно курсора мыши
	//и рисует контур ластика в новой точке
	//----------------------------------------------------------------------------
	private void MoveEraser(int x, int y)
    {
	  DrawEraser(); 
	  m_ptEraser.move(x - m_nEraserOffsetX, y - m_nEraserOffsetY);		
	  DrawEraser(); 
	}

	//ErasePixels стирает часть изображения, копируя в эту часть
	//соответствующую область фонового изображения
	//x, y - левая верхняя точка области (относительно левого верхнего угла картинки) 
	//w, h - ширина и высота области
	//----------------------------------------------------------------------------
	private void ErasePixels(int x, int y, int w, int h)
	{
		int nXEnd, nYEnd;

		//правая нижняя точка области
		nXEnd = x + w; 
		nYEnd = y + h;

		//проверяем граничные значения 
		//правой нижней точки области (>= 0 и <= размеров изображения)
		if( nXEnd > m_nImgWidth ) nXEnd= m_nImgWidth;
	    else if( nXEnd < 0 ) nXEnd = 0;
		if( nYEnd > m_nImgHeight ) nYEnd= m_nImgHeight;
	    else if( nYEnd < 0 ) nYEnd = 0;

		//проверяем граничные значения 
		//левой верхней точки стираемой области (>= 0 и <= размеров изображения)
		if(x < 0) x = 0;
		else if( x > m_nImgWidth ) x = m_nImgWidth;
		if( y < 0) y = 0;
		else if( y > m_nImgHeight ) y = m_nImgHeight;

		//nEndi - линейный индекс начала последней стираемой строки
		int nEndi = nYEnd * m_nImgWidth;
		//цикл от первой до последней стираемой строки
		for(int i = y * m_nImgWidth; i < nEndi; i +=m_nImgWidth )
		{
			//nEndj - линейный индекс последнего стираемого пиксела в данной строке
			int nEndj = i + nXEnd;
			//цикл от первого до последнего стираемого пиксела в данной строке 
			for(int j = i + x; j< nEndj ; j++)
			{
				//копируем пиксел из фонового в рисуемое изображение
				m_nPaintPixels[j] = m_nBackPixels[j];
			}
			
		}   

		//обновляем отрисовываемое изображение освобождением его памяти
		//будет воссоздано системой из m_nPaintPixels, измененных выше
		m_imgPaint.flush();

		//убрать с экрана контур ластика
		DrawEraser();

		//cropFilter выделяет только изменившуюся (cтертую) часть изображения 
		//в виде нового изображения. Это значительно ускоряет рисование
	 	CropImageFilter cropFilter = new CropImageFilter(x, y, nXEnd-x, nYEnd-y);
		Image imgPart = createImage( new FilteredImageSource( m_imgPaint.getSource(), 
		                                                      cropFilter) );	

		//нарисовать изменившуюся часть
		m_Graphics.drawImage( imgPart, m_ptPict.x + x, m_ptPict.y + y, null);

		//показать контуры ластика
		DrawEraser();

		//освободить память частичного изображения
		imgPart.flush();
	}

	// mouseDown включает режим рисования, если изображения загружены
	public boolean mouseDown(Event evt, int x, int y)
	{
		if( m_fAllLoaded )
			m_fErasing = true;
		return true;
	}

	// mouseUp выключает режим рисования, если изображения загружены
	public boolean mouseUp(Event evt, int x, int y)
	{
		
		if( m_fAllLoaded )
			m_fErasing = false;
		return true;
	}

	//mouseDrag вызывается при перемещении мыши с нажатой клавишей.
	//Если изображения загружены, перемещаем контур ластика в новую точку.
	//Стираем часть изображения, если включен режим стирания
	public boolean mouseDrag(Event evt, int x, int y)
	{
		if( m_fAllLoaded )
		{
			MoveEraser(x,y);

			//Если включен режим стирания и курсор находится в разрешенной области,
			//стираем часть изображения
			if( m_fErasing && m_rcEraseArea.inside(x,y) )
			{
				//левая верхняя точка стирания берется со 
				//сдвигом относительно картинки и курсора мыши
				ErasePixels( x - m_nEraserOffsetX  - m_ptPict.x,
							 y - m_nEraserOffsetY  - m_ptPict.y, 
							 m_nEraserWidth, m_nEraserHeight );
			}

			//если мы вышли за пределы апплета - убрать контур ластика
			if( x <= 0 || y <= 0 || x > size().width-1 || y > size().height-1 )
				mouseExit( evt, x, y);
		}
		return true;
	}

	//при перемещении курсора перемещаем контур ластика
	public boolean mouseMove(Event evt, int x, int y)
	{
		if( m_fAllLoaded ) //только если загружены изображения
			MoveEraser(x,y);
		return true;
	}

	//при входе курсора в пределы апплета - показать контур ластика
	public boolean mouseEnter(Event evt, int x, int y)
	{
		if( m_fAllLoaded ) //только если загружены изображения
			ShowEraser(x,y);
		return true;
	}

	//если мы вышли за пределы апплета - убрать контур ластика
	public boolean mouseExit(Event evt, int x, int y)
	{
		if( m_fAllLoaded ) //только если загружены изображения
			HideEraser();
		return true;
	}

}