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