diff --git a/src/playdate/api.nim b/src/playdate/api.nim index 984a644..80100c1 100644 --- a/src/playdate/api.nim +++ b/src/playdate/api.nim @@ -7,8 +7,8 @@ import bindings/utils {.all.} as memory import bindings/api export api -import graphics, system, file, sprite, display, sound, lua, json, utils, types -export graphics, system, file, sprite, display, sound, lua, json, utils, types +import graphics, system, file, sprite, display, sound, lua, json, utils, types, nineSlice +export graphics, system, file, sprite, display, sound, lua, json, utils, types, nineSlice macro initSDK*() = return quote do: diff --git a/src/playdate/nineSlice.nim b/src/playdate/nineSlice.nim new file mode 100644 index 0000000..f19b928 --- /dev/null +++ b/src/playdate/nineSlice.nim @@ -0,0 +1,171 @@ +import graphics +import std/[importutils, bitops, math, strutils] + +privateAccess(BitmapData) +privateAccess(LCDBitmap) + +type + NineSliceRow {.byref.} = object + ## The bytes for a single row within a nine slice + leftBytes: seq[uint8] + middleBytes: seq[uint8] + rightBytes: seq[uint8] + leftRightBitLen: int + + NineSliceData {.byref.} = object + ## Stores the rows for a single nine slice bitmap + top: seq[NineSliceRow] + middle: seq[NineSliceRow] + bottom: seq[NineSliceRow] + + NineSlice* = ref object + ## A precalculated nine slice + image: NineSliceData + case hasMask: bool + of true: mask: NineSliceData + of false: discard + +proc copyBits( + source: ptr UncheckedArray[uint8]; + target: var seq[uint8]; + sourceStartBit, sourceOffsetBit, sourceLen, targetStartBit, targetLen: int +) = + ## Copies a sequence of bits from the source to the target, where all bit positions are absolute relative to + ## the start of the source or target + let minLength = ceil((targetStartBit + targetLen) / 8).toInt + target.setLen(max(target.len, minLength)) + + for bit in 0..= 3) + assert(source.height >= 3) + + if source.getBitmapMask.resource == nil: + return NineSlice( + image: createNineSliceData(source.getData), + hasMask: false + ) + else: + return NineSlice( + image: createNineSliceData(source.getData), + hasMask: true, + mask: createNineSliceData(source.getBitmapMask.getData) + ) + +proc overlapBytes(left, right: uint8, offset: int): uint8 = + ## Given two bytes, creates a new byte that is partially made up of the left byte, and partially made up of + ## the right hand byte. For example, 00001111 and 11110000 with an offest of 5 would be ## 11111110 -- 3 bytes + ## were taken from then end of `left`, and 5 bytes were taken from the start of `right` + let leftContribution = left shl (8 - offset) + let rightContribution = right shr offset + result = leftContribution or rightContribution + +proc drawRow(target: var BitmapData, source: NineSliceRow, targetY: int) = + ## Draws a single row to a nine slice. + let targetOffsetByte = target.rowbytes * targetY + + # Draw the left column, a byte at a time + for i, byteValue in source.leftBytes: + target.data[i + targetOffsetByte] = byteValue + + # Draw the stretched out middle column + let imageWidthInBytes = target.width div 8 + let middleBytesLen = imageWidthInBytes - (2 * (source.leftRightBitLen div 8)) + for i in 0..