blob: 3395c9200b6d3697678562d79b786d510cf27e8f [file] [log] [blame]
/* ply-image.c - png file loader
*
* Copyright (C) 2006, 2007 Red Hat, Inc.
* Copyright (C) 2003 University of Southern California
*
* 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, 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., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Some implementation taken from the cairo library.
*
* Shamelessly taken and unmercifully hacked for temporary use
* in Chrome OS.
*
* Written by: Charlie Brej <cbrej@cs.man.ac.uk>
* Kristian Høgsberg <krh@redhat.com>
* Ray Strode <rstrode@redhat.com>
* Carl D. Worth <cworth@cworth.org>
*/
#include "ply-image.h"
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <math.h>
#include <png.h>
#include <linux/fb.h>
#include "ply-utils.h"
#define BILLION (1000 * 1000 * 1000)
typedef union
{
uint32_t *as_pixels;
png_byte *as_png_bytes;
char *address;
} ply_image_layout_t;
struct _ply_image
{
char *filename;
FILE *fp;
ply_image_layout_t layout;
size_t size;
long width;
long height;
};
static bool ply_image_open_file (ply_image_t *image);
static void ply_image_close_file (ply_image_t *image);
static bool
ply_image_open_file (ply_image_t *image)
{
assert (image != NULL);
image->fp = fopen (image->filename, "r");
if (image->fp == NULL)
return false;
return true;
}
static void
ply_image_close_file (ply_image_t *image)
{
assert (image != NULL);
if (image->fp == NULL)
return;
fclose (image->fp);
image->fp = NULL;
}
ply_image_t *
ply_image_new (const char *filename)
{
ply_image_t *image;
assert (filename != NULL);
image = calloc (1, sizeof (ply_image_t));
image->filename = strdup (filename);
image->fp = NULL;
image->layout.address = NULL;
image->size = -1;
image->width = -1;
image->height = -1;
return image;
}
void
ply_image_free (ply_image_t *image)
{
if (image == NULL)
return;
assert (image->filename != NULL);
if (image->layout.address != NULL)
{
free (image->layout.address);
image->layout.address = NULL;
}
free (image->filename);
free (image);
}
#if 0
static void
transform_to_argb32 (png_struct *png,
png_row_info *row_info,
png_byte *data)
{
unsigned int i;
for (i = 0; i < row_info->rowbytes; i += 4)
{
uint8_t red, green, blue, alpha;
uint32_t pixel_value;
red = data[i + 0];
green = data[i + 1];
blue = data[i + 2];
alpha = data[i + 3];
red = (uint8_t) CLAMP (((red / 255.0) * (alpha / 255.0)) * 255.0, 0, 255.0);
green = (uint8_t) CLAMP (((green / 255.0) * (alpha / 255.0)) * 255.0,
0, 255.0);
blue = (uint8_t) CLAMP (((blue / 255.0) * (alpha / 255.0)) * 255.0, 0, 255.0);
pixel_value = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
memcpy (data + i, &pixel_value, sizeof (uint32_t));
}
}
#endif
/*
* The following function courtesy of Intel Moblin's
* plymouth-lite port.
*/
static void
transform_to_rgb32 (png_struct *png,
png_row_info *row_info,
png_byte *data)
{
unsigned int i;
for (i = 0; i < row_info->rowbytes; i += 4)
{
uint8_t red, green, blue, alpha;
uint32_t pixel_value;
red = data[i + 0];
green = data[i + 1];
blue = data[i + 2];
alpha = data[i + 3];
pixel_value = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
memcpy (data + i, &pixel_value, sizeof (uint32_t));
}
}
bool
ply_image_load (ply_image_t *image)
{
png_struct *png;
png_info *info;
png_uint_32 width, height, bytes_per_row, row;
int bits_per_pixel, color_type, interlace_method;
png_byte **rows;
assert (image != NULL);
if (!ply_image_open_file (image))
return false;
png = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
assert (png != NULL);
info = png_create_info_struct (png);
assert (info != NULL);
png_init_io (png, image->fp);
if (setjmp (png_jmpbuf (png)) != 0)
{
ply_image_close_file (image);
return false;
}
png_read_info (png, info);
png_get_IHDR (png, info,
&width, &height, &bits_per_pixel,
&color_type, &interlace_method, NULL, NULL);
bytes_per_row = 4 * width;
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb (png);
if ((color_type == PNG_COLOR_TYPE_GRAY) && (bits_per_pixel < 8))
png_set_gray_1_2_4_to_8 (png);
if (png_get_valid (png, info, PNG_INFO_tRNS))
png_set_tRNS_to_alpha (png);
if (bits_per_pixel == 16)
png_set_strip_16 (png);
if (bits_per_pixel < 8)
png_set_packing (png);
if ((color_type == PNG_COLOR_TYPE_GRAY)
|| (color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
png_set_gray_to_rgb (png);
if (interlace_method != PNG_INTERLACE_NONE)
png_set_interlace_handling (png);
png_set_filler (png, 0xff, PNG_FILLER_AFTER);
png_set_read_user_transform_fn (png, transform_to_rgb32);
png_read_update_info (png, info);
rows = malloc (height * sizeof (png_byte *));
image->layout.address = malloc (height * bytes_per_row);
for (row = 0; row < height; row++)
rows[row] = &image->layout.as_png_bytes[row * bytes_per_row];
png_read_image (png, rows);
free (rows);
png_read_end (png, info);
ply_image_close_file (image);
png_destroy_read_struct (&png, &info, NULL);
image->width = width;
image->height = height;
return true;
}
uint32_t *
ply_image_get_data (ply_image_t *image)
{
assert (image != NULL);
return image->layout.as_pixels;
}
ssize_t
ply_image_get_size (ply_image_t *image)
{
assert (image != NULL);
return image->size;
}
long
ply_image_get_width (ply_image_t *image)
{
assert (image != NULL);
return image->width;
}
long
ply_image_get_height (ply_image_t *image)
{
assert (image != NULL);
return image->height;
}
static uint32_t
ply_image_interpolate (ply_image_t *image,
int width,
int height,
double x,
double y)
{
int ix;
int iy;
int i;
int offset_x;
int offset_y;
uint32_t pixels[2][2];
uint32_t reply = 0;
for (offset_y = 0; offset_y < 2; offset_y++)
for (offset_x = 0; offset_x < 2; offset_x++)
{
ix = x + offset_x;
iy = y + offset_y;
if (ix < 0 || ix >= width || iy < 0 || iy >= height)
pixels[offset_y][offset_x] = 0x00000000;
else
pixels[offset_y][offset_x] = image->layout.as_pixels[ix + iy * width];
}
ix = x;
iy = y;
x -= ix;
y -= iy;
for (i = 0; i < 4; i++)
{
int value = 0;
for (offset_y = 0; offset_y < 2; offset_y++)
for (offset_x = 0; offset_x < 2; offset_x++)
{
int subval = (pixels[offset_y][offset_x] >> (i * 8)) & 0xFF;
if (offset_x) subval *= x;
else subval *= 1-x;
if (offset_y) subval *= y;
else subval *= 1-y;
value += subval;
}
reply |= (value & 0xFF) << (i * 8);
}
return reply;
}
ply_image_t *
ply_image_resize (ply_image_t *image,
long width,
long height)
{
ply_image_t *new_image;
int x, y;
double old_x, old_y;
int old_width, old_height;
float scale_x, scale_y;
new_image = ply_image_new (image->filename);
new_image->layout.address = malloc (height * width * 4);
new_image->width = width;
new_image->height = height;
old_width = ply_image_get_width (image);
old_height = ply_image_get_height (image);
scale_x = ((double) old_width - 1) / MAX (width - 1, 1);
scale_y = ((double) old_height - 1) / MAX (height - 1, 1);
for (y = 0; y < height; y++)
{
old_y = y * scale_y;
for (x=0; x < width; x++)
{
old_x = x * scale_x;
new_image->layout.as_pixels[x + y * width] =
ply_image_interpolate (image, old_width, old_height, old_x, old_y);
}
}
return new_image;
}
ply_image_t *
ply_image_rotate (ply_image_t *image,
long center_x,
long center_y,
double theta_offset)
{
ply_image_t *new_image;
int x, y;
double old_x, old_y;
int width;
int height;
width = ply_image_get_width (image);
height = ply_image_get_height (image);
new_image = ply_image_new (image->filename);
new_image->layout.address = malloc (height * width * 4);
new_image->width = width;
new_image->height = height;
for (y = 0; y < height; y++)
{
for (x = 0; x < width; x++)
{
double d;
double theta;
d = sqrt ((x - center_x) * (x - center_x) +
(y - center_y) * (y - center_y));
theta = atan2 (y - center_y, x - center_x);
theta -= theta_offset;
old_x = center_x + d * cos (theta);
old_y = center_y + d * sin (theta);
new_image->layout.as_pixels[x + y * width] =
ply_image_interpolate (image, width, height, old_x, old_y);
}
}
return new_image;
}
ply_image_t *
ply_image_from_file(const char *path)
{
int exit_code;
ply_image_t *image = ply_image_new (path);
if (!ply_image_load (image))
{
exit_code = errno;
perror (path);
exit (exit_code);
}
return image;
}
#include "ply-frame-buffer.h"
#include <math.h>
#include <signal.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <values.h>
#include <linux/kd.h>
const int frame_rate = 20; /* Animation frames per second */
int bad_clock = 0; /* Set to 1 if the clock ever returns an error */
void
ply_frame_buffer_show_file_at_xy(ply_frame_buffer_t *buffer, const char *path,
long x, long y)
{
ply_image_t *image;
uint32_t *data;
long width, height;
ply_frame_buffer_area_t area;
image = ply_image_from_file (path);
data = ply_image_get_data (image);
width = ply_image_get_width (image);
height = ply_image_get_height (image);
ply_frame_buffer_get_size (buffer, &area);
area.x = (area.width / 2) - (width / 2); // + x;
area.y = (area.height / 2) - (height / 2); // + y;
area.width = width;
area.height = height;
ply_frame_buffer_fill (buffer, &area, x, y, data);
ply_image_free (image);
}
int64_t timespec_to_nsec(struct timespec ts)
{
int64_t nsec;
nsec = ts.tv_sec;
nsec *= BILLION;
nsec += ts.tv_nsec;
return nsec;
}
int64_t gettime_check(void)
{
struct timespec ts;
int r = clock_gettime(CLOCK_MONOTONIC, &ts);
if (r)
{
bad_clock = 1;
return 0;
}
return timespec_to_nsec(ts);
}
int usage(void)
{
fprintf(stderr,
"usage: ply-image --clear\n"
" ply-image <background> [<x-offset> <y-offset> "
"<frame-1> ... <frame-n>]\n");
exit(1);
}
int
main (int argc,
char **argv)
{
int exit_code = 0;
int clear = 0;
int help = 0;
ply_frame_buffer_t *buffer;
int i;
int xoff, yoff;
char *endptr;
// hide_cursor ();
/*
* Ad-hoc arg parsing, to keep the program small.
*/
if (argc > 1)
{
clear = strcasecmp (argv[1], "--clear") == 0;
help = strcasecmp (argv[1], "--help") == 0 ||
strcasecmp (argv[1], "-h") == 0;
}
if (help || argc == 1 || argc == 3 || argc == 4)
{
usage();
}
buffer = ply_frame_buffer_new (NULL);
if (!ply_frame_buffer_open (buffer))
{
exit_code = errno;
perror ("could not open framebuffer");
return exit_code;
}
if (clear)
{
ply_frame_buffer_clear (buffer);
}
else
{
/*
* Display main image.
*/
ply_frame_buffer_show_file_at_xy (buffer, argv[1], 0, 0);
if (argc >= 4)
{
/*
* Animate frames.
*/
int64_t draw_time_nsec, frame_period_nsec;
int64_t sleep_time_nsec, now_nsec, then_nsec;
xoff = strtol (argv[2], &endptr, 10);
if (endptr == argv[2] || *endptr != '\0')
{
usage ();
}
yoff = strtol (argv[3], &endptr, 10);
if (endptr == argv[3] || *endptr != '\0')
{
usage ();
}
/*
* Begin animation.
*
* The first time around, we don't know how long it takes to draw a
* frame, so we approximate it as 0. After that, we assume that the
* drawing time for the previous cycle is a good estimate for the
* next cycle. Errors may be introduced by different frame size and
* complexity (PNG decoding) and by the CPU load.
*/
draw_time_nsec = 0;
frame_period_nsec = BILLION / frame_rate;
now_nsec = gettime_check ();
for (i = 4; i < argc; i++)
{
/*
* Before displaying the next frame, sleep for the right amount
* of time so that the achieved period approximates the desired
* period.
*/
sleep_time_nsec = frame_period_nsec - draw_time_nsec;
if (sleep_time_nsec > 0)
{
struct timespec req;
req.tv_sec = sleep_time_nsec / BILLION;
req.tv_nsec = sleep_time_nsec % BILLION;
nanosleep (&req, NULL);
}
ply_frame_buffer_show_file_at_xy (buffer, argv[i], xoff, yoff);
then_nsec = now_nsec;
now_nsec = gettime_check ();
/*
* If the clock is bad (unlikely), assume draw time is
* instantaneous as a rough guess.
*/
draw_time_nsec = bad_clock ? 0 :
now_nsec - then_nsec - sleep_time_nsec;
}
}
}
// Skip these to save time.
// ply_frame_buffer_close (buffer);
// ply_frame_buffer_free (buffer);
return exit_code;
}