마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

ImageCrop 질문 (소스,사진 첨부)

–1 추천

캔버스 선 상자가 밖으로 아예 안벗어 나야하는데요 ... 그래서 방법이 큰 점 4개 찍고 점만 움직여야 할것 같은데 어떻게 해야할지 감이 안잡힙니다.

아래사진처럼 큰 점 네개만 움직일 수 있는 수정방법좀 부탁드립니다...ㅜ

public CropImage(Context context, AttributeSet attr) {
		super(context, attr);
		
		
		
		Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
		Point point = new Point();
		display.getSize(point);
		mWidth	= point.x;
		mHeight	= point.y;
		
		
//		sx = mWidth / 5; // 초기s Crop선의 위치 설정
//		ex = mWidth * 4 / 5;
//		sy = mHeight / 5;
//		ey = mHeight * 4 / 5;
		
		
//		sx = mWidth;
//		ex = mWidth;
//		sy = mHeight;
//		ey = mHeight;

		Log.e(TAG, outFilePath);
		cnxt = (ImageCropActivity) context;

		// 비트맵 크기 조절(메모리 문제로 인하여 1/2 크기로)
		BitmapFactory.Options resizeOpts = new Options();
		resizeOpts.inSampleSize = 2;
		try {
			bitmap = BitmapFactory.decodeStream(
					new FileInputStream(outFilePath), null, resizeOpts);
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		bitmap = BitmapUtil.resizeBitmap(bitmap, (int)mWidth);
		bitWidth	= bitmap.getWidth();
		bitHeight	= bitmap.getHeight();
//		if(bitHeight > bitWidth){
//			bitmap = BitmapUtil.resizeBitmap(bitmap, (int)mHeight);
//		}else{
//			
//		}
		
		Log.e(TAG, "" + bitHeight * bitWidth);
		
		hBmp = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.shape);
		wBmp = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.shape);

		if(bitWidth > bitHeight){
			sx = mWidth * 0; // 초기s Crop선의 위치 설정
			ex = bitWidth;
			sy = mHeight/7;
			ey = bitHeight + mHeight/7;	
		}else{
			sx = (mWidth - bitWidth)/2; // 초기s Crop선의 위치 설정
			ex = (mWidth - bitWidth)/2 + bitWidth;
			sy = mHeight/20;
			ey = bitHeight + mHeight/20;	
		}
		
		Log.d("크기","mwidth :" + mWidth + " mHeight : " + mHeight + "sx : " + sx + " ex : " + ex + " sy : " + sy + " ey :" + ey);

		
		// 페인트 설정
		pnt = new Paint();
		pnt.setColor(Color.MAGENTA);
		pnt.setStrokeWidth(3);
	}

	public void onDraw(Canvas canvas) {
		// 사각형 라인 그리기
//		canvas.drawBitmap(bitmap, mHeight-bitHeight/2, mWidth-bitWidth/2, null);
		
		if(bitWidth > bitHeight){
			canvas.drawBitmap(bitmap, 0 , mHeight/7 , null);
		}else{
			canvas.drawBitmap(bitmap, ((mWidth - bitWidth)/2) , mHeight/20 , null);
		}
		
		canvas.drawLine(sx, sy, ex, sy, pnt);
//		canvas.drawLine(0, 0, mWidth, 0, pnt);		// 왼쪽 위 점 -> 오른쪽 위 점

		canvas.drawLine(ex, sy, ex, ey, pnt);
//		canvas.drawLine(mWidth, 0, mWidth, mHeight , pnt);			// 오른쪽 위 점 -> 오른쪽 아래 점
		
		canvas.drawLine(sx, sy, sx, ey, pnt);
//		canvas.drawLine(0, 0, 0, mHeight, pnt);			//	시작 점 	-> 왼쪽 아래점
		
		canvas.drawLine(sx, ey, ex, ey, pnt);
//		canvas.drawLine(0, mHeight, mWidth, mHeight, pnt);			//	왼쪽 아래 점 - > 오른쪽 아래점
		
		
		
		// 상하좌우 버튼들
//		canvas.drawBitmap(hBmp, (ex + sx) / 2 - 19, sy - 19, null); // 폭이 38이므로
//		canvas.drawBitmap(hBmp, (ex + sx) / 2 - 19, ey - 19, null);
//		canvas.drawBitmap(wBmp, sx - 19, (ey + sy) / 2 - 19, null);
//		canvas.drawBitmap(wBmp, ex - 19, (ey + sy) / 2 - 19, null);
	}

	// 이벤트 처리, 현재의 그리기 모드에 따른 점의 위치를 조정
	float dx = 0, dy = 0;
	float oldx, oldy;
	boolean bsx, bsy, bex, bey;
	boolean bMove = false;

	public boolean onTouchEvent(MotionEvent e) {
		int x = (int) e.getX();
		int y = (int) e.getY();

		if (e.getAction() == MotionEvent.ACTION_DOWN) {
			oldx = x;
			oldy = y;

			// 눌려진곳이 선 근처인가 확인
			if ((x > sx - DEP) && (x < sx + DEP))
				bsx = true;
			else if ((x > ex - DEP) && (x < ex + DEP))
				bex = true;

			if ((y > sy - DEP) && (y < sy + DEP))
				bsy = true;
			else if ((y > ey - DEP) && (y < ey + DEP))
				bey = true;

			// 어느 하나라도 선택이 되었다면 move에서 값 변경
			if ((bsx || bex || bsy || bey))
				bMove = false;
			else if (((x > sx + DEP) && (x < ex - DEP))
					&& ((y > sy + DEP) && (y < ey - DEP)))
				bMove = true;

			return true;
		}

		if (e.getAction() == MotionEvent.ACTION_MOVE) {
			if (bsx)
				sx = x;
			if (bex)
				ex = x;
			if (bsy)
				sy = y;
			if (bey)
				ey = y;

			// 사각형의 시작 라인보다 끝라인이 크지않게 처리
			if (ex <= sx + DEP) {
				ex = sx + DEP;
				return true;
			}
			if (ey <= sy + DEP) {
				ey = sy + DEP;
				return true;
			}

			// 움직인 거리 구해서 적용
			if (bMove) {
				dx = oldx - x;
				dy = oldy - y;

				sx -= dx;
				ex -= dx;
				sy -= dy;
				ey -= dy;
				
				
				// 화면밖으로 나가지않게 처리
				if (bitWidth>bitHeight? sx <= 0 : sx <= (mWidth - bitWidth)/2)
					sx = bitWidth > bitHeight ? 0 : (mWidth - bitWidth)/2; 
				if (bitWidth>bitHeight ? ex >= mWidth : ex >= (mWidth - bitWidth)/2 + bitWidth)
					ex = bitWidth > bitHeight ? mWidth - 1 : (mWidth - bitWidth)/2 + bitWidth;
				if (bitWidth>bitHeight ?sy <= mHeight/7 : sy <= mHeight/20)
					sy = bitWidth>bitHeight ? mHeight/7 : mHeight/20;
				if (bitWidth>bitHeight ? ey >= bitHeight + mHeight/7 : ey >= bitHeight + mHeight/20)
					ey = bitWidth>bitHeight ? bitHeight + mHeight/7 - 1 : bitHeight + mHeight/20 -1;
			}

			invalidate(); // 움직일때 다시 그려줌
			oldx = x;
			oldy = y;
			return true;
		}

		// ACTION_UP 이면 그리기 종료
		if (e.getAction() == MotionEvent.ACTION_UP) {
			bsx = bex = bsy = bey = bMove = false;
			return true;
		}
		return false;
	}

	// 선택된 사각형의 이미지를 저장
	public void save() {
		if(bitWidth > bitHeight){
			tmp = Bitmap.createBitmap(bitmap, (int) (sx * 0), (int) (sy-mHeight/7),
					(int) (ex - sx), (int) (ey - sy));
		}else{
			tmp = Bitmap.createBitmap(bitmap, (int) (sx - (mWidth - bitWidth)/2), (int) (sy-mHeight/20),
					(int) (ex - sx), (int) (ey - sy));
		}
			
		byte[] byteArray = bitmapToByteArray(tmp);
		File file = new File(outFilePath);

		try {
			FileOutputStream fos = new FileOutputStream(file);
			fos.write(byteArray);
			fos.flush();
			fos.close();
		} catch (Exception e) {
			Toast.makeText(cnxt, "파일 저장 중 에러 발생 : " + e.getMessage(), 0).show();
			return;
		}
	}

 

