2012-12-18 25 views
6

Czy ktokolwiek z was ma pomysł, jak zniekształcić obraz w dowolnym czworokącie? Chcę zaimplementować obraz, który można przeciągnąć w dowolnym kierunku, zniekształcając obraz. Ktoś ma pomysł, jak to zrobić? Używam i piszę rzeczy na Androida od jakiegoś czasu, ale wygląda na to, że android nie ma na to żadnej funkcji. Nie mam ochoty pisać nowej biblioteki matematycznej :).Jak zniekształcić obraz do dowolnego kwadratu?

Pozdrowienia, Can

Odpowiedz

8

Wygląda jak trzeba Canvas.drawBitmapMesh. W zestawie SDK systemu Android znajduje się przykład pokazujący, jak z niego korzystać.

Do rysowania mapy bitowej na Canvas należy użyć Matrix. Możesz łatwo stworzyć taką transformację, która będzie pasować do twojego obrazu bitmapowego do dowolnego czworokąta z metodą Matrix.polyToPoly. Będzie to wyglądać tak:

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); 

Gdzie x0-x3, y3-y0 są współrzędne wierzchołków swoją czworobok.

+0

Wielkie dzięki za szybką odpowiedź. Czuję się głupio, ponieważ nie widzę metody setPolytoPoly w klasie macierzy, ale to powinno zadziałać. Testuję to jutro w pracy :-)! – Geki

+0

Problem z tą metodą polega na tym, że jest ona bardzo obciążająca obliczeniowo, a przez to bardzo wolna na urządzeniach o niskim końcu. – user2498079

+0

Tak, ale nie mamy praktycznie innego wyjścia, jak to zrobić. – Geki

3

Wystąpił problem z kodem. Mimo, że jest to właściwa metoda, zostały odwrócone pływaka [] Parametry, jak pojawia się w Android dokumentacji:

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) 

Więc, zgodnie z kodem, matryca powinna być utworzony jako:

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); 

w ten sposób, aplikacja działa poprawnie, tak jak pokazano na obrazku:

enter image description here

Według drugiej strony, w odniesieniu do tego, co mówi w swoim user2498079 skomentuj problem obliczeniowy w urządzeniach niskonakładowych, możesz użyć kilku łatwych do wykonania technik, aby zmniejszyć rozmiar obrazu źródłowego (i na przykład głębokość koloru) przed obliczeniem konwersji macierzy. Ułatwi to telefonowi niższej klasy zrealizowanie tego zadania.

1

Mam nadzieję, że to pomoże. Narożnik w lewym górnym rogu, w prawym górnym rogu działa z wyjątkiem lewego dolnego i prawego dolnego rogu. Czy ktoś może to dodać? Nie mogę wymyślić, jak wykonać dolne części. :)

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. Naprawiłem twój kod.

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

Dla każdego, kto googlował, utknął, robiąc to w iOS/Swift. [Oto kontrola jakości z rozwiązaniem dla systemu iOS!] (Http: // stackoverflow.com/a/39981054/294884) – Fattie

+0

wysłał nagrodę, aby podziękować za tę wspaniałą odpowiedź !!! – Fattie

+0

@JoeBlow Dzięki za nagrodę! Cieszę się, że to pomaga. – h2nghia