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

jpeg.c

/*
 * beryl-plugins::jpeg.c - adds JPEG image support to beryl.
 * Copyright: (C) 2006 Nicholas Thomas
 *                 Danny Baumann (JPEG writing, option stuff)
 *
 * 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.
 *
 */

#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>

#include <beryl.h>
#include <jpeglib.h>


#define JPEG_QUALITY_DEFAULT  80
#define JPEG_QUALITY_MIN      1
#define JPEG_QUALITY_MAX      100

#define JPEG_DISPLAY_OPTION_QUALITY 0
#define JPEG_DISPLAY_OPTION_NUM           1

static int displayPrivateIndex;

typedef struct _JPEGDisplay
{
      CompOption opt[JPEG_DISPLAY_OPTION_NUM];

      FileToImageProc fileToImage;
      ImageToFileProc imageToFile;
} JPEGDisplay;


#define GET_JPEG_DISPLAY(d)                         \
    ((JPEGDisplay *) (d)->privates[displayPrivateIndex].ptr)

#define JPEG_DISPLAY(d)              \
    JPEGDisplay *jd = GET_JPEG_DISPLAY (d)

#define NUM_OPTIONS(d) (sizeof ((d)->opt) / sizeof (CompOption))

static Bool rgbToBGRA(const JSAMPLE * source, void **data, int height,
                                int width, int alpha)
{
      int h, w;
      char *dest;

      dest = malloc(height * width * 4);
      if (!dest)
            return FALSE;

      *data = dest;

      for (h = 0; h < height; h++)
            for (w = 0; w < width; w++)
            {
                  dest[((h * width + w) * 4) + 0] = source[((h * width + w) * 3) + 2];    //B
                  dest[((h * width + w) * 4) + 1] = source[((h * width + w) * 3) + 1];    //G
                  dest[((h * width + w) * 4) + 2] = source[((h * width + w) * 3) + 0];    //R
                  dest[((h * width + w) * 4) + 3] = alpha;
            }

      return TRUE;
}

static Bool rgbaToRGB(char *source, JSAMPLE ** dest, int height, int width,
                                int stride)
{
      int h, w;
      int ps = stride / width;      /* pixel size */
      JSAMPLE *d;

      d = malloc(height * width * 3 * sizeof(JSAMPLE));
      if (!d)
            return FALSE;

      *dest = d;

      for (h = 0; h < height; h++)
            for (w = 0; w < width; w++)
            {
                  d[((h * width + w) * 3) + 0] = source[((h * width + w) * ps) + 0];      //R
                  d[((h * width + w) * 3) + 1] = source[((h * width + w) * ps) + 1];      //G
                  d[((h * width + w) * 3) + 2] = source[((h * width + w) * ps) + 2];      //B
            }

      return TRUE;
}

static Bool readJPEGFileToImage(FILE * file, int *width, int *height,
                                                void **data)
{
      struct jpeg_decompress_struct cinfo;
      struct jpeg_error_mgr jerr;
      JSAMPLE *buf;
      JSAMPROW *rows;
      int i;

      if (!file)
            return FALSE;

      cinfo.err = jpeg_std_error(&jerr);

      jpeg_create_decompress(&cinfo);

      jpeg_stdio_src(&cinfo, file);
      jpeg_read_header(&cinfo, TRUE);

      cinfo.out_color_space = JCS_RGB;

      jpeg_start_decompress(&cinfo);

      *height = cinfo.output_height;
      *width = cinfo.output_width;

      buf = malloc(cinfo.output_height * cinfo.output_width *
                         cinfo.output_components * sizeof(JSAMPLE));
      if (!buf)
            return FALSE;

      rows = malloc(cinfo.output_height * sizeof(JSAMPROW));
      if (!rows)
      {
            free(buf);
            return FALSE;
      }

      for (i = 0; i < cinfo.output_height; i++)
            rows[i] = &buf[i * cinfo.output_width * cinfo.output_components];

      while (cinfo.output_scanline < cinfo.output_height)
            jpeg_read_scanlines(&cinfo, &rows[cinfo.output_scanline],
                                          cinfo.output_height - cinfo.output_scanline);

      jpeg_finish_decompress(&cinfo);
      jpeg_destroy_decompress(&cinfo);

      // convert the rgb data into BGRA format
      if (!rgbToBGRA(buf, data, *height, *width, 255))
      {
            free(rows);
            free(buf);
            return FALSE;
      }

      free(rows);
      free(buf);
      return TRUE;
}


