Question

# -*- coding: utf-8 -*-

import os
os.environ['PYOPENGL_PLATFORM'] = 'egl'

import sys
import ctypes
import numpy as np
from OpenGL.EGL import *
from OpenGL.GL import *
from OpenGL.GLU import *
import freetype
from PIL import Image
from fontTools.ttLib import TTFont

# 定义 EGL 错误检查函数
def check_egl_error():
error = eglGetError()
if error != EGL_SUCCESS:
print(f"EGL error: {error}")
sys.exit(1)

# 初始化 EGL
def init_egl(width, height):
display = eglGetDisplay(EGL_DEFAULT_DISPLAY)
if display == EGL_NO_DISPLAY:
print("无法获取 EGL 显示")
sys.exit(1)

major = ctypes.c_long()
minor = ctypes.c_long()
if not eglInitialize(display, ctypes.byref(major), ctypes.byref(minor)):
print("无法初始化 EGL")
sys.exit(1)

# 选择合适的 EGL 配置
attribs = [
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
EGL_NONE
]
config = EGLConfig()
num_configs = ctypes.c_int()
if not eglChooseConfig(display, attribs, ctypes.byref(config), 1, ctypes.byref(num_configs)):
print("无法选择 EGL 配置")
sys.exit(1)

if num_configs.value < 1:
print("没有找到合适的 EGL 配置")
sys.exit(1)

# 创建 Pbuffer surface
pbuffer_attribs = [
EGL_WIDTH, width,
EGL_HEIGHT, height,
EGL_NONE
]
surface = eglCreatePbufferSurface(display, config, pbuffer_attribs)
if surface == EGL_NO_SURFACE:
print("无法创建 Pbuffer Surface")
sys.exit(1)

# 创建 OpenGL 上下文
context_attribs = [
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
]
context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attribs)
if context == EGL_NO_CONTEXT:
print("无法创建 EGL 上下文")
sys.exit(1)

# 绑定 OpenGL API
if not eglBindAPI(EGL_OPENGL_API):
print("无法绑定 OpenGL API")
sys.exit(1)

# 使上下文当前
if not eglMakeCurrent(display, surface, surface, context):
print("无法使 EGL 上下文当前")
sys.exit(1)

check_egl_error()
return display, surface, context

# 终止 EGL
def terminate_egl(display, surface, context):
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)
eglDestroySurface(display, surface)
eglDestroyContext(display, context)
eglTerminate(display)

# 字符检测和字体渲染设置
text = "暨南"
primary_font_path = "/mnt/lgs/Open-Sora/moviepys/input/阿里汉仪黑体.ttf" # 主字体路径
fallback_font_path = "/mnt/lgs/Open-Sora/moviepys/output/fonts/舟游黑.otf" # 备用字体路径
font_size = 100 # 字体大小

# 加载字体
try:
primary_face = freetype.Face(primary_font_path)
primary_face.set_char_size(font_size * 64)
except IOError:
print(f"无法加载主字体:{primary_font_path}")
sys.exit(1)

try:
fallback_face = freetype.Face(fallback_font_path)
fallback_face.set_char_size(font_size * 64)
except IOError:
print(f"无法加载备用字体:{fallback_font_path}")
sys.exit(1)

# 使用 fontTools 检查字体是否支持某个字符
def can_render(char, font_path):
try:
font = TTFont(font_path)
for table in font['cmap'].tables:
if ord(char) in table.cmap:
return True
return False
except Exception as e:
print(f"检查字符 '{char}' 时出错: {e}")
return False

# 准备字符和对应字体的列表
render_chars = []
for char in text:
if can_render(char, primary_font_path):
render_chars.append(('primary', char))
elif can_render(char, fallback_font_path):
render_chars.append(('fallback', char))
else:
print(f"字符 '{char}' 无法由主字体或备用字体渲染。使用主字体作为默认。")
render_chars.append(('primary', char))

# 生成每个字符的纹理
def load_glyph(face, char):
face.load_char(char, freetype.FT_LOAD_RENDER | freetype.FT_LOAD_FORCE_AUTOHINT)
bitmap = face.glyph.bitmap
width, height = bitmap.width, bitmap.rows
buffer = bitmap.buffer

