Vuo 2.4.4
Loading...
Searching...
No Matches
VuoFreeImage.c
Go to the documentation of this file.
1
10#pragma clang diagnostic push
11#pragma clang diagnostic ignored "-Wdocumentation"
13#define BOOL FREEIMAGE_BOOL
14#include <FreeImage.h>
15#undef BOOL
16#pragma clang diagnostic pop
17
18#include "VuoFreeImage.h"
19#include "VuoGlPool.h"
20
21#include <OpenGL/CGLMacro.h>
22
23#include "module.h"
24
25#ifdef VUO_COMPILER
27 "title" : "VuoFreeImage",
28 "dependencies" : [
29 "freeimage"
30 ]
31});
32#endif
33
37static void VuoFreeImage_error(FREE_IMAGE_FORMAT fif, const char *message)
38{
39 if (fif != FIF_UNKNOWN)
40 VUserLog("Error: %s (%s format)", message, FreeImage_GetFormatFromFIF(fif));
41 else
42 VUserLog("Error: %s", message);
43}
44
48__attribute__((constructor)) static void VuoFreeImage_init(void)
49{
50 FreeImage_Initialise(true);
51 FreeImage_SetOutputMessage(VuoFreeImage_error);
52}
53
64FIBITMAP *VuoFreeImage_convertVuoImageToFreeImage(VuoImage image, bool allowFloat, bool unpremultiply)
65{
66 if (!image)
67 return NULL;
68
70 unsigned int depth = 32;
71 if (cd == VuoImageColorDepth_16) depth = 64;
72 if (cd == VuoImageColorDepth_32) depth = 128;
73 VDebugLog("Input format : %s (depth = %dbpc)", VuoGl_stringForConstant(image->glInternalFormat), depth/4);
74
75 unsigned int bufferFormat = GL_BGRA;
76 if (image->glInternalFormat == GL_DEPTH_COMPONENT)
77 bufferFormat = GL_DEPTH_COMPONENT16;
78 else if (image->glInternalFormat == GL_LUMINANCE
79 || image->glInternalFormat == GL_LUMINANCE16F_ARB
80 || image->glInternalFormat == GL_LUMINANCE32F_ARB)
81 bufferFormat = GL_R16; // 1 channel, 16bit integer (just like depth)
82 else if (depth == 64
83 || image->glInternalFormat == GL_LUMINANCE_ALPHA16F_ARB)
84 bufferFormat = GL_RGBA16I_EXT;
85 else if (depth == 128)
86 {
87 if (allowFloat)
88 bufferFormat = GL_RGBA32F_ARB;
89 else
90 bufferFormat = GL_RGBA16I_EXT;
91 }
92 const unsigned char *buffer = VuoImage_getBuffer(image, bufferFormat);
93 if (!buffer)
94 {
95 VUserLog("Error: Couldn't get pixels.");
96 return NULL;
97 }
98
99 int width = image->pixelsWide;
100 int height = image->pixelsHigh;
101
102 FIBITMAP *fibmp;
103 if (bufferFormat == GL_BGRA)
104 {
105 VDebugLog("Output format: RGBA 8bpc integer%s", unpremultiply ? " (unpremultiplied)" : "");
106 const unsigned char *b = (unsigned char *)buffer;
107 fibmp = FreeImage_AllocateT(FIT_BITMAP, width, height, depth, FI_RGBA_BLUE_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_RED_MASK);
108 if (unpremultiply)
109 for (long int y = 0; y < height; ++y)
110 {
111 unsigned char *d = (unsigned char *)FreeImage_GetScanLine(fibmp, y);
112 for (long int x = 0; x < width; ++x)
113 {
114 float alpha = (float)b[3] / 255.;
115 // Clamp after un-premultiplying, to ensure the `unsigned char` values don't wrap
116 // https://b33p.net/kosada/node/11821
117 d[0] = VuoInteger_clamp((float)b[0] / alpha, 0, 255);
118 d[1] = VuoInteger_clamp((float)b[1] / alpha, 0, 255);
119 d[2] = VuoInteger_clamp((float)b[2] / alpha, 0, 255);
120 d[3] = b[3];
121 b += 4;
122 d += 4;
123 }
124 }
125 else
126 memcpy(FreeImage_GetBits(fibmp), buffer, width*height*4);
127 }
128 else if (bufferFormat == GL_RGBA16I_EXT)
129 {
130 bool mono = (image->glInternalFormat == GL_LUMINANCE_ALPHA16F_ARB);
131 VDebugLog("Output format: RGBA 16bpc integer%s%s", mono?" (mono)":"", unpremultiply ? " (unpremultiplied)" : "");
132 const unsigned short *b = (const unsigned short *)buffer;
133 fibmp = FreeImage_AllocateT(FIT_RGBA16, width, height, depth, FI_RGBA_BLUE_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_RED_MASK);
134 if (unpremultiply)
135 for (long int y = 0; y < height; ++y)
136 {
137 unsigned short *d = (unsigned short *)FreeImage_GetScanLine(fibmp, y);
138 for (long int x = 0; x < width; ++x)
139 {
140 float alpha = (float)b[3] / 65535.;
141 // Clamp after un-premultiplying, to ensure the `unsigned short` values don't wrap
142 // https://b33p.net/kosada/node/11821
143 d[0] = VuoInteger_clamp((float)b[ 2] / alpha, 0, 65535);
144 d[1] = VuoInteger_clamp((float)b[mono?2:1] / alpha, 0, 65535);
145 d[2] = VuoInteger_clamp((float)b[mono?2:0] / alpha, 0, 65535);
146 d[3] = b[3];
147 b += 4;
148 d += 4;
149 }
150 }
151 else
152 for (long int y = 0; y < height; ++y)
153 {
154 unsigned short *d = (unsigned short *)FreeImage_GetScanLine(fibmp, y);
155 for (long int x = 0; x < width; ++x)
156 {
157 d[0] = b[2];
158 d[1] = b[mono?2:1];
159 d[2] = b[mono?2:0];
160 d[3] = b[3];
161 b += 4;
162 d += 4;
163 }
164 }
165 }
166 else if (bufferFormat == GL_RGBA32F_ARB)
167 {
168 bool mono = (image->glInternalFormat == GL_LUMINANCE_ALPHA32F_ARB);
169 VDebugLog("Output format: RGBA 32bpc float%s%s", mono?" (mono)":"", unpremultiply ? " (unpremultiplied)" : "");
170 const float *b = (const float *)buffer;
171 fibmp = FreeImage_AllocateT(FIT_RGBAF, width, height, depth, FI_RGBA_BLUE_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_RED_MASK);
172 if (unpremultiply)
173 for (long int y = 0; y < height; ++y)
174 {
175 float *d = (float *)FreeImage_GetScanLine(fibmp, y);
176 for (long int x = 0; x < width; ++x)
177 {
178 float alpha = b[3];
179 d[0] = b[ 2] / alpha;
180 d[1] = b[mono?2:1] / alpha;
181 d[2] = b[mono?2:0] / alpha;
182 d[3] = b[3];
183 b += 4;
184 d += 4;
185 }
186 }
187 else
188 for (long int y = 0; y < height; ++y)
189 {
190 float *d = (float *)FreeImage_GetScanLine(fibmp, y);
191 for (long int x = 0; x < width; ++x)
192 {
193 d[0] = b[2];
194 d[1] = b[mono?2:1];
195 d[2] = b[mono?2:0];
196 d[3] = b[3];
197 b += 4;
198 d += 4;
199 }
200 }
201 }
202 else if (bufferFormat == GL_DEPTH_COMPONENT16
203 || bufferFormat == GL_R16)
204 {
205 VDebugLog("Output format: mono 16bpc integer");
206 const unsigned short *b = (const unsigned short *)buffer;
207 fibmp = FreeImage_AllocateT(FIT_UINT16, width, height, 16, 0, 0, 0);
208 for (long int y = 0; y < height; ++y)
209 {
210 unsigned short *d = (unsigned short *)FreeImage_GetScanLine(fibmp, y);
211 memcpy(d, b + y*width, width*2);
212 }
213 }
214 else
215 {
216 VUserLog("Error: Unknown bufferFormat.");
217 return NULL;
218 }
219
220 return fibmp;
221}
222
233VuoImage VuoFreeImage_convertFreeImageToVuoImage(FIBITMAP *fi, void *dataToFree, const char *imageURL)
234{
235 if (!fi)
236 return NULL;
237
238 // Decode the memory buffer into a straightforward array of BGRA pixels
239 GLuint format;
240 VuoImageColorDepth colorDepth;
241 unsigned char *pixels;
242 unsigned long pixelsWide;
243 unsigned long pixelsHigh;
244 {
245 pixelsWide = FreeImage_GetWidth(fi);
246 pixelsHigh = FreeImage_GetHeight(fi);
247
248 const FREE_IMAGE_TYPE type = FreeImage_GetImageType(fi);
249 const unsigned int bpp = FreeImage_GetBPP(fi);
250 const FREE_IMAGE_COLOR_TYPE colorType = FreeImage_GetColorType(fi);
251 const FIICCPROFILE *colorProfile = FreeImage_GetICCProfile(fi);
252 FITAG *exifColorSpace = NULL;
253 FreeImage_GetMetadata(FIMD_EXIF_EXIF, fi, "ColorSpace", &exifColorSpace);
254 VDebugLog("ImageType=%d BPP=%d colorType=%s Profile=%s(%d) EXIF_ColorSpace=%s",
255 type,
256 bpp,
257 colorType == FIC_MINISWHITE ? "minIsWhite" :
258 (colorType == FIC_MINISBLACK ? "minIsBlack" :
259 (colorType == FIC_RGB ? "RGB" :
260 (colorType == FIC_PALETTE ? "indexed" :
261 (colorType == FIC_RGBALPHA ? "RGBA" :
262 (colorType == FIC_CMYK ? "CMYK" : "unknown"))))),
263 colorProfile->flags & FIICC_COLOR_IS_CMYK ? "CMYK" : "RGB",
264 colorProfile->size,
265 FreeImage_TagToString(FIMD_EXIF_EXIF, exifColorSpace, NULL));
266
267 if (type == FIT_FLOAT
268 || type == FIT_DOUBLE
269 || type == FIT_UINT16
270 || type == FIT_INT16
271 || type == FIT_UINT32
272 || type == FIT_INT32)
273 {
274 // If it is > 8bpc greyscale, convert to float.
275 colorDepth = VuoImageColorDepth_32;
276 format = GL_LUMINANCE;
277
278 FIBITMAP *fiFloat = FreeImage_ConvertToFloat(fi);
279 FreeImage_Unload(fi);
280 fi = fiFloat;
281 }
282 else if (type == FIT_RGB16
283 || type == FIT_RGBF)
284 {
285 // If it is > 8bpc WITHOUT an alpha channel, convert to float.
286 colorDepth = VuoImageColorDepth_32;
287 format = GL_RGB;
288
289 FIBITMAP *fiFloat = FreeImage_ConvertToRGBF(fi);
290 FreeImage_Unload(fi);
291 fi = fiFloat;
292 }
293 else if (type == FIT_RGBA16
294 || type == FIT_RGBAF)
295 {
296 // If it is > 8bpc WITH an alpha channel, convert to float.
297 colorDepth = VuoImageColorDepth_32;
298 format = GL_RGBA;
299
300 FIBITMAP *fiFloat = FreeImage_ConvertToRGBAF(fi);
301 FreeImage_Unload(fi);
302 fi = fiFloat;
303
304 // FreeImage_PreMultiplyWithAlpha() only works on 8bpc images, so do it ourself.
305 float *pixels = (float *)FreeImage_GetBits(fi);
306 if (!pixels)
307 {
308 VUserLog("Error: '%s': FreeImage couldn't get this image's pixel data. The file might be corrupted.", imageURL);
309 FreeImage_Unload(fi);
310 free(dataToFree);
311 return NULL;
312 }
313 for (int y = 0; y < pixelsHigh; ++y)
314 for (int x = 0; x < pixelsWide; ++x)
315 {
316 float alpha = pixels[(y * pixelsWide + x) * 4 + 3];
317 pixels[(y * pixelsWide + x) * 4 + 0] *= alpha;
318 pixels[(y * pixelsWide + x) * 4 + 1] *= alpha;
319 pixels[(y * pixelsWide + x) * 4 + 2] *= alpha;
320 }
321 }
322 else
323 {
324 // Upload other images as 8bpc.
325 colorDepth = VuoImageColorDepth_8;
326
327 if (colorType == FIC_MINISWHITE
328 || colorType == FIC_MINISBLACK)
329 {
330 format = GL_LUMINANCE;
331
332 FIBITMAP *fiConverted = FreeImage_ConvertTo8Bits(fi);
333 FreeImage_Unload(fi);
334 fi = fiConverted;
335 }
336 else if (colorType == FIC_RGB
337 || (colorType == FIC_PALETTE && !FreeImage_IsTransparent(fi)))
338 {
339 format = GL_BGR;
340
341 FIBITMAP *fiConverted = FreeImage_ConvertTo24Bits(fi);
342 FreeImage_Unload(fi);
343 fi = fiConverted;
344 }
345 else if (colorType == FIC_RGBALPHA
346 || (colorType == FIC_PALETTE && FreeImage_IsTransparent(fi)))
347 {
348 format = GL_BGRA;
349
350 FIBITMAP *fiConverted = FreeImage_ConvertTo32Bits(fi);
351 FreeImage_Unload(fi);
352 fi = fiConverted;
353
354 if (!FreeImage_PreMultiplyWithAlpha(fi))
355 VUserLog("Warning: Premultiplication failed.");
356 }
357 else
358 {
359 VUserLog("Error: '%s': Unknown colorType %d.", imageURL, colorType);
360 FreeImage_Unload(fi);
361 free(dataToFree);
362 return NULL;
363 }
364 }
365
366 pixels = FreeImage_GetBits(fi);
367 if (!pixels)
368 {
369 VUserLog("Error: '%s': FreeImage couldn't get this image's pixel data. The file might be corrupted.", imageURL);
370 FreeImage_Unload(fi);
371 free(dataToFree);
372 return NULL;
373 }
374 }
375
376 // FreeImage's documentation says "Every scanline is DWORD-aligned."
377 // …so round the row stride up to the nearest 4 bytes.
378 // See @ref TestVuoImage::testFetchOddStride.
379 int bytesPerRow = pixelsWide * VuoGlTexture_getBytesPerPixelForInternalFormat(VuoImageColorDepth_getGlInternalFormat(format, colorDepth));
380 bytesPerRow = (bytesPerRow + 3) & ~0x3;
381
382
383 void (^freeCallback)(void *buffer) = ^(void *buffer) {
384 FreeImage_Unload(fi);
385 free(dataToFree);
386 };
387
388 VuoImage vuoImage = VuoImage_makeFromBufferWithStride(pixels, format, pixelsWide, pixelsHigh, bytesPerRow, colorDepth, freeCallback);
389 if (!vuoImage)
390 freeCallback(NULL);
391
392 return vuoImage;
393}