]> git.proxmox.com Git - grub2.git/blob - grub-core/video/fb/video_fb.c
merge with mainline
[grub2.git] / grub-core / video / fb / video_fb.c
1 /*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc.
4 *
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.
9 *
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.
14 *
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/>.
17 */
18
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
28 static struct
29 {
30 struct grub_video_fbrender_target *render_target;
31 struct grub_video_fbrender_target *front_target;
32 struct grub_video_fbrender_target *back_target;
33 struct grub_video_palette_data *palette;
34 unsigned int palette_size;
35 /* For page flipping strategy. */
36 int displayed_page; /* The page # that is the front buffer. */
37 int render_page; /* The page # that is the back buffer. */
38 grub_video_fb_set_page_t set_page;
39 char *offscreen_buffer;
40 grub_video_fb_doublebuf_update_screen_t update_screen;
41 } framebuffer;
42
43 /* Specify "standard" VGA palette, some video cards may
44 need this and this will also be used when using RGB modes. */
45 struct grub_video_palette_data grub_video_fbstd_colors[GRUB_VIDEO_FBSTD_NUMCOLORS] =
46 {
47 // {R, G, B, A}
48 {0x00, 0x00, 0x00, 0xFF}, // 0 = black
49 {0x00, 0x00, 0xA8, 0xFF}, // 1 = blue
50 {0x00, 0xA8, 0x00, 0xFF}, // 2 = green
51 {0x00, 0xA8, 0xA8, 0xFF}, // 3 = cyan
52 {0xA8, 0x00, 0x00, 0xFF}, // 4 = red
53 {0xA8, 0x00, 0xA8, 0xFF}, // 5 = magenta
54 {0xA8, 0x54, 0x00, 0xFF}, // 6 = brown
55 {0xA8, 0xA8, 0xA8, 0xFF}, // 7 = light gray
56
57 {0x54, 0x54, 0x54, 0xFF}, // 8 = dark gray
58 {0x54, 0x54, 0xFE, 0xFF}, // 9 = bright blue
59 {0x54, 0xFE, 0x54, 0xFF}, // 10 = bright green
60 {0x54, 0xFE, 0xFE, 0xFF}, // 11 = bright cyan
61 {0xFE, 0x54, 0x54, 0xFF}, // 12 = bright red
62 {0xFE, 0x54, 0xFE, 0xFF}, // 13 = bright magenta
63 {0xFE, 0xFE, 0x54, 0xFF}, // 14 = yellow
64 {0xFE, 0xFE, 0xFE, 0xFF} // 15 = white
65 };
66
67 grub_err_t
68 grub_video_fb_init (void)
69 {
70 grub_free (framebuffer.palette);
71 framebuffer.render_target = 0;
72 framebuffer.front_target = 0;
73 framebuffer.back_target = 0;
74 framebuffer.palette = 0;
75 framebuffer.palette_size = 0;
76 framebuffer.set_page = 0;
77 return GRUB_ERR_NONE;
78 }
79
80 grub_err_t
81 grub_video_fb_fini (void)
82 {
83 /* TODO: destroy render targets. */
84
85 grub_free (framebuffer.offscreen_buffer);
86 grub_free (framebuffer.palette);
87 framebuffer.render_target = 0;
88 framebuffer.front_target = 0;
89 framebuffer.back_target = 0;
90 framebuffer.palette = 0;
91 framebuffer.palette_size = 0;
92 framebuffer.set_page = 0;
93 framebuffer.offscreen_buffer = 0;
94 return GRUB_ERR_NONE;
95 }
96
97 grub_err_t
98 grub_video_fb_get_info (struct grub_video_mode_info *mode_info)
99 {
100 /* Copy mode info from active render target. */
101 grub_memcpy (mode_info, &framebuffer.render_target->mode_info,
102 sizeof (struct grub_video_mode_info));
103
104 return GRUB_ERR_NONE;
105 }
106
107 grub_err_t
108 grub_video_fb_get_palette (unsigned int start, unsigned int count,
109 struct grub_video_palette_data *palette_data)
110 {
111 unsigned int i;
112
113 /* Assume that we know everything from index color palette. */
114 for (i = 0; (i < count) && ((i + start) < framebuffer.palette_size); i++)
115 palette_data[i] = framebuffer.palette[start + i];
116
117 return GRUB_ERR_NONE;
118 }
119
120 grub_err_t
121 grub_video_fb_set_palette (unsigned int start, unsigned int count,
122 struct grub_video_palette_data *palette_data)
123 {
124 unsigned i;
125 if (start + count > framebuffer.palette_size)
126 {
127 framebuffer.palette_size = start + count;
128 framebuffer.palette = grub_realloc (framebuffer.palette,
129 sizeof (framebuffer.palette[0])
130 * framebuffer.palette_size);
131 if (!framebuffer.palette)
132 {
133 grub_video_fb_fini ();
134 return grub_errno;
135 }
136 }
137 for (i = 0; (i < count) && ((i + start) < framebuffer.palette_size); i++)
138 framebuffer.palette[start + i] = palette_data[i];
139 return GRUB_ERR_NONE;
140 }
141
142 grub_err_t
143 grub_video_fb_set_viewport (unsigned int x, unsigned int y,
144 unsigned int width, unsigned int height)
145 {
146 /* Make sure viewport is withing screen dimensions. If viewport was set
147 to be out of the region, mark its size as zero. */
148 if (x > framebuffer.render_target->mode_info.width)
149 {
150 x = 0;
151 width = 0;
152 }
153
154 if (y > framebuffer.render_target->mode_info.height)
155 {
156 y = 0;
157 height = 0;
158 }
159
160 if (x + width > framebuffer.render_target->mode_info.width)
161 width = framebuffer.render_target->mode_info.width - x;
162
163 if (y + height > framebuffer.render_target->mode_info.height)
164 height = framebuffer.render_target->mode_info.height - y;
165
166 framebuffer.render_target->viewport.x = x;
167 framebuffer.render_target->viewport.y = y;
168 framebuffer.render_target->viewport.width = width;
169 framebuffer.render_target->viewport.height = height;
170
171 return GRUB_ERR_NONE;
172 }
173
174 grub_err_t
175 grub_video_fb_get_viewport (unsigned int *x, unsigned int *y,
176 unsigned int *width, unsigned int *height)
177 {
178 if (x) *x = framebuffer.render_target->viewport.x;
179 if (y) *y = framebuffer.render_target->viewport.y;
180 if (width) *width = framebuffer.render_target->viewport.width;
181 if (height) *height = framebuffer.render_target->viewport.height;
182
183 return GRUB_ERR_NONE;
184 }
185
186 /* Maps color name to target optimized color format. */
187 grub_video_color_t
188 grub_video_fb_map_color (grub_uint32_t color_name)
189 {
190 /* TODO: implement color theme mapping code. */
191
192 if (color_name < framebuffer.palette_size)
193 {
194 if ((framebuffer.render_target->mode_info.mode_type
195 & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) != 0)
196 return color_name;
197 else
198 {
199 grub_video_color_t color;
200
201 color = grub_video_fb_map_rgb (framebuffer.palette[color_name].r,
202 framebuffer.palette[color_name].g,
203 framebuffer.palette[color_name].b);
204
205 return color;
206 }
207 }
208
209 return 0;
210 }
211
212 /* Maps RGB to target optimized color format. */
213 grub_video_color_t
214 grub_video_fb_map_rgb (grub_uint8_t red, grub_uint8_t green,
215 grub_uint8_t blue)
216 {
217 if ((framebuffer.render_target->mode_info.mode_type
218 & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) != 0)
219 {
220 int minindex = 0;
221 int delta = 0;
222 int tmp;
223 int val;
224 unsigned i;
225
226 /* Find best matching color. */
227 for (i = 0; i < framebuffer.palette_size; i++)
228 {
229 val = framebuffer.palette[i].r - red;
230 tmp = val * val;
231 val = framebuffer.palette[i].g - green;
232 tmp += val * val;
233 val = framebuffer.palette[i].b - blue;
234 tmp += val * val;
235
236 if (i == 0)
237 delta = tmp;
238
239 if (tmp < delta)
240 {
241 delta = tmp;
242 minindex = i;
243 if (tmp == 0)
244 break;
245 }
246 }
247
248 return minindex;
249 }
250 else if ((framebuffer.render_target->mode_info.mode_type
251 & GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP) != 0)
252 {
253 if (red == framebuffer.render_target->mode_info.fg_red
254 && green == framebuffer.render_target->mode_info.fg_green
255 && blue == framebuffer.render_target->mode_info.fg_blue)
256 return 1;
257 else
258 return 0;
259 }
260 else
261 {
262 grub_uint32_t value;
263 grub_uint8_t alpha = 255; /* Opaque color. */
264
265 red >>= 8 - framebuffer.render_target->mode_info.red_mask_size;
266 green >>= 8 - framebuffer.render_target->mode_info.green_mask_size;
267 blue >>= 8 - framebuffer.render_target->mode_info.blue_mask_size;
268 alpha >>= 8 - framebuffer.render_target->mode_info.reserved_mask_size;
269
270 value = red << framebuffer.render_target->mode_info.red_field_pos;
271 value |= green << framebuffer.render_target->mode_info.green_field_pos;
272 value |= blue << framebuffer.render_target->mode_info.blue_field_pos;
273 value |= alpha << framebuffer.render_target->mode_info.reserved_field_pos;
274
275 return value;
276 }
277
278 }
279
280 /* Maps RGBA to target optimized color format. */
281 grub_video_color_t
282 grub_video_fb_map_rgba (grub_uint8_t red, grub_uint8_t green,
283 grub_uint8_t blue, grub_uint8_t alpha)
284 {
285 if ((framebuffer.render_target->mode_info.mode_type
286 & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) != 0)
287 /* No alpha available in index color modes, just use
288 same value as in only RGB modes. */
289 return grub_video_fb_map_rgb (red, green, blue);
290 else if ((framebuffer.render_target->mode_info.mode_type
291 & GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP) != 0)
292 {
293 if (red == framebuffer.render_target->mode_info.fg_red
294 && green == framebuffer.render_target->mode_info.fg_green
295 && blue == framebuffer.render_target->mode_info.fg_blue
296 && alpha == framebuffer.render_target->mode_info.fg_alpha)
297 return 1;
298 else
299 return 0;
300 }
301 else
302 {
303 grub_uint32_t value;
304
305 red >>= 8 - framebuffer.render_target->mode_info.red_mask_size;
306 green >>= 8 - framebuffer.render_target->mode_info.green_mask_size;
307 blue >>= 8 - framebuffer.render_target->mode_info.blue_mask_size;
308 alpha >>= 8 - framebuffer.render_target->mode_info.reserved_mask_size;
309
310 value = red << framebuffer.render_target->mode_info.red_field_pos;
311 value |= green << framebuffer.render_target->mode_info.green_field_pos;
312 value |= blue << framebuffer.render_target->mode_info.blue_field_pos;
313 value |= alpha << framebuffer.render_target->mode_info.reserved_field_pos;
314
315 return value;
316 }
317 }
318
319 /* Splits target optimized format to components. */
320 grub_err_t
321 grub_video_fb_unmap_color (grub_video_color_t color,
322 grub_uint8_t *red, grub_uint8_t *green,
323 grub_uint8_t *blue, grub_uint8_t *alpha)
324 {
325 struct grub_video_fbblit_info target_info;
326
327 target_info.mode_info = &framebuffer.render_target->mode_info;
328 target_info.data = framebuffer.render_target->data;
329
330 grub_video_fb_unmap_color_int (&target_info, color, red, green, blue, alpha);
331
332 return GRUB_ERR_NONE;
333 }
334
335 /* Splits color in source format to components. */
336 void
337 grub_video_fb_unmap_color_int (struct grub_video_fbblit_info * source,
338 grub_video_color_t color,
339 grub_uint8_t *red, grub_uint8_t *green,
340 grub_uint8_t *blue, grub_uint8_t *alpha)
341 {
342 struct grub_video_mode_info *mode_info;
343 mode_info = source->mode_info;
344
345 if ((mode_info->mode_type
346 & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) != 0)
347 {
348 /* If we have an out-of-bounds color, return transparent black. */
349 if (color > 255)
350 {
351 *red = 0;
352 *green = 0;
353 *blue = 0;
354 *alpha = 0;
355 return;
356 }
357
358 *red = framebuffer.palette[color].r;
359 *green = framebuffer.palette[color].g;
360 *blue = framebuffer.palette[color].b;
361 *alpha = framebuffer.palette[color].a;
362 return;
363 }
364 else if ((mode_info->mode_type
365 & GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP) != 0)
366 {
367 if (color & 1)
368 {
369 *red = mode_info->fg_red;
370 *green = mode_info->fg_green;
371 *blue = mode_info->fg_blue;
372 *alpha = mode_info->fg_alpha;
373 }
374 else
375 {
376 *red = mode_info->bg_red;
377 *green = mode_info->bg_green;
378 *blue = mode_info->bg_blue;
379 *alpha = mode_info->bg_alpha;
380 }
381 }
382 else
383 {
384 grub_uint32_t tmp;
385
386 /* Get red component. */
387 tmp = color >> mode_info->red_field_pos;
388 tmp &= (1 << mode_info->red_mask_size) - 1;
389 tmp <<= 8 - mode_info->red_mask_size;
390 tmp |= (1 << (8 - mode_info->red_mask_size)) - 1;
391 *red = tmp & 0xFF;
392
393 /* Get green component. */
394 tmp = color >> mode_info->green_field_pos;
395 tmp &= (1 << mode_info->green_mask_size) - 1;
396 tmp <<= 8 - mode_info->green_mask_size;
397 tmp |= (1 << (8 - mode_info->green_mask_size)) - 1;
398 *green = tmp & 0xFF;
399
400 /* Get blue component. */
401 tmp = color >> mode_info->blue_field_pos;
402 tmp &= (1 << mode_info->blue_mask_size) - 1;
403 tmp <<= 8 - mode_info->blue_mask_size;
404 tmp |= (1 << (8 - mode_info->blue_mask_size)) - 1;
405 *blue = tmp & 0xFF;
406
407 /* Get alpha component. */
408 if (source->mode_info->reserved_mask_size > 0)
409 {
410 tmp = color >> mode_info->reserved_field_pos;
411 tmp &= (1 << mode_info->reserved_mask_size) - 1;
412 tmp <<= 8 - mode_info->reserved_mask_size;
413 tmp |= (1 << (8 - mode_info->reserved_mask_size)) - 1;
414 }
415 else
416 /* If there is no alpha component, assume it opaque. */
417 tmp = 255;
418
419 *alpha = tmp & 0xFF;
420 }
421 }
422
423 grub_err_t
424 grub_video_fb_fill_rect (grub_video_color_t color, int x, int y,
425 unsigned int width, unsigned int height)
426 {
427 struct grub_video_fbblit_info target;
428
429 /* Make sure there is something to do. */
430 if ((x >= (int)framebuffer.render_target->viewport.width) || (x + (int)width < 0))
431 return GRUB_ERR_NONE;
432 if ((y >= (int)framebuffer.render_target->viewport.height) || (y + (int)height < 0))
433 return GRUB_ERR_NONE;
434
435 /* Do not allow drawing out of viewport. */
436 if (x < 0)
437 {
438 width += x;
439 x = 0;
440 }
441 if (y < 0)
442 {
443 height += y;
444 y = 0;
445 }
446
447 if ((x + width) > framebuffer.render_target->viewport.width)
448 width = framebuffer.render_target->viewport.width - x;
449 if ((y + height) > framebuffer.render_target->viewport.height)
450 height = framebuffer.render_target->viewport.height - y;
451
452 /* Add viewport offset. */
453 x += framebuffer.render_target->viewport.x;
454 y += framebuffer.render_target->viewport.y;
455
456 /* Use fbblit_info to encapsulate rendering. */
457 target.mode_info = &framebuffer.render_target->mode_info;
458 target.data = framebuffer.render_target->data;
459
460 /* Try to figure out more optimized version. Note that color is already
461 mapped to target format so we can make assumptions based on that. */
462 if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888)
463 {
464 grub_video_fbfill_direct32 (&target, color, x, y,
465 width, height);
466 return GRUB_ERR_NONE;
467 }
468 else if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888)
469 {
470 grub_video_fbfill_direct32 (&target, color, x, y,
471 width, height);
472 return GRUB_ERR_NONE;
473 }
474 else if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888)
475 {
476 grub_video_fbfill_direct24 (&target, color, x, y,
477 width, height);
478 return GRUB_ERR_NONE;
479 }
480 else if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_565)
481 {
482 grub_video_fbfill_direct16 (&target, color, x, y,
483 width, height);
484 return GRUB_ERR_NONE;
485 }
486 else if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGR_565)
487 {
488 grub_video_fbfill_direct16 (&target, color, x, y,
489 width, height);
490 return GRUB_ERR_NONE;
491 }
492 else if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR)
493 {
494 grub_video_fbfill_direct8 (&target, color, x, y,
495 width, height);
496 return GRUB_ERR_NONE;
497 }
498
499 /* No optimized version found, use default (slow) filler. */
500 grub_video_fbfill (&target, color, x, y, width, height);
501
502 return GRUB_ERR_NONE;
503 }
504
505 /* NOTE: This function assumes that given coordinates are within bounds of
506 handled data. */
507 static void
508 common_blitter (struct grub_video_fbblit_info *target,
509 struct grub_video_fbblit_info *source,
510 enum grub_video_blit_operators oper, int x, int y,
511 unsigned int width, unsigned int height,
512 int offset_x, int offset_y)
513 {
514 if (oper == GRUB_VIDEO_BLIT_REPLACE)
515 {
516 /* Try to figure out more optimized version for replace operator. */
517 if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888)
518 {
519 if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888)
520 {
521 grub_video_fbblit_replace_directN (target, source,
522 x, y, width, height,
523 offset_x, offset_y);
524 return;
525 }
526 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888)
527 {
528 grub_video_fbblit_replace_BGRX8888_RGBX8888 (target, source,
529 x, y, width, height,
530 offset_x, offset_y);
531 return;
532 }
533 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGR_888)
534 {
535 grub_video_fbblit_replace_BGR888_RGBX8888 (target, source,
536 x, y, width, height,
537 offset_x, offset_y);
538 return;
539 }
540 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888)
541 {
542 grub_video_fbblit_replace_RGB888_RGBX8888 (target, source,
543 x, y, width, height,
544 offset_x, offset_y);
545 return;
546 }
547 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR)
548 {
549 grub_video_fbblit_replace_index_RGBX8888 (target, source,
550 x, y, width, height,
551 offset_x, offset_y);
552 return;
553 }
554 }
555 else if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888)
556 {
557 if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888)
558 {
559 grub_video_fbblit_replace_BGRX8888_RGB888 (target, source,
560 x, y, width, height,
561 offset_x, offset_y);
562 return;
563 }
564 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888)
565 {
566 grub_video_fbblit_replace_RGBX8888_RGB888 (target, source,
567 x, y, width, height,
568 offset_x, offset_y);
569 return;
570 }
571 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGR_888)
572 {
573 grub_video_fbblit_replace_BGR888_RGB888 (target, source,
574 x, y, width, height,
575 offset_x, offset_y);
576 return;
577 }
578 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888)
579 {
580 grub_video_fbblit_replace_directN (target, source,
581 x, y, width, height,
582 offset_x, offset_y);
583 return;
584 }
585 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR)
586 {
587 grub_video_fbblit_replace_index_RGB888 (target, source,
588 x, y, width, height,
589 offset_x, offset_y);
590 return;
591 }
592 }
593 else if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888)
594 {
595 if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888)
596 {
597 grub_video_fbblit_replace_directN (target, source,
598 x, y, width, height,
599 offset_x, offset_y);
600 return;
601 }
602 }
603 else if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR)
604 {
605 if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR)
606 {
607 grub_video_fbblit_replace_directN (target, source,
608 x, y, width, height,
609 offset_x, offset_y);
610 return;
611 }
612 }
613 else if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED)
614 {
615 if (target->mode_info->bpp == 32)
616 {
617 grub_video_fbblit_replace_32bit_1bit (target, source,
618 x, y, width, height,
619 offset_x, offset_y);
620 return;
621 }
622 else if (target->mode_info->bpp == 24)
623 {
624 grub_video_fbblit_replace_24bit_1bit (target, source,
625 x, y, width, height,
626 offset_x, offset_y);
627 return;
628 }
629 else if (target->mode_info->bpp == 16)
630 {
631 grub_video_fbblit_replace_16bit_1bit (target, source,
632 x, y, width, height,
633 offset_x, offset_y);
634 return;
635 }
636 else if (target->mode_info->bpp == 8)
637 {
638 grub_video_fbblit_replace_8bit_1bit (target, source,
639 x, y, width, height,
640 offset_x, offset_y);
641 return;
642 }
643 }
644
645 /* No optimized replace operator found, use default (slow) blitter. */
646 grub_video_fbblit_replace (target, source, x, y, width, height,
647 offset_x, offset_y);
648 }
649 else
650 {
651 /* Try to figure out more optimized blend operator. */
652 if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888)
653 {
654 if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888)
655 {
656 grub_video_fbblit_blend_BGRA8888_RGBA8888 (target, source,
657 x, y, width, height,
658 offset_x, offset_y);
659 return;
660 }
661 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888)
662 {
663 grub_video_fbblit_blend_RGBA8888_RGBA8888 (target, source,
664 x, y, width, height,
665 offset_x, offset_y);
666 return;
667 }
668 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGR_888)
669 {
670 grub_video_fbblit_blend_BGR888_RGBA8888 (target, source,
671 x, y, width, height,
672 offset_x, offset_y);
673 return;
674 }
675 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888)
676 {
677 grub_video_fbblit_blend_RGB888_RGBA8888 (target, source,
678 x, y, width, height,
679 offset_x, offset_y);
680 return;
681 }
682 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR)
683 {
684 grub_video_fbblit_blend_index_RGBA8888 (target, source,
685 x, y, width, height,
686 offset_x, offset_y);
687 return;
688 }
689 }
690 else if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888)
691 {
692 /* Note: There is really no alpha information here, so blend is
693 changed to replace. */
694
695 if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888)
696 {
697 grub_video_fbblit_replace_BGRX8888_RGB888 (target, source,
698 x, y, width, height,
699 offset_x, offset_y);
700 return;
701 }
702 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888)
703 {
704 grub_video_fbblit_replace_RGBX8888_RGB888 (target, source,
705 x, y, width, height,
706 offset_x, offset_y);
707 return;
708 }
709 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGR_888)
710 {
711 grub_video_fbblit_replace_BGR888_RGB888 (target, source,
712 x, y, width, height,
713 offset_x, offset_y);
714 return;
715 }
716 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888)
717 {
718 grub_video_fbblit_replace_directN (target, source,
719 x, y, width, height,
720 offset_x, offset_y);
721 return;
722 }
723 else if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR)
724 {
725 grub_video_fbblit_replace_index_RGB888 (target, source,
726 x, y, width, height,
727 offset_x, offset_y);
728 return;
729 }
730 }
731 else if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED)
732 {
733 if (target->mode_info->blit_format
734 == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888
735 || target->mode_info->blit_format
736 == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888)
737 {
738 grub_video_fbblit_blend_XXXA8888_1bit (target, source,
739 x, y, width, height,
740 offset_x, offset_y);
741 return;
742 }
743 else if (target->mode_info->blit_format
744 == GRUB_VIDEO_BLIT_FORMAT_BGR_888
745 || target->mode_info->blit_format
746 == GRUB_VIDEO_BLIT_FORMAT_RGB_888)
747 {
748 grub_video_fbblit_blend_XXX888_1bit (target, source,
749 x, y, width, height,
750 offset_x, offset_y);
751 return;
752 }
753 else if (target->mode_info->blit_format
754 == GRUB_VIDEO_BLIT_FORMAT_BGR_565
755 || target->mode_info->blit_format
756 == GRUB_VIDEO_BLIT_FORMAT_RGB_565)
757 {
758 grub_video_fbblit_blend_XXX565_1bit (target, source,
759 x, y, width, height,
760 offset_x, offset_y);
761 return;
762 }
763
764 }
765
766
767 /* No optimized blend operation found, use default (slow) blitter. */
768 grub_video_fbblit_blend (target, source, x, y, width, height,
769 offset_x, offset_y);
770 }
771 }
772
773 grub_err_t
774 grub_video_fb_blit_bitmap (struct grub_video_bitmap *bitmap,
775 enum grub_video_blit_operators oper, int x, int y,
776 int offset_x, int offset_y,
777 unsigned int width, unsigned int height)
778 {
779 struct grub_video_fbblit_info source;
780 struct grub_video_fbblit_info target;
781
782 /* Make sure there is something to do. */
783 if ((width == 0) || (height == 0))
784 return GRUB_ERR_NONE;
785 if ((x >= (int)framebuffer.render_target->viewport.width) || (x + (int)width < 0))
786 return GRUB_ERR_NONE;
787 if ((y >= (int)framebuffer.render_target->viewport.height) || (y + (int)height < 0))
788 return GRUB_ERR_NONE;
789 if ((x + (int)bitmap->mode_info.width) < 0)
790 return GRUB_ERR_NONE;
791 if ((y + (int)bitmap->mode_info.height) < 0)
792 return GRUB_ERR_NONE;
793 if ((offset_x >= (int)bitmap->mode_info.width)
794 || (offset_x + (int)width < 0))
795 return GRUB_ERR_NONE;
796 if ((offset_y >= (int)bitmap->mode_info.height)
797 || (offset_y + (int)height < 0))
798 return GRUB_ERR_NONE;
799
800 /* If we have negative coordinates, optimize drawing to minimum. */
801 if (offset_x < 0)
802 {
803 width += offset_x;
804 x -= offset_x;
805 offset_x = 0;
806 }
807
808 if (offset_y < 0)
809 {
810 height += offset_y;
811 y -= offset_y;
812 offset_y = 0;
813 }
814
815 if (x < 0)
816 {
817 width += x;
818 offset_x -= x;
819 x = 0;
820 }
821
822 if (y < 0)
823 {
824 height += y;
825 offset_y -= y;
826 y = 0;
827 }
828
829 /* Do not allow drawing out of viewport. */
830 if ((x + width) > framebuffer.render_target->viewport.width)
831 width = framebuffer.render_target->viewport.width - x;
832 if ((y + height) > framebuffer.render_target->viewport.height)
833 height = framebuffer.render_target->viewport.height - y;
834
835 if ((offset_x + width) > bitmap->mode_info.width)
836 width = bitmap->mode_info.width - offset_x;
837 if ((offset_y + height) > bitmap->mode_info.height)
838 height = bitmap->mode_info.height - offset_y;
839
840 /* Limit drawing to source render target dimensions. */
841 if (width > bitmap->mode_info.width)
842 width = bitmap->mode_info.width;
843
844 if (height > bitmap->mode_info.height)
845 height = bitmap->mode_info.height;
846
847 /* Add viewport offset. */
848 x += framebuffer.render_target->viewport.x;
849 y += framebuffer.render_target->viewport.y;
850
851 /* Use fbblit_info to encapsulate rendering. */
852 source.mode_info = &bitmap->mode_info;
853 source.data = bitmap->data;
854 target.mode_info = &framebuffer.render_target->mode_info;
855 target.data = framebuffer.render_target->data;
856
857 /* Do actual blitting. */
858 common_blitter (&target, &source, oper, x, y, width, height,
859 offset_x, offset_y);
860
861 return GRUB_ERR_NONE;
862 }
863
864 grub_err_t
865 grub_video_fb_blit_render_target (struct grub_video_fbrender_target *source,
866 enum grub_video_blit_operators oper,
867 int x, int y, int offset_x, int offset_y,
868 unsigned int width, unsigned int height)
869 {
870 struct grub_video_fbblit_info source_info;
871 struct grub_video_fbblit_info target_info;
872
873 /* Make sure there is something to do. */
874 if ((width == 0) || (height == 0))
875 return GRUB_ERR_NONE;
876 if ((x >= (int)framebuffer.render_target->viewport.width) || (x + (int)width < 0))
877 return GRUB_ERR_NONE;
878 if ((y >= (int)framebuffer.render_target->viewport.height) || (y + (int)height < 0))
879 return GRUB_ERR_NONE;
880 if ((x + (int)source->mode_info.width) < 0)
881 return GRUB_ERR_NONE;
882 if ((y + (int)source->mode_info.height) < 0)
883 return GRUB_ERR_NONE;
884 if ((offset_x >= (int)source->mode_info.width)
885 || (offset_x + (int)width < 0))
886 return GRUB_ERR_NONE;
887 if ((offset_y >= (int)source->mode_info.height)
888 || (offset_y + (int)height < 0))
889 return GRUB_ERR_NONE;
890
891 /* If we have negative coordinates, optimize drawing to minimum. */
892 if (offset_x < 0)
893 {
894 width += offset_x;
895 x -= offset_x;
896 offset_x = 0;
897 }
898
899 if (offset_y < 0)
900 {
901 height += offset_y;
902 y -= offset_y;
903 offset_y = 0;
904 }
905
906 if (x < 0)
907 {
908 width += x;
909 offset_x -= x;
910 x = 0;
911 }
912
913 if (y < 0)
914 {
915 height += y;
916 offset_y -= y;
917 y = 0;
918 }
919
920 /* Do not allow drawing out of viewport. */
921 if ((x + width) > framebuffer.render_target->viewport.width)
922 width = framebuffer.render_target->viewport.width - x;
923 if ((y + height) > framebuffer.render_target->viewport.height)
924 height = framebuffer.render_target->viewport.height - y;
925
926 if ((offset_x + width) > source->mode_info.width)
927 width = source->mode_info.width - offset_x;
928 if ((offset_y + height) > source->mode_info.height)
929 height = source->mode_info.height - offset_y;
930
931 /* Limit drawing to source render target dimensions. */
932 if (width > source->mode_info.width)
933 width = source->mode_info.width;
934
935 if (height > source->mode_info.height)
936 height = source->mode_info.height;
937
938 /* Add viewport offset. */
939 x += framebuffer.render_target->viewport.x;
940 y += framebuffer.render_target->viewport.y;
941
942 /* Use fbblit_info to encapsulate rendering. */
943 source_info.mode_info = &source->mode_info;
944 source_info.data = source->data;
945 target_info.mode_info = &framebuffer.render_target->mode_info;
946 target_info.data = framebuffer.render_target->data;
947
948 /* Do actual blitting. */
949 common_blitter (&target_info, &source_info, oper, x, y, width, height,
950 offset_x, offset_y);
951
952 return GRUB_ERR_NONE;
953 }
954
955 grub_err_t
956 grub_video_fb_scroll (grub_video_color_t color, int dx, int dy)
957 {
958 int width;
959 int height;
960 int src_x;
961 int src_y;
962 int dst_x;
963 int dst_y;
964
965 /* 1. Check if we have something to do. */
966 if ((dx == 0) && (dy == 0))
967 return GRUB_ERR_NONE;
968
969 width = framebuffer.render_target->viewport.width - grub_abs (dx);
970 height = framebuffer.render_target->viewport.height - grub_abs (dy);
971
972 if (dx < 0)
973 {
974 src_x = framebuffer.render_target->viewport.x - dx;
975 dst_x = framebuffer.render_target->viewport.x;
976 }
977 else
978 {
979 src_x = framebuffer.render_target->viewport.x;
980 dst_x = framebuffer.render_target->viewport.x + dx;
981 }
982
983 if (dy < 0)
984 {
985 src_y = framebuffer.render_target->viewport.y - dy;
986 dst_y = framebuffer.render_target->viewport.y;
987 }
988 else
989 {
990 src_y = framebuffer.render_target->viewport.y;
991 dst_y = framebuffer.render_target->viewport.y + dy;
992 }
993
994 /* 2. Check if there is need to copy data. */
995 if ((grub_abs (dx) < framebuffer.render_target->viewport.width)
996 && (grub_abs (dy) < framebuffer.render_target->viewport.height))
997 {
998 /* 3. Move data in render target. */
999 struct grub_video_fbblit_info target;
1000 int i, j;
1001 int linedelta, linelen;
1002
1003 target.mode_info = &framebuffer.render_target->mode_info;
1004 target.data = framebuffer.render_target->data;
1005
1006 linedelta = target.mode_info->pitch
1007 - width * target.mode_info->bytes_per_pixel;
1008 linelen = width * target.mode_info->bytes_per_pixel;
1009 #define DO_SCROLL \
1010 /* Check vertical direction of the move. */ \
1011 if (dy < 0 || (dy == 0 && dx < 0)) \
1012 { \
1013 dst = (void *) grub_video_fb_get_video_ptr (&target, \
1014 dst_x, dst_y); \
1015 src = (void *) grub_video_fb_get_video_ptr (&target, \
1016 src_x, src_y); \
1017 /* 3a. Move data upwards. */ \
1018 for (j = 0; j < height; j++) \
1019 { \
1020 for (i = 0; i < linelen; i++) \
1021 *(dst++) = *(src++); \
1022 dst += linedelta; \
1023 src += linedelta; \
1024 } \
1025 } \
1026 else \
1027 { \
1028 /* 3b. Move data downwards. */ \
1029 dst = (void *) grub_video_fb_get_video_ptr (&target, \
1030 dst_x + width, \
1031 dst_y + height - 1); \
1032 src = (void *) grub_video_fb_get_video_ptr (&target, \
1033 src_x + width, \
1034 src_y + height - 1); \
1035 dst--; \
1036 src--; \
1037 for (j = 0; j < height; j++) \
1038 { \
1039 for (i = 0; i < linelen; i++) \
1040 *(dst--) = *(src--); \
1041 dst -= linedelta; \
1042 src -= linedelta; \
1043 } \
1044 }
1045
1046 /* If everything is aligned on 32-bit use 32-bit copy. */
1047 if ((grub_addr_t) grub_video_fb_get_video_ptr (&target, src_x, src_y)
1048 % sizeof (grub_uint32_t) == 0
1049 && (grub_addr_t) grub_video_fb_get_video_ptr (&target, dst_x, dst_y)
1050 % sizeof (grub_uint32_t) == 0
1051 && linelen % sizeof (grub_uint32_t) == 0
1052 && linedelta % sizeof (grub_uint32_t) == 0)
1053 {
1054 grub_uint32_t *src, *dst;
1055 linelen /= sizeof (grub_uint32_t);
1056 linedelta /= sizeof (grub_uint32_t);
1057 DO_SCROLL
1058 }
1059 /* If everything is aligned on 16-bit use 16-bit copy. */
1060 else if ((grub_addr_t) grub_video_fb_get_video_ptr (&target, src_x, src_y)
1061 % sizeof (grub_uint16_t) == 0
1062 && (grub_addr_t) grub_video_fb_get_video_ptr (&target,
1063 dst_x, dst_y)
1064 % sizeof (grub_uint16_t) == 0
1065 && linelen % sizeof (grub_uint16_t) == 0
1066 && linedelta % sizeof (grub_uint16_t) == 0)
1067 {
1068 grub_uint16_t *src, *dst;
1069 linelen /= sizeof (grub_uint16_t);
1070 linedelta /= sizeof (grub_uint16_t);
1071 DO_SCROLL
1072 }
1073 /* If not aligned at all use 8-bit copy. */
1074 else
1075 {
1076 grub_uint8_t *src, *dst;
1077 DO_SCROLL
1078 }
1079 }
1080
1081 /* 4. Fill empty space with specified color. In this implementation
1082 there might be colliding areas but at the moment there is no need
1083 to optimize this. */
1084
1085 /* 4a. Fill top & bottom parts. */
1086 if (dy > 0)
1087 grub_video_fb_fill_rect (color, 0, 0, framebuffer.render_target->viewport.width, dy);
1088 else if (dy < 0)
1089 {
1090 if (framebuffer.render_target->viewport.height < grub_abs (dy))
1091 dy = -framebuffer.render_target->viewport.height;
1092
1093 grub_video_fb_fill_rect (color, 0, framebuffer.render_target->viewport.height + dy,
1094 framebuffer.render_target->viewport.width, -dy);
1095 }
1096
1097 /* 4b. Fill left & right parts. */
1098 if (dx > 0)
1099 grub_video_fb_fill_rect (color, 0, 0,
1100 dx, framebuffer.render_target->viewport.height);
1101 else if (dx < 0)
1102 {
1103 if (framebuffer.render_target->viewport.width < grub_abs (dx))
1104 dx = -framebuffer.render_target->viewport.width;
1105
1106 grub_video_fb_fill_rect (color, framebuffer.render_target->viewport.width + dx, 0,
1107 -dx, framebuffer.render_target->viewport.height);
1108 }
1109
1110 return GRUB_ERR_NONE;
1111 }
1112
1113
1114 grub_err_t
1115 grub_video_fb_create_render_target (struct grub_video_fbrender_target **result,
1116 unsigned int width, unsigned int height,
1117 unsigned int mode_type __attribute__ ((unused)))
1118 {
1119 struct grub_video_fbrender_target *target;
1120 unsigned int size;
1121
1122 /* Validate arguments. */
1123 if ((! result)
1124 || (width == 0)
1125 || (height == 0))
1126 return grub_error (GRUB_ERR_BAD_ARGUMENT,
1127 "invalid argument given");
1128
1129 /* Allocate memory for render target. */
1130 target = grub_malloc (sizeof (struct grub_video_fbrender_target));
1131 if (! target)
1132 return grub_errno;
1133
1134 /* TODO: Implement other types too.
1135 Currently only 32bit render targets are supported. */
1136
1137 /* Mark render target as allocated. */
1138 target->is_allocated = 1;
1139
1140 /* Maximize viewport. */
1141 target->viewport.x = 0;
1142 target->viewport.y = 0;
1143 target->viewport.width = width;
1144 target->viewport.height = height;
1145
1146 /* Setup render target format. */
1147 target->mode_info.width = width;
1148 target->mode_info.height = height;
1149 target->mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_RGB
1150 | GRUB_VIDEO_MODE_TYPE_ALPHA;
1151 target->mode_info.bpp = 32;
1152 target->mode_info.bytes_per_pixel = 4;
1153 target->mode_info.pitch = target->mode_info.bytes_per_pixel * width;
1154 target->mode_info.number_of_colors = framebuffer.palette_size; /* Emulated palette. */
1155 target->mode_info.red_mask_size = 8;
1156 target->mode_info.red_field_pos = 0;
1157 target->mode_info.green_mask_size = 8;
1158 target->mode_info.green_field_pos = 8;
1159 target->mode_info.blue_mask_size = 8;
1160 target->mode_info.blue_field_pos = 16;
1161 target->mode_info.reserved_mask_size = 8;
1162 target->mode_info.reserved_field_pos = 24;
1163
1164 target->mode_info.blit_format = grub_video_get_blit_format (&target->mode_info);
1165
1166 /* Calculate size needed for the data. */
1167 size = (width * target->mode_info.bytes_per_pixel) * height;
1168
1169 target->data = grub_malloc (size);
1170 if (! target->data)
1171 {
1172 grub_free (target);
1173 return grub_errno;
1174 }
1175
1176 /* Clear render target with black and maximum transparency. */
1177 grub_memset (target->data, 0, size);
1178
1179 /* TODO: Add render target to render target list. */
1180
1181 /* Save result to caller. */
1182 *result = target;
1183
1184 return GRUB_ERR_NONE;
1185 }
1186
1187 grub_err_t
1188 grub_video_fb_create_render_target_from_pointer (struct grub_video_fbrender_target **result,
1189 const struct grub_video_mode_info *mode_info,
1190 void *ptr)
1191 {
1192 struct grub_video_fbrender_target *target;
1193 unsigned y;
1194
1195 /* Allocate memory for render target. */
1196 target = grub_malloc (sizeof (struct grub_video_fbrender_target));
1197 if (! target)
1198 return grub_errno;
1199
1200 /* Mark framebuffer memory as non allocated. */
1201 target->is_allocated = 0;
1202 target->data = ptr;
1203
1204 grub_memcpy (&(target->mode_info), mode_info, sizeof (target->mode_info));
1205
1206 /* Reset viewport to match new mode. */
1207 target->viewport.x = 0;
1208 target->viewport.y = 0;
1209 target->viewport.width = mode_info->width;
1210 target->viewport.height = mode_info->height;
1211
1212 /* Clear render target with black and maximum transparency. */
1213 for (y = 0; y < mode_info->height; y++)
1214 grub_memset (target->data + mode_info->pitch * y, 0,
1215 mode_info->bytes_per_pixel * mode_info->width);
1216
1217 /* Save result to caller. */
1218 *result = target;
1219
1220 return GRUB_ERR_NONE;
1221 }
1222
1223 grub_err_t
1224 grub_video_fb_delete_render_target (struct grub_video_fbrender_target *target)
1225 {
1226 /* If there is no target, then just return without error. */
1227 if (! target)
1228 return GRUB_ERR_NONE;
1229
1230 /* TODO: Delist render target from render target list. */
1231
1232 /* If this is software render target, free it's memory. */
1233 if (target->is_allocated)
1234 grub_free (target->data);
1235
1236 /* Free render target. */
1237 grub_free (target);
1238
1239 return GRUB_ERR_NONE;
1240 }
1241
1242 grub_err_t
1243 grub_video_fb_set_active_render_target (struct grub_video_fbrender_target *target)
1244 {
1245 if (target == (struct grub_video_fbrender_target *)
1246 GRUB_VIDEO_RENDER_TARGET_DISPLAY)
1247 target = framebuffer.back_target;
1248
1249 if (! target->data)
1250 return grub_error (GRUB_ERR_BAD_ARGUMENT,
1251 "invalid render target given");
1252
1253 framebuffer.render_target = target;
1254
1255 return GRUB_ERR_NONE;
1256 }
1257
1258 grub_err_t
1259 grub_video_fb_get_active_render_target (struct grub_video_fbrender_target **target)
1260 {
1261 *target = framebuffer.render_target;
1262
1263 if (*target == framebuffer.back_target)
1264 *target = (struct grub_video_fbrender_target *) GRUB_VIDEO_RENDER_TARGET_DISPLAY;
1265
1266 return GRUB_ERR_NONE;
1267 }
1268
1269 static grub_err_t
1270 doublebuf_blit_update_screen (struct grub_video_fbrender_target *front,
1271 struct grub_video_fbrender_target *back)
1272 {
1273 grub_memcpy (front->data, back->data,
1274 front->mode_info.pitch * front->mode_info.height);
1275 return GRUB_ERR_NONE;
1276 }
1277
1278 static grub_err_t
1279 grub_video_fb_doublebuf_blit_init (struct grub_video_fbrender_target **front,
1280 struct grub_video_fbrender_target **back,
1281 struct grub_video_mode_info mode_info,
1282 void *framebuf)
1283 {
1284 grub_err_t err;
1285 int page_size = mode_info.pitch * mode_info.height;
1286
1287 err = grub_video_fb_create_render_target_from_pointer (front, &mode_info,
1288 framebuf);
1289 if (err)
1290 return err;
1291
1292 framebuffer.offscreen_buffer = grub_malloc (page_size);
1293 if (! framebuffer.offscreen_buffer)
1294 {
1295 grub_video_fb_delete_render_target (*front);
1296 *front = 0;
1297 return grub_errno;
1298 }
1299
1300 err = grub_video_fb_create_render_target_from_pointer (back, &mode_info,
1301 framebuffer.offscreen_buffer);
1302
1303 if (err)
1304 {
1305 grub_video_fb_delete_render_target (*front);
1306 grub_free (framebuffer.offscreen_buffer);
1307 framebuffer.offscreen_buffer = 0;
1308 *front = 0;
1309 return grub_errno;
1310 }
1311 (*back)->is_allocated = 1;
1312
1313 framebuffer.update_screen = doublebuf_blit_update_screen;
1314
1315 return GRUB_ERR_NONE;
1316 }
1317
1318 static grub_err_t
1319 doublebuf_pageflipping_update_screen (struct grub_video_fbrender_target *front
1320 __attribute__ ((unused)),
1321 struct grub_video_fbrender_target *back
1322 __attribute__ ((unused)))
1323 {
1324 int new_displayed_page;
1325 struct grub_video_fbrender_target *target;
1326 grub_err_t err;
1327
1328 /* Swap the page numbers in the framebuffer struct. */
1329 new_displayed_page = framebuffer.render_page;
1330 framebuffer.render_page = framebuffer.displayed_page;
1331 framebuffer.displayed_page = new_displayed_page;
1332
1333 err = framebuffer.set_page (framebuffer.displayed_page);
1334 if (err)
1335 {
1336 /* Restore previous state. */
1337 framebuffer.render_page = framebuffer.displayed_page;
1338 framebuffer.displayed_page = new_displayed_page;
1339 return err;
1340 }
1341
1342 target = framebuffer.back_target;
1343 framebuffer.back_target = framebuffer.front_target;
1344 framebuffer.front_target = target;
1345
1346 if (framebuffer.front_target->mode_info.mode_type
1347 & GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP)
1348 grub_memcpy (framebuffer.back_target->data, framebuffer.front_target->data,
1349 framebuffer.back_target->mode_info.pitch
1350 * framebuffer.back_target->mode_info.height);
1351
1352 err = grub_video_fb_get_active_render_target (&target);
1353 if (err)
1354 return err;
1355
1356 if (framebuffer.render_target == framebuffer.back_target)
1357 framebuffer.render_target = framebuffer.front_target;
1358 else if (framebuffer.render_target == framebuffer.front_target)
1359 framebuffer.render_target = framebuffer.back_target;
1360
1361 return err;
1362 }
1363
1364 static grub_err_t
1365 doublebuf_pageflipping_init (struct grub_video_mode_info *mode_info,
1366 volatile void *page0_ptr,
1367 grub_video_fb_set_page_t set_page_in,
1368 volatile void *page1_ptr)
1369 {
1370 grub_err_t err;
1371
1372 framebuffer.displayed_page = 0;
1373 framebuffer.render_page = 1;
1374
1375 framebuffer.update_screen = doublebuf_pageflipping_update_screen;
1376
1377 err = grub_video_fb_create_render_target_from_pointer (&framebuffer.front_target,
1378 mode_info,
1379 (void *) page0_ptr);
1380 if (err)
1381 return err;
1382
1383 err = grub_video_fb_create_render_target_from_pointer (&framebuffer.back_target,
1384 mode_info,
1385 (void *) page1_ptr);
1386 if (err)
1387 {
1388 grub_video_fb_delete_render_target (framebuffer.front_target);
1389 return err;
1390 }
1391
1392 /* Set the framebuffer memory data pointer and display the right page. */
1393 err = set_page_in (framebuffer.displayed_page);
1394 if (err)
1395 {
1396 grub_video_fb_delete_render_target (framebuffer.front_target);
1397 grub_video_fb_delete_render_target (framebuffer.back_target);
1398 return err;
1399 }
1400 framebuffer.set_page = set_page_in;
1401
1402 return GRUB_ERR_NONE;
1403 }
1404
1405 /* Select the best double buffering mode available. */
1406 grub_err_t
1407 grub_video_fb_setup (unsigned int mode_type, unsigned int mode_mask,
1408 struct grub_video_mode_info *mode_info,
1409 volatile void *page0_ptr,
1410 grub_video_fb_set_page_t set_page_in,
1411 volatile void *page1_ptr)
1412 {
1413 grub_err_t err;
1414 int updating_swap_needed;
1415
1416 updating_swap_needed
1417 = grub_video_check_mode_flag (mode_type, mode_mask,
1418 GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP, 0);
1419
1420 /* Do double buffering only if it's either requested or efficient. */
1421 if (set_page_in && grub_video_check_mode_flag (mode_type, mode_mask,
1422 GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED,
1423 !updating_swap_needed))
1424 {
1425 mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
1426 if (updating_swap_needed)
1427 mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP;
1428
1429 err = doublebuf_pageflipping_init (mode_info, page0_ptr,
1430 set_page_in,
1431 page1_ptr);
1432 if (!err)
1433 {
1434 framebuffer.render_target = framebuffer.back_target;
1435 return GRUB_ERR_NONE;
1436 }
1437
1438 mode_info->mode_type &= ~(GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED
1439 | GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP);
1440
1441 grub_errno = GRUB_ERR_NONE;
1442 }
1443
1444 if (grub_video_check_mode_flag (mode_type, mode_mask,
1445 GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED,
1446 0))
1447 {
1448 mode_info->mode_type |= (GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED
1449 | GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP);
1450
1451 err = grub_video_fb_doublebuf_blit_init (&framebuffer.front_target,
1452 &framebuffer.back_target,
1453 *mode_info,
1454 (void *) page0_ptr);
1455
1456 if (!err)
1457 {
1458 framebuffer.render_target = framebuffer.back_target;
1459 return GRUB_ERR_NONE;
1460 }
1461
1462 mode_info->mode_type &= ~(GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED
1463 | GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP);
1464
1465 grub_errno = GRUB_ERR_NONE;
1466 }
1467
1468 /* Fall back to no double buffering. */
1469 err = grub_video_fb_create_render_target_from_pointer (&framebuffer.front_target,
1470 mode_info,
1471 (void *) page0_ptr);
1472
1473 if (err)
1474 return err;
1475
1476 framebuffer.back_target = framebuffer.front_target;
1477 framebuffer.update_screen = 0;
1478
1479 mode_info->mode_type &= ~GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
1480
1481 framebuffer.render_target = framebuffer.back_target;
1482
1483 return GRUB_ERR_NONE;
1484 }
1485
1486
1487 grub_err_t
1488 grub_video_fb_swap_buffers (void)
1489 {
1490 grub_err_t err;
1491 if (!framebuffer.update_screen)
1492 return GRUB_ERR_NONE;
1493
1494 err = framebuffer.update_screen (framebuffer.front_target,
1495 framebuffer.back_target);
1496 if (err)
1497 return err;
1498
1499 return GRUB_ERR_NONE;
1500 }
1501
1502 grub_err_t
1503 grub_video_fb_get_info_and_fini (struct grub_video_mode_info *mode_info,
1504 void **framebuf)
1505 {
1506 grub_memcpy (mode_info, &(framebuffer.front_target->mode_info),
1507 sizeof (*mode_info));
1508 *framebuf = framebuffer.front_target->data;
1509
1510 grub_video_fb_fini ();
1511
1512 return GRUB_ERR_NONE;
1513 }