/*
* 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;
}
syntax highlighted by Code2HTML, v. 0.9.1