卡片翻页算法

1、描述:计算翻开的卡片到平面的投影来模拟卡片打开的效果 。视点位于卡片上边缘中点正上方。

  以w、h表示原卡片的宽高,w1、h1表示投影的宽高,H表示视点高度 , a为翻开的卡片与平面上的卡片夹角,有以下公式:

h1 = h*cosa *H /(H-h*sina)
w1 = w*H/(H-h*sina)

投影计算算法(这里仅考虑了第一象限)为:

    public ImageBuffer openCard(int[] pixels, int width, int height, double pi)
    {
        final int H = width * 3;
        double sina = Math.sin(pi);
        double cosa = Math.cos(pi);

        int newHeight = (int) (height * cosa * H / (H - height * sina));
        int newWidth = (int) (width * H / (H - height * sina));
        int[] newPixels = new int[newHeight * newWidth];
        int length = newPixels.length;

        for (int i = 0; i < height; i++)
        {
            double temp = H / (H - i * sina);
            int di = (int) (i * cosa * temp);
            int pre = i * width;
            int dPre = di * newWidth;
            int curWidth = (int) (width * temp);
            int dPos = (newWidth - curWidth) >> 1;
            for (int j = 0; j < width; j++)
            {
                int dj = (int) (j * temp);
                int index = dPre + dj + dPos;
                if (index >= length)
                {
                    index = length - 1;
                }
                newPixels[index] = pixels[pre + j];
            }
        }

        ImageBuffer ib = new ImageBuffer();
        ib.height = newHeight;
        ib.width = newWidth;
        ib.pixels = newPixels;
        return ib;
    }


public class ImageBuffer
{
    public int[] pixels;
    public int width;
    public int height;

    
    public String toString()
    {
        return "width : " + width + " height : " + height;
    }
}

2、上述算法会出现一个问题,不能保证卡片投影的每一个点都被设值。例如 如果投影面积大于原卡片面积,上述算法有部分点透明,效果比较查

      故改进算法,根据公式给每个投影上的点,找到原图上面的对应的点。公式为:

h = h1 * H / ( H*cosa +h1*sina)
w = w1 * H * cosa /( H* cosa + h1 *sina )

具体算法(这里已经考虑了第一、第二象限)为:

public static ImageBuffer reopenCard(int[] pixels, int width, int height, double pi)
    {
        final int H = width * 3;
        final double PI = 3.14; 
        final double HPI = PI/2;
        pi = pi%PI;
        double a = pi>HPI ? PI-pi : pi;
        
        double sina = Math.sin(a);
        double cosa = Math.cos(a);

        int newHeight = (int) (height * cosa * H / (H - height * sina));
        int newWidth = (int) (width * H / (H - height * sina));
        int[] newPixels = new int[newHeight * newWidth];

        for (int i = 0; i < newHeight; i++)
        {
            int h = pi>HPI ?newHeight -i : i;
            double temph = H * cosa + h * sina;
            int dh = (int) (h * H / temph);
            double tempw = H * cosa / temph;

            int curWidth = (int) (width * H / (H - dh * sina));
            int offset = (newWidth - curWidth) >> 1;
            int end = newWidth - offset;

            int pre = i * newWidth;
            int dPre = dh * width;
            for (int j = offset; j < end; j++)
            {                
                int dw = (int) ((j - offset) * tempw);
                newPixels[pre + j] = pixels[dPre + dw];
            }
        }

        ImageBuffer ib = new ImageBuffer();
        ib.height = newHeight;
        ib.width = newWidth;
        ib.pixels = newPixels;
        return ib;
    }

3、尝试使用NDK,希望能提升效率,但是测试发现效率反而有所下降,猜测可能是像素数组在java类型和c之间转换导致效率耗损:

#define HPI 1.57

JNIEXPORT jobject JNICALL
Java_com_xyl_bitmap_filter_FlipBitmap_openCard(JNIEnv *env , jobject jobj , jintArray pixels , jint width , jint height , jdouble pi )
{
  
  jdouble PI = 3.14;
  jint H = width *3;
  //pi = pi%PI;
  jdouble a = pi>HPI ? PI-pi : pi;

  jdouble sina = sin(a);
  jdouble cosa = cos(a);
  jint newHeight = (jint) (height * cosa * H / (H - height * sina));
  jint newWidth = (jint) (width * H / (H - height * sina));

  jint length = width*height;
  jint newLength = newHeight*newWidth;
  jint *jpixels ,* newPixels;

  jpixels = malloc(sizeof(jint)*length);
  (*env)->GetIntArrayRegion(env , pixels , 0 , length , jpixels);

  newPixels = malloc(sizeof(jint) *newLength);
  jint i=0,j=0;
  for (; i < newHeight; i++)
  {
    jint h = pi>HPI ?newHeight -i : i;
    jdouble temph = H * cosa + h * sina;
    jint dh = (jint) (h * H / temph);
    jdouble tempw = H * cosa / temph;
    jint curWidth = (jint) (width * H / (H - dh * sina));
    jint offset = (newWidth - curWidth) >> 1;
    jint end = newWidth - offset;
    jint pre = i * newWidth;
    jint dPre = dh * width;
    j = offset;
    for (; j < end; j++)
    {
      jint dw = (jint) ((j - offset) * tempw);
      newPixels[pre + j] = jpixels[dPre + dw];
    }
  }
  
  jintArray resultPixels = (*env)->NewIntArray(env , newLength);
  (*env)->SetIntArrayRegion(env , resultPixels , 0 ,newLength , newPixels);
  jclass imageBufferClass = (*env)->FindClass(env , "com/xyl/bitmap/filter/ImageBuffer");
  if(imageBufferClass == NULL)
  {
    return;
  }

  jmethodID cid = (*env)->GetMethodID(env , imageBufferClass ,"<init>" , "()V");
  if(cid == NULL)
  {
     return ;
  }
  jobject imageBuffer = (*env)->NewObject(env , imageBufferClass , cid );
  jfieldID widthId , heightId , pixelsId;
  widthId = (*env)->GetFieldID(env ,imageBufferClass,"width" ,"I");
  heightId = (*env)->GetFieldID(env ,imageBufferClass,"height" ,"I");
  pixelsId = (*env)->GetFieldID(env ,imageBufferClass,"pixels" ,"[I");
  if(widthId == NULL || heightId == NULL || pixelsId == NULL)
  {
    return;
  }

  (*env)->SetIntField(env , imageBuffer , widthId , newWidth);
  (*env)->SetIntField(env , imageBuffer , heightId , newHeight);
  (*env)->SetObjectField(env , imageBuffer , pixelsId , resultPixels);

  free(newPixels);
  free(jpixels);
  return imageBuffer;
}
原文地址:https://www.cnblogs.com/lipeil/p/2807382.html