static Bool writeJPEG(CompDisplay * d, void *buffer, FILE * file,
                                int width, int height, int stride)
{
      struct jpeg_compress_struct cinfo;
      struct jpeg_error_mgr jerr;
      JSAMPROW row_pointer[1];
      JSAMPLE *data;

      JPEG_DISPLAY(d);

      // convert the rgb data into BGRA format
      if (!rgbaToRGB(buffer, &data, height, width, stride))
            return FALSE;

      cinfo.err = jpeg_std_error(&jerr);
      jpeg_create_compress(&cinfo);

      jpeg_stdio_dest(&cinfo, file);

      cinfo.image_width = width;
      cinfo.image_height = height;
      cinfo.input_components = 3;
      cinfo.in_color_space = JCS_RGB;

      jpeg_set_defaults(&cinfo);
      jpeg_set_quality(&cinfo,
                               jd->opt[JPEG_DISPLAY_OPTION_QUALITY].value.i, TRUE);
      jpeg_start_compress(&cinfo, TRUE);

      while (cinfo.next_scanline < cinfo.image_height)
      {
            row_pointer[0] =
                        &data[(cinfo.image_height - cinfo.next_scanline -
                                 1) * width * 3];
            jpeg_write_scanlines(&cinfo, row_pointer, 1);
      }

      jpeg_finish_compress(&cinfo);
      jpeg_destroy_compress(&cinfo);

      free(data);

      return TRUE;
}

// Turns the path & name into a real, absolute path
// No extensions jiggery-pokery here, as JPEGs can be
// .jpg or .jpeg - or, indeed, whatever.
// Deals with the path, regardless of what it's passed.
static char *createFilename(const char *path, const char *name)
{
      int len;
      char *filename = NULL;

      if (path && !name)
            len = strlen(path) + 2;
      else if (!path && name)
            len = strlen(name) + 2;
      else if (path && name)
            len = strlen(path) + strlen(name) + 2;
      else
            return NULL;

      filename = malloc(len);
      if (!filename)
            return NULL;

      if (path && !name)
            sprintf(filename, "%s", path);
      else if (!path && name)
            sprintf(filename, "%s", name);
      else
            sprintf(filename, "%s/%s", path, name);

      return filename;

}

static Bool JPEGImageToFile(CompDisplay * d,
                                          const char *path,
                                          const char *name,
                                          const char *format,
                                          int width, int height, int stride, void *data)
{
      Bool status = FALSE;

      // Not a JPEG
      if (strcasecmp(format, "jpeg") != 0 && strcasecmp(format, "jpg") != 0)
      {
            JPEG_DISPLAY(d);
            UNWRAP(jd, d, imageToFile);
            status = (*d->imageToFile) (d, path, name, format, width, height,
                                                      stride, data);
            WRAP(jd, d, imageToFile, JPEGImageToFile);
            return status;
      }

      // Is a JPEG
      char *filename = createFilename(path, name);

      if (!filename)
            return FALSE;

      FILE *file = fopen(filename, "wb");

      if (file)
      {
            status = writeJPEG(d, data, file, width, height, stride);
            fclose(file);
      }

      free(filename);
      return status;
}

