1 /** EMULib Emulation Library *********************************/
2 /**                                                         **/
3 /**                          Touch.c                        **/
4 /**                                                         **/
5 /** This file contains functions that simulate joystick and **/
6 /** dialpad with the touch screen. It is normally used from **/
7 /** the platform-dependent functions that know where to get **/
8 /** pen coordinates from and where to draw pen cues to.     **/
9 /**                                                         **/
10 /** Copyright (C) Marat Fayzullin 2008-2014                 **/
11 /**     You are not allowed to distribute this software     **/
12 /**     commercially. Please, notify me, if you make any    **/
13 /**     changes to this file.                               **/
14 /*************************************************************/
15 #ifndef DEFINE_ONCE
16 #define DEFINE_ONCE
17 
18 #include "EMULib.h"
19 #include "Console.h"
20 #include "Touch.h"
21 #include <string.h>
22 #include <stdio.h>
23 
24 #define XKEYS       12                   /* Number of keys in a row */
25 #define YKEYS       6                    /* Number of key rows      */
26 
27 #define MAX_PENJOY_WIDTH 320             /* Max pen joystick width  */
28 
29 #if defined(ANDROID)
30 static int KEYSTEP = 39;                 /* Key step in pixels      */
31 static int KEYSIZE = 31;                 /* Key size in pixels      */
32 static int CHRSIZE = 16;                 /* Key label size          */
33 #elif defined(MEEGO)
34 static int KEYSTEP = 46;                 /* Key step in pixels      */
35 static int KEYSIZE = 38;                 /* Key size in pixels      */
36 static int CHRSIZE = 16;                 /* Key label size          */
37 #elif defined(MAEMO)
38 static int KEYSTEP = 34;                 /* Key step in pixels      */
39 static int KEYSIZE = 30;                 /* Key size in pixels      */
40 static int CHRSIZE = 16;                 /* Key label size          */
41 #else
42 static int KEYSTEP = 14;                 /* Key step in pixels      */
43 static int KEYSIZE = 12;                 /* Key size in pixels      */
44 static int CHRSIZE = 8;                  /* Key label size          */
45 #endif
46 
47 /* Currently selected virtual keyboard key */
48 static int KBDXPos = 0;
49 static int KBDYPos = 0;
50 
51 /* Horizontal offsets of virtual keyboard lines */
52 #if defined(ANDROID) || defined(MEEGO)
53 static const int KBDOffsets[YKEYS] = { 0,0,0,0,32,16 };
54 #elif defined(MAEMO)
55 static const int KBDOffsets[YKEYS] = { 0,0,0,0,20,10 };
56 #else
57 static const int KBDOffsets[YKEYS] = { 0,0,0,0,8,4 };
58 #endif
59 
60 /* Characters printed on virtual keyboard keys */
61 static const char *KBDLines[YKEYS+1] =
62 {
63   "\33\20\21\22\23\24\25\26\27\16\17\32",
64   "1234567890-=",
65   "\11QWERTYUIOP\10",
66   "^ASDFGHJKL;\15",
67   "ZXCVBNM,./",
68   "[]     \\'",
69   0
70 };
71 
72 static const char *PenCues[32] =
73 {
74   "LEFT","RIGHT","UP","DOWN","A","B","L","R",
75   "START","SELECT","EXIT","X","Y",0,0,0,
76   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
77 };
78 
79 static int CueSizes[32] =
80 {
81   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
82   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
83 };
84 
85 /* Characters returned from virtual keyboard */
86 static const unsigned char KBDKeys[YKEYS][XKEYS] =
87 {
88   {
89     0x1B,CON_F1,CON_F2,CON_F3,CON_F4,CON_F5,
90     CON_F6,CON_F7,CON_F8,CON_INSERT,CON_DELETE,CON_STOP
91   },
92   { '1','2','3','4','5','6','7','8','9','0','-','=' },
93   { CON_TAB,'Q','W','E','R','T','Y','U','I','O','P',CON_BS },
94   { '^','A','S','D','F','G','H','J','K','L',';',CON_ENTER },
95   { 'Z','X','C','V','B','N','M',',','.','/',0,0 },
96   { '[',']',' ',' ',' ',' ',' ','\\','\'',0,0,0 }
97 };
98 
99 static unsigned int FFWDButtons = 0;
100 static unsigned int MENUButtons = 0;
101 
102 static int JoyCuesSetup = 0;
103 static int OldCueImgW   = 0;
104 
105 static const struct { int W,H,X,Y; } DefButtons[] =
106 {
107   { 256,256, 8,     -48-16-256, },
108   { 128,128, -128-8,48+8,       },
109   { 128,128, -128-8,48+8+128+8, },
110   { 128,48,  0,     0,          },
111   { 128,48,  -128,  0,          },
112   { 128,48,  0,     -48,        },
113   { 128,48,  -128,  -48,        },
114   { 128,48,  -256-8,-48,        },
115 };
116 
117 static struct
118 {
119   int Bit;
120   Image Img;
121   int W,H,X,Y;
122   int Invisible;
123 } Buttons[] =
124 {
125   {  4, {0}, 0,0,0,0,0 }, // FIRE-A
126   {  5, {0}, 0,0,0,0,0 }, // FIRE-B
127   {  6, {0}, 0,0,0,0,0 }, // FIRE-L
128   {  7, {0}, 0,0,0,0,0 }, // FIRE-R
129   {  9, {0}, 0,0,0,0,0 }, // SELECT
130   {  8, {0}, 0,0,0,0,0 }, // START
131   { 11, {0}, 0,0,0,0,0 }, // FIRE-X
132   { -1, {0}, 0,0,0,0,0 }, // Arrows (have to be last)
133   { -2, {0}, 0,0,0,0,0 }
134 };
135 
abs(int X)136 static int abs(int X) { return(X>=0? X:-X); }
137 
138 /** GetKbdWidth()/GetKbdHeight() *****************************/
139 /** Return virtual keyboard dimensions.                     **/
140 /*************************************************************/
GetKbdWidth()141 unsigned int GetKbdWidth()  { return(KEYSTEP*XKEYS+8); }
GetKbdHeight()142 unsigned int GetKbdHeight() { return(KEYSTEP*YKEYS+8+CHRSIZE); }
143 
144 /** GenericFullJoystick() ************************************/
145 /** Treat whole screen as one big directional pad. Result   **/
146 /** compatible with GetJoystick() (arrows only though).     **/
147 /*************************************************************/
GenericFullJoystick(int X,int Y,int W,int H)148 unsigned int GenericFullJoystick(int X,int Y,int W,int H)
149 {
150   /* Just consider the whole screen as one big directional pad */
151   return(
152     (X<(W>>1)-(W>>3)? BTN_LEFT : X>(W>>1)+(W>>3)? BTN_RIGHT : 0)
153   | (Y<(H>>1)-(H>>3)? BTN_UP   : Y>(H>>1)+(H>>3)? BTN_DOWN  : 0)
154   );
155 }
156 
157 /** GenericPenJoystick() *************************************/
158 /** Get simulated joystick buttons from touch screen UI.    **/
159 /** Result compatible with GetJoystick().                   **/
160 /*************************************************************/
GenericPenJoystick(int X,int Y,int W,int H)161 unsigned int GenericPenJoystick(int X,int Y,int W,int H)
162 {
163   unsigned int J;
164   int W3;
165 
166   /* Simulate joystick when pen touches the screen at X,Y */
167   J = 0;
168 
169   /* Don't accept touches outside of the window frame */
170   if((X<0)||(Y<0)||(X>=W)||(Y>=H)) return(0);
171   W3 = W>MAX_PENJOY_WIDTH*3? MAX_PENJOY_WIDTH:W/3;
172 
173   /* Top 1/16 of the widget: FIREL and FIRER */
174   if(Y<(H>>3))
175   { if(X<W3) J|=BTN_FIREL; else if(X>=W-W3) J|=BTN_FIRER; }
176 
177   /* Bottom 1/16 of the widget: SELECT/EXIT and START */
178   if(!J&&(Y>=(H-(H>>3))))
179   { if(X<W3) J|=BTN_SELECT|BTN_EXIT; else if(X>=W-W3) J|=BTN_START; }
180 
181   /* Right upper corner of the screen is the fire buttons */
182   if(!J&&(X>=W-(W3>>1))&&(Y>=(H>>3))&&(Y<(H>>3)+W3+(W3>>3)))
183   {
184     /* Fire buttons overlap */
185     if(Y<=(H>>3)+(W3>>1)+(W3>>3)) J|=BTN_FIREA;
186     if(Y>=(H>>3)+(W3>>1))         J|=BTN_FIREB;
187   }
188 
189   /* Left 1/3 of the screen is the directional pad */
190   if(!J&&(X<W3)&&(Y>=H-(H>>3)-W3))
191   {
192     Y-=H-W3-(H>>3);
193     W3/=3;
194     if(X<W3) J|=BTN_LEFT; else if(X>=(W3<<1)) J|=BTN_RIGHT;
195     if(Y<W3) J|=BTN_UP;   else if(Y>=(W3<<1)) J|=BTN_DOWN;
196   }
197 
198   /* Apply dynamically assigned FFWD and MENU buttons */
199   J |= J&FFWDButtons? BTN_FFWD:0;
200   J |= J&MENUButtons? BTN_MENU:0;
201 
202   /* Done, return simulated "joystick" state */
203   return(J);
204 }
205 
206 /** GenericPenDialpad() **************************************/
207 /** Get simulated dialpad buttons from touch screen UI.     **/
208 /*************************************************************/
GenericPenDialpad(int X,int Y,int W,int H)209 unsigned char GenericPenDialpad(int X,int Y,int W,int H)
210 {
211   int W3,H2;
212 
213   /* Dialpad is the middle 1/3 of the screen */
214   W3 = W>MAX_PENJOY_WIDTH*3? MAX_PENJOY_WIDTH:W/3;
215   H2 = H>W3? ((H-W3)>>1):0;
216   W3 = (W-W3)>>1;
217   return(
218     (Y>=H2)&&(Y<H-H2)&&(X>=W3)&&(X<W-W3)?
219     3*(X-W3)/(W-(W3<<1))+3*((Y-H2)/((H-(H2<<1))>>2))+1 : 0
220   );
221 }
222 
223 /** GenericPenKeyboard() *************************************/
224 /** Get virtual on-screen keyboard buttons.                 **/
225 /*************************************************************/
GenericPenKeyboard(int X,int Y,int W,int H)226 unsigned char GenericPenKeyboard(int X,int Y,int W,int H)
227 {
228   int J;
229 
230   /* Pen coordinates relative to keyboard's top left corner */
231   X -= W-KEYSTEP*XKEYS-8;
232   Y -= H-KEYSTEP*YKEYS-8;
233 
234   /* Pen must be inside the keyboard */
235   if((X<0)||(Y<0)) return(0);
236 
237   /* Keyboard row index */
238   Y/= KEYSTEP;
239   if(Y>=YKEYS) return(0);
240 
241   /* Adjust for row position on screen */
242   for(J=0;J<=Y;++J) X-=KBDOffsets[J];
243   if(X<0) return(0);
244 
245   /* Keyboard column index */
246   X/= KEYSTEP;
247   if(X>=XKEYS) return(0);
248 
249   /* Memorize last pressed key */
250   KBDXPos = X;
251   KBDYPos = Y;
252 
253   /* Return key */
254   return(KBDKeys[Y][X]);
255 }
256 
257 /** GenericDialKeyboard() ************************************/
258 /** Process dialpad input to the virtual keyboard. Returns  **/
259 /** virtual keyboard key if selected, or 0 if not.          **/
260 /*************************************************************/
GenericDialKeyboard(unsigned char Key)261 unsigned char GenericDialKeyboard(unsigned char Key)
262 {
263   /* Interpret input key */
264   switch(Key)
265   {
266     case CON_LEFT:
267       KBDXPos = (KBDXPos>0? KBDXPos:strlen(KBDLines[KBDYPos]))-1;
268       break;
269     case CON_RIGHT:
270       KBDXPos = KBDXPos<strlen(KBDLines[KBDYPos])-1? KBDXPos+1:0;
271       break;
272     case CON_UP:
273       KBDYPos = KBDYPos>0? KBDYPos-1:YKEYS-1;
274       KBDXPos = KBDXPos<strlen(KBDLines[KBDYPos])? KBDXPos:strlen(KBDLines[KBDYPos])-1;
275       break;
276     case CON_DOWN:
277       KBDYPos = KBDYPos<YKEYS-1? KBDYPos+1:0;
278       KBDXPos = KBDXPos<strlen(KBDLines[KBDYPos])? KBDXPos:strlen(KBDLines[KBDYPos])-1;
279       break;
280     case CON_OK:
281       /* Return ASCII character */
282       return(KBDLines[KBDYPos][KBDXPos]);
283   }
284 
285   /* Key has not been interpreted */
286   return(0);
287 }
288 
289 /** SetPenCues() *********************************************/
290 /** Set pen cues for given buttons to a given string.       **/
291 /*************************************************************/
SetPenCues(unsigned int Buttons,const char * CueText)292 void SetPenCues(unsigned int Buttons,const char *CueText)
293 {
294   unsigned int J;
295 
296   if(!strcmp(CueText,"FFWD")||!strcmp(CueText,"SLOW")) FFWDButtons|=Buttons; else FFWDButtons&=~Buttons;
297   if(!strcmp(CueText,"MENU")) MENUButtons|=Buttons; else MENUButtons&=~Buttons;
298 
299   for(J=0;J<sizeof(PenCues)/sizeof(PenCues[0]);++J)
300     if(Buttons&(1<<J))
301     {
302       PenCues[J]  = CueText;
303       CueSizes[J] = strlen(CueText)*CHRSIZE;
304     }
305 
306 #ifdef ANDROID
307   // On Android, request update to the screen overlay
308   UpdateOverlay();
309 #endif
310 }
311 
312 /** SetPenKeyboard() *****************************************/
313 /** Set pen keyboard dimensions.                            **/
314 /*************************************************************/
SetPenKeyboard(unsigned int KeyStep,unsigned int KeySize,unsigned int ChrSize)315 void SetPenKeyboard(unsigned int KeyStep,unsigned int KeySize,unsigned int ChrSize)
316 {
317   int J;
318 
319   /* Character size must be a multiple of 8 */
320   CHRSIZE = ChrSize>8? (ChrSize&~7):8;
321   /* Make sure keys do not overlap */
322   if(KeySize+4>KeyStep) KeySize=KeyStep-4;
323   /* Make sure key labels fit into keys */
324   if(CHRSIZE+2>KeySize) KeySize=CHRSIZE+2;
325   /* Now grow key step again, if needed */
326   if(KeySize+4>KeyStep) KeyStep=KeySize+4;
327   /* Assign new virtual key dimensions */
328   KEYSIZE = KeySize;
329   KEYSTEP = KeyStep;
330 
331   /* Recompute cue lengths */
332   for(JoyCuesSetup=1,J=0;J<sizeof(PenCues)/sizeof(PenCues[0]);++J)
333     CueSizes[J] = PenCues[J]? strlen(PenCues[J])*CHRSIZE:0;
334 }
335 
336 /** InitFinJoystick() ****************************************/
337 /** Initialize finger joystick images by cropping them from **/
338 /** the given source image. Returns number of buttons set   **/
339 /** successfully (i.e. found inside the Src bounds).        **/
340 /*************************************************************/
InitFinJoystick(const Image * Src)341 int InitFinJoystick(const Image *Src)
342 {
343   int J,X0,Y0;
344 
345   /* Initialize virtual joystick images */
346   for(J=0;Buttons[J].Bit>-2;++J)
347   {
348     /* Free the existing image */
349     FreeImage(&Buttons[J].Img);
350 
351     /* If source image supplied... */
352     if(Src)
353     {
354       /* Compute button's top-left corner position in the Src */
355       X0 = DefButtons[J].X + (DefButtons[J].X<0? Src->W:0);
356       Y0 = DefButtons[J].Y + (DefButtons[J].Y<0? Src->H:0);
357 
358       /* Crop the button out of the Src image */
359       CropImage(&Buttons[J].Img,Src,X0,Y0,DefButtons[J].W,DefButtons[J].H);
360     }
361 
362     /* Reset button size and position to the defaults */
363     Buttons[J].X = DefButtons[J].X;
364     Buttons[J].Y = DefButtons[J].Y;
365     Buttons[J].W = DefButtons[J].W;
366     Buttons[J].H = DefButtons[J].H;
367 
368     /* Button now visible */
369     Buttons[J].Invisible = 0;
370   }
371 
372   /* Return number of buttons initialized */
373   return(J);
374 }
375 
376 /** SetFinButton() *******************************************/
377 /** Set finger joystick button(s) to given location. When   **/
378 /** Img=0, create wireframe buttons. When Mask=0, set the   **/
379 /** directional buttons image and location. When Mask ORed  **/
380 /** withBTN_INVISIBLE, create invisible buttons. Returns    **/
381 /** the number of virtual buttons set or 0 for none.        **/
382 /*************************************************************/
SetFinButton(unsigned int Mask,const Image * Img,int X,int Y,int W,int H)383 int SetFinButton(unsigned int Mask,const Image *Img,int X,int Y,int W,int H)
384 {
385   int I,J,Result,Invisible;
386 
387   /* Special Mask bit: make button invisible */
388   if(!(Mask&BTN_INVISIBLE)) Invisible=0;
389   else
390   {
391     Invisible = 1;
392     Img       = 0;
393     Mask     &= ~BTN_INVISIBLE;
394   }
395 
396   /* When Mask=0, we are assigning the arrow buttons */
397   if(!Mask) Mask=0x80000000;
398 
399   for(J=Result=0;Mask;++J,Mask>>=1)
400     if(Mask&1)
401       for(I=0;Buttons[I].Bit>-2;++I)
402         if((Buttons[I].Bit==J) || ((Buttons[I].Bit==-1)&&(J==31)))
403         {
404           /* When Img=0, make a wireframe or invisible button */
405           if(Img) CropImage(&Buttons[I].Img,Img,0,0,W,H);
406           else    FreeImage(&Buttons[I].Img);
407 
408           Buttons[I].Invisible = Invisible;
409           Buttons[I].X = X;
410           Buttons[I].Y = Y;
411           Buttons[I].W = W;
412           Buttons[I].H = H;
413           ++Result;
414         }
415 
416   /* Return number of modified buttons */
417   return(Result);
418 }
419 
420 /** GenericFinJoystick() *************************************/
421 /** Return the BTN_* bits corresponding to position X,Y of  **/
422 /** the finger joystick shown in Dst.                       **/
423 /*************************************************************/
GenericFinJoystick(int X,int Y,int W,int H,unsigned int CurState)424 unsigned int GenericFinJoystick(int X,int Y,int W,int H,unsigned int CurState)
425 {
426   unsigned int Result,I,J;
427   int X0,Y0,AX,AY,W0,H0;
428 
429   /* For every known button... */
430   for(J=Result=0;Buttons[J].Bit>-2;++J)
431   {
432     /* Compute finger position relative to the button */
433     X0 = X - Buttons[J].X - (Buttons[J].X<0? W:0);
434     Y0 = Y - Buttons[J].Y - (Buttons[J].Y<0? H:0);
435 
436     /* If button has been pressed... */
437     if(
438        ((X0>=0) && (Y0>=0) && (X0<Buttons[J].W) && (Y0<Buttons[J].H))
439     || ((Buttons[J].Bit<0) && !Result && (CurState&BTN_ARROWS))
440     )
441     {
442       /* ...and it is a normal button... */
443       if(Buttons[J].Bit>=0)
444       {
445         /* Compute button mask from bit number */
446         I = 1<<Buttons[J].Bit;
447         /* Also apply dynamically assigned FFWD and MENU buttons */
448         Result |= I|(I&FFWDButtons? BTN_FFWD:0)|(I&MENUButtons? BTN_MENU:0);
449       }
450       else
451       {
452         /* We are dealing with the joypad arrows here */
453         W0  = Buttons[J].W>>1;
454         H0  = Buttons[J].H>>1;
455         X0 -= W0;
456         Y0 -= H0;
457         AX  = abs(X0);
458         AY  = abs(Y0);
459 
460         /* If finger is outside the joypad, keep pressing previous arrows */
461         if((AX>W0) || (AY>H0))
462           Result |= CurState & ((X0<0? BTN_LEFT:BTN_RIGHT)|(Y0<0? BTN_UP:BTN_DOWN));
463 
464         /* Joypad's center is inactive at 1/16 of the radius */
465         else if((AX>=(W0>>3)) || (AY>=(H0>>3)))
466         {
467           /* This is joypad's edge at 1/4 of the radius */
468           W0 = W0>>1;
469           H0 = H0>>1;
470 
471           Result |=
472             ((X0<0) && ((AX>AY) || (AX>W0))? BTN_LEFT:0)
473           | ((Y0<0) && ((AY>AX) || (AY>H0))? BTN_UP:0)
474           | ((X0>0) && ((AX>AY) || (AX>W0))? BTN_RIGHT:0)
475           | ((Y0>0) && ((AY>AX) || (AY>H0))? BTN_DOWN:0);
476         }
477       }
478     }
479   }
480 
481   /* Done */
482   return(Result);
483 }
484 
485 #endif /* DEFINE_ONCE */
486 
487 #if !defined(BPP32) && !defined(BPP24) && !defined(BPP16) && !defined(BPP8)
488 /* When pixel size not defined, compile in the universal multiplexer */
489 #include "TouchMux.h"
490 #else
491 
492 #if defined(ANDROID) || defined(MEEGO)
493 #define CLR_NORMALF PIXEL(64,255,64)     /* Normal key foreground   */
494 #else
495 #define CLR_NORMALF PIXEL(0,0,0)         /* Normal key foreground   */
496 #define CLR_NORMALB PIXEL(255,255,255)   /* Normal key background   */
497 #endif
498 #define CLR_ACTIVEF PIXEL(255,255,255)   /* Active key foreground   */
499 #define CLR_ACTIVEB PIXEL(255,64,64)     /* Active key background   */
500 #define CLR_FPS     PIXEL(255,128,255)   /* Framerate counter color */
501 #define CLR_CUES    PIXEL(127,127,127)   /* Touch joypad cues color */
502 
503 /** PrintXY2() ***********************************************/
504 /** Print a string in given color on transparent background.**/
505 /*************************************************************/
PrintXY2(Image * Img,const char * S,int X,int Y,pixel FG)506 static void PrintXY2(Image *Img,const char *S,int X,int Y,pixel FG)
507 {
508   const unsigned char *C;
509   pixel *P;
510   int I,J,K,N;
511 
512   X = X<0? 0:X>Img->W-CHRSIZE? Img->W-CHRSIZE:X;
513   Y = Y<0? 0:Y>Img->H-CHRSIZE? Img->H-CHRSIZE:Y;
514 
515   for(K=X;*S;S++)
516     switch(*S)
517     {
518       case '\n':
519         K=X;Y+=CHRSIZE;
520         if(Y>Img->H-CHRSIZE) Y=0;
521         break;
522       default:
523         P=(pixel *)Img->Data+Img->L*Y+K;
524         for(C=CONGetFont()+(*S<<3),J=8;J;P+=Img->L*(CHRSIZE/8),++C,--J)
525           for(I=0,N=(int)*C<<24;N&&(I<CHRSIZE);I+=CHRSIZE/8,N<<=1)
526             if(N&0x80000000) P[I+1]=P[I]=FG;
527         K+=CHRSIZE;
528         if(X>Img->W-CHRSIZE)
529         {
530           K=0;Y+=CHRSIZE;
531           if(Y>Img->H-CHRSIZE) Y=0;
532         }
533         break;
534     }
535 }
536 
537 /** DrawVLine()/DrawHLine() **********************************/
538 /** Draw dotted lines used to show cues for PenJoystick().  **/
539 /*************************************************************/
DrawVLine(Image * Img,int X,int Y1,int Y2,pixel Color)540 static void DrawVLine(Image *Img,int X,int Y1,int Y2,pixel Color)
541 {
542   pixel *P;
543   int J;
544 
545   if((X<0)||(X>=Img->W)) return;
546 
547   Y1 = Y1<0? 0:Y1>=Img->H? Img->H-1:Y1;
548   Y2 = Y2<0? 0:Y2>=Img->H? Img->H-1:Y2;
549   if(Y1>Y2) { J=Y1;Y1=Y2;Y2=J; }
550 
551   P = (pixel *)Img->Data+Img->L*Y1+X;
552   for(J=Y1;J<=Y2;J+=4) { *P=Color;P+=Img->L<<2; }
553 }
554 
DrawHLine(Image * Img,int X1,int X2,int Y,pixel Color)555 static void DrawHLine(Image *Img,int X1,int X2,int Y,pixel Color)
556 {
557   pixel *P;
558   int J;
559 
560   if((Y<0)||(Y>=Img->H)) return;
561 
562   X1 = X1<0? 0:X1>=Img->W? Img->W-1:X1;
563   X2 = X2<0? 0:X2>=Img->W? Img->W-1:X2;
564   if(X1>X2) { J=X1;X1=X2;X2=J; }
565 
566   P = (pixel *)Img->Data+Img->L*Y+X1;
567   for(J=X1;J<=X2;J+=4) { *P=Color;P+=4; }
568 }
569 
570 /** DrawDialpad() ********************************************/
571 /** Draw virtual dialpad in a given image.                  **/
572 /*************************************************************/
DrawDialpad(Image * Img,int Color)573 void DrawDialpad(Image *Img,int Color)
574 {
575   pixel *P;
576   int W,H,H2,W9,W3;
577 
578   /* Use default color, if requested */
579   if(Color<0) Color=CLR_CUES;
580 
581   P  = (pixel *)Img->Data;
582   W  = Img->W;
583   H  = Img->H;
584   W3 = W>MAX_PENJOY_WIDTH*3? MAX_PENJOY_WIDTH:W/3;
585   H2 = H>W3? ((H-W3)>>1):0;
586   W9 = W3/3;
587   W3 = (W-W3)>>1;
588 
589   DrawHLine(Img,W3,W-W3,H2,Color);
590   DrawHLine(Img,W3,W-W3,(H2>>1)+(H>>2),Color);
591   DrawHLine(Img,W3,W-W3,H>>1,Color);
592   DrawHLine(Img,W3,W-W3,(H>>1)-(H2>>1)+(H>>2),Color);
593   DrawHLine(Img,W3,W-W3,H-H2-1,Color);
594   DrawVLine(Img,W3,H2,H-H2-1,Color);
595   DrawVLine(Img,W3+W9,H2,H-H2-1,Color);
596   DrawVLine(Img,W-W3-W9,H2,H-H2-1,Color);
597   DrawVLine(Img,W-W3,H2,H-H2-1,Color);
598   PrintXY2(Img,"1",W3+2,H2+2,Color);
599   PrintXY2(Img,"2",W3+W9+2,H2+2,Color);
600   PrintXY2(Img,"3",W-W3-W9+2,H2+2,Color);
601   PrintXY2(Img,"4",W3+2,(H2>>1)+(H>>2)+2,Color);
602   PrintXY2(Img,"5",W3+W9+2,(H2>>1)+(H>>2)+2,Color);
603   PrintXY2(Img,"6",W-W3-W9+2,(H2>>1)+(H>>2)+2,Color);
604   PrintXY2(Img,"7",W3+2,(H>>1)+2,Color);
605   PrintXY2(Img,"8",W3+W9+2,(H>>1)+2,Color);
606   PrintXY2(Img,"9",W-W3-W9+2,(H>>1)+2,Color);
607   PrintXY2(Img,"*",W3+2,(H>>1)-(H2>>1)+(H>>2)+2,Color);
608   PrintXY2(Img,"0",W3+W9+2,(H>>1)-(H2>>1)+(H>>2)+2,Color);
609   PrintXY2(Img,"#",W-W3-W9+2,(H>>1)-(H2>>1)+(H>>2)+2,Color);
610 }
611 
612 /** DrawPenCues() ********************************************/
613 /** Overlay dotted cue lines for using PenJoystick() onto a **/
614 /** given image. Show dialpad cues if requested.            **/
615 /*************************************************************/
DrawPenCues(Image * Img,int ShowDialpad,int Color)616 void DrawPenCues(Image *Img,int ShowDialpad,int Color)
617 {
618   pixel *P;
619   int W,H,W9,W3,X,Y,J;
620 
621   /* Use default color, if requested */
622   if(Color<0) Color=CLR_CUES;
623 
624   /* Set up pen keyboard for the first time */
625   if(!JoyCuesSetup) SetPenKeyboard(KEYSTEP,KEYSIZE,CHRSIZE);
626 
627   P  = (pixel *)Img->Data;
628   W  = Img->W;
629   H  = Img->H;
630   W3 = Img->W>MAX_PENJOY_WIDTH*3? MAX_PENJOY_WIDTH:W/3;
631   W9 = W3/3;
632 
633   /* Vertical edges */
634   DrawVLine(Img,W3,0,H>>3,Color);
635   DrawVLine(Img,W3,H-W3-(H>>3),H-1,Color);
636   DrawVLine(Img,W-W3,0,H>>3,Color);
637   DrawVLine(Img,W-W3,H-(H>>3),H,Color);
638 
639   /* Corner buttons */
640   DrawHLine(Img,0,W3,H>>3,Color);
641   DrawHLine(Img,W-W3,W-1,H>>3,Color);
642   DrawHLine(Img,0,W3,H-(H>>3),Color);
643   DrawHLine(Img,W-W3,W-1,H-(H>>3),Color);
644 
645   /* Fire buttons (with overlap) */
646   DrawHLine(Img,W-(W3>>1),W-1,(H>>3)+(W3>>1),Color);
647   DrawHLine(Img,W-(W3>>1),W-1,(H>>3)+(W3>>1)+(W3>>3),Color);
648   DrawHLine(Img,W-(W3>>1),W-1,(H>>3)+W3+(W3>>3),Color);
649   DrawVLine(Img,W-(W3>>1),H>>3,(H>>3)+W3+(W3>>3),Color);
650 
651   /* Directional buttons */
652   DrawVLine(Img,W9,H-W3-(H>>3),H-(H>>3),Color);
653   DrawVLine(Img,W3-W9,H-W3-(H>>3),H-(H>>3),Color);
654   DrawHLine(Img,0,W3,H-W3-(H>>3),Color);
655   DrawHLine(Img,0,W3,H-(W9<<1)-(H>>3),Color);
656   DrawHLine(Img,0,W3,H-W9-(H>>3),Color);
657 
658   /* Button labels */
659   if(PenCues[4]) PrintXY2(Img,PenCues[4],W-CueSizes[4]-2,(H>>3)+2,Color);
660   if(PenCues[5]) PrintXY2(Img,PenCues[5],W-CueSizes[5]-2,(H>>3)+(W3>>1)+(W3>>3)+2,Color);
661   if(PenCues[6]) PrintXY2(Img,PenCues[6],2,2,Color);
662   if(PenCues[7]) PrintXY2(Img,PenCues[7],W-CueSizes[7]-2,2,Color);
663   if(PenCues[8]) PrintXY2(Img,PenCues[8],W-CueSizes[8]-2,H-(H>>3)+2,Color);
664   if(PenCues[9]) PrintXY2(Img,PenCues[9],2,H-(H>>3)+2,Color);
665 
666   /* Arrow labels */
667   if(PenCues[0]&&(CueSizes[0]<=W9)) PrintXY2(Img,PenCues[0],2,H-(W9<<1)-(H>>3)+2,Color);
668   if(PenCues[1]&&(CueSizes[1]<=W9)) PrintXY2(Img,PenCues[1],(W9<<1)+2,H-(W9<<1)-(H>>3)+2,Color);
669   if(PenCues[2]&&(CueSizes[2]<=W9)) PrintXY2(Img,PenCues[2],W9+2,H-W3-(H>>3)+2,Color);
670   if(PenCues[3]&&(CueSizes[3]<=W9)) PrintXY2(Img,PenCues[3],W9+2,H-W9-(H>>3)+2,Color);
671 
672   /* If requested, show on-screen dialpad */
673   if(ShowDialpad) DrawDialpad(Img,Color);
674 }
675 
676 /** DrawKeyboard() *******************************************/
677 /** Draw virtual keyboard in a given image. Key modifiers   **/
678 /** and the key code passed in CurKey are highlighted.      **/
679 /*************************************************************/
DrawKeyboard(Image * Img,unsigned int CurKey)680 void DrawKeyboard(Image *Img,unsigned int CurKey)
681 {
682   int X,Y,J,I,K,L;
683   char S[2];
684   pixel *P;
685 
686   /* Keyboard in the right-bottom corner by default */
687   X = Img->W-GetKbdWidth();
688   Y = Img->H-GetKbdHeight();
689   if((X<0)||(Y<0)) return;
690 
691   /* Draw modifiers */
692   if(CurKey&CON_MODES)
693   {
694     J=X;
695     if(CurKey&CON_SHIFT)   { PrintXY2(Img,"SHIFT",J,Y,CLR_ACTIVEB);J+=6*CHRSIZE; }
696     if(CurKey&CON_CONTROL) { PrintXY2(Img,"CTRL",J,Y,CLR_ACTIVEB);J+=5*CHRSIZE; }
697     if(CurKey&CON_ALT)     { PrintXY2(Img,"ALT",J,Y,CLR_ACTIVEB);J+=4*CHRSIZE; }
698   }
699 
700   /* Draw keyboard under modifiers */
701   Y += CHRSIZE;
702 
703   /* Draw keys */
704   for(I=J=0,S[1]='\0';KBDLines[I];++I,Y+=KEYSTEP,X+=KBDOffsets[I]-J*KEYSTEP)
705     for(J=0;KBDLines[I][J];++J,X+=KEYSTEP)
706     {
707       /* Draw key frame */
708       P = (pixel *)Img->Data
709         + Img->L*(Y+(KEYSTEP-KEYSIZE)/2)
710         + X+(KEYSTEP-KEYSIZE)/2;
711 
712       /* Highlight current key */
713       if(KBDKeys[I][J]==(CurKey&CON_KEYCODE))
714       {
715 #ifdef CLR_ACTIVEB
716         for(K=1;K<KEYSIZE-1;++K) P[K]=CLR_ACTIVEB;
717         for(K=1,P+=Img->L;K<KEYSIZE-1;++K,P+=Img->L)
718           for(L=0;L<KEYSIZE;++L) P[L]=CLR_ACTIVEB;
719         for(K=1;K<KEYSIZE-1;++K) P[K]=CLR_ACTIVEB;
720 #endif
721         K=CLR_ACTIVEF;
722       }
723       else
724       {
725         for(K=1;K<KEYSIZE-1;++K) P[K]=CLR_NORMALF;
726         for(K=1,P+=Img->L;K<KEYSIZE-1;++K,P+=Img->L)
727         {
728 #ifdef CLR_NORMALB
729           for(L=1;L<KEYSIZE-1;++L) P[L]=CLR_NORMALB;
730 #endif
731           P[0]=P[KEYSIZE-1]=CLR_NORMALF;
732         }
733         for(K=1;K<KEYSIZE-1;++K) P[K]=CLR_NORMALF;
734         K=CLR_NORMALF;
735       }
736 
737       /* Draw key label */
738       S[0]=KBDLines[I][J];
739       PrintXY2(Img,S,X+(KEYSTEP-CHRSIZE)/2,Y+(KEYSTEP-CHRSIZE)/2,K);
740     }
741 }
742 
743 /** DrawFinJoystick() ****************************************/
744 /** Draw finger joystick into given destination image. When **/
745 /** DW=0 or DWH=0, the whole image will be updated. When    **/
746 /** DWxDH+DX+DY represent a dirty rectangle inside Dst, the **/
747 /** function will only update joystick buttons overlapping  **/
748 /** this rectangle, representing them with dotted lines.    **/
749 /** Returns the number of buttons overlapping the dirty     **/
750 /** rectangle.                                              **/
751 /*************************************************************/
DrawFinJoystick(Image * Dst,int DX,int DY,int DW,int DH,int TextColor)752 int DrawFinJoystick(Image *Dst,int DX,int DY,int DW,int DH,int TextColor)
753 {
754   int Result,X0,Y0,X1,Y1,XL,YL,J,NeedFrame,NeedLabel;
755   Image Dirty;
756 
757   /* Use default cue color, if requested */
758   if(TextColor<0) TextColor=CLR_CUES;
759 
760   /* Compute position of the "dirty" rectangle in the middle */
761   if(!DW) DX=0;
762   if(!DH) DY=0;
763 
764   /* This image corresponds to the "dirty" rectangle, if given */
765   CropImage(&Dirty,Dst,DX,DY,DW? DW:Dst->W,DH? DH:Dst->H);
766 
767   /* Draw controls */
768   for(J=Result=0;Buttons[J].Bit>-2;++J)
769   {
770     X0 = Buttons[J].X + (Buttons[J].X<0? Dst->W:0) - DX;
771     Y0 = Buttons[J].Y + (Buttons[J].Y<0? Dst->H:0) - DY;
772     X1 = X0 + Buttons[J].W;
773     Y1 = Y0 + Buttons[J].H;
774     NeedFrame = 0;
775     NeedLabel = 1;
776 
777     /* If need to draw something... */
778     if(!Buttons[J].Invisible)
779     {
780       /* If "dirty" rectangle given... */
781       if(DW && DH)
782       {
783         /* Image obscures "dirty" rectangle: will draw frame and label */
784         if((X0<DW) && (Y0<DH) && (X1>=0) && (Y1>=0))
785         { NeedFrame=NeedLabel=1;++Result; }
786       }
787       else
788       {
789         /* Draw frame if no image data */
790         if(!Buttons[J].Img.Data) NeedFrame=1;
791         else IMGCopy(Dst,X0,Y0,&Buttons[J].Img,0,0,Buttons[J].W,Buttons[J].H,PIXEL(255,0,255));
792         /* Draw label */
793         NeedLabel=1;
794       }
795     }
796 
797     /* If need to draw a frame... */
798     if(NeedFrame)
799     {
800       /* Draw button frame */
801       DrawVLine(&Dirty,X0,Y0,Y1-1,TextColor);
802       DrawVLine(&Dirty,X1-1,Y0,Y1-1,TextColor);
803       DrawHLine(&Dirty,X0,X1-1,Y0,TextColor);
804       DrawHLine(&Dirty,X0,X1-1,Y1-1,TextColor);
805 
806       /* Draw arrow pad */
807       if(Buttons[J].Bit<0)
808       {
809         int I;
810         I = (X1-X0)/3;
811         DrawVLine(&Dirty,X0+I,Y0,Y1-1,TextColor);
812         DrawVLine(&Dirty,X1-I,Y0,Y1-1,TextColor);
813         I = (Y1-Y0)/3;
814         DrawHLine(&Dirty,X0,X1-1,Y0+I,TextColor);
815         DrawHLine(&Dirty,X0,X1-1,Y1-I,TextColor);
816       }
817     }
818     /* If need to draw label and the label exists... */
819     if(NeedLabel && (Buttons[J].Bit>=0) && PenCues[Buttons[J].Bit])
820       PrintXY2(
821         Dst,
822         PenCues[Buttons[J].Bit],
823         ((X0+X1-CueSizes[Buttons[J].Bit])>>1)+DX,
824         ((Y0+Y1-CHRSIZE)>>1)+DY,
825         TextColor
826       );
827   }
828 
829   /* Return number of buttons overlapping "safe" rectangle */
830   return(Result);
831 }
832 
833 /** RenderVideo() ********************************************/
834 /** Draw video buffer to a given image. Return 0 on failure **/
835 /** or destination rectangle size inside OutImg on success. **/
836 /*************************************************************/
RenderVideo(Image * OutImg,Image * CueImg,int Effects,int PenKey,int FrameRate)837 unsigned int RenderVideo(Image *OutImg,Image *CueImg,int Effects,int PenKey,int FrameRate)
838 {
839   unsigned int DW,DH,SW,SH,X,Y,W,H;
840   Image TmpImg;
841 
842   /* Safety check */
843   if(!VideoImg || !VideoImg->Data) return(0);
844 
845   if(Effects&EFF_DIRECT)
846   {
847     W = VideoImg->W;
848     H = VideoImg->H;
849     X = 0;
850     Y = 0;
851   }
852   else
853   {
854     W = VideoW;
855     H = VideoH;
856     X = VideoX;
857     Y = VideoY;
858   }
859 
860   /* Determine destination image dimensions */
861   if(!OutImg)
862   {
863     /* If no destination image given, we assume VideoImg */
864     CropImage(&TmpImg,VideoImg,X,Y,W,H);
865     CueImg = CueImg? CueImg:&TmpImg;
866     OutImg = &TmpImg;
867     DW     = W;
868     DH     = H;
869   }
870   else if(!(Effects&EFF_SCALE)) { DW=W;DH=H; }
871   else if(Effects&EFF_STRETCH)  { DW=OutImg->W;DH=OutImg->H; }
872   else
873   {
874     DW = W/H;
875     DH = H/W;
876     SW = W*(DH>0? DH:1);
877     SH = H*(DW>0? DW:1);
878     DW = SW*OutImg->H/SH;
879     DH = SH*OutImg->W/SW;
880     if(DW>OutImg->W) DW=OutImg->W; else DH=OutImg->H;
881   }
882 
883   /* If destination image has not been given... */
884   if(OutImg==&TmpImg)
885   {
886     /* We do not copy or scale */
887   }
888   /* EFF_SOFTEN: Soften image using 2xSAI-like algorithm */
889   else if(Effects&EFF_SOFTEN)
890   {
891     CropImage(&TmpImg,OutImg,(OutImg->W-DW)>>1,(OutImg->H-DH)>>1,DW,DH);
892     SoftenImage(&TmpImg,VideoImg,X,Y,W,H);
893   }
894   /* EFF_SCALE: Scale image to the screen size */
895   else if(Effects&EFF_SCALE)
896   {
897     /* EFF_STRETCH: Stretch image to fill the whole screen */
898     if(Effects&EFF_STRETCH)
899       ScaleImage(OutImg,VideoImg,X,Y,W,H);
900     else
901     {
902 #if defined(ARM_CPU)
903       /* Scale image to fill the screen, using ARM assembler */
904       DW = ARMScaleImage(OutImg,VideoImg,X,Y,W,H,0);
905       /* Update destination image dimensions to optimized values */
906       DH = DW>>16;
907       DW&= 0xFFFF;
908 #else
909       /* Scale image to fill the screen, using C code */
910       CropImage(&TmpImg,OutImg,(OutImg->W-DW)>>1,(OutImg->H-DH)>>1,DW,DH);
911       ScaleImage(&TmpImg,VideoImg,X,Y,W,H);
912 #endif
913     }
914   }
915   /* DEFAULT: Not scaling, stretching, or softening image */
916   else
917   {
918     /* Center image at the screen */
919     IMGCopy(OutImg,(OutImg->W-W)>>1,(OutImg->H-H)>>1,VideoImg,X,Y,W,H,-1);
920     DW = W;
921     DH = H;
922   }
923 
924   /* EFF_TVLINES/EFF_LCDLINES: Apply "scanlines" effect  */
925   X = Effects&(EFF_TVLINES|EFF_LCDLINES);
926   if(X)
927   {
928     /* Make sure DW is a multiple of 8 pixels for optimization */
929     DW&=~7;
930     if(X==EFF_TVLINES)
931       TelevizeImage(OutImg,(OutImg->W-DW)>>1,(OutImg->H-DH)>>1,DW,DH);
932     else if(X==EFF_LCDLINES)
933       LcdizeImage(OutImg,(OutImg->W-DW)>>1,(OutImg->H-DH)>>1,DW,DH);
934     else
935       RasterizeImage(OutImg,(OutImg->W-DW)>>1,(OutImg->H-DH)>>1,DW,DH);
936   }
937 
938   /* If drawing any touch input cues... */
939   if(Effects&(EFF_VKBD|EFF_PENCUES))
940   {
941     /* If no image supplied for the input cues... */
942     if(!CueImg)
943     {
944       /* In landscape mode, draw input cues on top of OutImg */
945       if(OutImg->H<=OutImg->W) CueImg=OutImg;
946       else
947       {
948         /* In portrait mode, draw input cues below OutImg */
949         CropImage(&TmpImg,OutImg,0,DH,OutImg->W,OutImg->H-DH);
950         CueImg = &TmpImg;
951       }
952     }
953 
954 #if defined(ANDROID) || defined(MEEGO)
955     /* If cue image width changed... */
956     if(OldCueImgW!=CueImg->W)
957     {
958       /* Adjust virtual keyboard and cues size to fit screen */
959       if(CueImg->W>=1024)     SetPenKeyboard(72,64,16);
960       else if(CueImg->W>=768) SetPenKeyboard(60,52,16);
961       else if(CueImg->W>=640) SetPenKeyboard(46,38,16);
962       else if(CueImg->W>=480) SetPenKeyboard(39,31,16);
963       else if(CueImg->W>=320) SetPenKeyboard(24,20,16);
964       else                    SetPenKeyboard(16,14,8);
965       /* New cue image width now in effect */
966       OldCueImgW = CueImg->W;
967     }
968 
969     /* Draw virtual joystick */
970     if(Effects&EFF_PENCUES)
971       DrawFinJoystick(CueImg,(OutImg->W-DW)>>1,(OutImg->H-DH)>>1,DW,DH,CLR_CUES);
972 #else
973     /* Draw virtual joystick */
974     if(Effects&EFF_PENCUES) DrawPenCues(CueImg,Effects&EFF_DIALCUES,CLR_CUES);
975 #endif
976 
977     /* Draw virtual keyboard */
978     if(Effects&EFF_VKBD) DrawKeyboard(CueImg,PenKey);
979   }
980 
981   /* Show framerate if requested */
982   if((Effects&EFF_SHOWFPS)&&(FrameRate>0))
983   {
984     char S[8];
985     sprintf(S,"%dfps",FrameRate);
986     PrintXY2(OutImg,S,((OutImg->W-DW)>>1)+8,((OutImg->H-DH)>>1)+8,CLR_FPS);
987   }
988 
989   /* Done with the screen */
990   return((DW>1)&&(DH>1)? ((DW&0xFFFF)|(DH<<16)):0);
991 }
992 
993 #undef CLR_NORMALF
994 #undef CLR_NORMALB
995 #undef CLR_ACTIVEF
996 #undef CLR_ACTIVEB
997 #undef CLR_FPS
998 #undef CLR_CUES
999 
1000 #endif /* BPP32||BPP24||BPP16||BPP8 */
1001