PIKApp/app/core/pikapattern.c

322 lines
10 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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>
#include "libpikabase/pikabase.h"
#include "core-types.h"
#include "gegl/pika-gegl-loops.h"
#include "pikapattern.h"
#include "pikapattern-load.h"
#include "pikapattern-save.h"
#include "pikatagged.h"
#include "pikatempbuf.h"
#include "pika-intl.h"
static void pika_pattern_tagged_iface_init (PikaTaggedInterface *iface);
static void pika_pattern_finalize (GObject *object);
static gint64 pika_pattern_get_memsize (PikaObject *object,
gint64 *gui_size);
static gboolean pika_pattern_get_size (PikaViewable *viewable,
gint *width,
gint *height);
static PikaTempBuf * pika_pattern_get_new_preview (PikaViewable *viewable,
PikaContext *context,
gint width,
gint height);
static gchar * pika_pattern_get_description (PikaViewable *viewable,
gchar **tooltip);
static const gchar * pika_pattern_get_extension (PikaData *data);
static void pika_pattern_copy (PikaData *data,
PikaData *src_data);
static gchar * pika_pattern_get_checksum (PikaTagged *tagged);
G_DEFINE_TYPE_WITH_CODE (PikaPattern, pika_pattern, PIKA_TYPE_DATA,
G_IMPLEMENT_INTERFACE (PIKA_TYPE_TAGGED,
pika_pattern_tagged_iface_init))
#define parent_class pika_pattern_parent_class
static void
pika_pattern_class_init (PikaPatternClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
PikaObjectClass *pika_object_class = PIKA_OBJECT_CLASS (klass);
PikaViewableClass *viewable_class = PIKA_VIEWABLE_CLASS (klass);
PikaDataClass *data_class = PIKA_DATA_CLASS (klass);
object_class->finalize = pika_pattern_finalize;
pika_object_class->get_memsize = pika_pattern_get_memsize;
viewable_class->default_icon_name = "pika-tool-bucket-fill";
viewable_class->get_size = pika_pattern_get_size;
viewable_class->get_new_preview = pika_pattern_get_new_preview;
viewable_class->get_description = pika_pattern_get_description;
data_class->save = pika_pattern_save;
data_class->get_extension = pika_pattern_get_extension;
data_class->copy = pika_pattern_copy;
}
static void
pika_pattern_tagged_iface_init (PikaTaggedInterface *iface)
{
iface->get_checksum = pika_pattern_get_checksum;
}
static void
pika_pattern_init (PikaPattern *pattern)
{
pattern->mask = NULL;
}
static void
pika_pattern_finalize (GObject *object)
{
PikaPattern *pattern = PIKA_PATTERN (object);
g_clear_pointer (&pattern->mask, pika_temp_buf_unref);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gint64
pika_pattern_get_memsize (PikaObject *object,
gint64 *gui_size)
{
PikaPattern *pattern = PIKA_PATTERN (object);
gint64 memsize = 0;
memsize += pika_temp_buf_get_memsize (pattern->mask);
return memsize + PIKA_OBJECT_CLASS (parent_class)->get_memsize (object,
gui_size);
}
static gboolean
pika_pattern_get_size (PikaViewable *viewable,
gint *width,
gint *height)
{
PikaPattern *pattern = PIKA_PATTERN (viewable);
*width = pika_temp_buf_get_width (pattern->mask);
*height = pika_temp_buf_get_height (pattern->mask);
return TRUE;
}
static PikaTempBuf *
pika_pattern_get_new_preview (PikaViewable *viewable,
PikaContext *context,
gint width,
gint height)
{
PikaPattern *pattern = PIKA_PATTERN (viewable);
PikaTempBuf *temp_buf;
GeglBuffer *src_buffer;
gint true_width;
gint true_height;
gint copy_width;
gint copy_height;
true_width = pika_temp_buf_get_width (pattern->mask);
true_height = pika_temp_buf_get_height (pattern->mask);
copy_width = MIN (width, true_width);
copy_height = MIN (height, true_height);
src_buffer = pika_temp_buf_create_buffer (pattern->mask);
if (true_width > width || true_height > height)
{
gdouble ratio_x = (gdouble) width / (gdouble) true_width;
gdouble ratio_y = (gdouble) height / (gdouble) true_height;
gdouble scale = MIN (ratio_x, ratio_y);
gdouble aspect = (gdouble) true_width / (gdouble) true_height;
/* Adjusting dimensions for non-square patterns */
if (true_width > true_height)
copy_height = copy_width / aspect;
else if (true_width < true_height)
copy_width = copy_height * aspect;
temp_buf = pika_temp_buf_new (copy_width, copy_height,
pika_temp_buf_get_format (pattern->mask));
gegl_buffer_get (src_buffer,
GEGL_RECTANGLE (0, 0, copy_width, copy_height),
scale, pika_temp_buf_get_format (temp_buf),
pika_temp_buf_get_data (temp_buf),
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP);
}
else
{
GeglBuffer *dest_buffer;
temp_buf = pika_temp_buf_new (copy_width, copy_height,
pika_temp_buf_get_format (pattern->mask));
dest_buffer = pika_temp_buf_create_buffer (temp_buf);
pika_gegl_buffer_copy (src_buffer,
GEGL_RECTANGLE (0, 0, copy_width, copy_height),
GEGL_ABYSS_NONE, dest_buffer,
GEGL_RECTANGLE (0, 0, 0, 0));
g_object_unref (dest_buffer);
}
g_object_unref (src_buffer);
return temp_buf;
}
static gchar *
pika_pattern_get_description (PikaViewable *viewable,
gchar **tooltip)
{
PikaPattern *pattern = PIKA_PATTERN (viewable);
return g_strdup_printf ("%s (%d × %d)",
pika_object_get_name (pattern),
pika_temp_buf_get_width (pattern->mask),
pika_temp_buf_get_height (pattern->mask));
}
static const gchar *
pika_pattern_get_extension (PikaData *data)
{
return PIKA_PATTERN_FILE_EXTENSION;
}
static void
pika_pattern_copy (PikaData *data,
PikaData *src_data)
{
PikaPattern *pattern = PIKA_PATTERN (data);
PikaPattern *src_pattern = PIKA_PATTERN (src_data);
g_clear_pointer (&pattern->mask, pika_temp_buf_unref);
pattern->mask = pika_temp_buf_copy (src_pattern->mask);
pika_data_dirty (data);
}
static gchar *
pika_pattern_get_checksum (PikaTagged *tagged)
{
PikaPattern *pattern = PIKA_PATTERN (tagged);
gchar *checksum_string = NULL;
if (pattern->mask)
{
GChecksum *checksum = g_checksum_new (G_CHECKSUM_MD5);
g_checksum_update (checksum, pika_temp_buf_get_data (pattern->mask),
pika_temp_buf_get_data_size (pattern->mask));
checksum_string = g_strdup (g_checksum_get_string (checksum));
g_checksum_free (checksum);
}
return checksum_string;
}
PikaData *
pika_pattern_new (PikaContext *context,
const gchar *name)
{
PikaPattern *pattern;
guchar *data;
gint row, col;
g_return_val_if_fail (name != NULL, NULL);
g_return_val_if_fail (name[0] != '\n', NULL);
pattern = g_object_new (PIKA_TYPE_PATTERN,
"name", name,
NULL);
pattern->mask = pika_temp_buf_new (32, 32, babl_format ("R'G'B' u8"));
data = pika_temp_buf_get_data (pattern->mask);
for (row = 0; row < pika_temp_buf_get_height (pattern->mask); row++)
for (col = 0; col < pika_temp_buf_get_width (pattern->mask); col++)
{
memset (data, (col % 2) && (row % 2) ? 255 : 0, 3);
data += 3;
}
return PIKA_DATA (pattern);
}
PikaData *
pika_pattern_get_standard (PikaContext *context)
{
static PikaData *standard_pattern = NULL;
if (! standard_pattern)
{
g_set_weak_pointer (&standard_pattern,
pika_pattern_new (context, "Standard"));
pika_data_clean (standard_pattern);
pika_data_make_internal (standard_pattern, "pika-pattern-standard");
}
return standard_pattern;
}
PikaTempBuf *
pika_pattern_get_mask (PikaPattern *pattern)
{
g_return_val_if_fail (PIKA_IS_PATTERN (pattern), NULL);
return pattern->mask;
}
GeglBuffer *
pika_pattern_create_buffer (PikaPattern *pattern)
{
g_return_val_if_fail (PIKA_IS_PATTERN (pattern), NULL);
return pika_temp_buf_create_buffer (pattern->mask);
}