static Bool JPEGFileToImage(CompDisplay * d,
                                          const char *path,
                                          const char *name,
                                          int *width, int *height, int *stride, void **data)
{
      Bool status = FALSE;

      char *filename = createFilename(path, name);

      if (!filename)
            return FALSE;

      // Do some testing here to see if it's got a .jpg or .jpeg extension
      // TODO: fix for mixed & uppercase extensions!
      char *extension = strrchr(filename, '.');

      if (extension)
      {
            if (strcasecmp(extension, ".jpeg") == 0 ||
                  strcasecmp(extension, ".jpg") == 0)
            {
                  FILE *file = fopen(filename, "rb");

                  if (file)
                  {
                        status = readJPEGFileToImage(file, width, height, data);
                        fclose(file);

                        if (status)       // Success!
                        {
                              free(filename);
                              *stride = *width * 4;
                              return TRUE;
                        }
                  }
            }
      }

      free(filename);

      // Isn't a JPEG - pass to the next in the chain.
      JPEG_DISPLAY(d);

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

      return status;
}

static void JPEGDisplayInitOptions(JPEGDisplay * jd)
{
      CompOption *o;

      o = &jd->opt[JPEG_DISPLAY_OPTION_QUALITY];
      o->advanced = False;
      o->name = "quality";
      o->group = N_("Misc. options");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Compression Quality");
      o->longDesc = N_("Quality of Compression when saving JPEG images.");
      o->type = CompOptionTypeInt;
      o->value.i = JPEG_QUALITY_DEFAULT;
      o->rest.i.min = JPEG_QUALITY_MIN;
      o->rest.i.max = JPEG_QUALITY_MAX;
}

static CompOption *JPEGGetDisplayOptions(CompDisplay * display, int *count)
{
      if (display)
      {
            JPEG_DISPLAY(display);

            *count = NUM_OPTIONS(jd);
            return jd->opt;
      }
      else
      {
            JPEGDisplay *jd = malloc(sizeof(JPEGDisplay));

            JPEGDisplayInitOptions(jd);
            *count = NUM_OPTIONS(jd);
            return jd->opt;
      }
}

static Bool
JPEGSetDisplayOption(CompDisplay * display, char *name,
                               CompOptionValue * value)
{
      CompOption *o;
      int index;

      JPEG_DISPLAY(display);

      o = compFindOption(jd->opt, NUM_OPTIONS(jd), name, &index);
      if (!o)
            return FALSE;

      switch (index)
      {
      case JPEG_DISPLAY_OPTION_QUALITY:
            if (compSetIntOption(o, value))
                  return TRUE;
            break;
      default:
            break;
      }

      return FALSE;
}

static Bool JPEGInitDisplay(CompPlugin * p, CompDisplay * d)
{
      JPEGDisplay *jd;
      CompScreen *s;

      jd = malloc(sizeof(JPEGDisplay));
      if (!jd)
            return FALSE;

      WRAP(jd, d, fileToImage, JPEGFileToImage);
      WRAP(jd, d, imageToFile, JPEGImageToFile);

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

      JPEGDisplayInitOptions(jd);

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

      return TRUE;
}

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

      JPEG_DISPLAY(d);

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

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

      free(jd);
}


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

      return TRUE;
}

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

CompPluginFeature JPEGFeatures[] = {
      {"imageext:jpeg"}
      ,
      {"imageext:jpg"}
      ,
      {"imagemime:image/jpeg"}
      ,
};

CompPluginVTable JPEGVTable = {
      "jpeg",
      N_("JPEG"),
      N_("JPEG image format plugin"),
      JPEGInit,
      JPEGFini,
      JPEGInitDisplay,
      JPEGFiniDisplay,
      0,
      0,
      0,
      0,
      JPEGGetDisplayOptions,
      JPEGSetDisplayOption,
      0,
      0,
      0,
      0,
      JPEGFeatures,
      sizeof(JPEGFeatures) / sizeof(JPEGFeatures[0]),
      BERYL_ABI_INFO,
      "beryl-plugins",
      "imageformat",
      0,
      0,
      True,
};

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

Generated by  Doxygen 1.6.0   Back to index