Switch from "skip" (index-based) lsel implementation to spatial_id matching
diff --git a/include/avif/internal.h b/include/avif/internal.h
index 3515b41..93a4d8e 100644
--- a/include/avif/internal.h
+++ b/include/avif/internal.h
@@ -164,19 +164,21 @@
 // ---------------------------------------------------------------------------
 // avifCodecDecodeInput
 
+// Legal spatial_id values are [0,1,2,3], so this serves as a sentinel value for "do not filter by spatial_id"
+#define AVIF_SPATIAL_ID_UNSET 0xff
+
 typedef struct avifDecodeSample
 {
     avifROData data;
     avifBool ownsData;
     avifBool partialData; // if true, data exists but doesn't have all of the sample in it
 
-    uint32_t itemID; // if non-zero, data comes from a mergedExtents buffer in an avifDecoderItem, not a file offset
-    uint64_t offset; // additional offset into data. Can be used to offset into an itemID's payload as well.
-    size_t size;     //
-    uint8_t skip;    // After feeding this sample, this is how many frames must be skipped before returning a frame
-                     // This is used in layer selection, as layer selection requires that decoders decode all
-                     // layers sequentially, but only a specific layer index is actually wanted.
-    avifBool sync;   // is sync sample (keyframe)
+    uint32_t itemID;   // if non-zero, data comes from a mergedExtents buffer in an avifDecoderItem, not a file offset
+    uint64_t offset;   // additional offset into data. Can be used to offset into an itemID's payload as well.
+    size_t size;       //
+    uint8_t spatialID; // If set to a value other than AVIF_SPATIAL_ID_UNSET, the output frame's spatial_id must match
+                       // this ID, otherwise output frames from this sample should be skipped until it does.
+    avifBool sync;     // is sync sample (keyframe)
 } avifDecodeSample;
 AVIF_ARRAY_DECLARE(avifDecodeSampleArray, avifDecodeSample, sample);
 
diff --git a/src/codec_aom.c b/src/codec_aom.c
index 8c7a7e8..fc34a83 100644
--- a/src/codec_aom.c
+++ b/src/codec_aom.c
@@ -114,13 +114,15 @@
         codec->internal->iter = NULL;
     }
 
-    uint8_t skipRemaining = sample->skip;
     aom_image_t * nextFrame = NULL;
     for (;;) {
         nextFrame = aom_codec_get_frame(&codec->internal->decoder, &codec->internal->iter);
         if (nextFrame) {
-            if (skipRemaining) {
-                --skipRemaining;
+            if (sample->spatialID != AVIF_SPATIAL_ID_UNSET) {
+                if (sample->spatialID == nextFrame->spatial_id) {
+                    // Found the correct spatial_id.
+                    break;
+                }
             } else {
                 // Got an image!
                 break;
diff --git a/src/codec_dav1d.c b/src/codec_dav1d.c
index 268e633..b063fb0 100644
--- a/src/codec_dav1d.c
+++ b/src/codec_dav1d.c
@@ -76,7 +76,6 @@
         return AVIF_FALSE;
     }
 
-    uint8_t skipRemaining = sample->skip;
     for (;;) {
         if (dav1dData.data) {
             int res = dav1d_send_data(codec->internal->dav1dContext, &dav1dData);
@@ -101,10 +100,9 @@
             return AVIF_FALSE;
         } else {
             // Got a picture!
-            if (skipRemaining) {
+            if ((sample->spatialID != AVIF_SPATIAL_ID_UNSET) && (sample->spatialID != nextFrame.frame_hdr->spatial_id)) {
                 // Layer selection: skip this unwanted layer
                 dav1d_picture_unref(&nextFrame);
-                --skipRemaining;
             } else {
                 gotPicture = AVIF_TRUE;
                 break;
diff --git a/src/codec_libgav1.c b/src/codec_libgav1.c
index 93d7027..de5fa15 100644
--- a/src/codec_libgav1.c
+++ b/src/codec_libgav1.c
@@ -52,14 +52,12 @@
     codec->internal->gav1Image = NULL;
 
     const Libgav1DecoderBuffer * nextFrame = NULL;
-    uint8_t skipRemaining = sample->skip;
     for (;;) {
         if (Libgav1DecoderDequeueFrame(codec->internal->gav1Decoder, &nextFrame) != kLibgav1StatusOk) {
             return AVIF_FALSE;
         }
-        if (nextFrame && skipRemaining) {
+        if (nextFrame && (sample->spatialID != AVIF_SPATIAL_ID_UNSET) && (nextFrame->spatial_id != sample->spatialID)) {
             nextFrame = NULL;
-            --skipRemaining;
         } else {
             break;
         }
diff --git a/src/read.c b/src/read.c
index 70f573e..7be9f1f 100644
--- a/src/read.c
+++ b/src/read.c
@@ -440,8 +440,8 @@
             avifDecodeSample * sample = (avifDecodeSample *)avifArrayPushPtr(&decodeInput->samples);
             sample->offset = sampleOffset;
             sample->size = sampleSize;
-            sample->skip = 0;          // Never skip frames when decoding tracks (skip is for layer selection)
-            sample->sync = AVIF_FALSE; // to potentially be set to true following the outer loop
+            sample->spatialID = AVIF_SPATIAL_ID_UNSET; // Not filtering by spatial_id
+            sample->sync = AVIF_FALSE;                 // to potentially be set to true following the outer loop
 
             if (sampleSize > UINT64_MAX - sampleOffset) {
                 avifDiagnosticsPrintf(diag,
@@ -552,7 +552,7 @@
         assert(lselProp->u.lsel.layerID < MAX_AV1_LAYER_COUNT);
         sample->offset = 0;
         sample->size = sampleSize;
-        sample->skip = (uint8_t)lselProp->u.lsel.layerID;
+        sample->spatialID = (uint8_t)lselProp->u.lsel.layerID;
         sample->sync = AVIF_TRUE;
     } else if (allowProgressive && a1lxProp) {
         // Progressive image. Decode all layers and expose them all to the user.
@@ -570,7 +570,7 @@
             sample->itemID = item->id;
             sample->offset = offset;
             sample->size = layerSizes[i];
-            sample->skip = 0;
+            sample->spatialID = AVIF_SPATIAL_ID_UNSET;
             sample->sync = (i == 0); // Assume all layers depend on the first layer
 
             offset += layerSizes[i];
@@ -582,7 +582,7 @@
         sample->itemID = item->id;
         sample->offset = 0;
         sample->size = item->size;
-        sample->skip = 0;
+        sample->spatialID = AVIF_SPATIAL_ID_UNSET;
         sample->sync = AVIF_TRUE;
     }
     return AVIF_TRUE;