Initial checkin of Pika from heckimp

This commit is contained in:
2023-09-25 15:35:21 -07:00
commit 891e999216
6761 changed files with 5240685 additions and 0 deletions

1515
app/core/core-enums.c Normal file

File diff suppressed because it is too large Load Diff

789
app/core/core-enums.h Normal file
View File

@ -0,0 +1,789 @@
/* 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/>.
*/
#ifndef __CORE_ENUMS_H__
#define __CORE_ENUMS_H__
#if 0
This file is parsed by two scripts, enumgen.pl in pdb,
and pika-mkenums. All enums that are not marked with
/*< pdb-skip >*/ are exported to libpika and the PDB. Enums that are
not marked with /*< skip >*/ are registered with the GType system.
If you want the enum to be skipped by both scripts, you have to use
/*< pdb-skip, skip >*/.
The same syntax applies to enum values.
#endif
/*
* these enums are registered with the type system
*/
#define PIKA_TYPE_ALIGN_REFERENCE_TYPE (pika_align_reference_type_get_type ())
GType pika_align_reference_type_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_ALIGN_REFERENCE_IMAGE, /*< desc="Image" >*/
PIKA_ALIGN_REFERENCE_SELECTION, /*< desc="Selection" >*/
PIKA_ALIGN_REFERENCE_PICK, /*< desc="Picked reference object" >*/
} PikaAlignReferenceType;
#define PIKA_TYPE_ALIGNMENT_TYPE (pika_alignment_type_get_type ())
GType pika_alignment_type_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_ALIGN_LEFT, /*< desc="Align to the left" >*/
PIKA_ALIGN_HCENTER, /*< desc="Center horizontally" >*/
PIKA_ALIGN_RIGHT, /*< desc="Align to the right" >*/
PIKA_ALIGN_TOP, /*< desc="Align to the top" >*/
PIKA_ALIGN_VCENTER, /*< desc="Center vertically" >*/
PIKA_ALIGN_BOTTOM, /*< desc="Align to the bottom" >*/
PIKA_ARRANGE_HFILL, /*< desc="Distribute anchor points horizontally evenly" >*/
PIKA_ARRANGE_VFILL, /*< desc="Distribute anchor points vertically evenly" >*/
PIKA_DISTRIBUTE_EVEN_HORIZONTAL_GAP, /*< desc="Distribute horizontally with even horizontal gaps" >*/
PIKA_DISTRIBUTE_EVEN_VERTICAL_GAP, /*< desc="Distribute vertically with even vertical gaps" >*/
} PikaAlignmentType;
/**
* PikaBucketFillMode:
* @PIKA_BUCKET_FILL_FG: FG color fill
* @PIKA_BUCKET_FILL_BG: BG color fill
* @PIKA_BUCKET_FILL_PATTERN: Pattern fill
*
* Bucket fill modes.
*/
#define PIKA_TYPE_BUCKET_FILL_MODE (pika_bucket_fill_mode_get_type ())
GType pika_bucket_fill_mode_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_BUCKET_FILL_FG, /*< desc="FG color fill" >*/
PIKA_BUCKET_FILL_BG, /*< desc="BG color fill" >*/
PIKA_BUCKET_FILL_PATTERN /*< desc="Pattern fill" >*/
} PikaBucketFillMode;
#define PIKA_TYPE_CHANNEL_BORDER_STYLE (pika_channel_border_style_get_type ())
GType pika_channel_border_style_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_CHANNEL_BORDER_STYLE_HARD, /*< desc="Hard" >*/
PIKA_CHANNEL_BORDER_STYLE_SMOOTH, /*< desc="Smooth" >*/
PIKA_CHANNEL_BORDER_STYLE_FEATHERED /*< desc="Feathered" >*/
} PikaChannelBorderStyle;
/* Note: when appending values here, don't forget to update
* PikaColorFrame and other places use this enum to create combo
* boxes.
*/
#define PIKA_TYPE_COLOR_PICK_MODE (pika_color_pick_mode_get_type ())
GType pika_color_pick_mode_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_COLOR_PICK_MODE_PIXEL, /*< desc="Pixel" >*/
PIKA_COLOR_PICK_MODE_RGB_PERCENT, /*< desc="RGB (%)" >*/
PIKA_COLOR_PICK_MODE_RGB_U8, /*< desc="RGB (0..255)" >*/
PIKA_COLOR_PICK_MODE_GRAYSCALE, /*< desc="Grayscale (%)">*/
PIKA_COLOR_PICK_MODE_HSV, /*< desc="HSV" >*/
PIKA_COLOR_PICK_MODE_LCH, /*< desc="CIE LCh" >*/
PIKA_COLOR_PICK_MODE_LAB, /*< desc="CIE LAB" >*/
PIKA_COLOR_PICK_MODE_CMYK, /*< desc="CMYK" >*/
PIKA_COLOR_PICK_MODE_XYY, /*< desc="CIE xyY" >*/
PIKA_COLOR_PICK_MODE_YUV, /*< desc="CIE Yu'v'" >*/
PIKA_COLOR_PICK_MODE_LAST = PIKA_COLOR_PICK_MODE_YUV /*< skip >*/
} PikaColorPickMode;
#define PIKA_TYPE_COLOR_PROFILE_POLICY (pika_color_profile_policy_get_type ())
GType pika_color_profile_policy_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_COLOR_PROFILE_POLICY_ASK, /*< desc="Ask what to do" >*/
PIKA_COLOR_PROFILE_POLICY_KEEP, /*< desc="Keep embedded profile" >*/
PIKA_COLOR_PROFILE_POLICY_CONVERT_BUILTIN, /*< desc="Convert to built-in sRGB or grayscale profile" >*/
PIKA_COLOR_PROFILE_POLICY_CONVERT_PREFERRED, /*< desc="Convert to preferred RGB or grayscale profile (defaulting to built-in)" >*/
} PikaColorProfilePolicy;
#define PIKA_TYPE_COMPONENT_MASK (pika_component_mask_get_type ())
GType pika_component_mask_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_COMPONENT_MASK_RED = 1 << 0,
PIKA_COMPONENT_MASK_GREEN = 1 << 1,
PIKA_COMPONENT_MASK_BLUE = 1 << 2,
PIKA_COMPONENT_MASK_ALPHA = 1 << 3,
PIKA_COMPONENT_MASK_ALL = (PIKA_COMPONENT_MASK_RED |
PIKA_COMPONENT_MASK_GREEN |
PIKA_COMPONENT_MASK_BLUE |
PIKA_COMPONENT_MASK_ALPHA)
} PikaComponentMask;
#define PIKA_TYPE_CONTAINER_POLICY (pika_container_policy_get_type ())
GType pika_container_policy_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_CONTAINER_POLICY_STRONG,
PIKA_CONTAINER_POLICY_WEAK
} PikaContainerPolicy;
#define PIKA_TYPE_CONVERT_DITHER_TYPE (pika_convert_dither_type_get_type ())
GType pika_convert_dither_type_get_type (void) G_GNUC_CONST;
typedef enum
{
PIKA_CONVERT_DITHER_NONE, /*< desc="None" >*/
PIKA_CONVERT_DITHER_FS, /*< desc="Floyd-Steinberg (normal)" >*/
PIKA_CONVERT_DITHER_FS_LOWBLEED, /*< desc="Floyd-Steinberg (reduced color bleeding)" >*/
PIKA_CONVERT_DITHER_FIXED, /*< desc="Positioned" >*/
PIKA_CONVERT_DITHER_NODESTRUCT /*< pdb-skip, skip >*/
} PikaConvertDitherType;
#define PIKA_TYPE_CONVOLUTION_TYPE (pika_convolution_type_get_type ())
GType pika_convolution_type_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_NORMAL_CONVOL, /* Negative numbers truncated */
PIKA_ABSOLUTE_CONVOL, /* Absolute value */
PIKA_NEGATIVE_CONVOL /* add 127 to values */
} PikaConvolutionType;
#define PIKA_TYPE_CURVE_POINT_TYPE (pika_curve_point_type_get_type ())
GType pika_curve_point_type_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_CURVE_POINT_SMOOTH, /*< desc="Smooth" >*/
PIKA_CURVE_POINT_CORNER /*< desc="Corner" >*/
} PikaCurvePointType;
#define PIKA_TYPE_CURVE_TYPE (pika_curve_type_get_type ())
GType pika_curve_type_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_CURVE_SMOOTH, /*< desc="Smooth" >*/
PIKA_CURVE_FREE /*< desc="Freehand" >*/
} PikaCurveType;
#define PIKA_TYPE_DASH_PRESET (pika_dash_preset_get_type ())
GType pika_dash_preset_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_DASH_CUSTOM, /*< desc="Custom" >*/
PIKA_DASH_LINE, /*< desc="Line" >*/
PIKA_DASH_LONG_DASH, /*< desc="Long dashes" >*/
PIKA_DASH_MEDIUM_DASH, /*< desc="Medium dashes" >*/
PIKA_DASH_SHORT_DASH, /*< desc="Short dashes" >*/
PIKA_DASH_SPARSE_DOTS, /*< desc="Sparse dots" >*/
PIKA_DASH_NORMAL_DOTS, /*< desc="Normal dots" >*/
PIKA_DASH_DENSE_DOTS, /*< desc="Dense dots" >*/
PIKA_DASH_STIPPLES, /*< desc="Stipples" >*/
PIKA_DASH_DASH_DOT, /*< desc="Dash, dot" >*/
PIKA_DASH_DASH_DOT_DOT /*< desc="Dash, dot, dot" >*/
} PikaDashPreset;
#define PIKA_TYPE_DEBUG_POLICY (pika_debug_policy_get_type ())
GType pika_debug_policy_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_DEBUG_POLICY_WARNING, /*< desc="Debug warnings, critical errors and crashes" >*/
PIKA_DEBUG_POLICY_CRITICAL, /*< desc="Debug critical errors and crashes" >*/
PIKA_DEBUG_POLICY_FATAL, /*< desc="Debug crashes only" >*/
PIKA_DEBUG_POLICY_NEVER /*< desc="Never debug PIKA" >*/
} PikaDebugPolicy;
#define PIKA_TYPE_DIRTY_MASK (pika_dirty_mask_get_type ())
GType pika_dirty_mask_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_DIRTY_NONE = 0,
PIKA_DIRTY_IMAGE = 1 << 0,
PIKA_DIRTY_IMAGE_SIZE = 1 << 1,
PIKA_DIRTY_IMAGE_META = 1 << 2,
PIKA_DIRTY_IMAGE_STRUCTURE = 1 << 3,
PIKA_DIRTY_ITEM = 1 << 4,
PIKA_DIRTY_ITEM_META = 1 << 5,
PIKA_DIRTY_DRAWABLE = 1 << 6,
PIKA_DIRTY_VECTORS = 1 << 7,
PIKA_DIRTY_SELECTION = 1 << 8,
PIKA_DIRTY_ACTIVE_DRAWABLE = 1 << 9,
PIKA_DIRTY_ALL = 0xffff
} PikaDirtyMask;
#define PIKA_TYPE_DYNAMICS_OUTPUT_TYPE (pika_dynamics_output_type_get_type ())
GType pika_dynamics_output_type_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_DYNAMICS_OUTPUT_OPACITY, /*< desc="Opacity" >*/
PIKA_DYNAMICS_OUTPUT_SIZE, /*< desc="Size" >*/
PIKA_DYNAMICS_OUTPUT_ANGLE, /*< desc="Angle" >*/
PIKA_DYNAMICS_OUTPUT_COLOR, /*< desc="Color" >*/
PIKA_DYNAMICS_OUTPUT_HARDNESS, /*< desc="Hardness" >*/
PIKA_DYNAMICS_OUTPUT_FORCE, /*< desc="Force" >*/
PIKA_DYNAMICS_OUTPUT_ASPECT_RATIO, /*< desc="Aspect ratio" >*/
PIKA_DYNAMICS_OUTPUT_SPACING, /*< desc="Spacing" >*/
PIKA_DYNAMICS_OUTPUT_RATE, /*< desc="Rate" >*/
PIKA_DYNAMICS_OUTPUT_FLOW, /*< desc="Flow" >*/
PIKA_DYNAMICS_OUTPUT_JITTER, /*< desc="Jitter" >*/
} PikaDynamicsOutputType;
#define PIKA_TYPE_CUSTOM_STYLE (pika_custom_style_get_type ())
GType pika_custom_style_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_CUSTOM_STYLE_SOLID_COLOR, /*< desc="Solid color" >*/
PIKA_CUSTOM_STYLE_PATTERN /*< desc="Pattern" >*/
} PikaCustomStyle;
#define PIKA_TYPE_FILL_STYLE (pika_fill_style_get_type ())
GType pika_fill_style_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_FILL_STYLE_FG_COLOR, /*< desc="Foreground color" >*/
PIKA_FILL_STYLE_BG_COLOR, /*< desc="Background color" >*/
PIKA_FILL_STYLE_PATTERN /*< desc="Pattern" >*/
} PikaFillStyle;
#define PIKA_TYPE_FILTER_REGION (pika_filter_region_get_type ())
GType pika_filter_region_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_FILTER_REGION_SELECTION, /*< desc="Use the selection as input" >*/
PIKA_FILTER_REGION_DRAWABLE /*< desc="Use the entire layer as input" >*/
} PikaFilterRegion;
#define PIKA_TYPE_GRADIENT_COLOR (pika_gradient_color_get_type ())
GType pika_gradient_color_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_GRADIENT_COLOR_FIXED, /*< desc="Fixed" >*/
PIKA_GRADIENT_COLOR_FOREGROUND, /*< desc="Foreground color", abbrev="FG" >*/
PIKA_GRADIENT_COLOR_FOREGROUND_TRANSPARENT, /*< desc="Foreground color (transparent)", abbrev="FG (t)" >*/
PIKA_GRADIENT_COLOR_BACKGROUND, /*< desc="Background color", abbrev="BG" >*/
PIKA_GRADIENT_COLOR_BACKGROUND_TRANSPARENT /*< desc="Background color (transparent)", abbrev="BG (t)" >*/
} PikaGradientColor;
#define PIKA_TYPE_GRAVITY_TYPE (pika_gravity_type_get_type ())
GType pika_gravity_type_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_GRAVITY_NONE,
PIKA_GRAVITY_NORTH_WEST,
PIKA_GRAVITY_NORTH,
PIKA_GRAVITY_NORTH_EAST,
PIKA_GRAVITY_WEST,
PIKA_GRAVITY_CENTER,
PIKA_GRAVITY_EAST,
PIKA_GRAVITY_SOUTH_WEST,
PIKA_GRAVITY_SOUTH,
PIKA_GRAVITY_SOUTH_EAST
} PikaGravityType;
#define PIKA_TYPE_GUIDE_STYLE (pika_guide_style_get_type ())
GType pika_guide_style_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_GUIDE_STYLE_NONE,
PIKA_GUIDE_STYLE_NORMAL,
PIKA_GUIDE_STYLE_MIRROR,
PIKA_GUIDE_STYLE_MANDALA,
PIKA_GUIDE_STYLE_SPLIT_VIEW
} PikaGuideStyle;
#define PIKA_TYPE_HISTOGRAM_CHANNEL (pika_histogram_channel_get_type ())
GType pika_histogram_channel_get_type (void) G_GNUC_CONST;
typedef enum
{
PIKA_HISTOGRAM_VALUE = 0, /*< desc="Value" >*/
PIKA_HISTOGRAM_RED = 1, /*< desc="Red" >*/
PIKA_HISTOGRAM_GREEN = 2, /*< desc="Green" >*/
PIKA_HISTOGRAM_BLUE = 3, /*< desc="Blue" >*/
PIKA_HISTOGRAM_ALPHA = 4, /*< desc="Alpha" >*/
PIKA_HISTOGRAM_LUMINANCE = 5, /*< desc="Luminance" >*/
PIKA_HISTOGRAM_RGB = 6 /*< desc="RGB", pdb-skip >*/
} PikaHistogramChannel;
#define PIKA_TYPE_ITEM_SET (pika_item_set_get_type ())
GType pika_item_set_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_ITEM_SET_NONE, /*< desc="None" >*/
PIKA_ITEM_SET_ALL, /*< desc="All layers" >*/
PIKA_ITEM_SET_IMAGE_SIZED, /*< desc="Image-sized layers" >*/
PIKA_ITEM_SET_VISIBLE, /*< desc="All visible layers" >*/
} PikaItemSet;
#define PIKA_TYPE_MATTING_ENGINE (pika_matting_engine_get_type ())
GType pika_matting_engine_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_MATTING_ENGINE_GLOBAL, /*< desc="Matting Global" >*/
PIKA_MATTING_ENGINE_LEVIN, /*< desc="Matting Levin" >*/
} PikaMattingEngine;
#define PIKA_TYPE_MESSAGE_SEVERITY (pika_message_severity_get_type ())
GType pika_message_severity_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_MESSAGE_INFO, /*< desc="Message" >*/
PIKA_MESSAGE_WARNING, /*< desc="Warning" >*/
PIKA_MESSAGE_ERROR, /*< desc="Error" >*/
PIKA_MESSAGE_BUG_WARNING, /*< desc="WARNING" >*/
PIKA_MESSAGE_BUG_CRITICAL /*< desc="CRITICAL" >*/
} PikaMessageSeverity;
#define PIKA_TYPE_METADATA_ROTATION_POLICY (pika_metadata_rotation_policy_get_type ())
GType pika_metadata_rotation_policy_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_METADATA_ROTATION_POLICY_ASK, /*< desc="Ask what to do" >*/
PIKA_METADATA_ROTATION_POLICY_KEEP, /*< desc="Discard metadata without rotating" >*/
PIKA_METADATA_ROTATION_POLICY_ROTATE /*< desc="Rotate the image then discard metadata" >*/
} PikaMetadataRotationPolicy;
#define PIKA_TYPE_PASTE_TYPE (pika_paste_type_get_type ())
GType pika_paste_type_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_PASTE_TYPE_FLOATING,
PIKA_PASTE_TYPE_FLOATING_IN_PLACE,
PIKA_PASTE_TYPE_FLOATING_INTO,
PIKA_PASTE_TYPE_FLOATING_INTO_IN_PLACE,
PIKA_PASTE_TYPE_NEW_LAYER,
PIKA_PASTE_TYPE_NEW_LAYER_IN_PLACE,
PIKA_PASTE_TYPE_NEW_LAYER_OR_FLOATING,
PIKA_PASTE_TYPE_NEW_LAYER_OR_FLOATING_IN_PLACE,
PIKA_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING,
PIKA_PASTE_TYPE_NEW_MERGED_LAYER_OR_FLOATING_IN_PLACE,
} PikaPasteType;
#define PIKA_TYPE_WIN32_POINTER_INPUT_API (pika_win32_pointer_input_api_get_type ())
GType pika_win32_pointer_input_api_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_WIN32_POINTER_INPUT_API_WINTAB, /*< desc="Wintab" >*/
PIKA_WIN32_POINTER_INPUT_API_WINDOWS_INK /*< desc="Windows Ink" >*/
} PikaWin32PointerInputAPI;
#define PIKA_TYPE_THUMBNAIL_SIZE (pika_thumbnail_size_get_type ())
GType pika_thumbnail_size_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_THUMBNAIL_SIZE_NONE = 0, /*< desc="No thumbnails" >*/
PIKA_THUMBNAIL_SIZE_NORMAL = 128, /*< desc="Normal (128x128)" >*/
PIKA_THUMBNAIL_SIZE_LARGE = 256 /*< desc="Large (256x256)" >*/
} PikaThumbnailSize;
#define PIKA_TYPE_TRC_TYPE (pika_trc_type_get_type ())
GType pika_trc_type_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_TRC_LINEAR, /*< desc="Linear" >*/
PIKA_TRC_NON_LINEAR, /*< desc="Non-Linear" >*/
PIKA_TRC_PERCEPTUAL /*< desc="Perceptual" >*/
} PikaTRCType;
#define PIKA_TYPE_UNDO_EVENT (pika_undo_event_get_type ())
GType pika_undo_event_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_UNDO_EVENT_UNDO_PUSHED, /* a new undo has been added to the undo stack */
PIKA_UNDO_EVENT_UNDO_EXPIRED, /* an undo has been freed from the undo stack */
PIKA_UNDO_EVENT_REDO_EXPIRED, /* a redo has been freed from the redo stack */
PIKA_UNDO_EVENT_UNDO, /* an undo has been executed */
PIKA_UNDO_EVENT_REDO, /* a redo has been executed */
PIKA_UNDO_EVENT_UNDO_FREE, /* all undo and redo info has been cleared */
PIKA_UNDO_EVENT_UNDO_FREEZE, /* undo has been frozen */
PIKA_UNDO_EVENT_UNDO_THAW /* undo has been thawn */
} PikaUndoEvent;
#define PIKA_TYPE_UNDO_MODE (pika_undo_mode_get_type ())
GType pika_undo_mode_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_UNDO_MODE_UNDO,
PIKA_UNDO_MODE_REDO
} PikaUndoMode;
#define PIKA_TYPE_UNDO_TYPE (pika_undo_type_get_type ())
GType pika_undo_type_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
/* Type NO_UNDO_GROUP (0) is special - in the pikaimage structure it
* means there is no undo group currently being added to.
*/
PIKA_UNDO_GROUP_NONE = 0, /*< desc="<<invalid>>" >*/
PIKA_UNDO_GROUP_FIRST = PIKA_UNDO_GROUP_NONE, /*< skip >*/
PIKA_UNDO_GROUP_IMAGE_SCALE, /*< desc="Scale image" >*/
PIKA_UNDO_GROUP_IMAGE_RESIZE, /*< desc="Resize image" >*/
PIKA_UNDO_GROUP_IMAGE_FLIP, /*< desc="Flip image" >*/
PIKA_UNDO_GROUP_IMAGE_ROTATE, /*< desc="Rotate image" >*/
PIKA_UNDO_GROUP_IMAGE_TRANSFORM, /*< desc="Transform image" >*/
PIKA_UNDO_GROUP_IMAGE_CROP, /*< desc="Crop image" >*/
PIKA_UNDO_GROUP_IMAGE_CONVERT, /*< desc="Convert image" >*/
PIKA_UNDO_GROUP_IMAGE_ITEM_REMOVE, /*< desc="Remove item" >*/
PIKA_UNDO_GROUP_IMAGE_ITEM_REORDER, /*< desc="Reorder item" >*/
PIKA_UNDO_GROUP_IMAGE_LAYERS_MERGE, /*< desc="Merge layers" >*/
PIKA_UNDO_GROUP_IMAGE_VECTORS_MERGE, /*< desc="Merge paths" >*/
PIKA_UNDO_GROUP_IMAGE_QUICK_MASK, /*< desc="Quick Mask" >*/
PIKA_UNDO_GROUP_IMAGE_GRID, /*< desc="Grid" >*/
PIKA_UNDO_GROUP_GUIDE, /*< desc="Guide" >*/
PIKA_UNDO_GROUP_SAMPLE_POINT, /*< desc="Sample Point" >*/
PIKA_UNDO_GROUP_DRAWABLE, /*< desc="Layer/Channel" >*/
PIKA_UNDO_GROUP_DRAWABLE_MOD, /*< desc="Layer/Channel modification" >*/
PIKA_UNDO_GROUP_MASK, /*< desc="Selection mask" >*/
PIKA_UNDO_GROUP_ITEM_VISIBILITY, /*< desc="Item visibility" >*/
PIKA_UNDO_GROUP_ITEM_LOCK_CONTENTS, /*< desc="Lock/Unlock contents" >*/
PIKA_UNDO_GROUP_ITEM_LOCK_POSITION, /*< desc="Lock/Unlock position" >*/
PIKA_UNDO_GROUP_ITEM_LOCK_VISIBILITY, /*< desc="Lock/Unlock visibility" >*/
PIKA_UNDO_GROUP_ITEM_PROPERTIES, /*< desc="Item properties" >*/
PIKA_UNDO_GROUP_ITEM_DISPLACE, /*< desc="Move item" >*/
PIKA_UNDO_GROUP_ITEM_SCALE, /*< desc="Scale item" >*/
PIKA_UNDO_GROUP_ITEM_RESIZE, /*< desc="Resize item" >*/
PIKA_UNDO_GROUP_LAYER_ADD, /*< desc="Add layer" >*/
PIKA_UNDO_GROUP_LAYER_ADD_ALPHA, /*< desc="Add alpha channel" >*/
PIKA_UNDO_GROUP_LAYER_ADD_MASK, /*< desc="Add layer mask" >*/
PIKA_UNDO_GROUP_LAYER_APPLY_MASK, /*< desc="Apply layer mask" >*/
PIKA_UNDO_GROUP_LAYER_REMOVE_ALPHA, /*< desc="Remove alpha channel" >*/
PIKA_UNDO_GROUP_LAYER_LOCK_ALPHA, /*< desc="Lock/Unlock alpha channels" >*/
PIKA_UNDO_GROUP_LAYER_OPACITY, /*< desc="Set layers opacity" >*/
PIKA_UNDO_GROUP_LAYER_MODE, /*< desc="Set layers mode" >*/
PIKA_UNDO_GROUP_CHANNEL_ADD, /*< desc="Add channels" >*/
PIKA_UNDO_GROUP_FS_TO_LAYER, /*< desc="Floating selection to layer" >*/
PIKA_UNDO_GROUP_FS_FLOAT, /*< desc="Float selection" >*/
PIKA_UNDO_GROUP_FS_ANCHOR, /*< desc="Anchor floating selection" >*/
PIKA_UNDO_GROUP_EDIT_PASTE, /*< desc="Paste" >*/
PIKA_UNDO_GROUP_EDIT_CUT, /*< desc="Cut" >*/
PIKA_UNDO_GROUP_TEXT, /*< desc="Text" >*/
PIKA_UNDO_GROUP_TRANSFORM, /*< desc="Transform" >*/
PIKA_UNDO_GROUP_PAINT, /*< desc="Paint" >*/
PIKA_UNDO_GROUP_PARASITE_ATTACH, /*< desc="Attach parasite" >*/
PIKA_UNDO_GROUP_PARASITE_REMOVE, /*< desc="Remove parasite" >*/
PIKA_UNDO_GROUP_VECTORS_IMPORT, /*< desc="Import paths" >*/
PIKA_UNDO_GROUP_MISC, /*< desc="Plug-In" >*/
PIKA_UNDO_GROUP_LAST = PIKA_UNDO_GROUP_MISC, /*< skip >*/
/* Undo types which actually do something */
PIKA_UNDO_IMAGE_TYPE, /*< desc="Image type" >*/
PIKA_UNDO_IMAGE_PRECISION, /*< desc="Image precision" >*/
PIKA_UNDO_IMAGE_SIZE, /*< desc="Image size" >*/
PIKA_UNDO_IMAGE_RESOLUTION, /*< desc="Image resolution change" >*/
PIKA_UNDO_IMAGE_GRID, /*< desc="Grid" >*/
PIKA_UNDO_IMAGE_METADATA, /*< desc="Change metadata" >*/
PIKA_UNDO_IMAGE_COLORMAP, /*< desc="Change indexed palette" >*/
PIKA_UNDO_IMAGE_HIDDEN_PROFILE, /*< desc="Hide/Unhide color profile" >*/
PIKA_UNDO_GUIDE, /*< desc="Guide" >*/
PIKA_UNDO_SAMPLE_POINT, /*< desc="Sample Point" >*/
PIKA_UNDO_DRAWABLE, /*< desc="Layer/Channel" >*/
PIKA_UNDO_DRAWABLE_MOD, /*< desc="Layer/Channel modification" >*/
PIKA_UNDO_DRAWABLE_FORMAT, /*< desc="Layer/Channel format" >*/
PIKA_UNDO_MASK, /*< desc="Selection mask" >*/
PIKA_UNDO_ITEM_REORDER, /*< desc="Reorder item" >*/
PIKA_UNDO_ITEM_RENAME, /*< desc="Rename item" >*/
PIKA_UNDO_ITEM_DISPLACE, /*< desc="Move item" >*/
PIKA_UNDO_ITEM_VISIBILITY, /*< desc="Item visibility" >*/
PIKA_UNDO_ITEM_COLOR_TAG, /*< desc="Item color tag" >*/
PIKA_UNDO_ITEM_LOCK_CONTENT, /*< desc="Lock/Unlock content" >*/
PIKA_UNDO_ITEM_LOCK_POSITION, /*< desc="Lock/Unlock position" >*/
PIKA_UNDO_ITEM_LOCK_VISIBILITY, /*< desc="Lock/Unlock visibility" >*/
PIKA_UNDO_LAYER_ADD, /*< desc="New layer" >*/
PIKA_UNDO_LAYER_REMOVE, /*< desc="Delete layer" >*/
PIKA_UNDO_LAYER_MODE, /*< desc="Set layer mode" >*/
PIKA_UNDO_LAYER_OPACITY, /*< desc="Set layer opacity" >*/
PIKA_UNDO_LAYER_LOCK_ALPHA, /*< desc="Lock/Unlock alpha channel" >*/
PIKA_UNDO_GROUP_LAYER_SUSPEND_RESIZE, /*< desc="Suspend group layer resize" >*/
PIKA_UNDO_GROUP_LAYER_RESUME_RESIZE, /*< desc="Resume group layer resize" >*/
PIKA_UNDO_GROUP_LAYER_SUSPEND_MASK, /*< desc="Suspend group layer mask" >*/
PIKA_UNDO_GROUP_LAYER_RESUME_MASK, /*< desc="Resume group layer mask" >*/
PIKA_UNDO_GROUP_LAYER_START_TRANSFORM, /*< desc="Start transforming group layer" >*/
PIKA_UNDO_GROUP_LAYER_END_TRANSFORM, /*< desc="End transforming group layer" >*/
PIKA_UNDO_GROUP_LAYER_CONVERT, /*< desc="Convert group layer" >*/
PIKA_UNDO_TEXT_LAYER, /*< desc="Text layer" >*/
PIKA_UNDO_TEXT_LAYER_MODIFIED, /*< desc="Text layer modification" >*/
PIKA_UNDO_TEXT_LAYER_CONVERT, /*< desc="Convert text layer" >*/
PIKA_UNDO_LAYER_MASK_ADD, /*< desc="Add layer mask" >*/
PIKA_UNDO_LAYER_MASK_REMOVE, /*< desc="Delete layer mask" >*/
PIKA_UNDO_LAYER_MASK_APPLY, /*< desc="Apply layer mask" >*/
PIKA_UNDO_LAYER_MASK_SHOW, /*< desc="Show layer mask" >*/
PIKA_UNDO_CHANNEL_ADD, /*< desc="New channel" >*/
PIKA_UNDO_CHANNEL_REMOVE, /*< desc="Delete channel" >*/
PIKA_UNDO_CHANNEL_COLOR, /*< desc="Channel color" >*/
PIKA_UNDO_VECTORS_ADD, /*< desc="New path" >*/
PIKA_UNDO_VECTORS_REMOVE, /*< desc="Delete path" >*/
PIKA_UNDO_VECTORS_MOD, /*< desc="Path modification" >*/
PIKA_UNDO_FS_TO_LAYER, /*< desc="Floating selection to layer" >*/
PIKA_UNDO_TRANSFORM_GRID, /*< desc="Transform grid" >*/
PIKA_UNDO_PAINT, /*< desc="Paint" >*/
PIKA_UNDO_INK, /*< desc="Ink" >*/
PIKA_UNDO_FOREGROUND_SELECT, /*< desc="Select foreground" >*/
PIKA_UNDO_PARASITE_ATTACH, /*< desc="Attach parasite" >*/
PIKA_UNDO_PARASITE_REMOVE, /*< desc="Remove parasite" >*/
PIKA_UNDO_CANT /*< desc="Not undoable" >*/
} PikaUndoType;
#define PIKA_TYPE_VIEW_SIZE (pika_view_size_get_type ())
GType pika_view_size_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_VIEW_SIZE_TINY = 12, /*< desc="Tiny" >*/
PIKA_VIEW_SIZE_EXTRA_SMALL = 16, /*< desc="Very small" >*/
PIKA_VIEW_SIZE_SMALL = 24, /*< desc="Small" >*/
PIKA_VIEW_SIZE_MEDIUM = 32, /*< desc="Medium" >*/
PIKA_VIEW_SIZE_LARGE = 48, /*< desc="Large" >*/
PIKA_VIEW_SIZE_EXTRA_LARGE = 64, /*< desc="Very large" >*/
PIKA_VIEW_SIZE_HUGE = 96, /*< desc="Huge" >*/
PIKA_VIEW_SIZE_ENORMOUS = 128, /*< desc="Enormous" >*/
PIKA_VIEW_SIZE_GIGANTIC = 192 /*< desc="Gigantic" >*/
} PikaViewSize;
#define PIKA_TYPE_VIEW_TYPE (pika_view_type_get_type ())
GType pika_view_type_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_VIEW_TYPE_LIST, /*< desc="View as list" >*/
PIKA_VIEW_TYPE_GRID /*< desc="View as grid" >*/
} PikaViewType;
#define PIKA_TYPE_SELECT_METHOD (pika_select_method_get_type ())
GType pika_select_method_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
PIKA_SELECT_PLAIN_TEXT, /*< desc="Selection by basic text search" >*/
PIKA_SELECT_REGEX_PATTERN, /*< desc="Selection by regular expression search" >*/
PIKA_SELECT_GLOB_PATTERN, /*< desc="Selection by glob pattern search" >*/
} PikaSelectMethod;
/*
* non-registered enums; register them if needed
*/
typedef enum /*< pdb-skip, skip >*/
{
PIKA_CONTEXT_PROP_FIRST = 2,
PIKA_CONTEXT_PROP_IMAGE = PIKA_CONTEXT_PROP_FIRST,
PIKA_CONTEXT_PROP_DISPLAY = 3,
PIKA_CONTEXT_PROP_TOOL = 4,
PIKA_CONTEXT_PROP_PAINT_INFO = 5,
PIKA_CONTEXT_PROP_FOREGROUND = 6,
PIKA_CONTEXT_PROP_BACKGROUND = 7,
PIKA_CONTEXT_PROP_OPACITY = 8,
PIKA_CONTEXT_PROP_PAINT_MODE = 9,
PIKA_CONTEXT_PROP_BRUSH = 10,
PIKA_CONTEXT_PROP_DYNAMICS = 11,
PIKA_CONTEXT_PROP_MYBRUSH = 12,
PIKA_CONTEXT_PROP_PATTERN = 13,
PIKA_CONTEXT_PROP_GRADIENT = 14,
PIKA_CONTEXT_PROP_PALETTE = 15,
PIKA_CONTEXT_PROP_FONT = 16,
PIKA_CONTEXT_PROP_TOOL_PRESET = 17,
PIKA_CONTEXT_PROP_BUFFER = 18,
PIKA_CONTEXT_PROP_IMAGEFILE = 19,
PIKA_CONTEXT_PROP_TEMPLATE = 20,
PIKA_CONTEXT_PROP_LAST = PIKA_CONTEXT_PROP_TEMPLATE
} PikaContextPropType;
typedef enum /*< pdb-skip, skip >*/
{
PIKA_CONTEXT_PROP_MASK_IMAGE = 1 << 2,
PIKA_CONTEXT_PROP_MASK_DISPLAY = 1 << 3,
PIKA_CONTEXT_PROP_MASK_TOOL = 1 << 4,
PIKA_CONTEXT_PROP_MASK_PAINT_INFO = 1 << 5,
PIKA_CONTEXT_PROP_MASK_FOREGROUND = 1 << 6,
PIKA_CONTEXT_PROP_MASK_BACKGROUND = 1 << 7,
PIKA_CONTEXT_PROP_MASK_OPACITY = 1 << 8,
PIKA_CONTEXT_PROP_MASK_PAINT_MODE = 1 << 9,
PIKA_CONTEXT_PROP_MASK_BRUSH = 1 << 10,
PIKA_CONTEXT_PROP_MASK_DYNAMICS = 1 << 11,
PIKA_CONTEXT_PROP_MASK_MYBRUSH = 1 << 12,
PIKA_CONTEXT_PROP_MASK_PATTERN = 1 << 13,
PIKA_CONTEXT_PROP_MASK_GRADIENT = 1 << 14,
PIKA_CONTEXT_PROP_MASK_PALETTE = 1 << 15,
PIKA_CONTEXT_PROP_MASK_FONT = 1 << 16,
PIKA_CONTEXT_PROP_MASK_TOOL_PRESET = 1 << 17,
PIKA_CONTEXT_PROP_MASK_BUFFER = 1 << 18,
PIKA_CONTEXT_PROP_MASK_IMAGEFILE = 1 << 19,
PIKA_CONTEXT_PROP_MASK_TEMPLATE = 1 << 20,
/* aliases */
PIKA_CONTEXT_PROP_MASK_PAINT = (PIKA_CONTEXT_PROP_MASK_FOREGROUND |
PIKA_CONTEXT_PROP_MASK_BACKGROUND |
PIKA_CONTEXT_PROP_MASK_OPACITY |
PIKA_CONTEXT_PROP_MASK_PAINT_MODE |
PIKA_CONTEXT_PROP_MASK_BRUSH |
PIKA_CONTEXT_PROP_MASK_DYNAMICS |
PIKA_CONTEXT_PROP_MASK_PATTERN |
PIKA_CONTEXT_PROP_MASK_GRADIENT),
PIKA_CONTEXT_PROP_MASK_ALL = (PIKA_CONTEXT_PROP_MASK_IMAGE |
PIKA_CONTEXT_PROP_MASK_DISPLAY |
PIKA_CONTEXT_PROP_MASK_TOOL |
PIKA_CONTEXT_PROP_MASK_PAINT_INFO |
PIKA_CONTEXT_PROP_MASK_MYBRUSH |
PIKA_CONTEXT_PROP_MASK_PALETTE |
PIKA_CONTEXT_PROP_MASK_FONT |
PIKA_CONTEXT_PROP_MASK_TOOL_PRESET |
PIKA_CONTEXT_PROP_MASK_BUFFER |
PIKA_CONTEXT_PROP_MASK_IMAGEFILE |
PIKA_CONTEXT_PROP_MASK_TEMPLATE |
PIKA_CONTEXT_PROP_MASK_PAINT)
} PikaContextPropMask;
typedef enum /*< pdb-skip, skip >*/
{
PIKA_IMAGE_SCALE_OK,
PIKA_IMAGE_SCALE_TOO_SMALL,
PIKA_IMAGE_SCALE_TOO_BIG
} PikaImageScaleCheckType;
typedef enum /*< pdb-skip, skip >*/
{
PIKA_ITEM_TYPE_LAYERS = 1 << 0,
PIKA_ITEM_TYPE_CHANNELS = 1 << 1,
PIKA_ITEM_TYPE_VECTORS = 1 << 2,
PIKA_ITEM_TYPE_ALL = (PIKA_ITEM_TYPE_LAYERS |
PIKA_ITEM_TYPE_CHANNELS |
PIKA_ITEM_TYPE_VECTORS)
} PikaItemTypeMask;
#endif /* __CORE_ENUMS_H__ */

327
app/core/core-types.h Normal file
View File

@ -0,0 +1,327 @@
/* 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/>.
*/
#ifndef __CORE_TYPES_H__
#define __CORE_TYPES_H__
#include "libpikabase/pikabasetypes.h"
#include "libpikamath/pikamathtypes.h"
#include "libpikacolor/pikacolortypes.h"
#include "libpikamodule/pikamoduletypes.h"
#include "libpikathumb/pikathumb-types.h"
#include "config/config-types.h"
#include "core/core-enums.h"
/* former base/ defines */
#define MAX_CHANNELS 4
#define RED 0
#define GREEN 1
#define BLUE 2
#define ALPHA 3
#define GRAY 0
#define ALPHA_G 1
#define INDEXED 0
#define ALPHA_I 1
/* defines */
#define PIKA_COORDS_MIN_PRESSURE 0.0
#define PIKA_COORDS_MAX_PRESSURE 1.0
#define PIKA_COORDS_DEFAULT_PRESSURE 1.0
#define PIKA_COORDS_MIN_TILT -1.0
#define PIKA_COORDS_MAX_TILT 1.0
#define PIKA_COORDS_DEFAULT_TILT 0.0
#define PIKA_COORDS_MIN_WHEEL 0.0
#define PIKA_COORDS_MAX_WHEEL 1.0
#define PIKA_COORDS_DEFAULT_WHEEL 0.5
#define PIKA_COORDS_DEFAULT_DISTANCE 0.0
#define PIKA_COORDS_DEFAULT_ROTATION 0.0
#define PIKA_COORDS_DEFAULT_SLIDER 0.0
#define PIKA_COORDS_DEFAULT_VELOCITY 0.0
#define PIKA_COORDS_DEFAULT_DIRECTION 0.0
#define PIKA_COORDS_DEFAULT_XSCALE 1.0
#define PIKA_COORDS_DEFAULT_YSCALE 1.0
#define PIKA_COORDS_DEFAULT_VALUES { 0.0, 0.0, \
PIKA_COORDS_DEFAULT_PRESSURE, \
PIKA_COORDS_DEFAULT_TILT, \
PIKA_COORDS_DEFAULT_TILT, \
PIKA_COORDS_DEFAULT_WHEEL, \
PIKA_COORDS_DEFAULT_DISTANCE, \
PIKA_COORDS_DEFAULT_ROTATION, \
PIKA_COORDS_DEFAULT_SLIDER, \
PIKA_COORDS_DEFAULT_VELOCITY, \
PIKA_COORDS_DEFAULT_DIRECTION,\
PIKA_COORDS_DEFAULT_XSCALE, \
PIKA_COORDS_DEFAULT_YSCALE }
/* base classes */
typedef struct _PikaObject PikaObject;
typedef struct _PikaViewable PikaViewable;
typedef struct _PikaFilter PikaFilter;
typedef struct _PikaItem PikaItem;
typedef struct _PikaAuxItem PikaAuxItem;
typedef struct _Pika Pika;
typedef struct _PikaImage PikaImage;
typedef struct _PikaDisplay PikaDisplay;
/* containers */
typedef struct _PikaContainer PikaContainer;
typedef struct _PikaList PikaList;
typedef struct _PikaDocumentList PikaDocumentList;
typedef struct _PikaDrawableStack PikaDrawableStack;
typedef struct _PikaFilteredContainer PikaFilteredContainer;
typedef struct _PikaFilterStack PikaFilterStack;
typedef struct _PikaItemList PikaItemList;
typedef struct _PikaItemStack PikaItemStack;
typedef struct _PikaLayerStack PikaLayerStack;
typedef struct _PikaTaggedContainer PikaTaggedContainer;
typedef struct _PikaTreeProxy PikaTreeProxy;
/* not really a container */
typedef struct _PikaItemTree PikaItemTree;
/* context objects */
typedef struct _PikaContext PikaContext;
typedef struct _PikaFillOptions PikaFillOptions;
typedef struct _PikaStrokeOptions PikaStrokeOptions;
typedef struct _PikaToolOptions PikaToolOptions;
/* info objects */
typedef struct _PikaPaintInfo PikaPaintInfo;
typedef struct _PikaToolGroup PikaToolGroup;
typedef struct _PikaToolInfo PikaToolInfo;
typedef struct _PikaToolItem PikaToolItem;
/* data objects */
typedef struct _PikaDataFactory PikaDataFactory;
typedef struct _PikaDataLoaderFactory PikaDataLoaderFactory;
typedef struct _PikaResource PikaResource;
typedef struct _PikaData PikaData;
typedef struct _PikaBrush PikaBrush;
typedef struct _PikaBrushCache PikaBrushCache;
typedef struct _PikaBrushClipboard PikaBrushClipboard;
typedef struct _PikaBrushGenerated PikaBrushGenerated;
typedef struct _PikaBrushPipe PikaBrushPipe;
typedef struct _PikaCurve PikaCurve;
typedef struct _PikaDynamics PikaDynamics;
typedef struct _PikaDynamicsOutput PikaDynamicsOutput;
typedef struct _PikaGradient PikaGradient;
typedef struct _PikaMybrush PikaMybrush;
typedef struct _PikaPalette PikaPalette;
typedef struct _PikaPaletteMru PikaPaletteMru;
typedef struct _PikaPattern PikaPattern;
typedef struct _PikaPatternClipboard PikaPatternClipboard;
typedef struct _PikaToolPreset PikaToolPreset;
typedef struct _PikaTagCache PikaTagCache;
/* drawable objects */
typedef struct _PikaDrawable PikaDrawable;
typedef struct _PikaChannel PikaChannel;
typedef struct _PikaLayerMask PikaLayerMask;
typedef struct _PikaSelection PikaSelection;
typedef struct _PikaLayer PikaLayer;
typedef struct _PikaGroupLayer PikaGroupLayer;
/* auxiliary image items */
typedef struct _PikaGuide PikaGuide;
typedef struct _PikaSamplePoint PikaSamplePoint;
/* undo objects */
typedef struct _PikaUndo PikaUndo;
typedef struct _PikaUndoStack PikaUndoStack;
typedef struct _PikaUndoAccumulator PikaUndoAccumulator;
/* Symmetry transformations */
typedef struct _PikaSymmetry PikaSymmetry;
typedef struct _PikaMirror PikaMirror;
typedef struct _PikaTiling PikaTiling;
typedef struct _PikaMandala PikaMandala;
/* misc objects */
typedef struct _PikaAsync PikaAsync;
typedef struct _PikaAsyncSet PikaAsyncSet;
typedef struct _PikaBuffer PikaBuffer;
typedef struct _PikaDrawableFilter PikaDrawableFilter;
typedef struct _PikaEnvironTable PikaEnvironTable;
typedef struct _PikaExtension PikaExtension;
typedef struct _PikaExtensionManager PikaExtensionManager;
typedef struct _PikaHistogram PikaHistogram;
typedef struct _PikaIdTable PikaIdTable;
typedef struct _PikaImagefile PikaImagefile;
typedef struct _PikaImageProxy PikaImageProxy;
typedef struct _PikaInterpreterDB PikaInterpreterDB;
typedef struct _PikaLineArt PikaLineArt;
typedef struct _PikaObjectQueue PikaObjectQueue;
typedef struct _PikaParasiteList PikaParasiteList;
typedef struct _PikaPdbProgress PikaPdbProgress;
typedef struct _PikaProjection PikaProjection;
typedef struct _PikaSettings PikaSettings;
typedef struct _PikaSubProgress PikaSubProgress;
typedef struct _PikaTag PikaTag;
typedef struct _PikaTreeHandler PikaTreeHandler;
typedef struct _PikaTriviallyCancelableWaitable PikaTriviallyCancelableWaitable;
typedef struct _PikaUncancelableWaitable PikaUncancelableWaitable;
/* interfaces */
typedef struct _PikaCancelable PikaCancelable; /* dummy typedef */
typedef struct _PikaPickable PikaPickable; /* dummy typedef */
typedef struct _PikaProgress PikaProgress; /* dummy typedef */
typedef struct _PikaProjectable PikaProjectable; /* dummy typedef */
typedef struct _PikaTagged PikaTagged; /* dummy typedef */
typedef struct _PikaWaitable PikaWaitable; /* dummy typedef */
/* non-object types */
typedef struct _PikaBacktrace PikaBacktrace;
typedef struct _PikaBoundSeg PikaBoundSeg;
typedef struct _PikaChunkIterator PikaChunkIterator;
typedef struct _PikaCoords PikaCoords;
typedef struct _PikaGradientSegment PikaGradientSegment;
typedef struct _PikaPaletteEntry PikaPaletteEntry;
typedef struct _PikaScanConvert PikaScanConvert;
typedef struct _PikaTempBuf PikaTempBuf;
typedef guint32 PikaTattoo;
/* The following hack is made so that we can reuse the definition
* the cairo definition of cairo_path_t without having to translate
* between our own version of a bezier description and cairos version.
*
* to avoid having to include <cairo.h> in each and every file
* including this file we only use the "real" definition when cairo.h
* already has been included and use something else.
*
* Note that if you really want to work with PikaBezierDesc (except just
* passing pointers to it around) you also need to include <cairo.h>.
*/
#ifdef CAIRO_VERSION
typedef cairo_path_t PikaBezierDesc;
#else
typedef void * PikaBezierDesc;
#endif
/* functions */
typedef void (* PikaInitStatusFunc) (const gchar *text1,
const gchar *text2,
gdouble percentage);
typedef gboolean (* PikaObjectFilterFunc) (PikaObject *object,
gpointer user_data);
typedef gint64 (* PikaMemsizeFunc) (gpointer instance,
gint64 *gui_size);
typedef void (* PikaRunAsyncFunc) (PikaAsync *async,
gpointer user_data);
/* structs */
struct _PikaCoords
{
/* axes as reported by the device */
gdouble x;
gdouble y;
gdouble pressure;
gdouble xtilt;
gdouble ytilt;
gdouble wheel;
gdouble distance;
gdouble rotation;
gdouble slider;
/* synthetic axes */
gdouble velocity;
gdouble direction;
/* view transform */
gdouble xscale; /* the view scale */
gdouble yscale;
gdouble angle; /* the view rotation angle */
gboolean reflect; /* whether the view is reflected */
};
/* temp hack as replacement for GdkSegment */
typedef struct _PikaSegment PikaSegment;
struct _PikaSegment
{
gint x1;
gint y1;
gint x2;
gint y2;
};
#include "gegl/pika-gegl-types.h"
#include "paint/paint-types.h"
#include "text/text-types.h"
#include "vectors/vectors-types.h"
#include "pdb/pdb-types.h"
#include "plug-in/plug-in-types.h"
#endif /* __CORE_TYPES_H__ */

280
app/core/meson.build Normal file
View File

@ -0,0 +1,280 @@
stamp_core_enums = custom_target('stamp-core-enums.h',
input : [
files(
'core-enums.h'
),
],
output: [ 'stamp-core-enums.h', ],
command: [
mkenums_wrap, perl,
meson.project_source_root(), meson.current_source_dir(),
meson.current_build_dir(),
'core-',
'#include <gio/gio.h>\n' +
'#include "libpikabase/pikabase.h"\n',
'#include "pika-intl.h"'
],
build_by_default: true
)
appcoremarshal = gnome.genmarshal('pikamarshal',
prefix: 'pika_marshal',
sources: 'pikamarshal.list',
install_header: false,
)
libappcore_sources = [
'pika-atomic.c',
'pika-batch.c',
'pika-cairo.c',
'pika-contexts.c',
'pika-data-factories.c',
'pika-edit.c',
'pika-filter-history.c',
'pika-gradients.c',
'pika-gui.c',
'pika-internal-data.c',
'pika-memsize.c',
'pika-modules.c',
'pika-palettes.c',
'pika-parallel.cc',
'pika-parasites.c',
'pika-spawn.c',
'pika-tags.c',
'pika-templates.c',
'pika-transform-resize.c',
'pika-transform-3d-utils.c',
'pika-transform-utils.c',
'pika-units.c',
'pika-user-install.c',
'pika-utils.c',
'pika.c',
'pikaasync.c',
'pikaasyncset.c',
'pikaauxitem.c',
'pikaauxitemundo.c',
'pikabacktrace-linux.c',
'pikabacktrace-none.c',
'pikabacktrace-windows.c',
'pikabezierdesc.c',
'pikaboundary.c',
'pikabrush-boundary.c',
'pikabrush-load.c',
'pikabrush-mipmap.cc',
'pikabrush-save.c',
'pikabrush-transform.cc',
'pikabrush.c',
'pikabrushcache.c',
'pikabrushclipboard.c',
'pikabrushgenerated-load.c',
'pikabrushgenerated-save.c',
'pikabrushgenerated.c',
'pikabrushpipe-load.c',
'pikabrushpipe-save.c',
'pikabrushpipe.c',
'pikabuffer.c',
'pikacancelable.c',
'pikachannel-combine.c',
'pikachannel-select.c',
'pikachannel.c',
'pikachannelpropundo.c',
'pikachannelundo.c',
'pikachunkiterator.c',
'pikacontainer-filter.c',
'pikacontainer.c',
'pikacontext.c',
'pikacoords-interpolate.c',
'pikacoords.c',
'pikacurve-load.c',
'pikacurve-map.c',
'pikacurve-save.c',
'pikacurve.c',
'pikadashpattern.c',
'pikadata.c',
'pikadatafactory.c',
'pikadataloaderfactory.c',
'pikadisplay.c',
'pikadocumentlist.c',
'pikadrawable-bucket-fill.c',
'pikadrawable-combine.c',
'pikadrawable-edit.c',
'pikadrawable-equalize.c',
'pikadrawable-fill.c',
'pikadrawable-filters.c',
'pikadrawable-floating-selection.c',
'pikadrawable-foreground-extract.c',
'pikadrawable-gradient.c',
'pikadrawable-histogram.c',
'pikadrawable-levels.c',
'pikadrawable-offset.c',
'pikadrawable-operation.c',
'pikadrawable-preview.c',
'pikadrawable-shadow.c',
'pikadrawable-stroke.c',
'pikadrawable-transform.c',
'pikadrawable.c',
'pikadrawablefilter.c',
'pikadrawablemodundo.c',
'pikadrawablepropundo.c',
'pikadrawablestack.c',
'pikadrawableundo.c',
'pikadynamics-load.c',
'pikadynamics-save.c',
'pikadynamics.c',
'pikadynamicsoutput.c',
'pikaerror.c',
'pikaextension-error.c',
'pikaextension.c',
'pikaextensionmanager.c',
'pikafilloptions.c',
'pikafilter.c',
'pikafilteredcontainer.c',
'pikafilterstack.c',
'pikafloatingselectionundo.c',
'pikagradient-load.c',
'pikagradient-save.c',
'pikagradient.c',
'pikagrid.c',
'pikagrouplayer.c',
'pikagrouplayerundo.c',
'pikaguide.c',
'pikaguideundo.c',
'pikahistogram.c',
'pikaidtable.c',
'pikaimage-arrange.c',
'pikaimage-color-profile.c',
'pikaimage-colormap.c',
'pikaimage-convert-indexed.c',
'pikaimage-convert-precision.c',
'pikaimage-convert-type.c',
'pikaimage-crop.c',
'pikaimage-duplicate.c',
'pikaimage-flip.c',
'pikaimage-grid.c',
'pikaimage-guides.c',
'pikaimage-item-list.c',
'pikaimage-merge.c',
'pikaimage-metadata.c',
'pikaimage-new.c',
'pikaimage-pick-color.c',
'pikaimage-pick-item.c',
'pikaimage-preview.c',
'pikaimage-quick-mask.c',
'pikaimage-resize.c',
'pikaimage-rotate.c',
'pikaimage-sample-points.c',
'pikaimage-scale.c',
'pikaimage-snap.c',
'pikaimage-symmetry.c',
'pikaimage-transform.c',
'pikaimage-undo-push.c',
'pikaimage-undo.c',
'pikaimage.c',
'pikaimagefile.c',
'pikaimageproxy.c',
'pikaimageundo.c',
'pikaitem-exclusive.c',
'pikaitem-preview.c',
'pikaitem.c',
'pikaitemlist.c',
'pikaitempropundo.c',
'pikaitemstack.c',
'pikaitemtree.c',
'pikaitemundo.c',
'pikalayer-floating-selection.c',
'pikalayer-new.c',
'pikalayer.c',
'pikalayermask.c',
'pikalayermaskpropundo.c',
'pikalayermaskundo.c',
'pikalayerpropundo.c',
'pikalayerstack.c',
'pikalayerundo.c',
'pikalineart.c',
'pikalist.c',
'pikamaskundo.c',
'pikamybrush-load.c',
'pikamybrush.c',
'pikaobject.c',
'pikaobjectqueue.c',
'pikapaintinfo.c',
'pikapalette-import.c',
'pikapalette-load.c',
'pikapalette-save.c',
'pikapalette.c',
'pikapalettemru.c',
'pikaparamspecs-desc.c',
'pikaparamspecs.c',
'pikaparasitelist.c',
'pikapattern-load.c',
'pikapattern-save.c',
'pikapattern.c',
'pikapatternclipboard.c',
'pikapdbprogress.c',
'pikapickable-auto-shrink.c',
'pikapickable-contiguous-region.cc',
'pikapickable.c',
'pikaprogress.c',
'pikaprojectable.c',
'pikaprojection.c',
'pikaresource.c',
'pikasamplepoint.c',
'pikasamplepointundo.c',
'pikascanconvert.c',
'pikaselection.c',
'pikasettings.c',
'pikastrokeoptions.c',
'pikasubprogress.c',
'pikasymmetry-mandala.c',
'pikasymmetry-mirror.c',
'pikasymmetry-tiling.c',
'pikasymmetry.c',
'pikatag.c',
'pikatagcache.c',
'pikatagged.c',
'pikataggedcontainer.c',
'pikatempbuf.c',
'pikatemplate.c',
'pikatilehandlerprojectable.c',
'pikatoolgroup.c',
'pikatoolinfo.c',
'pikatoolitem.c',
'pikatooloptions.c',
'pikatoolpreset-load.c',
'pikatoolpreset-save.c',
'pikatoolpreset.c',
'pikatreehandler.c',
'pikatreeproxy.c',
'pikatriviallycancelablewaitable.c',
'pikauncancelablewaitable.c',
'pikaundo.c',
'pikaundostack.c',
'pikaunit.c',
'pikaviewable.c',
'pikawaitable.c',
'core-enums.c',
stamp_core_enums,
appcoremarshal,
cursors_sources,
pickers_sources,
icons_core_sources,
]
libappcore = static_library('appcore',
libappcore_sources,
include_directories: [ rootInclude, rootAppInclude, ],
c_args: '-DG_LOG_DOMAIN="Pika-Core"',
dependencies: [
cairo,
gegl,
gdk_pixbuf,
libmypaint,
gexiv2,
appstream_glib,
math,
dl,
libunwind,
],
)

99
app/core/pika-atomic.c Normal file
View File

@ -0,0 +1,99 @@
/* 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
*
* pika-atomic.c
* Copyright (C) 2018 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 <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include "core-types.h"
#include "pika-atomic.h"
/* GSList */
static GSList pika_atomic_slist_sentinel;
void
pika_atomic_slist_push_head (GSList **list,
gpointer data)
{
GSList *old_head;
GSList *new_head;
g_return_if_fail (list != NULL);
new_head = g_slist_alloc ();
new_head->data = data;
do
{
do
{
old_head = g_atomic_pointer_get (list);
}
while (old_head == &pika_atomic_slist_sentinel);
new_head->next = old_head;
}
while (! g_atomic_pointer_compare_and_exchange (list, old_head, new_head));
}
gpointer
pika_atomic_slist_pop_head (GSList **list)
{
GSList *old_head;
GSList *new_head;
gpointer data;
g_return_val_if_fail (list != NULL, NULL);
do
{
do
{
old_head = g_atomic_pointer_get (list);
}
while (old_head == &pika_atomic_slist_sentinel);
if (! old_head)
return NULL;
}
while (! g_atomic_pointer_compare_and_exchange (list,
old_head,
&pika_atomic_slist_sentinel));
new_head = old_head->next;
data = old_head->data;
g_atomic_pointer_set (list, new_head);
g_slist_free1 (old_head);
return data;
}

36
app/core/pika-atomic.h Normal file
View File

@ -0,0 +1,36 @@
/* 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
*
* pika-atomic.h
* Copyright (C) 2018 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 __PIKA_ATOMIC_H__
#define __PIKA_ATOMIC_H__
/* GSList */
void pika_atomic_slist_push_head (GSList **list,
gpointer data);
gpointer pika_atomic_slist_pop_head (GSList **list);
#endif /* __PIKA_ATOMIC_H__ */

311
app/core/pika-batch.c Normal file
View File

@ -0,0 +1,311 @@
/* 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 <stdlib.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include "libpikabase/pikabase.h"
#include "core-types.h"
#include "pika.h"
#include "pika-batch.h"
#include "pikaparamspecs.h"
#include "pdb/pikapdb.h"
#include "pdb/pikaprocedure.h"
#include "plug-in/pikapluginmanager.h"
#include "plug-in/pikapluginprocedure.h"
#include "pika-intl.h"
static void pika_batch_exit_after_callback (Pika *pika) G_GNUC_NORETURN;
static gint pika_batch_run_cmd (Pika *pika,
const gchar *proc_name,
PikaProcedure *procedure,
PikaRunMode run_mode,
const gchar *cmd);
gint
pika_batch_run (Pika *pika,
const gchar *batch_interpreter,
const gchar **batch_commands)
{
PikaProcedure *eval_proc;
GSList *batch_procedures;
GSList *iter;
gulong exit_id;
gint retval = EXIT_SUCCESS;
if (! batch_commands || ! batch_commands[0])
return retval;
batch_procedures = pika_plug_in_manager_get_batch_procedures (pika->plug_in_manager);
if (g_slist_length (batch_procedures) == 0)
{
g_message (_("No batch interpreters are available. "
"Batch mode disabled."));
retval = 69; /* EX_UNAVAILABLE - service unavailable (sysexits.h) */
return retval;
}
if (! batch_interpreter)
{
batch_interpreter = g_getenv ("PIKA_BATCH_INTERPRETER");
if (! batch_interpreter)
{
if (g_slist_length (batch_procedures) == 1)
{
batch_interpreter = pika_object_get_name (batch_procedures->data);;
if (pika->be_verbose)
g_printerr (_("No batch interpreter specified, using "
"'%s'.\n"), batch_interpreter);
}
else
{
retval = 64; /* EX_USAGE - command line usage error */
g_print ("%s\n\n%s\n",
_("No batch interpreter specified."),
_("Available interpreters are:"));
for (iter = batch_procedures; iter; iter = iter->next)
{
PikaPlugInProcedure *proc = iter->data;
gchar *locale_name;
locale_name = g_locale_from_utf8 (proc->batch_interpreter_name,
-1, NULL, NULL, NULL);
g_print ("- %s (%s)\n",
pika_object_get_name (iter->data),
locale_name ? locale_name : proc->batch_interpreter_name);
g_free (locale_name);
}
g_print ("\n%s\n",
_("Specify one of these interpreters as --batch-interpreter option."));
return retval;
}
}
}
for (iter = batch_procedures; iter; iter = iter->next)
{
if (g_strcmp0 (pika_object_get_name (iter->data),
batch_interpreter) == 0)
break;
}
if (iter == NULL)
{
retval = 69; /* EX_UNAVAILABLE - service unavailable (sysexits.h) */
g_print (_("The procedure '%s' is not a valid batch interpreter."),
batch_interpreter);
g_print ("\n%s\n\n%s\n",
_("Batch mode disabled."),
_("Available interpreters are:"));
for (iter = batch_procedures; iter; iter = iter->next)
{
PikaPlugInProcedure *proc = iter->data;
gchar *locale_name;
locale_name = g_locale_from_utf8 (proc->batch_interpreter_name,
-1, NULL, NULL, NULL);
g_print ("- %s (%s)\n",
pika_object_get_name (iter->data),
locale_name ? locale_name : proc->batch_interpreter_name);
g_free (locale_name);
}
g_print ("\n%s\n",
_("Specify one of these interpreters as --batch-interpreter option."));
return retval;
}
exit_id = g_signal_connect_after (pika, "exit",
G_CALLBACK (pika_batch_exit_after_callback),
NULL);
eval_proc = pika_pdb_lookup_procedure (pika->pdb, batch_interpreter);
if (eval_proc)
{
gint i;
retval = EXIT_SUCCESS;
for (i = 0; batch_commands[i]; i++)
{
retval = pika_batch_run_cmd (pika, batch_interpreter, eval_proc,
PIKA_RUN_NONINTERACTIVE, batch_commands[i]);
/* In case of several commands, stop and return last
* failed command.
*/
if (retval != EXIT_SUCCESS)
{
g_printerr ("Stopping at failing batch command [%d]: %s\n",
i, batch_commands[i]);
break;
}
}
}
else
{
retval = 69; /* EX_UNAVAILABLE - service unavailable (sysexits.h) */
g_message (_("The batch interpreter '%s' is not available. "
"Batch mode disabled."), batch_interpreter);
}
g_signal_handler_disconnect (pika, exit_id);
return retval;
}
/*
* The purpose of this handler is to exit PIKA cleanly when the batch
* procedure calls the pika-exit procedure. Without this callback, the
* message "batch command experienced an execution error" would appear
* and pika would hang forever.
*/
static void
pika_batch_exit_after_callback (Pika *pika)
{
if (pika->be_verbose)
g_print ("EXIT: %s\n", G_STRFUNC);
gegl_exit ();
exit (EXIT_SUCCESS);
}
static inline gboolean
PIKA_IS_PARAM_SPEC_RUN_MODE (GParamSpec *pspec)
{
return (G_IS_PARAM_SPEC_ENUM (pspec) &&
pspec->value_type == PIKA_TYPE_RUN_MODE);
}
static gint
pika_batch_run_cmd (Pika *pika,
const gchar *proc_name,
PikaProcedure *procedure,
PikaRunMode run_mode,
const gchar *cmd)
{
PikaValueArray *args;
PikaValueArray *return_vals;
GError *error = NULL;
gint i = 0;
gint retval = EXIT_SUCCESS;
args = pika_procedure_get_arguments (procedure);
if (procedure->num_args > i &&
PIKA_IS_PARAM_SPEC_RUN_MODE (procedure->args[i]))
{
g_value_set_enum (pika_value_array_index (args, i++), run_mode);
}
if (procedure->num_args > i &&
G_IS_PARAM_SPEC_STRING (procedure->args[i]))
{
g_value_set_static_string (pika_value_array_index (args, i++), cmd);
}
return_vals =
pika_pdb_execute_procedure_by_name_args (pika->pdb,
pika_get_user_context (pika),
NULL, &error,
proc_name, args);
switch (g_value_get_enum (pika_value_array_index (return_vals, 0)))
{
case PIKA_PDB_EXECUTION_ERROR:
/* Using Linux's standard exit code as found in /usr/include/sysexits.h
* Since other platforms may not have the header, I simply
* hardcode the few cases.
*/
retval = 70; /* EX_SOFTWARE - internal software error */
if (error)
{
g_printerr ("batch command experienced an execution error:\n"
"%s\n", error->message);
}
else
{
g_printerr ("batch command experienced an execution error\n");
}
break;
case PIKA_PDB_CALLING_ERROR:
retval = 64; /* EX_USAGE - command line usage error */
if (error)
{
g_printerr ("batch command experienced a calling error:\n"
"%s\n", error->message);
}
else
{
g_printerr ("batch command experienced a calling error\n");
}
break;
case PIKA_PDB_SUCCESS:
retval = EXIT_SUCCESS;
g_printerr ("batch command executed successfully\n");
break;
case PIKA_PDB_CANCEL:
/* Not in sysexits.h, but usually used for 'Script terminated by
* Control-C'. See: https://tldp.org/LDP/abs/html/exitcodes.html
*/
retval = 130;
break;
case PIKA_PDB_PASS_THROUGH:
retval = EXIT_FAILURE; /* Catchall. */
break;
}
pika_value_array_unref (return_vals);
pika_value_array_unref (args);
if (error)
g_error_free (error);
return retval;
}

31
app/core/pika-batch.h Normal file
View File

@ -0,0 +1,31 @@
/* 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/>.
*/
#ifndef __PIKA_BATCH_H__
#define __PIKA_BATCH_H__
gint pika_batch_run (Pika *pika,
const gchar *batch_interpreter,
const gchar **batch_commands);
#endif /* __PIKA_BATCH_H__ */

221
app/core/pika-cairo.c Normal file
View File

@ -0,0 +1,221 @@
/* 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
*
* pika-cairo.c
* Copyright (C) 2010-2012 Michael Natterer <mitch@gimp.org>
*
* Some code here is based on code from librsvg that was originally
* written by Raph Levien <raph@artofcode.com> for Gill.
*
* 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 <cairo.h>
#include <gegl.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "libpikamath/pikamath.h"
#include "libpikacolor/pikacolor.h"
#include "core-types.h"
#include "pika-cairo.h"
#define REV (2.0 * G_PI)
static cairo_user_data_key_t surface_data_key = { 0, };
cairo_pattern_t *
pika_cairo_pattern_create_stipple (const PikaRGB *fg,
const PikaRGB *bg,
gint index,
gdouble offset_x,
gdouble offset_y)
{
cairo_surface_t *surface;
cairo_pattern_t *pattern;
guchar *data;
guchar *d;
guchar fg_r, fg_g, fg_b, fg_a;
guchar bg_r, bg_g, bg_b, bg_a;
gint x, y;
g_return_val_if_fail (fg != NULL, NULL);
g_return_val_if_fail (bg != NULL, NULL);
data = g_malloc (8 * 8 * 4);
pika_rgba_get_uchar (fg, &fg_r, &fg_g, &fg_b, &fg_a);
pika_rgba_get_uchar (bg, &bg_r, &bg_g, &bg_b, &bg_a);
d = data;
for (y = 0; y < 8; y++)
{
for (x = 0; x < 8; x++)
{
if ((x + y + index) % 8 >= 4)
PIKA_CAIRO_ARGB32_SET_PIXEL (d, fg_r, fg_g, fg_b, fg_a);
else
PIKA_CAIRO_ARGB32_SET_PIXEL (d, bg_r, bg_g, bg_b, bg_a);
d += 4;
}
}
surface = cairo_image_surface_create_for_data (data,
CAIRO_FORMAT_ARGB32,
8, 8, 8 * 4);
cairo_surface_set_user_data (surface, &surface_data_key,
data, (cairo_destroy_func_t) g_free);
pattern = cairo_pattern_create_for_surface (surface);
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
cairo_surface_destroy (surface);
if (offset_x != 0.0 || offset_y != 0.0)
{
cairo_matrix_t matrix;
cairo_matrix_init_translate (&matrix,
fmod (offset_x, 8),
fmod (offset_y, 8));
cairo_pattern_set_matrix (pattern, &matrix);
}
return pattern;
}
void
pika_cairo_arc (cairo_t *cr,
gdouble center_x,
gdouble center_y,
gdouble radius,
gdouble start_angle,
gdouble slice_angle)
{
g_return_if_fail (cr != NULL);
if (slice_angle >= 0)
{
cairo_arc_negative (cr, center_x, center_y, radius,
- start_angle,
- start_angle - slice_angle);
}
else
{
cairo_arc (cr, center_x, center_y, radius,
- start_angle,
- start_angle - slice_angle);
}
}
void
pika_cairo_rounded_rectangle (cairo_t *cr,
gdouble x,
gdouble y,
gdouble width,
gdouble height,
gdouble corner_radius)
{
g_return_if_fail (cr != NULL);
if (width < 0.0)
{
x += width;
width = -width;
}
if (height < 0.0)
{
y += height;
height = -height;
}
corner_radius = CLAMP (corner_radius, 0.0, MIN (width, height) / 2.0);
if (corner_radius == 0.0)
{
cairo_rectangle (cr, x, y, width, height);
return;
}
cairo_new_sub_path (cr);
cairo_arc (cr,
x + corner_radius, y + corner_radius,
corner_radius,
0.50 * REV, 0.75 * REV);
cairo_line_to (cr,
x + width - corner_radius, y);
cairo_arc (cr,
x + width - corner_radius, y + corner_radius,
corner_radius,
0.75 * REV, 1.00 * REV);
cairo_line_to (cr,
x + width, y + height - corner_radius);
cairo_arc (cr,
x + width - corner_radius, y + height - corner_radius,
corner_radius,
0.00 * REV, 0.25 * REV);
cairo_line_to (cr,
x + corner_radius, y + height);
cairo_arc (cr,
x + corner_radius, y + height - corner_radius,
corner_radius,
0.25 * REV, 0.50 * REV);
cairo_line_to (cr,
x, y + corner_radius);
cairo_close_path (cr);
}
void
pika_cairo_segments (cairo_t *cr,
PikaSegment *segs,
gint n_segs)
{
gint i;
g_return_if_fail (cr != NULL);
g_return_if_fail (segs != NULL && n_segs > 0);
for (i = 0; i < n_segs; i++)
{
if (segs[i].x1 == segs[i].x2)
{
cairo_move_to (cr, segs[i].x1 + 0.5, segs[i].y1 + 0.5);
cairo_line_to (cr, segs[i].x2 + 0.5, segs[i].y2 - 0.5);
}
else
{
cairo_move_to (cr, segs[i].x1 + 0.5, segs[i].y1 + 0.5);
cairo_line_to (cr, segs[i].x2 - 0.5, segs[i].y2 + 0.5);
}
}
}

55
app/core/pika-cairo.h Normal file
View File

@ -0,0 +1,55 @@
/* 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
*
* pika-cairo.h
* Copyright (C) 2010-2012 Michael Natterer <mitch@gimp.org>
*
* Some code here is based on code from librsvg that was originally
* written by Raph Levien <raph@artofcode.com> for Gill.
*
* 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 __APP_PIKA_CAIRO_H__
#define __APP_PIKA_CAIRO_H__
cairo_pattern_t * pika_cairo_pattern_create_stipple (const PikaRGB *fg,
const PikaRGB *bg,
gint index,
gdouble offset_x,
gdouble offset_y);
void pika_cairo_arc (cairo_t *cr,
gdouble center_x,
gdouble center_y,
gdouble radius,
gdouble start_angle,
gdouble slice_angle);
void pika_cairo_rounded_rectangle (cairo_t *cr,
gdouble x,
gdouble y,
gdouble width,
gdouble height,
gdouble corner_radius);
void pika_cairo_segments (cairo_t *cr,
PikaSegment *segs,
gint n_segs);
#endif /* __APP_PIKA_CAIRO_H__ */

165
app/core/pika-contexts.c Normal file
View File

@ -0,0 +1,165 @@
/* 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-1999 Spencer Kimball and Peter Mattis
*
* pika-contexts.c
*
* 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 <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include "libpikabase/pikabase.h"
#include "libpikaconfig/pikaconfig.h"
#include "core-types.h"
#include "pika.h"
#include "pikaerror.h"
#include "pika-contexts.h"
#include "pikacontext.h"
#include "config/pikaconfig-file.h"
#include "pika-intl.h"
void
pika_contexts_init (Pika *pika)
{
PikaContext *context;
g_return_if_fail (PIKA_IS_PIKA (pika));
/* the default context contains the user's saved preferences
*
* TODO: load from disk
*/
context = pika_context_new (pika, "Default", NULL);
pika_set_default_context (pika, context);
g_object_unref (context);
/* the initial user_context is a straight copy of the default context
*/
context = pika_context_new (pika, "User", context);
pika_set_user_context (pika, context);
g_object_unref (context);
}
void
pika_contexts_exit (Pika *pika)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
pika_set_user_context (pika, NULL);
pika_set_default_context (pika, NULL);
}
gboolean
pika_contexts_load (Pika *pika,
GError **error)
{
GFile *file;
GError *my_error = NULL;
gboolean success;
g_return_val_if_fail (PIKA_IS_PIKA (pika), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
file = pika_directory_file ("contextrc", NULL);
if (pika->be_verbose)
g_print ("Parsing '%s'\n", pika_file_get_utf8_name (file));
success = pika_config_deserialize_file (PIKA_CONFIG (pika_get_user_context (pika)),
file,
NULL, &my_error);
g_object_unref (file);
if (! success)
{
if (my_error->code == PIKA_CONFIG_ERROR_OPEN_ENOENT)
{
g_clear_error (&my_error);
success = TRUE;
}
else
{
g_propagate_error (error, my_error);
}
}
return success;
}
gboolean
pika_contexts_save (Pika *pika,
GError **error)
{
GFile *file;
gboolean success;
g_return_val_if_fail (PIKA_IS_PIKA (pika), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
file = pika_directory_file ("contextrc", NULL);
if (pika->be_verbose)
g_print ("Writing '%s'\n", pika_file_get_utf8_name (file));
success = pika_config_serialize_to_file (PIKA_CONFIG (pika_get_user_context (pika)),
file,
"PIKA user context",
"end of user context",
NULL, error);
g_object_unref (file);
return success;
}
gboolean
pika_contexts_clear (Pika *pika,
GError **error)
{
GFile *file;
GError *my_error = NULL;
gboolean success = TRUE;
g_return_val_if_fail (PIKA_IS_PIKA (pika), FALSE);
file = pika_directory_file ("contextrc", NULL);
if (! g_file_delete (file, NULL, &my_error) &&
my_error->code != G_IO_ERROR_NOT_FOUND)
{
success = FALSE;
g_set_error (error, PIKA_ERROR, PIKA_FAILED,
_("Deleting \"%s\" failed: %s"),
pika_file_get_utf8_name (file), my_error->message);
}
g_clear_error (&my_error);
g_object_unref (file);
return success;
}

40
app/core/pika-contexts.h Normal file
View File

@ -0,0 +1,40 @@
/* 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
*
* pika-contexts.h
*
* 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 __PIKA_CONTEXTS_H__
#define __PIKA_CONTEXTS_H__
void pika_contexts_init (Pika *pika);
void pika_contexts_exit (Pika *pika);
gboolean pika_contexts_load (Pika *pika,
GError **error);
gboolean pika_contexts_save (Pika *pika,
GError **error);
gboolean pika_contexts_clear (Pika *pika,
GError **error);
#endif /* __PIKA_CONTEXTS_H__ */

View File

@ -0,0 +1,444 @@
/* 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-2002 Spencer Kimball, Peter Mattis, and others
*
* 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 <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include "libpikabase/pikabase.h"
#include "libpikaconfig/pikaconfig.h"
#include "core-types.h"
#include "config/pikarc.h"
#include "pika.h"
#include "pika-data-factories.h"
#include "pika-gradients.h"
#include "pika-memsize.h"
#include "pika-palettes.h"
#include "pikacontainer.h"
#include "pikabrush-load.h"
#include "pikabrush.h"
#include "pikabrushclipboard.h"
#include "pikabrushgenerated-load.h"
#include "pikabrushpipe-load.h"
#include "pikadataloaderfactory.h"
#include "pikadynamics.h"
#include "pikadynamics-load.h"
#include "pikagradient-load.h"
#include "pikagradient.h"
#include "pikamybrush-load.h"
#include "pikamybrush.h"
#include "pikapalette-load.h"
#include "pikapalette.h"
#include "pikapattern-load.h"
#include "pikapattern.h"
#include "pikapatternclipboard.h"
#include "pikatagcache.h"
#include "pikatoolpreset.h"
#include "pikatoolpreset-load.h"
#include "text/pikafontfactory.h"
#include "pika-intl.h"
void
pika_data_factories_init (Pika *pika)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
pika->brush_factory =
pika_data_loader_factory_new (pika,
PIKA_TYPE_BRUSH,
"brush-path",
"brush-path-writable",
"brush-paths",
pika_brush_new,
pika_brush_get_standard);
pika_object_set_static_name (PIKA_OBJECT (pika->brush_factory),
"brush factory");
pika_data_loader_factory_add_loader (pika->brush_factory,
"PIKA Brush",
pika_brush_load,
PIKA_BRUSH_FILE_EXTENSION,
TRUE);
pika_data_loader_factory_add_loader (pika->brush_factory,
"PIKA Brush Pixmap",
pika_brush_load,
PIKA_BRUSH_PIXMAP_FILE_EXTENSION,
FALSE);
pika_data_loader_factory_add_loader (pika->brush_factory,
"Photoshop ABR Brush",
pika_brush_load_abr,
PIKA_BRUSH_PS_FILE_EXTENSION,
FALSE);
pika_data_loader_factory_add_loader (pika->brush_factory,
"Paint Shop Pro JBR Brush",
pika_brush_load_abr,
PIKA_BRUSH_PSP_FILE_EXTENSION,
FALSE);
pika_data_loader_factory_add_loader (pika->brush_factory,
"PIKA Generated Brush",
pika_brush_generated_load,
PIKA_BRUSH_GENERATED_FILE_EXTENSION,
TRUE);
pika_data_loader_factory_add_loader (pika->brush_factory,
"PIKA Brush Pipe",
pika_brush_pipe_load,
PIKA_BRUSH_PIPE_FILE_EXTENSION,
TRUE);
pika->dynamics_factory =
pika_data_loader_factory_new (pika,
PIKA_TYPE_DYNAMICS,
"dynamics-path",
"dynamics-path-writable",
"dynamics-paths",
pika_dynamics_new,
pika_dynamics_get_standard);
pika_object_set_static_name (PIKA_OBJECT (pika->dynamics_factory),
"dynamics factory");
pika_data_loader_factory_add_loader (pika->dynamics_factory,
"PIKA Paint Dynamics",
pika_dynamics_load,
PIKA_DYNAMICS_FILE_EXTENSION,
TRUE);
pika->mybrush_factory =
pika_data_loader_factory_new (pika,
PIKA_TYPE_MYBRUSH,
"mypaint-brush-path",
"mypaint-brush-path-writable",
"mypaint-brush-paths",
NULL,
NULL);
pika_object_set_static_name (PIKA_OBJECT (pika->mybrush_factory),
"mypaint brush factory");
pika_data_loader_factory_add_loader (pika->mybrush_factory,
"MyPaint Brush",
pika_mybrush_load,
PIKA_MYBRUSH_FILE_EXTENSION,
FALSE);
pika->pattern_factory =
pika_data_loader_factory_new (pika,
PIKA_TYPE_PATTERN,
"pattern-path",
"pattern-path-writable",
"pattern-paths",
NULL,
pika_pattern_get_standard);
pika_object_set_static_name (PIKA_OBJECT (pika->pattern_factory),
"pattern factory");
pika_data_loader_factory_add_loader (pika->pattern_factory,
"PIKA Pattern",
pika_pattern_load,
PIKA_PATTERN_FILE_EXTENSION,
TRUE);
pika_data_loader_factory_add_fallback (pika->pattern_factory,
"Pattern from GdkPixbuf",
pika_pattern_load_pixbuf);
pika->gradient_factory =
pika_data_loader_factory_new (pika,
PIKA_TYPE_GRADIENT,
"gradient-path",
"gradient-path-writable",
"gradient-paths",
pika_gradient_new,
pika_gradient_get_standard);
pika_object_set_static_name (PIKA_OBJECT (pika->gradient_factory),
"gradient factory");
pika_data_loader_factory_add_loader (pika->gradient_factory,
"PIKA Gradient",
pika_gradient_load,
PIKA_GRADIENT_FILE_EXTENSION,
TRUE);
pika_data_loader_factory_add_loader (pika->gradient_factory,
"SVG Gradient",
pika_gradient_load_svg,
PIKA_GRADIENT_SVG_FILE_EXTENSION,
FALSE);
pika->palette_factory =
pika_data_loader_factory_new (pika,
PIKA_TYPE_PALETTE,
"palette-path",
"palette-path-writable",
"palette-paths",
pika_palette_new,
pika_palette_get_standard);
pika_object_set_static_name (PIKA_OBJECT (pika->palette_factory),
"palette factory");
pika_data_loader_factory_add_loader (pika->palette_factory,
"PIKA Palette",
pika_palette_load,
PIKA_PALETTE_FILE_EXTENSION,
TRUE);
pika->font_factory =
pika_font_factory_new (pika,
"font-path");
pika_object_set_static_name (PIKA_OBJECT (pika->font_factory),
"font factory");
pika->tool_preset_factory =
pika_data_loader_factory_new (pika,
PIKA_TYPE_TOOL_PRESET,
"tool-preset-path",
"tool-preset-path-writable",
"tool-preset-paths",
pika_tool_preset_new,
NULL);
pika_object_set_static_name (PIKA_OBJECT (pika->tool_preset_factory),
"tool preset factory");
pika_data_loader_factory_add_loader (pika->tool_preset_factory,
"PIKA Tool Preset",
pika_tool_preset_load,
PIKA_TOOL_PRESET_FILE_EXTENSION,
TRUE);
pika->tag_cache = pika_tag_cache_new ();
}
void
pika_data_factories_add_builtin (Pika *pika)
{
PikaData *data;
g_return_if_fail (PIKA_IS_PIKA (pika));
/* add the builtin FG -> BG etc. gradients */
pika_gradients_init (pika);
/* add the color history palette */
pika_palettes_init (pika);
/* add the clipboard brushes */
data = pika_brush_clipboard_new (pika, FALSE);
pika_data_make_internal (data, "pika-brush-clipboard-image");
pika_container_add (pika_data_factory_get_container (pika->brush_factory),
PIKA_OBJECT (data));
g_object_unref (data);
data = pika_brush_clipboard_new (pika, TRUE);
pika_data_make_internal (data, "pika-brush-clipboard-mask");
pika_container_add (pika_data_factory_get_container (pika->brush_factory),
PIKA_OBJECT (data));
g_object_unref (data);
/* add the clipboard pattern */
data = pika_pattern_clipboard_new (pika);
pika_data_make_internal (data, "pika-pattern-clipboard-image");
pika_container_add (pika_data_factory_get_container (pika->pattern_factory),
PIKA_OBJECT (data));
g_object_unref (data);
}
void
pika_data_factories_clear (Pika *pika)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
if (pika->brush_factory)
pika_data_factory_data_free (pika->brush_factory);
if (pika->dynamics_factory)
pika_data_factory_data_free (pika->dynamics_factory);
if (pika->mybrush_factory)
pika_data_factory_data_free (pika->mybrush_factory);
if (pika->pattern_factory)
pika_data_factory_data_free (pika->pattern_factory);
if (pika->gradient_factory)
pika_data_factory_data_free (pika->gradient_factory);
if (pika->palette_factory)
pika_data_factory_data_free (pika->palette_factory);
if (pika->font_factory)
pika_data_factory_data_free (pika->font_factory);
if (pika->tool_preset_factory)
pika_data_factory_data_free (pika->tool_preset_factory);
}
void
pika_data_factories_exit (Pika *pika)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
g_clear_object (&pika->brush_factory);
g_clear_object (&pika->dynamics_factory);
g_clear_object (&pika->mybrush_factory);
g_clear_object (&pika->pattern_factory);
g_clear_object (&pika->gradient_factory);
g_clear_object (&pika->palette_factory);
g_clear_object (&pika->font_factory);
g_clear_object (&pika->tool_preset_factory);
g_clear_object (&pika->tag_cache);
}
gint64
pika_data_factories_get_memsize (Pika *pika,
gint64 *gui_size)
{
gint64 memsize = 0;
g_return_val_if_fail (PIKA_IS_PIKA (pika), 0);
memsize += pika_object_get_memsize (PIKA_OBJECT (pika->named_buffers),
gui_size);
memsize += pika_object_get_memsize (PIKA_OBJECT (pika->brush_factory),
gui_size);
memsize += pika_object_get_memsize (PIKA_OBJECT (pika->dynamics_factory),
gui_size);
memsize += pika_object_get_memsize (PIKA_OBJECT (pika->mybrush_factory),
gui_size);
memsize += pika_object_get_memsize (PIKA_OBJECT (pika->pattern_factory),
gui_size);
memsize += pika_object_get_memsize (PIKA_OBJECT (pika->gradient_factory),
gui_size);
memsize += pika_object_get_memsize (PIKA_OBJECT (pika->palette_factory),
gui_size);
memsize += pika_object_get_memsize (PIKA_OBJECT (pika->font_factory),
gui_size);
memsize += pika_object_get_memsize (PIKA_OBJECT (pika->tool_preset_factory),
gui_size);
memsize += pika_object_get_memsize (PIKA_OBJECT (pika->tag_cache),
gui_size);
return memsize;
}
void
pika_data_factories_data_clean (Pika *pika)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
pika_data_factory_data_clean (pika->brush_factory);
pika_data_factory_data_clean (pika->dynamics_factory);
pika_data_factory_data_clean (pika->mybrush_factory);
pika_data_factory_data_clean (pika->pattern_factory);
pika_data_factory_data_clean (pika->gradient_factory);
pika_data_factory_data_clean (pika->palette_factory);
pika_data_factory_data_clean (pika->font_factory);
pika_data_factory_data_clean (pika->tool_preset_factory);
}
void
pika_data_factories_load (Pika *pika,
PikaInitStatusFunc status_callback)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
/* initialize the list of pika brushes */
status_callback (NULL, _("Brushes"), 0.1);
pika_data_factory_data_init (pika->brush_factory, pika->user_context,
pika->no_data);
/* initialize the list of pika dynamics */
status_callback (NULL, _("Dynamics"), 0.15);
pika_data_factory_data_init (pika->dynamics_factory, pika->user_context,
pika->no_data);
/* initialize the list of mypaint brushes */
status_callback (NULL, _("MyPaint Brushes"), 0.2);
pika_data_factory_data_init (pika->mybrush_factory, pika->user_context,
pika->no_data);
/* initialize the list of pika patterns */
status_callback (NULL, _("Patterns"), 0.3);
pika_data_factory_data_init (pika->pattern_factory, pika->user_context,
pika->no_data);
/* initialize the list of pika palettes */
status_callback (NULL, _("Palettes"), 0.4);
pika_data_factory_data_init (pika->palette_factory, pika->user_context,
pika->no_data);
/* initialize the list of pika gradients */
status_callback (NULL, _("Gradients"), 0.5);
pika_data_factory_data_init (pika->gradient_factory, pika->user_context,
pika->no_data);
/* initialize the color history */
status_callback (NULL, _("Color History"), 0.55);
pika_palettes_load (pika);
/* initialize the list of pika fonts */
status_callback (NULL, _("Fonts"), 0.6);
pika_data_factory_data_init (pika->font_factory, pika->user_context,
pika->no_fonts);
/* initialize the list of pika tool presets if we have a GUI */
if (! pika->no_interface)
{
status_callback (NULL, _("Tool Presets"), 0.7);
pika_data_factory_data_init (pika->tool_preset_factory, pika->user_context,
pika->no_data);
}
/* update tag cache */
status_callback (NULL, _("Updating tag cache"), 0.75);
pika_tag_cache_load (pika->tag_cache);
pika_tag_cache_add_container (pika->tag_cache,
pika_data_factory_get_container (pika->brush_factory));
pika_tag_cache_add_container (pika->tag_cache,
pika_data_factory_get_container (pika->dynamics_factory));
pika_tag_cache_add_container (pika->tag_cache,
pika_data_factory_get_container (pika->mybrush_factory));
pika_tag_cache_add_container (pika->tag_cache,
pika_data_factory_get_container (pika->pattern_factory));
pika_tag_cache_add_container (pika->tag_cache,
pika_data_factory_get_container (pika->gradient_factory));
pika_tag_cache_add_container (pika->tag_cache,
pika_data_factory_get_container (pika->palette_factory));
pika_tag_cache_add_container (pika->tag_cache,
pika_data_factory_get_container (pika->font_factory));
pika_tag_cache_add_container (pika->tag_cache,
pika_data_factory_get_container (pika->tool_preset_factory));
}
void
pika_data_factories_save (Pika *pika)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
pika_tag_cache_save (pika->tag_cache);
pika_data_factory_data_save (pika->brush_factory);
pika_data_factory_data_save (pika->dynamics_factory);
pika_data_factory_data_save (pika->mybrush_factory);
pika_data_factory_data_save (pika->pattern_factory);
pika_data_factory_data_save (pika->gradient_factory);
pika_data_factory_data_save (pika->palette_factory);
pika_data_factory_data_save (pika->font_factory);
pika_data_factory_data_save (pika->tool_preset_factory);
pika_palettes_save (pika);
}

View File

@ -0,0 +1,40 @@
/* 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-2002 Spencer Kimball, Peter Mattis, and others
*
* 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 __PIKA_DATA_FACTORIES_H__
#define __PIKA_DATA_FACTORIES_H__
void pika_data_factories_init (Pika *pika);
void pika_data_factories_add_builtin (Pika *pika);
void pika_data_factories_clear (Pika *pika);
void pika_data_factories_exit (Pika *pika);
gint64 pika_data_factories_get_memsize (Pika *pika,
gint64 *gui_size);
void pika_data_factories_data_clean (Pika *pika);
void pika_data_factories_load (Pika *pika,
PikaInitStatusFunc status_callback);
void pika_data_factories_save (Pika *pika);
#endif /* __PIKA_DATA_FACTORIES_H__ */

1209
app/core/pika-edit.c Normal file

File diff suppressed because it is too large Load Diff

68
app/core/pika-edit.h Normal file
View File

@ -0,0 +1,68 @@
/* 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/>.
*/
#ifndef __PIKA_EDIT_H__
#define __PIKA_EDIT_H__
PikaObject * pika_edit_cut (PikaImage *image,
GList *drawables,
PikaContext *context,
GError **error);
PikaObject * pika_edit_copy (PikaImage *image,
GList *drawables,
PikaContext *context,
GError **error);
PikaBuffer * pika_edit_copy_visible (PikaImage *image,
PikaContext *context,
GError **error);
GList * pika_edit_paste (PikaImage *image,
GList *drawables,
PikaObject *paste,
PikaPasteType paste_type,
PikaContext *context,
gboolean merged,
gint viewport_x,
gint viewport_y,
gint viewport_width,
gint viewport_height);
PikaImage * pika_edit_paste_as_new_image (Pika *pika,
PikaObject *paste,
PikaContext *context);
const gchar * pika_edit_named_cut (PikaImage *image,
const gchar *name,
GList *drawables,
PikaContext *context,
GError **error);
const gchar * pika_edit_named_copy (PikaImage *image,
const gchar *name,
GList *drawables,
PikaContext *context,
GError **error);
const gchar * pika_edit_named_copy_visible (PikaImage *image,
const gchar *name,
PikaContext *context,
GError **error);
#endif /* __PIKA_EDIT_H__ */

View File

@ -0,0 +1,164 @@
/* 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-2003 Spencer Kimball and Peter Mattis
*
* pika-filter-history.c
*
* 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 "config/pikacoreconfig.h"
#include "pika.h"
#include "pika-filter-history.h"
#include "pdb/pikaprocedure.h"
/* local function prototypes */
static gint pika_filter_history_compare (PikaProcedure *proc1,
PikaProcedure *proc2);
/* public functions */
gint
pika_filter_history_size (Pika *pika)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), 0);
return MAX (1, pika->config->filter_history_size);
}
gint
pika_filter_history_length (Pika *pika)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), 0);
return g_list_length (pika->filter_history);
}
PikaProcedure *
pika_filter_history_nth (Pika *pika,
gint n)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
return g_list_nth_data (pika->filter_history, n);
}
void
pika_filter_history_add (Pika *pika,
PikaProcedure *procedure)
{
GList *link;
g_return_if_fail (PIKA_IS_PIKA (pika));
g_return_if_fail (PIKA_IS_PROCEDURE (procedure));
/* return early if the procedure is already at the top */
if (pika->filter_history &&
pika_filter_history_compare (pika->filter_history->data, procedure) == 0)
return;
/* ref new first then unref old, they might be the same */
g_object_ref (procedure);
link = g_list_find_custom (pika->filter_history, procedure,
(GCompareFunc) pika_filter_history_compare);
if (link)
{
g_object_unref (link->data);
pika->filter_history = g_list_delete_link (pika->filter_history, link);
}
pika->filter_history = g_list_prepend (pika->filter_history, procedure);
link = g_list_nth (pika->filter_history, pika_filter_history_size (pika));
if (link)
{
g_object_unref (link->data);
pika->filter_history = g_list_delete_link (pika->filter_history, link);
}
pika_filter_history_changed (pika);
}
void
pika_filter_history_remove (Pika *pika,
PikaProcedure *procedure)
{
GList *link;
g_return_if_fail (PIKA_IS_PIKA (pika));
g_return_if_fail (PIKA_IS_PROCEDURE (procedure));
link = g_list_find_custom (pika->filter_history, procedure,
(GCompareFunc) pika_filter_history_compare);
if (link)
{
g_object_unref (link->data);
pika->filter_history = g_list_delete_link (pika->filter_history, link);
pika_filter_history_changed (pika);
}
}
void
pika_filter_history_clear (Pika *pika)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
if (pika->filter_history)
{
g_list_free_full (pika->filter_history, (GDestroyNotify) g_object_unref);
pika->filter_history = NULL;
pika_filter_history_changed (pika);
}
}
/* private functions */
static gint
pika_filter_history_compare (PikaProcedure *proc1,
PikaProcedure *proc2)
{
/* the procedures can have the same name, but could still be two
* different filters using the same operation, so also compare
* their menu labels
*/
return (pika_procedure_name_compare (proc1, proc2) ||
strcmp (pika_procedure_get_menu_label (proc1),
pika_procedure_get_menu_label (proc2)));
}

View File

@ -0,0 +1,39 @@
/* 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
*
* pika-filter-history.h
*
* 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 __PIKA_FILTER_HISTORY_H__
#define __PIKA_FILTER_HISTORY_H__
gint pika_filter_history_size (Pika *pika);
gint pika_filter_history_length (Pika *pika);
PikaProcedure * pika_filter_history_nth (Pika *pika,
gint n);
void pika_filter_history_add (Pika *pika,
PikaProcedure *procedure);
void pika_filter_history_remove (Pika *pika,
PikaProcedure *procedure);
void pika_filter_history_clear (Pika *pika);
#endif /* __PIKA_FILTER_HISTORY_H__ */

187
app/core/pika-gradients.c Normal file
View File

@ -0,0 +1,187 @@
/* 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-2002 Spencer Kimball, Peter Mattis, and others
*
* pika-gradients.c
* Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
*
* 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 <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include "core-types.h"
#include "pika.h"
#include "pika-gradients.h"
#include "pikacontext.h"
#include "pikacontainer.h"
#include "pikadatafactory.h"
#include "pikagradient.h"
#include "pika-intl.h"
#define CUSTOM_KEY "pika-gradient-custom"
#define FG_BG_RGB_KEY "pika-gradient-fg-bg-rgb"
#define FG_BG_HARDEDGE_KEY "pika-gradient-fg-bg-rgb-hardedge"
#define FG_BG_HSV_CCW_KEY "pika-gradient-fg-bg-hsv-ccw"
#define FG_BG_HSV_CW_KEY "pika-gradient-fg-bg-hsv-cw"
#define FG_TRANSPARENT_KEY "pika-gradient-fg-transparent"
#define FG_TRANSPARENT_HARDEDGE_KEY "pika-gradient-fg-transparent-hardedge"
/* local function prototypes */
static PikaGradient * pika_gradients_add_gradient (Pika *pika,
const gchar *name,
const gchar *id);
/* public functions */
void
pika_gradients_init (Pika *pika)
{
PikaGradient *gradient;
g_return_if_fail (PIKA_IS_PIKA (pika));
/* Custom */
gradient = pika_gradients_add_gradient (pika,
_("Custom"),
CUSTOM_KEY);
g_object_set (gradient,
"writable", TRUE,
NULL);
gradient->segments->left_color_type = PIKA_GRADIENT_COLOR_FOREGROUND;
gradient->segments->right_color_type = PIKA_GRADIENT_COLOR_BACKGROUND;
/* FG to BG (RGB) */
gradient = pika_gradients_add_gradient (pika,
_("FG to BG (RGB)"),
FG_BG_RGB_KEY);
gradient->segments->left_color_type = PIKA_GRADIENT_COLOR_FOREGROUND;
gradient->segments->right_color_type = PIKA_GRADIENT_COLOR_BACKGROUND;
pika_context_set_gradient (pika->user_context, gradient);
/* FG to BG (Hardedge) */
gradient = pika_gradients_add_gradient (pika,
_("FG to BG (Hardedge)"),
FG_BG_HARDEDGE_KEY);
gradient->segments->left_color_type = PIKA_GRADIENT_COLOR_FOREGROUND;
gradient->segments->right_color_type = PIKA_GRADIENT_COLOR_BACKGROUND;
gradient->segments->type = PIKA_GRADIENT_SEGMENT_STEP;
/* FG to BG (HSV counter-clockwise) */
gradient = pika_gradients_add_gradient (pika,
_("FG to BG (HSV counter-clockwise)"),
FG_BG_HSV_CCW_KEY);
gradient->segments->left_color_type = PIKA_GRADIENT_COLOR_FOREGROUND;
gradient->segments->right_color_type = PIKA_GRADIENT_COLOR_BACKGROUND;
gradient->segments->color = PIKA_GRADIENT_SEGMENT_HSV_CCW;
/* FG to BG (HSV clockwise hue) */
gradient = pika_gradients_add_gradient (pika,
_("FG to BG (HSV clockwise hue)"),
FG_BG_HSV_CW_KEY);
gradient->segments->left_color_type = PIKA_GRADIENT_COLOR_FOREGROUND;
gradient->segments->right_color_type = PIKA_GRADIENT_COLOR_BACKGROUND;
gradient->segments->color = PIKA_GRADIENT_SEGMENT_HSV_CW;
/* FG to Transparent */
gradient = pika_gradients_add_gradient (pika,
_("FG to Transparent"),
FG_TRANSPARENT_KEY);
gradient->segments->left_color_type = PIKA_GRADIENT_COLOR_FOREGROUND;
gradient->segments->right_color_type = PIKA_GRADIENT_COLOR_FOREGROUND_TRANSPARENT;
/* FG to Transparent (Hardedge) */
gradient = pika_gradients_add_gradient (pika,
_("FG to Transparent (Hardedge)"),
FG_TRANSPARENT_HARDEDGE_KEY);
gradient->segments->left_color_type = PIKA_GRADIENT_COLOR_FOREGROUND;
gradient->segments->right_color_type = PIKA_GRADIENT_COLOR_FOREGROUND_TRANSPARENT;
gradient->segments->type = PIKA_GRADIENT_SEGMENT_STEP;
}
PikaGradient *
pika_gradients_get_custom (Pika *pika)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
return g_object_get_data (G_OBJECT (pika), CUSTOM_KEY);
}
PikaGradient *
pika_gradients_get_fg_bg_rgb (Pika *pika)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
return g_object_get_data (G_OBJECT (pika), FG_BG_RGB_KEY);
}
PikaGradient *
pika_gradients_get_fg_bg_hsv_ccw (Pika *pika)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
return g_object_get_data (G_OBJECT (pika), FG_BG_HSV_CCW_KEY);
}
PikaGradient *
pika_gradients_get_fg_bg_hsv_cw (Pika *pika)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
return g_object_get_data (G_OBJECT (pika), FG_BG_HSV_CW_KEY);
}
PikaGradient *
pika_gradients_get_fg_transparent (Pika *pika)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
return g_object_get_data (G_OBJECT (pika), FG_TRANSPARENT_KEY);
}
/* private functions */
static PikaGradient *
pika_gradients_add_gradient (Pika *pika,
const gchar *name,
const gchar *id)
{
PikaGradient *gradient;
gradient = PIKA_GRADIENT (pika_gradient_new (pika_get_user_context (pika),
name));
pika_data_make_internal (PIKA_DATA (gradient), id);
pika_container_add (pika_data_factory_get_container (pika->gradient_factory),
PIKA_OBJECT (gradient));
g_object_unref (gradient);
g_object_set_data (G_OBJECT (pika), id, gradient);
return gradient;
}

38
app/core/pika-gradients.h Normal file
View File

@ -0,0 +1,38 @@
/* 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-2002 Spencer Kimball, Peter Mattis, and others
*
* pika-gradients.h
* Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
*
* 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 __PIKA_GRADIENTS__
#define __PIKA_GRADIENTS__
void pika_gradients_init (Pika *pika);
PikaGradient * pika_gradients_get_custom (Pika *pika);
PikaGradient * pika_gradients_get_fg_bg_rgb (Pika *pika);
PikaGradient * pika_gradients_get_fg_bg_hsv_ccw (Pika *pika);
PikaGradient * pika_gradients_get_fg_bg_hsv_cw (Pika *pika);
PikaGradient * pika_gradients_get_fg_transparent (Pika *pika);
#endif /* __PIKA_GRADIENTS__ */

559
app/core/pika-gui.c Normal file
View File

@ -0,0 +1,559 @@
/* 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-2002 Spencer Kimball, Peter Mattis, and others
*
* 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 <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include "libpikabase/pikabase.h"
#include "core-types.h"
#include "pika.h"
#include "pika-gui.h"
#include "pikacontainer.h"
#include "pikacontext.h"
#include "pikadisplay.h"
#include "pikaimage.h"
#include "pikaprogress.h"
#include "pikawaitable.h"
#include "about.h"
#include "pika-intl.h"
void
pika_gui_init (Pika *pika)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
pika->gui.ungrab = NULL;
pika->gui.set_busy = NULL;
pika->gui.unset_busy = NULL;
pika->gui.show_message = NULL;
pika->gui.help = NULL;
pika->gui.get_program_class = NULL;
pika->gui.get_display_name = NULL;
pika->gui.get_user_time = NULL;
pika->gui.get_theme_dir = NULL;
pika->gui.get_icon_theme_dir = NULL;
pika->gui.display_get_window_id = NULL;
pika->gui.display_create = NULL;
pika->gui.display_delete = NULL;
pika->gui.displays_reconnect = NULL;
pika->gui.progress_new = NULL;
pika->gui.progress_free = NULL;
pika->gui.pdb_dialog_set = NULL;
pika->gui.pdb_dialog_close = NULL;
pika->gui.recent_list_add_file = NULL;
pika->gui.recent_list_load = NULL;
pika->gui.get_mount_operation = NULL;
pika->gui.query_profile_policy = NULL;
pika->gui.query_rotation_policy = NULL;
}
void
pika_gui_ungrab (Pika *pika)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
if (pika->gui.ungrab)
pika->gui.ungrab (pika);
}
void
pika_set_busy (Pika *pika)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
/* FIXME: pika_busy HACK */
pika->busy++;
if (pika->busy == 1)
{
if (pika->gui.set_busy)
pika->gui.set_busy (pika);
}
}
static gboolean
pika_idle_unset_busy (gpointer data)
{
Pika *pika = data;
pika_unset_busy (pika);
pika->busy_idle_id = 0;
return FALSE;
}
void
pika_set_busy_until_idle (Pika *pika)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
if (! pika->busy_idle_id)
{
pika_set_busy (pika);
pika->busy_idle_id = g_idle_add_full (G_PRIORITY_HIGH,
pika_idle_unset_busy, pika,
NULL);
}
}
void
pika_unset_busy (Pika *pika)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
g_return_if_fail (pika->busy > 0);
/* FIXME: pika_busy HACK */
pika->busy--;
if (pika->busy == 0)
{
if (pika->gui.unset_busy)
pika->gui.unset_busy (pika);
}
}
void
pika_show_message (Pika *pika,
GObject *handler,
PikaMessageSeverity severity,
const gchar *domain,
const gchar *message)
{
const gchar *desc = (severity == PIKA_MESSAGE_ERROR) ? "Error" : "Message";
g_return_if_fail (PIKA_IS_PIKA (pika));
g_return_if_fail (handler == NULL || G_IS_OBJECT (handler));
g_return_if_fail (message != NULL);
if (! domain)
domain = PIKA_ACRONYM;
if (! pika->console_messages)
{
if (pika->gui.show_message)
{
pika->gui.show_message (pika, handler, severity,
domain, message);
return;
}
else if (PIKA_IS_PROGRESS (handler) &&
pika_progress_message (PIKA_PROGRESS (handler), pika,
severity, domain, message))
{
/* message has been handled by PikaProgress */
return;
}
}
pika_enum_get_value (PIKA_TYPE_MESSAGE_SEVERITY, severity,
NULL, NULL, &desc, NULL);
g_printerr ("%s-%s: %s\n\n", domain, desc, message);
}
void
pika_wait (Pika *pika,
PikaWaitable *waitable,
const gchar *format,
...)
{
va_list args;
gchar *message;
g_return_if_fail (PIKA_IS_PIKA (pika));
g_return_if_fail (PIKA_IS_WAITABLE (waitable));
g_return_if_fail (format != NULL);
if (pika_waitable_wait_for (waitable, 0.5 * G_TIME_SPAN_SECOND))
return;
va_start (args, format);
message = g_strdup_vprintf (format, args);
va_end (args);
if (! pika->console_messages &&
pika->gui.wait &&
pika->gui.wait (pika, waitable, message))
{
return;
}
/* Translator: This message is displayed while PIKA is waiting for
* some operation to finish. The %s argument is a message describing
* the operation.
*/
g_printerr (_("Please wait: %s\n"), message);
pika_waitable_wait (waitable);
g_free (message);
}
void
pika_help (Pika *pika,
PikaProgress *progress,
const gchar *help_domain,
const gchar *help_id)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
g_return_if_fail (progress == NULL || PIKA_IS_PROGRESS (progress));
if (pika->gui.help)
pika->gui.help (pika, progress, help_domain, help_id);
}
const gchar *
pika_get_program_class (Pika *pika)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
if (pika->gui.get_program_class)
return pika->gui.get_program_class (pika);
return NULL;
}
gchar *
pika_get_display_name (Pika *pika,
gint display_id,
GObject **monitor,
gint *monitor_number)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
g_return_val_if_fail (monitor != NULL, NULL);
g_return_val_if_fail (monitor_number != NULL, NULL);
if (pika->gui.get_display_name)
return pika->gui.get_display_name (pika, display_id,
monitor, monitor_number);
*monitor = NULL;
return NULL;
}
/**
* pika_get_user_time:
* @pika:
*
* Returns the timestamp of the last user interaction. The timestamp is
* taken from events caused by user interaction such as key presses or
* pointer movements. See gdk_x11_display_get_user_time().
*
* Returns: the timestamp of the last user interaction
*/
guint32
pika_get_user_time (Pika *pika)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), 0);
if (pika->gui.get_user_time)
return pika->gui.get_user_time (pika);
return 0;
}
GFile *
pika_get_theme_dir (Pika *pika)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
if (pika->gui.get_theme_dir)
return pika->gui.get_theme_dir (pika);
return NULL;
}
GFile *
pika_get_icon_theme_dir (Pika *pika)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
if (pika->gui.get_icon_theme_dir)
return pika->gui.get_icon_theme_dir (pika);
return NULL;
}
PikaObject *
pika_get_window_strategy (Pika *pika)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
if (pika->gui.get_window_strategy)
return pika->gui.get_window_strategy (pika);
return NULL;
}
PikaDisplay *
pika_get_empty_display (Pika *pika)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
if (pika->gui.get_empty_display)
return pika->gui.get_empty_display (pika);
return NULL;
}
guint32
pika_get_display_window_id (Pika *pika,
PikaDisplay *display)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), -1);
g_return_val_if_fail (PIKA_IS_DISPLAY (display), -1);
if (pika->gui.display_get_window_id)
return pika->gui.display_get_window_id (display);
return -1;
}
PikaDisplay *
pika_create_display (Pika *pika,
PikaImage *image,
PikaUnit unit,
gdouble scale,
GObject *monitor)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
g_return_val_if_fail (image == NULL || PIKA_IS_IMAGE (image), NULL);
g_return_val_if_fail (monitor == NULL || G_IS_OBJECT (monitor), NULL);
if (pika->gui.display_create)
return pika->gui.display_create (pika, image, unit, scale, monitor);
return NULL;
}
void
pika_delete_display (Pika *pika,
PikaDisplay *display)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
g_return_if_fail (PIKA_IS_DISPLAY (display));
if (pika->gui.display_delete)
pika->gui.display_delete (display);
}
void
pika_reconnect_displays (Pika *pika,
PikaImage *old_image,
PikaImage *new_image)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
g_return_if_fail (PIKA_IS_IMAGE (old_image));
g_return_if_fail (PIKA_IS_IMAGE (new_image));
if (pika->gui.displays_reconnect)
pika->gui.displays_reconnect (pika, old_image, new_image);
}
PikaProgress *
pika_new_progress (Pika *pika,
PikaDisplay *display)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
g_return_val_if_fail (display == NULL || PIKA_IS_DISPLAY (display), NULL);
if (pika->gui.progress_new)
return pika->gui.progress_new (pika, display);
return NULL;
}
void
pika_free_progress (Pika *pika,
PikaProgress *progress)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
g_return_if_fail (PIKA_IS_PROGRESS (progress));
if (pika->gui.progress_free)
pika->gui.progress_free (pika, progress);
}
gboolean
pika_pdb_dialog_new (Pika *pika,
PikaContext *context,
PikaProgress *progress,
PikaContainer *container,
const gchar *title,
const gchar *callback_name,
const gchar *object_name,
...)
{
gboolean retval = FALSE;
g_return_val_if_fail (PIKA_IS_PIKA (pika), FALSE);
g_return_val_if_fail (PIKA_IS_CONTEXT (context), FALSE);
g_return_val_if_fail (progress == NULL || PIKA_IS_PROGRESS (progress), FALSE);
g_return_val_if_fail (PIKA_IS_CONTAINER (container), FALSE);
g_return_val_if_fail (title != NULL, FALSE);
g_return_val_if_fail (callback_name != NULL, FALSE);
if (pika->gui.pdb_dialog_new)
{
va_list args;
va_start (args, object_name);
retval = pika->gui.pdb_dialog_new (pika, context, progress,
container, title,
callback_name, object_name,
args);
va_end (args);
}
return retval;
}
gboolean
pika_pdb_dialog_set (Pika *pika,
PikaContainer *container,
const gchar *callback_name,
const gchar *object_name,
...)
{
gboolean retval = FALSE;
g_return_val_if_fail (PIKA_IS_PIKA (pika), FALSE);
g_return_val_if_fail (PIKA_IS_CONTAINER (container), FALSE);
g_return_val_if_fail (callback_name != NULL, FALSE);
g_return_val_if_fail (object_name != NULL, FALSE);
if (pika->gui.pdb_dialog_set)
{
va_list args;
va_start (args, object_name);
retval = pika->gui.pdb_dialog_set (pika, container, callback_name,
object_name, args);
va_end (args);
}
return retval;
}
gboolean
pika_pdb_dialog_close (Pika *pika,
PikaContainer *container,
const gchar *callback_name)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), FALSE);
g_return_val_if_fail (PIKA_IS_CONTAINER (container), FALSE);
g_return_val_if_fail (callback_name != NULL, FALSE);
if (pika->gui.pdb_dialog_close)
return pika->gui.pdb_dialog_close (pika, container, callback_name);
return FALSE;
}
gboolean
pika_recent_list_add_file (Pika *pika,
GFile *file,
const gchar *mime_type)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), FALSE);
g_return_val_if_fail (G_IS_FILE (file), FALSE);
if (pika->gui.recent_list_add_file)
return pika->gui.recent_list_add_file (pika, file, mime_type);
return FALSE;
}
void
pika_recent_list_load (Pika *pika)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
if (pika->gui.recent_list_load)
pika->gui.recent_list_load (pika);
}
GMountOperation *
pika_get_mount_operation (Pika *pika,
PikaProgress *progress)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), FALSE);
g_return_val_if_fail (progress == NULL || PIKA_IS_PROGRESS (progress), FALSE);
if (pika->gui.get_mount_operation)
return pika->gui.get_mount_operation (pika, progress);
return g_mount_operation_new ();
}
PikaColorProfilePolicy
pika_query_profile_policy (Pika *pika,
PikaImage *image,
PikaContext *context,
PikaColorProfile **dest_profile,
PikaColorRenderingIntent *intent,
gboolean *bpc,
gboolean *dont_ask)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), PIKA_COLOR_PROFILE_POLICY_KEEP);
g_return_val_if_fail (PIKA_IS_IMAGE (image), PIKA_COLOR_PROFILE_POLICY_KEEP);
g_return_val_if_fail (PIKA_IS_CONTEXT (context), PIKA_COLOR_PROFILE_POLICY_KEEP);
g_return_val_if_fail (dest_profile != NULL, PIKA_COLOR_PROFILE_POLICY_KEEP);
if (pika->gui.query_profile_policy)
return pika->gui.query_profile_policy (pika, image, context,
dest_profile,
intent, bpc,
dont_ask);
return PIKA_COLOR_PROFILE_POLICY_KEEP;
}
PikaMetadataRotationPolicy
pika_query_rotation_policy (Pika *pika,
PikaImage *image,
PikaContext *context,
gboolean *dont_ask)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), PIKA_METADATA_ROTATION_POLICY_ROTATE);
g_return_val_if_fail (PIKA_IS_IMAGE (image), PIKA_METADATA_ROTATION_POLICY_ROTATE);
g_return_val_if_fail (PIKA_IS_CONTEXT (context), PIKA_METADATA_ROTATION_POLICY_ROTATE);
if (pika->gui.query_rotation_policy)
return pika->gui.query_rotation_policy (pika, image, context, dont_ask);
return PIKA_METADATA_ROTATION_POLICY_ROTATE;
}

216
app/core/pika-gui.h Normal file
View File

@ -0,0 +1,216 @@
/* 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-1997 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/>.
*/
#ifndef __PIKA_GUI_H__
#define __PIKA_GUI_H__
typedef struct _PikaGui PikaGui;
struct _PikaGui
{
void (* ungrab) (Pika *pika);
void (* set_busy) (Pika *pika);
void (* unset_busy) (Pika *pika);
void (* show_message) (Pika *pika,
GObject *handler,
PikaMessageSeverity severity,
const gchar *domain,
const gchar *message);
void (* help) (Pika *pika,
PikaProgress *progress,
const gchar *help_domain,
const gchar *help_id);
gboolean (* wait) (Pika *pika,
PikaWaitable *waitable,
const gchar *message);
const gchar * (* get_program_class) (Pika *pika);
gchar * (* get_display_name) (Pika *pika,
gint display_id,
GObject **monitor,
gint *monitor_number);
guint32 (* get_user_time) (Pika *pika);
GFile * (* get_theme_dir) (Pika *pika);
GFile * (* get_icon_theme_dir) (Pika *pika);
PikaObject * (* get_window_strategy) (Pika *pika);
PikaDisplay * (* get_empty_display) (Pika *pika);
guint32 (* display_get_window_id) (PikaDisplay *display);
PikaDisplay * (* display_create) (Pika *pika,
PikaImage *image,
PikaUnit unit,
gdouble scale,
GObject *monitor);
void (* display_delete) (PikaDisplay *display);
void (* displays_reconnect) (Pika *pika,
PikaImage *old_image,
PikaImage *new_image);
PikaProgress * (* progress_new) (Pika *pika,
PikaDisplay *display);
void (* progress_free) (Pika *pika,
PikaProgress *progress);
gboolean (* pdb_dialog_new) (Pika *pika,
PikaContext *context,
PikaProgress *progress,
PikaContainer *container,
const gchar *title,
const gchar *callback_name,
const gchar *object_name,
va_list args);
gboolean (* pdb_dialog_set) (Pika *pika,
PikaContainer *container,
const gchar *callback_name,
const gchar *object_name,
va_list args);
gboolean (* pdb_dialog_close) (Pika *pika,
PikaContainer *container,
const gchar *callback_name);
gboolean (* recent_list_add_file) (Pika *pika,
GFile *file,
const gchar *mime_type);
void (* recent_list_load) (Pika *pika);
GMountOperation
* (* get_mount_operation) (Pika *pika,
PikaProgress *progress);
PikaColorProfilePolicy
(* query_profile_policy) (Pika *pika,
PikaImage *image,
PikaContext *context,
PikaColorProfile **dest_profile,
PikaColorRenderingIntent *intent,
gboolean *bpc,
gboolean *dont_ask);
PikaMetadataRotationPolicy
(* query_rotation_policy) (Pika *pika,
PikaImage *image,
PikaContext *context,
gboolean *dont_ask);
};
void pika_gui_init (Pika *pika);
void pika_gui_ungrab (Pika *pika);
PikaObject * pika_get_window_strategy (Pika *pika);
PikaDisplay * pika_get_empty_display (Pika *pika);
PikaDisplay * pika_get_display_by_id (Pika *pika,
gint ID);
gint pika_get_display_id (Pika *pika,
PikaDisplay *display);
guint32 pika_get_display_window_id (Pika *pika,
PikaDisplay *display);
PikaDisplay * pika_create_display (Pika *pika,
PikaImage *image,
PikaUnit unit,
gdouble scale,
GObject *monitor);
void pika_delete_display (Pika *pika,
PikaDisplay *display);
void pika_reconnect_displays (Pika *pika,
PikaImage *old_image,
PikaImage *new_image);
void pika_set_busy (Pika *pika);
void pika_set_busy_until_idle (Pika *pika);
void pika_unset_busy (Pika *pika);
void pika_show_message (Pika *pika,
GObject *handler,
PikaMessageSeverity severity,
const gchar *domain,
const gchar *message);
void pika_help (Pika *pika,
PikaProgress *progress,
const gchar *help_domain,
const gchar *help_id);
void pika_wait (Pika *pika,
PikaWaitable *waitable,
const gchar *format,
...) G_GNUC_PRINTF (3, 4);
PikaProgress * pika_new_progress (Pika *pika,
PikaDisplay *display);
void pika_free_progress (Pika *pika,
PikaProgress *progress);
const gchar * pika_get_program_class (Pika *pika);
gchar * pika_get_display_name (Pika *pika,
gint display_id,
GObject **monitor,
gint *monitor_number);
guint32 pika_get_user_time (Pika *pika);
GFile * pika_get_theme_dir (Pika *pika);
GFile * pika_get_icon_theme_dir (Pika *pika);
gboolean pika_pdb_dialog_new (Pika *pika,
PikaContext *context,
PikaProgress *progress,
PikaContainer *container,
const gchar *title,
const gchar *callback_name,
const gchar *object_name,
...) G_GNUC_NULL_TERMINATED;
gboolean pika_pdb_dialog_set (Pika *pika,
PikaContainer *container,
const gchar *callback_name,
const gchar *object_name,
...) G_GNUC_NULL_TERMINATED;
gboolean pika_pdb_dialog_close (Pika *pika,
PikaContainer *container,
const gchar *callback_name);
gboolean pika_recent_list_add_file (Pika *pika,
GFile *file,
const gchar *mime_type);
void pika_recent_list_load (Pika *pika);
GMountOperation
* pika_get_mount_operation (Pika *pika,
PikaProgress *progress);
PikaColorProfilePolicy
pika_query_profile_policy (Pika *pika,
PikaImage *image,
PikaContext *context,
PikaColorProfile **dest_profile,
PikaColorRenderingIntent *intent,
gboolean *bpc,
gboolean *dont_ask);
PikaMetadataRotationPolicy
pika_query_rotation_policy (Pika *pika,
PikaImage *image,
PikaContext *context,
gboolean *dont_ask);
#endif /* __PIKA_GUI_H__ */

View File

@ -0,0 +1,350 @@
/* 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-2002 Spencer Kimball, Peter Mattis, and others
*
* pika-internal-data.c
* Copyright (C) 2017 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 <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include "libpikabase/pikabase.h"
#include "core-types.h"
#include "pika.h"
#include "pika-gradients.h"
#include "pika-internal-data.h"
#include "pikadata.h"
#include "pikadataloaderfactory.h"
#include "pikaerror.h"
#include "pikagradient-load.h"
#include "pika-intl.h"
#define PIKA_INTERNAL_DATA_DIRECTORY "internal-data"
typedef PikaData * (* PikaDataGetFunc) (Pika *pika);
typedef struct _PikaInternalDataFile PikaInternalDataFile;
struct _PikaInternalDataFile
{
const gchar *name;
PikaDataGetFunc get_func;
PikaDataLoadFunc load_func;
};
/* local function prototypes */
static gboolean pika_internal_data_create_directory (GError **error);
static GFile * pika_internal_data_get_file (const PikaInternalDataFile *data_file);
static gboolean pika_internal_data_load_data_file (Pika *pika,
const PikaInternalDataFile *data_file,
GError **error);
static gboolean pika_internal_data_save_data_file (Pika *pika,
const PikaInternalDataFile *data_file,
GError **error);
static gboolean pika_internal_data_delete_data_file (Pika *pika,
const PikaInternalDataFile *data_file,
GError **error);
/* static variables */
static const PikaInternalDataFile internal_data_files[] =
{
/* Custom gradient */
{
.name = "custom" PIKA_GRADIENT_FILE_EXTENSION,
.get_func = (PikaDataGetFunc) pika_gradients_get_custom,
.load_func = pika_gradient_load
}
};
/* public functions */
gboolean
pika_internal_data_load (Pika *pika,
GError **error)
{
gint i;
g_return_val_if_fail (PIKA_IS_PIKA (pika), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
for (i = 0; i < G_N_ELEMENTS (internal_data_files); i++)
{
const PikaInternalDataFile *data_file = &internal_data_files[i];
if (! pika_internal_data_load_data_file (pika, data_file, error))
return FALSE;
}
return TRUE;
}
gboolean
pika_internal_data_save (Pika *pika,
GError **error)
{
gint i;
g_return_val_if_fail (PIKA_IS_PIKA (pika), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (! pika_internal_data_create_directory (error))
return FALSE;
for (i = 0; i < G_N_ELEMENTS (internal_data_files); i++)
{
const PikaInternalDataFile *data_file = &internal_data_files[i];
if (! pika_internal_data_save_data_file (pika, data_file, error))
return FALSE;
}
return TRUE;
}
gboolean
pika_internal_data_clear (Pika *pika,
GError **error)
{
gint i;
g_return_val_if_fail (PIKA_IS_PIKA (pika), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
for (i = 0; i < G_N_ELEMENTS (internal_data_files); i++)
{
const PikaInternalDataFile *data_file = &internal_data_files[i];
if (! pika_internal_data_delete_data_file (pika, data_file, error))
return FALSE;
}
return TRUE;
}
/* private functions */
static gboolean
pika_internal_data_create_directory (GError **error)
{
GFile *directory;
GError *my_error = NULL;
gboolean success;
directory = pika_directory_file (PIKA_INTERNAL_DATA_DIRECTORY, NULL);
success = g_file_make_directory (directory, NULL, &my_error);
g_object_unref (directory);
if (! success)
{
if (my_error->code == G_IO_ERROR_EXISTS)
{
g_clear_error (&my_error);
success = TRUE;
}
else
{
g_propagate_error (error, my_error);
}
}
return success;
}
static GFile *
pika_internal_data_get_file (const PikaInternalDataFile *data_file)
{
return pika_directory_file (PIKA_INTERNAL_DATA_DIRECTORY,
data_file->name,
NULL);
}
static gboolean
pika_internal_data_load_data_file (Pika *pika,
const PikaInternalDataFile *data_file,
GError **error)
{
GFile *file;
GInputStream *input;
PikaData *data;
GList *list;
GError *my_error = NULL;
file = pika_internal_data_get_file (data_file);
if (pika->be_verbose)
g_print ("Parsing '%s'\n", pika_file_get_utf8_name (file));
input = G_INPUT_STREAM (g_file_read (file, NULL, &my_error));
if (! input)
{
g_object_unref (file);
if (my_error->code == G_IO_ERROR_NOT_FOUND)
{
g_clear_error (&my_error);
return TRUE;
}
else
{
g_propagate_error (error, my_error);
return FALSE;
}
}
list = data_file->load_func (pika->user_context, file, input, error);
g_object_unref (input);
g_object_unref (file);
if (! list)
return FALSE;
data = data_file->get_func (pika);
pika_data_copy (data, PIKA_DATA (list->data));
g_list_free_full (list, g_object_unref);
return TRUE;
}
static gboolean
pika_internal_data_save_data_file (Pika *pika,
const PikaInternalDataFile *data_file,
GError **error)
{
GFile *file;
GOutputStream *output;
PikaData *data;
gboolean success;
file = pika_internal_data_get_file (data_file);
if (pika->be_verbose)
g_print ("Writing '%s'\n", pika_file_get_utf8_name (file));
output = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE,
G_FILE_CREATE_NONE,
NULL, error));
if (! output)
{
g_object_unref (file);
return FALSE;
}
data = data_file->get_func (pika);
/* we bypass pika_data_save() and call the data's save() virtual function
* directly, since pika_data_save() is a nop for internal data.
*
* FIXME: we save the data whether it's dirty or not, since it might not
* exist on disk. currently, we only use this for cheap data, such as
* gradients, so this is not a big concern, but if we save more expensive
* data in the future, we should fix this.
*/
pika_assert (PIKA_DATA_GET_CLASS (data)->save);
success = PIKA_DATA_GET_CLASS (data)->save (data, output, error);
if (success)
{
if (! g_output_stream_close (output, NULL, error))
{
g_prefix_error (error,
_("Error saving '%s': "),
pika_file_get_utf8_name (file));
success = FALSE;
}
}
else
{
GCancellable *cancellable = g_cancellable_new ();
g_cancellable_cancel (cancellable);
if (error && *error)
{
g_prefix_error (error,
_("Error saving '%s': "),
pika_file_get_utf8_name (file));
}
else
{
g_set_error (error, PIKA_DATA_ERROR, PIKA_DATA_ERROR_WRITE,
_("Error saving '%s'"),
pika_file_get_utf8_name (file));
}
g_output_stream_close (output, cancellable, NULL);
g_object_unref (cancellable);
}
g_object_unref (output);
g_object_unref (file);
return success;
}
static gboolean
pika_internal_data_delete_data_file (Pika *pika,
const PikaInternalDataFile *data_file,
GError **error)
{
GFile *file;
GError *my_error = NULL;
gboolean success = TRUE;
file = pika_internal_data_get_file (data_file);
if (pika->be_verbose)
g_print ("Deleting '%s'\n", pika_file_get_utf8_name (file));
if (! g_file_delete (file, NULL, &my_error) &&
my_error->code != G_IO_ERROR_NOT_FOUND)
{
success = FALSE;
g_set_error (error, PIKA_ERROR, PIKA_FAILED,
_("Deleting \"%s\" failed: %s"),
pika_file_get_utf8_name (file), my_error->message);
}
g_clear_error (&my_error);
g_object_unref (file);
return success;
}

View File

@ -0,0 +1,38 @@
/* 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-2002 Spencer Kimball, Peter Mattis, and others
*
* pika-internal-data.h
* Copyright (C) 2017 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 __PIKA_INTERNAL_DATA__
#define __PIKA_INTERNAL_DATA__
gboolean pika_internal_data_load (Pika *pika,
GError **error);
gboolean pika_internal_data_save (Pika *pika,
GError **error);
gboolean pika_internal_data_clear (Pika *pika,
GError **error);
#endif /* __PIKA_INTERNAL_DATA__ */

364
app/core/pika-memsize.c Normal file
View File

@ -0,0 +1,364 @@
/* 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 <cairo.h>
#include <gegl.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "libpikabase/pikabase.h"
#include "libpikamath/pikamath.h"
#include "libpikacolor/pikacolor.h"
#include "core-types.h"
#include "pika-memsize.h"
#include "pikaparamspecs.h"
gint64
pika_g_type_instance_get_memsize (GTypeInstance *instance)
{
if (instance)
{
GTypeQuery type_query;
g_type_query (G_TYPE_FROM_INSTANCE (instance), &type_query);
return type_query.instance_size;
}
return 0;
}
gint64
pika_g_object_get_memsize (GObject *object)
{
if (object)
return pika_g_type_instance_get_memsize ((GTypeInstance *) object);
return 0;
}
gint64
pika_g_hash_table_get_memsize (GHashTable *hash,
gint64 data_size)
{
if (hash)
return (2 * sizeof (gint) +
5 * sizeof (gpointer) +
g_hash_table_size (hash) * (3 * sizeof (gpointer) + data_size));
return 0;
}
typedef struct
{
PikaMemsizeFunc func;
gint64 memsize;
gint64 gui_size;
} HashMemsize;
static void
hash_memsize_foreach (gpointer key,
gpointer value,
HashMemsize *memsize)
{
gint64 gui_size = 0;
memsize->memsize += memsize->func (value, &gui_size);
memsize->gui_size += gui_size;
}
gint64
pika_g_hash_table_get_memsize_foreach (GHashTable *hash,
PikaMemsizeFunc func,
gint64 *gui_size)
{
HashMemsize memsize;
g_return_val_if_fail (func != NULL, 0);
if (! hash)
return 0;
memsize.func = func;
memsize.memsize = 0;
memsize.gui_size = 0;
g_hash_table_foreach (hash, (GHFunc) hash_memsize_foreach, &memsize);
if (gui_size)
*gui_size = memsize.gui_size;
return memsize.memsize + pika_g_hash_table_get_memsize (hash, 0);
}
gint64
pika_g_slist_get_memsize (GSList *slist,
gint64 data_size)
{
return g_slist_length (slist) * (data_size + sizeof (GSList));
}
gint64
pika_g_slist_get_memsize_foreach (GSList *slist,
PikaMemsizeFunc func,
gint64 *gui_size)
{
GSList *l;
gint64 memsize = 0;
g_return_val_if_fail (func != NULL, 0);
for (l = slist; l; l = g_slist_next (l))
memsize += sizeof (GSList) + func (l->data, gui_size);
return memsize;
}
gint64
pika_g_list_get_memsize (GList *list,
gint64 data_size)
{
return g_list_length (list) * (data_size + sizeof (GList));
}
gint64
pika_g_list_get_memsize_foreach (GList *list,
PikaMemsizeFunc func,
gint64 *gui_size)
{
GList *l;
gint64 memsize = 0;
g_return_val_if_fail (func != NULL, 0);
for (l = list; l; l = g_list_next (l))
memsize += sizeof (GList) + func (l->data, gui_size);
return memsize;
}
gint64
pika_g_queue_get_memsize (GQueue *queue,
gint64 data_size)
{
if (queue)
{
return sizeof (GQueue) +
g_queue_get_length (queue) * (data_size + sizeof (GList));
}
return 0;
}
gint64
pika_g_queue_get_memsize_foreach (GQueue *queue,
PikaMemsizeFunc func,
gint64 *gui_size)
{
gint64 memsize = 0;
g_return_val_if_fail (func != NULL, 0);
if (queue)
{
GList *l;
memsize = sizeof (GQueue);
for (l = queue->head; l; l = g_list_next (l))
memsize += sizeof (GList) + func (l->data, gui_size);
}
return memsize;
}
gint64
pika_g_value_get_memsize (GValue *value)
{
gint64 memsize = 0;
if (! value)
return 0;
if (G_VALUE_HOLDS_STRING (value))
{
memsize += pika_string_get_memsize (g_value_get_string (value));
}
else if (G_VALUE_HOLDS_BOXED (value))
{
if (PIKA_VALUE_HOLDS_RGB (value))
{
memsize += sizeof (PikaRGB);
}
else if (PIKA_VALUE_HOLDS_MATRIX2 (value))
{
memsize += sizeof (PikaMatrix2);
}
else if (PIKA_VALUE_HOLDS_PARASITE (value))
{
memsize += pika_parasite_get_memsize (g_value_get_boxed (value),
NULL);
}
else if (PIKA_VALUE_HOLDS_ARRAY (value) ||
PIKA_VALUE_HOLDS_INT32_ARRAY (value) ||
PIKA_VALUE_HOLDS_FLOAT_ARRAY (value))
{
PikaArray *array = g_value_get_boxed (value);
if (array)
memsize += sizeof (PikaArray) +
(array->static_data ? 0 : array->length);
}
else if (G_VALUE_HOLDS (value, G_TYPE_BYTES))
{
GBytes *bytes = g_value_get_boxed (value);
if (bytes)
{
memsize += g_bytes_get_size (bytes);
}
}
else if (G_VALUE_HOLDS (value, G_TYPE_STRV))
{
gchar **array = g_value_get_boxed (value);
if (array)
{
guint length = g_strv_length (array);
memsize += (length + 1) * sizeof (gchar *);
for (gint i = 0; i < length; i++)
memsize += pika_string_get_memsize (array[i]);
}
}
else if (strcmp ("PikaValueArray", G_VALUE_TYPE_NAME (value)) == 0)
{
PikaValueArray *array = g_value_get_boxed (value);
if (array)
{
gint n_values = pika_value_array_length (array), i;
memsize += /* sizeof (PikaValueArray) */ sizeof (GValue *) + 3 * sizeof (gint);
for (i = 0; i < n_values; i++)
memsize += pika_g_value_get_memsize (pika_value_array_index (array, i));
}
}
else
{
g_printerr ("%s: unhandled boxed value type: %s\n",
G_STRFUNC, G_VALUE_TYPE_NAME (value));
}
}
else if (G_VALUE_HOLDS_OBJECT (value))
{
if (strcmp ("PikaPattern", G_VALUE_TYPE_NAME (value)) == 0)
memsize += pika_g_object_get_memsize (g_value_get_object (value));
else if (strcmp ("PikaFont", G_VALUE_TYPE_NAME (value)) == 0)
memsize += pika_g_object_get_memsize (g_value_get_object (value));
else
g_printerr ("%s: unhandled object value type: %s\n",
G_STRFUNC, G_VALUE_TYPE_NAME (value));
}
return memsize + sizeof (GValue);
}
gint64
pika_g_param_spec_get_memsize (GParamSpec *pspec)
{
gint64 memsize = 0;
if (! pspec)
return 0;
if (! (pspec->flags & G_PARAM_STATIC_NAME))
memsize += pika_string_get_memsize (g_param_spec_get_name (pspec));
if (! (pspec->flags & G_PARAM_STATIC_NICK))
memsize += pika_string_get_memsize (g_param_spec_get_nick (pspec));
if (! (pspec->flags & G_PARAM_STATIC_BLURB))
memsize += pika_string_get_memsize (g_param_spec_get_blurb (pspec));
return memsize + pika_g_type_instance_get_memsize ((GTypeInstance *) pspec);
}
gint64
pika_gegl_buffer_get_memsize (GeglBuffer *buffer)
{
if (buffer)
{
const Babl *format = gegl_buffer_get_format (buffer);
return ((gint64) babl_format_get_bytes_per_pixel (format) *
(gint64) gegl_buffer_get_width (buffer) *
(gint64) gegl_buffer_get_height (buffer) +
pika_g_object_get_memsize (G_OBJECT (buffer)));
}
return 0;
}
gint64
pika_gegl_pyramid_get_memsize (GeglBuffer *buffer)
{
if (buffer)
{
const Babl *format = gegl_buffer_get_format (buffer);
/* The pyramid levels constitute a geometric sum with a ratio of 1/4. */
return ((gint64) babl_format_get_bytes_per_pixel (format) *
(gint64) gegl_buffer_get_width (buffer) *
(gint64) gegl_buffer_get_height (buffer) * 1.33 +
pika_g_object_get_memsize (G_OBJECT (buffer)));
}
return 0;
}
gint64
pika_string_get_memsize (const gchar *string)
{
if (string)
return strlen (string) + 1;
return 0;
}
gint64
pika_parasite_get_memsize (PikaParasite *parasite,
gint64 *gui_size)
{
if (parasite)
return (sizeof (PikaParasite) +
pika_string_get_memsize (parasite->name) +
parasite->size);
return 0;
}

64
app/core/pika-memsize.h Normal file
View File

@ -0,0 +1,64 @@
/* 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/>.
*/
#ifndef __APP_PIKA_MEMSIZE_H__
#define __APP_PIKA_MEMSIZE_H__
gint64 pika_g_type_instance_get_memsize (GTypeInstance *instance);
gint64 pika_g_object_get_memsize (GObject *object);
gint64 pika_g_hash_table_get_memsize (GHashTable *hash,
gint64 data_size);
gint64 pika_g_hash_table_get_memsize_foreach (GHashTable *hash,
PikaMemsizeFunc func,
gint64 *gui_size);
gint64 pika_g_slist_get_memsize (GSList *slist,
gint64 data_size);
gint64 pika_g_slist_get_memsize_foreach (GSList *slist,
PikaMemsizeFunc func,
gint64 *gui_size);
gint64 pika_g_list_get_memsize (GList *list,
gint64 data_size);
gint64 pika_g_list_get_memsize_foreach (GList *list,
PikaMemsizeFunc func,
gint64 *gui_size);
gint64 pika_g_queue_get_memsize (GQueue *queue,
gint64 data_size);
gint64 pika_g_queue_get_memsize_foreach (GQueue *queue,
PikaMemsizeFunc func,
gint64 *gui_size);
gint64 pika_g_value_get_memsize (GValue *value);
gint64 pika_g_param_spec_get_memsize (GParamSpec *pspec);
gint64 pika_gegl_buffer_get_memsize (GeglBuffer *buffer);
gint64 pika_gegl_pyramid_get_memsize (GeglBuffer *buffer);
gint64 pika_string_get_memsize (const gchar *string);
gint64 pika_parasite_get_memsize (PikaParasite *parasite,
gint64 *gui_size);
#endif /* __APP_PIKA_MEMSIZE_H__ */

237
app/core/pika-modules.c Normal file
View File

@ -0,0 +1,237 @@
/* 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
*
* pikamodules.c
* (C) 1999 Austin Donnelly <austin@gimp.org>
*
* 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 <gio/gio.h>
#include "libpikabase/pikabase.h"
#include "libpikamodule/pikamodule.h"
#include "libpikaconfig/pikaconfig.h"
#include "core-types.h"
#include "config/pikacoreconfig.h"
#include "pika.h"
#include "pika-modules.h"
#include "pika-intl.h"
void
pika_modules_init (Pika *pika)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
if (! pika->no_interface)
{
pika->module_db = pika_module_db_new (pika->be_verbose);
pika->write_modulerc = FALSE;
}
}
void
pika_modules_exit (Pika *pika)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
g_clear_object (&pika->module_db);
}
void
pika_modules_load (Pika *pika)
{
GFile *file;
GScanner *scanner;
gchar *module_load_inhibit = NULL;
g_return_if_fail (PIKA_IS_PIKA (pika));
if (pika->no_interface)
return;
pika_module_db_set_verbose (pika->module_db, pika->be_verbose);
file = pika_directory_file ("modulerc", NULL);
if (pika->be_verbose)
g_print ("Parsing '%s'\n", pika_file_get_utf8_name (file));
scanner = pika_scanner_new_file (file, NULL);
g_object_unref (file);
if (scanner)
{
GTokenType token;
GError *error = NULL;
#define MODULE_LOAD_INHIBIT 1
g_scanner_scope_add_symbol (scanner, 0, "module-load-inhibit",
GINT_TO_POINTER (MODULE_LOAD_INHIBIT));
token = G_TOKEN_LEFT_PAREN;
while (g_scanner_peek_next_token (scanner) == token)
{
token = g_scanner_get_next_token (scanner);
switch (token)
{
case G_TOKEN_LEFT_PAREN:
token = G_TOKEN_SYMBOL;
break;
case G_TOKEN_SYMBOL:
if (scanner->value.v_symbol == GINT_TO_POINTER (MODULE_LOAD_INHIBIT))
{
token = G_TOKEN_STRING;
if (! pika_scanner_parse_string_no_validate (scanner,
&module_load_inhibit))
goto error;
}
token = G_TOKEN_RIGHT_PAREN;
break;
case G_TOKEN_RIGHT_PAREN:
token = G_TOKEN_LEFT_PAREN;
break;
default: /* do nothing */
break;
}
}
#undef MODULE_LOAD_INHIBIT
if (token != G_TOKEN_LEFT_PAREN)
{
g_scanner_get_next_token (scanner);
g_scanner_unexp_token (scanner, token, NULL, NULL, NULL,
_("fatal parse error"), TRUE);
}
error:
if (error)
{
pika_message_literal (pika, NULL, PIKA_MESSAGE_ERROR, error->message);
g_clear_error (&error);
}
pika_scanner_unref (scanner);
}
if (module_load_inhibit)
{
pika_module_db_set_load_inhibit (pika->module_db, module_load_inhibit);
g_free (module_load_inhibit);
}
pika_module_db_load (pika->module_db, pika->config->module_path);
}
void
pika_modules_unload (Pika *pika)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
if (! pika->no_interface && pika->write_modulerc)
{
PikaConfigWriter *writer;
GString *str;
GListModel *modules = G_LIST_MODEL (pika->module_db);
guint i;
const gchar *p;
GFile *file;
GError *error = NULL;
str = g_string_new (NULL);
for (i = 0; i < g_list_model_get_n_items (modules); i++)
{
PikaModule *module;
module = g_list_model_get_item (modules, i);
if (! pika_module_get_auto_load (module))
{
GFile *file = pika_module_get_file (module);
gchar *path = g_file_get_path (file);
g_string_append_c (str, G_SEARCHPATH_SEPARATOR);
g_string_append (str, path);
g_free (path);
}
g_clear_object (&module);
}
if (str->len > 0)
p = str->str + 1;
else
p = "";
file = pika_directory_file ("modulerc", NULL);
if (pika->be_verbose)
g_print ("Writing '%s'\n", pika_file_get_utf8_name (file));
writer = pika_config_writer_new_from_file (file,
TRUE,
"PIKA modulerc",
&error);
g_object_unref (file);
if (writer)
{
pika_config_writer_open (writer, "module-load-inhibit");
pika_config_writer_string (writer, p);
pika_config_writer_close (writer);
pika_config_writer_finish (writer, "end of modulerc", &error);
pika->write_modulerc = FALSE;
}
g_string_free (str, TRUE);
if (error)
{
pika_message_literal (pika, NULL, PIKA_MESSAGE_ERROR, error->message);
g_clear_error (&error);
}
}
}
void
pika_modules_refresh (Pika *pika)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
if (! pika->no_interface)
{
pika_module_db_refresh (pika->module_db, pika->config->module_path);
}
}

38
app/core/pika-modules.h Normal file
View File

@ -0,0 +1,38 @@
/* 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
*
* pikamodules.h
* (C) 1999 Austin Donnelly <austin@gimp.org>
*
* 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 __PIKA_MODULES_H__
#define __PIKA_MODULES_H__
void pika_modules_init (Pika *pika);
void pika_modules_exit (Pika *pika);
void pika_modules_load (Pika *pika);
void pika_modules_unload (Pika *pika);
void pika_modules_refresh (Pika *pika);
#endif /* __PIKA_MODULES_H__ */

147
app/core/pika-palettes.c Normal file
View File

@ -0,0 +1,147 @@
/* 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-2002 Spencer Kimball, Peter Mattis, and others
*
* pika-gradients.c
* Copyright (C) 2014 Michael Natterer <mitch@gimp.org>
*
* 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 <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include "libpikabase/pikabase.h"
#include "core-types.h"
#include "pika.h"
#include "pika-palettes.h"
#include "pikacontext.h"
#include "pikacontainer.h"
#include "pikadatafactory.h"
#include "pikapalettemru.h"
#include "pika-intl.h"
#define COLOR_HISTORY_KEY "pika-palette-color-history"
/* local function prototypes */
static PikaPalette * pika_palettes_add_palette (Pika *pika,
const gchar *name,
const gchar *id);
/* public functions */
void
pika_palettes_init (Pika *pika)
{
PikaPalette *palette;
g_return_if_fail (PIKA_IS_PIKA (pika));
palette = pika_palettes_add_palette (pika,
_("Color History"),
COLOR_HISTORY_KEY);
pika_context_set_palette (pika->user_context, palette);
}
void
pika_palettes_load (Pika *pika)
{
PikaPalette *palette;
GFile *file;
g_return_if_fail (PIKA_IS_PIKA (pika));
palette = pika_palettes_get_color_history (pika);
file = pika_directory_file ("colorrc", NULL);
if (pika->be_verbose)
g_print ("Parsing '%s'\n", pika_file_get_utf8_name (file));
pika_palette_mru_load (PIKA_PALETTE_MRU (palette), file);
g_object_unref (file);
}
void
pika_palettes_save (Pika *pika)
{
PikaPalette *palette;
GFile *file;
g_return_if_fail (PIKA_IS_PIKA (pika));
palette = pika_palettes_get_color_history (pika);
file = pika_directory_file ("colorrc", NULL);
if (pika->be_verbose)
g_print ("Writing '%s'\n", pika_file_get_utf8_name (file));
pika_palette_mru_save (PIKA_PALETTE_MRU (palette), file);
g_object_unref (file);
}
PikaPalette *
pika_palettes_get_color_history (Pika *pika)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
return g_object_get_data (G_OBJECT (pika), COLOR_HISTORY_KEY);
}
void
pika_palettes_add_color_history (Pika *pika,
const PikaRGB *color)
{
PikaPalette *history;
history = pika_palettes_get_color_history (pika);
pika_palette_mru_add (PIKA_PALETTE_MRU (history), color);
}
/* private functions */
static PikaPalette *
pika_palettes_add_palette (Pika *pika,
const gchar *name,
const gchar *id)
{
PikaData *palette;
palette = pika_palette_mru_new (name);
pika_data_make_internal (palette, id);
pika_container_add (pika_data_factory_get_container (pika->palette_factory),
PIKA_OBJECT (palette));
g_object_unref (palette);
g_object_set_data (G_OBJECT (pika), id, palette);
return PIKA_PALETTE (palette);
}

39
app/core/pika-palettes.h Normal file
View File

@ -0,0 +1,39 @@
/* 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-2002 Spencer Kimball, Peter Mattis, and others
*
* pika-palettes.h
* Copyright (C) 2014 Michael Natterer <mitch@gimp.org>
*
* 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 __PIKA_PALETTES__
#define __PIKA_PALETTES__
void pika_palettes_init (Pika *pika);
void pika_palettes_load (Pika *pika);
void pika_palettes_save (Pika *pika);
PikaPalette * pika_palettes_get_color_history (Pika *pika);
void pika_palettes_add_color_history (Pika *pika,
const PikaRGB *color);
#endif /* __PIKA_PALETTES__ */

556
app/core/pika-parallel.cc Normal file
View File

@ -0,0 +1,556 @@
/* 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
*
* pika-parallel.c
* Copyright (C) 2018 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 <gio/gio.h>
#include <gegl.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef G_OS_WIN32
#include <windows.h>
#endif
extern "C"
{
#include "core-types.h"
#include "config/pikageglconfig.h"
#include "pika.h"
#include "pika-parallel.h"
#include "pikaasync.h"
#include "pikacancelable.h"
#define PIKA_PARALLEL_MAX_THREADS 64
#define PIKA_PARALLEL_RUN_ASYNC_MAX_THREADS 1
typedef struct
{
PikaAsync *async;
gint priority;
PikaRunAsyncFunc func;
gpointer user_data;
GDestroyNotify user_data_destroy_func;
} PikaParallelRunAsyncTask;
typedef struct
{
GThread *thread;
gboolean quit;
PikaAsync *current_async;
} PikaParallelRunAsyncThread;
/* local function prototypes */
static void pika_parallel_notify_num_processors (PikaGeglConfig *config);
static void pika_parallel_set_n_threads (gint n_threads,
gboolean finish_tasks);
static void pika_parallel_run_async_set_n_threads (gint n_threads,
gboolean finish_tasks);
static gpointer pika_parallel_run_async_thread_func (PikaParallelRunAsyncThread *thread);
static void pika_parallel_run_async_enqueue_task (PikaParallelRunAsyncTask *task);
static PikaParallelRunAsyncTask * pika_parallel_run_async_dequeue_task (void);
static gboolean pika_parallel_run_async_execute_task (PikaParallelRunAsyncTask *task);
static void pika_parallel_run_async_abort_task (PikaParallelRunAsyncTask *task);
static void pika_parallel_run_async_cancel (PikaAsync *async);
static void pika_parallel_run_async_waiting (PikaAsync *async);
/* local variables */
static gint pika_parallel_run_async_n_threads = 0;
static PikaParallelRunAsyncThread pika_parallel_run_async_threads[PIKA_PARALLEL_RUN_ASYNC_MAX_THREADS];
static GMutex pika_parallel_run_async_mutex;
static GCond pika_parallel_run_async_cond;
static GQueue pika_parallel_run_async_queue = G_QUEUE_INIT;
/* public functions */
void
pika_parallel_init (Pika *pika)
{
PikaGeglConfig *config;
g_return_if_fail (PIKA_IS_PIKA (pika));
config = PIKA_GEGL_CONFIG (pika->config);
g_signal_connect (config, "notify::num-processors",
G_CALLBACK (pika_parallel_notify_num_processors),
NULL);
pika_parallel_notify_num_processors (config);
}
void
pika_parallel_exit (Pika *pika)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
g_signal_handlers_disconnect_by_func (pika->config,
(gpointer) pika_parallel_notify_num_processors,
NULL);
/* stop all threads */
pika_parallel_set_n_threads (0, /* finish_tasks = */ FALSE);
}
PikaAsync *
pika_parallel_run_async (PikaRunAsyncFunc func,
gpointer user_data)
{
return pika_parallel_run_async_full (0, func, user_data, NULL);
}
PikaAsync *
pika_parallel_run_async_full (gint priority,
PikaRunAsyncFunc func,
gpointer user_data,
GDestroyNotify user_data_destroy_func)
{
PikaAsync *async;
PikaParallelRunAsyncTask *task;
g_return_val_if_fail (func != NULL, NULL);
async = pika_async_new ();
task = g_slice_new (PikaParallelRunAsyncTask);
task->async = PIKA_ASYNC (g_object_ref (async));
task->priority = priority;
task->func = func;
task->user_data = user_data;
task->user_data_destroy_func = user_data_destroy_func;
if (pika_parallel_run_async_n_threads > 0)
{
g_signal_connect_after (async, "cancel",
G_CALLBACK (pika_parallel_run_async_cancel),
NULL);
g_signal_connect_after (async, "waiting",
G_CALLBACK (pika_parallel_run_async_waiting),
NULL);
g_mutex_lock (&pika_parallel_run_async_mutex);
pika_parallel_run_async_enqueue_task (task);
g_cond_signal (&pika_parallel_run_async_cond);
g_mutex_unlock (&pika_parallel_run_async_mutex);
}
else
{
while (pika_parallel_run_async_execute_task (task));
}
return async;
}
PikaAsync *
pika_parallel_run_async_independent (PikaRunAsyncFunc func,
gpointer user_data)
{
return pika_parallel_run_async_independent_full (0, func, user_data);
}
PikaAsync *
pika_parallel_run_async_independent_full (gint priority,
PikaRunAsyncFunc func,
gpointer user_data)
{
PikaAsync *async;
PikaParallelRunAsyncTask *task;
GThread *thread;
g_return_val_if_fail (func != NULL, NULL);
async = pika_async_new ();
task = g_slice_new0 (PikaParallelRunAsyncTask);
task->async = PIKA_ASYNC (g_object_ref (async));
task->priority = priority;
task->func = func;
task->user_data = user_data;
thread = g_thread_new (
"async-ind",
[] (gpointer data) -> gpointer
{
PikaParallelRunAsyncTask *task = (PikaParallelRunAsyncTask *) data;
/* adjust the thread's priority */
#if defined (G_OS_WIN32)
if (task->priority < 0)
{
SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_ABOVE_NORMAL);
}
else if (task->priority > 0)
{
SetThreadPriority (GetCurrentThread (), THREAD_MODE_BACKGROUND_BEGIN);
}
#elif defined (HAVE_UNISTD_H) && defined (__gnu_linux__)
if (task->priority)
{
nice (task->priority);
}
#endif
while (pika_parallel_run_async_execute_task (task));
return NULL;
},
task);
pika_async_add_callback (async,
[] (PikaAsync *async,
gpointer thread)
{
g_thread_join ((GThread *) thread);
},
thread);
return async;
}
/* private functions */
static void
pika_parallel_notify_num_processors (PikaGeglConfig *config)
{
pika_parallel_set_n_threads (config->num_processors,
/* finish_tasks = */ TRUE);
}
static void
pika_parallel_set_n_threads (gint n_threads,
gboolean finish_tasks)
{
pika_parallel_run_async_set_n_threads (n_threads, finish_tasks);
}
static void
pika_parallel_run_async_set_n_threads (gint n_threads,
gboolean finish_tasks)
{
gint i;
n_threads = CLAMP (n_threads, 0, PIKA_PARALLEL_RUN_ASYNC_MAX_THREADS);
if (n_threads > pika_parallel_run_async_n_threads) /* need more threads */
{
for (i = pika_parallel_run_async_n_threads; i < n_threads; i++)
{
PikaParallelRunAsyncThread *thread =
&pika_parallel_run_async_threads[i];
thread->quit = FALSE;
thread->thread = g_thread_new (
"async",
(GThreadFunc) pika_parallel_run_async_thread_func,
thread);
}
}
else if (n_threads < pika_parallel_run_async_n_threads) /* need less threads */
{
g_mutex_lock (&pika_parallel_run_async_mutex);
for (i = n_threads; i < pika_parallel_run_async_n_threads; i++)
{
PikaParallelRunAsyncThread *thread =
&pika_parallel_run_async_threads[i];
thread->quit = TRUE;
if (thread->current_async && ! finish_tasks)
pika_cancelable_cancel (PIKA_CANCELABLE (thread->current_async));
}
g_cond_broadcast (&pika_parallel_run_async_cond);
g_mutex_unlock (&pika_parallel_run_async_mutex);
for (i = n_threads; i < pika_parallel_run_async_n_threads; i++)
{
PikaParallelRunAsyncThread *thread =
&pika_parallel_run_async_threads[i];
g_thread_join (thread->thread);
}
}
pika_parallel_run_async_n_threads = n_threads;
if (n_threads == 0)
{
PikaParallelRunAsyncTask *task;
/* finish remaining tasks */
while ((task = pika_parallel_run_async_dequeue_task ()))
{
if (finish_tasks)
while (pika_parallel_run_async_execute_task (task));
else
pika_parallel_run_async_abort_task (task);
}
}
}
static gpointer
pika_parallel_run_async_thread_func (PikaParallelRunAsyncThread *thread)
{
g_mutex_lock (&pika_parallel_run_async_mutex);
while (TRUE)
{
PikaParallelRunAsyncTask *task;
while (! thread->quit &&
(task = pika_parallel_run_async_dequeue_task ()))
{
gboolean resume;
thread->current_async = PIKA_ASYNC (g_object_ref (task->async));
do
{
g_mutex_unlock (&pika_parallel_run_async_mutex);
resume = pika_parallel_run_async_execute_task (task);
g_mutex_lock (&pika_parallel_run_async_mutex);
}
while (resume &&
(g_queue_is_empty (&pika_parallel_run_async_queue) ||
task->priority <
((PikaParallelRunAsyncTask *)
g_queue_peek_head (
&pika_parallel_run_async_queue))->priority));
g_clear_object (&thread->current_async);
if (resume)
pika_parallel_run_async_enqueue_task (task);
}
if (thread->quit)
break;
g_cond_wait (&pika_parallel_run_async_cond,
&pika_parallel_run_async_mutex);
}
g_mutex_unlock (&pika_parallel_run_async_mutex);
return NULL;
}
static void
pika_parallel_run_async_enqueue_task (PikaParallelRunAsyncTask *task)
{
GList *link;
GList *iter;
if (pika_async_is_canceled (task->async))
{
pika_parallel_run_async_abort_task (task);
return;
}
link = g_list_alloc ();
link->data = task;
g_object_set_data (G_OBJECT (task->async),
"pika-parallel-run-async-link", link);
for (iter = g_queue_peek_tail_link (&pika_parallel_run_async_queue);
iter;
iter = g_list_previous (iter))
{
PikaParallelRunAsyncTask *other_task =
(PikaParallelRunAsyncTask *) iter->data;
if (other_task->priority <= task->priority)
break;
}
if (iter)
{
link->prev = iter;
link->next = iter->next;
iter->next = link;
if (link->next)
link->next->prev = link;
else
pika_parallel_run_async_queue.tail = link;
pika_parallel_run_async_queue.length++;
}
else
{
g_queue_push_head_link (&pika_parallel_run_async_queue, link);
}
}
static PikaParallelRunAsyncTask *
pika_parallel_run_async_dequeue_task (void)
{
PikaParallelRunAsyncTask *task;
task = (PikaParallelRunAsyncTask *) g_queue_pop_head (
&pika_parallel_run_async_queue);
if (task)
{
g_object_set_data (G_OBJECT (task->async),
"pika-parallel-run-async-link", NULL);
}
return task;
}
static gboolean
pika_parallel_run_async_execute_task (PikaParallelRunAsyncTask *task)
{
if (pika_async_is_canceled (task->async))
{
pika_parallel_run_async_abort_task (task);
return FALSE;
}
task->func (task->async, task->user_data);
if (pika_async_is_stopped (task->async))
{
g_object_unref (task->async);
g_slice_free (PikaParallelRunAsyncTask, task);
return FALSE;
}
return TRUE;
}
static void
pika_parallel_run_async_abort_task (PikaParallelRunAsyncTask *task)
{
if (task->user_data && task->user_data_destroy_func)
task->user_data_destroy_func (task->user_data);
pika_async_abort (task->async);
g_object_unref (task->async);
g_slice_free (PikaParallelRunAsyncTask, task);
}
static void
pika_parallel_run_async_cancel (PikaAsync *async)
{
GList *link;
PikaParallelRunAsyncTask *task = NULL;
link = (GList *) g_object_get_data (G_OBJECT (async),
"pika-parallel-run-async-link");
if (! link)
return;
g_mutex_lock (&pika_parallel_run_async_mutex);
link = (GList *) g_object_get_data (G_OBJECT (async),
"pika-parallel-run-async-link");
if (link)
{
g_object_set_data (G_OBJECT (async),
"pika-parallel-run-async-link", NULL);
task = (PikaParallelRunAsyncTask *) link->data;
g_queue_delete_link (&pika_parallel_run_async_queue, link);
}
g_mutex_unlock (&pika_parallel_run_async_mutex);
if (task)
pika_parallel_run_async_abort_task (task);
}
static void
pika_parallel_run_async_waiting (PikaAsync *async)
{
GList *link;
link = (GList *) g_object_get_data (G_OBJECT (async),
"pika-parallel-run-async-link");
if (! link)
return;
g_mutex_lock (&pika_parallel_run_async_mutex);
link = (GList *) g_object_get_data (G_OBJECT (async),
"pika-parallel-run-async-link");
if (link)
{
PikaParallelRunAsyncTask *task = (PikaParallelRunAsyncTask *) link->data;
task->priority = G_MININT;
g_queue_unlink (&pika_parallel_run_async_queue, link);
g_queue_push_head_link (&pika_parallel_run_async_queue, link);
}
g_mutex_unlock (&pika_parallel_run_async_mutex);
}
} /* extern "C" */

161
app/core/pika-parallel.h Normal file
View File

@ -0,0 +1,161 @@
/* 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
*
* pika-parallel.h
* Copyright (C) 2018 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 __PIKA_PARALLEL_H__
#define __PIKA_PARALLEL_H__
void pika_parallel_init (Pika *pika);
void pika_parallel_exit (Pika *pika);
PikaAsync * pika_parallel_run_async (PikaRunAsyncFunc func,
gpointer user_data);
PikaAsync * pika_parallel_run_async_full (gint priority,
PikaRunAsyncFunc func,
gpointer user_data,
GDestroyNotify user_data_destroy_func);
PikaAsync * pika_parallel_run_async_independent (PikaRunAsyncFunc func,
gpointer user_data);
PikaAsync * pika_parallel_run_async_independent_full (gint priority,
PikaRunAsyncFunc func,
gpointer user_data);
#ifdef __cplusplus
extern "C++"
{
#include <new>
template <class RunAsyncFunc>
inline PikaAsync *
pika_parallel_run_async (RunAsyncFunc func)
{
RunAsyncFunc *func_copy = g_new (RunAsyncFunc, 1);
new (func_copy) RunAsyncFunc (func);
return pika_parallel_run_async_full (0,
[] (PikaAsync *async,
gpointer user_data)
{
RunAsyncFunc *func_copy =
(RunAsyncFunc *) user_data;
(*func_copy) (async);
func_copy->~RunAsyncFunc ();
g_free (func_copy);
},
func_copy,
[] (gpointer user_data)
{
RunAsyncFunc *func_copy =
(RunAsyncFunc *) user_data;
func_copy->~RunAsyncFunc ();
g_free (func_copy);
});
}
template <class RunAsyncFunc,
class DestroyFunc>
inline PikaAsync *
pika_parallel_run_async_full (gint priority,
RunAsyncFunc func,
DestroyFunc destroy_func)
{
typedef struct
{
RunAsyncFunc func;
DestroyFunc destroy_func;
} Funcs;
Funcs *funcs_copy = g_new (Funcs, 1);
new (funcs_copy) Funcs {func, destroy_func};
return pika_parallel_run_async_full (priority,
[] (PikaAsync *async,
gpointer user_data)
{
Funcs *funcs_copy =
(Funcs *) user_data;
funcs_copy->func (async);
funcs_copy->~Funcs ();
g_free (funcs_copy);
},
funcs_copy,
[] (gpointer user_data)
{
Funcs *funcs_copy =
(Funcs *) user_data;
funcs_copy->destroy_func ();
funcs_copy->~Funcs ();
g_free (funcs_copy);
});
}
template <class RunAsyncFunc>
inline PikaAsync *
pika_parallel_run_async_independent_full (gint priority,
RunAsyncFunc func)
{
RunAsyncFunc *func_copy = g_new (RunAsyncFunc, 1);
new (func_copy) RunAsyncFunc (func);
return pika_parallel_run_async_independent_full (priority,
[] (PikaAsync *async,
gpointer user_data)
{
RunAsyncFunc *func_copy =
(RunAsyncFunc *) user_data;
(*func_copy) (async);
func_copy->~RunAsyncFunc ();
g_free (func_copy);
},
func_copy);
}
template <class RunAsyncFunc>
inline PikaAsync *
pika_parallel_run_async_independent (RunAsyncFunc func)
{
return pika_parallel_run_async_independent_full (0, func);
}
}
#endif /* __cplusplus */
#endif /* __PIKA_PARALLEL_H__ */

169
app/core/pika-parasites.c Normal file
View File

@ -0,0 +1,169 @@
/* gimpparasite.c: Copyright 1998 Jay Cox <jaycox@gimp.org>
*
* 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 <gio/gio.h>
#include "libpikabase/pikabase.h"
#include "libpikaconfig/pikaconfig.h"
#include "core-types.h"
#include "pika.h"
#include "pika-parasites.h"
#include "pikaparasitelist.h"
gboolean
pika_parasite_validate (Pika *pika,
const PikaParasite *parasite,
GError **error)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), FALSE);
g_return_val_if_fail (parasite != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return TRUE;
}
void
pika_parasite_attach (Pika *pika,
const PikaParasite *parasite)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
g_return_if_fail (parasite != NULL);
pika_parasite_list_add (pika->parasites, parasite);
}
void
pika_parasite_detach (Pika *pika,
const gchar *name)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
g_return_if_fail (name != NULL);
pika_parasite_list_remove (pika->parasites, name);
}
const PikaParasite *
pika_parasite_find (Pika *pika,
const gchar *name)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
g_return_val_if_fail (name != NULL, NULL);
return pika_parasite_list_find (pika->parasites, name);
}
static void
list_func (const gchar *key,
PikaParasite *parasite,
gchar ***current)
{
*(*current)++ = g_strdup (key);
}
gchar **
pika_parasite_list (Pika *pika)
{
gint count;
gchar **list;
gchar **current;
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
count = pika_parasite_list_length (pika->parasites);
list = current = g_new0 (gchar *, count + 1);
pika_parasite_list_foreach (pika->parasites, (GHFunc) list_func, &current);
return list;
}
/* FIXME: this doesn't belong here */
void
pika_parasite_shift_parent (PikaParasite *parasite)
{
g_return_if_fail (parasite != NULL);
parasite->flags = (parasite->flags >> 8);
}
/* parasiterc functions */
void
pika_parasiterc_load (Pika *pika)
{
GFile *file;
GError *error = NULL;
g_return_if_fail (PIKA_IS_PIKA (pika));
file = pika_directory_file ("parasiterc", NULL);
if (pika->be_verbose)
g_print ("Parsing '%s'\n", pika_file_get_utf8_name (file));
if (! pika_config_deserialize_file (PIKA_CONFIG (pika->parasites),
file, NULL, &error))
{
if (error->code != PIKA_CONFIG_ERROR_OPEN_ENOENT)
pika_message_literal (pika, NULL, PIKA_MESSAGE_ERROR, error->message);
g_error_free (error);
}
g_object_unref (file);
}
void
pika_parasiterc_save (Pika *pika)
{
const gchar *header =
"PIKA parasiterc\n"
"\n"
"This file will be entirely rewritten each time you exit.";
const gchar *footer =
"end of parasiterc";
GFile *file;
GError *error = NULL;
g_return_if_fail (PIKA_IS_PIKA (pika));
g_return_if_fail (PIKA_IS_PARASITE_LIST (pika->parasites));
file = pika_directory_file ("parasiterc", NULL);
if (pika->be_verbose)
g_print ("Writing '%s'\n", pika_file_get_utf8_name (file));
if (! pika_config_serialize_to_file (PIKA_CONFIG (pika->parasites),
file,
header, footer, NULL,
&error))
{
pika_message_literal (pika, NULL, PIKA_MESSAGE_ERROR, error->message);
g_error_free (error);
}
g_object_unref (file);
}

40
app/core/pika-parasites.h Normal file
View File

@ -0,0 +1,40 @@
/* gimpparasite.h: Copyright 1998 Jay Cox <jaycox@gimp.org>
*
* 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 __PIKA_PARASITES_H__
#define __PIKA_PARASITES_H__
/* some wrappers to access pika->parasites, mainly for the PDB */
gboolean pika_parasite_validate (Pika *pika,
const PikaParasite *parasite,
GError **error);
void pika_parasite_attach (Pika *pika,
const PikaParasite *parasite);
void pika_parasite_detach (Pika *pika,
const gchar *name);
const PikaParasite * pika_parasite_find (Pika *pika,
const gchar *name);
gchar ** pika_parasite_list (Pika *pika);
void pika_parasite_shift_parent (PikaParasite *parasite);
void pika_parasiterc_load (Pika *pika);
void pika_parasiterc_save (Pika *pika);
#endif /* __PIKA_PARASITES_H__ */

254
app/core/pika-spawn.c Normal file
View File

@ -0,0 +1,254 @@
/* 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
*
* pika-spawn.c
* Copyright (C) 2018 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 <glib-object.h>
#ifdef HAVE_VFORK
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef G_OS_WIN32
#include <windows.h>
#include <io.h>
#endif
#include "core-types.h"
#include "pika-spawn.h"
#include "pika-intl.h"
#ifdef HAVE_VFORK
/* copied from glib */
static gint
exec_err_to_g_error (gint en)
{
switch (en)
{
#ifdef EACCES
case EACCES:
return G_SPAWN_ERROR_ACCES;
break;
#endif
#ifdef EPERM
case EPERM:
return G_SPAWN_ERROR_PERM;
break;
#endif
#ifdef E2BIG
case E2BIG:
return G_SPAWN_ERROR_TOO_BIG;
break;
#endif
#ifdef ENOEXEC
case ENOEXEC:
return G_SPAWN_ERROR_NOEXEC;
break;
#endif
#ifdef ENAMETOOLONG
case ENAMETOOLONG:
return G_SPAWN_ERROR_NAMETOOLONG;
break;
#endif
#ifdef ENOENT
case ENOENT:
return G_SPAWN_ERROR_NOENT;
break;
#endif
#ifdef ENOMEM
case ENOMEM:
return G_SPAWN_ERROR_NOMEM;
break;
#endif
#ifdef ENOTDIR
case ENOTDIR:
return G_SPAWN_ERROR_NOTDIR;
break;
#endif
#ifdef ELOOP
case ELOOP:
return G_SPAWN_ERROR_LOOP;
break;
#endif
#ifdef ETXTBUSY
case ETXTBUSY:
return G_SPAWN_ERROR_TXTBUSY;
break;
#endif
#ifdef EIO
case EIO:
return G_SPAWN_ERROR_IO;
break;
#endif
#ifdef ENFILE
case ENFILE:
return G_SPAWN_ERROR_NFILE;
break;
#endif
#ifdef EMFILE
case EMFILE:
return G_SPAWN_ERROR_MFILE;
break;
#endif
#ifdef EINVAL
case EINVAL:
return G_SPAWN_ERROR_INVAL;
break;
#endif
#ifdef EISDIR
case EISDIR:
return G_SPAWN_ERROR_ISDIR;
break;
#endif
#ifdef ELIBBAD
case ELIBBAD:
return G_SPAWN_ERROR_LIBBAD;
break;
#endif
default:
return G_SPAWN_ERROR_FAILED;
break;
}
}
#endif /* HAVE_VFORK */
gboolean
pika_spawn_async (gchar **argv,
gchar **envp,
GSpawnFlags flags,
GPid *child_pid,
GError **error)
{
g_return_val_if_fail (argv != NULL, FALSE);
g_return_val_if_fail (argv[0] != NULL, FALSE);
#ifdef HAVE_VFORK
if (flags == (G_SPAWN_LEAVE_DESCRIPTORS_OPEN |
G_SPAWN_DO_NOT_REAP_CHILD |
G_SPAWN_CHILD_INHERITS_STDIN))
{
pid_t pid;
pid = vfork ();
if (pid < 0)
{
gint errsv = errno;
g_set_error (error,
G_SPAWN_ERROR,
G_SPAWN_ERROR_FORK,
_("Failed to fork (%s)"),
g_strerror (errsv));
return FALSE;
}
else if (pid == 0)
{
if (envp)
execve (argv[0], argv, envp);
else
execv (argv[0], argv);
_exit (errno);
}
else
{
int status = -1;
pid_t result;
result = waitpid (pid, &status, WNOHANG);
if (result)
{
if (result < 0)
{
g_warning ("waitpid() should not fail in "
"pika_spawn_async()");
}
if (WIFEXITED (status))
status = WEXITSTATUS (status);
else
status = -1;
g_set_error (error,
G_SPAWN_ERROR,
exec_err_to_g_error (status),
_("Failed to execute child process “%s” (%s)"),
argv[0],
g_strerror (status));
return FALSE;
}
if (child_pid) *child_pid = pid;
return TRUE;
}
}
#endif /* HAVE_VFORK */
return g_spawn_async (NULL, argv, envp, flags, NULL, NULL, child_pid, error);
}
void
pika_spawn_set_cloexec (gint fd)
{
#if defined (G_OS_WIN32)
SetHandleInformation ((HANDLE) _get_osfhandle (fd), HANDLE_FLAG_INHERIT, 0);
#elif defined (HAVE_FCNTL_H)
fcntl (fd, F_SETFD, fcntl (fd, F_GETFD, 0) | FD_CLOEXEC);
#elif defined (__GNUC__)
#warning pika_spawn_set_cloexec() is not implemented for the target platform
#endif
}

38
app/core/pika-spawn.h Normal file
View File

@ -0,0 +1,38 @@
/* 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
*
* pika-spawn.h
* Copyright (C) 2018 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 __PIKA_SPAWN_H__
#define __PIKA_SPAWN_H__
gboolean pika_spawn_async (gchar **argv,
gchar **envp,
GSpawnFlags flags,
GPid *child_pid,
GError **error);
void pika_spawn_set_cloexec (gint fd);
#endif /* __PIKA_SPAWN_H__ */

275
app/core/pika-tags.c Normal file
View File

@ -0,0 +1,275 @@
/* 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
*
* pika-tags.c
* Copyright (C) 2009 Aurimas Juška <aurisj@svn.gnome.org>
*
* 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 <gio/gio.h>
#include <gegl.h>
#include "libpikabase/pikabase.h"
#include "libpikamath/pikamath.h"
#include "core-types.h"
#include "config/pikaxmlparser.h"
#include "pika-utils.h"
#include "pika-tags.h"
#include "pika-intl.h"
#define PIKA_TAGS_FILE "tags.xml"
typedef struct
{
const gchar *locale;
GString *buf;
gboolean locale_matches;
} PikaTagsInstaller;
static void pika_tags_installer_load_start_element (GMarkupParseContext *context,
const gchar *element_name,
const gchar **attribute_names,
const gchar **attribute_values,
gpointer user_data,
GError **error);
static void pika_tags_installer_load_end_element (GMarkupParseContext *context,
const gchar *element_name,
gpointer user_data,
GError **error);
static void pika_tags_installer_load_text (GMarkupParseContext *context,
const gchar *text,
gsize text_len,
gpointer user_data,
GError **error);
static const gchar* attribute_name_to_value (const gchar **attribute_names,
const gchar **attribute_values,
const gchar *name);
gboolean
pika_tags_user_install (void)
{
GFile *file;
GOutputStream *output;
GMarkupParser markup_parser;
PikaXmlParser *xml_parser;
const char *tags_locale;
PikaTagsInstaller tags_installer = { 0, };
GError *error = NULL;
gboolean result = TRUE;
/* This is a special string to specify the language identifier to
* look for in the pika-tags-default.xml file. Please translate the
* C in it according to the name of the po file used for
* pika-tags-default.xml. E.g. lithuanian for the translation,
* that would be "tags-locale:lt".
*/
tags_locale = _("tags-locale:C");
if (g_str_has_prefix (tags_locale, "tags-locale:"))
{
tags_locale += strlen ("tags-locale:");
if (*tags_locale && *tags_locale != 'C')
tags_installer.locale = tags_locale;
}
else
{
g_warning ("Wrong translation for 'tags-locale:', fix the translation!");
}
tags_installer.buf = g_string_new (NULL);
g_string_append (tags_installer.buf, "<?xml version='1.0' encoding='UTF-8'?>\n");
g_string_append (tags_installer.buf, "<tags>\n");
markup_parser.start_element = pika_tags_installer_load_start_element;
markup_parser.end_element = pika_tags_installer_load_end_element;
markup_parser.text = pika_tags_installer_load_text;
markup_parser.passthrough = NULL;
markup_parser.error = NULL;
xml_parser = pika_xml_parser_new (&markup_parser, &tags_installer);
file = pika_data_directory_file ("tags", "pika-tags-default.xml", NULL);
result = pika_xml_parser_parse_gfile (xml_parser, file, &error);
g_object_unref (file);
pika_xml_parser_free (xml_parser);
if (! result)
{
g_string_free (tags_installer.buf, TRUE);
return FALSE;
}
g_string_append (tags_installer.buf, "\n</tags>\n");
file = pika_directory_file (PIKA_TAGS_FILE, NULL);
output = G_OUTPUT_STREAM (g_file_replace (file,
NULL, FALSE, G_FILE_CREATE_NONE,
NULL, &error));
if (! output)
{
g_printerr ("%s\n", error->message);
result = FALSE;
}
else if (! g_output_stream_write_all (output,
tags_installer.buf->str,
tags_installer.buf->len,
NULL, NULL, &error))
{
GCancellable *cancellable = g_cancellable_new ();
g_printerr (_("Error writing '%s': %s"),
pika_file_get_utf8_name (file), error->message);
result = FALSE;
/* Cancel the overwrite initiated by g_file_replace(). */
g_cancellable_cancel (cancellable);
g_output_stream_close (output, cancellable, NULL);
g_object_unref (cancellable);
}
else if (! g_output_stream_close (output, NULL, &error))
{
g_printerr (_("Error closing '%s': %s"),
pika_file_get_utf8_name (file), error->message);
result = FALSE;
}
if (output)
g_object_unref (output);
g_clear_error (&error);
g_object_unref (file);
g_string_free (tags_installer.buf, TRUE);
return result;
}
static void
pika_tags_installer_load_start_element (GMarkupParseContext *context,
const gchar *element_name,
const gchar **attribute_names,
const gchar **attribute_values,
gpointer user_data,
GError **error)
{
PikaTagsInstaller *tags_installer = user_data;
if (! strcmp (element_name, "resource"))
{
g_string_append_printf (tags_installer->buf, "\n <resource");
while (*attribute_names)
{
g_string_append_printf (tags_installer->buf, " %s=\"%s\"",
*attribute_names, *attribute_values);
attribute_names++;
attribute_values++;
}
g_string_append_printf (tags_installer->buf, ">\n");
}
else if (! strcmp (element_name, "thetag"))
{
const char *current_locale;
current_locale = attribute_name_to_value (attribute_names, attribute_values,
"xml:lang");
if (current_locale && tags_installer->locale)
{
tags_installer->locale_matches = ! strcmp (current_locale,
tags_installer->locale);
}
else
{
tags_installer->locale_matches = (current_locale ==
tags_installer->locale);
}
}
}
static void
pika_tags_installer_load_end_element (GMarkupParseContext *context,
const gchar *element_name,
gpointer user_data,
GError **error)
{
PikaTagsInstaller *tags_installer = user_data;
if (strcmp (element_name, "resource") == 0)
{
g_string_append (tags_installer->buf, " </resource>\n");
}
}
static void
pika_tags_installer_load_text (GMarkupParseContext *context,
const gchar *text,
gsize text_len,
gpointer user_data,
GError **error)
{
PikaTagsInstaller *tags_installer = user_data;
const gchar *current_element;
gchar *tag_string;
current_element = g_markup_parse_context_get_element (context);
if (tags_installer->locale_matches &&
current_element &&
strcmp (current_element, "thetag") == 0)
{
tag_string = g_markup_escape_text (text, text_len);
g_string_append_printf (tags_installer->buf, " <tag>%s</tag>\n",
tag_string);
g_free (tag_string);
}
}
static const gchar *
attribute_name_to_value (const gchar **attribute_names,
const gchar **attribute_values,
const gchar *name)
{
while (*attribute_names)
{
if (! strcmp (*attribute_names, name))
{
return *attribute_values;
}
attribute_names++;
attribute_values++;
}
return NULL;
}

29
app/core/pika-tags.h Normal file
View File

@ -0,0 +1,29 @@
/* 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-1997 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/>.
*/
#ifndef __PIKA_TAGS_H__
#define __PIKA_TAGS_H__
gboolean pika_tags_user_install (void);
#endif /* __PIKA_TAGS_H__ */

228
app/core/pika-templates.c Normal file
View File

@ -0,0 +1,228 @@
/* 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-1997 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 "libpikaconfig/pikaconfig.h"
#include "core-types.h"
#include "pika.h"
#include "pika-templates.h"
#include "pikalist.h"
#include "pikatemplate.h"
/* functions to load and save the pika templates files */
void
pika_templates_load (Pika *pika)
{
GFile *file;
GError *error = NULL;
g_return_if_fail (PIKA_IS_PIKA (pika));
g_return_if_fail (PIKA_IS_LIST (pika->templates));
file = pika_directory_file ("templaterc", NULL);
if (pika->be_verbose)
g_print ("Parsing '%s'\n", pika_file_get_utf8_name (file));
if (! pika_config_deserialize_file (PIKA_CONFIG (pika->templates),
file, NULL, &error))
{
if (error->code == PIKA_CONFIG_ERROR_OPEN_ENOENT)
{
g_clear_error (&error);
g_object_unref (file);
if (g_getenv ("PIKA_TESTING_ABS_TOP_SRCDIR"))
{
gchar *path;
path = g_build_filename (g_getenv ("PIKA_TESTING_ABS_TOP_SRCDIR"),
"etc", "templaterc", NULL);
file = g_file_new_for_path (path);
g_free (path);
}
else
{
file = pika_sysconf_directory_file ("templaterc", NULL);
}
if (! pika_config_deserialize_file (PIKA_CONFIG (pika->templates),
file, NULL, &error))
{
pika_message_literal (pika, NULL, PIKA_MESSAGE_ERROR,
error->message);
}
}
else
{
pika_message_literal (pika, NULL, PIKA_MESSAGE_ERROR, error->message);
}
g_clear_error (&error);
}
pika_list_reverse (PIKA_LIST (pika->templates));
g_object_unref (file);
}
void
pika_templates_save (Pika *pika)
{
const gchar *header =
"PIKA templaterc\n"
"\n"
"This file will be entirely rewritten each time you exit.";
const gchar *footer =
"end of templaterc";
GFile *file;
GError *error = NULL;
g_return_if_fail (PIKA_IS_PIKA (pika));
g_return_if_fail (PIKA_IS_LIST (pika->templates));
file = pika_directory_file ("templaterc", NULL);
if (pika->be_verbose)
g_print ("Writing '%s'\n", pika_file_get_utf8_name (file));
if (! pika_config_serialize_to_file (PIKA_CONFIG (pika->templates),
file,
header, footer, NULL,
&error))
{
pika_message_literal (pika, NULL, PIKA_MESSAGE_ERROR, error->message);
g_error_free (error);
}
g_object_unref (file);
}
/* just like pika_list_get_child_by_name() but matches case-insensitive
* and dpi/ppi-insensitive
*/
static PikaObject *
pika_templates_migrate_get_child_by_name (PikaContainer *container,
const gchar *name)
{
PikaList *list = PIKA_LIST (container);
PikaObject *retval = NULL;
GList *glist;
for (glist = list->queue->head; glist; glist = g_list_next (glist))
{
PikaObject *object = glist->data;
gchar *str1 = g_ascii_strdown (pika_object_get_name (object), -1);
gchar *str2 = g_ascii_strdown (name, -1);
if (! strcmp (str1, str2))
{
retval = object;
}
else
{
gchar *dpi = strstr (str1, "dpi");
if (dpi)
{
memcpy (dpi, "ppi", 3);
g_print ("replaced: %s\n", str1);
if (! strcmp (str1, str2))
retval = object;
}
}
g_free (str1);
g_free (str2);
}
return retval;
}
/**
* pika_templates_migrate:
* @olddir: the old user directory
*
* Migrating the templaterc from PIKA 2.0 to PIKA 2.2 needs this special
* hack since we changed the way that units are handled. This function
* merges the user's templaterc with the systemwide templaterc. The goal
* is to replace the unit for a couple of default templates with "pixels".
**/
void
pika_templates_migrate (const gchar *olddir)
{
PikaContainer *templates = pika_list_new (PIKA_TYPE_TEMPLATE, TRUE);
GFile *file = pika_directory_file ("templaterc", NULL);
if (pika_config_deserialize_file (PIKA_CONFIG (templates), file,
NULL, NULL))
{
GFile *sysconf_file;
sysconf_file = pika_sysconf_directory_file ("templaterc", NULL);
if (olddir && (strstr (olddir, "2.0") || strstr (olddir, "2.2")))
{
/* We changed the spelling of a couple of template names:
*
* - from upper to lower case between 2.0 and 2.2
* - from "dpi" to "ppi" between 2.2 and 2.4
*/
PikaContainerClass *class = PIKA_CONTAINER_GET_CLASS (templates);
gpointer func = class->get_child_by_name;
class->get_child_by_name = pika_templates_migrate_get_child_by_name;
pika_config_deserialize_file (PIKA_CONFIG (templates),
sysconf_file, NULL, NULL);
class->get_child_by_name = func;
}
else
{
pika_config_deserialize_file (PIKA_CONFIG (templates),
sysconf_file, NULL, NULL);
}
g_object_unref (sysconf_file);
pika_list_reverse (PIKA_LIST (templates));
pika_config_serialize_to_file (PIKA_CONFIG (templates), file,
NULL, NULL, NULL, NULL);
}
g_object_unref (file);
}

32
app/core/pika-templates.h Normal file
View File

@ -0,0 +1,32 @@
/* 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-1997 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/>.
*/
#ifndef __PIKA_TEMPLATES_H__
#define __PIKA_TEMPLATES_H__
void pika_templates_load (Pika *pika);
void pika_templates_save (Pika *pika);
void pika_templates_migrate (const gchar *olddir);
#endif /* __PIKA_TEMPLATES_H__ */

View File

@ -0,0 +1,363 @@
/* 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
*
* pika-3d-transform-utils.c
* Copyright (C) 2019 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 <glib-object.h>
#include "libpikamath/pikamath.h"
#include "core-types.h"
#include "pika-transform-3d-utils.h"
#define MIN_FOCAL_LENGTH 0.01
gdouble
pika_transform_3d_angle_of_view_to_focal_length (gdouble angle_of_view,
gdouble width,
gdouble height)
{
return MAX (width, height) / (2.0 * tan (angle_of_view / 2.0));
}
gdouble
pika_transform_3d_focal_length_to_angle_of_view (gdouble focal_length,
gdouble width,
gdouble height)
{
return 2.0 * atan (MAX (width, height) / (2.0 * focal_length));
}
gint
pika_transform_3d_permutation_to_rotation_order (const gint permutation[3])
{
if (permutation[1] == (permutation[0] + 1) % 3)
return permutation[0] << 1;
else
return (permutation[2] << 1) + 1;
}
void
pika_transform_3d_rotation_order_to_permutation (gint rotation_order,
gint permutation[3])
{
gboolean reverse = rotation_order & 1;
gint shift = rotation_order >> 1;
gint i;
for (i = 0; i < 3; i++)
permutation[reverse ? 2 - i : i] = (i + shift) % 3;
}
gint
pika_transform_3d_rotation_order_reverse (gint rotation_order)
{
return rotation_order ^ 1;
}
void
pika_transform_3d_vector3_rotate (PikaVector3 *vector,
const PikaVector3 *axis)
{
PikaVector3 normal;
PikaVector3 proj;
PikaVector3 u, v;
gdouble angle;
angle = pika_vector3_length (axis);
if (angle == 0.0)
return;
normal = pika_vector3_mul_val (*axis, 1.0 / angle);
proj = pika_vector3_mul_val (normal,
pika_vector3_inner_product_val (*vector,
normal));
u = pika_vector3_sub_val (*vector, proj);
v = pika_vector3_cross_product_val (u, normal);
pika_vector3_mul (&u, cos (angle));
pika_vector3_mul (&v, sin (angle));
*vector = proj;
pika_vector3_add (vector, vector, &u);
pika_vector3_add (vector, vector, &v);
}
PikaVector3
pika_transform_3d_vector3_rotate_val (PikaVector3 vector,
PikaVector3 axis)
{
pika_transform_3d_vector3_rotate (&vector, &axis);
return vector;
}
void
pika_transform_3d_matrix3_to_matrix4 (const PikaMatrix3 *matrix3,
PikaMatrix4 *matrix4,
gint axis)
{
gint i, j;
gint k, l;
for (i = 0; i < 4; i++)
{
if (i == axis)
{
matrix4->coeff[i][i] = 1.0;
}
else
{
matrix4->coeff[axis][i] = 0.0;
matrix4->coeff[i][axis] = 0.0;
}
}
for (i = 0; i < 3; i++)
{
k = i + (i >= axis);
for (j = 0; j < 3; j++)
{
l = j + (j >= axis);
matrix4->coeff[k][l] = matrix3->coeff[i][j];
}
}
}
void
pika_transform_3d_matrix4_to_matrix3 (const PikaMatrix4 *matrix4,
PikaMatrix3 *matrix3,
gint axis)
{
gint i, j;
gint k, l;
for (i = 0; i < 3; i++)
{
k = i + (i >= axis);
for (j = 0; j < 3; j++)
{
l = j + (j >= axis);
matrix3->coeff[i][j] = matrix4->coeff[k][l];
}
}
}
void
pika_transform_3d_matrix4_translate (PikaMatrix4 *matrix,
gdouble x,
gdouble y,
gdouble z)
{
gint i;
for (i = 0; i < 4; i++)
matrix->coeff[0][i] += x * matrix->coeff[3][i];
for (i = 0; i < 4; i++)
matrix->coeff[1][i] += y * matrix->coeff[3][i];
for (i = 0; i < 4; i++)
matrix->coeff[2][i] += z * matrix->coeff[3][i];
}
void
pika_transform_3d_matrix4_rotate (PikaMatrix4 *matrix,
const PikaVector3 *axis)
{
PikaMatrix4 rotation;
PikaVector3 v;
v = pika_transform_3d_vector3_rotate_val ((PikaVector3) {1.0, 0.0, 0.0},
*axis);
rotation.coeff[0][0] = v.x;
rotation.coeff[1][0] = v.y;
rotation.coeff[2][0] = v.z;
rotation.coeff[3][0] = 0.0;
v = pika_transform_3d_vector3_rotate_val ((PikaVector3) {0.0, 1.0, 0.0},
*axis);
rotation.coeff[0][1] = v.x;
rotation.coeff[1][1] = v.y;
rotation.coeff[2][1] = v.z;
rotation.coeff[3][1] = 0.0;
v = pika_transform_3d_vector3_rotate_val ((PikaVector3) {0.0, 0.0, 1.0},
*axis);
rotation.coeff[0][2] = v.x;
rotation.coeff[1][2] = v.y;
rotation.coeff[2][2] = v.z;
rotation.coeff[3][2] = 0.0;
rotation.coeff[0][3] = 0.0;
rotation.coeff[1][3] = 0.0;
rotation.coeff[2][3] = 0.0;
rotation.coeff[3][3] = 1.0;
pika_matrix4_mult (&rotation, matrix);
}
void
pika_transform_3d_matrix4_rotate_standard (PikaMatrix4 *matrix,
gint axis,
gdouble angle)
{
gdouble v[3] = {};
v[axis] = angle;
pika_transform_3d_matrix4_rotate (matrix, &(PikaVector3) {v[0], v[1], v[2]});
}
void
pika_transform_3d_matrix4_rotate_euler (PikaMatrix4 *matrix,
gint rotation_order,
gdouble angle_x,
gdouble angle_y,
gdouble angle_z,
gdouble pivot_x,
gdouble pivot_y,
gdouble pivot_z)
{
const gdouble angles[3] = {angle_x, angle_y, angle_z};
gint permutation[3];
gint i;
pika_transform_3d_rotation_order_to_permutation (rotation_order, permutation);
pika_transform_3d_matrix4_translate (matrix, -pivot_x, -pivot_y, -pivot_z);
for (i = 0; i < 3; i++)
{
pika_transform_3d_matrix4_rotate_standard (matrix,
permutation[i],
angles[permutation[i]]);
}
pika_transform_3d_matrix4_translate (matrix, +pivot_x, +pivot_y, +pivot_z);
}
void
pika_transform_3d_matrix4_rotate_euler_decompose (PikaMatrix4 *matrix,
gint rotation_order,
gdouble *angle_x,
gdouble *angle_y,
gdouble *angle_z)
{
PikaMatrix4 m = *matrix;
gdouble * const angles[3] = {angle_x, angle_y, angle_z};
gint permutation[3];
gboolean forward;
pika_transform_3d_rotation_order_to_permutation (rotation_order, permutation);
forward = permutation[1] == (permutation[0] + 1) % 3;
*angles[permutation[2]] = atan2 (m.coeff[permutation[1]][permutation[0]],
m.coeff[permutation[0]][permutation[0]]);
if (forward)
*angles[permutation[2]] *= -1.0;
pika_transform_3d_matrix4_rotate_standard (&m,
permutation[2],
-*angles[permutation[2]]);
*angles[permutation[1]] = atan2 (m.coeff[permutation[2]][permutation[0]],
m.coeff[permutation[0]][permutation[0]]);
if (! forward)
*angles[permutation[1]] *= -1.0;
pika_transform_3d_matrix4_rotate_standard (&m,
permutation[1],
-*angles[permutation[1]]);
*angles[permutation[0]] = atan2 (m.coeff[permutation[2]][permutation[1]],
m.coeff[permutation[1]][permutation[1]]);
if (forward)
*angles[permutation[0]] *= -1.0;
}
void
pika_transform_3d_matrix4_perspective (PikaMatrix4 *matrix,
gdouble camera_x,
gdouble camera_y,
gdouble camera_z)
{
gint i;
camera_z = MIN (camera_z, -MIN_FOCAL_LENGTH);
pika_transform_3d_matrix4_translate (matrix, -camera_x, -camera_y, 0.0);
for (i = 0; i < 4; i++)
matrix->coeff[3][i] += matrix->coeff[2][i] / -camera_z;
pika_transform_3d_matrix4_translate (matrix, +camera_x, +camera_y, 0.0);
}
void
pika_transform_3d_matrix (PikaMatrix3 *matrix,
gdouble camera_x,
gdouble camera_y,
gdouble camera_z,
gdouble offset_x,
gdouble offset_y,
gdouble offset_z,
gint rotation_order,
gdouble angle_x,
gdouble angle_y,
gdouble angle_z,
gdouble pivot_x,
gdouble pivot_y,
gdouble pivot_z)
{
PikaMatrix4 m;
pika_matrix4_identity (&m);
pika_transform_3d_matrix4_rotate_euler (&m,
rotation_order,
angle_x, angle_y, angle_z,
pivot_x, pivot_y, pivot_z);
pika_transform_3d_matrix4_translate (&m, offset_x, offset_y, offset_z);
pika_transform_3d_matrix4_perspective (&m, camera_x, camera_y, camera_z);
pika_transform_3d_matrix4_to_matrix3 (&m, matrix, 2);
}

View File

@ -0,0 +1,99 @@
/* 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
*
* pika-3d-transform-utils.h
* Copyright (C) 2019 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 __PIKA_TRANSFORM_3D_UTILS_H__
#define __PIKA_TRANSFORM_3D_UTILS_H__
gdouble pika_transform_3d_angle_of_view_to_focal_length (gdouble angle_of_view,
gdouble width,
gdouble height);
gdouble pika_transform_3d_focal_length_to_angle_of_view (gdouble focal_length,
gdouble width,
gdouble height);
gint pika_transform_3d_permutation_to_rotation_order (const gint permutation[3]);
void pika_transform_3d_rotation_order_to_permutation (gint rotation_order,
gint permutation[3]);
gint pika_transform_3d_rotation_order_reverse (gint rotation_order);
void pika_transform_3d_vector3_rotate (PikaVector3 *vector,
const PikaVector3 *axis);
PikaVector3 pika_transform_3d_vector3_rotate_val (PikaVector3 vector,
PikaVector3 axis);
void pika_transform_3d_matrix3_to_matrix4 (const PikaMatrix3 *matrix3,
PikaMatrix4 *matrix4,
gint axis);
void pika_transform_3d_matrix4_to_matrix3 (const PikaMatrix4 *matrix4,
PikaMatrix3 *matrix3,
gint axis);
void pika_transform_3d_matrix4_translate (PikaMatrix4 *matrix,
gdouble x,
gdouble y,
gdouble z);
void pika_transform_3d_matrix4_rotate (PikaMatrix4 *matrix,
const PikaVector3 *axis);
void pika_transform_3d_matrix4_rotate_standard (PikaMatrix4 *matrix,
gint axis,
gdouble angle);
void pika_transform_3d_matrix4_rotate_euler (PikaMatrix4 *matrix,
gint rotation_order,
gdouble angle_x,
gdouble angle_y,
gdouble angle_z,
gdouble pivot_x,
gdouble pivot_y,
gdouble pivot_z);
void pika_transform_3d_matrix4_rotate_euler_decompose (PikaMatrix4 *matrix,
gint rotation_order,
gdouble *angle_x,
gdouble *angle_y,
gdouble *angle_z);
void pika_transform_3d_matrix4_perspective (PikaMatrix4 *matrix,
gdouble camera_x,
gdouble camera_y,
gdouble camera_z);
void pika_transform_3d_matrix (PikaMatrix3 *matrix,
gdouble camera_x,
gdouble camera_y,
gdouble camera_z,
gdouble offset_x,
gdouble offset_y,
gdouble offset_z,
gint rotation_order,
gdouble angle_x,
gdouble angle_y,
gdouble angle_z,
gdouble pivot_x,
gdouble pivot_y,
gdouble pivot_z);
#endif /* __PIKA_TRANSFORM_3D_UTILS_H__ */

View File

@ -0,0 +1,835 @@
/* 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-2001 Spencer Kimball, Peter Mattis, and others
*
* 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 <gio/gio.h>
#include <gegl.h>
#include "libpikamath/pikamath.h"
#include "core-types.h"
#include "pika-transform-resize.h"
#include "pika-transform-utils.h"
#include "pika-utils.h"
#define EPSILON 0.00000001
typedef struct
{
PikaVector2 a, b, c, d;
gdouble area;
gdouble aspect;
} Rectangle;
static void pika_transform_resize_adjust (const PikaVector2 *points,
gint n_points,
gint *x1,
gint *y1,
gint *x2,
gint *y2);
static void pika_transform_resize_crop (const PikaVector2 *points,
gint n_points,
gdouble aspect,
gint *x1,
gint *y1,
gint *x2,
gint *y2);
static void add_rectangle (const PikaVector2 *points,
gint n_points,
Rectangle *r,
PikaVector2 a,
PikaVector2 b,
PikaVector2 c,
PikaVector2 d);
static gboolean intersect (PikaVector2 a,
PikaVector2 b,
PikaVector2 c,
PikaVector2 d,
PikaVector2 *i);
static gboolean intersect_x (PikaVector2 a,
PikaVector2 b,
PikaVector2 c,
PikaVector2 *i);
static gboolean intersect_y (PikaVector2 a,
PikaVector2 b,
PikaVector2 c,
PikaVector2 *i);
static gboolean in_poly (const PikaVector2 *points,
gint n_points,
PikaVector2 p);
static gboolean point_on_border (const PikaVector2 *points,
gint n_points,
PikaVector2 p);
static void find_two_point_rectangle (Rectangle *r,
const PikaVector2 *points,
gint n_points,
gint p);
static void find_three_point_rectangle_corner (Rectangle *r,
const PikaVector2 *points,
gint n_points,
gint p);
static void find_three_point_rectangle (Rectangle *r,
const PikaVector2 *points,
gint n_points,
gint p);
static void find_three_point_rectangle_triangle (Rectangle *r,
const PikaVector2 *points,
gint n_points,
gint p);
static void find_maximum_aspect_rectangle (Rectangle *r,
const PikaVector2 *points,
gint n_points,
gint p);
/*
* This function wants to be passed the inverse transformation matrix!!
*/
gboolean
pika_transform_resize_boundary (const PikaMatrix3 *inv,
PikaTransformResize resize,
gdouble u1,
gdouble v1,
gdouble u2,
gdouble v2,
gint *x1,
gint *y1,
gint *x2,
gint *y2)
{
PikaVector2 bounds[4];
PikaVector2 points[5];
gint n_points;
gboolean valid;
gint i;
g_return_val_if_fail (inv != NULL, FALSE);
/* initialize with the original boundary */
*x1 = floor (u1);
*y1 = floor (v1);
*x2 = ceil (u2);
*y2 = ceil (v2);
/* if clipping then just return the original rectangle */
if (resize == PIKA_TRANSFORM_RESIZE_CLIP)
return TRUE;
bounds[0] = (PikaVector2) { u1, v1 };
bounds[1] = (PikaVector2) { u2, v1 };
bounds[2] = (PikaVector2) { u2, v2 };
bounds[3] = (PikaVector2) { u1, v2 };
pika_transform_polygon (inv, bounds, 4, TRUE,
points, &n_points);
valid = (n_points >= 2);
/* check if the transformation matrix is valid at all */
for (i = 0; i < n_points && valid; i++)
valid = (isfinite (points[i].x) && isfinite (points[i].y));
if (! valid)
{
/* since there is no sensible way to deal with this, just do the same as
* with PIKA_TRANSFORM_RESIZE_CLIP: return
*/
return FALSE;
}
switch (resize)
{
case PIKA_TRANSFORM_RESIZE_ADJUST:
/* return smallest rectangle (with sides parallel to x- and y-axis)
* that surrounds the new points */
pika_transform_resize_adjust (points, n_points,
x1, y1, x2, y2);
break;
case PIKA_TRANSFORM_RESIZE_CROP:
pika_transform_resize_crop (points, n_points,
0.0,
x1, y1, x2, y2);
break;
case PIKA_TRANSFORM_RESIZE_CROP_WITH_ASPECT:
pika_transform_resize_crop (points, n_points,
(u2 - u1) / (v2 - v1),
x1, y1, x2, y2);
break;
case PIKA_TRANSFORM_RESIZE_CLIP:
/* Remove warning about not handling all enum values. We handle
* this case in the beginning of the function
*/
break;
}
/* ensure that resulting rectangle has at least area 1 */
if (*x1 == *x2)
(*x2)++;
if (*y1 == *y2)
(*y2)++;
return TRUE;
}
/* this calculates the smallest rectangle (with sides parallel to x- and
* y-axis) that contains the points d1 to d4
*/
static void
pika_transform_resize_adjust (const PikaVector2 *points,
gint n_points,
gint *x1,
gint *y1,
gint *x2,
gint *y2)
{
PikaVector2 top_left;
PikaVector2 bottom_right;
gint i;
top_left = bottom_right = points[0];
for (i = 1; i < n_points; i++)
{
top_left.x = MIN (top_left.x, points[i].x);
top_left.y = MIN (top_left.y, points[i].y);
bottom_right.x = MAX (bottom_right.x, points[i].x);
bottom_right.y = MAX (bottom_right.y, points[i].y);
}
*x1 = (gint) floor (top_left.x + EPSILON);
*y1 = (gint) floor (top_left.y + EPSILON);
*x2 = (gint) ceil (bottom_right.x - EPSILON);
*y2 = (gint) ceil (bottom_right.y - EPSILON);
}
static void
pika_transform_resize_crop (const PikaVector2 *orig_points,
gint n_points,
gdouble aspect,
gint *x1,
gint *y1,
gint *x2,
gint *y2)
{
PikaVector2 points[5];
Rectangle r;
PikaVector2 t,a;
gint i, j;
gint min;
memcpy (points, orig_points, sizeof (PikaVector2) * n_points);
/* find lowest, rightmost corner of surrounding rectangle */
a.x = 0;
a.y = 0;
for (i = 0; i < 4; i++)
{
if (points[i].x < a.x)
a.x = points[i].x;
if (points[i].y < a.y)
a.y = points[i].y;
}
/* and translate all the points to the first quadrant */
for (i = 0; i < n_points; i++)
{
points[i].x += (-a.x) * 2;
points[i].y += (-a.y) * 2;
}
/* find the convex hull using Jarvis's March as the points are passed
* in different orders due to pika_matrix3_transform_point()
*/
min = 0;
for (i = 0; i < n_points; i++)
{
if (points[i].y < points[min].y)
min = i;
}
t = points[0];
points[0] = points[min];
points[min] = t;
for (i = 1; i < n_points - 1; i++)
{
gdouble min_theta;
gdouble min_mag;
int next;
next = n_points - 1;
min_theta = 2.0 * G_PI;
min_mag = DBL_MAX;
for (j = i; j < n_points; j++)
{
gdouble theta;
gdouble sy;
gdouble sx;
gdouble mag;
sy = points[j].y - points[i - 1].y;
sx = points[j].x - points[i - 1].x;
if ((sx == 0.0) && (sy == 0.0))
{
next = j;
break;
}
theta = atan2 (-sy, -sx);
mag = (sx * sx) + (sy * sy);
if ((theta < min_theta) ||
((theta == min_theta) && (mag < min_mag)))
{
min_theta = theta;
min_mag = mag;
next = j;
}
}
t = points[i];
points[i] = points[next];
points[next] = t;
}
/* reverse the order of points */
for (i = 0; i < n_points / 2; i++)
{
t = points[i];
points[i] = points[n_points - i - 1];
points[n_points - i - 1] = t;
}
r.a.x = r.a.y = r.b.x = r.b.y = r.c.x = r.c.y = r.d.x = r.d.y = r.area = 0;
r.aspect = aspect;
if (aspect != 0)
{
for (i = 0; i < n_points; i++)
find_maximum_aspect_rectangle (&r, points, n_points, i);
}
else
{
for (i = 0; i < n_points; i++)
{
find_three_point_rectangle (&r, points, n_points, i);
find_three_point_rectangle_corner (&r, points, n_points, i);
find_two_point_rectangle (&r, points, n_points, i);
find_three_point_rectangle_triangle (&r, points, n_points, i);
}
}
if (r.area == 0)
{
/* saveguard if something went wrong, adjust and give warning */
pika_transform_resize_adjust (orig_points, n_points,
x1, y1, x2, y2);
g_printerr ("no rectangle found by algorithm, no cropping done\n");
return;
}
else
{
/* round and translate the calculated points back */
*x1 = floor (r.a.x + 0.5);
*y1 = floor (r.a.y + 0.5);
*x2 = ceil (r.c.x - 0.5);
*y2 = ceil (r.c.y - 0.5);
*x1 = *x1 - ((-a.x) * 2);
*y1 = *y1 - ((-a.y) * 2);
*x2 = *x2 - ((-a.x) * 2);
*y2 = *y2 - ((-a.y) * 2);
return;
}
}
static void
find_three_point_rectangle (Rectangle *r,
const PikaVector2 *points,
gint n_points,
gint p)
{
PikaVector2 a = points[p % n_points]; /* 0 1 2 3 */
PikaVector2 b = points[(p + 1) % n_points]; /* 1 2 3 0 */
PikaVector2 c = points[(p + 2) % n_points]; /* 2 3 0 1 */
PikaVector2 d = points[(p + 3) % n_points]; /* 3 0 1 2 */
PikaVector2 i1; /* intersection point */
PikaVector2 i2; /* intersection point */
PikaVector2 i3; /* intersection point */
if (intersect_x (b, c, a, &i1) &&
intersect_y (c, d, i1, &i2) &&
intersect_x (d, a, i2, &i3))
add_rectangle (points, n_points, r, i3, i3, i1, i1);
if (intersect_y (b, c, a, &i1) &&
intersect_x (c, d, i1, &i2) &&
intersect_y (d, a, i2, &i3))
add_rectangle (points, n_points, r, i3, i3, i1, i1);
if (intersect_x (d, c, a, &i1) &&
intersect_y (c, b, i1, &i2) &&
intersect_x (b, a, i2, &i3))
add_rectangle (points, n_points, r, i3, i3, i1, i1);
if (intersect_y (d, c, a, &i1) &&
intersect_x (c, b, i1, &i2) &&
intersect_y (b, a, i2, &i3))
add_rectangle (points, n_points, r, i3, i3, i1, i1);
}
static void
find_three_point_rectangle_corner (Rectangle *r,
const PikaVector2 *points,
gint n_points,
gint p)
{
PikaVector2 a = points[p % n_points]; /* 0 1 2 3 */
PikaVector2 b = points[(p + 1) % n_points]; /* 1 2 3 0 */
PikaVector2 c = points[(p + 2) % n_points]; /* 2 3 0 2 */
PikaVector2 d = points[(p + 3) % n_points]; /* 3 0 2 1 */
PikaVector2 i1; /* intersection point */
PikaVector2 i2; /* intersection point */
if (intersect_x (b, c, a , &i1) &&
intersect_y (c, d, i1, &i2))
add_rectangle (points, n_points, r, a, a, i1, i2);
if (intersect_y (b, c, a , &i1) &&
intersect_x (c, d, i1, &i2))
add_rectangle (points, n_points, r, a, a, i1, i2);
if (intersect_x (c, d, a , &i1) &&
intersect_y (b, c, i1, &i2))
add_rectangle (points, n_points, r, a, a, i1, i2);
if (intersect_y (c, d, a , &i1) &&
intersect_x (b, c, i1, &i2))
add_rectangle (points, n_points, r, a, a, i1, i2);
}
static void
find_two_point_rectangle (Rectangle *r,
const PikaVector2 *points,
gint n_points,
gint p)
{
PikaVector2 a = points[ p % n_points]; /* 0 1 2 3 */
PikaVector2 b = points[(p + 1) % n_points]; /* 1 2 3 0 */
PikaVector2 c = points[(p + 2) % n_points]; /* 2 3 0 1 */
PikaVector2 d = points[(p + 3) % n_points]; /* 3 0 1 2 */
PikaVector2 i1; /* intersection point */
PikaVector2 i2; /* intersection point */
PikaVector2 mid; /* Mid point */
add_rectangle (points, n_points, r, a, a, c, c);
add_rectangle (points, n_points, r, b, b, d, d);
if (intersect_x (c, b, a, &i1) &&
intersect_y (c, b, a, &i2))
{
mid.x = ( i1.x + i2.x ) / 2.0;
mid.y = ( i1.y + i2.y ) / 2.0;
add_rectangle (points, n_points, r, a, a, mid, mid);
}
}
static void
find_three_point_rectangle_triangle (Rectangle *r,
const PikaVector2 *points,
gint n_points,
gint p)
{
PikaVector2 a = points[p % n_points]; /* 0 1 2 3 */
PikaVector2 b = points[(p + 1) % n_points]; /* 1 2 3 0 */
PikaVector2 c = points[(p + 2) % n_points]; /* 2 3 0 1 */
PikaVector2 d = points[(p + 3) % n_points]; /* 3 0 1 2 */
PikaVector2 i1; /* intersection point */
PikaVector2 i2; /* intersection point */
PikaVector2 mid;
mid.x = (a.x + b.x) / 2.0;
mid.y = (a.y + b.y) / 2.0;
if (intersect_x (b, c, mid, &i1) &&
intersect_y (a, d, mid, &i2))
add_rectangle (points, n_points, r, mid, mid, i1, i2);
if (intersect_y (b, c, mid, &i1) &&
intersect_x (a, d, mid, &i2))
add_rectangle (points, n_points, r, mid, mid, i1, i2);
if (intersect_x (a, d, mid, &i1) &&
intersect_y (b, c, mid, &i2))
add_rectangle (points, n_points, r, mid, mid, i1, i2);
if (intersect_y (a, d, mid, &i1) &&
intersect_x (b, c, mid, &i2))
add_rectangle (points, n_points, r, mid, mid, i1, i2);
}
static void
find_maximum_aspect_rectangle (Rectangle *r,
const PikaVector2 *points,
gint n_points,
gint p)
{
PikaVector2 a = points[ p % n_points]; /* 0 1 2 3 */
PikaVector2 b = points[(p + 1) % n_points]; /* 1 2 3 0 */
PikaVector2 c = points[(p + 2) % n_points]; /* 2 3 0 1 */
PikaVector2 d = points[(p + 3) % n_points]; /* 3 0 1 2 */
PikaVector2 i1; /* intersection point */
PikaVector2 i2; /* intersection point */
PikaVector2 i3; /* intersection point */
if (intersect_x (b, c, a, &i1))
{
i2.x = i1.x + 1.0 * r->aspect;
i2.y = i1.y + 1.0;
if (intersect (d, a, i1, i2, &i3))
add_rectangle (points, n_points, r, i1, i3, i1, i3);
if (intersect (a, b, i1, i2, &i3))
add_rectangle (points, n_points, r, i1, i3, i1, i3);
if (intersect (c, d, i1, i2, &i3))
add_rectangle (points, n_points, r, i1, i3, i1, i3);
i2.x = i1.x - 1.0 * r->aspect;
i2.y = i1.y + 1.0;
if (intersect (d, a, i1, i2, &i3))
add_rectangle (points, n_points, r, i1, i3, i1, i3);
if (intersect (a, b, i1, i2, &i3))
add_rectangle (points, n_points, r, i1, i3, i1, i3);
if (intersect (c, d, i1, i2, &i3))
add_rectangle (points, n_points, r, i1, i3, i1, i3);
}
if (intersect_y (b, c, a, &i1))
{
i2.x = i1.x + 1.0 * r->aspect;
i2.y = i1.y + 1.0;
if (intersect (d, a, i1, i2, &i3))
add_rectangle (points, n_points, r, i1, i3, i1, i3);
if (intersect (a, b, i1, i2, &i3))
add_rectangle (points, n_points, r, i1, i3, i1, i3);
if (intersect (c, d, i1, i2, &i3))
add_rectangle (points, n_points, r, i1, i3, i1, i3);
i2.x = i1.x - 1.0 * r->aspect;
i2.y = i1.y + 1.0;
if (intersect (d, a, i1, i2, &i3))
add_rectangle (points, n_points, r, i1, i3, i1, i3);
if (intersect (a, b, i1, i2, &i3))
add_rectangle (points, n_points, r, i1, i3, i1, i3);
if (intersect (c, d, i1, i2, &i3))
add_rectangle (points, n_points, r, i1, i3, i1, i3);
}
if (intersect_x (c, d, a, &i1))
{
i2.x = i1.x + 1.0 * r->aspect;
i2.y = i1.y + 1.0;
if (intersect (d, a, i1, i2, &i3))
add_rectangle (points, n_points, r, i1, i3, i1, i3);
if (intersect (a, b, i1, i2, &i3))
add_rectangle (points, n_points, r, i1, i3, i1, i3);
if (intersect (b, c, i1, i2, &i3))
add_rectangle (points, n_points, r, i1, i3, i1, i3);
i2.x = i1.x - 1.0 * r->aspect;
i2.y = i1.y + 1.0;
if (intersect (d, a, i1, i2, &i3))
add_rectangle (points, n_points, r, i1, i3, i1, i3);
if (intersect (a, b, i1, i2, &i3))
add_rectangle (points, n_points, r, i1, i3, i1, i3);
if (intersect (b, c, i1, i2, &i3))
add_rectangle (points, n_points, r, i1, i3, i1, i3);
}
if (intersect_y (c, d, a, &i1))
{
i2.x = i1.x + 1.0 * r->aspect;
i2.y = i1.y + 1.0;
if (intersect (d, a, i1, i2, &i3))
add_rectangle (points, n_points, r, i1, i3, i1, i3);
if (intersect (a, b, i1, i2, &i3))
add_rectangle (points, n_points, r, i1, i3, i1, i3);
if (intersect (b, c, i1, i2, &i3))
add_rectangle (points, n_points, r, i1, i3, i1, i3);
i2.x = i1.x - 1.0 * r->aspect;
i2.y = i1.y + 1.0;
if (intersect (d, a, i1, i2, &i3))
add_rectangle (points, n_points, r, i1, i3, i1, i3);
if (intersect (a, b, i1, i2, &i3))
add_rectangle (points, n_points, r, i1, i3, i1, i3);
if (intersect (b, c, i1, i2, &i3))
add_rectangle (points, n_points, r, i1, i3, i1, i3);
}
}
/* check if point is inside the polygon "points", if point is on border
* its still inside.
*/
static gboolean
in_poly (const PikaVector2 *points,
gint n_points,
PikaVector2 p)
{
PikaVector2 p1, p2;
gint counter = 0;
gint i;
p1 = points[0];
for (i = 1; i <= n_points; i++)
{
p2 = points[i % n_points];
if (p.y > MIN (p1.y, p2.y))
{
if (p.y <= MAX (p1.y, p2.y))
{
if (p.x <= MAX (p1.x, p2.x))
{
if (p1.y != p2.y)
{
gdouble xinters = ((p.y - p1.y) * (p2.x - p1.x) /
(p2.y - p1.y) + p1.x);
if (p1.x == p2.x || p.x <= xinters)
counter++;
}
}
}
}
p1 = p2;
}
/* border check */
if (point_on_border (points, n_points, p))
return TRUE;
return (counter % 2 != 0);
}
/* check if the point p lies on the polygon "points"
*/
static gboolean
point_on_border (const PikaVector2 *points,
gint n_points,
PikaVector2 p)
{
gint i;
for (i = 0; i <= n_points; i++)
{
PikaVector2 a = points[i % n_points];
PikaVector2 b = points[(i + 1) % n_points];
gdouble a1 = (b.y - a.y);
gdouble b1 = (a.x - b.x);
gdouble c1 = a1 * a.x + b1 * a.y;
gdouble c2 = a1 * p.x + b1 * p.y;
if (ABS (c1 - c2) < EPSILON &&
MIN (a.x, b.x) <= p.x &&
MAX (a.x, b.x) >= p.x &&
MIN (a.y, b.y) <= p.y &&
MAX (a.y, b.y) >= p.y)
return TRUE;
}
return FALSE;
}
/* calculate the intersection point of the line a-b with the line c-d
* and write it to i, if existing.
*/
static gboolean
intersect (PikaVector2 a,
PikaVector2 b,
PikaVector2 c,
PikaVector2 d,
PikaVector2 *i)
{
gdouble a1 = (b.y - a.y);
gdouble b1 = (a.x - b.x);
gdouble c1 = a1 * a.x + b1 * a.y;
gdouble a2 = (d.y - c.y);
gdouble b2 = (c.x - d.x);
gdouble c2 = a2 * c.x + b2 * c.y;
gdouble det = a1 * b2 - a2 * b1;
if (det == 0)
return FALSE;
i->x = (b2 * c1 - b1 * c2) / det;
i->y = (a1 * c2 - a2 * c1) / det;
return TRUE;
}
/* calculate the intersection point of the line a-b with the vertical line
* through c and write it to i, if existing.
*/
static gboolean
intersect_x (PikaVector2 a,
PikaVector2 b,
PikaVector2 c,
PikaVector2 *i)
{
PikaVector2 d = c;
d.y += 1;
return intersect(a,b,c,d,i);
}
/* calculate the intersection point of the line a-b with the horizontal line
* through c and write it to i, if existing.
*/
static gboolean
intersect_y (PikaVector2 a,
PikaVector2 b,
PikaVector2 c,
PikaVector2 *i)
{
PikaVector2 d = c;
d.x += 1;
return intersect(a,b,c,d,i);
}
/* this takes the smallest ortho-aligned (the sides of the rectangle are
* parallel to the x- and y-axis) rectangle fitting around the points a to d,
* checks if the whole rectangle is inside the polygon described by points and
* writes it to r if the area is bigger than the rectangle already stored in r.
*/
static void
add_rectangle (const PikaVector2 *points,
gint n_points,
Rectangle *r,
PikaVector2 a,
PikaVector2 b,
PikaVector2 c,
PikaVector2 d)
{
gdouble width;
gdouble height;
gdouble minx, maxx;
gdouble miny, maxy;
/* get the orthoaligned (the sides of the rectangle are parallel to the x-
* and y-axis) rectangle surrounding the points a to d.
*/
minx = MIN4 (a.x, b.x, c.x, d.x);
maxx = MAX4 (a.x, b.x, c.x, d.x);
miny = MIN4 (a.y, b.y, c.y, d.y);
maxy = MAX4 (a.y, b.y, c.y, d.y);
a.x = minx;
a.y = miny;
b.x = maxx;
b.y = miny;
c.x = maxx;
c.y = maxy;
d.x = minx;
d.y = maxy;
width = maxx - minx;
height = maxy - miny;
/* check if this rectangle is inside the polygon "points" */
if (in_poly (points, n_points, a) &&
in_poly (points, n_points, b) &&
in_poly (points, n_points, c) &&
in_poly (points, n_points, d))
{
gdouble area = width * height;
/* check if the new rectangle is larger (in terms of area)
* than the currently stored rectangle in r, if yes store
* new rectangle to r
*/
if (r->area <= area)
{
r->a.x = a.x;
r->a.y = a.y;
r->b.x = b.x;
r->b.y = b.y;
r->c.x = c.x;
r->c.y = c.y;
r->d.x = d.x;
r->d.y = d.y;
r->area = area;
}
}
}

View File

@ -0,0 +1,38 @@
/* 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-2001 Spencer Kimball, Peter Mattis, and others
*
* 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 __PIKA_TRANSFORM_RESIZE_H__
#define __PIKA_TRANSFORM_RESIZE_H__
gboolean pika_transform_resize_boundary (const PikaMatrix3 *inv,
PikaTransformResize resize,
gdouble u1,
gdouble v1,
gdouble u2,
gdouble v2,
gint *x1,
gint *y1,
gint *x2,
gint *y2);
#endif /* __PIKA_TRANSFORM_RESIZE_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,129 @@
/* 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-2001 Spencer Kimball, Peter Mattis, and others
*
* 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 __PIKA_TRANSFORM_UTILS_H__
#define __PIKA_TRANSFORM_UTILS_H__
#define PIKA_TRANSFORM_NEAR_Z 0.02
void pika_transform_get_rotate_center (gint x,
gint y,
gint width,
gint height,
gboolean auto_center,
gdouble *center_x,
gdouble *center_y);
void pika_transform_get_flip_axis (gint x,
gint y,
gint width,
gint height,
PikaOrientationType flip_type,
gboolean auto_center,
gdouble *axis);
void pika_transform_matrix_flip (PikaMatrix3 *matrix,
PikaOrientationType flip_type,
gdouble axis);
void pika_transform_matrix_flip_free (PikaMatrix3 *matrix,
gdouble x1,
gdouble y1,
gdouble x2,
gdouble y2);
void pika_transform_matrix_rotate (PikaMatrix3 *matrix,
PikaRotationType rotate_type,
gdouble center_x,
gdouble center_y);
void pika_transform_matrix_rotate_rect (PikaMatrix3 *matrix,
gint x,
gint y,
gint width,
gint height,
gdouble angle);
void pika_transform_matrix_rotate_center (PikaMatrix3 *matrix,
gdouble center_x,
gdouble center_y,
gdouble angle);
void pika_transform_matrix_scale (PikaMatrix3 *matrix,
gint x,
gint y,
gint width,
gint height,
gdouble t_x,
gdouble t_y,
gdouble t_width,
gdouble t_height);
void pika_transform_matrix_shear (PikaMatrix3 *matrix,
gint x,
gint y,
gint width,
gint height,
PikaOrientationType orientation,
gdouble amount);
void pika_transform_matrix_perspective (PikaMatrix3 *matrix,
gint x,
gint y,
gint width,
gint height,
gdouble t_x1,
gdouble t_y1,
gdouble t_x2,
gdouble t_y2,
gdouble t_x3,
gdouble t_y3,
gdouble t_x4,
gdouble t_y4);
gboolean pika_transform_matrix_generic (PikaMatrix3 *matrix,
const PikaVector2 input_points[4],
const PikaVector2 output_points[4]);
gboolean pika_transform_polygon_is_convex (gdouble x1,
gdouble y1,
gdouble x2,
gdouble y2,
gdouble x3,
gdouble y3,
gdouble x4,
gdouble y4);
void pika_transform_polygon (const PikaMatrix3 *matrix,
const PikaVector2 *vertices,
gint n_vertices,
gboolean closed,
PikaVector2 *t_vertices,
gint *n_t_vertices);
void pika_transform_polygon_coords (const PikaMatrix3 *matrix,
const PikaCoords *vertices,
gint n_vertices,
gboolean closed,
PikaCoords *t_vertices,
gint *n_t_vertices);
void pika_transform_bezier_coords (const PikaMatrix3 *matrix,
const PikaCoords bezier[4],
GQueue *t_beziers[2],
gint *n_t_beziers,
gboolean *start_in,
gboolean *end_in);
#endif /* __PIKA_TRANSFORM_UTILS_H__ */

492
app/core/pika-units.c Normal file
View File

@ -0,0 +1,492 @@
/* 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-1999 Spencer Kimball and Peter Mattis
*
* pikaunit.c
* Copyright (C) 1999-2000 Michael Natterer <mitch@gimp.org>
*
* 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/>.
*/
/* This file contains functions to load & save the file containing the
* user-defined size units, when the application starts/finished.
*/
#include "config.h"
#include <gio/gio.h>
#include "libpikabase/pikabase.h"
#include "libpikabase/pikabase-private.h"
#include "libpikaconfig/pikaconfig.h"
#include "core-types.h"
#include "pika.h"
#include "pika-units.h"
#include "pikaunit.h"
#include "config/pikaconfig-file.h"
#include "pika-intl.h"
/*
* All deserialize functions return G_TOKEN_LEFT_PAREN on success,
* or the GTokenType they would have expected but didn't get.
*/
static GTokenType pika_unitrc_unit_info_deserialize (GScanner *scanner,
Pika *pika);
static Pika *the_unit_pika = NULL;
static gint
pika_units_get_number_of_units (void)
{
return _pika_unit_get_number_of_units (the_unit_pika);
}
static gint
pika_units_get_number_of_built_in_units (void)
{
return PIKA_UNIT_END;
}
static PikaUnit
pika_units_unit_new (gchar *identifier,
gdouble factor,
gint digits,
gchar *symbol,
gchar *abbreviation,
gchar *singular,
gchar *plural)
{
return _pika_unit_new (the_unit_pika,
identifier,
factor,
digits,
symbol,
abbreviation,
singular,
plural);
}
static gboolean
pika_units_unit_get_deletion_flag (PikaUnit unit)
{
return _pika_unit_get_deletion_flag (the_unit_pika, unit);
}
static void
pika_units_unit_set_deletion_flag (PikaUnit unit,
gboolean deletion_flag)
{
_pika_unit_set_deletion_flag (the_unit_pika, unit, deletion_flag);
}
static gdouble
pika_units_unit_get_factor (PikaUnit unit)
{
return _pika_unit_get_factor (the_unit_pika, unit);
}
static gint
pika_units_unit_get_digits (PikaUnit unit)
{
return _pika_unit_get_digits (the_unit_pika, unit);
}
static const gchar *
pika_units_unit_get_identifier (PikaUnit unit)
{
return _pika_unit_get_identifier (the_unit_pika, unit);
}
static const gchar *
pika_units_unit_get_symbol (PikaUnit unit)
{
return _pika_unit_get_symbol (the_unit_pika, unit);
}
static const gchar *
pika_units_unit_get_abbreviation (PikaUnit unit)
{
return _pika_unit_get_abbreviation (the_unit_pika, unit);
}
static const gchar *
pika_units_unit_get_singular (PikaUnit unit)
{
return _pika_unit_get_singular (the_unit_pika, unit);
}
static const gchar *
pika_units_unit_get_plural (PikaUnit unit)
{
return _pika_unit_get_plural (the_unit_pika, unit);
}
void
pika_units_init (Pika *pika)
{
PikaUnitVtable vtable;
g_return_if_fail (PIKA_IS_PIKA (pika));
g_return_if_fail (the_unit_pika == NULL);
the_unit_pika = pika;
vtable.unit_get_number_of_units = pika_units_get_number_of_units;
vtable.unit_get_number_of_built_in_units = pika_units_get_number_of_built_in_units;
vtable.unit_new = pika_units_unit_new;
vtable.unit_get_deletion_flag = pika_units_unit_get_deletion_flag;
vtable.unit_set_deletion_flag = pika_units_unit_set_deletion_flag;
vtable.unit_get_factor = pika_units_unit_get_factor;
vtable.unit_get_digits = pika_units_unit_get_digits;
vtable.unit_get_identifier = pika_units_unit_get_identifier;
vtable.unit_get_symbol = pika_units_unit_get_symbol;
vtable.unit_get_abbreviation = pika_units_unit_get_abbreviation;
vtable.unit_get_singular = pika_units_unit_get_singular;
vtable.unit_get_plural = pika_units_unit_get_plural;
pika_base_init (&vtable);
pika->user_units = NULL;
pika->n_user_units = 0;
}
void
pika_units_exit (Pika *pika)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
pika_user_units_free (pika);
}
/* unitrc functions **********/
enum
{
UNIT_INFO = 1,
UNIT_FACTOR,
UNIT_DIGITS,
UNIT_SYMBOL,
UNIT_ABBREV,
UNIT_SINGULAR,
UNIT_PLURAL
};
void
pika_unitrc_load (Pika *pika)
{
GFile *file;
GScanner *scanner;
GTokenType token;
GError *error = NULL;
g_return_if_fail (PIKA_IS_PIKA (pika));
file = pika_directory_file ("unitrc", NULL);
if (pika->be_verbose)
g_print ("Parsing '%s'\n", pika_file_get_utf8_name (file));
scanner = pika_scanner_new_file (file, &error);
if (! scanner && error->code == PIKA_CONFIG_ERROR_OPEN_ENOENT)
{
g_clear_error (&error);
g_object_unref (file);
file = pika_sysconf_directory_file ("unitrc", NULL);
scanner = pika_scanner_new_file (file, NULL);
}
if (! scanner)
{
g_clear_error (&error);
g_object_unref (file);
return;
}
g_scanner_scope_add_symbol (scanner, 0,
"unit-info", GINT_TO_POINTER (UNIT_INFO));
g_scanner_scope_add_symbol (scanner, UNIT_INFO,
"factor", GINT_TO_POINTER (UNIT_FACTOR));
g_scanner_scope_add_symbol (scanner, UNIT_INFO,
"digits", GINT_TO_POINTER (UNIT_DIGITS));
g_scanner_scope_add_symbol (scanner, UNIT_INFO,
"symbol", GINT_TO_POINTER (UNIT_SYMBOL));
g_scanner_scope_add_symbol (scanner, UNIT_INFO,
"abbreviation", GINT_TO_POINTER (UNIT_ABBREV));
g_scanner_scope_add_symbol (scanner, UNIT_INFO,
"singular", GINT_TO_POINTER (UNIT_SINGULAR));
g_scanner_scope_add_symbol (scanner, UNIT_INFO,
"plural", GINT_TO_POINTER (UNIT_PLURAL));
token = G_TOKEN_LEFT_PAREN;
while (g_scanner_peek_next_token (scanner) == token)
{
token = g_scanner_get_next_token (scanner);
switch (token)
{
case G_TOKEN_LEFT_PAREN:
token = G_TOKEN_SYMBOL;
break;
case G_TOKEN_SYMBOL:
if (scanner->value.v_symbol == GINT_TO_POINTER (UNIT_INFO))
{
g_scanner_set_scope (scanner, UNIT_INFO);
token = pika_unitrc_unit_info_deserialize (scanner, pika);
if (token == G_TOKEN_RIGHT_PAREN)
g_scanner_set_scope (scanner, 0);
}
break;
case G_TOKEN_RIGHT_PAREN:
token = G_TOKEN_LEFT_PAREN;
break;
default: /* do nothing */
break;
}
}
if (token != G_TOKEN_LEFT_PAREN)
{
g_scanner_get_next_token (scanner);
g_scanner_unexp_token (scanner, token, NULL, NULL, NULL,
_("fatal parse error"), TRUE);
pika_message_literal (pika, NULL, PIKA_MESSAGE_ERROR, error->message);
g_clear_error (&error);
pika_config_file_backup_on_error (file, "unitrc", NULL);
}
pika_scanner_unref (scanner);
g_object_unref (file);
}
void
pika_unitrc_save (Pika *pika)
{
PikaConfigWriter *writer;
GFile *file;
gint i;
GError *error = NULL;
g_return_if_fail (PIKA_IS_PIKA (pika));
file = pika_directory_file ("unitrc", NULL);
if (pika->be_verbose)
g_print ("Writing '%s'\n", pika_file_get_utf8_name (file));
writer =
pika_config_writer_new_from_file (file,
TRUE,
"PIKA units\n\n"
"This file contains the user unit database. "
"You can edit this list with the unit "
"editor. You are not supposed to edit it "
"manually, but of course you can do.\n"
"This file will be entirely rewritten each "
"time you exit.",
NULL);
g_object_unref (file);
if (!writer)
return;
/* save user defined units */
for (i = _pika_unit_get_number_of_built_in_units (pika);
i < _pika_unit_get_number_of_units (pika);
i++)
{
if (_pika_unit_get_deletion_flag (pika, i) == FALSE)
{
gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
pika_config_writer_open (writer, "unit-info");
pika_config_writer_string (writer,
_pika_unit_get_identifier (pika, i));
pika_config_writer_open (writer, "factor");
pika_config_writer_print (writer,
g_ascii_dtostr (buf, sizeof (buf),
_pika_unit_get_factor (pika, i)),
-1);
pika_config_writer_close (writer);
pika_config_writer_open (writer, "digits");
pika_config_writer_printf (writer,
"%d", _pika_unit_get_digits (pika, i));
pika_config_writer_close (writer);
pika_config_writer_open (writer, "symbol");
pika_config_writer_string (writer,
_pika_unit_get_symbol (pika, i));
pika_config_writer_close (writer);
pika_config_writer_open (writer, "abbreviation");
pika_config_writer_string (writer,
_pika_unit_get_abbreviation (pika, i));
pika_config_writer_close (writer);
pika_config_writer_open (writer, "singular");
pika_config_writer_string (writer,
_pika_unit_get_singular (pika, i));
pika_config_writer_close (writer);
pika_config_writer_open (writer, "plural");
pika_config_writer_string (writer,
_pika_unit_get_plural (pika, i));
pika_config_writer_close (writer);
pika_config_writer_close (writer);
}
}
if (! pika_config_writer_finish (writer, "end of units", &error))
{
pika_message_literal (pika, NULL, PIKA_MESSAGE_ERROR, error->message);
g_clear_error (&error);
}
}
/* private functions */
static GTokenType
pika_unitrc_unit_info_deserialize (GScanner *scanner,
Pika *pika)
{
gchar *identifier = NULL;
gdouble factor = 1.0;
gint digits = 2.0;
gchar *symbol = NULL;
gchar *abbreviation = NULL;
gchar *singular = NULL;
gchar *plural = NULL;
GTokenType token;
if (! pika_scanner_parse_string (scanner, &identifier))
return G_TOKEN_STRING;
token = G_TOKEN_LEFT_PAREN;
while (g_scanner_peek_next_token (scanner) == token)
{
token = g_scanner_get_next_token (scanner);
switch (token)
{
case G_TOKEN_LEFT_PAREN:
token = G_TOKEN_SYMBOL;
break;
case G_TOKEN_SYMBOL:
switch (GPOINTER_TO_INT (scanner->value.v_symbol))
{
case UNIT_FACTOR:
token = G_TOKEN_FLOAT;
if (! pika_scanner_parse_float (scanner, &factor))
goto cleanup;
break;
case UNIT_DIGITS:
token = G_TOKEN_INT;
if (! pika_scanner_parse_int (scanner, &digits))
goto cleanup;
break;
case UNIT_SYMBOL:
token = G_TOKEN_STRING;
if (! pika_scanner_parse_string (scanner, &symbol))
goto cleanup;
break;
case UNIT_ABBREV:
token = G_TOKEN_STRING;
if (! pika_scanner_parse_string (scanner, &abbreviation))
goto cleanup;
break;
case UNIT_SINGULAR:
token = G_TOKEN_STRING;
if (! pika_scanner_parse_string (scanner, &singular))
goto cleanup;
break;
case UNIT_PLURAL:
token = G_TOKEN_STRING;
if (! pika_scanner_parse_string (scanner, &plural))
goto cleanup;
break;
default:
break;
}
token = G_TOKEN_RIGHT_PAREN;
break;
case G_TOKEN_RIGHT_PAREN:
token = G_TOKEN_LEFT_PAREN;
break;
default:
break;
}
}
if (token == G_TOKEN_LEFT_PAREN)
{
token = G_TOKEN_RIGHT_PAREN;
if (g_scanner_peek_next_token (scanner) == token)
{
PikaUnit unit = _pika_unit_new (pika,
identifier, factor, digits,
symbol, abbreviation,
singular, plural);
/* make the unit definition persistent */
_pika_unit_set_deletion_flag (pika, unit, FALSE);
}
}
cleanup:
g_free (identifier);
g_free (symbol);
g_free (abbreviation);
g_free (singular);
g_free (plural);
return token;
}

33
app/core/pika-units.h Normal file
View File

@ -0,0 +1,33 @@
/* 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/>.
*/
#ifndef __PIKA_UNITS_H__
#define __PIKA_UNITS_H__
void pika_units_init (Pika *pika);
void pika_units_exit (Pika *pika);
void pika_unitrc_load (Pika *pika);
void pika_unitrc_save (Pika *pika);
#endif /* __PIKA_UNITS_H__ */

1114
app/core/pika-user-install.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,44 @@
/* 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/>.
*/
#ifndef __PIKA_USER_INSTALL_H__
#define __PIKA_USER_INSTALL_H__
typedef struct _PikaUserInstall PikaUserInstall;
typedef void (* PikaUserInstallLogFunc) (const gchar *message,
gboolean error,
gpointer user_data);
PikaUserInstall * pika_user_install_new (GObject *pika,
gboolean verbose);
gboolean pika_user_install_run (PikaUserInstall *install,
gint scale_factor);
void pika_user_install_free (PikaUserInstall *install);
void pika_user_install_set_log_handler (PikaUserInstall *install,
PikaUserInstallLogFunc log,
gpointer user_data);
#endif /* __USER_INSTALL_H__ */

1595
app/core/pika-utils.c Normal file

File diff suppressed because it is too large Load Diff

145
app/core/pika-utils.h Normal file
View File

@ -0,0 +1,145 @@
/* 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/>.
*/
#ifndef __APP_PIKA_UTILS_H__
#define __APP_PIKA_UTILS_H__
#ifdef PIKA_RELEASE
#define PIKA_TIMER_START()
#define PIKA_TIMER_END(message)
#else
#define PIKA_TIMER_START() \
{ GTimer *_timer = g_timer_new ();
#define PIKA_TIMER_END(message) \
g_printerr ("%s: %s took %0.4f seconds\n", \
G_STRFUNC, message, g_timer_elapsed (_timer, NULL)); \
g_timer_destroy (_timer); }
#endif
#define MIN4(a,b,c,d) MIN (MIN ((a), (b)), MIN ((c), (d)))
#define MAX4(a,b,c,d) MAX (MAX ((a), (b)), MAX ((c), (d)))
gint pika_get_pid (void);
guint64 pika_get_physical_memory_size (void);
gchar * pika_get_default_language (const gchar *category);
PikaUnit pika_get_default_unit (void);
gchar ** pika_properties_append (GType object_type,
gint *n_properties,
gchar **names,
GValue **values,
...) G_GNUC_NULL_TERMINATED;
gchar ** pika_properties_append_valist (GType object_type,
gint *n_properties,
gchar **names,
GValue **values,
va_list args);
void pika_properties_free (gint n_properties,
gchar **names,
GValue *values);
gchar * pika_markup_extract_text (const gchar *markup);
const gchar* pika_enum_get_value_name (GType enum_type,
gint value);
gboolean pika_get_fill_params (PikaContext *context,
PikaFillType fill_type,
PikaRGB *color,
PikaPattern **pattern,
GError **error);
/* Common values for the n_snap_lines parameter of
* pika_constrain_line.
*/
#define PIKA_CONSTRAIN_LINE_90_DEGREES 2
#define PIKA_CONSTRAIN_LINE_45_DEGREES 4
#define PIKA_CONSTRAIN_LINE_15_DEGREES 12
void pika_constrain_line (gdouble start_x,
gdouble start_y,
gdouble *end_x,
gdouble *end_y,
gint n_snap_lines,
gdouble offset_angle,
gdouble xres,
gdouble yres);
gint pika_file_compare (GFile *file1,
GFile *file2);
gboolean pika_file_is_executable (GFile *file);
gchar * pika_file_get_extension (GFile *file);
GFile * pika_file_with_new_extension (GFile *file,
GFile *ext_file);
gboolean pika_file_delete_recursive (GFile *file,
GError **error);
gchar * pika_data_input_stream_read_line_always
(GDataInputStream *stream,
gsize *length,
GCancellable *cancellable,
GError **error);
gboolean pika_ascii_strtoi (const gchar *nptr,
gchar **endptr,
gint base,
gint *result);
gboolean pika_ascii_strtod (const gchar *nptr,
gchar **endptr,
gdouble *result);
gchar * pika_appstream_to_pango_markup (const gchar *as_text);
void pika_appstream_to_pango_markups (const gchar *as_text,
gchar **introduction,
GList **release_items);
gint pika_g_list_compare (GList *list1,
GList *list2);
PikaTRCType pika_suggest_trc_for_component_type (PikaComponentType component_type,
PikaTRCType old_trc);
PikaAsync * pika_idle_run_async (PikaRunAsyncFunc func,
gpointer user_data);
PikaAsync * pika_idle_run_async_full (gint priority,
PikaRunAsyncFunc func,
gpointer user_data,
GDestroyNotify user_data_destroy_func);
PikaImage * pika_create_image_from_buffer (Pika *pika,
GeglBuffer *buffer,
const gchar *image_name);
gint pika_view_size_get_larger (gint view_size);
gint pika_view_size_get_smaller (gint view_size);
#ifdef G_OS_WIN32
gboolean pika_win32_have_wintab (void);
gboolean pika_win32_have_windows_ink (void);
#endif
#endif /* __APP_PIKA_UTILS_H__ */

1243
app/core/pika.c Normal file

File diff suppressed because it is too large Load Diff

256
app/core/pika.h Normal file
View File

@ -0,0 +1,256 @@
/* 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-1997 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/>.
*/
#ifndef __PIKA_H__
#define __PIKA_H__
#include "pikaobject.h"
#include "pika-gui.h"
#define PIKA_TYPE_PIKA (pika_get_type ())
#define PIKA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_PIKA, Pika))
#define PIKA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_PIKA, PikaClass))
#define PIKA_IS_PIKA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_PIKA))
#define PIKA_IS_PIKA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_PIKA))
typedef struct _PikaClass PikaClass;
struct _Pika
{
PikaObject parent_instance;
GApplication *app;
PikaCoreConfig *config;
PikaCoreConfig *edit_config; /* don't use this one, it's just
* for the preferences dialog
*/
gchar *session_name;
GFile *default_folder;
gboolean be_verbose;
gboolean no_data;
gboolean no_fonts;
gboolean no_interface;
gboolean show_gui;
gboolean use_shm;
gboolean use_cpu_accel;
PikaMessageHandlerType message_handler;
gboolean console_messages;
gboolean show_playground;
gboolean show_debug_menu;
PikaStackTraceMode stack_trace_mode;
PikaPDBCompatMode pdb_compat_mode;
PikaGui gui; /* gui vtable */
gboolean restored; /* becomes TRUE in pika_restore() */
gboolean initialized; /* Fully initialized (only set once at start). */
gboolean query_all; /* Force query all plug-ins. */
gint busy;
guint busy_idle_id;
GList *user_units;
gint n_user_units;
PikaParasiteList *parasites;
PikaContainer *paint_info_list;
PikaPaintInfo *standard_paint_info;
PikaModuleDB *module_db;
gboolean write_modulerc;
PikaExtensionManager *extension_manager;
PikaPlugInManager *plug_in_manager;
GList *filter_history;
PikaContainer *images;
guint32 next_guide_id;
guint32 next_sample_point_id;
PikaIdTable *image_table;
PikaIdTable *item_table;
PikaContainer *displays;
gint next_display_id;
GList *image_windows;
PikaImage *clipboard_image;
PikaBuffer *clipboard_buffer;
PikaContainer *named_buffers;
PikaDataFactory *brush_factory;
PikaDataFactory *dynamics_factory;
PikaDataFactory *mybrush_factory;
PikaDataFactory *pattern_factory;
PikaDataFactory *gradient_factory;
PikaDataFactory *palette_factory;
PikaDataFactory *font_factory;
PikaDataFactory *tool_preset_factory;
PikaTagCache *tag_cache;
PikaPDB *pdb;
PikaContainer *tool_info_list;
PikaToolInfo *standard_tool_info;
PikaContainer *tool_item_list;
PikaContainer *tool_item_ui_list;
/* the opened and saved images in MRU order */
PikaContainer *documents;
/* image_new values */
PikaContainer *templates;
PikaTemplate *image_new_last_template;
/* the list of all contexts */
GList *context_list;
/* the default context which is initialized from pikarc */
PikaContext *default_context;
/* the context used by the interface */
PikaContext *user_context;
};
struct _PikaClass
{
PikaObjectClass parent_class;
void (* initialize) (Pika *pika,
PikaInitStatusFunc status_callback);
void (* restore) (Pika *pika,
PikaInitStatusFunc status_callback);
gboolean (* exit) (Pika *pika,
gboolean force);
void (* clipboard_changed) (Pika *pika);
void (* filter_history_changed) (Pika *pika);
/* emitted if an image is loaded and opened with a display */
void (* image_opened) (Pika *pika,
GFile *file);
};
GType pika_get_type (void) G_GNUC_CONST;
Pika * pika_new (const gchar *name,
const gchar *session_name,
GFile *default_folder,
gboolean be_verbose,
gboolean no_data,
gboolean no_fonts,
gboolean no_interface,
gboolean use_shm,
gboolean use_cpu_accel,
gboolean console_messages,
gboolean show_playground,
gboolean show_debug_menu,
PikaStackTraceMode stack_trace_mode,
PikaPDBCompatMode pdb_compat_mode);
void pika_set_show_gui (Pika *pika,
gboolean show_gui);
gboolean pika_get_show_gui (Pika *pika);
void pika_load_config (Pika *pika,
GFile *alternate_system_pikarc,
GFile *alternate_pikarc);
void pika_initialize (Pika *pika,
PikaInitStatusFunc status_callback);
void pika_restore (Pika *pika,
PikaInitStatusFunc status_callback,
GError **error);
gboolean pika_is_restored (Pika *pika);
void pika_exit (Pika *pika,
gboolean force);
GList * pika_get_image_iter (Pika *pika);
GList * pika_get_display_iter (Pika *pika);
GList * pika_get_image_windows (Pika *pika);
GList * pika_get_paint_info_iter (Pika *pika);
GList * pika_get_tool_info_iter (Pika *pika);
GList * pika_get_tool_item_iter (Pika *pika);
GList * pika_get_tool_item_ui_iter (Pika *pika);
PikaObject * pika_get_clipboard_object (Pika *pika);
void pika_set_clipboard_image (Pika *pika,
PikaImage *image);
PikaImage * pika_get_clipboard_image (Pika *pika);
void pika_set_clipboard_buffer (Pika *pika,
PikaBuffer *buffer);
PikaBuffer * pika_get_clipboard_buffer (Pika *pika);
PikaImage * pika_create_image (Pika *pika,
gint width,
gint height,
PikaImageBaseType type,
PikaPrecision precision,
gboolean attach_comment);
void pika_set_default_context (Pika *pika,
PikaContext *context);
PikaContext * pika_get_default_context (Pika *pika);
void pika_set_user_context (Pika *pika,
PikaContext *context);
PikaContext * pika_get_user_context (Pika *pika);
PikaToolInfo * pika_get_tool_info (Pika *pika,
const gchar *tool_name);
void pika_message (Pika *pika,
GObject *handler,
PikaMessageSeverity severity,
const gchar *format,
...) G_GNUC_PRINTF (4, 5);
void pika_message_valist (Pika *pika,
GObject *handler,
PikaMessageSeverity severity,
const gchar *format,
va_list args) G_GNUC_PRINTF (4, 0);
void pika_message_literal (Pika *pika,
GObject *handler,
PikaMessageSeverity severity,
const gchar *message);
void pika_filter_history_changed (Pika *pika);
void pika_image_opened (Pika *pika,
GFile *file);
GFile * pika_get_temp_file (Pika *pika,
const gchar *extension);
#endif /* __PIKA_H__ */

754
app/core/pikaasync.c Normal file
View File

@ -0,0 +1,754 @@
/* 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
*
* pikaasync.c
* Copyright (C) 2018 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 <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include "core-types.h"
#include "pikaasync.h"
#include "pikacancelable.h"
#include "pikawaitable.h"
/* PikaAsync represents an asynchronous task. Both the public and the
* protected interfaces are intentionally minimal at this point, to keep things
* simple. They may be extended in the future as needed.
*
* PikaAsync implements the PikaWaitable and PikaCancelable interfaces.
*
* Upon creation, a PikaAsync object is in the "running" state. Once the task
* is complete (and before the object's destruction), it should be transitioned
* to the "stopped" state, using either 'pika_async_finish()' or
* 'pika_async_abort()'.
*
* Similarly, upon creation, a PikaAsync object is said to be "unsynced". It
* becomes synced once the execution of any of the completion callbacks added
* through 'pika_async_add_callback()' had started, or after a successful call
* to one of the 'pika_waitable_wait()' family of functions.
*
* Note that certain PikaAsync functions may only be called during a certain
* state, on a certain thread, or depending on whether or not the object is
* synced, as detailed for each function. When referring to threads, the "main
* thread" is the thread running the main loop, or any thread whose execution
* is synchronized with the main thread, and the "async thread" is the thread
* calling 'pika_async_finish()' or 'pika_async_abort()' (which may also be the
* main thread), or any thread whose execution is synchronized with the async
* thread.
*/
/* #define TIME_ASYNC_OPS */
enum
{
WAITING,
LAST_SIGNAL
};
typedef struct _PikaAsyncCallbackInfo PikaAsyncCallbackInfo;
struct _PikaAsyncCallbackInfo
{
PikaAsync *async;
PikaAsyncCallback callback;
gpointer data;
gpointer gobject;
};
struct _PikaAsyncPrivate
{
GMutex mutex;
GCond cond;
GQueue callbacks;
gpointer result;
GDestroyNotify result_destroy_func;
guint idle_id;
gboolean stopped;
gboolean finished;
gboolean synced;
gboolean canceled;
#ifdef TIME_ASYNC_OPS
guint64 start_time;
#endif
};
/* local function prototypes */
static void pika_async_waitable_iface_init (PikaWaitableInterface *iface);
static void pika_async_cancelable_iface_init (PikaCancelableInterface *iface);
static void pika_async_finalize (GObject *object);
static void pika_async_wait (PikaWaitable *waitable);
static gboolean pika_async_try_wait (PikaWaitable *waitable);
static gboolean pika_async_wait_until (PikaWaitable *waitable,
gint64 end_time);
static void pika_async_cancel (PikaCancelable *cancelable);
static gboolean pika_async_idle (PikaAsync *async);
static void pika_async_callback_weak_notify (PikaAsyncCallbackInfo *callback_info,
GObject *gobject);
static void pika_async_stop (PikaAsync *async);
static void pika_async_run_callbacks (PikaAsync *async);
G_DEFINE_TYPE_WITH_CODE (PikaAsync, pika_async, G_TYPE_OBJECT,
G_ADD_PRIVATE (PikaAsync)
G_IMPLEMENT_INTERFACE (PIKA_TYPE_WAITABLE,
pika_async_waitable_iface_init)
G_IMPLEMENT_INTERFACE (PIKA_TYPE_CANCELABLE,
pika_async_cancelable_iface_init))
#define parent_class pika_async_parent_class
static guint async_signals[LAST_SIGNAL] = { 0 };
/* local variables */
static volatile gint pika_async_n_running = 0;
/* private functions */
static void
pika_async_class_init (PikaAsyncClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
async_signals[WAITING] =
g_signal_new ("waiting",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (PikaAsyncClass, waiting),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
object_class->finalize = pika_async_finalize;
}
static void
pika_async_waitable_iface_init (PikaWaitableInterface *iface)
{
iface->wait = pika_async_wait;
iface->try_wait = pika_async_try_wait;
iface->wait_until = pika_async_wait_until;
}
static void
pika_async_cancelable_iface_init (PikaCancelableInterface *iface)
{
iface->cancel = pika_async_cancel;
}
static void
pika_async_init (PikaAsync *async)
{
async->priv = pika_async_get_instance_private (async);
g_mutex_init (&async->priv->mutex);
g_cond_init (&async->priv->cond);
g_queue_init (&async->priv->callbacks);
g_atomic_int_inc (&pika_async_n_running);
#ifdef TIME_ASYNC_OPS
async->priv->start_time = g_get_monotonic_time ();
#endif
}
static void
pika_async_finalize (GObject *object)
{
PikaAsync *async = PIKA_ASYNC (object);
g_warn_if_fail (async->priv->stopped);
g_warn_if_fail (async->priv->idle_id == 0);
g_warn_if_fail (g_queue_is_empty (&async->priv->callbacks));
if (async->priv->finished &&
async->priv->result &&
async->priv->result_destroy_func)
{
async->priv->result_destroy_func (async->priv->result);
async->priv->result = NULL;
}
g_cond_clear (&async->priv->cond);
g_mutex_clear (&async->priv->mutex);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
/* waits for 'waitable' to transition to the "stopped" state. if 'waitable' is
* already stopped, returns immediately.
*
* after the call, all callbacks previously added through
* 'pika_async_add_callback()' are guaranteed to have been called.
*
* may only be called on the main thread.
*/
static void
pika_async_wait (PikaWaitable *waitable)
{
PikaAsync *async = PIKA_ASYNC (waitable);
g_mutex_lock (&async->priv->mutex);
if (! async->priv->stopped)
{
g_signal_emit (async, async_signals[WAITING], 0);
while (! async->priv->stopped)
g_cond_wait (&async->priv->cond, &async->priv->mutex);
}
g_mutex_unlock (&async->priv->mutex);
pika_async_run_callbacks (async);
}
/* same as 'pika_async_wait()', but returns immediately if 'waitable' is not in
* the "stopped" state.
*
* returns TRUE if 'waitable' has transitioned to the "stopped" state, or FALSE
* otherwise.
*/
static gboolean
pika_async_try_wait (PikaWaitable *waitable)
{
PikaAsync *async = PIKA_ASYNC (waitable);
g_mutex_lock (&async->priv->mutex);
if (! async->priv->stopped)
{
g_mutex_unlock (&async->priv->mutex);
return FALSE;
}
g_mutex_unlock (&async->priv->mutex);
pika_async_run_callbacks (async);
return TRUE;
}
/* same as 'pika_async_wait()', taking an additional 'end_time' parameter,
* specifying the maximal monotonic time until which to wait for 'waitable' to
* stop.
*
* returns TRUE if 'waitable' has transitioned to the "stopped" state, or FALSE
* if the wait was interrupted before the transition.
*/
static gboolean
pika_async_wait_until (PikaWaitable *waitable,
gint64 end_time)
{
PikaAsync *async = PIKA_ASYNC (waitable);
g_mutex_lock (&async->priv->mutex);
if (! async->priv->stopped)
{
g_signal_emit (async, async_signals[WAITING], 0);
while (! async->priv->stopped)
{
if (! g_cond_wait_until (&async->priv->cond, &async->priv->mutex,
end_time))
{
g_mutex_unlock (&async->priv->mutex);
return FALSE;
}
}
}
g_mutex_unlock (&async->priv->mutex);
pika_async_run_callbacks (async);
return TRUE;
}
/* requests the cancellation of the task managed by 'cancelable'.
*
* note that 'pika_async_cancel()' doesn't directly cause 'cancelable' to be
* stopped, nor synced. furthermore, 'cancelable' may still complete
* successfully even when cancellation has been requested.
*
* may only be called on the main thread.
*/
static void
pika_async_cancel (PikaCancelable *cancelable)
{
PikaAsync *async = PIKA_ASYNC (cancelable);
async->priv->canceled = TRUE;
}
static gboolean
pika_async_idle (PikaAsync *async)
{
pika_waitable_wait (PIKA_WAITABLE (async));
return G_SOURCE_REMOVE;
}
static void
pika_async_callback_weak_notify (PikaAsyncCallbackInfo *callback_info,
GObject *gobject)
{
PikaAsync *async = callback_info->async;
gboolean unref_async = FALSE;
g_mutex_lock (&async->priv->mutex);
g_queue_remove (&async->priv->callbacks, callback_info);
g_slice_free (PikaAsyncCallbackInfo, callback_info);
if (g_queue_is_empty (&async->priv->callbacks) && async->priv->idle_id)
{
g_source_remove (async->priv->idle_id);
async->priv->idle_id = 0;
unref_async = TRUE;
}
g_mutex_unlock (&async->priv->mutex);
if (unref_async)
g_object_unref (async);
}
static void
pika_async_stop (PikaAsync *async)
{
#ifdef TIME_ASYNC_OPS
{
guint64 time = g_get_monotonic_time ();
g_printerr ("Asynchronous operation took %g seconds%s\n",
(time - async->priv->start_time) / 1000000.0,
async->priv->finished ? "" : " (aborted)");
}
#endif
g_atomic_int_dec_and_test (&pika_async_n_running);
if (! g_queue_is_empty (&async->priv->callbacks))
{
g_object_ref (async);
async->priv->idle_id = g_idle_add_full (G_PRIORITY_DEFAULT,
(GSourceFunc) pika_async_idle,
async, NULL);
}
async->priv->stopped = TRUE;
g_cond_broadcast (&async->priv->cond);
}
static void
pika_async_run_callbacks (PikaAsync *async)
{
PikaAsyncCallbackInfo *callback_info;
gboolean unref_async = FALSE;
if (async->priv->idle_id)
{
g_source_remove (async->priv->idle_id);
async->priv->idle_id = 0;
unref_async = TRUE;
}
async->priv->synced = TRUE;
while ((callback_info = g_queue_pop_head (&async->priv->callbacks)))
{
if (callback_info->gobject)
{
g_object_ref (callback_info->gobject);
g_object_weak_unref (callback_info->gobject,
(GWeakNotify) pika_async_callback_weak_notify,
callback_info);
}
callback_info->callback (async, callback_info->data);
if (callback_info->gobject)
g_object_unref (callback_info->gobject);
g_slice_free (PikaAsyncCallbackInfo, callback_info);
}
if (unref_async)
g_object_unref (async);
}
/* public functions */
/* creates a new PikaAsync object, initially unsynced and placed in the
* "running" state.
*/
PikaAsync *
pika_async_new (void)
{
return g_object_new (PIKA_TYPE_ASYNC,
NULL);
}
/* checks if 'async' is synced.
*
* may only be called on the main thread.
*/
gboolean
pika_async_is_synced (PikaAsync *async)
{
g_return_val_if_fail (PIKA_IS_ASYNC (async), FALSE);
return async->priv->synced;
}
/* registers a callback to be called when 'async' transitions to the "stopped"
* state. if 'async' is already stopped, the callback may be called directly.
*
* callbacks are called in the order in which they were added. 'async' is
* guaranteed to be kept alive, even without an external reference, between the
* point where it was stopped, and until all callbacks added while 'async' was
* externally referenced have been called.
*
* the callback is guaranteed to be called on the main thread.
*
* may only be called on the main thread.
*/
void
pika_async_add_callback (PikaAsync *async,
PikaAsyncCallback callback,
gpointer data)
{
PikaAsyncCallbackInfo *callback_info;
g_return_if_fail (PIKA_IS_ASYNC (async));
g_return_if_fail (callback != NULL);
g_mutex_lock (&async->priv->mutex);
if (async->priv->stopped && g_queue_is_empty (&async->priv->callbacks))
{
async->priv->synced = TRUE;
g_mutex_unlock (&async->priv->mutex);
callback (async, data);
return;
}
callback_info = g_slice_new0 (PikaAsyncCallbackInfo);
callback_info->async = async;
callback_info->callback = callback;
callback_info->data = data;
g_queue_push_tail (&async->priv->callbacks, callback_info);
g_mutex_unlock (&async->priv->mutex);
}
/* same as 'pika_async_add_callback()', however, takes an additional 'gobject'
* argument, which should be a GObject.
*
* 'gobject' is guaranteed to remain alive for the duration of the callback.
*
* When 'gobject' is destroyed, the callback is automatically removed.
*/
void
pika_async_add_callback_for_object (PikaAsync *async,
PikaAsyncCallback callback,
gpointer data,
gpointer gobject)
{
PikaAsyncCallbackInfo *callback_info;
g_return_if_fail (PIKA_IS_ASYNC (async));
g_return_if_fail (callback != NULL);
g_return_if_fail (G_IS_OBJECT (gobject));
g_mutex_lock (&async->priv->mutex);
if (async->priv->stopped && g_queue_is_empty (&async->priv->callbacks))
{
async->priv->synced = TRUE;
g_mutex_unlock (&async->priv->mutex);
g_object_ref (gobject);
callback (async, data);
g_object_unref (gobject);
return;
}
callback_info = g_slice_new0 (PikaAsyncCallbackInfo);
callback_info->async = async;
callback_info->callback = callback;
callback_info->data = data;
callback_info->gobject = gobject;
g_queue_push_tail (&async->priv->callbacks, callback_info);
g_object_weak_ref (gobject,
(GWeakNotify) pika_async_callback_weak_notify,
callback_info);
g_mutex_unlock (&async->priv->mutex);
}
/* removes all callbacks previously registered through
* 'pika_async_add_callback()', matching 'callback' and 'data', which hasn't
* been called yet.
*
* may only be called on the main thread.
*/
void
pika_async_remove_callback (PikaAsync *async,
PikaAsyncCallback callback,
gpointer data)
{
GList *iter;
gboolean unref_async = FALSE;
g_return_if_fail (PIKA_IS_ASYNC (async));
g_return_if_fail (callback != NULL);
g_mutex_lock (&async->priv->mutex);
iter = g_queue_peek_head_link (&async->priv->callbacks);
while (iter)
{
PikaAsyncCallbackInfo *callback_info = iter->data;
GList *next = g_list_next (iter);
if (callback_info->callback == callback &&
callback_info->data == data)
{
if (callback_info->gobject)
{
g_object_weak_unref (
callback_info->gobject,
(GWeakNotify) pika_async_callback_weak_notify,
callback_info);
}
g_queue_delete_link (&async->priv->callbacks, iter);
g_slice_free (PikaAsyncCallbackInfo, callback_info);
}
iter = next;
}
if (g_queue_is_empty (&async->priv->callbacks) && async->priv->idle_id)
{
g_source_remove (async->priv->idle_id);
async->priv->idle_id = 0;
unref_async = TRUE;
}
g_mutex_unlock (&async->priv->mutex);
if (unref_async)
g_object_unref (async);
}
/* checks if 'async' is in the "stopped" state.
*
* may only be called on the async thread.
*/
gboolean
pika_async_is_stopped (PikaAsync *async)
{
g_return_val_if_fail (PIKA_IS_ASYNC (async), FALSE);
return async->priv->stopped;
}
/* transitions 'async' to the "stopped" state, indicating that the task
* completed normally, possibly providing a result.
*
* 'async' shall be in the "running" state.
*
* may only be called on the async thread.
*/
void
pika_async_finish (PikaAsync *async,
gpointer result)
{
pika_async_finish_full (async, result, NULL);
}
/* same as 'pika_async_finish()', taking an additional GDestroyNotify function,
* used for freeing the result when 'async' is destroyed.
*/
void
pika_async_finish_full (PikaAsync *async,
gpointer result,
GDestroyNotify result_destroy_func)
{
g_return_if_fail (PIKA_IS_ASYNC (async));
g_return_if_fail (! async->priv->stopped);
g_mutex_lock (&async->priv->mutex);
async->priv->finished = TRUE;
async->priv->result = result;
async->priv->result_destroy_func = result_destroy_func;
pika_async_stop (async);
g_mutex_unlock (&async->priv->mutex);
}
/* checks if 'async' completed normally, using 'pika_async_finish()' (in
* contrast to 'pika_async_abort()').
*
* 'async' shall be in the "stopped" state.
*
* may only be called on the async thread, or on the main thread when 'async'
* is synced.
*/
gboolean
pika_async_is_finished (PikaAsync *async)
{
g_return_val_if_fail (PIKA_IS_ASYNC (async), FALSE);
g_return_val_if_fail (async->priv->stopped, FALSE);
return async->priv->finished;
}
/* returns the result of 'async', as passed to 'pika_async_finish()'.
*
* 'async' shall be in the "stopped" state, and should have completed normally.
*
* may only be called on the async thread, or on the main thread when 'async'
* is synced.
*/
gpointer
pika_async_get_result (PikaAsync *async)
{
g_return_val_if_fail (PIKA_IS_ASYNC (async), NULL);
g_return_val_if_fail (async->priv->stopped, NULL);
g_return_val_if_fail (async->priv->finished, NULL);
return async->priv->result;
}
/* transitions 'async' to the "stopped" state, indicating that the task
* was stopped before completion (normally, in response to a
* 'pika_cancelable_cancel()' call).
*
* 'async' shall be in the "running" state.
*
* may only be called on the async thread.
*/
void
pika_async_abort (PikaAsync *async)
{
g_return_if_fail (PIKA_IS_ASYNC (async));
g_return_if_fail (! async->priv->stopped);
g_mutex_lock (&async->priv->mutex);
pika_async_stop (async);
g_mutex_unlock (&async->priv->mutex);
}
/* checks if cancellation of 'async' has been requested.
*
* note that a return value of TRUE only indicates that
* 'pika_cancelable_cancel()' has been called for 'async', and not that 'async'
* is stopped, or, if it is stopped, that it was aborted.
*/
gboolean
pika_async_is_canceled (PikaAsync *async)
{
g_return_val_if_fail (PIKA_IS_ASYNC (async), FALSE);
return async->priv->canceled;
}
/* a convenience function, canceling 'async' and waiting for it to stop.
*
* may only be called on the main thread.
*/
void
pika_async_cancel_and_wait (PikaAsync *async)
{
g_return_if_fail (PIKA_IS_ASYNC (async));
pika_cancelable_cancel (PIKA_CANCELABLE (async));
pika_waitable_wait (PIKA_WAITABLE (async));
}
/* public functions (stats) */
gint
pika_async_get_n_running (void)
{
return pika_async_n_running;
}

99
app/core/pikaasync.h Normal file
View File

@ -0,0 +1,99 @@
/* 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
*
* pikaasync.h
* Copyright (C) 2018 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 __PIKA_ASYNC_H__
#define __PIKA_ASYNC_H__
#define PIKA_TYPE_ASYNC (pika_async_get_type ())
#define PIKA_ASYNC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_ASYNC, PikaAsync))
#define PIKA_ASYNC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_ASYNC, PikaAsyncClass))
#define PIKA_IS_ASYNC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_ASYNC))
#define PIKA_IS_ASYNC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_ASYNC))
#define PIKA_ASYNC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_ASYNC, PikaAsyncClass))
typedef void (* PikaAsyncCallback) (PikaAsync *async,
gpointer data);
typedef struct _PikaAsyncPrivate PikaAsyncPrivate;
typedef struct _PikaAsyncClass PikaAsyncClass;
struct _PikaAsync
{
GObject parent_instance;
PikaAsyncPrivate *priv;
};
struct _PikaAsyncClass
{
GObjectClass parent_class;
/* signals */
void (* waiting) (PikaAsync *async);
};
GType pika_async_get_type (void) G_GNUC_CONST;
PikaAsync * pika_async_new (void);
gboolean pika_async_is_synced (PikaAsync *async);
void pika_async_add_callback (PikaAsync *async,
PikaAsyncCallback callback,
gpointer data);
void pika_async_add_callback_for_object (PikaAsync *async,
PikaAsyncCallback callback,
gpointer data,
gpointer gobject);
void pika_async_remove_callback (PikaAsync *async,
PikaAsyncCallback callback,
gpointer data);
gboolean pika_async_is_stopped (PikaAsync *async);
void pika_async_finish (PikaAsync *async,
gpointer result);
void pika_async_finish_full (PikaAsync *async,
gpointer result,
GDestroyNotify result_destroy_func);
gboolean pika_async_is_finished (PikaAsync *async);
gpointer pika_async_get_result (PikaAsync *async);
void pika_async_abort (PikaAsync *async);
gboolean pika_async_is_canceled (PikaAsync *async);
void pika_async_cancel_and_wait (PikaAsync *async);
/* stats */
gint pika_async_get_n_running (void);
#endif /* __PIKA_ASYNC_H__ */

354
app/core/pikaasyncset.c Normal file
View File

@ -0,0 +1,354 @@
/* 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
*
* pikaasyncset.c
* Copyright (C) 2018 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 <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include "libpikabase/pikabase.h"
#include "core-types.h"
#include "pikaasync.h"
#include "pikaasyncset.h"
#include "pikacancelable.h"
#include "pikawaitable.h"
enum
{
PROP_0,
PROP_EMPTY
};
struct _PikaAsyncSetPrivate
{
GHashTable *asyncs;
};
/* local function prototypes */
static void pika_async_set_waitable_iface_init (PikaWaitableInterface *iface);
static void pika_async_set_cancelable_iface_init (PikaCancelableInterface *iface);
static void pika_async_set_dispose (GObject *object);
static void pika_async_set_finalize (GObject *object);
static void pika_async_set_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_async_set_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void pika_async_set_wait (PikaWaitable *waitable);
static gboolean pika_async_set_try_wait (PikaWaitable *waitable);
static gboolean pika_async_set_wait_until (PikaWaitable *waitable,
gint64 end_time);
static void pika_async_set_cancel (PikaCancelable *cancelable);
static void pika_async_set_async_callback (PikaAsync *async,
PikaAsyncSet *async_set);
G_DEFINE_TYPE_WITH_CODE (PikaAsyncSet, pika_async_set, G_TYPE_OBJECT,
G_ADD_PRIVATE (PikaAsyncSet)
G_IMPLEMENT_INTERFACE (PIKA_TYPE_WAITABLE,
pika_async_set_waitable_iface_init)
G_IMPLEMENT_INTERFACE (PIKA_TYPE_CANCELABLE,
pika_async_set_cancelable_iface_init))
#define parent_class pika_async_set_parent_class
/* private functions */
static void
pika_async_set_class_init (PikaAsyncSetClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = pika_async_set_dispose;
object_class->finalize = pika_async_set_finalize;
object_class->set_property = pika_async_set_set_property;
object_class->get_property = pika_async_set_get_property;
g_object_class_install_property (object_class, PROP_EMPTY,
g_param_spec_boolean ("empty",
NULL, NULL,
FALSE,
PIKA_PARAM_READABLE));
}
static void
pika_async_set_waitable_iface_init (PikaWaitableInterface *iface)
{
iface->wait = pika_async_set_wait;
iface->try_wait = pika_async_set_try_wait;
iface->wait_until = pika_async_set_wait_until;
}
static void
pika_async_set_cancelable_iface_init (PikaCancelableInterface *iface)
{
iface->cancel = pika_async_set_cancel;
}
static void
pika_async_set_init (PikaAsyncSet *async_set)
{
async_set->priv = pika_async_set_get_instance_private (async_set);
async_set->priv->asyncs = g_hash_table_new (NULL, NULL);
}
static void
pika_async_set_dispose (GObject *object)
{
PikaAsyncSet *async_set = PIKA_ASYNC_SET (object);
pika_async_set_clear (async_set);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
pika_async_set_finalize (GObject *object)
{
PikaAsyncSet *async_set = PIKA_ASYNC_SET (object);
g_hash_table_unref (async_set->priv->asyncs);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
pika_async_set_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
switch (property_id)
{
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_async_set_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaAsyncSet *async_set = PIKA_ASYNC_SET (object);
switch (property_id)
{
case PROP_EMPTY:
g_value_set_boolean (value, pika_async_set_is_empty (async_set));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_async_set_wait (PikaWaitable *waitable)
{
PikaAsyncSet *async_set = PIKA_ASYNC_SET (waitable);
while (! pika_async_set_is_empty (async_set))
{
PikaAsync *async;
GHashTableIter iter;
g_hash_table_iter_init (&iter, async_set->priv->asyncs);
g_hash_table_iter_next (&iter, (gpointer *) &async, NULL);
pika_waitable_wait (PIKA_WAITABLE (async));
}
}
static gboolean
pika_async_set_try_wait (PikaWaitable *waitable)
{
PikaAsyncSet *async_set = PIKA_ASYNC_SET (waitable);
while (! pika_async_set_is_empty (async_set))
{
PikaAsync *async;
GHashTableIter iter;
g_hash_table_iter_init (&iter, async_set->priv->asyncs);
g_hash_table_iter_next (&iter, (gpointer *) &async, NULL);
if (! pika_waitable_try_wait (PIKA_WAITABLE (async)))
return FALSE;
}
return TRUE;
}
static gboolean
pika_async_set_wait_until (PikaWaitable *waitable,
gint64 end_time)
{
PikaAsyncSet *async_set = PIKA_ASYNC_SET (waitable);
while (! pika_async_set_is_empty (async_set))
{
PikaAsync *async;
GHashTableIter iter;
g_hash_table_iter_init (&iter, async_set->priv->asyncs);
g_hash_table_iter_next (&iter, (gpointer *) &async, NULL);
if (! pika_waitable_wait_until (PIKA_WAITABLE (async), end_time))
return FALSE;
}
return TRUE;
}
static void
pika_async_set_cancel (PikaCancelable *cancelable)
{
PikaAsyncSet *async_set = PIKA_ASYNC_SET (cancelable);
GList *list;
list = g_hash_table_get_keys (async_set->priv->asyncs);
g_list_foreach (list, (GFunc) g_object_ref, NULL);
g_list_foreach (list, (GFunc) pika_cancelable_cancel, NULL);
g_list_free_full (list, g_object_unref);
}
static void
pika_async_set_async_callback (PikaAsync *async,
PikaAsyncSet *async_set)
{
g_hash_table_remove (async_set->priv->asyncs, async);
if (pika_async_set_is_empty (async_set))
g_object_notify (G_OBJECT (async_set), "empty");
}
/* public functions */
PikaAsyncSet *
pika_async_set_new (void)
{
return g_object_new (PIKA_TYPE_ASYNC_SET,
NULL);
}
void
pika_async_set_add (PikaAsyncSet *async_set,
PikaAsync *async)
{
g_return_if_fail (PIKA_IS_ASYNC_SET (async_set));
g_return_if_fail (PIKA_IS_ASYNC (async));
if (g_hash_table_add (async_set->priv->asyncs, async))
{
if (g_hash_table_size (async_set->priv->asyncs) == 1)
g_object_notify (G_OBJECT (async_set), "empty");
pika_async_add_callback (
async,
(PikaAsyncCallback) pika_async_set_async_callback,
async_set);
}
}
void
pika_async_set_remove (PikaAsyncSet *async_set,
PikaAsync *async)
{
g_return_if_fail (PIKA_IS_ASYNC_SET (async_set));
g_return_if_fail (PIKA_IS_ASYNC (async));
if (g_hash_table_remove (async_set->priv->asyncs, async))
{
pika_async_remove_callback (
async,
(PikaAsyncCallback) pika_async_set_async_callback,
async_set);
if (g_hash_table_size (async_set->priv->asyncs) == 0)
g_object_notify (G_OBJECT (async_set), "empty");
}
}
void
pika_async_set_clear (PikaAsyncSet *async_set)
{
PikaAsync *async;
GHashTableIter iter;
g_return_if_fail (PIKA_IS_ASYNC_SET (async_set));
if (pika_async_set_is_empty (async_set))
return;
g_hash_table_iter_init (&iter, async_set->priv->asyncs);
while (g_hash_table_iter_next (&iter, (gpointer *) &async, NULL))
{
pika_async_remove_callback (
async,
(PikaAsyncCallback) pika_async_set_async_callback,
async_set);
}
g_hash_table_remove_all (async_set->priv->asyncs);
g_object_notify (G_OBJECT (async_set), "empty");
}
gboolean
pika_async_set_is_empty (PikaAsyncSet *async_set)
{
g_return_val_if_fail (PIKA_IS_ASYNC_SET (async_set), FALSE);
return g_hash_table_size (async_set->priv->asyncs) == 0;
}

65
app/core/pikaasyncset.h Normal file
View File

@ -0,0 +1,65 @@
/* 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
*
* pikaasyncset.h
* Copyright (C) 2018 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 __PIKA_ASYNC_SET_H__
#define __PIKA_ASYNC_SET_H__
#define PIKA_TYPE_ASYNC_SET (pika_async_set_get_type ())
#define PIKA_ASYNC_SET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_ASYNC_SET, PikaAsyncSet))
#define PIKA_ASYNC_SET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_ASYNC_SET, PikaAsyncSetClass))
#define PIKA_IS_ASYNC_SET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_ASYNC_SET))
#define PIKA_IS_ASYNC_SET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_ASYNC_SET))
#define PIKA_ASYNC_SET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_ASYNC_SET, PikaAsyncSetClass))
typedef struct _PikaAsyncSetPrivate PikaAsyncSetPrivate;
typedef struct _PikaAsyncSetClass PikaAsyncSetClass;
struct _PikaAsyncSet
{
GObject parent_instance;
PikaAsyncSetPrivate *priv;
};
struct _PikaAsyncSetClass
{
GObjectClass parent_class;
};
GType pika_async_set_get_type (void) G_GNUC_CONST;
PikaAsyncSet * pika_async_set_new (void);
void pika_async_set_add (PikaAsyncSet *async_set,
PikaAsync *async);
void pika_async_set_remove (PikaAsyncSet *async_set,
PikaAsync *async);
void pika_async_set_clear (PikaAsyncSet *async_set);
gboolean pika_async_set_is_empty (PikaAsyncSet *async_set);
#endif /* __PIKA_ASYNC_SET_H__ */

152
app/core/pikaauxitem.c Normal file
View File

@ -0,0 +1,152 @@
/* 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 <gio/gio.h>
#include "libpikabase/pikabase.h"
#include "core-types.h"
#include "pikaauxitem.h"
enum
{
REMOVED,
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_ID
};
struct _PikaAuxItemPrivate
{
guint32 aux_item_id;
};
static void pika_aux_item_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void pika_aux_item_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (PikaAuxItem, pika_aux_item, G_TYPE_OBJECT)
static guint pika_aux_item_signals[LAST_SIGNAL] = { 0 };
static void
pika_aux_item_class_init (PikaAuxItemClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
pika_aux_item_signals[REMOVED] =
g_signal_new ("removed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (PikaAuxItemClass, removed),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
object_class->get_property = pika_aux_item_get_property;
object_class->set_property = pika_aux_item_set_property;
klass->removed = NULL;
g_object_class_install_property (object_class, PROP_ID,
g_param_spec_uint ("id", NULL, NULL,
0, G_MAXUINT32, 0,
G_PARAM_CONSTRUCT_ONLY |
PIKA_PARAM_READWRITE));
}
static void
pika_aux_item_init (PikaAuxItem *aux_item)
{
aux_item->priv = pika_aux_item_get_instance_private (aux_item);
}
static void
pika_aux_item_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaAuxItem *aux_item = PIKA_AUX_ITEM (object);
switch (property_id)
{
case PROP_ID:
g_value_set_uint (value, aux_item->priv->aux_item_id);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_aux_item_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PikaAuxItem *aux_item = PIKA_AUX_ITEM (object);
switch (property_id)
{
case PROP_ID:
aux_item->priv->aux_item_id = g_value_get_uint (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
guint32
pika_aux_item_get_id (PikaAuxItem *aux_item)
{
g_return_val_if_fail (PIKA_IS_AUX_ITEM (aux_item), 0);
return aux_item->priv->aux_item_id;
}
void
pika_aux_item_removed (PikaAuxItem *aux_item)
{
g_return_if_fail (PIKA_IS_AUX_ITEM (aux_item));
g_signal_emit (aux_item, pika_aux_item_signals[REMOVED], 0);
}

60
app/core/pikaauxitem.h Normal file
View File

@ -0,0 +1,60 @@
/* 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/>.
*/
#ifndef __PIKA_AUX_ITEM_H__
#define __PIKA_AUX_ITEM_H__
#define PIKA_TYPE_AUX_ITEM (pika_aux_item_get_type ())
#define PIKA_AUX_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_AUX_ITEM, PikaAuxItem))
#define PIKA_AUX_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_AUX_ITEM, PikaAuxItemClass))
#define PIKA_IS_AUX_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_AUX_ITEM))
#define PIKA_IS_AUX_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_AUX_ITEM))
#define PIKA_AUX_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_AUX_ITEM, PikaAuxItemClass))
typedef struct _PikaAuxItemPrivate PikaAuxItemPrivate;
typedef struct _PikaAuxItemClass PikaAuxItemClass;
struct _PikaAuxItem
{
GObject parent_instance;
PikaAuxItemPrivate *priv;
};
struct _PikaAuxItemClass
{
GObjectClass parent_class;
/* signals */
void (* removed) (PikaAuxItem *aux_item);
};
GType pika_aux_item_get_type (void) G_GNUC_CONST;
guint32 pika_aux_item_get_id (PikaAuxItem *aux_item);
void pika_aux_item_removed (PikaAuxItem *aux_item);
#endif /* __PIKA_AUX_ITEM_H__ */

144
app/core/pikaauxitemundo.c Normal file
View File

@ -0,0 +1,144 @@
/* 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 <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include "libpikabase/pikabase.h"
#include "core-types.h"
#include "pikaauxitem.h"
#include "pikaauxitemundo.h"
enum
{
PROP_0,
PROP_AUX_ITEM
};
static void pika_aux_item_undo_constructed (GObject *object);
static void pika_aux_item_undo_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_aux_item_undo_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void pika_aux_item_undo_free (PikaUndo *undo,
PikaUndoMode undo_mode);
G_DEFINE_ABSTRACT_TYPE (PikaAuxItemUndo, pika_aux_item_undo, PIKA_TYPE_UNDO)
#define parent_class pika_aux_item_undo_parent_class
static void
pika_aux_item_undo_class_init (PikaAuxItemUndoClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
PikaUndoClass *undo_class = PIKA_UNDO_CLASS (klass);
object_class->constructed = pika_aux_item_undo_constructed;
object_class->set_property = pika_aux_item_undo_set_property;
object_class->get_property = pika_aux_item_undo_get_property;
undo_class->free = pika_aux_item_undo_free;
g_object_class_install_property (object_class, PROP_AUX_ITEM,
g_param_spec_object ("aux-item", NULL, NULL,
PIKA_TYPE_AUX_ITEM,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
}
static void
pika_aux_item_undo_init (PikaAuxItemUndo *undo)
{
}
static void
pika_aux_item_undo_constructed (GObject *object)
{
PikaAuxItemUndo *aux_item_undo = PIKA_AUX_ITEM_UNDO (object);
G_OBJECT_CLASS (parent_class)->constructed (object);
pika_assert (PIKA_IS_AUX_ITEM (aux_item_undo->aux_item));
}
static void
pika_aux_item_undo_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PikaAuxItemUndo *aux_item_undo = PIKA_AUX_ITEM_UNDO (object);
switch (property_id)
{
case PROP_AUX_ITEM:
aux_item_undo->aux_item = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_aux_item_undo_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaAuxItemUndo *aux_item_undo = PIKA_AUX_ITEM_UNDO (object);
switch (property_id)
{
case PROP_AUX_ITEM:
g_value_set_object (value, aux_item_undo->aux_item);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_aux_item_undo_free (PikaUndo *undo,
PikaUndoMode undo_mode)
{
PikaAuxItemUndo *aux_item_undo = PIKA_AUX_ITEM_UNDO (undo);
g_clear_object (&aux_item_undo->aux_item);
PIKA_UNDO_CLASS (parent_class)->free (undo, undo_mode);
}

View File

@ -0,0 +1,56 @@
/* 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/>.
*/
#ifndef __PIKA_AUX_ITEM_UNDO_H__
#define __PIKA_AUX_ITEM_UNDO_H__
#include "pikaundo.h"
#define PIKA_TYPE_AUX_ITEM_UNDO (pika_aux_item_undo_get_type ())
#define PIKA_AUX_ITEM_UNDO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_AUX_ITEM_UNDO, PikaAuxItemUndo))
#define PIKA_AUX_ITEM_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_AUX_ITEM_UNDO, PikaAuxItemUndoClass))
#define PIKA_IS_AUX_ITEM_UNDO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_AUX_ITEM_UNDO))
#define PIKA_IS_AUX_ITEM_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_AUX_ITEM_UNDO))
#define PIKA_AUX_ITEM_UNDO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_AUX_ITEM_UNDO, PikaAuxItemUndoClass))
typedef struct _PikaAuxItemUndo PikaAuxItemUndo;
typedef struct _PikaAuxItemUndoClass PikaAuxItemUndoClass;
struct _PikaAuxItemUndo
{
PikaUndo parent_instance;
PikaAuxItem *aux_item;
};
struct _PikaAuxItemUndoClass
{
PikaUndoClass parent_class;
};
GType pika_aux_item_undo_get_type (void) G_GNUC_CONST;
#endif /* __PIKA_AUX_ITEM_UNDO_H__ */

View File

@ -0,0 +1,38 @@
/* 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
*
* pikabacktrace-backend.h
* Copyright (C) 2018 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 __PIKA_BACKTRACE_BACKEND_H__
#define __PIKA_BACKTRACE_BACKEND_H__
#ifdef __gnu_linux__
# define PIKA_BACKTRACE_BACKEND_LINUX
#elif defined (G_OS_WIN32) && defined (ARCH_X86)
# define PIKA_BACKTRACE_BACKEND_WINDOWS
#else
# define PIKA_BACKTRACE_BACKEND_NONE
#endif
#endif /* __PIKA_BACKTRACE_BACKEND_H__ */

View File

@ -0,0 +1,729 @@
/* 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-1999 Spencer Kimball and Peter Mattis
*
* pikabacktrace-linux.c
* Copyright (C) 2018 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/>.
*/
#define _GNU_SOURCE
#include "config.h"
#include <gio/gio.h>
#include "pikabacktrace-backend.h"
#ifdef PIKA_BACKTRACE_BACKEND_LINUX
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <signal.h>
#include <execinfo.h>
#include <dlfcn.h>
#include <string.h>
#include <stdio.h>
#ifdef HAVE_LIBBACKTRACE
#include <backtrace.h>
#endif
#ifdef HAVE_LIBUNWIND
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#endif
#include "core-types.h"
#include "pikabacktrace.h"
#define MAX_N_THREADS 256
#define MAX_N_FRAMES 256
#define MAX_THREAD_NAME_SIZE 32
#define N_SKIPPED_FRAMES 2
#define MAX_WAIT_TIME (G_TIME_SPAN_SECOND / 20)
#define BACKTRACE_SIGNAL SIGUSR1
typedef struct _PikaBacktraceThread PikaBacktraceThread;
struct _PikaBacktraceThread
{
pid_t tid;
gchar name[MAX_THREAD_NAME_SIZE];
gchar state;
guintptr frames[MAX_N_FRAMES];
gint n_frames;
};
struct _PikaBacktrace
{
PikaBacktraceThread *threads;
gint n_threads;
};
/* local function prototypes */
static inline gint pika_backtrace_normalize_frame (PikaBacktrace *backtrace,
gint thread,
gint frame);
static gint pika_backtrace_enumerate_threads (gboolean include_current_thread,
pid_t *threads,
gint size);
static void pika_backtrace_read_thread_name (pid_t tid,
gchar *name,
gint size);
static gchar pika_backtrace_read_thread_state (pid_t tid);
static void pika_backtrace_signal_handler (gint signum);
/* static variables */
static GMutex mutex;
static gint n_initializations;
static gboolean initialized;
static struct sigaction orig_action;
static pid_t blacklisted_threads[MAX_N_THREADS];
static gint n_blacklisted_threads;
static PikaBacktrace *handler_backtrace;
static gint handler_n_remaining_threads;
static gint handler_lock;
#ifdef HAVE_LIBBACKTRACE
static struct backtrace_state *backtrace_state;
#endif
static const gchar * const blacklisted_thread_names[] =
{
"gmain",
"threaded-ml"
};
/* private functions */
static inline gint
pika_backtrace_normalize_frame (PikaBacktrace *backtrace,
gint thread,
gint frame)
{
if (frame >= 0)
return frame + N_SKIPPED_FRAMES;
else
return backtrace->threads[thread].n_frames + frame;
}
static gint
pika_backtrace_enumerate_threads (gboolean include_current_thread,
pid_t *threads,
gint size)
{
DIR *dir;
struct dirent *dirent;
pid_t tid;
gint n_threads;
dir = opendir ("/proc/self/task");
if (! dir)
return 0;
tid = syscall (SYS_gettid);
n_threads = 0;
while (n_threads < size && (dirent = readdir (dir)))
{
pid_t id = g_ascii_strtoull (dirent->d_name, NULL, 10);
if (id)
{
if (! include_current_thread && id == tid)
id = 0;
}
if (id)
{
gint i;
for (i = 0; i < n_blacklisted_threads; i++)
{
if (id == blacklisted_threads[i])
{
id = 0;
break;
}
}
}
if (id)
threads[n_threads++] = id;
}
closedir (dir);
return n_threads;
}
static void
pika_backtrace_read_thread_name (pid_t tid,
gchar *name,
gint size)
{
gchar filename[64];
gint fd;
if (size <= 0)
return;
name[0] = '\0';
g_snprintf (filename, sizeof (filename),
"/proc/self/task/%llu/comm",
(unsigned long long) tid);
fd = open (filename, O_RDONLY);
if (fd >= 0)
{
gint n = read (fd, name, size);
if (n > 0)
name[n - 1] = '\0';
close (fd);
}
}
static gchar
pika_backtrace_read_thread_state (pid_t tid)
{
gchar buffer[64];
gint fd;
gchar state = '\0';
g_snprintf (buffer, sizeof (buffer),
"/proc/self/task/%llu/stat",
(unsigned long long) tid);
fd = open (buffer, O_RDONLY);
if (fd >= 0)
{
gint n = read (fd, buffer, sizeof (buffer));
if (n > 0)
buffer[n - 1] = '\0';
sscanf (buffer, "%*d %*s %c", &state);
close (fd);
}
return state;
}
static void
pika_backtrace_signal_handler (gint signum)
{
PikaBacktrace *curr_backtrace;
gint lock;
do
{
lock = g_atomic_int_get (&handler_lock);
if (lock < 0)
continue;
}
while (! g_atomic_int_compare_and_exchange (&handler_lock, lock, lock + 1));
curr_backtrace = g_atomic_pointer_get (&handler_backtrace);
if (curr_backtrace)
{
pid_t tid = syscall (SYS_gettid);
gint i;
for (i = 0; i < curr_backtrace->n_threads; i++)
{
PikaBacktraceThread *thread = &curr_backtrace->threads[i];
if (thread->tid == tid)
{
thread->n_frames = backtrace ((gpointer *) thread->frames,
MAX_N_FRAMES);
g_atomic_int_dec_and_test (&handler_n_remaining_threads);
break;
}
}
}
g_atomic_int_dec_and_test (&handler_lock);
}
/* public functions */
void
pika_backtrace_init (void)
{
#ifdef HAVE_LIBBACKTRACE
backtrace_state = backtrace_create_state (NULL, 0, NULL, NULL);
#endif
}
gboolean
pika_backtrace_start (void)
{
g_mutex_lock (&mutex);
if (n_initializations == 0)
{
struct sigaction action = {};
action.sa_handler = pika_backtrace_signal_handler;
action.sa_flags = SA_RESTART;
sigemptyset (&action.sa_mask);
if (sigaction (BACKTRACE_SIGNAL, &action, &orig_action) == 0)
{
pid_t *threads;
gint n_threads;
gint i;
n_blacklisted_threads = 0;
threads = g_new (pid_t, MAX_N_THREADS);
n_threads = pika_backtrace_enumerate_threads (TRUE,
threads, MAX_N_THREADS);
for (i = 0; i < n_threads; i++)
{
gchar name[MAX_THREAD_NAME_SIZE];
gint j;
pika_backtrace_read_thread_name (threads[i],
name, MAX_THREAD_NAME_SIZE);
for (j = 0; j < G_N_ELEMENTS (blacklisted_thread_names); j++)
{
if (! strcmp (name, blacklisted_thread_names[j]))
{
blacklisted_threads[n_blacklisted_threads++] = threads[i];
}
}
}
g_free (threads);
initialized = TRUE;
}
}
n_initializations++;
g_mutex_unlock (&mutex);
return initialized;
}
void
pika_backtrace_stop (void)
{
g_return_if_fail (n_initializations > 0);
g_mutex_lock (&mutex);
n_initializations--;
if (n_initializations == 0 && initialized)
{
if (sigaction (BACKTRACE_SIGNAL, &orig_action, NULL) < 0)
g_warning ("failed to restore original backtrace signal handler");
initialized = FALSE;
}
g_mutex_unlock (&mutex);
}
PikaBacktrace *
pika_backtrace_new (gboolean include_current_thread)
{
PikaBacktrace *backtrace;
pid_t pid;
pid_t *threads;
gint n_threads;
gint64 start_time;
gint i;
if (! initialized)
return NULL;
pid = getpid ();
threads = g_new (pid_t, MAX_N_THREADS);
n_threads = pika_backtrace_enumerate_threads (include_current_thread,
threads, MAX_N_THREADS);
if (n_threads == 0)
{
g_free (threads);
return NULL;
}
g_mutex_lock (&mutex);
backtrace = g_slice_new (PikaBacktrace);
backtrace->threads = g_new (PikaBacktraceThread, n_threads);
backtrace->n_threads = n_threads;
while (! g_atomic_int_compare_and_exchange (&handler_lock, 0, -1));
g_atomic_pointer_set (&handler_backtrace, backtrace);
g_atomic_int_set (&handler_n_remaining_threads, n_threads);
g_atomic_int_set (&handler_lock, 0);
for (i = 0; i < n_threads; i++)
{
PikaBacktraceThread *thread = &backtrace->threads[i];
thread->tid = threads[i];
thread->n_frames = 0;
pika_backtrace_read_thread_name (thread->tid,
thread->name, MAX_THREAD_NAME_SIZE);
thread->state = pika_backtrace_read_thread_state (thread->tid);
syscall (SYS_tgkill, pid, threads[i], BACKTRACE_SIGNAL);
}
g_free (threads);
start_time = g_get_monotonic_time ();
while (g_atomic_int_get (&handler_n_remaining_threads) > 0)
{
gint64 time = g_get_monotonic_time ();
if (time - start_time > MAX_WAIT_TIME)
break;
g_usleep (1000);
}
while (! g_atomic_int_compare_and_exchange (&handler_lock, 0, -1));
g_atomic_pointer_set (&handler_backtrace, NULL);
g_atomic_int_set (&handler_lock, 0);
#if 0
if (handler_n_remaining_threads > 0)
{
gint j = 0;
for (i = 0; i < n_threads; i++)
{
if (backtrace->threads[i].n_frames == 0)
{
if (n_blacklisted_threads < MAX_N_THREADS)
{
blacklisted_threads[n_blacklisted_threads++] =
backtrace->threads[i].tid;
}
}
else
{
if (j < i)
backtrace->threads[j] = backtrace->threads[i];
j++;
}
}
n_threads = j;
}
#endif
g_mutex_unlock (&mutex);
if (n_threads == 0)
{
pika_backtrace_free (backtrace);
return NULL;
}
return backtrace;
}
void
pika_backtrace_free (PikaBacktrace *backtrace)
{
if (! backtrace)
return;
g_free (backtrace->threads);
g_slice_free (PikaBacktrace, backtrace);
}
gint
pika_backtrace_get_n_threads (PikaBacktrace *backtrace)
{
g_return_val_if_fail (backtrace != NULL, 0);
return backtrace->n_threads;
}
guintptr
pika_backtrace_get_thread_id (PikaBacktrace *backtrace,
gint thread)
{
g_return_val_if_fail (backtrace != NULL, 0);
g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, 0);
return backtrace->threads[thread].tid;
}
const gchar *
pika_backtrace_get_thread_name (PikaBacktrace *backtrace,
gint thread)
{
g_return_val_if_fail (backtrace != NULL, NULL);
g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, NULL);
if (backtrace->threads[thread].name[0])
return backtrace->threads[thread].name;
else
return NULL;
}
gboolean
pika_backtrace_is_thread_running (PikaBacktrace *backtrace,
gint thread)
{
g_return_val_if_fail (backtrace != NULL, FALSE);
g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, FALSE);
return backtrace->threads[thread].state == 'R';
}
gint
pika_backtrace_find_thread_by_id (PikaBacktrace *backtrace,
guintptr thread_id,
gint thread_hint)
{
pid_t tid = thread_id;
gint i;
g_return_val_if_fail (backtrace != NULL, -1);
if (thread_hint >= 0 &&
thread_hint < backtrace->n_threads &&
backtrace->threads[thread_hint].tid == tid)
{
return thread_hint;
}
for (i = 0; i < backtrace->n_threads; i++)
{
if (backtrace->threads[i].tid == tid)
return i;
}
return -1;
}
gint
pika_backtrace_get_n_frames (PikaBacktrace *backtrace,
gint thread)
{
g_return_val_if_fail (backtrace != NULL, 0);
g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, 0);
return MAX (backtrace->threads[thread].n_frames - N_SKIPPED_FRAMES, 0);
}
guintptr
pika_backtrace_get_frame_address (PikaBacktrace *backtrace,
gint thread,
gint frame)
{
g_return_val_if_fail (backtrace != NULL, 0);
g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, 0);
frame = pika_backtrace_normalize_frame (backtrace, thread, frame);
g_return_val_if_fail (frame >= N_SKIPPED_FRAMES &&
frame < backtrace->threads[thread].n_frames, 0);
return backtrace->threads[thread].frames[frame];
}
#ifdef HAVE_LIBBACKTRACE
static void
pika_backtrace_syminfo_callback (PikaBacktraceAddressInfo *info,
guintptr pc,
const gchar *symname,
guintptr symval,
guintptr symsize)
{
if (symname)
g_strlcpy (info->symbol_name, symname, sizeof (info->symbol_name));
info->symbol_address = symval;
}
static gint
pika_backtrace_pcinfo_callback (PikaBacktraceAddressInfo *info,
guintptr pc,
const gchar *filename,
gint lineno,
const gchar *function)
{
if (function)
g_strlcpy (info->symbol_name, function, sizeof (info->symbol_name));
if (filename)
g_strlcpy (info->source_file, filename, sizeof (info->source_file));
info->source_line = lineno;
return 0;
}
#endif /* HAVE_LIBBACKTRACE */
gboolean
pika_backtrace_get_address_info (guintptr address,
PikaBacktraceAddressInfo *info)
{
Dl_info dl_info;
gboolean result = FALSE;
g_return_val_if_fail (info != NULL, FALSE);
info->object_name[0] = '\0';
info->symbol_name[0] = '\0';
info->symbol_address = 0;
info->source_file[0] = '\0';
info->source_line = 0;
if (dladdr ((gpointer) address, &dl_info))
{
if (dl_info.dli_fname)
{
g_strlcpy (info->object_name, dl_info.dli_fname,
sizeof (info->object_name));
}
if (dl_info.dli_sname)
{
g_strlcpy (info->symbol_name, dl_info.dli_sname,
sizeof (info->symbol_name));
}
info->symbol_address = (guintptr) dl_info.dli_saddr;
result = TRUE;
}
#ifdef HAVE_LIBBACKTRACE
if (backtrace_state)
{
backtrace_syminfo (
backtrace_state, address,
(backtrace_syminfo_callback) pika_backtrace_syminfo_callback,
NULL,
info);
backtrace_pcinfo (
backtrace_state, address,
(backtrace_full_callback) pika_backtrace_pcinfo_callback,
NULL,
info);
result = TRUE;
}
#endif /* HAVE_LIBBACKTRACE */
#ifdef HAVE_LIBUNWIND
/* we use libunwind to get the symbol name, when available, even if dladdr() or
* libbacktrace already found one, since it provides more descriptive names in
* some cases, and, in particular, full symbol names for C++ lambdas.
*
* note that, in some cases, this can result in a discrepancy between the
* symbol name, and the corresponding source location.
*/
#if 0
if (! info->symbol_name[0])
#endif
{
unw_context_t context = {};
unw_cursor_t cursor;
unw_word_t offset;
if (unw_init_local (&cursor, &context) == 0 &&
unw_set_reg (&cursor, UNW_REG_IP, address) == 0 &&
unw_get_proc_name (&cursor,
info->symbol_name, sizeof (info->symbol_name),
&offset) == 0)
{
info->symbol_address = address - offset;
result = TRUE;
}
}
#endif /* HAVE_LIBUNWIND */
return result;
}
#endif /* PIKA_BACKTRACE_BACKEND_LINUX */

View File

@ -0,0 +1,130 @@
/* 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-1999 Spencer Kimball and Peter Mattis
*
* pikabacktrace-none.c
* Copyright (C) 2018 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 <gio/gio.h>
#include "pikabacktrace-backend.h"
#ifdef PIKA_BACKTRACE_BACKEND_NONE
#include "core-types.h"
#include "pikabacktrace.h"
/* public functions */
void
pika_backtrace_init (void)
{
}
gboolean
pika_backtrace_start (void)
{
return FALSE;
}
void
pika_backtrace_stop (void)
{
}
PikaBacktrace *
pika_backtrace_new (gboolean include_current_thread)
{
return NULL;
}
void
pika_backtrace_free (PikaBacktrace *backtrace)
{
g_return_if_fail (backtrace == NULL);
}
gint
pika_backtrace_get_n_threads (PikaBacktrace *backtrace)
{
g_return_val_if_reached (0);
}
guintptr
pika_backtrace_get_thread_id (PikaBacktrace *backtrace,
gint thread)
{
g_return_val_if_reached (0);
}
const gchar *
pika_backtrace_get_thread_name (PikaBacktrace *backtrace,
gint thread)
{
g_return_val_if_reached (NULL);
}
gboolean
pika_backtrace_is_thread_running (PikaBacktrace *backtrace,
gint thread)
{
g_return_val_if_reached (FALSE);
}
gint
pika_backtrace_find_thread_by_id (PikaBacktrace *backtrace,
guintptr thread_id,
gint thread_hint)
{
g_return_val_if_reached (-1);
}
gint
pika_backtrace_get_n_frames (PikaBacktrace *backtrace,
gint thread)
{
g_return_val_if_reached (0);
}
guintptr
pika_backtrace_get_frame_address (PikaBacktrace *backtrace,
gint thread,
gint frame)
{
g_return_val_if_reached (0);
}
gboolean
pika_backtrace_get_address_info (guintptr address,
PikaBacktraceAddressInfo *info)
{
return FALSE;
}
#endif /* PIKA_BACKTRACE_BACKEND_NONE */

View File

@ -0,0 +1,744 @@
/* 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-1999 Spencer Kimball and Peter Mattis
*
* pikabacktrace-windows.c
* Copyright (C) 2018 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 <gio/gio.h>
#include "pikabacktrace-backend.h"
#ifdef PIKA_BACKTRACE_BACKEND_WINDOWS
#include <windows.h>
#include <psapi.h>
#include <tlhelp32.h>
#include <dbghelp.h>
#include <string.h>
#include "core-types.h"
#include "pikabacktrace.h"
#define MAX_N_THREADS 256
#define MAX_N_FRAMES 256
#define THREAD_ENUMERATION_INTERVAL G_TIME_SPAN_SECOND
typedef struct _Thread Thread;
typedef struct _PikaBacktraceThread PikaBacktraceThread;
struct _Thread
{
DWORD tid;
union
{
gchar *name;
guint64 time;
};
};
struct _PikaBacktraceThread
{
DWORD tid;
const gchar *name;
guint64 time;
guint64 last_time;
guintptr frames[MAX_N_FRAMES];
gint n_frames;
};
struct _PikaBacktrace
{
PikaBacktraceThread *threads;
gint n_threads;
};
/* local function prototypes */
static inline gint pika_backtrace_normalize_frame (PikaBacktrace *backtrace,
gint thread,
gint frame);
static void pika_backtrace_set_thread_name (DWORD tid,
const gchar *name);
static gboolean pika_backtrace_enumerate_threads (void);
static LONG WINAPI pika_backtrace_exception_handler (PEXCEPTION_POINTERS info);
/* static variables */
static GMutex mutex;
static gint n_initializations;
static gboolean initialized;
Thread threads[MAX_N_THREADS];
gint n_threads;
gint64 last_thread_enumeration_time;
Thread thread_names[MAX_N_THREADS];
gint n_thread_names;
gint thread_names_spinlock;
Thread thread_times[MAX_N_THREADS];
gint n_thread_times;
DWORD WINAPI (* pika_backtrace_SymSetOptions) (DWORD SymOptions);
BOOL WINAPI (* pika_backtrace_SymInitialize) (HANDLE hProcess,
PCSTR UserSearchPath,
BOOL fInvadeProcess);
BOOL WINAPI (* pika_backtrace_SymCleanup) (HANDLE hProcess);
BOOL WINAPI (* pika_backtrace_SymFromAddr) (HANDLE hProcess,
DWORD64 Address,
PDWORD64 Displacement,
PSYMBOL_INFO Symbol);
BOOL WINAPI (* pika_backtrace_SymGetLineFromAddr64) (HANDLE hProcess,
DWORD64 qwAddr,
PDWORD pdwDisplacement,
PIMAGEHLP_LINE64 Line64);
/* private functions */
static inline gint
pika_backtrace_normalize_frame (PikaBacktrace *backtrace,
gint thread,
gint frame)
{
if (frame >= 0)
return frame;
else
return backtrace->threads[thread].n_frames + frame;
}
static void
pika_backtrace_set_thread_name (DWORD tid,
const gchar *name)
{
while (! g_atomic_int_compare_and_exchange (&thread_names_spinlock,
0, 1));
if (n_thread_names < MAX_N_THREADS)
{
Thread *thread = &thread_names[n_thread_names++];
thread->tid = tid;
thread->name = g_strdup (name);
}
g_atomic_int_set (&thread_names_spinlock, 0);
}
static gboolean
pika_backtrace_enumerate_threads (void)
{
HANDLE hThreadSnap;
THREADENTRY32 te32;
DWORD pid;
gint64 time;
time = g_get_monotonic_time ();
if (time - last_thread_enumeration_time < THREAD_ENUMERATION_INTERVAL)
return n_threads > 0;
last_thread_enumeration_time = time;
n_threads = 0;
hThreadSnap = CreateToolhelp32Snapshot (TH32CS_SNAPTHREAD, 0);
if (hThreadSnap == INVALID_HANDLE_VALUE)
return FALSE;
te32.dwSize = sizeof (te32);
if (! Thread32First (hThreadSnap, &te32))
{
CloseHandle (hThreadSnap);
return FALSE;
}
pid = GetCurrentProcessId ();
while (! g_atomic_int_compare_and_exchange (&thread_names_spinlock, 0, 1));
do
{
if (n_threads == MAX_N_THREADS)
break;
if (te32.th32OwnerProcessID == pid)
{
Thread *thread = &threads[n_threads++];
gint i;
thread->tid = te32.th32ThreadID;
thread->name = NULL;
for (i = n_thread_names - 1; i >= 0; i--)
{
if (thread->tid == thread_names[i].tid)
{
thread->name = thread_names[i].name;
break;
}
}
}
}
while (Thread32Next (hThreadSnap, &te32));
g_atomic_int_set (&thread_names_spinlock, 0);
CloseHandle (hThreadSnap);
return n_threads > 0;
}
static LONG WINAPI
pika_backtrace_exception_handler (PEXCEPTION_POINTERS info)
{
#define EXCEPTION_SET_THREAD_NAME ((DWORD) 0x406D1388)
typedef struct _THREADNAME_INFO
{
DWORD dwType; /* must be 0x1000 */
LPCSTR szName; /* pointer to name (in user addr space) */
DWORD dwThreadID; /* thread ID (-1=caller thread) */
DWORD dwFlags; /* reserved for future use, must be zero */
} THREADNAME_INFO;
if (info->ExceptionRecord != NULL &&
info->ExceptionRecord->ExceptionCode == EXCEPTION_SET_THREAD_NAME &&
info->ExceptionRecord->NumberParameters *
sizeof (ULONG_PTR) == sizeof (THREADNAME_INFO))
{
THREADNAME_INFO name_info;
memcpy (&name_info, info->ExceptionRecord->ExceptionInformation,
sizeof (name_info));
if (name_info.dwType == 0x1000)
{
DWORD tid = name_info.dwThreadID;
if (tid == -1)
tid = GetCurrentThreadId ();
pika_backtrace_set_thread_name (tid, name_info.szName);
return EXCEPTION_CONTINUE_EXECUTION;
}
}
return EXCEPTION_CONTINUE_SEARCH;
#undef EXCEPTION_SET_THREAD_NAME
}
/* public functions */
void
pika_backtrace_init (void)
{
pika_backtrace_set_thread_name (GetCurrentThreadId (), g_get_prgname ());
AddVectoredExceptionHandler (TRUE, pika_backtrace_exception_handler);
}
gboolean
pika_backtrace_start (void)
{
g_mutex_lock (&mutex);
if (n_initializations == 0)
{
HMODULE hModule;
DWORD options;
hModule = LoadLibraryW (L"mgwhelp.dll");
#define INIT_PROC(name) \
G_STMT_START \
{ \
pika_backtrace_##name = name; \
\
if (hModule) \
{ \
gpointer proc = GetProcAddress (hModule, #name); \
\
if (proc) \
pika_backtrace_##name = proc; \
} \
} \
G_STMT_END
INIT_PROC (SymSetOptions);
INIT_PROC (SymInitialize);
INIT_PROC (SymCleanup);
INIT_PROC (SymFromAddr);
INIT_PROC (SymGetLineFromAddr64);
#undef INIT_PROC
options = SymGetOptions ();
options &= ~SYMOPT_UNDNAME;
options |= SYMOPT_OMAP_FIND_NEAREST |
SYMOPT_DEFERRED_LOADS |
SYMOPT_DEBUG;
#ifdef ARCH_X86_64
options |= SYMOPT_INCLUDE_32BIT_MODULES;
#endif
pika_backtrace_SymSetOptions (options);
if (pika_backtrace_SymInitialize (GetCurrentProcess (), NULL, TRUE))
{
n_threads = 0;
last_thread_enumeration_time = 0;
n_thread_times = 0;
initialized = TRUE;
}
}
n_initializations++;
g_mutex_unlock (&mutex);
return initialized;
}
void
pika_backtrace_stop (void)
{
g_return_if_fail (n_initializations > 0);
g_mutex_lock (&mutex);
n_initializations--;
if (n_initializations == 0)
{
if (initialized)
{
pika_backtrace_SymCleanup (GetCurrentProcess ());
initialized = FALSE;
}
}
g_mutex_unlock (&mutex);
}
PikaBacktrace *
pika_backtrace_new (gboolean include_current_thread)
{
PikaBacktrace *backtrace;
HANDLE hProcess;
DWORD tid;
gint i;
if (! initialized)
return NULL;
g_mutex_lock (&mutex);
if (! pika_backtrace_enumerate_threads ())
{
g_mutex_unlock (&mutex);
return NULL;
}
hProcess = GetCurrentProcess ();
tid = GetCurrentThreadId ();
backtrace = g_slice_new (PikaBacktrace);
backtrace->threads = g_new (PikaBacktraceThread, n_threads);
backtrace->n_threads = 0;
for (i = 0; i < n_threads; i++)
{
PikaBacktraceThread *thread = &backtrace->threads[backtrace->n_threads];
HANDLE hThread;
CONTEXT context = {};
STACKFRAME64 frame = {};
DWORD machine_type;
FILETIME creation_time;
FILETIME exit_time;
FILETIME kernel_time;
FILETIME user_time;
if (! include_current_thread && threads[i].tid == tid)
continue;
hThread = OpenThread (THREAD_QUERY_INFORMATION |
THREAD_GET_CONTEXT |
THREAD_SUSPEND_RESUME,
FALSE,
threads[i].tid);
if (hThread == INVALID_HANDLE_VALUE)
continue;
if (threads[i].tid != tid && SuspendThread (hThread) == (DWORD) -1)
{
CloseHandle (hThread);
continue;
}
context.ContextFlags = CONTEXT_FULL;
if (! GetThreadContext (hThread, &context))
{
if (threads[i].tid != tid)
ResumeThread (hThread);
CloseHandle (hThread);
continue;
}
#ifdef ARCH_X86_64
machine_type = IMAGE_FILE_MACHINE_AMD64;
frame.AddrPC.Offset = context.Rip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Rsp;
frame.AddrStack.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Rbp;
frame.AddrFrame.Mode = AddrModeFlat;
#elif defined (ARCH_X86)
machine_type = IMAGE_FILE_MACHINE_I386;
frame.AddrPC.Offset = context.Eip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Esp;
frame.AddrStack.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Ebp;
frame.AddrFrame.Mode = AddrModeFlat;
#else
#error unsupported architecture
#endif
thread->tid = threads[i].tid;
thread->name = threads[i].name;
thread->last_time = 0;
thread->time = 0;
thread->n_frames = 0;
while (thread->n_frames < MAX_N_FRAMES &&
StackWalk64 (machine_type, hProcess, hThread, &frame, &context,
NULL,
SymFunctionTableAccess64,
SymGetModuleBase64,
NULL))
{
thread->frames[thread->n_frames++] = frame.AddrPC.Offset;
if (frame.AddrPC.Offset == frame.AddrReturn.Offset)
break;
}
if (GetThreadTimes (hThread,
&creation_time, &exit_time,
&kernel_time, &user_time))
{
thread->time = (((guint64) kernel_time.dwHighDateTime << 32) |
((guint64) kernel_time.dwLowDateTime)) +
(((guint64) user_time.dwHighDateTime << 32) |
((guint64) user_time.dwLowDateTime));
if (i < n_thread_times && thread->tid == thread_times[i].tid)
{
thread->last_time = thread_times[i].time;
}
else
{
gint j;
for (j = 0; j < n_thread_times; j++)
{
if (thread->tid == thread_times[j].tid)
{
thread->last_time = thread_times[j].time;
break;
}
}
}
}
if (threads[i].tid != tid)
ResumeThread (hThread);
CloseHandle (hThread);
if (thread->n_frames > 0)
backtrace->n_threads++;
}
n_thread_times = backtrace->n_threads;
for (i = 0; i < backtrace->n_threads; i++)
{
thread_times[i].tid = backtrace->threads[i].tid;
thread_times[i].time = backtrace->threads[i].time;
}
g_mutex_unlock (&mutex);
if (backtrace->n_threads == 0)
{
pika_backtrace_free (backtrace);
return NULL;
}
return backtrace;
}
void
pika_backtrace_free (PikaBacktrace *backtrace)
{
if (backtrace)
{
g_free (backtrace->threads);
g_slice_free (PikaBacktrace, backtrace);
}
}
gint
pika_backtrace_get_n_threads (PikaBacktrace *backtrace)
{
g_return_val_if_fail (backtrace != NULL, 0);
return backtrace->n_threads;
}
guintptr
pika_backtrace_get_thread_id (PikaBacktrace *backtrace,
gint thread)
{
g_return_val_if_fail (backtrace != NULL, 0);
g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, 0);
return backtrace->threads[thread].tid;
}
const gchar *
pika_backtrace_get_thread_name (PikaBacktrace *backtrace,
gint thread)
{
g_return_val_if_fail (backtrace != NULL, NULL);
g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, NULL);
return backtrace->threads[thread].name;
}
gboolean
pika_backtrace_is_thread_running (PikaBacktrace *backtrace,
gint thread)
{
g_return_val_if_fail (backtrace != NULL, FALSE);
g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, FALSE);
return backtrace->threads[thread].time >
backtrace->threads[thread].last_time;
}
gint
pika_backtrace_find_thread_by_id (PikaBacktrace *backtrace,
guintptr thread_id,
gint thread_hint)
{
DWORD tid = thread_id;
gint i;
g_return_val_if_fail (backtrace != NULL, -1);
if (thread_hint >= 0 &&
thread_hint < backtrace->n_threads &&
backtrace->threads[thread_hint].tid == tid)
{
return thread_hint;
}
for (i = 0; i < backtrace->n_threads; i++)
{
if (backtrace->threads[i].tid == tid)
return i;
}
return -1;
}
gint
pika_backtrace_get_n_frames (PikaBacktrace *backtrace,
gint thread)
{
g_return_val_if_fail (backtrace != NULL, 0);
g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, 0);
return backtrace->threads[thread].n_frames;
}
guintptr
pika_backtrace_get_frame_address (PikaBacktrace *backtrace,
gint thread,
gint frame)
{
g_return_val_if_fail (backtrace != NULL, 0);
g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, 0);
frame = pika_backtrace_normalize_frame (backtrace, thread, frame);
g_return_val_if_fail (frame >= 0 &&
frame < backtrace->threads[thread].n_frames, 0);
return backtrace->threads[thread].frames[frame];
}
#define IN_MIDDLE_OF_UTF8_CODEPOINT(c) \
((c & 0xc0) == 0x80)
static void
utf8_copy_sized (char *dest,
const char *src,
size_t size)
{
if (size == 0)
return;
strncpy (dest, src, size);
if (dest[size - 1] != 0)
{
char *p = &dest[size - 1];
/* Checking for p > dest is not actually needed, but
* it's useful in case of malformed source string. */
while (IN_MIDDLE_OF_UTF8_CODEPOINT (*p) && G_LIKELY (p > dest))
*p-- = 0;
*p = 0;
}
}
#undef IN_MIDDLE_OF_UTF8_CODEPOINT
gboolean
pika_backtrace_get_address_info (guintptr address,
PikaBacktraceAddressInfo *info)
{
SYMBOL_INFO *symbol_info;
HANDLE hProcess;
HMODULE hModule;
DWORD64 offset = 0;
IMAGEHLP_LINE64 line = {};
DWORD line_offset = 0;
gboolean result = FALSE;
wchar_t buf[MAX_PATH];
DWORD wchars_count;
hProcess = GetCurrentProcess ();
hModule = (HMODULE) (guintptr) SymGetModuleBase64 (hProcess, address);
info->object_name[0] = '\0';
wchars_count = sizeof (buf) / sizeof (buf[0]);
if (hModule && GetModuleFileNameExW (hProcess, hModule, buf, wchars_count))
{
char *object_name;
if ((object_name = g_utf16_to_utf8 (buf, -1, NULL, NULL, NULL)))
{
utf8_copy_sized (info->object_name, object_name, sizeof (info->object_name));
result = TRUE;
g_free (object_name);
}
}
symbol_info = g_malloc (sizeof (SYMBOL_INFO) +
sizeof (info->symbol_name) - 1);
symbol_info->SizeOfStruct = sizeof (SYMBOL_INFO);
symbol_info->MaxNameLen = sizeof (info->symbol_name);
if (pika_backtrace_SymFromAddr (hProcess, address,
&offset, symbol_info))
{
g_strlcpy (info->symbol_name, symbol_info->Name,
sizeof (info->symbol_name));
info->symbol_address = offset ? address - offset : 0;
result = TRUE;
}
else
{
info->symbol_name[0] = '\0';
info->symbol_address = 0;
}
g_free (symbol_info);
if (pika_backtrace_SymGetLineFromAddr64 (hProcess, address,
&line_offset, &line))
{
g_strlcpy (info->source_file, line.FileName,
sizeof (info->source_file));
info->source_line = line.LineNumber;
result = TRUE;
}
else
{
info->source_file[0] = '\0';
info->source_line = 0;
}
return result;
}
#endif /* PIKA_BACKTRACE_BACKEND_WINDOWS */

74
app/core/pikabacktrace.h Normal file
View File

@ -0,0 +1,74 @@
/* 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
*
* pikabacktrace.h
* Copyright (C) 2018 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 __PIKA_BACKTRACE_H__
#define __PIKA_BACKTRACE_H__
typedef struct _PikaBacktraceAddressInfo PikaBacktraceAddressInfo;
struct _PikaBacktraceAddressInfo
{
gchar object_name[256];
gchar symbol_name[256];
guintptr symbol_address;
gchar source_file[256];
gint source_line;
};
void pika_backtrace_init (void);
gboolean pika_backtrace_start (void);
void pika_backtrace_stop (void);
PikaBacktrace * pika_backtrace_new (gboolean include_current_thread);
void pika_backtrace_free (PikaBacktrace *backtrace);
gint pika_backtrace_get_n_threads (PikaBacktrace *backtrace);
guintptr pika_backtrace_get_thread_id (PikaBacktrace *backtrace,
gint thread);
const gchar * pika_backtrace_get_thread_name (PikaBacktrace *backtrace,
gint thread);
gboolean pika_backtrace_is_thread_running (PikaBacktrace *backtrace,
gint thread);
gint pika_backtrace_find_thread_by_id (PikaBacktrace *backtrace,
guintptr thread_id,
gint thread_hint);
gint pika_backtrace_get_n_frames (PikaBacktrace *backtrace,
gint thread);
guintptr pika_backtrace_get_frame_address (PikaBacktrace *backtrace,
gint thread,
gint frame);
gboolean pika_backtrace_get_address_info (guintptr address,
PikaBacktraceAddressInfo *info);
#endif /* __PIKA_BACKTRACE_H__ */

195
app/core/pikabezierdesc.c Normal file
View File

@ -0,0 +1,195 @@
/* 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
*
* pikabezierdesc.c
* Copyright (C) 2010 Michael Natterer <mitch@gimp.org>
*
* 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 <gegl.h>
#include <cairo.h>
#include "core-types.h"
#include "pikabezierdesc.h"
#include "pikaboundary.h"
G_DEFINE_BOXED_TYPE (PikaBezierDesc, pika_bezier_desc, pika_bezier_desc_copy, pika_bezier_desc_free)
PikaBezierDesc *
pika_bezier_desc_new (cairo_path_data_t *data,
gint n_data)
{
PikaBezierDesc *desc;
g_return_val_if_fail (n_data == 0 || data != NULL, NULL);
desc = g_slice_new (PikaBezierDesc);
desc->status = CAIRO_STATUS_SUCCESS;
desc->num_data = n_data;
desc->data = data;
return desc;
}
static void
add_polyline (GArray *path_data,
const PikaVector2 *points,
gint n_points,
gboolean closed)
{
PikaVector2 prev = { 0.0, 0.0, };
cairo_path_data_t pd;
gint i;
for (i = 0; i < n_points; i++)
{
/* compress multiple identical coordinates */
if (i == 0 ||
prev.x != points[i].x ||
prev.y != points[i].y)
{
pd.header.type = (i == 0) ? CAIRO_PATH_MOVE_TO : CAIRO_PATH_LINE_TO;
pd.header.length = 2;
g_array_append_val (path_data, pd);
pd.point.x = points[i].x;
pd.point.y = points[i].y;
g_array_append_val (path_data, pd);
prev = points[i];
}
}
/* close the polyline when needed */
if (closed)
{
pd.header.type = CAIRO_PATH_CLOSE_PATH;
pd.header.length = 1;
g_array_append_val (path_data, pd);
}
}
PikaBezierDesc *
pika_bezier_desc_new_from_bound_segs (PikaBoundSeg *bound_segs,
gint n_bound_segs,
gint n_bound_groups)
{
GArray *path_data;
PikaVector2 *points;
gint n_points;
gint seg;
gint i;
guint path_data_len;
g_return_val_if_fail (bound_segs != NULL, NULL);
g_return_val_if_fail (n_bound_segs > 0, NULL);
path_data = g_array_new (FALSE, FALSE, sizeof (cairo_path_data_t));
points = g_new0 (PikaVector2, n_bound_segs + 4);
seg = 0;
n_points = 0;
points[n_points].x = (gdouble) (bound_segs[0].x1);
points[n_points].y = (gdouble) (bound_segs[0].y1);
n_points++;
for (i = 0; i < n_bound_groups; i++)
{
while (bound_segs[seg].x1 != -1 ||
bound_segs[seg].x2 != -1 ||
bound_segs[seg].y1 != -1 ||
bound_segs[seg].y2 != -1)
{
points[n_points].x = (gdouble) (bound_segs[seg].x1);
points[n_points].y = (gdouble) (bound_segs[seg].y1);
n_points++;
seg++;
}
/* Close the stroke points up */
points[n_points] = points[0];
n_points++;
add_polyline (path_data, points, n_points, TRUE);
n_points = 0;
seg++;
points[n_points].x = (gdouble) (bound_segs[seg].x1);
points[n_points].y = (gdouble) (bound_segs[seg].y1);
n_points++;
}
g_free (points);
path_data_len = path_data->len;
return pika_bezier_desc_new ((cairo_path_data_t *) g_array_free (path_data, FALSE),
path_data_len);
}
void
pika_bezier_desc_translate (PikaBezierDesc *desc,
gdouble offset_x,
gdouble offset_y)
{
gint i, j;
g_return_if_fail (desc != NULL);
for (i = 0; i < desc->num_data; i += desc->data[i].header.length)
for (j = 1; j < desc->data[i].header.length; ++j)
{
desc->data[i+j].point.x += offset_x;
desc->data[i+j].point.y += offset_y;
}
}
PikaBezierDesc *
pika_bezier_desc_copy (const PikaBezierDesc *desc)
{
g_return_val_if_fail (desc != NULL, NULL);
return pika_bezier_desc_new (g_memdup2 (desc->data,
desc->num_data * sizeof (cairo_path_data_t)),
desc->num_data);
}
void
pika_bezier_desc_free (PikaBezierDesc *desc)
{
g_return_if_fail (desc != NULL);
g_free (desc->data);
g_slice_free (PikaBezierDesc, desc);
}

51
app/core/pikabezierdesc.h Normal file
View File

@ -0,0 +1,51 @@
/* 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
*
* pikabezierdesc.h
* Copyright (C) 2010 Michael Natterer <mitch@gimp.org>
*
* 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 __PIKA_BEZIER_DESC_H__
#define __PIKA_BEZIER_DESC_H__
#define PIKA_TYPE_BEZIER_DESC (pika_bezier_desc_get_type ())
GType pika_bezier_desc_get_type (void) G_GNUC_CONST;
/* takes ownership of "data" */
PikaBezierDesc * pika_bezier_desc_new (cairo_path_data_t *data,
gint n_data);
/* expects sorted PikaBoundSegs */
PikaBezierDesc * pika_bezier_desc_new_from_bound_segs (PikaBoundSeg *bound_segs,
gint n_bound_segs,
gint n_bound_groups);
void pika_bezier_desc_translate (PikaBezierDesc *desc,
gdouble offset_x,
gdouble offset_y);
PikaBezierDesc * pika_bezier_desc_copy (const PikaBezierDesc *desc);
void pika_bezier_desc_free (PikaBezierDesc *desc);
#endif /* __PIKA_BEZIER_DESC_H__ */

1020
app/core/pikaboundary.c Normal file

File diff suppressed because it is too large Load Diff

72
app/core/pikaboundary.h Normal file
View File

@ -0,0 +1,72 @@
/* 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/>.
*/
#ifndef __PIKA_BOUNDARY_H__
#define __PIKA_BOUNDARY_H__
/* half intensity for mask */
#define PIKA_BOUNDARY_HALF_WAY 0.5
typedef enum
{
PIKA_BOUNDARY_WITHIN_BOUNDS,
PIKA_BOUNDARY_IGNORE_BOUNDS
} PikaBoundaryType;
struct _PikaBoundSeg
{
gint x1;
gint y1;
gint x2;
gint y2;
guint open : 1;
guint visited : 1;
};
PikaBoundSeg * pika_boundary_find (GeglBuffer *buffer,
const GeglRectangle *region,
const Babl *format,
PikaBoundaryType type,
gint x1,
gint y1,
gint x2,
gint y2,
gfloat threshold,
gint *num_segs);
PikaBoundSeg * pika_boundary_sort (const PikaBoundSeg *segs,
gint num_segs,
gint *num_groups);
PikaBoundSeg * pika_boundary_simplify (PikaBoundSeg *sorted_segs,
gint num_groups,
gint *num_segs);
/* offsets in-place */
void pika_boundary_offset (PikaBoundSeg *segs,
gint num_segs,
gint off_x,
gint off_y);
#endif /* __PIKA_BOUNDARY_H__ */

View File

@ -0,0 +1,134 @@
/* 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 <cairo.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include "core-types.h"
#include "pikabezierdesc.h"
#include "pikaboundary.h"
#include "pikabrush.h"
#include "pikabrush-boundary.h"
#include "pikatempbuf.h"
static PikaBezierDesc *
pika_brush_transform_boundary_exact (PikaBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gdouble hardness)
{
const PikaTempBuf *mask;
mask = pika_brush_transform_mask (brush,
scale, aspect_ratio,
angle, reflect, hardness);
if (mask)
{
GeglBuffer *buffer;
PikaBoundSeg *bound_segs;
gint n_bound_segs;
buffer = pika_temp_buf_create_buffer ((PikaTempBuf *) mask);
bound_segs = pika_boundary_find (buffer, NULL,
babl_format ("Y float"),
PIKA_BOUNDARY_WITHIN_BOUNDS,
0, 0,
gegl_buffer_get_width (buffer),
gegl_buffer_get_height (buffer),
0.0,
&n_bound_segs);
g_object_unref (buffer);
if (bound_segs)
{
PikaBoundSeg *stroke_segs;
gint n_stroke_groups;
stroke_segs = pika_boundary_sort (bound_segs, n_bound_segs,
&n_stroke_groups);
g_free (bound_segs);
if (stroke_segs)
{
PikaBezierDesc *path;
path = pika_bezier_desc_new_from_bound_segs (stroke_segs,
n_bound_segs,
n_stroke_groups);
g_free (stroke_segs);
return path;
}
}
}
return NULL;
}
static PikaBezierDesc *
pika_brush_transform_boundary_approx (PikaBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gdouble hardness)
{
return pika_brush_transform_boundary_exact (brush,
scale, aspect_ratio,
angle, reflect, hardness);
}
PikaBezierDesc *
pika_brush_real_transform_boundary (PikaBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gdouble hardness,
gint *width,
gint *height)
{
pika_brush_transform_size (brush, scale, aspect_ratio, angle, reflect,
width, height);
if (*width < 256 && *height < 256)
{
return pika_brush_transform_boundary_exact (brush,
scale, aspect_ratio,
angle, reflect, hardness);
}
return pika_brush_transform_boundary_approx (brush,
scale, aspect_ratio,
angle, reflect, hardness);
}

View File

@ -0,0 +1,36 @@
/* 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/>.
*/
#ifndef __PIKA_BRUSH_BOUNDARY_H__
#define __PIKA_BRUSH_BOUNDARY_H__
PikaBezierDesc * pika_brush_real_transform_boundary (PikaBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gdouble hardness,
gint *width,
gint *height);
#endif /* __PIKA_BRUSH_BOUNDARY_H__ */

View File

@ -0,0 +1,53 @@
/* 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/>.
*/
#ifndef __PIKA_BRUSH_HEADER_H__
#define __PIKA_BRUSH_HEADER_H__
#define PIKA_BRUSH_MAGIC (('G' << 24) + ('I' << 16) + \
('M' << 8) + ('P' << 0))
#define PIKA_BRUSH_MAX_SIZE 10000 /* Max size in either dimension in px */
#define PIKA_BRUSH_MAX_NAME 256 /* Max length of the brush's name */
/* All field entries are MSB */
typedef struct _PikaBrushHeader PikaBrushHeader;
struct _PikaBrushHeader
{
guint32 header_size; /* = sizeof (PikaBrushHeader) + brush name */
guint32 version; /* brush file version # */
guint32 width; /* width of brush */
guint32 height; /* height of brush */
guint32 bytes; /* depth of brush in bytes */
guint32 magic_number; /* PIKA brush magic number */
guint32 spacing; /* brush spacing */
};
/* In a brush file, next comes the brush name, null-terminated.
* After that comes the brush data -- width * height * bytes bytes of
* it...
*/
#endif /* __PIKA_BRUSH_HEADER_H__ */

1217
app/core/pikabrush-load.c Normal file

File diff suppressed because it is too large Load Diff

47
app/core/pikabrush-load.h Normal file
View File

@ -0,0 +1,47 @@
/* 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/>.
*/
#ifndef __PIKA_BRUSH_LOAD_H__
#define __PIKA_BRUSH_LOAD_H__
#define PIKA_BRUSH_FILE_EXTENSION ".gbr"
#define PIKA_BRUSH_PIXMAP_FILE_EXTENSION ".gpb"
#define PIKA_BRUSH_PS_FILE_EXTENSION ".abr"
#define PIKA_BRUSH_PSP_FILE_EXTENSION ".jbr"
GList * pika_brush_load (PikaContext *context,
GFile *file,
GInputStream *input,
GError **error);
PikaBrush * pika_brush_load_brush (PikaContext *context,
GFile *file,
GInputStream *input,
GError **error);
GList * pika_brush_load_abr (PikaContext *context,
GFile *file,
GInputStream *input,
GError **error);
#endif /* __PIKA_BRUSH_LOAD_H__ */

View File

@ -0,0 +1,518 @@
/* 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
*
* pikabrush-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>
#include "libpikamath/pikamath.h"
extern "C"
{
#include "core-types.h"
#include "pikabrush.h"
#include "pikabrush-mipmap.h"
#include "pikabrush-private.h"
#include "pikatempbuf.h"
} /* extern "C" */
#define PIXELS_PER_THREAD \
(/* each thread costs as much as */ 64.0 * 64.0 /* pixels */)
#define PIKA_BRUSH_MIPMAP(brush, mipmaps, x, y) \
((*(mipmaps))[(y) * (brush)->priv->n_horz_mipmaps + (x)])
/* local function prototypes */
static void pika_brush_mipmap_clear (PikaBrush *brush,
PikaTempBuf ***mipmaps);
static const PikaTempBuf * pika_brush_mipmap_get (PikaBrush *brush,
const PikaTempBuf *source,
PikaTempBuf ***mipmaps,
gdouble *scale_x,
gdouble *scale_y);
static PikaTempBuf * pika_brush_mipmap_downscale (const PikaTempBuf *source);
static PikaTempBuf * pika_brush_mipmap_downscale_horz (const PikaTempBuf *source);
static PikaTempBuf * pika_brush_mipmap_downscale_vert (const PikaTempBuf *source);
/* private functions */
static void
pika_brush_mipmap_clear (PikaBrush *brush,
PikaTempBuf ***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], pika_temp_buf_unref);
}
g_clear_pointer (mipmaps, g_free);
}
}
static const PikaTempBuf *
pika_brush_mipmap_get (PikaBrush *brush,
const PikaTempBuf *source,
PikaTempBuf ***mipmaps,
gdouble *scale_x,
gdouble *scale_y)
{
gint x;
gint y;
gint i;
if (! source)
return NULL;
if (! *mipmaps)
{
gint width = pika_temp_buf_get_width (source);
gint height = pika_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 (PikaTempBuf *, brush->priv->n_horz_mipmaps *
brush->priv->n_vert_mipmaps);
PIKA_BRUSH_MIPMAP (brush, mipmaps, 0, 0) = pika_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 (PIKA_BRUSH_MIPMAP (brush, mipmaps, x, y))
return PIKA_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 (PIKA_BRUSH_MIPMAP (brush, mipmaps, u, v))
{
for (; x - u > y - v; u++)
{
PIKA_BRUSH_MIPMAP (brush, mipmaps, u + 1, v) =
pika_brush_mipmap_downscale_horz (
PIKA_BRUSH_MIPMAP (brush, mipmaps, u, v));
}
for (; y - v > x - u; v++)
{
PIKA_BRUSH_MIPMAP (brush, mipmaps, u, v + 1) =
pika_brush_mipmap_downscale_vert (
PIKA_BRUSH_MIPMAP (brush, mipmaps, u, v));
}
for (; u < x; u++, v++)
{
PIKA_BRUSH_MIPMAP (brush, mipmaps, u + 1, v + 1) =
pika_brush_mipmap_downscale (
PIKA_BRUSH_MIPMAP (brush, mipmaps, u, v));
}
return PIKA_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 PikaTempBuf *
downscale (const PikaTempBuf *source)
{
PikaTempBuf *destination;
gint width = pika_temp_buf_get_width (source);
gint height = pika_temp_buf_get_height (source);
width /= 2;
height /= 2;
destination = pika_temp_buf_new (width, height,
pika_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 *) pika_temp_buf_get_data (source);
T *dest0 = (T *) pika_temp_buf_get_data (destination);
gint src_stride = N * pika_temp_buf_get_width (source);
gint dest_stride = N * pika_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 PikaTempBuf *
downscale_horz (const PikaTempBuf *source)
{
PikaTempBuf *destination;
gint width = pika_temp_buf_get_width (source);
gint height = pika_temp_buf_get_height (source);
width /= 2;
destination = pika_temp_buf_new (width, height,
pika_temp_buf_get_format (source));
gegl_parallel_distribute_range (
height, PIXELS_PER_THREAD / width,
[=] (gint offset,
gint size)
{
const T *src0 = (const T *) pika_temp_buf_get_data (source);
T *dest0 = (T *) pika_temp_buf_get_data (destination);
gint src_stride = N * pika_temp_buf_get_width (source);
gint dest_stride = N * pika_temp_buf_get_width (destination);
gint y;
src0 += offset * src_stride;
dest0 += offset * dest_stride;
for (y = 0; y < size; y++)
{
const T *src = src0;
T *dest = dest0;
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;
}
src0 += src_stride;
dest0 += dest_stride;
}
});
return destination;
}
static PikaTempBuf *
downscale_vert (const PikaTempBuf *source)
{
PikaTempBuf *destination;
gint width = pika_temp_buf_get_width (source);
gint height = pika_temp_buf_get_height (source);
height /= 2;
destination = pika_temp_buf_new (width, height,
pika_temp_buf_get_format (source));
gegl_parallel_distribute_range (
width, PIXELS_PER_THREAD / height,
[=] (gint offset,
gint size)
{
const T *src0 = (const T *) pika_temp_buf_get_data (source);
T *dest0 = (T *) pika_temp_buf_get_data (destination);
gint src_stride = N * pika_temp_buf_get_width (source);
gint dest_stride = N * pika_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 PikaTempBuf *
pika_brush_mipmap_dispatch (const PikaTempBuf *source,
Func func)
{
const Babl *format = pika_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 PikaTempBuf *
pika_brush_mipmap_downscale (const PikaTempBuf *source)
{
return pika_brush_mipmap_dispatch (
source,
[&] (auto algorithms)
{
return algorithms.downscale (source);
});
}
static PikaTempBuf *
pika_brush_mipmap_downscale_horz (const PikaTempBuf *source)
{
return pika_brush_mipmap_dispatch (
source,
[&] (auto algorithms)
{
return algorithms.downscale_horz (source);
});
}
static PikaTempBuf *
pika_brush_mipmap_downscale_vert (const PikaTempBuf *source)
{
return pika_brush_mipmap_dispatch (
source,
[&] (auto algorithms)
{
return algorithms.downscale_vert (source);
});
}
/* public functions */
void
pika_brush_mipmap_clear (PikaBrush *brush)
{
pika_brush_mipmap_clear (brush, &brush->priv->mask_mipmaps);
pika_brush_mipmap_clear (brush, &brush->priv->pixmap_mipmaps);
}
const PikaTempBuf *
pika_brush_mipmap_get_mask (PikaBrush *brush,
gdouble *scale_x,
gdouble *scale_y)
{
return pika_brush_mipmap_get (brush,
brush->priv->mask,
&brush->priv->mask_mipmaps,
scale_x, scale_y);
}
const PikaTempBuf *
pika_brush_mipmap_get_pixmap (PikaBrush *brush,
gdouble *scale_x,
gdouble *scale_y)
{
return pika_brush_mipmap_get (brush,
brush->priv->pixmap,
&brush->priv->pixmap_mipmaps,
scale_x, scale_y);
}
gsize
pika_brush_mipmap_get_memsize (PikaBrush *brush)
{
gsize memsize = 0;
if (brush->priv->mask_mipmaps)
{
gint i;
for (i = 1;
i < brush->priv->n_horz_mipmaps * brush->priv->n_vert_mipmaps;
i++)
{
memsize += pika_temp_buf_get_memsize (brush->priv->mask_mipmaps[i]);
}
}
if (brush->priv->pixmap_mipmaps)
{
gint i;
for (i = 1;
i < brush->priv->n_horz_mipmaps * brush->priv->n_vert_mipmaps;
i++)
{
memsize += pika_temp_buf_get_memsize (brush->priv->pixmap_mipmaps[i]);
}
}
return memsize;
}

View File

@ -0,0 +1,42 @@
/* 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
*
* pikabrush-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 __PIKA_BRUSH_MIPMAP_H__
#define __PIKA_BRUSH_MIPMAP_H__
void pika_brush_mipmap_clear (PikaBrush *brush);
const PikaTempBuf * pika_brush_mipmap_get_mask (PikaBrush *brush,
gdouble *scale_x,
gdouble *scale_y);
const PikaTempBuf * pika_brush_mipmap_get_pixmap (PikaBrush *brush,
gdouble *scale_x,
gdouble *scale_y);
gsize pika_brush_mipmap_get_memsize (PikaBrush *brush);
#endif /* __PIKA_BRUSH_MIPMAP_H__ */

View File

@ -0,0 +1,51 @@
/* 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/>.
*/
#ifndef __PIKA_BRUSH_PRIVATE_H__
#define __PIKA_BRUSH_PRIVATE_H__
struct _PikaBrushPrivate
{
PikaTempBuf *mask; /* the actual mask */
PikaTempBuf *blurred_mask; /* blurred actual mask cached */
PikaTempBuf *pixmap; /* optional pixmap data */
PikaTempBuf *blurred_pixmap; /* optional pixmap data blurred cache */
gdouble blur_hardness;
gint n_horz_mipmaps;
gint n_vert_mipmaps;
PikaTempBuf **mask_mipmaps;
PikaTempBuf **pixmap_mipmaps;
gint spacing; /* brush's spacing */
PikaVector2 x_axis; /* for calculating brush spacing */
PikaVector2 y_axis; /* for calculating brush spacing */
gint use_count; /* for keeping the caches alive */
PikaBrushCache *mask_cache;
PikaBrushCache *pixmap_cache;
PikaBrushCache *boundary_cache;
};
#endif /* __PIKA_BRUSH_PRIVATE_H__ */

111
app/core/pikabrush-save.c Normal file
View File

@ -0,0 +1,111 @@
/* 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 <gdk-pixbuf/gdk-pixbuf.h>
#include "core-types.h"
#include "pikabrush.h"
#include "pikabrush-header.h"
#include "pikabrush-save.h"
#include "pikatempbuf.h"
gboolean
pika_brush_save (PikaData *data,
GOutputStream *output,
GError **error)
{
PikaBrush *brush = PIKA_BRUSH (data);
PikaTempBuf *mask = pika_brush_get_mask (brush);
PikaTempBuf *pixmap = pika_brush_get_pixmap (brush);
PikaBrushHeader header;
const gchar *name;
gint width;
gint height;
name = pika_object_get_name (brush);
width = pika_temp_buf_get_width (mask);
height = pika_temp_buf_get_height (mask);
header.header_size = g_htonl (sizeof (PikaBrushHeader) +
strlen (name) + 1);
header.version = g_htonl (2);
header.width = g_htonl (width);
header.height = g_htonl (height);
header.bytes = g_htonl (pixmap ? 4 : 1);
header.magic_number = g_htonl (PIKA_BRUSH_MAGIC);
header.spacing = g_htonl (pika_brush_get_spacing (brush));
if (! g_output_stream_write_all (output, &header, sizeof (header),
NULL, NULL, error))
{
return FALSE;
}
if (! g_output_stream_write_all (output, name, strlen (name) + 1,
NULL, NULL, error))
{
return FALSE;
}
if (pixmap)
{
gsize size = width * height * 4;
guchar *data = g_malloc (size);
guchar *p = pika_temp_buf_get_data (pixmap);
guchar *m = pika_temp_buf_get_data (mask);
guchar *d = data;
gint i;
for (i = 0; i < width * height; i++)
{
*d++ = *p++;
*d++ = *p++;
*d++ = *p++;
*d++ = *m++;
}
if (! g_output_stream_write_all (output, data, size,
NULL, NULL, error))
{
g_free (data);
return FALSE;
}
g_free (data);
}
else
{
if (! g_output_stream_write_all (output,
pika_temp_buf_get_data (mask),
pika_temp_buf_get_data_size (mask),
NULL, NULL, error))
{
return FALSE;
}
}
return TRUE;
}

32
app/core/pikabrush-save.h Normal file
View File

@ -0,0 +1,32 @@
/* 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/>.
*/
#ifndef __PIKA_BRUSH_SAVE_H__
#define __PIKA_BRUSH_SAVE_H__
/* don't call this function directly, use pika_data_save() instead */
gboolean pika_brush_save (PikaData *data,
GOutputStream *output,
GError **error);
#endif /* __PIKA_BRUSH_SAVE_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,63 @@
/* 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
*
* pikabrush-transform.h
*
* 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 __PIKA_BRUSH_TRANSFORM_H__
#define __PIKA_BRUSH_TRANSFORM_H__
/* virtual functions of PikaBrush, don't call directly */
void pika_brush_real_transform_size (PikaBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gint *scaled_width,
gint *scaled_height);
PikaTempBuf * pika_brush_real_transform_mask (PikaBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gdouble hardness);
PikaTempBuf * pika_brush_real_transform_pixmap (PikaBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gdouble hardness);
void pika_brush_transform_get_scale (gdouble scale,
gdouble aspect_ratio,
gdouble *scale_x,
gdouble *scale_y);
void pika_brush_transform_matrix (gdouble width,
gdouble height,
gdouble scale_x,
gdouble scale_y,
gdouble angle,
gboolean reflect,
PikaMatrix3 *matrix);
#endif /* __PIKA_BRUSH_TRANSFORM_H__ */

942
app/core/pikabrush.c Normal file
View File

@ -0,0 +1,942 @@
/* 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 <cairo.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include "libpikabase/pikabase.h"
#include "libpikamath/pikamath.h"
#include "core-types.h"
#include "pikabezierdesc.h"
#include "pikabrush.h"
#include "pikabrush-boundary.h"
#include "pikabrush-load.h"
#include "pikabrush-mipmap.h"
#include "pikabrush-private.h"
#include "pikabrush-save.h"
#include "pikabrush-transform.h"
#include "pikabrushcache.h"
#include "pikabrushgenerated.h"
#include "pikabrushpipe.h"
#include "pikatagged.h"
#include "pikatempbuf.h"
#include "pika-intl.h"
enum
{
SPACING_CHANGED,
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_SPACING
};
static void pika_brush_tagged_iface_init (PikaTaggedInterface *iface);
static void pika_brush_finalize (GObject *object);
static void pika_brush_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_brush_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static gint64 pika_brush_get_memsize (PikaObject *object,
gint64 *gui_size);
static gboolean pika_brush_get_size (PikaViewable *viewable,
gint *width,
gint *height);
static PikaTempBuf * pika_brush_get_new_preview (PikaViewable *viewable,
PikaContext *context,
gint width,
gint height);
static gchar * pika_brush_get_description (PikaViewable *viewable,
gchar **tooltip);
static void pika_brush_dirty (PikaData *data);
static const gchar * pika_brush_get_extension (PikaData *data);
static void pika_brush_copy (PikaData *data,
PikaData *src_data);
static void pika_brush_real_begin_use (PikaBrush *brush);
static void pika_brush_real_end_use (PikaBrush *brush);
static PikaBrush * pika_brush_real_select_brush (PikaBrush *brush,
const PikaCoords *last_coords,
const PikaCoords *current_coords);
static gboolean pika_brush_real_want_null_motion (PikaBrush *brush,
const PikaCoords *last_coords,
const PikaCoords *current_coords);
static gchar * pika_brush_get_checksum (PikaTagged *tagged);
G_DEFINE_TYPE_WITH_CODE (PikaBrush, pika_brush, PIKA_TYPE_DATA,
G_ADD_PRIVATE (PikaBrush)
G_IMPLEMENT_INTERFACE (PIKA_TYPE_TAGGED,
pika_brush_tagged_iface_init))
#define parent_class pika_brush_parent_class
static guint brush_signals[LAST_SIGNAL] = { 0 };
static void
pika_brush_class_init (PikaBrushClass *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);
brush_signals[SPACING_CHANGED] =
g_signal_new ("spacing-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (PikaBrushClass, spacing_changed),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
object_class->finalize = pika_brush_finalize;
object_class->get_property = pika_brush_get_property;
object_class->set_property = pika_brush_set_property;
pika_object_class->get_memsize = pika_brush_get_memsize;
viewable_class->default_icon_name = "pika-tool-paintbrush";
viewable_class->get_size = pika_brush_get_size;
viewable_class->get_new_preview = pika_brush_get_new_preview;
viewable_class->get_description = pika_brush_get_description;
data_class->dirty = pika_brush_dirty;
data_class->save = pika_brush_save;
data_class->get_extension = pika_brush_get_extension;
data_class->copy = pika_brush_copy;
klass->begin_use = pika_brush_real_begin_use;
klass->end_use = pika_brush_real_end_use;
klass->select_brush = pika_brush_real_select_brush;
klass->want_null_motion = pika_brush_real_want_null_motion;
klass->transform_size = pika_brush_real_transform_size;
klass->transform_mask = pika_brush_real_transform_mask;
klass->transform_pixmap = pika_brush_real_transform_pixmap;
klass->transform_boundary = pika_brush_real_transform_boundary;
klass->spacing_changed = NULL;
g_object_class_install_property (object_class, PROP_SPACING,
g_param_spec_double ("spacing", NULL,
_("Brush Spacing"),
1.0, 5000.0, 20.0,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
}
static void
pika_brush_tagged_iface_init (PikaTaggedInterface *iface)
{
iface->get_checksum = pika_brush_get_checksum;
}
static void
pika_brush_init (PikaBrush *brush)
{
brush->priv = pika_brush_get_instance_private (brush);
brush->priv->spacing = 20;
brush->priv->x_axis.x = 15.0;
brush->priv->x_axis.y = 0.0;
brush->priv->y_axis.x = 0.0;
brush->priv->y_axis.y = 15.0;
brush->priv->blur_hardness = 1.0;
}
static void
pika_brush_finalize (GObject *object)
{
PikaBrush *brush = PIKA_BRUSH (object);
g_clear_pointer (&brush->priv->mask, pika_temp_buf_unref);
g_clear_pointer (&brush->priv->pixmap, pika_temp_buf_unref);
g_clear_pointer (&brush->priv->blurred_mask, pika_temp_buf_unref);
g_clear_pointer (&brush->priv->blurred_pixmap, pika_temp_buf_unref);
pika_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);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
pika_brush_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PikaBrush *brush = PIKA_BRUSH (object);
switch (property_id)
{
case PROP_SPACING:
pika_brush_set_spacing (brush, g_value_get_double (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_brush_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaBrush *brush = PIKA_BRUSH (object);
switch (property_id)
{
case PROP_SPACING:
g_value_set_double (value, pika_brush_get_spacing (brush));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static gint64
pika_brush_get_memsize (PikaObject *object,
gint64 *gui_size)
{
PikaBrush *brush = PIKA_BRUSH (object);
gint64 memsize = 0;
memsize += pika_temp_buf_get_memsize (brush->priv->mask);
memsize += pika_temp_buf_get_memsize (brush->priv->pixmap);
memsize += pika_brush_mipmap_get_memsize (brush);
return memsize + PIKA_OBJECT_CLASS (parent_class)->get_memsize (object,
gui_size);
}
static gboolean
pika_brush_get_size (PikaViewable *viewable,
gint *width,
gint *height)
{
PikaBrush *brush = PIKA_BRUSH (viewable);
*width = pika_temp_buf_get_width (brush->priv->mask);
*height = pika_temp_buf_get_height (brush->priv->mask);
return TRUE;
}
static PikaTempBuf *
pika_brush_get_new_preview (PikaViewable *viewable,
PikaContext *context,
gint width,
gint height)
{
PikaBrush *brush = PIKA_BRUSH (viewable);
const PikaTempBuf *mask_buf = brush->priv->mask;
const PikaTempBuf *pixmap_buf = brush->priv->pixmap;
PikaTempBuf *return_buf = NULL;
gint mask_width;
gint mask_height;
guchar *mask_data;
guchar *mask;
guchar *buf;
gint x, y;
gboolean scaled = FALSE;
mask_width = pika_temp_buf_get_width (mask_buf);
mask_height = pika_temp_buf_get_height (mask_buf);
if (mask_width > width || mask_height > height)
{
gdouble ratio_x = (gdouble) width / (gdouble) mask_width;
gdouble ratio_y = (gdouble) height / (gdouble) mask_height;
gdouble scale = MIN (ratio_x, ratio_y);
if (scale != 1.0)
{
pika_brush_begin_use (brush);
if (PIKA_IS_BRUSH_GENERATED (brush))
{
PikaBrushGenerated *gen_brush = PIKA_BRUSH_GENERATED (brush);
mask_buf = pika_brush_transform_mask (brush, scale,
(pika_brush_generated_get_aspect_ratio (gen_brush) - 1.0) * 20.0 / 19.0,
pika_brush_generated_get_angle (gen_brush) / 360.0,
FALSE,
pika_brush_generated_get_hardness (gen_brush));
}
else
mask_buf = pika_brush_transform_mask (brush, scale,
0.0, 0.0, FALSE, 1.0);
if (! mask_buf)
{
mask_buf = pika_temp_buf_new (1, 1, babl_format ("Y u8"));
pika_temp_buf_data_clear ((PikaTempBuf *) mask_buf);
}
else
{
pika_temp_buf_ref ((PikaTempBuf *) mask_buf);
}
if (pixmap_buf)
pixmap_buf = pika_brush_transform_pixmap (brush, scale,
0.0, 0.0, FALSE, 1.0);
mask_width = pika_temp_buf_get_width (mask_buf);
mask_height = pika_temp_buf_get_height (mask_buf);
scaled = TRUE;
}
}
return_buf = pika_temp_buf_new (mask_width, mask_height,
babl_format ("R'G'B'A u8"));
mask = mask_data = pika_temp_buf_lock (mask_buf, babl_format ("Y u8"),
GEGL_ACCESS_READ);
buf = pika_temp_buf_get_data (return_buf);
if (pixmap_buf)
{
guchar *pixmap_data;
guchar *pixmap;
pixmap = pixmap_data = pika_temp_buf_lock (pixmap_buf,
babl_format ("R'G'B' u8"),
GEGL_ACCESS_READ);
for (y = 0; y < mask_height; y++)
{
for (x = 0; x < mask_width ; x++)
{
*buf++ = *pixmap++;
*buf++ = *pixmap++;
*buf++ = *pixmap++;
*buf++ = *mask++;
}
}
pika_temp_buf_unlock (pixmap_buf, pixmap_data);
}
else
{
for (y = 0; y < mask_height; y++)
{
for (x = 0; x < mask_width ; x++)
{
*buf++ = 0;
*buf++ = 0;
*buf++ = 0;
*buf++ = *mask++;
}
}
}
pika_temp_buf_unlock (mask_buf, mask_data);
if (scaled)
{
pika_temp_buf_unref ((PikaTempBuf *) mask_buf);
pika_brush_end_use (brush);
}
return return_buf;
}
static gchar *
pika_brush_get_description (PikaViewable *viewable,
gchar **tooltip)
{
PikaBrush *brush = PIKA_BRUSH (viewable);
return g_strdup_printf ("%s (%d × %d)",
pika_object_get_name (brush),
pika_temp_buf_get_width (brush->priv->mask),
pika_temp_buf_get_height (brush->priv->mask));
}
static void
pika_brush_dirty (PikaData *data)
{
PikaBrush *brush = PIKA_BRUSH (data);
if (brush->priv->mask_cache)
pika_brush_cache_clear (brush->priv->mask_cache);
if (brush->priv->pixmap_cache)
pika_brush_cache_clear (brush->priv->pixmap_cache);
if (brush->priv->boundary_cache)
pika_brush_cache_clear (brush->priv->boundary_cache);
pika_brush_mipmap_clear (brush);
g_clear_pointer (&brush->priv->blurred_mask, pika_temp_buf_unref);
g_clear_pointer (&brush->priv->blurred_pixmap, pika_temp_buf_unref);
PIKA_DATA_CLASS (parent_class)->dirty (data);
}
static const gchar *
pika_brush_get_extension (PikaData *data)
{
return PIKA_BRUSH_FILE_EXTENSION;
}
static void
pika_brush_copy (PikaData *data,
PikaData *src_data)
{
PikaBrush *brush = PIKA_BRUSH (data);
PikaBrush *src_brush = PIKA_BRUSH (src_data);
g_clear_pointer (&brush->priv->mask, pika_temp_buf_unref);
if (src_brush->priv->mask)
brush->priv->mask = pika_temp_buf_copy (src_brush->priv->mask);
g_clear_pointer (&brush->priv->pixmap, pika_temp_buf_unref);
if (src_brush->priv->pixmap)
brush->priv->pixmap = pika_temp_buf_copy (src_brush->priv->pixmap);
brush->priv->spacing = src_brush->priv->spacing;
brush->priv->x_axis = src_brush->priv->x_axis;
brush->priv->y_axis = src_brush->priv->y_axis;
pika_data_dirty (data);
}
static void
pika_brush_real_begin_use (PikaBrush *brush)
{
brush->priv->mask_cache =
pika_brush_cache_new ((GDestroyNotify) pika_temp_buf_unref, 'M', 'm');
brush->priv->pixmap_cache =
pika_brush_cache_new ((GDestroyNotify) pika_temp_buf_unref, 'P', 'p');
brush->priv->boundary_cache =
pika_brush_cache_new ((GDestroyNotify) pika_bezier_desc_free, 'B', 'b');
}
static void
pika_brush_real_end_use (PikaBrush *brush)
{
g_clear_object (&brush->priv->mask_cache);
g_clear_object (&brush->priv->pixmap_cache);
g_clear_object (&brush->priv->boundary_cache);
g_clear_pointer (&brush->priv->blurred_mask, pika_temp_buf_unref);
g_clear_pointer (&brush->priv->blurred_pixmap, pika_temp_buf_unref);
}
static PikaBrush *
pika_brush_real_select_brush (PikaBrush *brush,
const PikaCoords *last_coords,
const PikaCoords *current_coords)
{
return brush;
}
static gboolean
pika_brush_real_want_null_motion (PikaBrush *brush,
const PikaCoords *last_coords,
const PikaCoords *current_coords)
{
return TRUE;
}
static gchar *
pika_brush_get_checksum (PikaTagged *tagged)
{
PikaBrush *brush = PIKA_BRUSH (tagged);
gchar *checksum_string = NULL;
if (brush->priv->mask)
{
GChecksum *checksum = g_checksum_new (G_CHECKSUM_MD5);
g_checksum_update (checksum,
pika_temp_buf_get_data (brush->priv->mask),
pika_temp_buf_get_data_size (brush->priv->mask));
if (brush->priv->pixmap)
g_checksum_update (checksum,
pika_temp_buf_get_data (brush->priv->pixmap),
pika_temp_buf_get_data_size (brush->priv->pixmap));
g_checksum_update (checksum,
(const guchar *) &brush->priv->spacing,
sizeof (brush->priv->spacing));
g_checksum_update (checksum,
(const guchar *) &brush->priv->x_axis,
sizeof (brush->priv->x_axis));
g_checksum_update (checksum,
(const guchar *) &brush->priv->y_axis,
sizeof (brush->priv->y_axis));
checksum_string = g_strdup (g_checksum_get_string (checksum));
g_checksum_free (checksum);
}
return checksum_string;
}
/* public functions */
PikaData *
pika_brush_new (PikaContext *context,
const gchar *name)
{
g_return_val_if_fail (name != NULL, NULL);
return pika_brush_generated_new (name,
PIKA_BRUSH_GENERATED_CIRCLE,
5.0, 2, 0.5, 1.0, 0.0);
}
PikaData *
pika_brush_get_standard (PikaContext *context)
{
static PikaData *standard_brush = NULL;
if (! standard_brush)
{
g_set_weak_pointer (&standard_brush,
pika_brush_new (context, "Standard"));
pika_data_clean (standard_brush);
pika_data_make_internal (standard_brush, "pika-brush-standard");
}
return standard_brush;
}
void
pika_brush_begin_use (PikaBrush *brush)
{
g_return_if_fail (PIKA_IS_BRUSH (brush));
brush->priv->use_count++;
if (brush->priv->use_count == 1)
PIKA_BRUSH_GET_CLASS (brush)->begin_use (brush);
}
void
pika_brush_end_use (PikaBrush *brush)
{
g_return_if_fail (PIKA_IS_BRUSH (brush));
g_return_if_fail (brush->priv->use_count > 0);
brush->priv->use_count--;
if (brush->priv->use_count == 0)
PIKA_BRUSH_GET_CLASS (brush)->end_use (brush);
}
PikaBrush *
pika_brush_select_brush (PikaBrush *brush,
const PikaCoords *last_coords,
const PikaCoords *current_coords)
{
g_return_val_if_fail (PIKA_IS_BRUSH (brush), NULL);
g_return_val_if_fail (last_coords != NULL, NULL);
g_return_val_if_fail (current_coords != NULL, NULL);
return PIKA_BRUSH_GET_CLASS (brush)->select_brush (brush,
last_coords,
current_coords);
}
gboolean
pika_brush_want_null_motion (PikaBrush *brush,
const PikaCoords *last_coords,
const PikaCoords *current_coords)
{
g_return_val_if_fail (PIKA_IS_BRUSH (brush), FALSE);
g_return_val_if_fail (last_coords != NULL, FALSE);
g_return_val_if_fail (current_coords != NULL, FALSE);
return PIKA_BRUSH_GET_CLASS (brush)->want_null_motion (brush,
last_coords,
current_coords);
}
void
pika_brush_transform_size (PikaBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gint *width,
gint *height)
{
g_return_if_fail (PIKA_IS_BRUSH (brush));
g_return_if_fail (scale > 0.0);
g_return_if_fail (width != NULL);
g_return_if_fail (height != NULL);
if (scale == 1.0 &&
aspect_ratio == 0.0 &&
fmod (angle, 0.5) == 0.0)
{
*width = pika_temp_buf_get_width (brush->priv->mask);
*height = pika_temp_buf_get_height (brush->priv->mask);
return;
}
PIKA_BRUSH_GET_CLASS (brush)->transform_size (brush,
scale, aspect_ratio, angle, reflect,
width, height);
}
const PikaTempBuf *
pika_brush_transform_mask (PikaBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gdouble hardness)
{
const PikaTempBuf *mask;
gint width;
gint height;
gdouble effective_hardness = hardness;
g_return_val_if_fail (PIKA_IS_BRUSH (brush), NULL);
g_return_val_if_fail (scale > 0.0, NULL);
pika_brush_transform_size (brush,
scale, aspect_ratio, angle, reflect,
&width, &height);
mask = pika_brush_cache_get (brush->priv->mask_cache,
width, height,
scale, aspect_ratio, angle, reflect, hardness);
if (! mask)
{
#if 0
/* This code makes sure that brushes using blur for hardness
* (all of them but generated) are blurred once and no more.
* It also makes hardnes dynamics not work for these brushes.
* This is intentional. Confoliving for each stamp is too expensive.*/
if (! brush->priv->blurred_mask &&
! PIKA_IS_BRUSH_GENERATED(brush) &&
! PIKA_IS_BRUSH_PIPE(brush) && /*Can't cache pipes. Sanely anyway*/
hardness < 1.0)
{
brush->priv->blurred_mask = PIKA_BRUSH_GET_CLASS (brush)->transform_mask (brush,
1.0,
0.0,
0.0,
FALSE,
hardness);
brush->priv->blur_hardness = hardness;
}
if (brush->priv->blurred_mask)
{
effective_hardness = 1.0; /*Hardness has already been applied*/
}
#endif
mask = PIKA_BRUSH_GET_CLASS (brush)->transform_mask (brush,
scale,
aspect_ratio,
angle,
reflect,
effective_hardness);
pika_brush_cache_add (brush->priv->mask_cache,
(gpointer) mask,
width, height,
scale, aspect_ratio, angle, reflect, effective_hardness);
}
return mask;
}
const PikaTempBuf *
pika_brush_transform_pixmap (PikaBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gdouble hardness)
{
const PikaTempBuf *pixmap;
gint width;
gint height;
gdouble effective_hardness = hardness;
g_return_val_if_fail (PIKA_IS_BRUSH (brush), NULL);
g_return_val_if_fail (brush->priv->pixmap != NULL, NULL);
g_return_val_if_fail (scale > 0.0, NULL);
pika_brush_transform_size (brush,
scale, aspect_ratio, angle, reflect,
&width, &height);
pixmap = pika_brush_cache_get (brush->priv->pixmap_cache,
width, height,
scale, aspect_ratio, angle, reflect, hardness);
if (! pixmap)
{
#if 0
if (! brush->priv->blurred_pixmap &&
! PIKA_IS_BRUSH_GENERATED(brush) &&
! PIKA_IS_BRUSH_PIPE(brush) /*Can't cache pipes. Sanely anyway*/
&& hardness < 1.0)
{
brush->priv->blurred_pixmap = PIKA_BRUSH_GET_CLASS (brush)->transform_pixmap (brush,
1.0,
0.0,
0.0,
FALSE,
hardness);
brush->priv->blur_hardness = hardness;
}
if (brush->priv->blurred_pixmap) {
effective_hardness = 1.0; /*Hardness has already been applied*/
}
#endif
pixmap = PIKA_BRUSH_GET_CLASS (brush)->transform_pixmap (brush,
scale,
aspect_ratio,
angle,
reflect,
effective_hardness);
pika_brush_cache_add (brush->priv->pixmap_cache,
(gpointer) pixmap,
width, height,
scale, aspect_ratio, angle, reflect, effective_hardness);
}
return pixmap;
}
const PikaBezierDesc *
pika_brush_transform_boundary (PikaBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gdouble hardness,
gint *width,
gint *height)
{
const PikaBezierDesc *boundary;
g_return_val_if_fail (PIKA_IS_BRUSH (brush), NULL);
g_return_val_if_fail (scale > 0.0, NULL);
g_return_val_if_fail (width != NULL, NULL);
g_return_val_if_fail (height != NULL, NULL);
pika_brush_transform_size (brush,
scale, aspect_ratio, angle, reflect,
width, height);
boundary = pika_brush_cache_get (brush->priv->boundary_cache,
*width, *height,
scale, aspect_ratio, angle, reflect, hardness);
if (! boundary)
{
boundary = PIKA_BRUSH_GET_CLASS (brush)->transform_boundary (brush,
scale,
aspect_ratio,
angle,
reflect,
hardness,
width,
height);
/* while the brush mask is always at least 1x1 pixels, its
* outline can correctly be NULL
*
* FIXME: make the cache handle NULL things when it is
* properly implemented
*/
if (boundary)
pika_brush_cache_add (brush->priv->boundary_cache,
(gpointer) boundary,
*width, *height,
scale, aspect_ratio, angle, reflect, hardness);
}
return boundary;
}
PikaTempBuf *
pika_brush_get_mask (PikaBrush *brush)
{
g_return_val_if_fail (brush != NULL, NULL);
g_return_val_if_fail (PIKA_IS_BRUSH (brush), NULL);
if (brush->priv->blurred_mask)
{
return brush->priv->blurred_mask;
}
return brush->priv->mask;
}
PikaTempBuf *
pika_brush_get_pixmap (PikaBrush *brush)
{
g_return_val_if_fail (brush != NULL, NULL);
g_return_val_if_fail (PIKA_IS_BRUSH (brush), NULL);
if(brush->priv->blurred_pixmap)
{
return brush->priv->blurred_pixmap;
}
return brush->priv->pixmap;
}
void
pika_brush_flush_blur_caches (PikaBrush *brush)
{
#if 0
g_clear_pointer (&brush->priv->blurred_mask, pika_temp_buf_unref);
g_clear_pointer (&brush->priv->blurred_pixmap, pika_temp_buf_unref);
if (brush->priv->mask_cache)
pika_brush_cache_clear (brush->priv->mask_cache);
if (brush->priv->pixmap_cache)
pika_brush_cache_clear (brush->priv->pixmap_cache);
if (brush->priv->boundary_cache)
pika_brush_cache_clear (brush->priv->boundary_cache);
#endif
}
gdouble
pika_brush_get_blur_hardness (PikaBrush *brush)
{
g_return_val_if_fail (PIKA_IS_BRUSH (brush), 0);
return brush->priv->blur_hardness;
}
gint
pika_brush_get_width (PikaBrush *brush)
{
g_return_val_if_fail (PIKA_IS_BRUSH (brush), 0);
if (brush->priv->blurred_mask)
return pika_temp_buf_get_width (brush->priv->blurred_mask);
if (brush->priv->blurred_pixmap)
return pika_temp_buf_get_width (brush->priv->blurred_pixmap);
return pika_temp_buf_get_width (brush->priv->mask);
}
gint
pika_brush_get_height (PikaBrush *brush)
{
g_return_val_if_fail (PIKA_IS_BRUSH (brush), 0);
if (brush->priv->blurred_mask)
return pika_temp_buf_get_height (brush->priv->blurred_mask);
if (brush->priv->blurred_pixmap)
return pika_temp_buf_get_height (brush->priv->blurred_pixmap);
return pika_temp_buf_get_height (brush->priv->mask);
}
gint
pika_brush_get_spacing (PikaBrush *brush)
{
g_return_val_if_fail (PIKA_IS_BRUSH (brush), 0);
return brush->priv->spacing;
}
void
pika_brush_set_spacing (PikaBrush *brush,
gint spacing)
{
g_return_if_fail (PIKA_IS_BRUSH (brush));
if (brush->priv->spacing != spacing)
{
brush->priv->spacing = spacing;
g_signal_emit (brush, brush_signals[SPACING_CHANGED], 0);
g_object_notify (G_OBJECT (brush), "spacing");
}
}
static const PikaVector2 fail = { 0.0, 0.0 };
PikaVector2
pika_brush_get_x_axis (PikaBrush *brush)
{
g_return_val_if_fail (PIKA_IS_BRUSH (brush), fail);
return brush->priv->x_axis;
}
PikaVector2
pika_brush_get_y_axis (PikaBrush *brush)
{
g_return_val_if_fail (PIKA_IS_BRUSH (brush), fail);
return brush->priv->y_axis;
}

156
app/core/pikabrush.h Normal file
View File

@ -0,0 +1,156 @@
/* 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/>.
*/
#ifndef __PIKA_BRUSH_H__
#define __PIKA_BRUSH_H__
#include "pikadata.h"
#define PIKA_TYPE_BRUSH (pika_brush_get_type ())
#define PIKA_BRUSH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_BRUSH, PikaBrush))
#define PIKA_BRUSH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_BRUSH, PikaBrushClass))
#define PIKA_IS_BRUSH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_BRUSH))
#define PIKA_IS_BRUSH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_BRUSH))
#define PIKA_BRUSH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_BRUSH, PikaBrushClass))
typedef struct _PikaBrushPrivate PikaBrushPrivate;
typedef struct _PikaBrushClass PikaBrushClass;
struct _PikaBrush
{
PikaData parent_instance;
PikaBrushPrivate *priv;
};
struct _PikaBrushClass
{
PikaDataClass parent_class;
/* virtual functions */
void (* begin_use) (PikaBrush *brush);
void (* end_use) (PikaBrush *brush);
PikaBrush * (* select_brush) (PikaBrush *brush,
const PikaCoords *last_coords,
const PikaCoords *current_coords);
gboolean (* want_null_motion) (PikaBrush *brush,
const PikaCoords *last_coords,
const PikaCoords *current_coords);
void (* transform_size) (PikaBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gint *width,
gint *height);
PikaTempBuf * (* transform_mask) (PikaBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gdouble hardness);
PikaTempBuf * (* transform_pixmap) (PikaBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gdouble hardness);
PikaBezierDesc * (* transform_boundary) (PikaBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gdouble hardness,
gint *width,
gint *height);
/* signals */
void (* spacing_changed) (PikaBrush *brush);
};
GType pika_brush_get_type (void) G_GNUC_CONST;
PikaData * pika_brush_new (PikaContext *context,
const gchar *name);
PikaData * pika_brush_get_standard (PikaContext *context);
void pika_brush_begin_use (PikaBrush *brush);
void pika_brush_end_use (PikaBrush *brush);
PikaBrush * pika_brush_select_brush (PikaBrush *brush,
const PikaCoords *last_coords,
const PikaCoords *current_coords);
gboolean pika_brush_want_null_motion (PikaBrush *brush,
const PikaCoords *last_coords,
const PikaCoords *current_coords);
/* Gets width and height of a transformed mask of the brush, for
* provided parameters.
*/
void pika_brush_transform_size (PikaBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gint *width,
gint *height);
const PikaTempBuf * pika_brush_transform_mask (PikaBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gdouble hardness);
const PikaTempBuf * pika_brush_transform_pixmap (PikaBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gdouble hardness);
const PikaBezierDesc * pika_brush_transform_boundary (PikaBrush *brush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gdouble hardness,
gint *width,
gint *height);
PikaTempBuf * pika_brush_get_mask (PikaBrush *brush);
PikaTempBuf * pika_brush_get_pixmap (PikaBrush *brush);
gint pika_brush_get_width (PikaBrush *brush);
gint pika_brush_get_height (PikaBrush *brush);
gint pika_brush_get_spacing (PikaBrush *brush);
void pika_brush_set_spacing (PikaBrush *brush,
gint spacing);
PikaVector2 pika_brush_get_x_axis (PikaBrush *brush);
PikaVector2 pika_brush_get_y_axis (PikaBrush *brush);
void pika_brush_flush_blur_caches (PikaBrush *brush);
gdouble pika_brush_get_blur_hardness (PikaBrush *brush);
#endif /* __PIKA_BRUSH_H__ */

306
app/core/pikabrushcache.c Normal file
View File

@ -0,0 +1,306 @@
/* 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
*
* pikabrushcache.c
* Copyright (C) 2011 Michael Natterer <mitch@gimp.org>
*
* 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 <gio/gio.h>
#include <gegl.h>
#include "libpikabase/pikabase.h"
#include "core-types.h"
#include "pikabrushcache.h"
#include "pika-log.h"
#include "pika-intl.h"
#define MAX_CACHED_DATA 20
enum
{
PROP_0,
PROP_DATA_DESTROY
};
typedef struct _PikaBrushCacheUnit PikaBrushCacheUnit;
struct _PikaBrushCacheUnit
{
gpointer data;
gint width;
gint height;
gdouble scale;
gdouble aspect_ratio;
gdouble angle;
gboolean reflect;
gdouble hardness;
};
static void pika_brush_cache_constructed (GObject *object);
static void pika_brush_cache_finalize (GObject *object);
static void pika_brush_cache_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_brush_cache_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
G_DEFINE_TYPE (PikaBrushCache, pika_brush_cache, PIKA_TYPE_OBJECT)
#define parent_class pika_brush_cache_parent_class
static void
pika_brush_cache_class_init (PikaBrushCacheClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = pika_brush_cache_constructed;
object_class->finalize = pika_brush_cache_finalize;
object_class->set_property = pika_brush_cache_set_property;
object_class->get_property = pika_brush_cache_get_property;
g_object_class_install_property (object_class, PROP_DATA_DESTROY,
g_param_spec_pointer ("data-destroy",
NULL, NULL,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
}
static void
pika_brush_cache_init (PikaBrushCache *brush)
{
}
static void
pika_brush_cache_constructed (GObject *object)
{
PikaBrushCache *cache = PIKA_BRUSH_CACHE (object);
G_OBJECT_CLASS (parent_class)->constructed (object);
pika_assert (cache->data_destroy != NULL);
}
static void
pika_brush_cache_finalize (GObject *object)
{
PikaBrushCache *cache = PIKA_BRUSH_CACHE (object);
pika_brush_cache_clear (cache);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
pika_brush_cache_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PikaBrushCache *cache = PIKA_BRUSH_CACHE (object);
switch (property_id)
{
case PROP_DATA_DESTROY:
cache->data_destroy = g_value_get_pointer (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_brush_cache_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaBrushCache *cache = PIKA_BRUSH_CACHE (object);
switch (property_id)
{
case PROP_DATA_DESTROY:
g_value_set_pointer (value, cache->data_destroy);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/* public functions */
PikaBrushCache *
pika_brush_cache_new (GDestroyNotify data_destroy,
gchar debug_hit,
gchar debug_miss)
{
PikaBrushCache *cache;
g_return_val_if_fail (data_destroy != NULL, NULL);
cache = g_object_new (PIKA_TYPE_BRUSH_CACHE,
"data-destroy", data_destroy,
NULL);
cache->debug_hit = debug_hit;
cache->debug_miss = debug_miss;
return cache;
}
void
pika_brush_cache_clear (PikaBrushCache *cache)
{
g_return_if_fail (PIKA_IS_BRUSH_CACHE (cache));
if (cache->cached_units)
{
GList *iter;
for (iter = cache->cached_units; iter; iter = g_list_next (iter))
{
PikaBrushCacheUnit *unit = iter->data;
cache->data_destroy (unit->data);
}
g_list_free_full (cache->cached_units, g_free);
cache->cached_units = NULL;
}
}
gconstpointer
pika_brush_cache_get (PikaBrushCache *cache,
gint width,
gint height,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gdouble hardness)
{
GList *iter;
g_return_val_if_fail (PIKA_IS_BRUSH_CACHE (cache), NULL);
for (iter = cache->cached_units; iter; iter = g_list_next (iter))
{
PikaBrushCacheUnit *unit = iter->data;
if (unit->data &&
unit->width == width &&
unit->height == height &&
unit->scale == scale &&
unit->aspect_ratio == aspect_ratio &&
unit->angle == angle &&
unit->reflect == reflect &&
unit->hardness == hardness)
{
if (pika_log_flags & PIKA_LOG_BRUSH_CACHE)
g_printerr ("%c", cache->debug_hit);
/* Make the returned cached brush first in the list. */
cache->cached_units = g_list_remove_link (cache->cached_units, iter);
iter->next = cache->cached_units;
if (cache->cached_units)
cache->cached_units->prev = iter;
cache->cached_units = iter;
return (gconstpointer) unit->data;
}
}
if (pika_log_flags & PIKA_LOG_BRUSH_CACHE)
g_printerr ("%c", cache->debug_miss);
return NULL;
}
void
pika_brush_cache_add (PikaBrushCache *cache,
gpointer data,
gint width,
gint height,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gdouble hardness)
{
GList *iter;
PikaBrushCacheUnit *unit;
GList *last = NULL;
gint length = 0;
g_return_if_fail (PIKA_IS_BRUSH_CACHE (cache));
g_return_if_fail (data != NULL);
for (iter = cache->cached_units; iter; iter = g_list_next (iter))
{
unit = iter->data;
if (data == unit->data)
return;
length++;
last = iter;
}
if (length > MAX_CACHED_DATA)
{
unit = last->data;
cache->data_destroy (unit->data);
cache->cached_units = g_list_delete_link (cache->cached_units, last);
g_free (unit);
}
unit = g_new0 (PikaBrushCacheUnit, 1);
unit->data = data;
unit->width = width;
unit->height = height;
unit->scale = scale;
unit->aspect_ratio = aspect_ratio;
unit->angle = angle;
unit->reflect = reflect;
unit->hardness = hardness;
cache->cached_units = g_list_prepend (cache->cached_units, unit);
}

87
app/core/pikabrushcache.h Normal file
View File

@ -0,0 +1,87 @@
/* 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
*
* pikabrushcache.h
* Copyright (C) 2011 Michael Natterer <mitch@gimp.org>
*
* 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 __PIKA_BRUSH_CACHE_H__
#define __PIKA_BRUSH_CACHE_H__
#include "pikaobject.h"
#define PIKA_TYPE_BRUSH_CACHE (pika_brush_cache_get_type ())
#define PIKA_BRUSH_CACHE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_BRUSH_CACHE, PikaBrushCache))
#define PIKA_BRUSH_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_BRUSH_CACHE, PikaBrushCacheClass))
#define PIKA_IS_BRUSH_CACHE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_BRUSH_CACHE))
#define PIKA_IS_BRUSH_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_BRUSH_CACHE))
#define PIKA_BRUSH_CACHE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_BRUSH_CACHE, PikaBrushCacheClass))
typedef struct _PikaBrushCacheClass PikaBrushCacheClass;
struct _PikaBrushCache
{
PikaObject parent_instance;
GDestroyNotify data_destroy;
GList *cached_units;
gchar debug_hit;
gchar debug_miss;
};
struct _PikaBrushCacheClass
{
PikaObjectClass parent_class;
};
GType pika_brush_cache_get_type (void) G_GNUC_CONST;
PikaBrushCache * pika_brush_cache_new (GDestroyNotify data_destory,
gchar debug_hit,
gchar debug_miss);
void pika_brush_cache_clear (PikaBrushCache *cache);
gconstpointer pika_brush_cache_get (PikaBrushCache *cache,
gint width,
gint height,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gdouble hardness);
void pika_brush_cache_add (PikaBrushCache *cache,
gpointer data,
gint width,
gint height,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gdouble hardness);
#endif /* __PIKA_BRUSH_CACHE_H__ */

View File

@ -0,0 +1,304 @@
/* 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
*
* pikabrushclipboard.c
* Copyright (C) 2006 Michael Natterer <mitch@gimp.org>
*
* 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 <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include "libpikabase/pikabase.h"
#include "core-types.h"
#include "pika.h"
#include "pikabuffer.h"
#include "pikabrush-private.h"
#include "pikabrushclipboard.h"
#include "pikaimage.h"
#include "pikapickable.h"
#include "pikatempbuf.h"
#include "pika-intl.h"
#define BRUSH_MAX_SIZE 1024
enum
{
PROP_0,
PROP_PIKA,
PROP_MASK_ONLY
};
/* local function prototypes */
static void pika_brush_clipboard_constructed (GObject *object);
static void pika_brush_clipboard_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_brush_clipboard_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static PikaData * pika_brush_clipboard_duplicate (PikaData *data);
static void pika_brush_clipboard_changed (Pika *pika,
PikaBrush *brush);
G_DEFINE_TYPE (PikaBrushClipboard, pika_brush_clipboard, PIKA_TYPE_BRUSH)
#define parent_class pika_brush_clipboard_parent_class
static void
pika_brush_clipboard_class_init (PikaBrushClipboardClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
PikaDataClass *data_class = PIKA_DATA_CLASS (klass);
object_class->constructed = pika_brush_clipboard_constructed;
object_class->set_property = pika_brush_clipboard_set_property;
object_class->get_property = pika_brush_clipboard_get_property;
data_class->duplicate = pika_brush_clipboard_duplicate;
g_object_class_install_property (object_class, PROP_PIKA,
g_param_spec_object ("pika", NULL, NULL,
PIKA_TYPE_PIKA,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class, PROP_MASK_ONLY,
g_param_spec_boolean ("mask-only", NULL, NULL,
FALSE,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
}
static void
pika_brush_clipboard_init (PikaBrushClipboard *brush)
{
}
static void
pika_brush_clipboard_constructed (GObject *object)
{
PikaBrushClipboard *brush = PIKA_BRUSH_CLIPBOARD (object);
G_OBJECT_CLASS (parent_class)->constructed (object);
pika_assert (PIKA_IS_PIKA (brush->pika));
g_signal_connect_object (brush->pika, "clipboard-changed",
G_CALLBACK (pika_brush_clipboard_changed),
brush, 0);
pika_brush_clipboard_changed (brush->pika, PIKA_BRUSH (brush));
}
static void
pika_brush_clipboard_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PikaBrushClipboard *brush = PIKA_BRUSH_CLIPBOARD (object);
switch (property_id)
{
case PROP_PIKA:
brush->pika = g_value_get_object (value);
break;
case PROP_MASK_ONLY:
brush->mask_only = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_brush_clipboard_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaBrushClipboard *brush = PIKA_BRUSH_CLIPBOARD (object);
switch (property_id)
{
case PROP_PIKA:
g_value_set_object (value, brush->pika);
break;
case PROP_MASK_ONLY:
g_value_set_boolean (value, brush->mask_only);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static PikaData *
pika_brush_clipboard_duplicate (PikaData *data)
{
PikaData *new = g_object_new (PIKA_TYPE_BRUSH, NULL);
pika_data_copy (new, data);
return new;
}
PikaData *
pika_brush_clipboard_new (Pika *pika,
gboolean mask_only)
{
const gchar *name;
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
if (mask_only)
name = _("Clipboard Mask");
else
name = _("Clipboard Image");
return g_object_new (PIKA_TYPE_BRUSH_CLIPBOARD,
"name", name,
"pika", pika,
"mask-only", mask_only,
NULL);
}
/* private functions */
static void
pika_brush_clipboard_changed (Pika *pika,
PikaBrush *brush)
{
PikaObject *paste;
GeglBuffer *buffer = NULL;
gint width;
gint height;
g_clear_pointer (&brush->priv->mask, pika_temp_buf_unref);
g_clear_pointer (&brush->priv->pixmap, pika_temp_buf_unref);
paste = pika_get_clipboard_object (pika);
if (PIKA_IS_IMAGE (paste))
{
pika_pickable_flush (PIKA_PICKABLE (paste));
buffer = pika_pickable_get_buffer (PIKA_PICKABLE (paste));
}
else if (PIKA_IS_BUFFER (paste))
{
buffer = pika_buffer_get_buffer (PIKA_BUFFER (paste));
}
if (buffer)
{
const Babl *format = gegl_buffer_get_format (buffer);
width = MIN (gegl_buffer_get_width (buffer), BRUSH_MAX_SIZE);
height = MIN (gegl_buffer_get_height (buffer), BRUSH_MAX_SIZE);
brush->priv->mask = pika_temp_buf_new (width, height,
babl_format ("Y u8"));
if (PIKA_BRUSH_CLIPBOARD (brush)->mask_only)
{
guchar *p;
gint i;
gegl_buffer_get (buffer,
GEGL_RECTANGLE (0, 0, width, height), 1.0,
babl_format ("Y u8"),
pika_temp_buf_get_data (brush->priv->mask),
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
/* invert the mask, it's more intuitive to think
* "black on white" than the other way around
*/
for (i = 0, p = pika_temp_buf_get_data (brush->priv->mask);
i < width * height;
i++, p++)
{
*p = 255 - *p;
}
}
else
{
brush->priv->pixmap = pika_temp_buf_new (width, height,
babl_format ("R'G'B' u8"));
/* copy the alpha channel into the brush's mask */
if (babl_format_has_alpha (format))
{
gegl_buffer_get (buffer,
GEGL_RECTANGLE (0, 0, width, height), 1.0,
babl_format ("A u8"),
pika_temp_buf_get_data (brush->priv->mask),
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
}
else
{
memset (pika_temp_buf_get_data (brush->priv->mask), 255,
width * height);
}
/* copy the color channels into the brush's pixmap */
gegl_buffer_get (buffer,
GEGL_RECTANGLE (0, 0, width, height), 1.0,
babl_format ("R'G'B' u8"),
pika_temp_buf_get_data (brush->priv->pixmap),
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
}
}
else
{
width = 17;
height = 17;
brush->priv->mask = pika_temp_buf_new (width, height,
babl_format ("Y u8"));
pika_temp_buf_data_clear (brush->priv->mask);
}
brush->priv->x_axis.x = width / 2;
brush->priv->x_axis.y = 0;
brush->priv->y_axis.x = 0;
brush->priv->y_axis.y = height / 2;
pika_data_dirty (PIKA_DATA (brush));
}

View File

@ -0,0 +1,62 @@
/* 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
*
* pikabrushclipboard.h
* Copyright (C) 2006 Michael Natterer <mitch@gimp.org>
*
* 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 __PIKA_BRUSH_CLIPBOARD_H__
#define __PIKA_BRUSH_CLIPBOARD_H__
#include "pikabrush.h"
#define PIKA_TYPE_BRUSH_CLIPBOARD (pika_brush_clipboard_get_type ())
#define PIKA_BRUSH_CLIPBOARD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_BRUSH_CLIPBOARD, PikaBrushClipboard))
#define PIKA_BRUSH_CLIPBOARD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_BRUSH_CLIPBOARD, PikaBrushClipboardClass))
#define PIKA_IS_BRUSH_CLIPBOARD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_BRUSH_CLIPBOARD))
#define PIKA_IS_BRUSH_CLIPBOARD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_BRUSH_CLIPBOARD))
#define PIKA_BRUSH_CLIPBOARD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_BRUSH_CLIPBOARD, PikaBrushClipboardClass))
typedef struct _PikaBrushClipboardClass PikaBrushClipboardClass;
struct _PikaBrushClipboard
{
PikaBrush parent_instance;
Pika *pika;
gboolean mask_only;
};
struct _PikaBrushClipboardClass
{
PikaBrushClass parent_class;
};
GType pika_brush_clipboard_get_type (void) G_GNUC_CONST;
PikaData * pika_brush_clipboard_new (Pika *pika,
gboolean mask_only);
#endif /* __PIKA_BRUSH_CLIPBOARD_H__ */

View File

@ -0,0 +1,288 @@
/* 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
*
* pika_brush_generated module Copyright 1998 Jay Cox <jaycox@earthlink.net>
*
* 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 <stdlib.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include "libpikabase/pikabase.h"
#include "core-types.h"
#include "pika-utils.h"
#include "pikabrushgenerated.h"
#include "pikabrushgenerated-load.h"
#include "pika-intl.h"
GList *
pika_brush_generated_load (PikaContext *context,
GFile *file,
GInputStream *input,
GError **error)
{
PikaBrush *brush;
GDataInputStream *data_input;
gchar *string;
gsize string_len;
gint linenum;
gchar *name = NULL;
PikaBrushGeneratedShape shape = PIKA_BRUSH_GENERATED_CIRCLE;
gboolean have_shape = FALSE;
gint spikes = 2;
gdouble spacing;
gdouble radius;
gdouble hardness;
gdouble aspect_ratio;
gdouble angle;
g_return_val_if_fail (G_IS_FILE (file), NULL);
g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
data_input = g_data_input_stream_new (input);
/* make sure the file we are reading is the right type */
linenum = 1;
string_len = 256;
string = pika_data_input_stream_read_line_always (data_input, &string_len,
NULL, error);
if (! string)
goto failed;
if (! g_str_has_prefix (string, "PIKA-VBR"))
{
g_set_error (error, PIKA_DATA_ERROR, PIKA_DATA_ERROR_READ,
_("Not a PIKA brush file."));
g_free (string);
goto failed;
}
g_free (string);
/* make sure we are reading a compatible version */
linenum++;
string_len = 256;
string = pika_data_input_stream_read_line_always (data_input, &string_len,
NULL, error);
if (! string)
goto failed;
if (! g_str_has_prefix (string, "1.0"))
{
if (! g_str_has_prefix (string, "1.5"))
{
g_set_error (error, PIKA_DATA_ERROR, PIKA_DATA_ERROR_READ,
_("Unknown PIKA brush version."));
g_free (string);
goto failed;
}
else
{
have_shape = TRUE;
}
}
g_free (string);
/* read name */
linenum++;
string_len = 256;
string = pika_data_input_stream_read_line_always (data_input, &string_len,
NULL, error);
if (! string)
goto failed;
g_strstrip (string);
/* the empty string is not an allowed name */
if (strlen (string) < 1)
{
name = g_strdup (_("Untitled"));
}
else
{
name = pika_any_to_utf8 (string, -1,
_("Invalid UTF-8 string in brush file '%s'."),
pika_file_get_utf8_name (file));
}
g_free (string);
if (have_shape)
{
GEnumClass *enum_class;
GEnumValue *shape_val;
enum_class = g_type_class_peek (PIKA_TYPE_BRUSH_GENERATED_SHAPE);
/* read shape */
linenum++;
string_len = 256;
string = pika_data_input_stream_read_line_always (data_input, &string_len,
NULL, error);
if (! string)
goto failed;
g_strstrip (string);
shape_val = g_enum_get_value_by_nick (enum_class, string);
if (! shape_val)
{
g_set_error (error, PIKA_DATA_ERROR, PIKA_DATA_ERROR_READ,
_("Unknown PIKA brush shape."));
g_free (string);
goto failed;
}
g_free (string);
shape = shape_val->value;
}
/* read brush spacing */
linenum++;
string_len = 256;
string = pika_data_input_stream_read_line_always (data_input, &string_len,
NULL, error);
if (! string)
goto failed;
if (! pika_ascii_strtod (string, NULL, &spacing))
{
g_set_error (error, PIKA_DATA_ERROR, PIKA_DATA_ERROR_READ,
_("Invalid brush spacing."));
g_free (string);
goto failed;
}
g_free (string);
/* read brush radius */
linenum++;
string_len = 256;
string = pika_data_input_stream_read_line_always (data_input, &string_len,
NULL, error);
if (! string)
goto failed;
if (! pika_ascii_strtod (string, NULL, &radius))
{
g_set_error (error, PIKA_DATA_ERROR, PIKA_DATA_ERROR_READ,
_("Invalid brush radius."));
g_free (string);
goto failed;
}
g_free (string);
if (have_shape)
{
/* read number of spikes */
linenum++;
string_len = 256;
string = pika_data_input_stream_read_line_always (data_input, &string_len,
NULL, error);
if (! string)
goto failed;
if (! pika_ascii_strtoi (string, NULL, 10, &spikes) ||
spikes < 2 || spikes > 20)
{
g_set_error (error, PIKA_DATA_ERROR, PIKA_DATA_ERROR_READ,
_("Invalid brush spike count."));
g_free (string);
goto failed;
}
g_free (string);
}
/* read brush hardness */
linenum++;
string_len = 256;
string = pika_data_input_stream_read_line_always (data_input, &string_len,
NULL, error);
if (! string)
goto failed;
if (! pika_ascii_strtod (string, NULL, &hardness))
{
g_set_error (error, PIKA_DATA_ERROR, PIKA_DATA_ERROR_READ,
_("Invalid brush hardness."));
g_free (string);
goto failed;
}
g_free (string);
/* read brush aspect_ratio */
linenum++;
string_len = 256;
string = pika_data_input_stream_read_line_always (data_input, &string_len,
NULL, error);
if (! string)
goto failed;
if (! pika_ascii_strtod (string, NULL, &aspect_ratio))
{
g_set_error (error, PIKA_DATA_ERROR, PIKA_DATA_ERROR_READ,
_("Invalid brush aspect ratio."));
g_free (string);
goto failed;
}
g_free (string);
/* read brush angle */
linenum++;
string_len = 256;
string = pika_data_input_stream_read_line_always (data_input, &string_len,
NULL, error);
if (! string)
goto failed;
if (! pika_ascii_strtod (string, NULL, &angle))
{
g_set_error (error, PIKA_DATA_ERROR, PIKA_DATA_ERROR_READ,
_("Invalid brush angle."));
g_free (string);
goto failed;
}
g_free (string);
g_object_unref (data_input);
brush = PIKA_BRUSH (pika_brush_generated_new (name, shape, radius, spikes,
hardness, aspect_ratio, angle));
g_free (name);
pika_brush_set_spacing (brush, spacing);
return g_list_prepend (NULL, brush);
failed:
g_object_unref (data_input);
if (name)
g_free (name);
g_prefix_error (error, _("In line %d of brush file: "), linenum);
return NULL;
}

View File

@ -0,0 +1,37 @@
/* 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
*
* brush_generated module Copyright 1998 Jay Cox <jaycox@earthlink.net>
*
* 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 __PIKA_BRUSH_GENERATED_LOAD_H__
#define __PIKA_BRUSH_GENERATED_LOAD_H__
#define PIKA_BRUSH_GENERATED_FILE_EXTENSION ".vbr"
GList * pika_brush_generated_load (PikaContext *context,
GFile *file,
GInputStream *input,
GError **error);
#endif /* __PIKA_BRUSH_GENERATED_LOAD_H__ */

View File

@ -0,0 +1,123 @@
/* 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
*
* pika_brush_generated module Copyright 1998 Jay Cox <jaycox@earthlink.net>
*
* 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 <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include "libpikabase/pikabase.h"
#include "core-types.h"
#include "pikabrushgenerated.h"
#include "pikabrushgenerated-save.h"
#include "pika-intl.h"
gboolean
pika_brush_generated_save (PikaData *data,
GOutputStream *output,
GError **error)
{
PikaBrushGenerated *brush = PIKA_BRUSH_GENERATED (data);
const gchar *name = pika_object_get_name (data);
GString *string;
gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
gboolean have_shape = FALSE;
g_return_val_if_fail (name != NULL && *name != '\0', FALSE);
/* write magic header */
string = g_string_new ("PIKA-VBR\n");
/* write version */
if (brush->shape != PIKA_BRUSH_GENERATED_CIRCLE || brush->spikes > 2)
{
g_string_append (string, "1.5\n");
have_shape = TRUE;
}
else
{
g_string_append (string, "1.0\n");
}
/* write name */
g_string_append_printf (string, "%.255s\n", name);
if (have_shape)
{
GEnumClass *enum_class;
GEnumValue *shape_val;
enum_class = g_type_class_peek (PIKA_TYPE_BRUSH_GENERATED_SHAPE);
/* write shape */
shape_val = g_enum_get_value (enum_class, brush->shape);
g_string_append_printf (string, "%s\n", shape_val->value_nick);
}
/* write brush spacing */
g_string_append_printf (string, "%s\n",
g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE,
pika_brush_get_spacing (PIKA_BRUSH (brush))));
/* write brush radius */
g_string_append_printf (string, "%s\n",
g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE,
brush->radius));
if (have_shape)
{
/* write brush spikes */
g_string_append_printf (string, "%d\n", brush->spikes);
}
/* write brush hardness */
g_string_append_printf (string, "%s\n",
g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE,
brush->hardness));
/* write brush aspect_ratio */
g_string_append_printf (string, "%s\n",
g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE,
brush->aspect_ratio));
/* write brush angle */
g_string_append_printf (string, "%s\n",
g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE,
brush->angle));
if (! g_output_stream_write_all (output, string->str, string->len,
NULL, NULL, error))
{
g_string_free (string, TRUE);
return FALSE;
}
g_string_free (string, TRUE);
return TRUE;
}

View File

@ -0,0 +1,34 @@
/* 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
*
* brush_generated module Copyright 1998 Jay Cox <jaycox@earthlink.net>
*
* 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 __PIKA_BRUSH_GENERATED_SAVE_H__
#define __PIKA_BRUSH_GENERATED_SAVE_H__
/* don't call this function directly, use pika_data_save() instead */
gboolean pika_brush_generated_save (PikaData *data,
GOutputStream *output,
GError **error);
#endif /* __PIKA_BRUSH_GENERATED_SAVE_H__ */

View File

@ -0,0 +1,879 @@
/* 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
*
* pika_brush_generated module Copyright 1998 Jay Cox <jaycox@earthlink.net>
*
* 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 <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include "libpikabase/pikabase.h"
#include "libpikamath/pikamath.h"
#include "core-types.h"
#include "pikabrush-private.h"
#include "pikabrushgenerated.h"
#include "pikabrushgenerated-load.h"
#include "pikabrushgenerated-save.h"
#include "pikatempbuf.h"
#include "pika-intl.h"
#define OVERSAMPLING 4
enum
{
PROP_0,
PROP_SHAPE,
PROP_RADIUS,
PROP_SPIKES,
PROP_HARDNESS,
PROP_ASPECT_RATIO,
PROP_ANGLE
};
/* local function prototypes */
static void pika_brush_generated_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_brush_generated_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void pika_brush_generated_dirty (PikaData *data);
static const gchar * pika_brush_generated_get_extension (PikaData *data);
static void pika_brush_generated_copy (PikaData *data,
PikaData *src_data);
static void pika_brush_generated_transform_size(PikaBrush *gbrush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gint *width,
gint *height);
static PikaTempBuf * pika_brush_generated_transform_mask(PikaBrush *gbrush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gdouble hardness);
static PikaTempBuf * pika_brush_generated_calc (PikaBrushGenerated *brush,
PikaBrushGeneratedShape shape,
gfloat radius,
gint spikes,
gfloat hardness,
gfloat aspect_ratio,
gfloat angle,
gboolean reflect,
PikaVector2 *xaxis,
PikaVector2 *yaxis);
static void pika_brush_generated_get_size (PikaBrushGenerated *gbrush,
PikaBrushGeneratedShape shape,
gfloat radius,
gint spikes,
gfloat hardness,
gfloat aspect_ratio,
gdouble angle_in_degrees,
gboolean reflect,
gint *width,
gint *height,
gdouble *_s,
gdouble *_c,
PikaVector2 *_x_axis,
PikaVector2 *_y_axis);
G_DEFINE_TYPE (PikaBrushGenerated, pika_brush_generated, PIKA_TYPE_BRUSH)
#define parent_class pika_brush_generated_parent_class
static void
pika_brush_generated_class_init (PikaBrushGeneratedClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
PikaDataClass *data_class = PIKA_DATA_CLASS (klass);
PikaBrushClass *brush_class = PIKA_BRUSH_CLASS (klass);
object_class->set_property = pika_brush_generated_set_property;
object_class->get_property = pika_brush_generated_get_property;
data_class->save = pika_brush_generated_save;
data_class->dirty = pika_brush_generated_dirty;
data_class->get_extension = pika_brush_generated_get_extension;
data_class->copy = pika_brush_generated_copy;
brush_class->transform_size = pika_brush_generated_transform_size;
brush_class->transform_mask = pika_brush_generated_transform_mask;
g_object_class_install_property (object_class, PROP_SHAPE,
g_param_spec_enum ("shape", NULL,
_("Brush Shape"),
PIKA_TYPE_BRUSH_GENERATED_SHAPE,
PIKA_BRUSH_GENERATED_CIRCLE,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_RADIUS,
g_param_spec_double ("radius", NULL,
_("Brush Radius"),
0.1, 4000.0, 5.0,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_SPIKES,
g_param_spec_int ("spikes", NULL,
_("Brush Spikes"),
2, 20, 2,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_HARDNESS,
g_param_spec_double ("hardness", NULL,
_("Brush Hardness"),
0.0, 1.0, 0.0,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_ASPECT_RATIO,
g_param_spec_double ("aspect-ratio",
NULL,
_("Brush Aspect Ratio"),
1.0, 20.0, 1.0,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_ANGLE,
g_param_spec_double ("angle", NULL,
_("Brush Angle"),
0.0, 180.0, 0.0,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
}
static void
pika_brush_generated_init (PikaBrushGenerated *brush)
{
brush->shape = PIKA_BRUSH_GENERATED_CIRCLE;
brush->radius = 5.0;
brush->hardness = 0.0;
brush->aspect_ratio = 1.0;
brush->angle = 0.0;
}
static void
pika_brush_generated_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PikaBrushGenerated *brush = PIKA_BRUSH_GENERATED (object);
switch (property_id)
{
case PROP_SHAPE:
pika_brush_generated_set_shape (brush, g_value_get_enum (value));
break;
case PROP_RADIUS:
pika_brush_generated_set_radius (brush, g_value_get_double (value));
break;
case PROP_SPIKES:
pika_brush_generated_set_spikes (brush, g_value_get_int (value));
break;
case PROP_HARDNESS:
pika_brush_generated_set_hardness (brush, g_value_get_double (value));
break;
case PROP_ASPECT_RATIO:
pika_brush_generated_set_aspect_ratio (brush, g_value_get_double (value));
break;
case PROP_ANGLE:
pika_brush_generated_set_angle (brush, g_value_get_double (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_brush_generated_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaBrushGenerated *brush = PIKA_BRUSH_GENERATED (object);
switch (property_id)
{
case PROP_SHAPE:
g_value_set_enum (value, brush->shape);
break;
case PROP_RADIUS:
g_value_set_double (value, brush->radius);
break;
case PROP_SPIKES:
g_value_set_int (value, brush->spikes);
break;
case PROP_HARDNESS:
g_value_set_double (value, brush->hardness);
break;
case PROP_ASPECT_RATIO:
g_value_set_double (value, brush->aspect_ratio);
break;
case PROP_ANGLE:
g_value_set_double (value, brush->angle);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_brush_generated_dirty (PikaData *data)
{
PikaBrushGenerated *brush = PIKA_BRUSH_GENERATED (data);
PikaBrush *gbrush = PIKA_BRUSH (brush);
g_clear_pointer (&gbrush->priv->mask, pika_temp_buf_unref);
gbrush->priv->mask = pika_brush_generated_calc (brush,
brush->shape,
brush->radius,
brush->spikes,
brush->hardness,
brush->aspect_ratio,
brush->angle,
FALSE,
&gbrush->priv->x_axis,
&gbrush->priv->y_axis);
PIKA_DATA_CLASS (parent_class)->dirty (data);
}
static const gchar *
pika_brush_generated_get_extension (PikaData *data)
{
return PIKA_BRUSH_GENERATED_FILE_EXTENSION;
}
static void
pika_brush_generated_copy (PikaData *data,
PikaData *src_data)
{
PikaBrushGenerated *brush = PIKA_BRUSH_GENERATED (data);
PikaBrushGenerated *src_brush = PIKA_BRUSH_GENERATED (src_data);
pika_data_freeze (data);
pika_brush_set_spacing (PIKA_BRUSH (brush),
pika_brush_get_spacing (PIKA_BRUSH (src_brush)));
brush->shape = src_brush->shape;
brush->radius = src_brush->radius;
brush->spikes = src_brush->spikes;
brush->hardness = src_brush->hardness;
brush->aspect_ratio = src_brush->aspect_ratio;
brush->angle = src_brush->angle;
pika_data_thaw (data);
}
static void
pika_brush_generated_transform_size (PikaBrush *gbrush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gint *width,
gint *height)
{
PikaBrushGenerated *brush = PIKA_BRUSH_GENERATED (gbrush);
gdouble ratio;
ratio = fabs (aspect_ratio) * 19.0 / 20.0 + 1.0;
ratio = MIN (ratio, 20);
/* Since generated brushes are symmetric they don't have aspect
* ratios < 1.0. it's the same as rotating by 90 degrees and 1 /
* ratio, so we fix the input for this case.
*/
if (aspect_ratio < 0.0)
angle = angle + 0.25;
angle = fmod (fmod (angle, 1.0) + 1.0, 1.0);
angle *= 360;
pika_brush_generated_get_size (brush,
brush->shape,
brush->radius * scale,
brush->spikes,
brush->hardness,
ratio,
angle,
reflect,
width, height,
NULL, NULL, NULL, NULL);
}
static PikaTempBuf *
pika_brush_generated_transform_mask (PikaBrush *gbrush,
gdouble scale,
gdouble aspect_ratio,
gdouble angle,
gboolean reflect,
gdouble hardness)
{
PikaBrushGenerated *brush = PIKA_BRUSH_GENERATED (gbrush);
gdouble ratio;
ratio = fabs (aspect_ratio) * 19.0 / 20.0 + 1.0;
ratio = MIN (ratio, 20);
/* Since generated brushes are symmetric they don't have aspect
* ratios < 1.0. it's the same as rotating by 90 degrees and 1 /
* ratio, so we fix the input for this case.
*/
if (aspect_ratio < 0.0)
angle = angle + 0.25;
angle = fmod (fmod (angle, 1.0) + 1.0, 1.0);
angle *= 360;
if (scale == 1.0 &&
ratio == brush->aspect_ratio &&
angle == brush->angle &&
reflect == FALSE &&
hardness == brush->hardness)
{
return pika_temp_buf_copy (pika_brush_get_mask (gbrush));
}
return pika_brush_generated_calc (brush,
brush->shape,
brush->radius * scale,
brush->spikes,
hardness,
ratio,
angle,
reflect,
NULL, NULL);
}
/* the actual brush rendering functions */
static gdouble
gauss (gdouble f)
{
/* this aint' a real gauss function */
if (f < -0.5)
{
f = -1.0 - f;
return (2.0 * f*f);
}
if (f < 0.5)
return (1.0 - 2.0 * f*f);
f = 1.0 - f;
return (2.0 * f*f);
}
/* set up lookup table */
static gfloat *
pika_brush_generated_calc_lut (gdouble radius,
gdouble hardness)
{
gfloat *lookup;
gint length;
gint x;
gdouble d;
gdouble sum;
gdouble exponent;
gdouble buffer[OVERSAMPLING];
length = OVERSAMPLING * ceil (1 + sqrt (2 * SQR (ceil (radius + 1.0))));
lookup = gegl_scratch_new (gfloat, length);
sum = 0.0;
if ((1.0 - hardness) < 0.0000004)
exponent = 1000000.0;
else
exponent = 0.4 / (1.0 - hardness);
for (x = 0; x < OVERSAMPLING; x++)
{
d = fabs ((x + 0.5) / OVERSAMPLING - 0.5);
if (d > radius)
buffer[x] = 0.0;
else
buffer[x] = gauss (pow (d / radius, exponent));
sum += buffer[x];
}
for (x = 0; d < radius || sum > 0.00001; d += 1.0 / OVERSAMPLING)
{
sum -= buffer[x % OVERSAMPLING];
if (d > radius)
buffer[x % OVERSAMPLING] = 0.0;
else
buffer[x % OVERSAMPLING] = gauss (pow (d / radius, exponent));
sum += buffer[x % OVERSAMPLING];
lookup[x++] = sum / OVERSAMPLING;
}
while (x < length)
{
lookup[x++] = 0.0f;
}
return lookup;
}
static PikaTempBuf *
pika_brush_generated_calc (PikaBrushGenerated *brush,
PikaBrushGeneratedShape shape,
gfloat radius,
gint spikes,
gfloat hardness,
gfloat aspect_ratio,
gfloat angle,
gboolean reflect,
PikaVector2 *xaxis,
PikaVector2 *yaxis)
{
gfloat *centerp;
gfloat *lookup;
gfloat a;
gint x, y;
gdouble c, s, cs, ss;
PikaVector2 x_axis;
PikaVector2 y_axis;
PikaTempBuf *mask;
gint width;
gint height;
gint half_width;
gint half_height;
pika_brush_generated_get_size (brush,
shape,
radius,
spikes,
hardness,
aspect_ratio,
angle,
reflect,
&width, &height,
&s, &c, &x_axis, &y_axis);
mask = pika_temp_buf_new (width, height,
babl_format ("Y float"));
half_width = width / 2;
half_height = height / 2;
centerp = (gfloat *) pika_temp_buf_get_data (mask) +
half_height * width + half_width;
lookup = pika_brush_generated_calc_lut (radius, hardness);
cs = cos (- 2 * G_PI / spikes);
ss = sin (- 2 * G_PI / spikes);
/* for an even number of spikes compute one half and mirror it */
for (y = ((spikes % 2) ? -half_height : 0); y <= half_height; y++)
{
for (x = -half_width; x <= half_width; x++)
{
gdouble d = 0;
gdouble tx = c * x - s * y;
gdouble ty = fabs (s * x + c * y);
if (spikes > 2)
{
gdouble angle = atan2 (ty, tx);
while (angle > G_PI / spikes)
{
gdouble sx = tx;
gdouble sy = ty;
tx = cs * sx - ss * sy;
ty = ss * sx + cs * sy;
angle -= 2 * G_PI / spikes;
}
}
ty *= aspect_ratio;
switch (shape)
{
case PIKA_BRUSH_GENERATED_CIRCLE:
d = sqrt (SQR (tx) + SQR (ty));
break;
case PIKA_BRUSH_GENERATED_SQUARE:
d = MAX (fabs (tx), fabs (ty));
break;
case PIKA_BRUSH_GENERATED_DIAMOND:
d = fabs (tx) + fabs (ty);
break;
}
if (d < radius + 1)
a = lookup[(gint) RINT (d * OVERSAMPLING)];
else
a = 0.0f;
centerp[y * width + x] = a;
if (spikes % 2 == 0)
centerp[-1 * y * width - x] = a;
}
}
gegl_scratch_free (lookup);
if (xaxis)
*xaxis = x_axis;
if (yaxis)
*yaxis = y_axis;
return mask;
}
/* This function is shared between pika_brush_generated_transform_size and
* pika_brush_generated_calc, therefore we provide a bunch of optional
* pointers for returnvalues.
*/
static void
pika_brush_generated_get_size (PikaBrushGenerated *gbrush,
PikaBrushGeneratedShape shape,
gfloat radius,
gint spikes,
gfloat hardness,
gfloat aspect_ratio,
gdouble angle_in_degrees,
gboolean reflect,
gint *width,
gint *height,
gdouble *_s,
gdouble *_c,
PikaVector2 *_x_axis,
PikaVector2 *_y_axis)
{
gdouble half_width = 0.0;
gdouble half_height = 0.0;
gint w, h;
gdouble c, s;
gdouble short_radius;
PikaVector2 x_axis;
PikaVector2 y_axis;
/* Since floatongpoint is not really accurate,
* we need to round to limit the errors.
* Errors in some border cases resulted in
* different height and width reported for
* the same input value on calling procedure side.
* This became problem at the rise of dynamics that
* allows for any angle to turn up.
**/
angle_in_degrees = RINT (angle_in_degrees * 1000.0) / 1000.0;
s = sin (pika_deg_to_rad (angle_in_degrees));
c = cos (pika_deg_to_rad (angle_in_degrees));
if (reflect)
c = -c;
short_radius = radius / aspect_ratio;
x_axis.x = c * radius;
x_axis.y = -1.0 * s * radius;
y_axis.x = s * short_radius;
y_axis.y = c * short_radius;
switch (shape)
{
case PIKA_BRUSH_GENERATED_CIRCLE:
half_width = sqrt (x_axis.x * x_axis.x + y_axis.x * y_axis.x);
half_height = sqrt (x_axis.y * x_axis.y + y_axis.y * y_axis.y);
break;
case PIKA_BRUSH_GENERATED_SQUARE:
half_width = fabs (x_axis.x) + fabs (y_axis.x);
half_height = fabs (x_axis.y) + fabs (y_axis.y);
break;
case PIKA_BRUSH_GENERATED_DIAMOND:
half_width = MAX (fabs (x_axis.x), fabs (y_axis.x));
half_height = MAX (fabs (x_axis.y), fabs (y_axis.y));
break;
}
if (spikes > 2)
{
/* could be optimized by respecting the angle */
half_width = half_height = sqrt (radius * radius +
short_radius * short_radius);
y_axis.x = s * radius;
y_axis.y = c * radius;
}
w = MAX (1, ceil (half_width * 2));
h = MAX (1, ceil (half_height * 2));
if (! (w & 0x1)) w++;
if (! (h & 0x1)) h++;
*width = w;
*height = h;
/* These will typically be set then this function is called by
* pika_brush_generated_calc, which needs the values in its algorithms.
*/
if (_s != NULL)
*_s = s;
if (_c != NULL)
*_c = c;
if (_x_axis != NULL)
*_x_axis = x_axis;
if (_y_axis != NULL)
*_y_axis = y_axis;
}
/* public functions */
PikaData *
pika_brush_generated_new (const gchar *name,
PikaBrushGeneratedShape shape,
gfloat radius,
gint spikes,
gfloat hardness,
gfloat aspect_ratio,
gfloat angle)
{
PikaBrushGenerated *brush;
g_return_val_if_fail (name != NULL, NULL);
g_return_val_if_fail (*name != '\0', NULL);
brush = g_object_new (PIKA_TYPE_BRUSH_GENERATED,
"name", name,
"mime-type", "application/x-pika-brush-generated",
"spacing", 20.0,
"shape", shape,
"radius", radius,
"spikes", spikes,
"hardness", hardness,
"aspect-ratio", aspect_ratio,
"angle", angle,
NULL);
return PIKA_DATA (brush);
}
PikaBrushGeneratedShape
pika_brush_generated_set_shape (PikaBrushGenerated *brush,
PikaBrushGeneratedShape shape)
{
g_return_val_if_fail (PIKA_IS_BRUSH_GENERATED (brush),
PIKA_BRUSH_GENERATED_CIRCLE);
if (brush->shape != shape)
{
brush->shape = shape;
g_object_notify (G_OBJECT (brush), "shape");
pika_data_dirty (PIKA_DATA (brush));
}
return brush->shape;
}
gfloat
pika_brush_generated_set_radius (PikaBrushGenerated *brush,
gfloat radius)
{
g_return_val_if_fail (PIKA_IS_BRUSH_GENERATED (brush), -1.0);
radius = CLAMP (radius, 0.0, 32767.0);
if (brush->radius != radius)
{
brush->radius = radius;
g_object_notify (G_OBJECT (brush), "radius");
pika_data_dirty (PIKA_DATA (brush));
}
return brush->radius;
}
gint
pika_brush_generated_set_spikes (PikaBrushGenerated *brush,
gint spikes)
{
g_return_val_if_fail (PIKA_IS_BRUSH_GENERATED (brush), -1);
spikes = CLAMP (spikes, 2, 20);
if (brush->spikes != spikes)
{
brush->spikes = spikes;
g_object_notify (G_OBJECT (brush), "spikes");
pika_data_dirty (PIKA_DATA (brush));
}
return brush->spikes;
}
gfloat
pika_brush_generated_set_hardness (PikaBrushGenerated *brush,
gfloat hardness)
{
g_return_val_if_fail (PIKA_IS_BRUSH_GENERATED (brush), -1.0);
hardness = CLAMP (hardness, 0.0, 1.0);
if (brush->hardness != hardness)
{
brush->hardness = hardness;
g_object_notify (G_OBJECT (brush), "hardness");
pika_data_dirty (PIKA_DATA (brush));
}
return brush->hardness;
}
gfloat
pika_brush_generated_set_aspect_ratio (PikaBrushGenerated *brush,
gfloat ratio)
{
g_return_val_if_fail (PIKA_IS_BRUSH_GENERATED (brush), -1.0);
ratio = CLAMP (ratio, 1.0, 1000.0);
if (brush->aspect_ratio != ratio)
{
brush->aspect_ratio = ratio;
g_object_notify (G_OBJECT (brush), "aspect-ratio");
pika_data_dirty (PIKA_DATA (brush));
}
return brush->aspect_ratio;
}
gfloat
pika_brush_generated_set_angle (PikaBrushGenerated *brush,
gfloat angle)
{
g_return_val_if_fail (PIKA_IS_BRUSH_GENERATED (brush), -1.0);
if (angle < 0.0)
angle = -1.0 * fmod (angle, 180.0);
else if (angle > 180.0)
angle = fmod (angle, 180.0);
if (brush->angle != angle)
{
brush->angle = angle;
g_object_notify (G_OBJECT (brush), "angle");
pika_data_dirty (PIKA_DATA (brush));
}
return brush->angle;
}
PikaBrushGeneratedShape
pika_brush_generated_get_shape (PikaBrushGenerated *brush)
{
g_return_val_if_fail (PIKA_IS_BRUSH_GENERATED (brush),
PIKA_BRUSH_GENERATED_CIRCLE);
return brush->shape;
}
gfloat
pika_brush_generated_get_radius (PikaBrushGenerated *brush)
{
g_return_val_if_fail (PIKA_IS_BRUSH_GENERATED (brush), -1.0);
return brush->radius;
}
gint
pika_brush_generated_get_spikes (PikaBrushGenerated *brush)
{
g_return_val_if_fail (PIKA_IS_BRUSH_GENERATED (brush), -1);
return brush->spikes;
}
gfloat
pika_brush_generated_get_hardness (PikaBrushGenerated *brush)
{
g_return_val_if_fail (PIKA_IS_BRUSH_GENERATED (brush), -1.0);
return brush->hardness;
}
gfloat
pika_brush_generated_get_aspect_ratio (PikaBrushGenerated *brush)
{
g_return_val_if_fail (PIKA_IS_BRUSH_GENERATED (brush), -1.0);
return brush->aspect_ratio;
}
gfloat
pika_brush_generated_get_angle (PikaBrushGenerated *brush)
{
g_return_val_if_fail (PIKA_IS_BRUSH_GENERATED (brush), -1.0);
return brush->angle;
}

View File

@ -0,0 +1,92 @@
/* 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
*
* brush_generated module Copyright 1998 Jay Cox <jaycox@earthlink.net>
*
* 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 __PIKA_BRUSH_GENERATED_H__
#define __PIKA_BRUSH_GENERATED_H__
#include "pikabrush.h"
#define PIKA_TYPE_BRUSH_GENERATED (pika_brush_generated_get_type ())
#define PIKA_BRUSH_GENERATED(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_BRUSH_GENERATED, PikaBrushGenerated))
#define PIKA_BRUSH_GENERATED_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_BRUSH_GENERATED, PikaBrushGeneratedClass))
#define PIKA_IS_BRUSH_GENERATED(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_BRUSH_GENERATED))
#define PIKA_IS_BRUSH_GENERATED_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_BRUSH_GENERATED))
#define PIKA_BRUSH_GENERATED_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_BRUSH_GENERATED, PikaBrushGeneratedClass))
typedef struct _PikaBrushGeneratedClass PikaBrushGeneratedClass;
struct _PikaBrushGenerated
{
PikaBrush parent_instance;
PikaBrushGeneratedShape shape;
gfloat radius;
gint spikes; /* 2 - 20 */
gfloat hardness; /* 0.0 - 1.0 */
gfloat aspect_ratio; /* y/x */
gfloat angle; /* in degrees */
};
struct _PikaBrushGeneratedClass
{
PikaBrushClass parent_class;
};
GType pika_brush_generated_get_type (void) G_GNUC_CONST;
PikaData * pika_brush_generated_new (const gchar *name,
PikaBrushGeneratedShape shape,
gfloat radius,
gint spikes,
gfloat hardness,
gfloat aspect_ratio,
gfloat angle);
PikaBrushGeneratedShape
pika_brush_generated_set_shape (PikaBrushGenerated *brush,
PikaBrushGeneratedShape shape);
gfloat pika_brush_generated_set_radius (PikaBrushGenerated *brush,
gfloat radius);
gint pika_brush_generated_set_spikes (PikaBrushGenerated *brush,
gint spikes);
gfloat pika_brush_generated_set_hardness (PikaBrushGenerated *brush,
gfloat hardness);
gfloat pika_brush_generated_set_aspect_ratio (PikaBrushGenerated *brush,
gfloat ratio);
gfloat pika_brush_generated_set_angle (PikaBrushGenerated *brush,
gfloat angle);
PikaBrushGeneratedShape
pika_brush_generated_get_shape (PikaBrushGenerated *brush);
gfloat pika_brush_generated_get_radius (PikaBrushGenerated *brush);
gint pika_brush_generated_get_spikes (PikaBrushGenerated *brush);
gfloat pika_brush_generated_get_hardness (PikaBrushGenerated *brush);
gfloat pika_brush_generated_get_aspect_ratio (PikaBrushGenerated *brush);
gfloat pika_brush_generated_get_angle (PikaBrushGenerated *brush);
#endif /* __PIKA_BRUSH_GENERATED_H__ */

View File

@ -0,0 +1,167 @@
/* 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
* Copyright (C) 1999 Adrian Likins and Tor Lillqvist
*
* 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 <stdlib.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "libpikabase/pikabase.h"
#include "core-types.h"
#include "pikabrush-load.h"
#include "pikabrush-private.h"
#include "pikabrushpipe.h"
#include "pikabrushpipe-load.h"
#include "pika-intl.h"
GList *
pika_brush_pipe_load (PikaContext *context,
GFile *file,
GInputStream *input,
GError **error)
{
PikaBrushPipe *pipe = NULL;
gint n_brushes = 0;
GString *buffer;
gchar *paramstring;
gchar c;
gsize bytes_read;
g_return_val_if_fail (G_IS_FILE (file), NULL);
g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
/* The file format starts with a painfully simple text header */
/* get the name */
buffer = g_string_new (NULL);
while (g_input_stream_read_all (input, &c, 1, &bytes_read, NULL, NULL) &&
bytes_read == 1 &&
c != '\n' &&
buffer->len < 1024)
{
g_string_append_c (buffer, c);
}
if (buffer->len > 0 && buffer->len < 1024)
{
gchar *utf8 =
pika_any_to_utf8 (buffer->str, buffer->len,
_("Invalid UTF-8 string in brush file '%s'."),
pika_file_get_utf8_name (file));
pipe = g_object_new (PIKA_TYPE_BRUSH_PIPE,
"name", utf8,
"mime-type", "image/x-pika-gih",
NULL);
g_free (utf8);
}
g_string_free (buffer, TRUE);
if (! pipe)
{
g_set_error (error, PIKA_DATA_ERROR, PIKA_DATA_ERROR_READ,
_("Fatal parse error in brush file '%s': "
"File is corrupt."),
pika_file_get_utf8_name (file));
return NULL;
}
/* get the number of brushes */
buffer = g_string_new (NULL);
while (g_input_stream_read_all (input, &c, 1, &bytes_read, NULL, NULL) &&
bytes_read == 1 &&
c != '\n' &&
buffer->len < 1024)
{
g_string_append_c (buffer, c);
}
if (buffer->len > 0 && buffer->len < 1024)
{
n_brushes = strtol (buffer->str, &paramstring, 10);
}
if (n_brushes < 1)
{
g_set_error (error, PIKA_DATA_ERROR, PIKA_DATA_ERROR_READ,
_("Fatal parse error in brush file '%s': "
"File is corrupt."),
pika_file_get_utf8_name (file));
g_object_unref (pipe);
g_string_free (buffer, TRUE);
return NULL;
}
while (*paramstring && g_ascii_isspace (*paramstring))
paramstring++;
pipe->brushes = g_new0 (PikaBrush *, n_brushes);
while (pipe->n_brushes < n_brushes)
{
pipe->brushes[pipe->n_brushes] = pika_brush_load_brush (context,
file, input,
error);
if (! pipe->brushes[pipe->n_brushes])
{
g_object_unref (pipe);
g_string_free (buffer, TRUE);
return NULL;
}
pipe->n_brushes++;
}
if (! pika_brush_pipe_set_params (pipe, paramstring))
{
g_set_error (error, PIKA_DATA_ERROR, PIKA_DATA_ERROR_READ,
_("Fatal parse error in brush file '%s': "
"Inconsistent parameters."),
pika_file_get_utf8_name (file));
g_object_unref (pipe);
g_string_free (buffer, TRUE);
return NULL;
}
g_string_free (buffer, TRUE);
/* Current brush is the first one. */
pipe->current = pipe->brushes[0];
/* just to satisfy the code that relies on this crap */
PIKA_BRUSH (pipe)->priv->spacing = pipe->current->priv->spacing;
PIKA_BRUSH (pipe)->priv->x_axis = pipe->current->priv->x_axis;
PIKA_BRUSH (pipe)->priv->y_axis = pipe->current->priv->y_axis;
PIKA_BRUSH (pipe)->priv->mask = pipe->current->priv->mask;
PIKA_BRUSH (pipe)->priv->pixmap = pipe->current->priv->pixmap;
return g_list_prepend (NULL, pipe);
}

View File

@ -0,0 +1,36 @@
/* 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
* Copyright (C) 1999 Adrian Likins and Tor Lillqvist
*
* 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 __PIKA_BRUSH_PIPE_LOAD_H__
#define __PIKA_BRUSH_PIPE_LOAD_H__
#define PIKA_BRUSH_PIPE_FILE_EXTENSION ".gih"
GList * pika_brush_pipe_load (PikaContext *context,
GFile *file,
GInputStream *input,
GError **error);
#endif /* __PIKA_BRUSH_PIPE_LOAD_H__ */

View File

@ -0,0 +1,63 @@
/* 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 <gdk-pixbuf/gdk-pixbuf.h>
#include "core-types.h"
#include "pikabrushpipe.h"
#include "pikabrushpipe-save.h"
gboolean
pika_brush_pipe_save (PikaData *data,
GOutputStream *output,
GError **error)
{
PikaBrushPipe *pipe = PIKA_BRUSH_PIPE (data);
const gchar *name;
gint i;
name = pika_object_get_name (pipe);
if (! g_output_stream_printf (output, NULL, NULL, error,
"%s\n%d %s\n",
name, pipe->n_brushes, pipe->params))
{
return FALSE;
}
for (i = 0; i < pipe->n_brushes; i++)
{
PikaBrush *brush = pipe->brushes[i];
if (brush &&
! PIKA_DATA_GET_CLASS (brush)->save (PIKA_DATA (brush),
output, error))
{
return FALSE;
}
}
return TRUE;
}

View File

@ -0,0 +1,32 @@
/* 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/>.
*/
#ifndef __PIKA_BRUSH_PIPE_SAVE_H__
#define __PIKA_BRUSH_PIPE_SAVE_H__
/* don't call this function directly, use pika_data_save() instead */
gboolean pika_brush_pipe_save (PikaData *data,
GOutputStream *output,
GError **error);
#endif /* __PIKA_BRUSH_PIPE_SAVE_H__ */

424
app/core/pikabrushpipe.c Normal file
View File

@ -0,0 +1,424 @@
/* 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
* Copyright (C) 1999 Adrian Likins and Tor Lillqvist
*
* 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 <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include "libpikabase/pikaparasiteio.h"
#include "libpikamath/pikamath.h"
#include "core-types.h"
#include "pikabrush-private.h"
#include "pikabrushpipe.h"
#include "pikabrushpipe-load.h"
#include "pikabrushpipe-save.h"
#include "pikatempbuf.h"
static void pika_brush_pipe_finalize (GObject *object);
static gint64 pika_brush_pipe_get_memsize (PikaObject *object,
gint64 *gui_size);
static gboolean pika_brush_pipe_get_popup_size (PikaViewable *viewable,
gint width,
gint height,
gboolean dot_for_dot,
gint *popup_width,
gint *popup_height);
static const gchar * pika_brush_pipe_get_extension (PikaData *data);
static void pika_brush_pipe_copy (PikaData *data,
PikaData *src_data);
static void pika_brush_pipe_begin_use (PikaBrush *brush);
static void pika_brush_pipe_end_use (PikaBrush *brush);
static PikaBrush * pika_brush_pipe_select_brush (PikaBrush *brush,
const PikaCoords *last_coords,
const PikaCoords *current_coords);
static gboolean pika_brush_pipe_want_null_motion (PikaBrush *brush,
const PikaCoords *last_coords,
const PikaCoords *current_coords);
G_DEFINE_TYPE (PikaBrushPipe, pika_brush_pipe, PIKA_TYPE_BRUSH);
#define parent_class pika_brush_pipe_parent_class
static void
pika_brush_pipe_class_init (PikaBrushPipeClass *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);
PikaBrushClass *brush_class = PIKA_BRUSH_CLASS (klass);
object_class->finalize = pika_brush_pipe_finalize;
pika_object_class->get_memsize = pika_brush_pipe_get_memsize;
viewable_class->get_popup_size = pika_brush_pipe_get_popup_size;
data_class->save = pika_brush_pipe_save;
data_class->get_extension = pika_brush_pipe_get_extension;
data_class->copy = pika_brush_pipe_copy;
brush_class->begin_use = pika_brush_pipe_begin_use;
brush_class->end_use = pika_brush_pipe_end_use;
brush_class->select_brush = pika_brush_pipe_select_brush;
brush_class->want_null_motion = pika_brush_pipe_want_null_motion;
}
static void
pika_brush_pipe_init (PikaBrushPipe *pipe)
{
pipe->current = NULL;
pipe->dimension = 0;
pipe->rank = NULL;
pipe->stride = NULL;
pipe->n_brushes = 0;
pipe->brushes = NULL;
pipe->select = NULL;
pipe->index = NULL;
}
static void
pika_brush_pipe_finalize (GObject *object)
{
PikaBrushPipe *pipe = PIKA_BRUSH_PIPE (object);
g_clear_pointer (&pipe->rank, g_free);
g_clear_pointer (&pipe->stride, g_free);
g_clear_pointer (&pipe->select, g_free);
g_clear_pointer (&pipe->index, g_free);
g_clear_pointer (&pipe->params, g_free);
if (pipe->brushes)
{
gint i;
for (i = 0; i < pipe->n_brushes; i++)
if (pipe->brushes[i])
g_object_unref (pipe->brushes[i]);
g_clear_pointer (&pipe->brushes, g_free);
}
PIKA_BRUSH (pipe)->priv->mask = NULL;
PIKA_BRUSH (pipe)->priv->pixmap = NULL;
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gint64
pika_brush_pipe_get_memsize (PikaObject *object,
gint64 *gui_size)
{
PikaBrushPipe *pipe = PIKA_BRUSH_PIPE (object);
gint64 memsize = 0;
gint i;
memsize += pipe->dimension * (sizeof (gint) /* rank */ +
sizeof (gint) /* stride */ +
sizeof (PipeSelectModes));
for (i = 0; i < pipe->n_brushes; i++)
memsize += pika_object_get_memsize (PIKA_OBJECT (pipe->brushes[i]),
gui_size);
return memsize + PIKA_OBJECT_CLASS (parent_class)->get_memsize (object,
gui_size);
}
static gboolean
pika_brush_pipe_get_popup_size (PikaViewable *viewable,
gint width,
gint height,
gboolean dot_for_dot,
gint *popup_width,
gint *popup_height)
{
return pika_viewable_get_size (viewable, popup_width, popup_height);
}
static const gchar *
pika_brush_pipe_get_extension (PikaData *data)
{
return PIKA_BRUSH_PIPE_FILE_EXTENSION;
}
static void
pika_brush_pipe_copy (PikaData *data,
PikaData *src_data)
{
PikaBrushPipe *pipe = PIKA_BRUSH_PIPE (data);
PikaBrushPipe *src_pipe = PIKA_BRUSH_PIPE (src_data);
gint i;
pipe->dimension = src_pipe->dimension;
g_clear_pointer (&pipe->rank, g_free);
pipe->rank = g_memdup2 (src_pipe->rank,
pipe->dimension * sizeof (gint));
g_clear_pointer (&pipe->stride, g_free);
pipe->stride = g_memdup2 (src_pipe->stride,
pipe->dimension * sizeof (gint));
g_clear_pointer (&pipe->select, g_free);
pipe->select = g_memdup2 (src_pipe->select,
pipe->dimension * sizeof (PipeSelectModes));
g_clear_pointer (&pipe->index, g_free);
pipe->index = g_memdup2 (src_pipe->index,
pipe->dimension * sizeof (gint));
for (i = 0; i < pipe->n_brushes; i++)
if (pipe->brushes[i])
g_object_unref (pipe->brushes[i]);
g_clear_pointer (&pipe->brushes, g_free);
pipe->n_brushes = src_pipe->n_brushes;
pipe->brushes = g_new0 (PikaBrush *, pipe->n_brushes);
for (i = 0; i < pipe->n_brushes; i++)
if (src_pipe->brushes[i])
{
pipe->brushes[i] =
PIKA_BRUSH (pika_data_duplicate (PIKA_DATA (src_pipe->brushes[i])));
pika_object_set_name (PIKA_OBJECT (pipe->brushes[i]),
pika_object_get_name (src_pipe->brushes[i]));
}
g_clear_pointer (&pipe->params, g_free);
pipe->params = g_strdup (src_pipe->params);
pipe->current = pipe->brushes[0];
PIKA_BRUSH (pipe)->priv->spacing = pipe->current->priv->spacing;
PIKA_BRUSH (pipe)->priv->x_axis = pipe->current->priv->x_axis;
PIKA_BRUSH (pipe)->priv->y_axis = pipe->current->priv->y_axis;
PIKA_BRUSH (pipe)->priv->mask = pipe->current->priv->mask;
PIKA_BRUSH (pipe)->priv->pixmap = pipe->current->priv->pixmap;
pika_data_dirty (data);
}
static void
pika_brush_pipe_begin_use (PikaBrush *brush)
{
PikaBrushPipe *pipe = PIKA_BRUSH_PIPE (brush);
gint i;
PIKA_BRUSH_CLASS (parent_class)->begin_use (brush);
for (i = 0; i < pipe->n_brushes; i++)
if (pipe->brushes[i])
pika_brush_begin_use (pipe->brushes[i]);
}
static void
pika_brush_pipe_end_use (PikaBrush *brush)
{
PikaBrushPipe *pipe = PIKA_BRUSH_PIPE (brush);
gint i;
PIKA_BRUSH_CLASS (parent_class)->end_use (brush);
for (i = 0; i < pipe->n_brushes; i++)
if (pipe->brushes[i])
pika_brush_end_use (pipe->brushes[i]);
}
static PikaBrush *
pika_brush_pipe_select_brush (PikaBrush *brush,
const PikaCoords *last_coords,
const PikaCoords *current_coords)
{
PikaBrushPipe *pipe = PIKA_BRUSH_PIPE (brush);
gint i, brushix, ix;
if (pipe->n_brushes == 1)
return PIKA_BRUSH (pipe->current);
brushix = 0;
for (i = 0; i < pipe->dimension; i++)
{
switch (pipe->select[i])
{
case PIPE_SELECT_INCREMENTAL:
ix = (pipe->index[i] + 1) % pipe->rank[i];
break;
case PIPE_SELECT_ANGULAR:
/* Coords angle is already nomalized,
* offset by 90 degrees is still needed
* because hoses were made PS compatible*/
ix = (gint) RINT ((1.0 - current_coords->direction + 0.25) * pipe->rank[i]) % pipe->rank[i];
break;
case PIPE_SELECT_VELOCITY:
ix = ROUND (current_coords->velocity * pipe->rank[i]);
break;
case PIPE_SELECT_RANDOM:
/* This probably isn't the right way */
ix = g_random_int_range (0, pipe->rank[i]);
break;
case PIPE_SELECT_PRESSURE:
ix = RINT (current_coords->pressure * (pipe->rank[i] - 1));
break;
case PIPE_SELECT_TILT_X:
ix = RINT (current_coords->xtilt / 2.0 * pipe->rank[i]) + pipe->rank[i] / 2;
break;
case PIPE_SELECT_TILT_Y:
ix = RINT (current_coords->ytilt / 2.0 * pipe->rank[i]) + pipe->rank[i] / 2;
break;
case PIPE_SELECT_CONSTANT:
default:
ix = pipe->index[i];
break;
}
pipe->index[i] = CLAMP (ix, 0, pipe->rank[i] - 1);
brushix += pipe->stride[i] * pipe->index[i];
}
/* Make sure is inside bounds */
brushix = CLAMP (brushix, 0, pipe->n_brushes - 1);
pipe->current = pipe->brushes[brushix];
return PIKA_BRUSH (pipe->current);
}
static gboolean
pika_brush_pipe_want_null_motion (PikaBrush *brush,
const PikaCoords *last_coords,
const PikaCoords *current_coords)
{
PikaBrushPipe *pipe = PIKA_BRUSH_PIPE (brush);
gint i;
if (pipe->n_brushes == 1)
return TRUE;
for (i = 0; i < pipe->dimension; i++)
if (pipe->select[i] == PIPE_SELECT_ANGULAR)
return FALSE;
return TRUE;
}
/* public functions */
gboolean
pika_brush_pipe_set_params (PikaBrushPipe *pipe,
const gchar *paramstring)
{
gint totalcells;
gint i;
g_return_val_if_fail (PIKA_IS_BRUSH_PIPE (pipe), FALSE);
g_return_val_if_fail (pipe->dimension == 0, FALSE); /* only on a new pipe! */
if (paramstring && *paramstring)
{
PikaPixPipeParams params;
pika_pixpipe_params_init (&params);
pika_pixpipe_params_parse (paramstring, &params);
pipe->dimension = params.dim;
pipe->rank = g_new0 (gint, pipe->dimension);
pipe->select = g_new0 (PipeSelectModes, pipe->dimension);
pipe->index = g_new0 (gint, pipe->dimension);
/* placement is not used at all ?? */
for (i = 0; i < pipe->dimension; i++)
{
pipe->rank[i] = MAX (1, params.rank[i]);
if (strcmp (params.selection[i], "incremental") == 0)
pipe->select[i] = PIPE_SELECT_INCREMENTAL;
else if (strcmp (params.selection[i], "angular") == 0)
pipe->select[i] = PIPE_SELECT_ANGULAR;
else if (strcmp (params.selection[i], "velocity") == 0)
pipe->select[i] = PIPE_SELECT_VELOCITY;
else if (strcmp (params.selection[i], "random") == 0)
pipe->select[i] = PIPE_SELECT_RANDOM;
else if (strcmp (params.selection[i], "pressure") == 0)
pipe->select[i] = PIPE_SELECT_PRESSURE;
else if (strcmp (params.selection[i], "xtilt") == 0)
pipe->select[i] = PIPE_SELECT_TILT_X;
else if (strcmp (params.selection[i], "ytilt") == 0)
pipe->select[i] = PIPE_SELECT_TILT_Y;
else
pipe->select[i] = PIPE_SELECT_CONSTANT;
pipe->index[i] = 0;
}
pika_pixpipe_params_free (&params);
pipe->params = g_strdup (paramstring);
}
else
{
pipe->dimension = 1;
pipe->rank = g_new (gint, 1);
pipe->rank[0] = pipe->n_brushes;
pipe->select = g_new (PipeSelectModes, 1);
pipe->select[0] = PIPE_SELECT_INCREMENTAL;
pipe->index = g_new (gint, 1);
pipe->index[0] = 0;
}
totalcells = 1; /* Not all necessarily present, maybe */
for (i = 0; i < pipe->dimension; i++)
totalcells *= pipe->rank[i];
pipe->stride = g_new0 (gint, pipe->dimension);
for (i = 0; i < pipe->dimension; i++)
{
if (i == 0)
pipe->stride[i] = totalcells / pipe->rank[i];
else
pipe->stride[i] = pipe->stride[i-1] / pipe->rank[i];
}
if (pipe->stride[pipe->dimension - 1] != 1)
return FALSE;
return TRUE;
}

Some files were not shown because too many files have changed in this diff Show More