Move SSD viewport checks to typed Kotlin

ober

850d12ea566d570845b70e5940aa02f09234ac63

diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index 8308bf6..7c5afa0 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -1176,6 +1176,7 @@
            (imageHeight : Int32)))))
 
     (typed-kotlin-file "com/sfb/ssdreview/SsdReviewGeometry.kt"
+      (kotlin-imports (android graphics Bitmap))
       (typed-library (com sfb ssdreview)
         (export hitGroupId selectionMatchingCells cellIdsForSelection
                 clampInitialScale clampScale clampGroupFitScale
@@ -1194,13 +1195,15 @@
                 touchPointerReady touchPointerNotReady
                 viewDimensionsReady previousViewSizeMissing
                 viewDimensionsMissing viewSizeChangeNeedsFit
-                bitmapViewReady bitmapViewNotReady
+                bitmapLoaded viewSizeChangeNeedsFitForBitmap
+                bitmapViewReady bitmapViewNotReady bitmapViewNotReadyForBitmap
                 viewportMatchesImage viewportImageMismatch
                 motionActionIsDown motionActionIsMove
                 motionActionIsUp motionActionIsUpOrCancel)
         (type Int32)
         (type Float32)
         (type FloatArray)
+        (type Bitmap)
         (def (selectionCellMatches? (rect : FloatArray) (cell : SsdCell)) : Bool
           (let ((overlap (rectOverlapArea rect (ssdCellRect cell)))
                 (area (* (SsdCell-w cell) (SsdCell-h cell))))
@@ -1263,10 +1266,20 @@
                                      (oldHeight : Int32)
                                      (hasBitmap : Bool)) : Bool
           (or (previousViewSizeMissing oldWidth oldHeight) (not hasBitmap)))
+        (def (bitmapLoaded (bitmap : (Nullable Bitmap))) : Bool
+          (not (nullable-null? bitmap)))
+        (def (viewSizeChangeNeedsFitForBitmap (oldWidth : Int32)
+                                              (oldHeight : Int32)
+                                              (bitmap : (Nullable Bitmap))) : Bool
+          (viewSizeChangeNeedsFit oldWidth oldHeight (bitmapLoaded bitmap)))
         (def (bitmapViewReady (hasBitmap : Bool) (width : Int32) (height : Int32)) : Bool
           (and hasBitmap (viewDimensionsReady width height)))
         (def (bitmapViewNotReady (hasBitmap : Bool) (width : Int32) (height : Int32)) : Bool
           (not (bitmapViewReady hasBitmap width height)))
+        (def (bitmapViewNotReadyForBitmap (bitmap : (Nullable Bitmap))
+                                          (width : Int32)
+                                          (height : Int32)) : Bool
+          (bitmapViewNotReady (bitmapLoaded bitmap) width height))
         (def (viewportMatchesImage (viewport : ViewportState)
                                    (imageWidth : Int32)
                                    (imageHeight : Int32)
@@ -4268,6 +4281,7 @@
                 shouldShowFiringArc shouldFocusFiringArc
                 canvasLabelPresent canvasLabelMissing groupIdSelected
                 shouldCaptureViewport pdfUriLoaded sessionMissing
+                viewportStatePresent viewportStateOrEmpty
                 hasReplacementLabel missingReplacementLabel hasResolvedLabel
                 ssdBrowserResumeReady focusChangeShouldShowDropdown
                 dialogReadyToShowDropdown focusedDialogReadyToShowDropdown
@@ -4314,6 +4328,7 @@
         (type DocumentFile)
         (type JSONObject)
         (type SsdSession)
+        (type ViewportState)
         (extern (fileIsFile (file : File)) : Bool
           (kotlin-member-get isFile))
         (extern (fileName (file : File)) : String
@@ -4760,6 +4775,18 @@
           (not (nullable-null? uri)))
         (def (sessionMissing (session : (Nullable SsdSession))) : Bool
           (nullable-null? session))
+        (def (viewportStatePresent (viewport : (Nullable ViewportState))) : Bool
+          (not (nullable-null? viewport)))
+        (def (viewportStateOrEmpty (viewport : (Nullable ViewportState))) : ViewportState
+          (if (nullable-null? viewport)
+            (make-ViewportState
+              (float32 1.0)
+              (float32 0.0)
+              (float32 0.0)
+              (int32 0)
+              (int32 0)
+              (int32 0))
+            (nullable-get viewport)))
         (def (pdfLoaded (hasPdf : Bool)) : Bool
           hasPdf)
         (def (pageNavigationReady (hasPdf : Bool) (pageCount : Int32)) : Bool
@@ -7376,14 +7403,15 @@
        "            .putInt(PREF_LAST_PAGE_INDEX, pageIndex)"
        "            .putInt(PREF_LAST_PAGE_COUNT, pageCount)"
        "        val viewport = if (shouldCaptureViewport(includeViewport)) reviewView.captureViewport() else null"
-       "        if (viewport != null) {"
+       "        if (viewportStatePresent(viewport)) {"
+       "            val savedViewport = viewportStateOrEmpty(viewport)"
        "            edit.putInt(PREF_LAST_VIEWPORT_PAGE_INDEX, pageIndex)"
-       "                .putFloat(PREF_LAST_VIEWPORT_SCALE, viewport.scale)"
-       "                .putFloat(PREF_LAST_VIEWPORT_OFFSET_X, viewport.offsetX)"
-       "                .putFloat(PREF_LAST_VIEWPORT_OFFSET_Y, viewport.offsetY)"
-       "                .putInt(PREF_LAST_VIEWPORT_ROTATION, viewport.rotationDegrees)"
-       "                .putInt(PREF_LAST_VIEWPORT_IMAGE_WIDTH, viewport.imageWidth)"
-       "                .putInt(PREF_LAST_VIEWPORT_IMAGE_HEIGHT, viewport.imageHeight)"
+       "                .putFloat(PREF_LAST_VIEWPORT_SCALE, savedViewport.scale)"
+       "                .putFloat(PREF_LAST_VIEWPORT_OFFSET_X, savedViewport.offsetX)"
+       "                .putFloat(PREF_LAST_VIEWPORT_OFFSET_Y, savedViewport.offsetY)"
+       "                .putInt(PREF_LAST_VIEWPORT_ROTATION, savedViewport.rotationDegrees)"
+       "                .putInt(PREF_LAST_VIEWPORT_IMAGE_WIDTH, savedViewport.imageWidth)"
+       "                .putInt(PREF_LAST_VIEWPORT_IMAGE_HEIGHT, savedViewport.imageHeight)"
        "        } else {"
        "            edit.remove(PREF_LAST_VIEWPORT_PAGE_INDEX)"
        "                .remove(PREF_LAST_VIEWPORT_SCALE)"
@@ -7949,7 +7977,7 @@
 	       "    })"
        ""
        "    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {"
-       "        if (viewSizeChangeNeedsFit(oldw, oldh, bitmap != null)) {"
+       "        if (viewSizeChangeNeedsFitForBitmap(oldw, oldh, bitmap)) {"
        "            fitImage()"
 	       "        } else {"
 	       "            clampOffsets()"
@@ -8069,7 +8097,7 @@
 	       "    }"
 	       ""
 	       "    fun centerOnGroup(group: SsdGroup) {"
-       "        if (bitmapViewNotReady(bitmap != null, width, height)) return"
+       "        if (bitmapViewNotReadyForBitmap(bitmap, width, height)) return"
 	       "        scale = focusGroupScale(scale, width.toFloat(), height.toFloat(), group)"
 	       "        updateMatrix()"
 	       "        val groupCenter = groupCenterPoint(group)"