public static double Calc(string formula) { try { if (string.IsNullOrEmpty(formula)) { return(0); } var resolvedFormula = FacerTags.ResolveTags(formula).Replace(" ", string.Empty); double dblVal; if (double.TryParse(resolvedFormula, out dblVal)) { return(dblVal); } else if (!formula.Contains('#') && !formula.Contains('$')) { throw new Exception("Not Numeric"); } //convert formula to c# resolvedFormula = ConditionalRegex.Replace(resolvedFormula, "($1)"); resolvedFormula = resolvedFormula .Replace("=", "==") .Replace('[', '(') .Replace(']', ')') .Replace(">==", ">=") .Replace("<==", "<=") .Replace("!==", "!="); var expression = new CompiledExpression(resolvedFormula); var result = expression.Eval(); return(Convert.ToDouble(result)); } catch { throw new Exception("Couldn't parse '" + Convert.ToString(formula) + "'"); } }
public static Bitmap Render(FacerWatchface watchface, EWatchType watchtype, EWatchfaceOverlay overlay, bool checkForErrors, List <WatchfaceRendererError> errors, bool showSelected = true) { Size dimensions; if (!WatchType.Dimensions.TryGetValue(watchtype, out dimensions)) { dimensions = new Size(320, 320); //unknown } var bmp = new Bitmap(dimensions.Width, dimensions.Height); using (var g = Graphics.FromImage(bmp)) { g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.TextRenderingHint = TextRenderingHint.AntiAlias; g.SmoothingMode = SmoothingMode.HighQuality; g.TextContrast = 1; g.PixelOffsetMode = PixelOffsetMode.None; g.PageUnit = GraphicsUnit.Pixel; g.CompositingQuality = CompositingQuality.HighQuality; g.Clear(Color.Black); var outRect = Rectangle.Empty; var selectedRectangle = Rectangle.Empty; Matrix selectedTransform = null; foreach (var layer in watchface.Layers) { try { if (Properties.Settings.Default.LowPowerMode && !layer.low_power) { continue; //don't show on dimmed } var opacity = (float)ExpressionCalculator.Calc(layer.opacity); if (opacity == 0) { continue; } var x = (float)ExpressionCalculator.Calc(layer.x); var y = (float)ExpressionCalculator.Calc(layer.y); var rotation = layer.r != "0" ? (float)ExpressionCalculator.Calc(layer.r) : 0.0F; g.TranslateTransform(x, y); g.RotateTransform(rotation); var alp = new PointF(0, 0);; int width, height; if (layer.type == "image") { var imageAtt = new ImageAttributes(); if (opacity < 100 || (layer.is_tinted ?? false)) { var tint_color = (layer.is_tinted ?? false) && layer.tint_color != null?Color.FromArgb(layer.tint_color.Value) : Color.Empty; const float intensity = 0.3f; var colorMatrix = new ColorMatrix(); if (opacity < 100) { colorMatrix.Matrix33 = opacity / 100f; } if (tint_color != Color.Empty) { colorMatrix.Matrix40 = tint_color.R / 255 * intensity; colorMatrix.Matrix41 = tint_color.G / 255 * intensity; colorMatrix.Matrix42 = tint_color.B / 255 * intensity; } imageAtt.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); } width = (int)ExpressionCalculator.Calc(layer.width); height = (int)ExpressionCalculator.Calc(layer.height); alp = AlignedPoint(width, height, layer.alignment.Value); Image img; if (!watchface.Images.TryGetValue(layer.hash, out img)) { AppendError(checkForErrors, errors, WatchfaceRendererErrorSeverity.Error, layer.GetIdentifier(), "The image \"" + layer.hash + "\" was not found in the images folder."); img = ImageNotFound; } outRect = new Rectangle((int)alp.X, (int)alp.Y, width, height); g.DrawImage(img, outRect, 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, imageAtt); } else if (layer.type == "text") { var colorToUse = Properties.Settings.Default.LowPowerMode ? layer.low_power_color : layer.color; var foreColor = Color.FromArgb(((int)ExpressionCalculator.Calc(colorToUse) & 0xFFFFFF) + ((int)(opacity / 100f * 255) << 24)); var fontSize = DpToPx((float)ExpressionCalculator.Calc(layer.size)); Font layerFont; if (layer.font_family == (int)FacerFont.Custom) { FacerCustomFont customFont; if (!watchface.CustomFonts.TryGetValue(layer.font_hash, out customFont)) { AppendError(checkForErrors, errors, WatchfaceRendererErrorSeverity.Error, layer.GetIdentifier(), "The font \"" + layer.hash + "\" was not found in the fonts folder."); var fontPair = FacerFontConfig[FacerFont.Roboto]; layerFont = new Font(fontPair.Item1.Families[0], fontSize, fontPair.Item2, GraphicsUnit.Point); } else { var fontStyle = (layer.bold ?? false) && (layer.italic ?? false) ? FontStyle.Bold | FontStyle.Italic : ((layer.bold ?? false) && !(layer.italic ?? false) ? FontStyle.Bold : (!(layer.bold ?? false) && (layer.italic ?? false) ? FontStyle.Italic : FontStyle.Regular)); layerFont = new Font(customFont.FontFamily, fontSize, customFont.GetAvailableFontStyle(fontStyle)); } } else { var facerFontKey = layer.font_family + 100 * (layer.bold ?? false ? 1 : 0) + 200 * (layer.italic ?? false ? 1 : 0); var fontPair = FacerFontConfig[(FacerFont)facerFontKey]; layerFont = new Font(fontPair.Item1.Families[0], fontSize, fontPair.Item2, GraphicsUnit.Point); } var resolvedText = FacerTags.ResolveTags(layer.text) .Replace("\n", string.Empty) .Replace("\x10", string.Empty).Replace("\x13", string.Empty); //remove new lines if (layer.transform == (int)FacerTextTransform.AllUppercase) { resolvedText = resolvedText.ToUpper(); } else if (layer.transform == (int)FacerTextTransform.AllLowercase) { resolvedText = resolvedText.ToLower(); } var textBrush = new SolidBrush(foreColor); var textAlign = (FacerTextAlignment)layer.alignment.Value; var textFormat = StringFormat.GenericTypographic; //ToStringFormat(textAlign); textFormat.LineAlignment = StringAlignment.Far; textFormat.Trimming = StringTrimming.None; textFormat.FormatFlags = StringFormatFlags.NoWrap; var measurements = TextRenderer.MeasureText(g, resolvedText, layerFont, bmp.Size, TextFormatFlags.Top | TextFormatFlags.NoClipping | TextFormatFlags.NoPadding | TextFormatFlags.SingleLine); var drawMeasurements = g.MeasureString(resolvedText, layerFont, bmp.Width, textFormat); width = measurements.Width; height = measurements.Height; var xOffset = textAlign == FacerTextAlignment.Left ? 0 : textAlign == FacerTextAlignment.Center ? width / 2 : width; var baselineOffset = layerFont.SizeInPoints / layerFont.FontFamily.GetEmHeight(layerFont.Style) * layerFont.FontFamily.GetCellAscent(layerFont.Style); var yOffset = (int)(g.DpiY / 72f * baselineOffset); var textPoint = new PointF(-(int)(drawMeasurements.Width - measurements.Width) - xOffset, height - yOffset); g.DrawString(resolvedText, layerFont, textBrush, textPoint, textFormat); outRect = new Rectangle((int)textPoint.X, (int)textPoint.Y - height, width, height); } else if (layer.type == "shape") { var foreColor = Color.FromArgb(((int)ExpressionCalculator.Calc(layer.color) & 0xFFFFFF) + ((int)(opacity / 100f * 255) << 24)); var radius = string.IsNullOrEmpty(layer.radius) ? 0 : (float)ExpressionCalculator.Calc(layer.radius); var shapeOptions = layer.shape_opt == ((int)FacerShapeOptions.Stroke).ToString(CultureInfo.InvariantCulture) ? FacerShapeOptions.Stroke : FacerShapeOptions.Fill; var strokeSize = (float)ExpressionCalculator.Calc(layer.stroke_size) / 2; Pen penToUse = shapeOptions == FacerShapeOptions.Stroke ? new Pen(foreColor, strokeSize) : null; Brush brushToUse = shapeOptions == FacerShapeOptions.Fill ? new SolidBrush(foreColor) : null; switch (layer.shape_type) { case (int)FacerShapeType.Circle: outRect = new Rectangle((int)(-radius), (int)(-radius), (int)(2 * radius), (int)(2 * radius)); if (shapeOptions == FacerShapeOptions.Stroke) { g.DrawEllipse(penToUse, outRect); outRect.Inflate((int)strokeSize, (int)strokeSize); outRect.Offset((int)(-strokeSize / 2f + 0.5f), (int)(-strokeSize / 2f + 0.5f)); } else { g.FillEllipse(brushToUse, outRect); } break; case (int)FacerShapeType.Line: case (int)FacerShapeType.Square: width = (int)ExpressionCalculator.Calc(layer.width); height = (int)ExpressionCalculator.Calc(layer.height); outRect = new Rectangle(0, 0, width, height); if (shapeOptions == FacerShapeOptions.Stroke) { g.DrawRectangle(penToUse, outRect); } else { g.FillRectangle(brushToUse, outRect); } break; case (int)FacerShapeType.Triangle: case (int)FacerShapeType.Polygon: outRect = new Rectangle((int)(-radius), (int)(-radius), (int)(2 * radius), (int)(2 * radius)); var polyN = layer.shape_type == (int)FacerShapeType.Triangle ? 3 : (int)ExpressionCalculator.Calc(layer.sides); var polyPoints = new Point[polyN]; for (var i = 0; i < polyN; i++) { polyPoints[i] = new Point( (int)(radius * Math.Cos(2 * Math.PI * i / polyN)), (int)(radius * Math.Sin(2 * Math.PI * i / polyN))); } if (shapeOptions == FacerShapeOptions.Stroke) { g.DrawPolygon(penToUse, polyPoints); outRect.Inflate((int)strokeSize, (int)strokeSize); outRect.Offset((int)(-strokeSize / 2f + 0.5f), (int)(-strokeSize / 2f + 0.5f)); } else { g.FillPolygon(brushToUse, polyPoints); } break; } } if (layer == watchface.SelectedLayer) { selectedRectangle = outRect; selectedTransform = g.Transform; } g.ResetTransform(); } catch (NullReferenceException) { AppendError(checkForErrors, errors, WatchfaceRendererErrorSeverity.Error, layer.GetIdentifier(), "General Error" ); } catch (Exception ex) { AppendError(checkForErrors, errors, WatchfaceRendererErrorSeverity.Error, layer.GetIdentifier(), ex.Message ); } } g.ResetTransform(); if (showSelected && selectedRectangle != Rectangle.Empty) { g.Transform = selectedTransform; g.DrawXorRectangle(bmp, selectedRectangle); g.ResetTransform(); } if (overlay.HasFlag(EWatchfaceOverlay.Card)) { var card = Properties.Resources.TestCard; g.DrawImage(card, 0, 0, dimensions.Width, dimensions.Height); } if (overlay.HasFlag(EWatchfaceOverlay.WearIcons)) { var icons = Properties.Resources.TestWearStatus; g.DrawImage(icons, dimensions.Width / 2f - icons.Width / 2f, 0.05f * dimensions.Height, icons.Width * dimensions.Width / 320f, icons.Height * dimensions.Height / 320f); } Bitmap mask; if (WatchType.Masks.TryGetValue(watchtype, out mask)) { CopyChannel(mask, bmp, ChannelARGB.Alpha, ChannelARGB.Alpha); } } return(bmp); }