if width == 0 or height == 0:
# 如果字符没有位图(例如空格),返回空纹理
texture_id = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texture_id)
# 创建一个1x1透明纹理
empty_image = np.zeros((1, 1, 4), dtype=np.ubyte)
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RGBA,
1,
1,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
empty_image
)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
return texture_id, face.glyph.bitmap_left, face.glyph.bitmap_top, 1, 1, face.glyph.advance.x

# 将灰度图转换为 RGBA
image = np.array(buffer, dtype=np.ubyte).reshape(height, width)
image_rgba = np.zeros((height, width, 4), dtype=np.ubyte)
image_rgba[:, :, 0] = 255 # R
image_rgba[:, :, 1] = 255 # G
image_rgba[:, :, 2] = 255 # B
image_rgba[:, :, 3] = image # A

# 垂直翻转纹理数据
image_rgba = np.flipud(image_rgba)

# 生成 OpenGL 纹理
texture_id = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texture_id)
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RGBA,
width,
height,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
image_rgba
)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)

return texture_id, face.glyph.bitmap_left, face.glyph.bitmap_top, width, height, face.glyph.advance.x

# 预加载所有字符的纹理
textures = {}
for idx, (font_type, char) in enumerate(render_chars):
key = f"{font_type}_{char}"
if key not in textures:
if font_type == 'primary':
face = primary_face
else:
face = fallback_face
textures[key] = load_glyph(face, char)

# 计算文本的总宽度和最大高度
total_width = 0
max_height = 0
char_metrics = []

for font_type, char in render_chars:
key = f"{font_type}_{char}"
tex, left, top, w, h, advance = textures[key]
char_metrics.append((tex, left, top, w, h, advance))
total_width += advance >> 6 # advance 是以1/64像素为单位
if h > max_height:
max_height = h

# 设置文本绘制的位置(居中)
size = (1080, 1920)
start_x = (size[0] - total_width) // 2
start_y = (size[1] - max_height) // 2

# 初始化 EGL
display, surface, context = init_egl(size[0], size[1])

# 设置 OpenGL 视口和投影
glViewport(0, 0, size[0], size[1])
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
# 设置 2D 投影矩阵,y 轴向下
gluOrtho2D(0, size[0], size[1], 0)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()

