1 /*
2  * tumble: build a PDF file from image files
3  *
4  * bitblt routines
5  * Copyright 2001, 2002, 2003, 2017 Eric Smith <spacewar@gmail.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.  Note that permission is
10  * not granted to redistribute this program under the terms of any
11  * other version of the General Public License.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
21  */
22 
23 
24 #include <stdbool.h>
25 #include <stdint.h>
26 #include <assert.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 
31 #include "bitblt.h"
32 
33 #include "bitblt_tables.h"
34 
35 
36 #define SWAP(type,a,b) do { type temp; temp = a; a = b; b = temp; } while (0)
37 
38 #define DIV_ROUND_UP(count,pow2) (((count) - 1) / (pow2) + 1)
39 
40 
reverse_bits(uint8_t * p,int byte_count)41 void reverse_bits (uint8_t *p, int byte_count)
42 {
43   while (byte_count--)
44     {
45       (*p) = bit_reverse_byte [*p];
46       p++;
47     }
48 }
49 
50 
bit_reverse_word(word_t d)51 static word_t bit_reverse_word (word_t d)
52 {
53   return (bit_reverse_byte [d >> 24] |
54 	  (bit_reverse_byte [(d >> 16) & 0xff] << 8) |
55 	  (bit_reverse_byte [(d >> 8) & 0xff] << 16) |
56 	  (bit_reverse_byte [d & 0xff] << 24));
57 }
58 
59 
reverse_range_of_bytes(uint8_t * b,uint32_t count)60 static void reverse_range_of_bytes (uint8_t *b, uint32_t count)
61 {
62   uint8_t *b2 = b + count - 1;
63 
64   while (b < b2)
65     {
66       uint8_t t = bit_reverse_byte [*b];
67       *b = bit_reverse_byte [*b2];
68       *b2 = t;
69       b++;
70       b2--;
71     }
72 
73   if (b == b2)
74     *b = bit_reverse_byte [*b];
75 }
76 
77 
78 static word_t *temp_buffer;
79 static word_t temp_buffer_size;
80 
realloc_temp_buffer(uint32_t size)81 static void realloc_temp_buffer (uint32_t size)
82 {
83   if (size <= temp_buffer_size)
84     return;
85   temp_buffer = realloc (temp_buffer, size);
86   if (! temp_buffer)
87     {
88       fprintf (stderr, "realloc failed in bitblt library\n");
89       exit (2);
90     }
91   temp_buffer_size = size;
92 }
93 
94 
pixel_mask(int x)95 static inline word_t pixel_mask (int x)
96 {
97 #if defined (MIXED_ENDIAN)  /* disgusting hack for mixed-endian */
98   word_t m;
99   m = 0x80 >> (x & 7);
100   m <<= (x & 24);
101   return (m);
102 #elif defined (LSB_RIGHT)
103   return (1U << ((BITS_PER_WORD - 1) - x));
104 #else
105   return (1U << x);
106 #endif
107 };
108 
109 
110 /* mask for range of bits left..right, inclusive */
pixel_range_mask(int left,int right)111 static inline word_t pixel_range_mask (int left, int right)
112 {
113   word_t m1, m2, val;
114 
115   /* $$$ one of these cases is wrong! */
116 #if defined (LSB_RIGHT)
117   m1 = (~ 0U) >> left;
118   m2 = (~ 0U) << (BITS_PER_WORD - 1 - right);
119 #else
120   m1 = (~ 0U) << left;
121   m2 = (~ 0U) >> (BITS_PER_WORD - 1 - right);
122 #endif
123   val = m1 & m2;
124 
125   printf ("left %d, right %d, mask %08x\n", left, right, val);
126   return (val);
127 };
128 
129 
create_bitmap(Rect * rect)130 Bitmap *create_bitmap (Rect *rect)
131 {
132   Bitmap *bitmap;
133   uint32_t width = rect_width (rect);
134   uint32_t height = rect_height (rect);
135 
136   if ((width <= 0) || (height <= 0))
137     return (NULL);
138 
139   bitmap = calloc (1, sizeof (Bitmap));
140   if (! bitmap)
141     return (NULL);
142   bitmap->rect = * rect;
143   bitmap->row_words = DIV_ROUND_UP (width, BITS_PER_WORD);
144   bitmap->bits = calloc (1, height * bitmap->row_words * sizeof (word_t));
145   if (! bitmap->bits)
146     {
147       free (bitmap);
148       return (NULL);
149     }
150   return (bitmap);
151 }
152 
free_bitmap(Bitmap * bitmap)153 void free_bitmap (Bitmap *bitmap)
154 {
155   free (bitmap->bits);
156   free (bitmap);
157 }
158 
get_pixel(Bitmap * bitmap,Point coord)159 bool get_pixel (Bitmap *bitmap, Point coord)
160 {
161   word_t *p;
162   int w,b;
163 
164   if ((coord.x < bitmap->rect.min.x) ||
165       (coord.x >= bitmap->rect.max.x) ||
166       (coord.y < bitmap->rect.min.y) ||
167       (coord.y >= bitmap->rect.max.y))
168     return (0);
169   coord.y -= bitmap->rect.min.y;
170   coord.x -= bitmap->rect.min.x;
171   w = coord.x / BITS_PER_WORD;
172   b = coord.x & (BITS_PER_WORD - 1);
173   p = bitmap->bits + coord.y * bitmap->row_words + w;
174   return (((*p) & pixel_mask (b)) != 0);
175 }
176 
set_pixel(Bitmap * bitmap,Point coord,bool value)177 void set_pixel (Bitmap *bitmap, Point coord, bool value)
178 {
179   word_t *p;
180   int w,b;
181 
182   if ((coord.x < bitmap->rect.min.x) ||
183       (coord.x >= bitmap->rect.max.x) ||
184       (coord.y < bitmap->rect.min.y) ||
185       (coord.y >= bitmap->rect.max.y))
186     return;
187   coord.y -= bitmap->rect.min.y;
188   coord.x -= bitmap->rect.min.x;
189   w = coord.x / BITS_PER_WORD;
190   b = coord.x & (BITS_PER_WORD - 1);
191   p = bitmap->bits + coord.y * bitmap->row_words + w;
192   if (value)
193     (*p) |= pixel_mask (b);
194   else
195     (*p) &= ~pixel_mask (b);
196 }
197 
198 
199 /* modifies rect1 to be the intersection of rect1 and rect2;
200    returns true if intersection is non-null */
clip_rect(Rect * rect1,Rect * rect2)201 static bool clip_rect (Rect *rect1, Rect *rect2)
202 {
203   if (rect1->min.y > rect2->max.y)
204     goto empty;
205   if (rect1->min.y < rect2->min.y)
206     {
207       if (rect1->max.y < rect2->max.y)
208 	goto empty;
209       rect1->min.y = rect2->min.y;
210     }
211   if (rect1->max.y > rect2->max.y)
212     rect1->max.y = rect2->max.y;
213 
214   if (rect1->min.x > rect2->max.x)
215     goto empty;
216   if (rect1->min.x < rect2->min.x)
217     {
218       if (rect1->max.x < rect2->max.x)
219 	goto empty;
220       rect1->min.x = rect2->min.x;
221     }
222   if (rect1->max.x > rect2->max.x)
223     rect1->max.x = rect2->max.x;
224 
225  empty:
226   rect1->min.x = rect1->min.y =
227     rect1->max.x = rect1->max.y = 0;
228   return (0);
229 }
230 
231 
blt_background(Bitmap * dest_bitmap,Rect dest_rect)232 static void blt_background (Bitmap *dest_bitmap,
233 			    Rect dest_rect)
234 {
235   uint32_t y;
236   word_t *rp;
237   uint32_t left_bit, left_word;
238   uint32_t right_bit, right_word;
239   word_t left_mask, right_mask;
240   int32_t word_count;
241 
242   /* This function requires a non-null dest rect */
243   assert (dest_rect.min.x < dest_rect.max.x);
244   assert (dest_rect.min.y < dest_rect.max.y);
245 
246   /* and that the rows of the dest rect lie entirely within the dest bitmap */
247   assert (dest_rect.min.y >= dest_bitmap->rect.min.y);
248   assert (dest_rect.max.y <= dest_bitmap->rect.max.y);
249 
250   /* clip the x axis of the dest_rect to the bounds of the dest bitmap */
251   if (dest_rect.min.x < dest_bitmap->rect.min.x)
252     dest_rect.min.x = dest_bitmap->rect.min.x;
253   if (dest_rect.max.x > dest_bitmap->rect.max.x)
254     dest_rect.max.x = dest_bitmap->rect.max.x;
255 
256   rp = dest_bitmap->bits +
257     (dest_rect.min.y - dest_bitmap->rect.min.y) * dest_bitmap->row_words +
258     (dest_rect.min.x - dest_bitmap->rect.min.x) / BITS_PER_WORD;
259 
260   left_bit = dest_rect.min.x % BITS_PER_WORD;
261   left_word = dest_rect.min.x / BITS_PER_WORD;
262 
263   right_bit = (dest_rect.max.x - 1) % BITS_PER_WORD;
264   right_word = (dest_rect.max.x - 1) / BITS_PER_WORD;
265 
266   word_count = right_word + 1 - left_word;
267 
268   /* special case if entire horizontal range fits in a single word */
269   if (word_count == 1)
270     {
271       left_mask = 0;
272       right_mask = ~ pixel_range_mask (left_bit, right_bit);
273       word_count = 0;
274     }
275   else
276     {
277       if (left_bit)
278 	{
279 	  left_mask = ~ pixel_range_mask (left_bit, BITS_PER_WORD - 1);
280 	  word_count--;
281 	}
282 
283       if (right_bit != (BITS_PER_WORD - 1))
284 	{
285 	  right_mask = ~ pixel_range_mask (0, right_bit);
286 	  word_count--;
287 	}
288     }
289 
290   for (y = 0; y < rect_height (& dest_rect); y++)
291     {
292       word_t *wp = rp;
293 
294       /* partial word at left, if any */
295       if (left_mask)
296 	*(wp++) &= left_mask;
297 
298       /* use Duff's Device for the full words */
299       if (word_count)
300 	{
301 	  int32_t i = word_count;
302 	  switch (i % 8)
303 	    {
304 	      while (i > 0)
305 		{
306 		  *(wp++) = 0;
307 		case 7: *(wp++) = 0;
308 		case 6: *(wp++) = 0;
309 		case 5: *(wp++) = 0;
310 		case 4: *(wp++) = 0;
311 		case 3: *(wp++) = 0;
312 		case 2: *(wp++) = 0;
313 		case 1: *(wp++) = 0;
314 		case 0: i -= 8;
315 		}
316 	    }
317 	}
318 
319       /* partial word at right, if any */
320       if (right_mask)
321 	*wp &= right_mask;
322 
323       /* advance to next row */
324       rp += dest_bitmap->row_words;
325     }
326 }
327 
328 
329 #if 0
330 static void blt (Bitmap *src_bitmap,
331 		 Rect *src_rect,
332 		 Bitmap *dest_bitmap,
333 		 Rect *dest_rect)
334 {
335   int32_t y;
336   word_t *rp;
337 
338   /* This function requires a non-null src rect */
339   assert (dest_rect->min.x < dest_rect->max.x);
340   assert (dest_rect->min.y < dest_rect->max.y);
341 
342   /* and a non-null dest rect */
343   assert (dest_rect->min.x < dest_rect->max.x);
344   assert (dest_rect->min.y < dest_rect->max.y);
345 
346   /* and that the widths and heights of the rects match */
347   assert (rect_width (src_rect) == rect_width (dest_rect));
348   assert (rect_height (src_rect) == rect_height (dest_rect));
349 
350   /* and that the rows of the src rect lie entirely within the src bitmap */
351   assert (dest_rect->min.y >= dest_bitmap->rect->min.y);
352   assert (dest_rect->max.y <= dest_bitmap->rect->max.y);
353 
354   /* and that the rows of the dest rect lie entirely within the dest bitmap */
355   assert (dest_rect->min.y >= dest_bitmap->rect->min.y);
356   assert (dest_rect->max.y <= dest_bitmap->rect->max.y);
357 
358   /* clip the x axis of the dest_rect to the bounds of the dest bitmap,
359      and adjust the src_rect to match */
360   if (dest_rect->min.x < dest_bitmap->rect.min.x)
361     {
362       src_rect->min.x += ???;
363       dest_rect->min.x = dest_bitmap->rect.min.x;
364     }
365   if (dest_rect->max.x > dest_bitmap->rect.max.x)
366     {
367       dest_rect->max.x = dest_bitmap->rect.max.x;
368     }
369 
370   rp = ???;
371   for (y = 0; y < rect_height (dest_rect); y++)
372     {
373   ???;
374       rp += dest_bitmap->row_words;
375     }
376 }
377 
378 
379 /*
380  * The destination rectangle is first clipped to the dest bitmap, and
381  * the source rectangle is adjusted in the corresponding manner.
382  * What's left is divided into five sections, any of which may be
383  * null.  The portion that actually corresponds to the intersection of
384  * the source rectangle and the source bitmpa is the "middle".  The
385  * other four sections will use the background color as the source
386  * operand.
387  *
388  *
389  *   y0 ->  -------------------------------------------------
390  *          |                     top                       |
391  *          |                                               |
392  *   y1 ->  -------------------------------------------------
393  *          |   left        |    middle     |    right      |
394  *          |               |               |               |
395  *   y2 ->  -------------------------------------------------
396  *          |                     bottom                    |
397  *          |                                               |
398  *   y3 ->  -------------------------------------------------
399  *
400  *          ^               ^               ^               ^
401  *          |               |               |               |
402  *         x0              x1              x2              x3
403  *
404  * */
405 Bitmap *bitblt (Bitmap *src_bitmap,
406 		Rect   *src_rect,
407 		Bitmap *dest_bitmap,
408 		Point  *dest_min,
409 		int tfn,
410 		int background)
411 {
412   Rect sr, dr;     /* src and dest rects, clipped to visible portion of
413 		      dest rect */
414   uint32_t drw, drh;    /* dest rect width, height - gets adjusted */
415   Point src_point, dest_point;
416 
417   /* dest coordinates: */
418   uint32_t x0, x1, x2, x3;
419   uint32_t y0, y1, y2, y3;
420 
421   {
422     sr = * src_rect;
423 
424     uint32_t srw = rect_width (& sr);
425     uint32_t srh = rect_height (& sr);
426 
427     if ((srw < 0) || (srh < 0))
428       goto done;  /* the source rect is empty! */
429 
430     dr.min.x = dest_min->x;
431     dr.min.y = dest_min->y;
432     dr.max.x = dr.min.x + srw;
433     dr.max.y = dr.min.y + srh;
434   }
435 
436   if (! dest_bitmap)
437     {
438       dest_bitmap = create_bitmap (& dr);
439       if (! dest_bitmap)
440 	return (NULL);
441     }
442 
443   if ((dr.min.x >= dest_bitmap->rect.max.x) ||
444       (dr.min.y >= dest_bitmap->rect.max.y))
445     goto done;  /* the dest rect isn't even in the dest bitmap! */
446 
447   /* crop dest rect to dest bitmap */
448   delta = dest_bitmap->rect.min.x - dr.min.x;
449   if (delta > 0)
450     {
451       sr.min.x += delta;
452       dr.min.x += delta;
453     }
454 
455   delta = dest_bitmap->rect.min.y - dr.min.y;
456   if (delta > 0)
457     {
458       sr.min.y += delta;
459       dr.min.y += delta;
460     }
461 
462   delta = dr.max.x - dest_bitmap->rect.max.x;
463   if (delta > 0)
464     {
465       sr.max.x -= delta;
466       dr.max.x -= delta;
467     }
468 
469   delta = dr.max.y - dest_bitmap->rect.max.y;
470   if (delta > 0)
471     {
472       sr.max.x -= delta;
473       dr.max.x -= delta;
474     }
475 
476   drw = rect_width (& dr);
477   drh = rect_height (& dh);
478 
479   x0 = dr.min.x;
480   y0 = dr.min.y;
481   x3 = dr.max.x;
482   y3 = dr.max.y;
483 
484 #if 0
485   /* if the source rect min y is >= the source bitmap max y,
486      we transfer background color to the entire dest rect */
487   if (sr.min.y >= src->rect.max.y)
488     {
489       blt_background (dest_bitmap, dr);
490       goto done;
491     }
492 #endif
493 
494   /* top */
495   if (y0 != y1)
496     {
497       dr2.min.x = x0;
498       dr2.max.x = x3;
499       dr2.min.y = y0;
500       dr2.max.y = y1;
501       blt_background (dest_bitmap, & dr2);
502     }
503 
504   /*
505    * top:  if the source rect min y is less than the source bitmap min y,
506    * we need to transfer some backgound color to the top part of the dest
507    * rect
508    */
509   if (sr.min.y < src->rect.min.y)
510     {
511       Rect dr2;
512       uint32 bg_height;
513 
514       bg_height = src->rect.min.y - sr.min.y;
515       if (bg_height > sh)
516 	bg_height = sh;
517 
518       dr2 = dr;
519       dr2.max.y = dr2.min.y + bg_height;
520 
521       blt_background (dest_bitmap, & dr2);
522 
523       /* now reduce the rect height by the number of lines of background
524 	 color */
525       sr.min.y += bg_height;
526       dr.min.y += bg_height;
527       sh -= bg_height;
528       dh -= bg_height;
529 
530       if (sr.min.y == sr.max.y)
531 	goto done;
532     }
533 
534   if (y1 != y2)
535     {
536       /* left */
537       if (x0 != x1)
538 	{
539 	  dr2.min.x = x1;
540 	  dr2.max.x = x1;
541 	  dr2.min.y = y1;
542 	  dr2.max.y = y2
543 	  blt_background (dest_bitmap, & dr2);
544 	}
545 
546       /* middle */
547       if (x1 != x2)
548 	{
549 	  /* ??? */
550 	}
551 
552       /* right */
553       if (x2 != x3)
554 	{
555 	  dr2.min.x = x2;
556 	  dr2.max.x = x3;
557 	  dr2.min.y = y1;
558 	  dr2.max.y = y2
559 	  blt_background (dest_bitmap, & dr2);
560 	}
561     }
562 
563   /* bottom */
564   if (y2 != y3)
565     {
566       dr2.min.x = x0;
567       dr2.max.x = x3;
568       dr2.min.y = y2;
569       dr2.max.y = y3;
570       blt_background (dest_bitmap, & dr2);
571     }
572 
573  done:
574   return (dest_bitmap);
575 }
576 #else
bitblt(Bitmap * src_bitmap,Rect * src_rect,Bitmap * dest_bitmap,Point * dest_min,int tfn,int background)577 Bitmap *bitblt (Bitmap *src_bitmap,
578 		Rect   *src_rect,
579 		Bitmap *dest_bitmap,
580 		Point  *dest_min,
581 		int tfn,
582 		int background)
583 {
584   Point src_point, dest_point;
585 
586   if (! dest_bitmap)
587     {
588       Rect dest_rect = {{ 0, 0 }, { dest_min->x + rect_width (src_rect),
589 				    dest_min->y + rect_height (src_rect) }};
590       dest_bitmap = create_bitmap (& dest_rect);
591       if (! dest_bitmap)
592 	return (NULL);
593     }
594 
595   if (tfn == TF_SRC)
596     {
597       for (src_point.y = src_rect->min.y;
598 	   src_point.y < src_rect->max.y;
599 	   src_point.y++)
600 	{
601 	  dest_point.y = dest_min->y + src_point.y - src_rect->min.y;
602 
603 	  for (src_point.x = src_rect->min.x;
604 	       src_point.x < src_rect->max.x;
605 	       src_point.x++)
606 	    {
607 	      bool a;
608 
609 	      dest_point.x = dest_min->x + src_point.x - src_rect->min.x;
610 
611 	      a = get_pixel (src_bitmap, src_point);
612 	      set_pixel (dest_bitmap, dest_point, a);
613 	    }
614 	}
615     }
616   else
617     {
618       for (src_point.y = src_rect->min.y;
619 	   src_point.y < src_rect->max.y;
620 	   src_point.y++)
621 	{
622 	  dest_point.y = dest_min->y + src_point.y - src_rect->min.y;
623 
624 	  for (src_point.x = src_rect->min.x;
625 	       src_point.x < src_rect->max.x;
626 	       src_point.x++)
627 	    {
628 	      bool a, b, c;
629 
630 	      dest_point.x = dest_min->x + src_point.x - src_rect->min.x;
631 
632 	      a = get_pixel (src_bitmap, src_point);
633 	      b = get_pixel (dest_bitmap, dest_point);
634 	      c = (tfn & (1 << (a * 2 + b))) != 0;
635 
636 	      set_pixel (dest_bitmap, dest_point, c);
637 	    }
638 	}
639     }
640   return (dest_bitmap);
641 }
642 #endif
643 
644 
645 /* in-place transformations */
flip_h(Bitmap * src)646 void flip_h (Bitmap *src)
647 {
648   word_t *rp;  /* row pointer */
649   int32_t y;
650   int shift1, shift2;
651 
652   rp = src->bits;
653   if ((rect_width (& src->rect) & 7) == 0)
654     {
655       for (y = src->rect.min.y; y < src->rect.max.y; y++)
656 	{
657           reverse_range_of_bytes ((uint8_t *) rp, rect_width (& src->rect) / 8);
658 	  rp += src->row_words;
659 	}
660       return;
661     }
662 
663   realloc_temp_buffer ((src->row_words + 1) * sizeof (word_t));
664 
665   temp_buffer [0] = 0;
666   shift1 = rect_width (& src->rect) & (BITS_PER_WORD - 1);
667   shift2 = BITS_PER_WORD - shift1;
668 
669   for (y = src->rect.min.y; y < src->rect.max.y; y++)
670     {
671       word_t d1, d2;
672       word_t *p1;  /* work src ptr */
673       word_t *p2;  /* work dest ptr */
674 
675       memcpy (temp_buffer + 1, rp, src->row_words * sizeof (word_t));
676       p1 = temp_buffer + src->row_words;
677       p2 = rp;
678 
679       d2 = *(p1--);
680 
681       while (p1 >= temp_buffer)
682 	{
683 	  word_t t;
684 	  d1 = *(p1--);
685 	  t = (d1 >> shift1) | (d2 << shift2);
686 	  *(p2++) = bit_reverse_word (t);
687 	  d2 = d1;
688 	}
689 
690       rp += src->row_words;
691     }
692 }
693 
694 
flip_v(Bitmap * src)695 void flip_v (Bitmap *src)
696 {
697   word_t *p1, *p2;
698 
699   realloc_temp_buffer (src->row_words * sizeof (word_t));
700 
701   p1 = src->bits;
702   p2 = src->bits + src->row_words * (rect_height (& src->rect) - 1);
703   while (p1 < p2)
704     {
705       memcpy (temp_buffer, p1, src->row_words * sizeof (word_t));
706       memcpy (p1, p2, src->row_words * sizeof (word_t));
707       memcpy (p2, temp_buffer, src->row_words * sizeof (word_t));
708       p1 += src->row_words;
709       p2 -= src->row_words;
710     }
711 }
712 
rot_180(Bitmap * src)713 void rot_180 (Bitmap *src)  /* combination of flip_h and flip_v */
714 {
715   flip_h (src);
716   flip_v (src);
717 }
718 
719 /* "in-place" transformations - will allocate new memory and free old */
720 // XXX hideously inefficient!
transpose(Bitmap * src)721 void transpose (Bitmap *src)
722 {
723   Rect transposed_rect;
724   Bitmap *dest;
725   Point src_coord, dest_coord;
726 
727   transposed_rect.min.x = src->rect.min.y;
728   transposed_rect.max.x = src->rect.max.y;
729   transposed_rect.min.y = src->rect.min.x;
730   transposed_rect.max.y = src->rect.max.x;
731   dest = create_bitmap (& transposed_rect);
732 
733   for (src_coord.y = src->rect.min.y; src_coord.y < src->rect.max.y; src_coord.y++)
734     {
735       dest_coord.x = src_coord.y;
736 
737       for (src_coord.x = src->rect.min.x; src_coord.x < src->rect.max.x; src_coord.x++)
738 	{
739 	  dest_coord.y = src_coord.x;
740 	  set_pixel(dest, dest_coord, get_pixel(src, src_coord));
741 	}
742     }
743 
744   SWAP(Bitmap, *src, *dest);
745   free_bitmap(dest);
746 }
747 
rot_90(Bitmap * src)748 void rot_90 (Bitmap *src)   /* transpose + flip_h */
749 {
750   transpose (src);
751   flip_h (src);
752 }
753 
rot_270(Bitmap * src)754 void rot_270 (Bitmap *src)  /* transpose + flip_v */
755 {
756   transpose (src);
757   flip_v (src);
758 }
759 
760 
761 /* frees original! */
resize_bitmap(Bitmap * src,int width_pixels,int height_pixels)762 Bitmap *resize_bitmap (Bitmap *src,
763 		       int width_pixels,
764 		       int height_pixels)
765 {
766   Rect src_rect;
767   Point dest_min;
768   Bitmap *dest;
769 
770   src_rect.min.x = (rect_width (& src->rect) - width_pixels) / 2;
771   src_rect.min.y = (rect_height (& src->rect) - height_pixels) / 2;
772   src_rect.max.x = src_rect.min.x + width_pixels;
773   src_rect.max.y = src_rect.min.y + height_pixels;
774 
775   dest_min.x = 0;
776   dest_min.y = 0;
777 
778   dest = bitblt (src, & src_rect, NULL, & dest_min, TF_SRC, 0);
779   free_bitmap (src);
780   return (dest);
781 }
782 
783 
784 /* "in place" rotation */
rotate_bitmap(Bitmap * src,int rotation)785 void rotate_bitmap (Bitmap *src, int rotation)
786 {
787   switch (rotation)
788     {
789     case 0: break;
790     case 90: rot_90 (src); break;
791     case 180: rot_180 (src); break;
792     case 270: rot_270 (src); break;
793     default:
794       fprintf (stderr, "rotation %d, but must be 0, 90, 180, or 270\n", rotation);
795     }
796 }
797