package com.github.sommeri.less4j.core.compiler.expressions; import com.github.sommeri.less4j.core.ast.ASTCssNodeType; import com.github.sommeri.less4j.core.ast.ColorExpression; import com.github.sommeri.less4j.core.ast.ColorExpression.ColorWithAlphaExpression; import com.github.sommeri.less4j.core.ast.BinaryExpression; import com.github.sommeri.less4j.core.ast.Expression; import com.github.sommeri.less4j.core.ast.BinaryExpressionOperator; import com.github.sommeri.less4j.core.ast.FaultyExpression; import com.github.sommeri.less4j.core.ast.NumberExpression; import com.github.sommeri.less4j.core.parser.HiddenTokenAwareTree; import com.github.sommeri.less4j.core.problems.BugHappened; import com.github.sommeri.less4j.core.problems.ProblemsHandler; class ColorsCalculator { private final ProblemsHandler problemsHandler; private static final int MIN = 0; private static final int MAX = 255; private static final double ALPHA_MIN = 0; private static final double ALPHA_MAX = 1.0; private static final double ALPHA_EPSILON = 0.0000000000000001; public ColorsCalculator(ProblemsHandler problemsHandler) { super(); this.problemsHandler = problemsHandler; } public Expression evalute(BinaryExpression originalExpression, Expression first, Expression second) { double red1 = calcRed(first); double green1 = calcGreen(first); double blue1 = calcBlue(first); double alpha1 = calcAlpha(first); double red2 = calcRed(second); double green2 = calcGreen(second); double blue2 = calcBlue(second); double alpha2 = calcAlpha(second); BinaryExpressionOperator operator = originalExpression.getOperator(); switch (operator.getOperator()) { case SOLIDUS: return divide(first, red1, green1, blue1, alpha1, red2, green2, blue2, alpha2, originalExpression.getUnderlyingStructure()); case STAR: return multiply(red1, green1, blue1, alpha1, red2, green2, blue2, alpha2, originalExpression.getUnderlyingStructure()); case MINUS: return subtract(first, red1, green1, blue1, alpha1, red2, green2, blue2, alpha2, originalExpression.getUnderlyingStructure()); case PLUS: return add(red1, green1, blue1, alpha1, red2, green2, blue2, alpha2, originalExpression.getUnderlyingStructure()); default: throw new BugHappened("Unknown operator.", operator); } } private Expression subtract(Expression first, double red1, double green1, double blue1, double alpha1, double red2, double green2, double blue2, double alpha2, HiddenTokenAwareTree parentToken) { if (first.getType()==ASTCssNodeType.NUMBER) { problemsHandler.subtractOrDiveColorFromNumber(first); return new FaultyExpression(first); } return createResultColor(parentToken, round(red1 - red2), round(green1 - green2), round(blue1 - blue2), alpha1, alpha2); } private ColorExpression multiply(double red1, double green1, double blue1, double alpha1, double red2, double green2, double blue2, double alpha2, HiddenTokenAwareTree parentToken) { return createResultColor(parentToken, round(red1 * red2), round(green1 * green2), round(blue1 * blue2), alpha1, alpha2); } private Expression divide(Expression first, double red1, double green1, double blue1, double alpha1, double red2, double green2, double blue2, double alpha2, HiddenTokenAwareTree parentToken) { if (first.getType()==ASTCssNodeType.NUMBER) { problemsHandler.subtractOrDiveColorFromNumber(first); return new FaultyExpression(first); } return createResultColor(parentToken, round(red1 / red2), round(green1 / green2), round(blue1 / blue2), alpha1, alpha2); } private ColorExpression add(double red1, double green1, double blue1, double alpha1, double red2, double green2, double blue2, double alpha2, HiddenTokenAwareTree parentToken) { return createResultColor(parentToken, round(red1 + red2), round(green1 + green2), round(blue1 + blue2), alpha1, alpha2); } private int round(double number) { if (number > MAX) return MAX; return number<MIN? MIN : (int)Math.round(number); } private double combineAlpha(double alpha1, double alpha2) { double alpha = alpha1 * (1.0 - alpha2) + alpha2; if (alpha > ALPHA_MAX) return ALPHA_MAX; return alpha<ALPHA_MIN? ALPHA_MIN : alpha; } private ColorExpression createResultColor(HiddenTokenAwareTree parentToken, double red, double green, double blue, double alpha1, double alpha2) { double roundAlpha = combineAlpha(alpha1, alpha2); if (roundAlpha<ALPHA_MAX-ALPHA_EPSILON) return new ColorWithAlphaExpression(parentToken, round(red), round(green), round(blue), roundAlpha); return new ColorExpression(parentToken, round(red), round(green), round(blue)); } public boolean accepts(BinaryExpressionOperator operator, Expression first, Expression second) { return acceptedOperand(first, second); } private boolean acceptedOperand(Expression first, Expression second) { //at least one color and both have to be either color or a number if (first.getType() != ASTCssNodeType.COLOR_EXPRESSION && first.getType() != ASTCssNodeType.NUMBER) return false; if (second.getType() != ASTCssNodeType.COLOR_EXPRESSION && second.getType() != ASTCssNodeType.NUMBER) return false; return first.getType() == ASTCssNodeType.COLOR_EXPRESSION || second.getType() == ASTCssNodeType.COLOR_EXPRESSION; } private double calcRed(Expression value) { if (value instanceof ColorExpression) { return ((ColorExpression) value).getRed(); } return ((NumberExpression) value).getValueAsDouble(); } private double calcGreen(Expression value) { if (value instanceof ColorExpression) { return ((ColorExpression) value).getGreen(); } return ((NumberExpression) value).getValueAsDouble(); } private double calcBlue(Expression value) { if (value instanceof ColorExpression) { return ((ColorExpression) value).getBlue(); } return ((NumberExpression) value).getValueAsDouble(); } private double calcAlpha(Expression value) { if (value instanceof ColorExpression) { return ((ColorExpression) value).getAlpha(); } return Double.POSITIVE_INFINITY; } }