我在OpenGL(+ GLEW和GLFW)中编写了一个图像查看器来学习如何处理纹理等,但是当我提供某些jpeg文件(大多数工作)时,程序会一直崩溃。我尝试使用SOIL,libjpeg和jpgd,但无论如何都崩溃了,所以它不是解压缩程序的错误。
我在解压缩文件数据(高度,宽度,通道)后检查(使用output_ * jpg解压缩器变量)文件数据,它们是正确的。我也将它们都转储到.raw文件并在Photoshop中打开它们加载没有任何问题(虽然我必须输入通道数,尺寸和检查交错)。它的大小不是问题,因为3000x2800 jpg正常加载而667x1111崩溃OpenGL。比较颜色十六进制值和十六进制编辑器中加载的文件在两种情况下都给出了相同的结果 - 它是每个通道图像数据的纯RGB 8位。
这是我的jpeg decompressin代码(byte只是一个unsigned int):
inline bool loadTexture(byte *&texture, const char *fName, int &width, int &height, int &actualComp, int reqComp)
{
texture = decompressJpeg(fName, width, height, actualComp, reqComp);
if(texture == nullptr)
{
std::cout<<"SOIL decoded\n";
texture = SOIL_load_image(fName, &width, &height, &actualComp, reqComp);
if(texture == nullptr)
return GL_FALSE;
}
return GL_TRUE;
}
unsigned char* decompressJpeg(const char *fName, int &width, int &height, int &actualComp, int reqComp)
{
std::ifstream f(fName, std::ios::binary);
if(!f.is_open())
return nullptr;
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr errManager;
cinfo.err = jpeg_std_error(&errManager);
jpeg_create_decompress(&cinfo);
f.seekg(0, f.end);
size_t length(f.tellg());
f.seekg(0, f.beg);
byte *data(new byte[length]);
f.read((char*)data, length);
f.close();
jpeg_mem_src(&cinfo, data, length);
jpeg_read_header(&cinfo, TRUE);
cinfo.out_color_space = JCS_RGB;
jpeg_start_decompress(&cinfo);
height = cinfo.output_height;
width = cinfo.output_width;
actualComp = cinfo.output_components;
unsigned char **tempVal(new unsigned char*[cinfo.output_height]);
for(int i(cinfo.output_height); i>0; --i)
{
*tempVal = new unsigned char[cinfo.output_width*cinfo.output_components];
++tempVal;
}
tempVal -= cinfo.output_height;
while(cinfo.output_height > cinfo.output_scanline)
{
jpeg_read_scanlines(&cinfo, tempVal, 1);
++tempVal;
}
tempVal -= cinfo.output_height;
jpeg_finish_decompress(&cinfo);
delete[] data;
unsigned char *retVal(new unsigned char[actualComp*height*width]);
for(int i(height); i>0; --i)
{
for(int j(width*actualComp); j>0; --j)
{
*retVal = **tempVal;
++retVal;
++(*tempVal);
}
(*tempVal) -= width*actualComp;
delete[] *tempVal;
++tempVal;
}
tempVal -= height;
delete[] tempVal;
jpeg_destroy_decompress(&cinfo);
retVal -= height*width*actualComp;
return retVal;
}
以下是代码的其余部分:
#include <glew.h>
#include <glfw3.h>
#include <SOIL.h>
#include <iostream>
#include <ctime>
#include <conio.h>
#include <fstream>
#include <cmath>
#include <jpeglib.h>
#define HEIGHT 1024
#define WIDTH 1280
typedef unsigned char byte;
void key_callback(GLFWwindow *window, int key, int scancode, int action, int mode);
inline void writeErrLog(const GLchar *log);
inline const bool loadShader(const GLuint shader, const char *sourceFile);
inline bool loadTexture(byte *&texture ,const char *fName, int &width, int &height, int &actualComp, int reqComp);
unsigned char* decompressJpeg(const char *fName, int &width, int &height, int &actualComp, int reqComp);
struct bitmap
{
public:
bitmap() : width(0), height(0), data(nullptr) {}
int width, height;
byte *data;
};
std::string programPath;
int main(int argc, char **argv)
{
if(argc<2)
return -1;
programPath = *argv;
programPath.erase(programPath.rfind('\\')+1);
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
GLFWwindow *window(glfwCreateWindow(WIDTH, HEIGHT, "Okienko", nullptr, nullptr) );
if(window == nullptr)
{
std::cout<<"Failed to create window\n\n";
glfwTerminate();
getch();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetKeyCallback(window, key_callback);
glewExperimental = GL_TRUE;
if(glewInit() != GLEW_OK)
{
std::cout<<"Failed to initialize GLEW\n\n";
glfwTerminate();
getch();
return -1;
}
glViewport(0, 0, WIDTH, HEIGHT);
unsigned long long int frame_time(0);
const GLuint vertexShader(glCreateShader(GL_VERTEX_SHADER)),
fragmentShader(glCreateShader(GL_FRAGMENT_SHADER)),
shaderProgram(glCreateProgram());
GLint operationStatus;
std::string shaderPath(programPath);
shaderPath.append("shaders\\vertex shader.glsl");
if(loadShader(vertexShader, shaderPath.c_str()) == GL_FALSE)
{
glfwTerminate();
getch();
return -1;
}
shaderPath = programPath;
shaderPath.append("shaders\\fragment shader.glsl");
if(loadShader(fragmentShader, shaderPath.c_str()) == GL_FALSE)
{
glfwTerminate();
getch();
return -1;
}
shaderPath.clear();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &operationStatus);
if(operationStatus == 0)
{
GLchar log[2048];
glGetProgramInfoLog(shaderProgram, 2048, nullptr, log);
glfwTerminate();
writeErrLog(log);
return -1;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
bitmap texture;
int comp;
if(loadTexture(texture.data, *(argv+1), texture.width, texture.height, comp, 3) == GL_FALSE)
{
std::string errLog("Failed to read texture. (");
errLog.append(*(argv+1));
errLog.append(")");
writeErrLog(errLog.c_str());
errLog.clear();
}
std::cout<<"channels:\t"<<comp<<'\n';
std::ofstream nam("image.raw", std::ios::binary);
nam.write((char*)texture.data, texture.width*texture.height*comp);
nam.close();
//GLint inColorUni(glGetUniformLocation(shaderProgram, "inColor"));
int newWidth, newHeight;
if( (float(texture.width)/float(texture.height)) / (float(WIDTH)/float(HEIGHT)) > 1.0f)
{
newWidth = std::min(texture.width, WIDTH);
newHeight = round(float(newWidth) / (float(texture.width)/float(texture.height)));
}
else
{
newHeight = std::min(texture.height, HEIGHT);
newWidth = round(float(newHeight) * (float(texture.width)/float(texture.height)));
}
// normalize
GLfloat x(GLfloat(newWidth)/GLfloat(WIDTH)*2.0f),
y(GLfloat(newHeight)/GLfloat(HEIGHT)*2.0f);
GLsizei vertices_len(20);
GLfloat *vertices(new GLfloat[vertices_len]);
GLfloat diffX((2.0f-x)/2.0f), diffY((2.0f-y)/2.0f);
std::cout <<"Resolution:\t"<<texture.width<<'x'<<texture.height<<"px"
<<"\nScaled:\t"<<newWidth<<'x'<<newHeight<<"px"
<<"\nNormalized:\t";
*vertices = -1.0f+diffX; //X
*++vertices = 1.0f-diffY; //Y
*++vertices = 0.0f; //Z
*++vertices = 0.0f; //S
*++vertices = 1.0f; //T
*++vertices = -1.0f+diffX; //X
*++vertices = -1.0f+diffY; //Y
*++vertices = 0.0f; //Z
*++vertices = 0.0f; //S
*++vertices = 0.0f; //T
*++vertices = 1.0f-diffX; //X
*++vertices = -1.0f+diffY; //Y
*++vertices = 0.0f; //Z
*++vertices = 1.0f; //S
*++vertices = 0.0f; //T
*++vertices = 1.0f-diffX; //X
*++vertices = 1.0f-diffY; //Y
*++vertices = 0.0f; //Z
*++vertices = 1.0f; //S
*++vertices = 1.0f; //T
vertices -= vertices_len-1;
GLsizei indices_len(6);
GLuint *indices(new GLuint[indices_len]);
*indices = 0;
*++indices = 1;
*++indices = 3;
*++indices = 1;
*++indices = 2;
*++indices = 3;
indices -= 5;
GLsizei color_len(4);
GLfloat *color(new GLfloat[color_len]);
*color = 0.5f;
*++color = 0.0f;
*++color = 1.0f;
*++color = 1.0f;
color -= 3;
GLuint triangleVBO, triangleObjectVAO, triangleEBO;
glGenVertexArrays(1, &triangleObjectVAO);
glGenBuffers(1, &triangleVBO);
glGenBuffers(1, &triangleEBO);
glBindVertexArray(triangleObjectVAO);
glBindBuffer(GL_ARRAY_BUFFER, triangleVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*vertices_len, vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, triangleEBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)*indices_len, indices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat), (GLvoid*)(3*sizeof(GLfloat)) );
glEnableVertexAttribArray(1);
glBindVertexArray(0);
delete[] vertices;
delete[] indices;
#define OKRES 7000.0
GLfloat timePoint(0.0f);
glUseProgram(shaderProgram);
GLuint tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texture.width, texture.height, 0, GL_RGB, GL_UNSIGNED_BYTE, texture.data);
GLfloat bgColor[] = {0.0f, 0.0f, 0.0f, 0.0f};
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bgColor);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
//SOIL_free_image_data(texture.data);
while(!glfwWindowShouldClose(window))
{
glfwPollEvents();
if(clock()-frame_time > 16) /* max 60 fps */
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
/*timePoint += clock()-frame_time;
if(timePoint>OKRES)
timePoint -= OKRES;
glUniform4f(inColorUni, 0.0f, 0.0f, sinf(3.1415926f * (timePoint/OKRES)), 1.0f);*/
glBindTexture(GL_TEXTURE_2D, tex);
glBindVertexArray(triangleObjectVAO);
//glDrawArrays(GL_TRIANGLES, 0, 3);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
glfwSwapBuffers(window);
frame_time = clock();
}
}
glDeleteVertexArrays(1, &triangleObjectVAO);
glDeleteBuffers(1, &triangleEBO);
glDeleteBuffers(1, &triangleVBO);
glfwTerminate();
return 0;
}
不要告诉我如何优化我的代码(除了处理jpg之外,因为它只是一种教程项目。我并不关心优化和我只是希望我的jpeg加载而不是崩溃OpenGL。
它崩溃了
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texture.width, texture.height, 0, GL_RGB, GL_UNSIGNED_BYTE, texture.data);
我认为着色器,关键回调,loadShaders和writeErrLog是无关紧要的,因为它们是尽可能基本的(顶点着色器实际上什么都不做,片段着色器只使用texture())但是我会发布它们需要。也不要问我为什么要这样分配顶点。
两个经过测试的jpeg似乎都不是渐进式的。 看来我给了glTexImage2D()不正确的值。 endianness会导致什么吗? (我不太了解如何编写字节顺序安全代码)另外,OGL如何确定给定的数组是否坏(除了size!= channels x height x width)?