protected void OnGridMotionSubOverlaysSelectionChanged(object sender, EventArgs e) { SubOverlay selectedItem = gridMotionSubOverlays.SelectedItem; avMotionSubOverlayAnimation.DataSource = (selectedItem != null) ? selectedItem.Animation : null; }
private void gridMotionSubOverlays_SelectionChanged(object sender, EventArgs e) { SubOverlay selectedItem = gridMotionSubOverlays.SelectedItem; if (selectedItem != null) { avMotionSubOverlayAnimation.DataSource = selectedItem.Animation; } }
protected void Calculate( ObjectBase Data, BgfBitmap MainFrame, bool UseViewerFrame = true, bool ApplyYOffset = true, byte RootHotspotIndex = 0, Real Quality = DEFAULTQUALITY, bool ScalePow2 = false, uint Width = 0, uint Height = 0, bool CenterVertical = false, bool CenterHorizontal = false, bool IsCustomShrink = false, Real CustomShrink = 1.0f) { BgfBitmap mainFrame = MainFrame; BgfFile mainResource = Data.Resource; byte mainColor = Data.ColorTranslation; BgfBitmap subOvFrame; BgfBitmapHotspot subOvHotspot; SubOverlay subOvParent; BgfBitmap subOvParentFrame; BgfBitmapHotspot subOvParentHotspot; bool rootSpotFound = false; // use custom compose root (suboverlay=mainoverlay) // if it's not found, fallback to full compose if (RootHotspotIndex > 0) { SubOverlay subOv = Data.GetSubOverlayByHotspot(RootHotspotIndex); if (subOv != null) { rootSpotFound = true; if (UseViewerFrame) { mainFrame = subOv.ViewerFrame; } else { mainFrame = subOv.FrontFrame; } mainResource = subOv.Resource; mainColor = subOv.ColorTranslation; } } if (mainFrame != null && mainResource != null) { Bgf = mainFrame; BgfColor = mainColor; size.X = (Real)mainFrame.Width / (Real)mainResource.ShrinkFactor; size.Y = (Real)mainFrame.Height / (Real)mainResource.ShrinkFactor; origin.X = (Real)mainFrame.XOffset / (Real)mainResource.ShrinkFactor; origin.Y = (Real)mainFrame.YOffset / (Real)mainResource.ShrinkFactor; MaxShrink = mainResource.ShrinkFactor; // used to calculate the boundingbox V2 min = new V2(Origin.X, Origin.Y); V2 max = new V2(Size.X, Size.Y); if (ApplyYOffset) { if (origin.X > 0.0f) { max.X += origin.X; } if (origin.Y > 0.0f) { max.Y += origin.Y; } } else { max += origin; } Real x, y; // walk suboverlay structure foreach (SubOverlay subOv in Data.CurrentSubOverlays) { if (UseViewerFrame) { subOvFrame = subOv.ViewerFrame; subOvHotspot = subOv.ViewerHotspot; subOvParent = subOv.ViewerParent; } else { subOvFrame = subOv.FrontFrame; subOvHotspot = subOv.FrontHotspot; subOvParent = subOv.FrontParent; } bool isSubRoot = (subOvParent != null && subOvParent.HotSpot == RootHotspotIndex); if (subOv.Resource != null && subOvFrame != null && subOvHotspot != null && (RootHotspotIndex <= 0 || !rootSpotFound || isSubRoot)) { SubOverlay.RenderInfo subOvInfo = new SubOverlay.RenderInfo(); // save subov & bitmap subOvInfo.SubOverlay = subOv; subOvInfo.Bgf = subOvFrame; // calculate the size of this suboverlay subOvInfo.Size.X = (Real)subOvFrame.Width / (Real)subOv.Resource.ShrinkFactor; subOvInfo.Size.Y = (Real)subOvFrame.Height / (Real)subOv.Resource.ShrinkFactor; // update maxshrink if greater if (subOv.Resource.ShrinkFactor > MaxShrink) { MaxShrink = subOv.Resource.ShrinkFactor; } // CASE 1: SubOverlay on mainoverlay if (subOvParent == null || isSubRoot) { // calculate the origin of this suboverlay on the mainoverlay subOvInfo.Origin.X = mainFrame.XOffset + ((Real)subOvHotspot.X) + ((Real)subOvFrame.XOffset); subOvInfo.Origin.Y = mainFrame.YOffset + ((Real)subOvHotspot.Y) + ((Real)subOvFrame.YOffset); subOvInfo.Origin.X /= mainResource.ShrinkFactor; subOvInfo.Origin.Y /= mainResource.ShrinkFactor; // determine type of hotspot if (subOvHotspot.Index < 0) { subOvInfo.HotspotType = HotSpotType.HOTSPOT_UNDER; } else { subOvInfo.HotspotType = HotSpotType.HOTSPOT_OVER; } } // CASE 2: SubOverlay on SubOverlay on MainOverlay else { if (UseViewerFrame) { subOvParentFrame = subOvParent.ViewerFrame; subOvParentHotspot = subOvParent.ViewerHotspot; } else { subOvParentFrame = subOvParent.FrontFrame; subOvParentHotspot = subOvParent.FrontHotspot; } if (subOvParentHotspot != null && subOvParentFrame != null && subOvParent.Resource != null) { // calculate the origin of this suboverlay on the suboverlay on the mainoverlay subOvInfo.Origin.X = (mainFrame.XOffset + (Real)subOvParentHotspot.X + (Real)subOvParentFrame.XOffset) / (Real)mainResource.ShrinkFactor; subOvInfo.Origin.X += ((Real)subOvHotspot.X + (Real)subOvFrame.XOffset) / (Real)subOvParent.Resource.ShrinkFactor; subOvInfo.Origin.Y = (mainFrame.YOffset + (Real)subOvParentHotspot.Y + (Real)subOvParentFrame.YOffset) / (Real)mainResource.ShrinkFactor; subOvInfo.Origin.Y += ((Real)subOvHotspot.Y + (Real)subOvFrame.YOffset) / (Real)subOvParent.Resource.ShrinkFactor; // determine type of nested hotspot if (subOvParentHotspot.Index > 0 && subOvHotspot.Index > 0) { subOvInfo.HotspotType = HotSpotType.HOTSPOT_OVEROVER; } else if (subOvParentHotspot.Index > 0 && subOvHotspot.Index < 0) { subOvInfo.HotspotType = HotSpotType.HOTSPOT_OVERUNDER; } else if (subOvParentHotspot.Index < 0 && subOvHotspot.Index > 0) { subOvInfo.HotspotType = HotSpotType.HOTSPOT_UNDEROVER; } else if (subOvParentHotspot.Index < 0 && subOvHotspot.Index < 0) { subOvInfo.HotspotType = HotSpotType.HOTSPOT_UNDERUNDER; } } } // update max boundingbox if (subOvInfo.Origin.X < min.X) { min.X = subOvInfo.Origin.X; } if (subOvInfo.Origin.Y < min.Y) { min.Y = subOvInfo.Origin.Y; } x = subOvInfo.Origin.X + subOvInfo.Size.X; y = subOvInfo.Origin.Y + subOvInfo.Size.Y; if (x > max.X) { max.X = x; } if (y > max.Y) { max.Y = y; } // save info for this suboverlay SubBgf.Add(subOvInfo); } } // get dimension from boundingbox dimension.X = Math.Abs(max.X - min.X); dimension.Y = Math.Abs(max.Y - min.Y); // move all origins so minimum hits 0/0 // preparation for drawing (pixel origin is 0/0) Translate(-min); // get the center of the dimension box // this is also the center of our image after the translate above V2 bbCenter = dimension * 0.5f; // get the center of the main overlay V2 mainOriginCenter = Origin + (Size * 0.5f); // move the x center of the main overlay to the x center of dimensionbox V2 centerMove = new V2(bbCenter.X - mainOriginCenter.X, 0.0f); Translate(centerMove); // since this moves a part outside the dimension box // we need to add this size of the move to the dimension, // to the right AND left side, so our centering above stays centered // then we remove to the center. V2 center = new V2(Math.Abs(centerMove.X), 0.0f); dimension.X += center.X * 2.0f; Translate(center); // scale so highest resolution resource has 1:1 ratio (no downscale) // or to user provided shrink value Scale(IsCustomShrink ? CustomShrink : (Real)MaxShrink); // user defined sizes if (Width > 0 && Height > 0) { // use user given size V2 userSize = new V2(Width, Height); // scale so we use at least all pixels either from upscaled width or height ScaleToBox(userSize, CenterHorizontal, CenterVertical, !IsCustomShrink); } else { Real maxQuality = Quality * QUALITYBASE; Real ratioX = maxQuality / dimension.X; Real ratioY = maxQuality / dimension.Y; if (ratioX <= ratioY && ratioX < 1.0f) { Scale(ratioX); } else if (ratioX > ratioY && ratioY < 1.0f) { Scale(ratioY); } // scale up to pow2 if set if (ScalePow2) { // get next power of 2 size V2 pow2Size = new V2( MathUtil.NextPowerOf2((uint)System.Math.Ceiling(dimension.X)), MathUtil.NextPowerOf2((uint)System.Math.Ceiling(dimension.Y))); // scale so we use at least all pixels either from upscaled width or height ScaleToBox(pow2Size, CenterHorizontal, CenterVertical, !IsCustomShrink); } } // calculate the world-size (shrink=1) of the composed object from dimension, uv-coordinates and scaling worldsize.X = (uvend.X * dimension.X) / Scaling; worldsize.Y = (uvend.Y * dimension.Y) / Scaling; } }
public void ProcessRequest(HttpContext context) { // -------------------------------------------------------------------------------------------- // 1) PARSE URL PARAMETERS // -------------------------------------------------------------------------------------------- // See Global.asax: // object/{scale}/{file}/{group}/{palette}/{angle} RouteValueDictionary parms = context.Request.RequestContext.RouteData.Values; string parmScale = parms.ContainsKey("scale") ? (string)parms["scale"] : null; string parmFile = parms.ContainsKey("file") ? (string)parms["file"] : null; string parmGroup = parms.ContainsKey("group") ? (string)parms["group"] : null; string parmPalette = parms.ContainsKey("palette") ? (string)parms["palette"] : null; string parmAngle = parms.ContainsKey("angle") ? (string)parms["angle"] : null; BgfCache.Entry entry; ushort scale; byte paletteidx = 0; ushort angle = 0; // verify that minimum parameters are valid/in range and bgf exists // angle ranges from [0-7] and are multiples of 512 if (!UInt16.TryParse(parmScale, out scale) || scale < MINSCALE || scale > MAXSCALE || !Byte.TryParse(parmPalette, out paletteidx) || !UInt16.TryParse(parmAngle, out angle) || angle > 7 || String.IsNullOrEmpty(parmFile) || !BgfCache.GetBGF(parmFile, out entry)) { context.Response.StatusCode = 404; return; } // multiply by 512 and remove full periods from angle angle = (ushort)((angle << 9) % GeometryConstants.MAXANGLE); // parse animation Animation anim = Animation.ExtractAnimation(parmGroup, '-'); if (anim == null || !anim.IsValid(entry.Bgf.FrameSets.Count)) { context.Response.StatusCode = 404; return; } // stores the latest lastmodified of main and all subov DateTime lastModified = entry.LastModified; // read suboverlay array params from query parameters: // object/..../?subov={file};{group};{palette};{hotspot}&subov=... string[] parmSubOverlays = context.Request.Params.GetValues("s"); if (parmSubOverlays != null) { foreach (string s in parmSubOverlays) { string[] subOvParms = s.Split(';'); BgfCache.Entry bgfSubOv; byte subOvPalette; byte subOvHotspot; if (subOvParms == null || subOvParms.Length < 4 || String.IsNullOrEmpty(subOvParms[0]) || String.IsNullOrEmpty(subOvParms[1]) || !byte.TryParse(subOvParms[2], out subOvPalette) || !byte.TryParse(subOvParms[3], out subOvHotspot) || subOvHotspot > 127 || !BgfCache.GetBGF(subOvParms[0], out bgfSubOv)) { context.Response.StatusCode = 404; return; } // get suboverlay animation Animation subOvAnim = Animation.ExtractAnimation(subOvParms[1], '-'); if (subOvAnim == null || !subOvAnim.IsValid(bgfSubOv.Bgf.FrameSets.Count)) { context.Response.StatusCode = 404; return; } // create suboverlay SubOverlay subOv = new SubOverlay(0, subOvAnim, subOvHotspot, subOvPalette, 0); // set bgf resource subOv.Resource = bgfSubOv.Bgf; // update lastModified if subov is newer if (bgfSubOv.LastModified > lastModified) { lastModified = bgfSubOv.LastModified; } // add to gameobject's suboverlays gameObject.SubOverlays.Add(subOv); } } // -------------------------------------------------------------------------------------------- // 2) SET CACHE HEADERS AND COMPARE FOR 304 RETURN // -------------------------------------------------------------------------------------------- // set cache behaviour context.Response.Cache.SetCacheability(HttpCacheability.Public); context.Response.Cache.VaryByParams["*"] = false; context.Response.Cache.SetLastModified(lastModified); // set return type context.Response.ContentType = "image/png"; context.Response.AddHeader("Content-Disposition", "inline; filename=object.png"); // check if client has valid cached version (returns 304) /*DateTime dateIfModifiedSince; * string modSince = context.Request.Headers["If-Modified-Since"]; * * // try to parse received client header * bool parseOk = DateTime.TryParse( * modSince, CultureInfo.CurrentCulture, * DateTimeStyles.AdjustToUniversal, out dateIfModifiedSince); * * // send 304 and stop if last file write equals client's cache timestamp * if (parseOk && dateIfModifiedSince == lastModified) * { * context.Response.SuppressContent = true; * context.Response.StatusCode = 304; * return; * }*/ // -------------------------------------------------------------------------------------------- // 3) PREPARE RESPONSE // -------------------------------------------------------------------------------------------- // set parsed/created values on game object gameObject.Resource = entry.Bgf; gameObject.ColorTranslation = paletteidx; gameObject.Animation = anim; gameObject.ViewerAngle = angle; // set imagecomposer scale/shrink imageComposer.CustomShrink = (float)scale * 0.1f; // tick object (triggers image recreation) gameObject.Tick(0, 1); if (imageComposer.Image == null) { context.Response.StatusCode = 404; return; } // -------------------------------------------------------------------------------------------- // 4) CREATE RESPONSE AND FINISH // -------------------------------------------------------------------------------------------- // write image as png imageComposer.Image.Save(context.Response.OutputStream, ImageFormat.Png); // clear imageComposer.Image.Dispose(); gameObject.SubOverlays.Clear(); }
public void ProcessRequest(HttpContext context) { // -------------------------------------------------------------------------------------------- // 1) PARSE URL PARAMETERS // -------------------------------------------------------------------------------------------- // See Global.asax: // render/{width}/{height}/{scale}/{file}/{anim}/{palette}/{angle} RouteValueDictionary parms = context.Request.RequestContext.RouteData.Values; string parmWidth = parms.ContainsKey("width") ? (string)parms["width"] : null; string parmHeight = parms.ContainsKey("height") ? (string)parms["height"] : null; string parmScale = parms.ContainsKey("scale") ? (string)parms["scale"] : null; string parmFile = parms.ContainsKey("file") ? (string)parms["file"] : null; string parmAnim = parms.ContainsKey("anim") ? (string)parms["anim"] : null; string parmPalette = parms.ContainsKey("palette") ? (string)parms["palette"] : null; string parmAngle = parms.ContainsKey("angle") ? (string)parms["angle"] : null; BgfCache.Entry entry; byte paletteidx; ushort angle; ushort width; ushort height; ushort scale; // verify that minimum parameters are valid/in range and bgf exists // angle ranges from [0-7] and are multiples of 512 if (!UInt16.TryParse(parmWidth, out width) || width < MINWIDTH || width > MAXWIDTH || !UInt16.TryParse(parmHeight, out height) || height < MINHEIGHT || height > MAXHEIGHT || !UInt16.TryParse(parmScale, out scale) || scale < MINSCALE || scale > MAXSCALE || !Byte.TryParse(parmPalette, out paletteidx) || !UInt16.TryParse(parmAngle, out angle) || angle > 7 || String.IsNullOrEmpty(parmFile) || !BgfCache.GetBGF(parmFile, out entry)) { Finish(context, 404); return; } // multiply by 512 and remove full periods from angle angle = (ushort)((angle << 9) % GeometryConstants.MAXANGLE); // parse animation Animation anim = Animation.ExtractAnimation(parmAnim, '-'); if (anim == null || !anim.IsValid(entry.Bgf.FrameSets.Count)) { Finish(context, 404); return; } // set groupmax anim.GroupMax = entry.Bgf.FrameSets.Count; // stores the latest lastmodified of main and all subov DateTime lastModified = entry.LastModified; // set parsed/created values on game-object ObjectBase gameObject = new ObjectBase(); gameObject.OverlayFileRID = entry.Num; gameObject.Resource = entry.Bgf; gameObject.ColorTranslation = paletteidx; gameObject.Animation = anim; gameObject.ViewerAngle = angle; // read suboverlay array params from query parameters: // object/..../?subov={file};{anim};{palette};{hotspot}&subov=... string[] parmSubOverlays = context.Request.Params.GetValues("s"); if (parmSubOverlays != null) { foreach (string s in parmSubOverlays) { string[] subOvParms = s.Split(';'); BgfCache.Entry bgfSubOv; byte subOvPalette; byte subOvHotspot; if (subOvParms == null || subOvParms.Length < 4 || String.IsNullOrEmpty(subOvParms[0]) || String.IsNullOrEmpty(subOvParms[1]) || !byte.TryParse(subOvParms[2], out subOvPalette) || !byte.TryParse(subOvParms[3], out subOvHotspot) || subOvHotspot > 127 || !BgfCache.GetBGF(subOvParms[0], out bgfSubOv)) { Finish(context, 404); return; } // get suboverlay animation Animation subOvAnim = Animation.ExtractAnimation(subOvParms[1], '-'); if (subOvAnim == null || !subOvAnim.IsValid(bgfSubOv.Bgf.FrameSets.Count)) { Finish(context, 404); return; } // set group max subOvAnim.GroupMax = bgfSubOv.Bgf.FrameSets.Count; // create suboverlay SubOverlay subOv = new SubOverlay(0, subOvAnim, subOvHotspot, subOvPalette, 0); // set bgf resource subOv.ResourceID = entry.Num; subOv.Resource = bgfSubOv.Bgf; // update lastModified if subov is newer if (bgfSubOv.LastModified > lastModified) { lastModified = bgfSubOv.LastModified; } // add to gameobject's suboverlays gameObject.SubOverlays.Add(subOv); } } // -------------------------------------------------------------------------------------------- // 2) SET CACHE HEADERS AND COMPARE FOR 304 RETURN // -------------------------------------------------------------------------------------------- // set cache behaviour context.Response.Cache.SetCacheability(HttpCacheability.Public); context.Response.Cache.VaryByParams["*"] = false; context.Response.Cache.SetLastModified(lastModified); // set return type context.Response.ContentType = "image/gif"; context.Response.AddHeader("Content-Disposition", "inline; filename=object.gif"); // check if client has valid cached version (returns 304) /*DateTime dateIfModifiedSince; * string modSince = context.Request.Headers["If-Modified-Since"]; * * // try to parse received client header * bool parseOk = DateTime.TryParse( * modSince, CultureInfo.CurrentCulture, * DateTimeStyles.AdjustToUniversal, out dateIfModifiedSince); * * // send 304 and stop if last file write equals client's cache timestamp * if (parseOk && dateIfModifiedSince == lastModified) * { * context.Response.SuppressContent = true; * Finish(context, 304); * return; * }*/ // -------------------------------------------------------------------------------------------- // 3) PREPARE RESPONSE // -------------------------------------------------------------------------------------------- // set gif instance size gif.CanvasWidth = width; gif.CanvasHeight = height; // set imagecomposer size and shrink imageComposer.Width = width; imageComposer.Height = height; imageComposer.CustomShrink = (float)scale * 0.1f; // set gameobject (causes initial frame) imageComposer.DataSource = gameObject; // run animationlength in 1 ms steps (causes new image events) for (int i = 0; i < gameObject.AnimationLength + 10; i++) { tick += 1.0; gameObject.Tick(tick, 1.0); } // handle single frame case if (gif.Frames.Count == 0 && frame != null) { gif.Frames.Add(frame); } // -------------------------------------------------------------------------------------------- // 4) CREATE RESPONSE AND FINISH // -------------------------------------------------------------------------------------------- // write the gif to output stream gif.Write(context.Response.OutputStream); // cleanup Finish(context); }