캐릭 (420 포인트) 님이 2015년 5월 27일 질문

3개의 답변

+1 추천

캐릭님이 하고자 하는 것을 대충 보았는데요.

크롭영역의 설정하는 부분에서 crop영역이 크롭가능영역을 벗어나지 못하게 하게 하고 싶어 하시는 듯합니다.

그렇다는것은 터치 이벤트처리에서 이런 부분은 제어 하시면 될듯합니다.

그리고 꼭 꼬지점 4개를 이동하는 방식으로 해야 해결되 수 있는 것 또한 아닌듯 합니다.

좀더 직관적이고 단순한 방식의 제어를 하게 하기 위한 방법인 듯합니다.

아무튼 가장 먼저하셔야 할일은 원본이미지의  크기를 구하셔야합니다.

다음은 화면에서 이미지를 나타내는 view의 canvas의 크기를 구하셔야하구요.

그후론 간단하지요 터치 처리부분에서 실제 이미지가 노여진 영역을 경계값으로 설정하는 것이지요.

이 경계 영역 밖으로 crop 포인트가 벗어나면 못벗어나게 해주시면 됩니다.

확대 축소 없이 이미지가 뷰의 center에 놓인다고 가정했을 때.

화면에 뿌려지는 이미지의 크기가 imageRect, 이미지를 보여주는 view의 크기가 viewRect, 라면 좌우 경계는

