Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / video / fb / video_fb.c
blobcfe441f427824769fa57ed56579f29bfb15e5c61
1 /*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc.
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
19 #include <grub/video.h>
20 #include <grub/video_fb.h>
21 #include <grub/misc.h>
22 #include <grub/mm.h>
23 #include <grub/fbblit.h>
24 #include <grub/fbfill.h>
25 #include <grub/fbutil.h>
26 #include <grub/bitmap.h>
27 #include <grub/dl.h>
29 GRUB_MOD_LICENSE ("GPLv3+");
31 typedef grub_err_t (*grub_video_fb_doublebuf_update_screen_t) (void);
32 typedef volatile void *framebuf_t;
34 struct dirty
36 int first_line;
37 int last_line;
40 static struct
42 struct grub_video_fbrender_target *render_target;
43 struct grub_video_fbrender_target *back_target;
44 struct grub_video_palette_data *palette;
45 framebuf_t pages[2];
47 unsigned int palette_size;
49 struct dirty current_dirty;
50 struct dirty previous_dirty;
52 /* For page flipping strategy. */
53 int displayed_page; /* The page # that is the front buffer. */
54 int render_page; /* The page # that is the back buffer. */
55 grub_video_fb_set_page_t set_page;
56 char *offscreen_buffer;
57 grub_video_fb_doublebuf_update_screen_t update_screen;
58 } framebuffer;
60 /* Specify "standard" VGA palette, some video cards may
61 need this and this will also be used when using RGB modes. */
62 struct grub_video_palette_data grub_video_fbstd_colors[GRUB_VIDEO_FBSTD_NUMCOLORS] =
64 // {R, G, B, A}
65 {0x00, 0x00, 0x00, 0xFF}, // 0 = black
66 {0x00, 0x00, 0xA8, 0xFF}, // 1 = blue
67 {0x00, 0xA8, 0x00, 0xFF}, // 2 = green
68 {0x00, 0xA8, 0xA8, 0xFF}, // 3 = cyan
69 {0xA8, 0x00, 0x00, 0xFF}, // 4 = red
70 {0xA8, 0x00, 0xA8, 0xFF}, // 5 = magenta
71 {0xA8, 0x54, 0x00, 0xFF}, // 6 = brown
72 {0xA8, 0xA8, 0xA8, 0xFF}, // 7 = light gray
74 {0x54, 0x54, 0x54, 0xFF}, // 8 = dark gray
75 {0x54, 0x54, 0xFE, 0xFF}, // 9 = bright blue
76 {0x54, 0xFE, 0x54, 0xFF}, // 10 = bright green
77 {0x54, 0xFE, 0xFE, 0xFF}, // 11 = bright cyan
78 {0xFE, 0x54, 0x54, 0xFF}, // 12 = bright red
79 {0xFE, 0x54, 0xFE, 0xFF}, // 13 = bright magenta
80 {0xFE, 0xFE, 0x54, 0xFF}, // 14 = yellow
81 {0xFE, 0xFE, 0xFE, 0xFF} // 15 = white
84 grub_err_t
85 grub_video_fb_init (void)
87 grub_free (framebuffer.palette);
88 framebuffer.render_target = 0;
89 framebuffer.back_target = 0;
90 framebuffer.palette = 0;
91 framebuffer.palette_size = 0;
92 framebuffer.set_page = 0;
93 return GRUB_ERR_NONE;
96 grub_err_t
97 grub_video_fb_fini (void)
99 /* TODO: destroy render targets. */
101 grub_free (framebuffer.offscreen_buffer);
102 grub_free (framebuffer.palette);
103 framebuffer.render_target = 0;
104 framebuffer.back_target = 0;
105 framebuffer.palette = 0;
106 framebuffer.palette_size = 0;
107 framebuffer.set_page = 0;
108 framebuffer.offscreen_buffer = 0;
109 return GRUB_ERR_NONE;
112 grub_err_t
113 grub_video_fb_get_info (struct grub_video_mode_info *mode_info)
115 /* Copy mode info from active render target. */
116 grub_memcpy (mode_info, &framebuffer.render_target->mode_info,
117 sizeof (struct grub_video_mode_info));
119 return GRUB_ERR_NONE;
122 grub_err_t
123 grub_video_fb_get_palette (unsigned int start, unsigned int count,
124 struct grub_video_palette_data *palette_data)
126 unsigned int i;
128 /* Assume that we know everything from index color palette. */
129 for (i = 0; (i < count) && ((i + start) < framebuffer.palette_size); i++)
130 palette_data[i] = framebuffer.palette[start + i];
132 return GRUB_ERR_NONE;
135 grub_err_t
136 grub_video_fb_set_palette (unsigned int start, unsigned int count,
137 struct grub_video_palette_data *palette_data)
139 unsigned i;
140 if (start + count > framebuffer.palette_size)
142 framebuffer.palette_size = start + count;
143 framebuffer.palette = grub_realloc (framebuffer.palette,
144 sizeof (framebuffer.palette[0])
145 * framebuffer.palette_size);
146 if (!framebuffer.palette)
148 grub_video_fb_fini ();
149 return grub_errno;
152 for (i = 0; (i < count) && ((i + start) < framebuffer.palette_size); i++)
153 framebuffer.palette[start + i] = palette_data[i];
154 return GRUB_ERR_NONE;
157 grub_err_t
158 grub_video_fb_set_viewport (unsigned int x, unsigned int y,
159 unsigned int width, unsigned int height)
161 /* Make sure viewport is withing screen dimensions. If viewport was set
162 to be out of the region, mark its size as zero. */
163 if (x > framebuffer.render_target->mode_info.width)
165 x = 0;
166 width = 0;
169 if (y > framebuffer.render_target->mode_info.height)
171 y = 0;
172 height = 0;
175 if (x + width > framebuffer.render_target->mode_info.width)
176 width = framebuffer.render_target->mode_info.width - x;
178 if (y + height > framebuffer.render_target->mode_info.height)
179 height = framebuffer.render_target->mode_info.height - y;
181 framebuffer.render_target->viewport.x = x;
182 framebuffer.render_target->viewport.y = y;
183 framebuffer.render_target->viewport.width = width;
184 framebuffer.render_target->viewport.height = height;
186 return GRUB_ERR_NONE;
189 grub_err_t
190 grub_video_fb_get_viewport (unsigned int *x, unsigned int *y,
191 unsigned int *width, unsigned int *height)
193 if (x) *x = framebuffer.render_target->viewport.x;
194 if (y) *y = framebuffer.render_target->viewport.y;
195 if (width) *width = framebuffer.render_target->viewport.width;
196 if (height) *height = framebuffer.render_target->viewport.height;
198 return GRUB_ERR_NONE;
201 /* Maps color name to target optimized color format. */
202 grub_video_color_t
203 grub_video_fb_map_color (grub_uint32_t color_name)
205 /* TODO: implement color theme mapping code. */
207 if (color_name < framebuffer.palette_size)
209 if ((framebuffer.render_target->mode_info.mode_type
210 & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) != 0)
211 return color_name;
212 else
214 grub_video_color_t color;
216 color = grub_video_fb_map_rgb (framebuffer.palette[color_name].r,
217 framebuffer.palette[color_name].g,
218 framebuffer.palette[color_name].b);
220 return color;
224 return 0;
227 /* Maps RGB to target optimized color format. */
228 grub_video_color_t
229 grub_video_fb_map_rgb (grub_uint8_t red, grub_uint8_t green,
230 grub_uint8_t blue)
232 if ((framebuffer.render_target->mode_info.mode_type
233 & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) != 0)
235 int minindex = 0;
236 int delta = 0;
237 int tmp;
238 int val;
239 unsigned i;
241 /* Find best matching color. */
242 for (i = 0; i < framebuffer.palette_size; i++)
244 val = framebuffer.palette[i].r - red;
245 tmp = val * val;
246 val = framebuffer.palette[i].g - green;
247 tmp += val * val;
248 val = framebuffer.palette[i].b - blue;
249 tmp += val * val;
251 if (i == 0)
252 delta = tmp;
254 if (tmp < delta)
256 delta = tmp;
257 minindex = i;
258 if (tmp == 0)
259 break;
263 return minindex;
265 else if ((framebuffer.render_target->mode_info.mode_type
266 & GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP) != 0)
268 if (red == framebuffer.render_target->mode_info.fg_red
269 && green == framebuffer.render_target->mode_info.fg_green
270 && blue == framebuffer.render_target->mode_info.fg_blue)
271 return 1;
272 else
273 return 0;
275 else
277 grub_uint32_t value;
278 grub_uint8_t alpha = 255; /* Opaque color. */
280 red >>= 8 - framebuffer.render_target->mode_info.red_mask_size;
281 green >>= 8 - framebuffer.render_target->mode_info.green_mask_size;
282 blue >>= 8 - framebuffer.render_target->mode_info.blue_mask_size;
283 alpha >>= 8 - framebuffer.render_target->mode_info.reserved_mask_size;
285 value = red << framebuffer.render_target->mode_info.red_field_pos;
286 value |= green << framebuffer.render_target->mode_info.green_field_pos;
287 value |= blue << framebuffer.render_target->mode_info.blue_field_pos;
288 value |= alpha << framebuffer.render_target->mode_info.reserved_field_pos;
290 return value;
295 /* Maps RGBA to target optimized color format. */
296 grub_video_color_t
297 grub_video_fb_map_rgba (grub_uint8_t red, grub_uint8_t green,
298 grub_uint8_t blue, grub_uint8_t alpha)
300 if ((framebuffer.render_target->mode_info.mode_type
301 & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) != 0)
302 /* No alpha available in index color modes, just use
303 same value as in only RGB modes. */
304 return grub_video_fb_map_rgb (red, green, blue);
305 else if ((framebuffer.render_target->mode_info.mode_type
306 & GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP) != 0)
308 if (red == framebuffer.render_target->mode_info.fg_red
309 && green == framebuffer.render_target->mode_info.fg_green
310 && blue == framebuffer.render_target->mode_info.fg_blue
311 && alpha == framebuffer.render_target->mode_info.fg_alpha)
312 return 1;
313 else
314 return 0;
316 else
318 grub_uint32_t value;
320 red >>= 8 - framebuffer.render_target->mode_info.red_mask_size;
321 green >>= 8 - framebuffer.render_target->mode_info.green_mask_size;
322 blue >>= 8 - framebuffer.render_target->mode_info.blue_mask_size;
323 alpha >>= 8 - framebuffer.render_target->mode_info.reserved_mask_size;
325 value = red << framebuffer.render_target->mode_info.red_field_pos;
326 value |= green << framebuffer.render_target->mode_info.green_field_pos;
327 value |= blue << framebuffer.render_target->mode_info.blue_field_pos;
328 value |= alpha << framebuffer.render_target->mode_info.reserved_field_pos;
330 return value;
334 /* Splits target optimized format to components. */
335 grub_err_t
336 grub_video_fb_unmap_color (grub_video_color_t color,
337 grub_uint8_t *red, grub_uint8_t *green,
338 grub_uint8_t *blue, grub_uint8_t *alpha)
340 struct grub_video_fbblit_info target_info;
342 target_info.mode_info = &framebuffer.render_target->mode_info;
343 target_info.data = framebuffer.render_target->data;
345 grub_video_fb_unmap_color_int (&target_info, color, red, green, blue, alpha);
347 return GRUB_ERR_NONE;
350 /* Splits color in source format to components. */
351 void
352 grub_video_fb_unmap_color_int (struct grub_video_fbblit_info * source,
353 grub_video_color_t color,
354 grub_uint8_t *red, grub_uint8_t *green,
355 grub_uint8_t *blue, grub_uint8_t *alpha)
357 struct grub_video_mode_info *mode_info;
358 mode_info = source->mode_info;
360 if ((mode_info->mode_type
361 & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) != 0)
363 /* If we have an out-of-bounds color, return transparent black. */
364 if (color > 255)
366 *red = 0;
367 *green = 0;
368 *blue = 0;
369 *alpha = 0;
370 return;
373 *red = framebuffer.palette[color].r;
374 *green = framebuffer.palette[color].g;
375 *blue = framebuffer.palette[color].b;
376 *alpha = framebuffer.palette[color].a;
377 return;
379 else if ((mode_info->mode_type
380 & GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP) != 0)
382 if (color & 1)
384 *red = mode_info->fg_red;
385 *green = mode_info->fg_green;
386 *blue = mode_info->fg_blue;
387 *alpha = mode_info->fg_alpha;
389 else
391 *red = mode_info->bg_red;
392 *green = mode_info->bg_green;
393 *blue = mode_info->bg_blue;
394 *alpha = mode_info->bg_alpha;
397 else
399 grub_uint32_t tmp;
401 /* Get red component. */
402 tmp = color >> mode_info->red_field_pos;
403 tmp &= (1 << mode_info->red_mask_size) - 1;
404 tmp <<= 8 - mode_info->red_mask_size;
405 tmp |= (1 << (8 - mode_info->red_mask_size)) - 1;
406 *red = tmp & 0xFF;
408 /* Get green component. */
409 tmp = color >> mode_info->green_field_pos;
410 tmp &= (1 << mode_info->green_mask_size) - 1;
411 tmp <<= 8 - mode_info->green_mask_size;
412 tmp |= (1 << (8 - mode_info->green_mask_size)) - 1;
413 *green = tmp & 0xFF;
415 /* Get blue component. */
416 tmp = color >> mode_info->blue_field_pos;
417 tmp &= (1 << mode_info->blue_mask_size) - 1;
418 tmp <<= 8 - mode_info->blue_mask_size;
419 tmp |= (1 << (8 - mode_info->blue_mask_size)) - 1;
420 *blue = tmp & 0xFF;
422 /* Get alpha component. */
423 if (source->mode_info->reserved_mask_size > 0)
425 tmp = color >> mode_info->reserved_field_pos;
426 tmp &= (1 << mode_info->reserved_mask_size) - 1;
427 tmp <<= 8 - mode_info->reserved_mask_size;
428 tmp |= (1 << (8 - mode_info->reserved_mask_size)) - 1;
430 else
431 /* If there is no alpha component, assume it opaque. */
432 tmp = 255;
434 *alpha = tmp & 0xFF;
438 static void
439 dirty (int y, int height)
441 if (framebuffer.render_target != framebuffer.back_target)
442 return;
443 if (framebuffer.current_dirty.first_line > y)
444 framebuffer.current_dirty.first_line = y;
445 if (framebuffer.current_dirty.last_line < y + height)
446 framebuffer.current_dirty.last_line = y + height;
449 grub_err_t
450 grub_video_fb_fill_rect (grub_video_color_t color, int x, int y,
451 unsigned int width, unsigned int height)
453 struct grub_video_fbblit_info target;
455 /* Make sure there is something to do. */
456 if ((x >= (int)framebuffer.render_target->viewport.width) || (x + (int)width < 0))
457 return GRUB_ERR_NONE;
458 if ((y >= (int)framebuffer.render_target->viewport.height) || (y + (int)height < 0))
459 return GRUB_ERR_NONE;
461 /* Do not allow drawing out of viewport. */
462 if (x < 0)
464 width += x;
465 x = 0;
467 if (y < 0)
469 height += y;
470 y = 0;
473 if ((x + width) > framebuffer.render_target->viewport.width)
474 width = framebuffer.render_target->viewport.width - x;
475 if ((y + height) > framebuffer.render_target->viewport.height)
476 height = framebuffer.render_target->viewport.height - y;
478 /* Add viewport offset. */
479 x += framebuffer.render_target->viewport.x;
480 y += framebuffer.render_target->viewport.y;
482 dirty (y, height);
484 /* Use fbblit_info to encapsulate rendering. */
485 target.mode_info = &framebuffer.render_target->mode_info;
486 target.data = framebuffer.render_target->data;
488 /* Try to figure out more optimized version. Note that color is already
489 mapped to target format so we can make assumptions based on that. */
490 if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888)
492 grub_video_fbfill_direct32 (&target, color, x, y,
493 width, height);
494 return GRUB_ERR_NONE;
496 else if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888)
498 grub_video_fbfill_direct32 (&target, color, x, y,
499 width, height);
500 return GRUB_ERR_NONE;
502 else if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888)
504 grub_video_fbfill_direct24 (&target, color, x, y,
505 width, height);
506 return GRUB_ERR_NONE;
508 else if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_565)
510 grub_video_fbfill_direct16 (&target, color, x, y,
511 width, height);
512 return GRUB_ERR_NONE;
514 else if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGR_565)
516 grub_video_fbfill_direct16 (&target, color, x, y,
517 width, height);
518 return GRUB_ERR_NONE;
520 else if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR)
522 grub_video_fbfill_direct8 (&target, color, x, y,
523 width, height);
524 return GRUB_ERR_NONE;
527 /* No optimized version found, use default (slow) filler. */
528 grub_video_fbfill (&target, color, x, y, width, height);
530 return GRUB_ERR_NONE;
533 /* NOTE: This function assumes that given coordinates are within bounds of
534 handled data. */
535 static void
536 common_blitter (struct grub_video_fbblit_info *target,
537 struct grub_video_fbblit_info *source,
538 enum grub_video_blit_operators oper, int x, int y,
539 unsigned int width, unsigned int height,
540 int offset_x, int offset_y)
542 dirty (y, height);
544 if (oper == GRUB_VIDEO_BLIT_REPLACE)
546 /* Try to figure out more optimized version for replace operator. */
547 if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888)
549 if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888)
551 grub_video_fbblit_replace_directN (target, source,
552 x, y, width, height,
553 offset_x, offset_y);
554 return;
556 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888)
558 grub_video_fbblit_replace_BGRX8888_RGBX8888 (target, source,
559 x, y, width, height,
560 offset_x, offset_y);
561 return;
563 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGR_888)
565 grub_video_fbblit_replace_BGR888_RGBX8888 (target, source,
566 x, y, width, height,
567 offset_x, offset_y);
568 return;
570 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888)
572 grub_video_fbblit_replace_RGB888_RGBX8888 (target, source,
573 x, y, width, height,
574 offset_x, offset_y);
575 return;
577 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR)
579 grub_video_fbblit_replace_index_RGBX8888 (target, source,
580 x, y, width, height,
581 offset_x, offset_y);
582 return;
585 else if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888)
587 if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888)
589 grub_video_fbblit_replace_BGRX8888_RGB888 (target, source,
590 x, y, width, height,
591 offset_x, offset_y);
592 return;
594 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888)
596 grub_video_fbblit_replace_RGBX8888_RGB888 (target, source,
597 x, y, width, height,
598 offset_x, offset_y);
599 return;
601 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGR_888)
603 grub_video_fbblit_replace_BGR888_RGB888 (target, source,
604 x, y, width, height,
605 offset_x, offset_y);
606 return;
608 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888)
610 grub_video_fbblit_replace_directN (target, source,
611 x, y, width, height,
612 offset_x, offset_y);
613 return;
615 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR)
617 grub_video_fbblit_replace_index_RGB888 (target, source,
618 x, y, width, height,
619 offset_x, offset_y);
620 return;
623 else if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888)
625 if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888)
627 grub_video_fbblit_replace_directN (target, source,
628 x, y, width, height,
629 offset_x, offset_y);
630 return;
633 else if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR)
635 if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR)
637 grub_video_fbblit_replace_directN (target, source,
638 x, y, width, height,
639 offset_x, offset_y);
640 return;
643 else if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED)
645 if (target->mode_info->bpp == 32)
647 grub_video_fbblit_replace_32bit_1bit (target, source,
648 x, y, width, height,
649 offset_x, offset_y);
650 return;
652 #ifdef GRUB_HAVE_UNALIGNED_ACCESS
653 else if (target->mode_info->bpp == 24)
655 grub_video_fbblit_replace_24bit_1bit (target, source,
656 x, y, width, height,
657 offset_x, offset_y);
658 return;
660 #endif
661 else if (target->mode_info->bpp == 16)
663 grub_video_fbblit_replace_16bit_1bit (target, source,
664 x, y, width, height,
665 offset_x, offset_y);
666 return;
668 else if (target->mode_info->bpp == 8)
670 grub_video_fbblit_replace_8bit_1bit (target, source,
671 x, y, width, height,
672 offset_x, offset_y);
673 return;
677 /* No optimized replace operator found, use default (slow) blitter. */
678 grub_video_fbblit_replace (target, source, x, y, width, height,
679 offset_x, offset_y);
681 else
683 /* Try to figure out more optimized blend operator. */
684 if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888)
686 if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888)
688 grub_video_fbblit_blend_BGRA8888_RGBA8888 (target, source,
689 x, y, width, height,
690 offset_x, offset_y);
691 return;
693 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888)
695 grub_video_fbblit_blend_RGBA8888_RGBA8888 (target, source,
696 x, y, width, height,
697 offset_x, offset_y);
698 return;
700 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGR_888)
702 grub_video_fbblit_blend_BGR888_RGBA8888 (target, source,
703 x, y, width, height,
704 offset_x, offset_y);
705 return;
707 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888)
709 grub_video_fbblit_blend_RGB888_RGBA8888 (target, source,
710 x, y, width, height,
711 offset_x, offset_y);
712 return;
714 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR)
716 grub_video_fbblit_blend_index_RGBA8888 (target, source,
717 x, y, width, height,
718 offset_x, offset_y);
719 return;
722 else if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888)
724 /* Note: There is really no alpha information here, so blend is
725 changed to replace. */
727 if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888)
729 grub_video_fbblit_replace_BGRX8888_RGB888 (target, source,
730 x, y, width, height,
731 offset_x, offset_y);
732 return;
734 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888)
736 grub_video_fbblit_replace_RGBX8888_RGB888 (target, source,
737 x, y, width, height,
738 offset_x, offset_y);
739 return;
741 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGR_888)
743 grub_video_fbblit_replace_BGR888_RGB888 (target, source,
744 x, y, width, height,
745 offset_x, offset_y);
746 return;
748 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888)
750 grub_video_fbblit_replace_directN (target, source,
751 x, y, width, height,
752 offset_x, offset_y);
753 return;
755 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR)
757 grub_video_fbblit_replace_index_RGB888 (target, source,
758 x, y, width, height,
759 offset_x, offset_y);
760 return;
763 else if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED)
765 if (target->mode_info->blit_format
766 == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888
767 || target->mode_info->blit_format
768 == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888)
770 grub_video_fbblit_blend_XXXA8888_1bit (target, source,
771 x, y, width, height,
772 offset_x, offset_y);
773 return;
775 #ifdef GRUB_HAVE_UNALIGNED_ACCESS
776 else if (target->mode_info->blit_format
777 == GRUB_VIDEO_BLIT_FORMAT_BGR_888
778 || target->mode_info->blit_format
779 == GRUB_VIDEO_BLIT_FORMAT_RGB_888)
781 grub_video_fbblit_blend_XXX888_1bit (target, source,
782 x, y, width, height,
783 offset_x, offset_y);
784 return;
786 #endif
787 else if (target->mode_info->blit_format
788 == GRUB_VIDEO_BLIT_FORMAT_BGR_565
789 || target->mode_info->blit_format
790 == GRUB_VIDEO_BLIT_FORMAT_RGB_565)
792 grub_video_fbblit_blend_XXX565_1bit (target, source,
793 x, y, width, height,
794 offset_x, offset_y);
795 return;
801 /* No optimized blend operation found, use default (slow) blitter. */
802 grub_video_fbblit_blend (target, source, x, y, width, height,
803 offset_x, offset_y);
807 grub_err_t
808 grub_video_fb_blit_bitmap (struct grub_video_bitmap *bitmap,
809 enum grub_video_blit_operators oper, int x, int y,
810 int offset_x, int offset_y,
811 unsigned int width, unsigned int height)
813 struct grub_video_fbblit_info source;
814 struct grub_video_fbblit_info target;
816 /* Make sure there is something to do. */
817 if ((width == 0) || (height == 0))
818 return GRUB_ERR_NONE;
819 if ((x >= (int)framebuffer.render_target->viewport.width) || (x + (int)width < 0))
820 return GRUB_ERR_NONE;
821 if ((y >= (int)framebuffer.render_target->viewport.height) || (y + (int)height < 0))
822 return GRUB_ERR_NONE;
823 if ((x + (int)bitmap->mode_info.width) < 0)
824 return GRUB_ERR_NONE;
825 if ((y + (int)bitmap->mode_info.height) < 0)
826 return GRUB_ERR_NONE;
827 if ((offset_x >= (int)bitmap->mode_info.width)
828 || (offset_x + (int)width < 0))
829 return GRUB_ERR_NONE;
830 if ((offset_y >= (int)bitmap->mode_info.height)
831 || (offset_y + (int)height < 0))
832 return GRUB_ERR_NONE;
834 /* If we have negative coordinates, optimize drawing to minimum. */
835 if (offset_x < 0)
837 width += offset_x;
838 x -= offset_x;
839 offset_x = 0;
842 if (offset_y < 0)
844 height += offset_y;
845 y -= offset_y;
846 offset_y = 0;
849 if (x < 0)
851 width += x;
852 offset_x -= x;
853 x = 0;
856 if (y < 0)
858 height += y;
859 offset_y -= y;
860 y = 0;
863 /* Do not allow drawing out of viewport. */
864 if ((x + width) > framebuffer.render_target->viewport.width)
865 width = framebuffer.render_target->viewport.width - x;
866 if ((y + height) > framebuffer.render_target->viewport.height)
867 height = framebuffer.render_target->viewport.height - y;
869 if ((offset_x + width) > bitmap->mode_info.width)
870 width = bitmap->mode_info.width - offset_x;
871 if ((offset_y + height) > bitmap->mode_info.height)
872 height = bitmap->mode_info.height - offset_y;
874 /* Limit drawing to source render target dimensions. */
875 if (width > bitmap->mode_info.width)
876 width = bitmap->mode_info.width;
878 if (height > bitmap->mode_info.height)
879 height = bitmap->mode_info.height;
881 /* Add viewport offset. */
882 x += framebuffer.render_target->viewport.x;
883 y += framebuffer.render_target->viewport.y;
885 /* Use fbblit_info to encapsulate rendering. */
886 source.mode_info = &bitmap->mode_info;
887 source.data = bitmap->data;
888 target.mode_info = &framebuffer.render_target->mode_info;
889 target.data = framebuffer.render_target->data;
891 /* Do actual blitting. */
892 common_blitter (&target, &source, oper, x, y, width, height,
893 offset_x, offset_y);
895 return GRUB_ERR_NONE;
898 grub_err_t
899 grub_video_fb_blit_render_target (struct grub_video_fbrender_target *source,
900 enum grub_video_blit_operators oper,
901 int x, int y, int offset_x, int offset_y,
902 unsigned int width, unsigned int height)
904 struct grub_video_fbblit_info source_info;
905 struct grub_video_fbblit_info target_info;
907 /* Make sure there is something to do. */
908 if ((width == 0) || (height == 0))
909 return GRUB_ERR_NONE;
910 if ((x >= (int)framebuffer.render_target->viewport.width) || (x + (int)width < 0))
911 return GRUB_ERR_NONE;
912 if ((y >= (int)framebuffer.render_target->viewport.height) || (y + (int)height < 0))
913 return GRUB_ERR_NONE;
914 if ((x + (int)source->mode_info.width) < 0)
915 return GRUB_ERR_NONE;
916 if ((y + (int)source->mode_info.height) < 0)
917 return GRUB_ERR_NONE;
918 if ((offset_x >= (int)source->mode_info.width)
919 || (offset_x + (int)width < 0))
920 return GRUB_ERR_NONE;
921 if ((offset_y >= (int)source->mode_info.height)
922 || (offset_y + (int)height < 0))
923 return GRUB_ERR_NONE;
925 /* If we have negative coordinates, optimize drawing to minimum. */
926 if (offset_x < 0)
928 width += offset_x;
929 x -= offset_x;
930 offset_x = 0;
933 if (offset_y < 0)
935 height += offset_y;
936 y -= offset_y;
937 offset_y = 0;
940 if (x < 0)
942 width += x;
943 offset_x -= x;
944 x = 0;
947 if (y < 0)
949 height += y;
950 offset_y -= y;
951 y = 0;
954 /* Do not allow drawing out of viewport. */
955 if ((x + width) > framebuffer.render_target->viewport.width)
956 width = framebuffer.render_target->viewport.width - x;
957 if ((y + height) > framebuffer.render_target->viewport.height)
958 height = framebuffer.render_target->viewport.height - y;
960 if ((offset_x + width) > source->mode_info.width)
961 width = source->mode_info.width - offset_x;
962 if ((offset_y + height) > source->mode_info.height)
963 height = source->mode_info.height - offset_y;
965 /* Limit drawing to source render target dimensions. */
966 if (width > source->mode_info.width)
967 width = source->mode_info.width;
969 if (height > source->mode_info.height)
970 height = source->mode_info.height;
972 /* Add viewport offset. */
973 x += framebuffer.render_target->viewport.x;
974 y += framebuffer.render_target->viewport.y;
976 /* Use fbblit_info to encapsulate rendering. */
977 source_info.mode_info = &source->mode_info;
978 source_info.data = source->data;
979 target_info.mode_info = &framebuffer.render_target->mode_info;
980 target_info.data = framebuffer.render_target->data;
982 /* Do actual blitting. */
983 common_blitter (&target_info, &source_info, oper, x, y, width, height,
984 offset_x, offset_y);
986 return GRUB_ERR_NONE;
989 grub_err_t
990 grub_video_fb_scroll (grub_video_color_t color, int dx, int dy)
992 int width;
993 int height;
994 int src_x;
995 int src_y;
996 int dst_x;
997 int dst_y;
999 /* 1. Check if we have something to do. */
1000 if ((dx == 0) && (dy == 0))
1001 return GRUB_ERR_NONE;
1003 width = framebuffer.render_target->viewport.width - grub_abs (dx);
1004 height = framebuffer.render_target->viewport.height - grub_abs (dy);
1006 dirty (framebuffer.render_target->viewport.y,
1007 framebuffer.render_target->viewport.height);
1009 if (dx < 0)
1011 src_x = framebuffer.render_target->viewport.x - dx;
1012 dst_x = framebuffer.render_target->viewport.x;
1014 else
1016 src_x = framebuffer.render_target->viewport.x;
1017 dst_x = framebuffer.render_target->viewport.x + dx;
1020 if (dy < 0)
1022 src_y = framebuffer.render_target->viewport.y - dy;
1023 dst_y = framebuffer.render_target->viewport.y;
1025 else
1027 src_y = framebuffer.render_target->viewport.y;
1028 dst_y = framebuffer.render_target->viewport.y + dy;
1031 /* 2. Check if there is need to copy data. */
1032 if ((grub_abs (dx) < framebuffer.render_target->viewport.width)
1033 && (grub_abs (dy) < framebuffer.render_target->viewport.height))
1035 /* 3. Move data in render target. */
1036 struct grub_video_fbblit_info target;
1037 int i, j;
1038 int linedelta, linelen;
1040 target.mode_info = &framebuffer.render_target->mode_info;
1041 target.data = framebuffer.render_target->data;
1043 linedelta = target.mode_info->pitch
1044 - width * target.mode_info->bytes_per_pixel;
1045 linelen = width * target.mode_info->bytes_per_pixel;
1046 #define DO_SCROLL \
1047 /* Check vertical direction of the move. */ \
1048 if (dy < 0 || (dy == 0 && dx < 0)) \
1050 dst = (void *) grub_video_fb_get_video_ptr (&target, \
1051 dst_x, dst_y); \
1052 src = (void *) grub_video_fb_get_video_ptr (&target, \
1053 src_x, src_y); \
1054 /* 3a. Move data upwards. */ \
1055 for (j = 0; j < height; j++) \
1057 for (i = 0; i < linelen; i++) \
1058 *(dst++) = *(src++); \
1059 dst += linedelta; \
1060 src += linedelta; \
1063 else \
1065 /* 3b. Move data downwards. */ \
1066 dst = (void *) grub_video_fb_get_video_ptr (&target, \
1067 dst_x + width, \
1068 dst_y + height - 1); \
1069 src = (void *) grub_video_fb_get_video_ptr (&target, \
1070 src_x + width, \
1071 src_y + height - 1); \
1072 dst--; \
1073 src--; \
1074 for (j = 0; j < height; j++) \
1076 for (i = 0; i < linelen; i++) \
1077 *(dst--) = *(src--); \
1078 dst -= linedelta; \
1079 src -= linedelta; \
1083 /* If everything is aligned on 32-bit use 32-bit copy. */
1084 if ((grub_addr_t) grub_video_fb_get_video_ptr (&target, src_x, src_y)
1085 % sizeof (grub_uint32_t) == 0
1086 && (grub_addr_t) grub_video_fb_get_video_ptr (&target, dst_x, dst_y)
1087 % sizeof (grub_uint32_t) == 0
1088 && linelen % sizeof (grub_uint32_t) == 0
1089 && linedelta % sizeof (grub_uint32_t) == 0)
1091 grub_uint32_t *src, *dst;
1092 linelen /= sizeof (grub_uint32_t);
1093 linedelta /= sizeof (grub_uint32_t);
1094 DO_SCROLL
1096 /* If everything is aligned on 16-bit use 16-bit copy. */
1097 else if ((grub_addr_t) grub_video_fb_get_video_ptr (&target, src_x, src_y)
1098 % sizeof (grub_uint16_t) == 0
1099 && (grub_addr_t) grub_video_fb_get_video_ptr (&target,
1100 dst_x, dst_y)
1101 % sizeof (grub_uint16_t) == 0
1102 && linelen % sizeof (grub_uint16_t) == 0
1103 && linedelta % sizeof (grub_uint16_t) == 0)
1105 grub_uint16_t *src, *dst;
1106 linelen /= sizeof (grub_uint16_t);
1107 linedelta /= sizeof (grub_uint16_t);
1108 DO_SCROLL
1110 /* If not aligned at all use 8-bit copy. */
1111 else
1113 grub_uint8_t *src, *dst;
1114 DO_SCROLL
1118 /* 4. Fill empty space with specified color. In this implementation
1119 there might be colliding areas but at the moment there is no need
1120 to optimize this. */
1122 /* 4a. Fill top & bottom parts. */
1123 if (dy > 0)
1124 grub_video_fb_fill_rect (color, 0, 0, framebuffer.render_target->viewport.width, dy);
1125 else if (dy < 0)
1127 if (framebuffer.render_target->viewport.height < grub_abs (dy))
1128 dy = -framebuffer.render_target->viewport.height;
1130 grub_video_fb_fill_rect (color, 0, framebuffer.render_target->viewport.height + dy,
1131 framebuffer.render_target->viewport.width, -dy);
1134 /* 4b. Fill left & right parts. */
1135 if (dx > 0)
1136 grub_video_fb_fill_rect (color, 0, 0,
1137 dx, framebuffer.render_target->viewport.height);
1138 else if (dx < 0)
1140 if (framebuffer.render_target->viewport.width < grub_abs (dx))
1141 dx = -framebuffer.render_target->viewport.width;
1143 grub_video_fb_fill_rect (color, framebuffer.render_target->viewport.width + dx, 0,
1144 -dx, framebuffer.render_target->viewport.height);
1147 return GRUB_ERR_NONE;
1151 grub_err_t
1152 grub_video_fb_create_render_target (struct grub_video_fbrender_target **result,
1153 unsigned int width, unsigned int height,
1154 unsigned int mode_type __attribute__ ((unused)))
1156 struct grub_video_fbrender_target *target;
1157 unsigned int size;
1159 /* Validate arguments. */
1160 if ((! result)
1161 || (width == 0)
1162 || (height == 0))
1163 return grub_error (GRUB_ERR_BUG,
1164 "invalid argument given");
1166 /* Allocate memory for render target. */
1167 target = grub_malloc (sizeof (struct grub_video_fbrender_target));
1168 if (! target)
1169 return grub_errno;
1171 /* TODO: Implement other types too.
1172 Currently only 32bit render targets are supported. */
1174 /* Mark render target as allocated. */
1175 target->is_allocated = 1;
1177 /* Maximize viewport. */
1178 target->viewport.x = 0;
1179 target->viewport.y = 0;
1180 target->viewport.width = width;
1181 target->viewport.height = height;
1183 /* Setup render target format. */
1184 target->mode_info.width = width;
1185 target->mode_info.height = height;
1186 target->mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_RGB
1187 | GRUB_VIDEO_MODE_TYPE_ALPHA;
1188 target->mode_info.bpp = 32;
1189 target->mode_info.bytes_per_pixel = 4;
1190 target->mode_info.pitch = target->mode_info.bytes_per_pixel * width;
1191 target->mode_info.number_of_colors = framebuffer.palette_size; /* Emulated palette. */
1192 target->mode_info.red_mask_size = 8;
1193 target->mode_info.red_field_pos = 0;
1194 target->mode_info.green_mask_size = 8;
1195 target->mode_info.green_field_pos = 8;
1196 target->mode_info.blue_mask_size = 8;
1197 target->mode_info.blue_field_pos = 16;
1198 target->mode_info.reserved_mask_size = 8;
1199 target->mode_info.reserved_field_pos = 24;
1201 target->mode_info.blit_format = grub_video_get_blit_format (&target->mode_info);
1203 /* Calculate size needed for the data. */
1204 size = (width * target->mode_info.bytes_per_pixel) * height;
1206 target->data = grub_malloc (size);
1207 if (! target->data)
1209 grub_free (target);
1210 return grub_errno;
1213 /* Clear render target with black and maximum transparency. */
1214 grub_memset (target->data, 0, size);
1216 /* TODO: Add render target to render target list. */
1218 /* Save result to caller. */
1219 *result = target;
1221 return GRUB_ERR_NONE;
1224 grub_err_t
1225 grub_video_fb_create_render_target_from_pointer (struct grub_video_fbrender_target **result,
1226 const struct grub_video_mode_info *mode_info,
1227 void *ptr)
1229 struct grub_video_fbrender_target *target;
1230 unsigned y;
1232 #ifndef GRUB_HAVE_UNALIGNED_ACCESS
1233 if (!(mode_info->bytes_per_pixel & (mode_info->bytes_per_pixel - 1))
1234 && ((grub_addr_t) ptr & (mode_info->bytes_per_pixel - 1)))
1235 return grub_error (GRUB_ERR_BAD_ARGUMENT, "unaligned pointer");
1236 if (!(mode_info->bytes_per_pixel & (mode_info->bytes_per_pixel - 1))
1237 && (mode_info->pitch & (mode_info->bytes_per_pixel - 1)))
1238 return grub_error (GRUB_ERR_BAD_ARGUMENT, "unaligned pitch");
1239 #endif
1241 /* Allocate memory for render target. */
1242 target = grub_malloc (sizeof (struct grub_video_fbrender_target));
1243 if (! target)
1244 return grub_errno;
1246 /* Mark framebuffer memory as non allocated. */
1247 target->is_allocated = 0;
1248 target->data = ptr;
1250 grub_memcpy (&(target->mode_info), mode_info, sizeof (target->mode_info));
1252 /* Reset viewport to match new mode. */
1253 target->viewport.x = 0;
1254 target->viewport.y = 0;
1255 target->viewport.width = mode_info->width;
1256 target->viewport.height = mode_info->height;
1258 /* Clear render target with black and maximum transparency. */
1259 for (y = 0; y < mode_info->height; y++)
1260 grub_memset (target->data + mode_info->pitch * y, 0,
1261 mode_info->bytes_per_pixel * mode_info->width);
1263 /* Save result to caller. */
1264 *result = target;
1266 return GRUB_ERR_NONE;
1269 grub_err_t
1270 grub_video_fb_delete_render_target (struct grub_video_fbrender_target *target)
1272 /* If there is no target, then just return without error. */
1273 if (! target)
1274 return GRUB_ERR_NONE;
1276 /* TODO: Delist render target from render target list. */
1278 /* If this is software render target, free it's memory. */
1279 if (target->is_allocated)
1280 grub_free (target->data);
1282 /* Free render target. */
1283 grub_free (target);
1285 return GRUB_ERR_NONE;
1288 grub_err_t
1289 grub_video_fb_set_active_render_target (struct grub_video_fbrender_target *target)
1291 if (target == (struct grub_video_fbrender_target *)
1292 GRUB_VIDEO_RENDER_TARGET_DISPLAY)
1293 target = framebuffer.back_target;
1295 if (! target->data)
1296 return grub_error (GRUB_ERR_BUG,
1297 "invalid render target given");
1299 framebuffer.render_target = target;
1301 return GRUB_ERR_NONE;
1304 grub_err_t
1305 grub_video_fb_get_active_render_target (struct grub_video_fbrender_target **target)
1307 *target = framebuffer.render_target;
1309 if (*target == framebuffer.back_target)
1310 *target = (struct grub_video_fbrender_target *) GRUB_VIDEO_RENDER_TARGET_DISPLAY;
1312 return GRUB_ERR_NONE;
1315 static grub_err_t
1316 doublebuf_blit_update_screen (void)
1318 if (framebuffer.current_dirty.first_line
1319 <= framebuffer.current_dirty.last_line)
1320 grub_memcpy ((char *) framebuffer.pages[0]
1321 + framebuffer.current_dirty.first_line
1322 * framebuffer.back_target->mode_info.pitch,
1323 (char *) framebuffer.back_target->data
1324 + framebuffer.current_dirty.first_line
1325 * framebuffer.back_target->mode_info.pitch,
1326 framebuffer.back_target->mode_info.pitch
1327 * (framebuffer.current_dirty.last_line
1328 - framebuffer.current_dirty.first_line));
1329 framebuffer.current_dirty.first_line
1330 = framebuffer.back_target->mode_info.height;
1331 framebuffer.current_dirty.last_line = 0;
1333 return GRUB_ERR_NONE;
1336 static grub_err_t
1337 grub_video_fb_doublebuf_blit_init (struct grub_video_fbrender_target **back,
1338 struct grub_video_mode_info mode_info,
1339 volatile void *framebuf)
1341 grub_err_t err;
1342 grub_size_t page_size = mode_info.pitch * mode_info.height;
1344 framebuffer.offscreen_buffer = grub_zalloc (page_size);
1345 if (! framebuffer.offscreen_buffer)
1346 return grub_errno;
1348 err = grub_video_fb_create_render_target_from_pointer (&framebuffer.back_target,
1349 &mode_info,
1350 framebuffer.offscreen_buffer);
1352 if (err)
1354 grub_free (framebuffer.offscreen_buffer);
1355 framebuffer.offscreen_buffer = 0;
1356 return grub_errno;
1358 (*back)->is_allocated = 1;
1360 framebuffer.update_screen = doublebuf_blit_update_screen;
1361 framebuffer.pages[0] = framebuf;
1362 framebuffer.displayed_page = 0;
1363 framebuffer.render_page = 0;
1364 framebuffer.current_dirty.first_line = mode_info.height;
1365 framebuffer.current_dirty.last_line = 0;
1367 return GRUB_ERR_NONE;
1370 static grub_err_t
1371 doublebuf_pageflipping_update_screen (void)
1373 int new_displayed_page;
1374 grub_err_t err;
1375 int first_line, last_line;
1377 first_line = framebuffer.current_dirty.first_line;
1378 last_line = framebuffer.current_dirty.last_line;
1379 if (first_line > framebuffer.previous_dirty.first_line)
1380 first_line = framebuffer.previous_dirty.first_line;
1381 if (last_line < framebuffer.previous_dirty.last_line)
1382 last_line = framebuffer.previous_dirty.last_line;
1384 if (first_line <= last_line)
1385 grub_memcpy ((char *) framebuffer.pages[framebuffer.render_page]
1386 + first_line * framebuffer.back_target->mode_info.pitch,
1387 (char *) framebuffer.back_target->data
1388 + first_line * framebuffer.back_target->mode_info.pitch,
1389 framebuffer.back_target->mode_info.pitch
1390 * (last_line - first_line));
1391 framebuffer.previous_dirty = framebuffer.current_dirty;
1392 framebuffer.current_dirty.first_line
1393 = framebuffer.back_target->mode_info.height;
1394 framebuffer.current_dirty.last_line = 0;
1396 /* Swap the page numbers in the framebuffer struct. */
1397 new_displayed_page = framebuffer.render_page;
1398 framebuffer.render_page = framebuffer.displayed_page;
1399 framebuffer.displayed_page = new_displayed_page;
1401 err = framebuffer.set_page (framebuffer.displayed_page);
1402 if (err)
1404 /* Restore previous state. */
1405 framebuffer.render_page = framebuffer.displayed_page;
1406 framebuffer.displayed_page = new_displayed_page;
1407 return err;
1410 return GRUB_ERR_NONE;
1413 static grub_err_t
1414 doublebuf_pageflipping_init (struct grub_video_mode_info *mode_info,
1415 volatile void *page0_ptr,
1416 grub_video_fb_set_page_t set_page_in,
1417 volatile void *page1_ptr)
1419 grub_err_t err;
1420 grub_size_t page_size = mode_info->pitch * mode_info->height;
1422 framebuffer.offscreen_buffer = grub_malloc (page_size);
1423 if (! framebuffer.offscreen_buffer)
1425 return grub_errno;
1428 err = grub_video_fb_create_render_target_from_pointer (&framebuffer.back_target,
1429 mode_info,
1430 framebuffer.offscreen_buffer);
1432 if (err)
1434 grub_free (framebuffer.offscreen_buffer);
1435 framebuffer.offscreen_buffer = 0;
1436 return grub_errno;
1438 framebuffer.back_target->is_allocated = 1;
1440 framebuffer.displayed_page = 0;
1441 framebuffer.render_page = 1;
1443 framebuffer.update_screen = doublebuf_pageflipping_update_screen;
1444 framebuffer.pages[0] = page0_ptr;
1445 framebuffer.pages[1] = page1_ptr;
1447 framebuffer.current_dirty.first_line
1448 = framebuffer.back_target->mode_info.height;
1449 framebuffer.current_dirty.last_line = 0;
1450 framebuffer.previous_dirty.first_line
1451 = framebuffer.back_target->mode_info.height;
1452 framebuffer.previous_dirty.last_line = 0;
1454 /* Set the framebuffer memory data pointer and display the right page. */
1455 err = set_page_in (framebuffer.displayed_page);
1456 if (err)
1458 grub_video_fb_delete_render_target (framebuffer.back_target);
1459 return err;
1461 framebuffer.set_page = set_page_in;
1463 return GRUB_ERR_NONE;
1466 /* Select the best double buffering mode available. */
1467 grub_err_t
1468 grub_video_fb_setup (unsigned int mode_type, unsigned int mode_mask,
1469 struct grub_video_mode_info *mode_info,
1470 volatile void *page0_ptr,
1471 grub_video_fb_set_page_t set_page_in,
1472 volatile void *page1_ptr)
1474 grub_err_t err;
1476 /* Do double buffering only if it's either requested or efficient. */
1477 if (set_page_in && grub_video_check_mode_flag (mode_type, mode_mask,
1478 GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED,
1481 mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
1482 mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP;
1484 err = doublebuf_pageflipping_init (mode_info, page0_ptr,
1485 set_page_in,
1486 page1_ptr);
1487 if (!err)
1489 framebuffer.render_target = framebuffer.back_target;
1490 return GRUB_ERR_NONE;
1493 mode_info->mode_type &= ~(GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED
1494 | GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP);
1496 grub_errno = GRUB_ERR_NONE;
1499 if (grub_video_check_mode_flag (mode_type, mode_mask,
1500 GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED,
1503 /* It was much nicer with the cast directly at function call but
1504 some older gcc versions don't accept it properly.*/
1505 void *tmp = (void *) page0_ptr;
1506 mode_info->mode_type |= (GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED
1507 | GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP);
1509 err = grub_video_fb_doublebuf_blit_init (&framebuffer.back_target,
1510 *mode_info,
1511 tmp);
1513 if (!err)
1515 framebuffer.render_target = framebuffer.back_target;
1516 return GRUB_ERR_NONE;
1519 mode_info->mode_type &= ~(GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED
1520 | GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP);
1522 grub_errno = GRUB_ERR_NONE;
1525 /* Fall back to no double buffering. */
1526 err = grub_video_fb_create_render_target_from_pointer (&framebuffer.back_target,
1527 mode_info,
1528 (void *) page0_ptr);
1530 if (err)
1531 return err;
1533 framebuffer.update_screen = 0;
1534 framebuffer.pages[0] = page0_ptr;
1535 framebuffer.displayed_page = 0;
1536 framebuffer.render_page = 0;
1537 framebuffer.set_page = 0;
1538 framebuffer.current_dirty.first_line
1539 = framebuffer.back_target->mode_info.height;
1540 framebuffer.current_dirty.last_line = 0;
1542 mode_info->mode_type &= ~GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
1544 framebuffer.render_target = framebuffer.back_target;
1546 return GRUB_ERR_NONE;
1550 grub_err_t
1551 grub_video_fb_swap_buffers (void)
1553 grub_err_t err;
1554 if (!framebuffer.update_screen)
1555 return GRUB_ERR_NONE;
1557 err = framebuffer.update_screen ();
1558 if (err)
1559 return err;
1561 return GRUB_ERR_NONE;
1564 grub_err_t
1565 grub_video_fb_get_info_and_fini (struct grub_video_mode_info *mode_info,
1566 void **framebuf)
1568 grub_memcpy (mode_info, &(framebuffer.back_target->mode_info),
1569 sizeof (*mode_info));
1571 /* We are about to load a kernel. Switch back to page zero, since some
1572 kernel drivers expect that. */
1573 if (framebuffer.set_page && framebuffer.displayed_page != 0)
1575 framebuffer.update_screen ();
1578 *framebuf = (void *) framebuffer.pages[framebuffer.displayed_page];
1580 grub_video_fb_fini ();
1582 return GRUB_ERR_NONE;