[gimp] app: use mipmaps when downscaling raster brushes
- From: Ell <ell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] app: use mipmaps when downscaling raster brushes
- Date: Wed, 12 Feb 2020 22:07:33 +0000 (UTC)
commit ee39f0ec13e0cdb701e34835710e00b5286e24ae
Author: Ell <ell_se yahoo com>
Date: Wed Feb 12 23:50:28 2020 +0200
app: use mipmaps when downscaling raster brushes
When downscaling raster brushes, we currently resample the original
brush with no fitlering (other than bilinear interpolation), which
results in very noticable aliasing when using heavily downscaled
big brushes.
This commit constructs a box-filtered mipmap hierarchy for the
original brush on-demand, and the closest mipmap is used as the
resampling source for downscaled brushes, significantly improving
the output quality.
app/core/Makefile.am | 2 +
app/core/gimpbrush-mipmap.cc | 509 ++++++++++++++++++++++++++++++++++++++++
app/core/gimpbrush-mipmap.h | 38 +++
app/core/gimpbrush-private.h | 35 +--
app/core/gimpbrush-transform.cc | 156 ++++++------
app/core/gimpbrush.c | 7 +
app/core/meson.build | 1 +
7 files changed, 657 insertions(+), 91 deletions(-)
---
diff --git a/app/core/Makefile.am b/app/core/Makefile.am
index 825388a07c..ef8a83f2fe 100644
--- a/app/core/Makefile.am
+++ b/app/core/Makefile.am
@@ -114,6 +114,8 @@ libappcore_a_sources = \
gimpbrush-header.h \
gimpbrush-load.c \
gimpbrush-load.h \
+ gimpbrush-mipmap.cc \
+ gimpbrush-mipmap.h \
gimpbrush-private.h \
gimpbrush-save.c \
gimpbrush-save.h \
diff --git a/app/core/gimpbrush-mipmap.cc b/app/core/gimpbrush-mipmap.cc
new file mode 100644
index 0000000000..bf2e0f4274
--- /dev/null
+++ b/app/core/gimpbrush-mipmap.cc
@@ -0,0 +1,509 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpbrush-mipmap.c
+ * Copyright (C) 2020 Ell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+extern "C"
+{
+
+#include "libgimpmath/gimpmath.h"
+
+#include "core-types.h"
+
+#include "gegl/gimp-gegl-loops.h"
+
+#include "gimpbrush.h"
+#include "gimpbrush-mipmap.h"
+#include "gimpbrush-private.h"
+#include "gimptempbuf.h"
+
+} /* extern "C" */
+
+
+#define PIXELS_PER_THREAD \
+ (/* each thread costs as much as */ 64.0 * 64.0 /* pixels */)
+
+#define GIMP_BRUSH_MIPMAP(brush, mipmaps, x, y) \
+ ((*(mipmaps))[(y) * (brush)->priv->n_horz_mipmaps + (x)])
+
+
+/* local function prototypes */
+
+static void gimp_brush_mipmap_clear (GimpBrush *brush,
+ GimpTempBuf ***mipmaps);
+
+static const GimpTempBuf * gimp_brush_mipmap_get (GimpBrush *brush,
+ const GimpTempBuf *source,
+ GimpTempBuf ***mipmaps,
+ gdouble *scale_x,
+ gdouble *scale_y);
+
+static GimpTempBuf * gimp_brush_mipmap_downscale (const GimpTempBuf *source);
+static GimpTempBuf * gimp_brush_mipmap_downscale_horz (const GimpTempBuf *source);
+static GimpTempBuf * gimp_brush_mipmap_downscale_vert (const GimpTempBuf *source);
+
+
+/* private functions */
+
+static void
+gimp_brush_mipmap_clear (GimpBrush *brush,
+ GimpTempBuf ***mipmaps)
+{
+ if (*mipmaps)
+ {
+ gint i;
+
+ for (i = 0;
+ i < brush->priv->n_horz_mipmaps * brush->priv->n_vert_mipmaps;
+ i++)
+ {
+ g_clear_pointer (&(*mipmaps)[i], gimp_temp_buf_unref);
+ }
+
+ g_clear_pointer (mipmaps, g_free);
+ }
+}
+
+static const GimpTempBuf *
+gimp_brush_mipmap_get (GimpBrush *brush,
+ const GimpTempBuf *source,
+ GimpTempBuf ***mipmaps,
+ gdouble *scale_x,
+ gdouble *scale_y)
+{
+ gint x;
+ gint y;
+ gint i;
+
+ if (! source)
+ return NULL;
+
+ if (! *mipmaps)
+ {
+ gint width = gimp_temp_buf_get_width (source);
+ gint height = gimp_temp_buf_get_height (source);
+
+ brush->priv->n_horz_mipmaps = floor (log (width) / M_LN2) + 1;
+ brush->priv->n_vert_mipmaps = floor (log (height) / M_LN2) + 1;
+
+ *mipmaps = g_new0 (GimpTempBuf *, brush->priv->n_horz_mipmaps *
+ brush->priv->n_vert_mipmaps);
+
+ GIMP_BRUSH_MIPMAP (brush, mipmaps, 0, 0) = gimp_temp_buf_ref (source);
+ }
+
+ x = floor (SAFE_CLAMP (log (1.0 / MAX (*scale_x, 0.0)) / M_LN2,
+ 0, brush->priv->n_horz_mipmaps - 1));
+ y = floor (SAFE_CLAMP (log (1.0 / MAX (*scale_y, 0.0)) / M_LN2,
+ 0, brush->priv->n_vert_mipmaps - 1));
+
+ *scale_x *= pow (2.0, x);
+ *scale_y *= pow (2.0, y);
+
+ if (GIMP_BRUSH_MIPMAP (brush, mipmaps, x, y))
+ return GIMP_BRUSH_MIPMAP (brush, mipmaps, x, y);
+
+ g_return_val_if_fail (x >= 0 || y >= 0, NULL);
+
+ for (i = 1; i <= x + y; i++)
+ {
+ gint u = x - i;
+ gint v = y;
+
+ if (u < 0)
+ {
+ v += u;
+ u = 0;
+ }
+
+ while (u <= x && v >= 0)
+ {
+ if (GIMP_BRUSH_MIPMAP (brush, mipmaps, u, v))
+ {
+ for (; x - u > y - v; u++)
+ {
+ GIMP_BRUSH_MIPMAP (brush, mipmaps, u + 1, v) =
+ gimp_brush_mipmap_downscale_horz (
+ GIMP_BRUSH_MIPMAP (brush, mipmaps, u, v));
+ }
+
+ for (; y - v > x - u; v++)
+ {
+ GIMP_BRUSH_MIPMAP (brush, mipmaps, u, v + 1) =
+ gimp_brush_mipmap_downscale_vert (
+ GIMP_BRUSH_MIPMAP (brush, mipmaps, u, v));
+ }
+
+ for (; u < x; u++, v++)
+ {
+ GIMP_BRUSH_MIPMAP (brush, mipmaps, u + 1, v + 1) =
+ gimp_brush_mipmap_downscale (
+ GIMP_BRUSH_MIPMAP (brush, mipmaps, u, v));
+ }
+
+ return GIMP_BRUSH_MIPMAP (brush, mipmaps, u, v);
+ }
+
+ u++;
+ v--;
+ }
+ }
+
+ g_return_val_if_reached (NULL);
+}
+
+template <class T>
+struct MipmapTraits;
+
+template <>
+struct MipmapTraits<guint8>
+{
+ static guint8
+ mix (guint8 a,
+ guint8 b)
+ {
+ return ((guint32) a + (guint32) b + 1) / 2;
+ }
+
+ static guint8
+ mix (guint8 a,
+ guint8 b,
+ guint8 c,
+ guint8 d)
+ {
+ return ((guint32) a + (guint32) b + (guint32) c + (guint32) d + 2) / 4;
+ }
+};
+
+template <>
+struct MipmapTraits<gfloat>
+{
+ static gfloat
+ mix (gfloat a,
+ gfloat b)
+ {
+ return (a + b) / 2.0;
+ }
+
+ static gfloat
+ mix (gfloat a,
+ gfloat b,
+ gfloat c,
+ gfloat d)
+ {
+ return (a + b + c + d) / 4.0;
+ }
+};
+
+template <class T,
+ gint N>
+struct MipmapAlgorithms
+{
+ static GimpTempBuf *
+ downscale (const GimpTempBuf *source)
+ {
+ GimpTempBuf *destination;
+ gint width = gimp_temp_buf_get_width (source);
+ gint height = gimp_temp_buf_get_height (source);
+
+ width /= 2;
+ height /= 2;
+
+ destination = gimp_temp_buf_new (width, height,
+ gimp_temp_buf_get_format (source));
+
+ gegl_parallel_distribute_area (
+ GEGL_RECTANGLE (0, 0, width, height), PIXELS_PER_THREAD,
+ [=] (const GeglRectangle *area)
+ {
+ const T *src0 = (const T *) gimp_temp_buf_get_data (source);
+ T *dest0 = (T *) gimp_temp_buf_get_data (destination);
+ gint src_stride = N * gimp_temp_buf_get_width (source);
+ gint dest_stride = N * gimp_temp_buf_get_width (destination);
+ gint y;
+
+ src0 += 2 * (area->y * src_stride + N * area->x);
+ dest0 += area->y * dest_stride + N * area->x;
+
+ for (y = 0; y < area->height; y++)
+ {
+ const T *src = src0;
+ T *dest = dest0;
+ gint x;
+
+ for (x = 0; x < area->width; x++)
+ {
+ gint c;
+
+ for (c = 0; c < N; c++)
+ {
+ dest[c] = MipmapTraits<T>::mix (src[c],
+ src[N + c],
+ src[src_stride + c],
+ src[src_stride + N + c]);
+ }
+
+ src += 2 * N;
+ dest += N;
+ }
+
+ src0 += 2 * src_stride;
+ dest0 += dest_stride;
+ }
+ });
+
+ return destination;
+ }
+
+ static GimpTempBuf *
+ downscale_horz (const GimpTempBuf *source)
+ {
+ GimpTempBuf *destination;
+ gint width = gimp_temp_buf_get_width (source);
+ gint height = gimp_temp_buf_get_height (source);
+
+ width /= 2;
+
+ destination = gimp_temp_buf_new (width, height,
+ gimp_temp_buf_get_format (source));
+
+ gegl_parallel_distribute_range (
+ height, PIXELS_PER_THREAD / width,
+ [=] (gint offset,
+ gint size)
+ {
+ const T *src = (const T *) gimp_temp_buf_get_data (source);
+ T *dest = (T *) gimp_temp_buf_get_data (destination);
+ gint y;
+
+ src += offset * gimp_temp_buf_get_width (source) * N;
+ dest += offset * gimp_temp_buf_get_width (destination) * N;
+
+ for (y = 0; y < size; y++)
+ {
+ gint x;
+
+ for (x = 0; x < width; x++)
+ {
+ gint c;
+
+ for (c = 0; c < N; c++)
+ dest[c] = MipmapTraits<T>::mix (src[c], src[N + c]);
+
+ src += 2 * N;
+ dest += N;
+ }
+ }
+ });
+
+ return destination;
+ }
+
+ static GimpTempBuf *
+ downscale_vert (const GimpTempBuf *source)
+ {
+ GimpTempBuf *destination;
+ gint width = gimp_temp_buf_get_width (source);
+ gint height = gimp_temp_buf_get_height (source);
+
+ height /= 2;
+
+ destination = gimp_temp_buf_new (width, height,
+ gimp_temp_buf_get_format (source));
+
+ gegl_parallel_distribute_range (
+ width, PIXELS_PER_THREAD / height,
+ [=] (gint offset,
+ gint size)
+ {
+ const T *src0 = (const T *) gimp_temp_buf_get_data (source);
+ T *dest0 = (T *) gimp_temp_buf_get_data (destination);
+ gint src_stride = N * gimp_temp_buf_get_width (source);
+ gint dest_stride = N * gimp_temp_buf_get_width (destination);
+ gint x;
+
+ src0 += offset * N;
+ dest0 += offset * N;
+
+ for (x = 0; x < size; x++)
+ {
+ const T *src = src0;
+ T *dest = dest0;
+ gint y;
+
+ for (y = 0; y < height; y++)
+ {
+ gint c;
+
+ for (c = 0; c < N; c++)
+ dest[c] = MipmapTraits<T>::mix (src[c], src[src_stride + c]);
+
+ src += 2 * src_stride;
+ dest += dest_stride;
+ }
+
+ src0 += N;
+ dest0 += N;
+ }
+ });
+
+ return destination;
+ }
+};
+
+template <class Func>
+static GimpTempBuf *
+gimp_brush_mipmap_dispatch (const GimpTempBuf *source,
+ Func func)
+{
+ const Babl *format = gimp_temp_buf_get_format (source);
+ const Babl *type;
+ gint n_components;
+
+ type = babl_format_get_type (format, 0);
+ n_components = babl_format_get_n_components (format);
+
+ if (type == babl_type ("u8"))
+ {
+ switch (n_components)
+ {
+ case 1:
+ return func (MipmapAlgorithms<guint8, 1> ());
+
+ case 3:
+ return func (MipmapAlgorithms<guint8, 3> ());
+ }
+ }
+ else if (type == babl_type ("float"))
+ {
+ switch (n_components)
+ {
+ case 1:
+ return func (MipmapAlgorithms<gfloat, 1> ());
+
+ case 3:
+ return func (MipmapAlgorithms<gfloat, 3> ());
+ }
+ }
+
+ g_return_val_if_reached (NULL);
+}
+
+static GimpTempBuf *
+gimp_brush_mipmap_downscale (const GimpTempBuf *source)
+{
+ return gimp_brush_mipmap_dispatch (
+ source,
+ [&] (auto algorithms)
+ {
+ return algorithms.downscale (source);
+ });
+}
+
+static GimpTempBuf *
+gimp_brush_mipmap_downscale_horz (const GimpTempBuf *source)
+{
+ return gimp_brush_mipmap_dispatch (
+ source,
+ [&] (auto algorithms)
+ {
+ return algorithms.downscale_horz (source);
+ });
+}
+
+static GimpTempBuf *
+gimp_brush_mipmap_downscale_vert (const GimpTempBuf *source)
+{
+ return gimp_brush_mipmap_dispatch (
+ source,
+ [&] (auto algorithms)
+ {
+ return algorithms.downscale_vert (source);
+ });
+}
+
+
+/* public functions */
+
+void
+gimp_brush_mipmap_clear (GimpBrush *brush)
+{
+ gimp_brush_mipmap_clear (brush, &brush->priv->mask_mipmaps);
+ gimp_brush_mipmap_clear (brush, &brush->priv->pixmap_mipmaps);
+}
+
+const GimpTempBuf *
+gimp_brush_mipmap_get_mask (GimpBrush *brush,
+ gdouble *scale_x,
+ gdouble *scale_y)
+{
+ return gimp_brush_mipmap_get (brush,
+ brush->priv->mask,
+ &brush->priv->mask_mipmaps,
+ scale_x, scale_y);
+}
+
+const GimpTempBuf *
+gimp_brush_mipmap_get_pixmap (GimpBrush *brush,
+ gdouble *scale_x,
+ gdouble *scale_y)
+{
+ return gimp_brush_mipmap_get (brush,
+ brush->priv->pixmap,
+ &brush->priv->pixmap_mipmaps,
+ scale_x, scale_y);
+}
+
+gsize
+gimp_brush_mipmap_get_memsize (GimpBrush *brush)
+{
+ gsize memsize = 0;
+
+ if (brush->priv->mask_mipmaps)
+ {
+ gint i;
+
+ for (i = 0;
+ i < brush->priv->n_horz_mipmaps * brush->priv->n_vert_mipmaps;
+ i++)
+ {
+ memsize += gimp_temp_buf_get_memsize (brush->priv->mask_mipmaps[i]);
+ }
+ }
+
+ if (brush->priv->pixmap_mipmaps)
+ {
+ gint i;
+
+ for (i = 0;
+ i < brush->priv->n_horz_mipmaps * brush->priv->n_vert_mipmaps;
+ i++)
+ {
+ memsize += gimp_temp_buf_get_memsize (brush->priv->pixmap_mipmaps[i]);
+ }
+ }
+
+ return memsize;
+}
diff --git a/app/core/gimpbrush-mipmap.h b/app/core/gimpbrush-mipmap.h
new file mode 100644
index 0000000000..b2b8fd3cca
--- /dev/null
+++ b/app/core/gimpbrush-mipmap.h
@@ -0,0 +1,38 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpbrush-mipmap.h
+ * Copyright (C) 2020 Ell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_BRUSH_MIPMAP_H__
+#define __GIMP_BRUSH_MIPMAP_H__
+
+
+void gimp_brush_mipmap_clear (GimpBrush *brush);
+
+const GimpTempBuf * gimp_brush_mipmap_get_mask (GimpBrush *brush,
+ gdouble *scale_x,
+ gdouble *scale_y);
+
+const GimpTempBuf * gimp_brush_mipmap_get_pixmap (GimpBrush *brush,
+ gdouble *scale_x,
+ gdouble *scale_y);
+
+gsize gimp_brush_mipmap_get_memsize (GimpBrush *brush);
+
+
+#endif /* __GIMP_BRUSH_MIPMAP_H__ */
diff --git a/app/core/gimpbrush-private.h b/app/core/gimpbrush-private.h
index d96b847d85..c8d4d29294 100644
--- a/app/core/gimpbrush-private.h
+++ b/app/core/gimpbrush-private.h
@@ -21,21 +21,26 @@
struct _GimpBrushPrivate
{
- GimpTempBuf *mask; /* the actual mask */
- GimpTempBuf *blurred_mask; /* blurred actual mask cached */
- GimpTempBuf *pixmap; /* optional pixmap data */
- GimpTempBuf *blurred_pixmap; /* optional pixmap data blurred cache */
-
- gdouble blur_hardness;
-
- gint spacing; /* brush's spacing */
- GimpVector2 x_axis; /* for calculating brush spacing */
- GimpVector2 y_axis; /* for calculating brush spacing */
-
- gint use_count; /* for keeping the caches alive */
- GimpBrushCache *mask_cache;
- GimpBrushCache *pixmap_cache;
- GimpBrushCache *boundary_cache;
+ GimpTempBuf *mask; /* the actual mask */
+ GimpTempBuf *blurred_mask; /* blurred actual mask cached */
+ GimpTempBuf *pixmap; /* optional pixmap data */
+ GimpTempBuf *blurred_pixmap; /* optional pixmap data blurred cache */
+
+ gdouble blur_hardness;
+
+ gint n_horz_mipmaps;
+ gint n_vert_mipmaps;
+ GimpTempBuf **mask_mipmaps;
+ GimpTempBuf **pixmap_mipmaps;
+
+ gint spacing; /* brush's spacing */
+ GimpVector2 x_axis; /* for calculating brush spacing */
+ GimpVector2 y_axis; /* for calculating brush spacing */
+
+ gint use_count; /* for keeping the caches alive */
+ GimpBrushCache *mask_cache;
+ GimpBrushCache *pixmap_cache;
+ GimpBrushCache *boundary_cache;
};
diff --git a/app/core/gimpbrush-transform.cc b/app/core/gimpbrush-transform.cc
index 0a97fd61b4..4b4f4ce01c 100644
--- a/app/core/gimpbrush-transform.cc
+++ b/app/core/gimpbrush-transform.cc
@@ -34,6 +34,7 @@ extern "C"
#include "gegl/gimp-gegl-loops.h"
#include "gimpbrush.h"
+#include "gimpbrush-mipmap.h"
#include "gimpbrush-transform.h"
#include "gimptempbuf.h"
@@ -44,7 +45,7 @@ extern "C"
/* local function prototypes */
-static void gimp_brush_transform_bounding_box (GimpBrush *brush,
+static void gimp_brush_transform_bounding_box (const GimpTempBuf *temp_buf,
const GimpMatrix3 *matrix,
gint *x,
gint *y,
@@ -73,18 +74,21 @@ gimp_brush_real_transform_size (GimpBrush *brush,
gint *width,
gint *height)
{
- GimpMatrix3 matrix;
- gdouble scale_x, scale_y;
- gint x, y;
+ const GimpTempBuf *source;
+ GimpMatrix3 matrix;
+ gdouble scale_x, scale_y;
+ gint x, y;
gimp_brush_transform_get_scale (scale, aspect_ratio,
&scale_x, &scale_y);
- gimp_brush_transform_matrix (gimp_brush_get_width (brush),
- gimp_brush_get_height (brush),
+ source = gimp_brush_mipmap_get_mask (brush, &scale_x, &scale_y);
+
+ gimp_brush_transform_matrix (gimp_temp_buf_get_width (source),
+ gimp_temp_buf_get_height (source),
scale_x, scale_y, angle, reflect, &matrix);
- gimp_brush_transform_bounding_box (brush, &matrix, &x, &y, width, height);
+ gimp_brush_transform_bounding_box (source, &matrix, &x, &y, width, height);
}
/*
@@ -121,33 +125,33 @@ gimp_brush_real_transform_mask (GimpBrush *brush,
gboolean reflect,
gdouble hardness)
{
- GimpTempBuf *result;
- GimpTempBuf *source;
- const guchar *src;
- GimpMatrix3 matrix;
- gdouble scale_x, scale_y;
- gint src_width;
- gint src_height;
- gint src_width_minus_one;
- gint src_height_minus_one;
- gint dest_width;
- gint dest_height;
- gint blur_radius;
- gint x, y;
- gdouble b_lx, b_rx, t_lx, t_rx;
- gdouble b_ly, b_ry, t_ly, t_ry;
- gdouble src_tl_to_tr_delta_x;
- gdouble src_tl_to_tr_delta_y;
- gdouble src_tl_to_bl_delta_x;
- gdouble src_tl_to_bl_delta_y;
- gint src_walk_ux_i;
- gint src_walk_uy_i;
- gint src_walk_vx_i;
- gint src_walk_vy_i;
- gint src_x_min_i;
- gint src_y_min_i;
- gint src_x_max_i;
- gint src_y_max_i;
+ GimpTempBuf *result;
+ const GimpTempBuf *source;
+ const guchar *src;
+ GimpMatrix3 matrix;
+ gdouble scale_x, scale_y;
+ gint src_width;
+ gint src_height;
+ gint src_width_minus_one;
+ gint src_height_minus_one;
+ gint dest_width;
+ gint dest_height;
+ gint blur_radius;
+ gint x, y;
+ gdouble b_lx, b_rx, t_lx, t_rx;
+ gdouble b_ly, b_ry, t_ly, t_ry;
+ gdouble src_tl_to_tr_delta_x;
+ gdouble src_tl_to_tr_delta_y;
+ gdouble src_tl_to_bl_delta_x;
+ gdouble src_tl_to_bl_delta_y;
+ gint src_walk_ux_i;
+ gint src_walk_uy_i;
+ gint src_walk_vx_i;
+ gint src_walk_vy_i;
+ gint src_x_min_i;
+ gint src_y_min_i;
+ gint src_x_max_i;
+ gint src_y_max_i;
/*
* tl, tr etc are used because it is easier to visualize top left,
@@ -182,10 +186,10 @@ gimp_brush_real_transform_mask (GimpBrush *brush,
gimp_brush_transform_get_scale (scale, aspect_ratio,
&scale_x, &scale_y);
- source = gimp_brush_get_mask (brush);
+ source = gimp_brush_mipmap_get_mask (brush, &scale_x, &scale_y);
- src_width = gimp_brush_get_width (brush);
- src_height = gimp_brush_get_height (brush);
+ src_width = gimp_temp_buf_get_width (source);
+ src_height = gimp_temp_buf_get_height (source);
gimp_brush_transform_matrix (src_width, src_height,
scale_x, scale_y, angle, reflect, &matrix);
@@ -196,7 +200,7 @@ gimp_brush_real_transform_mask (GimpBrush *brush,
src_width_minus_one = src_width - 1;
src_height_minus_one = src_height - 1;
- gimp_brush_transform_bounding_box (brush, &matrix,
+ gimp_brush_transform_bounding_box (source, &matrix,
&x, &y, &dest_width, &dest_height);
blur_radius = 0;
@@ -213,7 +217,7 @@ gimp_brush_real_transform_mask (GimpBrush *brush,
scale_x, scale_y, 0.0, FALSE,
&unrotated_matrix);
- gimp_brush_transform_bounding_box (brush, &unrotated_matrix,
+ gimp_brush_transform_bounding_box (source, &unrotated_matrix,
&unrotated_x, &unrotated_y,
&unrotated_dest_width,
&unrotated_dest_height);
@@ -435,33 +439,33 @@ gimp_brush_real_transform_pixmap (GimpBrush *brush,
gboolean reflect,
gdouble hardness)
{
- GimpTempBuf *result;
- GimpTempBuf *source;
- const guchar *src;
- GimpMatrix3 matrix;
- gdouble scale_x, scale_y;
- gint src_width;
- gint src_height;
- gint src_width_minus_one;
- gint src_height_minus_one;
- gint dest_width;
- gint dest_height;
- gint blur_radius;
- gint x, y;
- gdouble b_lx, b_rx, t_lx, t_rx;
- gdouble b_ly, b_ry, t_ly, t_ry;
- gdouble src_tl_to_tr_delta_x;
- gdouble src_tl_to_tr_delta_y;
- gdouble src_tl_to_bl_delta_x;
- gdouble src_tl_to_bl_delta_y;
- gint src_walk_ux_i;
- gint src_walk_uy_i;
- gint src_walk_vx_i;
- gint src_walk_vy_i;
- gint src_x_min_i;
- gint src_y_min_i;
- gint src_x_max_i;
- gint src_y_max_i;
+ GimpTempBuf *result;
+ const GimpTempBuf *source;
+ const guchar *src;
+ GimpMatrix3 matrix;
+ gdouble scale_x, scale_y;
+ gint src_width;
+ gint src_height;
+ gint src_width_minus_one;
+ gint src_height_minus_one;
+ gint dest_width;
+ gint dest_height;
+ gint blur_radius;
+ gint x, y;
+ gdouble b_lx, b_rx, t_lx, t_rx;
+ gdouble b_ly, b_ry, t_ly, t_ry;
+ gdouble src_tl_to_tr_delta_x;
+ gdouble src_tl_to_tr_delta_y;
+ gdouble src_tl_to_bl_delta_x;
+ gdouble src_tl_to_bl_delta_y;
+ gint src_walk_ux_i;
+ gint src_walk_uy_i;
+ gint src_walk_vx_i;
+ gint src_walk_vy_i;
+ gint src_x_min_i;
+ gint src_y_min_i;
+ gint src_x_max_i;
+ gint src_y_max_i;
/*
* tl, tr etc are used because it is easier to visualize top left,
@@ -496,10 +500,10 @@ gimp_brush_real_transform_pixmap (GimpBrush *brush,
gimp_brush_transform_get_scale (scale, aspect_ratio,
&scale_x, &scale_y);
- source = gimp_brush_get_pixmap (brush);
+ source = gimp_brush_mipmap_get_pixmap (brush, &scale_x, &scale_y);
- src_width = gimp_brush_get_width (brush);
- src_height = gimp_brush_get_height (brush);
+ src_width = gimp_temp_buf_get_width (source);
+ src_height = gimp_temp_buf_get_height (source);
gimp_brush_transform_matrix (src_width, src_height,
scale_x, scale_y, angle, reflect, &matrix);
@@ -510,7 +514,7 @@ gimp_brush_real_transform_pixmap (GimpBrush *brush,
src_width_minus_one = src_width - 1;
src_height_minus_one = src_height - 1;
- gimp_brush_transform_bounding_box (brush, &matrix,
+ gimp_brush_transform_bounding_box (source, &matrix,
&x, &y, &dest_width, &dest_height);
blur_radius = 0;
@@ -527,7 +531,7 @@ gimp_brush_real_transform_pixmap (GimpBrush *brush,
scale_x, scale_y, 0.0, FALSE,
&unrotated_matrix);
- gimp_brush_transform_bounding_box (brush, &unrotated_matrix,
+ gimp_brush_transform_bounding_box (source, &unrotated_matrix,
&unrotated_x, &unrotated_y,
&unrotated_dest_width,
&unrotated_dest_height);
@@ -767,17 +771,17 @@ gimp_brush_transform_matrix (gdouble width,
/* private functions */
static void
-gimp_brush_transform_bounding_box (GimpBrush *brush,
+gimp_brush_transform_bounding_box (const GimpTempBuf *temp_buf,
const GimpMatrix3 *matrix,
gint *x,
gint *y,
gint *width,
gint *height)
{
- const gdouble w = gimp_brush_get_width (brush);
- const gdouble h = gimp_brush_get_height (brush);
- gdouble x1, x2, x3, x4;
- gdouble y1, y2, y3, y4;
+ const gdouble w = gimp_temp_buf_get_width (temp_buf);
+ const gdouble h = gimp_temp_buf_get_height (temp_buf);
+ gdouble x1, x2, x3, x4;
+ gdouble y1, y2, y3, y4;
gimp_matrix3_transform_point (matrix, 0, 0, &x1, &y1);
gimp_matrix3_transform_point (matrix, w, 0, &x2, &y2);
diff --git a/app/core/gimpbrush.c b/app/core/gimpbrush.c
index ddb1e2be59..8e151158ed 100644
--- a/app/core/gimpbrush.c
+++ b/app/core/gimpbrush.c
@@ -30,6 +30,7 @@
#include "gimpbrush.h"
#include "gimpbrush-boundary.h"
#include "gimpbrush-load.h"
+#include "gimpbrush-mipmap.h"
#include "gimpbrush-private.h"
#include "gimpbrush-save.h"
#include "gimpbrush-transform.h"
@@ -189,6 +190,8 @@ gimp_brush_finalize (GObject *object)
g_clear_pointer (&brush->priv->blurred_mask, gimp_temp_buf_unref);
g_clear_pointer (&brush->priv->blurred_pixmap, gimp_temp_buf_unref);
+ gimp_brush_mipmap_clear (brush);
+
g_clear_object (&brush->priv->mask_cache);
g_clear_object (&brush->priv->pixmap_cache);
g_clear_object (&brush->priv->boundary_cache);
@@ -246,6 +249,8 @@ gimp_brush_get_memsize (GimpObject *object,
memsize += gimp_temp_buf_get_memsize (brush->priv->mask);
memsize += gimp_temp_buf_get_memsize (brush->priv->pixmap);
+ memsize += gimp_brush_mipmap_get_memsize (brush);
+
return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
gui_size);
}
@@ -410,6 +415,8 @@ gimp_brush_dirty (GimpData *data)
if (brush->priv->boundary_cache)
gimp_brush_cache_clear (brush->priv->boundary_cache);
+ gimp_brush_mipmap_clear (brush);
+
g_clear_pointer (&brush->priv->blurred_mask, gimp_temp_buf_unref);
g_clear_pointer (&brush->priv->blurred_pixmap, gimp_temp_buf_unref);
diff --git a/app/core/meson.build b/app/core/meson.build
index 2164b727a5..89a205d32e 100644
--- a/app/core/meson.build
+++ b/app/core/meson.build
@@ -59,6 +59,7 @@ libappcore_sources = [
'gimpboundary.c',
'gimpbrush-boundary.c',
'gimpbrush-load.c',
+ 'gimpbrush-mipmap.cc',
'gimpbrush-save.c',
'gimpbrush-transform.cc',
'gimpbrush.c',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]