Package com.agifans.picedit.picture

Source Code of com.agifans.picedit.picture.Picture

package com.agifans.picedit.picture;

import java.awt.Image;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBufferInt;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import javax.swing.JOptionPane;

import com.agifans.picedit.picture.PictureCache.PictureCacheEntry;
import com.agifans.picedit.types.PictureType;
import com.agifans.picedit.types.ToolType;
import com.agifans.picedit.utils.EgaPalette;

/**
* This class represents an AGI/SCI Picture.
*
* @author Lance Ewing
*/
public class Picture {

    /**
     * Holds the RGB values for the 16 EGA colours.
     */
    private final static int[] colours = EgaPalette.colours;

    /**
     * Holds the current position within the picture code buffer.
     */
    private int picturePosition;
   
    /**
     * Holds the linked list of picture codes for this picture.
     */
    private LinkedList<PictureCode> pictureCodes;
   
    /**
     * Holds the pixel data for the visual screen of the picture.
     */
    private int visualScreen[];

    /**
     * Holds the pixel data for the priority screen of the picture.
     */
    private int priorityScreen[];

    /**
     * Holds the pixel data for the control screen of the picture.
     */
    private int controlScreen[];

    /**
     * The Image for the visual screen.
     */
    private Image visualImage;

    /**
     * The Image for the priority screen.
     */
    private Image priorityImage;

    /**
     * The Image for the control screen.
     */
    private Image controlImage;

    /**
     * Holds the editing status for this Picture.
     */
    private EditStatus editStatus;

    /**
     * Holds the cache of picture state at various picture positions. Used for faster picture draws.
     */
    private PictureCache pictureCache;
   
    /**
     * List of registered PictureChangeListeners.
     */
    private List<PictureChangeListener> pictureChangeListeners;
   
    /**
     * Says whether the picture is currently being loaded from a file.
     */
    private boolean isLoading;
   
    /**
     * Says whether the picture is currently being drawn.
     */
    private boolean isDrawing;
   
    /**
     * The picture position where the current selection starts.
     */
    private int firstSelectedPosition;
   
    /**
     * The picture position where the current selection ends.
     */
    private int lastSelectedPosition;
   
    /**
     * Constructor for Picture.
     *
     * @param editStatus the EditStatus containing current editing status.
     */
    public Picture(EditStatus editStatus) {
        PictureType pictureType = editStatus.getPictureType();

        this.visualScreen = new int[pictureType.getNumberOfPixels()];
        this.visualImage = createScreenImage(pictureType.getWidth(), pictureType.getHeight(), visualScreen);
        this.priorityScreen = new int[pictureType.getNumberOfPixels()];
        this.priorityImage = createScreenImage(pictureType.getWidth(), pictureType.getHeight(), priorityScreen);

        if (pictureType.equals(PictureType.SCI0)) {
            this.controlScreen = new int[pictureType.getNumberOfPixels()];
            this.controlImage = createScreenImage(pictureType.getWidth(), pictureType.getHeight(), controlScreen);
        }

        this.editStatus = editStatus;
        this.pictureCache = new PictureCache(editStatus);
        this.pictureChangeListeners = new ArrayList<PictureChangeListener>();
       
        clearPicture();
       
        this.firstSelectedPosition = -1;
        this.lastSelectedPosition = -1;
    }

    /**
     * Adds the given PictureChangeListener to the List of listeners that are notified when
     * the data for this Picture changes.
     *
     * @param pictureChangeListener The PictureChangeListener to add.
     */
    public void addPictureChangeListener(PictureChangeListener pictureChangeListener) {
        this.pictureChangeListeners.add(pictureChangeListener);
    }
   
    /**
     * Fires a picture codes removed event to all PictureChangeListeners.
     *
     * @param fromIndex The index where the picture codes started to be removed.
     * @param toIndex The index where the picture codes finished being removed.
     */
    public void firePictureCodesRemoved(int fromIndex, int toIndex) {
        for (PictureChangeListener listener : pictureChangeListeners) {
            listener.pictureCodesRemoved(fromIndex, toIndex);
        }
    }
   
    /**
     * Fires a pictures codes added event to all PictureChangeListeners.
     *
     * @param fromIndex The index where the picture codes started to be added.
     * @param toIndex The index where the picture codes finished being added.
     */
    public void firePictureCodesAdded(int fromIndex, int toIndex) {
        if (!isLoading) {
            for (PictureChangeListener listener : pictureChangeListeners) {
                listener.pictureCodesAdded(fromIndex, toIndex);
            }
        }
    }
   
    /**
     * Fires a selection interval collapsed event to all PictureChangeListeners.
     */
    public void fireSelectionIntervalCollapsed() {
        for (PictureChangeListener listener : pictureChangeListeners) {
            listener.selectionIntervalCollapsed();
        }
    }
   
    /**
     * Creates a BufferedImage of the given size using the given data array to hold
     * the pixel data.
     *
     * @param width the width of the Image to create.
     * @param height the height of the Image to create.
     * @param screenDataArray the int array that will hold the pixel data for this Image.
     *
     * @return the created Image.
     */
    public Image createScreenImage(int width, int height, int[] screenDataArray) {
        DataBufferInt dataBuffer = new DataBufferInt(screenDataArray, screenDataArray.length);
        ColorModel colorModel = ColorModel.getRGBdefault();
        int[] bandMasks = new int[] { 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 };
        WritableRaster raster = Raster.createPackedRaster(dataBuffer, width, height, width, bandMasks, null);
        return new BufferedImage(colorModel, raster, false, null);
    }

    /**
     * Clears the picture code buffer, picture cache and picture screens. This
     * would usually be done when it is a completely new picture, e.g. when
     * loading a picture or creating a new picture.
     */
    public void clearPicture() {
        clearPictureCodes();
        clearPictureScreens();
        clearPictureCache();
    }
   
    /**
     * Clears the visual, priority (and control) screens.
     */
    public void clearPictureScreens() {
        Arrays.fill(visualScreen, EgaPalette.transparent);
        Arrays.fill(priorityScreen, EgaPalette.transparent);
        if (editStatus.getPictureType().equals(PictureType.SCI0)) {
            Arrays.fill(controlScreen, EgaPalette.transparent);
        }
    }
   
    /**
     * Clears the picture cache.
     */
    public void clearPictureCache() {
        this.pictureCache.clear();
    }
   
    /**
     * Clears the picture code buffer.
     */
    public void clearPictureCodes() {
        if (pictureCodes != null) {
            firePictureCodesRemoved(0, pictureCodes.size() - 1);
        }
        picturePosition = 0;
        pictureCodes = new LinkedList<PictureCode>();
        pictureCodes.add(new PictureCode(PictureCodeType.END));
    }
   
    /**
     * Gets the current picture position.
     *
     * @return The current picture position.
     */
    public int getPicturePosition() {
        return picturePosition;
    }

    /**
     * Sets the picture position to the given value.
     *
     * @param picturePosition The new picture position.
     */
    public void setPicturePosition(int picturePosition) {
        this.picturePosition = picturePosition;
    }
   
    /**
     * Gets the size of the picture (number of picture codes, excluding the end code 0xFF).
     *
     * @return The size of the picture (number of picture codes, excluding the end code 0xFF).
     */
    public int getSize() {
      return (getPictureCodes().size() - 1);
    }
   
