Make sure we are getting the correct atlas for glyph mask format.

Bug: b/491421267
Change-Id: I4eacd46599eca2df8c10a3fc894b9ce890fae1e2
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/1184076
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
(cherry picked from commit 0cab3e4ee34b3bca6ba7df676639d73ffe4b2135)
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/1184918
diff --git a/bench/GlyphQuadFillBench.cpp b/bench/GlyphQuadFillBench.cpp
index 6793512..4fd0965 100644
--- a/bench/GlyphQuadFillBench.cpp
+++ b/bench/GlyphQuadFillBench.cpp
@@ -68,7 +68,7 @@
         const sktext::gpu::AtlasSubRun* subRun =
                 sktext::gpu::TextBlobTools::FirstSubRun(fBlob.get());
         SkASSERT_RELEASE(subRun);
-        subRun->testingOnly_packedGlyphIDToGlyph(&fCache);
+        subRun->testingOnly_packedGlyphIDToGlyph(&fCache, subRun->maskFormat());
         fVertices.reset(new char[subRun->vertexStride(drawMatrix) * subRun->glyphCount() * 4]);
     }
 
diff --git a/gn/tests.gni b/gn/tests.gni
index 8ae8936..6286969 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -424,6 +424,7 @@
 ganesh_tests_sources = [
   "$_tests/AdvancedBlendTest.cpp",
   "$_tests/ApplyGammaTest.cpp",
+  "$_tests/AtlasOobTest.cpp",
   "$_tests/BackendAllocationTest.cpp",
   "$_tests/BackendSurfaceMutableStateTest.cpp",
   "$_tests/BlendTest.cpp",
diff --git a/src/gpu/ganesh/text/GrAtlasManager.cpp b/src/gpu/ganesh/text/GrAtlasManager.cpp
index 403bfe2..1e7d9aa 100644
--- a/src/gpu/ganesh/text/GrAtlasManager.cpp
+++ b/src/gpu/ganesh/text/GrAtlasManager.cpp
@@ -178,8 +178,7 @@
     }
     SkASSERT(glyph != nullptr);
 
-    MaskFormat glyphFormat = Glyph::FormatFromSkGlyph(skGlyph.maskFormat());
-    MaskFormat expectedMaskFormat = this->resolveMaskFormat(glyphFormat);
+    MaskFormat expectedMaskFormat = this->resolveMaskFormat(glyph->fGlyphEntryKey.fFormat);
     int bytesPerPixel = MaskFormatBytesPerPixel(expectedMaskFormat);
 
     int padding;
@@ -299,7 +298,7 @@
 
     uint64_t currentAtlasGen = atlasManager->atlasGeneration(maskFormat);
 
