core: use a monotonic clock for animation timing calculations
[fbsplash.git] / core / src / image.c
blob6973575c58784c7144a2de2beb2047e3d17b7e57
1 /*
2 * image.c - Functions to load & unpack PNGs and JPEGs
4 * Copyright (C) 2004-2005, Michal Januszewski <spock@gentoo.org>
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License v2. See the file COPYING in the main directory of this archive for
8 * more details.
12 #include <stdio.h>
13 #include <stdlib.h>
15 #include "config.h"
17 #ifdef CONFIG_PNG
18 #ifdef TARGET_KERNEL
19 #include "png.h"
20 #else
21 #include <png.h>
22 #endif
23 #endif
25 #ifdef TARGET_KERNEL
26 #include "jpeglib.h"
27 #else
28 #include <jpeglib.h>
29 #endif
31 #include "common.h"
32 #include "render.h"
34 #ifdef CONFIG_PNG
35 #define PALETTE_COLORS 240
36 static int load_png(stheme_t *theme, char *filename, u8 **data, struct fb_cmap *cmap, unsigned int *width, unsigned int *height, u8 want_alpha)
38 png_structp png_ptr;
39 png_infop info_ptr;
40 png_bytep row_pointer;
41 png_colorp palette;
42 int rowbytes, num_palette;
43 int i, j, bytespp = fbd.bytespp;
44 u8 *buf = NULL;
45 u8 *t;
47 if (want_alpha)
48 bytespp = 4;
50 FILE *fp = fopen(filename,"r");
51 if (!fp)
52 return -1;
54 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
55 info_ptr = png_create_info_struct(png_ptr);
57 if (setjmp(png_jmpbuf(png_ptr))) {
58 return -1;
61 png_init_io(png_ptr, fp);
62 png_read_info(png_ptr, info_ptr);
64 if (cmap && info_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
65 return -2;
67 if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
68 info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
69 png_set_gray_to_rgb(png_ptr);
71 if (info_ptr->bit_depth == 16)
72 png_set_strip_16(png_ptr);
74 if (!want_alpha && info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
75 png_set_strip_alpha(png_ptr);
77 #ifndef TARGET_KERNEL
78 if (!(info_ptr->color_type & PNG_COLOR_MASK_ALPHA) & want_alpha) {
79 png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER);
81 #endif
82 png_read_update_info(png_ptr, info_ptr);
84 if (!cmap && info_ptr->color_type != PNG_COLOR_TYPE_RGB && info_ptr->color_type != PNG_COLOR_TYPE_RGBA)
85 return -3;
87 if (cmap) {
88 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
90 if (num_palette > cmap->len)
91 return -3;
94 rowbytes = png_get_rowbytes(png_ptr, info_ptr);
96 if ((width && *width && info_ptr->width != *width) || (height && *height && info_ptr->height != *height)) {
97 iprint(MSG_ERROR, "Image size mismatch: %s.\n", filename);
98 return -2;
99 } else {
100 *width = info_ptr->width;
101 *height = info_ptr->height;
104 *data = malloc(theme->xres * theme->yres * fbd.bytespp);
105 if (!*data) {
106 iprint(MSG_CRITICAL, "Failed to allocate memory for image: %s.\n", filename);
107 return -4;
110 buf = malloc(rowbytes);
111 if (!buf) {
112 iprint(MSG_CRITICAL, "Failed to allocate memory for image line buffer.\n");
113 free(*data);
114 return -4;
117 for (i = 0; i < info_ptr->height; i++) {
118 if (cmap) {
119 row_pointer = *data + info_ptr->width * i;
120 } else if (want_alpha) {
121 row_pointer = *data + info_ptr->width * i * 4;
122 } else {
123 row_pointer = buf;
126 png_read_row(png_ptr, row_pointer, NULL);
128 if (cmap) {
129 int h = 256 - cmap->len;
130 t = *data + info_ptr->width * i;
132 if (h) {
133 /* Move the colors up by 'h' offset. This is used because fbcon
134 * takes the first 16 colors. */
135 for (j = 0; j < rowbytes; j++) {
136 t[j] += h;
140 /* We only need to convert the image if the alpha channel is not required */
141 } else if (!want_alpha) {
142 u8 *tmp = *data + info_ptr->width * bytespp * i;
143 rgba2fb((rgbacolor*)buf, tmp, tmp, info_ptr->width, i, 0, 0xff);
147 if (cmap) {
148 for (i = 0; i < cmap->len; i++) {
149 cmap->red[i] = palette[i].red * 257;
150 cmap->green[i] = palette[i].green * 257;
151 cmap->blue[i] = palette[i].blue * 257;
155 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
157 free(buf);
158 fclose(fp);
160 return 0;
163 static int is_png(char *filename)
165 unsigned char header[8];
166 FILE *fp = fopen(filename,"r");
168 if (!fp)
169 return -1;
171 fread(header, 1, 8, fp);
172 fclose(fp);
174 return !png_sig_cmp(header, 0, 8);
176 #endif /* PNG */
178 static int load_jpeg(char *filename, u8 **data, unsigned int *width, unsigned int *height)
180 struct jpeg_decompress_struct cinfo;
181 struct jpeg_error_mgr jerr;
182 FILE* injpeg;
184 u8 *buf = NULL;
185 int i;
187 cinfo.err = jpeg_std_error(&jerr);
188 jpeg_create_decompress(&cinfo);
190 if ((injpeg = fopen(filename,"r")) == NULL) {
191 iprint(MSG_ERROR, "Can't open file %s!\n", filename);
192 return -1;
195 jpeg_stdio_src(&cinfo, injpeg);
196 jpeg_read_header(&cinfo, TRUE);
197 jpeg_start_decompress(&cinfo);
199 if ((width && cinfo.output_width != *width) || (height && cinfo.output_height != *height)) {
200 iprint(MSG_ERROR, "Image size mismatch: %s.\n", filename);
201 return -2;
202 } else {
203 *width = cinfo.output_width;
204 *height = cinfo.output_height;
207 buf = malloc(cinfo.output_width * cinfo.output_components * sizeof(char));
208 if (!buf) {
209 iprint(MSG_ERROR, "Failed to allocate JPEG decompression buffer.\n");
210 return -1;
213 *data = malloc(cinfo.output_width * cinfo.output_height * fbd.bytespp);
214 if (!*data) {
215 iprint(MSG_ERROR, "Failed to allocate memory for image: %s.\n", filename);
216 return -4;
219 for (i = 0; i < cinfo.output_height; i++) {
220 u8 *tmp;
221 jpeg_read_scanlines(&cinfo, (JSAMPARRAY) &buf, 1);
222 tmp = *data + cinfo.output_width * fbd.bytespp * i;
223 rgba2fb((rgbacolor*)buf, tmp, tmp, cinfo.output_width, i, 0, 0xff);
226 jpeg_finish_decompress(&cinfo);
227 jpeg_destroy_decompress(&cinfo);
228 fclose(injpeg);
230 free(buf);
231 return 0;
234 static int load_bg_images(stheme_t *theme, char mode)
236 struct fb_image *img = (mode == 'v') ? &theme->verbose_img : &theme->silent_img;
237 char *pic;
238 int i;
240 img->width = theme->xres;
241 img->height = theme->yres;
242 img->depth = fbd.var.bits_per_pixel;
244 /* Deal with 8bpp modes. Only PNGs can be loaded, and pic256
245 * option has to be used to specify the filename of the image */
246 if (fbd.var.bits_per_pixel == 8) {
247 pic = (mode == 'v') ? theme->pic256 : theme->silentpic256;
249 if (!pic)
250 return -1;
252 #ifdef CONFIG_PNG
253 if (!is_png(pic)) {
254 iprint(MSG_ERROR, "Unrecognized format of the verbose 8bpp background image.\n");
255 return -1;
258 /* We have a palette of 256 colors, but fbcon takes 16 of these for
259 * font colors in verbose mode, so we have 240 left for the picture */
260 if (mode != 's') {
261 i = PALETTE_COLORS;
262 img->cmap.start = 16;
263 } else {
264 i = 256;
265 img->cmap.start = 0;
268 img->cmap.transp = NULL;
269 img->cmap.red = malloc(i * 3 * 2);
271 if (!img->cmap.red) {
272 iprint(MSG_ERROR, "Failed to allocate memory for the image palette.\n");
273 return -4;
276 img->cmap.green = img->cmap.red + i;
277 img->cmap.blue = img->cmap.green + i;
278 img->cmap.len = i;
280 if (load_png(theme, pic, (u8**)&img->data, &img->cmap, &img->width, &img->height, 0)) {
281 iprint(MSG_ERROR, "Failed to load PNG file %s.\n", pic);
282 return -1;
284 #else
285 iprint(MSG_WARN, "This version of splashutils has been compiled without support for 8bpp modes.\n");
286 return -1;
287 #endif
288 /* Deal with 15, 16, 24 and 32bpp modes */
289 } else {
290 pic = (mode == 'v') ? theme->pic : theme->silentpic;
292 if (!pic)
293 return -2;
295 #ifdef CONFIG_PNG
296 if (is_png(pic)) {
297 i = load_png(theme, pic, (u8**)&img->data, NULL, &img->width, &img->height, 0);
298 } else
299 #endif
301 i = load_jpeg(pic, (u8**)&img->data, &img->width, &img->height);
304 if (i) {
305 iprint(MSG_ERROR, "Failed to load image %s.\n", pic);
306 return -1;
310 return 0;
313 int load_images(stheme_t *theme, char mode)
315 item *i;
317 if (load_bg_images(theme, mode))
318 return -1;
320 if (mode == 's') {
321 #ifdef CONFIG_PNG
322 for (i = theme->icons.head; i != NULL; i = i->next) {
323 icon_img *ii = (icon_img*) i->p;
324 ii->w = ii->h = 0;
326 if (!is_png(ii->filename)) {
327 iprint(MSG_ERROR, "Icon %s is not a PNG file.\n", ii->filename);
328 continue;
331 if (load_png(theme, ii->filename, &ii->picbuf, NULL, &ii->w, &ii->h, 1)) {
332 iprint(MSG_ERROR, "Failed to load icon %s.\n", ii->filename);
333 ii->picbuf = NULL;
334 ii->w = ii->h = 0;
335 continue;
338 #endif
341 return 0;