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