Logo Search packages:      
Sourcecode: beryl-plugins version File versions

png.c

/*
 * Copyright © 2006 Novell, Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * Author: David Reveman <davidr@novell.com>
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <png.h>
#include <setjmp.h>

#include <beryl.h>

#define PNG_SIG_SIZE 8

static int displayPrivateIndex;

typedef struct _PngDisplay
{
      FileToImageProc fileToImage;
      ImageToFileProc imageToFile;
} PngDisplay;

#define GET_PNG_DISPLAY(d)                          \
    ((PngDisplay *) (d)->privates[displayPrivateIndex].ptr)

#define PNG_DISPLAY(d)               \
    PngDisplay *pd = GET_PNG_DISPLAY (d)


static void
premultiplyData(png_structp png, png_row_infop row_info, png_bytep data)
{
      unsigned int i;

      for (i = 0; i < row_info->rowbytes; i += 4)
      {
            unsigned char *base = &data[i];
            unsigned char blue = base[0];
            unsigned char green = base[1];
            unsigned char red = base[2];
            unsigned char alpha = base[3];
            int p;

            red = (unsigned)red *(unsigned)alpha / 255;
            green = (unsigned)green *(unsigned)alpha / 255;
            blue = (unsigned)blue *(unsigned)alpha / 255;

            p = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
            memcpy(base, &p, sizeof(int));
      }
}

static Bool
readPngData(png_struct * png,
                  png_info * info, void **data, int *width, int *height)
{
      png_uint_32 png_width, png_height;
      int depth, color_type, interlace, i;
      unsigned int pixel_size;
      png_byte **row_pointers;
      char *d;

      png_read_info(png, info);

      png_get_IHDR(png, info,
                         &png_width, &png_height, &depth,
                         &color_type, &interlace, NULL, NULL);

      *width = (int)png_width;
      *height = (int)png_height;

      /* convert palette/gray image to rgb */
      if (color_type == PNG_COLOR_TYPE_PALETTE)
            png_set_palette_to_rgb(png);

      /* expand gray bit depth if needed */
      if (color_type == PNG_COLOR_TYPE_GRAY && depth < 8)
            png_set_gray_1_2_4_to_8(png);

      /* transform transparency to alpha */
      if (png_get_valid(png, info, PNG_INFO_tRNS))
            png_set_tRNS_to_alpha(png);

      if (depth == 16)
            png_set_strip_16(png);

      if (depth < 8)
            png_set_packing(png);

      /* convert grayscale to RGB */
      if (color_type == PNG_COLOR_TYPE_GRAY ||
            color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
            png_set_gray_to_rgb(png);

      if (interlace != PNG_INTERLACE_NONE)
            png_set_interlace_handling(png);

      png_set_bgr(png);
      png_set_filler(png, 0xff, PNG_FILLER_AFTER);

      png_set_read_user_transform_fn(png, premultiplyData);

      png_read_update_info(png, info);

      pixel_size = 4;
      d = (char *)malloc(png_width * png_height * pixel_size);
      if (!d)
            return FALSE;

      *data = d;

      row_pointers = (png_byte **) malloc(png_height * sizeof(char *));
      if (!row_pointers)
      {
            free(d);
            return FALSE;
      }

      for (i = 0; i < png_height; i++)
            row_pointers[i] = (png_byte *) (d + i * png_width * pixel_size);

      png_read_image(png, row_pointers);
      png_read_end(png, info);

      free(row_pointers);

      return TRUE;
}

static Bool
readPngFileToImage(FILE * file, int *width, int *height, void **data)
{
      unsigned char png_sig[PNG_SIG_SIZE];
      int sig_bytes;
      png_struct *png;
      png_info *info;
      Bool status;

      sig_bytes = fread(png_sig, 1, PNG_SIG_SIZE, file);
      if (png_check_sig(png_sig, sig_bytes) == 0)
            return FALSE;

      png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
      if (!png)
            return FALSE;

      info = png_create_info_struct(png);
      if (!info)
      {
            png_destroy_read_struct(&png, NULL, NULL);

            return FALSE;
      }

      png_init_io(png, file);
      png_set_sig_bytes(png, sig_bytes);

      status = readPngData(png, info, data, width, height);

      png_destroy_read_struct(&png, &info, NULL);

      return status;
}

