public AssemblerResource <T> Merge(AssemblerResource <T> resource, AssemblerResource <T> layer, SkeletonType skeletonType, Overlay overlay = null) { // If we need to keep track of individual layers, // have the canvas store their metadata in a list ImageTools.Merge(resource, layer, skeletonType, overlay); if (!resource.IsEmpty) { resource.Definition.Merge(layer.Definition); } return(resource); }
public void CleanupLayers(AssemblerResource <T> res, SkeletonType skeletonType) { if (skeletonType == SkeletonType.Anchors) { ImageTools.ExtractAnchors(res.Data); } if (res.SplitLayers != null && res.SplitLayers.Any()) { foreach (var layer in res.SplitLayers) { ImageTools.Merge(res.Data, layer.image, 0, 0, layer.Anchor.X, layer.Anchor.Y); } } }
public AssemblerResource <T> Assemble(List <AssemblerResource <T> > resources, SkeletonType skeletonType, Overlay overlay = null, ObjectType mainType = ObjectType.na, Dictionary <PixelInfo, PixelInfo> palette = null) { var useAnchors = skeletonType == SkeletonType.Anchors; if (overlay != null || skeletonType == SkeletonType.Anchors) { var width = !useAnchors ? overlay.Width : resources.Max(r => r.Data.Width); var height = !useAnchors ? overlay.Height : resources.Max(r => r.Data.Height); var canvas = ImageTools.BlankCanvas(width, height); resources.Insert(0, AssemblerResource <T> .Canvas(canvas)); } var assembled = resources.Aggregate((res, next) => Merge(res, next, skeletonType, overlay)); CleanupLayers(assembled, skeletonType); // TODO, IMPORTANT - this somehow breaks the loader's definitions! // It replaces the type in the original resource definition if (mainType != ObjectType.na) { assembled.Definition.ObjectType = mainType; } assembled.Data = ImageTools.Trim(assembled.Data); assembled.Colors = ImageTools.ColorInfo(assembled.Data); if (palette != null) { ImageTools.ApplyPalette(assembled.Data, palette); } assembled.Data.Apply(); return(assembled); }
//TODO cleanup this, not readable and does too many things. Split up the methods by skeleton type and by overlay (with overlay/without) public void Merge(AssemblerResource <T> canvas, AssemblerResource <T> layer, SkeletonType skeletonType, Overlay overlay) { var newParts = new List <(ImgPoint point, IImage <T> Image, bool isPattern)>(); if (overlay != null && overlay.SourceType.ToObjectType() != layer.Definition.ObjectType) { // Each overlay color will tell us how many parts we need, // i.e. if there are N overlay point for color X, then we need N parts // If the resource has multiple images, use those, otherwise just use one image multiple times. var offSets = overlay.Points[OverlayColorRef[layer.Definition.ObjectType]].Cast <ImgPoint>().ToList(); if (offSets.Count > 1 && layer.Definition.Meta.CannotDuplicate) { ImgPoint newOffset; if (offSets.Count > 2) { newOffset = offSets.PickRandom(); } else { newOffset = new ImgPoint((offSets[0].X + offSets[1].X) / 2, (offSets[0].Y + offSets[1].Y) / 2); } offSets = new List <ImgPoint>() { newOffset }; } // "00FF26" & "FF00E5" are the default anchor colors - just an arbitrary choice. Change if needed. // TODO rewrite this, see anchor system overlay in garbler todo var anchorInUse = skeletonType == SkeletonType.Overlay ? AnchorOverlay : AnchorSkeleton; if (!layer.Definition.Meta.IsSet) { var anchor = layer.Definition.Meta.MainAnchor ?? ExtractAnchor(layer.Data, anchorInUse); for (int i = 0; i < offSets.Count; i++) { AddPart(offSets[i], anchor, layer.Data, i > offSets.Count / 2); } } else { // TODO rewrite with new anchor system throw new NotImplementedException("currently disabled, re-enable when needed & after anchor system overhaul"); //var imagesAndAnchors = layer.AdditionalImages.Select(t => // new { // anchor = ExtractAnchor(t, anchorInUse), // image = t // }).ToArray(); //for (int i = 0, j = 0; i < offSets.Count; i++, j++) //{ // if (j >= imagesAndAnchors.Length) // j = 0; // var item = imagesAndAnchors[j]; // AddPart(offSets[i], item.anchor, item.image, i >= offSets.Count / 2); //} } // PastSplit: At the moment we only allow to split a layer in two halves. Improve this if needed. void AddPart(ImgPoint offset, ImgPoint anchor, IImage <T> image, bool PastSplit) { var offsetAnchor = new ImgPoint(offset.X - anchor.X, offset.Y - anchor.Y); if (PastSplit && layer.Instruction.DepthSplit > 0) { canvas.SplitLayers.Add(new SplitLayer <T>() { Anchor = offsetAnchor, image = image, Queue = layer.Instruction.DepthSplit }); } else { newParts.Add((offsetAnchor, image, layer.Instruction.IsPattern)); } } } else if (skeletonType == SkeletonType.Anchors) { // TODO rewrite with new anchor system if (canvas.IsEmpty) { newParts.Add((new ImgPoint( canvas.Data.Width / 2 - layer.Data.Width / 2, canvas.Data.Height - layer.Data.Height), layer.Data, false)); } else { var layerAnchor = layer.Definition.Meta.MainAnchor ?? ExtractAnchor(layer.Data, AnchorOverlay); var canvasAnchor = canvas.Definition.Meta.MainAnchor ?? ExtractAnchor(canvas.Data, AnchorOverlay); var offsetAnchor = new ImgPoint(canvasAnchor.X - layerAnchor.X, canvasAnchor.Y - layerAnchor.Y, RestrictedPoints); newParts.Add((offsetAnchor, layer.Data, layer.Instruction.IsPattern)); } } else { newParts.Add((new ImgPoint(0, 0), layer.Data, layer.Instruction.IsPattern)); } if (canvas.SplitLayers != null && canvas.SplitLayers.Any()) { var insertions = canvas.SplitLayers .Where(l => l.Queue == 0); // Split layers out of the queue come out first newParts.InsertRange(0, insertions.Select(l => (l.Anchor, l.image, false))); canvas.SplitLayers = canvas.SplitLayers.Except(insertions).ToList(); foreach (var splitLayer in canvas.SplitLayers) { splitLayer.Queue--; } } foreach (var part in newParts) { Merge(canvas.Data, part.Image, 0, 0, part.point.X, part.point.Y, part.isPattern); canvas.IsEmpty = false; } }
public AssemblerResource <T> Assemble(Instructions instructions) { var resources = new List <AssemblerResource <T> >(); Overlay overlay = null; foreach (var instructionLayer in instructions) { // This goes through the current layer ie new List<Enum>() { Shape.round, ObjectType.vase } var props = instructionLayer.Types.Select(l => new { propInfo = ResourceDefinition.DefinitionPropInfo.SingleOrDefault( p => p.PropertyType == l.GetType()), value = l } ); // Then loops through all the resources definitions until it finds one // that has ALL the required properties set as described. // If we end up with a lot of assets, have the loader create indexes for individual properties in a definition. var definitions = Loader.TypeResourceDefinitions.Where(d => props .All(p => Equals(p.propInfo.GetValue(d), p.value))) .ToList(); var definition = string.IsNullOrEmpty(instructionLayer.SubType) ? definitions.PickRandom() : definitions.Where(d => d.SubTypes.Contains(instructionLayer.SubType)).ToList().PickRandom(); if (instructions.UseOnce) { Loader.TypeResourceDefinitions.Remove(definition); } AssemblerResource <T> resource; if (!definition.Meta.IsSet) { resource = new AssemblerResource <T>(definition, ImageTools.BytesToImage(Loader.LoadImageByName(definition.Meta.ResourceName)), instructionLayer); } else { resource = new AssemblerResource <T>(definition, definition.Meta.ResourceNames.Select(r => ImageTools.BytesToImage(Loader.LoadImageByName(r))).ToList(), instructionLayer); } resources.Add(resource); if (instructionLayer.HasOverlay) { overlay = definition.Meta.Overlay; // deprecated, remove //var overlayBytes = _loader.GetMatchingOverlay(definition.Meta.ResourceName); //overlay = _imageTools.ReadOverlayData(overlayBytes, definition.ObjectType); } } //TODO all these parameters are stupid, fix it return(Assemble(resources, instructions.SkeletonType, overlay, instructions.MainType, instructions.Palette)); }