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