    /**
     * Adds a code to the picture code buffer.
     *
     * @param type The type of PictureCode.
     */
    public void addPictureCode(PictureCodeType type) {
        addPictureCode(type, type.getActionCode(), null);
    }
   
    /**
     * Adds a code of type ABSOLUTE_POINT to the picture code buffer.
     *
     * @param x The x position of the point.
     * @param y The y position of the point.
     */
    public void addPictureCode(int x, int y) {
        addPictureCode(PictureCodeType.ABSOLUTE_POINT_DATA, ((x << 8) | y), new Point(x, y));
    }
   
    /**
     * Adds a cod of the given point type to the picture code buffer.
     *
     * @param pointType The type of PictureCode.
     * @param x The x position of the point.
     * @param y The y position of the point.
     */
    public void addPictureCode(PictureCodeType pointType, int x, int y) {
        addPictureCode(pointType, ((x << 8) | y), new Point(x, y));
    }
   
    /**
     * Adds a code to the picture code buffer.
     *
     * @param type The type of PictureCode.
     * @param code The code to add to the picture code buffer.
     */
    public void addPictureCode(PictureCodeType type, int code) {
        addPictureCode(type, code, null);
    }
   
    /**
     * Adds a code to the picture code buffer.
     *
     * @param type The type of PictureCode.
     * @param code The code to add to the picture code buffer.
     * @param point Optional Point specifying absolute location that the picture code relates to.
     */
    public void addPictureCode(PictureCodeType type, int code, Point point) {
        pictureCache.clear(picturePosition);
        pictureCodes.add(picturePosition, new PictureCode(type, code, point));
        firePictureCodesAdded(picturePosition, picturePosition);
        picturePosition = picturePosition + 1;
        editStatus.setUnsavedChanges(true);
    }

    /**
     * Sets the selection interval for the Picture, which is the start and end of the
     * picture positions that are currently selected. Selection can occur either by using
     * the selection tool or by using the left hand side "Commands" JList.
     *
     * @param firstSelectedPosition The start of the interval.
     * @param lastSelectedPosition The end of the interval.
     */
    public void setSelectionInterval(int firstSelectedPosition, int lastSelectedPosition) {
        this.firstSelectedPosition = firstSelectedPosition;
        this.lastSelectedPosition = lastSelectedPosition;
    }
   
    /**
     * Collapses the selection interval by setting the start and end to the current
     * position, which would normally be what the end of the selection would have been
     * set to.
     */
    public void collapseSelectionInterval() {
        this.firstSelectedPosition = this.lastSelectedPosition = this.picturePosition;
        fireSelectionIntervalCollapsed();
    }
   
    /**
     * Gets the first picture position in the selection interval.
     *
     * @return The first picture position in the selection interval.
     */
    public int getFirstSelectedPosition() {
        return firstSelectedPosition;
    }
   
    /**
     * Gets the last picture position in the selection interval.
     *
     * @return The last picture position in the selection interval.
     */
    public int getLastSelectedPosition() {
        return lastSelectedPosition;
    }
   
    /**
     * Processes the setting of a new visual colour.
     *
     * @param newVisualColour The new visual colour.
     */
    public void processVisualColourChange(int newVisualColour) {
        editStatus.setVisualColour(newVisualColour);
        this.addPictureCode(PictureCodeType.SET_VISUAL_COLOR);
        this.addPictureCode(PictureCodeType.COLOR_DATA, newVisualColour);
    }
   
    /**
     * Processes the turning off of the visual colour.
     */
    public void processVisualColourOff() {
        editStatus.setVisualColour(EditStatus.VISUAL_OFF);
        this.addPictureCode(PictureCodeType.SET_VISUAL_COLOR_OFF);
    }
   
    /**
     * Processes the setting of a new priority colour.
     *
     * @param newPriorityColour The new priority colour.
     */
    public void processPriorityColourChange(int newPriorityColour) {
        editStatus.setPriorityColour(newPriorityColour);
        this.addPictureCode(PictureCodeType.SET_PRIORITY_COLOR);
        this.addPictureCode(PictureCodeType.COLOR_DATA, newPriorityColour);
    }
   
    /**
     * Processes the turning off of the priority colour.
     */
    public void processPriorityColourOff() {
        editStatus.setPriorityColour(EditStatus.PRIORITY_OFF);
        this.addPictureCode(PictureCodeType.SET_PRIORITY_COLOR_OFF);
    }
   
    /**
     * Gets the picture code buffer.
     *
     * @return The picture code buffer.
     */
    public LinkedList<PictureCode> getPictureCodes() {
        return pictureCodes;
    }
   
    /**
     * Gets the PictureCode at the current picture position.
     *
     * @return The PictureCode at the current picture position.
     */
    public PictureCode getCurrentPictureCode() {
        return pictureCodes.get(picturePosition);
    }
   
   
    /**
     * Works backwards if necessary to work out what the current picture action or tool
     * is, i.e. if the current position is on a data code rather than an action code then
     * it will navigation backwards to find the association action code.
     *
     * @return The current action code that the current picture position is within.
     */
    public PictureCode getCurrentPictureAction() {
      if (pictureCodes.size() == 1) {
          return null;
      }
      int position = picturePosition;
      while ((position > 0) && !pictureCodes.get(position).isActionCode()) {
          position--;
      }
      return pictureCodes.get(position);
    }

    public PictureCode getNextPictureAction() {
        PictureCode pictureCode = null;
        if (picturePosition < pictureCodes.size() - 1) {
            int position = picturePosition + 1;
            while ((position < pictureCodes.size()) && !pictureCodes.get(position).isActionCode()) {
                position++;
            }
            if (position < pictureCodes.size()) {
                pictureCode = pictureCodes.get(position);
            }
        }
        return pictureCode;
    }
   
    public PictureCode incrementPicturePosition() {
        picturePosition++;
        if (picturePosition >= pictureCodes.size()) {
            picturePosition = pictureCodes.size() - 1;
            return null;
        } else {
            return pictureCodes.get(picturePosition);
        }
    }

    public PictureCode decrementPicturePosition() {
        picturePosition--;
        if (picturePosition < 0) {
            picturePosition = 0;
        }
        return pictureCodes.get(picturePosition);
    }

    public PictureCode deleteAtPicturePosition() {
        PictureCode pictureCode = null;
        if (picturePosition < (pictureCodes.size() - 1)) {
            pictureCodes.remove(picturePosition);
            firePictureCodesRemoved(picturePosition, picturePosition);
            if (picturePosition < (pictureCodes.size() - 1)) {
                pictureCode = pictureCodes.get(picturePosition);
            }
            editStatus.setUnsavedChanges(true);
        }
        return pictureCode;
    }
   
    /**
     * Attempts to delete the pictures codes from the given 'from' picture code index to the
     * give 'to' picture code index. It may be that the delete is not completely possible
     * since the outcome might leave the picture broken. So this is taken in to account so that
     * the end result is always a working picture.
     *
     * @param fromPosition The picture position to delete picture codes from.
     * @param toPosition The picture position to delete picture codes to.
     */
    public void deletePictureCodes(int fromPosition, int toPosition) {
        int numOfCodesToRemove = (toPosition - fromPosition) + 1;
        for (int count = 0; count < numOfCodesToRemove; count++) {
            if (!getCurrentPictureCode().isEndCode()) {
                pictureCodes.remove(fromPosition);
                firePictureCodesRemoved(fromPosition, fromPosition);
            }
        }
       
        // TODO: Detect when the starting point has been removed for a relative or step line action and convert the next point in to an absolute starting point.
       
        editStatus.setUnsavedChanges(true);
        pictureCache.clear(fromPosition);
        drawPicture();
    }
   
