summaryrefslogtreecommitdiff
path: root/indra/llkdu/llimagej2ckdu.cpp
diff options
context:
space:
mode:
authorErik Kundiman <erik@megapahit.org>2026-06-13 14:20:22 +0800
committerErik Kundiman <erik@megapahit.org>2026-06-13 17:44:11 +0800
commit191669d38d232d2bd61a0dd2252c7a3543a2b467 (patch)
treec1783f307fd217f38b31cebbe830ba0481925f8a /indra/llkdu/llimagej2ckdu.cpp
parent47b583e9caa3388cd41f70e8de0a5a950082979d (diff)
parent663bf4d3eba16e1d0a781ac5261541e7e2d6b4f2 (diff)
Merge tag 'Second_Life_Release#663bf4d3-26.3' into 26.3
Diffstat (limited to 'indra/llkdu/llimagej2ckdu.cpp')
-rw-r--r--indra/llkdu/llimagej2ckdu.cpp72
1 files changed, 70 insertions, 2 deletions
diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp
index e7ac6bdb31..74aaffbd65 100644
--- a/indra/llkdu/llimagej2ckdu.cpp
+++ b/indra/llkdu/llimagej2ckdu.cpp
@@ -158,6 +158,9 @@ public:
kdu_codestream* codestreamp);
~LLKDUDecodeState();
bool processTileDecode(F32 decode_time, bool limit_time = true);
+#ifdef LL_WINDOWS
+ bool processTileDecodeSEH(F32 decode_time, bool limit_time = true);
+#endif
private:
S32 mNumComponents;
@@ -282,10 +285,16 @@ void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, bool keep_codestream, ECod
try
{
-
S32 data_size = base.getDataSize();
S32 max_bytes = (base.getMaxBytes() ? base.getMaxBytes() : data_size);
+ // Data sanity checks
+ // 0 data size appears to be valid.
+ if (data_size < 0 || data_size > 128 * 1024 * 1024) // 128MB
+ {
+ LLTHROW(KDUError(STRINGIZE("Invalid data size: " << data_size)));
+ }
+
//
// Initialization
//
@@ -385,7 +394,8 @@ void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, bool keep_codestream, ECod
catch (std::bad_alloc&)
{
// we are in a thread, can't show an 'out of memory' here,
- // main thread will keep going
+ // as main thread will keep going
+ // Todo: should cause main thread to stop and show an error.
throw;
}
catch (...)
@@ -602,7 +612,11 @@ bool LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco
// Do the actual processing
F32 remaining_time = limit_time ? decode_time - decode_timer.getElapsedTimeF32().value() : 0.0f;
// This is where we do the actual decode. If we run out of time, return false.
+#ifdef LL_WINDOWS
+ if (mDecodeState->processTileDecodeSEH(remaining_time, limit_time))
+#else
if (mDecodeState->processTileDecode(remaining_time, limit_time))
+#endif
{
mDecodeState.reset();
}
@@ -1302,6 +1316,11 @@ all necessary level shifting, type conversion, rounding and truncation. */
LLKDUDecodeState::LLKDUDecodeState(kdu_tile tile, kdu_byte *buf, S32 row_gap,
kdu_codestream* codestreamp)
{
+ if (!buf)
+ {
+ LLTHROW(KDUError("Null buffer passed to LLKDUDecodeState"));
+ }
+
S32 c;
mTile = tile;
@@ -1311,6 +1330,11 @@ LLKDUDecodeState::LLKDUDecodeState(kdu_tile tile, kdu_byte *buf, S32 row_gap,
mNumComponents = tile.get_num_components();
llassert(mNumComponents <= 4);
+ if (mNumComponents <= 0 || mNumComponents > 4)
+ {
+ LL_WARNS() << "Invalid component count: " << mNumComponents << ", clamping to valid range" << LL_ENDL;
+ mNumComponents = llclamp(mNumComponents, 1, 4);
+ }
mUseYCC = tile.get_ycc();
for (c = 0; c < 4; ++c)
@@ -1363,6 +1387,20 @@ LLKDUDecodeState::~LLKDUDecodeState()
mTile.close();
}
+#ifdef LL_WINDOWS
+bool LLKDUDecodeState::processTileDecodeSEH(F32 decode_time, bool limit_time)
+{
+ __try
+ {
+ return processTileDecode(decode_time, limit_time);
+ }
+ __except (msc_exception_filter(GetExceptionCode(), GetExceptionInformation()))
+ {
+ return false;
+ }
+}
+#endif
+
bool LLKDUDecodeState::processTileDecode(F32 decode_time, bool limit_time)
/* Decompresses a tile, writing the data into the supplied byte buffer.
The buffer contains interleaved image components, if there are any.
@@ -1513,3 +1551,33 @@ void kdc_flow_control::process_components()
}
}
}
+
+// Layer-factored byte estimator. Walks the resolution pyramid to count
+// layers, weights by layer_factor, then picks between a sqrt-based "new"
+// estimate and a raw-dimensions "old" estimate per TextureNewByteRange.
+// Reference: https://wiki.lindenlab.com/wiki/THX1138_KDU_Improvements#Byte_Range_Study
+S32 LLImageJ2CKDU::estimateDataSize(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate) const
+{
+ S32 width = (w > 0) ? w : 2048;
+ S32 height = (h > 0) ? h : 2048;
+ S32 nb_layers = 1;
+ S32 surface = width * height;
+ S32 s = MAX_BLOCK_SIZE * MAX_BLOCK_SIZE;
+ while (surface > s)
+ {
+ nb_layers++;
+ s *= 4;
+ }
+ F32 layer_factor = 3.0f * (7 - llclamp(nb_layers, 1, 6));
+
+ width >>= discard_level;
+ height >>= discard_level;
+ width = llmax(width, 1);
+ height = llmax(height, 1);
+
+ S32 new_bytes = (S32)(sqrtf((F32)(width * height)) * (F32)comp * rate * 1000.f / layer_factor);
+ S32 old_bytes = (S32)((F32)(width * height * comp) * rate);
+ S32 bytes = (LLImage::useNewByteRange() && (new_bytes < old_bytes)) ? new_bytes : old_bytes;
+ bytes = llmax(bytes, LLImageJ2C::calcHeaderSizeJ2C());
+ return bytes;
+}