#if 0
static void
userReadData(png_structp png_ptr, png_bytep data, png_size_t length)
{
      const unsigned char **buffer = (const unsigned char **)
                  png_get_io_ptr(png_ptr);

      memcpy(data, *buffer, length);
      *buffer += length;
}

static Bool
readPngBuffer(const unsigned char *buffer,
                    char **data, unsigned int *width, unsigned int *height)
{
      unsigned char png_sig[PNG_SIG_SIZE];
      png_struct *png;
      png_info *info;
      const unsigned char *b = buffer + PNG_SIG_SIZE;
      Bool status;

      memcpy(png_sig, buffer, PNG_SIG_SIZE);
      if (png_check_sig(png_sig, PNG_SIG_SIZE) == 0)
            return FALSE;

      png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
      if (!png)
            return FALSE;

      info = png_create_info_struct(png);
      if (!info)
      {
            png_destroy_read_struct(&png, NULL, NULL);
            return FALSE;
      }

      png_set_read_fn(png, (void *)&b, userReadData);
      png_set_sig_bytes(png, PNG_SIG_SIZE);

      status = readPngData(png, info, data, width, height);

      png_destroy_read_struct(&png, &info, NULL);

      return status;
}
#endif

static Bool
writePng(unsigned char *buffer,
             png_rw_ptr writeFunc,
             void *closure, int width, int height, int stride)
{
      png_struct *png;
      png_info *info;
      png_byte **rows;
      png_color_16 white;
      int i;

      rows = malloc(height * sizeof(png_byte *));
      if (!rows)
            return FALSE;

      for (i = 0; i < height; i++)
            rows[height - i - 1] = buffer + i * stride;

      png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
      if (!png)
      {
            free(rows);

            return FALSE;
      }

      info = png_create_info_struct(png);
      if (!info)
      {
            png_destroy_read_struct(&png, NULL, NULL);
            free(rows);

            return FALSE;
      }

      if (setjmp(png_jmpbuf(png)))
      {
            png_destroy_read_struct(&png, NULL, NULL);
            free(rows);

            return FALSE;
      }

      png_set_write_fn(png, closure, writeFunc, NULL);

      png_set_IHDR(png, info,
                         width, height, 8,
                         PNG_COLOR_TYPE_RGB_ALPHA,
                         PNG_INTERLACE_NONE,
                         PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);

      white.red = 0xff;
      white.blue = 0xff;
      white.green = 0xff;

      png_set_bKGD(png, info, &white);

      png_write_info(png, info);
      png_write_image(png, rows);
      png_write_end(png, info);

      png_destroy_write_struct(&png, &info);
      free(rows);

      return TRUE;
}

static void stdioWriteFunc(png_structp png, png_bytep data, png_size_t size)
{
      FILE *fp;

      fp = png_get_io_ptr(png);
      if (fwrite(data, 1, size, fp) != size)
            png_error(png, "Write Error");
}

static char *pngExtension(const char *name)
{

      if (strlen(name) > 4)
      {
            if (strcasecmp(name + (strlen(name) - 4), ".png") == 0)
                  return "";
      }

      return ".png";
}

static Bool
pngImageToFile(CompDisplay * d,
                     const char *path,
                     const char *name,
                     const char *format,
                     int width, int height, int stride, void *data)
{
      Bool status = FALSE;
      char *extension = pngExtension(name);
      char *file;
      FILE *fp;
      int len;

      PNG_DISPLAY(d);

      len = (path ? strlen(path) : 0) + strlen(name) + strlen(extension) + 2;

      file = malloc(len);
      if (file)
      {
            if (path)
                  sprintf(file, "%s/%s%s", path, name, extension);
            else
                  sprintf(file, "%s%s", name, extension);
      }

      if (file && strcasecmp(format, "png") == 0)
      {
            fp = fopen(file, "wb");
            if (fp)
            {
                  status = writePng(data, stdioWriteFunc, fp, width, height,
                                            stride);
                  fclose(fp);
            }

            if (status)
            {
                  free(file);
                  return TRUE;
            }
      }

      UNWRAP(pd, d, imageToFile);
      status = (*d->imageToFile) (d, path, name, format, width, height, stride,
                                                data);
      WRAP(pd, d, imageToFile, pngImageToFile);

      if (!status && file)
      {
            fp = fopen(file, "wb");
            if (fp)
            {
                  status = writePng(data, stdioWriteFunc, fp, width, height,
                                            stride);
                  fclose(fp);
            }
      }

      if (file)
            free(file);

      return status;
}

