/* 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 . */ #include "config.h" #include #include #include #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); }