#include #include #include #include "PngI.h" static double get_display_exponent(void); static int png_process_image(Screen *screen, XImage *ximage, int image_rowbytes, int image_channels, unsigned char bg_red, unsigned char bg_green, unsigned char bg_blue, unsigned char *image_data); static int png_msb(unsigned long u32val); static int png_load_file(FILE *infile, unsigned long *pWidth, unsigned long *pHeight, unsigned char *red, unsigned char *green, unsigned char *blue, int *pChannels, unsigned long *pRowbytes, unsigned char **image_data); int _XmPngGetImage(Screen *screen, FILE *infile, Pixel background, XImage **ximage) { XColor xcolor; int image_channels; unsigned char bg_red = 0, bg_green = 0, bg_blue = 0; unsigned char *image_data = NULL; unsigned long image_width, image_height, image_rowbytes; unsigned char *xdata; int pad; int rc; xcolor.pixel = background; XQueryColor(screen->display, screen->cmap, &xcolor); bg_red = xcolor.red; bg_green = xcolor.green; bg_blue = xcolor.blue; rc = png_load_file(infile, &image_width, &image_height, NULL, NULL, NULL, &image_channels, &image_rowbytes, &image_data); /* XXX if background is XmINSPECIFIED_PIXEL, we cat try to get background from the PNG image, but I can doesn't have this information. rc = readpng_load_file(infile, &image_width, &image_height, &bg_red, &bg_green, &bg_blue, &image_channels, &image_rowbytes, &image_data); */ if (rc) return rc; if (screen->root_depth == 24 || screen->root_depth == 32) { xdata = (unsigned char *) malloc(4 * image_width * image_height); pad = 32; } else if (screen->root_depth == 16) { xdata = (unsigned char *) malloc(2 * image_width * image_height); pad = 16; } else { /* depth == 8 */ xdata = (unsigned char *) malloc(image_width * image_height); pad = 8; } if (!xdata) return 4; *ximage = XCreateImage(screen->display, screen->root_visual, screen->root_depth, ZPixmap, 0, (char *) xdata, image_width, image_height, pad, 0); if (!*ximage) { free(xdata); return 4; } (*ximage)->byte_order = MSBFirst; rc = png_process_image(screen, *ximage, image_rowbytes, image_channels, bg_red, bg_green, bg_blue, image_data); if (image_data) { free(image_data); image_data = NULL; } return rc; } static double get_display_exponent(void) { double LUT_exponent; /* just the lookup table */ double CRT_exponent = 2.2; /* just the monitor */ double default_display_exponent; /* whole display system */ char *p; double display_exponent; /* First set the default value for our display-system exponent, i.e., * the product of the CRT exponent and the exponent corresponding to * the frame-buffer's lookup table (LUT), if any. This is not an * exhaustive list of LUT values (e.g., OpenStep has a lot of weird * ones), but it should cover 99% of the current possibilities. */ LUT_exponent = 1.0; /* assume no LUT: most PCs */ /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */ default_display_exponent = LUT_exponent * CRT_exponent; /* If the user has set the SCREEN_GAMMA environment variable as suggested * (somewhat imprecisely) in the libpng documentation, use that; otherwise * use the default value we just calculated. Either way, the user may * override this via a command-line option. */ if ((p = getenv("SCREEN_GAMMA")) != NULL) display_exponent = atof(p); else display_exponent = default_display_exponent; return display_exponent; } /* return value = 0 for success, 1 for bad sig, 2 for bad struct (IHDR or kBGD), 4 for no mem 5 if fails due to no bKGD chunk */ static int png_load_file(FILE *infile, unsigned long *pWidth, unsigned long *pHeight, unsigned char *red, unsigned char *green, unsigned char *blue, int *pChannels, unsigned long *pRowbytes, unsigned char **image_data) { png_structp png_ptr = NULL; png_infop info_ptr = NULL; int bit_depth, color_type; png_uint_32 width, height; // int rc; unsigned char sig[8]; png_color_16p pBackground; double gamma; png_uint_32 i, rowbytes; png_bytepp row_pointers = NULL; fread(sig, 1, 8, infile); if (png_sig_cmp(sig, 0, 8)) return 1; /* bad signature */ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) return 4; /* out of memory */ info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, NULL, NULL); return 4; /* out of memory */ } /* setjmp() must be called in every function that calls a PNG-reading * libpng function */ if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return 2; } png_init_io(png_ptr, infile); png_set_sig_bytes(png_ptr, 8); /* we already read the 8 signature bytes */ png_read_info(png_ptr, info_ptr); /* read all PNG info up to image data */ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); if (pWidth) *pWidth = width; if (pHeight) *pHeight = height; if (red && green && blue) { if (png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD)) { png_get_bKGD(png_ptr, info_ptr, &pBackground); if (bit_depth == 16) { *red = pBackground->red >> 8; *green = pBackground->green >> 8; *blue = pBackground->blue >> 8; } else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { if (bit_depth == 1) *red = *green = *blue = pBackground->gray ? 255 : 0; else if (bit_depth == 2) *red = *green = *blue = (255 / 3) * pBackground->gray; else /* bit_depth == 4 */ *red = *green = *blue = (255 / 15) * pBackground->gray; } else { *red = (unsigned char) pBackground->red; *green = (unsigned char) pBackground->green; *blue = (unsigned char) pBackground->blue; } } } if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 16) png_set_expand(png_ptr); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_expand(png_ptr); if (bit_depth == 16) png_set_strip_16(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); if (png_get_gAMA(png_ptr, info_ptr, &gamma)) png_set_gamma(png_ptr, get_display_exponent(), gamma); /* all transformations have been registered; now update info_ptr data, * get rowbytes and channels, and allocate image memory */ png_read_update_info(png_ptr, info_ptr); *pRowbytes = rowbytes = png_get_rowbytes(png_ptr, info_ptr); *pChannels = (int) png_get_channels(png_ptr, info_ptr); if ((*image_data = (unsigned char *) malloc(rowbytes * height)) == NULL) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return 4; } if ((row_pointers = (png_bytepp) malloc(height * sizeof(png_bytep))) == NULL) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); free(*image_data); *image_data = NULL; return 4; } /* set the individual row_pointers to point at the correct offsets */ for (i = 0; i < height; ++i) row_pointers[i] = *image_data + i * rowbytes; /* now we can go ahead and just read the whole image */ png_read_image(png_ptr, row_pointers); /* and we're done! (png_read_end() can be omitted if no processing of * post-IDAT text/time/etc. is desired) */ free(row_pointers); row_pointers = NULL; png_read_end(png_ptr, NULL); if (png_ptr && info_ptr) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); png_ptr = NULL; info_ptr = NULL; } return 0; } static int png_process_image(Screen *screen, XImage *ximage, int image_rowbytes, int image_channels, unsigned char bg_red, unsigned char bg_green, unsigned char bg_blue, unsigned char *image_data) { unsigned char *src; char *dest; unsigned char r, g, b, a; unsigned long i, row, lastrow = 0; unsigned long pixel; int ximage_rowbytes = ximage->bytes_per_line; static int RShift, GShift, BShift; static unsigned long RMask, GMask, BMask; unsigned short red, green, blue; RMask = screen->root_visual->red_mask; GMask = screen->root_visual->green_mask; BMask = screen->root_visual->blue_mask; if (screen->root_depth == 15 || screen->root_depth == 16) { RShift = 15 - png_msb(RMask); GShift = 15 - png_msb(GMask); BShift = 15 - png_msb(BMask); } else if (screen->root_depth > 16) { RShift = png_msb(RMask) - 7; GShift = png_msb(GMask) - 7; BShift = png_msb(BMask) - 7; } if (screen->root_depth == 24 || screen->root_depth == 32) { for (lastrow = row = 0; row < ximage->height; ++row) { src = image_data + row * image_rowbytes; dest = ximage->data + row * ximage_rowbytes; if (image_channels == 3) { for (i = ximage->width; i > 0; --i) { red = *src++; green = *src++; blue = *src++; #ifdef NO_24BIT_MASKS pixel = (red << RShift) | (green << GShift) | (blue << BShift); /* recall that we set ximage->byte_order = MSBFirst above */ /* GRR BUG: this assumes bpp == 32, but may be 24: */ *dest++ = (char) ((pixel >> 24) & 0xff); *dest++ = (char) ((pixel >> 16) & 0xff); *dest++ = (char) ((pixel >> 8) & 0xff); *dest++ = (char) (pixel & 0xff); #else red = (RShift < 0) ? red << (-RShift) : red >> RShift; green = (GShift < 0) ? green << (-GShift) : green >> GShift; blue = (BShift < 0) ? blue << (-BShift) : blue >> BShift; pixel = (red & RMask) | (green & GMask) | (blue & BMask); /* recall that we set ximage->byte_order = MSBFirst above */ *dest++ = (char) ((pixel >> 24) & 0xff); *dest++ = (char) ((pixel >> 16) & 0xff); *dest++ = (char) ((pixel >> 8) & 0xff); *dest++ = (char) (pixel & 0xff); #endif } } else { /* if (image_channels == 4) */ for (i = ximage->width; i > 0; --i) { r = *src++; g = *src++; b = *src++; a = *src++; if (a == 255) { red = r; green = g; blue = b; } else if (a == 0) { red = bg_red; green = bg_green; blue = bg_blue; } else { /* this macro (from png.h) composites the foreground * and background values and puts the result into the * first argument */ png_composite(red, r, a, bg_red); png_composite(green, g, a, bg_green); png_composite(blue, b, a, bg_blue); } pixel = (red << RShift) | (green << GShift) | (blue << BShift); /* recall that we set ximage->byte_order = MSBFirst above */ *dest++ = (char) ((pixel >> 24) & 0xff); *dest++ = (char) ((pixel >> 16) & 0xff); *dest++ = (char) ((pixel >> 8) & 0xff); *dest++ = (char) (pixel & 0xff); } } } } else if (screen->root_depth == 16) { for (lastrow = row = 0; row < ximage->height; ++row) { src = image_data + row * image_rowbytes; dest = ximage->data + row * ximage_rowbytes; if (image_channels == 3) { for (i = ximage->width; i > 0; --i) { red = ((unsigned short) (*src) << 8); ++src; green = ((unsigned short) (*src) << 8); ++src; blue = ((unsigned short) (*src) << 8); ++src; pixel = ((red >> RShift) & RMask) | ((green >> GShift) & GMask) | ((blue >> BShift) & BMask); /* recall that we set ximage->byte_order = MSBFirst above */ *dest++ = (char) ((pixel >> 8) & 0xff); *dest++ = (char) (pixel & 0xff); } } else { /* if (image_channels == 4) */ for (i = ximage->width; i > 0; --i) { r = *src++; g = *src++; b = *src++; a = *src++; if (a == 255) { red = ((unsigned short) r << 8); green = ((unsigned short) g << 8); blue = ((unsigned short) b << 8); } else if (a == 0) { red = ((unsigned short) bg_red << 8); green = ((unsigned short) bg_green << 8); blue = ((unsigned short) bg_blue << 8); } else { /* this macro (from png.h) composites the foreground * and background values and puts the result back into * the first argument (== fg byte here: safe) */ png_composite(r, r, a, bg_red); png_composite(g, g, a, bg_green); png_composite(b, b, a, bg_blue); red = ((unsigned short) r << 8); green = ((unsigned short) g << 8); blue = ((unsigned short) b << 8); } pixel = ((red >> RShift) & RMask) | ((green >> GShift) & GMask) | ((blue >> BShift) & BMask); /* recall that we set ximage->byte_order = MSBFirst above */ *dest++ = (char) ((pixel >> 8) & 0xff); *dest++ = (char) (pixel & 0xff); } } } } else { /* depth == 8 */ /* XXX: add 8-bit support */ } return 0; } static int png_msb(unsigned long u32val) { int i; for (i = 31; i >= 0; --i) { if (u32val & 0x80000000L) break; u32val <<= 1; } return i; }