-    this->packedGlyphIDToGlyph(target->strikeCache());
+    this->packedGlyphIDToGlyph(target->strikeCache(), maskFormat);
 
     if (fAtlasGeneration != currentAtlasGen) {
         // Calculate the texture coordinates for the vertexes during first use (fAtlasGeneration
@@ -316,9 +315,10 @@
         for (const Variant& variant : glyphs) {
             Glyph* gpuGlyph = variant.glyph;
             SkASSERT(gpuGlyph != nullptr);
-
+            SkASSERT(gpuGlyph->fGlyphEntryKey.fFormat == maskFormat);
             if (!atlasManager->hasGlyph(maskFormat, gpuGlyph)) {
-                const SkGlyph& skGlyph = *metricsAndImages.glyph(gpuGlyph->fPackedID);
+                const SkGlyph& skGlyph =
+                    *metricsAndImages.glyph(gpuGlyph->fGlyphEntryKey.fPackedID);
                 auto code = atlasManager->addGlyphToAtlas(
                         skGlyph, gpuGlyph, srcPadding, target->resourceProvider(), uploadTarget);
                 if (code != GrDrawOpAtlas::ErrorCode::kSucceeded) {
diff --git a/src/gpu/graphite/Device.cpp b/src/gpu/graphite/Device.cpp
index ae9f4e3..197a1a8 100644
--- a/src/gpu/graphite/Device.cpp
+++ b/src/gpu/graphite/Device.cpp
@@ -1428,6 +1428,7 @@
                                   int padding) {
         return glyphs->regenerateAtlasForGraphite(begin, end, maskFormat, padding, fRecorder);
     };
+
     for (int subRunCursor = 0; subRunCursor < subRunEnd;) {
         // For the remainder of the run, add any atlas uploads to the Recorder's TextAtlasManager
         auto[ok, glyphsRegenerated] = subRun->regenerateAtlas(subRunCursor, subRunEnd,
diff --git a/src/gpu/graphite/text/TextAtlasManager.cpp b/src/gpu/graphite/text/TextAtlasManager.cpp
index 6602a76..cbb51a6 100644
--- a/src/gpu/graphite/text/TextAtlasManager.cpp
+++ b/src/gpu/graphite/text/TextAtlasManager.cpp
@@ -207,8 +207,7 @@
     }
     SkASSERT(glyph != nullptr);
 
-    MaskFormat glyphFormat = Glyph::FormatFromSkGlyph(skGlyph.maskFormat());
-    MaskFormat expectedMaskFormat = this->resolveMaskFormat(glyphFormat);
+    MaskFormat expectedMaskFormat = this->resolveMaskFormat(glyph->fGlyphEntryKey.fFormat);
     int bytesPerPixel = MaskFormatBytesPerPixel(expectedMaskFormat);
 
     int padding;
@@ -359,7 +358,7 @@
 
     uint64_t currentAtlasGen = atlasManager->atlasGeneration(maskFormat);
 
-    this->packedGlyphIDToGlyph(recorder->priv().strikeCache());
+    this->packedGlyphIDToGlyph(recorder->priv().strikeCache(), maskFormat);
 
     if (fAtlasGeneration != currentAtlasGen) {
         // Calculate the texture coordinates for the vertexes during first use (fAtlasGeneration
@@ -375,9 +374,10 @@
         for (const Variant& variant : glyphs) {
             Glyph* gpuGlyph = variant.glyph;
             SkASSERT(gpuGlyph != nullptr);
-
+            SkASSERT(gpuGlyph->fGlyphEntryKey.fFormat == maskFormat);
             if (!atlasManager->hasGlyph(maskFormat, gpuGlyph)) {
-                const SkGlyph& skGlyph = *metricsAndImages.glyph(gpuGlyph->fPackedID);
+                const SkGlyph& skGlyph =
+                    *metricsAndImages.glyph(gpuGlyph->fGlyphEntryKey.fPackedID);
                 auto code = atlasManager->addGlyphToAtlas(skGlyph, gpuGlyph, srcPadding);
                 if (code != DrawAtlas::ErrorCode::kSucceeded) {
                     success = code != DrawAtlas::ErrorCode::kError;
diff --git a/src/text/gpu/Glyph.h b/src/text/gpu/Glyph.h
index 821612d..7942006 100644
--- a/src/text/gpu/Glyph.h
+++ b/src/text/gpu/Glyph.h
@@ -14,6 +14,25 @@
 
 namespace sktext::gpu {
 
+struct GlyphEntryKey {
+    explicit GlyphEntryKey(SkPackedGlyphID id, skgpu::MaskFormat format)
+            : fPackedID(id), fFormat(format) {}
+
+    const SkPackedGlyphID fPackedID;
+    skgpu::MaskFormat fFormat;
+
+    bool operator==(const GlyphEntryKey& that) const {
+        return fPackedID == that.fPackedID && fFormat == that.fFormat;
+    }
+    bool operator!=(const GlyphEntryKey& that) const {
+        return !(*this == that);
+    }
+
+    uint32_t hash() const {
+        return fPackedID.hash();
+    }
+};
+
 class Glyph {
 public:
     static skgpu::MaskFormat FormatFromSkGlyph(SkMask::Format format) {
@@ -34,10 +53,11 @@
         SkUNREACHABLE;
     }
 
-    explicit Glyph(SkPackedGlyphID packedGlyphID) : fPackedID(packedGlyphID) {}
+    explicit Glyph(SkPackedGlyphID packedGlyphID, skgpu::MaskFormat format)
+             : fGlyphEntryKey(packedGlyphID, format) {}
 
-    const SkPackedGlyphID       fPackedID;
-    skgpu::AtlasLocator         fAtlasLocator;
+    const GlyphEntryKey fGlyphEntryKey;
+    skgpu::AtlasLocator fAtlasLocator;
 };
 
 }  // namespace sktext::gpu
diff --git a/src/text/gpu/GlyphVector.cpp b/src/text/gpu/GlyphVector.cpp
index 2a8e85f..7bec7a0 100644
--- a/src/text/gpu/GlyphVector.cpp
+++ b/src/text/gpu/GlyphVector.cpp
@@ -99,14 +99,14 @@
 
 // packedGlyphIDToGlyph must be run in single-threaded mode.
 // If fSkStrike is not sk_sp<SkStrike> then the conversion to Glyph* has not happened.
-void GlyphVector::packedGlyphIDToGlyph(StrikeCache* cache) {
+void GlyphVector::packedGlyphIDToGlyph(StrikeCache* cache, MaskFormat maskFormat) {
     if (fTextStrike == nullptr) {
         SkStrike* strike = fStrikePromise.strike();
         fTextStrike = cache->findOrCreateStrike(strike->strikeSpec());
 
         // Get all the atlas locations for each glyph.
         for (Variant& variant : fGlyphs) {
-            variant.glyph = fTextStrike->getGlyph(variant.packedGlyphID);
+            variant.glyph = fTextStrike->getGlyph(variant.packedGlyphID, maskFormat);
         }
 
         // This must be pinned for the Atlas filling to work.
diff --git a/src/text/gpu/GlyphVector.h b/src/text/gpu/GlyphVector.h
index 42b92a9..1eec632 100644
--- a/src/text/gpu/GlyphVector.h
+++ b/src/text/gpu/GlyphVector.h
@@ -68,7 +68,7 @@
     // the sub runs.
     int unflattenSize() const { return GlyphVectorSize(fGlyphs.size()); }
 
-    void packedGlyphIDToGlyph(StrikeCache* cache);
+    void packedGlyphIDToGlyph(StrikeCache* cache, skgpu::MaskFormat);
 
     static size_t GlyphVectorSize(size_t count) {
         return sizeof(Variant) * count;
diff --git a/src/text/gpu/StrikeCache.cpp b/src/text/gpu/StrikeCache.cpp
index add3127..19df483 100644
--- a/src/text/gpu/StrikeCache.cpp
+++ b/src/text/gpu/StrikeCache.cpp
@@ -207,10 +207,11 @@
         : fStrikeCache(strikeCache)
         , fStrikeSpec{strikeSpec} {}
 
-Glyph* TextStrike::getGlyph(SkPackedGlyphID packedGlyphID) {
-    Glyph* glyph = fCache.findOrNull(packedGlyphID);
+Glyph* TextStrike::getGlyph(SkPackedGlyphID packedGlyphID, skgpu::MaskFormat format) {
+    GlyphEntryKey localKey(packedGlyphID, format);
+    Glyph* glyph = fCache.findOrNull(localKey);
     if (glyph == nullptr) {
-        glyph = fAlloc.make<Glyph>(packedGlyphID);
+        glyph = fAlloc.make<Glyph>(packedGlyphID, format);
         fCache.set(glyph);
         fMemoryUsed += sizeof(Glyph);
         if (!fRemoved) {
@@ -220,11 +221,11 @@
     return glyph;
 }
 
-const SkPackedGlyphID& TextStrike::HashTraits::GetKey(const Glyph* glyph) {
-    return glyph->fPackedID;
+const GlyphEntryKey& TextStrike::HashTraits::GetKey(const Glyph* glyph) {
+    return glyph->fGlyphEntryKey;
 }
 
-uint32_t TextStrike::HashTraits::Hash(SkPackedGlyphID key) {
+uint32_t TextStrike::HashTraits::Hash(GlyphEntryKey key) {
     return key.hash();
 }
 
diff --git a/src/text/gpu/StrikeCache.h b/src/text/gpu/StrikeCache.h
index 007c45c..014afd5 100644
--- a/src/text/gpu/StrikeCache.h
+++ b/src/text/gpu/StrikeCache.h
@@ -13,6 +13,7 @@
 #include "src/core/SkDescriptor.h"
 #include "src/core/SkStrikeSpec.h"
 #include "src/core/SkTHash.h"
+#include "src/gpu/AtlasTypes.h"
 
 #include <cstddef>
 #include <cstdint>
@@ -32,6 +33,7 @@
 namespace sktext::gpu {
 
 class Glyph;
+struct GlyphEntryKey;
 class StrikeCache;
 
 // The TextStrike manages an SkArenaAlloc for Glyphs. The SkStrike is what actually creates
@@ -43,7 +45,7 @@
     TextStrike(StrikeCache* strikeCache,
                const SkStrikeSpec& strikeSpec);
 
-    Glyph* getGlyph(SkPackedGlyphID);
+    Glyph* getGlyph(SkPackedGlyphID, skgpu::MaskFormat format);
     const SkStrikeSpec& strikeSpec() const { return fStrikeSpec; }
     const SkDescriptor& getDescriptor() const { return fStrikeSpec.descriptor(); }
 
@@ -54,11 +56,11 @@
     const SkStrikeSpec fStrikeSpec;
 
     struct HashTraits {
-        static const SkPackedGlyphID& GetKey(const Glyph* glyph);
-        static uint32_t Hash(SkPackedGlyphID key);
+        static const GlyphEntryKey& GetKey(const Glyph* glyph);
+        static uint32_t Hash(GlyphEntryKey key);
     };
     // Map SkPackedGlyphID -> Glyph*.
-    skia_private::THashTable<Glyph*, SkPackedGlyphID, HashTraits> fCache;
+    skia_private::THashTable<Glyph*, GlyphEntryKey, HashTraits> fCache;
 
     // Store for the glyph information.
     SkArenaAlloc fAlloc{512};
diff --git a/src/text/gpu/SubRunContainer.cpp b/src/text/gpu/SubRunContainer.cpp
index 3a061a2..a19460c 100644
--- a/src/text/gpu/SubRunContainer.cpp
+++ b/src/text/gpu/SubRunContainer.cpp
@@ -651,8 +651,9 @@
 
     int glyphSrcPadding() const override { return 0; }
 
-    void testingOnly_packedGlyphIDToGlyph(StrikeCache* cache) const override {
-        fGlyphs.packedGlyphIDToGlyph(cache);
+    void testingOnly_packedGlyphIDToGlyph(StrikeCache* cache,
+                                          skgpu::MaskFormat maskFormat) const override {
+        fGlyphs.packedGlyphIDToGlyph(cache, maskFormat);
     }
 
     std::tuple<bool, SkRect> deviceRectAndNeedsTransform(
@@ -755,8 +756,9 @@
 
     const AtlasSubRun* testingOnly_atlasSubRun() const override { return this; }
 
-    void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const override {
-        fGlyphs.packedGlyphIDToGlyph(cache);
+    void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache,
+                                          skgpu::MaskFormat maskFormat) const override {
+        fGlyphs.packedGlyphIDToGlyph(cache, maskFormat);
     }
 
     int glyphSrcPadding() const override { return 1; }
@@ -884,8 +886,9 @@
 
     const AtlasSubRun* testingOnly_atlasSubRun() const override { return this; }
 
-    void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const override {
-        fGlyphs.packedGlyphIDToGlyph(cache);
+    void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache,
+                                          skgpu::MaskFormat maskFormat) const override {
+        fGlyphs.packedGlyphIDToGlyph(cache, maskFormat);
     }
 
     int glyphSrcPadding() const override { return SK_DistanceFieldInset; }
diff --git a/src/text/gpu/SubRunContainer.h b/src/text/gpu/SubRunContainer.h
index 2573dbb..4d1a3c8 100644
--- a/src/text/gpu/SubRunContainer.h
+++ b/src/text/gpu/SubRunContainer.h
@@ -167,7 +167,7 @@
 
     const VertexFiller& vertexFiller() const { return fVertexFiller; }
 
-    virtual void testingOnly_packedGlyphIDToGlyph(StrikeCache* cache) const = 0;
+    virtual void testingOnly_packedGlyphIDToGlyph(StrikeCache* cache, skgpu::MaskFormat) const = 0;
 
 protected:
     const VertexFiller fVertexFiller;
diff --git a/tests/AtlasOobTest.cpp b/tests/AtlasOobTest.cpp
new file mode 100644
index 0000000..4e6fb02
--- /dev/null
+++ b/tests/AtlasOobTest.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2026 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "include/core/SkCanvas.h"
+#include "include/core/SkGraphics.h"
+#include "include/core/SkSerialProcs.h"
+#include "include/core/SkSurface.h"
+#include "include/private/chromium/SkChromeRemoteGlyphCache.h"
+#include "include/private/chromium/Slug.h"
+#include "src/core/SkDescriptor.h"
+#include "src/core/SkReadBuffer.h"
+#include "src/core/SkTypeface_remote.h"
+#include "src/core/SkWriteBuffer.h"
+#include "src/gpu/AtlasTypes.h"
+#include "tests/CtsEnforcement.h"
+#include "tests/Test.h"
+#include "tools/ToolUtils.h"
+
+#if defined(SK_GANESH)
+#include "include/gpu/ganesh/GrDirectContext.h"
+#include "include/gpu/ganesh/SkSurfaceGanesh.h"
+#endif
+
+#if defined(SK_GRAPHITE)
+#include "include/gpu/graphite/Context.h"
+#include "include/gpu/graphite/Surface.h"
+#include "tools/graphite/GraphiteTestContext.h"
+#endif  // defined(SK_GRAPHITE)
+
+#include <vector>
+#include <cstring>
+
+namespace {
+class FakeDiscardableManager : public SkStrikeClient::DiscardableHandleManager {
+public:
+    bool deleteHandle(SkDiscardableHandleId) override { return false; }
+    void notifyCacheMiss(SkStrikeClient::CacheMissType, int) override {}
+    void notifyReadFailure(const ReadFailureData&) override {}
+    void assertHandleValid(SkDiscardableHandleId) override {}
+};
+
+unsigned char kStrikeData[] = {
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x07, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65,
+  0x00, 0x00, 0x00, 0x65, 0xd8, 0x50, 0xda, 0x99, 0x4c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x63, 0x65, 0x72, 0x73, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x80, 0x41,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41,
+  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x64, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41,
+  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x66, 0x86, 0x07, 0xc2, 0x42,
+  0x4c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x63, 0x65, 0x72, 0x73, 0x38, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00
+};
+
+unsigned char kDrawSlugOp[] = {
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x41,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f,
+  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41,
+  0x00, 0x00, 0x00, 0x41, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x86, 0x07, 0xc2, 0x42, 0x4c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x63, 0x65, 0x72, 0x73,
+  0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+} // namespace
+
+// TODO: We expect this test to correctly hit an SkUnreachable and then crash. That does not work
+// with our current testing framework because we have no to "expect" a crash. So for now we will
+// land this test with only the valid loop enabled, but to test this is working locally, you should
+// change the loop to have both iterations.
+static void run_atlas_oob_test(skiatest::Reporter* reporter, SkCanvas* canvas) {
+    auto discardableManager = sk_make_sp<FakeDiscardableManager>();
+    SkStrikeClient client(discardableManager, false);
+
+    // 1. Prepare Strike Data
+    if (!client.readStrikeData(kStrikeData, sizeof(kStrikeData))) {
+        REPORTER_ASSERT(reporter, false, "Failed to read initial strike data");
+    }
+
+    // 2. Prepare and Execute DrawSlug ops
+    SkPaint paint;
+    for (int idx = 0; idx < 1; ++idx) {
+//    for (int idx = 0; idx < 2; ++idx) {
+        if (idx == 0) {
+                kDrawSlugOp[0x48] = (unsigned char)skgpu::MaskFormat::kARGB;
+        } else if (idx == 1) {
+                kDrawSlugOp[0x48] = (unsigned char)skgpu::MaskFormat::kA8;
+        }
+        kDrawSlugOp[0xd8] = SkMask::kARGB32_Format;
+        kDrawSlugOp[0xe0] = 0x99;
+
+        auto slug = client.deserializeSlugForTest(kDrawSlugOp, sizeof(kDrawSlugOp));
+        if (slug) {
+            slug->draw(canvas, paint);
+        }
+    }
+
+}
+
+#if defined(SK_GANESH)
+DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(Atlas_Oob_ganesh, reporter, ctxInfo, CtsEnforcement::kNextRelease) {
+    auto dContext = ctxInfo.directContext();
+    SkImageInfo info = SkImageInfo::MakeN32Premul(1024, 1024);
+    auto surface = SkSurfaces::RenderTarget(dContext, skgpu::Budgeted::kNo, info);
+    if (!surface) return;
+    auto canvas = surface->getCanvas();
+
+    run_atlas_oob_test(reporter, canvas);
+
+    dContext->flushAndSubmit();
+}
+#endif // defined(SK_GANESH)
+
+#if defined(SK_GRAPHITE)
+DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(Atlas_Oob_graphite, reporter, context, CtsEnforcement::kNextRelease) {
+    using namespace skgpu::graphite;
+    std::unique_ptr<Recorder> recorder = context->makeRecorder();
+    SkImageInfo info = SkImageInfo::MakeN32Premul(1024, 1024);
+    auto surface = SkSurfaces::RenderTarget(recorder.get(), info);
+    if (!surface) return;
+    auto canvas = surface->getCanvas();
+
+    run_atlas_oob_test(reporter, canvas);
+
+    std::unique_ptr<Recording> recording = recorder->snap();
+    InsertRecordingInfo recordingInfo;
+    recordingInfo.fRecording = recording.get();
+    context->insertRecording(recordingInfo);
+    context->submit();
+}
+#endif // defined(SK_GRAPHITE)