Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/dvi/mdvi-lib/bitmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'dvi/mdvi-lib/bitmap.c')
-rw-r--r--dvi/mdvi-lib/bitmap.c1106
1 files changed, 1106 insertions, 0 deletions
diff --git a/dvi/mdvi-lib/bitmap.c b/dvi/mdvi-lib/bitmap.c
new file mode 100644
index 0000000..b30369f
--- /dev/null
+++ b/dvi/mdvi-lib/bitmap.c
@@ -0,0 +1,1106 @@
+/*
+ * Copyright (C) 2000, Matias Atria
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Bitmap manipulation routines */
+
+#include <stdlib.h>
+
+#include "mdvi.h"
+
+/* bit_masks[n] contains a BmUnit with `n' contiguous bits */
+
+static BmUnit bit_masks[] = {
+ 0x0, 0x1, 0x3, 0x7,
+ 0xf, 0x1f, 0x3f, 0x7f,
+ 0xff,
+#if BITMAP_BYTES > 1
+ 0x1ff, 0x3ff, 0x7ff,
+ 0xfff, 0x1fff, 0x3fff, 0x7fff,
+ 0xffff,
+#if BITMAP_BYTES > 2
+ 0x1ffff, 0x3ffff, 0x7ffff,
+ 0xfffff, 0x1fffff, 0x3fffff, 0x7fffff,
+ 0xffffff, 0x1ffffff, 0x3ffffff, 0x7ffffff,
+ 0xfffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff,
+ 0xffffffff
+#endif /* BITMAP_BYTES > 2 */
+#endif /* BITMAP_BYTES > 1 */
+};
+
+#ifndef NODEBUG
+#define SHOW_OP_DATA (DEBUGGING(BITMAP_OPS) && DEBUGGING(BITMAP_DATA))
+#endif
+
+/* cache for color tables, to avoid creating them for every glyph */
+typedef struct {
+ Ulong fg;
+ Ulong bg;
+ Uint nlevels;
+ Ulong *pixels;
+ int density;
+ double gamma;
+ Uint hits;
+} ColorCache;
+
+/*
+ * Some useful macros to manipulate bitmap data
+ * SEGMENT(m,n) = bit mask for a segment of `m' contiguous bits
+ * starting at column `n'. These macros assume that
+ * m + n <= BITMAP_BITS, 0 <= m, n.
+ */
+#ifdef WORD_BIG_ENDIAN
+#define SEGMENT(m,n) (bit_masks[m] << (BITMAP_BITS - (m) - (n)))
+#else
+#define SEGMENT(m,n) (bit_masks[m] << (n))
+#endif
+
+/* sampling and shrinking routines shamelessly stolen from xdvi */
+
+static int sample_count[] = {
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
+};
+
+/* bit_swap[j] = j with all bits inverted (i.e. msb -> lsb) */
+static Uchar bit_swap[] = {
+ 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
+ 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+ 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+ 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+ 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
+ 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+ 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
+ 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+ 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+ 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+ 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
+ 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+ 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
+ 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+ 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+ 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+ 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
+ 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+ 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
+ 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+ 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+ 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+ 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
+ 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+ 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
+ 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+ 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+ 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+ 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
+ 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+ 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
+ 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
+};
+
+#define CCSIZE 256
+static ColorCache color_cache[CCSIZE];
+static int cc_entries;
+
+#define GAMMA_DIFF 0.005
+
+static Ulong *get_color_table(DviDevice *dev,
+ int nlevels, Ulong fg, Ulong bg, double gamma, int density);
+
+
+/* create a color table */
+static Ulong *get_color_table(DviDevice *dev,
+ int nlevels, Ulong fg, Ulong bg, double gamma, int density)
+{
+ ColorCache *cc, *tofree;
+ int lohits;
+ Ulong *pixels;
+ int status;
+
+ lohits = color_cache[0].hits;
+ tofree = &color_cache[0];
+ /* look in the cache and see if we have one that matches this request */
+ for(cc = &color_cache[0]; cc < &color_cache[cc_entries]; cc++) {
+ if(cc->hits < lohits) {
+ lohits = cc->hits;
+ tofree = cc;
+ }
+ if(cc->fg == fg && cc->bg == bg && cc->density == density &&
+ cc->nlevels == nlevels && fabs(cc->gamma - gamma) <= GAMMA_DIFF)
+ break;
+ }
+
+ if(cc < &color_cache[cc_entries]) {
+ cc->hits++;
+ return cc->pixels;
+ }
+
+ DEBUG((DBG_DEVICE, "Adding color table to cache (fg=%lu, bg=%lu, n=%d)\n",
+ fg, bg, nlevels));
+
+ /* no entry was found in the cache, create a new one */
+ if(cc_entries < CCSIZE) {
+ cc = &color_cache[cc_entries++];
+ cc->pixels = NULL;
+ } else {
+ cc = tofree;
+ xfree(cc->pixels);
+ }
+ pixels = xnalloc(Ulong, nlevels);
+ status = dev->alloc_colors(dev->device_data,
+ pixels, nlevels, fg, bg, gamma, density);
+ if(status < 0) {
+ xfree(pixels);
+ return NULL;
+ }
+ cc->fg = fg;
+ cc->bg = bg;
+ cc->gamma = gamma;
+ cc->density = density;
+ cc->nlevels = nlevels;
+ cc->pixels = pixels;
+ cc->hits = 1;
+ return pixels;
+}
+
+/*
+ * next we have three bitmap functions to convert bitmaps in LSB bit order
+ * with 8, 16 and 32 bits per unit, to our internal format. The differences
+ * are minimal, but writing a generic function to handle all unit sizes is
+ * hopelessly slow.
+ */
+
+BITMAP *bitmap_convert_lsb8(Uchar *bits, int w, int h)
+{
+ BITMAP *bm;
+ int i;
+ Uchar *unit;
+ register Uchar *curr;
+ Uchar *end;
+ int bytes;
+
+ DEBUG((DBG_BITMAP_OPS, "convert LSB %dx%d@8 -> bitmap\n", w, h));
+
+ bm = bitmap_alloc_raw(w, h);
+
+ /* this is the number of bytes in the original bitmap */
+ bytes = ROUND(w, 8);
+ unit = (Uchar *)bm->data;
+ end = unit + bm->stride;
+ curr = bits;
+ /* we try to do this as fast as we can */
+ for(i = 0; i < h; i++) {
+#ifdef WORD_LITTLE_ENDIAN
+ memcpy(unit, curr, bytes);
+ curr += bytes;
+#else
+ int j;
+
+ for(j = 0; j < bytes; curr++, j++)
+ unit[j] = bit_swap[*curr];
+#endif
+ memzero(unit + bytes, bm->stride - bytes);
+ unit += bm->stride;
+ }
+ if(SHOW_OP_DATA)
+ bitmap_print(stderr, bm);
+ return bm;
+}
+
+BITMAP *bitmap_convert_msb8(Uchar *data, int w, int h)
+{
+ BITMAP *bm;
+ Uchar *unit;
+ Uchar *curr;
+ int i;
+ int bytes;
+
+ bm = bitmap_alloc(w, h);
+ bytes = ROUND(w, 8);
+ unit = (Uchar *)bm->data;
+ curr = data;
+ for(i = 0; i < h; i++) {
+#ifdef WORD_LITTLE_ENDIAN
+ int j;
+
+ for(j = 0; j < bytes; curr++, j++)
+ unit[j] = bit_swap[*curr];
+#else
+ memcpy(unit, curr, bytes);
+ curr += bytes;
+#endif
+ memzero(unit + bytes, bm->stride - bytes);
+ unit += bm->stride;
+ }
+ if(SHOW_OP_DATA)
+ bitmap_print(stderr, bm);
+ return bm;
+}
+
+
+BITMAP *bitmap_copy(BITMAP *bm)
+{
+ BITMAP *nb = bitmap_alloc(bm->width, bm->height);
+
+ DEBUG((DBG_BITMAP_OPS, "copy %dx%d\n", bm->width, bm->height));
+ memcpy(nb->data, bm->data, bm->height * bm->stride);
+ return nb;
+}
+
+BITMAP *bitmap_alloc(int w, int h)
+{
+ BITMAP *bm;
+
+ bm = xalloc(BITMAP);
+ bm->width = w;
+ bm->height = h;
+ bm->stride = BM_BYTES_PER_LINE(bm);
+ if(h && bm->stride)
+ bm->data = (BmUnit *)xcalloc(h, bm->stride);
+ else
+ bm->data = NULL;
+
+ return bm;
+}
+
+BITMAP *bitmap_alloc_raw(int w, int h)
+{
+ BITMAP *bm;
+
+ bm = xalloc(BITMAP);
+ bm->width = w;
+ bm->height = h;
+ bm->stride = BM_BYTES_PER_LINE(bm);
+ if(h && bm->stride)
+ bm->data = (BmUnit *)xmalloc(h * bm->stride);
+ else
+ bm->data = NULL;
+
+ return bm;
+}
+
+void bitmap_destroy(BITMAP *bm)
+{
+ if(bm->data)
+ free(bm->data);
+ free(bm);
+}
+
+void bitmap_print(FILE *out, BITMAP *bm)
+{
+ int i, j;
+ BmUnit *a, mask;
+ static const char labels[] = {
+ '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'
+ };
+ int sub;
+
+ a = bm->data;
+ fprintf(out, " ");
+ if(bm->width > 10) {
+ putchar('0');
+ sub = 0;
+ for(j = 2; j <= bm->width; j++)
+ if((j %10) == 0) {
+ if((j % 100) == 0) {
+ fprintf(out, "*");
+ sub += 100;
+ } else
+ fprintf(out, "%d", (j - sub)/10);
+ } else
+ putc(' ', out);
+ fprintf(out, "\n ");
+ }
+ for(j = 0; j < bm->width; j++)
+ putc(labels[j % 10], out);
+ putchar('\n');
+ for(i = 0; i < bm->height; i++) {
+ mask = FIRSTMASK;
+ a = (BmUnit *)((char *)bm->data + i * bm->stride);
+ fprintf(out, "%3d ", i+1);
+ for(j = 0; j < bm->width; j++) {
+ if(*a & mask)
+ putc('#', out);
+ else
+ putc('.', out);
+ if(mask == LASTMASK) {
+ a++;
+ mask = FIRSTMASK;
+ } else
+ NEXTMASK(mask);
+ }
+ putchar('\n');
+ }
+}
+
+void bitmap_set_col(BITMAP *bm, int row, int col, int count, int state)
+{
+ BmUnit *ptr;
+ BmUnit mask;
+
+ ptr = __bm_unit_ptr(bm, col, row);
+ mask = FIRSTMASKAT(col);
+
+ while(count-- > 0) {
+ if(state)
+ *ptr |= mask;
+ else
+ *ptr &= ~mask;
+ /* move to next row */
+ ptr = bm_offset(ptr, bm->stride);
+ }
+}
+
+/*
+ * to use this function you should first make sure that
+ * there is room for `count' bits in the scanline
+ *
+ * A general-purpose (but not very efficient) function to paint `n' pixels
+ * on a bitmap, starting at position (x, y) would be:
+ *
+ * bitmap_paint_bits(__bm_unit_ptr(bitmap, x, y), x % BITMAP_BITS, n)
+ *
+ */
+void bitmap_paint_bits(BmUnit *ptr, int n, int count)
+{
+ /* paint the head */
+ if(n + count > BITMAP_BITS) {
+ *ptr |= SEGMENT(BITMAP_BITS - n, n);
+ count -= BITMAP_BITS - n;
+ ptr++;
+ } else {
+ *ptr |= SEGMENT(count, n);
+ return;
+ }
+
+ /* paint the middle */
+ for(; count >= BITMAP_BITS; count -= BITMAP_BITS)
+ *ptr++ = bit_masks[BITMAP_BITS];
+
+ /* paint the tail */
+ if(count > 0)
+ *ptr |= SEGMENT(count, 0);
+}
+
+/*
+ * same as paint_bits but clears pixels instead of painting them. Written
+ * as a separate function for efficiency reasons.
+ */
+void bitmap_clear_bits(BmUnit *ptr, int n, int count)
+{
+ if(n + count > BITMAP_BITS) {
+ *ptr &= ~SEGMENT(BITMAP_BITS - n, n);
+ count -= BITMAP_BITS;
+ ptr++;
+ } else {
+ *ptr &= ~SEGMENT(count, n);
+ return;
+ }
+
+ for(; count >= BITMAP_BITS; count -= BITMAP_BITS)
+ *ptr++ = 0;
+
+ if(count > 0)
+ *ptr &= ~SEGMENT(count, 0);
+}
+
+/* the general function to paint rows. Still used by the PK reader, but that
+ * will change soon (The GF reader already uses bitmap_paint_bits()).
+ */
+void bitmap_set_row(BITMAP *bm, int row, int col, int count, int state)
+{
+ BmUnit *ptr;
+
+ ptr = __bm_unit_ptr(bm, col, row);
+ if(state)
+ bitmap_paint_bits(ptr, col & (BITMAP_BITS-1), count);
+ else
+ bitmap_clear_bits(ptr, col & (BITMAP_BITS-1), count);
+}
+
+/*
+ * Now several `flipping' operations
+ */
+
+void bitmap_flip_horizontally(BITMAP *bm)
+{
+ BITMAP nb;
+ BmUnit *fptr, *tptr;
+ BmUnit fmask, tmask;
+ int w, h;
+
+ nb.width = bm->width;
+ nb.height = bm->height;
+ nb.stride = bm->stride;
+ nb.data = xcalloc(bm->height, bm->stride);
+
+ fptr = bm->data;
+ tptr = __bm_unit_ptr(&nb, nb.width-1, 0);
+ for(h = 0; h < bm->height; h++) {
+ BmUnit *fline, *tline;
+
+ fline = fptr;
+ tline = tptr;
+ fmask = FIRSTMASK;
+ tmask = FIRSTMASKAT(nb.width-1);
+ for(w = 0; w < bm->width; w++) {
+ if(*fline & fmask)
+ *tline |= tmask;
+ if(fmask == LASTMASK) {
+ fmask = FIRSTMASK;
+ fline++;
+ } else
+ NEXTMASK(fmask);
+ if(tmask == FIRSTMASK) {
+ tmask = LASTMASK;
+ tline--;
+ } else
+ PREVMASK(tmask);
+ }
+ fptr = bm_offset(fptr, bm->stride);
+ tptr = bm_offset(tptr, bm->stride);
+ }
+ DEBUG((DBG_BITMAP_OPS, "flip_horizontally (%d,%d) -> (%d,%d)\n",
+ bm->width, bm->height, nb.width, nb.height));
+ xfree(bm->data);
+ bm->data = nb.data;
+ if(SHOW_OP_DATA)
+ bitmap_print(stderr, bm);
+}
+
+void bitmap_flip_vertically(BITMAP *bm)
+{
+ BITMAP nb;
+ BmUnit *fptr, *tptr;
+ BmUnit fmask;
+ int w, h;
+
+ nb.width = bm->width;
+ nb.height = bm->height;
+ nb.stride = bm->stride;
+ nb.data = xcalloc(bm->height, bm->stride);
+
+ fptr = bm->data;
+ tptr = __bm_unit_ptr(&nb, 0, nb.height-1);
+ for(h = 0; h < bm->height; h++) {
+ BmUnit *fline, *tline;
+
+ fline = fptr;
+ tline = tptr;
+ fmask = FIRSTMASK;
+ for(w = 0; w < bm->width; w++) {
+ if(*fline & fmask)
+ *tline |= fmask;
+ if(fmask == LASTMASK) {
+ fmask = FIRSTMASK;
+ fline++;
+ tline++;
+ } else
+ NEXTMASK(fmask);
+ }
+ fptr = bm_offset(fptr, bm->stride);
+ tptr = (BmUnit *)((char *)tptr - bm->stride);
+ }
+ DEBUG((DBG_BITMAP_OPS, "flip_vertically (%d,%d) -> (%d,%d)\n",
+ bm->width, bm->height, nb.width, nb.height));
+ xfree(bm->data);
+ bm->data = nb.data;
+ if(SHOW_OP_DATA)
+ bitmap_print(stderr, bm);
+}
+
+void bitmap_flip_diagonally(BITMAP *bm)
+{
+ BITMAP nb;
+ BmUnit *fptr, *tptr;
+ BmUnit fmask, tmask;
+ int w, h;
+
+ nb.width = bm->width;
+ nb.height = bm->height;
+ nb.stride = bm->stride;
+ nb.data = xcalloc(bm->height, bm->stride);
+
+ fptr = bm->data;
+ tptr = __bm_unit_ptr(&nb, nb.width-1, nb.height-1);
+ for(h = 0; h < bm->height; h++) {
+ BmUnit *fline, *tline;
+
+ fline = fptr;
+ tline = tptr;
+ fmask = FIRSTMASK;
+ tmask = FIRSTMASKAT(nb.width-1);
+ for(w = 0; w < bm->width; w++) {
+ if(*fline & fmask)
+ *tline |= tmask;
+ if(fmask == LASTMASK) {
+ fmask = FIRSTMASK;
+ fline++;
+ } else
+ NEXTMASK(fmask);
+ if(tmask == FIRSTMASK) {
+ tmask = LASTMASK;
+ tline--;
+ } else
+ PREVMASK(tmask);
+ }
+ fptr = bm_offset(fptr, bm->stride);
+ tptr = bm_offset(tptr, -nb.stride);
+ }
+ DEBUG((DBG_BITMAP_OPS, "flip_diagonally (%d,%d) -> (%d,%d)\n",
+ bm->width, bm->height, nb.width, nb.height));
+ xfree(bm->data);
+ bm->data = nb.data;
+ if(SHOW_OP_DATA)
+ bitmap_print(stderr, bm);
+}
+
+void bitmap_rotate_clockwise(BITMAP *bm)
+{
+ BITMAP nb;
+ BmUnit *fptr, *tptr;
+ BmUnit fmask, tmask;
+ int w, h;
+
+ nb.width = bm->height;
+ nb.height = bm->width;
+ nb.stride = BM_BYTES_PER_LINE(&nb);
+ nb.data = xcalloc(nb.height, nb.stride);
+
+ fptr = bm->data;
+ tptr = __bm_unit_ptr(&nb, nb.width - 1, 0);
+
+ tmask = FIRSTMASKAT(nb.width-1);
+ for(h = 0; h < bm->height; h++) {
+ BmUnit *fline, *tline;
+
+ fmask = FIRSTMASK;
+ fline = fptr;
+ tline = tptr;
+ for(w = 0; w < bm->width; w++) {
+ if(*fline & fmask)
+ *tline |= tmask;
+ if(fmask == LASTMASK) {
+ fmask = FIRSTMASK;
+ fline++;
+ } else
+ NEXTMASK(fmask);
+ /* go to next row */
+ tline = bm_offset(tline, nb.stride);
+ }
+ fptr = bm_offset(fptr, bm->stride);
+ if(tmask == FIRSTMASK) {
+ tmask = LASTMASK;
+ tptr--;
+ } else
+ PREVMASK(tmask);
+ }
+
+ DEBUG((DBG_BITMAP_OPS, "rotate_clockwise (%d,%d) -> (%d,%d)\n",
+ bm->width, bm->height, nb.width, nb.height));
+ xfree(bm->data);
+ bm->data = nb.data;
+ bm->width = nb.width;
+ bm->height = nb.height;
+ bm->stride = nb.stride;
+ if(SHOW_OP_DATA)
+ bitmap_print(stderr, bm);
+}
+
+void bitmap_rotate_counter_clockwise(BITMAP *bm)
+{
+ BITMAP nb;
+ BmUnit *fptr, *tptr;
+ BmUnit fmask, tmask;
+ int w, h;
+
+ nb.width = bm->height;
+ nb.height = bm->width;
+ nb.stride = BM_BYTES_PER_LINE(&nb);
+ nb.data = xcalloc(nb.height, nb.stride);
+
+ fptr = bm->data;
+ tptr = __bm_unit_ptr(&nb, 0, nb.height - 1);
+
+ tmask = FIRSTMASK;
+ for(h = 0; h < bm->height; h++) {
+ BmUnit *fline, *tline;
+
+ fmask = FIRSTMASK;
+ fline = fptr;
+ tline = tptr;
+ for(w = 0; w < bm->width; w++) {
+ if(*fline & fmask)
+ *tline |= tmask;
+ if(fmask == LASTMASK) {
+ fmask = FIRSTMASK;
+ fline++;
+ } else
+ NEXTMASK(fmask);
+ /* go to previous row */
+ tline = bm_offset(tline, -nb.stride);
+ }
+ fptr = bm_offset(fptr, bm->stride);
+ if(tmask == LASTMASK) {
+ tmask = FIRSTMASK;
+ tptr++;
+ } else
+ NEXTMASK(tmask);
+ }
+
+ DEBUG((DBG_BITMAP_OPS, "rotate_counter_clockwise (%d,%d) -> (%d,%d)\n",
+ bm->width, bm->height, nb.width, nb.height));
+ xfree(bm->data);
+ bm->data = nb.data;
+ bm->width = nb.width;
+ bm->height = nb.height;
+ bm->stride = nb.stride;
+ if(SHOW_OP_DATA)
+ bitmap_print(stderr, bm);
+}
+
+void bitmap_flip_rotate_clockwise(BITMAP *bm)
+{
+ BITMAP nb;
+ BmUnit *fptr, *tptr;
+ BmUnit fmask, tmask;
+ int w, h;
+
+ nb.width = bm->height;
+ nb.height = bm->width;
+ nb.stride = BM_BYTES_PER_LINE(&nb);
+ nb.data = xcalloc(nb.height, nb.stride);
+
+ fptr = bm->data;
+ tptr = __bm_unit_ptr(&nb, nb.width-1, nb.height-1);
+
+ tmask = FIRSTMASKAT(nb.width-1);
+ for(h = 0; h < bm->height; h++) {
+ BmUnit *fline, *tline;
+
+ fmask = FIRSTMASK;
+ fline = fptr;
+ tline = tptr;
+ for(w = 0; w < bm->width; w++) {
+ if(*fline & fmask)
+ *tline |= tmask;
+ if(fmask == LASTMASK) {
+ fmask = FIRSTMASK;
+ fline++;
+ } else
+ NEXTMASK(fmask);
+ /* go to previous line */
+ tline = bm_offset(tline, -nb.stride);
+ }
+ fptr = bm_offset(fptr, bm->stride);
+ if(tmask == FIRSTMASK) {
+ tmask = LASTMASK;
+ tptr--;
+ } else
+ PREVMASK(tmask);
+ }
+ DEBUG((DBG_BITMAP_OPS, "flip_rotate_clockwise (%d,%d) -> (%d,%d)\n",
+ bm->width, bm->height, nb.width, nb.height));
+ xfree(bm->data);
+ bm->data = nb.data;
+ bm->width = nb.width;
+ bm->height = nb.height;
+ bm->stride = nb.stride;
+ if(SHOW_OP_DATA)
+ bitmap_print(stderr, bm);
+}
+
+void bitmap_flip_rotate_counter_clockwise(BITMAP *bm)
+{
+ BITMAP nb;
+ BmUnit *fptr, *tptr;
+ BmUnit fmask, tmask;
+ int w, h;
+
+ nb.width = bm->height;
+ nb.height = bm->width;
+ nb.stride = BM_BYTES_PER_LINE(&nb);
+ nb.data = xcalloc(nb.height, nb.stride);
+
+ fptr = bm->data;
+ tptr = nb.data;
+ tmask = FIRSTMASK;
+
+ for(h = 0; h < bm->height; h++) {
+ BmUnit *fline, *tline;
+
+ fmask = FIRSTMASK;
+ fline = fptr;
+ tline = tptr;
+ for(w = 0; w < bm->width; w++) {
+ if(*fline & fmask)
+ *tline |= tmask;
+ if(fmask == LASTMASK) {
+ fmask = FIRSTMASK;
+ fline++;
+ } else
+ NEXTMASK(fmask);
+ /* go to next line */
+ tline = bm_offset(tline, nb.stride);
+ }
+ fptr = bm_offset(fptr, bm->stride);
+ if(tmask == LASTMASK) {
+ tmask = FIRSTMASK;
+ tptr++;
+ } else
+ NEXTMASK(tmask);
+ }
+
+ DEBUG((DBG_BITMAP_OPS, "flip_rotate_counter_clockwise (%d,%d) -> (%d,%d)\n",
+ bm->width, bm->height, nb.width, nb.height));
+ xfree(bm->data);
+ bm->data = nb.data;
+ bm->width = nb.width;
+ bm->height = nb.height;
+ bm->stride = nb.stride;
+ if(SHOW_OP_DATA)
+ bitmap_print(stderr, bm);
+}
+
+#if 0
+void bitmap_transform(BITMAP *map, DviOrientation orient)
+{
+ switch(orient) {
+ case MDVI_ORIENT_TBLR:
+ break;
+ case MDVI_ORIENT_TBRL:
+ bitmap_flip_horizontally(map);
+ break;
+ case MDVI_ORIENT_BTLR:
+ bitmap_flip_vertically(map);
+ break;
+ case MDVI_ORIENT_BTRL:
+ bitmap_flip_diagonally(map);
+ break;
+ case MDVI_ORIENT_RP90:
+ bitmap_rotate_counter_clockwise(map);
+ break;
+ case MDVI_ORIENT_RM90:
+ bitmap_rotate_clockwise(map);
+ break;
+ case MDVI_ORIENT_IRP90:
+ bitmap_flip_rotate_counter_clockwise(map);
+ break;
+ case MDVI_ORIENT_IRM90:
+ bitmap_flip_rotate_clockwise(map);
+ break;
+ }
+}
+#endif
+
+/*
+ * Count the number of non-zero bits in a box of dimensions w x h, starting
+ * at column `step' in row `data'.
+ *
+ * Shamelessly stolen from xdvi.
+ */
+static int do_sample(BmUnit *data, int stride, int step, int w, int h)
+{
+ BmUnit *ptr, *end, *cp;
+ int shift, n;
+ int bits_left;
+ int wid;
+
+ ptr = data + step / BITMAP_BITS;
+ end = bm_offset(data, h * stride);
+ shift = FIRSTSHIFTAT(step);
+ bits_left = w;
+ n = 0;
+ while(bits_left) {
+#ifndef WORD_BIG_ENDIAN
+ wid = BITMAP_BITS - shift;
+#else
+ wid = shift;
+#endif
+ if(wid > bits_left)
+ wid = bits_left;
+ if(wid > 8)
+ wid = 8;
+#ifdef WORD_BIG_ENDIAN
+ shift -= wid;
+#endif
+ for(cp = ptr; cp < end; cp = bm_offset(cp, stride))
+ n += sample_count[(*cp >> shift) & bit_masks[wid]];
+#ifndef WORD_BIG_ENDIAN
+ shift += wid;
+#endif
+#ifdef WORD_BIG_ENDIAN
+ if(shift == 0) {
+ shift = BITMAP_BITS;
+ ptr++;
+ }
+#else
+ if(shift == BITMAP_BITS) {
+ shift = 0;
+ ptr++;
+ }
+#endif
+ bits_left -= wid;
+ }
+ return n;
+}
+
+void mdvi_shrink_box(DviContext *dvi, DviFont *font,
+ DviFontChar *pk, DviGlyph *dest)
+{
+ int x, y, z;
+ DviGlyph *glyph;
+ int hs, vs;
+
+ hs = dvi->params.hshrink;
+ vs = dvi->params.vshrink;
+ glyph = &pk->glyph;
+
+ x = (int)glyph->x / hs;
+ if((int)glyph->x - x * hs > 0)
+ x++;
+ dest->w = x + ROUND((int)glyph->w - glyph->x, hs);
+
+ z = (int)glyph->y + 1;
+ y = z / vs;
+ if(z - y * vs <= 0)
+ y--;
+ dest->h = y + ROUND((int)glyph->h - z, vs) + 1;
+ dest->x = x;
+ dest->y = glyph->y / vs;
+ dest->data = MDVI_GLYPH_EMPTY;
+ DEBUG((DBG_BITMAPS, "shrink_box: (%dw,%dh,%dx,%dy) -> (%dw,%dh,%dx,%dy)\n",
+ glyph->w, glyph->h, glyph->x, glyph->y,
+ dest->w, dest->h, dest->x, dest->y));
+}
+
+void mdvi_shrink_glyph(DviContext *dvi, DviFont *font,
+ DviFontChar *pk, DviGlyph *dest)
+{
+ int rows_left, rows, init_cols;
+ int cols_left, cols;
+ BmUnit *old_ptr, *new_ptr;
+ BITMAP *oldmap, *newmap;
+ BmUnit m, *cp;
+ DviGlyph *glyph;
+ int sample, min_sample;
+ int old_stride;
+ int new_stride;
+ int x, y;
+ int w, h;
+ int hs, vs;
+
+ hs = dvi->params.hshrink;
+ vs = dvi->params.vshrink;
+
+ min_sample = vs * hs * dvi->params.density / 100;
+
+ glyph = &pk->glyph;
+ oldmap = (BITMAP *)glyph->data;
+
+ x = (int)glyph->x / hs;
+ init_cols = (int)glyph->x - x * hs;
+ if(init_cols <= 0)
+ init_cols += hs;
+ else
+ x++;
+ w = x + ROUND((int)glyph->w - glyph->x, hs);
+
+ cols = (int)glyph->y + 1;
+ y = cols / vs;
+ rows = cols - y * vs;
+ if(rows <= 0) {
+ rows += vs;
+ y--;
+ }
+ h = y + ROUND((int)glyph->h - cols, vs) + 1;
+
+ /* create the new glyph */
+ newmap = bitmap_alloc(w, h);
+ dest->data = newmap;
+ dest->x = x;
+ dest->y = glyph->y / vs;
+ dest->w = w;
+ dest->h = h;
+
+ old_ptr = oldmap->data;
+ old_stride = oldmap->stride;
+ new_ptr = newmap->data;
+ new_stride = newmap->stride;
+ rows_left = glyph->h;
+
+ while(rows_left) {
+ if(rows > rows_left)
+ rows = rows_left;
+ cols_left = glyph->w;
+ m = FIRSTMASK;
+ cp = new_ptr;
+ cols = init_cols;
+ while(cols_left > 0) {
+ if(cols > cols_left)
+ cols = cols_left;
+ sample = do_sample(old_ptr, old_stride,
+ glyph->w - cols_left, cols, rows);
+ if(sample >= min_sample)
+ *cp |= m;
+ if(m == LASTMASK) {
+ m = FIRSTMASK;
+ cp++;
+ } else
+ NEXTMASK(m);
+ cols_left -= cols;
+ cols = hs;
+ }
+ new_ptr = bm_offset(new_ptr, new_stride);
+ old_ptr = bm_offset(old_ptr, rows * old_stride);
+ rows_left -= rows;
+ rows = vs;
+ }
+ DEBUG((DBG_BITMAPS, "shrink_glyph: (%dw,%dh,%dx,%dy) -> (%dw,%dh,%dx,%dy)\n",
+ glyph->w, glyph->h, glyph->x, glyph->y,
+ dest->w, dest->h, dest->x, dest->y));
+ if(DEBUGGING(BITMAP_DATA))
+ bitmap_print(stderr, newmap);
+}
+
+void mdvi_shrink_glyph_grey(DviContext *dvi, DviFont *font,
+ DviFontChar *pk, DviGlyph *dest)
+{
+ int rows_left, rows;
+ int cols_left, cols, init_cols;
+ long sampleval, samplemax;
+ BmUnit *old_ptr;
+ void *image;
+ int w, h;
+ int x, y;
+ DviGlyph *glyph;
+ BITMAP *map;
+ Ulong *pixels;
+ int npixels;
+ Ulong colortab[2];
+ int hs, vs;
+ DviDevice *dev;
+
+ hs = dvi->params.hshrink;
+ vs = dvi->params.vshrink;
+ dev = &dvi->device;
+
+ glyph = &pk->glyph;
+ map = (BITMAP *)glyph->data;
+
+ x = (int)glyph->x / hs;
+ init_cols = (int)glyph->x - x * hs;
+ if(init_cols <= 0)
+ init_cols += hs;
+ else
+ x++;
+ w = x + ROUND((int)glyph->w - glyph->x, hs);
+
+ cols = (int)glyph->y + 1;
+ y = cols / vs;
+ rows = cols - y * vs;
+ if(rows <= 0) {
+ rows += vs;
+ y--;
+ }
+ h = y + ROUND((int)glyph->h - cols, vs) + 1;
+ ASSERT(w && h);
+
+ /* before touching anything, do this */
+ image = dev->create_image(dev->device_data, w, h, BITMAP_BITS);
+ if(image == NULL) {
+ mdvi_shrink_glyph(dvi, font, pk, dest);
+ return;
+ }
+
+ /* save these colors */
+ pk->fg = MDVI_CURRFG(dvi);
+ pk->bg = MDVI_CURRBG(dvi);
+
+ samplemax = vs * hs;
+ npixels = samplemax + 1;
+ pixels = get_color_table(&dvi->device, npixels, pk->fg, pk->bg,
+ dvi->params.gamma, dvi->params.density);
+ if(pixels == NULL) {
+ npixels = 2;
+ colortab[0] = pk->fg;
+ colortab[1] = pk->bg;
+ pixels = &colortab[0];
+ }
+
+ /* setup the new glyph */
+ dest->data = image;
+ dest->x = x;
+ dest->y = glyph->y / vs;
+ dest->w = w;
+ dest->h = h;
+
+ y = 0;
+ old_ptr = map->data;
+ rows_left = glyph->h;
+
+ while(rows_left && y < h) {
+ x = 0;
+ if(rows > rows_left)
+ rows = rows_left;
+ cols_left = glyph->w;
+ cols = init_cols;
+ while(cols_left && x < w) {
+ if(cols > cols_left)
+ cols = cols_left;
+ sampleval = do_sample(old_ptr, map->stride,
+ glyph->w - cols_left, cols, rows);
+ /* scale the sample value by the number of grey levels */
+ if(npixels - 1 != samplemax)
+ sampleval = ((npixels-1) * sampleval) / samplemax;
+ ASSERT(sampleval < npixels);
+ dev->put_pixel(image, x, y, pixels[sampleval]);
+ cols_left -= cols;
+ cols = hs;
+ x++;
+ }
+ for(; x < w; x++)
+ dev->put_pixel(image, x, y, pixels[0]);
+ old_ptr = bm_offset(old_ptr, rows * map->stride);
+ rows_left -= rows;
+ rows = vs;
+ y++;
+ }
+
+ for(; y < h; y++) {
+ for(x = 0; x < w; x++)
+ dev->put_pixel(image, x, y, pixels[0]);
+ }
+ DEBUG((DBG_BITMAPS, "shrink_glyph_grey: (%dw,%dh,%dx,%dy) -> (%dw,%dh,%dx,%dy)\n",
+ glyph->w, glyph->h, glyph->x, glyph->y,
+ dest->w, dest->h, dest->x, dest->y));
+}
+