only make textures when GL context exists
This commit is contained in:
parent
73c504abb7
commit
fc672bcdff
@ -199,6 +199,11 @@ class ImageData(BaseLayoutElement):
|
|||||||
# The actual image will be loaded in the background and the texture created
|
# The actual image will be loaded in the background and the texture created
|
||||||
# via _on_async_image_loaded() callback when ready
|
# 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
|
# Use cached texture if available
|
||||||
if hasattr(self, '_texture_id') and self._texture_id:
|
if hasattr(self, '_texture_id') and self._texture_id:
|
||||||
texture_id = self._texture_id
|
texture_id = self._texture_id
|
||||||
@ -337,12 +342,13 @@ class ImageData(BaseLayoutElement):
|
|||||||
"""
|
"""
|
||||||
Callback when async image loading completes.
|
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:
|
Args:
|
||||||
pil_image: Loaded PIL Image (already RGBA, already resized)
|
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
|
from PIL import Image
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -359,6 +365,38 @@ class ImageData(BaseLayoutElement):
|
|||||||
pil_image = pil_image.transpose(Image.ROTATE_90) # CCW 270 = rotate left
|
pil_image = pil_image.transpose(Image.ROTATE_90) # CCW 270 = rotate left
|
||||||
print(f"ImageData: Applied PIL rotation {angle}° to {self.image_path}")
|
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
|
# Delete old texture if it exists
|
||||||
if hasattr(self, '_texture_id') and self._texture_id:
|
if hasattr(self, '_texture_id') and self._texture_id:
|
||||||
glDeleteTextures([self._texture_id])
|
glDeleteTextures([self._texture_id])
|
||||||
@ -376,19 +414,18 @@ class ImageData(BaseLayoutElement):
|
|||||||
# Cache texture
|
# Cache texture
|
||||||
self._texture_id = texture_id
|
self._texture_id = texture_id
|
||||||
self._texture_path = self.image_path
|
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
|
# Clear pending image to free memory
|
||||||
self.image_dimensions = (pil_image.width, pil_image.height)
|
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:
|
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._texture_id = None
|
||||||
self._async_loading = False
|
self._pending_pil_image = None
|
||||||
|
return False
|
||||||
|
|
||||||
def _on_async_image_load_failed(self, error_msg: str):
|
def _on_async_image_load_failed(self, error_msg: str):
|
||||||
"""
|
"""
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user