/* * pikture.c * PIKTURE format plugin for GIMP. * This plugin is designed to interface with GIMP (licensed under GPLv3). This plugin itself is an independent work and is provided under the Apache License 2.0. Users are free to use, modify, and distribute this plugin independently of GIMP's licensing terms. * Version: 101 * * Copyright 2026 Nexoniarz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "pikture.c" #include #define LOAD_PROC "file-pik-load" #define SAVE_PROC "file-pik-save" struct _PikturePlugin { GimpPlugIn parent_instance; }; #define PIKTURE_PLUGIN_TYPE (pikture_plugin_get_type()) G_DECLARE_FINAL_TYPE (PikturePlugin, pikture_plugin, PIKTURE, PLUGIN, GimpPlugIn) static GList * pikture_plugin_query_procedures (GimpPlugIn *plug_in); static GimpProcedure * pikture_plugin_create_procedure (GimpPlugIn *plug_in, const gchar *name); static GimpValueArray * pikture_load_run (GimpProcedure *procedure, GimpRunMode run_mode, GFile *file, GimpMetadata *metadata, GimpMetadataLoadFlags *flags, GimpProcedureConfig *config, gpointer run_data); static GimpValueArray * pikture_save_run (GimpProcedure *procedure, GimpRunMode run_mode, GimpImage *image, GFile *file, GimpExportOptions *options, GimpMetadata *metadata, GimpProcedureConfig *config, gpointer run_data); G_DEFINE_TYPE (PikturePlugin, pikture_plugin, GIMP_TYPE_PLUG_IN) static void pikture_plugin_class_init (PikturePluginClass *klass) { GimpPlugInClass *plug_in_class = GIMP_PLUG_IN_CLASS (klass); plug_in_class->query_procedures = pikture_plugin_query_procedures; plug_in_class->create_procedure = pikture_plugin_create_procedure; } static void pikture_plugin_init (PikturePlugin *plugin) {} static GList * pikture_plugin_query_procedures (GimpPlugIn *plug_in) { GList *list = NULL; list = g_list_append (list, g_strdup (LOAD_PROC)); list = g_list_append (list, g_strdup (SAVE_PROC)); return list; } static GimpProcedure * pikture_plugin_create_procedure (GimpPlugIn *plug_in, const gchar *name) { GimpProcedure *procedure = NULL; if (g_strcmp0 (name, LOAD_PROC) == 0) { procedure = gimp_load_procedure_new (plug_in, name, GIMP_PDB_PROC_TYPE_PLUGIN, pikture_load_run, NULL, NULL); gimp_procedure_set_menu_label (procedure, "PIKTURE image"); gimp_procedure_set_documentation (procedure, "Loads a PIKTURE image", "Loads a PIKTURE image", NULL); gimp_procedure_set_attribution (procedure, "Nexoniarz", "Nexoniarz", "2026"); gimp_file_procedure_set_mime_types (GIMP_FILE_PROCEDURE (procedure), "image/x-pikture"); gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure), "pik"); gimp_file_procedure_set_magics (GIMP_FILE_PROCEDURE (procedure), "0,string,PIK!"); } else if (g_strcmp0 (name, SAVE_PROC) == 0) { procedure = gimp_export_procedure_new (plug_in, name, GIMP_PDB_PROC_TYPE_PLUGIN, TRUE, (GimpRunExportFunc) pikture_save_run, NULL, NULL); gimp_procedure_set_image_types (procedure, "RGB, RGBA, GRAY, GRAYA"); gimp_procedure_set_menu_label (procedure, "PIKTURE image"); gimp_procedure_set_documentation (procedure, "Exports a PIKTURE image", "Exports a PIKTURE image", NULL); gimp_procedure_set_attribution (procedure, "Nexoniarz", "Nexoniarz", "2026"); gimp_file_procedure_set_mime_types (GIMP_FILE_PROCEDURE (procedure), "image/x-pikture"); gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure), "pik"); } return procedure; } static GimpValueArray * pikture_load_run (GimpProcedure *procedure, GimpRunMode run_mode, GFile *file, GimpMetadata *metadata, GimpMetadataLoadFlags *flags, GimpProcedureConfig *config, gpointer run_data) { GimpValueArray *return_vals; gchar *filename = g_file_get_path (file); pikture_t *pik = pikture_load(filename); g_free (filename); if (!pik) { GError *error = g_error_new (GIMP_PLUG_IN_ERROR, 0, "Failed to load PIKTURE file."); return gimp_procedure_new_return_values (procedure, GIMP_PDB_EXECUTION_ERROR, error); } GimpImageBaseType base_type = GIMP_RGB; GimpImageType layer_type = GIMP_RGBA_IMAGE; const gchar* b_format = "R'G'B'A u8"; if (pik->channels == 1) { base_type = GIMP_GRAY; layer_type = GIMP_GRAY_IMAGE; b_format = "Y' u8"; } else if (pik->channels == 2) { base_type = GIMP_GRAY; layer_type = GIMP_GRAYA_IMAGE; b_format = "Y'A u8"; } else if (pik->channels == 3) { base_type = GIMP_RGB; layer_type = GIMP_RGB_IMAGE; b_format = "R'G'B' u8"; } GimpImage *image = gimp_image_new (pik->width, pik->height, base_type); GimpLayer *layer = gimp_layer_new (image, "Background", pik->width, pik->height, layer_type, 100.0, gimp_image_get_default_new_layer_mode (image)); gimp_image_insert_layer (image, layer, NULL, 0); GeglBuffer *buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)); gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, pik->width, pik->height), 0, babl_format (b_format), pik->pixels, GEGL_AUTO_ROWSTRIDE); g_object_unref (buffer); pikture_free(pik); return_vals = gimp_procedure_new_return_values (procedure, GIMP_PDB_SUCCESS, NULL); g_value_set_object (gimp_value_array_index (return_vals, 1), image); return return_vals; } static GimpValueArray * pikture_save_run (GimpProcedure *procedure, GimpRunMode run_mode, GimpImage *image, GFile *file, GimpExportOptions *options, GimpMetadata *metadata, GimpProcedureConfig *config, gpointer run_data) { gchar *filename = g_file_get_path (file); GimpLayer **layers = gimp_image_get_layers (image); if (!layers || !layers[0]) { if (layers) g_free (layers); g_free (filename); GError *error = g_error_new (GIMP_PLUG_IN_ERROR, 0, "No layers to export."); return gimp_procedure_new_return_values (procedure, GIMP_PDB_EXECUTION_ERROR, error); } GimpDrawable *drawable = GIMP_DRAWABLE (layers[0]); pikture_t pik; pik.width = gimp_drawable_get_width (drawable); pik.height = gimp_drawable_get_height (drawable); gboolean has_alpha = gimp_drawable_has_alpha (drawable); gboolean is_gray = gimp_drawable_is_gray (drawable); const gchar* b_format = "R'G'B'A u8"; if (is_gray) { if (has_alpha) { pik.channels = 2; b_format = "Y'A u8"; } else { pik.channels = 1; b_format = "Y' u8"; } } else { if (has_alpha) { pik.channels = 4; b_format = "R'G'B'A u8"; } else { pik.channels = 3; b_format = "R'G'B' u8"; } } pik.depth = pik.channels * 8; pik.pixels = malloc (pik.width * pik.height * pik.channels); GeglBuffer *buffer = gimp_drawable_get_buffer (drawable); gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, pik.width, pik.height), 1.0, babl_format (b_format), pik.pixels, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); g_object_unref (buffer); int result = pikture_save(filename, &pik); free (pik.pixels); g_free (filename); g_free (layers); if (!result) { GError *error = g_error_new (GIMP_PLUG_IN_ERROR, 0, "Failed to export PIKTURE file."); return gimp_procedure_new_return_values (procedure, GIMP_PDB_EXECUTION_ERROR, error); } return gimp_procedure_new_return_values (procedure, GIMP_PDB_SUCCESS, NULL); } GIMP_MAIN (PIKTURE_PLUGIN_TYPE)