Home | History | Annotate | Download | only in skin
      1 /* Copyright (C) 2007-2008 The Android Open Source Project
      2 **
      3 ** This software is licensed under the terms of the GNU General Public
      4 ** License version 2, as published by the Free Software Foundation, and
      5 ** may be copied, distributed, and modified under those terms.
      6 **
      7 ** This program is distributed in the hope that it will be useful,
      8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
      9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     10 ** GNU General Public License for more details.
     11 */
     12 #include "android/skin/trackball.h"
     13 #include "android/skin/image.h"
     14 #include "android/utils/system.h"
     15 #include "android/user-events.h"
     16 #include <math.h>
     17 
     18 /***********************************************************************/
     19 /***********************************************************************/
     20 /*****                                                             *****/
     21 /*****       T R A C K   B A L L                                   *****/
     22 /*****                                                             *****/
     23 /***********************************************************************/
     24 /***********************************************************************/
     25 
     26 // a 3-d vector
     27 typedef  double   VectorRec[3];
     28 typedef  double*  Vector;
     29 
     30 /* define FIX16_IS_FLOAT to use floats for computations */
     31 #define  FIX16_IS_FLOAT
     32 
     33 #ifdef FIX16_IS_FLOAT
     34 typedef float   Fix16;
     35 #define  FIX16_ONE           1.0
     36 #define  FIX16_FROM_FLOAT(x)  (x)
     37 #define  FIX16_TO_FLOAT(x)    (x)
     38 
     39 #else
     40 typedef int     Fix16;
     41 
     42 #define  FIX16_SHIFT  16
     43 #define  FIX16_ONE            (1 << FIX16_SHIFT)
     44 #define  FIX16_FROM_FLOAT(x)  (Fix16)((x) * FIX16_ONE)
     45 #define  FIX16_TO_FLOAT(x)    ((x)/(1.0*FIX16_ONE))
     46 
     47 #endif
     48 
     49 typedef Fix16   Fix16VectorRec[3];
     50 typedef Fix16*  Fix16Vector;
     51 
     52 static Fix16
     53 fixedvector_len( Fix16Vector  v )
     54 {
     55     double  x = FIX16_TO_FLOAT(v[0]);
     56     double  y = FIX16_TO_FLOAT(v[1]);
     57     double  z = FIX16_TO_FLOAT(v[2]);
     58     double  len = sqrt( x*x + y*y + z*z );
     59 
     60     return FIX16_FROM_FLOAT(len);
     61 }
     62 
     63 static void
     64 fixedvector_from_vector( Fix16Vector  f, Vector  v )
     65 {
     66     f[0] = FIX16_FROM_FLOAT(v[0]);
     67     f[1] = FIX16_FROM_FLOAT(v[1]);
     68     f[2] = FIX16_FROM_FLOAT(v[2]);
     69 }
     70 
     71 
     72 #ifdef FIX16_IS_FLOAT
     73 static double
     74 fixedvector_dot( Fix16Vector  u, Fix16Vector  v )
     75 {
     76     return u[0]*v[0] + u[1]*v[1] + u[2]*v[2];
     77 }
     78 #else
     79 static Fix16
     80 fixedvector_dot( Fix16Vector  u, Fix16Vector  v )
     81 {
     82     long long  t;
     83 
     84     t = (long long)u[0] * v[0] + (long long)u[1] * v[1] + (long long)u[2] * v[2];
     85     return (Fix16)(t >> FIX16_SHIFT);
     86 }
     87 #endif
     88 
     89 static int
     90 norm( int  dx, int  dy )
     91 {
     92     return (int) sqrt( dx*1.0*dx + dy*1.0*dy );
     93 }
     94 
     95 /*** ROTATOR: used to rotate the reference axis when mouse motion happens
     96  ***/
     97 
     98 typedef struct
     99 {
    100     VectorRec   d;
    101     VectorRec   n;
    102     double      angle;
    103 
    104 } RotatorRec, *Rotator;
    105 
    106 
    107 #define  ANGLE_FACTOR  (M_PI/200)
    108 
    109 static void
    110 rotator_reset( Rotator  rot, int  dx, int  dy )
    111 {
    112     double  len = sqrt( dx*dx + dy*dy );
    113     double  zx, zy;
    114 
    115     if (len < 1e-3 ) {
    116         zx = 1.;
    117         zy = 0;
    118     } else {
    119         zx = dx / len;
    120         zy = dy / len;
    121     }
    122     rot->d[0] = zx;
    123     rot->d[1] = zy;
    124     rot->d[2] = 0.;
    125 
    126     rot->n[0] = -rot->d[1];
    127     rot->n[1] =  rot->d[0];
    128     rot->n[2] = 0;
    129 
    130     rot->angle = len * ANGLE_FACTOR;
    131 }
    132 
    133 static void
    134 rotator_apply( Rotator  rot, double*  vec )
    135 {
    136     double   d, n, z, d2, z2, cs, sn;
    137 
    138     /* project on D, N, Z */
    139     d = vec[0]*rot->d[0] + vec[1]*rot->d[1];
    140     n = vec[0]*rot->n[0] + vec[1]*rot->n[1];
    141     z = vec[2];
    142 
    143     /* rotate on D, Z */
    144     cs = cos( rot->angle );
    145     sn = sin( rot->angle );
    146 
    147     d2 =  cs*d + sn*z;
    148     z2 = -sn*d + cs*z;
    149 
    150     /* project on X, Y, Z */
    151     vec[0] = d2*rot->d[0] + n*rot->n[0];
    152     vec[1] = d2*rot->d[1] + n*rot->n[1];
    153     vec[2] = z2;
    154 }
    155 
    156 /*** TRACKBALL OBJECT
    157  ***/
    158 typedef struct { int  x, y, offset, alpha; Fix16VectorRec  f; } SphereCoordRec, *SphereCoord;
    159 
    160 typedef struct SkinTrackBall
    161 {
    162     int             diameter;
    163     unsigned*       pixels;
    164     SDL_Surface*    surface;
    165     VectorRec       axes[3];  /* current ball axes */
    166 
    167 #define  DOT_GRID        3                        /* number of horizontal and vertical cells per side grid */
    168 #define  DOT_CELLS       2                        /* number of random dots per cell */
    169 #define  DOT_MAX         (6*DOT_GRID*DOT_GRID*DOT_CELLS)  /* total number of dots */
    170 #define  DOT_RANDOM_X    1007                     /* horizontal random range in each cell */
    171 #define  DOT_RANDOM_Y    1007                     /* vertical random range in each cell */
    172 
    173 #define  DOT_THRESHOLD  FIX16_FROM_FLOAT(0.17)
    174 
    175     Fix16VectorRec   dots[ DOT_MAX ];
    176 
    177     SphereCoordRec*  sphere_map;
    178     int              sphere_count;
    179 
    180     unsigned         ball_color;
    181     unsigned         dot_color;
    182     unsigned         ring_color;
    183 
    184     Uint32           ticks_last;  /* ticks since last move */
    185     int              acc_x;
    186     int              acc_y;
    187     int              acc_threshold;
    188     double           acc_scale;
    189 
    190     /* rotation applied to events send to the system */
    191     SkinRotation     rotation;
    192 
    193 } TrackBallRec, *TrackBall;
    194 
    195 
    196 /* The following constants are used to better mimic a real trackball.
    197  *
    198  * ACC_THRESHOLD is used to filter small ball movements out.
    199  * If the length of the relative mouse motion is smaller than this
    200  * constant, then no corresponding ball event will be sent to the
    201  * system.
    202  *
    203  * ACC_SCALE is used to scale the relative mouse motion vector into
    204  * the corresponding ball motion vector.
    205  */
    206 #define  ACC_THRESHOLD  20
    207 #define  ACC_SCALE      0.2
    208 
    209 static void
    210 trackball_init( TrackBall  ball, int  diameter, int  ring,
    211                 unsigned   ball_color, unsigned  dot_color,
    212                 unsigned   ring_color )
    213 {
    214     int  diameter2 = diameter + ring*2;
    215 
    216     memset( ball, 0, sizeof(*ball) );
    217 
    218     ball->acc_threshold = ACC_THRESHOLD;
    219     ball->acc_scale     = ACC_SCALE;
    220 
    221     /* init SDL surface */
    222     ball->diameter   = diameter2;
    223     ball->ball_color = ball_color;
    224     ball->dot_color  = dot_color;
    225     ball->ring_color = ring_color;
    226 
    227     ball->rotation   = SKIN_ROTATION_0;
    228 
    229     ball->pixels   = (unsigned*)calloc( diameter2*diameter2, sizeof(unsigned) );
    230     ball->surface  = sdl_surface_from_argb32( ball->pixels, diameter2, diameter2 );
    231 
    232     /* init axes */
    233     ball->axes[0][0] = 1.; ball->axes[0][1] = 0.; ball->axes[0][2] = 0.;
    234     ball->axes[1][0] = 0.; ball->axes[1][1] = 1.; ball->axes[1][2] = 0.;
    235     ball->axes[2][0] = 0.; ball->axes[2][1] = 0.; ball->axes[2][2] = 1.;
    236 
    237     /* init dots */
    238     {
    239         int  side, nn = 0;
    240 
    241         for (side = 0; side < 6; side++) {
    242             VectorRec  origin, axis1, axis2;
    243             int        xx, yy;
    244 
    245             switch (side) {
    246             case 0:
    247                 origin[0] = -1; origin[1] = -1; origin[2] = +1;
    248                 axis1 [0] =  1; axis1 [1] =  0; axis1 [2] =  0;
    249                 axis2 [0] =  0; axis2 [1] =  1; axis2 [2] =  0;
    250                 break;
    251             case 1:
    252                 origin[0] = -1; origin[1] = -1; origin[2] = -1;
    253                 axis1 [0] =  1; axis1 [1] =  0; axis1 [2] =  0;
    254                 axis2 [0] =  0; axis2 [1] =  1; axis2 [2] =  0;
    255                 break;
    256             case 2:
    257                 origin[0] = +1; origin[1] = -1; origin[2] = -1;
    258                 axis1 [0] =  0; axis1 [1] =  0; axis1 [2] =  1;
    259                 axis2 [0] =  0; axis2 [1] =  1; axis2 [2] =  0;
    260                 break;
    261             case 3:
    262                 origin[0] = -1; origin[1] = -1; origin[2] = -1;
    263                 axis1 [0] =  0; axis1 [1] =  0; axis1 [2] =  1;
    264                 axis2 [0] =  0; axis2 [1] =  1; axis2 [2] =  0;
    265                 break;
    266             case 4:
    267                 origin[0] = -1; origin[1] = -1; origin[2] = -1;
    268                 axis1 [0] =  1; axis1 [1] =  0; axis1 [2] =  0;
    269                 axis2 [0] =  0; axis2 [1] =  0; axis2 [2] =  1;
    270                 break;
    271             default:
    272                 origin[0] = -1; origin[1] = +1; origin[2] = -1;
    273                 axis1 [0] =  1; axis1 [1] =  0; axis1 [2] =  0;
    274                 axis2 [0] =  0; axis2 [1] =  0; axis2 [2] =  1;
    275             }
    276 
    277             for (xx = 0; xx < DOT_GRID; xx++) {
    278                 double  tx = xx*(2./DOT_GRID);
    279                 for (yy = 0; yy < DOT_GRID; yy++) {
    280                     double  ty = yy*(2./DOT_GRID);
    281                     double  x0  = origin[0] + axis1[0]*tx + axis2[0]*ty;
    282                     double  y0  = origin[1] + axis1[1]*tx + axis2[1]*ty;
    283                     double  z0  = origin[2] + axis1[2]*tx + axis2[2]*ty;
    284                     int     cc;
    285                     for (cc = 0; cc < DOT_CELLS; cc++) {
    286                         double  h = (rand() % DOT_RANDOM_X)/((double)DOT_RANDOM_X*DOT_GRID/2);
    287                         double  v = (rand() % DOT_RANDOM_Y)/((double)DOT_RANDOM_Y*DOT_GRID/2);
    288                         double  x = x0 + axis1[0]*h + axis2[0]*v;
    289                         double  y = y0 + axis1[1]*h + axis2[1]*v;
    290                         double  z = z0 + axis1[2]*h + axis2[2]*v;
    291                         double  invlen = 1/sqrt( x*x + y*y + z*z );
    292 
    293                         ball->dots[nn][0] = FIX16_FROM_FLOAT(x*invlen);
    294                         ball->dots[nn][1] = FIX16_FROM_FLOAT(y*invlen);
    295                         ball->dots[nn][2] = FIX16_FROM_FLOAT(z*invlen);
    296                         nn++;
    297                     }
    298                 }
    299             }
    300         }
    301     }
    302 
    303     /* init sphere */
    304     {
    305         int     diameter2 = diameter + 2*ring;
    306         double  radius    = diameter*0.5;
    307         double  radius2   = diameter2*0.5;
    308         int     xx, yy;
    309         int     empty = 0, total = 0;
    310 
    311         ball->sphere_map = calloc( diameter2*diameter2, sizeof(SphereCoordRec) );
    312 
    313         for (yy = 0; yy < diameter2; yy++) {
    314             for (xx = 0; xx < diameter2; xx++) {
    315                 double       x0    = xx - radius2;
    316                 double       y0    = yy - radius2;
    317                 double       r0    = sqrt( x0*x0 + y0*y0 );
    318                 SphereCoord  coord = &ball->sphere_map[total];
    319 
    320                 if (r0 <= radius) {  /* ball pixel */
    321                     double  rx = x0/radius;
    322                     double  ry = y0/radius;
    323                     double  rz = sqrt( 1.0 - rx*rx - ry*ry );
    324 
    325                     coord->x      = xx;
    326                     coord->y      = yy;
    327                     coord->offset = xx + yy*diameter2;
    328                     coord->alpha  = 256;
    329                     coord->f[0]   = FIX16_FROM_FLOAT(rx);
    330                     coord->f[1]   = FIX16_FROM_FLOAT(ry);
    331                     coord->f[2]   = FIX16_FROM_FLOAT(rz);
    332                     if (r0 >= radius-1.) {
    333                         coord->alpha = 256*(radius - r0);
    334                     }
    335                     /* illumination model */
    336                     {
    337 #define  LIGHT_X         -2.0
    338 #define  LIGHT_Y         -2.5
    339 #define  LIGHT_Z          5.0
    340 
    341                         double  lx = LIGHT_X - rx;
    342                         double  ly = LIGHT_Y - ry;
    343                         double  lz = LIGHT_Z - rz;
    344                         double  lir = 1/sqrt(lx*lx + ly*ly + lz*lz);
    345                         double  cosphi = lir*(lx*rx + ly*ry + lz*rz);
    346                         double  scale  = 1.1*cosphi + 0.3;
    347 
    348                         if (scale < 0)
    349                             scale = 0;
    350 
    351                         coord->alpha = coord->alpha * scale;
    352                     }
    353                     total++;
    354                 } else if (r0 <= radius2) { /* ring pixel */
    355                     coord->x      = xx;
    356                     coord->y      = yy;
    357                     coord->offset = xx + yy*diameter2;
    358                     coord->alpha  = 0;
    359                     if (r0 >= radius2-1.) {
    360                         coord->alpha = -256*(r0 - (radius2-1.));
    361                     }
    362                     total++;
    363 
    364                 } else   /* outside pixel */
    365                     empty++;
    366             }
    367         }
    368         ball->sphere_count = total;
    369     }
    370 }
    371 
    372 static int
    373 trackball_contains( TrackBall  ball, int  x, int  y )
    374 {
    375     return ( (unsigned)(x) < (unsigned)ball->diameter &&
    376              (unsigned)(y) < (unsigned)ball->diameter );
    377 }
    378 
    379 static void
    380 trackball_done( TrackBall  ball )
    381 {
    382     free( ball->sphere_map );
    383     ball->sphere_map   = NULL;
    384     ball->sphere_count = 0;
    385 
    386     if (ball->surface) {
    387         SDL_FreeSurface( ball->surface );
    388         ball->surface = NULL;
    389     }
    390 
    391     if (ball->pixels) {
    392         free( ball->pixels );
    393         ball->pixels = NULL;
    394     }
    395 }
    396 
    397 /*** TRACKBALL SPHERE PIXELS
    398  ***/
    399 static unsigned
    400 color_blend( unsigned  from, unsigned  to,  int  alpha )
    401 {
    402     unsigned  from_ag    = (from >> 8) & 0x00ff00ff;
    403     unsigned  to_ag      = (to >> 8) & 0x00ff00ff;
    404     unsigned  from_rb    = from & 0x00ff00ff;
    405     unsigned  to_rb      = to & 0x00ff00ff;
    406     unsigned  result_ag = (from_ag + (alpha*(to_ag - from_ag) >> 8)) & 0xff00ff;
    407     unsigned  result_rb = (from_rb + (alpha*(to_rb - from_rb) >> 8)) & 0xff00ff;
    408 
    409     return (result_ag << 8) | result_rb;
    410 }
    411 
    412 static int
    413 trackball_move( TrackBall  ball,  int  dx, int  dy )
    414 {
    415     RotatorRec  rot[1];
    416     Uint32      now = SDL_GetTicks();
    417 
    418     ball->acc_x += dx;
    419     ball->acc_y += dy;
    420 
    421     if ( norm( ball->acc_x, ball->acc_y ) > ball->acc_threshold )
    422     {
    423         int  ddx = ball->acc_x * ball->acc_scale;
    424         int  ddy = ball->acc_y * ball->acc_scale;
    425         int  ddt;
    426 
    427         ball->acc_x = 0;
    428         ball->acc_y = 0;
    429 
    430         switch (ball->rotation) {
    431         case SKIN_ROTATION_0:
    432             break;
    433 
    434         case SKIN_ROTATION_90:
    435             ddt = ddx;
    436             ddx = ddy;
    437             ddy = -ddt;
    438             break;
    439 
    440         case SKIN_ROTATION_180:
    441             ddx = -ddx;
    442             ddy = -ddy;
    443             break;
    444 
    445         case SKIN_ROTATION_270:
    446             ddt = ddx;
    447             ddx = -ddy;
    448             ddy = ddt;
    449             break;
    450         }
    451 
    452         user_event_mouse(ddx, ddy, 1, 0);
    453     }
    454 
    455     rotator_reset( rot, dx, dy );
    456     rotator_apply( rot, ball->axes[0] );
    457     rotator_apply( rot, ball->axes[1] );
    458     rotator_apply( rot, ball->axes[2] );
    459 
    460     if ( ball->ticks_last == 0 )
    461         ball->ticks_last = now;
    462     else if ( now > ball->ticks_last + (1000/60) ) {
    463         ball->ticks_last = now;
    464         return 1;
    465     }
    466     return 0;
    467 }
    468 
    469 #define  BACK_COLOR   0x00000000
    470 #define  LIGHT_COLOR  0xffffffff
    471 
    472 static void
    473 trackball_refresh( TrackBall  ball )
    474 {
    475     int             diameter = ball->diameter;
    476     unsigned*       pixels   = ball->pixels;
    477     Fix16VectorRec  faxes[3];
    478     Fix16           dot_threshold = DOT_THRESHOLD * diameter;
    479     int             nn;
    480 
    481     SDL_LockSurface( ball->surface );
    482 
    483     fixedvector_from_vector( (Fix16Vector)&faxes[0], (Vector)&ball->axes[0] );
    484     fixedvector_from_vector( (Fix16Vector)&faxes[1], (Vector)&ball->axes[1] );
    485     fixedvector_from_vector( (Fix16Vector)&faxes[2], (Vector)&ball->axes[2] );
    486 
    487     for (nn = 0; nn < ball->sphere_count; nn++) {
    488         SphereCoord  coord = &ball->sphere_map[nn];
    489         unsigned     color = BACK_COLOR;
    490 
    491         if (coord->alpha > 0) {
    492             /* are we near one of the points ? */
    493             Fix16  ax = fixedvector_dot( (Fix16Vector)&coord->f, (Fix16Vector)&faxes[0] );
    494             Fix16  ay = fixedvector_dot( (Fix16Vector)&coord->f, (Fix16Vector)&faxes[1] );
    495             Fix16  az = fixedvector_dot( (Fix16Vector)&coord->f, (Fix16Vector)&faxes[2] );
    496 
    497             Fix16  best_dist = FIX16_ONE;
    498             int    pp;
    499 
    500             color = ball->ball_color;
    501 
    502             for (pp = 0; pp < DOT_MAX; pp++) {
    503                 Fix16VectorRec  d;
    504                 Fix16           dist;
    505 
    506                 d[0] = ball->dots[pp][0] - ax;
    507                 d[1] = ball->dots[pp][1] - ay;
    508                 d[2] = ball->dots[pp][2] - az;
    509 
    510                 if (d[0] > dot_threshold || d[0] < -dot_threshold ||
    511                     d[1] > dot_threshold || d[1] < -dot_threshold ||
    512                     d[2] > dot_threshold || d[2] < -dot_threshold )
    513                     continue;
    514 
    515                 dist = fixedvector_len( (Fix16Vector)&d );
    516 
    517                 if (dist < best_dist)
    518                     best_dist = dist;
    519             }
    520             if (best_dist < DOT_THRESHOLD) {
    521                 int  a = 256*(DOT_THRESHOLD - best_dist) / DOT_THRESHOLD;
    522                 color = color_blend( color, ball->dot_color, a );
    523             }
    524 
    525             if (coord->alpha < 256) {
    526                 int  a = coord->alpha;
    527                 color = color_blend( ball->ring_color, color, a );
    528             }
    529             else if (coord->alpha > 256) {
    530                 int  a = (coord->alpha - 256);
    531                 color = color_blend( color, LIGHT_COLOR, a );
    532             }
    533         }
    534         else /* coord->alpha <= 0 */
    535         {
    536             color = ball->ring_color;
    537 
    538             if (coord->alpha < 0) {
    539                 int  a = -coord->alpha;
    540                 color = color_blend( color, BACK_COLOR, a );
    541             }
    542         }
    543 
    544         pixels[coord->x + diameter*coord->y] = color;
    545     }
    546     SDL_UnlockSurface( ball->surface );
    547 }
    548 
    549 void
    550 trackball_draw( TrackBall  ball, int  x, int  y, SDL_Surface*  dst )
    551 {
    552     SDL_Rect  d;
    553 
    554     d.x = x;
    555     d.y = y;
    556     d.w = ball->diameter;
    557     d.h = ball->diameter;
    558 
    559     SDL_BlitSurface( ball->surface, NULL, dst, &d );
    560     SDL_UpdateRects( dst, 1, &d );
    561 }
    562 
    563 
    564 SkinTrackBall*
    565 skin_trackball_create  ( SkinTrackBallParameters*  params )
    566 {
    567     TrackBall  ball;
    568 
    569     ANEW0(ball);
    570     trackball_init( ball,
    571                     params->diameter,
    572                     params->ring,
    573                     params->ball_color,
    574                     params->dot_color,
    575                     params->ring_color );
    576     return  ball;
    577 }
    578 
    579 int
    580 skin_trackball_contains( SkinTrackBall*  ball, int  x, int  y )
    581 {
    582     return  trackball_contains(ball, x, y);
    583 }
    584 
    585 int
    586 skin_trackball_move( SkinTrackBall*  ball, int  dx, int  dy )
    587 {
    588     return  trackball_move(ball, dx, dy);
    589 }
    590 
    591 void
    592 skin_trackball_refresh ( SkinTrackBall*  ball )
    593 {
    594     trackball_refresh(ball);
    595 }
    596 
    597 void
    598 skin_trackball_draw( SkinTrackBall*  ball, int  x, int  y, SDL_Surface*  dst )
    599 {
    600     trackball_draw(ball, x, y, dst);
    601 }
    602 
    603 void
    604 skin_trackball_destroy ( SkinTrackBall*  ball )
    605 {
    606     if (ball) {
    607         trackball_done(ball);
    608         AFREE(ball);
    609     }
    610 }
    611 
    612 void
    613 skin_trackball_rect( SkinTrackBall*  ball, SDL_Rect*  rect )
    614 {
    615     rect->x = 0;
    616     rect->y = 0;
    617     rect->w = ball->diameter;
    618     rect->h = ball->diameter;
    619 }
    620 
    621 
    622 void
    623 skin_trackball_set_rotation( SkinTrackBall*  ball, SkinRotation  rotation )
    624 {
    625     ball->rotation = rotation & 3;
    626 }
    627