kdbanman / TwistTouch

Android project toying with touch gestures and fast image processing.
0 stars 0 forks source link

Swirl on 2 finger rotation #20

Closed kdbanman closed 8 years ago

kdbanman commented 8 years ago

Swirl code

#define C_PI 3.141592653589793238462643383279502884197169399375

void CImageProcessor::Swirl(Bitmap * pBitmap, double factor)
{

    // This function effectively swirls an image

    int width = pBitmap->GetWidth();
    int height = pBitmap->GetHeight();

    double cX = (double)width/2.0f;
    double cY = (double)height/2.0f;

    BitmapData bitmapData;
    pBitmap->LockBits(&Rect(0,0,pBitmap->GetWidth(), pBitmap->GetHeight()), ImageLockModeWrite, PixelFormat32bppARGB, &bitmapData);

    unsigned int *pRawBitmapOrig = (unsigned int*)bitmapData.Scan0;   // for easy access and indexing

    unsigned int *pBitmapCopy = new unsigned int[bitmapData.Stride*height/4];

    memcpy(pBitmapCopy, pRawBitmapOrig, (bitmapData.Stride*height/4) * sizeof(unsigned int));

    int nPixels = height*bitmapData.Stride/4;

    #pragma omp parallel for
    for (int i=0; i < height; i++)
    {

        double relY = cY-i;
        for (int j=0; j < width; j++)
        {

            double relX = j-cX;
            // relX and relY are points in our UV space

            // Calculate the angle our points are relative to UV origin. Everything is in radians.
            double originalAngle;

            if (relX != 0)
            {
                originalAngle = atan(abs(relY)/abs(relX));

                if ( relX > 0 && relY < 0) originalAngle = 2.0f*C_PI - originalAngle;

                else if (relX <= 0 && relY >=0) originalAngle = C_PI-originalAngle;

                else if (relX <=0 && relY <0) originalAngle += C_PI;
            }

            else
            {
                // Take care of rare special case
                if (relY >= 0) originalAngle = 0.5f * C_PI;

                else originalAngle = 1.5f * C_PI;
            }

            // Calculate the distance from the center of the UV using pythagorean distance
            double radius = sqrt(relX*relX + relY*relY);

            // Use any equation we want to determine how much to rotate image by
            //double newAngle = originalAngle + factor*radius;  // a progressive twist
            double newAngle = originalAngle + 1/(factor*radius+(4.0f/C_PI));

            // Transform source UV coordinates back into bitmap coordinates
            int srcX = (int)(floor(radius * cos(newAngle)+0.5f));

            int srcY = (int)(floor(radius * sin(newAngle)+0.5f));

            srcX += cX;
            srcY += cY;
            srcY = height - srcY;

            // Clamp the source to legal image pixel
            if (srcX < 0) srcX = 0;

            else if (srcX >= width) srcX = width-1;

            if (srcY < 0) srcY = 0;

            else if (srcY >= height) srcY = height-1;

            // Set the pixel color
            pRawBitmapOrig[i*bitmapData.Stride/4 + j] = pBitmapCopy[srcY*bitmapData.Stride/4 + srcX];
        }
    }

    delete[] pBitmapCopy;
    pBitmap->UnlockBits(&bitmapData);
}

Finger rotation code

public class RotationGestureDetector {
    private static final int INVALID_POINTER_ID = -1;
    private float fX, fY, sX, sY;
    private int ptrID1, ptrID2;
    private float mAngle;

    private OnRotationGestureListener mListener;

    public float getAngle() {
        return mAngle;
    }

    public RotationGestureDetector(OnRotationGestureListener listener){
        mListener = listener;
        ptrID1 = INVALID_POINTER_ID;
        ptrID2 = INVALID_POINTER_ID;
    }

    public boolean onTouchEvent(MotionEvent event){
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                ptrID1 = event.getPointerId(event.getActionIndex());
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                ptrID2 = event.getPointerId(event.getActionIndex());
                sX = event.getX(event.findPointerIndex(ptrID1));
                sY = event.getY(event.findPointerIndex(ptrID1));
                fX = event.getX(event.findPointerIndex(ptrID2));
                fY = event.getY(event.findPointerIndex(ptrID2));
                break;
            case MotionEvent.ACTION_MOVE:
                if(ptrID1 != INVALID_POINTER_ID && ptrID2 != INVALID_POINTER_ID){
                    float nfX, nfY, nsX, nsY;
                    nsX = event.getX(event.findPointerIndex(ptrID1));
                    nsY = event.getY(event.findPointerIndex(ptrID1));
                    nfX = event.getX(event.findPointerIndex(ptrID2));
                    nfY = event.getY(event.findPointerIndex(ptrID2));

                    mAngle = angleBetweenLines(fX, fY, sX, sY, nfX, nfY, nsX, nsY);

                    if (mListener != null) {
                        mListener.OnRotation(this);
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                ptrID1 = INVALID_POINTER_ID;
                break;
            case MotionEvent.ACTION_POINTER_UP:
                ptrID2 = INVALID_POINTER_ID;
                break;
            case MotionEvent.ACTION_CANCEL:
                ptrID1 = INVALID_POINTER_ID;
                ptrID2 = INVALID_POINTER_ID;
                break;
        }
        return true;
    }

    private float angleBetweenLines (float fX, float fY, float sX, float sY, float nfX, float nfY, float nsX, float nsY)
    {
        float angle1 = (float) Math.atan2( (fY - sY), (fX - sX) );
        float angle2 = (float) Math.atan2( (nfY - nsY), (nfX - nsX) );

        float angle = ((float)Math.toDegrees(angle1 - angle2)) % 360;
        if (angle < -180.f) angle += 360.0f;
        if (angle > 180.f) angle -= 360.0f;
        return angle;
    }

    public static interface OnRotationGestureListener {
        public void OnRotation(RotationGestureDetector rotationDetector);
    }
}
kdbanman commented 8 years ago

dissecting other's code is hard.. I'm doing the rotation myself.