Describe the Bug
Hello, dear engineer. I hope you're doing well.
I am currently using an older version of Cornerstone (the one built with vanilla JavaScript), and I’ve run into an issue.
On 32-bit Windows systems with low RAM—say, 2GB—when I open a patient study in the browser and then try to open a second one (even after closing the tab for the first patient), I get an "Uncaught RangeError: Array buffer allocation" error.
I would appreciate your guidance on this.
Unfortunately, switching to the newer version of Cornerstone isn't an option for me right now.
Many hospitals and private clinics in Iran are using this version, and a lot of them aren't in a position to upgrade to more powerful hardware.
fixed this bug
i used cornerstoneWADOImageLoader.min.js version 3.0.3
change to
cornerstoneWADOImageLoader.min.js version 4.13.2
BUT
Another issue has come up; I'm currently testing on an older phone, like an iPhone 5s.
It's throwing an error and failing to load the image.
Here is my code.
` async function loadSeries(cornerstone, imageIds, element, stack, loaded = false) {
//stopCineLoop(); // جلوگیری از اجرای همزمان
deactivateCine();
var isLoaded = loaded;
const isLowEnd = isLowEndDevice();
try {
// لود و نمایش تصویر فعلی
const image = await cornerstone.loadImage(imageIds[stack.currentImageIdIndex]);
if (!isLowEnd) {
// اگه سیستم قویه، همه عکسها رو cache کن
imageIds.forEach(imageId => cornerstone.loadAndCacheImage(imageId));
}
// ========== استخراج Image Orientation و اضافه کردن به element ==========
try {
if (image && image.data) {
// دریافت Image Orientation (تگ 0020,0037)
const imageOrientation = image.data.string('x00200037');
if (imageOrientation) {
const values = imageOrientation.split('\\').map(parseFloat);
if (values.length === 6) {
// ذخیره در dataset عنصر
//element.dataset.imageOrientation = imageOrientation;
//element.dataset.imageOrientationRow = `${values[0]},${values[1]},${values[2]}`;
//element.dataset.imageOrientationCol = `${values[3]},${values[4]},${values[5]}`;
// محاسبه و ذخیره نوع صفحه (Axial/Coronal/Sagittal)
const planeType = detectPlaneTypeFromOrientation(values);
element.dataset.planeType = planeType; // 'axial', 'coronal', 'sagittal'
}
}
// همچنین Body Part Examined را هم بگیریم (مهم برای همگامسازی)
//const bodyPartExamined = image.data.string('x00180015');
//if (bodyPartExamined) {
// element.dataset.bodyPart = bodyPartExamined;
//}
// Frame of Reference UID
//const frameOfReference = image.data.string('x00200052');
//if (frameOfReference) {
// element.dataset.frameOfReference = frameOfReference;
//}
}
} catch (err) {
console.warn('Error extracting DICOM metadata:', err);
}
// ========== پایان اضافه کردن ==========
//برای overlay 1404/09/22
lastImageWithOverlay = image;
//1404/04/23 haghverdi چک کردن فریم های هر فایل دایکام -------------------------------------
const numFrames = parseInt(image.data.string('x00280008'), 10);
//1404/12/12 اگه مولتی فریم بود aa3DCursor غیرفعلا کنیم
if (numFrames && numFrames >= 1) {
document.getElementById("cursor3dToggle").disabled = true;
document.getElementById("cursor3dToggle").style.cssText = "background: gray;cursor: no-drop;font-weight: 600;";
}
// محاسبه FPS براساس فایل دایکام و ست کردن در FPS
let fpsDicom = parseInt(image.data.string('x00180040'));
cineLoop.fps = fpsDicom ? fpsDicom : 9;
$("#cineFps").val(cineLoop.fps);
//اگر تعداد فریم ها بیشتر از یک عدد بود یعنی چند فریم هست و باید CINE Loop براش فعال کنیم
if (numFrames && numFrames > 1 && imageIds.length === 1) {
// ریست فقط برای مولتیفریم جدید
resetMultiFrame(element);
multiFrame = true;
//console.log(`این فایل ${numFrames} فریم دارد ✅`);
const baseImageId = imageIds[0];
const multiFrameIds = [];
for (var i = 0; i < numFrames; i++) {
multiFrameIds.push(`${baseImageId}?frame=${i}`);
}
stack.imageIds = multiFrameIds;
stack.currentImageIdIndex = 0;
loadSeries(cornerstone, multiFrameIds, element, stack, false);
document.querySelector("#stack").classList.add("active", "is-active");
document.querySelector("#pan").classList.remove("active", "is-active");
cornerstoneTools.setToolActive('StackScroll', {
mouseButtonMask: 1
});
cornerstoneTools.setToolActive('StackScrollMouseWheel', {});
//cornerstoneTools.setToolDisabled('ScaleOverlay');// برای از بین بردن هشدار overlay
return;
//const newImage = await cornerstone.loadImage(stack.imageIds[0]);
//cornerstone.displayImage(element, newImage);
}
cornerstone.displayImage(element, image);
cornerstone.reset(element);
// بررسی وجود Pixel Spacing (اگر نبود غیرفعال میکنیم)
//1405/03/18
//const rowSpacing = image.rowPixelSpacing;
//const colSpacing = image.columnPixelSpacing;
//if (!rowSpacing || !colSpacing) {
// console.warn("No Pixel Spacing found → Disabling ScaleOverlay.");
// cornerstoneTools.setToolDisabled('ScaleOverlay');
//}
//1405/03/18 گاهی اوقات مقدار pixelSpacing در imagerPixelSpacing هست
let rowSpacing = image.rowPixelSpacing;
let colSpacing = image.columnPixelSpacing;
if (!rowSpacing || !colSpacing) {
try {
const imagerPixelSpacing = image.data.string('x00181164');
//console.log(imagerPixelSpacing);
if (imagerPixelSpacing && imagerPixelSpacing !== 'undefined') {
const parts = imagerPixelSpacing.split('\\');
if (parts.length >= 2) {
rowSpacing = parseFloat(parts[0]);
colSpacing = parseFloat(parts[1]);
// مقدار را به خود image هم اختصاص بدهید
image.rowPixelSpacing = rowSpacing;
image.columnPixelSpacing = colSpacing;
// فقط ست کردن در image کافی نیست درست کار نمیکنه باید تو متادیتا قرار بدیم
cornerstone.metaData.addProvider(function (type, imageId) {
if (type === 'imagePlaneModule') {
return {
rowPixelSpacing: rowSpacing,
columnPixelSpacing: colSpacing,
rowImagePixelSpacing: rowSpacing,
columnImagePixelSpacing: colSpacing
};
}
}, 999);
}
} else {
console.warn("No Pixel Spacing found → Disabling ScaleOverlay.");
cornerstoneTools.setToolDisabled('ScaleOverlay');
}
} catch (error) {
console.error('Error reading ImagerPixelSpacing:', error);
cornerstoneTools.setToolDisabled('ScaleOverlay');
}
}
// متغیرهای global را بهروزرسانی کن:
currentElement = element;
currentStack = stack;
// گرفتن viewport بعد از reset (که scale اولیه تنظیم شده)
const viewport = cornerstone.getViewport(element);
const initialScale = viewport.scale;
// حالا تعریف minZoom و maxZoom
const minZoom = initialScale / 2;
const maxZoom = initialScale * 10;
//تنظیم مجدد زوم روی دسکتاپ
cornerstoneTools.getToolForElement(element, 'Zoom').configuration = {
minScale: minZoom,
maxScale: maxZoom
};
//تنظیم مجدد زوم روی موبایل
const zoomTouchPinchTool = cornerstoneTools.getToolForElement(element, 'ZoomTouchPinch');
if (zoomTouchPinchTool) {
zoomTouchPinchTool.configuration = {
minScale: minZoom,
maxScale: maxZoom
};
}
//1404/04/19
// display on or off 3D button by check modality || images length smaller from 10
var isCT_MRI = image.data.string('x00080060') === "CT" || image.data.string('x00080060') === "MR";
$("body").attr("data-modality", image.data.string('x00080060'));
//1404/12/12 3DCursor غیرفعلا کنیم
if (!isCT_MRI) {
document.getElementById("cursor3dToggle").disabled = true;
document.getElementById("cursor3dToggle").style.cssText = "background: gray;cursor: no-drop;font-weight: 600;";
}
if (!isCT_MRI || stack.imageIds.length < 10) {
document.getElementById("render3d").disabled = true;
document.getElementById("render3d").style.cssText = "background: gray;cursor: no-drop;font-weight: 600;";
//document.getElementById("render3d").setAttribute("style", "display: none !important;");
} else {
document.getElementById("render3d").disabled = false;
document.getElementById("render3d").style.cssText = "background: #fff;cursor: pointer;font-weight: 600;";
//document.getElementById("render3d").setAttribute("style", "font-size: 14px; font-weight: 600;");
}
//دکمه Cine Loop زده شده باشه
//if (isCT_MRI && stack.imageIds.length > 1 && window.innerWidth > 580)
// activateCine();
//1404/04/30 haghverdi اگر تعداد عکس ها کمتر از دو تا بود غیرفعال شود
if (stack.imageIds.length <= 1) {
document.getElementById("Cine").style.cssText = "visibility: hidden;";
} else {
document.getElementById("Cine").style.cssText = "visibility: unset;";
}
// ابزارها
cornerstoneTools.addStackStateManager(element, ['stack', toolName]);
cornerstoneTools.addToolState(element, 'stack', stack);
// وقتی تصویر تغییر کرد
function onImageRendered(e) {
const eventData = e.detail;
const currentImageIdIndex = imageIds.indexOf(eventData.image.imageId);
element.dataset.id = currentImageIdIndex;
// آپدیت اطلاعات
cornerstone.setToPixelCoordinateSystem(eventData.enabledElement, eventData.canvasContext);
element.querySelector('.WW').textContent = Math.round(eventData.viewport.voi.windowWidth);
element.querySelector('.WL').textContent = Math.round(eventData.viewport.voi.windowCenter);
element.querySelector('.Scale').textContent = +eventData.viewport.scale.toFixed(2);
element.querySelector(".FrameNum").textContent = `${currentImageIdIndex + 1} / ${imageIds.length}`;
// Range Slider
let rangeSlider = element.querySelector(".range-slider");
rangeSlider.max = imageIds.length - 1;
rangeSlider.value = currentImageIdIndex;
rangeSlider.style.display = imageIds.length === 1 ? "none" : "block";
// اگه سیستم ضعیف بود، نزدیکترین عکسها رو prefetch کن
if (isLowEnd && !isLoaded) {
isLoaded = true;
loadImagesWithBreak(imageIds, element);
}
}
element.addEventListener('cornerstoneimagerendered', onImageRendered);
//برای overlay 1404/09/22
let lastImageKey = null;
element.addEventListener('cornerstonenewimage', e => {
const img = e.detail.image;
const key = `${img.rows}x${img.columns}|${img.rowPixelSpacing}|${img.columnPixelSpacing}`;
if (lastImageKey !== key) {
cornerstone.fitToWindow(element);
lastImageKey = key;
}
lastImageWithOverlay = img;
});
element.addEventListener('cornerstoneimagerendered', drawOverlay);
//اگر مولتی فریم بود Cine loop فعال بشه
//if (multiFrame && stack.imageIds.length > 1 && window.innerWidth > 580)
// activateCine();
// متادیتا
let dateUnFormated = image.data.string('x00080012');
let dateFormated = dateUnFormated == undefined ? "no date" : `${dateUnFormated.slice(0, 4)}/${dateUnFormated.slice(4, 6)}/${dateUnFormated.slice(6, 8)}`;
element.querySelector('.Date').textContent = dateFormated;
//1405/04/02 تاریخ رو طبق زمان گرفتن تصویر تنظیم کردم که هر تصویر مشخص باشه چه تاریخی گرفته شده
//let activeItem = document.querySelector(".tumbnail-item.active");
//if (element.querySelector(".Date").textContent == "no date" && activeItem != null) {
// element.querySelector(".Date").textContent = `${activeItem.dataset.studydate.slice(0, 4)}/${activeItem.dataset.studydate.slice(4, 6)}/${activeItem.dataset.studydate.slice(6, 8)}`;
//}
let formattedDate;
let activeItem = document.querySelector(".tumbnail-item.active");
if (image.data.string('x00080022') == "" || image.data.string('x00080022') == null) {
formattedDate = `${activeItem.dataset.studydate.slice(0, 4)}/${activeItem.dataset.studydate.slice(4, 6)}/${activeItem.dataset.studydate.slice(6, 8)}`;
} else {
let date = image.data.string('x00080022');
let year = parseInt(date.slice(0, 4));
let month = parseInt(date.slice(4, 6));
let day = parseInt(date.slice(6, 8));
let jalaliDate = jalaali.toJalaali(year, month, day);
formattedDate = `${jalaliDate.jy}/${String(jalaliDate.jm).padStart(2, '0')}/${String(jalaliDate.jd).padStart(2, '0')}`;
}
if (element.querySelector(".Date").textContent == "no date" && activeItem != null) {
element.querySelector(".Date").textContent = formattedDate;
}
//1405/03/24 ساعت رو طبق زمان گرفتن تصویر تنظیم کردم که هر تصویر مشخص باشه چه ساعتی گرفته شده
//element.querySelector('.Time').textContent = activeItem != null ? activeItem.dataset.studytime : "";
element.querySelector('.Time').textContent = formatDicomTime(image.data.string('x00080032'));
element.querySelector('.left-right').textContent = image.data.string('x00204000') != "" ? image.data.string('x00204000') : "";
element.querySelector('.left-right').setAttribute("title", image.data.string('x00204000') !== "" ? image.data.string('x00204000') : "");
element.querySelector(".serieName").textContent = element.dataset.folder;
element.querySelector('.ImageType').textContent = image.data.string('x00280008');
element.querySelector('.InstitutionName').textContent = image.data.string('x00080080');
element.querySelector('.ManufactureModel').textContent = image.data.string('x00081090') != "" ? image.data.string('x00081090') : "";
element.querySelector('.StationName').textContent = image.data.string('x00081010') != "" ? image.data.string('x00081010') : "";
element.querySelector('.CoilName').textContent = image.data.string('x00181250') != "" ? image.data.string('x00181250') : "";
var patientName = image.data.string('x00100010');
element.querySelector('.PatientName').textContent = patientName ? patientName.replace(/\^/g, " ").trim() : "";
element.querySelector('.PatientID').textContent = image.data.string('x00100020');
element.querySelector('.PatientAge').textContent = image.data.string('x00101010');
element.querySelector('.Thk').textContent = image.data.string('x00180050') == undefined ? "" : image.data.string('x00180050').slice(0, 4).concat("mm");
element.querySelector('.rows').textContent = image.data.uint16('x00280010');
element.querySelector('.columns').textContent = image.data.uint16('x00280011');
element.querySelector(".loading-badge").style.display = "none";
element.querySelector(".top-right-details").style.display = "flex";
if (hideInfo) {
$("#info").trigger("click");
}
} catch (error) {
// ❌ خطا در لود تصویر
console.error('Failed to load DICOM:', error);
// مخفی کردن لودر
const loadingBadge = element.querySelector(".loading-badge");
if (loadingBadge) loadingBadge.style.display = "none";
// نمایش پیام ساده به کاربر
element.innerHTML = `
<div style="display: flex; justify-content: center; align-items: center; height: 100%; background: #1a1a1a; direction: rtl;">
<div class="text-danger p-3 text-center">
<h5>❌ خطا در بارگذاری تصویر</h5>
<span>loadSeries Fun</span>
<p class="small">${error.message || 'فایل DICOM خراب یا قابل دسترس نیست'}</p>
<button onclick="location.reload()" class="btn btn-sm btn-primary mt-2">تلاش مجدد</button>
</div>
</div>
`;
}
}
`
The code goes in this section.
فایل DICOM خراب یا قابل دسترس نیست
I took a log.
I placed markers before and after this line; it seems to run into a problem when it reaches this line.
" const image = await cornerstone.loadImage(imageIds[stack.currentImageIdIndex]);
"
Describe the Bug
Hello, dear engineer. I hope you're doing well.
I am currently using an older version of Cornerstone (the one built with vanilla JavaScript), and I’ve run into an issue.
On 32-bit Windows systems with low RAM—say, 2GB—when I open a patient study in the browser and then try to open a second one (even after closing the tab for the first patient), I get an "Uncaught RangeError: Array buffer allocation" error.
I would appreciate your guidance on this.
Unfortunately, switching to the newer version of Cornerstone isn't an option for me right now.
Many hospitals and private clinics in Iran are using this version, and a lot of them aren't in a position to upgrade to more powerful hardware.
fixed this bug
i used cornerstoneWADOImageLoader.min.js version 3.0.3
change to
cornerstoneWADOImageLoader.min.js version 4.13.2
BUT
Another issue has come up; I'm currently testing on an older phone, like an iPhone 5s.
It's throwing an error and failing to load the image.
Here is my code.
` async function loadSeries(cornerstone, imageIds, element, stack, loaded = false) {
//stopCineLoop(); // جلوگیری از اجرای همزمان
deactivateCine();
var isLoaded = loaded;
const isLowEnd = isLowEndDevice();
}
`
The code goes in this section.
فایل DICOM خراب یا قابل دسترس نیست
I took a log.
I placed markers before and after this line; it seems to run into a problem when it reaches this line.
" const image = await cornerstone.loadImage(imageIds[stack.currentImageIdIndex]);
"