Move SSD viewport capture to typed Kotlin

ober

ce771c7f39003e1f3286a8d22c93765b5c530f18

diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index 2870f64..3fd7b89 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -1259,6 +1259,7 @@
                 nullableFloatArrayPresent nullableFloatArrayOrEmpty
                 nullableFirstFloatArrayPresent nullableFirstFloatArrayOrEmpty
                 nullableBitmapWidthOrZero nullableBitmapHeightOrZero
+                captureViewportState
                 bitmapViewReady bitmapViewNotReady bitmapViewNotReadyForBitmap
                 viewportMatchesImage viewportImageMismatch
                 motionActionIsDown motionActionIsMove
@@ -1268,6 +1269,7 @@
         (type FloatArray)
         (type Bitmap)
         (type SsdSession)
+        (type ViewportState)
         (extern (reviewBitmapWidth (bitmap : Bitmap)) : Int32
           (kotlin-member-get width))
         (extern (reviewBitmapHeight (bitmap : Bitmap)) : Int32
@@ -1366,6 +1368,22 @@
           (if (nullable-null? bitmap)
             (int32 0)
             (reviewBitmapHeight (nullable-get bitmap))))
+        (def (captureViewportState (bitmap : (Nullable Bitmap))
+                                   (scale : Float32)
+                                   (offsetX : Float32)
+                                   (offsetY : Float32)
+                                   (rotationDegrees : Int32)) : (Nullable ViewportState)
+          (if (bitmapLoaded bitmap)
+            (let ((img (nullable-get bitmap)))
+              (nullable-some
+                (make-ViewportState
+                  scale
+                  offsetX
+                  offsetY
+                  rotationDegrees
+                  (reviewBitmapWidth img)
+                  (reviewBitmapHeight img))))
+            (nullable-none ViewportState)))
         (def (viewSizeChangeNeedsFitForBitmap (oldWidth : Int32)
                                               (oldHeight : Int32)
                                               (bitmap : (Nullable Bitmap))) : Bool
@@ -8449,9 +8467,7 @@
        "    }"
        ""
        "    fun captureViewport(): ViewportState? {"
-       "        if (!bitmapLoaded(bitmap)) return null"
-       "        val img = checkNotNull(bitmap)"
-       "        return ViewportState(scale, offsetX, offsetY, rotationDegrees, img.width, img.height)"
+       "        return captureViewportState(bitmap, scale, offsetX, offsetY, rotationDegrees)"
        "    }"
        ""
        "    fun restoreViewport(viewport: ViewportState?) {"