/* * CDE - Common Desktop Environment * * Copyright (c) 1993-2012, The Open Group. All rights reserved. * * These libraries and programs are free software; you can * redistribute them and/or modify them under the terms of the GNU * Lesser General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * These libraries and programs are distributed in the hope that * they will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public * License along with these libraries and programs; if not, write * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301 USA */ /**--------------------------------------------------------------------- *** *** (c)Copyright 1991 Hewlett-Packard Co. *** *** RESTRICTED RIGHTS LEGEND *** Use, duplication, or disclosure by the U.S. Government is subject to *** restrictions as set forth in sub-paragraph (c)(1)(ii) of the Rights in *** Technical Data and Computer Software clause in DFARS 252.227-7013. *** Hewlett-Packard Company *** 3000 Hanover Street *** Palo Alto, CA 94304 U.S.A. *** Rights for non-DOD U.S. Government Departments and Agencies are as set *** forth in FAR 52.227-19(c)(1,2). *** ***-------------------------------------------------------------------*/ static char rcsid[] = "$XConsortium: ilhplrotation.c /main/3 1995/10/23 15:48:35 rswiston $"; static char version[] = "$XConsortium: ilhplrotation.c /main/3 1995/10/23 15:48:35 rswiston $"; static char vdate[] = "$XConsortium: ilhplrotation.c /main/3 1995/10/23 15:48:35 rswiston $"; # include # include # include # include /* Get definition of */ # include /* FLX_MAX */ # include "ilhplrotation.h" #ifdef MSDOS # define P16BIT 1 #endif #ifdef P16BIT /* for 16bit compilers */ # define OPERAND short # define OPERAND2 unsigned short # define PIXEL_UNIT 16384 /* pixel width, used to convert floating point operations into integer operations */ # define PIXEL_BITS 14 /* log(PIXEL_UNIT), must be <= 14 (16-2) */ # define INTER_LEVELS 256 /* # diff. interpolated values */ # define INTER_BITS 8 /* # diff. interp. values = 2^INTER_BITS */ # define PIX_SHIFT 6 /* must = PIXEL_BITS - INTER_BITS */ #else /* for 32bit compilers */ # define OPERAND long # define OPERAND2 long # define PIXEL_UNIT 1073741824L /* pixel width, used to convert floating point operations into integer operations */ # define PIXEL_BITS 30 /* log(PIXEL_UNIT), must be <= 30 (32-2) */ # define INTER_LEVELS 256 /* # diff. interpolated values */ # define INTER_BITS 8 /* log(INTER_LEVELS), 10 is sufficient */ # define PIX_SHIFT 22 /* must = PIXEL_BITS - INTER_BITS */ #endif # define PI 3.14159265359 typedef enum {Q_FIRST, Q_SECOND, Q_THIRD, Q_FOURTH, Q_0_180, Q_90_270} QUADRANT_TYPE; /* 1st quad., 2nd quad., 3rd quad., 4th quad., 0 or 180 degree, 90 or 270 */ /* * Build_Mult_Table * Build Multiplication Table: table[i][j] = (i*j) >> INTER_BITS where * 0 <= i < INTER_LEVELS, -255 <= j <= 255. * Return 0 if successful, -1 if memory can't be allocated. */ static short Build_Mult_Table(table) OPERAND *table[INTER_LEVELS]; { short i, j; OPERAND value, *p; /* Allocate Memory */ for (i = 0; i < INTER_LEVELS; i++) { if ((table[i] = (OPERAND *) malloc(511*sizeof(OPERAND)))==NULL) { for (j = 0; j < i; j++) { free(table[j]); table[j] = NULL; } return(-1); } } /* Fill in Data */ for (i = 0; i < INTER_LEVELS; i++) { for (p = table[i],value = -255*i, j= -255; j <256; j++, value += i) { *p++ = value >> INTER_BITS; } } /* Shift all table[j] pointers */ for (i = 0; i < INTER_LEVELS; i++) { table[i] += 255; } return(0); } /* * Free_Mult_Table * Free space of Multiplication Table. */ static void Free_Mult_Table(table) OPERAND *table[INTER_LEVELS]; { short i; /* Shift then free all table[j] pointers */ for (i = 0; i < INTER_LEVELS; i++) { if (table[i] != NULL) { free(table[i] - 255); table[i] = NULL; } } } /* Static Variables */ static short r_input_w , r_input_h; /* dimension of input image */ static short r_output_w, r_output_h; /* dimension of output image */ static short r_image_bytes; /* number of bytes per image pixel */ static short r_rotation_mode; /* rotation mode */ static unsigned char r_bg_red, /* background color, r_bg_red is background */ r_bg_green, r_bg_blue; /* gray level if r_image_bytes == 1 */ static float r_angle; /* rotation angle */ static float r_sinA, r_cosA; /* sin, cos of rotation angle */ static float r_tanA, r_cotA; /* tan, cotan of rotation angle */ static QUADRANT_TYPE r_quadrant; /* which quadrant that r_angle belongs to */ static DATA_IN_PTR r_buf = NULL; /* contains image data(RGB interl. if color)*/ static DATA_IN_PTR r_data; /* points to 1st pixel in r_buf */ static OPERAND r_next_row_out; /* next output row number */ static OPERAND r_next_row_in ; /* next input row number */ static OPERAND r_first_mark; /* These 2 variables are used to compute */ static OPERAND r_last_mark; /* First_Pixel and Last_Pixel */ static short r_mul_tbls_valid = 0; /* validity of multiplication tables below */ static OPERAND *r_mul_tbls[INTER_LEVELS]; static void Free_All_Buffers() { if (r_buf != NULL) free(r_buf); r_buf = NULL; if (r_mul_tbls_valid) { Free_Mult_Table(r_mul_tbls); r_mul_tbls_valid = 0; } } static OPERAND First_Mark() { if (r_rotation_mode == 0) { return((OPERAND) (r_input_w*r_sinA - 0.5)); } else { return((OPERAND) (r_input_w*r_sinA)); } } static OPERAND First_Pixel(row_num) OPERAND row_num; { if ((r_angle == 0.0) || (r_angle == 90.0) || (r_angle == 180.0) || (r_angle == 270.0)) { return(0); } if (r_rotation_mode == 0) { if (row_num <= r_first_mark) { return(r_input_w*r_cosA - (row_num+0.5)*r_cotA + 0.5); } else { return((row_num+0.5 - r_input_w*r_sinA)*r_tanA + 0.5); } } else { if (row_num < r_first_mark) { return(r_input_w*r_cosA - (row_num+1)*r_cotA); } else if (row_num == r_first_mark) { return(0); } else { return((row_num - r_input_w*r_sinA)*r_tanA); } } } static OPERAND Last_Mark() { if (r_rotation_mode == 0) { return((OPERAND) (r_input_h*r_cosA - 0.5)); } else { return((OPERAND) (r_input_h*r_cosA)); } } static OPERAND Last_Pixel(row_num) OPERAND row_num; { if ((r_angle == 0.0) || (r_angle == 180.0)) { return(r_input_w); } else if ((r_angle == 90.0) || (r_angle == 270.0)) { return(r_input_h); } if (r_rotation_mode == 0) { if (row_num <= r_last_mark) { return(r_input_w*r_cosA + (row_num+0.5)*r_tanA - 0.5); } else { return(r_input_w*r_cosA + r_input_h/r_sinA - (row_num+0.5)*r_cotA - 0.5); } } else { if (row_num < r_last_mark) { return(r_input_w*r_cosA + (row_num+1)*r_tanA); } else if (row_num == r_last_mark) { return(r_output_w - 1); } else { return(r_input_w*r_cosA + r_input_h/r_sinA - row_num*r_cotA); } } } static short Output_Width() { if (r_rotation_mode == 0) { return((short) (r_input_w*r_cosA + r_input_h*r_sinA + 0.5)); } else { return((short) ceil(r_input_w*r_cosA + r_input_h*r_sinA)); } } static short Output_Height() { if (r_rotation_mode == 0) { return((short) (r_input_w*r_sinA + r_input_h*r_cosA + 0.5)); } else { return((short) ceil(r_input_w*r_sinA + r_input_h*r_cosA)); } } /* * IR_Rotate_Image_Begin * Returns a pointer to the location where the image data of the first row * must be read into, or NULL if memory can not be allocated. */ DATA_IN_PTR _il_Rotate_Image_Begin( short input_w, /* Input image width */ short input_h, /* Input image height */ short input_bytes, /* Input image bytes per pixel (1 or 3). 1 : gray scale image (8 bits / pixel). 3 : color image (24 bits / pixel). */ short rotation_mode, /* 0 : subsampling (nearest pixel) !0 : bi-linear interpolation */ float angle, /* Rotation Angle */ unsigned char bg_red, /* background color */ unsigned char bg_green, /* , if input_bytes == 1, */ unsigned char bg_blue, /* background gray level = bg_red */ short *output_w, /* Returned value for output image width*/ short *output_h /* Returned val. for output image height*/ ) { double angleRad; DATA_IN_PTR top, bot; short i; /* Initialize global variables */ Free_All_Buffers(); if (rotation_mode == 0) { r_buf = (DATA_IN_PTR) malloc((size_t)input_w*(size_t)input_h* input_bytes*sizeof(DATA_IN)); r_data = r_buf; } else { /* Add 2 pix above, below, to the left & right of the orig. image */ r_buf = (DATA_IN_PTR) malloc((size_t)(input_w+4)*(size_t)(input_h+4)* input_bytes*sizeof(DATA_IN)); r_data = r_buf + ((input_w+4)*2 + 2)*input_bytes; } if (r_buf == NULL) { Free_All_Buffers(); return(NULL); } r_input_w = input_w; r_input_h = input_h; r_image_bytes = input_bytes; r_rotation_mode = rotation_mode; r_bg_red = bg_red; r_bg_green = bg_green; r_bg_blue = bg_blue; /* Normalize angle to the range [0, 360) */ r_angle = angle; while (r_angle < 0.0) r_angle += 360.0; while (r_angle >= 360.0) r_angle -= 360.0; if (r_angle == 0.0) r_quadrant = Q_0_180; else if (r_angle < 90.0) r_quadrant = Q_FIRST; else if (r_angle == 90.0) r_quadrant = Q_90_270; else if (r_angle < 180.0) r_quadrant = Q_SECOND; else if (r_angle == 180.0) r_quadrant = Q_0_180; else if (r_angle < 270.0) r_quadrant = Q_THIRD; else if (r_angle == 270.0) r_quadrant = Q_90_270; else r_quadrant = Q_FOURTH; angleRad = ((double) angle*PI) / 180.0; r_sinA = sin(angleRad); r_cosA = cos(angleRad); if ((r_angle == 0.0) || (r_angle == 180.0)) { r_tanA = 0.0; r_cotA = FLT_MAX; } else if ((r_angle == 90.0) || (r_angle == 270.0)) { r_tanA = FLT_MAX; r_cotA = 0.0; } else { r_tanA = tan(angleRad); r_cotA = 1.0/r_tanA; } r_next_row_out = 0; r_next_row_in = 0; /* These calls must follow the init. of r_input_w, r_rotation_mode... */ r_first_mark = First_Mark(); r_last_mark = Last_Mark(); *output_w = r_output_w = Output_Width(); *output_h = r_output_h = Output_Height(); if (rotation_mode != 0) { /* Fill in the extra pixels with background color */ top = r_buf; bot = r_buf + ((input_h+2)*(input_w+4) - 2)*input_bytes; if (input_bytes == 1) { for (i = (input_w+4)*2 + 2; i > 0; --i) { *top++ = bg_red; *bot++ = bg_red; } } else { for (i = (input_w+4)*2 + 2; i > 0; --i) { *top++ = bg_red; *top++ = bg_green; *top++ = bg_blue; *bot++ = bg_red; *bot++ = bg_green; *bot++ = bg_blue; } } r_mul_tbls_valid = !Build_Mult_Table(r_mul_tbls); } return(r_data); } /* * IR_Rotate_Send_Row */ short _il_Rotate_Send_Row( DATA_IN_PTR *input_data ) { DATA_IN_PTR data; short i; data = *input_data + r_input_w*r_image_bytes; if (r_rotation_mode != 0) { if (r_image_bytes == 1) { for (i = 0; i < 4; i++) { *data++ = r_bg_red; } } else { for (i = 0; i < 4; i++) { *data++ = r_bg_red; *data++ = r_bg_green; *data++ = r_bg_blue; } } } *input_data = data; if (++r_next_row_in == r_input_h) { return(r_output_h); } else { return(0); } } /* * IR_Rotate_Get_Row_Mode0 */ static void _il_Rotate_Get_Row_Mode0(out) DATA_OUT_PTR out; { OPERAND first_pix, last_pix, col; float Y, x, y; OPERAND ex, ey, dx, dy; DATA_IN_PTR data; /* Find coord of first & last pixels in the new image coord. system */ first_pix = First_Pixel(r_next_row_out); last_pix = Last_Pixel (r_next_row_out); /* Find coord of first pixel in the original image coord. system */ Y = r_next_row_out + 0.5 - r_input_w*r_sinA; x = (first_pix + 0.5)*r_cosA - Y*r_sinA; y = (first_pix + 0.5)*r_sinA + Y*r_cosA; /* ex, ey are the distances from x, y respectively to the upper left corner of the pixel surrounding the point (x,y), i.e. ________ | | ^ | | ey | x |-- v |______| || */ ex = (x - (long) x)*(double)PIXEL_UNIT; ey = (y - (long) y)*(double)PIXEL_UNIT; /* Do Not round off, Truncate instead to avoid overflow */ dx = (OPERAND)(r_cosA*PIXEL_UNIT) - PIXEL_UNIT; dy = (OPERAND)(r_sinA*PIXEL_UNIT) - PIXEL_UNIT; if (r_image_bytes == 3) { size_t input_row_size; for (col = 0; col < first_pix; col++) { *out++ = r_bg_red; *out++ = r_bg_green; *out++ = r_bg_blue; } input_row_size = r_input_w*(size_t)3; data = r_data + (size_t)3*(((long)y-1)*r_input_w + ((long)x)); for (; col <= last_pix; col++, ex += dx, ey += dy) { if (ex < 0) { ex += PIXEL_UNIT; data -= 3; } if (ey >= 0) { data += input_row_size; } else { ey += PIXEL_UNIT; } *out++ = *data++; *out++ = *data++; *out++ = *data++; } for (; col < r_output_w; col++) { *out++ = r_bg_red; *out++ = r_bg_green; *out++ = r_bg_blue; } } else { for (col = 0; col < first_pix; col++) { *out++ = r_bg_red; } data = r_data + ((long)y-1)*r_input_w + ((long)x-1); for (; col <= last_pix; col++, ex += dx, ey += dy) { if (ex >= 0) { ++data; } else { ex += PIXEL_UNIT; } if (ey >= 0) { data += r_input_w; } else { ey += PIXEL_UNIT; } *out++ = *data; } for (; col < r_output_w; col++) { *out++ = r_bg_red; } } ++r_next_row_out; } /* * IR_Rotate_Get_Row_Mode1 */ static void _il_Rotate_Get_Row_Mode1(out) DATA_OUT_PTR out; { OPERAND first_pix, last_pix, col; float Y, x, y; OPERAND ex, ey, dx, dy; DATA_IN_PTR data, data2; OPERAND val1, val2, x1, y1, t; size_t input_row_size; /* Find coord of first & last pixels in the new image coord. system */ first_pix = First_Pixel(r_next_row_out); last_pix = Last_Pixel (r_next_row_out); /* Find coord of first pixel in the original image coord. system */ Y = r_next_row_out + 0.5 - r_input_w*r_sinA; x = (first_pix + 0.5)*r_cosA - Y*r_sinA; y = (first_pix + 0.5)*r_sinA + Y*r_cosA; /* ex, ey are the distances from x, y respectively to the center of the pixel right above and to the left of the point(x,y) , i.e. * * ^ ey x -- v (the center of the upper left pixel is at (floor(x-0.5)+0.5, floor(y-0.5)+0.5) ) * * || */ ex = (x - (floor(x-0.5)+0.5))*(double)PIXEL_UNIT; ey = (y - (floor(y-0.5)+0.5))*(double)PIXEL_UNIT; /* Do Not round off, Truncate instead to avoid overflow */ dx = (OPERAND)(r_cosA*PIXEL_UNIT) - PIXEL_UNIT; dy = (OPERAND)(r_sinA*PIXEL_UNIT) - PIXEL_UNIT; input_row_size = (r_input_w+4)*(size_t)r_image_bytes; if (r_image_bytes == 3) { for (col = 0; col < first_pix; col++) { *out++ = r_bg_red; *out++ = r_bg_green; *out++ = r_bg_blue; } if (r_mul_tbls_valid) { OPERAND *tex, *tey; data = r_data + (((size_t)floor(y-0.5)-1)*(r_input_w+4) + (size_t)floor(x-0.5))*r_image_bytes; data2 = data + input_row_size; for (; col <= last_pix; col++, ex += dx, ey += dy) { if (ex < 0) { ex += PIXEL_UNIT; data -= 3; data2 -= 3; } if (ey >= 0) { data += input_row_size; data2 += input_row_size; } else { ey += PIXEL_UNIT; } tex = r_mul_tbls[ex >> PIX_SHIFT]; tey = r_mul_tbls[ey >> PIX_SHIFT]; x1 = *data++; val1 = *(tex + ((size_t)(*(data +2)) - x1)) + x1; x1 = *data2++; val2 = *(tex + ((size_t)(*(data2+2)) - x1)) + x1; *out++ = *(tey + (val2 - val1)) + val1; x1 = *data++; val1 = *(tex + ((size_t)(*(data +2)) - x1)) + x1; x1 = *data2++; val2 = *(tex + ((size_t)(*(data2+2)) - x1)) + x1; *out++ = *(tey + (val2 - val1)) + val1; x1 = *data++; val1 = *(tex + ((size_t)(*(data +2)) - x1)) + x1; x1 = *data2++; val2 = *(tex + ((size_t)(*(data2+2)) - x1)) + x1; *out++ = *(tey + (val2 - val1)) + val1; } } else { #ifdef P16BIT data = r_data + (((size_t)floor(y-0.5)-1)*(r_input_w+4) + ((size_t)floor(x-0.5)-1))*r_image_bytes; for (; col <= last_pix; col++, ex += dx, ey += dy) { if (ex >= 0) { data += 3; } else { ex += PIXEL_UNIT; } if (ey >= 0) { data += input_row_size; } else { ey += PIXEL_UNIT; } data2 = data + input_row_size; x1 = PIXEL_UNIT - ex; y1 = PIXEL_UNIT - ey; val1 = (*(data +3)*ex + *(data ) * x1) >> PIXEL_BITS; val2 = (*(data2+3)*ex + *(data2) * x1) >> PIXEL_BITS; *out++ = (val2*ey + val1*y1) >> PIXEL_BITS; val1 = (*(data +4)*ex + *(data +1)*x1) >> PIXEL_BITS; val2 = (*(data2+4)*ex + *(data2+1)*x1) >> PIXEL_BITS; *out++ = (val2*ey + val1*y1) >> PIXEL_BITS; val1 = (*(data +5)*ex + *(data +2)*x1) >> PIXEL_BITS; val2 = (*(data2+5)*ex + *(data2+2)*x1) >> PIXEL_BITS; *out++ = (val2*ey + val1*y1) >> PIXEL_BITS; } #else data = r_data + (((size_t)floor(y-0.5)-1)*(r_input_w+4) + (size_t)floor(x-0.5))*r_image_bytes; for (; col <= last_pix; col++, ex += dx, ey += dy) { if (ex < 0) { ex += PIXEL_UNIT; data -= 3; } if (ey >= 0) { data += input_row_size; } else { ey += PIXEL_UNIT; } data2 = data + input_row_size; x1 = ex >> PIX_SHIFT; y1 = ey >> PIX_SHIFT; t = *data++; val1 = ((((OPERAND)(*(data +2))-t)*x1) >> INTER_BITS) + t; t = *data2++; val2 = ((((OPERAND)(*(data2+2))-t)*x1) >> INTER_BITS) + t; *out++ = (((val2 - val1)*y1) >> INTER_BITS) + val1; t = *data++; val1 = ((((OPERAND)(*(data +2))-t)*x1) >> INTER_BITS) + t; t = *data2++; val2 = ((((OPERAND)(*(data2+2))-t)*x1) >> INTER_BITS) + t; *out++ = (((val2 - val1)*y1) >> INTER_BITS) + val1; t = *data++; val1 = ((((OPERAND)(*(data +2))-t)*x1) >> INTER_BITS) + t; t = *data2; val2 = ((((OPERAND)(*(data2+3))-t)*x1) >> INTER_BITS) + t; *out++ = (((val2 - val1)*y1) >> INTER_BITS) + val1; } #endif } for (; col < r_output_w; col++) { *out++ = r_bg_red; *out++ = r_bg_green; *out++ = r_bg_blue; } } else { for (col = 0; col < first_pix; col++) { *out++ = r_bg_red; } if (r_mul_tbls_valid) { OPERAND *tex; data = r_data + ((size_t)floor(y-0.5)-1)*input_row_size + (size_t)floor(x-0.5); data2 = data + input_row_size; for (; col <= last_pix; col++, ex += dx, ey += dy) { if (ex < 0) { ex += PIXEL_UNIT; --data; --data2; } if (ey >= 0) { data += input_row_size; data2 += input_row_size; } else { ey += PIXEL_UNIT; } tex = r_mul_tbls[ex >> PIX_SHIFT]; x1 = *data++; val1 = *(tex + ((size_t)(*data ) - x1)) + x1; x1 = *data2++; val2 = *(tex + ((size_t)(*data2) - x1)) + x1; *out++ = *(r_mul_tbls[ey >>PIX_SHIFT] + (val2 - val1)) + val1; } } else { #ifdef P16BIT data = r_data + ((size_t)floor(y-0.5)-1)*input_row_size + ((size_t)floor(x-0.5)-1); data2 = data + input_row_size; for (; col <= last_pix; col++, ex += dx, ey += dy) { if (ex >= 0) { ++data; ++data2; } else { ex += PIXEL_UNIT; } if (ey >= 0) { data += input_row_size; data2 += input_row_size; } else { ey += PIXEL_UNIT; } val1 =(*(data +1)*ex + *(data )*(PIXEL_UNIT-ex)) >>PIXEL_BITS; val2 =(*(data2+1)*ex + *(data2)*(PIXEL_UNIT-ex)) >>PIXEL_BITS; *out++ =(val2*ey + val1*(PIXEL_UNIT-ey)) >> PIXEL_BITS; } #else data = r_data + ((size_t)floor(y-0.5)-1)*input_row_size + (size_t)floor(x-0.5); data2 = data + input_row_size; for (; col <= last_pix; col++, ex += dx, ey += dy) { if (ex < 0) { ex += PIXEL_UNIT; --data; --data2; } if (ey >= 0) { data += input_row_size; data2 += input_row_size; } else { ey += PIXEL_UNIT; } x1 = ex >> PIX_SHIFT; t = *data++; val1 = ((((OPERAND)(*data) - t)*x1) >> INTER_BITS) + t; t = *data2++; val2 = ((((OPERAND)(*data2)- t)*x1) >> INTER_BITS) + t; *out++ = (((val2-val1)*(ey >> PIX_SHIFT)) >>INTER_BITS) +val1; } #endif } for (; col < r_output_w; col++) { *out++ = r_bg_red; } } ++r_next_row_out; } /* * IR_Rotate_Get_Row */ void _il_Rotate_Get_Row( DATA_OUT_PTR out ) { if (r_rotation_mode == 0) _il_Rotate_Get_Row_Mode0(out); else _il_Rotate_Get_Row_Mode1(out); } void _il_Rotate_Image_End() { Free_All_Buffers(); }