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