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