    /**
     * Deletes the currently selected picture codes.
     */
    public void deleteSelectedPictureCodes() {
        deletePictureCodes(firstSelectedPosition, lastSelectedPosition);
    }
   
    /**
     * Process movement back one picture action through the picture code buffer. A
     * picture action is the full set of codes for a tool, e.g. all the points for a
     * single draw absolute action. Picture actions are highlighted in black on the
     * picture code JList.
     */
    public void moveBackOnePictureAction() {
        // Move back through the codes until we find an Action code.
        PictureCode pictureCode = null;
        picturePosition = firstSelectedPosition;
        do {
            pictureCode = decrementPicturePosition();
        } while ((pictureCode != null) && !pictureCode.isActionCode() && (picturePosition > 0));
        drawPicture();
    }

    /**
     * Process movement backward one picture code, i.e. picture position minus 1.
     */
    public void moveBackOnePictureCode() {
        picturePosition = firstSelectedPosition;
        decrementPicturePosition();
        drawPicture();
    }
   
    /**
     * Process movement forward one picture action through the picture code buffer. A
     * picture action is the full set of codes for a tool, e.g. all the points for a
     * single draw absolute action. Picture actions are highlighted in black on the
     * picture code JList.
     */
    public void moveForwardOnePictureAction() {
        if (picturePosition < (pictureCodes.size() - 1)) {
            PictureCode pictureCode = null;
            do {
                pictureCode = incrementPicturePosition();
            } while ((pictureCode != null) && !pictureCode.isActionCode());

            drawPicture();
        }
    }

    /**
     * Process movement forward one picture code, i.e. picture position plus 1.
     */
    public void moveForwardOnePictureCode() {
        if (picturePosition < (pictureCodes.size() - 1)) {
            if ((lastSelectedPosition - firstSelectedPosition) != 1) {
                picturePosition = firstSelectedPosition;
                incrementPicturePosition();
                drawPicture();
            } else {
                // If there is a selection interval in place and it is only two
                // picture codes long, then we're already where we need to be  as
                // far as picture position is concerned, but we need the selection interval
                // be collapsed to a single position, i.e. the end of the interval.
                collapseSelectionInterval();
            }
        }
    }
   
    /**
     * Process movement to the start of the picture code buffer.
     */
    public void moveToStartOfPictureBuffer() {
        setPicturePosition(0);
        drawPicture();
    }

    /**
     * Process movement to the end of the picture code buffer.
     */
    public void moveToEndOfPictureBuffer() {
        if (picturePosition < (pictureCodes.size() - 1)) {
            setPicturePosition(pictureCodes.size() - 1);
            drawPicture();
        }
    }

    /**
     * Process deletion of the current picture action, i.e. the picture
     * action at the current picture position.
     */
    public void deleteCurrentPictureAction() {
        PictureCode pictureCode = deleteAtPicturePosition();
        while ((pictureCode != null) && (pictureCode.isDataCode())) {
            pictureCode = deleteAtPicturePosition();
        }
        pictureCache.clear(picturePosition);
        drawPicture();
    }

    /**
     * Returns the visual image to be drawn on the screen.
     *
     * @return the visual image to be drawn on the screen.
     */
    public Image getVisualImage() {
        return visualImage;
    }

    /**
     * Returns the priority image to be drawn on the screen.
     *
     * @return the priority image to be drawn on the screen.
     */
    public Image getPriorityImage() {
        return priorityImage;
    }
   
    /**
     * Gets the raw RGB byte array for the priority screen.
     *
     * @return The raw RGB byte array for the priority screen.
     */
    public int[] getPriorityScreen() {
        return priorityScreen;
    }
   
    /**
     * Returns the control image to be drawn on the screen.
     *
     * @return the control image to be drawn on the screen.
     */
    public Image getControlImage() {
        return controlImage;
    }

