|
@@ -1,535 +0,0 @@
|
|
|
-package com.its.app.utils;
|
|
|
-
|
|
|
-
|
|
|
-import java.awt.*;
|
|
|
-import java.awt.image.BufferedImage;
|
|
|
-import java.awt.image.DataBufferInt;
|
|
|
-import java.util.ArrayList;
|
|
|
-
|
|
|
-/**
|
|
|
- * Drawing class which provides flood fill functionality. Found
|
|
|
- * on http://www.javagaming.org forum and adapted to be more generic. Antialiasing
|
|
|
- * and texture filling capability added.
|
|
|
- *
|
|
|
- * @author kingaschi (Christph Aschwanden - king@kingx.com)
|
|
|
- * @author moogie (Javagaming Forum)
|
|
|
- * @author tom (Javagaming Forum)
|
|
|
- * @since April 26, 2005
|
|
|
- */
|
|
|
-public final class FloodFill {
|
|
|
-
|
|
|
- /**
|
|
|
- * Line info class for linear non-recursive fill.
|
|
|
- *
|
|
|
- * @author king
|
|
|
- * @since April 27, 2005
|
|
|
- */
|
|
|
- static class LineInfo {
|
|
|
-
|
|
|
- /** The left position. */
|
|
|
- int left;
|
|
|
- /** The right position. */
|
|
|
- int right;
|
|
|
- /** The y position. */
|
|
|
- int y;
|
|
|
-
|
|
|
- /**
|
|
|
- * Sets the line info.
|
|
|
- *
|
|
|
- * @param left Previous left position.
|
|
|
- * @param right Previous right position.
|
|
|
- * @param y Y position.
|
|
|
- */
|
|
|
- void setInfo(int left, int right, int y) {
|
|
|
- this.left = left;
|
|
|
- this.right = right;
|
|
|
- this.y = y;
|
|
|
- }
|
|
|
- }
|
|
|
- /** The array used for fast flood fill. Instantiated only once to improve performance. */
|
|
|
- private ArrayList<LineInfo> linearNRTodo = new ArrayList<LineInfo>();
|
|
|
- /** The index into linear non-recursive fill. */
|
|
|
- private int index;
|
|
|
-
|
|
|
- /** True, if antialised fill should be used. */
|
|
|
- private boolean antialiased = true;
|
|
|
- /** The raw image data to fill. */
|
|
|
- private int[] image;
|
|
|
- /** The raw mask image data with the borders. */
|
|
|
- private int[] maskImage;
|
|
|
- /** The start x position for the fill. */
|
|
|
- private int startX;
|
|
|
- /** The start y position for the fill. */
|
|
|
- private int startY;
|
|
|
- /** The fill color to use for the original image. */
|
|
|
- private int fillColor;
|
|
|
- /** The fill color to use for the mask image. */
|
|
|
- private int maskColor;
|
|
|
- /** The pattern fill color to use. */
|
|
|
- private int patternColor;
|
|
|
- /** The width of the chessboard pattern. */
|
|
|
- private int patternWidth;
|
|
|
- /** The height of the chessboard pattern. */
|
|
|
- private int patternHeight;
|
|
|
- /** The color to replace with the fill color. */
|
|
|
- private int startColor;
|
|
|
- /** The width of the image to fill. */
|
|
|
- private int width;
|
|
|
- /** The height of the image to fill. */
|
|
|
- private int height;
|
|
|
-
|
|
|
- /** The result image. */
|
|
|
- private BufferedImage bufferedImage;
|
|
|
- /** The mask image used. */
|
|
|
- private BufferedImage bufferedMaskImage;
|
|
|
-
|
|
|
-
|
|
|
- /**
|
|
|
- * Constructor for flood fill, requires the image for filling operation.
|
|
|
- *
|
|
|
- * @param imageToFill The image used for filling.
|
|
|
- */
|
|
|
- public FloodFill(Image imageToFill) {
|
|
|
- this(imageToFill, null);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Constructor for flood fill, requires the image and mask for filling operation.
|
|
|
- *
|
|
|
- * @param imageToFill The image used for filling.
|
|
|
- * @param maskImage The image containing the border lines.
|
|
|
- */
|
|
|
- public FloodFill(Image imageToFill, Image maskImage) {
|
|
|
- // sets image to fill
|
|
|
- setImage(imageToFill);
|
|
|
-
|
|
|
- // sets the mask
|
|
|
- setMask(maskImage);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Returns true, if antialiased filling is used.
|
|
|
- *
|
|
|
- * @return True, for antialiased filling.
|
|
|
- */
|
|
|
- public boolean isAntialiased() {
|
|
|
- return this.antialiased;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Sets if antialiased filling is used.
|
|
|
- *
|
|
|
- * @param antialiased True, for antialiased filling.
|
|
|
- */
|
|
|
- public void setAntialiased(boolean antialiased) {
|
|
|
- this.antialiased = antialiased;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Returns the width of the fill area.
|
|
|
- *
|
|
|
- * @return The width of the fill area.
|
|
|
- */
|
|
|
- public int getWidth() {
|
|
|
- return this.width;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Returns the height of the fill area.
|
|
|
- *
|
|
|
- * @return The height of the fill area.
|
|
|
- */
|
|
|
- public int getHeight() {
|
|
|
- return this.height;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Returns the image that was filled.
|
|
|
- *
|
|
|
- * @return The image that was filled.
|
|
|
- */
|
|
|
- public BufferedImage getImage() {
|
|
|
- return bufferedImage;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Sets the image to be filled.
|
|
|
- *
|
|
|
- * @param imageToFill The image to be filled.
|
|
|
- */
|
|
|
- public void setImage(Image imageToFill) {
|
|
|
- // copy image to fill into buffered image first
|
|
|
- width = imageToFill.getWidth(null);
|
|
|
- height = imageToFill.getHeight(null);
|
|
|
- bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
|
|
- Graphics g = bufferedImage.getGraphics();
|
|
|
- g.drawImage(imageToFill, 0, 0, null);
|
|
|
-
|
|
|
- // get fill image
|
|
|
- this.image = ((DataBufferInt)bufferedImage.getRaster().getDataBuffer()).getData();
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Returns the mask containing the fill borders.
|
|
|
- *
|
|
|
- * @return The mask with the fill borders.
|
|
|
- */
|
|
|
- public BufferedImage getMask() {
|
|
|
- return bufferedMaskImage;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Sets the mask image which contains the borders.
|
|
|
- *
|
|
|
- * @param maskImage The mask image to set. If null, the image to fill is used as
|
|
|
- * the mask.
|
|
|
- */
|
|
|
- public void setMask(Image maskImage) {
|
|
|
- this.bufferedMaskImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
|
|
- Graphics g = this.bufferedMaskImage.getGraphics();
|
|
|
-
|
|
|
- if (maskImage == null) {
|
|
|
- // if no mask, use the original image to fill
|
|
|
- g.drawImage(this.bufferedImage, 0, 0, null);
|
|
|
- }
|
|
|
- else {
|
|
|
- // if mask, use it
|
|
|
- g.drawImage(maskImage, 0, 0, null);
|
|
|
- }
|
|
|
-
|
|
|
- this.maskImage = bufferedMaskImage.getRGB(0, 0, width, height, null, 0, width);
|
|
|
- }
|
|
|
- /**
|
|
|
- * Flood fills parts of an image.
|
|
|
- *
|
|
|
- * @param x The x coordinate to start filling.
|
|
|
- * @param y The y coordinate to start filling.
|
|
|
- * @param color The new fill color.
|
|
|
- */
|
|
|
- public void fill(int x, int y, Color color) {
|
|
|
- this.startX = x;
|
|
|
- this.startY = y;
|
|
|
- this.fillColor = color.getRGB();
|
|
|
- this.maskColor = color.getRGB(); // the fill color for the mask
|
|
|
- this.startColor = this.maskImage[startY * width + startX];
|
|
|
- if (fillColor != startColor) {
|
|
|
- floodFill();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Fills a bounded area of an image. Uses a chessboard pattern. The width in pixels
|
|
|
- * of the chessboard pattern can be specified.
|
|
|
- *
|
|
|
- * @param x The x coordinate to start filling.
|
|
|
- * @param y The y coordinate to start filling.
|
|
|
- * @param color The new fill color.
|
|
|
- * @param patternColor The color of the pattern to use.
|
|
|
- * @param patternWidth The width of the chessboard pattern.
|
|
|
- * @param patternHeight The height of the chessboard pattern.
|
|
|
- */
|
|
|
- public void fill(int x, int y, Color color, Color patternColor, int patternWidth, int patternHeight) {
|
|
|
- this.startX = x;
|
|
|
- this.startY = y;
|
|
|
- this.fillColor = color.getRGB();
|
|
|
- this.patternColor = patternColor.getRGB();
|
|
|
- this.patternWidth = (patternWidth > 0) ? patternWidth : Integer.MAX_VALUE;
|
|
|
- this.patternHeight = (patternHeight > 0) ? patternHeight : Integer.MAX_VALUE;
|
|
|
- this.startColor = this.maskImage[startY * width + startX];
|
|
|
-
|
|
|
- // the fill color for the mask
|
|
|
- this.maskColor = ((this.fillColor >> 2) & 0x3F3F3F3F)
|
|
|
- + ((this.patternColor >> 1) & 0x7F7F7F7F);
|
|
|
-
|
|
|
- if (this.maskColor != this.startColor) {
|
|
|
- // mask color ok (=different)
|
|
|
- floodFill2();
|
|
|
- }
|
|
|
- else {
|
|
|
- // create new mask color first
|
|
|
- this.maskColor = ((this.fillColor >> 1) & 0x7F7F7F7F)
|
|
|
- + ((this.patternColor >> 2) & 0x3F3F3F3F);
|
|
|
- floodFill2();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Fills the line at (x, y). Then fills the line above and below the current line.
|
|
|
- * The border is defined as any color except the start color. Non-recursive version,
|
|
|
- * doesn't have JVM stack size limitations.
|
|
|
- */
|
|
|
- private void floodFill() {
|
|
|
- // init stack
|
|
|
- linearNRTodo.clear();
|
|
|
- index = 0;
|
|
|
- floodFill(startX, startY);
|
|
|
-
|
|
|
- // loop through todo list
|
|
|
- while (index < linearNRTodo.size()) {
|
|
|
- // get loop data
|
|
|
- LineInfo lineInfo = linearNRTodo.get(index);
|
|
|
- index++;
|
|
|
- int y = lineInfo.y;
|
|
|
- int left = lineInfo.left;
|
|
|
- int right = lineInfo.right;
|
|
|
-
|
|
|
- // check top
|
|
|
- if (y > 0) {
|
|
|
- int yOff = (y - 1) * width;
|
|
|
- int x = left;
|
|
|
- while (x <= right) {
|
|
|
- if (maskImage[yOff + x] == startColor) {
|
|
|
- x = floodFill(x, y - 1);
|
|
|
- }
|
|
|
- else {
|
|
|
- // fill antialised if allowed
|
|
|
- if (antialiased) {
|
|
|
- int antialiasedColor = maskImage[yOff + x];
|
|
|
- antialiasedColor = (antialiasedColor >> 1) & 0x7F7F7F7F;
|
|
|
- antialiasedColor = antialiasedColor + ((fillColor >> 1) & 0x7F7F7F7F);
|
|
|
- if (antialiasedColor != startColor) {
|
|
|
- image[yOff + x] = antialiasedColor;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- x++;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // check bottom
|
|
|
- if (y < height - 1) {
|
|
|
- int yOff = (y + 1) * width;
|
|
|
- int x = left;
|
|
|
- while (x <= right) {
|
|
|
- if (maskImage[yOff + x] == startColor) {
|
|
|
- x = floodFill(x, y + 1);
|
|
|
- }
|
|
|
- else {
|
|
|
- // fill antialised if allowed
|
|
|
- if (antialiased) {
|
|
|
- int antialiasedColor = maskImage[yOff + x];
|
|
|
- antialiasedColor = (antialiasedColor >> 1) & 0x7F7F7F7F;
|
|
|
- antialiasedColor = antialiasedColor + ((fillColor >> 1) & 0x7F7F7F7F);
|
|
|
- if (antialiasedColor != startColor) {
|
|
|
- image[yOff + x] = antialiasedColor;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- x++;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Fills the line at (x, y). And adds to the stack.
|
|
|
- *
|
|
|
- * @param x The x-coordinate of the start position.
|
|
|
- * @param y The y-coordinate of the start position.
|
|
|
- * @return Right.
|
|
|
- */
|
|
|
- private int floodFill(int x, int y) {
|
|
|
- int yOff = y * width;
|
|
|
-
|
|
|
- // fill left of (x,y) until border or edge of image
|
|
|
- int left = x;
|
|
|
- do {
|
|
|
- image[yOff + left] = fillColor;
|
|
|
- maskImage[yOff + left] = maskColor;
|
|
|
- left--;
|
|
|
- }
|
|
|
- while ((left >= 0) && (maskImage[yOff + left] == startColor));
|
|
|
- // fill antialised if allowed
|
|
|
- if ((antialiased) && (left >= 0)) {
|
|
|
- int antialiasedColor = maskImage[yOff + left];
|
|
|
- antialiasedColor = (antialiasedColor >> 1) & 0x7F7F7F7F;
|
|
|
- antialiasedColor = antialiasedColor + ((fillColor >> 1) & 0x7F7F7F7F);
|
|
|
- if (antialiasedColor != startColor) {
|
|
|
- image[yOff + left] = antialiasedColor;
|
|
|
- }
|
|
|
- }
|
|
|
- left++;
|
|
|
-
|
|
|
- // fill right of (x, y) until border or edge of image
|
|
|
- int right = x;
|
|
|
- do {
|
|
|
- image[yOff + right] = fillColor;
|
|
|
- maskImage[yOff + right] = maskColor;
|
|
|
- right++;
|
|
|
- }
|
|
|
- while ((right < width) && (maskImage[yOff + right] == startColor));
|
|
|
- // fill antialised if allowed
|
|
|
- if ((antialiased) && (right < width)) {
|
|
|
- int antialiasedColor = maskImage[yOff + right];
|
|
|
- antialiasedColor = (antialiasedColor >> 1) & 0x7F7F7F7F;
|
|
|
- antialiasedColor = antialiasedColor + ((fillColor >> 1) & 0x7F7F7F7F);
|
|
|
- if (antialiasedColor != startColor) {
|
|
|
- image[yOff + right] = antialiasedColor;
|
|
|
- }
|
|
|
- }
|
|
|
- right--;
|
|
|
-
|
|
|
- // add to stack
|
|
|
- if (index == 0) {
|
|
|
- LineInfo lineInfo = new LineInfo();
|
|
|
- lineInfo.setInfo(left, right, y);
|
|
|
- linearNRTodo.add(lineInfo);
|
|
|
- }
|
|
|
- else {
|
|
|
- index--;
|
|
|
- linearNRTodo.get(index).setInfo(left, right, y);
|
|
|
- }
|
|
|
-
|
|
|
- // return right position
|
|
|
- return right;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Fills the line at (x, y). Then fills the line above and below the current line.
|
|
|
- * The border is defined as any color except the start color. Non-recursive version,
|
|
|
- * doesn't have JVM stack size limitations.
|
|
|
- */
|
|
|
- private void floodFill2() {
|
|
|
- // init stack
|
|
|
- linearNRTodo.clear();
|
|
|
- index = 0;
|
|
|
- floodFill2(startX, startY);
|
|
|
-
|
|
|
- // loop through todo list
|
|
|
- while (index < linearNRTodo.size()) {
|
|
|
- // get loop data
|
|
|
- LineInfo lineInfo = linearNRTodo.get(index);
|
|
|
- index++;
|
|
|
- int y = lineInfo.y;
|
|
|
- int left = lineInfo.left;
|
|
|
- int right = lineInfo.right;
|
|
|
-
|
|
|
- // check top
|
|
|
- if (y > 0) {
|
|
|
- int yOff = (y - 1) * width;
|
|
|
- int x = left;
|
|
|
- while (x <= right) {
|
|
|
- if (maskImage[yOff + x] == startColor) {
|
|
|
- x = floodFill2(x, y - 1);
|
|
|
- }
|
|
|
- else {
|
|
|
- // fill antialised if allowed
|
|
|
- if (antialiased) {
|
|
|
- int antialiasedColor = maskImage[yOff + x];
|
|
|
- antialiasedColor = (antialiasedColor >> 1) & 0x7F7F7F7F;
|
|
|
- antialiasedColor = antialiasedColor + ((fillColor2(x, y - 1) >> 1) & 0x7F7F7F7F);
|
|
|
- if (antialiasedColor != startColor) {
|
|
|
- image[yOff + x] = antialiasedColor;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- x++;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // check bottom
|
|
|
- if (y < height - 1) {
|
|
|
- int yOff = (y + 1) * width;
|
|
|
- int x = left;
|
|
|
- while (x <= right) {
|
|
|
- if (maskImage[yOff + x] == startColor) {
|
|
|
- x = floodFill2(x, y + 1);
|
|
|
- }
|
|
|
- else {
|
|
|
- // fill antialised if allowed
|
|
|
- if (antialiased) {
|
|
|
- int antialiasedColor = maskImage[yOff + x];
|
|
|
- antialiasedColor = (antialiasedColor >> 1) & 0x7F7F7F7F;
|
|
|
- antialiasedColor = antialiasedColor + ((fillColor2(x, y + 1) >> 1) & 0x7F7F7F7F);
|
|
|
- if (antialiasedColor != startColor) {
|
|
|
- image[yOff + x] = antialiasedColor;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- x++;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Fills the line at (x, y). And adds to the stack.
|
|
|
- *
|
|
|
- * @param x The x-coordinate of the start position.
|
|
|
- * @param y The y-coordinate of the start position.
|
|
|
- * @return Right.
|
|
|
- */
|
|
|
- private int floodFill2(int x, int y) {
|
|
|
- int yOff = y * width;
|
|
|
-
|
|
|
- // fill left of (x,y) until border or edge of image
|
|
|
- int left = x;
|
|
|
- do {
|
|
|
- image[yOff + left] = fillColor2(left, y);
|
|
|
- maskImage[yOff + left] = maskColor;
|
|
|
- left--;
|
|
|
- }
|
|
|
- while ((left >= 0) && (maskImage[yOff + left] == startColor));
|
|
|
- // fill antialised if allowed
|
|
|
- if ((antialiased) && (left >= 0)) {
|
|
|
- int antialiasedColor = maskImage[yOff + left];
|
|
|
- antialiasedColor = (antialiasedColor >> 1) & 0x7F7F7F7F;
|
|
|
- antialiasedColor = antialiasedColor + ((fillColor2(left, y) >> 1) & 0x7F7F7F7F);
|
|
|
- if (antialiasedColor != startColor) {
|
|
|
- image[yOff + left] = antialiasedColor;
|
|
|
- }
|
|
|
- }
|
|
|
- left++;
|
|
|
-
|
|
|
- // fill right of (x, y) until border or edge of image
|
|
|
- int right = x;
|
|
|
- do {
|
|
|
- image[yOff + right] = fillColor2(right, y);
|
|
|
- maskImage[yOff + right] = maskColor;
|
|
|
- right++;
|
|
|
- }
|
|
|
- while ((right < width) && (maskImage[yOff + right] == startColor));
|
|
|
- // fill antialised if allowed
|
|
|
- if ((antialiased) && (right < width)) {
|
|
|
- int antialiasedColor = maskImage[yOff + right];
|
|
|
- antialiasedColor = (antialiasedColor >> 1) & 0x7F7F7F7F;
|
|
|
- antialiasedColor = antialiasedColor + ((fillColor2(right, y) >> 1) & 0x7F7F7F7F);
|
|
|
- if (antialiasedColor != startColor) {
|
|
|
- image[yOff + right] = antialiasedColor;
|
|
|
- }
|
|
|
- }
|
|
|
- right--;
|
|
|
-
|
|
|
- // add to stack
|
|
|
- if (index == 0) {
|
|
|
- LineInfo lineInfo = new LineInfo();
|
|
|
- lineInfo.setInfo(left, right, y);
|
|
|
- linearNRTodo.add(lineInfo);
|
|
|
- }
|
|
|
- else {
|
|
|
- index--;
|
|
|
- linearNRTodo.get(index).setInfo(left, right, y);
|
|
|
- }
|
|
|
-
|
|
|
- // return right position
|
|
|
- return right;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Returns the fill color for a given x and y value.
|
|
|
- *
|
|
|
- * @param x The x position to return the color for.
|
|
|
- * @param y The y position to return the color for.
|
|
|
- * @return The color for the given position.
|
|
|
- */
|
|
|
- private int fillColor2(int x, int y) {
|
|
|
- x /= this.patternWidth;
|
|
|
- y /= this.patternHeight;
|
|
|
- if ((x + y) % 2 == 0) {
|
|
|
- return this.fillColor;
|
|
|
- }
|
|
|
- else {
|
|
|
- return this.patternColor;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|