Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Color functions #94

Merged
merged 29 commits into from
Jan 23, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
0d0f3b9
Initial implementation of colour functions.
karlvr Jan 22, 2013
083f384
Tests referenced incorrect resources (swapped)
karlvr Jan 22, 2013
37ee370
ColorFunctions: create constants for all of the functions as a sign o…
karlvr Jan 22, 2013
b0b884f
Color functions: implement rob, rgba, hsl, hsla, argb.
karlvr Jan 22, 2013
ed299d3
Mixin test-case output tweaked to match now implemented rgba function
karlvr Jan 22, 2013
8285d43
Color functions: more functions implemented
karlvr Jan 22, 2013
b4169f2
Color functions: use HSL functions from less.js
karlvr Jan 22, 2013
9ac66e9
Color functions: implement saturation, lightness, red, green, blue, a…
karlvr Jan 22, 2013
274c650
Misc functions: added color and unit functions
karlvr Jan 22, 2013
789d0e5
Don't process filter declarations.
karlvr Jan 22, 2013
c779b63
Color functions: change colour to store its RGB values as floating point
karlvr Jan 22, 2013
72f0c7e
Color functions: implement Contrast.
karlvr Jan 22, 2013
b427975
If validation fails for the function return null, so we don't alter the
karlvr Jan 22, 2013
7855a14
Color functions: Contrast now supports failing silently if the first
karlvr Jan 22, 2013
2e7e37b
Color functions: hsv, hsva
karlvr Jan 23, 2013
47a33cb
Color functions: all blending functions
karlvr Jan 23, 2013
0e40194
Color functions: tint, shade
karlvr Jan 23, 2013
9ce81b3
Color functions: fix hue, saturation, lightness rounding.
karlvr Jan 23, 2013
7385fe3
Add new test-cases for functions from less.js
karlvr Jan 23, 2013
00a5181
Math functions: update round function to support two arguments (second
karlvr Jan 23, 2013
53676a4
Less4j cannot parse the pi: declaration and fails with an error.
karlvr Jan 23, 2013
fa9259c
Functions: implement remaining math functions
karlvr Jan 23, 2013
bfe01aa
Functions: convert function
karlvr Jan 23, 2013
5a8d6f7
Functions: added type functions, eg. iscolor etc
karlvr Jan 23, 2013
25ac15d
Functions: extract
karlvr Jan 23, 2013
4f0a8b0
Functions: remove add and increment functions and their tests, also
karlvr Jan 23, 2013
9903cfa
Functions test: tweak css output so tests pass, results were equivalent
karlvr Jan 23, 2013
c4887b3
Functions: implement pi()
karlvr Jan 23, 2013
34e7c0f
Functions: improve error messages
karlvr Jan 23, 2013
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -692,7 +692,7 @@ hexColor
;

function
: (a=IDENT | a=PERCENT) LPAREN b=functionParameter RPAREN -> ^(TERM_FUNCTION $a $b*)
: (a=IDENT | a=PERCENT) LPAREN b=functionParameter? RPAREN -> ^(TERM_FUNCTION $a $b*)
;

functionParameter
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package com.github.sommeri.less4j.core.ast;

public enum ASTCssNodeType {
UNKNOWN, CSS_CLASS, DECLARATION, STYLE_SHEET, RULE_SET, SELECTOR, SIMPLE_SELECTOR, PSEUDO_CLASS, PSEUDO_ELEMENT, SELECTOR_ATTRIBUTE, ID_SELECTOR, CHARSET_DECLARATION, FONT_FACE, IDENTIFIER_EXPRESSION, COMPOSED_EXPRESSION, STRING_EXPRESSION, NUMBER, COLOR_EXPRESSION, FUNCTION, MEDIA, COMMENT, DECLARATIONS_BODY, SELECTOR_OPERATOR, SELECTOR_COMBINATOR, EXPRESSION_OPERATOR, NTH, NAMED_EXPRESSION, MEDIA_QUERY, MEDIA_EXPRESSION, MEDIUM, MEDIUM_MODIFIER, MEDIUM_TYPE, MEDIUM_EX_FEATURE, VARIABLE_DECLARATION, VARIABLE, INDIRECT_VARIABLE, PARENTHESES_EXPRESSION, SIGNED_EXPRESSION, ARGUMENT_DECLARATION, MIXIN_REFERENCE, GUARD_CONDITION, COMPARISON_EXPRESSION, GUARD, NESTED_SELECTOR_APPENDER, REUSABLE_STRUCTURE, FAULTY_EXPRESSION, ESCAPED_SELECTOR, ESCAPED_VALUE, INTERPOLABLE_NAME, FIXED_NAME_PART, VARIABLE_NAME_PART, KEYFRAMES, KEYFRAMES_NAME, KEYFRAMES_BODY, REUSABLE_STRUCTURE_NAME, VIEWPORT, GENERAL_BODY, PAGE, NAME, PAGE_MA, PAGE_MARGIN_BOX, IMPORT, FAULTY_NODE
UNKNOWN, CSS_CLASS, DECLARATION, STYLE_SHEET, RULE_SET, SELECTOR, SIMPLE_SELECTOR, PSEUDO_CLASS, PSEUDO_ELEMENT, SELECTOR_ATTRIBUTE, ID_SELECTOR, CHARSET_DECLARATION, FONT_FACE, IDENTIFIER_EXPRESSION, COMPOSED_EXPRESSION, STRING_EXPRESSION, NUMBER, COLOR_EXPRESSION, FUNCTION, MEDIA, COMMENT, DECLARATIONS_BODY, SELECTOR_OPERATOR, SELECTOR_COMBINATOR, EXPRESSION_OPERATOR, NTH, NAMED_EXPRESSION, MEDIA_QUERY, MEDIA_EXPRESSION, MEDIUM, MEDIUM_MODIFIER, MEDIUM_TYPE, MEDIUM_EX_FEATURE, VARIABLE_DECLARATION, VARIABLE, INDIRECT_VARIABLE, PARENTHESES_EXPRESSION, SIGNED_EXPRESSION, ARGUMENT_DECLARATION, MIXIN_REFERENCE, GUARD_CONDITION, COMPARISON_EXPRESSION, GUARD, NESTED_SELECTOR_APPENDER, REUSABLE_STRUCTURE, FAULTY_EXPRESSION, ESCAPED_SELECTOR, ESCAPED_VALUE, INTERPOLABLE_NAME, FIXED_NAME_PART, VARIABLE_NAME_PART, KEYFRAMES, KEYFRAMES_NAME, KEYFRAMES_BODY, REUSABLE_STRUCTURE_NAME, VIEWPORT, GENERAL_BODY, PAGE, NAME, PAGE_MA, PAGE_MARGIN_BOX, IMPORT, FAULTY_NODE, ANONYMOUS, EMPTY_EXPRESSION
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.github.sommeri.less4j.core.ast;

import java.util.Collections;
import java.util.List;

import com.github.sommeri.less4j.core.parser.HiddenTokenAwareTree;

public class AnonymousExpression extends Expression {

private String value;

public AnonymousExpression(HiddenTokenAwareTree token, String value) {
super(token);
this.value = value;
}

public String getValue() {
return value;
}

@Override
public List<? extends ASTCssNode> getChilds() {
return Collections.emptyList();
}

@Override
public ASTCssNodeType getType() {
return ASTCssNodeType.ANONYMOUS;
}

@Override
public AnonymousExpression clone() {
AnonymousExpression result = (AnonymousExpression) super.clone();
return result;
}

}
Original file line number Diff line number Diff line change
@@ -1,29 +1,34 @@
package com.github.sommeri.less4j.core.ast;

import java.awt.Color;
import java.util.Collections;
import java.util.List;

import com.github.sommeri.less4j.core.parser.HiddenTokenAwareTree;

public class ColorExpression extends Expression {

private String value;
private int red;
private int green;
private int blue;
protected String value;
protected double red;
protected double green;
protected double blue;

public ColorExpression(HiddenTokenAwareTree token, String value) {
super(token);
setValue(value);
}

public ColorExpression(HiddenTokenAwareTree token, int red, int green, int blue) {
public ColorExpression(HiddenTokenAwareTree token, double red, double green, double blue) {
super(token);
this.red = red;
this.green = green;
this.blue = blue;
this.value = encode(red, green, blue);
}

public ColorExpression(HiddenTokenAwareTree token, Color color) {
this(token, color.getRed(), color.getGreen(), color.getBlue());
}

public String getValue() {
return value;
Expand All @@ -36,18 +41,22 @@ public void setValue(String value) {
blue=decode(value, 2);
}

public int getRed() {
public double getRed() {
return red;
}

public int getGreen() {
public double getGreen() {
return green;
}

public int getBlue() {
public double getBlue() {
return blue;
}

public double getAlpha() {
return 1.0f;
}

private int decode(String color, int i) {
if (color.length()<7) {
String substring = color.substring(i+1, i+2);
Expand All @@ -57,15 +66,15 @@ private int decode(String color, int i) {
return Integer.parseInt(color.substring(i*2+1, i*2+3), 16);
}

private String encode(int red, int green, int blue) {
private String encode(double red, double green, double blue) {
return "#" + toHex(red) + toHex(green) + toHex(blue);
}

private String toHex(int color) {
protected String toHex(double color) {
String prefix = "";
if (color<16)
prefix = "0";
return prefix + Integer.toHexString(color);
return prefix + Integer.toHexString((int) Math.round(color));
}

@Override
Expand All @@ -87,4 +96,52 @@ public String toString() {
public ColorExpression clone() {
return (ColorExpression) super.clone();
}

public String toARGB() {
return "#FF" + toHex(red) + toHex(green) + toHex(blue);
}

public Color toColor() {
return new Color((int)Math.round(this.red), (int)Math.round(this.green), (int)Math.round(this.blue));
}

public static class ColorWithAlphaExpression extends ColorExpression {

/**
* Alpha in the range 0-1.
*/
private double alpha;

public ColorWithAlphaExpression(HiddenTokenAwareTree token, double red, double green, double blue, double alpha) {
super(token, red, green, blue);
this.alpha = alpha;
if (alpha != 1.0) {
this.value = encode(red, green, blue, alpha);
}
}

public ColorWithAlphaExpression(HiddenTokenAwareTree token, Color color) {
this(token, color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha() / 255.0f);
}

@Override
public double getAlpha() {
return alpha;
}

protected String encode(double red, double green, double blue, double alpha) {
return "rgba(" + Math.round(red) + ", " + Math.round(green) + ", " + Math.round(blue) + ", " + alpha + ")";
}

@Override
public String toARGB() {
return "#" + toHex(Math.round(alpha * 255)) + toHex(red) + toHex(green) + toHex(blue);
}

@Override
public Color toColor() {
return new Color((int)Math.round(this.red), (int)Math.round(this.green), (int)Math.round(this.blue), Math.round(this.alpha * 255));
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ public List<? extends ASTCssNode> getChilds() {
public boolean isFontDeclaration() {
return getName()!=null? getName().toLowerCase().equals("font") : false;
}

public boolean isFilterDeclaration() {
return getName()!=null? getName().toLowerCase().equals("filter") : false;
}

@Override
public String toString() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.github.sommeri.less4j.core.ast;

import java.util.Collections;
import java.util.List;

import com.github.sommeri.less4j.core.parser.HiddenTokenAwareTree;

public class EmptyExpression extends Expression {

public EmptyExpression(HiddenTokenAwareTree token) {
super(token);
}

@Override
public List<? extends ASTCssNode> getChilds() {
return Collections.emptyList();
}

@Override
public ASTCssNodeType getType() {
return ASTCssNodeType.EMPTY_EXPRESSION;
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.github.sommeri.less4j.core.ast;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.github.sommeri.less4j.core.parser.HiddenTokenAwareTree;

Expand Down Expand Up @@ -94,11 +96,102 @@ public boolean hasOriginalString() {
public ASTCssNodeType getType() {
return ASTCssNodeType.NUMBER;
}

public NumberExpression convertTo(String targetUnit) {
Map<Dimension, String> conversions = new HashMap<Dimension, String>();
for (Dimension dim : Dimension.values()) {
if (dim.getConversions().containsKey(targetUnit)) {
conversions.put(dim, targetUnit);
}
}

return convertTo(conversions);
}

public NumberExpression convertTo(Map<Dimension, String> conversions) {
double value = getValueAsDouble();
String unit = getSuffix();
Dimension dimension = getDimension();

String targetUnit = conversions.get(dimension);
if (targetUnit == null) {
/* No conversion requested for this type of number */
return this;
}

Map<String, Number> group = dimension.getConversions();

Number multiplier = group.get(unit);
Number divisor = group.get(targetUnit);

if (multiplier != null && divisor != null) {
return new NumberExpression(getUnderlyingStructure(), value * multiplier.doubleValue() / divisor.doubleValue(), targetUnit, null, getDimension());
} else {
throw new IllegalArgumentException("Conversion between " + unit + " and " + targetUnit + " is not supported.");
}
}

public enum Dimension {
NUMBER, PERCENTAGE, LENGTH, EMS, EXS, ANGLE, TIME, FREQ, REPEATER, UNKNOWN;
}

private Map<String, Number> conversions;

public static Dimension forSuffix(String suffix) {
if (suffix.equals("%")) {
return PERCENTAGE;
} else if (suffix.equals("px") || suffix.equals("cm") || suffix.equals("mm") || suffix.equals("in") || suffix.equals("pt") || suffix.equals("pc")) {
return LENGTH;
} else if (suffix.equals("em")) {
return EMS;
} else if (suffix.equals("ex")) {
return EXS;
} else if (suffix.equals("deg") || suffix.equals("rad") || suffix.equals("grad")) {
return ANGLE;
} else if (suffix.equals("ms") || suffix.equals("s")) {
return TIME;
} else if (suffix.equals("khz") || suffix.equals("hz")) {
return FREQ;
} else {
return UNKNOWN;
}
}

public Map<String, Number> getConversions() {
if (conversions != null)
return conversions;

HashMap<String, Number> result = new HashMap<String, Number>();
switch (this) {
case LENGTH:
result.put("m", 1);
result.put("cm", 0.01);
result.put("mm", 0.001);
result.put("in", 0.0254);
result.put("pt", 0.0254 / 72);
result.put("pc", 0.0254 / 72 * 12);
break;

case TIME:
result.put("s", 1);
result.put("ms", 0.001);
break;

case ANGLE:
result.put("rad", 1.0 / (2 * Math.PI));
result.put("deg", 1.0 / 360);
result.put("grad", 1.0 / 400);
result.put("turn", 1);
break;

default:
break;
}

this.conversions = result;
return result;
}
}

@Override
public String toString() {
if (originalString != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ private void evaluateExpressions(ASTCssNode node) {
}

private void evaluateInDeclaration(Declaration node) {
if (!node.isFontDeclaration()) {
if (!node.isFontDeclaration() && !node.isFilterDeclaration()) {
evaluateExpressions(node);
return;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.github.sommeri.less4j.core.compiler.expressions;

import com.github.sommeri.less4j.core.ast.NumberExpression;
import com.github.sommeri.less4j.core.ast.NumberExpression.Dimension;

abstract class AbstractFunction implements Function {

static double scaled(NumberExpression n, int size) {
if (n.getDimension() == Dimension.PERCENTAGE) {
return n.getValueAsDouble() * size / 100;
} else {
return number(n);
}
}

static double number(NumberExpression n) {
if (n.getDimension() == Dimension.PERCENTAGE) {
return n.getValueAsDouble() / 100;
} else {
return n.getValueAsDouble();
}
}

}
Loading