2012-12-18 2 views
6

У любого из вас есть идея, как исказить изображение в любом четырехугольном? Я хочу реализовать изображение, которое вы можете вытащить из любого угла в любом направлении, искажая изображение. У кого-нибудь есть идея, как это сделать? Я использую и записываю материал в android некоторое время сейчас, но это не похоже, что у Android есть функция для этого. Мне не хочется писать новую математическую библиотеку :).Как исказить изображение на любой четырехугольник?

Привет, Может

ответ

8

Похоже, что вам нужно Canvas.drawBitmapMesh. В Android SDK есть образец, показывающий, как его использовать.

Для рисования растрового изображения необходимо использовать Matrix. Canvas. Вы можете легко создать такое преобразование, которое будет соответствовать вашему растровому изображению в любой четырехугольник с помощью метода Matrix.polyToPoly. Он будет выглядеть следующим образом:

matrix.setPolyToPoly(
     new float[] { 
      0, 0, 
      bitmap.getWidth(), 0 
      0, bitmap.getHeight(), 
      bitmap.getWidth(), bitmap.getHeight() 
     }, 0, 
     new float[] { 
      x0, y0, 
      x1, y1, 
      x2, y2, 
      x3, y3 
     }, 0, 
     4); 

canvas.drawBitmap(bitmap, matrix, paint); 

Где x0-x3, y0-y3 являются координаты вашей четырехугольник вершина.

+0

Большое спасибо за ваш быстрый ответ. Я чувствую себя глупо, потому что не вижу метода setPolytoPoly в классе матрицы, но это должно работать. Я буду проверять это на работе завтра :-)! – Geki

+0

Проблема с этим методом заключается в том, что он очень интенсивно работает в вычислительной среде и, следовательно, очень медленный на низкоуровневых устройствах. – user2498079

+0

Да, но у нас практически нет другого выбора, кроме как сделать это. – Geki

3

Существует проблема с вашим кодом. Несмотря на то, что это правильный метод, вы перевернули с плавающей точкой [] параметры, как это видно в Android документации:

public boolean setPolyToPoly (float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount) 
WHERE 
src: The array of src [x,y] pairs (points) 
... 
dst: The array of dst [x,y] pairs (points) 

Таким образом, в соответствии с вашим кодом, матрица должна быть создана как:

matrix.setPolyToPoly(
     new float[] { 
      x0, y0, 
      x1, y1, 
      x2, y2, 
      x3, y3}, 
    0, 
new float[] { 
     0, 0, 
     bitmap.getWidth(), 0 
     0, bitmap.getHeight(), 
     bitmap.getWidth(), bitmap.getHeight() 
    }, 0, 
    4); 

Таким образом, приложение работает отлично, как показано на рисунке:

enter image description here

по другой стороны, что касается того, что user2498079 говорит в его комментарий о вычислительной проблеме в устройствах с низким уровнем затрат, вы можете использовать некоторые простые в использовании методы для уменьшения размера исходного изображения (и глубины цвета, например) до вычисления преобразования матрицы. Это облегчило бы для младшего телефона реализовать эту задачу.

1

Надеюсь, это поможет. Угол вверху слева, сверху справа работает, кроме нижнего и нижнего правого. Может кто-нибудь добавить его. Я не могу понять, как делать нижние части. :)

