summaryrefslogtreecommitdiffstats
path: root/lib/fonts/font_rotate.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/fonts/font_rotate.c')
-rw-r--r--lib/fonts/font_rotate.c275
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);