Blame lib/Xm/Png.c

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