public class PerspectiveDistortView extends View implements OnTouchListener { 

private Paint paintRect, paintCircle; 
public int LEFT; 
public int TOP; 
public int RIGHT; 
public int BOTTOM; 
Point CIRCLE_TOP_LEFT; 
Point CIRCLE_TOP_RIGHT; 
Point CIRCLE_BOTTOM_LEFT; 
Point CIRCLE_BOTTOM_RIGHT; 
private int lastX, lastY; 
Bitmap image; 
Rect src, dst; 
Matrix matrix2; 
boolean isTouchCirclePoints = true; 
float deform2 = 5f; 

public PerspectiveDistortView(Context context) { 
    super(context); 
    // TODO Auto-generated constructor stub 
    init(); 
} 

public PerspectiveDistortView(Context context, AttributeSet attrs) { 
    super(context, attrs); 
    // TODO Auto-generated constructor stub 
    init(); 
} 

public PerspectiveDistortView(Context context, AttributeSet attrs, 
     int defStyle) { 
    super(context, attrs, defStyle); 
    // TODO Auto-generated constructor stub 
    init(); 
} 

private void init(){ 
    this.setOnTouchListener(this); 
    paintRect = new Paint(); 
    paintRect.setColor(0xffff0000); 
    paintRect.setAntiAlias(true); 
    paintRect.setDither(true); 
    paintRect.setStyle(Paint.Style.STROKE); 
    paintRect.setStrokeJoin(Paint.Join.BEVEL); 
    paintRect.setStrokeCap(Paint.Cap.BUTT); 
    paintRect.setStrokeWidth(3); 
    paintCircle = new Paint(); 
    paintCircle.setColor(0xff000000); 
    paintCircle.setAntiAlias(true); 
    paintCircle.setDither(true); 
    paintCircle.setStyle(Paint.Style.FILL_AND_STROKE); 
    paintCircle.setStrokeJoin(Paint.Join.BEVEL); 
    paintCircle.setStrokeCap(Paint.Cap.BUTT); 

    LEFT = 90; 
    TOP = 40; 
    RIGHT = 500; 
    BOTTOM = 700; 
    CIRCLE_TOP_LEFT = new Point(LEFT, TOP); 
    CIRCLE_TOP_RIGHT = new Point(RIGHT, TOP); 
    CIRCLE_BOTTOM_LEFT = new Point(LEFT, BOTTOM); 
    CIRCLE_BOTTOM_RIGHT = new Point(RIGHT, BOTTOM); 

    image = BitmapFactory.decodeResource(getResources(), R.drawable.ai); 

    src = new Rect(); 
    dst = new Rect(); 

    matrix2 = new Matrix(); 
} 

@Override 
protected void onDraw(Canvas canvas) { 
    // draw image 
    src.left = LEFT; 
    src.top = TOP; 
    src.right = RIGHT; 
    src.bottom = BOTTOM + image.getHeight(); 

    dst.left = CIRCLE_TOP_LEFT.x; 
    dst.top = CIRCLE_TOP_LEFT.y; 
    dst.right = CIRCLE_TOP_RIGHT.x; 
    dst.bottom = CIRCLE_BOTTOM_RIGHT.y; 

    // Free Transform bitmap 
     int bw = image.getWidth(); 
     int bh = image.getHeight(); 
     RectF src = new RectF(LEFT, TOP, bw, bh); 
     RectF dst = new RectF(CIRCLE_TOP_LEFT.x + 35, CIRCLE_TOP_LEFT.y + 30, CIRCLE_TOP_RIGHT.x, CIRCLE_BOTTOM_RIGHT.y); 
     matrix2.setRectToRect(src, dst, ScaleToFit.FILL); 

     float[] pts = { 
         // source 
         0, 0, 
         0, bh, 
         bw, bh, 
         bw, 0, 
         // destination 
         0, 0, 
         0, 0, 
         0, 0, 
         0, 0}; 
     matrix2.mapPoints(pts, 8, pts, 0, 4); 
     int DX = 100; 
     pts[10] -= CIRCLE_TOP_LEFT.x - LEFT; 
     pts[12] -= CIRCLE_TOP_RIGHT.x - RIGHT; 
     pts[13] += 0; 
     pts[14] += 0; 
     pts[15] += CIRCLE_TOP_RIGHT.y - CIRCLE_TOP_LEFT.y; 

     matrix2.setPolyToPoly(pts, 0, pts, 8, 4); 
     canvas.drawBitmap(image, matrix2, null); 
     isTouchCirclePoints = false; 

    // line left 
    canvas.drawLine(CIRCLE_TOP_LEFT.x, CIRCLE_TOP_LEFT.y, CIRCLE_BOTTOM_LEFT.x, CIRCLE_BOTTOM_LEFT.y, paintRect); 
    // line top 
    canvas.drawLine(CIRCLE_TOP_LEFT.x, CIRCLE_TOP_LEFT.y, CIRCLE_TOP_RIGHT.x, CIRCLE_TOP_RIGHT.y, paintRect); 
    // line right 
    canvas.drawLine(CIRCLE_TOP_RIGHT.x, CIRCLE_TOP_RIGHT.y, CIRCLE_BOTTOM_RIGHT.x, CIRCLE_BOTTOM_RIGHT.y, paintRect); 
    // line bottom 
    canvas.drawLine(CIRCLE_BOTTOM_LEFT.x, CIRCLE_BOTTOM_LEFT.y, CIRCLE_BOTTOM_RIGHT.x, CIRCLE_BOTTOM_RIGHT.y, paintRect); 
    // circle top left 
    canvas.drawCircle(CIRCLE_TOP_LEFT.x, CIRCLE_TOP_LEFT.y, 10, paintCircle); 
    // circle top right 
    canvas.drawCircle(CIRCLE_TOP_RIGHT.x, CIRCLE_TOP_RIGHT.y, 10, paintCircle); 
    // circle bottom left 
    canvas.drawCircle(CIRCLE_BOTTOM_LEFT.x, CIRCLE_BOTTOM_LEFT.y, 10, paintCircle); 
    // circle bottom right 
    canvas.drawCircle(CIRCLE_BOTTOM_RIGHT.x, CIRCLE_BOTTOM_RIGHT.y, 10, paintCircle); 
} 

@Override 
public boolean onTouch(View view, MotionEvent event) { 
    lastX = (int) event.getX(); 
    lastY = (int)event.getY(); 
    if (inCircle(lastX, lastY, CIRCLE_TOP_LEFT.x, CIRCLE_TOP_LEFT.y, 40)) 
    { 
     isTouchCirclePoints = true; 
     CIRCLE_TOP_LEFT.set(lastX, lastY); 
    } else if (inCircle(lastX, lastY, CIRCLE_TOP_RIGHT.x, CIRCLE_TOP_RIGHT.y, 40)) 
    { 
     isTouchCirclePoints = true; 
     CIRCLE_TOP_RIGHT.set(lastX, lastY); 
    } else if (inCircle(lastX, lastY, CIRCLE_BOTTOM_LEFT.x, CIRCLE_BOTTOM_LEFT.y, 40)) 
    { 
     isTouchCirclePoints = true; 
     CIRCLE_BOTTOM_LEFT.set(lastX, lastY); 
    } else if (inCircle(lastX, lastY, CIRCLE_BOTTOM_RIGHT.x, CIRCLE_BOTTOM_RIGHT.y, 40)) 
    { 
     isTouchCirclePoints = true; 
     CIRCLE_BOTTOM_RIGHT.set(lastX, lastY); 
    } 
    invalidate(); 
    return true; 
} 

private boolean inCircle(float x, float y, float circleCenterX, float circleCenterY, float circleRadius) { 
    double dx = Math.pow(x - circleCenterX, 2); 
    double dy = Math.pow(y - circleCenterY, 2); 

    if ((dx + dy) < Math.pow(circleRadius, 2)) { 
     return true; 
    } else { 
     return false; 
    } 
} 

}