    /**
     * Loads an AGI picture from the given File.
     *
     * @param pictureFile the File to load the AGI picture from.
     */
    public void loadPicture(File pictureFile) {
        BufferedInputStream in = null;
       
        try {
            // This stops the change listening from firing. We'll call that at the end of the method instead.
            isLoading = true;
           
            // Make sure we start with a clean picture.
            editStatus.clear();
            this.clearPicture();

            // Store file name for display on title bar.
            editStatus.setPictureFile(pictureFile);
           
            // Open the file for reading.
            in = new BufferedInputStream(new FileInputStream(pictureFile));
           
            // Read the file in to an int array to make it easy to convert to PictureCodes.
            int[] rawPictureCodes = new int[(int)pictureFile.length() + 1];
            int rawPictureCodesIndex = 0;
            while ((rawPictureCodes[rawPictureCodesIndex++] = in.read()) != -1) {}

            // Process the raw int array to create a PictureCodes LinkedList.
            int pictureCode, index = 0, x, y, brushCode = 0;
            while ((pictureCode = rawPictureCodes[index++]) != -1) {
                if (pictureCode != 0xFF) {
                    switch (pictureCode) {
                        case 0xF0:
                            addPictureCode(PictureCodeType.SET_VISUAL_COLOR);
                            addPictureCode(PictureCodeType.COLOR_DATA, rawPictureCodes[index++]);
                            break;
                           
                        case 0xF1:
                            addPictureCode(PictureCodeType.SET_VISUAL_COLOR_OFF);
                            break;
                           
                        case 0xF2:
                            addPictureCode(PictureCodeType.SET_PRIORITY_COLOR);
                            addPictureCode(PictureCodeType.COLOR_DATA, rawPictureCodes[index++]);
                            break;
                           
                        case 0xF3:
                            addPictureCode(PictureCodeType.SET_PRIORITY_COLOR_OFF);
                            break;
                           
                        case 0xF4:
                            addPictureCode(PictureCodeType.DRAW_VERTICAL_STEP_LINE);
                            x = pictureCode = rawPictureCodes[index++];
                            y = pictureCode = rawPictureCodes[index++];
                            addPictureCode(x, y);
                            while (true) {
                                if ((y = rawPictureCodes[index++]) >= 0xF0) {
                                    break;
                                }
                                addPictureCode(PictureCodeType.Y_POSITION_DATA, y, new Point(x, y));
                                if ((x = rawPictureCodes[index++]) >= 0xF0) {
                                    break;
                                }
                                addPictureCode(PictureCodeType.X_POSITION_DATA, x, new Point(x, y));
                            }
                            index--;
                            break;
                           
                        case 0xF5:
                            addPictureCode(PictureCodeType.DRAW_HORIZONTAL_STEP_LINE);
                            x = pictureCode = rawPictureCodes[index++];
                            y = pictureCode = rawPictureCodes[index++];
                            addPictureCode(x, y);
                            while (true) {
                                if ((x = rawPictureCodes[index++]) >= 0xF0) {
                                    break;
                                }
                                addPictureCode(PictureCodeType.X_POSITION_DATA, x, new Point(x, y));
                                if ((y = rawPictureCodes[index++]) >= 0xF0) {
                                    break;
                                }
                                addPictureCode(PictureCodeType.Y_POSITION_DATA, y, new Point(x, y));
                            }
                            index--;
                            break;
                           
                        case 0xF6:
                            addPictureCode(PictureCodeType.DRAW_LINE);
                            while (true) {
                                if ((x = rawPictureCodes[index++]) >= 0xF0) {
                                    break;
                                }
                                if ((y = rawPictureCodes[index++]) >= 0xF0) {
                                    break;
                                }
                                addPictureCode(x, y);
                            }
                            index--;
                            break;
                           
                        case 0xF7:
                            addPictureCode(PictureCodeType.DRAW_SHORT_LINE);
                            x = pictureCode = rawPictureCodes[index++];
                            y = pictureCode = rawPictureCodes[index++];
                            addPictureCode(x, y);
                            while ((pictureCode = rawPictureCodes[index++]) < 0xF0) {
                                int dx = ((pictureCode & 0xF0) >> 4) & 0x0F;
                                int dy = (pictureCode & 0x0F);
                                if ((dx & 0x08) > 0) {
                                    dx = (-1) * (dx & 0x07);
                                }
                                if ((dy & 0x08) > 0) {
                                    dy = (-1) * (dy & 0x07);
                                }
                                x = x + dx;
                                y = y + dy;
                                addPictureCode(PictureCodeType.RELATIVE_POINT_DATA, pictureCode, new Point(x, y));
                            }
                            index--;
                            break;
                           
                        case 0xF8:
                            addPictureCode(PictureCodeType.DRAW_FILL);
                            while (true) {
                                if ((x = rawPictureCodes[index++]) >= 0xF0) {
                                    break;
                                }
                                if ((y = rawPictureCodes[index++]) >= 0xF0) {
                                    break;
                                }
                                addPictureCode(PictureCodeType.FILL_POINT_DATA, x, y);
                            }
                            index--;
                            break;
                           
                        case 0xF9:
                            addPictureCode(PictureCodeType.SET_BRUSH_TYPE);
                            brushCode = rawPictureCodes[index++];
                            addPictureCode(PictureCodeType.BRUSH_TYPE_DATA, brushCode);
                            break;
                           
                        case 0xFA:
                            addPictureCode(PictureCodeType.DRAW_BRUSH_POINT);
                            while (true) {
                                if ((brushCode & 0x20) > 0) {
                                    if ((pictureCode = rawPictureCodes[index++]) >= 0xF0) {
                                        break;
                                    }
                                    addPictureCode(PictureCodeType.BRUSH_PATTERN_DATA, pictureCode);
                                }
                                if ((x = rawPictureCodes[index++]) >= 0xF0) {
                                    break;
                                }
                                if ((y = rawPictureCodes[index++]) >= 0xF0) {
                                    break;
                                }
                                addPictureCode(PictureCodeType.BRUSH_POINT_DATA, x, y);
                            }
                            index--;
                            break;
                           
                        case 0xFF:
                            // End of the picture.
                            break;
                           
                        default:
                            // An attempt to load a picture that is corrupt.
                            System.out.printf("Unknown picture code : %X, picturePosition: %d\n", pictureCode, pictureCodes.size());
                            System.exit(0);
                            break;
                    }
                   
                } else {
                    // 0xFF is the end of an AGI picture.
                    break;
                }
            }
           
            this.drawPicture();
            editStatus.setTool(ToolType.NONE);
            editStatus.setUnsavedChanges(false);
           
            // Now that we've finished loading, trigger an event for the whole Picture.
            isLoading = false;
            firePictureCodesAdded(0, pictureCodes.size());

        } catch (FileNotFoundException fnfe) {
            // This can happen for files in the history.
            JOptionPane.showMessageDialog(null, "That file no longer exists.", "File not found", JOptionPane.WARNING_MESSAGE);
        } catch (IOException ioe) {
            System.out.printf("Error loading picture : %s.\n", pictureFile.getPath());
            System.exit(1);
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (Exception e) {
                    // Not worried about file close errors.
                }
            }
        }
    }

    /**
     * Saves the AGI picture to the given File.
     *
     * @param pictureFile the File to write the AGI picture out to.
     */
    public void savePicture(File pictureFile) {
        BufferedOutputStream out = null;

        try {
            // Store file name for display on title bar.
            editStatus.setPictureFile(pictureFile);
           
            // Open the file for reading.
            out = new BufferedOutputStream(new FileOutputStream(pictureFile));

            // Write each of the picture codes out to the file.
            for (PictureCode pictureCode : this.getPictureCodes()) {
                if (pictureCode.isAbsolutePoint()) {
                    int code = pictureCode.getCode();
                    int x = (code & 0xFF00) >> 8;
                    int y = (code & 0x00FF);
                    out.write(x);
                    out.write(y);
                } else {
                    out.write(pictureCode.getCode());
                }
            }
           
            editStatus.setUnsavedChanges(false);
           
        } catch (FileNotFoundException fnfe) {
            System.out.printf("Unable to create picture file : %s. %s\n", pictureFile.getPath(), fnfe.getMessage());
            System.exit(1);
        } catch (IOException ioe) {
            System.out.printf("Error saving picture : %s. %s\n", pictureFile.getPath(), ioe.getMessage());
            System.exit(1);
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (Exception e) {
                    // Not worried about file close errors.
                }
            }
        }
    }
   
    /**
     * Draws the picture from the beginning up to the current picture position.
     */
    public void drawPicture() {
      int action = 0;
        int index = 0;

        // Tells other parts of the application that want to ask that we're drawing the picture now.
        isDrawing = true;
       
        PictureCacheEntry cacheEntry = this.pictureCache.getCacheEntry(picturePosition);
        if (cacheEntry != null) {
          // Copy the cached screen arrays into the main picture images.
          System.arraycopy(cacheEntry.getVisualScreen(), 0, visualScreen, 0, visualScreen.length);
          System.arraycopy(cacheEntry.getPriorityScreen(), 0, priorityScreen, 0, priorityScreen.length);
          if (editStatus.getPictureType().equals(PictureType.SCI0)) {
            System.arraycopy(cacheEntry.getControlScreen(), 0, controlScreen, 0, controlScreen.length);
          }
         
          // Skip straight to the cached position.
          index = cacheEntry.getPicturePosition();
         
          // Restore the settings from the EditStatus that apply to the cached position.
          editStatus.setBrushShape(cacheEntry.getBrushShape());
          editStatus.setBrushSize(cacheEntry.getBrushSize());
          editStatus.setBrushTexture(cacheEntry.getBrushTexture());
          editStatus.setControlColour(cacheEntry.getControlColour());
          editStatus.setPriorityColour(cacheEntry.getPriorityColour());
          editStatus.setTool(cacheEntry.getTool());
          editStatus.setVisualColour(cacheEntry.getVisualColour());
         
        } else {
          // Clear the picture bitmaps to the original colours.
          clearPictureScreens();
        
          // When drawing from the start, we need to clear everything except for the data.
          editStatus.clear(false);
        }

        if ((picturePosition > 0) && (index < picturePosition)) {
            do {
                boolean isCacheable = true;
               
                // Get the next picture action.
                PictureCode pictureCode = pictureCodes.get(index++);
                action = pictureCode.getCode();
               
                // Process the actions data.
                switch (action) {
                    case 0xF0:
                        editStatus.setVisualColour(pictureCodes.get(index++).getCode());
                        isCacheable = false;
                        break;
                    case 0xF1:
                        editStatus.setVisualColour(EditStatus.VISUAL_OFF);
                        isCacheable = false;
                        break;
                    case 0xF2:
                        editStatus.setPriorityColour(pictureCodes.get(index++).getCode());
                        isCacheable = false;
                        break;
                    case 0xF3:
                        editStatus.setPriorityColour(EditStatus.PRIORITY_OFF);
                        isCacheable = false;
                        break;
                    case 0xF4:
                        editStatus.setTool(ToolType.STEPLINE);
                        index = drawPictureYCorner(pictureCodes, index);
                        break;
                    case 0xF5:
                        editStatus.setTool(ToolType.STEPLINE);
                        index = drawPictureXCorner(pictureCodes, index);
                        break;
                    case 0xF6:
                        editStatus.setTool(ToolType.LINE);
                        index = drawPictureAbsoluteLine(pictureCodes, index);
                        break;
                    case 0xF7:
                        editStatus.setTool(ToolType.SHORTLINE);
                        index = drawPictureRelativeDraw(pictureCodes, index);
                        break;
                    case 0xF8:
                        editStatus.setTool(ToolType.FILL);
                        index = drawPictureFill(pictureCodes, index);
                        break;
                    case 0xF9:
                        editStatus.setBrushCode(pictureCodes.get(index++).getCode());
                        isCacheable = false;
                        break;
                    case 0xFA:
                        editStatus.setTool(ToolType.BRUSH);
                        index = drawPicturePlotBrush(pictureCodes, index);
                        break;
                    case 0xFF:
                        // End of the picture.
                        break;
                    default:
                        // An attempt to load a picture that is corrupt.
                        System.out.printf("Unknown picture code : %X, index: %d, picturePosition: %d, pictureSize: %d\n", action, index, picturePosition, pictureCodes.size());
                        System.exit(0);
                        break;
                }
               
                // Add the current picture state to the picture cache.
                if (isCacheable) {
                    // Cache only if a gap of at least 100 has been reached and the next picture code is an action code.
                    if (((cacheEntry == null) || ((index - cacheEntry.getPicturePosition()) > 100)) && pictureCodes.get(index).isActionCode()) {
                        cacheEntry = pictureCache.addCacheEntry(index, visualScreen, priorityScreen, controlScreen);
                    }
                }
            } while ((index < picturePosition) && (action != 0xFF));
        }
       
        // If the current picture position is on a data code, then clear the selected tool. We don't allow inserts within a picture action.
        if (getCurrentPictureCode().isDataCode()) {
            editStatus.setTool(ToolType.NONE);
        }
       
        // Tells other parts of the application that want to ask that we are not longer drawing the picture.
        isDrawing = false;
    }

    /**
     * Returns true if the picture is currently being drawn; otherwise false.
     *
     * @return true if the picture is currently being drawn; otherwise false.
     */
    public boolean isBeingDrawn() {
        return isDrawing;
    }
   
    /**
     * Draws a yCorner (drawing action 0xF4).
     *
     * @param picturesCodes the List of picture codes to draw Y corners from.
     * @param index the index within the List to start processing from.
     *
     * @return the index of the next picture action.
     */
    public int drawPictureYCorner(List<PictureCode> pictureCodes, int index) {
        int code, x1, x2, y1, y2;
       
        PictureCode pictureCode = pictureCodes.get(index++);
        code = pictureCode.getCode();
        x1 = (code & 0xFF00) >> 8;
        y1 = (code & 0x00FF);

        // A line must always have a least one point.
        putPixel(x1, y1);
       
        while (index <= picturePosition) {
            y2 = pictureCodes.get(index++).getCode();
            if (y2 >= 0xF0) {
                break;
            }
            drawLine(x1, y1, x1, y2);
            y1 = y2;
            if (index > picturePosition) {
                break;
            }
            x2 = pictureCodes.get(index++).getCode();
            if (x2 >= 0xF0) {
                break;
            }
            drawLine(x1, y1, x2, y1);
            x1 = x2;
        }

        return (index - 1);
    }

    /**
     * Draws an xCorner (drawing action 0xF5).
     *
     * @param picturesCodes the List of picture codes to draw X corners from.
     * @param index the index within the List to start processing from.
     *
     * @return the index of the next picture action.
     */
    public int drawPictureXCorner(List<PictureCode> pictureCodes, int index) {
        int code, x1, x2, y1, y2;

        PictureCode pictureCode = pictureCodes.get(index++);
        code = pictureCode.getCode();
        x1 = (code & 0xFF00) >> 8;
        y1 = (code & 0x00FF);

        // A line must always have a least one point.
        putPixel(x1, y1);
       
        while (index <= picturePosition) {
            x2 = pictureCodes.get(index++).getCode();
            if (x2 >= 0xF0) {
                break;
            }
            drawLine(x1, y1, x2, y1);
            x1 = x2;
            if (index > picturePosition) {
                break;
            }
            y2 = pictureCodes.get(index++).getCode();
            if (y2 >= 0xF0) {
                break;
            }
            drawLine(x1, y1, x1, y2);
            y1 = y2;
        }

        return (index - 1);
    }

    /**
     * Draws long lines to actual locations (cf. relative) (drawing action 0xF6).
     *
     * @param picturesCodes the List of picture codes to draw absolute lines from.
     * @param index the index within the List to start processing from.
     *
     * @return the index of the next picture action.
     */
    public int drawPictureAbsoluteLine(List<PictureCode> pictureCodes, int index) {
        int code, x1, y1, x2, y2, lineCount=0;

        PictureCode pictureCode = pictureCodes.get(index++);
        code = pictureCode.getCode();
        x1 = (code & 0xFF00) >> 8;
        y1 = (code & 0x00FF);

        // A line must always have a least one point.
        putPixel(x1, y1);
       
        while (index <= picturePosition) {
            pictureCode = pictureCodes.get(index++);
            if (pictureCode.getType() != PictureCodeType.ABSOLUTE_POINT_DATA) {
                break;
            }
            code = pictureCode.getCode();
            x2 = (code & 0xFF00) >> 8;
            y2 = (code & 0x00FF);
            drawLine(x1, y1, x2, y2);
            x1 = x2;
            y1 = y2;
            lineCount++;
        }

        return (index - 1);
    }

    /**
     * Draws short lines relative to last position.  (drawing action 0xF7).
     *
     * @param picturesCodes the List of picture codes to draw relative lines from.
     * @param index the index within the List to start processing from.
     *
     * @return the index of the next picture action.
     */
    public int drawPictureRelativeDraw(List<PictureCode> pictureCodes, int index) {
        int x1, y1, disp;
        int dx, dy;

        PictureCode pictureCode = pictureCodes.get(index++);
        int code = pictureCode.getCode();
        x1 = (code & 0xFF00) >> 8;
        y1 = (code & 0x00FF);

        // A line must always have a least one point.
        putPixel(x1, y1);
       
        while (index <= picturePosition) {
            disp = pictureCodes.get(index++).getCode();
            if (disp >= 0xF0) {
                break;
            }
            dx = ((disp & 0xF0) >> 4) & 0x0F;
            dy = (disp & 0x0F);
            if ((dx & 0x08) > 0) {
                dx = (-1) * (dx & 0x07);
            }
            if ((dy & 0x08) > 0) {
                dy = (-1) * (dy & 0x07);
            }
            drawLine(x1, y1, x1 + dx, y1 + dy);
            x1 += dx;
            y1 += dy;
        }

        return (index - 1);
    }

    /**
     * AGI flood fill. (drawing action 0xF8).
     *
     * @param picturesCodes the List of picture codes to draw fills from.
     * @param index the index within the List to start processing from.
     *
     * @return the index of the next picture action.
     */
    public int drawPictureFill(List<PictureCode> pictureCodes, int index) {
        int code, x1, y1;

        while (index <= picturePosition) {
            PictureCode pictureCode = pictureCodes.get(index++);
            if (pictureCode.getType() != PictureCodeType.FILL_POINT_DATA) {
                break;
            }
            code = pictureCode.getCode();
            x1 = (code & 0xFF00) >> 8;
            y1 = (code & 0x00FF);
            fill(x1, y1);
        }

        return (index - 1);
    }

    /**
     * Plots points and various brush patterns. (drawing action 0xF8).
     *
     * @param picturesCodes the List of picture codes to plot brushes from.
     * @param index the index within the List to start processing from.
     *
     * @return the index of the next picture action.
     */
    public int drawPicturePlotBrush(List<PictureCode> pictureCodes, int index) {
        int code, x1, y1, patNum = 0;

        int patCode = editStatus.getBrushCode();

        while (index <= picturePosition) {
            if ((patCode & 0x20) > 0) {
                if ((patNum = pictureCodes.get(index++).getCode()) >= 0xF0) {
                    break;
                }
                patNum = (patNum >> 1 & 0x7f);
            }
            if (index > picturePosition) {
                break;
            }
            PictureCode pictureCode = pictureCodes.get(index++);
            if (pictureCode.getType() != PictureCodeType.BRUSH_POINT_DATA) {
                break;
            }
            code = pictureCode.getCode();
            x1 = (code & 0xFF00) >> 8;
            y1 = (code & 0x00FF);
            plotPattern(patNum, x1, y1);
        }

        return (index - 1);
    }

    /**
     * Draws a single pixel on the AGI picture.
     *
     * @param x The X position of the pixel.
     * @param y The Y position of the pixel.
     */
    public void putPixel(int x, int y) {
        int index = (y << 7) + (y << 5) + x;
       
        if (editStatus.isVisualDrawEnabled()) {
            visualScreen[index] = colours[editStatus.getVisualColour()];
        }
        if (editStatus.isPriorityDrawEnabled()) {
            priorityScreen[index] = colours[editStatus.getPriorityColour()];
        }
    }
   
    /**
     * Draw a line the most efficient way we can. Speed is preferred over
     * removal of duplicated code.
     *
     * @param x1 Start X Coordinate.
     * @param y1 Start Y Coordinate.
     * @param x2 End X Coordinate.
     * @param y2 End Y Coordinate.
     */
    public final void drawLine(int x1, int y1, int x2, int y2) {
        int x, y, index, endIndex, visualRGBCode, priorityRGBCode;

        // Vertical Line.
        if (x1 == x2) {
            if (y1 > y2) {
                y = y1;
                y1 = y2;
                y2 = y;
            }

            index = (y1 << 7) + (y1 << 5) + x1;
            endIndex = (y2 << 7) + (y2 << 5) + x2;

            if (editStatus.isVisualDrawEnabled()) {
                if (editStatus.isPriorityDrawEnabled()) {
                    // Vertical line on both visual and priority screens.
                    visualRGBCode = colours[editStatus.getVisualColour()];
                    priorityRGBCode = colours[editStatus.getPriorityColour()];

                    for (; index <= endIndex; index += 160) {
                        visualScreen[index] = visualRGBCode;
                        priorityScreen[index] = priorityRGBCode;
                    }
                } else {
                    // Vertical line on only the visual screen.
                    visualRGBCode = colours[editStatus.getVisualColour()];

                    for (; index <= endIndex; index += 160) {
                        visualScreen[index] = visualRGBCode;
                    }
                }
            } else if (editStatus.isPriorityDrawEnabled()) {
                // Vertical line on only the priority screen.
                priorityRGBCode = colours[editStatus.getPriorityColour()];

                for (; index <= endIndex; index += 160) {
                    priorityScreen[index] = priorityRGBCode;
                }
            }
        }
        // Horizontal Line.
        else if (y1 == y2) {
            if (x1 > x2) {
                x = x1;
                x1 = x2;
                x2 = x;
            }

            index = (y1 << 7) + (y1 << 5) + x1;
            endIndex = (y2 << 7) + (y2 << 5) + x2;

            if (editStatus.isVisualDrawEnabled()) {
                if (editStatus.isPriorityDrawEnabled()) {
                    // Horizontal line on both visual and priority screens.
                    visualRGBCode = colours[editStatus.getVisualColour()];
                    priorityRGBCode = colours[editStatus.getPriorityColour()];

                    for (; index <= endIndex; index++) {
                        visualScreen[index] = visualRGBCode;
                        priorityScreen[index] = priorityRGBCode;
                    }
                } else {
                    // Horizontal line on only the visual screen.
                    visualRGBCode = colours[editStatus.getVisualColour()];

                    for (; index <= endIndex; index++) {
                        visualScreen[index] = visualRGBCode;
                    }
                }
            } else if (editStatus.isPriorityDrawEnabled()) {
                // Horizontal line on only the priority screen.
                priorityRGBCode = colours[editStatus.getPriorityColour()];

                for (; index <= endIndex; index++) {
                    priorityScreen[index] = priorityRGBCode;
                }
            }

        } else {
            int deltaX = x2 - x1;
            int deltaY = y2 - y1;
            int stepX = 1;
            int stepY = 1;
            int detDelta;
            int errorX;
            int errorY;
            int count;

            if (deltaY < 0) {
                stepY = -1;
                deltaY = -deltaY;
            }

            if (deltaX < 0) {
                stepX = -1;
                deltaX = -deltaX;
            }

            if (deltaY > deltaX) {
                count = deltaY;
                detDelta = deltaY;
                errorX = deltaY / 2;
                errorY = 0;
            } else {
                count = deltaX;
                detDelta = deltaX;
                errorX = 0;
                errorY = deltaX / 2;
            }

            x = x1;
            y = y1;

            if (editStatus.isVisualDrawEnabled()) {
                if (editStatus.isPriorityDrawEnabled()) {
                    // Both visual and priority screens.
                    visualRGBCode = colours[editStatus.getVisualColour()];
                    priorityRGBCode = colours[editStatus.getPriorityColour()];

                    index = (y << 7) + (y << 5) + x;
                    visualScreen[index] = visualRGBCode;
                    priorityScreen[index] = priorityRGBCode;

                    do {
                        errorY = (errorY + deltaY);
                        if (errorY >= detDelta) {
                            errorY -= detDelta;
                            y += stepY;
                        }

                        errorX = (errorX + deltaX);
                        if (errorX >= detDelta) {
                            errorX -= detDelta;
                            x += stepX;
                        }

                        index = (y << 7) + (y << 5) + x;
                        visualScreen[index] = visualRGBCode;
                        priorityScreen[index] = priorityRGBCode;
                        count--;
                    } while (count > 0);

                    index = (y << 7) + (y << 5) + x;
                    visualScreen[index] = visualRGBCode;
                    priorityScreen[index] = priorityRGBCode;

                } else {
                    // Only the visual screen.
                    visualRGBCode = colours[editStatus.getVisualColour()];

                    visualScreen[(y << 7) + (y << 5) + x] = visualRGBCode;

                    do {
                        errorY = (errorY + deltaY);
                        if (errorY >= detDelta) {
                            errorY -= detDelta;
                            y += stepY;
                        }

                        errorX = (errorX + deltaX);
                        if (errorX >= detDelta) {
                            errorX -= detDelta;
                            x += stepX;
                        }

                        visualScreen[(y << 7) + (y << 5) + x] = visualRGBCode;
                        count--;
                    } while (count > 0);

                    visualScreen[(y << 7) + (y << 5) + x] = visualRGBCode;
                }
            } else if (editStatus.isPriorityDrawEnabled()) {
                // Only the priority screen.
                priorityRGBCode = colours[editStatus.getPriorityColour()];

                priorityScreen[(y << 7) + (y << 5) + x] = priorityRGBCode;

                do {
                    errorY = (errorY + deltaY);
                    if (errorY >= detDelta) {
                        errorY -= detDelta;
                        y += stepY;
                    }

                    errorX = (errorX + deltaX);
                    if (errorX >= detDelta) {
                        errorX -= detDelta;
                        x += stepX;
                    }

                    priorityScreen[(y << 7) + (y << 5) + x] = priorityRGBCode;
                    count--;
                } while (count > 0);

                priorityScreen[(y << 7) + (y << 5) + x] = priorityRGBCode;
            }
        }
    }

    /**
     * Performs a fill at the given position on the picture.
     *
     * @param x the X position to fill at.
     * @param y the Y position to fill at.
     */
    public void fill(int x, int y) {
        // If the fill colour is white then return immediately.
        if (editStatus.getVisualColour() == EditStatus.TRANSPARENT) {
            return;
        }

        int fillQueue[] = new int[8000];
        int rpos = 0;
        int spos = 0;
        int index = (y << 7) + (y << 5) + x;
        int white = EgaPalette.transparent;
        int red = EgaPalette.transparent;
        int[] fillColours = colours;
       
        // The fill type determines how we fill.
        switch (editStatus.getFillType()) {
            case NONE:
                // No fill so return immediately.
                return;
            case TRANSPARENT:
                fillColours = EgaPalette.transparent_colours;
                break;
        }
       
        if (editStatus.isVisualDrawEnabled()) {
            if (editStatus.isPriorityDrawEnabled()) {
                // Fill both visual and priority.
                int visualRGBCode = fillColours[editStatus.getVisualColour()];
                int priorityRGBCode = fillColours[editStatus.getPriorityColour()];

                fillQueue[spos++] = index;

                while (rpos != spos) {

                    index = fillQueue[rpos++];

                    if (visualScreen[index] == white) {
                        // Fill current position.
                        visualScreen[index] = visualRGBCode;
                        priorityScreen[index] = priorityRGBCode;

                        int lineStartIndex = (index / 160) * 160;
                        int lineEndIndex = lineStartIndex + 159;

                        // Go west.
                        int westIndex = index - 1;
                        while ((westIndex >= lineStartIndex) && (visualScreen[westIndex] == white)) {
                            westIndex--;
                        }

                        // Go east
                        int eastIndex = index + 1;
                        while ((eastIndex <= lineEndIndex) && (visualScreen[eastIndex] == white)) {
                            eastIndex++;
                        }

                        // Draw line.
                        westIndex++;
                        eastIndex--;
                        for (index = westIndex; index <= eastIndex; index++) {
                            visualScreen[index] = visualRGBCode;
                            priorityScreen[index] = priorityRGBCode;
                        }

                        int lastRGBColour = 0x80000000;

                        // Test above.
                        westIndex -= 160;
                        eastIndex -= 160;
                        if (westIndex > -1) {
                            for (index = westIndex; index <= eastIndex; index++) {
                                int rgbColour = visualScreen[index];
                                if ((rgbColour == white) && (lastRGBColour != white)) {
                                    fillQueue[spos++] = index;
                                }
                                lastRGBColour = rgbColour;
                            }
                        }

                        // Test below.
                        westIndex += 320;
                        eastIndex += 320;
                        lastRGBColour = 0x80000000;
                        if (eastIndex < 26880) {
                            for (index = westIndex; index <= eastIndex; index++) {
                                int rgbColour = visualScreen[index];
                                if ((rgbColour == white) && (lastRGBColour != white)) {
                                    fillQueue[spos++] = index;
                                }
                                lastRGBColour = rgbColour;
                            }
                        }
                    }
                }
            } else {
                // Visual only fill.
                int visualRGBCode = fillColours[editStatus.getVisualColour()];

                fillQueue[spos++] = index;

                while (rpos != spos) {

                    index = fillQueue[rpos++];

                    if (visualScreen[index] == white) {
                        // Fill current position.
                        visualScreen[index] = visualRGBCode;

                        int lineStartIndex = (index / 160) * 160;
                        int lineEndIndex = lineStartIndex + 159;

                        // Go west.
                        int westIndex = index - 1;
                        while ((westIndex >= lineStartIndex) && (visualScreen[westIndex] == white)) {
                            westIndex--;
                        }

                        // Go east
                        int eastIndex = index + 1;
                        while ((eastIndex <= lineEndIndex) && (visualScreen[eastIndex] == white)) {
                            eastIndex++;
                        }

                        // Draw line.
                        westIndex++;
                        eastIndex--;
                        for (index = westIndex; index <= eastIndex; index++) {
                            visualScreen[index] = visualRGBCode;
                        }

                        int lastRGBColour = 0x80000000;

                        // Test above.
                        westIndex -= 160;
                        eastIndex -= 160;
                        if (westIndex > -1) {
                            for (index = westIndex; index <= eastIndex; index++) {
                                int rgbColour = visualScreen[index];
                                if ((rgbColour == white) && (lastRGBColour != white)) {
                                    fillQueue[spos++] = index;
                                }
                                lastRGBColour = rgbColour;
                            }
                        }

                        // Test below.
                        westIndex += 320;
                        eastIndex += 320;
                        lastRGBColour = 0x80000000;
                        if (eastIndex < 26880) {
                            for (index = westIndex; index <= eastIndex; index++) {
                                int rgbColour = visualScreen[index];
                                if ((rgbColour == white) && (lastRGBColour != white)) {
                                    fillQueue[spos++] = index;
                                }
                                lastRGBColour = rgbColour;
                            }
                        }
                    }
                }
            }
        } else if (editStatus.isPriorityDrawEnabled()) {
            // Priority only fill.
            int priorityRGBCode = fillColours[editStatus.getPriorityColour()];

            fillQueue[spos++] = index;

            while (rpos != spos) {

                index = fillQueue[rpos++];

                if (priorityScreen[index] == red) {
                    // Fill current position.
                    priorityScreen[index] = priorityRGBCode;

                    int lineStartIndex = (index / 160) * 160;
                    int lineEndIndex = lineStartIndex + 159;

                    // Go west.
                    int westIndex = index - 1;
                    while ((westIndex >= lineStartIndex) && (priorityScreen[westIndex] == red)) {
                        westIndex--;
                    }

                    // Go east
                    int eastIndex = index + 1;
                    while ((eastIndex <= lineEndIndex) && (priorityScreen[eastIndex] == red)) {
                        eastIndex++;
                    }

                    // Draw line.
                    westIndex++;
                    eastIndex--;
                    for (index = westIndex; index <= eastIndex; index++) {
                        priorityScreen[index] = priorityRGBCode;
                    }

                    int lastRGBColour = 0x80000000;

                    // Test above.
                    westIndex -= 160;
                    eastIndex -= 160;
                    if (westIndex > -1) {
                        for (index = westIndex; index <= eastIndex; index++) {
                            int rgbColour = priorityScreen[index];
                            if ((rgbColour == red) && (lastRGBColour != red)) {
                                fillQueue[spos++] = index;
                            }
                            lastRGBColour = rgbColour;
                        }
                    }

                    // Test below.
                    westIndex += 320;
                    eastIndex += 320;
                    lastRGBColour = 0x80000000;
                    if (eastIndex < 26880) {
                        for (index = westIndex; index <= eastIndex; index++) {
                            int rgbColour = priorityScreen[index];
                            if ((rgbColour == red) && (lastRGBColour != red)) {
                                fillQueue[spos++] = index;
                            }
                            lastRGBColour = rgbColour;
                        }
                    }
                }
            }
        }
    }

    /** Circle Bitmaps */
    public static final short circles[][] = new short[][] { { 0x80 }, { 0xfc }, { 0x5f, 0xf4 }, { 0x66, 0xff, 0xf6, 0x60 }, { 0x23, 0xbf, 0xff, 0xff, 0xee, 0x20 }, { 0x31, 0xe7, 0x9e, 0xff, 0xff, 0xde, 0x79, 0xe3, 0x00 }, { 0x38, 0xf9, 0xf3, 0xef, 0xff, 0xff, 0xff, 0xfe, 0xf9, 0xf3, 0xe3, 0x80 }, { 0x18, 0x3c, 0x7e, 0x7e, 0x7e, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7e, 0x7e, 0x7e, 0x3c, 0x18 } };

    /** Splatter Brush Bitmaps */
    public static final short splatterMap[] = new short[] { 0x20, 0x94, 0x02, 0x24, 0x90, 0x82, 0xa4, 0xa2, 0x82, 0x09, 0x0a, 0x22, 0x12, 0x10, 0x42, 0x14, 0x91, 0x4a, 0x91, 0x11, 0x08, 0x12, 0x25, 0x10, 0x22, 0xa8, 0x14, 0x24, 0x00, 0x50, 0x24, 0x04 };

    /** Starting Bit Position */
    public static final short splatterStart[] = new short[] { 0x00, 0x18, 0x30, 0xc4, 0xdc, 0x65, 0xeb, 0x48, 0x60, 0xbd, 0x89, 0x05, 0x0a, 0xf4, 0x7d, 0x7d, 0x85, 0xb0, 0x8e, 0x95, 0x1f, 0x22, 0x0d, 0xdf, 0x2a, 0x78, 0xd5, 0x73, 0x1c, 0xb4, 0x40, 0xa1, 0xb9, 0x3c, 0xca, 0x58, 0x92, 0x34, 0xcc, 0xce, 0xd7, 0x42, 0x90, 0x0f, 0x8b, 0x7f, 0x32, 0xed, 0x5c, 0x9d, 0xc8, 0x99, 0xad, 0x4e, 0x56, 0xa6, 0xf7, 0x68, 0xb7, 0x25, 0x82, 0x37, 0x3a, 0x51, 0x69, 0x26, 0x38, 0x52, 0x9e, 0x9a, 0x4f, 0xa7, 0x43, 0x10, 0x80, 0xee, 0x3d, 0x59, 0x35, 0xcf, 0x79, 0x74, 0xb5, 0xa2, 0xb1, 0x96, 0x23, 0xe0, 0xbe, 0x05, 0xf5, 0x6e, 0x19, 0xc5, 0x66, 0x49, 0xf0, 0xd1, 0x54, 0xa9, 0x70, 0x4b, 0xa4, 0xe2, 0xe6, 0xe5, 0xab, 0xe4, 0xd2, 0xaa, 0x4c, 0xe3, 0x06, 0x6f, 0xc6, 0x4a, 0xa4, 0x75, 0x97, 0xe1 };

    /**
     * Plots a brush pattern. Draws pixels, circles, squares, or splatter
     * brush patterns depending on the pattern code.
     *
     * @param patNum the pattern number to use.
     * @param x the X position to plot at.
     * @param y the Y position to plot at.
     */
    public void plotPattern(int patNum, int x, int y) {
        int circlePos = 0;
        int x1, y1, penSize, bitPos = splatterStart[patNum];
        int visualRGBCode = (editStatus.isVisualDrawEnabled() ? colours[editStatus.getVisualColour()] : 0);
        int priorityRGBCode = (editStatus.isPriorityDrawEnabled() ? colours[editStatus.getPriorityColour()] : 0);
        int patCode = editStatus.getBrushCode();

        penSize = (patCode & 7);

        if (x < ((penSize / 2) + 1)) {
            x = ((penSize / 2) + 1);

        } else if (x > 160 - ((penSize / 2) + 1)) {
            x = 160 - ((penSize / 2) + 1);
        }

        if (y < penSize) {
            y = penSize;

        } else if (y >= 168 - penSize) {
            y = 167 - penSize;
        }

        for (y1 = y - penSize; y1 <= y + penSize; y1++) {
            for (x1 = x - ((int) Math.ceil((float) penSize / 2)); x1 <= x + ((int) Math.floor((float) penSize / 2)); x1++) {
                if ((patCode & 0x10) > 0) { /* Square */
                    if ((patCode & 0x20) > 0) {
                        if (((splatterMap[bitPos >> 3] >> (7 - (bitPos & 7))) & 1) > 0) {
                            if (editStatus.isVisualDrawEnabled()) {
                                visualScreen[(y1 << 7) + (y1 << 5) + x1] = visualRGBCode;
                            }
                            if (editStatus.isPriorityDrawEnabled()) {
                                priorityScreen[(y1 << 7) + (y1 << 5) + x1] = priorityRGBCode;
                            }
                        }
                        bitPos++;
                        if (bitPos == 0xff) {
                            bitPos = 0;
                        }
                    } else {
                        if (editStatus.isVisualDrawEnabled()) {
                            visualScreen[(y1 << 7) + (y1 << 5) + x1] = visualRGBCode;
                        }
                        if (editStatus.isPriorityDrawEnabled()) {
                            priorityScreen[(y1 << 7) + (y1 << 5) + x1] = priorityRGBCode;
                        }
                    }
                } else { /* Circle */
                    if (((circles[patCode & 7][circlePos >> 3] >> (7 - (circlePos & 7))) & 1) > 0) {
                        if ((patCode & 0x20) > 0) {
                            if (((splatterMap[bitPos >> 3] >> (7 - (bitPos & 7))) & 1) > 0) {
                                if (editStatus.isVisualDrawEnabled()) {
                                    visualScreen[(y1 << 7) + (y1 << 5) + x1] = visualRGBCode;
                                }
                                if (editStatus.isPriorityDrawEnabled()) {
                                    priorityScreen[(y1 << 7) + (y1 << 5) + x1] = priorityRGBCode;
                                }
                            }
                            bitPos++;
                            if (bitPos == 0xff) {
                                bitPos = 0;
                            }
                        } else {
                            if (editStatus.isVisualDrawEnabled()) {
                                visualScreen[(y1 << 7) + (y1 << 5) + x1] = visualRGBCode;
                            }
                            if (editStatus.isPriorityDrawEnabled()) {
                                priorityScreen[(y1 << 7) + (y1 << 5) + x1] = priorityRGBCode;
                            }
                        }
                    }
                    circlePos++;
                }
            }
        }
    }
}
TOP

Related Classes of com.agifans.picedit.picture.Picture

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.