{"id":2687,"date":"2023-05-11T23:23:36","date_gmt":"2023-05-12T04:23:36","guid":{"rendered":"https:\/\/badecho.com\/?p=2687"},"modified":"2023-05-12T23:16:36","modified_gmt":"2023-05-13T04:16:36","slug":"clipping-regions","status":"publish","type":"post","link":"https:\/\/badecho.com\/index.php\/2023\/05\/11\/clipping-regions\/","title":{"rendered":"Clipping Regions with MonoGame"},"content":{"rendered":"\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" width=\"856\" height=\"448\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2023\/05\/ClippingRegions.png\" alt=\"Clipping Regions with MonoGame\" class=\"wp-image-2688\" srcset=\"https:\/\/badecho.com\/wp-content\/uploads\/2023\/05\/ClippingRegions.png 856w, https:\/\/badecho.com\/wp-content\/uploads\/2023\/05\/ClippingRegions-300x157.png 300w, https:\/\/badecho.com\/wp-content\/uploads\/2023\/05\/ClippingRegions-768x402.png 768w, https:\/\/badecho.com\/wp-content\/uploads\/2023\/05\/ClippingRegions-480x251.png 480w\" sizes=\"(max-width: 856px) 100vw, 856px\" \/><\/figure><\/div>\n\n\n\n<p>As work continues on my secret Bad Echo game and its accompanying (and public) <a href=\"https:\/\/github.com\/BadEcho\/core\/tree\/master\/src\/Game\" target=\"_blank\" rel=\"noreferrer noopener\">Bad Echo Game Framework<\/a>, my (initially quite limited) experience with game development has slowly grown.<\/p>\n\n\n\n<p>Given how new I (still) am to game programming, I haven&#8217;t had the opportunity to write any articles as of late, as most problems I&#8217;ve been encountering have more to do with my inexperience with the subject matter (and therefore don&#8217;t warrant writing an article about them).<\/p>\n\n\n\n<p>But it&#8217;s simply been too long! So an article I must write. And today&#8217;s will deal with <em>clipping regions<\/em> in MonoGame: what they are, what they do, and how to use them. Let&#8217;s keep this nice and short!<\/p>\n\n\n\n<h2>Out of Bounds Text!<\/h2>\n\n\n\n<p>I first became acquainted with the notion of clipping regions when designing a user interface framework for the Bad Echo game library.<\/p>\n\n\n\n<p>The user interface framework contains several controls such as <code>Label<\/code>, which will display text. Here&#8217;s an image showing a label inside a parent layout panel.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" width=\"194\" height=\"85\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2023\/05\/LabelSizedToText.png\" alt=\"A white bordered text label sized to fit its content.\" class=\"wp-image-2689\"\/><figcaption>A white bordered text label sized to fit its content.<\/figcaption><\/figure><\/div>\n\n\n\n<p>The width and height of a control may be either explicitly set or left alone, leaving it up to the layout system to figure out the dimensions needed to display the content.<\/p>\n\n\n\n<p>At one point, I tried to set the width and height to a small value, to see what the label control ended up looking like.<\/p>\n\n\n\n<p>The content of a control is restricted to the <code>Rectangle<\/code> value its <code>ContentBounds<\/code> property is set to; naturally, I expected the text content of our label control to no longer be visible if its permissible bounds were reduced such that said content could no longer fit.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" width=\"164\" height=\"70\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2023\/05\/LabelTextNotClipped.png\" alt=\"The text remains, even though the label has been shrunk.\" class=\"wp-image-2690\"\/><figcaption>The text remains.<\/figcaption><\/figure><\/div>\n\n\n\n<p>Oops.<\/p>\n\n\n\n<h2>A Look at the Drawing Code<\/h2>\n\n\n\n<p>With <code><a href=\"https:\/\/github.com\/BadEcho\/core\/blob\/master\/src\/Game\/UI\/Control.cs\" target=\"_blank\" rel=\"noreferrer noopener\">Control<\/a><\/code> derivations, the <code>ContentBounds<\/code> property is typically sourced in the control class&#8217;s overridden <code>DrawCore<\/code> method when drawing its content.<\/p>\n\n\n\n<p>The typical <code>SpriteBatch.Draw<\/code> overload we use is one accepting a destination <code>Rectangle<\/code> value that specifies the coordinates on the screen for drawing the sprite.<\/p>\n\n\n\n<p>If the destination rectangle is too small to support our content, we shouldn&#8217;t really see anything, right?<\/p>\n\n\n\n<h6>Label.DrawCore Snippet<\/h6>\n\n\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nVector2 contentPosition = ContentBounds.Location.ToVector2();\n\nspriteBatch.DrawString(Font, Text, contentPosition, Color.White);\n<\/pre>\n\n\n<p>Uh oh, looks like the <code>SpriteBatch<\/code> method for drawing <code>SpriteFont<\/code> text doesn&#8217;t actually accept a destination <code>Rectangle<\/code> value in any of its overloads, but rather a straight up <code>Vector2<\/code> position.<\/p>\n\n\n\n<p>Is there any way we can apply a constraining region for our control such that any part of the sprite outside of said region&#8217;s bounds is clipped?<\/p>\n\n\n\n<h2>Yes. Scissors and Clips.<\/h2>\n\n\n\n<p>The <code>GraphicsDevice.ScissorRectangle<\/code> property allows you to set an effective clipping region. Anything drawn outside this region during the current render pass will not be displayed on the screen.<\/p>\n\n\n\n<p>By default, this property is going to be set to the active viewport, that is (by default) the window or screen that the game is running on.<\/p>\n\n\n\n<p>We take a stack-like approach in regards to using this property, essentially pushing the current value onto a local variable, and then popping the value back onto <code>GraphicsDevice.ScissorRectangle<\/code> when we&#8217;re done with it.<\/p>\n\n\n\n<p>The <code>Control.Draw<\/code> method, which calls the overridden <code>Control.DrawCore<\/code> method, ends up being the perfect place to enforce the derived control&#8217;s <code>ContentBounds<\/code> value as the clipping region.<\/p>\n\n\n\n<h6>Control.Draw Snippet<\/h6>\n\n\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nRectangle clippingRectangle = spriteBatch.GraphicsDevice.ScissorRectangle;\n\nspriteBatch.GraphicsDevice.ScissorRectangle = ContentBounds;\n\nDrawCore(spriteBatch);\n\nspriteBatch.GraphicsDevice.ScissorRectangle = clippingRectangle;\n<\/pre>\n\n\n<h2>Setting up the SpriteBatch<\/h2>\n\n\n\n<p>Before the above code can have any sort of effect, we need to change the configuration for our <code>SpriteBatch<\/code> instance when beginning our sprite batch operation.<\/p>\n\n\n\n<h3>The Proper Drawing Order<\/h3>\n\n\n\n<p>The first parameter to <code>SpriteBatch.Begin<\/code> is a <code>SpriteSortMode<\/code> value that specifies the sprite drawing order. The default value for this parameter is <code>SpriteSortMode.Deferred<\/code>, which defers the drawing of all sprites until we make the call to <code>SpriteBatch.End<\/code>.<\/p>\n\n\n\n<p>This default behavior clearly won&#8217;t work for our purposes, given the stack-like approach we&#8217;ve taken in manipulating the <code>GraphicsDevice.ScissorRectangle<\/code> property.<\/p>\n\n\n\n<p>Remember, we&#8217;re temporarily setting the clipping region to the control&#8217;s <code>ContentBounds<\/code> value, letting the derived control make its <code>SpriteBatch.Draw<\/code> calls, and then restoring the old <code>GraphicsDevice.ScissorRectangle<\/code> value.<\/p>\n\n\n\n<p>These temporary clipping regions get ignored if we use a deferred sprite drawing order; the clipping region returned by <code>GraphicsDevice.ScissorRectangle<\/code> at the point in time <code>SpriteBatch.End<\/code> is called gets applied to every sprite in the batch instead. <\/p>\n\n\n\n<p>So, we&#8217;ll want to make sure that we pass a value of <code>SpriteSortMode.Immediate<\/code> instead, which will cause the sprites to get drawn each time <code>SpriteBatch.Draw<\/code> is called.<\/p>\n\n\n\n<h3>The Proper Rasterization<\/h3>\n\n\n\n<p>The <code>SpriteBatch.Begin<\/code> method also accepts a <code>RasterizerState<\/code> parameter, which configures how vector data (shapes) are converted into raster data (pixels).<\/p>\n\n\n\n<p>If we want to enable clipping, we need to make sure the <code>RasterizerState.ScissorTestEnable<\/code> property is set to true. A <em><a href=\"https:\/\/learn.microsoft.com\/en-us\/windows\/win32\/direct3d9\/scissor-test\" target=\"_blank\" rel=\"noreferrer noopener\">scissor test<\/a><\/em> is an operation that culls all pixels outside of a <em>scissor rectangle<\/em> (i.e., our clipping region). <\/p>\n\n\n\n<p>So, with all of that in mind, we make sure our <code>SpriteBatch<\/code> operation is begun properly, as the following snippet demonstrates:<\/p>\n\n\n\n<h6>SpriteBatch.Begin Snippet<\/h6>\n\n\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nspriteBatch.Begin(SpriteSortMode.Immediate,\n                  blendState: BlendState.AlphaBlend,\n                  rasterizerState: new RasterizerState { ScissorTestEnable = true });\n\nscreen.Draw(spriteBatch);\n\nspriteBatch.End();\n<\/pre>\n\n\n<p>And we end up with the following results:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" width=\"140\" height=\"60\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2023\/05\/LabelTextClipped.png\" alt=\"The shrunken label now clips its text.\" class=\"wp-image-2691\"\/><figcaption>The shrunken label now clips its text.<\/figcaption><\/figure><\/div>\n\n\n\n<h2>Everything Working as Expected!<\/h2>\n\n\n\n<p>Yes indeed. A clipping region lets us enforce a rectangular limit on what we wish to draw.<\/p>\n\n\n\n<p>And thus concludes this very short, and to-the-point, article.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>As work continues on my secret Bad Echo game and its accompanying (and public) Bad Echo Game Framework, my (initially [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[10],"tags":[41,42,74],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v14.9 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\r\n<title>Clipping Regions with MonoGame - omni&#039;s hackpad<\/title>\r\n<meta name=\"description\" content=\"A look at how rectangular clipping regions work with MonoGame, and how we can use them to crop visuals that exceed their allotted boundaries.\" \/>\r\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\r\n<link rel=\"canonical\" href=\"https:\/\/badecho.com\/index.php\/2023\/05\/11\/clipping-regions\/\" \/>\r\n<meta property=\"og:locale\" content=\"en_US\" \/>\r\n<meta property=\"og:type\" content=\"article\" \/>\r\n<meta property=\"og:title\" content=\"Clipping Regions with MonoGame - omni&#039;s hackpad\" \/>\r\n<meta property=\"og:description\" content=\"A look at how rectangular clipping regions work with MonoGame, and how we can use them to crop visuals that exceed their allotted boundaries.\" \/>\r\n<meta property=\"og:url\" content=\"https:\/\/badecho.com\/index.php\/2023\/05\/11\/clipping-regions\/\" \/>\r\n<meta property=\"og:site_name\" content=\"omni&#039;s hackpad\" \/>\r\n<meta property=\"article:published_time\" content=\"2023-05-12T04:23:36+00:00\" \/>\r\n<meta property=\"article:modified_time\" content=\"2023-05-13T04:16:36+00:00\" \/>\r\n<meta property=\"og:image\" content=\"https:\/\/badecho.com\/wp-content\/uploads\/2023\/05\/ClippingRegions.png\" \/>\r\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\r\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebSite\",\"@id\":\"https:\/\/badecho.com\/#website\",\"url\":\"https:\/\/badecho.com\/\",\"name\":\"omni&#039;s hackpad\",\"description\":\"Game Code Disassembly. Omnified Modification. Madness.\",\"publisher\":{\"@id\":\"https:\/\/badecho.com\/#\/schema\/person\/3de67496328be7ae6e1f52faf582e9d2\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":\"https:\/\/badecho.com\/?s={search_term_string}\",\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/badecho.com\/index.php\/2023\/05\/11\/clipping-regions\/#primaryimage\",\"inLanguage\":\"en-US\",\"url\":\"https:\/\/badecho.com\/wp-content\/uploads\/2023\/05\/ClippingRegions.png\",\"width\":856,\"height\":448,\"caption\":\"Clipping Regions with MonoGame\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/badecho.com\/index.php\/2023\/05\/11\/clipping-regions\/#webpage\",\"url\":\"https:\/\/badecho.com\/index.php\/2023\/05\/11\/clipping-regions\/\",\"name\":\"Clipping Regions with MonoGame - omni&#039;s hackpad\",\"isPartOf\":{\"@id\":\"https:\/\/badecho.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/badecho.com\/index.php\/2023\/05\/11\/clipping-regions\/#primaryimage\"},\"datePublished\":\"2023-05-12T04:23:36+00:00\",\"dateModified\":\"2023-05-13T04:16:36+00:00\",\"description\":\"A look at how rectangular clipping regions work with MonoGame, and how we can use them to crop visuals that exceed their allotted boundaries.\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/badecho.com\/index.php\/2023\/05\/11\/clipping-regions\/\"]}]},{\"@type\":\"Article\",\"@id\":\"https:\/\/badecho.com\/index.php\/2023\/05\/11\/clipping-regions\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/badecho.com\/index.php\/2023\/05\/11\/clipping-regions\/#webpage\"},\"author\":{\"@id\":\"https:\/\/badecho.com\/#\/schema\/person\/3de67496328be7ae6e1f52faf582e9d2\"},\"headline\":\"Clipping Regions with MonoGame\",\"datePublished\":\"2023-05-12T04:23:36+00:00\",\"dateModified\":\"2023-05-13T04:16:36+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/badecho.com\/index.php\/2023\/05\/11\/clipping-regions\/#webpage\"},\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/badecho.com\/#\/schema\/person\/3de67496328be7ae6e1f52faf582e9d2\"},\"image\":{\"@id\":\"https:\/\/badecho.com\/index.php\/2023\/05\/11\/clipping-regions\/#primaryimage\"},\"keywords\":\".NET,C#,MonoGame\",\"articleSection\":\"General Dev\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/badecho.com\/index.php\/2023\/05\/11\/clipping-regions\/#respond\"]}]},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/badecho.com\/#\/schema\/person\/3de67496328be7ae6e1f52faf582e9d2\",\"name\":\"Matt Weber\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/badecho.com\/#personlogo\",\"inLanguage\":\"en-US\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/7e345ac2708b3a41c7bd70a4a0440d41?s=96&d=mm&r=g\",\"caption\":\"Matt Weber\"},\"logo\":{\"@id\":\"https:\/\/badecho.com\/#personlogo\"}}]}<\/script>\r\n<!-- \/ Yoast SEO plugin. -->","_links":{"self":[{"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/posts\/2687"}],"collection":[{"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/comments?post=2687"}],"version-history":[{"count":6,"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/posts\/2687\/revisions"}],"predecessor-version":[{"id":2698,"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/posts\/2687\/revisions\/2698"}],"wp:attachment":[{"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/media?parent=2687"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/categories?post=2687"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/tags?post=2687"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}