4

@donmj. Я исправил ваш код.

public class PerspectiveDistortView extends View implements View.OnTouchListener { 

private Paint paintRect, paintCircle; 
public int LEFT; 
public int TOP; 
public int RIGHT; 
public int BOTTOM; 
Point CIRCLE_TOP_LEFT; 
Point CIRCLE_TOP_RIGHT; 
Point CIRCLE_BOTTOM_LEFT; 
Point CIRCLE_BOTTOM_RIGHT; 
private int lastX, lastY; 
Bitmap image; 
Matrix matrix2; 
boolean isTouchCirclePoints = true; 

public PerspectiveDistortView(Context context) { 
    super(context); 
    // TODO Auto-generated constructor stub 
    init(); 
} 

public PerspectiveDistortView(Context context, AttributeSet attrs) { 
    super(context, attrs); 
    // TODO Auto-generated constructor stub 
    init(); 
} 

public PerspectiveDistortView(Context context, AttributeSet attrs, 
           int defStyle) { 
    super(context, attrs, defStyle); 
    // TODO Auto-generated constructor stub 
    init(); 
} 

private void init() { 
    this.setOnTouchListener(this); 
    paintRect = new Paint(); 
    paintRect.setColor(0xffff0000); 
    paintRect.setAntiAlias(true); 
    paintRect.setDither(true); 
    paintRect.setStyle(Paint.Style.STROKE); 
    paintRect.setStrokeJoin(Paint.Join.BEVEL); 
    paintRect.setStrokeCap(Paint.Cap.BUTT); 
    paintRect.setStrokeWidth(3); 
    paintCircle = new Paint(); 
    paintCircle.setColor(0xff000000); 
    paintCircle.setAntiAlias(true); 
    paintCircle.setDither(true); 
    paintCircle.setStyle(Paint.Style.FILL_AND_STROKE); 
    paintCircle.setStrokeJoin(Paint.Join.BEVEL); 
    paintCircle.setStrokeCap(Paint.Cap.BUTT); 

    LEFT = 90; 
    TOP = 40; 
    RIGHT = 500; 
    BOTTOM = 700; 
    CIRCLE_TOP_LEFT = new Point(LEFT, TOP); 
    CIRCLE_TOP_RIGHT = new Point(RIGHT, TOP); 
    CIRCLE_BOTTOM_LEFT = new Point(LEFT, BOTTOM); 
    CIRCLE_BOTTOM_RIGHT = new Point(RIGHT, BOTTOM); 

    image = BitmapFactory.decodeResource(getResources(), R.drawable.penguins); 

    matrix2 = new Matrix(); 
} 

@Override 
protected void onDraw(Canvas canvas) { 
    // Free Transform bitmap 
    int bw = image.getWidth(); 
    int bh = image.getHeight(); 

    float[] pts = { 
      // source 
      0, 0, 
      0, bh, 
      bw, bh, 
      bw, 0, 
      // destination 
      0, 0, 
      0, 0, 
      0, 0, 
      0, 0}; 
    pts[8] = CIRCLE_TOP_LEFT.x; 
    pts[9] = CIRCLE_TOP_LEFT.y; 
    pts[10] = CIRCLE_BOTTOM_LEFT.x; 
    pts[11] = CIRCLE_BOTTOM_LEFT.y; 
    pts[12] = CIRCLE_BOTTOM_RIGHT.x; 
    pts[13] = CIRCLE_BOTTOM_RIGHT.y; 
    pts[14] = CIRCLE_TOP_RIGHT.x; 
    pts[15] = CIRCLE_TOP_RIGHT.y; 

    matrix2.setPolyToPoly(pts, 0, pts, 8, 4); 
    canvas.drawBitmap(image, matrix2, null); 
    isTouchCirclePoints = false; 

    // line left 
    canvas.drawLine(CIRCLE_TOP_LEFT.x, CIRCLE_TOP_LEFT.y, CIRCLE_BOTTOM_LEFT.x, CIRCLE_BOTTOM_LEFT.y, paintRect); 
    // line top 
    canvas.drawLine(CIRCLE_TOP_LEFT.x, CIRCLE_TOP_LEFT.y, CIRCLE_TOP_RIGHT.x, CIRCLE_TOP_RIGHT.y, paintRect); 
    // line right 
    canvas.drawLine(CIRCLE_TOP_RIGHT.x, CIRCLE_TOP_RIGHT.y, CIRCLE_BOTTOM_RIGHT.x, CIRCLE_BOTTOM_RIGHT.y, paintRect); 
    // line bottom 
    canvas.drawLine(CIRCLE_BOTTOM_LEFT.x, CIRCLE_BOTTOM_LEFT.y, CIRCLE_BOTTOM_RIGHT.x, CIRCLE_BOTTOM_RIGHT.y, paintRect); 
    // circle top left 
    canvas.drawCircle(CIRCLE_TOP_LEFT.x, CIRCLE_TOP_LEFT.y, 10, paintCircle); 
    // circle top right 
    canvas.drawCircle(CIRCLE_TOP_RIGHT.x, CIRCLE_TOP_RIGHT.y, 10, paintCircle); 
    // circle bottom left 
    canvas.drawCircle(CIRCLE_BOTTOM_LEFT.x, CIRCLE_BOTTOM_LEFT.y, 10, paintCircle); 
    // circle bottom right 
    canvas.drawCircle(CIRCLE_BOTTOM_RIGHT.x, CIRCLE_BOTTOM_RIGHT.y, 10, paintCircle); 
} 

@Override 
public boolean onTouch(View view, MotionEvent event) { 
    lastX = (int) event.getX(); 
    lastY = (int) event.getY(); 

    if (inCircle(lastX, lastY, CIRCLE_TOP_LEFT.x, CIRCLE_TOP_LEFT.y, 40)) { 
     isTouchCirclePoints = true; 
     CIRCLE_TOP_LEFT.set(lastX, lastY); 
    } else if (inCircle(lastX, lastY, CIRCLE_TOP_RIGHT.x, CIRCLE_TOP_RIGHT.y, 40)) { 
     isTouchCirclePoints = true; 
     CIRCLE_TOP_RIGHT.set(lastX, lastY); 
    } else if (inCircle(lastX, lastY, CIRCLE_BOTTOM_LEFT.x, CIRCLE_BOTTOM_LEFT.y, 40)) { 
     isTouchCirclePoints = true; 
     CIRCLE_BOTTOM_LEFT.set(lastX, lastY); 
    } else if (inCircle(lastX, lastY, CIRCLE_BOTTOM_RIGHT.x, CIRCLE_BOTTOM_RIGHT.y, 40)) { 
     isTouchCirclePoints = true; 
     CIRCLE_BOTTOM_RIGHT.set(lastX, lastY); 
    } 
    invalidate(); 
    return true; 
} 

private boolean inCircle(float x, float y, float circleCenterX, float circleCenterY, float circleRadius) { 
    double dx = Math.pow(x - circleCenterX, 2); 
    double dy = Math.pow(y - circleCenterY, 2); 

    if ((dx + dy) < Math.pow(circleRadius, 2)) { 
     return true; 
    } else { 
     return false; 
    } 
} 
} 
+0

Просто для тех, кто приключен к Google, который застрял с этим в iOS/Swift. [Вот QA с решением для iOS!] (Http: // stackoverflow.com/a/39981054/294884) – Fattie

+0

послал щедрость, чтобы поблагодарить вас за этот замечательный ответ !!! – Fattie

+0

@JoeBlow Спасибо за вашу щедрость! Я рад, что это помогает. – h2nghia

Смежные вопросы