summaryrefslogtreecommitdiff
path: root/trunk/pango/pangoft2-render.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/pango/pangoft2-render.c')
-rw-r--r--trunk/pango/pangoft2-render.c710
1 files changed, 710 insertions, 0 deletions
diff --git a/trunk/pango/pangoft2-render.c b/trunk/pango/pangoft2-render.c
new file mode 100644
index 00000000..ef052efa
--- /dev/null
+++ b/trunk/pango/pangoft2-render.c
@@ -0,0 +1,710 @@
+/* Pango
+ * pangoft2-render.c: Rendering routines to FT_Bitmap objects
+ *
+ * Copyright (C) 2004 Red Hat Software
+ * Copyright (C) 2000 Tor Lillqvist
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <math.h>
+
+#include "pangoft2-private.h"
+
+/* for compatibility with older freetype versions */
+#ifndef FT_LOAD_TARGET_MONO
+#define FT_LOAD_TARGET_MONO FT_LOAD_MONOCHROME
+#endif
+
+typedef struct _PangoFT2RendererClass PangoFT2RendererClass;
+
+#define PANGO_FT2_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PANGO_TYPE_FT2_RENDERER, PangoFT2RendererClass))
+#define PANGO_IS_FT2_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PANGO_TYPE_FT2_RENDERER))
+#define PANGO_FT2_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PANGO_TYPE_FT2_RENDERER, PangoFT2RendererClass))
+
+struct _PangoFT2Renderer
+{
+ PangoRenderer parent_instance;
+
+ FT_Bitmap *bitmap;
+};
+
+struct _PangoFT2RendererClass
+{
+ PangoRendererClass parent_class;
+};
+
+static void pango_ft2_renderer_draw_glyph (PangoRenderer *renderer,
+ PangoFont *font,
+ PangoGlyph glyph,
+ double x,
+ double y);
+static void pango_ft2_renderer_draw_trapezoid (PangoRenderer *renderer,
+ PangoRenderPart part,
+ double y1,
+ double x11,
+ double x21,
+ double y2,
+ double x12,
+ double x22);
+
+G_DEFINE_TYPE (PangoFT2Renderer, pango_ft2_renderer, PANGO_TYPE_RENDERER)
+
+static void
+pango_ft2_renderer_init (PangoFT2Renderer *renderer)
+{
+}
+
+static void
+pango_ft2_renderer_class_init (PangoFT2RendererClass *klass)
+{
+ PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass);
+
+ renderer_class->draw_glyph = pango_ft2_renderer_draw_glyph;
+ renderer_class->draw_trapezoid = pango_ft2_renderer_draw_trapezoid;
+}
+
+static void
+pango_ft2_renderer_set_bitmap (PangoFT2Renderer *renderer,
+ FT_Bitmap *bitmap)
+{
+ renderer->bitmap = bitmap;
+}
+
+typedef struct
+{
+ FT_Bitmap bitmap;
+ int bitmap_left;
+ int bitmap_top;
+} PangoFT2RenderedGlyph;
+
+static void
+pango_ft2_free_rendered_glyph (PangoFT2RenderedGlyph *rendered)
+{
+ g_free (rendered->bitmap.buffer);
+ g_slice_free (PangoFT2RenderedGlyph, rendered);
+}
+
+static PangoFT2RenderedGlyph *
+pango_ft2_font_render_box_glyph (int width,
+ int height,
+ int top)
+{
+ PangoFT2RenderedGlyph *box;
+ int i, j, offset1, offset2, line_width;
+
+ line_width = MAX ((height + 43) / 44, 1);
+ if (width < 1 || height < 1)
+ line_width = 0;
+
+ box = g_slice_new (PangoFT2RenderedGlyph);
+
+ box->bitmap_left = 0;
+ box->bitmap_top = top;
+
+ box->bitmap.pixel_mode = ft_pixel_mode_grays;
+
+ box->bitmap.width = width;
+ box->bitmap.rows = height;
+ box->bitmap.pitch = height;
+
+ box->bitmap.buffer = g_malloc0 (box->bitmap.rows * box->bitmap.pitch);
+
+ /* draw the box */
+ for (j = 0; j < line_width; j++)
+ {
+ offset1 = box->bitmap.pitch * (MIN (1 + j, height - 1));
+ offset2 = box->bitmap.pitch * (MAX (box->bitmap.rows - 2 - j, 0));
+ for (i = 1;
+ i < box->bitmap.width - 1;
+ i++)
+ {
+ box->bitmap.buffer[offset1 + i] = 0xff;
+ box->bitmap.buffer[offset2 + i] = 0xff;
+ }
+ }
+ for (j = 0; j < line_width; j++)
+ {
+ offset1 = MIN (1 + j, width - 1);
+ offset2 = MAX (box->bitmap.width - 2 - j, 0);
+ for (i = box->bitmap.pitch;
+ i < (box->bitmap.rows - 1) * box->bitmap.pitch;
+ i += box->bitmap.pitch)
+ {
+ box->bitmap.buffer[offset1 + i] = 0xff;
+ box->bitmap.buffer[offset2 + i] = 0xff;
+ }
+ }
+
+ return box;
+}
+
+static PangoFT2RenderedGlyph *
+pango_ft2_font_render_glyph (PangoFont *font,
+ int glyph_index)
+{
+ FT_Face face;
+
+ if (glyph_index & PANGO_GLYPH_UNKNOWN_FLAG)
+ {
+ PangoFT2RenderedGlyph *box;
+ PangoFontMetrics *metrics;
+
+ if (!font)
+ goto generic_box;
+
+ metrics = pango_font_get_metrics (font, NULL);
+ if (!metrics)
+ goto generic_box;
+
+ box = pango_ft2_font_render_box_glyph (PANGO_PIXELS (metrics->approximate_char_width),
+ PANGO_PIXELS (metrics->ascent + metrics->descent),
+ PANGO_PIXELS (metrics->ascent));
+ pango_font_metrics_unref (metrics);
+
+ return box;
+ }
+
+ face = pango_ft2_font_get_face (font);
+
+ if (face)
+ {
+ PangoFT2RenderedGlyph *rendered;
+ PangoFT2Font *ft2font = (PangoFT2Font *) font;
+
+ rendered = g_slice_new (PangoFT2RenderedGlyph);
+
+ /* Draw glyph */
+ FT_Load_Glyph (face, glyph_index, ft2font->load_flags);
+ FT_Render_Glyph (face->glyph,
+ (ft2font->load_flags & FT_LOAD_TARGET_MONO ?
+ ft_render_mode_mono : ft_render_mode_normal));
+
+ rendered->bitmap = face->glyph->bitmap;
+ rendered->bitmap.buffer = g_memdup (face->glyph->bitmap.buffer,
+ face->glyph->bitmap.rows * face->glyph->bitmap.pitch);
+ rendered->bitmap_left = face->glyph->bitmap_left;
+ rendered->bitmap_top = face->glyph->bitmap_top;
+
+ return rendered;
+ }
+ else
+ {
+generic_box:
+ return pango_ft2_font_render_box_glyph (PANGO_UNKNOWN_GLYPH_WIDTH,
+ PANGO_UNKNOWN_GLYPH_HEIGHT,
+ PANGO_UNKNOWN_GLYPH_HEIGHT);
+ }
+}
+
+static void
+pango_ft2_renderer_draw_glyph (PangoRenderer *renderer,
+ PangoFont *font,
+ PangoGlyph glyph,
+ double x,
+ double y)
+{
+ FT_Bitmap *bitmap = PANGO_FT2_RENDERER (renderer)->bitmap;
+ PangoFT2RenderedGlyph *rendered_glyph;
+ gboolean add_glyph_to_cache;
+ guchar *src, *dest;
+
+ int x_start, x_limit;
+ int y_start, y_limit;
+ int ixoff = floor (x + 0.5);
+ int iyoff = floor (y + 0.5);
+ int ix, iy;
+
+ if (glyph & PANGO_GLYPH_UNKNOWN_FLAG)
+ {
+ glyph = pango_ft2_get_unknown_glyph (font);
+ if (glyph == PANGO_GLYPH_EMPTY)
+ {
+ /* No unknown glyph found for the font, draw a box */
+
+ /* Since we only draw an empty box for FT2 renderer,
+ * we unify the rendered bitmaps in the cache.
+ */
+ glyph = PANGO_GLYPH_UNKNOWN_FLAG;
+ }
+ }
+
+ rendered_glyph = _pango_ft2_font_get_cache_glyph_data (font, glyph);
+ add_glyph_to_cache = FALSE;
+ if (rendered_glyph == NULL)
+ {
+ rendered_glyph = pango_ft2_font_render_glyph (font, glyph);
+ add_glyph_to_cache = TRUE;
+ }
+
+ x_start = MAX (0, - (ixoff + rendered_glyph->bitmap_left));
+ x_limit = MIN (rendered_glyph->bitmap.width,
+ bitmap->width - (ixoff + rendered_glyph->bitmap_left));
+
+ y_start = MAX (0, - (iyoff - rendered_glyph->bitmap_top));
+ y_limit = MIN (rendered_glyph->bitmap.rows,
+ bitmap->rows - (iyoff - rendered_glyph->bitmap_top));
+
+ src = rendered_glyph->bitmap.buffer +
+ y_start * rendered_glyph->bitmap.pitch;
+
+ dest = bitmap->buffer +
+ (y_start + iyoff - rendered_glyph->bitmap_top) * bitmap->pitch +
+ x_start + ixoff + rendered_glyph->bitmap_left;
+
+ switch (rendered_glyph->bitmap.pixel_mode)
+ {
+ case ft_pixel_mode_grays:
+ src += x_start;
+ for (iy = y_start; iy < y_limit; iy++)
+ {
+ guchar *s = src;
+ guchar *d = dest;
+
+ for (ix = x_start; ix < x_limit; ix++)
+ {
+ switch (*s)
+ {
+ case 0:
+ break;
+ case 0xff:
+ *d = 0xff;
+ default:
+ *d = MIN ((gushort) *d + (gushort) *s, 0xff);
+ break;
+ }
+
+ s++;
+ d++;
+ }
+
+ dest += bitmap->pitch;
+ src += rendered_glyph->bitmap.pitch;
+ }
+ break;
+
+ case ft_pixel_mode_mono:
+ src += x_start / 8;
+ for (iy = y_start; iy < y_limit; iy++)
+ {
+ guchar *s = src;
+ guchar *d = dest;
+
+ for (ix = x_start; ix < x_limit; ix++)
+ {
+ if ((*s) & (1 << (7 - (ix % 8))))
+ *d |= 0xff;
+
+ if ((ix % 8) == 7)
+ s++;
+ d++;
+ }
+
+ dest += bitmap->pitch;
+ src += rendered_glyph->bitmap.pitch;
+ }
+ break;
+
+ default:
+ g_warning ("pango_ft2_render: "
+ "Unrecognized glyph bitmap pixel mode %d\n",
+ rendered_glyph->bitmap.pixel_mode);
+ break;
+ }
+
+ if (add_glyph_to_cache)
+ {
+ _pango_ft2_font_set_glyph_cache_destroy (font,
+ (GDestroyNotify) pango_ft2_free_rendered_glyph);
+ _pango_ft2_font_set_cache_glyph_data (font,
+ glyph, rendered_glyph);
+ }
+}
+
+typedef struct {
+ double y;
+ double x1;
+ double x2;
+} Position;
+
+static void
+draw_simple_trap (PangoRenderer *renderer,
+ Position *t,
+ Position *b)
+{
+ FT_Bitmap *bitmap = PANGO_FT2_RENDERER (renderer)->bitmap;
+ int iy = floor (t->y);
+ int x1, x2, x;
+ double dy = b->y - t->y;
+ guchar *dest;
+
+ if (iy < 0 || iy >= bitmap->rows)
+ return;
+ dest = bitmap->buffer + iy * bitmap->pitch;
+
+ if (t->x1 < b->x1)
+ x1 = floor (t->x1);
+ else
+ x1 = floor (b->x1);
+
+ if (t->x2 > b->x2)
+ x2 = ceil (t->x2);
+ else
+ x2 = ceil (b->x2);
+
+ x1 = CLAMP (x1, 0, bitmap->width);
+ x2 = CLAMP (x2, 0, bitmap->width);
+
+ for (x = x1; x < x2; x++)
+ {
+ double top_left = MAX (t->x1, x);
+ double top_right = MIN (t->x2, x + 1);
+ double bottom_left = MAX (b->x1, x);
+ double bottom_right = MIN (b->x2, x + 1);
+ double c = 0.5 * dy * ((top_right - top_left) + (bottom_right - bottom_left));
+
+ /* When converting to [0,255], we round up. This is intended
+ * to prevent the problem of pixels that get divided into
+ * multiple slices not being fully black.
+ */
+ int ic = c * 256;
+
+ dest[x] = MIN (dest[x] + ic, 255);
+ }
+}
+
+static void
+interpolate_position (Position *result,
+ Position *top,
+ Position *bottom,
+ double val,
+ double val1,
+ double val2)
+{
+ result->y = (top->y * (val2 - val) + bottom->y * (val - val1)) / (val2 - val1);
+ result->x1 = (top->x1 * (val2 - val) + bottom->x1 * (val - val1)) / (val2 - val1);
+ result->x2 = (top->x2 * (val2 - val) + bottom->x2 * (val - val1)) / (val2 - val1);
+}
+
+/* This draws a trapezoid with the parallel sides aligned with
+ * the X axis. We do this by subdividing the trapezoid vertically
+ * into thin slices (themselves trapezoids) where two edge sides are each
+ * contained within a single pixel and then rasterizing each
+ * slice. There are frequently multiple slices within a single
+ * line so we have to accumulate to get the final result.
+ */
+static void
+pango_ft2_renderer_draw_trapezoid (PangoRenderer *renderer,
+ PangoRenderPart part,
+ double y1,
+ double x11,
+ double x21,
+ double y2,
+ double x12,
+ double x22)
+{
+ Position pos;
+ Position t;
+ Position b;
+ gboolean done = FALSE;
+
+ if (y1 == y2)
+ return;
+
+ pos.y = t.y = y1;
+ pos.x1 = t.x1 = x11;
+ pos.x2 = t.x2 = x21;
+ b.y = y2;
+ b.x1 = x12;
+ b.x2 = x22;
+
+ while (!done)
+ {
+ Position pos_next;
+ double y_next, x1_next, x2_next;
+ double ix1, ix2;
+
+ /* The algorithm here is written to emphasize simplicity and
+ * numerical stability as opposed to speed.
+ *
+ * While the end result is slicing up the polygon vertically,
+ * conceptually we aren't walking in the X direction, rather we
+ * are walking along the edges. When we compute crossing of
+ * horizontal pixel boundaries, we use the X coordinate as the
+ * interpolating variable, when we compute crossing for vertical
+ * pixel boundaries, we use the Y coordinate.
+ *
+ * This allows us to handle almost exactly horizontal edges without
+ * running into difficulties. (Almost exactly horizontal edges
+ * come up frequently due to inexactness in computing, say,
+ * a 90 degree rotation transformation)
+ */
+
+ pos_next = b;
+ done = TRUE;
+
+ /* Check for crossing vertical pixel boundaries */
+ y_next = floor (pos.y) + 1;
+ if (y_next < pos_next.y)
+ {
+ interpolate_position (&pos_next, &t, &b,
+ y_next, t.y, b.y);
+ pos_next.y = y_next;
+ done = FALSE;
+ }
+
+ /* Check left side for crossing horizontal pixel boundaries */
+ ix1 = floor (pos.x1);
+
+ if (b.x1 < t.x1)
+ {
+ if (ix1 == pos.x1)
+ x1_next = ix1 - 1;
+ else
+ x1_next = ix1;
+
+ if (x1_next > pos_next.x1)
+ {
+ interpolate_position (&pos_next, &t, &b,
+ x1_next, t.x1, b.x1);
+ pos_next.x1 = x1_next;
+ done = FALSE;
+ }
+ }
+ else if (b.x1 > t.x1)
+ {
+ x1_next = ix1 + 1;
+
+ if (x1_next < pos_next.x1)
+ {
+ interpolate_position (&pos_next, &t, &b,
+ x1_next, t.x1, b.x1);
+ pos_next.x1 = x1_next;
+ done = FALSE;
+ }
+ }
+
+ /* Check right side for crossing horizontal pixel boundaries */
+ ix2 = floor (pos.x2);
+
+ if (b.x2 < t.x2)
+ {
+ if (ix2 == pos.x2)
+ x2_next = ix2 - 1;
+ else
+ x2_next = ix2;
+
+ if (x2_next > pos_next.x2)
+ {
+ interpolate_position (&pos_next, &t, &b,
+ x2_next, t.x2, b.x2);
+ pos_next.x2 = x2_next;
+ done = FALSE;
+ }
+ }
+ else if (x22 > x21)
+ {
+ x2_next = ix2 + 1;
+
+ if (x2_next < pos_next.x2)
+ {
+ interpolate_position (&pos_next, &t, &b,
+ x2_next, t.x2, b.x2);
+ pos_next.x2 = x2_next;
+ done = FALSE;
+ }
+ }
+
+ draw_simple_trap (renderer, &pos, &pos_next);
+ pos = pos_next;
+ }
+}
+
+/**
+ * pango_ft2_render_layout_subpixel:
+ * @bitmap: a <type>FT_Bitmap</type> to render the layout onto
+ * @layout: a #PangoLayout
+ * @x: the X position of the left of the layout (in Pango units)
+ * @y: the Y position of the top of the layout (in Pango units)
+ *
+ * Render a #PangoLayout onto a FreeType2 bitmap, with he
+ * location specified in fixed-point Pango units rather than
+ * pixels. (Using this will avoid extra inaccuracies from
+ * rounding to integer pixels multiple times, even if the
+ * final glyph positions are integers.)
+ *
+ * Since: 1.6
+ */
+void
+pango_ft2_render_layout_subpixel (FT_Bitmap *bitmap,
+ PangoLayout *layout,
+ int x,
+ int y)
+{
+ PangoContext *context;
+ PangoFontMap *fontmap;
+ PangoRenderer *renderer;
+
+ g_return_if_fail (bitmap != NULL);
+ g_return_if_fail (PANGO_IS_LAYOUT (layout));
+
+ context = pango_layout_get_context (layout);
+ fontmap = pango_context_get_font_map (context);
+ renderer = _pango_ft2_font_map_get_renderer (PANGO_FT2_FONT_MAP (fontmap));
+
+ pango_ft2_renderer_set_bitmap (PANGO_FT2_RENDERER (renderer), bitmap);
+
+ pango_renderer_draw_layout (renderer, layout, x, y);
+}
+
+/**
+ * pango_ft2_render_layout:
+ * @bitmap: a <type>FT_Bitmap</type> to render the layout onto
+ * @layout: a #PangoLayout
+ * @x: the X position of the left of the layout (in pixels)
+ * @y: the Y position of the top of the layout (in pixels)
+ *
+ * Render a #PangoLayout onto a FreeType2 bitmap
+ */
+void
+pango_ft2_render_layout (FT_Bitmap *bitmap,
+ PangoLayout *layout,
+ int x,
+ int y)
+{
+ pango_ft2_render_layout_subpixel (bitmap, layout, x * PANGO_SCALE, y * PANGO_SCALE);
+}
+
+/**
+ * pango_ft2_render_layout_line_subpixel:
+ * @bitmap: a <type>FT_Bitmap</type> to render the line onto
+ * @line: a #PangoLayoutLine
+ * @x: the x position of start of string (in Pango units)
+ * @y: the y position of baseline (in Pango units)
+ *
+ * Render a #PangoLayoutLine onto a FreeType2 bitmap, with he
+ * location specified in fixed-point Pango units rather than
+ * pixels. (Using this will avoid extra inaccuracies from
+ * rounding to integer pixels multiple times, even if the
+ * final glyph positions are integers.)
+ *
+ * Since: 1.6
+ */
+void
+pango_ft2_render_layout_line_subpixel (FT_Bitmap *bitmap,
+ PangoLayoutLine *line,
+ int x,
+ int y)
+{
+ PangoContext *context;
+ PangoFontMap *fontmap;
+ PangoRenderer *renderer;
+
+ g_return_if_fail (bitmap != NULL);
+ g_return_if_fail (line != NULL);
+
+ context = pango_layout_get_context (line->layout);
+ fontmap = pango_context_get_font_map (context);
+ renderer = _pango_ft2_font_map_get_renderer (PANGO_FT2_FONT_MAP (fontmap));
+
+ pango_ft2_renderer_set_bitmap (PANGO_FT2_RENDERER (renderer), bitmap);
+
+ pango_renderer_draw_layout_line (renderer, line, x, y);
+}
+
+/**
+ * pango_ft2_render_layout_line:
+ * @bitmap: a <type>FT_Bitmap</type> to render the line onto
+ * @line: a #PangoLayoutLine
+ * @x: the x position of start of string (in pixels)
+ * @y: the y position of baseline (in pixels)
+ *
+ * Render a #PangoLayoutLine onto a FreeType2 bitmap
+ */
+void
+pango_ft2_render_layout_line (FT_Bitmap *bitmap,
+ PangoLayoutLine *line,
+ int x,
+ int y)
+{
+ pango_ft2_render_layout_line_subpixel (bitmap, line, x * PANGO_SCALE, y * PANGO_SCALE);
+}
+
+/**
+ * pango_ft2_render_transformed:
+ * @bitmap: the FreeType2 bitmap onto which to draw the string
+ * @font: the font in which to draw the string
+ * @matrix: a #PangoMatrix, or %NULL to use an identity transformation
+ * @glyphs: the glyph string to draw
+ * @x: the x position of the start of the string (in Pango
+ * units in user space coordinates)
+ * @y: the y position of the baseline (in Pango units
+ * in user space coordinates)
+ *
+ * Renders a #PangoGlyphString onto a FreeType2 bitmap, possibly
+ * transforming the layed-out coordinates through a transformation
+ * matrix. Note that the transformation matrix for @font is not
+ * changed, so to produce correct rendering results, the @font
+ * must have been loaded using a #PangoContext with an identical
+ * transformation matrix to that passed in to this function.
+ *
+ * Since: 1.6
+ **/
+void
+pango_ft2_render_transformed (FT_Bitmap *bitmap,
+ const PangoMatrix *matrix,
+ PangoFont *font,
+ PangoGlyphString *glyphs,
+ int x,
+ int y)
+{
+ PangoFontMap *fontmap;
+ PangoRenderer *renderer;
+
+ g_return_if_fail (bitmap != NULL);
+ g_return_if_fail (glyphs != NULL);
+ g_return_if_fail (PANGO_FT2_IS_FONT (font));
+
+ fontmap = PANGO_FC_FONT (font)->fontmap;
+ renderer = _pango_ft2_font_map_get_renderer (PANGO_FT2_FONT_MAP (fontmap));
+
+ pango_ft2_renderer_set_bitmap (PANGO_FT2_RENDERER (renderer), bitmap);
+ pango_renderer_set_matrix (renderer, matrix);
+
+ pango_renderer_draw_glyphs (renderer, font, glyphs, x, y);
+}
+
+/**
+ * pango_ft2_render:
+ * @bitmap: the FreeType2 bitmap onto which to draw the string
+ * @font: the font in which to draw the string
+ * @glyphs: the glyph string to draw
+ * @x: the x position of the start of the string (in pixels)
+ * @y: the y position of the baseline (in pixels)
+ *
+ * Renders a #PangoGlyphString onto a FreeType2 bitmap.
+ **/
+void
+pango_ft2_render (FT_Bitmap *bitmap,
+ PangoFont *font,
+ PangoGlyphString *glyphs,
+ int x,
+ int y)
+{
+ pango_ft2_render_transformed (bitmap, NULL, font, glyphs, x * PANGO_SCALE, y * PANGO_SCALE);
+}
+