From 282bb50ab4a5b08e0dfa3f35770fd37fee842929 Mon Sep 17 00:00:00 2001 From: DeDiamondPro <67508414+DeDiamondPro@users.noreply.github.com> Date: Sat, 1 Jun 2024 19:39:12 +0200 Subject: [PATCH] add support for style HTML tag --- build.gradle.kts | 2 +- .../minemark/elementa/style/MarkdownStyle.kt | 9 +- .../minecraft/style/MarkdownTextStyle.java | 8 +- .../minemark/MineMarkCoreBuilder.java | 65 +- .../minemark/MineMarkHtmlParser.java | 6 +- .../minemark/elements/BasicElement.java | 5 +- .../minemark/elements/ChildBasedElement.java | 5 +- .../minemark/elements/Inline.java | 3 + .../elements/SingleTagMultiElement.java | 17 +- .../minemark/elements/impl/LinkElement.java | 5 +- .../impl/formatting/CssStyleElement.java | 115 ++ .../minemark/style/TextStyleConfig.java | 16 +- .../minemark/utils/ColorFactory.java | 1648 +++++++++++++++++ 13 files changed, 1879 insertions(+), 25 deletions(-) create mode 100644 src/main/java/dev/dediamondpro/minemark/elements/impl/formatting/CssStyleElement.java create mode 100644 src/main/java/dev/dediamondpro/minemark/utils/ColorFactory.java diff --git a/build.gradle.kts b/build.gradle.kts index 9dda300..10e7075 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -21,7 +21,7 @@ plugins { } group = "dev.dediamondpro" -version = "1.1.0+localtest6" +version = "1.1.0+localtest15" repositories { mavenCentral() diff --git a/elementa/src/main/kotlin/dev/dediamondpro/minemark/elementa/style/MarkdownStyle.kt b/elementa/src/main/kotlin/dev/dediamondpro/minemark/elementa/style/MarkdownStyle.kt index 125ae56..6fbe7c5 100644 --- a/elementa/src/main/kotlin/dev/dediamondpro/minemark/elementa/style/MarkdownStyle.kt +++ b/elementa/src/main/kotlin/dev/dediamondpro/minemark/elementa/style/MarkdownStyle.kt @@ -23,6 +23,7 @@ import dev.dediamondpro.minemark.style.* import gg.essential.elementa.font.DefaultFonts import gg.essential.elementa.font.FontProvider import java.awt.Color +import java.util.function.Function data class MarkdownStyle @JvmOverloads constructor( private val textStyle: MarkdownTextStyle = MarkdownTextStyle( @@ -64,5 +65,9 @@ data class MarkdownStyle @JvmOverloads constructor( } class MarkdownTextStyle( - defaultFontSize: Float, defaultTextColor: Color?, padding: Float, val font: FontProvider -) : TextStyleConfig(defaultFontSize, defaultTextColor, padding) \ No newline at end of file + defaultFontSize: Float, + defaultTextColor: Color?, + padding: Float, + val font: FontProvider, + adaptFontSize: Function = Function { it / 16f } +) : TextStyleConfig(defaultFontSize, defaultTextColor, padding, adaptFontSize) \ No newline at end of file diff --git a/minecraft/src/main/java/dev/dediamondpro/minemark/minecraft/style/MarkdownTextStyle.java b/minecraft/src/main/java/dev/dediamondpro/minemark/minecraft/style/MarkdownTextStyle.java index f010a23..db69202 100644 --- a/minecraft/src/main/java/dev/dediamondpro/minemark/minecraft/style/MarkdownTextStyle.java +++ b/minecraft/src/main/java/dev/dediamondpro/minemark/minecraft/style/MarkdownTextStyle.java @@ -20,12 +20,18 @@ import dev.dediamondpro.minemark.style.TextStyleConfig; import java.awt.*; +import java.util.function.Function; public class MarkdownTextStyle extends TextStyleConfig { private final boolean hasShadow; + public MarkdownTextStyle(float defaultFontSize, Color defaultTextColor, float padding, boolean hasShadow, Function adaptFontSize) { + super(defaultFontSize, defaultTextColor, padding, adaptFontSize); + this.hasShadow = hasShadow; + } + public MarkdownTextStyle(float defaultFontSize, Color defaultTextColor, float padding, boolean hasShadow) { - super(defaultFontSize, defaultTextColor, padding); + super(defaultFontSize, defaultTextColor, padding, (size) -> size / 16f); this.hasShadow = hasShadow; } diff --git a/src/main/java/dev/dediamondpro/minemark/MineMarkCoreBuilder.java b/src/main/java/dev/dediamondpro/minemark/MineMarkCoreBuilder.java index a6ba64b..e09327a 100644 --- a/src/main/java/dev/dediamondpro/minemark/MineMarkCoreBuilder.java +++ b/src/main/java/dev/dediamondpro/minemark/MineMarkCoreBuilder.java @@ -21,6 +21,7 @@ import dev.dediamondpro.minemark.elements.impl.LinkElement; import dev.dediamondpro.minemark.elements.impl.ParagraphElement; import dev.dediamondpro.minemark.elements.impl.formatting.AlignmentElement; +import dev.dediamondpro.minemark.elements.impl.formatting.CssStyleElement; import dev.dediamondpro.minemark.elements.impl.formatting.FormattingElement; import dev.dediamondpro.minemark.elements.impl.list.ListHolderElement; import dev.dediamondpro.minemark.elements.impl.table.TableHolderElement; @@ -78,8 +79,8 @@ public MineMarkCoreBuilder addElement(@NotNull List tags, @NotNull /** * Add a supported element to be used * - * @param tag HTML tags the element will be applied to - * @param element An {@link TagBasedElementCreator.BasicElementCreator} of that element + * @param tag HTML tags the element will be applied to + * @param element An {@link TagBasedElementCreator.BasicElementCreator} of that element */ public MineMarkCoreBuilder addElement(@NotNull String tag, @NotNull TagBasedElementCreator.BasicElementCreator element) { return addElement(Collections.singletonList(tag), element); @@ -95,6 +96,50 @@ public MineMarkCoreBuilder addElement(@NotNull Elements elementName, @NotN return addElement(elementName.tags, element); } + /** + * Add a supported element to be used + * + * @param position The position the element should be added at + * @param element An {@link ElementCreator} of that element + */ + public MineMarkCoreBuilder addElement(int position, @NotNull ElementCreator element) { + this.elements.add(position, element); + return this; + } + + /** + * Add a supported element to be used + * + * @param position The position the element should be added at + * @param tags HTML tags the element will be applied to + * @param element An {@link ElementCreator} of that element + */ + public MineMarkCoreBuilder addElement(int position, @NotNull List tags, @NotNull TagBasedElementCreator.BasicElementCreator element) { + return addElement(position, new TagBasedElementCreator<>(tags, element)); + } + + /** + * Add a supported element to be used + * + * @param position The position the element should be added at + * @param tag HTML tags the element will be applied to + * @param element An {@link TagBasedElementCreator.BasicElementCreator} of that element + */ + public MineMarkCoreBuilder addElement(int position, @NotNull String tag, @NotNull TagBasedElementCreator.BasicElementCreator element) { + return addElement(position, Collections.singletonList(tag), element); + } + + /** + * Add a supported element to be used + * + * @param position The position the element should be added at + * @param elementName HTML tags the element will be applied to + * @param element An {@link TagBasedElementCreator.BasicElementCreator} of that element + */ + public MineMarkCoreBuilder addElement(int position, @NotNull Elements elementName, @NotNull TagBasedElementCreator.BasicElementCreator element) { + return addElement(position, elementName.tags, element); + } + /** * Add supported elements to be used * @@ -143,13 +188,15 @@ public MineMarkCore build() { throw new IllegalArgumentException("A text element has to be provided by using \"setTextElement(textElement\""); } if (withDefaultElements) { - addElement(Elements.PARAGRAPH, ParagraphElement::new); - addElement(Elements.FORMATTING, FormattingElement::new); - addElement(Elements.LINK, LinkElement::new); - addElement(Elements.LIST_PARENT, ListHolderElement::new); - addElement(Elements.TABLE, TableHolderElement::new); - addElement(Elements.TABLE_ROW, TableRowElement::new); - addElement(new AlignmentElement.AlignmentElementCreator<>()); + // Add default (formatting) elements, these are added first so they take priority and their formatting applies properly + addElement(0, Elements.PARAGRAPH, ParagraphElement::new); + addElement(0, Elements.FORMATTING, FormattingElement::new); + addElement(0, Elements.LINK, LinkElement::new); + addElement(0, Elements.LIST_PARENT, ListHolderElement::new); + addElement(0, Elements.TABLE, TableHolderElement::new); + addElement(0, Elements.TABLE_ROW, TableRowElement::new); + addElement(0, new AlignmentElement.AlignmentElementCreator<>()); + addElement(0, new CssStyleElement.CssStyleElementCreator<>()); } return new MineMarkCore<>(textElement, elements, extensions, urlSanitizer); } diff --git a/src/main/java/dev/dediamondpro/minemark/MineMarkHtmlParser.java b/src/main/java/dev/dediamondpro/minemark/MineMarkHtmlParser.java index 0a173fe..ef64c18 100644 --- a/src/main/java/dev/dediamondpro/minemark/MineMarkHtmlParser.java +++ b/src/main/java/dev/dediamondpro/minemark/MineMarkHtmlParser.java @@ -60,7 +60,7 @@ public void startElement(String uri, String localName, String qName, Attributes } addText(); Element newElement = createElement(style, currentElement.getLayoutStyle(), currentElement, qName, attributes); - currentElement = newElement != null ? newElement : new EmptyElement<>(style, layoutStyle, currentElement, qName, attributes); + currentElement = newElement != null ? newElement : new EmptyElement<>(style, currentElement.getLayoutStyle(), currentElement, qName, attributes); } @Override @@ -140,9 +140,9 @@ private Element createElement(S style, LayoutStyle layoutStyle, @NotNull E if (multipleElement == null) { multipleElement = new SingleTagMultiElement<>(style, layoutStyle, parent, qName, attributes); - multipleElement.addElement(elementCreator, style, layoutStyle, qName, attributes); + multipleElement.addElement(elementCreator); } - multipleElement.addElement(element, style, layoutStyle, qName, attributes); + multipleElement.addElement(element); } return multipleElement != null ? multipleElement : elementCreator != null ? elementCreator.createElement(style, layoutStyle, parent, qName, attributes) : null; diff --git a/src/main/java/dev/dediamondpro/minemark/elements/BasicElement.java b/src/main/java/dev/dediamondpro/minemark/elements/BasicElement.java index 192ef53..7a5330c 100644 --- a/src/main/java/dev/dediamondpro/minemark/elements/BasicElement.java +++ b/src/main/java/dev/dediamondpro/minemark/elements/BasicElement.java @@ -46,12 +46,13 @@ public void generateLayout(LayoutData layoutData, R renderData) { float width = getWidth(layoutData, renderData); float height = getHeight(layoutData, renderData); float padding = getPadding(layoutData, renderData); - if ((!(this instanceof Inline) && layoutData.isLineOccupied()) || layoutData.getX() + width > layoutData.getMaxWidth()) { + boolean inline = this instanceof Inline && (((Inline) this).isInline()); + if ((!inline && layoutData.isLineOccupied()) || layoutData.getX() + width > layoutData.getMaxWidth()) { layoutData.nextLine(); } layoutData.updatePadding(padding); position = layoutData.addElement(layoutStyle.getAlignment(), width, height); - if (!(this instanceof Inline) && layoutData.isLineOccupied()) { + if (!inline && layoutData.isLineOccupied()) { layoutData.nextLine(); } } diff --git a/src/main/java/dev/dediamondpro/minemark/elements/ChildBasedElement.java b/src/main/java/dev/dediamondpro/minemark/elements/ChildBasedElement.java index 5cf7d37..6b23db2 100644 --- a/src/main/java/dev/dediamondpro/minemark/elements/ChildBasedElement.java +++ b/src/main/java/dev/dediamondpro/minemark/elements/ChildBasedElement.java @@ -33,7 +33,8 @@ public ChildBasedElement(@NotNull S style, @NotNull LayoutStyle layoutStyle, @Nu @Override @ApiStatus.Internal public void generateLayout(LayoutData layoutData, R renderData) { - if (!(this instanceof Inline) && layoutData.isLineOccupied()) { + boolean inline = this instanceof Inline && (((Inline) this).isInline()); + if (!inline && layoutData.isLineOccupied()) { layoutData.nextLine(); } float padding = getPadding(layoutData, renderData); @@ -42,7 +43,7 @@ public void generateLayout(LayoutData layoutData, R renderData) { child.generateLayout(layoutData, renderData); } layoutData.updateBottomSpacing(padding); - if (!(this instanceof Inline)) { + if (!inline) { layoutData.nextLine(); } } diff --git a/src/main/java/dev/dediamondpro/minemark/elements/Inline.java b/src/main/java/dev/dediamondpro/minemark/elements/Inline.java index a305070..d13d3de 100644 --- a/src/main/java/dev/dediamondpro/minemark/elements/Inline.java +++ b/src/main/java/dev/dediamondpro/minemark/elements/Inline.java @@ -19,4 +19,7 @@ public interface Inline { + default boolean isInline() { + return true; + } } diff --git a/src/main/java/dev/dediamondpro/minemark/elements/SingleTagMultiElement.java b/src/main/java/dev/dediamondpro/minemark/elements/SingleTagMultiElement.java index 37af437..2893bfc 100644 --- a/src/main/java/dev/dediamondpro/minemark/elements/SingleTagMultiElement.java +++ b/src/main/java/dev/dediamondpro/minemark/elements/SingleTagMultiElement.java @@ -48,10 +48,21 @@ public void generateLayout(LayoutData layoutData, R renderData) { @Override public @NotNull ArrayList> getChildren() { // We return the children of the deepest child so all new elements are added to the deepest child, - return deepestChildElement.children; + if (deepestChildElement == this) return children; + return deepestChildElement.getChildren(); } - public void addElement(@NotNull ElementCreator elementCreator, S style, LayoutStyle layoutStyle, @NotNull String qName, @Nullable Attributes attributes) { - deepestChildElement = elementCreator.createElement(style, layoutStyle, deepestChildElement, qName, attributes); + @Override + public LayoutStyle getLayoutStyle() { + if (deepestChildElement == this) return layoutStyle; + return deepestChildElement.getLayoutStyle(); + } + + public void addElement(@NotNull ElementCreator elementCreator) { + assert parent != null; + Element newElement = elementCreator.createElement(style, getLayoutStyle(), parent, qName, attributes); + parent.getChildren().remove(newElement); + deepestChildElement.getChildren().add(newElement); + deepestChildElement = newElement; } } diff --git a/src/main/java/dev/dediamondpro/minemark/elements/impl/LinkElement.java b/src/main/java/dev/dediamondpro/minemark/elements/impl/LinkElement.java index 1dd7d8d..4d74ec1 100644 --- a/src/main/java/dev/dediamondpro/minemark/elements/impl/LinkElement.java +++ b/src/main/java/dev/dediamondpro/minemark/elements/impl/LinkElement.java @@ -40,8 +40,11 @@ public LinkElement(@NotNull S style, @NotNull LayoutStyle layoutStyle, @Nullable this.link = attributes != null ? attributes.getValue("href") : null; if (link != null) { this.layoutStyle = this.layoutStyle.clone(); - this.layoutStyle.setTextColor(style.getLinkStyle().getTextColor()); this.layoutStyle.setPartOfLink(true); + // Only change the text color if no custom color is active + if (style.getTextStyle().getDefaultTextColor().equals(this.layoutStyle.getTextColor())) { + this.layoutStyle.setTextColor(style.getLinkStyle().getTextColor()); + } } } diff --git a/src/main/java/dev/dediamondpro/minemark/elements/impl/formatting/CssStyleElement.java b/src/main/java/dev/dediamondpro/minemark/elements/impl/formatting/CssStyleElement.java new file mode 100644 index 0000000..c47a131 --- /dev/null +++ b/src/main/java/dev/dediamondpro/minemark/elements/impl/formatting/CssStyleElement.java @@ -0,0 +1,115 @@ +/* + * This file is part of MineMark + * Copyright (C) 2024 DeDiamondPro + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License Version 3 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package dev.dediamondpro.minemark.elements.impl.formatting; + +import dev.dediamondpro.minemark.LayoutStyle; +import dev.dediamondpro.minemark.elements.ChildBasedElement; +import dev.dediamondpro.minemark.elements.Element; +import dev.dediamondpro.minemark.elements.Inline; +import dev.dediamondpro.minemark.elements.creators.ElementCreator; +import dev.dediamondpro.minemark.style.Style; +import dev.dediamondpro.minemark.utils.ColorFactory; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.xml.sax.Attributes; + +public class CssStyleElement extends ChildBasedElement implements Inline { + private boolean isInline = true; + + public CssStyleElement(@NotNull S style, @NotNull LayoutStyle layoutStyle, @Nullable Element parent, @NotNull String qName, @Nullable Attributes attributes) { + super(style, layoutStyle, parent, qName, attributes); + assert attributes != null; + String inlineCss = attributes.getValue("style"); + if (inlineCss == null) return; + this.layoutStyle = this.layoutStyle.clone(); + String[] parts = inlineCss.split(";"); + for (String part : parts) { + String[] propertyValue = part.split(":"); + if (propertyValue.length != 2) continue; + String property = propertyValue[0].trim(); + String value = propertyValue[1].trim(); + switch (property) { + case "color": + this.layoutStyle.setTextColor(ColorFactory.web(value)); + break; + case "text-align": + switch (value.toLowerCase()) { + case "left": + this.layoutStyle.setAlignment(LayoutStyle.Alignment.LEFT); + isInline = false; + break; + case "center": + this.layoutStyle.setAlignment(LayoutStyle.Alignment.CENTER); + isInline = false; + break; + case "right": + this.layoutStyle.setAlignment(LayoutStyle.Alignment.RIGHT); + isInline = false; + break; + } + break; + case "text-decoration": + for (String decoration : value.split(" ")) { + switch (decoration) { + case "underline": + this.layoutStyle.setUnderlined(true); + break; + case "line-through": + this.layoutStyle.setStrikethrough(true); + break; + } + } + break; + case "font-size": + Float fontSize = null; + if (value.endsWith("px")) { + fontSize = style.getTextStyle().adaptFontSize(Float.parseFloat(value.substring(0, value.length() - 2))); + } else if (value.endsWith("%")) { + fontSize = layoutStyle.getFontSize() * (Float.parseFloat(value.substring(0, value.length() - 1)) / 100); + } + if (fontSize != null) { + this.layoutStyle.setFontSize(fontSize); + } + break; + } + } + } + + @Override + public boolean isInline() { + return isInline; + } + + @Override + public String toString() { + return "CssStyleElement {" + attributes.getValue("style") + "}"; + } + + public static class CssStyleElementCreator implements ElementCreator { + + @Override + public Element createElement(S style, LayoutStyle layoutStyle, @NotNull Element parent, @NotNull String qName, @NotNull Attributes attributes) { + return new CssStyleElement<>(style, layoutStyle, parent, qName, attributes); + } + + @Override + public boolean appliesTo(S style, LayoutStyle layoutStyle, @NotNull Element parent, @NotNull String qName, @NotNull Attributes attributes) { + return attributes.getValue("style") != null; + } + } +} diff --git a/src/main/java/dev/dediamondpro/minemark/style/TextStyleConfig.java b/src/main/java/dev/dediamondpro/minemark/style/TextStyleConfig.java index 7f9699d..b25ded2 100644 --- a/src/main/java/dev/dediamondpro/minemark/style/TextStyleConfig.java +++ b/src/main/java/dev/dediamondpro/minemark/style/TextStyleConfig.java @@ -18,16 +18,26 @@ package dev.dediamondpro.minemark.style; import java.awt.*; +import java.util.function.Function; public class TextStyleConfig { private final float defaultFontSize; private final Color defaultTextColor; private final float padding; + /** + * Function used to adapt font size from px to the unit the rendering implementation uses + */ + private final Function adaptFontSize; - public TextStyleConfig(float defaultFontSize, Color defaultTextColor, float padding) { + public TextStyleConfig(float defaultFontSize, Color defaultTextColor, float padding, Function adaptFontSize) { this.defaultFontSize = defaultFontSize; this.defaultTextColor = defaultTextColor; this.padding = padding; + this.adaptFontSize = adaptFontSize; + } + + public TextStyleConfig(float defaultFontSize, Color defaultTextColor, float padding) { + this(defaultFontSize, defaultTextColor, padding, (size) -> size); } public float getDefaultFontSize() { @@ -41,4 +51,8 @@ public Color getDefaultTextColor() { public float getPadding() { return padding; } + + public float adaptFontSize(float size) { + return adaptFontSize.apply(size); + } } diff --git a/src/main/java/dev/dediamondpro/minemark/utils/ColorFactory.java b/src/main/java/dev/dediamondpro/minemark/utils/ColorFactory.java new file mode 100644 index 0000000..a135819 --- /dev/null +++ b/src/main/java/dev/dediamondpro/minemark/utils/ColorFactory.java @@ -0,0 +1,1648 @@ +// This code is taken from https://github.com/beryx/awt-color-factory/blob/master/src/main/java/org/beryx/awt/color/ColorFactory.java +// And is licensed under GPL-2.0 with classpath exception (https://openjdk.org/legal/gplv2+ce.html) + +/* + * Copyright (c) 2018 the original author or authors + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. The authors designate this + * particular file as subject to the "Classpath" exception as provided + * by the authors in the LICENSE file that accompanied this code. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + + +// This file contains code adapted from the OpenJFX project, which is part of OpenJDK. +// Specifically, code portions from the following classes appear (in changed or unchanged form) in this file: +// javafx.scene.paint.Color.java (http://hg.openjdk.java.net/openjfx/jfx-dev/rt/raw-file/2c80e5ef751e/modules/javafx.graphics/src/main/java/javafx/scene/paint/Color.java) +// com.sun.javafx.util.Utils.java (http://hg.openjdk.java.net/openjfx/jfx-dev/rt/raw-file/2c80e5ef751e/modules/javafx.graphics/src/main/java/com/sun/javafx/util/Utils.java) +// +// The original copyright header attached to the above mentioned sources is included below: + +/* + * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package dev.dediamondpro.minemark.utils; + +import java.awt.*; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +public final class ColorFactory { + /** + * Creates a {@code Color} based on the specified values in the HSB color model, + * and a given opacity. + * + * @param hue the hue, in degrees + * @param saturation the saturation, {@code 0.0 to 1.0} + * @param brightness the brightness, {@code 0.0 to 1.0} + * @param opacity the opacity, {@code 0.0 to 1.0} + * @return the {@code Color} + * @throws IllegalArgumentException if {@code saturation}, {@code brightness} or + * {@code opacity} are out of range + */ + public static Color hsb(double hue, double saturation, double brightness, double opacity) { + checkSB(saturation, brightness); + double[] rgb = HSBtoRGB(hue, saturation, brightness); + Color result = new Color((float)rgb[0], (float)rgb[1], (float)rgb[2], (float)opacity); + return result; + } + + /** + * Creates an opaque {@code Color} based on the specified values in the HSB color model. + * + * @param hue the hue, in degrees + * @param saturation the saturation, {@code 0.0 to 1.0} + * @param brightness the brightness, {@code 0.0 to 1.0} + * @return the {@code Color} + * @throws IllegalArgumentException if {@code saturation} or {@code brightness} are + * out of range + */ + public static Color hsb(double hue, double saturation, double brightness) { + return hsb(hue, saturation, brightness, 1.0); + } + + private static void checkSB(double saturation, double brightness) { + if (saturation < 0.0 || saturation > 1.0) { + throw new IllegalArgumentException("Color.hsb's saturation parameter (" + saturation + ") expects values 0.0-1.0"); + } + if (brightness < 0.0 || brightness > 1.0) { + throw new IllegalArgumentException("Color.hsb's brightness parameter (" + brightness + ") expects values 0.0-1.0"); + } + } + + /** + * Creates an RGB color specified with an HTML or CSS attribute string. + * + *

+ * This method supports the following formats: + *

    + *
  • Any standard HTML color name + *
  • An HTML long or short format hex string with an optional hex alpha + * channel. + * Hexadecimal values may be preceded by either {@code "0x"} or {@code "#"} + * and can either be 2 digits in the range {@code 00} to {@code 0xFF} or a + * single digit in the range {@code 0} to {@code F}. + *
  • An {@code rgb(r,g,b)} or {@code rgba(r,g,b,a)} format string. + * Each of the {@code r}, {@code g}, or {@code b} values can be an integer + * from 0 to 255 or a floating point percentage value from 0.0 to 100.0 + * followed by the percent ({@code %}) character. + * The alpha component, if present, is a + * floating point value from 0.0 to 1.0. Spaces are allowed before or + * after the numbers and between the percentage number and its percent + * sign ({@code %}). + *
  • An {@code hsl(h,s,l)} or {@code hsla(h,s,l,a)} format string. + * The {@code h} value is a floating point number from 0.0 to 360.0 + * representing the hue angle on a color wheel in degrees with + * {@code 0.0} or {@code 360.0} representing red, {@code 120.0} + * representing green, and {@code 240.0} representing blue. The + * {@code s} value is the saturation of the desired color represented + * as a floating point percentage from gray ({@code 0.0}) to + * the fully saturated color ({@code 100.0}) and the {@code l} value + * is the desired lightness or brightness of the desired color represented + * as a floating point percentage from black ({@code 0.0}) to the full + * brightness of the color ({@code 100.0}). + * The alpha component, if present, is a floating + * point value from 0.0 to 1.0. Spaces are allowed before or + * after the numbers and between the percentage number and its percent + * sign ({@code %}). + *
+ * + *

For formats without an alpha component and for named colors, opacity + * is set according to the {@code opacity} argument. For colors specified + * with an alpha component, the resulting opacity is a combination of the + * parsed alpha component and the {@code opacity} argument, so a + * transparent color becomes more transparent by specifying opacity.

+ * + *

Examples:

+ *
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Web Color Format Table
Web Format StringEquivalent constructor or factory call
Color.web("orange", 0.5);new Color(1.0, 0xA5/255.0, 0.0, 0.5)
Color.web("0xff66cc33", 0.5);new Color(1.0, 0.4, 0.8, 0.1)
Color.web("0xff66cc", 0.5);new Color(1.0, 0.4, 0.8, 0.5)
Color.web("#ff66cc", 0.5);new Color(1.0, 0.4, 0.8, 0.5)
Color.web("#f68", 0.5);new Color(1.0, 0.4, 0.8, 0.5)
Color.web("rgb(255,102,204)", 0.5);new Color(1.0, 0.4, 0.8, 0.5)
Color.web("rgb(100%,50%,50%)", 0.5);new Color(1.0, 0.5, 0.5, 0.5)
Color.web("rgb(255,50%,50%,0.25)", 0.5);new Color(1.0, 0.5, 0.5, 0.125)
Color.web("hsl(240,100%,100%)", 0.5);Color.hsb(240.0, 1.0, 1.0, 0.5)
+ * Color.web("hsla(120,0%,0%,0.25)", 0.5); + * + * Color.hsb(120.0, 0.0, 0.0, 0.125) + *
+ *
+ * + * @param colorString the name or numeric representation of the color + * in one of the supported formats + * @param opacity the opacity component in range from 0.0 (transparent) + * to 1.0 (opaque) + * @return the RGB color specified with the colorString + * @throws NullPointerException if {@code colorString} is {@code null} + * @throws IllegalArgumentException if {@code colorString} specifies + * an unsupported color name or contains an illegal numeric value + */ + public static Color web(String colorString, double opacity) { + if (colorString == null) { + throw new NullPointerException( + "The color components or name must be specified"); + } + if (colorString.isEmpty()) { + throw new IllegalArgumentException("Invalid color specification"); + } + + String color = colorString.toLowerCase(Locale.ROOT); + + if (color.startsWith("#")) { + color = color.substring(1); + } else if (color.startsWith("0x")) { + color = color.substring(2); + } else if (color.startsWith("rgb")) { + if (color.startsWith("(", 3)) { + return parseRGBColor(color, 4, false, opacity); + } else if (color.startsWith("a(", 3)) { + return parseRGBColor(color, 5, true, opacity); + } + } else if (color.startsWith("hsl")) { + if (color.startsWith("(", 3)) { + return parseHSLColor(color, 4, false, opacity); + } else if (color.startsWith("a(", 3)) { + return parseHSLColor(color, 5, true, opacity); + } + } else { + Color col = NamedColors.get(color); + if (col != null) { + if (opacity == 1.0) { + return col; + } else { + return new Color(col.getRed(), col.getGreen(), col.getBlue(), (int)(255 * opacity + 0.5)); + } + } + } + + int len = color.length(); + + try { + int r; + int g; + int b; + int a; + + if (len == 3) { + r = Integer.parseInt(color.substring(0, 1), 16); + g = Integer.parseInt(color.substring(1, 2), 16); + b = Integer.parseInt(color.substring(2, 3), 16); + return new Color(r / 15.0f, g / 15.0f, b / 15.0f, (float)opacity); + } else if (len == 4) { + r = Integer.parseInt(color.substring(0, 1), 16); + g = Integer.parseInt(color.substring(1, 2), 16); + b = Integer.parseInt(color.substring(2, 3), 16); + a = Integer.parseInt(color.substring(3, 4), 16); + return new Color(r / 15.0f, g / 15.0f, b / 15.0f, (float)(opacity * a / 15.0)); + } else if (len == 6) { + r = Integer.parseInt(color.substring(0, 2), 16); + g = Integer.parseInt(color.substring(2, 4), 16); + b = Integer.parseInt(color.substring(4, 6), 16); + return new Color(r, g, b, (int)(opacity*255+0.5)); + } else if (len == 8) { + r = Integer.parseInt(color.substring(0, 2), 16); + g = Integer.parseInt(color.substring(2, 4), 16); + b = Integer.parseInt(color.substring(4, 6), 16); + a = Integer.parseInt(color.substring(6, 8), 16); + return new Color(r, g, b, (int)(opacity * a + 0.5)); + } + } catch (NumberFormatException nfe) {} + + throw new IllegalArgumentException("Invalid color specification"); + } + + private static Color parseRGBColor(String color, int roff, + boolean hasAlpha, double a) + { + try { + int rend = color.indexOf(',', roff); + int gend = rend < 0 ? -1 : color.indexOf(',', rend+1); + int bend = gend < 0 ? -1 : color.indexOf(hasAlpha ? ',' : ')', gend+1); + int aend = hasAlpha ? (bend < 0 ? -1 : color.indexOf(')', bend+1)) : bend; + if (aend >= 0) { + double r = parseComponent(color, roff, rend, PARSE_COMPONENT); + double g = parseComponent(color, rend+1, gend, PARSE_COMPONENT); + double b = parseComponent(color, gend+1, bend, PARSE_COMPONENT); + if (hasAlpha) { + a *= parseComponent(color, bend+1, aend, PARSE_ALPHA); + } + return new Color((float)r, (float)g, (float)b, (float)a); + } + } catch (NumberFormatException nfe) {} + + throw new IllegalArgumentException("Invalid color specification"); + } + + private static Color parseHSLColor(String color, int hoff, + boolean hasAlpha, double a) + { + try { + int hend = color.indexOf(',', hoff); + int send = hend < 0 ? -1 : color.indexOf(',', hend+1); + int lend = send < 0 ? -1 : color.indexOf(hasAlpha ? ',' : ')', send+1); + int aend = hasAlpha ? (lend < 0 ? -1 : color.indexOf(')', lend+1)) : lend; + if (aend >= 0) { + double h = parseComponent(color, hoff, hend, PARSE_ANGLE); + double s = parseComponent(color, hend+1, send, PARSE_PERCENT); + double l = parseComponent(color, send+1, lend, PARSE_PERCENT); + if (hasAlpha) { + a *= parseComponent(color, lend+1, aend, PARSE_ALPHA); + } + return hsb(h, s, l, a); + } + } catch (NumberFormatException nfe) {} + + throw new IllegalArgumentException("Invalid color specification"); + } + + private static final int PARSE_COMPONENT = 0; // percent, or clamped to [0,255] => [0,1] + private static final int PARSE_PERCENT = 1; // clamped to [0,100]% => [0,1] + private static final int PARSE_ANGLE = 2; // clamped to [0,360] + private static final int PARSE_ALPHA = 3; // clamped to [0.0,1.0] + private static double parseComponent(String color, int off, int end, int type) { + color = color.substring(off, end).trim(); + if (color.endsWith("%")) { + if (type > PARSE_PERCENT) { + throw new IllegalArgumentException("Invalid color specification"); + } + type = PARSE_PERCENT; + color = color.substring(0, color.length()-1).trim(); + } else if (type == PARSE_PERCENT) { + throw new IllegalArgumentException("Invalid color specification"); + } + double c = ((type == PARSE_COMPONENT) + ? Integer.parseInt(color) + : Double.parseDouble(color)); + switch (type) { + case PARSE_ALPHA: + return (c < 0.0) ? 0.0 : ((c > 1.0) ? 1.0 : c); + case PARSE_PERCENT: + return (c <= 0.0) ? 0.0 : ((c >= 100.0) ? 1.0 : (c / 100.0)); + case PARSE_COMPONENT: + return (c <= 0.0) ? 0.0 : ((c >= 255.0) ? 1.0 : (c / 255.0)); + case PARSE_ANGLE: + return ((c < 0.0) + ? ((c % 360.0) + 360.0) + : ((c > 360.0) + ? (c % 360.0) + : c)); + } + + throw new IllegalArgumentException("Invalid color specification"); + } + + /** + * Creates an RGB color specified with an HTML or CSS attribute string. + * + *

+ * This method supports the following formats: + *

    + *
  • Any standard HTML color name + *
  • An HTML long or short format hex string with an optional hex alpha + * channel. + * Hexadecimal values may be preceded by either {@code "0x"} or {@code "#"} + * and can either be 2 digits in the range {@code 00} to {@code 0xFF} or a + * single digit in the range {@code 0} to {@code F}. + *
  • An {@code rgb(r,g,b)} or {@code rgba(r,g,b,a)} format string. + * Each of the {@code r}, {@code g}, or {@code b} values can be an integer + * from 0 to 255 or a floating point percentage value from 0.0 to 100.0 + * followed by the percent ({@code %}) character. + * The alpha component, if present, is a + * floating point value from 0.0 to 1.0. Spaces are allowed before or + * after the numbers and between the percentage number and its percent + * sign ({@code %}). + *
  • An {@code hsl(h,s,l)} or {@code hsla(h,s,l,a)} format string. + * The {@code h} value is a floating point number from 0.0 to 360.0 + * representing the hue angle on a color wheel in degrees with + * {@code 0.0} or {@code 360.0} representing red, {@code 120.0} + * representing green, and {@code 240.0} representing blue. The + * {@code s} value is the saturation of the desired color represented + * as a floating point percentage from gray ({@code 0.0}) to + * the fully saturated color ({@code 100.0}) and the {@code l} value + * is the desired lightness or brightness of the desired color represented + * as a floating point percentage from black ({@code 0.0}) to the full + * brightness of the color ({@code 100.0}). + * The alpha component, if present, is a floating + * point value from 0.0 to 1.0. Spaces are allowed before or + * after the numbers and between the percentage number and its percent + * sign ({@code %}). + *
+ * + *

Examples:

+ *
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Web Color Format Table
Web Format StringEquivalent constant or factory call
Color.web("orange");Color.ORANGE
Color.web("0xff668840");Color.rgb(255, 102, 136, 0.25)
Color.web("0xff6688");Color.rgb(255, 102, 136, 1.0)
Color.web("#ff6688");Color.rgb(255, 102, 136, 1.0)
Color.web("#f68");Color.rgb(255, 102, 136, 1.0)
Color.web("rgb(255,102,136)");Color.rgb(255, 102, 136, 1.0)
Color.web("rgb(100%,50%,50%)");Color.rgb(255, 128, 128, 1.0)
Color.web("rgb(255,50%,50%,0.25)");Color.rgb(255, 128, 128, 0.25)
Color.web("hsl(240,100%,100%)");Color.hsb(240.0, 1.0, 1.0, 1.0)
+ * Color.web("hsla(120,0%,0%,0.25)"); + * + * Color.hsb(120.0, 0.0, 0.0, 0.25) + *
+ *
+ * + * @param colorString the name or numeric representation of the color + * in one of the supported formats + * @return an RGB color + * @throws NullPointerException if {@code colorString} is {@code null} + * @throws IllegalArgumentException if {@code colorString} specifies + * an unsupported color name or contains an illegal numeric value + */ + public static Color web(String colorString) { + return web(colorString, 1.0); + } + + /** + * Creates a color value from a string representation. The format + * of the string representation is the same as in {@link #web(String)}. + * + * @param value the string to convert + * @throws NullPointerException if the {@code value} is {@code null} + * @throws IllegalArgumentException if the {@code value} specifies + * an unsupported color name or illegal hexadecimal value + * @return a {@code Color} object holding the value represented + * by the string argument + * @see #web(String) + */ + public static Color valueOf(String value) { + if (value == null) { + throw new NullPointerException("color must be specified"); + } + + return web(value); + } + + private static int to32BitInteger(int red, int green, int blue, int alpha) { + int i = red; + i = i << 8; + i = i | green; + i = i << 8; + i = i | blue; + i = i << 8; + i = i | alpha; + return i; + } + + /** + * A fully transparent color with an ARGB value of #00000000. + */ + public static final Color TRANSPARENT = new Color(0f, 0f, 0f, 0f); + + /** + * The color alice blue with an RGB value of #F0F8FF + *
+ */ + public static final Color ALICEBLUE = new Color(0.9411765f, 0.972549f, 1.0f); + + /** + * The color antique white with an RGB value of #FAEBD7 + *
+ */ + public static final Color ANTIQUEWHITE = new Color(0.98039216f, 0.92156863f, 0.84313726f); + + /** + * The color aqua with an RGB value of #00FFFF + *
+ */ + public static final Color AQUA = new Color(0.0f, 1.0f, 1.0f); + + /** + * The color aquamarine with an RGB value of #7FFFD4 + *
+ */ + public static final Color AQUAMARINE = new Color(0.49803922f, 1.0f, 0.83137256f); + + /** + * The color azure with an RGB value of #F0FFFF + *
+ */ + public static final Color AZURE = new Color(0.9411765f, 1.0f, 1.0f); + + /** + * The color beige with an RGB value of #F5F5DC + *
+ */ + public static final Color BEIGE = new Color(0.9607843f, 0.9607843f, 0.8627451f); + + /** + * The color bisque with an RGB value of #FFE4C4 + *
+ */ + public static final Color BISQUE = new Color(1.0f, 0.89411765f, 0.76862746f); + + /** + * The color black with an RGB value of #000000 + *
+ */ + public static final Color BLACK = new Color(0.0f, 0.0f, 0.0f); + + /** + * The color blanched almond with an RGB value of #FFEBCD + *
+ */ + public static final Color BLANCHEDALMOND = new Color(1.0f, 0.92156863f, 0.8039216f); + + /** + * The color blue with an RGB value of #0000FF + *
+ */ + public static final Color BLUE = new Color(0.0f, 0.0f, 1.0f); + + /** + * The color blue violet with an RGB value of #8A2BE2 + *
+ */ + public static final Color BLUEVIOLET = new Color(0.5411765f, 0.16862746f, 0.8862745f); + + /** + * The color brown with an RGB value of #A52A2A + *
+ */ + public static final Color BROWN = new Color(0.64705884f, 0.16470589f, 0.16470589f); + + /** + * The color burly wood with an RGB value of #DEB887 + *
+ */ + public static final Color BURLYWOOD = new Color(0.87058824f, 0.72156864f, 0.5294118f); + + /** + * The color cadet blue with an RGB value of #5F9EA0 + *
+ */ + public static final Color CADETBLUE = new Color(0.37254903f, 0.61960787f, 0.627451f); + + /** + * The color chartreuse with an RGB value of #7FFF00 + *
+ */ + public static final Color CHARTREUSE = new Color(0.49803922f, 1.0f, 0.0f); + + /** + * The color chocolate with an RGB value of #D2691E + *
+ */ + public static final Color CHOCOLATE = new Color(0.8235294f, 0.4117647f, 0.11764706f); + + /** + * The color coral with an RGB value of #FF7F50 + *
+ */ + public static final Color CORAL = new Color(1.0f, 0.49803922f, 0.3137255f); + + /** + * The color cornflower blue with an RGB value of #6495ED + *
+ */ + public static final Color CORNFLOWERBLUE = new Color(0.39215687f, 0.58431375f, 0.92941177f); + + /** + * The color cornsilk with an RGB value of #FFF8DC + *
+ */ + public static final Color CORNSILK = new Color(1.0f, 0.972549f, 0.8627451f); + + /** + * The color crimson with an RGB value of #DC143C + *
+ */ + public static final Color CRIMSON = new Color(0.8627451f, 0.078431375f, 0.23529412f); + + /** + * The color cyan with an RGB value of #00FFFF + *
+ */ + public static final Color CYAN = new Color(0.0f, 1.0f, 1.0f); + + /** + * The color dark blue with an RGB value of #00008B + *
+ */ + public static final Color DARKBLUE = new Color(0.0f, 0.0f, 0.54509807f); + + /** + * The color dark cyan with an RGB value of #008B8B + *
+ */ + public static final Color DARKCYAN = new Color(0.0f, 0.54509807f, 0.54509807f); + + /** + * The color dark goldenrod with an RGB value of #B8860B + *
+ */ + public static final Color DARKGOLDENROD = new Color(0.72156864f, 0.5254902f, 0.043137256f); + + /** + * The color dark gray with an RGB value of #A9A9A9 + *
+ */ + public static final Color DARKGRAY = new Color(0.6627451f, 0.6627451f, 0.6627451f); + + /** + * The color dark green with an RGB value of #006400 + *
+ */ + public static final Color DARKGREEN = new Color(0.0f, 0.39215687f, 0.0f); + + /** + * The color dark grey with an RGB value of #A9A9A9 + *
+ */ + public static final Color DARKGREY = DARKGRAY; + + /** + * The color dark khaki with an RGB value of #BDB76B + *
+ */ + public static final Color DARKKHAKI = new Color(0.7411765f, 0.7176471f, 0.41960785f); + + /** + * The color dark magenta with an RGB value of #8B008B + *
+ */ + public static final Color DARKMAGENTA = new Color(0.54509807f, 0.0f, 0.54509807f); + + /** + * The color dark olive green with an RGB value of #556B2F + *
+ */ + public static final Color DARKOLIVEGREEN = new Color(0.33333334f, 0.41960785f, 0.18431373f); + + /** + * The color dark orange with an RGB value of #FF8C00 + *
+ */ + public static final Color DARKORANGE = new Color(1.0f, 0.54901963f, 0.0f); + + /** + * The color dark orchid with an RGB value of #9932CC + *
+ */ + public static final Color DARKORCHID = new Color(0.6f, 0.19607843f, 0.8f); + + /** + * The color dark red with an RGB value of #8B0000 + *
+ */ + public static final Color DARKRED = new Color(0.54509807f, 0.0f, 0.0f); + + /** + * The color dark salmon with an RGB value of #E9967A + *
+ */ + public static final Color DARKSALMON = new Color(0.9137255f, 0.5882353f, 0.47843137f); + + /** + * The color dark sea green with an RGB value of #8FBC8F + *
+ */ + public static final Color DARKSEAGREEN = new Color(0.56078434f, 0.7372549f, 0.56078434f); + + /** + * The color dark slate blue with an RGB value of #483D8B + *
+ */ + public static final Color DARKSLATEBLUE = new Color(0.28235295f, 0.23921569f, 0.54509807f); + + /** + * The color dark slate gray with an RGB value of #2F4F4F + *
+ */ + public static final Color DARKSLATEGRAY = new Color(0.18431373f, 0.30980393f, 0.30980393f); + + /** + * The color dark slate grey with an RGB value of #2F4F4F + *
+ */ + public static final Color DARKSLATEGREY = DARKSLATEGRAY; + + /** + * The color dark turquoise with an RGB value of #00CED1 + *
+ */ + public static final Color DARKTURQUOISE = new Color(0.0f, 0.80784315f, 0.81960785f); + + /** + * The color dark violet with an RGB value of #9400D3 + *
+ */ + public static final Color DARKVIOLET = new Color(0.5803922f, 0.0f, 0.827451f); + + /** + * The color deep pink with an RGB value of #FF1493 + *
+ */ + public static final Color DEEPPINK = new Color(1.0f, 0.078431375f, 0.5764706f); + + /** + * The color deep sky blue with an RGB value of #00BFFF + *
+ */ + public static final Color DEEPSKYBLUE = new Color(0.0f, 0.7490196f, 1.0f); + + /** + * The color dim gray with an RGB value of #696969 + *
+ */ + public static final Color DIMGRAY = new Color(0.4117647f, 0.4117647f, 0.4117647f); + + /** + * The color dim grey with an RGB value of #696969 + *
+ */ + public static final Color DIMGREY = DIMGRAY; + + /** + * The color dodger blue with an RGB value of #1E90FF + *
+ */ + public static final Color DODGERBLUE = new Color(0.11764706f, 0.5647059f, 1.0f); + + /** + * The color firebrick with an RGB value of #B22222 + *
+ */ + public static final Color FIREBRICK = new Color(0.69803923f, 0.13333334f, 0.13333334f); + + /** + * The color floral white with an RGB value of #FFFAF0 + *
+ */ + public static final Color FLORALWHITE = new Color(1.0f, 0.98039216f, 0.9411765f); + + /** + * The color forest green with an RGB value of #228B22 + *
+ */ + public static final Color FORESTGREEN = new Color(0.13333334f, 0.54509807f, 0.13333334f); + + /** + * The color fuchsia with an RGB value of #FF00FF + *
+ */ + public static final Color FUCHSIA = new Color(1.0f, 0.0f, 1.0f); + + /** + * The color gainsboro with an RGB value of #DCDCDC + *
+ */ + public static final Color GAINSBORO = new Color(0.8627451f, 0.8627451f, 0.8627451f); + + /** + * The color ghost white with an RGB value of #F8F8FF + *
+ */ + public static final Color GHOSTWHITE = new Color(0.972549f, 0.972549f, 1.0f); + + /** + * The color gold with an RGB value of #FFD700 + *
+ */ + public static final Color GOLD = new Color(1.0f, 0.84313726f, 0.0f); + + /** + * The color goldenrod with an RGB value of #DAA520 + *
+ */ + public static final Color GOLDENROD = new Color(0.85490197f, 0.64705884f, 0.1254902f); + + /** + * The color gray with an RGB value of #808080 + *
+ */ + public static final Color GRAY = new Color(0.5019608f, 0.5019608f, 0.5019608f); + + /** + * The color green with an RGB value of #008000 + *
+ */ + public static final Color GREEN = new Color(0.0f, 0.5019608f, 0.0f); + + /** + * The color green yellow with an RGB value of #ADFF2F + *
+ */ + public static final Color GREENYELLOW = new Color(0.6784314f, 1.0f, 0.18431373f); + + /** + * The color grey with an RGB value of #808080 + *
+ */ + public static final Color GREY = GRAY; + + /** + * The color honeydew with an RGB value of #F0FFF0 + *
+ */ + public static final Color HONEYDEW = new Color(0.9411765f, 1.0f, 0.9411765f); + + /** + * The color hot pink with an RGB value of #FF69B4 + *
+ */ + public static final Color HOTPINK = new Color(1.0f, 0.4117647f, 0.7058824f); + + /** + * The color indian red with an RGB value of #CD5C5C + *
+ */ + public static final Color INDIANRED = new Color(0.8039216f, 0.36078432f, 0.36078432f); + + /** + * The color indigo with an RGB value of #4B0082 + *
+ */ + public static final Color INDIGO = new Color(0.29411766f, 0.0f, 0.50980395f); + + /** + * The color ivory with an RGB value of #FFFFF0 + *
+ */ + public static final Color IVORY = new Color(1.0f, 1.0f, 0.9411765f); + + /** + * The color khaki with an RGB value of #F0E68C + *
+ */ + public static final Color KHAKI = new Color(0.9411765f, 0.9019608f, 0.54901963f); + + /** + * The color lavender with an RGB value of #E6E6FA + *
+ */ + public static final Color LAVENDER = new Color(0.9019608f, 0.9019608f, 0.98039216f); + + /** + * The color lavender blush with an RGB value of #FFF0F5 + *
+ */ + public static final Color LAVENDERBLUSH = new Color(1.0f, 0.9411765f, 0.9607843f); + + /** + * The color lawn green with an RGB value of #7CFC00 + *
+ */ + public static final Color LAWNGREEN = new Color(0.4862745f, 0.9882353f, 0.0f); + + /** + * The color lemon chiffon with an RGB value of #FFFACD + *
+ */ + public static final Color LEMONCHIFFON = new Color(1.0f, 0.98039216f, 0.8039216f); + + /** + * The color light blue with an RGB value of #ADD8E6 + *
+ */ + public static final Color LIGHTBLUE = new Color(0.6784314f, 0.84705883f, 0.9019608f); + + /** + * The color light coral with an RGB value of #F08080 + *
+ */ + public static final Color LIGHTCORAL = new Color(0.9411765f, 0.5019608f, 0.5019608f); + + /** + * The color light cyan with an RGB value of #E0FFFF + *
+ */ + public static final Color LIGHTCYAN = new Color(0.8784314f, 1.0f, 1.0f); + + /** + * The color light goldenrod yellow with an RGB value of #FAFAD2 + *
+ */ + public static final Color LIGHTGOLDENRODYELLOW = new Color(0.98039216f, 0.98039216f, 0.8235294f); + + /** + * The color light gray with an RGB value of #D3D3D3 + *
+ */ + public static final Color LIGHTGRAY = new Color(0.827451f, 0.827451f, 0.827451f); + + /** + * The color light green with an RGB value of #90EE90 + *
+ */ + public static final Color LIGHTGREEN = new Color(0.5647059f, 0.93333334f, 0.5647059f); + + /** + * The color light grey with an RGB value of #D3D3D3 + *
+ */ + public static final Color LIGHTGREY = LIGHTGRAY; + + /** + * The color light pink with an RGB value of #FFB6C1 + *
+ */ + public static final Color LIGHTPINK = new Color(1.0f, 0.7137255f, 0.75686276f); + + /** + * The color light salmon with an RGB value of #FFA07A + *
+ */ + public static final Color LIGHTSALMON = new Color(1.0f, 0.627451f, 0.47843137f); + + /** + * The color light sea green with an RGB value of #20B2AA + *
+ */ + public static final Color LIGHTSEAGREEN = new Color(0.1254902f, 0.69803923f, 0.6666667f); + + /** + * The color light sky blue with an RGB value of #87CEFA + *
+ */ + public static final Color LIGHTSKYBLUE = new Color(0.5294118f, 0.80784315f, 0.98039216f); + + /** + * The color light slate gray with an RGB value of #778899 + *
+ */ + public static final Color LIGHTSLATEGRAY = new Color(0.46666667f, 0.53333336f, 0.6f); + + /** + * The color light slate grey with an RGB value of #778899 + *
+ */ + public static final Color LIGHTSLATEGREY = LIGHTSLATEGRAY; + + /** + * The color light steel blue with an RGB value of #B0C4DE + *
+ */ + public static final Color LIGHTSTEELBLUE = new Color(0.6901961f, 0.76862746f, 0.87058824f); + + /** + * The color light yellow with an RGB value of #FFFFE0 + *
+ */ + public static final Color LIGHTYELLOW = new Color(1.0f, 1.0f, 0.8784314f); + + /** + * The color lime with an RGB value of #00FF00 + *
+ */ + public static final Color LIME = new Color(0.0f, 1.0f, 0.0f); + + /** + * The color lime green with an RGB value of #32CD32 + *
+ */ + public static final Color LIMEGREEN = new Color(0.19607843f, 0.8039216f, 0.19607843f); + + /** + * The color linen with an RGB value of #FAF0E6 + *
+ */ + public static final Color LINEN = new Color(0.98039216f, 0.9411765f, 0.9019608f); + + /** + * The color magenta with an RGB value of #FF00FF + *
+ */ + public static final Color MAGENTA = new Color(1.0f, 0.0f, 1.0f); + + /** + * The color maroon with an RGB value of #800000 + *
+ */ + public static final Color MAROON = new Color(0.5019608f, 0.0f, 0.0f); + + /** + * The color medium aquamarine with an RGB value of #66CDAA + *
+ */ + public static final Color MEDIUMAQUAMARINE = new Color(0.4f, 0.8039216f, 0.6666667f); + + /** + * The color medium blue with an RGB value of #0000CD + *
+ */ + public static final Color MEDIUMBLUE = new Color(0.0f, 0.0f, 0.8039216f); + + /** + * The color medium orchid with an RGB value of #BA55D3 + *
+ */ + public static final Color MEDIUMORCHID = new Color(0.7294118f, 0.33333334f, 0.827451f); + + /** + * The color medium purple with an RGB value of #9370DB + *
+ */ + public static final Color MEDIUMPURPLE = new Color(0.5764706f, 0.4392157f, 0.85882354f); + + /** + * The color medium sea green with an RGB value of #3CB371 + *
+ */ + public static final Color MEDIUMSEAGREEN = new Color(0.23529412f, 0.7019608f, 0.44313726f); + + /** + * The color medium slate blue with an RGB value of #7B68EE + *
+ */ + public static final Color MEDIUMSLATEBLUE = new Color(0.48235294f, 0.40784314f, 0.93333334f); + + /** + * The color medium spring green with an RGB value of #00FA9A + *
+ */ + public static final Color MEDIUMSPRINGGREEN = new Color(0.0f, 0.98039216f, 0.6039216f); + + /** + * The color medium turquoise with an RGB value of #48D1CC + *
+ */ + public static final Color MEDIUMTURQUOISE = new Color(0.28235295f, 0.81960785f, 0.8f); + + /** + * The color medium violet red with an RGB value of #C71585 + *
+ */ + public static final Color MEDIUMVIOLETRED = new Color(0.78039217f, 0.08235294f, 0.52156866f); + + /** + * The color midnight blue with an RGB value of #191970 + *
+ */ + public static final Color MIDNIGHTBLUE = new Color(0.09803922f, 0.09803922f, 0.4392157f); + + /** + * The color mint cream with an RGB value of #F5FFFA + *
+ */ + public static final Color MINTCREAM = new Color(0.9607843f, 1.0f, 0.98039216f); + + /** + * The color misty rose with an RGB value of #FFE4E1 + *
+ */ + public static final Color MISTYROSE = new Color(1.0f, 0.89411765f, 0.88235295f); + + /** + * The color moccasin with an RGB value of #FFE4B5 + *
+ */ + public static final Color MOCCASIN = new Color(1.0f, 0.89411765f, 0.70980394f); + + /** + * The color navajo white with an RGB value of #FFDEAD + *
+ */ + public static final Color NAVAJOWHITE = new Color(1.0f, 0.87058824f, 0.6784314f); + + /** + * The color navy with an RGB value of #000080 + *
+ */ + public static final Color NAVY = new Color(0.0f, 0.0f, 0.5019608f); + + /** + * The color old lace with an RGB value of #FDF5E6 + *
+ */ + public static final Color OLDLACE = new Color(0.99215686f, 0.9607843f, 0.9019608f); + + /** + * The color olive with an RGB value of #808000 + *
+ */ + public static final Color OLIVE = new Color(0.5019608f, 0.5019608f, 0.0f); + + /** + * The color olive drab with an RGB value of #6B8E23 + *
+ */ + public static final Color OLIVEDRAB = new Color(0.41960785f, 0.5568628f, 0.13725491f); + + /** + * The color orange with an RGB value of #FFA500 + *
+ */ + public static final Color ORANGE = new Color(1.0f, 0.64705884f, 0.0f); + + /** + * The color orange red with an RGB value of #FF4500 + *
+ */ + public static final Color ORANGERED = new Color(1.0f, 0.27058825f, 0.0f); + + /** + * The color orchid with an RGB value of #DA70D6 + *
+ */ + public static final Color ORCHID = new Color(0.85490197f, 0.4392157f, 0.8392157f); + + /** + * The color pale goldenrod with an RGB value of #EEE8AA + *
+ */ + public static final Color PALEGOLDENROD = new Color(0.93333334f, 0.9098039f, 0.6666667f); + + /** + * The color pale green with an RGB value of #98FB98 + *
+ */ + public static final Color PALEGREEN = new Color(0.59607846f, 0.9843137f, 0.59607846f); + + /** + * The color pale turquoise with an RGB value of #AFEEEE + *
+ */ + public static final Color PALETURQUOISE = new Color(0.6862745f, 0.93333334f, 0.93333334f); + + /** + * The color pale violet red with an RGB value of #DB7093 + *
+ */ + public static final Color PALEVIOLETRED = new Color(0.85882354f, 0.4392157f, 0.5764706f); + + /** + * The color papaya whip with an RGB value of #FFEFD5 + *
+ */ + public static final Color PAPAYAWHIP = new Color(1.0f, 0.9372549f, 0.8352941f); + + /** + * The color peach puff with an RGB value of #FFDAB9 + *
+ */ + public static final Color PEACHPUFF = new Color(1.0f, 0.85490197f, 0.7254902f); + + /** + * The color peru with an RGB value of #CD853F + *
+ */ + public static final Color PERU = new Color(0.8039216f, 0.52156866f, 0.24705882f); + + /** + * The color pink with an RGB value of #FFC0CB + *
+ */ + public static final Color PINK = new Color(1.0f, 0.7529412f, 0.79607844f); + + /** + * The color plum with an RGB value of #DDA0DD + *
+ */ + public static final Color PLUM = new Color(0.8666667f, 0.627451f, 0.8666667f); + + /** + * The color powder blue with an RGB value of #B0E0E6 + *
+ */ + public static final Color POWDERBLUE = new Color(0.6901961f, 0.8784314f, 0.9019608f); + + /** + * The color purple with an RGB value of #800080 + *
+ */ + public static final Color PURPLE = new Color(0.5019608f, 0.0f, 0.5019608f); + + /** + * The color red with an RGB value of #FF0000 + *
+ */ + public static final Color RED = new Color(1.0f, 0.0f, 0.0f); + + /** + * The color rosy brown with an RGB value of #BC8F8F + *
+ */ + public static final Color ROSYBROWN = new Color(0.7372549f, 0.56078434f, 0.56078434f); + + /** + * The color royal blue with an RGB value of #4169E1 + *
+ */ + public static final Color ROYALBLUE = new Color(0.25490198f, 0.4117647f, 0.88235295f); + + /** + * The color saddle brown with an RGB value of #8B4513 + *
+ */ + public static final Color SADDLEBROWN = new Color(0.54509807f, 0.27058825f, 0.07450981f); + + /** + * The color salmon with an RGB value of #FA8072 + *
+ */ + public static final Color SALMON = new Color(0.98039216f, 0.5019608f, 0.44705883f); + + /** + * The color sandy brown with an RGB value of #F4A460 + *
+ */ + public static final Color SANDYBROWN = new Color(0.95686275f, 0.6431373f, 0.3764706f); + + /** + * The color sea green with an RGB value of #2E8B57 + *
+ */ + public static final Color SEAGREEN = new Color(0.18039216f, 0.54509807f, 0.34117648f); + + /** + * The color sea shell with an RGB value of #FFF5EE + *
+ */ + public static final Color SEASHELL = new Color(1.0f, 0.9607843f, 0.93333334f); + + /** + * The color sienna with an RGB value of #A0522D + *
+ */ + public static final Color SIENNA = new Color(0.627451f, 0.32156864f, 0.1764706f); + + /** + * The color silver with an RGB value of #C0C0C0 + *
+ */ + public static final Color SILVER = new Color(0.7529412f, 0.7529412f, 0.7529412f); + + /** + * The color sky blue with an RGB value of #87CEEB + *
+ */ + public static final Color SKYBLUE = new Color(0.5294118f, 0.80784315f, 0.92156863f); + + /** + * The color slate blue with an RGB value of #6A5ACD + *
+ */ + public static final Color SLATEBLUE = new Color(0.41568628f, 0.3529412f, 0.8039216f); + + /** + * The color slate gray with an RGB value of #708090 + *
+ */ + public static final Color SLATEGRAY = new Color(0.4392157f, 0.5019608f, 0.5647059f); + + /** + * The color slate grey with an RGB value of #708090 + *
+ */ + public static final Color SLATEGREY = SLATEGRAY; + + /** + * The color snow with an RGB value of #FFFAFA + *
+ */ + public static final Color SNOW = new Color(1.0f, 0.98039216f, 0.98039216f); + + /** + * The color spring green with an RGB value of #00FF7F + *
+ */ + public static final Color SPRINGGREEN = new Color(0.0f, 1.0f, 0.49803922f); + + /** + * The color steel blue with an RGB value of #4682B4 + *
+ */ + public static final Color STEELBLUE = new Color(0.27450982f, 0.50980395f, 0.7058824f); + + /** + * The color tan with an RGB value of #D2B48C + *
+ */ + public static final Color TAN = new Color(0.8235294f, 0.7058824f, 0.54901963f); + + /** + * The color teal with an RGB value of #008080 + *
+ */ + public static final Color TEAL = new Color(0.0f, 0.5019608f, 0.5019608f); + + /** + * The color thistle with an RGB value of #D8BFD8 + *
+ */ + public static final Color THISTLE = new Color(0.84705883f, 0.7490196f, 0.84705883f); + + /** + * The color tomato with an RGB value of #FF6347 + *
+ */ + public static final Color TOMATO = new Color(1.0f, 0.3882353f, 0.2784314f); + + /** + * The color turquoise with an RGB value of #40E0D0 + *
+ */ + public static final Color TURQUOISE = new Color(0.2509804f, 0.8784314f, 0.8156863f); + + /** + * The color violet with an RGB value of #EE82EE + *
+ */ + public static final Color VIOLET = new Color(0.93333334f, 0.50980395f, 0.93333334f); + + /** + * The color wheat with an RGB value of #F5DEB3 + *
+ */ + public static final Color WHEAT = new Color(0.9607843f, 0.87058824f, 0.7019608f); + + /** + * The color white with an RGB value of #FFFFFF + *
+ */ + public static final Color WHITE = new Color(1.0f, 1.0f, 1.0f); + + /** + * The color white smoke with an RGB value of #F5F5F5 + *
+ */ + public static final Color WHITESMOKE = new Color(0.9607843f, 0.9607843f, 0.9607843f); + + /** + * The color yellow with an RGB value of #FFFF00 + *
+ */ + public static final Color YELLOW = new Color(1.0f, 1.0f, 0.0f); + + /** + * The color yellow green with an RGB value of #9ACD32 + *
+ */ + public static final Color YELLOWGREEN = new Color(0.6039216f, 0.8039216f, 0.19607843f); + + /* + * Named colors moved to nested class to initialize them only when they + * are needed. + */ + private static final class NamedColors { + private static final Map namedColors = + createNamedColors(); + + private NamedColors() { + } + + private static Color get(String name) { + return namedColors.get(name); + } + + private static Map createNamedColors() { + Map colors = new HashMap<>(256); + + colors.put("aliceblue", ALICEBLUE); + colors.put("antiquewhite", ANTIQUEWHITE); + colors.put("aqua", AQUA); + colors.put("aquamarine", AQUAMARINE); + colors.put("azure", AZURE); + colors.put("beige", BEIGE); + colors.put("bisque", BISQUE); + colors.put("black", BLACK); + colors.put("blanchedalmond", BLANCHEDALMOND); + colors.put("blue", BLUE); + colors.put("blueviolet", BLUEVIOLET); + colors.put("brown", BROWN); + colors.put("burlywood", BURLYWOOD); + colors.put("cadetblue", CADETBLUE); + colors.put("chartreuse", CHARTREUSE); + colors.put("chocolate", CHOCOLATE); + colors.put("coral", CORAL); + colors.put("cornflowerblue", CORNFLOWERBLUE); + colors.put("cornsilk", CORNSILK); + colors.put("crimson", CRIMSON); + colors.put("cyan", CYAN); + colors.put("darkblue", DARKBLUE); + colors.put("darkcyan", DARKCYAN); + colors.put("darkgoldenrod", DARKGOLDENROD); + colors.put("darkgray", DARKGRAY); + colors.put("darkgreen", DARKGREEN); + colors.put("darkgrey", DARKGREY); + colors.put("darkkhaki", DARKKHAKI); + colors.put("darkmagenta", DARKMAGENTA); + colors.put("darkolivegreen", DARKOLIVEGREEN); + colors.put("darkorange", DARKORANGE); + colors.put("darkorchid", DARKORCHID); + colors.put("darkred", DARKRED); + colors.put("darksalmon", DARKSALMON); + colors.put("darkseagreen", DARKSEAGREEN); + colors.put("darkslateblue", DARKSLATEBLUE); + colors.put("darkslategray", DARKSLATEGRAY); + colors.put("darkslategrey", DARKSLATEGREY); + colors.put("darkturquoise", DARKTURQUOISE); + colors.put("darkviolet", DARKVIOLET); + colors.put("deeppink", DEEPPINK); + colors.put("deepskyblue", DEEPSKYBLUE); + colors.put("dimgray", DIMGRAY); + colors.put("dimgrey", DIMGREY); + colors.put("dodgerblue", DODGERBLUE); + colors.put("firebrick", FIREBRICK); + colors.put("floralwhite", FLORALWHITE); + colors.put("forestgreen", FORESTGREEN); + colors.put("fuchsia", FUCHSIA); + colors.put("gainsboro", GAINSBORO); + colors.put("ghostwhite", GHOSTWHITE); + colors.put("gold", GOLD); + colors.put("goldenrod", GOLDENROD); + colors.put("gray", GRAY); + colors.put("green", GREEN); + colors.put("greenyellow", GREENYELLOW); + colors.put("grey", GREY); + colors.put("honeydew", HONEYDEW); + colors.put("hotpink", HOTPINK); + colors.put("indianred", INDIANRED); + colors.put("indigo", INDIGO); + colors.put("ivory", IVORY); + colors.put("khaki", KHAKI); + colors.put("lavender", LAVENDER); + colors.put("lavenderblush", LAVENDERBLUSH); + colors.put("lawngreen", LAWNGREEN); + colors.put("lemonchiffon", LEMONCHIFFON); + colors.put("lightblue", LIGHTBLUE); + colors.put("lightcoral", LIGHTCORAL); + colors.put("lightcyan", LIGHTCYAN); + colors.put("lightgoldenrodyellow", LIGHTGOLDENRODYELLOW); + colors.put("lightgray", LIGHTGRAY); + colors.put("lightgreen", LIGHTGREEN); + colors.put("lightgrey", LIGHTGREY); + colors.put("lightpink", LIGHTPINK); + colors.put("lightsalmon", LIGHTSALMON); + colors.put("lightseagreen", LIGHTSEAGREEN); + colors.put("lightskyblue", LIGHTSKYBLUE); + colors.put("lightslategray", LIGHTSLATEGRAY); + colors.put("lightslategrey", LIGHTSLATEGREY); + colors.put("lightsteelblue", LIGHTSTEELBLUE); + colors.put("lightyellow", LIGHTYELLOW); + colors.put("lime", LIME); + colors.put("limegreen", LIMEGREEN); + colors.put("linen", LINEN); + colors.put("magenta", MAGENTA); + colors.put("maroon", MAROON); + colors.put("mediumaquamarine", MEDIUMAQUAMARINE); + colors.put("mediumblue", MEDIUMBLUE); + colors.put("mediumorchid", MEDIUMORCHID); + colors.put("mediumpurple", MEDIUMPURPLE); + colors.put("mediumseagreen", MEDIUMSEAGREEN); + colors.put("mediumslateblue", MEDIUMSLATEBLUE); + colors.put("mediumspringgreen", MEDIUMSPRINGGREEN); + colors.put("mediumturquoise", MEDIUMTURQUOISE); + colors.put("mediumvioletred", MEDIUMVIOLETRED); + colors.put("midnightblue", MIDNIGHTBLUE); + colors.put("mintcream", MINTCREAM); + colors.put("mistyrose", MISTYROSE); + colors.put("moccasin", MOCCASIN); + colors.put("navajowhite", NAVAJOWHITE); + colors.put("navy", NAVY); + colors.put("oldlace", OLDLACE); + colors.put("olive", OLIVE); + colors.put("olivedrab", OLIVEDRAB); + colors.put("orange", ORANGE); + colors.put("orangered", ORANGERED); + colors.put("orchid", ORCHID); + colors.put("palegoldenrod", PALEGOLDENROD); + colors.put("palegreen", PALEGREEN); + colors.put("paleturquoise", PALETURQUOISE); + colors.put("palevioletred", PALEVIOLETRED); + colors.put("papayawhip", PAPAYAWHIP); + colors.put("peachpuff", PEACHPUFF); + colors.put("peru", PERU); + colors.put("pink", PINK); + colors.put("plum", PLUM); + colors.put("powderblue", POWDERBLUE); + colors.put("purple", PURPLE); + colors.put("red", RED); + colors.put("rosybrown", ROSYBROWN); + colors.put("royalblue", ROYALBLUE); + colors.put("saddlebrown", SADDLEBROWN); + colors.put("salmon", SALMON); + colors.put("sandybrown", SANDYBROWN); + colors.put("seagreen", SEAGREEN); + colors.put("seashell", SEASHELL); + colors.put("sienna", SIENNA); + colors.put("silver", SILVER); + colors.put("skyblue", SKYBLUE); + colors.put("slateblue", SLATEBLUE); + colors.put("slategray", SLATEGRAY); + colors.put("slategrey", SLATEGREY); + colors.put("snow", SNOW); + colors.put("springgreen", SPRINGGREEN); + colors.put("steelblue", STEELBLUE); + colors.put("tan", TAN); + colors.put("teal", TEAL); + colors.put("thistle", THISTLE); + colors.put("tomato", TOMATO); + colors.put("transparent", TRANSPARENT); + colors.put("turquoise", TURQUOISE); + colors.put("violet", VIOLET); + colors.put("wheat", WHEAT); + colors.put("white", WHITE); + colors.put("whitesmoke", WHITESMOKE); + colors.put("yellow", YELLOW); + colors.put("yellowgreen", YELLOWGREEN); + + return colors; + } + } + + public static double[] HSBtoRGB(double hue, double saturation, double brightness) { + // normalize the hue + double normalizedHue = ((hue % 360) + 360) % 360; + hue = normalizedHue/360; + + double r = 0, g = 0, b = 0; + if (saturation == 0) { + r = g = b = brightness; + } else { + double h = (hue - Math.floor(hue)) * 6.0; + double f = h - java.lang.Math.floor(h); + double p = brightness * (1.0 - saturation); + double q = brightness * (1.0 - saturation * f); + double t = brightness * (1.0 - (saturation * (1.0 - f))); + switch ((int) h) { + case 0: + r = brightness; + g = t; + b = p; + break; + case 1: + r = q; + g = brightness; + b = p; + break; + case 2: + r = p; + g = brightness; + b = t; + break; + case 3: + r = p; + g = q; + b = brightness; + break; + case 4: + r = t; + g = p; + b = brightness; + break; + case 5: + r = brightness; + g = p; + b = q; + break; + } + } + double[] f = new double[3]; + f[0] = r; + f[1] = g; + f[2] = b; + return f; + } + + public static double[] RGBtoHSB(double r, double g, double b) { + double hue, saturation, brightness; + double[] hsbvals = new double[3]; + double cmax = (r > g) ? r : g; + if (b > cmax) cmax = b; + double cmin = (r < g) ? r : g; + if (b < cmin) cmin = b; + + brightness = cmax; + if (cmax != 0) + saturation = (double) (cmax - cmin) / cmax; + else + saturation = 0; + + if (saturation == 0) { + hue = 0; + } else { + double redc = (cmax - r) / (cmax - cmin); + double greenc = (cmax - g) / (cmax - cmin); + double bluec = (cmax - b) / (cmax - cmin); + if (r == cmax) + hue = bluec - greenc; + else if (g == cmax) + hue = 2.0 + redc - bluec; + else + hue = 4.0 + greenc - redc; + hue = hue / 6.0; + if (hue < 0) + hue = hue + 1.0; + } + hsbvals[0] = hue * 360; + hsbvals[1] = saturation; + hsbvals[2] = brightness; + return hsbvals; + } +} \ No newline at end of file