COPASI API  4.16.103
CLSimpleImageTexturizer.cpp
Go to the documentation of this file.
1 // Copyright (C) 2015 by Pedro Mendes, Virginia Tech Intellectual
2 // Properties, Inc., University of Heidelberg, and The University
3 // of Manchester.
4 // All rights reserved.
5 
6 #include <fstream>
7 // libjpeg
8 #include <jpeglib.h>
9 #include <jerror.h>
10 
11 #include <cmath>
12 
14 #include "utility_classes.h"
15 
16 CLTextureSpec* CLSimpleImageTexturizer::operator()(const std::string& filename, const std::string& basedir)
17 {
18  return this->create_texture_for_image(filename, basedir);
19 }
20 
21 /**
22  * This method creates a texture from a given image file.
23  * It is up to the caller to free the texture space once it is no longer needed.
24  * If no texture could be created, NULL is returned.
25  */
26 CLTextureSpec* CLSimpleImageTexturizer::create_texture_for_image(const std::string& filename, const std::string& basedir)
27 {
28  std::string reference = to_absolute_path(filename, basedir);
29  CLTextureSpec* pTexture = NULL;
30  // test if the filename ends in "JPG","JPEG","jpeg" or "jpg"
31  std::string::size_type pos = reference.rfind(".");
32  std::string ending;
33 
34  if (pos != std::string::npos)
35  {
36  ++pos;
37  ending = reference.substr(pos);
38  std::transform(ending.begin(), ending.end(), ending.begin(), (int(*)(int))std::tolower);
39  }
40 
41  if (!ending.empty())
42  {
43  if (ending == "jpeg" || ending == "jpg")
44  {
45  pTexture = create_texture_for_jpeg_image(reference);
46  }
47  else if (ending == "png") // test if the filename ends in "PNG" or "png"
48  {
49  pTexture = create_texture_for_png_image(reference);
50  }
51  }
52 
53  return pTexture;
54 }
55 
56 /**
57  * This method creates a texture from a given JPEG image file.
58  * It is up to the caller to free the texture space once it is no longer needed.
59  * If no texture could be created, NULL is returned.
60  */
62 {
63  CLTextureSpec* pTexture = NULL;
64  FILE *fp = fopen(filename.c_str(), "rb");
65 
66  if (fp)
67  {
68  struct jpeg_decompress_struct cinfo;
69  struct jpeg_error_mgr jerr;
70  cinfo.err = jpeg_std_error(&jerr);
71  jpeg_create_decompress(&cinfo);
72  jpeg_stdio_src(&cinfo, fp);
73  jpeg_read_header(&cinfo, true);
74  cinfo.out_color_space = JCS_RGB;
75  jpeg_start_decompress(&cinfo);
76  // read the size and allocate the memory needed
77  unsigned int width = cinfo.output_width;
78  assert(cinfo.output_components == 3);
79  unsigned int row_stride = width * cinfo.output_components;
80  unsigned int height = cinfo.output_height;
81  pTexture = new CLTextureSpec();
82  pTexture->mTextureWidth = width;
83  pTexture->mTextureHeight = height;
84  pTexture->mTextureName = 0;
85  pTexture->mNumComponents = 3;
86  GLubyte* pData = new GLubyte[3 * width * height];
87  JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)
88  ((j_common_ptr) & cinfo, JPOOL_IMAGE, row_stride, 1);
89 
90  while (cinfo.output_scanline < cinfo.output_height)
91  {
92  jpeg_read_scanlines(&cinfo, buffer, 1);
93  memcpy(pData + (cinfo.output_scanline - 1)*row_stride, buffer[0], row_stride);
94  }
95 
96  jpeg_finish_decompress(&cinfo);
97  jpeg_destroy_decompress(&cinfo);
98  fclose(fp);
99 
100  // right now the texture is not a power of two, but it has the same size as the image
101  // we need to scale it to a power of two, for this we check which size of the texture
102  // supported by the OpenGL implementation and rescale it
103  GLint w = 0;
104  width = (unsigned int)ceil(log(pTexture->mTextureWidth) / log(2.0));
105  height = (unsigned int)ceil(log(pTexture->mTextureHeight) / log(2.0));
106  width = (1 << width);
107  height = (1 << height);
108  GLenum format = GL_RGB;
109  GLint internalFormat = GL_RGB;
110  glTexImage2D(GL_PROXY_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, NULL);
111  glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w);
112 
113  while (w == 0 && width > 0 && height > 0)
114  {
115  // divide the size by two in each direction
116  width = width >> 1;
117  height = height >> 1;
118  glTexImage2D(GL_PROXY_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, NULL);
119  glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w);
120  }
121 
122  if (w == 0)
123  {
124  // delete the texture since we can not draw it anyway
125  delete pTexture;
126  pTexture = NULL;
127  }
128  else if (w != pTexture->mTextureWidth)
129  {
130  if (w < pTexture->mTextureWidth)
131  {
132  // create a warning that the texture had to be scaled down
133  std::cerr << "Image texture to large. Scaling down to size that is supported by current OpenGL implementation." << std::endl;
134  }
135 
136  // rescale the texture
137  GLubyte* newData = new GLubyte[pTexture->mNumComponents * width * height];
138 
139  if (gluScaleImage(format, (GLint)pTexture->mTextureWidth, (GLint)pTexture->mTextureHeight, GL_UNSIGNED_BYTE, pData, width, height, GL_UNSIGNED_BYTE, newData) == 0)
140  {
141  delete[] pData;
142  pData = newData;
143  pTexture->mTextureWidth = width;
144  pTexture->mTextureHeight = height;
145  pTexture->mTextWidth = pTexture->mTextureWidth;
146  pTexture->mTextHeight = pTexture->mTextureHeight;
147  }
148  else
149  {
150  // an error has occured
151  delete[] newData;
152  delete pTexture;
153  pTexture = NULL;
154  }
155 
156  if (pTexture != NULL)
157  {
158  glGenTextures(1, &pTexture->mTextureName);
159  assert(pTexture->mTextureName != 0);
160  glBindTexture(GL_TEXTURE_2D, pTexture->mTextureName);
161  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
162  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
163  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
164  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
165  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
166  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, (GLsizei)pTexture->mTextureWidth, (GLsizei)pTexture->mTextureHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, pData);
167  }
168  }
169 
170  delete[] pData;
171  }
172 
173  return pTexture;
174 }
175 
176 /**
177  * This method creates a texture from a given PNG image file.
178  * It is up to the caller to free the texture space once it is no longer needed.
179  * If no texture could be created, NULL is returned.
180  */
182 {
183  CLTextureSpec* pTexture = NULL;
184  FILE *fp = fopen(filename.c_str(), "rb");
185 
186  if (fp)
187  {
188  png_byte header[8];
189  fread(header, 1, 8, fp);
190 
191  if (!png_sig_cmp(header, 0, 8))
192  {
193  png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
194 
195  if (png_ptr)
196  {
197  png_infop info_ptr = png_create_info_struct(png_ptr);
198  png_infop end_info = NULL;
199 
200  if (info_ptr)
201  {
202  end_info = png_create_info_struct(png_ptr);
203 
204  if (end_info)
205  {
206  png_init_io(png_ptr, fp);
207  png_set_sig_bytes(png_ptr, 8);
208  png_read_info(png_ptr, info_ptr);
209  unsigned int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
210  unsigned int color_type = png_get_color_type(png_ptr, info_ptr);
211 
212  // convert a palette to an RGB image
213  if (color_type == PNG_COLOR_TYPE_PALETTE)
214  {
215  png_set_palette_to_rgb(png_ptr);
216  }
217 
218  // convert low bit_depth grayscale images to 8 Bit
219  if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
220  {
221  png_set_gray_1_2_4_to_8(png_ptr);
222  }
223 
224  // add an alpha channel from info
225  if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
226  {
227  png_set_tRNS_to_alpha(png_ptr);
228  }
229 
230  // convert 16Bit images to 8 Bit
231  if (bit_depth == 16)
232  {
233  png_set_strip_16(png_ptr);
234  }
235 
236  // convert low bit_depth images to 8 Bit
237  if (bit_depth < 8)
238  {
239  png_set_packing(png_ptr);
240  }
241 
242  // convert grayscale to RGB
243  if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
244  {
245  png_set_gray_to_rgb(png_ptr);
246  }
247 
248  // add an alpha channel to RGB images
249  if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY)
250  {
251  png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
252  }
253 
254  // enable interlace handling to expand the image if it
255  // is interlaced
256  /*unsigned int num_passes=*/png_set_interlace_handling(png_ptr);
257  // now we reread the info and get the width and height
258  // in order to allocate enough memory
259  png_read_update_info(png_ptr, info_ptr);
260  unsigned int width = png_get_image_width(png_ptr, info_ptr);
261  unsigned int height = png_get_image_height(png_ptr, info_ptr);
262 
263  if (height > 0)
264  {
265  // create the texture object
266  pTexture = new CLTextureSpec();
267  // we have to reserve enough memory for a width*height
268  // RGBA image
269  GLubyte* pData = new GLubyte[4 * width * height];
270  pTexture->mTextureWidth = width;
271  pTexture->mTextureHeight = height;
272  pTexture->mNumComponents = 4;
273  pTexture->mTextureName = 0;
274  // create an array with pointers to the rows
275  png_bytepp pRow_pointers = new png_bytep[height];
276  unsigned int i;
277 
278  for (i = 0; i < height; ++i)
279  {
280  pRow_pointers[i] = (&(pData[4 * width * i]));
281  }
282 
283  png_read_image(png_ptr, pRow_pointers);
284  png_read_end(png_ptr, end_info);
285  fclose(fp);
286  // right now the texture is not a power of two, but it has the same size as the image
287  // we need to scale it to a power of two, for this we check which size of the texture
288  // supported by the OpenGL implementation and rescale it
289  GLint w = 0;
290  width = (unsigned int)ceil(log(pTexture->mTextureWidth) / log(2.0));
291  height = (unsigned int)ceil(log(pTexture->mTextureHeight) / log(2.0));
292  width = (1 << width);
293  height = (1 << height);
294  GLenum format = GL_RGBA;
295  GLint internalFormat = GL_RGBA;
296  glTexImage2D(GL_PROXY_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, NULL);
297  glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w);
298 
299  while (w == 0 && width > 0 && height > 0)
300  {
301  // divide the size by two in each direction
302  width = width >> 1;
303  height = height >> 1;
304  glTexImage2D(GL_PROXY_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, NULL);
305  glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w);
306  }
307 
308  if (w == 0)
309  {
310  // delete the texture since we can not draw it anyway
311  delete pTexture;
312  pTexture = NULL;
313  }
314  else if (w != pTexture->mTextureWidth)
315  {
316  if (w < pTexture->mTextureWidth)
317  {
318  // create a warning that the texture had to be scaled down
319  std::cerr << "Image texture to large. Scaling down to size that is supported by current OpenGL implementation." << std::endl;
320  }
321 
322  // rescale the texture
323  GLubyte* newData = new GLubyte[pTexture->mNumComponents * width * height];
324 
325  if (gluScaleImage(format, (GLint)pTexture->mTextureWidth, (GLint)pTexture->mTextureHeight, GL_UNSIGNED_BYTE, pData, width, height, GL_UNSIGNED_BYTE, newData) == 0)
326  {
327  delete[] pData;
328  pData = newData;
329  pTexture->mTextureWidth = width;
330  pTexture->mTextureHeight = height;
331  pTexture->mTextWidth = pTexture->mTextureWidth;
332  pTexture->mTextHeight = pTexture->mTextureHeight;
333  }
334  else
335  {
336  // an error has occured
337  delete[] newData;
338  delete pTexture;
339  pTexture = NULL;
340  }
341 
342  if (pTexture != NULL)
343  {
344  glGenTextures(1, &pTexture->mTextureName);
345  assert(pTexture->mTextureName != 0);
346  glBindTexture(GL_TEXTURE_2D, pTexture->mTextureName);
347  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
348  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
349  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
350  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
351  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
352  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, (GLsizei)pTexture->mTextureWidth, (GLsizei)pTexture->mTextureHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, pData);
353  }
354  }
355 
356  delete[] pData;
357  delete[] pRow_pointers;
358  }
359  }
360  }
361 
362  // delete the png structures again
363  png_destroy_read_struct(&png_ptr, (png_infopp)&info_ptr, (png_infopp)&end_info);
364  }
365  }
366  }
367 
368  return pTexture;
369 }
static std::string to_absolute_path(const std::string &filename, const std::string &basedir)
#define GL_CLAMP_TO_EDGE
Definition: glext.h:95
double mTextureWidth
void transform(QGraphicsItem *item, const CLTransformation2D *trans, const CLGroup *group)
CLTextureSpec * create_texture_for_jpeg_image(const std::string &filename)
CLTextureSpec * create_texture_for_image(const std::string &filename, const std::string &basedir)
CLTextureSpec * create_texture_for_png_image(const std::string &filename)
double mTextureHeight
unsigned int mNumComponents
virtual CLTextureSpec * operator()(const std::string &filename, const std::string &basedir)