1 /* $Id: tenm_graphic.c,v 1.133 2005/06/29 20:24:51 oohara Exp $ */
2 #include <stdio.h>
3 /* malloc  */
4 #include <stdlib.h>
5 #include <math.h>
6 /* for tenm_load_image */
7 #include <stdarg.h>
8 /* isprint */
9 #include <ctype.h>
10 
11 #include <SDL.h>
12 #include <SDL_image.h>
13 
14 #include "tenm_math.h"
15 #include "tenm_primitive.h"
16 #include "tenm_object.h"
17 #include "tenm_sdl_init.h"
18 
19 #include "tenm_graphic.h"
20 
21 /* number of printable characters */
22 #define TENM_FONT_NUMBER_CHAR 95
23 
24 static SDL_Surface *dpy = NULL;
25 
26 static int tenm_font_index(int c);
27 static int tenm_draw_line2(int a_x, int a_y, int b_x, int b_y, int width,
28                            tenm_color color, int xy_reversed);
29 
30 /* return 0 on success, 1 on error */
31 int
tenm_graphic_init(int width,int height,int flag,const char * name)32 tenm_graphic_init(int width, int height, int flag, const char *name)
33 {
34   Uint32 sdl_flag;
35 
36   /* sanity check */
37   if (name == NULL)
38   {
39     fprintf(stderr, "tenm_graphic_init: name is NULL\n");
40     return 1;
41   }
42   if (SDL_WasInit(SDL_INIT_VIDEO) != 0)
43   {
44     fprintf(stderr, "tenm_graphic_init: warning: video is already "
45             "initialized\n");
46     return 0;
47   }
48 
49   if (tenm_sdl_init(SDL_INIT_VIDEO) != 0)
50   {
51     fprintf(stderr, "tenm_graphic_init: cannot initialize SDL video\n");
52     return 1;
53   }
54 
55   if (dpy != NULL)
56   {
57     fprintf(stderr, "tenm_graphic_init: "
58             "dpy is already set\n");
59     /* this is a fatal error --- if we are here, video is not
60      * initialized before tenm_graphic_init() is called, so
61      * the situation is weird enough
62      */
63     /* dpy should not be freed here --- it is the job of SDL_Quit() */
64     SDL_QuitSubSystem(SDL_INIT_VIDEO);
65     return 1;
66   }
67 
68   if (flag & TENM_FULLSCREEN)
69     sdl_flag = SDL_FULLSCREEN;
70   else
71     sdl_flag = 0;
72   dpy = SDL_SetVideoMode(width, height, 0, sdl_flag);
73   if (dpy == NULL)
74   {
75     fprintf(stderr, "tenm_graphic_init: "
76             "SDL_SetVideoMode failed (%s)\n", SDL_GetError());
77     SDL_QuitSubSystem(SDL_INIT_VIDEO);
78     return 1;
79   }
80 
81   SDL_WM_SetCaption(name, name);
82   /* ignore the return value of SDL_ShowCursor() because the cursor
83    * may be out of the window when it is called
84    */
85   SDL_ShowCursor(SDL_DISABLE);
86 
87   return 0;
88 }
89 
90 /* draw a line between A(a_x, a_y) and B(b_x, b_y)
91  * return 0 on success, 1 on error
92  */
93 int
tenm_draw_line(int a_x,int a_y,int b_x,int b_y,int width,tenm_color color)94 tenm_draw_line(int a_x, int a_y, int b_x, int b_y, int width,
95                tenm_color color)
96 {
97   int dx;
98   int dy;
99   int dx_abs;
100   int dy_abs;
101 
102   /* sanity check */
103   if (width <= 0)
104   {
105     fprintf(stderr, "tenm_draw_line: width is non-positive (%d)\n", width);
106     return 1;
107   }
108 
109   dx = b_x - a_x;
110   dy = b_y - a_y;
111   if (dx >= 0)
112     dx_abs = dx;
113   else
114     dx_abs = -dx;
115   if (dy >= 0)
116     dy_abs = dy;
117   else
118     dy_abs = -dy;
119 
120   if ((dx == 0) && (dy == 0))
121     return tenm_draw_point(a_x, a_y, color);
122 
123   if (dx_abs >= dy_abs)
124   {
125     if (dx >= 0)
126       return tenm_draw_line2(a_x, a_y, b_x, b_y, width, color, 0);
127     else
128       return tenm_draw_line2(b_x, b_y, a_x, a_y, width, color, 0);
129   }
130   else
131   {
132     if (dy >= 0)
133       return tenm_draw_line2(a_y, a_x, b_y, b_x, width, color, 1);
134     else
135       return tenm_draw_line2(b_y, b_x, a_y, a_x, width, color, 1);
136   }
137 
138   /* should not reach here */
139   fprintf(stderr, "tenm_draw_line: should not reach here\n");
140   return 1;
141 }
142 
143 /* draw a circle whose center is (center_x, center_y)
144  * with a line whose width is width (arg 4)
145  * note that the center of the circle is always blank,
146  * even if width == radius
147  * return 0 on success, 1 on error
148  */
149 int
tenm_draw_circle(int center_x,int center_y,int radius,int width,tenm_color color)150 tenm_draw_circle(int center_x, int center_y, int radius, int width,
151                  tenm_color color)
152 {
153   int i;
154   int x_outer;
155   int x_inner;
156   int y;
157   int error_tilde_outer;
158   int error_tilde_inner;
159   int status = 0;
160 
161   /* sanity check */
162   if (radius <= 0)
163   {
164     fprintf(stderr, "tenm_draw_circle: radius is non-positive (%d)\n", radius);
165     return 1;
166   }
167   if ((width <= 0) || (width > radius))
168   {
169     fprintf(stderr, "tenm_draw_circle: width is out of range (%d)\n", width);
170     return 1;
171   }
172 
173   /* the Bresenham algorithm with the "wide line" hack */
174   x_outer = radius;
175   x_inner = radius - width + 1;
176   y = 0;
177   /* the next point is either (x, y + 1) or (x - 1, y + 1) */
178 
179   /* ((x)^2 + (y + 1)^2 - radius^2) + ((x - 1)^2 + (y + 1)^2 - radius^2) */
180   error_tilde_outer = 3 - 2 * radius;
181   error_tilde_inner = 3 - 2 * (radius - width + 1);
182   while (x_outer >= y)
183   {
184     for (i = x_outer; (i >= x_inner) && (i >= 0); i--)
185     {
186       if (tenm_draw_point(center_x + i,
187                           center_y + y,
188                           color) != 0)
189         status = 1;
190       if (tenm_draw_point(center_x + i,
191                           center_y - y,
192                           color) != 0)
193         status = 1;
194       if (tenm_draw_point(center_x - i,
195                           center_y + y,
196                           color) != 0)
197         status = 1;
198       if (tenm_draw_point(center_x - i,
199                           center_y - y,
200                           color) != 0)
201         status = 1;
202 
203       if (tenm_draw_point(center_x + y,
204                           center_y + i,
205                           color) != 0)
206         status = 1;
207       if (tenm_draw_point(center_x + y,
208                           center_y - i,
209                           color) != 0)
210         status = 1;
211       if (tenm_draw_point(center_x - y,
212                           center_y + i,
213                           color) != 0)
214         status = 1;
215       if (tenm_draw_point(center_x - y,
216                           center_y - i,
217                           color) != 0)
218         status = 1;
219     }
220 
221     if (error_tilde_outer >= 0)
222     {
223       /* the next point is (x - 1, y + 1) */
224       /* error_tilde(next) - error_tilde
225        * = (((x - 1)^2 + (y + 2)^2 - radius^2)
226        *    + ((x - 2)^2 + (y + 2)^2 - radius^2))
227        *   - (((x)^2 + (y + 1)^2 - radius^2)
228        *      + ((x - 1)^2 + (y + 1)^2 - radius^2))
229        */
230       error_tilde_outer += (-4) * x_outer + 4 * y + 10;
231       x_outer--;
232     }
233     else
234     {
235       /* the next point is (x, y + 1) */
236       /* error_tilde(next) - error_tilde
237        * = (((x)^2 + (y + 2)^2 - radius^2)
238        *    + ((x - 1)^2 + (y + 2)^2 - radius^2))
239        *   - (((x)^2 + (y + 1)^2 - radius^2)
240        *      + ((x - 1)^2 + (y + 1)^2 - radius^2))
241        */
242       error_tilde_outer += 4 * y + 6;
243     }
244 
245     if (x_inner >= y)
246     {
247       if (error_tilde_inner >= 0)
248       {
249         error_tilde_inner += (-4) * x_inner + 4 * y + 10;
250         x_inner--;
251       }
252       else
253       {
254         error_tilde_inner += 4 * y + 6;
255       }
256     }
257 
258     y++;
259 
260   }
261 
262   return status;
263 }
264 
265 /* you must _NOT_ lock dpy before calling this function
266  * return 0 on success, 1 on error
267  */
268 int
tenm_draw_point(int x,int y,tenm_color color)269 tenm_draw_point(int x, int y, tenm_color color)
270 {
271   return tenm_draw_rectangle(x, y, 1, 1, color);
272 }
273 
274 /* you must _NOT_ lock dpy before calling this function
275  * return 0 on success, 1 on error
276  */
277 int
tenm_draw_rectangle(int x,int y,int w,int h,tenm_color color)278 tenm_draw_rectangle(int x, int y, int w, int h, tenm_color color)
279 {
280   SDL_Rect rec;
281 
282   /* sanity check */
283   if ((x + (w - 1) < 0) || (x - (w - 1) >= dpy->w))
284     return 0;
285   if ((y + (h - 1) < 0) || (y - (h - 1) >= dpy->h))
286     return 0;
287 
288   rec.x = x;
289   rec.y = y;
290   rec.w = w;
291   rec.h = h;
292   if (SDL_FillRect(dpy, &rec, color) != 0)
293   {
294     fprintf(stderr, "tenm_draw_rectangle: SDL_FillRect failed "
295             "(%s) (x = %d, y = %d, w = %d, h = %d)\n", SDL_GetError(),
296             x, y, w, h);
297     return 1;
298   }
299 
300   return 0;
301 }
302 
303 tenm_color
tenm_map_color(int red,int green,int blue)304 tenm_map_color(int red, int green, int blue)
305 {
306   /* sanity check */
307   if (red < 0)
308   {
309     fprintf(stderr, "tenm_map_color: red (%d) too small\n", red);
310     red = 0;
311   }
312   else if (red > 255)
313   {
314     fprintf(stderr, "tenm_map_color: red (%d) too big\n", red);
315     red = 255;
316   }
317   if (green < 0)
318   {
319     fprintf(stderr, "tenm_map_color: green (%d) too small\n", green);
320     green = 0;
321   }
322   else if (green > 255)
323   {
324     fprintf(stderr, "tenm_map_color: green (%d) too big\n", green);
325     green = 255;
326   }
327   if (blue < 0)
328   {
329     fprintf(stderr, "tenm_map_color: blue (%d) too small\n", blue);
330     blue = 0;
331   }
332   else if (blue > 255)
333   {
334     fprintf(stderr, "tenm_map_color: blue (%d) too big\n", blue);
335     blue = 255;
336   }
337 
338   /* I can't fix gcc "different width" warnings here */
339   return SDL_MapRGB(dpy->format, red, green, blue);
340 }
341 
342 /* return 0 on success, 1 on error */
343 int
tenm_clear_window(tenm_color color)344 tenm_clear_window(tenm_color color)
345 {
346   if (SDL_FillRect(dpy, NULL, color) != 0)
347   {
348     fprintf(stderr, "tenm_clear_window: SDL_FillRect failed (%s)\n",
349             SDL_GetError());
350     return 1;
351   }
352   return 0;
353 }
354 
355 /* return 0 on success, 1 on error */
356 int
tenm_redraw_window(void)357 tenm_redraw_window(void)
358 {
359   if (SDL_Flip(dpy) != 0)
360   {
361     fprintf(stderr, "tenm_redraw_window: SDL_Flip failed (%s)\n",
362             SDL_GetError());
363     return 1;
364   }
365   return 0;
366 }
367 
368 /* return 0 on success, 1 on error */
369 int
tenm_draw_primitive(tenm_primitive * p,tenm_color color)370 tenm_draw_primitive(tenm_primitive *p, tenm_color color)
371 {
372   int i;
373   int j;
374   int status = 0;
375 
376   if (p == NULL)
377   {
378     fprintf(stderr, "tenm_draw_primitive: p is NULL\n");
379     return 0;
380   }
381 
382   switch (p->klass)
383   {
384   case TENM_POINT:
385     if (tenm_draw_point((int) ((tenm_point *) p)->x,
386                         (int) ((tenm_point *) p)->y,
387                         color) != 0)
388       return 1;
389     else
390       return 0;
391     break;
392   case TENM_CIRCLE:
393     if (tenm_draw_circle((int) ((tenm_circle *) p)->center->x,
394                          (int) ((tenm_circle *) p)->center->y,
395                          (int) ((tenm_circle *) p)->r,
396                          1, color) != 0)
397       return 1;
398     else
399       return 0;
400     break;
401   case TENM_SEGMENT:
402     if (tenm_draw_line((int) ((tenm_segment *) p)->a->x,
403                        (int) ((tenm_segment *) p)->a->y,
404                        (int) ((tenm_segment *) p)->b->x,
405                        (int) ((tenm_segment *) p)->b->y,
406                        1, color) != 0)
407       return 1;
408     else
409       return 0;
410     break;
411   case TENM_POLYGON:
412     for (i = 0; i < ((tenm_polygon *) p)->n; i++)
413       for (j = i + 1; j < ((tenm_polygon *) p)->n; j++)
414         if (tenm_draw_line((int) ((tenm_polygon *) p)->v[i]->x,
415                            (int) ((tenm_polygon *) p)->v[i]->y,
416                            (int) ((tenm_polygon *) p)->v[j]->x,
417                            (int) ((tenm_polygon *) p)->v[j]->y,
418                            1, color) != 0)
419           status = 1;
420     return status;
421     break;
422   default:
423     fprintf(stderr, "tenm_draw_primitive: strange primitive (%d)\n", p->klass);
424     return 1;
425     break;
426   }
427   /* should not reach here */
428   fprintf(stderr, "tenm_draw_primitive: should not reach here\n");
429   return 1;
430 }
431 
432 /* return 0 on success, 1 on error */
433 int
tenm_draw_mass(tenm_mass * p,tenm_color color)434 tenm_draw_mass(tenm_mass *p, tenm_color color)
435 {
436   int i;
437   int status = 0;
438 
439   /* sanity check */
440   if (p == NULL)
441   {
442     fprintf(stderr, "tenm_draw_mass: p is NULL\n");
443     return 1;
444   }
445   if (p->n <= 0)
446   {
447     fprintf(stderr, "tenm_draw_mass: p->n is non-negative\n");
448     return 1;
449   }
450   if (p->p == NULL)
451   {
452     fprintf(stderr, "tenm_draw_mass: p->p is NULL\n");
453     return 1;
454   }
455 
456   for (i = 0; i < p->n; i++)
457   {
458     if (tenm_draw_primitive(p->p[i], color))
459     {
460       fprintf(stderr, "tenm_draw_mass: tenm_draw_primitive failed (%d)\n", i);
461       status = 1;
462     }
463   }
464 
465   return status;
466 }
467 
468 /* load image files and split them
469  * ... is one or more of
470  * (const char *file_name, int row, int column, tenm_color transparent_color)
471  * return NULL on error
472  */
473 tenm_image *
tenm_load_image(int number_file,...)474 tenm_load_image(int number_file, ...)
475 {
476   int i;
477   int j;
478   int k;
479   int l;
480   const char *file_name;
481   int row;
482   int column;
483   tenm_color transparent_color;
484   int width;
485   int height;
486   int suffix;
487   va_list ap;
488   tenm_image *temp;
489   SDL_Surface **temp_surface;
490   SDL_Surface *master;
491   SDL_Rect temp_rect_src;
492   SDL_Rect temp_rect_dest;
493 
494   /* sanity check */
495   if (number_file <= 0)
496   {
497     fprintf(stderr, "tenm_load_image: strange number_file (%d)\n",
498             number_file);
499     return NULL;
500   }
501 
502   temp = (tenm_image *) malloc(sizeof(tenm_image));
503   if (temp == NULL)
504   {
505     fprintf(stderr, "tenm_load_image: malloc failed\n");
506     return NULL;
507   }
508   temp->n = 0;
509   temp->image = NULL;
510 
511   va_start(ap, number_file);
512   for (i = 0; i < number_file; i++)
513   {
514     /* you can't use char in va_arg */
515     file_name = (const char *) va_arg(ap, const int *);
516     row = va_arg(ap, int);
517     column = va_arg(ap, int);
518     transparent_color = va_arg(ap, tenm_color);
519 
520     /* sanity check */
521     if (file_name == NULL)
522     {
523       fprintf(stderr, "tenm_load_image: file_name is NULL (i = %d)\n", i);
524       if (temp->image != NULL)
525       {
526         for (l = 0; l < temp->n; l++)
527           SDL_FreeSurface(temp->image[l]);
528         free(temp->image);
529       }
530       free(temp);
531       va_end(ap);
532       return NULL;
533     }
534     if (row <= 0)
535     {
536       fprintf(stderr, "tenm_load_image: row is non-positive (%d) "
537               "(i = %d)\n", row, i);
538       if (temp->image != NULL)
539       {
540         for (l = 0; l < temp->n; l++)
541           SDL_FreeSurface(temp->image[l]);
542         free(temp->image);
543       }
544       free(temp);
545       va_end(ap);
546       return NULL;
547     }
548     if (column <= 0)
549     {
550       fprintf(stderr, "tenm_load_image: column is non-positive (%d) "
551               "(i = %d)\n", column, i);
552       if (temp->image != NULL)
553       {
554         for (l = 0; l < temp->n; l++)
555           SDL_FreeSurface(temp->image[l]);
556         free(temp->image);
557       }
558       free(temp);
559       va_end(ap);
560       return NULL;
561     }
562 
563     master = IMG_Load(file_name);
564     if (master == NULL)
565     {
566       fprintf(stderr, "tenm_load_image: IMG_Load(%s) failed (%s) (%i)\n",
567               file_name, IMG_GetError(), i);
568       if (temp->image != NULL)
569       {
570         for (l = 0; l < temp->n; l++)
571           SDL_FreeSurface(temp->image[l]);
572         free(temp->image);
573       }
574       free(temp);
575       va_end(ap);
576       return NULL;
577     }
578 
579     width = master->w / column;
580     if (master->w % column != 0)
581       fprintf(stderr, "tenm_load_image: warning: width (%d) of image (%s) "
582               "cannot be divided by %d\n", master->w, file_name, column);
583     height = master->h / row;
584     if (master->h % row != 0)
585       fprintf(stderr, "tenm_load_image: warning: height (%d) of image (%s) "
586               "cannot be divided by %d\n", master->h, file_name, row);
587 
588     /* don't substitute tenm->image directly, or you will be in a trouble
589      * if realloc fails  */
590     if (temp->image == NULL)
591       temp_surface = (SDL_Surface **) malloc(sizeof(SDL_Surface *)
592                                              * row * column);
593     else
594       temp_surface = (SDL_Surface **) realloc(temp->image,
595                                               sizeof(SDL_Surface *)
596                                               * (row * column + temp->n));
597     if (temp_surface == NULL)
598     {
599       fprintf(stderr, "tenm_load_image: memory allocation to image "
600               "(%d) failed\n", i);
601       SDL_FreeSurface(master);
602       if (temp->image != NULL)
603       {
604         for (l = 0; l < temp->n; l++)
605           SDL_FreeSurface(temp->image[l]);
606         free(temp->image);
607       }
608       free(temp);
609       return NULL;
610     }
611     temp->image = temp_surface;
612 
613     for (j = 0; j < row; j++)
614       for (k = 0; k < column; k++)
615       {
616         suffix = temp->n + j * column + k;
617         /* plain malloc is not enough */
618         temp->image[suffix] = SDL_CreateRGBSurface(0, width, height,
619                                                    dpy->format->BitsPerPixel,
620                                                    dpy->format->Rmask,
621                                                    dpy->format->Gmask,
622                                                    dpy->format->Bmask,
623                                                    dpy->format->Amask);
624         if (temp->image[suffix] == NULL)
625         {
626           fprintf(stderr, "tenm_load_image: SDL_CreateRGBSurface(%d) "
627                   "failed (%s)\n", suffix, SDL_GetError());
628           SDL_FreeSurface(master);
629           for (l = 0; l < suffix; l++)
630             SDL_FreeSurface(temp->image[l]);
631           free(temp->image);
632           free(temp);
633           return NULL;
634         }
635         if (dpy->format->BitsPerPixel == 8)
636           SDL_SetPalette(temp->image[suffix], SDL_LOGPAL|SDL_PHYSPAL,
637                          dpy->format->palette->colors,
638                          0, dpy->format->palette->ncolors);
639         temp_rect_src.x = width * k;
640         temp_rect_src.y = row * j;
641         temp_rect_src.w = width;
642         temp_rect_src.h = height;
643         temp_rect_dest.x = 0;
644         temp_rect_dest.y = 0;
645         temp_rect_dest.w = width;
646         temp_rect_dest.h = height;
647         if (SDL_BlitSurface(master, &temp_rect_src,
648                             temp->image[suffix], &temp_rect_dest) != 0)
649         {
650           fprintf(stderr, "tenm_load_image: SDL_BlitSurface failed (%s)\n",
651                   SDL_GetError());
652           SDL_FreeSurface(master);
653           /* we need to free temp->image[suffix] too */
654           for (l = 0; l <= suffix; l++)
655             SDL_FreeSurface(temp->image[l]);
656           free(temp->image);
657           free(temp);
658           return NULL;
659         }
660         if (SDL_SetColorKey(temp->image[suffix], SDL_SRCCOLORKEY,
661                             transparent_color) != 0)
662         {
663           fprintf(stderr, "tenm_load_image: SDL_SetColorKey failed (%s)\n",
664                   SDL_GetError());
665           SDL_FreeSurface(master);
666           /* we need to free temp->image[suffix] too */
667           for (l = 0; l <= suffix; l++)
668             SDL_FreeSurface(temp->image[l]);
669           free(temp->image);
670           free(temp);
671           return NULL;
672         }
673       }
674     temp->n += row * column;
675     SDL_FreeSurface(master);
676   }
677 
678   va_end(ap);
679 
680   return temp;
681 }
682 
683 void
tenm_image_delete(tenm_image * p)684 tenm_image_delete(tenm_image *p)
685 {
686   int i;
687   /* sanity check */
688   if (p == NULL)
689   {
690     fprintf(stderr, "tenm_image_delete: p is NULL\n");
691     return;
692   }
693   if (p->n <= 0)
694   {
695     fprintf(stderr, "tenm_image_delete: p->n is non-positive (%d)\n", p->n);
696     return;
697   }
698   if (p->image == NULL)
699   {
700     fprintf(stderr, "tenm_image_delete: p->image is NULL\n");
701     return;
702   }
703 
704   for (i = 0; i < p->n; i++)
705     SDL_FreeSurface(p->image[i]);
706   free(p->image);
707   free(p);
708 }
709 
710 /* return 0 on success, 1 on error */
711 int
tenm_draw_image(int x,int y,tenm_image * image,int number)712 tenm_draw_image(int x, int y, tenm_image *image, int number)
713 {
714   SDL_Rect temp;
715 
716   /* sanity check */
717   if (image == NULL)
718   {
719     fprintf(stderr, "tenm_draw_image: image is NULL\n");
720     return 1;
721   }
722   if (image->n <= 0)
723   {
724     fprintf(stderr, "tenm_draw_image: image->n is non-positive\n");
725     return 1;
726   }
727   if (image->image == NULL)
728   {
729     fprintf(stderr, "tenm_draw_image: image->image is NULL\n");
730     return 1;
731   }
732   if ((number < 0) || (number >= image->n))
733   {
734     fprintf(stderr, "tenm_draw_image: number (%d) is out of range "
735             "(0 -- %d)\n", number, image->n - 1);
736     return 1;
737   }
738 
739   temp.x = x;
740   temp.y = y;
741   temp.w = image->image[number]->w;
742   temp.h = image->image[number]->h;
743 
744   if (SDL_BlitSurface(image->image[number], NULL, dpy, &temp) != 0)
745   {
746     fprintf(stderr, "tenm_draw_image: SDL_BlitSurface failed (%s)\n",
747             SDL_GetError());
748     return 1;
749   }
750 
751   return 0;
752 }
753 
754 /* return 0 on success, 1 on error */
755 int
tenm_draw_string(int x,int y,tenm_image * font,const char * string,int length)756 tenm_draw_string(int x, int y, tenm_image *font, const char *string,
757                  int length)
758 {
759   int i;
760   int suffix;
761   int temp_x;
762   int status = 0;
763 
764   /* sanity check */
765   if (font == NULL)
766   {
767     fprintf(stderr, "tenm_draw_string: font is NULL\n");
768     return 1;
769   }
770   if (font->n < TENM_FONT_NUMBER_CHAR)
771   {
772     fprintf(stderr, "tenm_draw_string: not enough images in font "
773             "(%d) (%d required)\n", font->n, TENM_FONT_NUMBER_CHAR);
774     return 1;
775   }
776   if (font->image == NULL)
777   {
778     fprintf(stderr, "tenm_draw_string: font->image is NULL\n");
779     return 1;
780   }
781   if (string == NULL)
782   {
783     fprintf(stderr, "tenm_draw_string: string is NULL\n");
784     return 1;
785   }
786   if (length <= 0)
787   {
788     fprintf(stderr, "tenm_draw_string: length is non-positive (%d)\n", length);
789     return 1;
790   }
791 
792   temp_x = x;
793   for (i = 0; (i < length) && (string[i] != '\0'); i++)
794   {
795     if (!isprint(string[i]))
796     {
797       fprintf(stderr, "tenm_draw_string: char %d unprintable (\\x%x)\n",
798               i, string[i]);
799       status = 1;
800       continue;
801     }
802     suffix = tenm_font_index(string[i]);
803     if ((suffix < 0) || (suffix >= font->n))
804     {
805       fprintf(stderr, "tenm_draw_string: tenm_font_index(%d) returned "
806               "strange number (%d)\n", i, suffix);
807       status = 1;
808       continue;
809     }
810 
811     if (tenm_draw_image(temp_x, y - font->image[suffix]->h / 2,
812                         font, suffix) != 0)
813     {
814       fprintf(stderr, "tenm_draw_string: tenm_draw_image(%d) failed\n", i);
815       status = 1;
816       continue;
817     }
818 
819     temp_x += font->image[suffix]->w;
820   }
821   return status;
822 }
823 
824 /* order of characters (ASCII order)
825  *    !"#$%&'()*+,-./0123456789:;<=>?
826  *   @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
827  *   `abcdefghijklmnopqrstuvwxyz{|}~
828  */
829 
830 /* return a non-negative index for the character c
831  * return -1 on error
832  */
833 static int
tenm_font_index(int c)834 tenm_font_index(int c)
835 {
836   /* sanity check */
837   if (!isprint(c))
838   {
839     fprintf(stderr, "tenm_font_index: unprintable char (\\x%x)\n", c);
840     return -1;
841   }
842 
843   /* don't assume ASCII */
844   switch (c)
845   {
846   case ' ':
847     return 0;
848     break;
849   case '!':
850     return 1;
851     break;
852   case '"':
853     return 2;
854     break;
855   case '#':
856     return 3;
857     break;
858   case '$':
859     return 4;
860     break;
861   case '%':
862     return 5;
863     break;
864   case '&':
865     return 6;
866     break;
867   case '\'':
868     return 7;
869     break;
870   case '(':
871     return 8;
872     break;
873   case ')':
874     return 9;
875     break;
876   case '*':
877     return 10;
878     break;
879   case '+':
880     return 11;
881     break;
882   case ',':
883     return 12;
884     break;
885   case '-':
886     return 13;
887     break;
888   case '.':
889     return 14;
890     break;
891   case '/':
892     return 15;
893     break;
894   case '0':
895     return 16;
896     break;
897   case '1':
898     return 17;
899     break;
900   case '2':
901     return 18;
902     break;
903   case '3':
904     return 19;
905     break;
906   case '4':
907     return 20;
908     break;
909   case '5':
910     return 21;
911     break;
912   case '6':
913     return 22;
914     break;
915   case '7':
916     return 23;
917     break;
918   case '8':
919     return 24;
920     break;
921   case '9':
922     return 25;
923     break;
924   case ':':
925     return 26;
926     break;
927   case ';':
928     return 27;
929     break;
930   case '<':
931     return 28;
932     break;
933   case '=':
934     return 29;
935     break;
936   case '>':
937     return 30;
938     break;
939   case '?':
940     return 31;
941     break;
942   case '@':
943     return 32;
944     break;
945   case 'A':
946     return 33;
947     break;
948   case 'B':
949     return 34;
950     break;
951   case 'C':
952     return 35;
953     break;
954   case 'D':
955     return 36;
956     break;
957   case 'E':
958     return 37;
959     break;
960   case 'F':
961     return 38;
962     break;
963   case 'G':
964     return 39;
965     break;
966   case 'H':
967     return 40;
968     break;
969   case 'I':
970     return 41;
971     break;
972   case 'J':
973     return 42;
974     break;
975   case 'K':
976     return 43;
977     break;
978   case 'L':
979     return 44;
980     break;
981   case 'M':
982     return 45;
983     break;
984   case 'N':
985     return 46;
986     break;
987   case 'O':
988     return 47;
989     break;
990   case 'P':
991     return 48;
992     break;
993   case 'Q':
994     return 49;
995     break;
996   case 'R':
997     return 50;
998     break;
999   case 'S':
1000     return 51;
1001     break;
1002   case 'T':
1003     return 52;
1004     break;
1005   case 'U':
1006     return 53;
1007     break;
1008   case 'V':
1009     return 54;
1010     break;
1011   case 'W':
1012     return 55;
1013     break;
1014   case 'X':
1015     return 56;
1016     break;
1017   case 'Y':
1018     return 57;
1019     break;
1020   case 'Z':
1021     return 58;
1022     break;
1023   case '[':
1024     return 59;
1025     break;
1026   case '\\':
1027     return 60;
1028     break;
1029   case ']':
1030     return 61;
1031     break;
1032   case '^':
1033     return 62;
1034     break;
1035   case '_':
1036     return 63;
1037     break;
1038   case '`':
1039     return 64;
1040     break;
1041   case 'a':
1042     return 65;
1043     break;
1044   case 'b':
1045     return 66;
1046     break;
1047   case 'c':
1048     return 67;
1049     break;
1050   case 'd':
1051     return 68;
1052     break;
1053   case 'e':
1054     return 69;
1055     break;
1056   case 'f':
1057     return 70;
1058     break;
1059   case 'g':
1060     return 71;
1061     break;
1062   case 'h':
1063     return 72;
1064     break;
1065   case 'i':
1066     return 73;
1067     break;
1068   case 'j':
1069     return 74;
1070     break;
1071   case 'k':
1072     return 75;
1073     break;
1074   case 'l':
1075     return 76;
1076     break;
1077   case 'm':
1078     return 77;
1079     break;
1080   case 'n':
1081     return 78;
1082     break;
1083   case 'o':
1084     return 79;
1085     break;
1086   case 'p':
1087     return 80;
1088     break;
1089   case 'q':
1090     return 81;
1091     break;
1092   case 'r':
1093     return 82;
1094     break;
1095   case 's':
1096     return 83;
1097     break;
1098   case 't':
1099     return 84;
1100     break;
1101   case 'u':
1102     return 85;
1103     break;
1104   case 'v':
1105     return 86;
1106     break;
1107   case 'w':
1108     return 87;
1109     break;
1110   case 'x':
1111     return 88;
1112     break;
1113   case 'y':
1114     return 89;
1115     break;
1116   case 'z':
1117     return 90;
1118     break;
1119   case '{':
1120     return 91;
1121     break;
1122   case '|':
1123     return 92;
1124     break;
1125   case '}':
1126     return 93;
1127     break;
1128   case '~':
1129     return 94;
1130     break;
1131   default:
1132     fprintf(stderr, "tenm_font_index: unknown char (\\x%x)\n", c);
1133     return -1;
1134     break;
1135   }
1136   /* should not reach here */
1137   fprintf(stderr, "tenm_font_index: should not reach here\n");
1138   return -1;
1139 }
1140 
1141 /* draw a line between A(a_x, a_y) and B(b_x, b_y)
1142  * assumes b_x > a_x and abs(b_x - a_x) >= abs(b_y - a_x)
1143  * return 0 on success, 1 on error
1144  */
1145 static int
tenm_draw_line2(int a_x,int a_y,int b_x,int b_y,int width,tenm_color color,int xy_reversed)1146 tenm_draw_line2(int a_x, int a_y, int b_x, int b_y, int width,
1147                 tenm_color color, int xy_reversed)
1148 {
1149   int x;
1150   int y;
1151   int x2;
1152   int y2;
1153   int dx;
1154   int dy_abs;
1155   /* 2 * abs(b_x - a_x) * (y - y_real_line) */
1156   int error_tilde;
1157   int error_tilde2;
1158   int y_changed;
1159   int x2_changed;
1160   int status = 0;
1161   int range;
1162 
1163   /* sanity check */
1164   if (b_x <= a_x)
1165   {
1166     fprintf(stderr, "tenm_draw_line2: not (b_x <= a_x)\n");
1167     return 1;
1168   }
1169   if ((b_x - a_x < a_y - b_y) || (b_x - a_x < b_y - a_y))
1170   {
1171     fprintf(stderr, "tenm_draw_line2: not "
1172             "((b_x - a_x < a_y - b_y) || (b_x - a_x < b_y - a_y))\n");
1173     return 1;
1174   }
1175   if (width <= 0)
1176   {
1177     fprintf(stderr, "tenm_draw_line2: width is non-positive (%d)\n", width);
1178     return 1;
1179   }
1180 
1181   dx = b_x - a_x;
1182   if (a_y >= b_y)
1183     dy_abs = a_y - b_y;
1184   else
1185     dy_abs = b_y - a_y;
1186   /* to minimize error */
1187   if (dy_abs == 0)
1188     range = width;
1189   else
1190     range = (int) (((double) (dx * width))
1191                    / tenm_sqrt(dx * dx + dy_abs * dy_abs));
1192   if (range <= 0)
1193     range = 1;
1194 
1195   /* the Bresenham algorithm with the "wide line" hack */
1196   x = a_x;
1197   y = a_y;
1198   /* the next point is:
1199    * either (x + 1, y) or (x + 1, y + 1) if b_y >= a_y
1200    * either (x + 1, y) or (x + 1, y - 1) otherwise
1201    */
1202   error_tilde = 0;
1203   y_changed = 0;
1204 
1205   while (x <= b_x)
1206   {
1207     /* Bresenham again */
1208     x2 = 0;
1209     y2 = 0;
1210     /* not very accurate, but who cares? */
1211     error_tilde2 = 0;
1212     x2_changed = 0;
1213 
1214     while (y2 < range)
1215     {
1216       if (xy_reversed)
1217       {
1218         if (tenm_draw_point(y + y2, x + x2, color) != 0)
1219           status = 1;
1220         if (tenm_draw_point(y - y2, x - x2, color) != 0)
1221           status = 1;
1222       }
1223       else
1224       {
1225         if (tenm_draw_point(x + x2, y + y2, color) != 0)
1226           status = 1;
1227         if (tenm_draw_point(x - x2, y - y2, color) != 0)
1228           status = 1;
1229       }
1230 
1231       if ((y_changed) && (x2_changed))
1232       {
1233         if (xy_reversed)
1234         {
1235           if (b_y >= a_y)
1236           {
1237             if (tenm_draw_point(y + y2 - 1, x + x2, color) != 0)
1238               status = 1;
1239             if (tenm_draw_point(y - y2, x- x2 - 1, color) != 0)
1240               status = 1;
1241           }
1242           else
1243           {
1244             if (tenm_draw_point(y + y2, x + x2 - 1, color) != 0)
1245               status = 1;
1246             if (tenm_draw_point(y - y2 + 1, x - x2, color) != 0)
1247               status = 1;
1248           }
1249         }
1250         else
1251         {
1252           if (b_y >= a_y)
1253           {
1254             if (tenm_draw_point(x + x2, y + y2 - 1, color) != 0)
1255               status = 1;
1256             if (tenm_draw_point(x - x2 - 1, y - y2, color) != 0)
1257               status = 1;
1258           }
1259           else
1260           {
1261             if (tenm_draw_point(x + x2 - 1, y + y2, color) != 0)
1262               status = 1;
1263             if (tenm_draw_point(x - x2, y - y2 + 1, color) != 0)
1264               status = 1;
1265           }
1266         }
1267       }
1268 
1269       y2++;
1270       error_tilde2 += 2 * dy_abs;
1271       if (error_tilde2 >= dx)
1272       {
1273         error_tilde2 -= 2 * dx;
1274         if (b_y >= a_y)
1275           x2--;
1276         else
1277           x2++;
1278         x2_changed = 1;
1279       }
1280       else
1281       {
1282         x2_changed = 0;
1283       }
1284     }
1285 
1286     x++;
1287     error_tilde += 2 * dy_abs;
1288     if (error_tilde >= dx)
1289     {
1290       error_tilde -= 2 * dx;
1291       if (b_y >= a_y)
1292         y++;
1293       else
1294         y--;
1295       y_changed = 1;
1296     }
1297     else
1298     {
1299       y_changed = 0;
1300     }
1301   }
1302 
1303   return status;
1304 }
1305