diff --git a/src/rs/alexanderstojanovich/dfg/fonts/BMF.java b/src/rs/alexanderstojanovich/dfg/fonts/BMF.java index b7fcc26..71ea05f 100644 --- a/src/rs/alexanderstojanovich/dfg/fonts/BMF.java +++ b/src/rs/alexanderstojanovich/dfg/fonts/BMF.java @@ -1,5 +1,5 @@ -/* - * Copyright (C) 2019 Coa +/* + * Copyright (C) 2020 Alexander Stojanovich * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,340 +14,340 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package rs.alexanderstojanovich.dfg.fonts; - -import java.awt.Color; -import java.awt.image.BufferedImage; - -/** - * - * @author Coa - */ -public class BMF extends DoomFont { - - // THIS WAS INSPIRED BY C library by Tomas Dvorak - // SO ALL CREDITS TO HIM FOR DOING SUCH A FANTASTIC JOB - // This version is always 0x11, irrelevant - private int version = 0x11; - // Some information about the font - private String info; - - // As said - line height.. - private int line_height; - // Size over the base line - private int size_over; - // Size under the base line Signed value - private int size_under; - // Space after each character, in addition to each character's own shift - private int add_space; - // Inner size (UNKNOWN PURPOSE) - private int size_inner; - // Count of used colors (not the same as palette size) - private int colors; - // Highest used color index - private int highest_color; - - // Characters of Bytemap Font - // (it's length is all the chars that can be displayed) - // and containts only the chars that can be displayed, not outside the range - //-------------------------------------------------------------------------- - // A - CONSTRUCTORS - //-------------------------------------------------------------------------- - // A1 - CONSTRUCTOR USED WHEN READING FROM THE BINARY "BMF" FILE - public BMF(byte[] buffer) { - super(buffer); - this.loadFont(); // loads font by creating char data about it - } - - // A2 - CONSTRUCTOR USED WHEN MAKING "BMF" FONT FROM PRE EXISTING INSTALLED FONT - NEW SCHOOL VARIANT - public BMF(String info, int spacing, - int line_height, int size_over, int size_under, - BufferedImage image, BMFChar[] charVector) { - super(image, charVector); - this.info = info; - this.add_space = spacing; - this.line_height = line_height; - this.size_over = size_over; - this.size_under = size_under; - this.unloadFont(); - } - - //-------------------------------------------------------------------------- - // B - IMPLEMENTED METHODS FOR INITIALIZATION - //-------------------------------------------------------------------------- - @Override - protected String initFontType() { - return "BMF"; - } - - @Override - protected Color initTransparentColor() { - return new Color(35, 0, 60); - } - - @Override - protected boolean initVerticalOffsets() { - return false; - } - - //-------------------------------------------------------------------------- - // C - PRIVATE METHODS FOR CONSTRUCTORS - //-------------------------------------------------------------------------- - // priv method for the constructor A1 (BMF) -> Buffer to Font - private void loadFont() { - if (buffer[0] == (byte) 0xE1 && buffer[1] == (byte) 0xE6 && buffer[2] == (byte) 0xD5 && buffer[3] == (byte) 0x1A) { // BMF Magic Header - // -- READING FIRST DATA AFTER THE HEADER - this.version = buffer[4] & 0xFF; // version (currently 11h) - this.line_height = buffer[5] & 0xFF; // line-height - this.size_over = buffer[6]; // size-over the base line (-128…127) - this.size_under = buffer[7]; // size-under the base line (-128…127) - this.add_space = buffer[8]; // add-space after each char (-128…127) - this.size_inner = buffer[9]; // size-inner (non-caps level) (-128…127) - this.colors = buffer[10] & 0xFF; // count of used colors (should be <= 32) - this.highest_color = buffer[11] & 0xFF; // highest used color attribute - // -- RESERVED - // -- READ NUMBER OF RGB ENTRIES (P) - int p = buffer[16] & 0xFF; // number of RGB entries (P) - // -- READ FONT PALETTE - this.pos = 17; // is position - // -- FONT PALETTE (RGB bytes, max=63) - this.palette.add(transparentColor); - for (int i = 0; i < p; i++) { - int red = buffer[pos + i * 3] & 0xFF; - int green = buffer[pos + i * 3 + 1] & 0xFF; - int blue = buffer[pos + i * 3 + 2] & 0xFF; - Color color = new Color( - Math.min(red << 2 | red >> 4, 0xFF), - Math.min(green << 2 | green >> 4, 0xFF), - Math.min(blue << 2 | blue >> 4, 0xFF) - ); - if (!color.equals(transparentColor) /*&& !this.palette.contains(color)*/ && this.palette.size() <= PAL_MAX_SIZE) { - this.palette.add(color); - } - - } - pos += 3 * p; - // -- READ INFO LENGTH - int l = buffer[pos++] & 0xFF; - // -- READ INFO STRING - char[] infoStr = new char[l]; - for (int i = 0; i < l; i++) { - infoStr[i] = (char) buffer[pos++]; - } - this.info = String.valueOf(infoStr); - // -- READING NUMBER OF CHARACTERS IN THE FONT - int numchars = ((buffer[pos + 1] & 0xFF) << 8) | (buffer[pos] & 0xFF); - pos += 2; - // -- END - // -- AND THE NEW BEGINNING - this.chars = new BMFChar[numchars]; - // -- READING CHAR ITSELF, ONE BY ONE - for (int i = 0; i < numchars; i++) { - char c = (char) buffer[pos]; - int w = buffer[pos + 1] & 0xFF; - int h = buffer[pos + 2] & 0xFF; - this.chars[i] = new BMFChar(c, w, h); - BMFChar ch = (BMFChar) this.chars[i]; - if (h > this.maxheight) { - this.maxheight = h; - } - ch.setRelx((int) buffer[pos + 3]); - ch.setRely((int) buffer[pos + 4]); - ch.setShift(buffer[pos + 5]); - ch.setOffset(this.totalwidth); - pos += 6; - this.totalwidth += w + this.add_space; - System.arraycopy(buffer, pos, ch.getData(), 0, w * h); - pos += w * h; - } - } - this.error = (this.line_height <= 0 || this.palette.isEmpty() || this.chars.length <= 0 || this.chars.length > 256); - if (this.line_height <= 0) { - this.errorMsg = "Error - Negative or zero line height!"; - } else if (this.palette.isEmpty()) { - this.errorMsg = "Error - This font has no colors!"; - } else if (this.chars.length <= 0) { - this.errorMsg = "Error - This font has no characters!"; - } else if (this.chars.length > 256) { - this.errorMsg = "Error - This font has over 256 characters!"; - } - // error is not successful - } - - // priv method for the constructor A2 (Big Font) -> Font to Buffer - private void unloadFont() { - // -- WRITING MAGIC HEADER - this.buffer[0] = (byte) 0xE1; - this.buffer[1] = (byte) 0xE6; - this.buffer[2] = (byte) 0xD5; - this.buffer[3] = (byte) 0x1A; - // -- WRITING MORE METADATA - this.buffer[4] = (byte) this.version; - this.buffer[5] = (byte) this.line_height; - this.buffer[6] = (byte) this.size_over; - this.buffer[7] = (byte) this.size_under; - this.buffer[8] = (byte) this.add_space; - this.buffer[9] = (byte) this.size_inner; - this.buffer[10] = (byte) this.colors; - this.buffer[11] = (byte) this.highest_color; - // -- WRITING ZEROS AS RESERVED - NOT IMPORTANT - this.buffer[12] = 0x00; - this.buffer[13] = 0x00; - this.buffer[14] = 0x00; - this.buffer[15] = 0x00; - // -- WRITING PALETTE SIZE - int p = Math.min(this.palette.size() - 1, 0xFF); - this.buffer[16] = (byte) p; - this.pos = 17; - // -- WRITING PALETTE ITSELF - for (int i = 1; i < this.palette.size(); i++) { - this.buffer[pos] = (byte) ((this.palette.get(i).getRed() & 0xFF) >> 2); - this.buffer[pos + 1] = (byte) ((this.palette.get(i).getGreen() & 0xFF) >> 2); - this.buffer[pos + 2] = (byte) ((this.palette.get(i).getBlue() & 0xFF) >> 2); - pos += 3; - } - // -- WRITING INFO AND ITS CONTENT - this.buffer[pos++] = (byte) this.info.length(); - for (int i = 0; i < this.info.getBytes().length; i++) { - this.buffer[pos++] = this.info.getBytes()[i]; - } - // -- WRITING NUMBER OF CHARS IN THE FONT - if (this.chars.length < 256) { - this.buffer[pos] = (byte) this.chars.length; - this.buffer[pos + 1] = 0x00; - } else { - this.buffer[pos] = 0x00; - this.buffer[pos + 1] = 0x01; - } - this.pos += 2; - // -- FOR EACH CHAR WRITE THE SPECIFICS - for (DoomFontChar ch : this.chars) { - BMFChar bmfCh = (BMFChar) ch; - this.buffer[pos] = (byte) ch.getC(); - this.buffer[pos + 1] = (byte) ch.getW(); - this.buffer[pos + 2] = (byte) ch.getH(); - this.buffer[pos + 3] = (byte) bmfCh.getRelx(); - this.buffer[pos + 4] = (byte) bmfCh.getRely(); - this.buffer[pos + 5] = (byte) bmfCh.getShift(); - this.pos += 6; - System.arraycopy(ch.getData(), 0, buffer, pos, ch.getData().length); - this.pos += ch.getData().length; - } - } - - // generates image displaying all the characters in the BMF font (overriden default) - @Override - public BufferedImage generateImage(boolean transparency) { - BufferedImage image = null; - image = new BufferedImage(this.totalwidth + 2, this.maxheight + 2, - transparency ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB); - for (DoomFontChar ch : this.chars) { - BMFChar bmfCh = (BMFChar) ch; - for (int x = 0; x < ch.getW(); x++) { - for (int y = 0; y < ch.getH(); y++) { - int e = ch.getW() * y + x; - int index = ch.getData()[e] & 0xFF; - if (index >= 0 && index < this.palette.size()) { - Color color = this.palette.get(index); - if (!color.equals(transparentColor)) { - int px = x + bmfCh.getRelx() + ch.getOffset(); - int py = y + bmfCh.getRely(); - if (px >= 0 && px < this.totalwidth && py >= 0 && py < this.maxheight) { - image.setRGB(px, py, color.getRGB()); - } - } - } - } - } - } - return image; - } - - // generaters image displaying text, it allows typing in the BMF font (overriden default) - @Override - public BufferedImage generateImage(boolean transparency, String text) { - // 1. initializing - BufferedImage image = null; - if (text.isEmpty()) { - return null; - } - int totalwidth = 0; - int[] offsets = new int[text.length()]; - // 2. calculating - for (int i = 0; i < text.length(); i++) { - char c = text.charAt(i); - offsets[i] = totalwidth; - BMFChar ch = (BMFChar) this.giveChar(c); - if (ch != null) { - totalwidth += ch.getShift() + this.add_space; - } - } - // 3. creating image - image = new BufferedImage(totalwidth + 2, this.maxheight + 2, - transparency ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB); - // 4. writing to pixels of the image - for (int i = 0; i < text.length(); i++) { - char c = text.charAt(i); - BMFChar ch = (BMFChar) this.giveChar(c); - if (ch != null) { - for (int x = 0; x < ch.getW(); x++) { - for (int y = 0; y < ch.getH(); y++) { - int e = ch.getW() * y + x; - int index = ch.getData()[e] & 0xFF; - if (index >= 0 && index < this.palette.size()) { - Color color = this.palette.get(index); - if (!color.equals(transparentColor)) { - int px = x + ch.getRelx() + offsets[i]; - int py = y + ch.getRely(); - if (px >= 0 && px < totalwidth && py >= 0 && py < this.maxheight) { - image.setRGB(px, py, color.getRGB()); - } - } - } - } - } - } - } - return image; - } - - //-------------------------------------------------------------------------- - // C - GETTERS (TRIVIAL) - //-------------------------------------------------------------------------- - public int getVersion() { - return version; - } - - public String getInfo() { - return info; - } - - public int getLine_height() { - return line_height; - } - - public int getSize_over() { - return size_over; - } - - public int getSize_under() { - return size_under; - } - - public int getAdd_space() { - return add_space; - } - - public int getSize_inner() { - return size_inner; - } - - public int getColors() { - return colors; - } - - public int getHighest_color() { - return highest_color; - } - -} +package rs.alexanderstojanovich.dfg.fonts; + +import java.awt.Color; +import java.awt.image.BufferedImage; + +/** + * + * @author Alexander Stojanovich + */ +public class BMF extends DoomFont { + + // THIS WAS INSPIRED BY C library by Tomas Dvorak + // SO ALL CREDITS TO HIM FOR DOING SUCH A FANTASTIC JOB + // This version is always 0x11, irrelevant + private int version = 0x11; + // Some information about the font + private String info; + + // As said - line height.. + private int line_height; + // Size over the base line + private int size_over; + // Size under the base line Signed value + private int size_under; + // Space after each character, in addition to each character's own shift + private int add_space; + // Inner size (UNKNOWN PURPOSE) + private int size_inner; + // Count of used colors (not the same as palette size) + private int colors; + // Highest used color index + private int highest_color; + + // Characters of Bytemap Font + // (it's length is all the chars that can be displayed) + // and containts only the chars that can be displayed, not outside the range + //-------------------------------------------------------------------------- + // A - CONSTRUCTORS + //-------------------------------------------------------------------------- + // A1 - CONSTRUCTOR USED WHEN READING FROM THE BINARY "BMF" FILE + public BMF(byte[] buffer) { + super(buffer); + this.loadFont(); // loads font by creating char data about it + } + + // A2 - CONSTRUCTOR USED WHEN MAKING "BMF" FONT FROM PRE EXISTING INSTALLED FONT - NEW SCHOOL VARIANT + public BMF(String info, int spacing, + int line_height, int size_over, int size_under, + BufferedImage image, BMFChar[] charVector) { + super(image, charVector); + this.info = info; + this.add_space = spacing; + this.line_height = line_height; + this.size_over = size_over; + this.size_under = size_under; + this.unloadFont(); + } + + //-------------------------------------------------------------------------- + // B - IMPLEMENTED METHODS FOR INITIALIZATION + //-------------------------------------------------------------------------- + @Override + protected String initFontType() { + return "BMF"; + } + + @Override + protected Color initTransparentColor() { + return new Color(35, 0, 60); + } + + @Override + protected boolean initVerticalOffsets() { + return false; + } + + //-------------------------------------------------------------------------- + // C - PRIVATE METHODS FOR CONSTRUCTORS + //-------------------------------------------------------------------------- + // priv method for the constructor A1 (BMF) -> Buffer to Font + private void loadFont() { + if (buffer[0] == (byte) 0xE1 && buffer[1] == (byte) 0xE6 && buffer[2] == (byte) 0xD5 && buffer[3] == (byte) 0x1A) { // BMF Magic Header + // -- READING FIRST DATA AFTER THE HEADER + this.version = buffer[4] & 0xFF; // version (currently 11h) + this.line_height = buffer[5] & 0xFF; // line-height + this.size_over = buffer[6]; // size-over the base line (-128…127) + this.size_under = buffer[7]; // size-under the base line (-128…127) + this.add_space = buffer[8]; // add-space after each char (-128…127) + this.size_inner = buffer[9]; // size-inner (non-caps level) (-128…127) + this.colors = buffer[10] & 0xFF; // count of used colors (should be <= 32) + this.highest_color = buffer[11] & 0xFF; // highest used color attribute + // -- RESERVED + // -- READ NUMBER OF RGB ENTRIES (P) + int p = buffer[16] & 0xFF; // number of RGB entries (P) + // -- READ FONT PALETTE + this.pos = 17; // is position + // -- FONT PALETTE (RGB bytes, max=63) + this.palette.add(transparentColor); + for (int i = 0; i < p; i++) { + int red = buffer[pos + i * 3] & 0xFF; + int green = buffer[pos + i * 3 + 1] & 0xFF; + int blue = buffer[pos + i * 3 + 2] & 0xFF; + Color color = new Color( + Math.min(red << 2 | red >> 4, 0xFF), + Math.min(green << 2 | green >> 4, 0xFF), + Math.min(blue << 2 | blue >> 4, 0xFF) + ); + if (!color.equals(transparentColor) /*&& !this.palette.contains(color)*/ && this.palette.size() <= PAL_MAX_SIZE) { + this.palette.add(color); + } + + } + pos += 3 * p; + // -- READ INFO LENGTH + int l = buffer[pos++] & 0xFF; + // -- READ INFO STRING + char[] infoStr = new char[l]; + for (int i = 0; i < l; i++) { + infoStr[i] = (char) buffer[pos++]; + } + this.info = String.valueOf(infoStr); + // -- READING NUMBER OF CHARACTERS IN THE FONT + int numchars = ((buffer[pos + 1] & 0xFF) << 8) | (buffer[pos] & 0xFF); + pos += 2; + // -- END + // -- AND THE NEW BEGINNING + this.chars = new BMFChar[numchars]; + // -- READING CHAR ITSELF, ONE BY ONE + for (int i = 0; i < numchars; i++) { + char c = (char) buffer[pos]; + int w = buffer[pos + 1] & 0xFF; + int h = buffer[pos + 2] & 0xFF; + this.chars[i] = new BMFChar(c, w, h); + BMFChar ch = (BMFChar) this.chars[i]; + if (h > this.maxheight) { + this.maxheight = h; + } + ch.setRelx((int) buffer[pos + 3]); + ch.setRely((int) buffer[pos + 4]); + ch.setShift(buffer[pos + 5]); + ch.setOffset(this.totalwidth); + pos += 6; + this.totalwidth += w + this.add_space; + System.arraycopy(buffer, pos, ch.getData(), 0, w * h); + pos += w * h; + } + } + this.error = (this.line_height <= 0 || this.palette.isEmpty() || this.chars.length <= 0 || this.chars.length > 256); + if (this.line_height <= 0) { + this.errorMsg = "Error - Negative or zero line height!"; + } else if (this.palette.isEmpty()) { + this.errorMsg = "Error - This font has no colors!"; + } else if (this.chars.length <= 0) { + this.errorMsg = "Error - This font has no characters!"; + } else if (this.chars.length > 256) { + this.errorMsg = "Error - This font has over 256 characters!"; + } + // error is not successful + } + + // priv method for the constructor A2 (Big Font) -> Font to Buffer + private void unloadFont() { + // -- WRITING MAGIC HEADER + this.buffer[0] = (byte) 0xE1; + this.buffer[1] = (byte) 0xE6; + this.buffer[2] = (byte) 0xD5; + this.buffer[3] = (byte) 0x1A; + // -- WRITING MORE METADATA + this.buffer[4] = (byte) this.version; + this.buffer[5] = (byte) this.line_height; + this.buffer[6] = (byte) this.size_over; + this.buffer[7] = (byte) this.size_under; + this.buffer[8] = (byte) this.add_space; + this.buffer[9] = (byte) this.size_inner; + this.buffer[10] = (byte) this.colors; + this.buffer[11] = (byte) this.highest_color; + // -- WRITING ZEROS AS RESERVED - NOT IMPORTANT + this.buffer[12] = 0x00; + this.buffer[13] = 0x00; + this.buffer[14] = 0x00; + this.buffer[15] = 0x00; + // -- WRITING PALETTE SIZE + int p = Math.min(this.palette.size() - 1, 0xFF); + this.buffer[16] = (byte) p; + this.pos = 17; + // -- WRITING PALETTE ITSELF + for (int i = 1; i < this.palette.size(); i++) { + this.buffer[pos] = (byte) ((this.palette.get(i).getRed() & 0xFF) >> 2); + this.buffer[pos + 1] = (byte) ((this.palette.get(i).getGreen() & 0xFF) >> 2); + this.buffer[pos + 2] = (byte) ((this.palette.get(i).getBlue() & 0xFF) >> 2); + pos += 3; + } + // -- WRITING INFO AND ITS CONTENT + this.buffer[pos++] = (byte) this.info.length(); + for (int i = 0; i < this.info.getBytes().length; i++) { + this.buffer[pos++] = this.info.getBytes()[i]; + } + // -- WRITING NUMBER OF CHARS IN THE FONT + if (this.chars.length < 256) { + this.buffer[pos] = (byte) this.chars.length; + this.buffer[pos + 1] = 0x00; + } else { + this.buffer[pos] = 0x00; + this.buffer[pos + 1] = 0x01; + } + this.pos += 2; + // -- FOR EACH CHAR WRITE THE SPECIFICS + for (DoomFontChar ch : this.chars) { + BMFChar bmfCh = (BMFChar) ch; + this.buffer[pos] = (byte) ch.getC(); + this.buffer[pos + 1] = (byte) ch.getW(); + this.buffer[pos + 2] = (byte) ch.getH(); + this.buffer[pos + 3] = (byte) bmfCh.getRelx(); + this.buffer[pos + 4] = (byte) bmfCh.getRely(); + this.buffer[pos + 5] = (byte) bmfCh.getShift(); + this.pos += 6; + System.arraycopy(ch.getData(), 0, buffer, pos, ch.getData().length); + this.pos += ch.getData().length; + } + } + + // generates image displaying all the characters in the BMF font (overriden default) + @Override + public BufferedImage generateImage(boolean transparency) { + BufferedImage image = null; + image = new BufferedImage(this.totalwidth + 2, this.maxheight + 2, + transparency ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB); + for (DoomFontChar ch : this.chars) { + BMFChar bmfCh = (BMFChar) ch; + for (int x = 0; x < ch.getW(); x++) { + for (int y = 0; y < ch.getH(); y++) { + int e = ch.getW() * y + x; + int index = ch.getData()[e] & 0xFF; + if (index >= 0 && index < this.palette.size()) { + Color color = this.palette.get(index); + if (!color.equals(transparentColor)) { + int px = x + bmfCh.getRelx() + ch.getOffset(); + int py = y + bmfCh.getRely(); + if (px >= 0 && px < this.totalwidth && py >= 0 && py < this.maxheight) { + image.setRGB(px, py, color.getRGB()); + } + } + } + } + } + } + return image; + } + + // generaters image displaying text, it allows typing in the BMF font (overriden default) + @Override + public BufferedImage generateImage(boolean transparency, String text) { + // 1. initializing + BufferedImage image = null; + if (text.isEmpty()) { + return null; + } + int totalwidth = 0; + int[] offsets = new int[text.length()]; + // 2. calculating + for (int i = 0; i < text.length(); i++) { + char c = text.charAt(i); + offsets[i] = totalwidth; + BMFChar ch = (BMFChar) this.giveChar(c); + if (ch != null) { + totalwidth += ch.getShift() + this.add_space; + } + } + // 3. creating image + image = new BufferedImage(totalwidth + 2, this.maxheight + 2, + transparency ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB); + // 4. writing to pixels of the image + for (int i = 0; i < text.length(); i++) { + char c = text.charAt(i); + BMFChar ch = (BMFChar) this.giveChar(c); + if (ch != null) { + for (int x = 0; x < ch.getW(); x++) { + for (int y = 0; y < ch.getH(); y++) { + int e = ch.getW() * y + x; + int index = ch.getData()[e] & 0xFF; + if (index >= 0 && index < this.palette.size()) { + Color color = this.palette.get(index); + if (!color.equals(transparentColor)) { + int px = x + ch.getRelx() + offsets[i]; + int py = y + ch.getRely(); + if (px >= 0 && px < totalwidth && py >= 0 && py < this.maxheight) { + image.setRGB(px, py, color.getRGB()); + } + } + } + } + } + } + } + return image; + } + + //-------------------------------------------------------------------------- + // C - GETTERS (TRIVIAL) + //-------------------------------------------------------------------------- + public int getVersion() { + return version; + } + + public String getInfo() { + return info; + } + + public int getLine_height() { + return line_height; + } + + public int getSize_over() { + return size_over; + } + + public int getSize_under() { + return size_under; + } + + public int getAdd_space() { + return add_space; + } + + public int getSize_inner() { + return size_inner; + } + + public int getColors() { + return colors; + } + + public int getHighest_color() { + return highest_color; + } + +} diff --git a/src/rs/alexanderstojanovich/dfg/fonts/BMFChar.java b/src/rs/alexanderstojanovich/dfg/fonts/BMFChar.java index 3bc3639..b7876ef 100644 --- a/src/rs/alexanderstojanovich/dfg/fonts/BMFChar.java +++ b/src/rs/alexanderstojanovich/dfg/fonts/BMFChar.java @@ -1,5 +1,5 @@ -/* - * Copyright (C) 2019 Coa +/* + * Copyright (C) 2020 Alexander Stojanovich * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,53 +14,53 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package rs.alexanderstojanovich.dfg.fonts; - -/** - * - * @author Coa - */ -public class BMFChar extends DoomFontChar { - - // Character x and y offset relative to the corresponding cursor - private int relx, rely; - // Horizontal cursor shift after drawing the character - // (used instead of width) in addition to the global font value of space - // added after each character - private int shift; - - //-------------------------------------------------------------------------- - // A - CONSTRUCTOR - //-------------------------------------------------------------------------- - public BMFChar(char c, int w, int h) { - super(c, w, h); - } - - //-------------------------------------------------------------------------- - // B - GETTERS AND SETTERS (TRIVIAL) - //-------------------------------------------------------------------------- - public int getRelx() { - return relx; - } - - public void setRelx(int relx) { - this.relx = relx; - } - - public int getRely() { - return rely; - } - - public void setRely(int rely) { - this.rely = rely; - } - - public int getShift() { - return shift; - } - - public void setShift(int shift) { - this.shift = shift; - } - -} +package rs.alexanderstojanovich.dfg.fonts; + +/** + * + * @author Alexander Stojanovich + */ +public class BMFChar extends DoomFontChar { + + // Character x and y offset relative to the corresponding cursor + private int relx, rely; + // Horizontal cursor shift after drawing the character + // (used instead of width) in addition to the global font value of space + // added after each character + private int shift; + + //-------------------------------------------------------------------------- + // A - CONSTRUCTOR + //-------------------------------------------------------------------------- + public BMFChar(char c, int w, int h) { + super(c, w, h); + } + + //-------------------------------------------------------------------------- + // B - GETTERS AND SETTERS (TRIVIAL) + //-------------------------------------------------------------------------- + public int getRelx() { + return relx; + } + + public void setRelx(int relx) { + this.relx = relx; + } + + public int getRely() { + return rely; + } + + public void setRely(int rely) { + this.rely = rely; + } + + public int getShift() { + return shift; + } + + public void setShift(int shift) { + this.shift = shift; + } + +} diff --git a/src/rs/alexanderstojanovich/dfg/fonts/BigFont.java b/src/rs/alexanderstojanovich/dfg/fonts/BigFont.java index a883fef..9fbec03 100644 --- a/src/rs/alexanderstojanovich/dfg/fonts/BigFont.java +++ b/src/rs/alexanderstojanovich/dfg/fonts/BigFont.java @@ -1,5 +1,5 @@ -/* - * Copyright (C) 2019 Coa +/* + * Copyright (C) 2020 Alexander Stojanovich * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,232 +14,232 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package rs.alexanderstojanovich.dfg.fonts; - -import com.sun.jimi.core.util.Packbits; -import java.awt.Color; -import java.awt.image.BufferedImage; -import java.util.Arrays; - -/** - * - * @author Coa - */ -public class BigFont extends DoomFont { - - private boolean constantWidth = false; - private boolean usesKerning = false; - private int kerning = 0; - - //-------------------------------------------------------------------------- - // A - CONSTRUCTORS - //-------------------------------------------------------------------------- - // A1 - CONSTRUCTOR USED WHEN READING FROM THE BINARY FONT FILE - public BigFont(byte[] buffer) { - super(buffer); - this.loadFont(); // loads font by creating char data about it - } - - // A2 - CONSTRUCTOR USED WHEN MAKING BIG FONT FROM PRE EXISTING INSTALLED FONT - public BigFont(BufferedImage image, DoomFontChar[] charVector) { - super(image, charVector); - this.unloadFont(); // unloads font to the buffer - } - - //-------------------------------------------------------------------------- - // B - IMPLEMENTED METHODS FOR INITIALIZATION - //-------------------------------------------------------------------------- - @Override - protected String initFontType() { - return "FON2"; - } - - @Override - protected Color initTransparentColor() { - return new Color(35, 0, 60); - } - - @Override - protected boolean initVerticalOffsets() { - return false; - } - - //-------------------------------------------------------------------------- - // C - PRIVATE METHODS FOR CONSTRUCTORS - //-------------------------------------------------------------------------- - // priv method for the constructor A1 (Big Font) -> Buffer to Font - private void loadFont() { - if (buffer[0] == 'F' && buffer[1] == 'O' && buffer[2] == 'N' && buffer[3] == '2') { // The characters 'F', 'O', 'N', and '2'. - // -- READING FIRST DATA AFTER THE HEADER - // Technically used as a boolean, since there is only one flag. - // 1 if the font kerning information, 0 otherwise. - this.maxheight = (buffer[5] & 0xFF) << 8 | (buffer[4] & 0xFF); // Unsigned value for character height. - int firstChar = buffer[6] & 0xFF; // ASCII number for the first character described in the font. - int lastChar = buffer[7] & 0xFF; // ASCII number for the last character described in the font. - int numchars = lastChar - firstChar + 1; - this.constantWidth = (buffer[8] != 0x00); // 1 if the characters are of constant width, 0 otherwise. - int p = buffer[10] & 0xFF; // Amount of active colors in the palette. - // The true palette size is one greater, - // as the last palette entry is for the inactive color. - p++; - this.usesKerning = (buffer[11] != 0x00); - // -- READ POSSIBLE KERNING VALUE - this.pos = 12; - if (this.usesKerning) { - this.kerning = ((buffer[pos + 1] & 0xFF) << 8) | (buffer[pos] & 0xFF); - pos += 2; - } - // -- READ CHARACTER WIDTHS - int[] chWidths = new int[(this.constantWidth) ? 1 : numchars]; - - for (int i = 0; i < chWidths.length; i++) { - chWidths[i] = ((buffer[pos + 1] & 0xFF) << 8) | (buffer[pos] & 0xFF); - pos += 2; - } - // -- READ FONT PALETTE - for (int i = 0; i < p; i++) { - int red = buffer[pos + i * 3] & 0xFF; - int green = buffer[pos + i * 3 + 1] & 0xFF; - int blue = buffer[pos + i * 3 + 2] & 0xFF; - Color color = new Color(red, green, blue); - if (!this.palette.contains(color) && this.palette.size() <= PAL_MAX_SIZE) { - this.palette.add(color); - } - - } - this.transparentColor = this.palette.get(0); - pos += 3 * p; - // -- END - // -- AND THE NEW BEGINNING - this.chars = new DoomFontChar[numchars]; - // -- READING CHAR ITSELF, ONE BY ONE - for (int i = 0; i < numchars; i++) { - char c = (char) (firstChar + i); - int w = chWidths[i]; - if (w > 0) { - int h = maxheight; - int numpixels = w * h; - this.chars[i] = new DoomFontChar(c, w, h); - int e = 0; // encoded data index - int len = 0; // length - byte code; - while (numpixels > 0) { // THANKS SLADE FOR HELP! - code = buffer[pos++]; - if (code > 0) { - len = code + 1; - // Overflows shouldn't happen! - if (len > numpixels) { - this.chars = null; - this.error = true; - this.errorMsg = "Error - Character Overflow!"; - return; - } - System.arraycopy(buffer, pos, this.chars[i].getData(), e, len); - e += len; - pos += len; - numpixels -= len; - } else if (code != -128) { - len = (-code) + 1; - // Overflows shouldn't happen! - if (len > numpixels) { - this.chars = null; - this.error = true; - this.errorMsg = "Error - Character Overflow!"; - return; - } - code = buffer[pos++]; - Arrays.fill(this.chars[i].getData(), e, e + len, code); - e += len; - numpixels -= len; - } - } - this.chars[i].setOffset(totalwidth); - this.totalwidth += w; - } - } - } - this.error = (this.maxheight <= 0 || this.palette.isEmpty() || this.chars.length <= 0 || this.chars.length > 256); - if (this.maxheight <= 0) { - this.errorMsg = "Error - Negative or zero font height!"; - } else if (this.palette.isEmpty()) { - this.errorMsg = "Error - This font has no colors!"; - } else if (this.chars.length <= 0) { - this.errorMsg = "Error - This font has no characters!"; - } else if (this.chars.length > 256) { - this.errorMsg = "Error - This font has over 256 characters!"; - } - // error is not successful - } - - // priv method for the constructor A2 (Big Font) -> Font to Buffer - private void unloadFont() { - // -- WRITING HEADER OF THE BIG FONT - this.buffer[0] = 'F'; // The characters 'F', 'O', 'N', and '2'. - this.buffer[1] = 'O'; - this.buffer[2] = 'N'; - this.buffer[3] = '2'; - this.buffer[4] = (byte) (this.maxheight & 0xFF); // Unsigned value for character height. - this.buffer[5] = (byte) ((this.maxheight >> 8) & 0xFF); // In Little-endian format - this.buffer[6] = (byte) (this.chars[0].getC()); // ASCII number for the first character described in the font. - this.buffer[7] = (byte) (this.chars[this.chars.length - 1].getC()); // ASCII number for the last character described in the font. - this.buffer[8] = (byte) (this.constantWidth ? 0x01 : 0x00); // Constant width : 1 if the characters are of constant width, 0 otherwise. - this.buffer[9] = (byte) 0x00; // Shading type : Seems unused. - // -- WRITING PALETTE SIZE - // Palette size : Amount of active colors in the palette. The true palette size is one greater, - // as the last palette entry is for the inactive color. - int p = Math.min(this.palette.size() - 1, 0xFF); - this.buffer[10] = (byte) p; - - this.buffer[11] = (byte) (this.usesKerning ? 0x01 : 0x00); // Whether or not this font uses kerning - this.pos = 12; - if (this.usesKerning) { // if there is kerning information, - this.buffer[pos] = (byte) (this.kerning & 0xFF); // the header is followed by a signed two-byte value for it. - this.buffer[pos + 1] = (byte) ((this.kerning >> 8) & 0xFF); - pos += 2; - } - if (this.constantWidth && this.chars != null) { - buffer[pos] = (byte) (this.chars[0].getW() & 0xFF); - buffer[pos + 1] = (byte) ((this.chars[0].getW() >> 8) & 0xFF); - pos += 2; - } else { - for (DoomFontChar ch : this.chars) { - buffer[pos] = (byte) (ch.getW() & 0xFF); - buffer[pos + 1] = (byte) ((ch.getW() >> 8) & 0xFF); - pos += 2; - } - } - // -- WRITING PALETTE ITSELF - for (int i = 0; i < this.palette.size(); i++) { - buffer[pos] = (byte) (this.palette.get(i).getRed() & 0xFF); - buffer[pos + 1] = (byte) (this.palette.get(i).getGreen() & 0xFF); - buffer[pos + 2] = (byte) (this.palette.get(i).getBlue() & 0xFF); - pos += 3; - } - // -- LOOPING THROUGH ALL THE CHARS -- COMPRESSING DATA -- FINALIZING - for (DoomFontChar ch : this.chars) { - if (ch != null) { - byte[] temp = new byte[ch.getData().length]; - // compress the char data and return the compressed size (len) - int len = Packbits.packbits(ch.getData(), temp); - System.arraycopy(temp, 0, buffer, pos, len); - this.pos += len; - } - } - } - //-------------------------------------------------------------------------- - // D - GETTERS - //-------------------------------------------------------------------------- - - public boolean isConstantWidth() { - return constantWidth; - } - - public boolean isUsesKerning() { - return usesKerning; - } - - public int getKerning() { - return kerning; - } - -} +package rs.alexanderstojanovich.dfg.fonts; + +import com.sun.jimi.core.util.Packbits; +import java.awt.Color; +import java.awt.image.BufferedImage; +import java.util.Arrays; + +/** + * + * @author Alexander Stojanovich + */ +public class BigFont extends DoomFont { + + private boolean constantWidth = false; + private boolean usesKerning = false; + private int kerning = 0; + + //-------------------------------------------------------------------------- + // A - CONSTRUCTORS + //-------------------------------------------------------------------------- + // A1 - CONSTRUCTOR USED WHEN READING FROM THE BINARY FONT FILE + public BigFont(byte[] buffer) { + super(buffer); + this.loadFont(); // loads font by creating char data about it + } + + // A2 - CONSTRUCTOR USED WHEN MAKING BIG FONT FROM PRE EXISTING INSTALLED FONT + public BigFont(BufferedImage image, DoomFontChar[] charVector) { + super(image, charVector); + this.unloadFont(); // unloads font to the buffer + } + + //-------------------------------------------------------------------------- + // B - IMPLEMENTED METHODS FOR INITIALIZATION + //-------------------------------------------------------------------------- + @Override + protected String initFontType() { + return "FON2"; + } + + @Override + protected Color initTransparentColor() { + return new Color(35, 0, 60); + } + + @Override + protected boolean initVerticalOffsets() { + return false; + } + + //-------------------------------------------------------------------------- + // C - PRIVATE METHODS FOR CONSTRUCTORS + //-------------------------------------------------------------------------- + // priv method for the constructor A1 (Big Font) -> Buffer to Font + private void loadFont() { + if (buffer[0] == 'F' && buffer[1] == 'O' && buffer[2] == 'N' && buffer[3] == '2') { // The characters 'F', 'O', 'N', and '2'. + // -- READING FIRST DATA AFTER THE HEADER + // Technically used as a boolean, since there is only one flag. + // 1 if the font kerning information, 0 otherwise. + this.maxheight = (buffer[5] & 0xFF) << 8 | (buffer[4] & 0xFF); // Unsigned value for character height. + int firstChar = buffer[6] & 0xFF; // ASCII number for the first character described in the font. + int lastChar = buffer[7] & 0xFF; // ASCII number for the last character described in the font. + int numchars = lastChar - firstChar + 1; + this.constantWidth = (buffer[8] != 0x00); // 1 if the characters are of constant width, 0 otherwise. + int p = buffer[10] & 0xFF; // Amount of active colors in the palette. + // The true palette size is one greater, + // as the last palette entry is for the inactive color. + p++; + this.usesKerning = (buffer[11] != 0x00); + // -- READ POSSIBLE KERNING VALUE + this.pos = 12; + if (this.usesKerning) { + this.kerning = ((buffer[pos + 1] & 0xFF) << 8) | (buffer[pos] & 0xFF); + pos += 2; + } + // -- READ CHARACTER WIDTHS + int[] chWidths = new int[(this.constantWidth) ? 1 : numchars]; + + for (int i = 0; i < chWidths.length; i++) { + chWidths[i] = ((buffer[pos + 1] & 0xFF) << 8) | (buffer[pos] & 0xFF); + pos += 2; + } + // -- READ FONT PALETTE + for (int i = 0; i < p; i++) { + int red = buffer[pos + i * 3] & 0xFF; + int green = buffer[pos + i * 3 + 1] & 0xFF; + int blue = buffer[pos + i * 3 + 2] & 0xFF; + Color color = new Color(red, green, blue); + if (!this.palette.contains(color) && this.palette.size() <= PAL_MAX_SIZE) { + this.palette.add(color); + } + + } + this.transparentColor = this.palette.get(0); + pos += 3 * p; + // -- END + // -- AND THE NEW BEGINNING + this.chars = new DoomFontChar[numchars]; + // -- READING CHAR ITSELF, ONE BY ONE + for (int i = 0; i < numchars; i++) { + char c = (char) (firstChar + i); + int w = chWidths[i]; + if (w > 0) { + int h = maxheight; + int numpixels = w * h; + this.chars[i] = new DoomFontChar(c, w, h); + int e = 0; // encoded data index + int len = 0; // length + byte code; + while (numpixels > 0) { // THANKS SLADE FOR HELP! + code = buffer[pos++]; + if (code > 0) { + len = code + 1; + // Overflows shouldn't happen! + if (len > numpixels) { + this.chars = null; + this.error = true; + this.errorMsg = "Error - Character Overflow!"; + return; + } + System.arraycopy(buffer, pos, this.chars[i].getData(), e, len); + e += len; + pos += len; + numpixels -= len; + } else if (code != -128) { + len = (-code) + 1; + // Overflows shouldn't happen! + if (len > numpixels) { + this.chars = null; + this.error = true; + this.errorMsg = "Error - Character Overflow!"; + return; + } + code = buffer[pos++]; + Arrays.fill(this.chars[i].getData(), e, e + len, code); + e += len; + numpixels -= len; + } + } + this.chars[i].setOffset(totalwidth); + this.totalwidth += w; + } + } + } + this.error = (this.maxheight <= 0 || this.palette.isEmpty() || this.chars.length <= 0 || this.chars.length > 256); + if (this.maxheight <= 0) { + this.errorMsg = "Error - Negative or zero font height!"; + } else if (this.palette.isEmpty()) { + this.errorMsg = "Error - This font has no colors!"; + } else if (this.chars.length <= 0) { + this.errorMsg = "Error - This font has no characters!"; + } else if (this.chars.length > 256) { + this.errorMsg = "Error - This font has over 256 characters!"; + } + // error is not successful + } + + // priv method for the constructor A2 (Big Font) -> Font to Buffer + private void unloadFont() { + // -- WRITING HEADER OF THE BIG FONT + this.buffer[0] = 'F'; // The characters 'F', 'O', 'N', and '2'. + this.buffer[1] = 'O'; + this.buffer[2] = 'N'; + this.buffer[3] = '2'; + this.buffer[4] = (byte) (this.maxheight & 0xFF); // Unsigned value for character height. + this.buffer[5] = (byte) ((this.maxheight >> 8) & 0xFF); // In Little-endian format + this.buffer[6] = (byte) (this.chars[0].getC()); // ASCII number for the first character described in the font. + this.buffer[7] = (byte) (this.chars[this.chars.length - 1].getC()); // ASCII number for the last character described in the font. + this.buffer[8] = (byte) (this.constantWidth ? 0x01 : 0x00); // Constant width : 1 if the characters are of constant width, 0 otherwise. + this.buffer[9] = (byte) 0x00; // Shading type : Seems unused. + // -- WRITING PALETTE SIZE + // Palette size : Amount of active colors in the palette. The true palette size is one greater, + // as the last palette entry is for the inactive color. + int p = Math.min(this.palette.size() - 1, 0xFF); + this.buffer[10] = (byte) p; + + this.buffer[11] = (byte) (this.usesKerning ? 0x01 : 0x00); // Whether or not this font uses kerning + this.pos = 12; + if (this.usesKerning) { // if there is kerning information, + this.buffer[pos] = (byte) (this.kerning & 0xFF); // the header is followed by a signed two-byte value for it. + this.buffer[pos + 1] = (byte) ((this.kerning >> 8) & 0xFF); + pos += 2; + } + if (this.constantWidth && this.chars != null) { + buffer[pos] = (byte) (this.chars[0].getW() & 0xFF); + buffer[pos + 1] = (byte) ((this.chars[0].getW() >> 8) & 0xFF); + pos += 2; + } else { + for (DoomFontChar ch : this.chars) { + buffer[pos] = (byte) (ch.getW() & 0xFF); + buffer[pos + 1] = (byte) ((ch.getW() >> 8) & 0xFF); + pos += 2; + } + } + // -- WRITING PALETTE ITSELF + for (int i = 0; i < this.palette.size(); i++) { + buffer[pos] = (byte) (this.palette.get(i).getRed() & 0xFF); + buffer[pos + 1] = (byte) (this.palette.get(i).getGreen() & 0xFF); + buffer[pos + 2] = (byte) (this.palette.get(i).getBlue() & 0xFF); + pos += 3; + } + // -- LOOPING THROUGH ALL THE CHARS -- COMPRESSING DATA -- FINALIZING + for (DoomFontChar ch : this.chars) { + if (ch != null) { + byte[] temp = new byte[ch.getData().length]; + // compress the char data and return the compressed size (len) + int len = Packbits.packbits(ch.getData(), temp); + System.arraycopy(temp, 0, buffer, pos, len); + this.pos += len; + } + } + } + //-------------------------------------------------------------------------- + // D - GETTERS + //-------------------------------------------------------------------------- + + public boolean isConstantWidth() { + return constantWidth; + } + + public boolean isUsesKerning() { + return usesKerning; + } + + public int getKerning() { + return kerning; + } + +} diff --git a/src/rs/alexanderstojanovich/dfg/fonts/ConsoleFont.java b/src/rs/alexanderstojanovich/dfg/fonts/ConsoleFont.java index b38241e..bdd5b72 100644 --- a/src/rs/alexanderstojanovich/dfg/fonts/ConsoleFont.java +++ b/src/rs/alexanderstojanovich/dfg/fonts/ConsoleFont.java @@ -1,5 +1,5 @@ -/* - * Copyright (C) 2019 Coa +/* + * Copyright (C) 2020 Alexander Stojanovich * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,163 +14,163 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package rs.alexanderstojanovich.dfg.fonts; - -import com.sun.jimi.core.util.Packbits; -import java.awt.Color; -import java.awt.image.BufferedImage; -import java.util.Arrays; - -/** - * - * @author Coa - */ -public class ConsoleFont extends DoomFont { - - private static final float LUMA_RED_COEFF = 0.2126f; - private static final float LUMA_GREEN_COEFF = 0.7152f; - private static final float LUMA_BLUE_COEFF = 0.0722f; - - //-------------------------------------------------------------------------- - // A - CONSTRUCTORS - //-------------------------------------------------------------------------- - // A1 - CONSTRUCTOR USED WHEN READING FROM THE BINARY FONT FILE - public ConsoleFont(byte[] buffer) { - super(buffer); - this.loadFont(); - } - - // A2 - CONSTRUCTOR USED WHEN MAKING "BMF" FONT FROM PRE EXISTING INSTALLED FONT - public ConsoleFont(BufferedImage image, DoomFontChar[] charVector) { - super(image, charVector); - this.unloadFont(); - } - - //-------------------------------------------------------------------------- - // B - IMPLEMENTED METHODS FOR INITIALIZATION - //-------------------------------------------------------------------------- - @Override - protected String initFontType() { - return "FON1"; - } - - @Override - protected Color initTransparentColor() { - return Color.BLACK; - } - - @Override - protected boolean initVerticalOffsets() { - return true; - } - - //-------------------------------------------------------------------------- - // C - PRIVATE METHODS FOR CONSTRUCTORS - //-------------------------------------------------------------------------- - // priv method for the constructor A1 (Console Font) -> Buffer to Font - private void loadFont() { - if (buffer[0] == 'F' && buffer[1] == 'O' && buffer[2] == 'N' && buffer[3] == '1') { // The characters 'F', 'O', 'N', and '1'. - // -- READING FIRST DATA AFTER THE HEADER - // Technically used as a boolean, since there is only one flag. - // 1 if the font kerning information, 0 otherwise. - int w = (buffer[5] & 0xFF) << 8 | (buffer[4] & 0xFF); // Unsigned value for character width. - int h = (buffer[7] & 0xFF) << 8 | (buffer[6] & 0xFF); // Unsigned value for character height. - this.totalwidth = w; - this.pos = 8; - this.chars = new DoomFontChar[256]; - for (int i = 0; i < this.chars.length; i++) { - int numpixels = w * h; - byte[] temp = new byte[numpixels]; // temp data - int e = 0; // encoded data index - int len = 0; // length - byte code; - while (numpixels > 0) { // THANKS SLADE FOR HELP! - code = buffer[pos++]; - if (code > 0) { - len = code + 1; - // Overflows shouldn't happen! - if (len > numpixels) { - this.chars = null; - this.error = true; - this.errorMsg = "Error - Character Overflow!"; - return; - } - System.arraycopy(buffer, pos, temp, e, len); - e += len; - pos += len; - numpixels -= len; - } else if (code != -128) { - len = (-code) + 1; - // Overflows shouldn't happen! - if (len > numpixels) { - this.chars = null; - this.error = true; - this.errorMsg = "Error - Character Overflow!"; - return; - } - code = buffer[pos++]; - Arrays.fill(temp, e, e + len, code); - e += len; - numpixels -= len; - } - } - - this.chars[i] = new DoomFontChar((char) i, w, h); - this.chars[i].setOffset(this.maxheight); - this.maxheight += this.chars[i].getH(); - - // Converting grayscale temp info to indexed color info of char data - for (int j = 0; j < temp.length; j++) { - Color color = new Color(temp[j] & 0xFF, temp[j] & 0xFF, temp[j] & 0xFF); - if (!this.palette.contains(color)) { - this.palette.add(color); - } - if (!color.equals(transparentColor)) { - this.chars[i].getData()[j] = (byte) palette.indexOf(color); - } - } - - } - this.error = (this.maxheight <= 0 || this.palette.isEmpty() || this.chars.length <= 0 || this.chars.length > 256); - if (this.maxheight <= 0) { - this.errorMsg = "Error - Negative or zero font height!"; - } else if (this.palette.isEmpty()) { - this.errorMsg = "Error - This font has no colors!"; - } else if (this.chars.length <= 0) { - this.errorMsg = "Error - This font has no characters!"; - } else if (this.chars.length > 256) { - this.errorMsg = "Error - This font has over 256 characters!"; - } - } - } - - // priv method for the constructor A2 (Console Font) -> Font to Buffer - private void unloadFont() { - // -- WRITING HEADER OF THE BIG FONT - this.buffer[0] = 'F'; // The characters 'F', 'O', 'N', and '1'. - this.buffer[1] = 'O'; - this.buffer[2] = 'N'; - this.buffer[3] = '1'; - this.buffer[4] = (byte) (this.chars[0].getW() & 0xFF); // Unsigned value for character height. - this.buffer[5] = (byte) (((this.chars[0].getW()) >> 8) & 0xFF); // In Little-endian format - this.buffer[6] = (byte) (this.chars[0].getH() & 0xFF); // Unsigned value for character width. - this.buffer[7] = (byte) ((this.chars[0].getH() >> 8) & 0xFF); // In Little-endian format. - this.pos = 8; - // -- LOOPING THROUGH ALL THE CHARS -- COMPRESSING DATA -- FINALIZING - for (DoomFontChar ch : this.chars) { - if (ch != null) { - byte[] grayscale = new byte[ch.getData().length]; - for (int i = 0; i < ch.getData().length; i++) { - int index = ch.getData()[i] & 0xFF; - Color col = this.palette.get(index); - grayscale[i] = (byte) (col.getRed() * LUMA_RED_COEFF + col.getGreen() * LUMA_GREEN_COEFF + col.getBlue() * LUMA_BLUE_COEFF); - } - byte[] temp = new byte[grayscale.length]; - // compress the char data and return the compressed size (len) - int len = Packbits.packbits(grayscale, temp); - System.arraycopy(temp, 0, buffer, pos, len); - this.pos += len; - } - } - } -} +package rs.alexanderstojanovich.dfg.fonts; + +import com.sun.jimi.core.util.Packbits; +import java.awt.Color; +import java.awt.image.BufferedImage; +import java.util.Arrays; + +/** + * + * @author Alexander Stojanovich + */ +public class ConsoleFont extends DoomFont { + + private static final float LUMA_RED_COEFF = 0.2126f; + private static final float LUMA_GREEN_COEFF = 0.7152f; + private static final float LUMA_BLUE_COEFF = 0.0722f; + + //-------------------------------------------------------------------------- + // A - CONSTRUCTORS + //-------------------------------------------------------------------------- + // A1 - CONSTRUCTOR USED WHEN READING FROM THE BINARY FONT FILE + public ConsoleFont(byte[] buffer) { + super(buffer); + this.loadFont(); + } + + // A2 - CONSTRUCTOR USED WHEN MAKING "BMF" FONT FROM PRE EXISTING INSTALLED FONT + public ConsoleFont(BufferedImage image, DoomFontChar[] charVector) { + super(image, charVector); + this.unloadFont(); + } + + //-------------------------------------------------------------------------- + // B - IMPLEMENTED METHODS FOR INITIALIZATION + //-------------------------------------------------------------------------- + @Override + protected String initFontType() { + return "FON1"; + } + + @Override + protected Color initTransparentColor() { + return Color.BLACK; + } + + @Override + protected boolean initVerticalOffsets() { + return true; + } + + //-------------------------------------------------------------------------- + // C - PRIVATE METHODS FOR CONSTRUCTORS + //-------------------------------------------------------------------------- + // priv method for the constructor A1 (Console Font) -> Buffer to Font + private void loadFont() { + if (buffer[0] == 'F' && buffer[1] == 'O' && buffer[2] == 'N' && buffer[3] == '1') { // The characters 'F', 'O', 'N', and '1'. + // -- READING FIRST DATA AFTER THE HEADER + // Technically used as a boolean, since there is only one flag. + // 1 if the font kerning information, 0 otherwise. + int w = (buffer[5] & 0xFF) << 8 | (buffer[4] & 0xFF); // Unsigned value for character width. + int h = (buffer[7] & 0xFF) << 8 | (buffer[6] & 0xFF); // Unsigned value for character height. + this.totalwidth = w; + this.pos = 8; + this.chars = new DoomFontChar[256]; + for (int i = 0; i < this.chars.length; i++) { + int numpixels = w * h; + byte[] temp = new byte[numpixels]; // temp data + int e = 0; // encoded data index + int len = 0; // length + byte code; + while (numpixels > 0) { // THANKS SLADE FOR HELP! + code = buffer[pos++]; + if (code > 0) { + len = code + 1; + // Overflows shouldn't happen! + if (len > numpixels) { + this.chars = null; + this.error = true; + this.errorMsg = "Error - Character Overflow!"; + return; + } + System.arraycopy(buffer, pos, temp, e, len); + e += len; + pos += len; + numpixels -= len; + } else if (code != -128) { + len = (-code) + 1; + // Overflows shouldn't happen! + if (len > numpixels) { + this.chars = null; + this.error = true; + this.errorMsg = "Error - Character Overflow!"; + return; + } + code = buffer[pos++]; + Arrays.fill(temp, e, e + len, code); + e += len; + numpixels -= len; + } + } + + this.chars[i] = new DoomFontChar((char) i, w, h); + this.chars[i].setOffset(this.maxheight); + this.maxheight += this.chars[i].getH(); + + // Converting grayscale temp info to indexed color info of char data + for (int j = 0; j < temp.length; j++) { + Color color = new Color(temp[j] & 0xFF, temp[j] & 0xFF, temp[j] & 0xFF); + if (!this.palette.contains(color)) { + this.palette.add(color); + } + if (!color.equals(transparentColor)) { + this.chars[i].getData()[j] = (byte) palette.indexOf(color); + } + } + + } + this.error = (this.maxheight <= 0 || this.palette.isEmpty() || this.chars.length <= 0 || this.chars.length > 256); + if (this.maxheight <= 0) { + this.errorMsg = "Error - Negative or zero font height!"; + } else if (this.palette.isEmpty()) { + this.errorMsg = "Error - This font has no colors!"; + } else if (this.chars.length <= 0) { + this.errorMsg = "Error - This font has no characters!"; + } else if (this.chars.length > 256) { + this.errorMsg = "Error - This font has over 256 characters!"; + } + } + } + + // priv method for the constructor A2 (Console Font) -> Font to Buffer + private void unloadFont() { + // -- WRITING HEADER OF THE BIG FONT + this.buffer[0] = 'F'; // The characters 'F', 'O', 'N', and '1'. + this.buffer[1] = 'O'; + this.buffer[2] = 'N'; + this.buffer[3] = '1'; + this.buffer[4] = (byte) (this.chars[0].getW() & 0xFF); // Unsigned value for character height. + this.buffer[5] = (byte) (((this.chars[0].getW()) >> 8) & 0xFF); // In Little-endian format + this.buffer[6] = (byte) (this.chars[0].getH() & 0xFF); // Unsigned value for character width. + this.buffer[7] = (byte) ((this.chars[0].getH() >> 8) & 0xFF); // In Little-endian format. + this.pos = 8; + // -- LOOPING THROUGH ALL THE CHARS -- COMPRESSING DATA -- FINALIZING + for (DoomFontChar ch : this.chars) { + if (ch != null) { + byte[] grayscale = new byte[ch.getData().length]; + for (int i = 0; i < ch.getData().length; i++) { + int index = ch.getData()[i] & 0xFF; + Color col = this.palette.get(index); + grayscale[i] = (byte) (col.getRed() * LUMA_RED_COEFF + col.getGreen() * LUMA_GREEN_COEFF + col.getBlue() * LUMA_BLUE_COEFF); + } + byte[] temp = new byte[grayscale.length]; + // compress the char data and return the compressed size (len) + int len = Packbits.packbits(grayscale, temp); + System.arraycopy(temp, 0, buffer, pos, len); + this.pos += len; + } + } + } +} diff --git a/src/rs/alexanderstojanovich/dfg/fonts/DoomFont.java b/src/rs/alexanderstojanovich/dfg/fonts/DoomFont.java index 59d3b2a..b040aa3 100644 --- a/src/rs/alexanderstojanovich/dfg/fonts/DoomFont.java +++ b/src/rs/alexanderstojanovich/dfg/fonts/DoomFont.java @@ -1,5 +1,5 @@ -/* - * Copyright (C) 2019 Coa +/* + * Copyright (C) 2020 Alexander Stojanovich * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,369 +14,369 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package rs.alexanderstojanovich.dfg.fonts; - -import java.awt.Color; -import java.awt.image.BufferedImage; -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.Comparator; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author Coa - */ -public abstract class DoomFont { // Doom Font is an abstract class, we don't know much about it - - // Font type in set {FON1, FON2, BMF} - protected final String type = initFontType(); - - // Doom color comparator (taken from ZDoom) - protected static Comparator doomComp = new Comparator() { - @Override - public int compare(Color o1, Color o2) { - return (299 * (o1.getRed() - o2.getRed()) + 587 * (o1.getGreen() - o2.getGreen()) + 114 * (o1.getBlue() - o2.getBlue())); - } - }; - - // Array List of RGB entries - palette of colors - protected ArrayList palette = new ArrayList(); - - // Index 0 is transparent and not described, - // Index 1 is where the whole thing with palette starts, - // There's a reason though we cannot allow palettes larger than 256. - public static final int PAL_MAX_SIZE = 255; - - // Setting this color is important (it can be either Color.BLACK or new Color(35, 0, 60) or something else..) - protected Color transparentColor = initTransparentColor(); - - // Setting this color is also important (it must be added as the last color) - // for some things to work - protected static final Color UNUSED_COLOR = new Color(167, 107, 107); - - // Characters of the Doom Font - // (it's length is all the chars that can be displayed) - // and containts only the chars that can be displayed, not outside the range - protected DoomFontChar[] chars; - - // Values for display - protected int totalwidth = 0; - protected int maxheight = 0; - - protected byte[] buffer = new byte[65536]; // buffer is 64K size - protected int pos = 0; // position in the buffer; - - // Are offsets vertical (for BigFont and BMF offsets are horizontal - // but for ConsoleFont offsets are vertical), requires initialisation - protected boolean verticalOffsets = initVerticalOffsets(); - - // Helping the user figure out where error occurred - protected boolean error = false; - protected String errorMsg; - - //-------------------------------------------------------------------------- - // A - CONSTRUCTORS - //-------------------------------------------------------------------------- - // A1 - CONSTRUCTOR USED WHEN READING FROM THE BINARY FONT FILE - protected DoomFont(byte[] buffer) { - this.buffer = buffer; - } - - // A2 - CONSTRUCTOR USED WHEN MAKING DOOM FONT FROM PRE EXISTING INSTALLED FONT - protected DoomFont(BufferedImage image, DoomFontChar[] charVector) { - this.chars = charVector; - if (image != null && charVector != null) { - this.palette.add(transparentColor); - for (DoomFontChar ch : charVector) { - for (int x = 0; x < ch.getW(); x++) { - for (int y = 0; y < ch.getH(); y++) { - int e = ch.getW() * y + x; - int px, py; - px = x + ch.getOffset(); - py = y; - if (px >= 0 && px < image.getWidth() && py >= 0 && py < image.getHeight()) { - Color color = new Color(image.getRGB(px, py)); - if (!color.equals(Color.BLACK) && !color.equals(transparentColor)) { - if (palette.size() < PAL_MAX_SIZE) { // if pallete size is less then MAX ALLOWED SIZE - if (!palette.contains(color)) { // if it doesn't contain color - palette.add(color); // add the color - } - ch.getData()[e] = (byte) (Math.min(palette.indexOf(color), 0xFF)); - } else if (palette.size() == PAL_MAX_SIZE) { // if pallete is MAXED OUT, use approximation - if (!palette.contains(color)) { - int mindeviation = 255000; - int mindevindex = -1; - for (Color palColor : palette) { // by finding color with minimal absolute deviation - int deviation = Math.abs(doomComp.compare(color, palColor)); - if (deviation < mindeviation) { - mindeviation = deviation; - mindevindex = palette.indexOf(palColor); - } - } - if (mindevindex != -1) { // and parsing that color index into the character data - ch.getData()[e] = (byte) (Math.min(mindevindex, 0xFF)); - } - } else { - ch.getData()[e] = (byte) (Math.min(palette.indexOf(color), 0xFF)); - } - } - } - } - } - } - - this.totalwidth += ch.getW(); - if (ch.getH() > this.maxheight) { - this.maxheight = ch.getH(); - } - } - this.palette.add(UNUSED_COLOR); // TRICK FOR ZDOOM :) - cuz one-color palette won't work - } - } - - //-------------------------------------------------------------------------- - // B - ABSTRACT INITIALIZATION METHODS - //-------------------------------------------------------------------------- - // alright we need to initialise the font type, which we don't know beforehand - protected abstract String initFontType(); - - // we need to init this transparent color which is different for the fonts - protected abstract Color initTransparentColor(); - - // initialise vertical offsets, which can be vertical (console font) or horizontal (big font and bmf) - protected abstract boolean initVerticalOffsets(); - - //-------------------------------------------------------------------------- - // C - CORE METHODS - //-------------------------------------------------------------------------- - // search all the characters in the font and returns one which holds the value same as the key - protected DoomFontChar giveChar(char key) { - for (DoomFontChar ch : this.chars) { - if (ch != null && ch.getC() == key) { - return ch; - } - } - return null; - } - - // generates image displaying all the characters in the font - public BufferedImage generateImage(boolean transparency) { - BufferedImage image = null; - image = new BufferedImage(this.totalwidth + 2, this.maxheight + 2, - transparency ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB); - for (DoomFontChar ch : this.chars) { - if (ch != null) { - for (int x = 0; x < ch.getW(); x++) { - for (int y = 0; y < ch.getH(); y++) { - int e = ch.getW() * y + x; - int index = ch.getData()[e] & 0xFF; - if (index >= 0 && index < this.palette.size()) { - Color color = this.palette.get(index); - if (!color.equals(transparentColor)) { - int px, py; - if (this.verticalOffsets) { - px = x; - py = y + ch.getOffset(); - } else { - px = x + ch.getOffset(); - py = y; - } - if (px >= 0 && px < this.totalwidth && py >= 0 && py < this.maxheight) { - image.setRGB(px, py, color.getRGB()); - } - } - } - } - } - } - } - return image; - } - - // generaters image displaying text, it allows typing in the font - public BufferedImage generateImage(boolean transparency, String text) { - // 1. initializing - BufferedImage image = null; - if (text.isEmpty()) { - return null; - } - int totalwidth = 0; - int[] offsets = new int[text.length()]; - // 2. calculating - for (int i = 0; i < text.length(); i++) { - char c = text.charAt(i); - offsets[i] = totalwidth; - DoomFontChar ch = this.giveChar(c); - if (ch != null) { - totalwidth += ch.getW(); - } - } - int maxheight = (this.verticalOffsets) ? this.chars[0].getH() : this.maxheight; - // 3. creating image - image = new BufferedImage(totalwidth + 2, maxheight + 2, - transparency ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB); - // 4. writing to pixels of the image - for (int i = 0; i < text.length(); i++) { - char c = text.charAt(i); - DoomFontChar ch = this.giveChar(c); - if (ch != null) { - for (int x = 0; x < ch.getW(); x++) { - for (int y = 0; y < ch.getH(); y++) { - int e = ch.getW() * y + x; - int index = ch.getData()[e] & 0xFF; - if (index >= 0 && index < this.palette.size()) { - Color color = this.palette.get(index); - if (!color.equals(transparentColor)) { - int px, py; - px = x + offsets[i]; - py = y; - if (px >= 0 && px < totalwidth && py >= 0 && py < this.maxheight) { - image.setRGB(px, py, color.getRGB()); - } - } - } - } - } - } - } - return image; - } - - // saves the font to the file - public boolean saveToFile(File file) { - boolean success = false; - if (file.exists()) { // if file exists delete it - file.delete(); - } // if someone chosen to save file without extension add proper extension - if (!file.getName().contains(".lmp") && !file.getName().contains(".bmf")) { - switch (this.type) { - case "FON1": - file = new File(file.getAbsolutePath() + ".lmp"); - break; - case "FON2": - file = new File(file.getAbsolutePath() + ".lmp"); - break; - case "BMF": - file = new File(file.getAbsolutePath() + ".bmf"); - break; - } - } - FileOutputStream fos = null; - try { - fos = new FileOutputStream(file); - fos.write(this.buffer, 0, this.pos); // write buffer to the targeted file - success = true; - } catch (FileNotFoundException ex) { - Logger.getLogger(DoomFont.class.getName()).log(Level.SEVERE, null, ex); - } catch (IOException ex) { - Logger.getLogger(DoomFont.class.getName()).log(Level.SEVERE, null, ex); - } finally { - if (fos != null) { - try { - fos.close(); - } catch (IOException ex) { - Logger.getLogger(DoomFont.class.getName()).log(Level.SEVERE, null, ex); - } - } - } - return success; - } - - //-------------------------------------------------------------------------- - // D - STATIC METHODS - //-------------------------------------------------------------------------- - // polymorphic way of loading the file, it returns font based on the it's header, that's why it's static - public static DoomFont loadFromFile(File file) { - DoomFont doomFont = null; - byte[] buffer = new byte[65536]; // 64K buffer oh-yeah! - if (file != null) { - if (file.exists() && (file.getName().contains(".lmp") || file.getName().contains(".bmf"))) { - FileInputStream fis = null; - try { - fis = new FileInputStream(file); - fis.read(buffer); - } catch (FileNotFoundException ex) { - Logger.getLogger(DoomFont.class.getName()).log(Level.SEVERE, null, ex); - } catch (IOException ex) { - Logger.getLogger(DoomFont.class.getName()).log(Level.SEVERE, null, ex); - } finally { - if (fis != null) { - try { - fis.close(); - } catch (IOException ex) { - Logger.getLogger(BMF.class.getName()).log(Level.SEVERE, null, ex); - } - } - } - // POLYMORPHIC WAY OF MAKING FONTS - // depending on the header {FON1, FON2 or BMF} - // it returns one font in that set or it stays null - if (buffer[0] == 'F' && buffer[1] == 'O' && buffer[2] == 'N' && buffer[3] == '1') { - // The characters 'F', 'O', 'N', and '1'. - doomFont = new ConsoleFont(buffer); - } else if (buffer[0] == 'F' && buffer[1] == 'O' && buffer[2] == 'N' && buffer[3] == '2') { - // The characters 'F', 'O', 'N', and '2'. - doomFont = new BigFont(buffer); - } else if (buffer[0] == (byte) 0xE1 && buffer[1] == (byte) 0xE6 && buffer[2] == (byte) 0xD5 && buffer[3] == (byte) 0x1A) { - // BMF Magic Header - doomFont = new BMF(buffer); - } - } - } - return doomFont; - } - - //-------------------------------------------------------------------------- - // E - GETTERS - //-------------------------------------------------------------------------- - public String getType() { - return type; - } - - public ArrayList getPalette() { - return palette; - } - - public Color getTransparentColor() { - return transparentColor; - } - - public DoomFontChar[] getChars() { - return chars; - } - - public int getTotalwidth() { - return totalwidth; - } - - public int getMaxheight() { - return maxheight; - } - - public byte[] getBuffer() { - return buffer; - } - - public int getPos() { - return pos; - } - - public boolean isVerticalOffsets() { - return verticalOffsets; - } - - public boolean isError() { - return error; - } - - public String getErrorMsg() { - return errorMsg; - } - -} +package rs.alexanderstojanovich.dfg.fonts; + +import java.awt.Color; +import java.awt.image.BufferedImage; +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.Comparator; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author Alexander Stojanovich + */ +public abstract class DoomFont { // Doom Font is an abstract class, we don't know much about it + + // Font type in set {FON1, FON2, BMF} + protected final String type = initFontType(); + + // Doom color comparator (taken from ZDoom) + protected static Comparator doomComp = new Comparator() { + @Override + public int compare(Color o1, Color o2) { + return (299 * (o1.getRed() - o2.getRed()) + 587 * (o1.getGreen() - o2.getGreen()) + 114 * (o1.getBlue() - o2.getBlue())); + } + }; + + // Array List of RGB entries - palette of colors + protected ArrayList palette = new ArrayList(); + + // Index 0 is transparent and not described, + // Index 1 is where the whole thing with palette starts, + // There's a reason though we cannot allow palettes larger than 256. + public static final int PAL_MAX_SIZE = 255; + + // Setting this color is important (it can be either Color.BLACK or new Color(35, 0, 60) or something else..) + protected Color transparentColor = initTransparentColor(); + + // Setting this color is also important (it must be added as the last color) + // for some things to work + protected static final Color UNUSED_COLOR = new Color(167, 107, 107); + + // Characters of the Doom Font + // (it's length is all the chars that can be displayed) + // and containts only the chars that can be displayed, not outside the range + protected DoomFontChar[] chars; + + // Values for display + protected int totalwidth = 0; + protected int maxheight = 0; + + protected byte[] buffer = new byte[65536]; // buffer is 64K size + protected int pos = 0; // position in the buffer; + + // Are offsets vertical (for BigFont and BMF offsets are horizontal + // but for ConsoleFont offsets are vertical), requires initialisation + protected boolean verticalOffsets = initVerticalOffsets(); + + // Helping the user figure out where error occurred + protected boolean error = false; + protected String errorMsg; + + //-------------------------------------------------------------------------- + // A - CONSTRUCTORS + //-------------------------------------------------------------------------- + // A1 - CONSTRUCTOR USED WHEN READING FROM THE BINARY FONT FILE + protected DoomFont(byte[] buffer) { + this.buffer = buffer; + } + + // A2 - CONSTRUCTOR USED WHEN MAKING DOOM FONT FROM PRE EXISTING INSTALLED FONT + protected DoomFont(BufferedImage image, DoomFontChar[] charVector) { + this.chars = charVector; + if (image != null && charVector != null) { + this.palette.add(transparentColor); + for (DoomFontChar ch : charVector) { + for (int x = 0; x < ch.getW(); x++) { + for (int y = 0; y < ch.getH(); y++) { + int e = ch.getW() * y + x; + int px, py; + px = x + ch.getOffset(); + py = y; + if (px >= 0 && px < image.getWidth() && py >= 0 && py < image.getHeight()) { + Color color = new Color(image.getRGB(px, py)); + if (!color.equals(Color.BLACK) && !color.equals(transparentColor)) { + if (palette.size() < PAL_MAX_SIZE) { // if pallete size is less then MAX ALLOWED SIZE + if (!palette.contains(color)) { // if it doesn't contain color + palette.add(color); // add the color + } + ch.getData()[e] = (byte) (Math.min(palette.indexOf(color), 0xFF)); + } else if (palette.size() == PAL_MAX_SIZE) { // if pallete is MAXED OUT, use approximation + if (!palette.contains(color)) { + int mindeviation = 255000; + int mindevindex = -1; + for (Color palColor : palette) { // by finding color with minimal absolute deviation + int deviation = Math.abs(doomComp.compare(color, palColor)); + if (deviation < mindeviation) { + mindeviation = deviation; + mindevindex = palette.indexOf(palColor); + } + } + if (mindevindex != -1) { // and parsing that color index into the character data + ch.getData()[e] = (byte) (Math.min(mindevindex, 0xFF)); + } + } else { + ch.getData()[e] = (byte) (Math.min(palette.indexOf(color), 0xFF)); + } + } + } + } + } + } + + this.totalwidth += ch.getW(); + if (ch.getH() > this.maxheight) { + this.maxheight = ch.getH(); + } + } + this.palette.add(UNUSED_COLOR); // TRICK FOR ZDOOM :) - cuz one-color palette won't work + } + } + + //-------------------------------------------------------------------------- + // B - ABSTRACT INITIALIZATION METHODS + //-------------------------------------------------------------------------- + // alright we need to initialise the font type, which we don't know beforehand + protected abstract String initFontType(); + + // we need to init this transparent color which is different for the fonts + protected abstract Color initTransparentColor(); + + // initialise vertical offsets, which can be vertical (console font) or horizontal (big font and bmf) + protected abstract boolean initVerticalOffsets(); + + //-------------------------------------------------------------------------- + // C - CORE METHODS + //-------------------------------------------------------------------------- + // search all the characters in the font and returns one which holds the value same as the key + protected DoomFontChar giveChar(char key) { + for (DoomFontChar ch : this.chars) { + if (ch != null && ch.getC() == key) { + return ch; + } + } + return null; + } + + // generates image displaying all the characters in the font + public BufferedImage generateImage(boolean transparency) { + BufferedImage image = null; + image = new BufferedImage(this.totalwidth + 2, this.maxheight + 2, + transparency ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB); + for (DoomFontChar ch : this.chars) { + if (ch != null) { + for (int x = 0; x < ch.getW(); x++) { + for (int y = 0; y < ch.getH(); y++) { + int e = ch.getW() * y + x; + int index = ch.getData()[e] & 0xFF; + if (index >= 0 && index < this.palette.size()) { + Color color = this.palette.get(index); + if (!color.equals(transparentColor)) { + int px, py; + if (this.verticalOffsets) { + px = x; + py = y + ch.getOffset(); + } else { + px = x + ch.getOffset(); + py = y; + } + if (px >= 0 && px < this.totalwidth && py >= 0 && py < this.maxheight) { + image.setRGB(px, py, color.getRGB()); + } + } + } + } + } + } + } + return image; + } + + // generaters image displaying text, it allows typing in the font + public BufferedImage generateImage(boolean transparency, String text) { + // 1. initializing + BufferedImage image = null; + if (text.isEmpty()) { + return null; + } + int totalwidth = 0; + int[] offsets = new int[text.length()]; + // 2. calculating + for (int i = 0; i < text.length(); i++) { + char c = text.charAt(i); + offsets[i] = totalwidth; + DoomFontChar ch = this.giveChar(c); + if (ch != null) { + totalwidth += ch.getW(); + } + } + int maxheight = (this.verticalOffsets) ? this.chars[0].getH() : this.maxheight; + // 3. creating image + image = new BufferedImage(totalwidth + 2, maxheight + 2, + transparency ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB); + // 4. writing to pixels of the image + for (int i = 0; i < text.length(); i++) { + char c = text.charAt(i); + DoomFontChar ch = this.giveChar(c); + if (ch != null) { + for (int x = 0; x < ch.getW(); x++) { + for (int y = 0; y < ch.getH(); y++) { + int e = ch.getW() * y + x; + int index = ch.getData()[e] & 0xFF; + if (index >= 0 && index < this.palette.size()) { + Color color = this.palette.get(index); + if (!color.equals(transparentColor)) { + int px, py; + px = x + offsets[i]; + py = y; + if (px >= 0 && px < totalwidth && py >= 0 && py < this.maxheight) { + image.setRGB(px, py, color.getRGB()); + } + } + } + } + } + } + } + return image; + } + + // saves the font to the file + public boolean saveToFile(File file) { + boolean success = false; + if (file.exists()) { // if file exists delete it + file.delete(); + } // if someone chosen to save file without extension add proper extension + if (!file.getName().contains(".lmp") && !file.getName().contains(".bmf")) { + switch (this.type) { + case "FON1": + file = new File(file.getAbsolutePath() + ".lmp"); + break; + case "FON2": + file = new File(file.getAbsolutePath() + ".lmp"); + break; + case "BMF": + file = new File(file.getAbsolutePath() + ".bmf"); + break; + } + } + FileOutputStream fos = null; + try { + fos = new FileOutputStream(file); + fos.write(this.buffer, 0, this.pos); // write buffer to the targeted file + success = true; + } catch (FileNotFoundException ex) { + Logger.getLogger(DoomFont.class.getName()).log(Level.SEVERE, null, ex); + } catch (IOException ex) { + Logger.getLogger(DoomFont.class.getName()).log(Level.SEVERE, null, ex); + } finally { + if (fos != null) { + try { + fos.close(); + } catch (IOException ex) { + Logger.getLogger(DoomFont.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + return success; + } + + //-------------------------------------------------------------------------- + // D - STATIC METHODS + //-------------------------------------------------------------------------- + // polymorphic way of loading the file, it returns font based on the it's header, that's why it's static + public static DoomFont loadFromFile(File file) { + DoomFont doomFont = null; + byte[] buffer = new byte[65536]; // 64K buffer oh-yeah! + if (file != null) { + if (file.exists() && (file.getName().contains(".lmp") || file.getName().contains(".bmf"))) { + FileInputStream fis = null; + try { + fis = new FileInputStream(file); + fis.read(buffer); + } catch (FileNotFoundException ex) { + Logger.getLogger(DoomFont.class.getName()).log(Level.SEVERE, null, ex); + } catch (IOException ex) { + Logger.getLogger(DoomFont.class.getName()).log(Level.SEVERE, null, ex); + } finally { + if (fis != null) { + try { + fis.close(); + } catch (IOException ex) { + Logger.getLogger(BMF.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + // POLYMORPHIC WAY OF MAKING FONTS + // depending on the header {FON1, FON2 or BMF} + // it returns one font in that set or it stays null + if (buffer[0] == 'F' && buffer[1] == 'O' && buffer[2] == 'N' && buffer[3] == '1') { + // The characters 'F', 'O', 'N', and '1'. + doomFont = new ConsoleFont(buffer); + } else if (buffer[0] == 'F' && buffer[1] == 'O' && buffer[2] == 'N' && buffer[3] == '2') { + // The characters 'F', 'O', 'N', and '2'. + doomFont = new BigFont(buffer); + } else if (buffer[0] == (byte) 0xE1 && buffer[1] == (byte) 0xE6 && buffer[2] == (byte) 0xD5 && buffer[3] == (byte) 0x1A) { + // BMF Magic Header + doomFont = new BMF(buffer); + } + } + } + return doomFont; + } + + //-------------------------------------------------------------------------- + // E - GETTERS + //-------------------------------------------------------------------------- + public String getType() { + return type; + } + + public ArrayList getPalette() { + return palette; + } + + public Color getTransparentColor() { + return transparentColor; + } + + public DoomFontChar[] getChars() { + return chars; + } + + public int getTotalwidth() { + return totalwidth; + } + + public int getMaxheight() { + return maxheight; + } + + public byte[] getBuffer() { + return buffer; + } + + public int getPos() { + return pos; + } + + public boolean isVerticalOffsets() { + return verticalOffsets; + } + + public boolean isError() { + return error; + } + + public String getErrorMsg() { + return errorMsg; + } + +} diff --git a/src/rs/alexanderstojanovich/dfg/fonts/DoomFontChar.java b/src/rs/alexanderstojanovich/dfg/fonts/DoomFontChar.java index 83b5ef5..bfa4e6f 100644 --- a/src/rs/alexanderstojanovich/dfg/fonts/DoomFontChar.java +++ b/src/rs/alexanderstojanovich/dfg/fonts/DoomFontChar.java @@ -1,5 +1,5 @@ -/* - * Copyright (C) 2019 Coa +/* + * Copyright (C) 2020 Alexander Stojanovich * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,78 +14,78 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package rs.alexanderstojanovich.dfg.fonts; - -/** - * - * @author Coa - */ -public class DoomFontChar { - - // Which character it is - protected char c; - // Character image width and height - protected int w, h; - // Character image as raw pixels in row-major format - // Data says on which pixel is which entry in the palette of the font - protected byte[] data; - // Used for remembering the offset of the char - // When rendering the chars from left to right - // (or from up to down for Console font) - // into single image this offset increases - protected int offset; - - //-------------------------------------------------------------------------- - // A - CONSTRUCTOR - //-------------------------------------------------------------------------- - public DoomFontChar(char c, int w, int h) { - this.c = c; - this.w = w; - this.h = h; - this.data = new byte[w * h]; - } - - //-------------------------------------------------------------------------- - // B - GETTERS AND SETTERS (TRIVIAL) - //-------------------------------------------------------------------------- - public char getC() { - return c; - } - - public void setC(char c) { - this.c = c; - } - - public int getW() { - return w; - } - - public void setW(int w) { - this.w = w; - } - - public int getH() { - return h; - } - - public void setH(int h) { - this.h = h; - } - - public byte[] getData() { - return data; - } - - public void setData(byte[] data) { - this.data = data; - } - - public int getOffset() { - return offset; - } - - public void setOffset(int offset) { - this.offset = offset; - } - -} +package rs.alexanderstojanovich.dfg.fonts; + +/** + * + * @author Alexander Stojanovich + */ +public class DoomFontChar { + + // Which character it is + protected char c; + // Character image width and height + protected int w, h; + // Character image as raw pixels in row-major format + // Data says on which pixel is which entry in the palette of the font + protected byte[] data; + // Used for remembering the offset of the char + // When rendering the chars from left to right + // (or from up to down for Console font) + // into single image this offset increases + protected int offset; + + //-------------------------------------------------------------------------- + // A - CONSTRUCTOR + //-------------------------------------------------------------------------- + public DoomFontChar(char c, int w, int h) { + this.c = c; + this.w = w; + this.h = h; + this.data = new byte[w * h]; + } + + //-------------------------------------------------------------------------- + // B - GETTERS AND SETTERS (TRIVIAL) + //-------------------------------------------------------------------------- + public char getC() { + return c; + } + + public void setC(char c) { + this.c = c; + } + + public int getW() { + return w; + } + + public void setW(int w) { + this.w = w; + } + + public int getH() { + return h; + } + + public void setH(int h) { + this.h = h; + } + + public byte[] getData() { + return data; + } + + public void setData(byte[] data) { + this.data = data; + } + + public int getOffset() { + return offset; + } + + public void setOffset(int offset) { + this.offset = offset; + } + +} diff --git a/src/rs/alexanderstojanovich/dfg/gui/GUI.java b/src/rs/alexanderstojanovich/dfg/gui/GUI.java index d5f41c7..6b7a580 100644 --- a/src/rs/alexanderstojanovich/dfg/gui/GUI.java +++ b/src/rs/alexanderstojanovich/dfg/gui/GUI.java @@ -1,5 +1,5 @@ -/* - * Copyright (C) 2019 Coa +/* + * Copyright (C) 2020 Alexander Stojanovich * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -43,7 +43,7 @@ /** * - * @author Coa + * @author Alexander Stojanovich */ public class GUI extends javax.swing.JFrame { @@ -1606,7 +1606,7 @@ private void infoAbout() { sb.append("\tThe purpose of this program is viewing and creating Byte Map and Lump Fonts for \n"); sb.append("\tthe newest generation of Doom ports including ZDoom, GZDoom and Zandronum.\n"); sb.append("\n"); - sb.append("Copyright © 2019\n"); + sb.append("Copyright © 2020\n"); sb.append("Alexander \"Ermac\" Stojanovich\n"); ImageIcon icon = new ImageIcon(icon_url); JOptionPane.showMessageDialog(this, sb.toString(), "About", JOptionPane.INFORMATION_MESSAGE, icon); diff --git a/src/rs/alexanderstojanovich/dfg/gui/GUILogic.java b/src/rs/alexanderstojanovich/dfg/gui/GUILogic.java index bf592b5..1d647a3 100644 --- a/src/rs/alexanderstojanovich/dfg/gui/GUILogic.java +++ b/src/rs/alexanderstojanovich/dfg/gui/GUILogic.java @@ -1,5 +1,5 @@ -/* - * Copyright (C) 2019 Coa +/* + * Copyright (C) 2020 Alexander Stojanovich * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,685 +14,685 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package rs.alexanderstojanovich.dfg.gui; - -import java.awt.Color; -import java.awt.Font; -import java.awt.GradientPaint; -import java.awt.Graphics2D; -import java.awt.RenderingHints; -import java.awt.font.FontRenderContext; -import java.awt.font.TextLayout; -import java.awt.geom.AffineTransform; -import java.awt.geom.Rectangle2D; -import java.awt.image.AffineTransformOp; -import java.awt.image.BufferedImage; -import java.awt.image.IndexColorModel; -import java.awt.image.WritableRaster; -import java.io.File; -import javax.swing.ImageIcon; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.border.BevelBorder; -import rs.alexanderstojanovich.dfg.fonts.BMF; -import rs.alexanderstojanovich.dfg.fonts.BMFChar; -import rs.alexanderstojanovich.dfg.fonts.BigFont; -import rs.alexanderstojanovich.dfg.fonts.ConsoleFont; -import rs.alexanderstojanovich.dfg.fonts.DoomFont; -import rs.alexanderstojanovich.dfg.fonts.DoomFontChar; -import rs.alexanderstojanovich.dfg.util.ColorSample; -import rs.alexanderstojanovich.dfg.util.Palette; - -/** - * - * @author Coa - */ -public class GUILogic { - - // tells us did we initialize the GUI_Logic - private boolean initialized = false; - // font used to crate images - private Font myFont = new Font("Courier New", Font.PLAIN, 12); - // text whibmfCharVector[i] contains all the characters in the font - private String myText; - // info about the author and the font - private String myInfo = ""; - // this text is a test which user wrote in order to test the font - private String myTest = ""; - // spacing between the characters (default is 0) - private int spacing = 0; - - // base line height for BMF font - private int line_height = 0; - // size over the base line (often negative number) - private int size_over = 0; - // size under the base line (often positive number) - private int size_under = 0; - - // BMF font either from loading or derived from prexising font - private DoomFont fontLoad, fontDer; - - // Image which contains only rendered character data - private BufferedImage imageRender; - - // Foreground (main or primary) color - private Color fgColor = Color.YELLOW; - // Background (secondary) color - private Color bgColor = Color.CYAN; - // Outlining color - private Color outlineColor = Color.BLUE; - - // shadow color (if shadow has been selected by the user) - private Color shadowColor = Color.GRAY; - - // Way of displaying colors on the layered pane - // via several labels coloured differently - private JLabel[] colorVector = new JLabel[256]; - // Color panel which holds all the color labels - private JPanel colorPanel; //= new JPanel(new GridLayout(16, 16, 1, 1)); - // Color vector for displaying the color map - private DoomFontChar[] charVector; - // Doom Font Format - private String fontFormat = "FON1"; - - // Image zoom factor (in percentage) - private int zoom = 100; - - //-------------------------------------------------------------------------- - // A - CONSTRUCTORS - //-------------------------------------------------------------------------- - public GUILogic(JPanel colorPanel) { - this.colorPanel = colorPanel; - initColorVectors(); - this.initialized = true; - } - - //-------------------------------------------------------------------------- - // B - METHODS - //-------------------------------------------------------------------------- - // init Palette display (it's called Color Vector) - private void initColorVectors() { - for (int i = 0; i < colorVector.length; i++) { - colorVector[i] = new JLabel(); - colorVector[i].setBackground(Color.BLACK); - colorVector[i].setOpaque(true); - colorVector[i].setSize(9, 9); - colorVector[i].setBorder(new BevelBorder(BevelBorder.RAISED)); - colorPanel.add(colorVector[i], new Integer(i)); - } - } - - // open the file and load the font - public boolean fileOpen(File file) { - boolean ok = false; - if (file != null) { - fontLoad = DoomFont.loadFromFile(file); - ok = ((fontLoad != null) && !fontLoad.isError()); - } - return ok; - } - - // saving loaded font to file - public boolean fileSaveFontLoad(File file) { - boolean ok = false; - if (fontLoad != null && file != null) { - ok = fontLoad.saveToFile(file); - } - return ok; - } - - // saving derived font to file - public boolean fileSaveFontDer(File file) { - boolean ok = false; - if (file != null) { - switch (fontFormat) { - case "FON1": - fontDer = new ConsoleFont(imageRender, charVector); - break; - case "FON2": - fontDer = new BigFont(imageRender, charVector); - break; - case "BMF": - fontDer = new BMF(myInfo, spacing, line_height, size_over, size_under, imageRender, (BMFChar[]) charVector); - break; - } - ok = fontDer.saveToFile(file); - } - return ok; - } - - // make icon for loaded font - public ImageIcon giveFontLoadIcon(boolean transparency) { - ImageIcon imageIcon = null; - if (fontLoad != null) { - if (!myTest.isEmpty()) { - imageRender = fontLoad.generateImage(transparency, myTest); - } else { - imageRender = fontLoad.generateImage(transparency); - } - if (imageRender != null) { - AffineTransform xform = new AffineTransform(); - xform.scale(zoom / 100.0, zoom / 100.0); - AffineTransformOp atOp = new AffineTransformOp(xform, null); - BufferedImage destImage = atOp.filter(imageRender, null); - imageIcon = new ImageIcon(destImage); - } - colorPanel.setEnabled(true); - - for (int i = 0; i < fontLoad.getPalette().size(); i++) { - Color col = fontLoad.getPalette().get(i); - colorVector[i].setBackground(col); - colorVector[i].setToolTipText("Red = " + col.getRed() - + ", Green = " + col.getGreen() + ", Blue = " + col.getBlue()); - } - - for (int j = fontLoad.getPalette().size(); j < colorVector.length; j++) { - colorVector[j].setBackground(Color.BLACK); - colorVector[j].setToolTipText(null); - } - } - return imageIcon; - } - - // get Max String Bounds if Console Font is selected (it's gonna convert font to monospace) - private static Rectangle2D maxStrBounds(Font font, FontRenderContext frc, String text) { - double maxWidth = 0.0; - Rectangle2D maxStrBounds = null; - for (int i = 0; i < text.length(); i++) { - char c = text.charAt(i); - Rectangle2D bounds = font.getStringBounds(String.valueOf(c), frc); - if (bounds.getWidth() > maxWidth) { - maxStrBounds = bounds; - maxWidth = bounds.getWidth(); - } - } - return maxStrBounds; - } - - // make icon in case for Big Font and BMF, boolean monospace is true in case of Console Font - public ImageIcon giveFontDerIcon(boolean monospace, boolean transparency, boolean antialiasing, boolean useGradient, int outlineWidth, boolean shadow, int shadowAngle, double multiplier) { - ImageIcon imageIcon = null; - if (myFont != null && myText != null) { - // define sampler - double sampler = multiplier; - if (outlineWidth > 0) { - sampler *= 2.0 * outlineWidth; - } - if (shadow) { - sampler *= 2.0; - } - // create the FontRenderContext object which helps us to measure the text - FontRenderContext frc = new FontRenderContext(null, antialiasing, true); - Rectangle2D maxCharBounds = maxStrBounds(myFont, frc, myText); - Rectangle2D globalBounds = myFont.getStringBounds(myText, frc); - if (monospace) { - maxCharBounds.setRect(globalBounds.getX(), globalBounds.getY(), (sampler + spacing + maxCharBounds.getWidth()) * myText.length(), maxCharBounds.getHeight() + sampler); - } else { - globalBounds.setRect(globalBounds.getX(), globalBounds.getY(), globalBounds.getWidth() + (sampler + spacing) * myText.length(), globalBounds.getHeight() + sampler); - } - - Rectangle2D bounds = (monospace) ? maxCharBounds : globalBounds; - - // determining the three parameters for Byte Map Font - line_height = (int) Math.round(bounds.getHeight()); - size_over = (int) Math.floor(bounds.getMinY()); - size_under = (int) Math.floor(bounds.getMaxY()); - - // calculating with and height and adding +1 to be correctly displayed - int w = (int) Math.round(bounds.getWidth()) + 2; // + 2 is used so borders around chars're correctly displayed - int h = (int) Math.round(bounds.getHeight()) + 2; // + 2 is used so borders around chars're correctly displayed - - // create BufferedImage objects - BufferedImage imageResult; // -- which is used later.. - BufferedImage imageOverlay; // -- which is used for rendering char boundaries - - // creating buffered images for display - imageRender = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); - imageOverlay = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); - imageResult = new BufferedImage(w, h, transparency ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB); - - // calling createGraphics() to get the Graphics2D for each - Graphics2D graphicsOverlay = imageOverlay.createGraphics(); - Graphics2D graphicsRender = imageRender.createGraphics(); - - // do the first necessary translation for each - graphicsOverlay.translate(0, -bounds.getY()); - graphicsRender.translate(0, -bounds.getY()); - - // set the font for both - graphicsOverlay.setFont(myFont); - graphicsRender.setFont(myFont); - - if (antialiasing) { - graphicsRender.setRenderingHint( - RenderingHints.KEY_TEXT_ANTIALIASING, - RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - - graphicsRender.setRenderingHint( - RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - } else { - graphicsRender.setRenderingHint( - RenderingHints.KEY_TEXT_ANTIALIASING, - RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT); - - graphicsRender.setRenderingHint( - RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_DEFAULT); - } - - graphicsRender.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); - - // looping troughout the characters of the font - if (fontFormat.equals("BMF")) { - charVector = new BMFChar[myText.length()]; - } else { - charVector = new DoomFontChar[myText.length()]; - } - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < myText.length(); i++) { - char c = myText.charAt(i); - sb.append(c); - // 1. drawing char bounds first - Rectangle2D cb = (monospace) ? maxStrBounds(myFont, frc, myText) : myFont.getStringBounds(String.valueOf(c), frc); - cb.setRect(cb.getX(), cb.getY(), cb.getWidth() + sampler, cb.getHeight() + sampler); - - graphicsOverlay.setColor(transparency ? Color.BLACK : Color.MAGENTA); - graphicsOverlay.draw(cb); - graphicsOverlay.translate(cb.getWidth() + spacing, 0); - - Rectangle2D cbx; - int offset; - if (monospace) { - offset = ((int) Math.round(i * cb.getWidth()) + i * spacing); - } else { - cbx = myFont.getStringBounds(String.valueOf(sb), frc); - cbx.setRect(cbx.getX(), cbx.getY(), cbx.getWidth() + sampler, cbx.getHeight() + sampler); - offset = (int) Math.round(cbx.getWidth() - cb.getWidth() + i * (spacing + sampler)); - } - - if (fontFormat.equals("BMF")) { - charVector[i] = new BMFChar(c, (int) Math.round(cb.getWidth()), (int) Math.round(cb.getHeight())); - BMFChar bmfCh = (BMFChar) charVector[i]; - bmfCh.setShift((int) Math.round(cb.getWidth())); - bmfCh.setOffset(offset); - } else { - charVector[i] = new DoomFontChar(c, (int) Math.round(cb.getWidth()), (int) Math.round(cb.getHeight())); - charVector[i].setOffset(offset); - } - // 2. then if gradient is selected we choose paint or simple color -> for drawing - if (useGradient) { - TextLayout chLayout = new TextLayout(String.valueOf(c), myFont, frc); - Rectangle2D gb = chLayout.getBounds(); - GradientPaint gp = new GradientPaint( - 0.0f, (float) gb.getMinY() - (float) (0.5f * sampler), - fgColor, - 0.0f, (float) gb.getMaxY() + (float) (0.5f * sampler), - bgColor, false); - graphicsRender.setPaint(gp); - } else { - graphicsRender.setColor(fgColor); - } - graphicsRender.drawString(String.valueOf(c), (float) (0.5f * sampler), (float) (0.5f * sampler)); - - // 3. necessary translation - graphicsRender.translate(cb.getWidth() + spacing, 0); - } - - //if antialiasing is selected multiply color with it's alpha - if (antialiasing) { - for (int px = 0; px < imageRender.getWidth(); px++) { - for (int py = 0; py < imageRender.getHeight(); py++) { - Color srcCol = new Color(imageRender.getRGB(px, py), true); - if (srcCol.getAlpha() > 0) { // this if is in order to not ruin the borders around the chars - Color dstCol = new Color( // constructor with the three floats is called - (srcCol.getAlpha() / 255.0f) * (srcCol.getRed() / 255.0f), - (srcCol.getAlpha() / 255.0f) * (srcCol.getGreen() / 255.0f), - (srcCol.getAlpha() / 255.0f) * (srcCol.getBlue() / 255.0f) - ); - imageRender.setRGB(px, py, dstCol.getRGB()); - } - } - } - } - // if outline is selected; - if (outlineWidth > 0) { - // Copy of raster of unaltered image is needed!! - WritableRaster wr = imageRender.copyData(null); - for (int px = 0; px < imageRender.getWidth(); px++) { - for (int py = 0; py < imageRender.getHeight(); py++) { - Color pixCol = new Color(imageRender.getRGB(px, py), true); - // writtable raster must be associated with ARGB image!! - ColorSample cs = ColorSample.getSample(wr, px, py, outlineWidth); - if (pixCol.getAlpha() == 0 && cs.getAlpha() > 0) { - imageRender.setRGB(px, py, outlineColor.getRGB()); - } - } - } - } - - // if user selected shadow; this is for shadow effect - if (shadow) { - WritableRaster wr = imageRender.copyData(null); - for (DoomFontChar ch : charVector) { - for (int px = ch.getOffset(); px < ch.getW() + ch.getOffset(); px++) { - for (int py = 0; py < ch.getH(); py++) { - // calculate dx an dy cuz they are one square away from px and py - float cos = (float) Math.cos(Math.toRadians(shadowAngle)); - float sin = (float) Math.sin(Math.toRadians(shadowAngle)); - - float dx = px + cos; - float dy = py + sin; - // constrain dx and dy to the image bounds - if (dx < ch.getOffset()) { - dx = ch.getOffset(); - } else if (dx > ch.getW() + ch.getOffset() - 1) { - dx = (float) (ch.getW() + ch.getOffset() - 1); - } - - if (dy < 0) { - dy = 0; - } else if (dy > ch.getH() - 1) { - dy = (float) (ch.getH() - 1); - } - - Color dstPixCol = new Color(imageRender.getRGB(Math.round(dx), Math.round(dy)), true); - ColorSample csg = ColorSample.getGaussianBlurSample(wr, px, py); // calc gauss blur - float csa = csg.getAlpha() / 255.0f; // reason behind this value used is to prevent "too many wrong" pixels - if (dstPixCol.getAlpha() == 0 && csa >= 0.195346f) { - // to create nice shadow effect multiply shadow color components with alpha_sqrt - float alpha_sqrt = (float) Math.sqrt(csa); - float red = alpha_sqrt * shadowColor.getRed() / 255.0f; - float green = alpha_sqrt * shadowColor.getGreen() / 255.0f; - float blue = alpha_sqrt * shadowColor.getBlue() / 255.0f; - Color fineCol = new Color(red, green, blue); - imageRender.setRGB(Math.round(dx), Math.round(dy), fineCol.getRGB()); - } - } - } - } - } - - // finalizing - merging overlay with rendered - Graphics2D graphicsResult = imageResult.createGraphics(); - graphicsResult.drawImage(imageOverlay, 0, 0, null); - // if user chose palette in the image, make conversion.. - if (Palette.isLoaded()) { - IndexColorModel icm = new IndexColorModel(8, Palette.getColors().length, Palette.getColBuff(), 0, true); - BufferedImage imageIndexed = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_INDEXED, icm); - imageIndexed.createGraphics().drawImage(imageRender, 0, 0, null); - imageRender = imageIndexed; - } - - graphicsResult.drawImage(imageRender, 0, 0, null); - - AffineTransform xform = new AffineTransform(); - xform.scale(zoom / 100.0, zoom / 100.0); - AffineTransformOp atOp = new AffineTransformOp(xform, null); - BufferedImage destImage = atOp.filter(imageResult, null); - - imageIcon = new ImageIcon(destImage); - } - - return imageIcon; - } - - // Is wrapper for loading 6-bit RGB palette and displaying it's colors - public void load6bitRGBPalette() { - Palette.load6bitRGB(); - if (initialized) { - for (int i = 0; i < Palette.getColors().length; i++) { - Color col = new Color(Palette.getColors()[i]); - colorVector[i].setBackground(col); - colorVector[i].setToolTipText("Red = " + col.getRed() - + ", Green = " + col.getGreen() + ", Blue = " + col.getBlue()); - } - - for (int i = Palette.getColors().length; i < colorVector.length; i++) { - colorVector[i].setBackground(Color.BLACK); - colorVector[i].setToolTipText(null); - } - } - } - - // Is wrapper for loading 8-bit RGB palette and displaying it's colors - public void load8bitRGBPalette() { - Palette.load8bitRGB(); - if (initialized) { - for (int i = 0; i < Palette.getColors().length; i++) { - Color col = new Color(Palette.getColors()[i]); - colorVector[i].setBackground(col); - colorVector[i].setToolTipText("Red = " + col.getRed() - + ", Green = " + col.getGreen() + ", Blue = " + col.getBlue()); - } - } - } - - // Loads palette from the file if palette Name is not null otherwise resets the palette - // If palette is loaded it displays the colors otherwise it displays black squares - public void loadPalette(String paletteName) { - if (paletteName == null) { - Palette.reset(); - } else { - Palette.load(paletteName); - } - if (initialized && Palette.isLoaded()) { - for (int i = 0; i < Palette.getColors().length; i++) { - Color col = new Color(Palette.getColors()[i]); - colorVector[i].setBackground(col); - colorVector[i].setToolTipText("Red = " + col.getRed() - + ", Green = " + col.getGreen() + ", Blue = " + col.getBlue()); - } - } else if (initialized) { - for (JLabel label : colorVector) { - label.setBackground(Color.BLACK); - label.setToolTipText(null); - } - } - } - - // Asynchronous reset - returns the logic into initial state - public void reset() { - myFont = new Font("Courier New", Font.PLAIN, 12); - myText = null; - myInfo = ""; - myTest = ""; - spacing = 0; - - fontLoad = null; - fontDer = null; - - imageRender = null; - - fgColor = Color.YELLOW; - bgColor = Color.CYAN; - - outlineColor = Color.BLUE; - shadowColor = Color.GRAY; - - charVector = null; - - fontFormat = "FON1"; - - zoom = 100; // resetting zoom; damn forgot this - Palette.reset(); - - for (JLabel label : colorVector) { - label.setBackground(Color.BLACK); - label.setToolTipText("Red = " + Color.BLACK.getRed() - + ", Green = " + Color.BLACK.getGreen() + ", Blue = " + Color.BLACK.getBlue()); - } - } - - //-------------------------------------------------------------------------- - // C - GETTERS AND SETTERS - //-------------------------------------------------------------------------- - public boolean isInitialized() { - return initialized; - } - - public void setInitialized(boolean initialized) { - this.initialized = initialized; - } - - public Font getMyFont() { - return myFont; - } - - public void setMyFont(Font myFont) { - this.myFont = myFont; - } - - public String getMyText() { - return myText; - } - - public void setMyText(String myText) { - this.myText = myText; - } - - public String getMyInfo() { - return myInfo; - } - - public void setMyInfo(String myInfo) { - this.myInfo = myInfo; - } - - public String getMyTest() { - return myTest; - } - - public void setMyTest(String myTest) { - this.myTest = myTest; - } - - public int getSpacing() { - return spacing; - } - - public void setSpacing(int spacing) { - this.spacing = spacing; - } - - public int getLine_height() { - return line_height; - } - - public void setLine_height(int line_height) { - this.line_height = line_height; - } - - public int getSize_over() { - return size_over; - } - - public void setSize_over(int size_over) { - this.size_over = size_over; - } - - public int getSize_under() { - return size_under; - } - - public void setSize_under(int size_under) { - this.size_under = size_under; - } - - public DoomFont getFontLoad() { - return fontLoad; - } - - public void setFontLoad(DoomFont fontLoad) { - this.fontLoad = fontLoad; - } - - public DoomFont getFontDer() { - return fontDer; - } - - public void setFontDer(DoomFont fontDer) { - this.fontDer = fontDer; - } - - public BufferedImage getImageRender() { - return imageRender; - } - - public void setImageRender(BufferedImage imageRender) { - this.imageRender = imageRender; - } - - public Color getFgColor() { - return fgColor; - } - - public void setFgColor(Color fgColor) { - this.fgColor = fgColor; - } - - public Color getBgColor() { - return bgColor; - } - - public void setBgColor(Color bgColor) { - this.bgColor = bgColor; - } - - public Color getOutlineColor() { - return outlineColor; - } - - public void setOutlineColor(Color outlineColor) { - this.outlineColor = outlineColor; - } - - public JLabel[] getColorVector() { - return colorVector; - } - - public void setColorVector(JLabel[] colorVector) { - this.colorVector = colorVector; - } - - public JPanel getColorPanel() { - return colorPanel; - } - - public void setColorPanel(JPanel colorPanel) { - this.colorPanel = colorPanel; - } - - public DoomFontChar[] getCharVector() { - return charVector; - } - - public void setCharVector(DoomFontChar[] charVector) { - this.charVector = charVector; - } - - public String getFontFormat() { - return fontFormat; - } - - public void setFontFormat(String fontFormat) { - this.fontFormat = fontFormat; - } - - public int getZoom() { - return zoom; - } - - public void setZoom(int zoom) { - this.zoom = zoom; - } - - public Color getShadowColor() { - return shadowColor; - } - - public void setShadowColor(Color shadowColor) { - this.shadowColor = shadowColor; - } - -} +package rs.alexanderstojanovich.dfg.gui; + +import java.awt.Color; +import java.awt.Font; +import java.awt.GradientPaint; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.font.FontRenderContext; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.AffineTransformOp; +import java.awt.image.BufferedImage; +import java.awt.image.IndexColorModel; +import java.awt.image.WritableRaster; +import java.io.File; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.BevelBorder; +import rs.alexanderstojanovich.dfg.fonts.BMF; +import rs.alexanderstojanovich.dfg.fonts.BMFChar; +import rs.alexanderstojanovich.dfg.fonts.BigFont; +import rs.alexanderstojanovich.dfg.fonts.ConsoleFont; +import rs.alexanderstojanovich.dfg.fonts.DoomFont; +import rs.alexanderstojanovich.dfg.fonts.DoomFontChar; +import rs.alexanderstojanovich.dfg.util.ColorSample; +import rs.alexanderstojanovich.dfg.util.Palette; + +/** + * + * @author Alexander Stojanovich + */ +public class GUILogic { + + // tells us did we initialize the GUI_Logic + private boolean initialized = false; + // font used to crate images + private Font myFont = new Font("Courier New", Font.PLAIN, 12); + // text whibmfCharVector[i] contains all the characters in the font + private String myText; + // info about the author and the font + private String myInfo = ""; + // this text is a test which user wrote in order to test the font + private String myTest = ""; + // spacing between the characters (default is 0) + private int spacing = 0; + + // base line height for BMF font + private int line_height = 0; + // size over the base line (often negative number) + private int size_over = 0; + // size under the base line (often positive number) + private int size_under = 0; + + // BMF font either from loading or derived from prexising font + private DoomFont fontLoad, fontDer; + + // Image which contains only rendered character data + private BufferedImage imageRender; + + // Foreground (main or primary) color + private Color fgColor = Color.YELLOW; + // Background (secondary) color + private Color bgColor = Color.CYAN; + // Outlining color + private Color outlineColor = Color.BLUE; + + // shadow color (if shadow has been selected by the user) + private Color shadowColor = Color.GRAY; + + // Way of displaying colors on the layered pane + // via several labels coloured differently + private JLabel[] colorVector = new JLabel[256]; + // Color panel which holds all the color labels + private JPanel colorPanel; //= new JPanel(new GridLayout(16, 16, 1, 1)); + // Color vector for displaying the color map + private DoomFontChar[] charVector; + // Doom Font Format + private String fontFormat = "FON1"; + + // Image zoom factor (in percentage) + private int zoom = 100; + + //-------------------------------------------------------------------------- + // A - CONSTRUCTORS + //-------------------------------------------------------------------------- + public GUILogic(JPanel colorPanel) { + this.colorPanel = colorPanel; + initColorVectors(); + this.initialized = true; + } + + //-------------------------------------------------------------------------- + // B - METHODS + //-------------------------------------------------------------------------- + // init Palette display (it's called Color Vector) + private void initColorVectors() { + for (int i = 0; i < colorVector.length; i++) { + colorVector[i] = new JLabel(); + colorVector[i].setBackground(Color.BLACK); + colorVector[i].setOpaque(true); + colorVector[i].setSize(9, 9); + colorVector[i].setBorder(new BevelBorder(BevelBorder.RAISED)); + colorPanel.add(colorVector[i], new Integer(i)); + } + } + + // open the file and load the font + public boolean fileOpen(File file) { + boolean ok = false; + if (file != null) { + fontLoad = DoomFont.loadFromFile(file); + ok = ((fontLoad != null) && !fontLoad.isError()); + } + return ok; + } + + // saving loaded font to file + public boolean fileSaveFontLoad(File file) { + boolean ok = false; + if (fontLoad != null && file != null) { + ok = fontLoad.saveToFile(file); + } + return ok; + } + + // saving derived font to file + public boolean fileSaveFontDer(File file) { + boolean ok = false; + if (file != null) { + switch (fontFormat) { + case "FON1": + fontDer = new ConsoleFont(imageRender, charVector); + break; + case "FON2": + fontDer = new BigFont(imageRender, charVector); + break; + case "BMF": + fontDer = new BMF(myInfo, spacing, line_height, size_over, size_under, imageRender, (BMFChar[]) charVector); + break; + } + ok = fontDer.saveToFile(file); + } + return ok; + } + + // make icon for loaded font + public ImageIcon giveFontLoadIcon(boolean transparency) { + ImageIcon imageIcon = null; + if (fontLoad != null) { + if (!myTest.isEmpty()) { + imageRender = fontLoad.generateImage(transparency, myTest); + } else { + imageRender = fontLoad.generateImage(transparency); + } + if (imageRender != null) { + AffineTransform xform = new AffineTransform(); + xform.scale(zoom / 100.0, zoom / 100.0); + AffineTransformOp atOp = new AffineTransformOp(xform, null); + BufferedImage destImage = atOp.filter(imageRender, null); + imageIcon = new ImageIcon(destImage); + } + colorPanel.setEnabled(true); + + for (int i = 0; i < fontLoad.getPalette().size(); i++) { + Color col = fontLoad.getPalette().get(i); + colorVector[i].setBackground(col); + colorVector[i].setToolTipText("Red = " + col.getRed() + + ", Green = " + col.getGreen() + ", Blue = " + col.getBlue()); + } + + for (int j = fontLoad.getPalette().size(); j < colorVector.length; j++) { + colorVector[j].setBackground(Color.BLACK); + colorVector[j].setToolTipText(null); + } + } + return imageIcon; + } + + // get Max String Bounds if Console Font is selected (it's gonna convert font to monospace) + private static Rectangle2D maxStrBounds(Font font, FontRenderContext frc, String text) { + double maxWidth = 0.0; + Rectangle2D maxStrBounds = null; + for (int i = 0; i < text.length(); i++) { + char c = text.charAt(i); + Rectangle2D bounds = font.getStringBounds(String.valueOf(c), frc); + if (bounds.getWidth() > maxWidth) { + maxStrBounds = bounds; + maxWidth = bounds.getWidth(); + } + } + return maxStrBounds; + } + + // make icon in case for Big Font and BMF, boolean monospace is true in case of Console Font + public ImageIcon giveFontDerIcon(boolean monospace, boolean transparency, boolean antialiasing, boolean useGradient, int outlineWidth, boolean shadow, int shadowAngle, double multiplier) { + ImageIcon imageIcon = null; + if (myFont != null && myText != null) { + // define sampler + double sampler = multiplier; + if (outlineWidth > 0) { + sampler *= 2.0 * outlineWidth; + } + if (shadow) { + sampler *= 2.0; + } + // create the FontRenderContext object which helps us to measure the text + FontRenderContext frc = new FontRenderContext(null, antialiasing, true); + Rectangle2D maxCharBounds = maxStrBounds(myFont, frc, myText); + Rectangle2D globalBounds = myFont.getStringBounds(myText, frc); + if (monospace) { + maxCharBounds.setRect(globalBounds.getX(), globalBounds.getY(), (sampler + spacing + maxCharBounds.getWidth()) * myText.length(), maxCharBounds.getHeight() + sampler); + } else { + globalBounds.setRect(globalBounds.getX(), globalBounds.getY(), globalBounds.getWidth() + (sampler + spacing) * myText.length(), globalBounds.getHeight() + sampler); + } + + Rectangle2D bounds = (monospace) ? maxCharBounds : globalBounds; + + // determining the three parameters for Byte Map Font + line_height = (int) Math.round(bounds.getHeight()); + size_over = (int) Math.floor(bounds.getMinY()); + size_under = (int) Math.floor(bounds.getMaxY()); + + // calculating with and height and adding +1 to be correctly displayed + int w = (int) Math.round(bounds.getWidth()) + 2; // + 2 is used so borders around chars're correctly displayed + int h = (int) Math.round(bounds.getHeight()) + 2; // + 2 is used so borders around chars're correctly displayed + + // create BufferedImage objects + BufferedImage imageResult; // -- which is used later.. + BufferedImage imageOverlay; // -- which is used for rendering char boundaries + + // creating buffered images for display + imageRender = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + imageOverlay = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + imageResult = new BufferedImage(w, h, transparency ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB); + + // calling createGraphics() to get the Graphics2D for each + Graphics2D graphicsOverlay = imageOverlay.createGraphics(); + Graphics2D graphicsRender = imageRender.createGraphics(); + + // do the first necessary translation for each + graphicsOverlay.translate(0, -bounds.getY()); + graphicsRender.translate(0, -bounds.getY()); + + // set the font for both + graphicsOverlay.setFont(myFont); + graphicsRender.setFont(myFont); + + if (antialiasing) { + graphicsRender.setRenderingHint( + RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + + graphicsRender.setRenderingHint( + RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + } else { + graphicsRender.setRenderingHint( + RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT); + + graphicsRender.setRenderingHint( + RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_DEFAULT); + } + + graphicsRender.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); + + // looping troughout the characters of the font + if (fontFormat.equals("BMF")) { + charVector = new BMFChar[myText.length()]; + } else { + charVector = new DoomFontChar[myText.length()]; + } + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < myText.length(); i++) { + char c = myText.charAt(i); + sb.append(c); + // 1. drawing char bounds first + Rectangle2D cb = (monospace) ? maxStrBounds(myFont, frc, myText) : myFont.getStringBounds(String.valueOf(c), frc); + cb.setRect(cb.getX(), cb.getY(), cb.getWidth() + sampler, cb.getHeight() + sampler); + + graphicsOverlay.setColor(transparency ? Color.BLACK : Color.MAGENTA); + graphicsOverlay.draw(cb); + graphicsOverlay.translate(cb.getWidth() + spacing, 0); + + Rectangle2D cbx; + int offset; + if (monospace) { + offset = ((int) Math.round(i * cb.getWidth()) + i * spacing); + } else { + cbx = myFont.getStringBounds(String.valueOf(sb), frc); + cbx.setRect(cbx.getX(), cbx.getY(), cbx.getWidth() + sampler, cbx.getHeight() + sampler); + offset = (int) Math.round(cbx.getWidth() - cb.getWidth() + i * (spacing + sampler)); + } + + if (fontFormat.equals("BMF")) { + charVector[i] = new BMFChar(c, (int) Math.round(cb.getWidth()), (int) Math.round(cb.getHeight())); + BMFChar bmfCh = (BMFChar) charVector[i]; + bmfCh.setShift((int) Math.round(cb.getWidth())); + bmfCh.setOffset(offset); + } else { + charVector[i] = new DoomFontChar(c, (int) Math.round(cb.getWidth()), (int) Math.round(cb.getHeight())); + charVector[i].setOffset(offset); + } + // 2. then if gradient is selected we choose paint or simple color -> for drawing + if (useGradient) { + TextLayout chLayout = new TextLayout(String.valueOf(c), myFont, frc); + Rectangle2D gb = chLayout.getBounds(); + GradientPaint gp = new GradientPaint( + 0.0f, (float) gb.getMinY() - (float) (0.5f * sampler), + fgColor, + 0.0f, (float) gb.getMaxY() + (float) (0.5f * sampler), + bgColor, false); + graphicsRender.setPaint(gp); + } else { + graphicsRender.setColor(fgColor); + } + graphicsRender.drawString(String.valueOf(c), (float) (0.5f * sampler), (float) (0.5f * sampler)); + + // 3. necessary translation + graphicsRender.translate(cb.getWidth() + spacing, 0); + } + + //if antialiasing is selected multiply color with it's alpha + if (antialiasing) { + for (int px = 0; px < imageRender.getWidth(); px++) { + for (int py = 0; py < imageRender.getHeight(); py++) { + Color srcCol = new Color(imageRender.getRGB(px, py), true); + if (srcCol.getAlpha() > 0) { // this if is in order to not ruin the borders around the chars + Color dstCol = new Color( // constructor with the three floats is called + (srcCol.getAlpha() / 255.0f) * (srcCol.getRed() / 255.0f), + (srcCol.getAlpha() / 255.0f) * (srcCol.getGreen() / 255.0f), + (srcCol.getAlpha() / 255.0f) * (srcCol.getBlue() / 255.0f) + ); + imageRender.setRGB(px, py, dstCol.getRGB()); + } + } + } + } + // if outline is selected; + if (outlineWidth > 0) { + // Copy of raster of unaltered image is needed!! + WritableRaster wr = imageRender.copyData(null); + for (int px = 0; px < imageRender.getWidth(); px++) { + for (int py = 0; py < imageRender.getHeight(); py++) { + Color pixCol = new Color(imageRender.getRGB(px, py), true); + // writtable raster must be associated with ARGB image!! + ColorSample cs = ColorSample.getSample(wr, px, py, outlineWidth); + if (pixCol.getAlpha() == 0 && cs.getAlpha() > 0) { + imageRender.setRGB(px, py, outlineColor.getRGB()); + } + } + } + } + + // if user selected shadow; this is for shadow effect + if (shadow) { + WritableRaster wr = imageRender.copyData(null); + for (DoomFontChar ch : charVector) { + for (int px = ch.getOffset(); px < ch.getW() + ch.getOffset(); px++) { + for (int py = 0; py < ch.getH(); py++) { + // calculate dx an dy cuz they are one square away from px and py + float cos = (float) Math.cos(Math.toRadians(shadowAngle)); + float sin = (float) Math.sin(Math.toRadians(shadowAngle)); + + float dx = px + cos; + float dy = py + sin; + // constrain dx and dy to the image bounds + if (dx < ch.getOffset()) { + dx = ch.getOffset(); + } else if (dx > ch.getW() + ch.getOffset() - 1) { + dx = (float) (ch.getW() + ch.getOffset() - 1); + } + + if (dy < 0) { + dy = 0; + } else if (dy > ch.getH() - 1) { + dy = (float) (ch.getH() - 1); + } + + Color dstPixCol = new Color(imageRender.getRGB(Math.round(dx), Math.round(dy)), true); + ColorSample csg = ColorSample.getGaussianBlurSample(wr, px, py); // calc gauss blur + float csa = csg.getAlpha() / 255.0f; // reason behind this value used is to prevent "too many wrong" pixels + if (dstPixCol.getAlpha() == 0 && csa >= 0.195346f) { + // to create nice shadow effect multiply shadow color components with alpha_sqrt + float alpha_sqrt = (float) Math.sqrt(csa); + float red = alpha_sqrt * shadowColor.getRed() / 255.0f; + float green = alpha_sqrt * shadowColor.getGreen() / 255.0f; + float blue = alpha_sqrt * shadowColor.getBlue() / 255.0f; + Color fineCol = new Color(red, green, blue); + imageRender.setRGB(Math.round(dx), Math.round(dy), fineCol.getRGB()); + } + } + } + } + } + + // finalizing - merging overlay with rendered + Graphics2D graphicsResult = imageResult.createGraphics(); + graphicsResult.drawImage(imageOverlay, 0, 0, null); + // if user chose palette in the image, make conversion.. + if (Palette.isLoaded()) { + IndexColorModel icm = new IndexColorModel(8, Palette.getColors().length, Palette.getColBuff(), 0, true); + BufferedImage imageIndexed = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_INDEXED, icm); + imageIndexed.createGraphics().drawImage(imageRender, 0, 0, null); + imageRender = imageIndexed; + } + + graphicsResult.drawImage(imageRender, 0, 0, null); + + AffineTransform xform = new AffineTransform(); + xform.scale(zoom / 100.0, zoom / 100.0); + AffineTransformOp atOp = new AffineTransformOp(xform, null); + BufferedImage destImage = atOp.filter(imageResult, null); + + imageIcon = new ImageIcon(destImage); + } + + return imageIcon; + } + + // Is wrapper for loading 6-bit RGB palette and displaying it's colors + public void load6bitRGBPalette() { + Palette.load6bitRGB(); + if (initialized) { + for (int i = 0; i < Palette.getColors().length; i++) { + Color col = new Color(Palette.getColors()[i]); + colorVector[i].setBackground(col); + colorVector[i].setToolTipText("Red = " + col.getRed() + + ", Green = " + col.getGreen() + ", Blue = " + col.getBlue()); + } + + for (int i = Palette.getColors().length; i < colorVector.length; i++) { + colorVector[i].setBackground(Color.BLACK); + colorVector[i].setToolTipText(null); + } + } + } + + // Is wrapper for loading 8-bit RGB palette and displaying it's colors + public void load8bitRGBPalette() { + Palette.load8bitRGB(); + if (initialized) { + for (int i = 0; i < Palette.getColors().length; i++) { + Color col = new Color(Palette.getColors()[i]); + colorVector[i].setBackground(col); + colorVector[i].setToolTipText("Red = " + col.getRed() + + ", Green = " + col.getGreen() + ", Blue = " + col.getBlue()); + } + } + } + + // Loads palette from the file if palette Name is not null otherwise resets the palette + // If palette is loaded it displays the colors otherwise it displays black squares + public void loadPalette(String paletteName) { + if (paletteName == null) { + Palette.reset(); + } else { + Palette.load(paletteName); + } + if (initialized && Palette.isLoaded()) { + for (int i = 0; i < Palette.getColors().length; i++) { + Color col = new Color(Palette.getColors()[i]); + colorVector[i].setBackground(col); + colorVector[i].setToolTipText("Red = " + col.getRed() + + ", Green = " + col.getGreen() + ", Blue = " + col.getBlue()); + } + } else if (initialized) { + for (JLabel label : colorVector) { + label.setBackground(Color.BLACK); + label.setToolTipText(null); + } + } + } + + // Asynchronous reset - returns the logic into initial state + public void reset() { + myFont = new Font("Courier New", Font.PLAIN, 12); + myText = null; + myInfo = ""; + myTest = ""; + spacing = 0; + + fontLoad = null; + fontDer = null; + + imageRender = null; + + fgColor = Color.YELLOW; + bgColor = Color.CYAN; + + outlineColor = Color.BLUE; + shadowColor = Color.GRAY; + + charVector = null; + + fontFormat = "FON1"; + + zoom = 100; // resetting zoom; damn forgot this + Palette.reset(); + + for (JLabel label : colorVector) { + label.setBackground(Color.BLACK); + label.setToolTipText("Red = " + Color.BLACK.getRed() + + ", Green = " + Color.BLACK.getGreen() + ", Blue = " + Color.BLACK.getBlue()); + } + } + + //-------------------------------------------------------------------------- + // C - GETTERS AND SETTERS + //-------------------------------------------------------------------------- + public boolean isInitialized() { + return initialized; + } + + public void setInitialized(boolean initialized) { + this.initialized = initialized; + } + + public Font getMyFont() { + return myFont; + } + + public void setMyFont(Font myFont) { + this.myFont = myFont; + } + + public String getMyText() { + return myText; + } + + public void setMyText(String myText) { + this.myText = myText; + } + + public String getMyInfo() { + return myInfo; + } + + public void setMyInfo(String myInfo) { + this.myInfo = myInfo; + } + + public String getMyTest() { + return myTest; + } + + public void setMyTest(String myTest) { + this.myTest = myTest; + } + + public int getSpacing() { + return spacing; + } + + public void setSpacing(int spacing) { + this.spacing = spacing; + } + + public int getLine_height() { + return line_height; + } + + public void setLine_height(int line_height) { + this.line_height = line_height; + } + + public int getSize_over() { + return size_over; + } + + public void setSize_over(int size_over) { + this.size_over = size_over; + } + + public int getSize_under() { + return size_under; + } + + public void setSize_under(int size_under) { + this.size_under = size_under; + } + + public DoomFont getFontLoad() { + return fontLoad; + } + + public void setFontLoad(DoomFont fontLoad) { + this.fontLoad = fontLoad; + } + + public DoomFont getFontDer() { + return fontDer; + } + + public void setFontDer(DoomFont fontDer) { + this.fontDer = fontDer; + } + + public BufferedImage getImageRender() { + return imageRender; + } + + public void setImageRender(BufferedImage imageRender) { + this.imageRender = imageRender; + } + + public Color getFgColor() { + return fgColor; + } + + public void setFgColor(Color fgColor) { + this.fgColor = fgColor; + } + + public Color getBgColor() { + return bgColor; + } + + public void setBgColor(Color bgColor) { + this.bgColor = bgColor; + } + + public Color getOutlineColor() { + return outlineColor; + } + + public void setOutlineColor(Color outlineColor) { + this.outlineColor = outlineColor; + } + + public JLabel[] getColorVector() { + return colorVector; + } + + public void setColorVector(JLabel[] colorVector) { + this.colorVector = colorVector; + } + + public JPanel getColorPanel() { + return colorPanel; + } + + public void setColorPanel(JPanel colorPanel) { + this.colorPanel = colorPanel; + } + + public DoomFontChar[] getCharVector() { + return charVector; + } + + public void setCharVector(DoomFontChar[] charVector) { + this.charVector = charVector; + } + + public String getFontFormat() { + return fontFormat; + } + + public void setFontFormat(String fontFormat) { + this.fontFormat = fontFormat; + } + + public int getZoom() { + return zoom; + } + + public void setZoom(int zoom) { + this.zoom = zoom; + } + + public Color getShadowColor() { + return shadowColor; + } + + public void setShadowColor(Color shadowColor) { + this.shadowColor = shadowColor; + } + +} diff --git a/src/rs/alexanderstojanovich/dfg/gui/GUISplashScreen.java b/src/rs/alexanderstojanovich/dfg/gui/GUISplashScreen.java index 8082bd7..8b67b35 100644 --- a/src/rs/alexanderstojanovich/dfg/gui/GUISplashScreen.java +++ b/src/rs/alexanderstojanovich/dfg/gui/GUISplashScreen.java @@ -1,5 +1,5 @@ -/* - * Copyright (C) 2019 Coa +/* + * Copyright (C) 2020 Alexander Stojanovich * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,84 +14,84 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package rs.alexanderstojanovich.dfg.gui; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Toolkit; -import java.net.URL; -import javax.swing.ImageIcon; -import javax.swing.JLabel; -import javax.swing.JProgressBar; -import javax.swing.JWindow; - -/** - * - * @author Coa - */ -public class GUISplashScreen extends JWindow implements Runnable { - - // path to splash screen image - private static final String SPLASH_FILE_NAME = "dfg_splash.png"; - - // label which contains splash image - private JLabel splashImgLbl = new JLabel(); - // progress bar (the progress is dependant on the GUI initialization progress) - private JProgressBar progressBar = new JProgressBar(0, 100); - - //-------------------------------------------------------------------------- - // A - CONSTRUCTORS - //-------------------------------------------------------------------------- - public GUISplashScreen() { - URL splashImgURL = this.getClass().getResource(GUI.RESOURCES_DIR + SPLASH_FILE_NAME); - ImageIcon splashImgIcon = new ImageIcon(splashImgURL); - this.splashImgLbl.setSize(splashImgIcon.getIconWidth(), splashImgIcon.getIconHeight()); - this.splashImgLbl.setIcon(splashImgIcon); - this.progressBar.setForeground(new Color(255, 150, 35)); - } - - //-------------------------------------------------------------------------- - // B - METHODS - //-------------------------------------------------------------------------- - // Center the JFrame which is splash screen into center of the screen - private void setUpPosition() { - Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); - this.setLocation(dim.width / 2 - this.getWidth() / 2, dim.height / 2 - this.getHeight() / 2); - } - - // call this method after constructor to add components and finish the effects - public void setUp() { - this.setLayout(new BorderLayout()); - this.setSize(splashImgLbl.getWidth(), splashImgLbl.getHeight()); - this.setUpPosition(); - this.getContentPane().add(splashImgLbl, BorderLayout.CENTER); - this.getContentPane().add(progressBar, BorderLayout.SOUTH); - this.setVisible(true); - } - - // this what this Runnable things to, updates it's progress bar depending on - // the GUI initialization tasks - @Override - public void run() { - while (GUI.getProgress() < 100) { - this.progressBar.setValue(GUI.getProgress()); - this.progressBar.validate(); - } - this.progressBar.setValue(GUI.getProgress()); - this.progressBar.validate(); - this.dispose(); - } - - //-------------------------------------------------------------------------- - // C - GETTERS - //-------------------------------------------------------------------------- - public JLabel getSplashImgLbl() { - return splashImgLbl; - } - - public JProgressBar getProgressBar() { - return progressBar; - } - -} +package rs.alexanderstojanovich.dfg.gui; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Toolkit; +import java.net.URL; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JProgressBar; +import javax.swing.JWindow; + +/** + * + * @author Alexander Stojanovich + */ +public class GUISplashScreen extends JWindow implements Runnable { + + // path to splash screen image + private static final String SPLASH_FILE_NAME = "dfg_splash.png"; + + // label which contains splash image + private JLabel splashImgLbl = new JLabel(); + // progress bar (the progress is dependant on the GUI initialization progress) + private JProgressBar progressBar = new JProgressBar(0, 100); + + //-------------------------------------------------------------------------- + // A - CONSTRUCTORS + //-------------------------------------------------------------------------- + public GUISplashScreen() { + URL splashImgURL = this.getClass().getResource(GUI.RESOURCES_DIR + SPLASH_FILE_NAME); + ImageIcon splashImgIcon = new ImageIcon(splashImgURL); + this.splashImgLbl.setSize(splashImgIcon.getIconWidth(), splashImgIcon.getIconHeight()); + this.splashImgLbl.setIcon(splashImgIcon); + this.progressBar.setForeground(new Color(255, 150, 35)); + } + + //-------------------------------------------------------------------------- + // B - METHODS + //-------------------------------------------------------------------------- + // Center the JFrame which is splash screen into center of the screen + private void setUpPosition() { + Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); + this.setLocation(dim.width / 2 - this.getWidth() / 2, dim.height / 2 - this.getHeight() / 2); + } + + // call this method after constructor to add components and finish the effects + public void setUp() { + this.setLayout(new BorderLayout()); + this.setSize(splashImgLbl.getWidth(), splashImgLbl.getHeight()); + this.setUpPosition(); + this.getContentPane().add(splashImgLbl, BorderLayout.CENTER); + this.getContentPane().add(progressBar, BorderLayout.SOUTH); + this.setVisible(true); + } + + // this what this Runnable things to, updates it's progress bar depending on + // the GUI initialization tasks + @Override + public void run() { + while (GUI.getProgress() < 100) { + this.progressBar.setValue(GUI.getProgress()); + this.progressBar.validate(); + } + this.progressBar.setValue(GUI.getProgress()); + this.progressBar.validate(); + this.dispose(); + } + + //-------------------------------------------------------------------------- + // C - GETTERS + //-------------------------------------------------------------------------- + public JLabel getSplashImgLbl() { + return splashImgLbl; + } + + public JProgressBar getProgressBar() { + return progressBar; + } + +} diff --git a/src/rs/alexanderstojanovich/dfg/util/ColorSample.java b/src/rs/alexanderstojanovich/dfg/util/ColorSample.java index 9433b74..f5b68b4 100644 --- a/src/rs/alexanderstojanovich/dfg/util/ColorSample.java +++ b/src/rs/alexanderstojanovich/dfg/util/ColorSample.java @@ -1,5 +1,5 @@ -/* - * Copyright (C) 2019 Coa +/* + * Copyright (C) 2020 Alexander Stojanovich * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,152 +14,152 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package rs.alexanderstojanovich.dfg.util; - -import java.awt.image.WritableRaster; - -/** - * - * @author Coa - */ -public class ColorSample { // used primarily for drawing outline in GUILogic - - private int red = 0; - private int green = 0; - private int blue = 0; - private int alpha = 0; - - //-------------------------------------------------------------------------- - // A - CONSTRUCTORS - //-------------------------------------------------------------------------- - public ColorSample() { - // .. EMPTY -> DEFAULT CONSTRUCTOR - } - - // Constructor for Essential Static Method - public ColorSample(int red, int green, int blue, int alpha) { - this.red = red; - this.green = green; - this.blue = blue; - this.alpha = alpha; - } - - //-------------------------------------------------------------------------- - // B - ESSENTIAL STATIC METHOD - //-------------------------------------------------------------------------- - // B1 - Get Sample from all of adjacent pixels on given the offset - public static ColorSample getSample(WritableRaster wr, int px, int py, int offset) { - // INPUT LOGIC - int len = 2 * offset + 1; - int[] offX = new int[len]; - int[] offY = new int[len]; - int e = 0; // used for indexing - for (int i = -offset; i <= offset; i++) { - if (i < 0) { - offX[e] = Math.max(px + i, 0); - offY[e] = Math.max(py + i, 0); - } else { - offX[e] = Math.min(px + i, wr.getWidth() - 1); - offY[e] = Math.min(py + i, wr.getHeight() - 1); - } - e++; - } - //---------------------------------------------------------------------- - // RED, GREEN, BLUE AND ALPHA SAMPLE - int sumR = 0; - int sumG = 0; - int sumB = 0; - int sumA = 0; - for (int i = 0; i < offX.length; i++) { - for (int j = 0; j < offY.length; j++) { - sumR += wr.getSample(offX[i], offY[j], 0); - sumG += wr.getSample(offX[i], offY[j], 1); - sumB += wr.getSample(offX[i], offY[j], 2); - sumA += wr.getSample(offX[i], offY[j], 3); - } - } - int avgR = sumR / (len * len); - int avgG = sumG / (len * len); - int avgB = sumB / (len * len); - int avgA = sumA / (len * len); - //---------------------------------------------------------------------- - // OUTPUT LOGIC - return new ColorSample(avgR, avgG, avgB, avgA); - } - - // B2 - Get Sample from all of adjacent pixels with Gauss kernel coefficients for single pass - public static ColorSample getGaussianBlurSample(WritableRaster wr, int px, int py) { - // INPUT LOGIC - final float a = 0.123317f; // up-left-right-down - final float b = 0.077847f; // diagonal - final float c = 0.195346f; // center - int[] offX = {Math.max(px - 1, 0), px, Math.min(px + 1, wr.getWidth() - 1)}; - int[] offY = {Math.max(py - 1, 0), py, Math.min(py + 1, wr.getHeight() - 1)}; - int red = 0; - int green = 0; - int blue = 0; - int alpha = 0; - // [0] - RED - red += b * (wr.getSample(offX[0], offY[0], 0) - + wr.getSample(offX[2], offY[0], 0) - + wr.getSample(offX[0], offY[2], 0) - + wr.getSample(offX[2], offY[2], 0)); - - red += c * (wr.getSample(offX[1], offY[1], 0)); - - red += a * (wr.getSample(offX[1], offY[0], 0) + wr.getSample(offX[0], offY[1], 0) - + wr.getSample(offX[1], offY[2], 0) + wr.getSample(offX[2], offY[1], 0)); - // [1] - GREEN - green += b * (wr.getSample(offX[0], offY[0], 1) - + wr.getSample(offX[2], offY[0], 1) - + wr.getSample(offX[0], offY[2], 1) - + wr.getSample(offX[2], offY[2], 1)); - - green += c * (wr.getSample(offX[1], offY[1], 1)); - - green += a * (wr.getSample(offX[1], offY[0], 1) + wr.getSample(offX[0], offY[1], 1) - + wr.getSample(offX[1], offY[2], 1) + wr.getSample(offX[2], offY[1], 1)); - // [2] - BLUE - blue += b * (wr.getSample(offX[0], offY[0], 2) - + wr.getSample(offX[2], offY[0], 2) - + wr.getSample(offX[0], offY[2], 2) - + wr.getSample(offX[2], offY[2], 2)); - - blue += c * (wr.getSample(offX[1], offY[1], 2)); - - blue += a * (wr.getSample(offX[1], offY[0], 2) + wr.getSample(offX[0], offY[1], 2) - + wr.getSample(offX[1], offY[2], 2) + wr.getSample(offX[2], offY[1], 2)); - // [3] - ALPHA - alpha += b * (wr.getSample(offX[0], offY[0], 3) - + wr.getSample(offX[2], offY[0], 3) - + wr.getSample(offX[0], offY[2], 3) - + wr.getSample(offX[2], offY[2], 3)); - - alpha += c * (wr.getSample(offX[1], offY[1], 3)); - - alpha += a * (wr.getSample(offX[1], offY[0], 3) + wr.getSample(offX[0], offY[1], 3) - + wr.getSample(offX[1], offY[2], 3) + wr.getSample(offX[2], offY[1], 3)); - // FINALLY, OUTPUT LOGIC - return new ColorSample(red, green, blue, alpha); - } - - //-------------------------------------------------------------------------- - // C - GETTERS - //-------------------------------------------------------------------------- - public int getRed() { - return red; - } - - public int getGreen() { - return green; - } - - public int getBlue() { - return blue; - } - - public int getAlpha() { - return alpha; - } - -} +package rs.alexanderstojanovich.dfg.util; + +import java.awt.image.WritableRaster; + +/** + * + * @author Alexander Stojanovich + */ +public class ColorSample { // used primarily for drawing outline in GUILogic + + private int red = 0; + private int green = 0; + private int blue = 0; + private int alpha = 0; + + //-------------------------------------------------------------------------- + // A - CONSTRUCTORS + //-------------------------------------------------------------------------- + public ColorSample() { + // .. EMPTY -> DEFAULT CONSTRUCTOR + } + + // Constructor for Essential Static Method + public ColorSample(int red, int green, int blue, int alpha) { + this.red = red; + this.green = green; + this.blue = blue; + this.alpha = alpha; + } + + //-------------------------------------------------------------------------- + // B - ESSENTIAL STATIC METHOD + //-------------------------------------------------------------------------- + // B1 - Get Sample from all of adjacent pixels on given the offset + public static ColorSample getSample(WritableRaster wr, int px, int py, int offset) { + // INPUT LOGIC + int len = 2 * offset + 1; + int[] offX = new int[len]; + int[] offY = new int[len]; + int e = 0; // used for indexing + for (int i = -offset; i <= offset; i++) { + if (i < 0) { + offX[e] = Math.max(px + i, 0); + offY[e] = Math.max(py + i, 0); + } else { + offX[e] = Math.min(px + i, wr.getWidth() - 1); + offY[e] = Math.min(py + i, wr.getHeight() - 1); + } + e++; + } + //---------------------------------------------------------------------- + // RED, GREEN, BLUE AND ALPHA SAMPLE + int sumR = 0; + int sumG = 0; + int sumB = 0; + int sumA = 0; + for (int i = 0; i < offX.length; i++) { + for (int j = 0; j < offY.length; j++) { + sumR += wr.getSample(offX[i], offY[j], 0); + sumG += wr.getSample(offX[i], offY[j], 1); + sumB += wr.getSample(offX[i], offY[j], 2); + sumA += wr.getSample(offX[i], offY[j], 3); + } + } + int avgR = sumR / (len * len); + int avgG = sumG / (len * len); + int avgB = sumB / (len * len); + int avgA = sumA / (len * len); + //---------------------------------------------------------------------- + // OUTPUT LOGIC + return new ColorSample(avgR, avgG, avgB, avgA); + } + + // B2 - Get Sample from all of adjacent pixels with Gauss kernel coefficients for single pass + public static ColorSample getGaussianBlurSample(WritableRaster wr, int px, int py) { + // INPUT LOGIC + final float a = 0.123317f; // up-left-right-down + final float b = 0.077847f; // diagonal + final float c = 0.195346f; // center + int[] offX = {Math.max(px - 1, 0), px, Math.min(px + 1, wr.getWidth() - 1)}; + int[] offY = {Math.max(py - 1, 0), py, Math.min(py + 1, wr.getHeight() - 1)}; + int red = 0; + int green = 0; + int blue = 0; + int alpha = 0; + // [0] - RED + red += b * (wr.getSample(offX[0], offY[0], 0) + + wr.getSample(offX[2], offY[0], 0) + + wr.getSample(offX[0], offY[2], 0) + + wr.getSample(offX[2], offY[2], 0)); + + red += c * (wr.getSample(offX[1], offY[1], 0)); + + red += a * (wr.getSample(offX[1], offY[0], 0) + wr.getSample(offX[0], offY[1], 0) + + wr.getSample(offX[1], offY[2], 0) + wr.getSample(offX[2], offY[1], 0)); + // [1] - GREEN + green += b * (wr.getSample(offX[0], offY[0], 1) + + wr.getSample(offX[2], offY[0], 1) + + wr.getSample(offX[0], offY[2], 1) + + wr.getSample(offX[2], offY[2], 1)); + + green += c * (wr.getSample(offX[1], offY[1], 1)); + + green += a * (wr.getSample(offX[1], offY[0], 1) + wr.getSample(offX[0], offY[1], 1) + + wr.getSample(offX[1], offY[2], 1) + wr.getSample(offX[2], offY[1], 1)); + // [2] - BLUE + blue += b * (wr.getSample(offX[0], offY[0], 2) + + wr.getSample(offX[2], offY[0], 2) + + wr.getSample(offX[0], offY[2], 2) + + wr.getSample(offX[2], offY[2], 2)); + + blue += c * (wr.getSample(offX[1], offY[1], 2)); + + blue += a * (wr.getSample(offX[1], offY[0], 2) + wr.getSample(offX[0], offY[1], 2) + + wr.getSample(offX[1], offY[2], 2) + wr.getSample(offX[2], offY[1], 2)); + // [3] - ALPHA + alpha += b * (wr.getSample(offX[0], offY[0], 3) + + wr.getSample(offX[2], offY[0], 3) + + wr.getSample(offX[0], offY[2], 3) + + wr.getSample(offX[2], offY[2], 3)); + + alpha += c * (wr.getSample(offX[1], offY[1], 3)); + + alpha += a * (wr.getSample(offX[1], offY[0], 3) + wr.getSample(offX[0], offY[1], 3) + + wr.getSample(offX[1], offY[2], 3) + wr.getSample(offX[2], offY[1], 3)); + // FINALLY, OUTPUT LOGIC + return new ColorSample(red, green, blue, alpha); + } + + //-------------------------------------------------------------------------- + // C - GETTERS + //-------------------------------------------------------------------------- + public int getRed() { + return red; + } + + public int getGreen() { + return green; + } + + public int getBlue() { + return blue; + } + + public int getAlpha() { + return alpha; + } + +} diff --git a/src/rs/alexanderstojanovich/dfg/util/Palette.java b/src/rs/alexanderstojanovich/dfg/util/Palette.java index 9ae7b79..130bfae 100644 --- a/src/rs/alexanderstojanovich/dfg/util/Palette.java +++ b/src/rs/alexanderstojanovich/dfg/util/Palette.java @@ -1,5 +1,5 @@ -/* - * Copyright (C) 2019 Coa +/* + * Copyright (C) 2020 Alexander Stojanovich * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,137 +14,137 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package rs.alexanderstojanovich.dfg.util; - -import java.awt.Color; -import java.io.IOException; -import java.io.InputStream; -import java.util.logging.Level; -import java.util.logging.Logger; -import rs.alexanderstojanovich.dfg.gui.GUI; - -/** - * - * @author Coa - */ -public class Palette { - - // All colors in the palette, required for an indexed model - private static int colors[]; - // Color buffer aka color map - private static byte colBuff[]; - // whether or not palette is loaded or not - private static boolean loaded = false; - - //-------------------------------------------------------------------------- - // A - STATIC METHODS - //-------------------------------------------------------------------------- - // load palette with given file name, index 0 - transparent - public static void load(String fileName) { - loaded = false; - InputStream in = Palette.class.getResourceAsStream(GUI.RESOURCES_DIR + fileName); - byte[] buff = null; - if (in != null) { - buff = new byte[768]; - try { - in.read(buff); - in.close(); - } catch (IOException ex) { - Logger.getLogger(Palette.class.getName()).log(Level.SEVERE, null, ex); - } - } - - if (buff != null) { - int index = 0; - colors = new int[256]; - colBuff = new byte[1024]; - for (int i = 0; i < buff.length / 3; i++) { - Color col = new Color(buff[i * 3] & 0xFF, buff[i * 3 + 1] & 0xFF, buff[i * 3 + 2] & 0xFF, (i == 0) ? 0 : 0xFF); - colors[index] = col.getRGB(); - colBuff[4 * index] = (byte) col.getRed(); - colBuff[4 * index + 1] = (byte) col.getGreen(); - colBuff[4 * index + 2] = (byte) col.getBlue(); - colBuff[4 * index + 3] = (byte) col.getAlpha(); - index++; - } - loaded = true; - } - } - - // generate 6-bit RGB palette (64 colors), index 0 - transparent - public static void load6bitRGB() { - loaded = false; - colors = new int[64]; - colBuff = new byte[256]; - int index = 0; - for (int r = 0; r < 4; r++) { - for (int g = 0; g < 4; g++) { - for (int b = 0; b < 4; b++) { - Color col = new Color( - Math.min(4 * (r << 6) / 3, 0xFF), - Math.min(4 * (g << 6) / 3, 0xFF), - Math.min(4 * (b << 6) / 3, 0xFF), - (index == 0) ? 0 : 0xFF - ); - colors[index] = col.getRGB(); - colBuff[4 * index] = (byte) col.getRed(); - colBuff[4 * index + 1] = (byte) col.getGreen(); - colBuff[4 * index + 2] = (byte) col.getBlue(); - colBuff[4 * index + 3] = (byte) col.getAlpha(); - index++; - } - } - } - loaded = true; - } - - // generate 8-bit RGB palette (256 colors), index 0 - transparent - public static void load8bitRGB() { - loaded = false; - colors = new int[256]; - colBuff = new byte[1024]; - int index = 0; - for (int r = 0; r < 8; r++) { - for (int g = 0; g < 8; g++) { - for (int b = 0; b < 4; b++) { - Color col = new Color( - Math.min(8 * (r << 5) / 7, 0xFF), - Math.min(8 * (g << 5) / 7, 0xFF), - Math.min(4 * (b << 6) / 3, 0xFF), - (index == 0) ? 0 : 0xFF - ); - colors[index] = col.getRGB(); - colBuff[4 * index] = (byte) col.getRed(); - colBuff[4 * index + 1] = (byte) col.getGreen(); - colBuff[4 * index + 2] = (byte) col.getBlue(); - colBuff[4 * index + 3] = (byte) col.getAlpha(); - index++; - } - } - } - loaded = true; - } - - // asynch reset - returns palette into initial state - public static void reset() { - loaded = false; - colors = null; - colBuff = null; - } - - //-------------------------------------------------------------------------- - // B - STATIC GETTERS - //-------------------------------------------------------------------------- - public static int[] getColors() { - return colors; - } - - public static byte[] getColBuff() { - return colBuff; - } - - public static boolean isLoaded() { - return loaded; - } - -} +package rs.alexanderstojanovich.dfg.util; + +import java.awt.Color; +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.Level; +import java.util.logging.Logger; +import rs.alexanderstojanovich.dfg.gui.GUI; + +/** + * + * @author Alexander Stojanovich + */ +public class Palette { + + // All colors in the palette, required for an indexed model + private static int colors[]; + // Color buffer aka color map + private static byte colBuff[]; + // whether or not palette is loaded or not + private static boolean loaded = false; + + //-------------------------------------------------------------------------- + // A - STATIC METHODS + //-------------------------------------------------------------------------- + // load palette with given file name, index 0 - transparent + public static void load(String fileName) { + loaded = false; + InputStream in = Palette.class.getResourceAsStream(GUI.RESOURCES_DIR + fileName); + byte[] buff = null; + if (in != null) { + buff = new byte[768]; + try { + in.read(buff); + in.close(); + } catch (IOException ex) { + Logger.getLogger(Palette.class.getName()).log(Level.SEVERE, null, ex); + } + } + + if (buff != null) { + int index = 0; + colors = new int[256]; + colBuff = new byte[1024]; + for (int i = 0; i < buff.length / 3; i++) { + Color col = new Color(buff[i * 3] & 0xFF, buff[i * 3 + 1] & 0xFF, buff[i * 3 + 2] & 0xFF, (i == 0) ? 0 : 0xFF); + colors[index] = col.getRGB(); + colBuff[4 * index] = (byte) col.getRed(); + colBuff[4 * index + 1] = (byte) col.getGreen(); + colBuff[4 * index + 2] = (byte) col.getBlue(); + colBuff[4 * index + 3] = (byte) col.getAlpha(); + index++; + } + loaded = true; + } + } + + // generate 6-bit RGB palette (64 colors), index 0 - transparent + public static void load6bitRGB() { + loaded = false; + colors = new int[64]; + colBuff = new byte[256]; + int index = 0; + for (int r = 0; r < 4; r++) { + for (int g = 0; g < 4; g++) { + for (int b = 0; b < 4; b++) { + Color col = new Color( + Math.min(4 * (r << 6) / 3, 0xFF), + Math.min(4 * (g << 6) / 3, 0xFF), + Math.min(4 * (b << 6) / 3, 0xFF), + (index == 0) ? 0 : 0xFF + ); + colors[index] = col.getRGB(); + colBuff[4 * index] = (byte) col.getRed(); + colBuff[4 * index + 1] = (byte) col.getGreen(); + colBuff[4 * index + 2] = (byte) col.getBlue(); + colBuff[4 * index + 3] = (byte) col.getAlpha(); + index++; + } + } + } + loaded = true; + } + + // generate 8-bit RGB palette (256 colors), index 0 - transparent + public static void load8bitRGB() { + loaded = false; + colors = new int[256]; + colBuff = new byte[1024]; + int index = 0; + for (int r = 0; r < 8; r++) { + for (int g = 0; g < 8; g++) { + for (int b = 0; b < 4; b++) { + Color col = new Color( + Math.min(8 * (r << 5) / 7, 0xFF), + Math.min(8 * (g << 5) / 7, 0xFF), + Math.min(4 * (b << 6) / 3, 0xFF), + (index == 0) ? 0 : 0xFF + ); + colors[index] = col.getRGB(); + colBuff[4 * index] = (byte) col.getRed(); + colBuff[4 * index + 1] = (byte) col.getGreen(); + colBuff[4 * index + 2] = (byte) col.getBlue(); + colBuff[4 * index + 3] = (byte) col.getAlpha(); + index++; + } + } + } + loaded = true; + } + + // asynch reset - returns palette into initial state + public static void reset() { + loaded = false; + colors = null; + colBuff = null; + } + + //-------------------------------------------------------------------------- + // B - STATIC GETTERS + //-------------------------------------------------------------------------- + public static int[] getColors() { + return colors; + } + + public static byte[] getColBuff() { + return colBuff; + } + + public static boolean isLoaded() { + return loaded; + } + +}