import java.applet.*;
import java.awt.*;
import java.awt.image.*;

/**
 * JVtxFont builds some kind of font to display the teletext characters
 * on screen. As Java has no possibility to define own fonts, and working
 * with small images is awfully slow, I chose a different approach. A
 * font is a simple GIF image containing 8 rows with 32 characters each.
 * Every character is represented using simple horizontal line operations
 * and vertical line breaks.  
 * @author Martin Denn <mdenn@unix-ag.uni-kl.de>
 * @version 0.02
 */
public class JVtxFont {
    byte font[];
    int lookup[] = new int[256];
    int buffer[];
    int w, h;
    int char_w, char_h;
    Color vtx_color[] = { Color.black, Color.red, Color.green, Color.yellow,
                          Color.blue, Color.magenta, Color.cyan, Color.white};

    /**
     * Constructor. 
     * Builds a new JVtxFont class by loading a GIF image and deconstructing
     * it into a line/line break representation, stored in an array and
     * indexed to provide an easy access to every character.
     * @param img the font's GIF image
     * @param w the image's width in pixels
     * @param h the image's height in pixels
     */
    public JVtxFont(Image img, int w, int h) {
       
        int i,j;
        int c, sum;
        this.w = w;
        char_w = w/32;
        this.h = h;
        char_h = h/8;
       
        buffer=new int[w*h];
        PixelGrabber pg = new PixelGrabber(img, 0, 0, w, h, buffer, 0, w);
        try {
            pg.grabPixels();
        } catch (InterruptedException e) {
            System.err.println("interrupted waiting for pixels!");
            return;
        }
        /* get amount of space needed for font */
        sum=0;
        for (i=0; i<8; i++) {
            for (j=0; j<32; j++) {
                c=buildChar(j*char_w ,i*char_h, 0, true);
                sum+=c;
            }
        }
        /* build font */
        font = new byte[sum];
        sum=0;
        for (i=0; i<8; i++) {
            for (j=0; j<32; j++) {
                lookup[i*32+j] = sum;
                c=buildChar(j*char_w ,i*char_h, sum, false);
                sum+=c;
            }
        }
    }

    /**
     * Builds the character.
     * This methods translates a rectangular area out of the font's 
     * image into a line/line break representation of one single
     * character.
     * @param x the x position of the rectangular area
     * @param y the y position of the rectangular area
     * @param offset the position in the buffer where to store the
     *   line/line break representation.
     * @param count_only if set to true only count the space required
     *   in the buffer to store the image.
     */
    private int buildChar(int x, int y, int offset, boolean count_only) {
        int i, j;
        int acc_nl; // accumulated newlines
        int pos;
        int state;  // black or white 
        int nr;

        acc_nl=0;
        nr=0;
        for (i=y; i<y+char_h; i++) {
            state=0xFFFFFFFF;
            for (j=x; j<x+char_w; j++) {
                if (buffer[i*w+j] != state){
                    /* output neccessary linefeed */
                    if (acc_nl>0) {
                        if (!count_only) 
                            font[offset+nr] = (byte) (acc_nl | 0x80);
                        nr++;
                        acc_nl = 0;
                    }
                    /* if white->black, print position */
                    if (state == 0xFFFFFFFF) {
                        if (!count_only)
                            font[offset+nr] = (byte) (j-x);
                        nr++;
                    }
                    /* if black->white, print position-1 */
                    else {
                        if (!count_only)
                            font[offset+nr] = (byte) (j-x-1);
                       nr++;
                    }
                    state = buffer[i*w+j];
                }
            }
            /* if black in the last column, print position-1 */
            if (state != 0xFFFFFFFF) {
                if (!count_only)
                    font[offset+nr] = (byte) (j-x-1);
                nr++;
            }
            acc_nl++;
        }
        if (!count_only) 
            font[offset+nr] = (byte) 255;
        nr++;
        return nr;
    }

    /**
     * Translates pixel positions to character position.
     * @param p the position in pixels
     * @return the character position
     */
    public Point getPosition(Point p) {
        return new Point(p.x / char_w, p.y / char_h); 
    }

    /**
     * Paint a single character on screen
     * @param g the graphics context
     * @param fg the foreground colour
     * @param bg the background colour
     * @param char_x the character's x position in characters
     * @param char_y the character's y position in characters
     * @param c the character's value
     * @param attr the character's attribute byte
     * @param showchar if the character should be painted (or only the 
     *    its background)
     */
    public void paintChar(Graphics g, int fg, int bg, int char_x, 
                          int char_y, byte c, byte attr, boolean showchar) {
        int i, line;
        int x, x1, x2, y;
        boolean graphics;
        
        i=lookup[0x000000ff & c];
        x=char_x*char_w;
        y=char_y*char_h;
        /* paint background */
        g.setColor(vtx_color[bg]);
        g.fillRect(x, y, char_w, char_h);

        if (!showchar) {
            return;
        }

        g.setColor(vtx_color[fg]);
        line=0;
     
        graphics=(((0x000000ff & c)>=128) && ((0x000000ff & c)<=192));
        if ((attr & 0x03) > 0) { 
            /* double height */
            if ((attr & 0x02) > 0) { 
                /* lower half -> cut upper half */
                while ((font[i] != (byte) 255) && (line < char_h)) {
                    if ((font[i] & 0x80) == 0x80) {
                        line = line + 2*(font[i] & 0x7f);
                        i++;
                    }
                    else {
                        i+=2;
                    }
                }
                line = line - char_h;
            }
            while ((font[i] != (byte) 255) && (line < char_h)) {
                if ((font[i] & 0x80) == 0x80) {
                    line = line + 2*(font[i] & 0x7f);
                    i++;
                }
                else {
                    g.drawLine(x+font[i], y+line, x+font[i+1], y+line);
                    g.drawLine(x+font[i], y+line+1, x+font[i+1], y+line+1);
                    i+=2;
                }
            }
        }
        else if (((attr & 0x04) > 0) && graphics) { 
            /* separated graphics; this is a bit too complex... */
            while (font[i] != (byte) 255) {
                if ((font[i] & 0x80) == 0x80) {
                    line+=(font[i] & 0x7f);
                    i++;
                }
                else {
                    if ((line != (char_h - 1)) && 
                        (line != ((char_h/3) - 1)) &&
                        (line != ((2*char_h/3) -1))) {
                        x1 = font[i];
                        if (x1 == ((char_w/2) - 1)) {
                            x1 = (char_w/2);
                        }
                        x2 = font[i+1];
                        if (x2 == ((char_w/2) - 1)) {
                            x2 = (char_w/2) - 2;
                        }
                        else if (x2 == (char_w - 1)) {
                            x2 = (char_w - 2);
                        }
                        if (x1 < ((char_w/2) - 1)) {
                            if (x2 >= (char_w/2)) {
                                g.drawLine(x+x1, y+line, 
                                           x+((char_w/2) - 2), y+line);
                                x1 = ((char_w/2));
                            }
                            else {
                                g.drawLine(x+x1, y+line, x+x2, y+line);
                            }
                        }
                        if (x1 >= (char_w/2)) {
                            g.drawLine(x+x1, y+line, x+x2, y+line);
                        }
                    }
                    i+=2;
                }
            }
        }
        else { 
            /* normal graphics */
            while (font[i] != (byte) 255) {
                if ((font[i] & 0x80) == 0x80) {
                    line+=(font[i] & 0x7f);
                    i++;
                }
                else {
                    g.drawLine(x+font[i], y+line, x+font[i+1], y+line);
                    i+=2;
                }
            }
        }            
    }
}
