2012-12-18 20 views
6

Qualcuno di voi ha un'idea, come distorcere un'immagine in qualsiasi quadrangolo? Voglio implementare un'immagine, che puoi tirare qualsiasi angolo in qualsiasi direzione, distorcendo l'immagine. Qualcuno ha un'idea di come farlo? Io uso e scrivo cose in Android per un po 'di tempo, ma non sembra che Android abbia una funzione per questo. Non mi sento davvero di scrivere una nuova biblioteca matematica :).Come distorcere un'immagine su qualsiasi quadrangolo?

Saluti, Can

risposta

8

Sembra che tu abbia bisogno Canvas.drawBitmapMesh. C'è un esempio in Android SDK che mostra come usarlo.

È necessario utilizzare Matrix per disegnare la bitmap su Canvas. È possibile creare facilmente tale trasformazione che si adatta all'immagine bitmap in qualsiasi quadrangolo con il metodo Matrix.polyToPoly. Sarà simile a questa:

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

Dove x0-x3, y0-Y3 sono le coordinate dei vertici quadrilatero.

+0

Grazie mille per la risposta rapida. Mi sento stupido per non aver visto il metodo setPolytoPoly nella classe matrix, ma dovrebbe funzionare. Lo metterò alla prova domani al lavoro :-)! – Geki

+0

Il problema con questo metodo è che è molto computazionalmente intenso e quindi molto lento sui dispositivi di fascia bassa. – user2498079

+0

Sì, ma non abbiamo praticamente altra scelta che farlo. – Geki

3

C'è un problema con il tuo codice. Anche se è il metodo corretto, si è invertito i parametri [] float, come appare nella documentazione 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) 

Così, secondo con il codice, la matrice deve essere creata come:

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

in questo modo, l'applicazione funziona bene, come mostrato nell'immagine:

enter image description here

con altra parte, per quanto riguarda ciò che dice nel suo user2498079 commentare il problema computazionale nei dispositivi di fascia bassa, è possibile utilizzare alcune tecniche facili da usare per ridurre la dimensione dell'immagine di origine (e la profondità del colore, ad esempio) prima del calcolo della conversione della matrice. Sarebbe più facile per il telefono di fascia bassa realizzare questo compito.

1

Spero che questo aiuti. L'angolo in alto a sinistra, in alto a destra, funziona tranne che per l'angolo in basso a sinistra e in basso a destra. Qualcuno può aggiungerlo. Non riesco a capire come fare le parti inferiori. :)

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. Ho corretto il tuo codice.

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

Solo per chiunque chiacchiera qui chi è bloccato a farlo in iOS/Swift. [Ecco un controllo di qualità con una soluzione in calo per iOS!] (Http: // StackOverflow.com/a/39981054/294884) – Fattie

+0

inviato una taglia per ringraziarvi per questa grande risposta !!! – Fattie

+0

@JoeBlow Grazie per la tua generosità! Sono contento che aiuti. – h2nghia