1 /* bitmap_scale.c - Bitmap scaling. */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2006,2007,2008,2009 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
21 #include <grub/misc.h>
22 #include <grub/video.h>
23 #include <grub/bitmap.h>
24 #include <grub/bitmap_scale.h>
25 #include <grub/types.h>
28 GRUB_MOD_LICENSE ("GPLv3+");
30 /* Prototypes for module-local functions. */
31 static grub_err_t
scale_nn (struct grub_video_bitmap
*dst
,
32 struct grub_video_bitmap
*src
);
33 static grub_err_t
scale_bilinear (struct grub_video_bitmap
*dst
,
34 struct grub_video_bitmap
*src
);
37 verify_source_bitmap (struct grub_video_bitmap
*src
)
39 /* Verify the simplifying assumptions. */
41 return grub_error (GRUB_ERR_BUG
,
42 "null src bitmap in grub_video_bitmap_create_scaled");
43 if (src
->mode_info
.red_field_pos
% 8 != 0
44 || src
->mode_info
.green_field_pos
% 8 != 0
45 || src
->mode_info
.blue_field_pos
% 8 != 0
46 || src
->mode_info
.reserved_field_pos
% 8 != 0)
47 return grub_error (GRUB_ERR_BUG
,
48 "src format not supported for scale");
49 if (src
->mode_info
.width
== 0 || src
->mode_info
.height
== 0)
50 return grub_error (GRUB_ERR_BUG
,
51 "source bitmap has a zero dimension");
52 if (src
->mode_info
.bytes_per_pixel
* 8 != src
->mode_info
.bpp
)
53 return grub_error (GRUB_ERR_BUG
,
54 "bitmap to scale has inconsistent Bpp and bpp");
59 grub_video_bitmap_scale (struct grub_video_bitmap
*dst
,
60 struct grub_video_bitmap
*src
,
61 enum grub_video_bitmap_scale_method scale_method
)
65 case GRUB_VIDEO_BITMAP_SCALE_METHOD_FASTEST
:
66 case GRUB_VIDEO_BITMAP_SCALE_METHOD_NEAREST
:
67 return scale_nn (dst
, src
);
68 case GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST
:
69 case GRUB_VIDEO_BITMAP_SCALE_METHOD_BILINEAR
:
70 return scale_bilinear (dst
, src
);
72 return grub_error (GRUB_ERR_BUG
, "Invalid scale_method value");
76 /* This function creates a new scaled version of the bitmap SRC. The new
77 bitmap has dimensions DST_WIDTH by DST_HEIGHT. The scaling algorithm
78 is given by SCALE_METHOD. If an error is encountered, the return code is
79 not equal to GRUB_ERR_NONE, and the bitmap DST is either not created, or
80 it is destroyed before this function returns.
82 Supports only direct color modes which have components separated
83 into bytes (e.g., RGBA 8:8:8:8 or BGR 8:8:8 true color).
84 But because of this simplifying assumption, the implementation is
85 greatly simplified. */
87 grub_video_bitmap_create_scaled (struct grub_video_bitmap
**dst
,
88 int dst_width
, int dst_height
,
89 struct grub_video_bitmap
*src
,
90 enum grub_video_bitmap_scale_method
95 grub_err_t err
= verify_source_bitmap(src
);
96 if (err
!= GRUB_ERR_NONE
)
98 if (dst_width
<= 0 || dst_height
<= 0)
99 return grub_error (GRUB_ERR_BUG
,
100 "requested to scale to a size w/ a zero dimension");
102 /* Create the new bitmap. */
104 ret
= grub_video_bitmap_create (dst
, dst_width
, dst_height
,
105 src
->mode_info
.blit_format
);
106 if (ret
!= GRUB_ERR_NONE
)
107 return ret
; /* Error. */
109 ret
= grub_video_bitmap_scale (*dst
, src
, scale_method
);
111 if (ret
== GRUB_ERR_NONE
)
113 /* Success: *dst is now a pointer to the scaled bitmap. */
114 return GRUB_ERR_NONE
;
118 /* Destroy the bitmap and return the error code. */
119 grub_video_bitmap_destroy (*dst
);
126 make_h_align (unsigned *x
, unsigned *w
, unsigned new_w
,
127 grub_video_bitmap_h_align_t h_align
)
129 grub_err_t ret
= GRUB_ERR_NONE
;
134 return GRUB_ERR_NONE
;
138 case GRUB_VIDEO_BITMAP_H_ALIGN_LEFT
:
141 case GRUB_VIDEO_BITMAP_H_ALIGN_CENTER
:
142 *x
= (*w
- new_w
) / 2;
144 case GRUB_VIDEO_BITMAP_H_ALIGN_RIGHT
:
148 ret
= grub_error (GRUB_ERR_BUG
, "Invalid h_align value");
156 make_v_align (unsigned *y
, unsigned *h
, unsigned new_h
,
157 grub_video_bitmap_v_align_t v_align
)
159 grub_err_t ret
= GRUB_ERR_NONE
;
164 return GRUB_ERR_NONE
;
168 case GRUB_VIDEO_BITMAP_V_ALIGN_TOP
:
171 case GRUB_VIDEO_BITMAP_V_ALIGN_CENTER
:
172 *y
= (*h
- new_h
) / 2;
174 case GRUB_VIDEO_BITMAP_V_ALIGN_BOTTOM
:
178 ret
= grub_error (GRUB_ERR_BUG
, "Invalid v_align value");
186 grub_video_bitmap_scale_proportional (struct grub_video_bitmap
**dst
,
187 int dst_width
, int dst_height
,
188 struct grub_video_bitmap
*src
,
189 enum grub_video_bitmap_scale_method
191 grub_video_bitmap_selection_method_t
193 grub_video_bitmap_v_align_t v_align
,
194 grub_video_bitmap_h_align_t h_align
)
197 grub_err_t ret
= verify_source_bitmap(src
);
198 if (ret
!= GRUB_ERR_NONE
)
200 if (dst_width
<= 0 || dst_height
<= 0)
201 return grub_error (GRUB_ERR_BUG
,
202 "requested to scale to a size w/ a zero dimension");
204 ret
= grub_video_bitmap_create (dst
, dst_width
, dst_height
,
205 src
->mode_info
.blit_format
);
206 if (ret
!= GRUB_ERR_NONE
)
207 return ret
; /* Error. */
211 unsigned dw
= dst_width
;
212 unsigned dh
= dst_height
;
215 unsigned sw
= src
->mode_info
.width
;
216 unsigned sh
= src
->mode_info
.height
;
218 switch (selection_method
)
220 case GRUB_VIDEO_BITMAP_SELECTION_METHOD_CROP
:
221 /* Comparing sw/sh VS dw/dh. */
222 if (sw
* dh
< dw
* sh
)
223 ret
= make_v_align (&sy0
, &sh
, sw
* dh
/ dw
, v_align
);
225 ret
= make_h_align (&sx0
, &sw
, sh
* dw
/ dh
, h_align
);
227 case GRUB_VIDEO_BITMAP_SELECTION_METHOD_PADDING
:
228 if (sw
* dh
< dw
* sh
)
229 ret
= make_h_align (&dx0
, &dw
, sw
* dh
/ sh
, h_align
);
231 ret
= make_v_align (&dy0
, &dh
, sh
* dw
/ sw
, v_align
);
233 case GRUB_VIDEO_BITMAP_SELECTION_METHOD_FITWIDTH
:
234 if (sw
* dh
< dw
* sh
)
235 ret
= make_v_align (&sy0
, &sh
, sw
* dh
/ dw
, v_align
);
237 ret
= make_v_align (&dy0
, &dh
, sh
* dw
/ sw
, v_align
);
239 case GRUB_VIDEO_BITMAP_SELECTION_METHOD_FITHEIGHT
:
240 if (sw
* dh
< dw
* sh
)
241 ret
= make_h_align (&dx0
, &dw
, sw
* dh
/ sh
, h_align
);
243 ret
= make_h_align (&sx0
, &sw
, sh
* dw
/ dh
, h_align
);
246 ret
= grub_error (GRUB_ERR_BUG
, "Invalid selection_method value");
250 if (ret
== GRUB_ERR_NONE
)
252 /* Backup original data. */
253 int src_width_orig
= src
->mode_info
.width
;
254 int src_height_orig
= src
->mode_info
.height
;
255 grub_uint8_t
*src_data_orig
= src
->data
;
256 int dst_width_orig
= (*dst
)->mode_info
.width
;
257 int dst_height_orig
= (*dst
)->mode_info
.height
;
258 grub_uint8_t
*dst_data_orig
= (*dst
)->data
;
260 int dstride
= (*dst
)->mode_info
.pitch
;
261 int sstride
= src
->mode_info
.pitch
;
262 /* bytes_per_pixel is the same for both src and dst. */
263 int bytes_per_pixel
= src
->mode_info
.bytes_per_pixel
;
265 /* Crop src and dst. */
266 src
->mode_info
.width
= sw
;
267 src
->mode_info
.height
= sh
;
268 src
->data
= (grub_uint8_t
*) src
->data
+ sx0
* bytes_per_pixel
270 (*dst
)->mode_info
.width
= dw
;
271 (*dst
)->mode_info
.height
= dh
;
272 (*dst
)->data
= (grub_uint8_t
*) (*dst
)->data
+ dx0
* bytes_per_pixel
275 /* Scale our image. */
276 ret
= grub_video_bitmap_scale (*dst
, src
, scale_method
);
278 /* Restore original data. */
279 src
->mode_info
.width
= src_width_orig
;
280 src
->mode_info
.height
= src_height_orig
;
281 src
->data
= src_data_orig
;
282 (*dst
)->mode_info
.width
= dst_width_orig
;
283 (*dst
)->mode_info
.height
= dst_height_orig
;
284 (*dst
)->data
= dst_data_orig
;
287 if (ret
== GRUB_ERR_NONE
)
289 /* Success: *dst is now a pointer to the scaled bitmap. */
290 return GRUB_ERR_NONE
;
294 /* Destroy the bitmap and return the error code. */
295 grub_video_bitmap_destroy (*dst
);
302 verify_bitmaps (struct grub_video_bitmap
*dst
, struct grub_video_bitmap
*src
)
304 /* Verify the simplifying assumptions. */
305 if (dst
== 0 || src
== 0)
306 return grub_error (GRUB_ERR_BUG
, "null bitmap in scale function");
307 if (dst
->mode_info
.red_field_pos
% 8 != 0
308 || dst
->mode_info
.green_field_pos
% 8 != 0
309 || dst
->mode_info
.blue_field_pos
% 8 != 0
310 || dst
->mode_info
.reserved_field_pos
% 8 != 0)
311 return grub_error (GRUB_ERR_BUG
,
312 "dst format not supported");
313 if (src
->mode_info
.red_field_pos
% 8 != 0
314 || src
->mode_info
.green_field_pos
% 8 != 0
315 || src
->mode_info
.blue_field_pos
% 8 != 0
316 || src
->mode_info
.reserved_field_pos
% 8 != 0)
317 return grub_error (GRUB_ERR_BUG
,
318 "src format not supported");
319 if (dst
->mode_info
.red_field_pos
!= src
->mode_info
.red_field_pos
320 || dst
->mode_info
.red_mask_size
!= src
->mode_info
.red_mask_size
321 || dst
->mode_info
.green_field_pos
!= src
->mode_info
.green_field_pos
322 || dst
->mode_info
.green_mask_size
!= src
->mode_info
.green_mask_size
323 || dst
->mode_info
.blue_field_pos
!= src
->mode_info
.blue_field_pos
324 || dst
->mode_info
.blue_mask_size
!= src
->mode_info
.blue_mask_size
325 || dst
->mode_info
.reserved_field_pos
!=
326 src
->mode_info
.reserved_field_pos
327 || dst
->mode_info
.reserved_mask_size
!=
328 src
->mode_info
.reserved_mask_size
)
329 return grub_error (GRUB_ERR_BUG
,
330 "dst and src not compatible");
331 if (dst
->mode_info
.bytes_per_pixel
!= src
->mode_info
.bytes_per_pixel
)
332 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
333 "dst and src not compatible");
334 if (dst
->mode_info
.width
== 0 || dst
->mode_info
.height
== 0
335 || src
->mode_info
.width
== 0 || src
->mode_info
.height
== 0)
336 return grub_error (GRUB_ERR_BUG
, "bitmap has a zero dimension");
338 return GRUB_ERR_NONE
;
341 /* Nearest neighbor bitmap scaling algorithm.
343 Copy the bitmap SRC to the bitmap DST, scaling the bitmap to fit the
344 dimensions of DST. This function uses the nearest neighbor algorithm to
345 interpolate the pixels.
347 Supports only direct color modes which have components separated
348 into bytes (e.g., RGBA 8:8:8:8 or BGR 8:8:8 true color).
349 But because of this simplifying assumption, the implementation is
350 greatly simplified. */
352 scale_nn (struct grub_video_bitmap
*dst
, struct grub_video_bitmap
*src
)
354 grub_err_t err
= verify_bitmaps(dst
, src
);
355 if (err
!= GRUB_ERR_NONE
)
358 grub_uint8_t
*ddata
= dst
->data
;
359 grub_uint8_t
*sdata
= src
->data
;
360 unsigned dw
= dst
->mode_info
.width
;
361 unsigned dh
= dst
->mode_info
.height
;
362 unsigned sw
= src
->mode_info
.width
;
363 unsigned sh
= src
->mode_info
.height
;
364 unsigned dstride
= dst
->mode_info
.pitch
;
365 unsigned sstride
= src
->mode_info
.pitch
;
366 /* bytes_per_pixel is the same for both src and dst. */
367 unsigned bytes_per_pixel
= dst
->mode_info
.bytes_per_pixel
;
370 for (dy
= 0; dy
< dh
; dy
++)
373 for (dx
= 0; dx
< dw
; dx
++)
381 /* Compute the source coordinate that the destination coordinate
382 maps to. Note: sx/sw = dx/dw => sx = sw*dx/dw. */
386 /* Get the address of the pixels in src and dst. */
387 dptr
= ddata
+ dy
* dstride
+ dx
* bytes_per_pixel
;
388 sptr
= sdata
+ sy
* sstride
+ sx
* bytes_per_pixel
;
390 /* Copy the pixel color value. */
391 for (comp
= 0; comp
< bytes_per_pixel
; comp
++)
392 dptr
[comp
] = sptr
[comp
];
395 return GRUB_ERR_NONE
;
398 /* Bilinear interpolation image scaling algorithm.
400 Copy the bitmap SRC to the bitmap DST, scaling the bitmap to fit the
401 dimensions of DST. This function uses the bilinear interpolation algorithm
402 to interpolate the pixels.
404 Supports only direct color modes which have components separated
405 into bytes (e.g., RGBA 8:8:8:8 or BGR 8:8:8 true color).
406 But because of this simplifying assumption, the implementation is
407 greatly simplified. */
409 scale_bilinear (struct grub_video_bitmap
*dst
, struct grub_video_bitmap
*src
)
411 grub_err_t err
= verify_bitmaps(dst
, src
);
412 if (err
!= GRUB_ERR_NONE
)
415 grub_uint8_t
*ddata
= dst
->data
;
416 grub_uint8_t
*sdata
= src
->data
;
417 unsigned dw
= dst
->mode_info
.width
;
418 unsigned dh
= dst
->mode_info
.height
;
419 unsigned sw
= src
->mode_info
.width
;
420 unsigned sh
= src
->mode_info
.height
;
421 int dstride
= dst
->mode_info
.pitch
;
422 int sstride
= src
->mode_info
.pitch
;
423 /* bytes_per_pixel is the same for both src and dst. */
424 int bytes_per_pixel
= dst
->mode_info
.bytes_per_pixel
;
427 for (dy
= 0; dy
< dh
; dy
++)
430 for (dx
= 0; dx
< dw
; dx
++)
438 /* Compute the source coordinate that the destination coordinate
439 maps to. Note: sx/sw = dx/dw => sx = sw*dx/dw. */
443 /* Get the address of the pixels in src and dst. */
444 dptr
= ddata
+ dy
* dstride
+ dx
* bytes_per_pixel
;
445 sptr
= sdata
+ sy
* sstride
+ sx
* bytes_per_pixel
;
447 /* If we have enough space to do so, use bilinear interpolation.
448 Otherwise, fall back to nearest neighbor for this pixel. */
449 if (sx
< sw
- 1 && sy
< sh
- 1)
451 /* Do bilinear interpolation. */
453 /* Fixed-point .8 numbers representing the fraction of the
454 distance in the x (u) and y (v) direction within the
455 box of 4 pixels in the source. */
456 int u
= (256 * sw
* dx
/ dw
) - (sx
* 256);
457 int v
= (256 * sh
* dy
/ dh
) - (sy
* 256);
459 for (comp
= 0; comp
< bytes_per_pixel
; comp
++)
461 /* Get the component's values for the
462 four source corner pixels. */
463 int f00
= sptr
[comp
];
464 int f10
= sptr
[comp
+ bytes_per_pixel
];
465 int f01
= sptr
[comp
+ sstride
];
466 int f11
= sptr
[comp
+ sstride
+ bytes_per_pixel
];
468 /* Count coeffecients. */
469 int c00
= (256 - u
) * (256 - v
);
470 int c10
= u
* (256 - v
);
471 int c01
= (256 - u
) * v
;
475 int fxy
= c00
* f00
+ c01
* f01
+ c10
* f10
+ c11
* f11
;
476 fxy
= fxy
/ (256 * 256);
483 /* Fall back to nearest neighbor interpolation. */
484 /* Copy the pixel color value. */
485 for (comp
= 0; comp
< bytes_per_pixel
; comp
++)
486 dptr
[comp
] = sptr
[comp
];
490 return GRUB_ERR_NONE
;