DEV Community

Georg Becker
Georg Becker

Posted on

9-slicing for Mini Micro

TLDR Here is code to show how 9-slicing can be done in Mini Micro: https://github.com/shellrider-games/ms-Image9Slice

When creating game UIs a feature I need pretty regularly is 9-slicing. 9 slicing is a technique used to resize images that aims to prevent distortions due to scaling by ensuring that the image corners do not scale and only the middle part of an image is resized. It may sound a little unintuitive. Why we would want that behaviour? But it is a concept best understood through a visual explanation.

Image description

This works by cutting the source image into 9 different parts and scaling the individual pieces differently. The 4 corners are not scaled (1, 3, 7, 9), the border pieces are either scaled vertically (4, 6) or horizontally (2, 8), and the middle part is scaled in both directions (5).

Image description

Mini Micro does not provide this functionality out of the box (Well I was wrong there, actually there is an Image9Slice class in /sys/lib/gui). This gives us the opportunity to implement 9-slicing ourselves and understand the topic even better.

To solve this in MiniMicro I created a module called Image9Slice.

Image9Slice = {}
Image9Slice.src = null
Image9Slice.left = 0
Image9Slice.bottom = 0
Image9Slice.right = 0
Image9Slice.top = 0
Enter fullscreen mode Exit fullscreen mode

First thing I did is setup a Image9Slice class with src, which represents an Image object and left, bottom, right and top for the margins.
I also added a create function, which allows me to create new instances of my class, kinda like a constructor in other languages.

Image9Slice.create = function(src, left, bottom, right, top)
    noob = new Image9Slice
    noob.src = src
    noob.left = left
    noob.bottom = bottom
    noob.right = right
    noob.top = top
    return noob
end function
Enter fullscreen mode Exit fullscreen mode

The final magic happens in the draw function, in which I use an Image9Slice object, the position on the screen as left and bottom, the width and height of the drawn image, a pixelDisplay which is the display and an optional parameter scale which allows us to scale the source image. The scale parameter is very useful in case you want to use this technique for pixel-art UIs.

draw = function(image, left, bottom, width, height, pixelDisplay, scale=1)
    qa.assert(
      image isa Image9Slice,
      "image must be of type Image9Slice")
    qa.assert(
      pixelDisplay isa Display and pixelDisplay.mode == 3,
      "pixelDisplay must be a PixelDisplay")

    middleHeight = height - image.bottom * scale - image.top * scale
    srcHeight = image.src.height - image.bottom - image.top
    topStop = bottom + height - image.top * scale
    srcTopStop = image.src.height - image.top

    middleWidth = width - image.left * scale - image.right * scale
    srcWidth = image.src.width - image.left - image.right
    rightStop = left + width - image.right * scale
    srcRightStop = image.src.width - image.right

    pixelDisplay.drawImage image.src,
      left, bottom, image.left * scale, image.bottom * scale,
      0, 0, image.left, image.bottom
    pixelDisplay.drawImage image.src,
      left, bottom + image.bottom * scale, image.left * scale, middleHeight,
      0, image.bottom, image.left, srcHeight
    pixelDisplay.drawImage image.src,
      left, topStop, image.left * scale, image.top * scale,
      0, srcTopStop, image.left, image.top

    pixelDisplay.drawImage image.src,
      left + image.left * scale, bottom, middleWidth, image.bottom * scale,
      image.left, 0, srcWidth, image.bottom
    pixelDisplay.drawImage image.src,
      left + image.left * scale, bottom + image.bottom * scale, middleWidth, middleHeight,
      image.left, image.bottom, srcWidth, srcHeight
    pixelDisplay.drawImage image.src,
      left + image.left * scale, topStop, middleWidth, image.top * scale,
      image.left, srcTopStop, srcWidth, image.top

    pixelDisplay.drawImage image.src,
      rightStop, bottom, image.right * scale, image.bottom * scale,
      srcRightStop, 0, image.right, image.bottom
    pixelDisplay.drawImage image.src,
      rightStop, bottom + image.bottom * scale, image.right * scale, middleHeight,
      srcRightStop, image.bottom, image.right, srcHeight
    pixelDisplay.drawImage image.src,
      rightStop, topStop, image.right * scale, image.top *scale,
      srcRightStop, srcTopStop, image.right, image.top  
end function
Enter fullscreen mode Exit fullscreen mode

The bulk of the function performs calculations to figure out where on the screen to draw the 9 image slices and which pixels of the source image to use. In my demo code that runs if you clone the repo from Github (https://github.com/shellrider-games/ms-Image9Slice), I use this function to draw 2 panels from a 16 x 16 pixel source image upscaled by a factor of 3.

Image description

I hope you found this useful and maybe, next time you create an UI you can use 9-slicing as the basis for creating your own custom, reusable widgets.

Top comments (0)