From: Egbert Eich Subject: Add bootsplash image scaler Patch-mainline: Never References: bnc#570082 The initrd most often contains a single fixed size boot image which is of the size of the VESA framebuffer used for boot. If the size of the framebuffer changes when KMS is initialized for example the boot splash is turned off. Takashi Iwai has provided a patch which allows to add multiple images to initrd so that the kernel can select the appropriate size. This is only feasible for mobile devices with a build in panel of a fixed resolution in which case one would have to ship two images at the most: the one of the VESA resolution at boot time and the one of the panel as used by KMS. The attached patch adds a boot splash scaler which allows the down scaling of bootsplash image which is bigger than the screen size. This way by supplying a single image large enough to accomodate the largest screens possible all resolutions can be derived from it. Acked-by: Michal Marek --- drivers/video/bootsplash/bootsplash.c | 858 +++++++++++++++++++++++++++++++--- drivers/video/bootsplash/decode-jpg.c | 4 drivers/video/bootsplash/render.c | 16 drivers/video/console/fbcon.h | 11 include/linux/fb.h | 3 5 files changed, 810 insertions(+), 82 deletions(-) --- a/drivers/video/bootsplash/bootsplash.c +++ b/drivers/video/bootsplash/bootsplash.c @@ -6,6 +6,7 @@ * Stefan Reinauer, , * Steffen Winterfeldt, , * Michael Schroeder + * 2009, 2010 Egbert Eich * * Ideas & SuSE screen work by Ken Wimer, * @@ -55,7 +56,9 @@ static unsigned char *jpg_errors[] = { "wrong marker", "no EOI", "bad tables", - "depth mismatch" + "depth mismatch", + "scale error", + "out of memory" }; static struct jpeg_decdata *decdata = 0; /* private decoder data */ @@ -64,7 +67,9 @@ static int splash_registered = 0; static int splash_usesilent = 0; /* shall we display the silentjpeg? */ int splash_default = 0xf01; -static int splash_check_jpeg(unsigned char *jpeg, int width, int height, int depth); +static int jpeg_get(unsigned char *buf, unsigned char *pic, int width, int height, int depth, + struct jpeg_decdata *decdata); +static int splash_look_for_jpeg(struct vc_data *vc, int width, int height); static int __init splash_setup(char *options) { @@ -120,7 +125,8 @@ static int boxextract(unsigned char *buf return 12; } -static void boxit(unsigned char *pic, int bytes, unsigned char *buf, int num, int percent, int overpaint, int octpp) +static void boxit(unsigned char *pic, int bytes, unsigned char *buf, int num, + int percent, int xoff, int yoff, int overpaint, int octpp) { int x, y, p, doblend, r, g, b, a, add; unsigned int i = 0; @@ -245,7 +251,7 @@ static void boxit(unsigned char *pic, in } add = (xs & 1); add ^= (add ^ y) & 1 ? 1 : 3; /* 2x2 ordered dithering */ - picp.ub = (pic + xs * octpp + y * bytes); + picp.ub = (pic + (xs + xoff) * octpp + (y + yoff) * bytes); for (x = xs; x <= xe; x++) { if (!(sti & 0x80000000)) { sti <<= 1; @@ -310,19 +316,172 @@ static void boxit(unsigned char *pic, in } } +static void box_offsets(unsigned char *buf, int num, + int screen_w, int screen_h, int pic_w, int pic_h, + int *x_off, int *y_off) +{ + int a, doblend; + int x_min = pic_w, x_max = 0; + int y_min = pic_h, y_max = 0; + unsigned int i = 0; + unsigned short data1[4]; + unsigned char cols1[16]; + unsigned short data2[4]; + unsigned char cols2[16]; + unsigned char *bufend; + unsigned int stin, stinn, stixs, stixe, stiys, stiye; + int xs, xe, ys, ye; + + SPLASH_DEBUG(); + + if ((screen_w == pic_w && screen_h == pic_h) || num == 0) + *x_off = *y_off = 0; + + bufend = buf + num * 12; + stin = 1; + stinn = 0; + stixs = stixe = 0; + stiys = stiye = 0; + + while(buf < bufend) { + doblend = 0; + buf += boxextract(buf, data1, cols1, &doblend); + if (data1[0] == 32767 && data1[1] == 32767) { + /* box stipple */ + if (stinn == 32) + continue; + if (stinn == 0) { + stixs = data1[2]; + stixe = data1[3]; + stiys = stiye = 0; + } else if (stinn == 4) { + stiys = data1[2]; + stiye = data1[3]; + } + stin = stinn; + continue; + } + stinn = 0; + if (data1[0] > 32767) + buf += boxextract(buf, data2, cols2, &doblend); + if (data1[0] == 32767 && data1[1] == 32766) { + /* box copy */ + i = 12 * (short)data1[3]; + doblend = 0; + i += boxextract(buf + i, data1, cols1, &doblend); + if (data1[0] > 32767) + boxextract(buf + i, data2, cols2, &doblend); + } + if (data1[0] == 32767) + continue; + if (data1[2] > 32767) { + data1[2] = ~data1[2]; + } + if (data1[3] > 32767) { + data1[3] = ~data1[3]; + } + if (data1[0] > 32767) { + data1[0] = ~data1[0]; + for (i = 0; i < 4; i++) + data1[i] = (data1[i] * (65536 - 1) + data2[i] * 1) >> 16; + } + *(unsigned int *)cols2 = *(unsigned int *)cols1; + a = cols2[3]; + if (a == 0 && !doblend) + continue; + + if (stixs >= 32768) { + xs = (stixs ^ 65535) + data1[0]; + xe = stixe ? stixe + data1[0] : data1[2]; + } else if (stixe >= 32768) { + xs = stixs ? data1[2] - stixs : data1[0]; + xe = data1[2] - (stixe ^ 65535); + } else { + xs = stixs; + xe = stixe ? stixe : data1[2]; + } + if (stiys >= 32768) { + ys = (stiys ^ 65535) + data1[1]; + ye = stiye ? stiye + data1[1] : data1[3]; + } else if (stiye >= 32768) { + ys = stiys ? data1[3] - stiys : data1[1]; + ye = data1[3] - (stiye ^ 65535); + } else { + ys = stiys; + ye = stiye ? stiye : data1[3]; + } + if (xs < data1[0]) + xs = data1[0]; + if (xe > data1[2]) + xe = data1[2]; + if (ys < data1[1]) + ys = data1[1]; + if (ye > data1[3]) + ye = data1[3]; + + if (xs < x_min) + x_min = xs; + if (xe > x_max) + x_max = xe; + if (ys < y_min) + y_min = ys; + if (ye > y_max) + y_max = ye; + } + { + int x_center = (x_min + x_max) / 2; + int y_center = (y_min + y_max) / 2; + + if (screen_w == pic_w) + *x_off = 0; + else { + if (x_center < (pic_w + pic_w / 10) >> 1 && x_center > (pic_w - pic_w / 10) >> 1) + *x_off = (screen_w - pic_w) >> 1; + else { + int x = x_center * screen_w / pic_w; + *x_off = x - x_center; + if (x_min + x_off > 0) + *x_off = 0; + if (x_max + *x_off > screen_w) + *x_off = screen_w - pic_w; + } + } + if (screen_h == pic_h) + *y_off = 0; + else { + if (y_center < (pic_h + pic_h / 10) >> 1 && y_center > (pic_h - pic_h / 10) >> 1) + *y_off = (screen_h - pic_h) >> 1; + else { + int x = y_center * screen_h / pic_h; + *y_off = x - y_center; + if (y_min + y_off > 0) + *y_off = 0; + if (y_max + *x_off > screen_h) + *y_off = screen_h - pic_h; + } + } + } +} + static int splash_check_jpeg(unsigned char *jpeg, int width, int height, int depth) { int size, err; unsigned char *mem; - size = ((width + 15) & ~15) * ((height + 15) & ~15) * (depth >> 3); + size = ((width + 15) & ~15) * ((height + 15) & ~15) * ((depth + 1) >> 3); mem = vmalloc(size); if (!mem) { printk(KERN_INFO "bootsplash: no memory for decoded picture.\n"); return -1; } - if (!decdata) - decdata = vmalloc(sizeof(*decdata)); + if (!decdata) { + decdata = vmalloc(sizeof(*decdata)); + if (!decdata) { + printk(KERN_INFO "bootsplash: not enough memory.\n"); + vfree(mem); + return -1; + } + } if ((err = jpeg_decode(jpeg, mem, ((width + 15) & ~15), ((height + 15) & ~15), depth, decdata))) printk(KERN_INFO "bootsplash: error while decompressing picture: %s (%d)\n",jpg_errors[err - 1], err); vfree(mem); @@ -337,6 +496,7 @@ static void splash_free(struct vc_data * for (sd = vc->vc_splash_data; sd; sd = next) { next = sd->next; vfree(sd->splash_sboxes); + vfree(sd->splash_pic); vfree(sd); } vc->vc_splash_data = 0; @@ -432,6 +592,11 @@ static void splash_pivot_current(struct state = sd->splash_state; percent = sd->splash_percent; silent = sd->splash_dosilent; + vfree(sd->splash_pic); + sd->splash_pic_size = 0; + sd->splash_pic = NULL; + sd->splash_text_wi = sd->splash_jpg_text_wi; + sd->splash_text_he = sd->splash_jpg_text_he; for (; sd->next; sd = sd->next) { if (sd->next == new) { sd->next = new->next; @@ -441,6 +606,17 @@ static void splash_pivot_current(struct new->splash_state = state; new->splash_percent = percent; new->splash_dosilent = silent; + new->splash_text_wi = new->splash_jpg_text_wi; + new->splash_text_he = new->splash_jpg_text_he; + + vfree(new->splash_pic); + new->splash_pic = NULL; + new->splash_pic_size = 0; + + new->splash_boxes_xoff = 0; + new->splash_boxes_yoff = 0; + new->splash_sboxes_xoff = 0; + new->splash_sboxes_yoff = 0; return; } } @@ -459,7 +635,7 @@ static int splash_getraw(unsigned char * int palcnt; int i, len; const int *offsets; - struct vc_data *vc; + struct vc_data *vc = NULL; struct fb_info *info; struct splash_data *sd; struct splash_data *splash_found = NULL; @@ -489,7 +665,16 @@ static int splash_getraw(unsigned char * vc_allocate(unit); } vc = vc_cons[unit].d; + if (!vc) + continue; + info = registered_fb[(int)con2fb_map[unit]]; + + if (info->fbops->fb_imageblit != cfb_imageblit) { + splash_free(vc, info); + printk(KERN_ERR "bootsplash: found, but framebuffer can't handle it!\n"); + return -1; + } width = info->var.xres; height = info->var.yres; splash_size = splash_geti(ndata, SPLASH_OFF_SIZE); @@ -539,6 +724,9 @@ static int splash_getraw(unsigned char * } if (update) *update = up; + vfree(sd->splash_pic); + sd->splash_pic = NULL; + sd->splash_pic_size = 0; } return unit; } @@ -579,6 +767,12 @@ static int splash_getraw(unsigned char * memset(sd, 0, sizeof(*sd)); jpeg_get_size(ndata + len + boxcnt * 12 + palcnt, &sd->splash_width, &sd->splash_height); + if (splash_check_jpeg(ndata + len + boxcnt * 12 + palcnt, + sd->splash_width, sd->splash_height, info->var.bits_per_pixel)) { + ndata += len + splash_size - 1; + vfree(sd); + continue; + } if (silentsize) { sd->splash_silentjpeg = vmalloc(silentsize); if (sd->splash_silentjpeg) { @@ -596,6 +790,8 @@ static int splash_getraw(unsigned char * sd->splash_text_yo = splash_gets(ndata, SPLASH_OFF_YO); sd->splash_text_wi = splash_gets(ndata, SPLASH_OFF_WI); sd->splash_text_he = splash_gets(ndata, SPLASH_OFF_HE); + sd->splash_pic = NULL; + sd->splash_pic_size = 0; sd->splash_percent = oldpercent == -1 ? splash_gets(ndata, SPLASH_OFF_PERCENT) : oldpercent; if (version == 1) { sd->splash_text_xo *= 8; @@ -606,6 +802,9 @@ static int splash_getraw(unsigned char * sd->splash_fg_color = (splash_default >> 4) & 0x0f; sd->splash_state = splash_default & 1; } + sd->splash_jpg_text_wi = sd->splash_text_wi; + sd->splash_jpg_text_he = sd->splash_text_he; + /* fake penguin box for older formats */ if (version == 1) boxcnt = splash_mkpenguin(sd, sd->splash_text_xo + 10, sd->splash_text_yo + 10, sd->splash_text_wi - 20, sd->splash_text_he - 20, 0xf0, 0xf0, 0xf0); @@ -627,15 +826,6 @@ static int splash_getraw(unsigned char * ndata += len + splash_size - 1; continue; } - if (splash_check_jpeg(ndata + len + boxcnt * 12 + palcnt, width, height, info->var.bits_per_pixel)) { - ndata += len + splash_size - 1; - continue; - } - if (!vc_cons[unit].d || info->fbops->fb_imageblit != cfb_imageblit) { - splash_free(vc, info); - printk(KERN_ERR "bootsplash: found, but framebuffer can't handle it!\n"); - return -1; - } printk(KERN_INFO "bootsplash: ...found (%dx%d, %d bytes, v%d).\n", width, height, splash_size, version); if (version == 1) { printk(KERN_WARNING "bootsplash: Using deprecated v1 header. Updating your splash utility recommended.\n"); @@ -649,6 +839,16 @@ static int splash_getraw(unsigned char * if (splash_found) { splash_pivot_current(vc, splash_found); return unit_found; + } else { + vc = vc_cons[0].d; + if (vc) { + info = registered_fb[(int)con2fb_map[0]]; + width = info->var.xres; + height = info->var.yres; + if (!splash_look_for_jpeg(vc, width, height)) + return -1; + return 0; + } } printk(KERN_ERR "bootsplash: ...no good signature found.\n"); @@ -715,27 +915,71 @@ int splash_verbose(void) return 0; } -static void splash_off(struct fb_info *info) +static void splash_off(struct vc_data *vc,struct fb_info *info) { + int rows = info->var.xres / vc->vc_font.width; + int cols = info->var.yres / vc->vc_font.height; SPLASH_DEBUG(); + info->splash_data = 0; - if (info->splash_pic) - vfree(info->splash_pic); - info->splash_pic = 0; - info->splash_pic_size = 0; + if (rows != vc->vc_rows || cols != vc->vc_cols) + vc_resize(vc, rows, cols); + if (vc->vc_def_color != 0x07) + con_remap_def_color(vc, 0x07); } /* look for the splash with the matching size and set it as the current */ static int splash_look_for_jpeg(struct vc_data *vc, int width, int height) { - struct splash_data *sd; + struct splash_data *sd, *found = NULL; + int found_delta_x = INT_MAX, found_delta_y = INT_MAX; for (sd = vc->vc_splash_data; sd; sd = sd->next) { - if (sd->splash_width == width && sd->splash_height == height) { - splash_pivot_current(vc, sd); - return 0; + int delta_x = abs(sd->splash_width - width) * height; + int delta_y = abs(sd->splash_height - height) * width; + if (!found || (found_delta_x + found_delta_y > delta_x + delta_y)) { + found = sd; + found_delta_x = delta_x; + found_delta_y = delta_y; } } + + if (found) { + SPLASH_DEBUG("bootsplash: scalable image found (%dx%d scaled to %dx%d).", + found->splash_width, found->splash_height, width, height); + + splash_pivot_current(vc, found); + + /* textarea margins are constant independent from image size */ + if (found->splash_height != height) + found->splash_text_he = height - (found->splash_height - found->splash_jpg_text_he); + else + found->splash_text_he = found->splash_jpg_text_he; + if (found->splash_width != width) + found->splash_text_wi = width - (found->splash_width - found->splash_jpg_text_wi); + else + found->splash_text_wi = found->splash_jpg_text_wi; + + if (found->splash_width != width || found->splash_height != height) { + box_offsets(found->splash_boxes, found->splash_boxcount, + width, height, found->splash_width, found->splash_height, + &found->splash_boxes_xoff, &found->splash_boxes_yoff); + SPLASH_DEBUG("bootsplash: offsets for boxes: x=%d y=%d", + found->splash_boxes_xoff,found->splash_boxes_yoff); + + if (found->splash_sboxes) { + box_offsets(found->splash_sboxes, found->splash_sboxcount, + width, height, found->splash_width, found->splash_height, + &found->splash_sboxes_xoff, &found->splash_sboxes_yoff); + SPLASH_DEBUG("bootsplash: offsets sboxes: x=%d y=%d", + found->splash_sboxes_xoff,found->splash_sboxes_yoff); + } + } else { + found->splash_sboxes_xoff = 0; + found->splash_sboxes_yoff = 0; + } + return 0; + } return -1; } @@ -743,13 +987,14 @@ int splash_prepare(struct vc_data *vc, s { int err; int width, height, depth, octpp, size, sbytes; + int pic_update = 0; SPLASH_DEBUG("vc_num: %i", vc->vc_num); if (!vc->vc_splash_data || !vc->vc_splash_data->splash_state) { if (decdata) vfree(decdata); decdata = 0; - splash_off(info); + splash_off(vc,info); return -1; } @@ -759,52 +1004,62 @@ int splash_prepare(struct vc_data *vc, s octpp = (depth + 1) >> 3; if (depth == 24 || depth < 15) { /* Other targets might need fixing */ - splash_off(info); + splash_off(vc,info); return -2; } if (splash_look_for_jpeg(vc, width, height) < 0) { printk(KERN_INFO "bootsplash: no matching splash %dx%d\n", width, height); - splash_off(info); + splash_off(vc,info); return -2; } sbytes = ((width + 15) & ~15) * octpp; size = sbytes * ((height + 15) & ~15); - if (size != info->splash_pic_size) { - vfree(info->splash_pic); - info->splash_pic = NULL; - } - if (!info->splash_pic) - info->splash_pic = vmalloc(size); - if (!info->splash_pic) { + if (size != vc->vc_splash_data->splash_pic_size) { + vfree(vc->vc_splash_data->splash_pic); + vc->vc_splash_data->splash_pic = NULL; + } + if (!vc->vc_splash_data->splash_pic) { + vc->vc_splash_data->splash_pic = vmalloc(size); + pic_update = 1; + } + if (!vc->vc_splash_data->splash_pic) { printk(KERN_INFO "bootsplash: not enough memory.\n"); - splash_off(info); + splash_off(vc,info); return -3; } - if (!decdata) + if (!decdata) { decdata = vmalloc(sizeof(*decdata)); + if (!decdata) { + printk(KERN_INFO "bootsplash: not enough memory.\n"); + splash_off(vc,info); + return -3; + } + } if (vc->vc_splash_data->splash_silentjpeg && vc->vc_splash_data->splash_dosilent) { - /* fill area after framebuffer with other jpeg */ - if ((err = jpeg_decode(vc->vc_splash_data->splash_silentjpeg, info->splash_pic, - ((width + 15) & ~15), ((height + 15) & ~15), depth, decdata))) { + pic_update = 1; + if ((err = jpeg_get(vc->vc_splash_data->splash_silentjpeg, vc->vc_splash_data->splash_pic, + width, height, depth, decdata))) { printk(KERN_INFO "bootsplash: error while decompressing silent picture: %s (%d)\n", jpg_errors[err - 1], err); vc->vc_splash_data->splash_dosilent = 0; } else { if (vc->vc_splash_data->splash_sboxcount) - boxit(info->splash_pic, + boxit(vc->vc_splash_data->splash_pic, sbytes, vc->vc_splash_data->splash_sboxes, vc->vc_splash_data->splash_sboxcount, vc->vc_splash_data->splash_percent, + vc->vc_splash_data->splash_sboxes_xoff, + vc->vc_splash_data->splash_sboxes_yoff, 0, octpp); splashcopy(info->screen_base, - info->splash_pic, + vc->vc_splash_data->splash_pic, info->var.yres, info->var.xres, info->fix.line_length, sbytes, @@ -813,27 +1068,43 @@ int splash_prepare(struct vc_data *vc, s } else vc->vc_splash_data->splash_dosilent = 0; - if ((err = jpeg_decode(vc->vc_splash_data->splash_jpeg, info->splash_pic, - ((width + 15) & ~15), ((height + 15) & ~15), depth, decdata))) { - printk(KERN_INFO "bootsplash: error while decompressing picture: %s (%d) .\n", - jpg_errors[err - 1], err); - splash_off(info); - return -4; + if (pic_update) { + if ((err = jpeg_get(vc->vc_splash_data->splash_jpeg, vc->vc_splash_data->splash_pic, + width, height, depth, decdata))) { + printk(KERN_INFO "bootsplash: error while decompressing picture: %s (%d) .\n", + jpg_errors[err - 1], err); + splash_off(vc,info); + return -4; + } } - info->splash_pic_size = size; - info->splash_pic_stride = sbytes; + + vc->vc_splash_data->splash_pic_size = size; + vc->vc_splash_data->splash_pic_stride = sbytes; + if (vc->vc_splash_data->splash_boxcount) - boxit(info->splash_pic, + boxit(vc->vc_splash_data->splash_pic, sbytes, vc->vc_splash_data->splash_boxes, vc->vc_splash_data->splash_boxcount, vc->vc_splash_data->splash_percent, + vc->vc_splash_data->splash_boxes_xoff, + vc->vc_splash_data->splash_boxes_yoff, 0, octpp); - if (vc->vc_splash_data->splash_state) + if (vc->vc_splash_data->splash_state) { + int cols = vc->vc_splash_data->splash_text_wi / vc->vc_font.width; + int rows = vc->vc_splash_data->splash_text_he / vc->vc_font.height; + int color = vc->vc_splash_data->splash_color << 4 | vc->vc_splash_data->splash_fg_color; info->splash_data = vc->vc_splash_data; - else { - splash_off(info); + + /* vc_resize also calls con_switch which resets yscroll */ + if (rows != vc->vc_rows || cols != vc->vc_cols) + vc_resize(vc, cols, rows); + if (vc->vc_def_color != color) + con_remap_def_color(vc, color); + + } else { + splash_off(vc,info); return -5; } return 0; @@ -856,12 +1127,16 @@ static struct proc_dir_entry *proc_splas static int splash_recolor(struct vc_data *vc) { + int color; + SPLASH_DEBUG(); if (!vc->vc_splash_data) return -1; if (!vc->vc_splash_data->splash_state) return 0; - con_remap_def_color(vc, vc->vc_splash_data->splash_color << 4 | vc->vc_splash_data->splash_fg_color); + color = vc->vc_splash_data->splash_color << 4 | vc->vc_splash_data->splash_fg_color; + if (vc->vc_def_color != color) + con_remap_def_color(vc, color); if (fg_console == vc->vc_num) { update_region(vc, vc->vc_origin + vc->vc_size_row * vc->vc_top, @@ -884,10 +1159,6 @@ static int splash_status(struct vc_data splash_prepare(vc, info); if (vc->vc_splash_data && vc->vc_splash_data->splash_state) { if (info->splash_data) { - con_remap_def_color(vc, info->splash_data->splash_color << 4 | info->splash_data->splash_fg_color); - /* vc_resize also calls con_switch which resets yscroll */ - vc_resize(vc, info->splash_data->splash_text_wi / vc->vc_font.width, - info->splash_data->splash_text_he / vc->vc_font.height); if (fg_console == vc->vc_num) { update_region(vc, vc->vc_origin + vc->vc_size_row * vc->vc_top, @@ -895,11 +1166,9 @@ static int splash_status(struct vc_data splash_clear_margins(vc, info, 0); } } - } else { - /* Switch bootsplash off */ - con_remap_def_color(vc, 0x07); - vc_resize(vc, info->var.xres / vc->vc_font.width, info->var.yres / vc->vc_font.height); - } + } else + splash_off(vc,info); + return 0; } @@ -956,10 +1225,9 @@ void splash_set_percent(struct vc_data * || pe < oldpe) { if (splash_hasinter(vc->vc_splash_data->splash_boxes, vc->vc_splash_data->splash_boxcount)) { - splash_status(vc); - } - else - splash_prepare(vc, info); + splash_status(vc); + } else + splash_prepare(vc, info); } else { int octpp = (info->var.bits_per_pixel + 1) >> 3; if (info->splash_data) { @@ -970,6 +1238,8 @@ void splash_set_percent(struct vc_data * info->splash_data->splash_sboxes, info->splash_data->splash_sboxcount, info->splash_data->splash_percent, + info->splash_data->splash_sboxes_xoff, + info->splash_data->splash_sboxes_yoff, 1, octpp); #if 0 @@ -979,6 +1249,8 @@ void splash_set_percent(struct vc_data * info->splash_data->splash_boxes, info->splash_data->splash_boxcount, info->splash_data->splash_percent, + info->splash_data->splash_boxes_xoff, + info->splash_data->splash_boxes_yoff, 1, octpp); #endif @@ -1100,6 +1372,8 @@ static int splash_write_proc(struct file info->splash_data->splash_sboxes, info->splash_data->splash_sboxcount, info->splash_data->splash_percent, + info->splash_data->splash_sboxes_xoff, + info->splash_data->splash_sboxes_yoff, 1, octpp); } else if ((up & 1) != 0) { @@ -1108,6 +1382,8 @@ static int splash_write_proc(struct file info->splash_data->splash_boxes, info->splash_data->splash_boxcount, info->splash_data->splash_percent, + info->splash_data->splash_boxes_xoff, + info->splash_data->splash_boxes_yoff, 1, octpp); } @@ -1226,3 +1502,447 @@ void splash_init(void) return; } +#define SPLASH_ALIGN 15 + +static u32 *do_coefficients(u32 from, u32 to, u32 *shift) +{ + u32 *coefficients; + u32 left = to; + int n = 1; + u32 upper = 31; + int col_cnt = 0; + int row_cnt = 0; + int m; + u32 rnd = from >> 1; + + if (from > to) { + left = to; + rnd = from >> 1; + + while (upper > 0) { + if ((1 << upper) & from) + break; + upper--; + } + upper++; + + *shift = 32 - 8 - 1 - upper; + + coefficients = vmalloc(sizeof (u32) * (from / to + 2) * from + 1); + if (!coefficients) + return NULL; + + n = 1; + while (1) { + u32 sum = left; + col_cnt = 0; + m = n++; + while (sum < from) { + coefficients[n++] = ((left << *shift) + rnd) / from; + col_cnt++; + left = to; + sum += left; + } + left = sum - from; + coefficients[n++] = (((to - left) << *shift) + rnd) / from; + col_cnt++; + coefficients[m] = col_cnt; + row_cnt++; + if (!left) { + coefficients[0] = row_cnt; + return coefficients; + } + } + } else { + left = 0; + rnd = to >> 1; + + while (upper > 0) { + if ((1 << upper) & to) + break; + upper--; + } + upper++; + + *shift = 32 - 8 - 1 - upper; + + coefficients = vmalloc(sizeof (u32) * 3 * from + 1); + if (!coefficients) + return NULL; + + while (1) { + u32 diff; + u32 sum = left; + col_cnt = 0; + row_cnt++; + while (sum < to) { + col_cnt++; + sum += from; + } + left = sum - to; + diff = from - left; + if (!left) { + coefficients[n] = col_cnt; + coefficients[0] = row_cnt; + return coefficients; + } + coefficients[n++] = col_cnt - 1; + coefficients[n++] = ((diff << *shift) + rnd) / from; + coefficients[n++] = ((left << *shift) + rnd) / from; + } + } +} + + +struct pixel +{ + u32 red; + u32 green; + u32 blue; +}; + +#define put_pixel(pix, buf, depth) \ + switch (depth) { \ + case 15: \ + *(u16 *)(buf) = (u16)((pix).red << 10 | (pix).green << 5 | (pix).blue); \ + (buf) += 2; \ + break; \ + case 16: \ + *(u16 *)(buf) = (u16)((pix).red << 11 | (pix).green << 5 | (pix).blue); \ + (buf) += 2; \ + break; \ + case 24: \ + *(u16 *)(buf) = (u16)((pix).red << 8 | (pix).green); \ + buf += 2; \ + *((buf)++) = (pix).blue; \ + break; \ + case 32: \ + *(u32 *)(buf) = (u32)((pix).red << 16 | (pix).green << 8 | (pix).blue); \ + (buf) += 4; \ + break; \ +} + +#define get_pixel(pix, buf, depth) \ + switch (depth) { \ +case 15: \ + (pix).red = ((*(u16 *)(buf)) >> 10) & 0x1f; \ + (pix).green = ((*(u16 *)(buf)) >> 5) & 0x1f; \ + (pix).blue = (*(u16 *)(buf)) & 0x1f; \ + (buf) += 2; \ + break; \ +case 16: \ + (pix).red = ((*(u16 *)(buf)) >> 11) & 0x1f; \ + (pix).green = ((*(u16 *)(buf)) >> 5) & 0x3f; \ + (pix).blue = (*(u16 *)(buf)) & 0x1f; \ + (buf) += 2; \ + break; \ +case 24: \ + (pix).blue = *(((buf))++); \ + (pix).green = *(((buf))++); \ + (pix).red = *(((buf))++); \ + break; \ +case 32: \ + (pix).blue = *(((buf))++); \ + (pix).green = *(((buf))++); \ + (pix).red = *(((buf))++); \ + (buf)++; \ + break; \ +} + +static inline void +scale_x_down(int depth, int src_w, unsigned char **src_p, u32 *x_coeff, u32 x_shift, u32 y_coeff, struct pixel *row_buffer) +{ + u32 curr_x_coeff = 1; + struct pixel curr_pixel, tmp_pixel; + u32 x_array_size = x_coeff[0]; + int x_column_num; + int i; + int l,m; + int k = 0; + u32 rnd = (1 << (x_shift - 1)); + + for (i = 0; i < src_w; ) { + curr_x_coeff = 1; + get_pixel(tmp_pixel, *src_p, depth); + i++; + for (l = 0; l < x_array_size; l++) { + x_column_num = x_coeff[curr_x_coeff++]; + curr_pixel.red = curr_pixel.green = curr_pixel.blue = 0; + for (m = 0; m < x_column_num - 1; m++) { + curr_pixel.red += tmp_pixel.red * x_coeff[curr_x_coeff]; + curr_pixel.green += tmp_pixel.green * x_coeff[curr_x_coeff]; + curr_pixel.blue += tmp_pixel.blue * x_coeff[curr_x_coeff]; + curr_x_coeff++; + get_pixel(tmp_pixel, *src_p, depth); + i++; + } + curr_pixel.red += tmp_pixel.red * x_coeff[curr_x_coeff]; + curr_pixel.green += tmp_pixel.green * x_coeff[curr_x_coeff]; + curr_pixel.blue += tmp_pixel.blue * x_coeff[curr_x_coeff]; + curr_x_coeff++; + curr_pixel.red = (curr_pixel.red + rnd) >> x_shift; + curr_pixel.green = (curr_pixel.green + rnd) >> x_shift; + curr_pixel.blue = (curr_pixel.blue + rnd) >> x_shift; + row_buffer[k].red += curr_pixel.red * y_coeff; + row_buffer[k].green += curr_pixel.green * y_coeff; + row_buffer[k].blue += curr_pixel.blue * y_coeff; + k++; + } + } +} + +static inline void +scale_x_up(int depth, int src_w, unsigned char **src_p, u32 *x_coeff, u32 x_shift, u32 y_coeff, struct pixel *row_buffer) +{ + u32 curr_x_coeff = 1; + struct pixel curr_pixel, tmp_pixel; + u32 x_array_size = x_coeff[0]; + int x_column_num; + int i; + int l,m; + int k = 0; + u32 rnd = (1 << (x_shift - 1)); + + for (i = 0; i < src_w;) { + curr_x_coeff = 1; + get_pixel(tmp_pixel, *src_p, depth); + i++; + for (l = 0; l < x_array_size - 1; l++) { + x_column_num = x_coeff[curr_x_coeff++]; + for (m = 0; m < x_column_num; m++) { + row_buffer[k].red += tmp_pixel.red * y_coeff; + row_buffer[k].green += tmp_pixel.green * y_coeff; + row_buffer[k].blue += tmp_pixel.blue * y_coeff; + k++; + } + curr_pixel.red = tmp_pixel.red * x_coeff[curr_x_coeff]; + curr_pixel.green = tmp_pixel.green * x_coeff[curr_x_coeff]; + curr_pixel.blue = tmp_pixel.blue * x_coeff[curr_x_coeff]; + curr_x_coeff++; + get_pixel(tmp_pixel, *src_p, depth); + i++; + row_buffer[k].red += ((curr_pixel.red + tmp_pixel.red * x_coeff[curr_x_coeff] + rnd) >> x_shift) * y_coeff; + row_buffer[k].green += ((curr_pixel.green + tmp_pixel.green * x_coeff[curr_x_coeff] + rnd) >> x_shift) * y_coeff; + row_buffer[k].blue += ((curr_pixel.blue + tmp_pixel.blue * x_coeff[curr_x_coeff] + rnd) >> x_shift) * y_coeff; + k++; + curr_x_coeff++; + } + for (m = 0; m < x_coeff[curr_x_coeff]; m++) { + row_buffer[k].red += tmp_pixel.red * y_coeff; + row_buffer[k].green += tmp_pixel.green * y_coeff; + row_buffer[k].blue += tmp_pixel.blue * y_coeff; + k++; + } + } +} + +static int scale_y_down(unsigned char *src, unsigned char *dst, int depth, int src_w, int src_h, int dst_w, int dst_h) +{ + int octpp = (depth + 1) >> 3; + int src_x_bytes = octpp * ((src_w + SPLASH_ALIGN) & ~SPLASH_ALIGN); + int dst_x_bytes = octpp * ((dst_w + SPLASH_ALIGN) & ~SPLASH_ALIGN); + int j; + struct pixel *row_buffer; + u32 x_shift, y_shift; + u32 *x_coeff; + u32 *y_coeff; + u32 curr_y_coeff = 1; + unsigned char *src_p; + unsigned char *src_p_line = src; + char *dst_p_line; + int r,s; + int y_array_rows; + int y_column_num; + int k; + u32 rnd; + int xup; + + row_buffer = (struct pixel *)vmalloc(sizeof(struct pixel) * (dst_w + 1)); + x_coeff = do_coefficients(src_w, dst_w, &x_shift); + y_coeff = do_coefficients(src_h, dst_h, &y_shift); + if (!row_buffer || !x_coeff || !y_coeff) { + vfree(row_buffer); + vfree(x_coeff); + vfree(y_coeff); + return -ENOMEM; + } + y_array_rows = y_coeff[0]; + rnd = (1 << (y_shift - 1)); + xup = (src_w <= dst_w) ? 1 : 0; + + dst_p_line = dst; + + for (j = 0; j < src_h;) { + curr_y_coeff = 1; + for (r = 0; r < y_array_rows; r++) { + y_column_num = y_coeff[curr_y_coeff++]; + for (k = 0; k < dst_w + 1; k++) + row_buffer[k].red = row_buffer[k].green = row_buffer[k].blue = 0; + src_p = src_p_line; + if (xup) + scale_x_up(depth, src_w, &src_p, x_coeff, x_shift, y_coeff[curr_y_coeff], row_buffer ); + else + scale_x_down(depth, src_w, &src_p, x_coeff, x_shift, y_coeff[curr_y_coeff], row_buffer ); + curr_y_coeff++; + for (s = 1; s < y_column_num; s++) { + src_p = src_p_line = src_p_line + src_x_bytes; + j++; + if (xup) + scale_x_up(depth, src_w, &src_p, x_coeff, x_shift, y_coeff[curr_y_coeff], row_buffer ); + else + scale_x_down(depth, src_w, &src_p, x_coeff, x_shift, y_coeff[curr_y_coeff], row_buffer ); + curr_y_coeff++; + } + for (k = 0; k < dst_w; k++) { + row_buffer[k].red = ( row_buffer[k].red + rnd) >> y_shift; + row_buffer[k].green = (row_buffer[k].green + rnd) >> y_shift; + row_buffer[k].blue = (row_buffer[k].blue + rnd) >> y_shift; + put_pixel (row_buffer[k], dst, depth); + } + dst = dst_p_line = dst_p_line + dst_x_bytes; + } + src_p_line = src_p_line + src_x_bytes; + j++; + } + vfree(row_buffer); + vfree(x_coeff); + vfree(y_coeff); + return 0; +} + +static int scale_y_up(unsigned char *src, unsigned char *dst, int depth, int src_w, int src_h, int dst_w, int dst_h) +{ + int octpp = (depth + 1) >> 3; + int src_x_bytes = octpp * ((src_w + SPLASH_ALIGN) & ~SPLASH_ALIGN); + int dst_x_bytes = octpp * ((dst_w + SPLASH_ALIGN) & ~SPLASH_ALIGN); + int j; + u32 x_shift, y_shift; + u32 *x_coeff; + u32 *y_coeff; + struct pixel *row_buf_list[2]; + struct pixel *row_buffer; + u32 curr_y_coeff = 1; + unsigned char *src_p; + unsigned char *src_p_line = src; + char *dst_p_line; + int r,s; + int y_array_rows; + int y_column_num; + int k; + u32 rnd; + int bi; + int xup; + int writes; + + x_coeff = do_coefficients(src_w, dst_w, &x_shift); + y_coeff = do_coefficients(src_h, dst_h, &y_shift); + row_buf_list[0] = (struct pixel *)vmalloc(2 * sizeof(struct pixel) * (dst_w + 1)); + if (!row_buf_list[0] || !x_coeff || !y_coeff) { + vfree(row_buf_list[0]); + vfree(x_coeff); + vfree(y_coeff); + return -ENOMEM; + } + row_buf_list[1] = row_buf_list[0] + (dst_w + 1); + + y_array_rows = y_coeff[0]; + rnd = (1 << (y_shift - 1)); + bi = 1; + xup = (src_w <= dst_w) ? 1 : 0; + writes = 0; + + dst_p_line = dst; + src_p = src_p_line; + + row_buffer = row_buf_list[0]; + + for (j = 0; j < src_h;) { + memset(row_buf_list[0], 0, 2 * sizeof(struct pixel) * (dst_w + 1)); + curr_y_coeff = 1; + if (xup) + scale_x_up(depth, src_w, &src_p, x_coeff, x_shift, 1, row_buffer ); + else + scale_x_down(depth, src_w, &src_p, x_coeff, x_shift, 1, row_buffer ); + src_p = src_p_line = src_p_line + src_x_bytes; + j++; + for (r = 0; r < y_array_rows - 1; r++) { + struct pixel *old_row_buffer = row_buffer; + u32 prev_y_coeff_val; + + y_column_num = y_coeff[curr_y_coeff]; + for (s = 0; s < y_column_num; s++) { + for (k = 0; k < dst_w; k++) + put_pixel(row_buffer[k], dst, depth); + dst = dst_p_line = dst_p_line + dst_x_bytes; + writes++; + } + curr_y_coeff++; + row_buffer = row_buf_list[(bi++) % 2]; + prev_y_coeff_val = y_coeff[curr_y_coeff++]; + if (xup) + scale_x_up(depth, src_w, &src_p, x_coeff, x_shift, 1, row_buffer ); + else + scale_x_down(depth, src_w, &src_p, x_coeff, x_shift, 1, row_buffer ); + src_p = src_p_line = src_p_line + src_x_bytes; + j++; + for (k = 0; k > y_shift; + pix.green = (old_row_buffer[k].green * prev_y_coeff_val + row_buffer[k].green * y_coeff[curr_y_coeff] + rnd) >> y_shift; + pix.blue = (old_row_buffer[k].blue * prev_y_coeff_val + row_buffer[k].blue * y_coeff[curr_y_coeff] + rnd) >> y_shift; + old_row_buffer[k].red = old_row_buffer[k].green = old_row_buffer[k].blue = 0; + put_pixel(pix, dst, depth); + } + dst = dst_p_line = dst_p_line + dst_x_bytes; + writes++; + curr_y_coeff++; + } + for (r = 0; r < y_coeff[curr_y_coeff]; r++) { + for (k = 0; k < dst_w; k++) { + put_pixel(row_buffer[k], dst, depth); + } + dst = dst_p_line = dst_p_line + dst_x_bytes; + writes++; + } + } + vfree(row_buf_list[0]); + vfree(x_coeff); + vfree(y_coeff); + + return 0; +} + +static int jpeg_get(unsigned char *buf, unsigned char *pic, + int width, int height, int depth, + struct jpeg_decdata *decdata) +{ + int my_width, my_height; + int err; + + jpeg_get_size(buf, &my_width, &my_height); + + if (my_height != height || my_width != width) { + int my_size = ((my_width + 15) & ~15) + * ((my_height + 15) & ~15) * ((depth + 1) >> 3); + unsigned char *mem = vmalloc(my_size); + if (!mem) + return 17; + if ((err = jpeg_decode(buf, mem, ((my_width + 15) & ~15), + ((my_height + 15) & ~15), depth, decdata))) { + vfree(mem); + return err; + } + printk(KERN_INFO "bootsplash: scaling image from %dx%d to %dx%d\n", my_width, my_height, width, height); + if (my_height <= height) + err = scale_y_up(mem, pic, depth, my_width, my_height, ((width + 15) & ~15), ((height + 15) & ~15)); + else + err = scale_y_down(mem, pic, depth, my_width, my_height, ((width + 15) & ~15), ((height + 15) & ~15)); + vfree(mem); + if (err < 0) + return 17; + } else { + if ((err = jpeg_decode(buf, pic, ((width + 15) & ~15), ((height + 15) & ~15), depth, decdata))) + return err; + } + return 0; +} --- a/drivers/video/bootsplash/decode-jpg.c +++ b/drivers/video/bootsplash/decode-jpg.c @@ -888,9 +888,9 @@ PREC q[][64]; #define PIC_32(yin, xin, p, xout) \ ( \ y = outy[(yin) * 8 + xin], \ - STORECLAMP(p[(xout) * 4 + 0], y + cr), \ + STORECLAMP(p[(xout) * 4 + 0], y + cb), \ STORECLAMP(p[(xout) * 4 + 1], y - cg), \ - STORECLAMP(p[(xout) * 4 + 2], y + cb), \ + STORECLAMP(p[(xout) * 4 + 2], y + cr), \ p[(xout) * 4 + 3] = 0 \ ) --- a/drivers/video/bootsplash/render.c +++ b/drivers/video/bootsplash/render.c @@ -45,7 +45,7 @@ void splash_putcs(struct vc_data *vc, st transparent = sd->splash_color == bg_color; xpos = xpos * vc->vc_font.width + sd->splash_text_xo; ypos = ypos * vc->vc_font.height + sd->splash_text_yo; - splashsrc.ub = (u8 *)(info->splash_pic + ypos * info->splash_pic_stride + xpos * octpp); + splashsrc.ub = (u8 *)(sd->splash_pic + ypos * sd->splash_pic_stride + xpos * octpp); dst.ub = (u8 *)(info->screen_base + ypos * info->fix.line_length + xpos * octpp); fgx = ((u32 *)info->pseudo_palette)[fg_color]; if (transparent && sd->splash_color == 15) { @@ -109,10 +109,10 @@ void splash_putcs(struct vc_data *vc, st } } dst.ub += info->fix.line_length - vc->vc_font.width * octpp; - splashsrc.ub += info->splash_pic_stride - vc->vc_font.width * octpp; + splashsrc.ub += sd->splash_pic_stride - vc->vc_font.width * octpp; } dst.ub -= info->fix.line_length * vc->vc_font.height - vc->vc_font.width * octpp; - splashsrc.ub -= info->splash_pic_stride * vc->vc_font.height - vc->vc_font.width * octpp; + splashsrc.ub -= sd->splash_pic_stride * vc->vc_font.height - vc->vc_font.width * octpp; } } @@ -136,7 +136,7 @@ static void splash_renderc(struct fb_inf sd = info->splash_data; transparent = sd->splash_color == bg_color; - splashsrc.ub = (u8*)(info->splash_pic + ypos * info->splash_pic_stride + xpos * octpp); + splashsrc.ub = (u8*)(sd->splash_pic + ypos * sd->splash_pic_stride + xpos * octpp); dst.ub = (u8*)(info->screen_base + ypos * info->fix.line_length + xpos * octpp); fgx = ((u32 *)info->pseudo_palette)[fg_color]; if (transparent && sd->splash_color == 15) { @@ -197,7 +197,7 @@ static void splash_renderc(struct fb_inf } } dst.ub += info->fix.line_length - width * octpp; - splashsrc.ub += info->splash_pic_stride - width * octpp; + splashsrc.ub += sd->splash_pic_stride - width * octpp; } } @@ -255,10 +255,11 @@ static void splashset(u8 *dst, int heigh static void splashfill(struct fb_info *info, int sy, int sx, int height, int width) { int octpp = (info->var.bits_per_pixel + 1) >> 3; + struct splash_data *sd = info->splash_data; splashcopy((u8 *)(info->screen_base + sy * info->fix.line_length + sx * octpp), - (u8 *)(info->splash_pic + sy * info->splash_pic_stride + sx * octpp), - height, width, info->fix.line_length, info->splash_pic_stride, + (u8 *)(sd->splash_pic + sy * sd->splash_pic_stride + sx * octpp), + height, width, info->fix.line_length, sd->splash_pic_stride, octpp); } @@ -442,6 +443,7 @@ void splash_bmove_redraw(struct vc_data void splash_blank(struct vc_data *vc, struct fb_info *info, int blank) { SPLASH_DEBUG(); + if (blank) { splashset((u8 *)info->screen_base, info->var.yres, info->var.xres, --- a/drivers/video/console/fbcon.h +++ b/drivers/video/console/fbcon.h @@ -34,8 +34,10 @@ struct splash_data { int splash_height; /* height of image */ int splash_text_xo; /* text area origin */ int splash_text_yo; - int splash_text_wi; /* text area size */ + int splash_text_wi; /* text area size used*/ int splash_text_he; + int splash_jpg_text_wi; /* text area size of jpeg*/ + int splash_jpg_text_he; int splash_showtext; /* silent/verbose mode */ int splash_boxcount; int splash_percent; @@ -45,12 +47,19 @@ struct splash_data { unsigned char *splash_boxes; unsigned char *splash_jpeg; /* jpeg */ unsigned char *splash_palette; /* palette for 8-bit */ + int splash_boxes_xoff; + int splash_boxes_yoff; int splash_dosilent; /* show silent jpeg */ unsigned char *splash_silentjpeg; unsigned char *splash_sboxes; int splash_sboxcount; struct splash_data *next; + int splash_sboxes_xoff; + int splash_sboxes_yoff; + int splash_pic_stride; + unsigned char *splash_pic; + int splash_pic_size; }; #endif --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -861,9 +861,6 @@ struct fb_info { void *par; #ifdef CONFIG_BOOTSPLASH struct splash_data *splash_data; - unsigned char *splash_pic; - int splash_pic_size; - int splash_pic_stride; char fb_cursordata[64]; #endif /* we need the PCI or similiar aperture base/size not