Easy Way
Have you looked at template_preview
tex = bpy.data.textures['.hidden']
col = layout.box().column()
col.template_preview(tex)
Hard Way
So this works, but it requires a lot of finesse to make it usable. Not figured out yet how to create a layout of a fixed size to append to the panel layout for rendering over the top of. This code will render over the top of the properties panel, as opposed to the Panel UI element.
import bpy
import gpu
import bgl
from gpu_extras.batch import batch_for_shader
def draw():
x1 = 0
x2 = 200
y1 = 0
y2 = 200
shader = gpu.shader.from_builtin('2D_IMAGE')
batch = batch_for_shader(
shader, 'TRI_FAN',
{
"pos": ((x1, y1), (x2, y1), (x2, y2), (x1, y2)),
"texCoord": ((0, 0), (1, 0), (1, 1), (0, 1)),
},
)
image = bpy.data.images['logo']
if image.gl_load():
return # an exception happened
bgl.glActiveTexture(bgl.GL_TEXTURE0)
bgl.glBindTexture(bgl.GL_TEXTURE_2D, image.bindcode)
shader.bind()
shader.uniform_int("image", 0)
batch.draw(shader)
image.gl_free()
handler = bpy.types.SpaceProperties.draw_handler_add(draw,(),'WINDOW','POST_PIXEL')
# then remove the handler with ...
bpy.types.SpaceProperties.draw_handler_remove(handler, 'WINDOW')