static Bool
pngFileToImage(CompDisplay * d,
                     const char *path,
                     const char *name,
                     int *width, int *height, int *stride, void **data)
{
      Bool status = FALSE;
      char *extension = pngExtension(name);
      char *file;
      int len;

      PNG_DISPLAY(d);

      len = (path ? strlen(path) : 0) + strlen(name) + strlen(extension) + 2;

      file = malloc(len);
      if (file)
      {
            FILE *fp;

            if (path)
                  sprintf(file, "%s/%s%s", path, name, extension);
            else
                  sprintf(file, "%s%s", name, extension);

            fp = fopen(file, "r");
            if (fp)
            {
                  status = readPngFileToImage(fp, width, height, data);
                  fclose(fp);
            }

            free(file);

            if (status)
            {
                  *stride = *width * 4;
                  return TRUE;
            }
      }

      UNWRAP(pd, d, fileToImage);
      status = (*d->fileToImage) (d, path, name, width, height, stride, data);
      WRAP(pd, d, fileToImage, pngFileToImage);

      return status;
}

static Bool pngInitDisplay(CompPlugin * p, CompDisplay * d)
{
      PngDisplay *pd;
      CompScreen *s;

      pd = malloc(sizeof(PngDisplay));
      if (!pd)
            return FALSE;

      WRAP(pd, d, fileToImage, pngFileToImage);
      WRAP(pd, d, imageToFile, pngImageToFile);

      d->privates[displayPrivateIndex].ptr = pd;

      for (s = d->screens; s; s = s->next)
            updateDefaultIcon(s);

      return TRUE;
}

static void pngFiniDisplay(CompPlugin * p, CompDisplay * d)
{
      CompScreen *s;

      PNG_DISPLAY(d);

      UNWRAP(pd, d, fileToImage);
      UNWRAP(pd, d, imageToFile);

      for (s = d->screens; s; s = s->next)
            updateDefaultIcon(s);

      free(pd);
}

static Bool pngInit(CompPlugin * p)
{
      displayPrivateIndex = allocateDisplayPrivateIndex();
      if (displayPrivateIndex < 0)
            return FALSE;

      return TRUE;
}

static void pngFini(CompPlugin * p)
{
      if (displayPrivateIndex >= 0)
            freeDisplayPrivateIndex(displayPrivateIndex);
}

CompPluginFeature pngFeatures[] = {
      {"imageext:png"}
      ,
      {"imagemime:image/png"}
      ,
};

CompPluginVTable pngVTable = {
      "png",
      N_("Png"),
      N_("Png image loader"),
      pngInit,
      pngFini,
      pngInitDisplay,
      pngFiniDisplay,
      0,                                        /* InitScreen */
      0,                                        /* FiniScreen */
      0,                                        /* InitWindow */
      0,                                        /* FiniWindow */
      0,                                        /* GetDisplayOptions */
      0,                                        /* SetDisplayOption */
      0,                                        /* GetScreenOptions */
      0,                                        /* SetScreenOption */
      0,                                        /* Deps */
      0,                                        /* nDeps */
      pngFeatures,                        /* Features */
      sizeof(pngFeatures) / sizeof(pngFeatures[0]),   /* nFeatures */
      BERYL_ABI_INFO,
      "beryl-plugins",
      "imageformat",
      0,
      0,
      True,
};

CompPluginVTable *getCompPluginInfo(void)
{
      return &pngVTable;
}

Generated by  Doxygen 1.6.0   Back to index