only make textures when GL context exists
Some checks failed
Lint / lint (push) Has been cancelled
Python CI / test (push) Has been cancelled
Tests / test (3.10) (push) Has been cancelled
Tests / test (3.11) (push) Has been cancelled
Tests / test (3.9) (push) Has been cancelled

This commit is contained in:
Duncan Tourolle 2025-11-27 13:40:43 +01:00
parent 73c504abb7
commit fc672bcdff

View File

@ -199,6 +199,11 @@ class ImageData(BaseLayoutElement):
# The actual image will be loaded in the background and the texture created
# via _on_async_image_loaded() callback when ready
# Create texture from pending image if one exists (deferred from async load)
# This ensures texture creation happens when GL context is active
if hasattr(self, '_pending_pil_image') and self._pending_pil_image is not None:
self._create_texture_from_pending_image()
# Use cached texture if available
if hasattr(self, '_texture_id') and self._texture_id:
texture_id = self._texture_id
@ -337,12 +342,13 @@ class ImageData(BaseLayoutElement):
"""
Callback when async image loading completes.
NOTE: This is called from a signal, potentially before GL context is ready.
We store the image and create the texture during the next render() call
when the GL context is guaranteed to be active.
Args:
pil_image: Loaded PIL Image (already RGBA, already resized)
"""
from OpenGL.GL import (glGenTextures, glBindTexture, glTexImage2D, GL_TEXTURE_2D,
glTexParameteri, GL_TEXTURE_MIN_FILTER, GL_TEXTURE_MAG_FILTER,
GL_LINEAR, GL_RGBA, GL_UNSIGNED_BYTE, glDeleteTextures)
from PIL import Image
try:
@ -359,6 +365,38 @@ class ImageData(BaseLayoutElement):
pil_image = pil_image.transpose(Image.ROTATE_90) # CCW 270 = rotate left
print(f"ImageData: Applied PIL rotation {angle}° to {self.image_path}")
# Store the image for texture creation during next render()
# This avoids GL context issues when callback runs on wrong thread/timing
self._pending_pil_image = pil_image
self._img_width = pil_image.width
self._img_height = pil_image.height
self._async_loading = False
# Update metadata for future renders - always update to reflect rotated dimensions
self.image_dimensions = (pil_image.width, pil_image.height)
# print(f"ImageData: Image ready for texture creation: {self.image_path}")
except Exception as e:
print(f"ImageData: Error processing async loaded image: {e}")
self._pending_pil_image = None
self._async_loading = False
def _create_texture_from_pending_image(self):
"""
Create OpenGL texture from pending PIL image.
Called during render() when GL context is active.
"""
if not hasattr(self, '_pending_pil_image') or self._pending_pil_image is None:
return False
from OpenGL.GL import (glGenTextures, glBindTexture, glTexImage2D, GL_TEXTURE_2D,
glTexParameteri, GL_TEXTURE_MIN_FILTER, GL_TEXTURE_MAG_FILTER,
GL_LINEAR, GL_RGBA, GL_UNSIGNED_BYTE, glDeleteTextures)
try:
pil_image = self._pending_pil_image
# Delete old texture if it exists
if hasattr(self, '_texture_id') and self._texture_id:
glDeleteTextures([self._texture_id])
@ -376,19 +414,18 @@ class ImageData(BaseLayoutElement):
# Cache texture
self._texture_id = texture_id
self._texture_path = self.image_path
self._img_width = pil_image.width
self._img_height = pil_image.height
self._async_loading = False
# Update metadata for future renders - always update to reflect rotated dimensions
self.image_dimensions = (pil_image.width, pil_image.height)
# Clear pending image to free memory
self._pending_pil_image = None
# print(f"ImageData: Async loaded texture for {self.image_path}")
# print(f"ImageData: Created texture for {self.image_path}")
return True
except Exception as e:
print(f"ImageData: Error creating texture from async loaded image: {e}")
print(f"ImageData: Error creating texture: {e}")
self._texture_id = None
self._async_loading = False
self._pending_pil_image = None
return False
def _on_async_image_load_failed(self, error_msg: str):
"""