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 | 855 +++++++++++++++++++++++++++++++--- 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, 807 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 @@ "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_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 @@ 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 @@ } 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 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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); @@ -579,6 +764,12 @@ 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 +787,8 @@ 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 +799,9 @@ 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 +823,6 @@ 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 +836,16 @@ 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 +912,71 @@ 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 +984,14 @@ { 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 +1001,62 @@ 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 +1065,43 @@ } 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 +1124,16 @@ 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 +1156,6 @@ 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 +1163,9 @@ 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 +1222,9 @@ || 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 +1235,8 @@ 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 +1246,8 @@ 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 +1369,8 @@ 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 +1379,8 @@ 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 +1499,447 @@ 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 @@ #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 @@ 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 @@ } } 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 @@ 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 @@ } } 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 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_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 @@ 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 @@ 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 @@ -877,9 +877,6 @@ 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