if (viewRect.width - imageRect.width > 0)

{

leftLimit = (viewRect.width - imageRect.width)/2;

rigthLimit =  leftLmit+imageRect.width;

}

else {

leftLimit = 0;

rightLimit = viewRect.width;

}

와 같이 될 꺼고 상하 경계도 비슷한 코드로 이루어 지겠지요.

그럼 저 경계를 대상으로 터치이벤트처리에서 감시하다 저경계를 넘게 되면 저 경계로 돌려주면 되겠지요.

예를 들자면 만일 터치에서의 크롭영역으로 설정하는 왼쪽 경계를 담당하는 값이 leftRequest라고 한다면

if(leftRequest < leftLimit) leftRequest = leftLimit;

와 같은 코드로 검사를 수행하시면 영역을 벗어 나지는 않게 될 꺼라 생각듭니다.

물로 4개의 포인트로 처리하시는 부분에 대해서는

4개의 포인트를 가지고 처리하시면되는데 4개의 포인트는 항상 서로 연관되어 있으니 그점을 주의하시면 될듯 합니다.

 point1, point2, point3, point4을 왼쪽위, 오른쪽위, 오른쪽아래, 왼쪽아래 식으로 가정했을때  실질적으로 필요한 포인트는 point1, point3 나 point2, point4 만 있으면 crop영역은 지정될 것이고.

point1.x, point4.x는 항상같은 값을 가져야하며 leftRequest에 해당하겠지요.

point1.y, point2.y 는 같은 값이며 topRequest에 해당하는 값을 가질것입니다.

그리고 crop영역을설정하기 위한 터치 처리 시작점에서 항상 저 4개의 포인트를 먼저 left,right,top,bottom의 값을 통해서 만드시고 4개의 포인트 근처가 터치되었는지 확인하시면 될듯합니다.

확대축소가 이루어진다면 확대축소율을 고려해서 경계영역을 설정 하시면됩니다.

