diff options
Diffstat (limited to 'lib/fonts/font_rotate.c')
| -rw-r--r-- | lib/fonts/font_rotate.c | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/lib/fonts/font_rotate.c b/lib/fonts/font_rotate.c new file mode 100644 index 000000000..275406008 --- /dev/null +++ b/lib/fonts/font_rotate.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Font rotation + * + * Copyright (C) 2005 Antonino Daplas <adaplas @pol.net> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include <linux/errno.h> +#include <linux/export.h> +#include <linux/math.h> +#include <linux/overflow.h> +#include <linux/slab.h> +#include <linux/string.h> + +#include "font.h" + +/* number of bits per line */ +static unsigned int font_glyph_bit_pitch(unsigned int width) +{ + return round_up(width, 8); +} + +static unsigned int __font_glyph_pos(unsigned int x, unsigned int y, unsigned int bit_pitch, + unsigned int *bit) +{ + unsigned int off = y * bit_pitch + x; + unsigned int bit_shift = off % 8; + + *bit = 0x80 >> bit_shift; /* MSB has position 0, LSB has position 7 */ + + return off / 8; +} + +static bool font_glyph_test_bit(const unsigned char *glyph, unsigned int x, unsigned int y, + unsigned int bit_pitch) +{ + unsigned int bit; + unsigned int i = __font_glyph_pos(x, y, bit_pitch, &bit); + + return glyph[i] & bit; +} + +static void font_glyph_set_bit(unsigned char *glyph, unsigned int x, unsigned int y, + unsigned int bit_pitch) +{ + unsigned int bit; + unsigned int i = __font_glyph_pos(x, y, bit_pitch, &bit); + + glyph[i] |= bit; +} + +static void __font_glyph_rotate_90(const unsigned char *glyph, + unsigned int width, unsigned int height, + unsigned char *out) +{ + unsigned int x, y; + unsigned int shift = (8 - (height % 8)) & 7; + unsigned int bit_pitch = font_glyph_bit_pitch(width); + unsigned int out_bit_pitch = font_glyph_bit_pitch(height); + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + if (font_glyph_test_bit(glyph, x, y, bit_pitch)) { + font_glyph_set_bit(out, out_bit_pitch - 1 - y - shift, x, + out_bit_pitch); + } + } + } +} + +/** + * font_glyph_rotate_90 - Rotate a glyph pattern by 90° in clockwise direction + * @glyph: The glyph to rotate + * @width: The glyph width in bits per scanline + * @height: The number of scanlines in the glyph + * @out: The rotated glyph bitmap + * + * The parameters @width and @height refer to the input glyph given in @glyph. + * The caller has to provide the output buffer @out of sufficient size to hold + * the rotated glyph. Rotating by 90° flips the width and height for the output + * glyph. Depending on the glyph pitch, the size of the output glyph can be + * different than the size of the input. Callers have to take this into account + * when allocating the output memory. + */ +void font_glyph_rotate_90(const unsigned char *glyph, unsigned int width, unsigned int height, + unsigned char *out) +{ + memset(out, 0, font_glyph_size(height, width)); /* flip width/height */ + + __font_glyph_rotate_90(glyph, width, height, out); +} +EXPORT_SYMBOL_GPL(font_glyph_rotate_90); + +static void __font_glyph_rotate_180(const unsigned char *glyph, + unsigned int width, unsigned int height, + unsigned char *out) +{ + unsigned int x, y; + unsigned int shift = (8 - (width % 8)) & 7; + unsigned int bit_pitch = font_glyph_bit_pitch(width); + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + if (font_glyph_test_bit(glyph, x, y, bit_pitch)) { + font_glyph_set_bit(out, bit_pitch - 1 - x - shift, height - 1 - y, + bit_pitch); + } + } + } +} + +/** + * font_glyph_rotate_180 - Rotate a glyph pattern by 180° + * @glyph: The glyph to rotate + * @width: The glyph width in bits per scanline + * @height: The number of scanlines in the glyph + * @out: The rotated glyph bitmap + * + * The parameters @width and @height refer to the input glyph given in @glyph. + * The caller has to provide the output buffer @out of sufficient size to hold + * the rotated glyph. + */ +void font_glyph_rotate_180(const unsigned char *glyph, unsigned int width, unsigned int height, + unsigned char *out) +{ + memset(out, 0, font_glyph_size(width, height)); + + __font_glyph_rotate_180(glyph, width, height, out); +} +EXPORT_SYMBOL_GPL(font_glyph_rotate_180); + +static void __font_glyph_rotate_270(const unsigned char *glyph, + unsigned int width, unsigned int height, + unsigned char *out) +{ + unsigned int x, y; + unsigned int shift = (8 - (width % 8)) & 7; + unsigned int bit_pitch = font_glyph_bit_pitch(width); + unsigned int out_bit_pitch = font_glyph_bit_pitch(height); + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + if (font_glyph_test_bit(glyph, x, y, bit_pitch)) + font_glyph_set_bit(out, y, bit_pitch - 1 - x - shift, + out_bit_pitch); + } + } +} + +/** + * font_glyph_rotate_270 - Rotate a glyph pattern by 270° in clockwise direction + * @glyph: The glyph to rotate + * @width: The glyph width in bits per scanline + * @height: The number of scanlines in the glyph + * @out: The rotated glyph bitmap + * + * The parameters @width and @height refer to the input glyph given in @glyph. + * The caller has to provide the output buffer @out of sufficient size to hold + * the rotated glyph. Rotating by 270° flips the width and height for the output + * glyph. Depending on the glyph pitch, the size of the output glyph can be + * different than the size of the input. Callers have to take this into account + * when allocating the output memory. + */ +void font_glyph_rotate_270(const unsigned char *glyph, unsigned int width, unsigned int height, + unsigned char *out) +{ + memset(out, 0, font_glyph_size(height, width)); /* flip width/height */ + + __font_glyph_rotate_270(glyph, width, height, out); +} +EXPORT_SYMBOL_GPL(font_glyph_rotate_270); + +/** + * font_data_rotate - Rotate font data by multiples of 90° + * @fd: The font data to rotate + * @width: The glyph width in bits per scanline + * @height: The number of scanlines in the glyph + * @charcount: The number of glyphs in the font + * @steps: Number of rotation steps of 90° + * @buf: Preallocated output buffer; can be NULL + * @bufsize: The size of @buf in bytes; can be NULL + * + * The parameters @width and @height refer to the visible number of pixels + * and scanlines in a single glyph. The number of glyphs is given in @charcount. + * Rotation happens in steps of 90°. The @steps parameter can have any value, + * but only 0 to 3 produce distinct results. With 4 or higher, a full rotation + * has been performed. You can pass any value for @steps and the helper will + * perform the appropriate rotation. Note that the returned buffer is not + * compatible with font_data_t. It only contains glyph data in the same format + * as returned by font_data_buf(). Callers are responsible to free the returned + * buffer with kfree(). Font rotation typically happens when displays get + * re-oriented. To avoid unnecessary re-allocation of the memory buffer, the + * caller can pass in an earlier result buffer in @buf for reuse. The old and + * new buffer sizes are given and retrieved by the caller in @bufsize. The + * allocation semantics are compatible with krealloc(). + * + * Returns: + * A buffer with rotated glyphs on success, or an error pointer otherwise + */ +unsigned char *font_data_rotate(font_data_t *fd, unsigned int width, unsigned int height, + unsigned int charcount, unsigned int steps, + unsigned char *buf, size_t *bufsize) +{ + const unsigned char *src = font_data_buf(fd); + unsigned int s_cellsize = font_glyph_size(width, height); + unsigned int d_cellsize, i; + unsigned char *dst; + size_t size; + + steps %= 4; + + switch (steps) { + case 0: + case 2: + d_cellsize = s_cellsize; + break; + case 1: + case 3: + d_cellsize = font_glyph_size(height, width); /* flip width/height */ + break; + } + + if (check_mul_overflow(charcount, d_cellsize, &size)) + return ERR_PTR(-EINVAL); + + if (!buf || !bufsize || size > *bufsize) { + dst = kmalloc_array(charcount, d_cellsize, GFP_KERNEL); + if (!dst) + return ERR_PTR(-ENOMEM); + + kfree(buf); + buf = dst; + if (bufsize) + *bufsize = size; + } else { + dst = buf; + } + + switch (steps) { + case 0: + memcpy(dst, src, size); + break; + case 1: + memset(dst, 0, size); + for (i = 0; i < charcount; ++i) { + __font_glyph_rotate_90(src, width, height, dst); + src += s_cellsize; + dst += d_cellsize; + } + break; + case 2: + memset(dst, 0, size); + for (i = 0; i < charcount; ++i) { + __font_glyph_rotate_180(src, width, height, dst); + src += s_cellsize; + dst += d_cellsize; + } + break; + case 3: + memset(dst, 0, size); + for (i = 0; i < charcount; ++i) { + __font_glyph_rotate_270(src, width, height, dst); + src += s_cellsize; + dst += d_cellsize; + } + break; + } + + return buf; +} +EXPORT_SYMBOL_GPL(font_data_rotate); |