# 设置背景为透明
glClearColor(0.0, 0.0, 0.0, 0.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

# 启用透明度混合
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

# 开始绘制文本
glEnable(GL_TEXTURE_2D)
glColor4f(1.0, 1.0, 1.0, 1.0) # 设置颜色为白色

current_x = start_x
for tex, left, top, w, h, advance in char_metrics:
glBindTexture(GL_TEXTURE_2D, tex)

xpos = current_x + left
ypos = start_y + (max_height - h)

# 绘制纹理四边形
glBegin(GL_QUADS)
glTexCoord2f(0, 0)
glVertex2f(xpos, ypos)
glTexCoord2f(1, 0)
glVertex2f(xpos + w, ypos)
glTexCoord2f(1, 1)
glVertex2f(xpos + w, ypos + h)
glTexCoord2f(0, 1)
glVertex2f(xpos, ypos + h)
glEnd()

current_x += advance >> 6

glDisable(GL_TEXTURE_2D)

# 读取像素数据
glPixelStorei(GL_PACK_ALIGNMENT, 1)
data = glReadPixels(0, 0, size[0], size[1], GL_RGBA, GL_UNSIGNED_BYTE)
image = Image.frombytes("RGBA", size, data)
image = image.transpose(Image.FLIP_TOP_BOTTOM) # 翻转图像以匹配 OpenGL 坐标系

# 保存图像
output_path = "/mnt/lgs/Open-Sora/gstreamers/output/opengl_rendered_text.png"
image.save(output_path)
print(f"OpenGL 渲染结果已保存为 {output_path}")

# 清理资源
for tex in textures.values():
glDeleteTextures(tex[0])

# 终止 EGL
terminate_egl(display, surface, context)

opengl_venvroot@ecs-60954587:/mnt/lgs/Open-Sora# /mnt/lgs/Open-Sora/opengl_venv/bin/python /mnt/lgs/Open-Sora/gstreamers/test1.py
Traceback (most recent call last):
File "/mnt/lgs/Open-Sora/gstreamers/test1.py", line 241, in <module>
glMatrixMode(GL_MODELVIEW)
File "src/errorchecker.pyx", line 58, in OpenGL_accelerate.errorchecker._ErrorChecker.glCheckError
OpenGL.error.GLError: GLError(
err = 1282,
description = b'invalid operation',
baseOperation = glMatrixMode,
cArguments = (GL_MODELVIEW,)
)
给出修改后的全部代码,中文回复

Thought

探讨错误原因

根据OpenGL错误1282(GL_INVALID_OPERATION),这个错误发生在调用glMatrixMode(GL_MODELVIEW)时。找到问题的根源,解决这个运行问题。

探索现代OpenGL

搭建EGL和OpenGL上下文后,旧式OpenGL函数如glMatrixMode和gluOrtho2D可能不可用,或者OpenGL上下文未完全初始化。

绑定API

我在代码中创建了一个OpenGL ES 2.0上下文,并通过eglBindAPI绑定了OpenGL API。这样的话,如果尝试使用桌面OpenGL函数,就会抛出GL_INVALID_OPERATION错误。

调整代码设置

现在我在调整代码以创建桌面OpenGL上下文,而不是OpenGL ES上下文。我正在反思如何合适地设置上下文属性和绑定API。

指定OpenGL上下文

在请求OpenGL 2.1上下文时,需要设置特定的属性。如果不设置版本,则默认为此版本。同时,有些实现需要特殊属性来创建兼容性配置文件上下文。

探讨解决方案

OpenGL ES 2.0 中不使用固定功能管道函数,因此需要重新编写代码或创建支持固定功能管道的 OpenGL 上下文。 Option 2 需要显著的代码更改。

纠正绑定顺序

在创建上下文之前绑定API是关键,这里目前的顺序反了过来。

顺序和属性调整问题

调整代码策略

我正在探讨将eglBindAPI(EGL_OPENGL_API)调用移到eglCreateContext调用前,并去掉EGL_CONTEXT_CLIENT_VERSION,因为它适用于OpenGL ES而不是OpenGL。

创建OpenGL上下文

我正在设置OpenGL上下文以确保eglBindAPI调用发生在任何上下文创建之前。这样做可以避免创建错误的上下文。

初始化和配置

我正在初始化用以处理图形渲染的环境。这包括调用函数来设置显示、绑定API、选择配置、创建表面和OpenGL上下文。

调整方法

我正在尝试创建OpenGL ES上下文时使用桌面OpenGL函数,可能导致错误。可以检查EGL实现是否支持创建OpenGL上下文,或替代GLU函数以确保可用性。

编写代码

我正在根据指示改写代码,提供所需的中文说明。这让我思考如何明确功能需求并确保代码的准确性。

初始化平台和环境

我正在设置 EGL 错误检测和 EGL 初始化,包括显示获取、EGL API 绑定、配置选择、Pbuffer 表面和 OpenGL 上下文创建。任务似乎复杂但关键。

渲染字符纹理

在将字符渲染为纹理时,首先查看字符是否可渲染。如果可以,则加载字符的位图并转换为纹理;否则,使用透明纹理。

调整创建画布

我在创建OpenGL上下文之前先绑定了OpenGL API,移除了与OpenGL ES相关的上下文属性,保持上下文属性为默认值。这是为了确保可以正确使用OpenGL。

Answer

# -*- coding: utf-8 -*- import os os.environ['PYOPENGL_PLATFORM'] = 'egl' import sys import ctypes import numpy as np from OpenGL.EGL import * from OpenGL.GL import * from OpenGL.GLU import * import freetype from PIL import Image from fontTools.ttLib import TTFont # 定义 EGL 错误检查函数 def check_egl_error(): error = eglGetError() if error != EGL_SUCCESS: print(f"EGL error: {error}") sys.exit(1) # 初始化 EGL def init_egl(width, height): display = eglGetDisplay(EGL_DEFAULT_DISPLAY) if display == EGL_NO_DISPLAY: print("无法获取 EGL 显示") sys.exit(1) major = ctypes.c_long() minor = ctypes.c_long() if not eglInitialize(display, ctypes.byref(major), ctypes.byref(minor)): print("无法初始化 EGL") sys.exit(1) # 绑定 OpenGL API if not eglBindAPI(EGL_OPENGL_API): print("无法绑定 OpenGL API") sys.exit(1) # 选择合适的 EGL 配置 attribs = [ EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_NONE ] config = EGLConfig() num_configs = ctypes.c_int() if not eglChooseConfig(display, attribs, ctypes.byref(config), 1, ctypes.byref(num_configs)): print("无法选择 EGL 配置") sys.exit(1) if num_configs.value < 1: print("没有找到合适的 EGL 配置") sys.exit(1) # 创建 Pbuffer surface pbuffer_attribs = [ EGL_WIDTH, width, EGL_HEIGHT, height, EGL_NONE ] surface = eglCreatePbufferSurface(display, config, pbuffer_attribs) if surface == EGL_NO_SURFACE: print("无法创建 Pbuffer Surface") sys.exit(1) # 创建 OpenGL 上下文 context_attribs = [EGL_NONE] context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attribs) if context == EGL_NO_CONTEXT: print("无法创建 EGL 上下文") sys.exit(1) # 使上下文当前 if not eglMakeCurrent(display, surface, surface, context): print("无法使 EGL 上下文当前") sys.exit(1) check_egl_error() return display, surface, context # 终止 EGL def terminate_egl(display, surface, context): eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) eglDestroySurface(display, surface) eglDestroyContext(display, context) eglTerminate(display) # 字符检测和字体渲染设置 text = "暨南" primary_font_path = "/mnt/lgs/Open-Sora/moviepys/input/阿里汉仪黑体.ttf" # 主字体路径 fallback_font_path = "/mnt/lgs/Open-Sora/moviepys/output/fonts/舟游黑.otf" # 备用字体路径 font_size = 100 # 字体大小 # 加载字体 try: primary_face = freetype.Face(primary_font_path) primary_face.set_char_size(font_size * 64) except IOError: print(f"无法加载主字体:{primary_font_path}") sys.exit(1) try: fallback_face = freetype.Face(fallback_font_path) fallback_face.set_char_size(font_size * 64) except IOError: print(f"无法加载备用字体:{fallback_font_path}") sys.exit(1) # 使用 fontTools 检查字体是否支持某个字符 def can_render(char, font_path): try: font = TTFont(font_path) for table in font['cmap'].tables: if ord(char) in table.cmap: return True return False except Exception as e: print(f"检查字符 '{char}' 时出错: {e}") return False # 准备字符和对应字体的列表 render_chars = [] for char in text: if can_render(char, primary_font_path): render_chars.append(('primary', char)) elif can_render(char, fallback_font_path): render_chars.append(('fallback', char)) else: print(f"字符 '{char}' 无法由主字体或备用字体渲染。使用主字体作为默认。") render_chars.append(('primary', char)) # 生成每个字符的纹理 def load_glyph(face, char): face.load_char(char, freetype.FT_LOAD_RENDER | freetype.FT_LOAD_FORCE_AUTOHINT) bitmap = face.glyph.bitmap width, height = bitmap.width, bitmap.rows buffer = bitmap.buffer if width == 0 or height == 0: # 如果字符没有位图(例如空格),返回空纹理 texture_id = glGenTextures(1) glBindTexture(GL_TEXTURE_2D, texture_id) # 创建一个1x1透明纹理 empty_image = np.zeros((1, 1, 4), dtype=np.ubyte) glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, empty_image ) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) return texture_id, face.glyph.bitmap_left, face.glyph.bitmap_top, 1, 1, face.glyph.advance.x # 将灰度图转换为 RGBA image = np.array(buffer, dtype=np.ubyte).reshape(height, width) image_rgba = np.zeros((height, width, 4), dtype=np.ubyte) image_rgba[:, :, 0] = 255 # R image_rgba[:, :, 1] = 255 # G image_rgba[:, :, 2] = 255 # B image_rgba[:, :, 3] = image # A # 垂直翻转纹理数据 image_rgba = np.flipud(image_rgba) # 生成 OpenGL 纹理 texture_id = glGenTextures(1) glBindTexture(GL_TEXTURE_2D, texture_id) glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_rgba ) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) return texture_id, face.glyph.bitmap_left, face.glyph.bitmap_top, width, height, face.glyph.advance.x # 预加载所有字符的纹理 textures = {} for idx, (font_type, char) in enumerate(render_chars): key = f"{font_type}_{char}" if key not in textures: if font_type == 'primary': face = primary_face else: face = fallback_face textures[key] = load_glyph(face, char) # 计算文本的总宽度和最大高度 total_width = 0 max_height = 0 char_metrics = [] for font_type, char in render_chars: key = f"{font_type}_{char}" tex, left, top, w, h, advance = textures[key] char_metrics.append((tex, left, top, w, h, advance)) total_width += advance >> 6 # advance 是以1/64像素为单位 if h > max_height: max_height = h # 设置文本绘制的位置(居中) size = (1080, 1920) start_x = (size[0] - total_width) // 2 start_y = (size[1] - max_height) // 2 # 初始化 EGL display, surface, context = init_egl(size[0], size[1]) # 设置 OpenGL 视口和投影 glViewport(0, 0, size[0], size[1]) glMatrixMode(GL_PROJECTION) glLoadIdentity() # 设置 2D 投影矩阵,y 轴向下 gluOrtho2D(0, size[0], size[1], 0) glMatrixMode(GL_MODELVIEW) glLoadIdentity() # 设置背景为透明 glClearColor(0.0, 0.0, 0.0, 0.0) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # 启用透明度混合 glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) # 开始绘制文本 glEnable(GL_TEXTURE_2D) glColor4f(1.0, 1.0, 1.0, 1.0) # 设置颜色为白色 current_x = start_x for tex, left, top, w, h, advance in char_metrics: glBindTexture(GL_TEXTURE_2D, tex) xpos = current_x + left ypos = start_y + (max_height - h) # 绘制纹理四边形 glBegin(GL_QUADS) glTexCoord2f(0, 0) glVertex2f(xpos, ypos) glTexCoord2f(1, 0) glVertex2f(xpos + w, ypos) glTexCoord2f(1, 1) glVertex2f(xpos + w, ypos + h) glTexCoord2f(0, 1) glVertex2f(xpos, ypos + h) glEnd() current_x += advance >> 6 glDisable(GL_TEXTURE_2D) # 读取像素数据 glPixelStorei(GL_PACK_ALIGNMENT, 1) data = glReadPixels(0, 0, size[0], size[1], GL_RGBA, GL_UNSIGNED_BYTE) image = Image.frombytes("RGBA", size, data) image = image.transpose(Image.FLIP_TOP_BOTTOM) # 翻转图像以匹配 OpenGL 坐标系 # 保存图像 output_path = "/mnt/lgs/Open-Sora/gstreamers/output/opengl_rendered_text.png" image.save(output_path) print(f"OpenGL 渲染结果已保存为 {output_path}") # 清理资源 for tex in textures.values(): glDeleteTextures(tex[0]) # 终止 EGL terminate_egl(display, surface, context)

主要修改如下:

  1. 调整 EGL 上下文的创建顺序:在创建 OpenGL 上下文之前,先绑定 OpenGL API。将 eglBindAPI(EGL_OPENGL_API) 调用移动到了 eglCreateContext 之前。

  2. 修改上下文属性:创建 OpenGL 上下文时,移除了 EGL_CONTEXT_CLIENT_VERSION 属性,因为它适用于 OpenGL ES。现在的上下文属性仅为 [EGL_NONE],以获取默认的 OpenGL 上下文。

  3. 确保使用正确的 OpenGL API:通过在绑定 API 时明确使用 EGL_OPENGL_API,确保创建的是桌面版 OpenGL 上下文,而非 OpenGL ES。

这些修改确保了创建的 OpenGL 上下文支持固定功能管线,因此可以使用 glMatrixModeglLoadIdentitygluOrtho2D 等函数,从而避免了 GL_INVALID_OPERATION 错误。

希望这些修改能解决您的问题。如有其他疑问,请随时提出!

Share this Q&A