mirror of
https://github.com/torvalds/linux.git
synced 2026-05-13 00:28:54 +02:00
Fix the horizontal bit position when rotating a glyph by 180°. The
original code in rotate_ud() rounded the value in width up to a
multiple of 8, aka the bit pitch, and calculated the rotated pixel
from that value. The new code stores the glyph's pitch in bit_pitch,
but fails to update the rotated pixel's output accordingly. Simply
replacing the variable does this.
The bug can be reproduced by setting a font with an unaligned width,
such as sun12x22, like this:
setfont sun12x22
echo 2 > /sys/class/graphics/fbcon/rotate
Without the fix, the font looks distorted.
Fixes: a30e9e6b01 ("lib/fonts: Refactor glyph-rotation helpers")
Closes: https://lore.gitlab.freedesktop.org/drm-ai-reviews/review-patch7-20260407092555.58816-8-tzimmermann@suse.de/
Cc: dri-devel@lists.freedesktop.org
Cc: linux-fbdev@vger.kernel.org
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Signed-off-by: Helge Deller <deller@gmx.de>
276 lines
8.3 KiB
C
276 lines
8.3 KiB
C
// 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);
|