efl/src/modules/evas/image_loaders/png/evas_image_load_png.c

921 lines
27 KiB
C
Raw Normal View History

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <png.h>
#include <setjmp.h>
#include "evas_common_private.h"
#include "evas_private.h"
#define PNG_BYTES_TO_CHECK 4
#define WHITE 0xFFFFFFFF
#define BLACK 0xFF000000
typedef struct _Evas_PNG_Info Evas_PNG_Info;
struct _Evas_PNG_Info
{
unsigned char *map;
size_t length;
size_t position;
png_structp png_ptr;
png_infop info_ptr;
png_uint_32 w32, h32;
int bit_depth, color_type, interlace_type;
volatile Eina_Bool hasa;
};
typedef struct _Evas_Loader_Internal Evas_Loader_Internal;
struct _Evas_Loader_Internal
{
Eina_File *f;
Evas_Image_Load_Opts *opts;
};
static const Evas_Colorspace cspace_grey[2] = {
EVAS_COLORSPACE_GRY8,
EVAS_COLORSPACE_ARGB8888
};
static const Evas_Colorspace cspace_grey_alpha[2] = {
EVAS_COLORSPACE_AGRY88,
EVAS_COLORSPACE_ARGB8888
};
static void
_evas_image_png_update_x_content(Eina_Rectangle *r, int index)
{
if (r->x == 0)
{
r->x = index;
r->w = 1;
}
else
{
r->w = index - r->x;
}
}
static void
_evas_image_png_update_y_content(Eina_Rectangle *r, int index)
{
if (r->y == 0)
{
r->y = index;
r->h = 1;
}
else
{
r->h = index - r->y;
}
}
static void
_evas_image_png_read(png_structp png_ptr, png_bytep out, png_size_t count)
{
Evas_PNG_Info *epi = png_get_io_ptr(png_ptr);
2013-06-20 04:28:18 -07:00
if (!epi) return;
if (epi->position == epi->length) return;
if (epi->position + count > epi->length) count = epi->length - epi->position;
memcpy(out, epi->map + epi->position, count);
epi->position += count;
}
static void *
evas_image_load_file_open_png(Eina_File *f, Eina_Stringshare *key EINA_UNUSED,
Evas_Image_Load_Opts *opts,
Evas_Image_Animated *animated EINA_UNUSED,
int *error)
{
Evas_Loader_Internal *loader;
loader = calloc(1, sizeof (Evas_Loader_Internal));
if (!loader)
{
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
return NULL;
}
loader->f = f;
loader->opts = opts;
return loader;
}
static void
evas_image_load_file_close_png(void *loader_data)
{
free(loader_data);
}
static Eina_Bool
_evas_image_load_file_internal_head_png(Evas_Loader_Internal *loader,
Evas_Image_Property *prop,
Evas_PNG_Info *epi,
int *error, Eina_Bool is_for_data)
{
Evas_Image_Load_Opts *opts = loader->opts;
Eina_File *f = loader->f;
2015-04-25 10:59:18 -07:00
volatile Eina_Bool r = EINA_FALSE;
*error = EVAS_LOAD_ERROR_NONE;
epi->hasa = 0;
if (!is_for_data)
epi->map = eina_file_map_all(f, EINA_FILE_RANDOM);
else
epi->map = eina_file_map_all(f, EINA_FILE_SEQUENTIAL);
if (!epi->map)
{
*error = EVAS_LOAD_ERROR_CORRUPT_FILE;
goto close_file;
}
epi->length = eina_file_size_get(f);
epi->position = 0;
if (epi->length < PNG_BYTES_TO_CHECK)
{
*error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
goto close_file;
}
if (png_sig_cmp(epi->map, 0, PNG_BYTES_TO_CHECK))
{
*error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
goto close_file;
}
epi->png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!epi->png_ptr)
{
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
goto close_file;
}
epi->info_ptr = png_create_info_struct(epi->png_ptr);
if (!epi->info_ptr)
{
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
goto close_file;
}
png_set_read_fn(epi->png_ptr, epi, _evas_image_png_read);
if (setjmp(png_jmpbuf(epi->png_ptr)))
{
*error = EVAS_LOAD_ERROR_CORRUPT_FILE;
goto close_file;
}
png_read_info(epi->png_ptr, epi->info_ptr);
png_get_IHDR(epi->png_ptr, epi->info_ptr, (png_uint_32 *) (&epi->w32),
(png_uint_32 *) (&epi->h32), &epi->bit_depth, &epi->color_type,
&epi->interlace_type, NULL, NULL);
if ((epi->w32 < 1) || (epi->h32 < 1) ||
(epi->w32 > IMG_MAX_SIZE) || (epi->h32 > IMG_MAX_SIZE) ||
IMG_TOO_BIG(epi->w32, epi->h32))
{
if (IMG_TOO_BIG(epi->w32, epi->h32))
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
else
*error = EVAS_LOAD_ERROR_GENERIC;
goto close_file;
}
if (opts->emile.region.w > 0 && opts->emile.region.h > 0)
{
if (((int) epi->w32 < opts->emile.region.x + opts->emile.region.w) ||
((int) epi->h32 < opts->emile.region.y + opts->emile.region.h))
{
*error = EVAS_LOAD_ERROR_GENERIC;
goto close_file;
}
if(opts->emile.scale_down_by > 1)
{
prop->info.w = opts->emile.region.w / opts->emile.scale_down_by;
prop->info.h = opts->emile.region.h / opts->emile.scale_down_by;
}
else
{
prop->info.w = opts->emile.region.w;
prop->info.h = opts->emile.region.h;
}
}
else if (opts->emile.scale_down_by > 1)
{
prop->info.w = (int) epi->w32 / opts->emile.scale_down_by;
prop->info.h = (int) epi->h32 / opts->emile.scale_down_by;
if ((prop->info.w < 1) || (prop->info.h < 1))
{
*error = EVAS_LOAD_ERROR_GENERIC;
goto close_file;
}
}
else
{
prop->info.w = (int) epi->w32;
prop->info.h = (int) epi->h32;
}
evas: fix png regression issue Summary: The evas_image_load_file_data_png had called png_set_tRNS_to_alpha from following commit. 6988a38 evas: fix png loader to actually produce lower resolution content when asked. You could refer to following information regarding png_set_tRNS_to_alpha which is available on page http://www.libpng.org/pub/png/libpng-manual.txt The following code transforms grayscale images of less than 8 to 8 bits, changes paletted images to RGB, and adds a full alpha channel if there is transparency information in a tRNS chunk. This is most useful on grayscale images with bit depths of 2 or 4 or if there is a multiple-image viewing application that wishes to treat all images in the same way. if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png_ptr); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png_ptr) Accidentally commit "382c580 evas: add support for .9.png file to PNG loader." adding a new feature with small code refactoring missed the line calling png_set_tRNS_to_alpha. So we got a rendering issue. It made around 75% size white rectangle using a grayscale and transparent image. I'd like to attach the image which has following type information for test purpose. $ identify -verbose ./grayscale_transparent.png | grep type -i Mime type: image/png Type: Bilevel png:IHDR.color-type-orig: 0 png:IHDR.color_type: 0 (Grayscale) Test Plan: This is the sample image file grayscale_transparent.png {F3748665} Reviewers: cedric, Hermet, jsuya Reviewed By: Hermet Subscribers: #reviewers, #committers Tags: #efl Differential Revision: https://phab.enlightenment.org/D9580
2019-08-16 00:54:50 -07:00
if (png_get_valid(epi->png_ptr, epi->info_ptr, PNG_INFO_tRNS))
{
/* expand transparency entry -> alpha channel if present */
if (!is_for_data) png_set_tRNS_to_alpha(epi->png_ptr);
evas: fix png regression issue Summary: The evas_image_load_file_data_png had called png_set_tRNS_to_alpha from following commit. 6988a38 evas: fix png loader to actually produce lower resolution content when asked. You could refer to following information regarding png_set_tRNS_to_alpha which is available on page http://www.libpng.org/pub/png/libpng-manual.txt The following code transforms grayscale images of less than 8 to 8 bits, changes paletted images to RGB, and adds a full alpha channel if there is transparency information in a tRNS chunk. This is most useful on grayscale images with bit depths of 2 or 4 or if there is a multiple-image viewing application that wishes to treat all images in the same way. if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png_ptr); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png_ptr) Accidentally commit "382c580 evas: add support for .9.png file to PNG loader." adding a new feature with small code refactoring missed the line calling png_set_tRNS_to_alpha. So we got a rendering issue. It made around 75% size white rectangle using a grayscale and transparent image. I'd like to attach the image which has following type information for test purpose. $ identify -verbose ./grayscale_transparent.png | grep type -i Mime type: image/png Type: Bilevel png:IHDR.color-type-orig: 0 png:IHDR.color_type: 0 (Grayscale) Test Plan: This is the sample image file grayscale_transparent.png {F3748665} Reviewers: cedric, Hermet, jsuya Reviewed By: Hermet Subscribers: #reviewers, #committers Tags: #efl Differential Revision: https://phab.enlightenment.org/D9580
2019-08-16 00:54:50 -07:00
epi->hasa = 1;
}
switch (epi->color_type)
{
case PNG_COLOR_TYPE_RGB_ALPHA:
epi->hasa = 1;
break;
case PNG_COLOR_TYPE_GRAY_ALPHA:
epi->hasa = 1;
prop->info.cspaces = cspace_grey_alpha;
break;
case PNG_COLOR_TYPE_GRAY:
if (!epi->hasa) prop->info.cspaces = cspace_grey;
break;
}
if (epi->hasa) prop->info.alpha = 1;
prop->need_data = eina_str_has_extension(eina_file_filename_get(f), ".9.png");
if (prop->need_data)
{
// Adjust size to take into account the 9 patch pixels information
prop->info.w -= 2;
prop->info.h -= 2;
}
r = EINA_TRUE;
if (!is_for_data) return r;
close_file:
if (epi->png_ptr) png_destroy_read_struct(&epi->png_ptr,
epi->info_ptr ? &epi->info_ptr : NULL,
NULL);
if (epi->map) eina_file_map_free(f, epi->map);
memset(epi, 0, sizeof (Evas_PNG_Info));
return r;
}
static Eina_Bool
evas_image_load_file_head_png(void *loader_data,
Evas_Image_Property *prop,
int *error)
{
Evas_Loader_Internal *loader = loader_data;
Evas_PNG_Info epi;
memset(&epi, 0, sizeof (Evas_PNG_Info));
if (!_evas_image_load_file_internal_head_png(loader, prop, &epi, error, EINA_TRUE))
return EINA_FALSE;
return EINA_TRUE;
}
static inline Eina_Bool
_is_black(DATA32 *ptr)
{
if (*ptr == BLACK) return EINA_TRUE;
return EINA_FALSE;
}
static Eina_Bool
evas_image_load_file_head_with_data_png(void *loader_data,
Evas_Image_Property *prop,
void *pixels,
int *error)
{
Evas_Loader_Internal *loader = loader_data;
Evas_Image_Load_Opts *opts;
Eina_File *f;
unsigned char *src_ptr;
unsigned char *pixels2;
unsigned char *surface;
Evas_PNG_Info epi;
Eina_Rectangle region;
unsigned int pack_offset;
int w, h;
2015-04-25 10:59:18 -07:00
char passes;
int i, j, p, k;
2015-04-25 10:59:18 -07:00
volatile int scale_ratio = 1;
int image_w = 0, image_h = 0;
volatile Eina_Bool r = EINA_FALSE;
Eina_Bool nine_patch = EINA_FALSE;
opts = loader->opts;
f = loader->f;
memset(&epi, 0, sizeof (Evas_PNG_Info));
region = opts->emile.region;
if (!_evas_image_load_file_internal_head_png(loader, prop, &epi, error, EINA_FALSE))
return EINA_FALSE;
if (setjmp(png_jmpbuf(epi.png_ptr)))
{
*error = EVAS_LOAD_ERROR_CORRUPT_FILE;
goto close_file;
}
image_w = epi.w32;
image_h = epi.h32;
// We are leveragging region code to not load the border information pixels
// from 9patch files into the surface used by Evas
if (prop->need_data)
2010-09-18 16:39:30 -07:00
{
nine_patch = EINA_TRUE;
region.x += 1;
region.y += 1;
if (region.w > 0 && region.h > 0)
{
if (region.x + region.w + 1 < image_w) region.w += 1;
else region.w = image_w - region.x - 1;
if (region.y + region.h + 1 < image_h) region.h += 1;
else region.h = image_h - region.y - 1;
}
else
{
region.w = image_w - region.x - 1;
region.h = image_h - region.y - 1;
}
2010-09-18 16:39:30 -07:00
}
surface = pixels;
/* Prep for transformations... ultimately we want ARGB */
/* expand palette -> RGB if necessary */
if (epi.color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(epi.png_ptr);
/* expand gray (w/reduced bits) -> 8-bit RGB if necessary */
if ((epi.color_type == PNG_COLOR_TYPE_GRAY) ||
(epi.color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
{
if (prop->info.cspace == EVAS_COLORSPACE_ARGB8888)
png_set_gray_to_rgb(epi.png_ptr);
if (epi.bit_depth < 8) png_set_expand_gray_1_2_4_to_8(epi.png_ptr);
}
/* reduce 16bit color -> 8bit color if necessary */
if (epi.bit_depth > 8) png_set_strip_16(epi.png_ptr);
/* pack all pixels to byte boundaries */
png_set_packing(epi.png_ptr);
w = prop->info.w;
h = prop->info.h;
switch (prop->info.cspace)
{
case EVAS_COLORSPACE_ARGB8888:
/* we want ARGB */
#ifdef WORDS_BIGENDIAN
png_set_swap_alpha(epi.png_ptr);
if (!epi.hasa) png_set_filler(epi.png_ptr, 0xff, PNG_FILLER_BEFORE);
#else
png_set_bgr(epi.png_ptr);
if (!epi.hasa) png_set_filler(epi.png_ptr, 0xff, PNG_FILLER_AFTER);
#endif
pack_offset = sizeof(DATA32);
break;
case EVAS_COLORSPACE_AGRY88:
/* we want AGRY */
#ifdef WORDS_BIGENDIAN
png_set_swap_alpha(epi.png_ptr);
if (!epi.hasa) png_set_filler(epi.png_ptr, 0xff, PNG_FILLER_BEFORE);
#else
if (!epi.hasa) png_set_filler(epi.png_ptr, 0xff, PNG_FILLER_AFTER);
#endif
pack_offset = sizeof(DATA16);
break;
case EVAS_COLORSPACE_GRY8: pack_offset = sizeof(DATA8); break;
default: abort();
}
passes = png_set_interlace_handling(epi.png_ptr);
/* we read image line by line in all case because of .9.png */
pixels2 = malloc(image_w * image_h * pack_offset);
if (!pixels2)
{
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
goto close_file;
}
for (p = 0; p < passes; p++)
{
for (i = 0; i < image_h; i++)
png_read_row(epi.png_ptr, pixels2 + (i * image_w * pack_offset), NULL);
}
png_read_end(epi.png_ptr, epi.info_ptr);
if (nine_patch && pack_offset != sizeof (DATA32))
{
*error = EVAS_LOAD_ERROR_CORRUPT_FILE;
free(pixels2);
goto close_file;
}
if (nine_patch)
{
DATA32 *src_ptr1;
DATA32 *src_ptr2;
Eina_Bool stretchable = EINA_FALSE;
Eina_Rectangle optional_content = { 0 };
uint8_t current = 0;
memset(&prop->content, 0, sizeof (Eina_Rectangle));
// Top line of the image
src_ptr1 = (DATA32*) pixels2 + 1;
// Bottom line of the image
src_ptr2 = src_ptr1 + (image_h - 1) * image_w;
// Extract top stretch zone and horizontal content area
for (i = 1; i < image_w - 1;
i++, src_ptr1++, src_ptr2++)
{
Eina_Bool bp1 = _is_black(src_ptr1);
Eina_Bool bp2 = _is_black(src_ptr2);
// Updating horizontal area where content can be located
if (bp2)
_evas_image_png_update_x_content(&prop->content, i);
// In case no content area is provided, let's make it up
if (bp1)
_evas_image_png_update_x_content(&optional_content, i);
// Switching from a non stretchable to a stretchable zone or the opposite
if (!((stretchable && (bp1)) ||
(!stretchable && (!bp1))))
{
evas_loader_helper_stretch_region_push(&prop->stretch.horizontal.region,
&current, stretchable);
stretchable = !stretchable;
}
// Keep counting in the area we are in
current++;
if (current != 0x7F) continue;
// The bucket is full
evas_loader_helper_stretch_region_push(&prop->stretch.horizontal.region,
&current, stretchable);
}
current = 0;
// Left border of the image
src_ptr1 = (DATA32*) pixels2 + image_w;
// Right border of the image
src_ptr2 = src_ptr1 + (image_w - 1);
for (i = 1; i < image_h - 1;
i++, src_ptr1 += image_w, src_ptr2 += image_w)
{
Eina_Bool bp1 = _is_black(src_ptr1);
Eina_Bool bp2 = _is_black(src_ptr2);
// Updating vertical area where content can be located
if (bp2)
_evas_image_png_update_y_content(&prop->content, i);
// In case no content area is provided, let's make it up
if (bp1)
_evas_image_png_update_y_content(&optional_content, i);
// Switching from a non stretchable to a stretchable zone or the opposite
if (!((stretchable && (bp1)) ||
(!stretchable && (!bp1))))
{
evas_loader_helper_stretch_region_push(&prop->stretch.vertical.region,
&current, stretchable);
stretchable = !stretchable;
}
// Keep counting in the area we are in
current++;
if (current != 0x7F) continue;
// The bucket is full
evas_loader_helper_stretch_region_push(&prop->stretch.vertical.region,
&current, stretchable);
}
// Content zone is optional, if not provided, we should use the one we guessed
if (prop->content.x == 0 || prop->content.y == 0)
memcpy(&prop->content, &optional_content, sizeof (Eina_Rectangle));
// Check that the limit of our content zone are correct
// This could be handled as an incorrect file completly,
// but let's try to recover
if (prop->content.x == 0 || prop->content.y == 0)
{
*error = EVAS_LOAD_ERROR_CORRUPT_FILE;
free(pixels2);
goto close_file;
}
if ((prop->content.x + prop->content.w >= image_w - 1) &&
(prop->content.y + prop->content.h >= image_h - 1))
{
*error = EVAS_LOAD_ERROR_CORRUPT_FILE;
free(pixels2);
goto close_file;
}
// Correct the work space to be the same as the image one for intersection
// with region and correct back afterward.
prop->content.x++;
prop->content.y++;
if (eina_rectangle_intersection(&prop->content, &region))
{
prop->content.x--;
prop->content.y--;
}
}
src_ptr = pixels2 + (region.y * image_w * pack_offset) + region.x * pack_offset;
//general case: 4 bytes pixel.
if (pack_offset == sizeof(DATA32))
{
DATA32 *dst_ptr = (DATA32 *) surface;
DATA32 *src_ptr2 = (DATA32 *) src_ptr;
for (i = 0; i < h; i++)
{
for (j = 0; j < w; j++)
{
*dst_ptr = *src_ptr2;
++dst_ptr;
src_ptr2 += scale_ratio;
}
src_ptr2 += scale_ratio * (image_w - w);
}
}
else
{
unsigned char *dst_ptr = surface;
for (i = 0; i < h; i++)
{
for (j = 0; j < w; j++)
{
for (k = 0; k < (int)pack_offset; k++)
dst_ptr[k] = src_ptr[k + scale_ratio * j * pack_offset];
dst_ptr += pack_offset;
}
src_ptr += scale_ratio * (image_w - w) * pack_offset;
}
}
free(pixels2);
prop->info.premul = EINA_TRUE;
*error = EVAS_LOAD_ERROR_NONE;
r = EINA_TRUE;
close_file:
if (epi.png_ptr) png_destroy_read_struct(&epi.png_ptr,
epi.info_ptr ? &epi.info_ptr : NULL,
NULL);
if (epi.map) eina_file_map_free(f, epi.map);
return r;
}
static Eina_Bool
evas_image_load_file_data_png(void *loader_data,
Evas_Image_Property *prop,
void *pixels,
int *error)
{
Evas_Loader_Internal *loader = loader_data;
Evas_Image_Load_Opts *opts;
Eina_File *f;
unsigned char *surface;
Evas_PNG_Info epi;
unsigned int pack_offset;
int w, h;
char passes;
int i, j, p, k;
volatile int scale_ratio = 1;
volatile int region_set = 0;
int image_w = 0, image_h = 0;
volatile Eina_Bool r = EINA_FALSE;
opts = loader->opts;
f = loader->f;
memset(&epi, 0, sizeof (Evas_PNG_Info));
if (!_evas_image_load_file_internal_head_png(loader, prop, &epi, error, EINA_FALSE))
return EINA_FALSE;
if (setjmp(png_jmpbuf(epi.png_ptr)))
{
*error = EVAS_LOAD_ERROR_CORRUPT_FILE;
goto close_file;
}
image_w = epi.w32;
image_h = epi.h32;
if (opts->emile.scale_down_by > 1)
{
scale_ratio = opts->emile.scale_down_by;
epi.w32 /= scale_ratio;
epi.h32 /= scale_ratio;
}
if ((opts->emile.region.w > 0 && opts->emile.region.h > 0) &&
(opts->emile.region.w != image_w || opts->emile.region.h != image_h))
{
epi.w32 = opts->emile.region.w / scale_ratio;
epi.h32 = opts->emile.region.h / scale_ratio;
region_set = 1;
}
if (prop->info.w != epi.w32 ||
prop->info.h != epi.h32)
{
*error = EVAS_LOAD_ERROR_GENERIC;
goto close_file;
}
surface = pixels;
/* Prep for transformations... ultimately we want ARGB */
/* expand palette -> RGB if necessary */
if (epi.color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(epi.png_ptr);
/* expand gray (w/reduced bits) -> 8-bit RGB if necessary */
if ((epi.color_type == PNG_COLOR_TYPE_GRAY) ||
(epi.color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
{
if (prop->info.cspace == EVAS_COLORSPACE_ARGB8888)
png_set_gray_to_rgb(epi.png_ptr);
if (epi.bit_depth < 8) png_set_expand_gray_1_2_4_to_8(epi.png_ptr);
}
/* reduce 16bit color -> 8bit color if necessary */
if (epi.bit_depth > 8) png_set_strip_16(epi.png_ptr);
/* pack all pixels to byte boundaries */
png_set_packing(epi.png_ptr);
w = epi.w32;
h = epi.h32;
switch (prop->info.cspace)
{
case EVAS_COLORSPACE_ARGB8888:
/* we want ARGB */
#ifdef WORDS_BIGENDIAN
png_set_swap_alpha(epi.png_ptr);
if (!epi.hasa) png_set_filler(epi.png_ptr, 0xff, PNG_FILLER_BEFORE);
#else
png_set_bgr(epi.png_ptr);
if (!epi.hasa) png_set_filler(epi.png_ptr, 0xff, PNG_FILLER_AFTER);
#endif
pack_offset = sizeof(DATA32);
break;
case EVAS_COLORSPACE_AGRY88:
/* we want AGRY */
#ifdef WORDS_BIGENDIAN
png_set_swap_alpha(epi.png_ptr);
if (!epi.hasa) png_set_filler(epi.png_ptr, 0xff, PNG_FILLER_BEFORE);
#else
if (!epi.hasa) png_set_filler(epi.png_ptr, 0xff, PNG_FILLER_AFTER);
#endif
pack_offset = sizeof(DATA16);
break;
case EVAS_COLORSPACE_GRY8: pack_offset = sizeof(DATA8); break;
default: abort();
}
passes = png_set_interlace_handling(epi.png_ptr);
/* we read image line by line if scale down was set */
if (scale_ratio == 1 && region_set == 0)
{
for (p = 0; p < passes; p++)
{
for (i = 0; i < h; i++)
png_read_row(epi.png_ptr, surface + (i * w * pack_offset), NULL);
}
png_read_end(epi.png_ptr, epi.info_ptr);
}
else
{
unsigned char *src_ptr;
unsigned char *dst_ptr = surface;
int skip_row = 0, region_x = 0, region_y = 0;
if (region_set)
{
region_x = opts->emile.region.x;
region_y = opts->emile.region.y;
}
if (passes == 1)
{
int line_size = (image_w * pack_offset) - (region_x * pack_offset);
unsigned char *tmp_line = (unsigned char *) alloca(image_w * pack_offset);
//accumulate pixel color here.
unsigned short *interp_buf = (unsigned short *) alloca(line_size * sizeof(unsigned short));
unsigned short *pbuf;
for (skip_row = 0; skip_row < region_y; skip_row++)
png_read_row(epi.png_ptr, tmp_line, NULL);
png_read_row(epi.png_ptr, tmp_line, NULL);
src_ptr = tmp_line + (region_x * pack_offset);
//The first pixel, of the first line
for (k = 0; k < (int) pack_offset; k++)
dst_ptr[k] = src_ptr[k];
dst_ptr += pack_offset;
src_ptr += (scale_ratio * pack_offset);
for (j = 1; j < w; j++)
{
//rgba
interp_buf[0] = 0;
interp_buf[1] = 0;
interp_buf[2] = 0;
interp_buf[3] = 0;
//horizontal interpolation.
for (p = 0; p < scale_ratio; p++)
{
for (k = 0; k < (int) pack_offset; k++)
interp_buf[k] += src_ptr[k - (int)(p * pack_offset)];
}
for (k = 0; k < (int) pack_offset; k++)
dst_ptr[k] = (interp_buf[k] / scale_ratio);
dst_ptr += pack_offset;
src_ptr += (scale_ratio * pack_offset);
}
//next lines
for (i = 1; i < h; i++)
{
memset(interp_buf, 0x00, line_size * sizeof(unsigned short));
//vertical interpolation.
for (j = 0; j < scale_ratio; j++)
{
png_read_row(epi.png_ptr, tmp_line, NULL);
src_ptr = tmp_line + (region_x * pack_offset);
for (p = 0; p < line_size; ++p)
interp_buf[p] += src_ptr[p];
}
for (p = 0; p < line_size; ++p)
interp_buf[p] /= scale_ratio;
pbuf = interp_buf;
//The first pixel of the current line
for (k = 0; k < (int) pack_offset; k++)
dst_ptr[k] = pbuf[k];
dst_ptr += pack_offset;
pbuf += scale_ratio * pack_offset;
for (j = 1; j < w; j++)
{
//horizontal interpolation.
for (p = 1; p < scale_ratio; ++p)
{
for (k = 0; k < (int) pack_offset; k++)
pbuf[k] += pbuf[k - (int)(p * pack_offset)];
}
for (k = 0; k < (int) pack_offset; k++)
dst_ptr[k] = (pbuf[k] / scale_ratio);
dst_ptr += pack_offset;
pbuf += (scale_ratio * pack_offset);
}
}
for (skip_row = region_y + h * scale_ratio; skip_row < image_h; skip_row++)
png_read_row(epi.png_ptr, tmp_line, NULL);
}
else
{
//TODO: Scale-down interpolation for multi-pass?
unsigned char *pixels2 = malloc(image_w * image_h * pack_offset);
if (pixels2)
{
for (p = 0; p < passes; p++)
{
for (i = 0; i < image_h; i++)
png_read_row(epi.png_ptr, pixels2 + (i * image_w * pack_offset), NULL);
}
png_read_end(epi.png_ptr, epi.info_ptr);
src_ptr = pixels2 + (region_y * image_w * pack_offset) + region_x * pack_offset;
//general case: 4 bytes pixel.
if (pack_offset == sizeof(DATA32))
{
DATA32 *dst_ptr2 = (DATA32 *) surface;
DATA32 *src_ptr2 = (DATA32 *) src_ptr;
for (i = 0; i < h; i++)
{
for (j = 0; j < w; j++)
{
*dst_ptr2 = *src_ptr2;
++dst_ptr2;
src_ptr2 += scale_ratio;
}
src_ptr2 += scale_ratio * image_w;
}
}
else
{
unsigned char *dst_ptr2 = surface;
for (i = 0; i < h; i++)
{
for (j = 0; j < w; j++)
{
for (k = 0; k < (int)pack_offset; k++)
dst_ptr2[k] = src_ptr[k + scale_ratio * j * pack_offset];
dst_ptr2 += pack_offset;
}
src_ptr += scale_ratio * image_w * pack_offset;
}
}
free(pixels2);
}
}
}
prop->info.premul = EINA_TRUE;
*error = EVAS_LOAD_ERROR_NONE;
r = EINA_TRUE;
close_file:
if (epi.png_ptr) png_destroy_read_struct(&epi.png_ptr,
epi.info_ptr ? &epi.info_ptr : NULL,
NULL);
if (epi.map) eina_file_map_free(f, epi.map);
return r;
}
static Evas_Image_Load_Func evas_image_load_png_func =
{
EVAS_IMAGE_LOAD_VERSION,
evas_image_load_file_open_png,
evas_image_load_file_close_png,
evas_image_load_file_head_png,
evas_image_load_file_head_with_data_png,
evas_image_load_file_data_png,
NULL,
2013-05-06 18:50:57 -07:00
EINA_TRUE,
EINA_FALSE
};
static int
module_open(Evas_Module *em)
{
if (!em) return 0;
em->functions = (void *)(&evas_image_load_png_func);
return 1;
}
static void
module_close(Evas_Module *em EINA_UNUSED)
{
}
static Evas_Module_Api evas_modapi =
{
EVAS_MODULE_API_VERSION,
"png",
"none",
{
module_open,
module_close
}
};
EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, png);
#ifndef EVAS_STATIC_BUILD_PNG
EVAS_EINA_MODULE_DEFINE(image_loader, png);
#endif