summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--minuitwrp/Android.mk14
-rw-r--r--minuitwrp/graphics.cpp94
-rw-r--r--minuitwrp/graphics_fbdev.cpp27
-rw-r--r--minuitwrp/graphics_overlay.cpp31
-rw-r--r--minuitwrp/graphics_utils.cpp45
-rw-r--r--minuitwrp/minui.h18
-rw-r--r--minuitwrp/truetype.cpp50
7 files changed, 225 insertions, 54 deletions
diff --git a/minuitwrp/Android.mk b/minuitwrp/Android.mk
index 0f8aae6d3..58634e34e 100644
--- a/minuitwrp/Android.mk
+++ b/minuitwrp/Android.mk
@@ -145,8 +145,18 @@ ifeq ($(TW_FBIOPAN), true)
LOCAL_CFLAGS += -DTW_FBIOPAN
endif
-ifeq ($(BOARD_HAS_FLIPPED_SCREEN), true)
-LOCAL_CFLAGS += -DBOARD_HAS_FLIPPED_SCREEN
+ifneq ($(TW_ROTATION),)
+ ifeq (,$(filter 0 90 180 270, $(TW_ROTATION)))
+ $(error TW_ROTATION must be set to 0, 90, 180 or 270. Currently set to $(TW_ROTATION))
+ endif
+ LOCAL_CFLAGS += -DTW_ROTATION=$(TW_ROTATION)
+else
+ # Support for old flag
+ ifeq ($(BOARD_HAS_FLIPPED_SCREEN), true)
+ LOCAL_CFLAGS += -DTW_ROTATION=180
+ else
+ LOCAL_CFLAGS += -DTW_ROTATION=0
+ endif
endif
ifeq ($(TW_IGNORE_MAJOR_AXIS_0), true)
diff --git a/minuitwrp/graphics.cpp b/minuitwrp/graphics.cpp
index d914eef3d..0abcb0c53 100644
--- a/minuitwrp/graphics.cpp
+++ b/minuitwrp/graphics.cpp
@@ -35,6 +35,8 @@
#include "../gui/placement.h"
#include "minui.h"
#include "graphics.h"
+// For std::min and std::max
+#include <algorithm>
struct GRFont {
GRSurface* texture;
@@ -105,32 +107,51 @@ int gr_textEx_scaleW(int x, int y, const char *s, void* pFont, int max_width, in
else if (placement == BOTTOM_LEFT || placement == BOTTOM_RIGHT)
y -= measured_height;
}
- return gr_ttf_textExWH(gl, x, y + y_scale, s, vfont, measured_width + x, -1);
+ return gr_ttf_textExWH(gl, x, y + y_scale, s, vfont, measured_width + x, -1, gr_draw);
}
void gr_clip(int x, int y, int w, int h)
{
GGLContext *gl = gr_context;
- gl->scissor(gl, x, y, w, h);
+ int x0_disp, y0_disp, x1_disp, y1_disp;
+ int l_disp, r_disp, t_disp, b_disp;
+
+ x0_disp = ROTATION_X_DISP(x, y, gr_draw);
+ y0_disp = ROTATION_Y_DISP(x, y, gr_draw);
+ x1_disp = ROTATION_X_DISP(x + w, y + h, gr_draw);
+ y1_disp = ROTATION_Y_DISP(x + w, y + h, gr_draw);
+ l_disp = std::min(x0_disp, x1_disp);
+ r_disp = std::max(x0_disp, x1_disp);
+ t_disp = std::min(y0_disp, y1_disp);
+ b_disp = std::max(y0_disp, y1_disp);
+ gl->scissor(gl, l_disp, t_disp, r_disp, b_disp);
gl->enable(gl, GGL_SCISSOR_TEST);
}
void gr_noclip()
{
GGLContext *gl = gr_context;
- gl->scissor(gl, 0, 0, gr_fb_width(), gr_fb_height());
+ gl->scissor(gl, 0, 0,
+ gr_draw->width - 2 * overscan_offset_x,
+ gr_draw->height - 2 * overscan_offset_y);
gl->disable(gl, GGL_SCISSOR_TEST);
}
void gr_line(int x0, int y0, int x1, int y1, int width)
{
GGLContext *gl = gr_context;
+ int x0_disp, y0_disp, x1_disp, y1_disp;
+
+ x0_disp = ROTATION_X_DISP(x0, y0, gr_draw);
+ y0_disp = ROTATION_Y_DISP(x0, y0, gr_draw);
+ x1_disp = ROTATION_X_DISP(x1, y1, gr_draw);
+ y1_disp = ROTATION_Y_DISP(x1, y1, gr_draw);
if(gr_is_curr_clr_opaque)
gl->disable(gl, GGL_BLEND);
- const int coords0[2] = { x0 << 4, y0 << 4 };
- const int coords1[2] = { x1 << 4, y1 << 4 };
+ const int coords0[2] = { x0_disp << 4, y0_disp << 4 };
+ const int coords1[2] = { x1_disp << 4, y1_disp << 4 };
gl->linex(gl, coords0, coords1, width << 4);
if(gr_is_curr_clr_opaque)
@@ -218,17 +239,29 @@ void gr_clear()
void gr_fill(int x, int y, int w, int h)
{
GGLContext *gl = gr_context;
+ int x0_disp, y0_disp, x1_disp, y1_disp;
+ int l_disp, r_disp, t_disp, b_disp;
if(gr_is_curr_clr_opaque)
gl->disable(gl, GGL_BLEND);
- gl->recti(gl, x, y, x + w, y + h);
+ x0_disp = ROTATION_X_DISP(x, y, gr_draw);
+ y0_disp = ROTATION_Y_DISP(x, y, gr_draw);
+ x1_disp = ROTATION_X_DISP(x + w, y + h, gr_draw);
+ y1_disp = ROTATION_Y_DISP(x + w, y + h, gr_draw);
+ l_disp = std::min(x0_disp, x1_disp);
+ r_disp = std::max(x0_disp, x1_disp);
+ t_disp = std::min(y0_disp, y1_disp);
+ b_disp = std::max(y0_disp, y1_disp);
+
+ gl->recti(gl, l_disp, t_disp, r_disp, b_disp);
if(gr_is_curr_clr_opaque)
gl->enable(gl, GGL_BLEND);
}
-void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy) {
+void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy)
+{
if (gr_context == NULL) {
return;
}
@@ -239,15 +272,50 @@ void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy) {
if(surface->format == GGL_PIXEL_FORMAT_RGBX_8888)
gl->disable(gl, GGL_BLEND);
+ int dx0_disp, dy0_disp, dx1_disp, dy1_disp;
+ int l_disp, r_disp, t_disp, b_disp;
+
+ // Figuring out display coordinates works for TW_ROTATION == 0 too,
+ // and isn't as expensive as allocating and rotating another surface,
+ // so we do this anyway.
+ dx0_disp = ROTATION_X_DISP(dx, dy, gr_draw);
+ dy0_disp = ROTATION_Y_DISP(dx, dy, gr_draw);
+ dx1_disp = ROTATION_X_DISP(dx + w, dy + h, gr_draw);
+ dy1_disp = ROTATION_Y_DISP(dx + w, dy + h, gr_draw);
+ l_disp = std::min(dx0_disp, dx1_disp);
+ r_disp = std::max(dx0_disp, dx1_disp);
+ t_disp = std::min(dy0_disp, dy1_disp);
+ b_disp = std::max(dy0_disp, dy1_disp);
+
+#if TW_ROTATION != 0
+ // Do not perform relatively expensive operation if not needed
+ GGLSurface surface_rotated;
+ surface_rotated.version = sizeof(surface_rotated);
+ // Skip the **(TW_ROTATION == 0)** || (TW_ROTATION == 180) check
+ // because we are under a TW_ROTATION != 0 conditional compilation statement
+ surface_rotated.width = (TW_ROTATION == 180) ? surface->width : surface->height;
+ surface_rotated.height = (TW_ROTATION == 180) ? surface->height : surface->width;
+ surface_rotated.stride = surface_rotated.width;
+ surface_rotated.format = surface->format;
+ surface_rotated.data = (GGLubyte*) malloc(surface_rotated.stride * surface_rotated.height * 4);
+ surface_ROTATION_transform((gr_surface) &surface_rotated, (const gr_surface) surface, 4);
+
+ gl->bindTexture(gl, &surface_rotated);
+#else
gl->bindTexture(gl, surface);
+#endif
gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
gl->enable(gl, GGL_TEXTURE_2D);
- gl->texCoord2i(gl, sx - dx, sy - dy);
- gl->recti(gl, dx, dy, dx + w, dy + h);
+ gl->texCoord2i(gl, sx - l_disp, sy - t_disp);
+ gl->recti(gl, l_disp, t_disp, r_disp, b_disp);
gl->disable(gl, GGL_TEXTURE_2D);
+#if TW_ROTATION != 0
+ free(surface_rotated.data);
+#endif
+
if(surface->format == GGL_PIXEL_FORMAT_RGBX_8888)
gl->enable(gl, GGL_BLEND);
}
@@ -369,12 +437,16 @@ void gr_exit(void)
int gr_fb_width(void)
{
- return gr_draw->width - 2*overscan_offset_x;
+ return (TW_ROTATION == 0 || TW_ROTATION == 180) ?
+ gr_draw->width - 2 * overscan_offset_x :
+ gr_draw->height - 2 * overscan_offset_y;
}
int gr_fb_height(void)
{
- return gr_draw->height - 2*overscan_offset_y;
+ return (TW_ROTATION == 0 || TW_ROTATION == 180) ?
+ gr_draw->height - 2 * overscan_offset_y :
+ gr_draw->width - 2 * overscan_offset_x;
}
void gr_fb_blank(bool blank)
diff --git a/minuitwrp/graphics_fbdev.cpp b/minuitwrp/graphics_fbdev.cpp
index 8cf85f5eb..fc5fcf385 100644
--- a/minuitwrp/graphics_fbdev.cpp
+++ b/minuitwrp/graphics_fbdev.cpp
@@ -293,7 +293,6 @@ static GRSurface* fbdev_flip(minui_backend* backend __unused) {
ucfb_vaddr[idx + 2] = tmp;
}
#endif
-#ifndef BOARD_HAS_FLIPPED_SCREEN
if (double_buffered) {
// Copy from the in-memory surface to the framebuffer.
memcpy(gr_framebuffer[1-displayed_buffer].data, gr_draw->data,
@@ -304,32 +303,6 @@ static GRSurface* fbdev_flip(minui_backend* backend __unused) {
memcpy(gr_framebuffer[0].data, gr_draw->data,
gr_draw->height * gr_draw->row_bytes);
}
-#else
- int gr_active_fb = 0;
- if (double_buffered)
- gr_active_fb = 1-displayed_buffer;
-
- /* flip buffer 180 degrees for devices with physically inverted screens */
- unsigned int row_pixels = gr_draw->row_bytes / gr_framebuffer[0].pixel_bytes;
- if (gr_framebuffer[0].pixel_bytes == 4) {
- for (unsigned int y = 0; y < gr_draw->height; ++y) {
- uint32_t* dst = reinterpret_cast<uint32_t*>(gr_framebuffer[gr_active_fb].data) + y * row_pixels;
- uint32_t* src = reinterpret_cast<uint32_t*>(gr_draw->data) + (gr_draw->height - y - 1) * row_pixels + gr_draw->width;
- for (unsigned int x = 0; x < gr_draw->width; ++x)
- *(dst++) = *(--src);
- }
- } else {
- for (unsigned int y = 0; y < gr_draw->height; ++y) {
- uint16_t* dst = reinterpret_cast<uint16_t*>(gr_framebuffer[gr_active_fb].data) + y * row_pixels;
- uint16_t* src = reinterpret_cast<uint16_t*>(gr_draw->data) + (gr_draw->height - y - 1) * row_pixels + gr_draw->width;
- for (unsigned int x = 0; x < gr_draw->width; ++x)
- *(dst++) = *(--src);
- }
- }
-
- if (double_buffered)
- set_displayed_framebuffer(1-displayed_buffer);
-#endif
return gr_draw;
}
diff --git a/minuitwrp/graphics_overlay.cpp b/minuitwrp/graphics_overlay.cpp
index b4efae421..4dff7f4a6 100644
--- a/minuitwrp/graphics_overlay.cpp
+++ b/minuitwrp/graphics_overlay.cpp
@@ -323,9 +323,13 @@ int allocate_overlay(int fd, GRSurface gr_fb)
overlayL.dst_rect.w = gr_fb.width;
overlayL.dst_rect.h = gr_fb.height;
overlayL.alpha = 0xFF;
-#ifdef BOARD_HAS_FLIPPED_SCREEN
- overlayL.flags = MDP_ROT_180;
-#endif
+ // If this worked, life would have been so much easier
+ //switch (TW_ROTATION) {
+ //case 0: overlayL.flags = MDP_ROT_NOP; break;
+ //case 90: overlayL.flags = MDP_ROT_90; break;
+ //case 180: overlayL.flags = MDP_ROT_180; break;
+ //case 270: overlayL.flags = MDP_ROT_270; break;
+ //}
overlayL.transp_mask = MDP_TRANSP_NOP;
overlayL.id = MSMFB_NEW_REQUEST;
ret = ioctl(fd, MSMFB_OVERLAY_SET, &overlayL);
@@ -363,9 +367,13 @@ int allocate_overlay(int fd, GRSurface gr_fb)
overlayL.dst_rect.w = lWidth;
overlayL.dst_rect.h = height;
overlayL.alpha = 0xFF;
-#ifdef BOARD_HAS_FLIPPED_SCREEN
- overlayL.flags = MDP_ROT_180;
-#endif
+ // If this worked, life would have been so much easier
+ //switch (TW_ROTATION) {
+ //case 0: overlayL.flags = MDP_ROT_NOP; break;
+ //case 90: overlayL.flags = MDP_ROT_90; break;
+ //case 180: overlayL.flags = MDP_ROT_180; break;
+ //case 270: overlayL.flags = MDP_ROT_270; break;
+ //}
overlayL.transp_mask = MDP_TRANSP_NOP;
overlayL.id = MSMFB_NEW_REQUEST;
ret = ioctl(fd, MSMFB_OVERLAY_SET, &overlayL);
@@ -393,11 +401,14 @@ int allocate_overlay(int fd, GRSurface gr_fb)
overlayR.dst_rect.w = rWidth;
overlayR.dst_rect.h = height;
overlayR.alpha = 0xFF;
-#ifdef BOARD_HAS_FLIPPED_SCREEN
- overlayR.flags = MDSS_MDP_RIGHT_MIXER | MDP_ROT_180;
-#else
overlayR.flags = MDSS_MDP_RIGHT_MIXER;
-#endif
+ // If this worked, life would have been so much easier
+ //switch (TW_ROTATION) {
+ //case 0: overlayR.flags |= MDP_ROT_NOP; break;
+ //case 90: overlayR.flags |= MDP_ROT_90; break;
+ //case 180: overlayR.flags |= MDP_ROT_180; break;
+ //case 270: overlayR.flags |= MDP_ROT_270; break;
+ //}
overlayR.transp_mask = MDP_TRANSP_NOP;
overlayR.id = MSMFB_NEW_REQUEST;
ret = ioctl(fd, MSMFB_OVERLAY_SET, &overlayR);
diff --git a/minuitwrp/graphics_utils.cpp b/minuitwrp/graphics_utils.cpp
index 67c836ed5..c591e5398 100644
--- a/minuitwrp/graphics_utils.cpp
+++ b/minuitwrp/graphics_utils.cpp
@@ -19,6 +19,7 @@
#include <png.h>
#include <pixelflinger/pixelflinger.h>
#include <linux/fb.h>
+#include <string.h>
#include "minui.h"
@@ -121,3 +122,47 @@ exit:
fclose(fp);
return res;
}
+
+#define MATRIX_ELEMENT(matrix, row, col, row_size, elem_size) \
+ (((uint8_t*) (matrix)) + (((row) * (elem_size)) * (row_size)) + ((col) * (elem_size)))
+
+#define DO_MATRIX_ROTATION(bits_per_pixel, bytes_per_pixel) \
+{ \
+ for (size_t y = 0; y < src->height; y++) { \
+ for (size_t x = 0; x < src->width; x++) { \
+ /* output pointer in dst->data */ \
+ uint##bits_per_pixel##_t *op; \
+ /* input pointer from src->data */ \
+ const uint##bits_per_pixel##_t *ip; \
+ /* Display coordinates (in dst) corresponding to (x, y) in src */ \
+ size_t x_disp = ROTATION_X_DISP(x, y, dst); \
+ size_t y_disp = ROTATION_Y_DISP(x, y, dst); \
+ \
+ ip = (const uint##bits_per_pixel##_t*) \
+ MATRIX_ELEMENT(src->data, y, x, \
+ src->stride, bytes_per_pixel); \
+ op = (uint##bits_per_pixel##_t*) \
+ MATRIX_ELEMENT(dst->data, y_disp, x_disp, \
+ dst->stride, bytes_per_pixel); \
+ *op = *ip; \
+ } \
+ } \
+}
+
+void surface_ROTATION_transform(gr_surface dst_ptr, const gr_surface src_ptr,
+ size_t num_bytes_per_pixel)
+{
+ GGLSurface *dst = (GGLSurface*) dst_ptr;
+ const GGLSurface *src = (GGLSurface*) src_ptr;
+
+ /* Handle duplicated code via a macro.
+ * This is currently used for rotating surfaces of graphical resources
+ * (32-bit pixel format) and of font glyphs (8-bit pixel format).
+ * If you need to add handling of other pixel formats feel free to do so.
+ */
+ if (num_bytes_per_pixel == 4) {
+ DO_MATRIX_ROTATION(32, 4);
+ } else if (num_bytes_per_pixel == 1) {
+ DO_MATRIX_ROTATION(8, 1);
+ }
+}
diff --git a/minuitwrp/minui.h b/minuitwrp/minui.h
index 018f3274f..2c462e75f 100644
--- a/minuitwrp/minui.h
+++ b/minuitwrp/minui.h
@@ -58,7 +58,8 @@ int gr_getMaxFontHeight(void *font);
void *gr_ttf_loadFont(const char *filename, int size, int dpi);
void *gr_ttf_scaleFont(void *font, int max_width, int measured_width);
void gr_ttf_freeFont(void *font);
-int gr_ttf_textExWH(void *context, int x, int y, const char *s, void *pFont, int max_width, int max_height);
+int gr_ttf_textExWH(void *context, int x, int y, const char *s, void *pFont,
+ int max_width, int max_height, const gr_surface gr_draw);
int gr_ttf_measureEx(const char *s, void *font);
int gr_ttf_maxExW(const char *s, void *font, int max_width);
int gr_ttf_getMaxFontHeight(void *font);
@@ -73,6 +74,21 @@ int gr_free_surface(gr_surface surface);
// Functions in graphics_utils.c
int gr_save_screenshot(const char *dest);
+// Transform minuitwrp API coordinates into display coordinates,
+// for panels that are hardware-mounted in a rotated manner.
+#define ROTATION_X_DISP(x, y, surface) \
+ ((TW_ROTATION == 0) ? (x) : \
+ (TW_ROTATION == 90) ? ((surface)->width - (y) - 1) : \
+ (TW_ROTATION == 180) ? ((surface)->width - (x) - 1) : \
+ (TW_ROTATION == 270) ? (y) : -1)
+#define ROTATION_Y_DISP(x, y, surface) \
+ ((TW_ROTATION == 0) ? (y) : \
+ (TW_ROTATION == 90) ? (x) : \
+ (TW_ROTATION == 180) ? ((surface)->height - (y) - 1) : \
+ (TW_ROTATION == 270) ? ((surface)->height - (x) - 1) : -1)
+
+void surface_ROTATION_transform(gr_surface dst_ptr, const gr_surface src_ptr, size_t num_bytes_per_pixel);
+
// input event structure, include <linux/input.h> for the definition.
// see http://www.mjmwired.net/kernel/Documentation/input/ for info.
struct input_event;
diff --git a/minuitwrp/truetype.cpp b/minuitwrp/truetype.cpp
index 3e5f70719..0416b0e9f 100644
--- a/minuitwrp/truetype.cpp
+++ b/minuitwrp/truetype.cpp
@@ -14,6 +14,8 @@
#include <pixelflinger/pixelflinger.h>
#include <pthread.h>
+// For std::min and std::max
+#include <algorithm>
#define STRING_CACHE_MAX_ENTRIES 400
#define STRING_CACHE_TRUNCATE_ENTRIES 150
@@ -697,10 +699,14 @@ int gr_ttf_maxExW(const char *s, void *font, int max_width)
return max_bytes;
}
-int gr_ttf_textExWH(void *context, int x, int y, const char *s, void *pFont, int max_width, int max_height)
+int gr_ttf_textExWH(void *context, int x, int y,
+ const char *s, void *pFont,
+ int max_width, int max_height,
+ const gr_surface gr_draw_surface)
{
GGLContext *gl = (GGLContext *)context;
TrueTypeFont *font = (TrueTypeFont *)pFont;
+ const GRSurface *gr_draw = (const GRSurface*) gr_draw_surface;
// not actualy max width, but max_width + x
if(max_width != -1)
@@ -719,6 +725,21 @@ int gr_ttf_textExWH(void *context, int x, int y, const char *s, void *pFont, int
return -1;
}
+#if TW_ROTATION != 0
+ // Do not perform relatively expensive operation if not needed
+ GGLSurface string_surface_rotated;
+ string_surface_rotated.version = sizeof(string_surface_rotated);
+ // Skip the **(TW_ROTATION == 0)** || (TW_ROTATION == 180) check
+ // because we are under a TW_ROTATION != 0 conditional compilation statement
+ string_surface_rotated.width = (TW_ROTATION == 180) ? e->surface.width : e->surface.height;
+ string_surface_rotated.height = (TW_ROTATION == 180) ? e->surface.height : e->surface.width;
+ string_surface_rotated.stride = string_surface_rotated.width;
+ string_surface_rotated.format = e->surface.format;
+ // e->surface.format is GGL_PIXEL_FORMAT_A_8 (grayscale)
+ string_surface_rotated.data = (GGLubyte*) malloc(string_surface_rotated.stride * string_surface_rotated.height * 1);
+ surface_ROTATION_transform((gr_surface) &string_surface_rotated, (const gr_surface) &e->surface, 1);
+#endif
+
int y_bottom = y + e->surface.height;
int res = e->rendered_bytes;
@@ -732,16 +753,39 @@ int gr_ttf_textExWH(void *context, int x, int y, const char *s, void *pFont, int
}
}
+ // Figuring out display coordinates works for TW_ROTATION == 0 too,
+ // and isn't as expensive as allocating and rotating another surface,
+ // so we do this anyway.
+ int x0_disp, y0_disp, x1_disp, y1_disp;
+ int l_disp, r_disp, t_disp, b_disp;
+
+ x0_disp = ROTATION_X_DISP(x, y, gr_draw);
+ y0_disp = ROTATION_Y_DISP(x, y, gr_draw);
+ x1_disp = ROTATION_X_DISP(x + e->surface.width, y_bottom, gr_draw);
+ y1_disp = ROTATION_Y_DISP(x + e->surface.width, y_bottom, gr_draw);
+ l_disp = std::min(x0_disp, x1_disp);
+ r_disp = std::max(x0_disp, x1_disp);
+ t_disp = std::min(y0_disp, y1_disp);
+ b_disp = std::max(y0_disp, y1_disp);
+
+#if TW_ROTATION != 0
+ gl->bindTexture(gl, &string_surface_rotated);
+#else
gl->bindTexture(gl, &e->surface);
+#endif
gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
gl->enable(gl, GGL_TEXTURE_2D);
- gl->texCoord2i(gl, -x, -y);
- gl->recti(gl, x, y, x + e->surface.width, y_bottom);
+ gl->texCoord2i(gl, -l_disp, -t_disp);
+ gl->recti(gl, l_disp, t_disp, r_disp, b_disp);
gl->disable(gl, GGL_TEXTURE_2D);
+#if TW_ROTATION != 0
+ free(string_surface_rotated.data);
+#endif
+
pthread_mutex_unlock(&font->mutex);
return res;
}