컴러기 (22,230 포인트) 님이 2015년 5월 27일 답변
터치로 상자를 움직일때는 경계영역을 못넘어가게 설정해놓았습니다.
하지만 상자를 키울때 넘어가는건 막지 못했습니다.
상자가 밖으로 나가고 안쪽을 터치하면 경계 영역으로 다시 돌아오도록 했는데요. 혹시 project 를 직접 확인해주실수 있으십니까 ? ...
Canvas 로 그려진 라인 상자를 늘릴때 넘어가는 부분만 유독 안고쳐져서요 ㅠ
흠. 코드를 자세히 봤는데요. 라인을 선택하셔서 크기조절을 하시는부분에서요
bMove가 false인경우는 sx, sy, ex, ey에 대한 범위 경계 검사가 않되는 것 같내요.
bMove가 false인경우는 라인이 선택되어서 크기조절인경우도 bMove는 false내요.
그럴때도 각각의 라인의 경계를 검사하셔야 할듯 합니다.
즉 if(bMove) {...} 블럭의 //화면밖으로 나가지 않게 처리 이하 코드들을 if(bMove)블럭 밖으로 옮겨 보시지요
밖으로 옮기면 상자를 더 키울  수 없지 않고 밖으로도 나가는 것 같습니다.
꼭지점 4개를 찍어서 키울 수 있게 해야하나요 ... ㅠ?
+1 추천
    public boolean onTouchEvent(MotionEvent e) {
        int x = (int) e.getX();
        int y = (int) e.getY();
 
        if (e.getAction() == MotionEvent.ACTION_DOWN) {
            oldx = x;
            oldy = y;
 
            // 눌려진곳이 선 근처인가 확인
            if ((x > sx - DEP) && (x < sx + DEP))
                bsx = true;
            else if ((x > ex - DEP) && (x < ex + DEP))
                bex = true;
 
            if ((y > sy - DEP) && (y < sy + DEP))
                bsy = true;
            else if ((y > ey - DEP) && (y < ey + DEP))
                bey = true;
 
            // 어느 하나라도 선택이 되었다면 move에서 값 변경
            if ((bsx || bex || bsy || bey))
                bMove = false;
            else if (((x > sx + DEP) && (x < ex - DEP))
                    && ((y > sy + DEP) && (y < ey - DEP)))
                bMove = true;
 
            return true;
        }
 
        if (e.getAction() == MotionEvent.ACTION_MOVE) {
            if (bsx) {
                sx = x;
                if (mBitmapBoundRectF.left > sx) sx =  mBitmapBound.left;
   }
            if (bex) {
                ex = x;
                if (mBitmapBoundRectF.right < ex) ex =  mBitmapBound.right;
            }
            if (bsy) {
                sy = y;
                if (mBitmapBoundRectF.top > sy) sy =  mBitmapBound.top;
            }
            if (bey) {
                ey = y;
                if (mBitmapBoundRectF.bottom < ey) ey =  mBitmapBound.bottom;
            }
 
            // 사각형의 시작 라인보다 끝라인이 크지않게 처리
            if (ex <= sx + DEP) {
                ex = sx + DEP;
                return true;
            }
          

음 여러부분으로 나누어서 코드를 적어야할듯 하내요 한번에 들어가지 않아서요 ㅠㅠ

답변한계가 8000글자 제한에 걸려서  한번에 코드변경부분이 다 올라가지 않내요.

아무튼 두개로 나눠진 답변은 제가 조금 바꿔본부분을 옮겼습니다.

우선  mBitampBoundRectF라는 경계 영역을 저장한   object 를 하나 만들었습니다.

그리고 이미지 그리신 부분 바로 아래에서 경계영역을 설정 햇습니다.

그리고 터치함수부분에서 각 경계영역 검사를 수행햇습니다.

제가수정한 부분을 힌트로 해서 원하시는 효과가 나오시길...

컴러기 (22,230 포인트) 님이 2015년 5월 28일 답변
컴러기님이 2015년 5월 28일 수정
0 추천
RectF mBitmapBoundRectF;

public CropImage(Context context, AttributeSet attr) {
        super(context, attr);
         
         
         
        Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
        Point point = new Point();
        display.getSize(point);
        mWidth  = point.x;
        mHeight = point.y;
         
         
//      sx = mWidth / 5; // 초기s Crop선의 위치 설정
//      ex = mWidth * 4 / 5;
//      sy = mHeight / 5;
//      ey = mHeight * 4 / 5;
         
         
//      sx = mWidth;
//      ex = mWidth;
//      sy = mHeight;
//      ey = mHeight;
 
        Log.e(TAG, outFilePath);
        cnxt = (ImageCropActivity) context;
 
        // 비트맵 크기 조절(메모리 문제로 인하여 1/2 크기로)
        BitmapFactory.Options resizeOpts = new Options();
        resizeOpts.inSampleSize = 2;
        try {
            bitmap = BitmapFactory.decodeStream(
                    new FileInputStream(outFilePath), null, resizeOpts);
             
        } catch (Exception e) {
            e.printStackTrace();
        }
         
        bitmap = BitmapUtil.resizeBitmap(bitmap, (int)mWidth);
        bitWidth    = bitmap.getWidth();
        bitHeight   = bitmap.getHeight();
//      if(bitHeight > bitWidth){
//          bitmap = BitmapUtil.resizeBitmap(bitmap, (int)mHeight);
//      }else{
//          
//      }
         
        Log.e(TAG, "" + bitHeight * bitWidth);
         
        hBmp = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.shape);
        wBmp = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.shape);
 
        if(bitWidth > bitHeight){
            sx = mWidth * 0; // 초기s Crop선의 위치 설정
            ex = bitWidth;
            sy = mHeight/7;
            ey = bitHeight + mHeight/7; 
        }else{
            sx = (mWidth - bitWidth)/2; // 초기s Crop선의 위치 설정
            ex = (mWidth - bitWidth)/2 + bitWidth;
            sy = mHeight/20;
            ey = bitHeight + mHeight/20;    
        }
        mBitmapBoundRectF = new RectF(sx,sy,ex,ey);
  
        Log.d("크기","mwidth :" + mWidth + " mHeight : " + mHeight + "sx : " + sx + " ex : " + ex + " sy : " + sy + " ey :" + ey);
 
         
        // 페인트 설정
        pnt = new Paint();
        pnt.setColor(Color.MAGENTA);
        pnt.setStrokeWidth(3);
    }
 

이부분은 처음 이미지 로드해서 화면에 이미지 표시하는 부분으로 파악했습니다.

컴러기 (22,230 포인트) 님이 2015년 5월 28일 답변
컴러기님이 2015년 5월 28일 수정
...