2016-02-16 02:35:43 +01:00
|
|
|
|
/* GIMP plug-in to load and export Paint Shop Pro files (.PSP and .TUB)
|
1999-08-20 00:47:05 +00:00
|
|
|
|
*
|
|
|
|
|
* Copyright (C) 1999 Tor Lillqvist
|
|
|
|
|
*
|
2009-01-17 22:28:01 +00:00
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
1999-08-20 00:47:05 +00:00
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
2009-01-17 22:28:01 +00:00
|
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
1999-08-20 00:47:05 +00:00
|
|
|
|
* (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
|
2018-07-11 23:27:07 +02:00
|
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
1999-08-20 00:47:05 +00:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*
|
2016-02-16 02:35:43 +01:00
|
|
|
|
* Work in progress! Doesn't handle exporting yet.
|
1999-08-20 00:47:05 +00:00
|
|
|
|
*
|
1999-08-20 23:52:19 +00:00
|
|
|
|
* For a copy of the PSP file format documentation, surf to
|
|
|
|
|
* http://www.jasc.com.
|
|
|
|
|
*
|
1999-08-20 00:47:05 +00:00
|
|
|
|
*/
|
|
|
|
|
|
2005-08-15 11:07:27 +00:00
|
|
|
|
#define LOAD_PROC "file-psp-load"
|
2024-04-13 15:10:25 +00:00
|
|
|
|
#define EXPORT_PROC "file-psp-export"
|
2008-08-11 10:06:13 +00:00
|
|
|
|
#define PLUG_IN_BINARY "file-psp"
|
2011-04-08 20:31:34 +02:00
|
|
|
|
#define PLUG_IN_ROLE "gimp-file-psp"
|
2005-08-15 11:07:27 +00:00
|
|
|
|
|
1999-08-20 00:47:05 +00:00
|
|
|
|
/* set to the level of debugging output you want, 0 for none */
|
1999-08-22 20:08:15 +00:00
|
|
|
|
#define PSP_DEBUG 0
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
|
|
|
|
#define IFDBG(level) if (PSP_DEBUG >= level)
|
|
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
2003-06-13 14:37:00 +00:00
|
|
|
|
#include <errno.h>
|
1999-08-20 00:47:05 +00:00
|
|
|
|
#include <string.h>
|
2000-01-08 15:23:28 +00:00
|
|
|
|
#include <zlib.h>
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
2025-05-29 13:10:15 +00:00
|
|
|
|
#include <gexiv2/gexiv2.h>
|
2005-03-04 15:12:29 +00:00
|
|
|
|
#include <glib/gstdio.h>
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
|
|
|
|
#include <libgimp/gimp.h>
|
1999-10-20 01:45:41 +00:00
|
|
|
|
#include <libgimp/gimpui.h>
|
2001-05-21 20:30:16 +00:00
|
|
|
|
#include <libgimpbase/gimpparasiteio.h>
|
2000-01-08 15:23:28 +00:00
|
|
|
|
|
2000-05-01 17:01:18 +00:00
|
|
|
|
#include "libgimp/stdplugins-intl.h"
|
|
|
|
|
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
1999-08-21 19:00:41 +00:00
|
|
|
|
/* Note that the upcoming PSP version 6 writes PSP file format version
|
1999-08-20 23:52:19 +00:00
|
|
|
|
* 4.0, but the documentation for that apparently isn't publicly
|
1999-08-21 19:00:41 +00:00
|
|
|
|
* available (yet). The format is luckily designed to be somwehat
|
|
|
|
|
* downward compatible, however. The semantics of many of the
|
|
|
|
|
* additional fields and block types can be relatively easily reverse
|
|
|
|
|
* engineered.
|
1999-08-20 00:47:05 +00:00
|
|
|
|
*/
|
|
|
|
|
|
1999-08-21 19:00:41 +00:00
|
|
|
|
/* The following was cut and pasted from the PSP file format
|
|
|
|
|
* documentation version 3.0.(Minor stylistic changes done.)
|
|
|
|
|
*
|
|
|
|
|
*
|
1999-08-20 00:47:05 +00:00
|
|
|
|
* To be on the safe side, here is the whole copyright notice from the
|
|
|
|
|
* specification:
|
|
|
|
|
*
|
1999-08-20 23:52:19 +00:00
|
|
|
|
* The Paint Shop Pro File Format Specification (the Specification) is
|
|
|
|
|
* copyright 1998 by Jasc Software, Inc. Jasc grants you a
|
|
|
|
|
* nonexclusive license to use the Specification for the sole purposes
|
|
|
|
|
* of developing software products(s) incorporating the
|
|
|
|
|
* Specification. You are also granted the right to identify your
|
|
|
|
|
* software product(s) as incorporating the Paint Shop Pro Format
|
|
|
|
|
* (PSP) provided that your software in incorporating the
|
|
|
|
|
* Specification complies with the terms, definitions, constraints and
|
|
|
|
|
* specifications contained in the Specification and subject to the
|
|
|
|
|
* following: DISCLAIMER OF WARRANTIES. THE SPECIFICATION IS PROVIDED
|
|
|
|
|
* AS IS. JASC DISCLAIMS ALL OTHER WARRANTIES, EXPRESS OR IMPLIED,
|
|
|
|
|
* INCLUDING, BUT NOT LIMITED TO, ANY IMPLIED WARRANTIES OF
|
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
|
* NONINFRINGEMENT.
|
1999-08-20 00:47:05 +00:00
|
|
|
|
*
|
|
|
|
|
* You are solely responsible for the selection, use, efficiency and
|
|
|
|
|
* suitability of the Specification for your software products. OTHER
|
|
|
|
|
* WARRANTIES EXCLUDED. JASC SHALL NOT BE LIABLE FOR ANY DIRECT,
|
|
|
|
|
* INDIRECT, CONSEQUENTIAL, EXEMPLARY, PUNITIVE OR INCIDENTAL DAMAGES
|
|
|
|
|
* ARISING FROM ANY CAUSE EVEN IF JASC HAS BEEN ADVISED OF THE
|
|
|
|
|
* POSSIBILITY OF SUCH DAMAGES. CERTAIN JURISDICTIONS DO NOT PERMIT
|
|
|
|
|
* THE LIMITATION OR EXCLUSION OF INCIDENTAL DAMAGES, SO THIS
|
|
|
|
|
* LIMITATION MAY NOT APPLY TO YOU. IN NO EVENT WILL JASC BE LIABLE
|
|
|
|
|
* FOR ANY AMOUNT GREATER THAN WHAT YOU ACTUALLY PAID FOR THE
|
|
|
|
|
* SPECIFICATION. Should any warranties be found to exist, such
|
|
|
|
|
* warranties shall be limited in duration to ninety (90) days
|
|
|
|
|
* following the date you receive the Specification.
|
|
|
|
|
*
|
|
|
|
|
* Indemnification. By your inclusion of the Paint Shop Pro File
|
|
|
|
|
* Format in your software product(s) you agree to indemnify and hold
|
|
|
|
|
* Jasc Software, Inc. harmless from any and all claims of any kind or
|
|
|
|
|
* nature made by any of your customers with respect to your software
|
|
|
|
|
* product(s).
|
|
|
|
|
*
|
|
|
|
|
* Export Laws. You agree that you and your customers will not export
|
|
|
|
|
* your software or Specification except in compliance with the laws
|
|
|
|
|
* and regulations of the United States.
|
|
|
|
|
*
|
|
|
|
|
* US Government Restricted Rights. The Specification and any
|
|
|
|
|
* accompanying materials are provided with Restricted Rights. Use,
|
|
|
|
|
* duplication or disclosure by the Government is subject to
|
|
|
|
|
* restrictions as set forth in subparagraph (c)(1)(ii) of The Rights
|
|
|
|
|
* in Technical Data and Computer Software clause at DFARS
|
|
|
|
|
* 252.227-7013, or subparagraphs (c)(1) and (2) of the Commercial
|
|
|
|
|
* Computer Software - Restricted Rights at 48 CFR 52.227-19, as
|
|
|
|
|
* applicable. Contractor/manufacturer is Jasc Software, Inc., PO Box
|
|
|
|
|
* 44997, Eden Prairie MN 55344.
|
|
|
|
|
*
|
|
|
|
|
* Jasc reserves the right to amend, modify, change, revoke or
|
|
|
|
|
* withdraw the Specification at any time and from time to time. Jasc
|
1999-08-21 19:00:41 +00:00
|
|
|
|
* shall have no obligation to support or maintain the Specification.
|
|
|
|
|
*/
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
|
|
|
|
/* Block identifiers.
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
2008-04-21 16:31:33 +00:00
|
|
|
|
PSP_IMAGE_BLOCK = 0, /* General Image Attributes Block (main) */
|
|
|
|
|
PSP_CREATOR_BLOCK, /* Creator Data Block (main) */
|
|
|
|
|
PSP_COLOR_BLOCK, /* Color Palette Block (main and sub) */
|
|
|
|
|
PSP_LAYER_START_BLOCK, /* Layer Bank Block (main) */
|
|
|
|
|
PSP_LAYER_BLOCK, /* Layer Block (sub) */
|
|
|
|
|
PSP_CHANNEL_BLOCK, /* Channel Block (sub) */
|
|
|
|
|
PSP_SELECTION_BLOCK, /* Selection Block (main) */
|
|
|
|
|
PSP_ALPHA_BANK_BLOCK, /* Alpha Bank Block (main) */
|
|
|
|
|
PSP_ALPHA_CHANNEL_BLOCK, /* Alpha Channel Block (sub) */
|
|
|
|
|
PSP_THUMBNAIL_BLOCK, /* Thumbnail Block (main) */
|
|
|
|
|
PSP_EXTENDED_DATA_BLOCK, /* Extended Data Block (main) */
|
|
|
|
|
PSP_TUBE_BLOCK, /* Picture Tube Data Block (main) */
|
|
|
|
|
PSP_ADJUSTMENT_EXTENSION_BLOCK, /* Adjustment Layer Extension Block (sub) (since PSP6)*/
|
|
|
|
|
PSP_VECTOR_EXTENSION_BLOCK, /* Vector Layer Extension Block (sub) (since PSP6) */
|
|
|
|
|
PSP_SHAPE_BLOCK, /* Vector Shape Block (sub) (since PSP6) */
|
|
|
|
|
PSP_PAINTSTYLE_BLOCK, /* Paint Style Block (sub) (since PSP6) */
|
|
|
|
|
PSP_COMPOSITE_IMAGE_BANK_BLOCK, /* Composite Image Bank (main) (since PSP6) */
|
|
|
|
|
PSP_COMPOSITE_ATTRIBUTES_BLOCK, /* Composite Image Attributes (sub) (since PSP6) */
|
|
|
|
|
PSP_JPEG_BLOCK, /* JPEG Image Block (sub) (since PSP6) */
|
|
|
|
|
PSP_LINESTYLE_BLOCK, /* Line Style Block (sub) (since PSP7) */
|
|
|
|
|
PSP_TABLE_BANK_BLOCK, /* Table Bank Block (main) (since PSP7) */
|
|
|
|
|
PSP_TABLE_BLOCK, /* Table Block (sub) (since PSP7) */
|
|
|
|
|
PSP_PAPER_BLOCK, /* Vector Table Paper Block (sub) (since PSP7) */
|
|
|
|
|
PSP_PATTERN_BLOCK, /* Vector Table Pattern Block (sub) (since PSP7) */
|
|
|
|
|
PSP_GRADIENT_BLOCK, /* Vector Table Gradient Block (not used) (since PSP8) */
|
|
|
|
|
PSP_GROUP_EXTENSION_BLOCK, /* Group Layer Block (sub) (since PSP8) */
|
|
|
|
|
PSP_MASK_EXTENSION_BLOCK, /* Mask Layer Block (sub) (since PSP8) */
|
|
|
|
|
PSP_BRUSH_BLOCK, /* Brush Data Block (main) (since PSP8) */
|
2020-09-05 23:25:35 -04:00
|
|
|
|
PSP_ART_MEDIA_BLOCK, /* Art Media Layer Block (main) (since PSP9) */
|
|
|
|
|
PSP_ART_MEDIA_MAP_BLOCK, /* Art Media Layer map data Block (main) (since PSP9) */
|
|
|
|
|
PSP_ART_MEDIA_TILE_BLOCK, /* Art Media Layer map tile Block(main) (since PSP9) */
|
|
|
|
|
PSP_ART_MEDIA_TEXTURE_BLOCK, /* AM Layer map texture Block (main) (since PSP9) */
|
|
|
|
|
PSP_COLORPROFILE_BLOCK, /* ICC Color profile block (since PSP10) */
|
|
|
|
|
PSP_RASTER_EXTENSION_BLOCK, /* Assumed name based on usage, probably since PSP11 */
|
1999-08-20 00:47:05 +00:00
|
|
|
|
} PSPBlockID;
|
|
|
|
|
|
|
|
|
|
/* Bitmap type.
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
2008-04-19 17:59:32 +00:00
|
|
|
|
PSP_DIB_IMAGE = 0, /* Layer color bitmap */
|
|
|
|
|
PSP_DIB_TRANS_MASK, /* Layer transparency mask bitmap */
|
|
|
|
|
PSP_DIB_USER_MASK, /* Layer user mask bitmap */
|
|
|
|
|
PSP_DIB_SELECTION, /* Selection mask bitmap */
|
|
|
|
|
PSP_DIB_ALPHA_MASK, /* Alpha channel mask bitmap */
|
2008-04-21 16:31:33 +00:00
|
|
|
|
PSP_DIB_THUMBNAIL, /* Thumbnail bitmap */
|
|
|
|
|
PSP_DIB_THUMBNAIL_TRANS_MASK, /* Thumbnail transparency mask (since PSP6) */
|
|
|
|
|
PSP_DIB_ADJUSTMENT_LAYER, /* Adjustment layer bitmap (since PSP6) */
|
|
|
|
|
PSP_DIB_COMPOSITE, /* Composite image bitmap (since PSP6) */
|
|
|
|
|
PSP_DIB_COMPOSITE_TRANS_MASK, /* Composite image transparency (since PSP6) */
|
|
|
|
|
PSP_DIB_PAPER, /* Paper bitmap (since PSP7) */
|
|
|
|
|
PSP_DIB_PATTERN, /* Pattern bitmap (since PSP7) */
|
|
|
|
|
PSP_DIB_PATTERN_TRANS_MASK, /* Pattern transparency mask (since PSP7) */
|
1999-08-20 00:47:05 +00:00
|
|
|
|
} PSPDIBType;
|
|
|
|
|
|
2008-04-21 16:31:33 +00:00
|
|
|
|
/* Type of image in the composite image bank block. (since PSP6)
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
|
|
|
|
PSP_IMAGE_COMPOSITE = 0, /* Composite Image */
|
|
|
|
|
PSP_IMAGE_THUMBNAIL, /* Thumbnail Image */
|
|
|
|
|
} PSPCompositeImageType;
|
|
|
|
|
|
|
|
|
|
/* Graphic contents flags. (since PSP6)
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
|
|
|
|
/* Layer types */
|
|
|
|
|
keGCRasterLayers = 0x00000001, /* At least one raster layer */
|
|
|
|
|
keGCVectorLayers = 0x00000002, /* At least one vector layer */
|
|
|
|
|
keGCAdjustmentLayers = 0x00000004, /* At least one adjustment layer */
|
2020-09-05 23:25:35 -04:00
|
|
|
|
keGCGroupLayers = 0x00000008, /* at least one group layer */
|
|
|
|
|
keGCMaskLayers = 0x00000010, /* at least one mask layer */
|
|
|
|
|
keGCArtMediaLayers = 0x00000020, /* at least one art media layer */
|
2008-04-21 16:31:33 +00:00
|
|
|
|
|
|
|
|
|
/* Additional attributes */
|
2020-09-05 23:25:35 -04:00
|
|
|
|
keGCMergedCache = 0x00800000, /* merged cache (composite image) */
|
2008-04-21 16:31:33 +00:00
|
|
|
|
keGCThumbnail = 0x01000000, /* Has a thumbnail */
|
|
|
|
|
keGCThumbnailTransparency = 0x02000000, /* Thumbnail transp. */
|
|
|
|
|
keGCComposite = 0x04000000, /* Has a composite image */
|
|
|
|
|
keGCCompositeTransparency = 0x08000000, /* Composite transp. */
|
|
|
|
|
keGCFlatImage = 0x10000000, /* Just a background */
|
|
|
|
|
keGCSelection = 0x20000000, /* Has a selection */
|
|
|
|
|
keGCFloatingSelectionLayer = 0x40000000, /* Has float. selection */
|
|
|
|
|
keGCAlphaChannels = 0x80000000, /* Has alpha channel(s) */
|
|
|
|
|
} PSPGraphicContents;
|
|
|
|
|
|
|
|
|
|
/* Character style flags. (since PSP6)
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
|
|
|
|
keStyleItalic = 0x00000001, /* Italic property bit */
|
|
|
|
|
keStyleStruck = 0x00000002, /* Strikeout property bit */
|
|
|
|
|
keStyleUnderlined = 0x00000004, /* Underlined property bit */
|
|
|
|
|
keStyleWarped = 0x00000008, /* Warped property bit (since PSP8) */
|
|
|
|
|
keStyleAntiAliased = 0x00000010, /* Antialiased property bit (since PSP8) */
|
|
|
|
|
} PSPCharacterProperties;
|
|
|
|
|
|
|
|
|
|
/* Table type. (since PSP7)
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
|
|
|
|
keTTUndefined = 0, /* Undefined table type */
|
|
|
|
|
keTTGradientTable, /* Gradient table type */
|
|
|
|
|
keTTPaperTable, /* Paper table type */
|
|
|
|
|
keTTPatternTable /* Pattern table type */
|
|
|
|
|
} PSPTableType;
|
|
|
|
|
|
|
|
|
|
/* Layer flags. (since PSP6)
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
|
|
|
|
keVisibleFlag = 0x00000001, /* Layer is visible */
|
|
|
|
|
keMaskPresenceFlag = 0x00000002, /* Layer has a mask */
|
|
|
|
|
} PSPLayerProperties;
|
|
|
|
|
|
|
|
|
|
/* Shape property flags. (since PSP6)
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
|
|
|
|
keShapeAntiAliased = 0x00000001, /* Shape is antialiased */
|
|
|
|
|
keShapeSelected = 0x00000002, /* Shape is selected */
|
|
|
|
|
keShapeVisible = 0x00000004, /* Shape is visible */
|
|
|
|
|
} PSPShapeProperties;
|
|
|
|
|
|
|
|
|
|
/* Polyline node type flags. (since PSP7)
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
|
|
|
|
keNodeUnconstrained = 0x0000, /* Default node type */
|
|
|
|
|
keNodeSmooth = 0x0001, /* Node is smooth */
|
|
|
|
|
keNodeSymmetric = 0x0002, /* Node is symmetric */
|
|
|
|
|
keNodeAligned = 0x0004, /* Node is aligned */
|
|
|
|
|
keNodeActive = 0x0008, /* Node is active */
|
|
|
|
|
keNodeLocked = 0x0010, /* Node is locked */
|
|
|
|
|
keNodeSelected = 0x0020, /* Node is selected */
|
|
|
|
|
keNodeVisible = 0x0040, /* Node is visible */
|
|
|
|
|
keNodeClosed = 0x0080, /* Node is closed */
|
|
|
|
|
|
|
|
|
|
/* TODO: This might be a thinko in the spec document only or in the image
|
|
|
|
|
* format itself. Need to investigate that later
|
|
|
|
|
*/
|
|
|
|
|
keNodeLockedPSP6 = 0x0016, /* Node is locked */
|
|
|
|
|
keNodeSelectedPSP6 = 0x0032, /* Node is selected */
|
|
|
|
|
keNodeVisiblePSP6 = 0x0064, /* Node is visible */
|
|
|
|
|
keNodeClosedPSP6 = 0x0128, /* Node is closed */
|
|
|
|
|
|
|
|
|
|
} PSPPolylineNodeTypes;
|
|
|
|
|
|
|
|
|
|
/* Blend modes. (since PSP6)
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
|
|
|
|
PSP_BLEND_NORMAL,
|
|
|
|
|
PSP_BLEND_DARKEN,
|
|
|
|
|
PSP_BLEND_LIGHTEN,
|
|
|
|
|
PSP_BLEND_HUE,
|
|
|
|
|
PSP_BLEND_SATURATION,
|
|
|
|
|
PSP_BLEND_COLOR,
|
|
|
|
|
PSP_BLEND_LUMINOSITY,
|
|
|
|
|
PSP_BLEND_MULTIPLY,
|
|
|
|
|
PSP_BLEND_SCREEN,
|
|
|
|
|
PSP_BLEND_DISSOLVE,
|
|
|
|
|
PSP_BLEND_OVERLAY,
|
|
|
|
|
PSP_BLEND_HARD_LIGHT,
|
|
|
|
|
PSP_BLEND_SOFT_LIGHT,
|
|
|
|
|
PSP_BLEND_DIFFERENCE,
|
|
|
|
|
PSP_BLEND_DODGE,
|
|
|
|
|
PSP_BLEND_BURN,
|
|
|
|
|
PSP_BLEND_EXCLUSION,
|
|
|
|
|
PSP_BLEND_TRUE_HUE, /* since PSP8 */
|
|
|
|
|
PSP_BLEND_TRUE_SATURATION, /* since PSP8 */
|
|
|
|
|
PSP_BLEND_TRUE_COLOR, /* since PSP8 */
|
|
|
|
|
PSP_BLEND_TRUE_LIGHTNESS, /* since PSP8 */
|
|
|
|
|
PSP_BLEND_ADJUST = 255,
|
|
|
|
|
} PSPBlendModes;
|
|
|
|
|
|
|
|
|
|
/* Adjustment layer types. (since PSP6)
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
|
|
|
|
keAdjNone = 0, /* Undefined adjustment layer type */
|
|
|
|
|
keAdjLevel, /* Level adjustment */
|
|
|
|
|
keAdjCurve, /* Curve adjustment */
|
|
|
|
|
keAdjBrightContrast, /* Brightnesscontrast adjustment */
|
|
|
|
|
keAdjColorBal, /* Color balance adjustment */
|
|
|
|
|
keAdjHSL, /* HSL adjustment */
|
|
|
|
|
keAdjChannelMixer, /* Channel mixer adjustment */
|
|
|
|
|
keAdjInvert, /* Invert adjustment */
|
|
|
|
|
keAdjThreshold, /* Threshold adjustment */
|
|
|
|
|
keAdjPoster /* Posterize adjustment */
|
|
|
|
|
} PSPAdjustmentLayerType;
|
|
|
|
|
|
|
|
|
|
/* Vector shape types. (since PSP6)
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
|
|
|
|
keVSTUnknown = 0, /* Undefined vector type */
|
|
|
|
|
keVSTText, /* Shape represents lines of text */
|
|
|
|
|
keVSTPolyline, /* Shape represents a multiple segment line */
|
|
|
|
|
keVSTEllipse, /* Shape represents an ellipse (or circle) */
|
|
|
|
|
keVSTPolygon, /* Shape represents a closed polygon */
|
|
|
|
|
keVSTGroup, /* Shape represents a group shape (since PSP7) */
|
|
|
|
|
} PSPVectorShapeType;
|
|
|
|
|
|
|
|
|
|
/* Text element types. (since PSP6)
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
|
|
|
|
keTextElemUnknown = 0, /* Undefined text element type */
|
|
|
|
|
keTextElemChar, /* A single character code */
|
|
|
|
|
keTextElemCharStyle, /* A character style change */
|
|
|
|
|
keTextElemLineStyle /* A line style change */
|
|
|
|
|
} PSPTextElementType;
|
|
|
|
|
|
|
|
|
|
/* Text alignment types. (since PSP6)
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
|
|
|
|
keTextAlignmentLeft = 0, /* Left text alignment */
|
|
|
|
|
keTextAlignmentCenter, /* Center text alignment */
|
|
|
|
|
keTextAlignmentRight /* Right text alignment */
|
|
|
|
|
} PSPTextAlignment;
|
|
|
|
|
|
2020-09-05 23:25:35 -04:00
|
|
|
|
/* Text antialias modes. */
|
|
|
|
|
typedef enum {
|
|
|
|
|
keNoAntialias = 0, /* Antialias off */
|
|
|
|
|
keSharpAntialias, /* Sharp */
|
|
|
|
|
keSmoothAntialias /* Smooth */
|
|
|
|
|
} PSPAntialiasMode;
|
|
|
|
|
|
|
|
|
|
/* Text flow types */
|
|
|
|
|
typedef enum {
|
|
|
|
|
keTFHorizontalDown = 0, /* Horizontal then down */
|
|
|
|
|
keTFVerticalLeft, /* Vertical then left */
|
|
|
|
|
keTFVerticalRight, /* Vertical then right */
|
|
|
|
|
keTFHorizontalUp /* Horizontal then up */
|
|
|
|
|
} PSPTextFlow;
|
|
|
|
|
|
2008-04-21 16:31:33 +00:00
|
|
|
|
/* Paint style types. (since PSP6)
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
|
|
|
|
keStyleNone = 0x0000, /* No paint style info applies */
|
|
|
|
|
keStyleColor = 0x0001, /* Color paint style info */
|
|
|
|
|
keStyleGradient = 0x0002, /* Gradient paint style info */
|
|
|
|
|
keStylePattern = 0x0004, /* Pattern paint style info (since PSP7) */
|
|
|
|
|
keStylePaper = 0x0008, /* Paper paint style info (since PSP7) */
|
|
|
|
|
keStylePen = 0x0010, /* Organic pen paint style info (since PSP7) */
|
|
|
|
|
} PSPPaintStyleType;
|
|
|
|
|
|
|
|
|
|
/* Gradient type. (since PSP7)
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
|
|
|
|
keSGTLinear = 0, /* Linera gradient type */
|
|
|
|
|
keSGTRadial, /* Radial gradient type */
|
|
|
|
|
keSGTRectangular, /* Rectangulat gradient type */
|
|
|
|
|
keSGTSunburst /* Sunburst gradient type */
|
|
|
|
|
} PSPStyleGradientType;
|
|
|
|
|
|
|
|
|
|
/* Paint Style Cap Type (Start & End). (since PSP7)
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
|
|
|
|
keSCTCapFlat = 0, /* Flat cap type (was round in psp6) */
|
|
|
|
|
keSCTCapRound, /* Round cap type (was square in psp6) */
|
|
|
|
|
keSCTCapSquare, /* Square cap type (was flat in psp6) */
|
|
|
|
|
keSCTCapArrow, /* Arrow cap type */
|
|
|
|
|
keSCTCapCadArrow, /* Cad arrow cap type */
|
|
|
|
|
keSCTCapCurvedTipArrow, /* Curved tip arrow cap type */
|
|
|
|
|
keSCTCapRingBaseArrow, /* Ring base arrow cap type */
|
|
|
|
|
keSCTCapFluerDelis, /* Fluer deLis cap type */
|
|
|
|
|
keSCTCapFootball, /* Football cap type */
|
|
|
|
|
keSCTCapXr71Arrow, /* Xr71 arrow cap type */
|
|
|
|
|
keSCTCapLilly, /* Lilly cap type */
|
|
|
|
|
keSCTCapPinapple, /* Pinapple cap type */
|
|
|
|
|
keSCTCapBall, /* Ball cap type */
|
|
|
|
|
keSCTCapTulip /* Tulip cap type */
|
|
|
|
|
} PSPStyleCapType;
|
|
|
|
|
|
|
|
|
|
/* Paint Style Join Type. (since PSP7)
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
|
|
|
|
keSJTJoinMiter = 0,
|
|
|
|
|
keSJTJoinRound,
|
|
|
|
|
keSJTJoinBevel
|
|
|
|
|
} PSPStyleJoinType;
|
|
|
|
|
|
|
|
|
|
/* Organic pen type. (since PSP7)
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
|
|
|
|
keSPTOrganicPenNone = 0, /* Undefined pen type */
|
|
|
|
|
keSPTOrganicPenMesh, /* Mesh pen type */
|
|
|
|
|
keSPTOrganicPenSand, /* Sand pen type */
|
|
|
|
|
keSPTOrganicPenCurlicues, /* Curlicues pen type */
|
|
|
|
|
keSPTOrganicPenRays, /* Rays pen type */
|
|
|
|
|
keSPTOrganicPenRipple, /* Ripple pen type */
|
|
|
|
|
keSPTOrganicPenWave, /* Wave pen type */
|
|
|
|
|
keSPTOrganicPen /* Generic pen type */
|
|
|
|
|
} PSPStylePenType;
|
|
|
|
|
|
|
|
|
|
|
1999-08-20 00:47:05 +00:00
|
|
|
|
/* Channel types.
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
2008-04-19 17:59:32 +00:00
|
|
|
|
PSP_CHANNEL_COMPOSITE = 0, /* Channel of single channel bitmap */
|
|
|
|
|
PSP_CHANNEL_RED, /* Red channel of 24 bit bitmap */
|
|
|
|
|
PSP_CHANNEL_GREEN, /* Green channel of 24 bit bitmap */
|
|
|
|
|
PSP_CHANNEL_BLUE /* Blue channel of 24 bit bitmap */
|
1999-08-20 00:47:05 +00:00
|
|
|
|
} PSPChannelType;
|
|
|
|
|
|
|
|
|
|
/* Possible metrics used to measure resolution.
|
|
|
|
|
*/
|
1999-08-20 23:52:19 +00:00
|
|
|
|
typedef enum {
|
2008-04-19 17:59:32 +00:00
|
|
|
|
PSP_METRIC_UNDEFINED = 0, /* Metric unknown */
|
|
|
|
|
PSP_METRIC_INCH, /* Resolution is in inches */
|
|
|
|
|
PSP_METRIC_CM /* Resolution is in centimeters */
|
1999-08-20 00:47:05 +00:00
|
|
|
|
} PSP_METRIC;
|
|
|
|
|
|
|
|
|
|
/* Possible types of compression.
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
2008-04-19 17:59:32 +00:00
|
|
|
|
PSP_COMP_NONE = 0, /* No compression */
|
|
|
|
|
PSP_COMP_RLE, /* RLE compression */
|
2008-04-21 16:31:33 +00:00
|
|
|
|
PSP_COMP_LZ77, /* LZ77 compression */
|
|
|
|
|
PSP_COMP_JPEG /* JPEG compression (only used by thumbnail and composite image) (since PSP6) */
|
1999-08-20 00:47:05 +00:00
|
|
|
|
} PSPCompression;
|
|
|
|
|
|
|
|
|
|
/* Picture tube placement mode.
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
2008-04-19 17:59:32 +00:00
|
|
|
|
tpmRandom, /* Place tube images in random intervals */
|
|
|
|
|
tpmConstant /* Place tube images in constant intervals */
|
1999-08-20 00:47:05 +00:00
|
|
|
|
} TubePlacementMode;
|
|
|
|
|
|
|
|
|
|
/* Picture tube selection mode.
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
2008-04-19 17:59:32 +00:00
|
|
|
|
tsmRandom, /* Randomly select the next image in */
|
|
|
|
|
/* tube to display */
|
|
|
|
|
tsmIncremental, /* Select each tube image in turn */
|
|
|
|
|
tsmAngular, /* Select image based on cursor direction */
|
|
|
|
|
tsmPressure, /* Select image based on pressure */
|
|
|
|
|
/* (from pressure-sensitive pad) */
|
|
|
|
|
tsmVelocity /* Select image based on cursor speed */
|
1999-08-20 00:47:05 +00:00
|
|
|
|
} TubeSelectionMode;
|
|
|
|
|
|
|
|
|
|
/* Extended data field types.
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
2008-04-21 16:31:33 +00:00
|
|
|
|
PSP_XDATA_TRNS_INDEX = 0, /* Transparency index field */
|
|
|
|
|
PSP_XDATA_GRID, /* Image grid information (since PSP7) */
|
|
|
|
|
PSP_XDATA_GUIDE, /* Image guide information (since PSP7) */
|
2013-10-29 22:48:46 +01:00
|
|
|
|
PSP_XDATA_EXIF, /* Image Exif information (since PSP8) */
|
2023-10-14 23:52:02 +00:00
|
|
|
|
PSP_XDATA_IPTC, /* Image IPTC information (since PSP8) */
|
2025-05-29 13:10:15 +00:00
|
|
|
|
PSP_XDATA_XMP,
|
1999-08-20 00:47:05 +00:00
|
|
|
|
} PSPExtendedDataID;
|
|
|
|
|
|
|
|
|
|
/* Creator field types.
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
2008-04-19 17:59:32 +00:00
|
|
|
|
PSP_CRTR_FLD_TITLE = 0, /* Image document title field */
|
|
|
|
|
PSP_CRTR_FLD_CRT_DATE, /* Creation date field */
|
|
|
|
|
PSP_CRTR_FLD_MOD_DATE, /* Modification date field */
|
|
|
|
|
PSP_CRTR_FLD_ARTIST, /* Artist name field */
|
|
|
|
|
PSP_CRTR_FLD_CPYRGHT, /* Copyright holder name field */
|
|
|
|
|
PSP_CRTR_FLD_DESC, /* Image document description field */
|
|
|
|
|
PSP_CRTR_FLD_APP_ID, /* Creating app id field */
|
|
|
|
|
PSP_CRTR_FLD_APP_VER /* Creating app version field */
|
1999-08-20 00:47:05 +00:00
|
|
|
|
} PSPCreatorFieldID;
|
|
|
|
|
|
2008-04-21 16:31:33 +00:00
|
|
|
|
/* Grid units type. (since PSP7)
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
|
|
|
|
keGridUnitsPixels = 0, /* Grid units is pixels */
|
|
|
|
|
keGridUnitsInches, /* Grid units is inches */
|
|
|
|
|
keGridUnitsCentimeters /* Grid units is centimeters */
|
|
|
|
|
} PSPGridUnitsType;
|
|
|
|
|
|
|
|
|
|
/* Guide orientation type. (since PSP7)
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
|
|
|
|
keHorizontalGuide = 0,
|
|
|
|
|
keVerticalGuide
|
|
|
|
|
} PSPGuideOrientationType;
|
|
|
|
|
|
1999-08-20 00:47:05 +00:00
|
|
|
|
/* Creator application identifiers.
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
2008-04-19 17:59:32 +00:00
|
|
|
|
PSP_CREATOR_APP_UNKNOWN = 0, /* Creator application unknown */
|
1999-08-20 00:47:05 +00:00
|
|
|
|
PSP_CREATOR_APP_PAINT_SHOP_PRO /* Creator is Paint Shop Pro */
|
|
|
|
|
} PSPCreatorAppID;
|
|
|
|
|
|
|
|
|
|
/* Layer types.
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
2008-04-19 17:59:32 +00:00
|
|
|
|
PSP_LAYER_NORMAL = 0, /* Normal layer */
|
|
|
|
|
PSP_LAYER_FLOATING_SELECTION /* Floating selection layer */
|
2008-04-21 16:31:33 +00:00
|
|
|
|
} PSPLayerTypePSP5;
|
|
|
|
|
|
|
|
|
|
/* Layer types. (since PSP6)
|
|
|
|
|
*/
|
|
|
|
|
typedef enum {
|
|
|
|
|
keGLTUndefined = 0, /* Undefined layer type */
|
|
|
|
|
keGLTRaster, /* Standard raster layer */
|
|
|
|
|
keGLTFloatingRasterSelection, /* Floating selection (raster layer) */
|
|
|
|
|
keGLTVector, /* Vector layer */
|
|
|
|
|
keGLTAdjustment, /* Adjustment layer */
|
2020-09-05 23:25:35 -04:00
|
|
|
|
keGLTGroup, /* Group layer (since PSP8) */
|
|
|
|
|
keGLTMask, /* Mask layer (since PSP8) */
|
|
|
|
|
keGLTArtMedia /* Art media layer (since PSP9) */
|
2008-04-21 16:31:33 +00:00
|
|
|
|
} PSPLayerTypePSP6;
|
|
|
|
|
|
2020-09-05 23:25:35 -04:00
|
|
|
|
/* Art media layer map types (since PSP7) */
|
|
|
|
|
typedef enum {
|
|
|
|
|
keArtMediaColorMap = 0,
|
|
|
|
|
keArtMediaBumpMap,
|
|
|
|
|
keArtMediaShininessMap,
|
|
|
|
|
keArtMediaReflectivityMap,
|
|
|
|
|
keArtMediaDrynessMap
|
|
|
|
|
} PSPArtMediaMapType;
|
|
|
|
|
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
|
|
|
|
/* Truth values.
|
|
|
|
|
*/
|
2008-04-19 17:59:32 +00:00
|
|
|
|
#if 0 /* FALSE and TRUE taken by GLib */
|
1999-08-20 00:47:05 +00:00
|
|
|
|
typedef enum {
|
|
|
|
|
FALSE = 0,
|
|
|
|
|
TRUE
|
|
|
|
|
} PSP_BOOLEAN;
|
|
|
|
|
#else
|
|
|
|
|
typedef gboolean PSP_BOOLEAN;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* End of cut&paste from psp spec */
|
|
|
|
|
|
1999-08-20 23:52:19 +00:00
|
|
|
|
/* We store the various PSP data in own structures.
|
|
|
|
|
* We cannot use structs intended to be direct copies of the file block
|
|
|
|
|
* headers because of struct alignment issues.
|
1999-08-20 00:47:05 +00:00
|
|
|
|
*/
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
2020-09-08 22:14:50 -04:00
|
|
|
|
guint32 width, height;
|
|
|
|
|
gdouble resolution;
|
|
|
|
|
guchar metric;
|
|
|
|
|
guint16 compression;
|
|
|
|
|
guint16 depth;
|
|
|
|
|
guchar grayscale;
|
|
|
|
|
guint32 active_layer;
|
|
|
|
|
guint16 layer_count;
|
|
|
|
|
guint16 bytes_per_sample;
|
|
|
|
|
GimpImageBaseType base_type;
|
|
|
|
|
GimpPrecision precision;
|
1999-08-20 00:47:05 +00:00
|
|
|
|
} PSPimage;
|
|
|
|
|
|
|
|
|
|
|
2019-08-24 19:52:36 +02:00
|
|
|
|
typedef struct _Psp Psp;
|
|
|
|
|
typedef struct _PspClass PspClass;
|
|
|
|
|
|
|
|
|
|
struct _Psp
|
|
|
|
|
{
|
|
|
|
|
GimpPlugIn parent_instance;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct _PspClass
|
|
|
|
|
{
|
|
|
|
|
GimpPlugInClass parent_class;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define PSP_TYPE (psp_get_type ())
|
2023-10-18 18:29:37 +02:00
|
|
|
|
#define PSP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PSP_TYPE, Psp))
|
2019-08-24 19:52:36 +02:00
|
|
|
|
|
|
|
|
|
GType psp_get_type (void) G_GNUC_CONST;
|
|
|
|
|
|
2023-08-05 19:07:04 +02:00
|
|
|
|
static GList * psp_query_procedures (GimpPlugIn *plug_in);
|
|
|
|
|
static GimpProcedure * psp_create_procedure (GimpPlugIn *plug_in,
|
|
|
|
|
const gchar *name);
|
|
|
|
|
|
|
|
|
|
static GimpValueArray * psp_load (GimpProcedure *procedure,
|
|
|
|
|
GimpRunMode run_mode,
|
|
|
|
|
GFile *file,
|
|
|
|
|
GimpMetadata *metadata,
|
|
|
|
|
GimpMetadataLoadFlags *flags,
|
|
|
|
|
GimpProcedureConfig *config,
|
|
|
|
|
gpointer run_data);
|
2024-04-13 15:10:25 +00:00
|
|
|
|
static GimpValueArray * psp_export (GimpProcedure *procedure,
|
2023-08-05 19:07:04 +02:00
|
|
|
|
GimpRunMode run_mode,
|
|
|
|
|
GimpImage *image,
|
|
|
|
|
GFile *file,
|
2024-05-06 18:38:12 +00:00
|
|
|
|
GimpExportOptions *options,
|
2023-08-05 19:07:04 +02:00
|
|
|
|
GimpMetadata *metadata,
|
|
|
|
|
GimpProcedureConfig *config,
|
|
|
|
|
gpointer run_data);
|
|
|
|
|
|
|
|
|
|
static GimpImage * load_image (GFile *file,
|
2025-05-29 13:10:15 +00:00
|
|
|
|
GimpMetadata *metadata,
|
2023-08-05 19:07:04 +02:00
|
|
|
|
GError **error);
|
2024-04-13 15:10:25 +00:00
|
|
|
|
static gboolean export_image (GFile *file,
|
2023-08-05 19:07:04 +02:00
|
|
|
|
GimpImage *image,
|
|
|
|
|
gint n_drawables,
|
2024-04-30 13:50:24 +00:00
|
|
|
|
GList *drawables,
|
2023-08-05 19:07:04 +02:00
|
|
|
|
GObject *config,
|
|
|
|
|
GError **error);
|
|
|
|
|
static gboolean save_dialog (GimpProcedure *procedure,
|
|
|
|
|
GObject *config,
|
|
|
|
|
GimpImage *image);
|
2019-08-24 19:52:36 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
G_DEFINE_TYPE (Psp, psp, GIMP_TYPE_PLUG_IN)
|
|
|
|
|
|
|
|
|
|
GIMP_MAIN (PSP_TYPE)
|
2022-05-26 00:59:36 +02:00
|
|
|
|
DEFINE_STD_SET_I18N
|
2019-08-24 19:52:36 +02:00
|
|
|
|
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
2019-08-24 19:52:36 +02:00
|
|
|
|
static guint16 psp_ver_major;
|
|
|
|
|
static guint16 psp_ver_minor;
|
|
|
|
|
|
1999-08-20 23:52:19 +00:00
|
|
|
|
|
2019-08-24 19:52:36 +02:00
|
|
|
|
static void
|
|
|
|
|
psp_class_init (PspClass *klass)
|
|
|
|
|
{
|
|
|
|
|
GimpPlugInClass *plug_in_class = GIMP_PLUG_IN_CLASS (klass);
|
2003-11-06 15:27:05 +00:00
|
|
|
|
|
2019-08-24 19:52:36 +02:00
|
|
|
|
plug_in_class->query_procedures = psp_query_procedures;
|
|
|
|
|
plug_in_class->create_procedure = psp_create_procedure;
|
2022-05-26 00:59:36 +02:00
|
|
|
|
plug_in_class->set_i18n = STD_SET_I18N;
|
2019-08-24 19:52:36 +02:00
|
|
|
|
}
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
|
|
|
|
static void
|
2019-08-24 19:52:36 +02:00
|
|
|
|
psp_init (Psp *psp)
|
1999-08-20 00:47:05 +00:00
|
|
|
|
{
|
2019-08-24 19:52:36 +02:00
|
|
|
|
}
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
2019-08-24 19:52:36 +02:00
|
|
|
|
static GList *
|
|
|
|
|
psp_query_procedures (GimpPlugIn *plug_in)
|
|
|
|
|
{
|
|
|
|
|
GList *list = NULL;
|
|
|
|
|
|
|
|
|
|
list = g_list_append (list, g_strdup (LOAD_PROC));
|
2005-08-15 11:07:27 +00:00
|
|
|
|
#if 0
|
2019-08-24 19:52:36 +02:00
|
|
|
|
/* commented out until exporting is implemented */
|
2024-04-13 15:10:25 +00:00
|
|
|
|
list = g_list_append (list, g_strdup (EXPORT_PROC));
|
2005-08-15 11:07:27 +00:00
|
|
|
|
#endif
|
2019-08-24 19:52:36 +02:00
|
|
|
|
return list;
|
|
|
|
|
}
|
2005-08-15 11:07:27 +00:00
|
|
|
|
|
2019-08-24 19:52:36 +02:00
|
|
|
|
static GimpProcedure *
|
|
|
|
|
psp_create_procedure (GimpPlugIn *plug_in,
|
|
|
|
|
const gchar *name)
|
|
|
|
|
{
|
|
|
|
|
GimpProcedure *procedure = NULL;
|
2004-05-14 00:42:38 +00:00
|
|
|
|
|
2019-08-24 19:52:36 +02:00
|
|
|
|
if (! strcmp (name, LOAD_PROC))
|
|
|
|
|
{
|
2023-08-06 03:21:27 +02:00
|
|
|
|
procedure = gimp_load_procedure_new (plug_in, name,
|
|
|
|
|
GIMP_PDB_PROC_TYPE_PLUGIN,
|
|
|
|
|
psp_load, NULL, NULL);
|
2019-08-24 19:52:36 +02:00
|
|
|
|
|
2022-07-04 22:50:53 +02:00
|
|
|
|
gimp_procedure_set_menu_label (procedure, _("Paint Shop Pro image"));
|
2019-08-24 19:52:36 +02:00
|
|
|
|
|
|
|
|
|
gimp_procedure_set_documentation (procedure,
|
2023-04-02 12:43:50 +00:00
|
|
|
|
_("Loads images from the Paint Shop "
|
|
|
|
|
"Pro PSP file format"),
|
|
|
|
|
_("This plug-in loads and exports images "
|
|
|
|
|
"in Paint Shop Pro's native PSP format. "
|
|
|
|
|
"Vector layers aren't handled. "
|
|
|
|
|
"Exporting isn't yet implemented."),
|
2019-08-24 19:52:36 +02:00
|
|
|
|
name);
|
|
|
|
|
gimp_procedure_set_attribution (procedure,
|
|
|
|
|
"Tor Lillqvist",
|
|
|
|
|
"Tor Lillqvist",
|
|
|
|
|
"1999");
|
|
|
|
|
|
|
|
|
|
gimp_file_procedure_set_mime_types (GIMP_FILE_PROCEDURE (procedure),
|
|
|
|
|
"image/x-psp");
|
|
|
|
|
gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure),
|
2020-08-26 19:09:28 -04:00
|
|
|
|
"psp,tub,pspimage,psptube");
|
2019-08-24 19:52:36 +02:00
|
|
|
|
gimp_file_procedure_set_magics (GIMP_FILE_PROCEDURE (procedure),
|
|
|
|
|
"0,string,Paint\\040Shop\\040Pro\\040Image\\040File\n\032");
|
|
|
|
|
}
|
2024-04-13 15:10:25 +00:00
|
|
|
|
else if (! strcmp (name, EXPORT_PROC))
|
2019-08-24 19:52:36 +02:00
|
|
|
|
{
|
2024-04-20 03:08:57 +00:00
|
|
|
|
procedure = gimp_export_procedure_new (plug_in, name,
|
|
|
|
|
GIMP_PDB_PROC_TYPE_PLUGIN,
|
|
|
|
|
FALSE, psp_export, NULL, NULL);
|
2019-08-24 19:52:36 +02:00
|
|
|
|
|
|
|
|
|
gimp_procedure_set_image_types (procedure, "*");
|
|
|
|
|
|
2022-07-04 22:50:53 +02:00
|
|
|
|
gimp_procedure_set_menu_label (procedure, _("Paint Shop Pro image"));
|
2023-04-02 12:43:50 +00:00
|
|
|
|
gimp_file_procedure_set_format_name (GIMP_FILE_PROCEDURE (procedure),
|
|
|
|
|
"PSP");
|
2019-08-24 19:52:36 +02:00
|
|
|
|
gimp_procedure_set_documentation (procedure,
|
|
|
|
|
"Exports images in the Paint Shop Pro "
|
|
|
|
|
"PSP file format",
|
|
|
|
|
"This plug-in loads and exports images "
|
|
|
|
|
"in Paint Shop Pro's native PSP format. "
|
|
|
|
|
"Vector layers aren't handled. "
|
|
|
|
|
"Exporting isn't yet implemented.",
|
|
|
|
|
name);
|
|
|
|
|
gimp_procedure_set_attribution (procedure,
|
|
|
|
|
"Tor Lillqvist",
|
|
|
|
|
"Tor Lillqvist",
|
|
|
|
|
"1999");
|
|
|
|
|
|
|
|
|
|
gimp_file_procedure_set_mime_types (GIMP_FILE_PROCEDURE (procedure),
|
|
|
|
|
"image/x-psp");
|
|
|
|
|
gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure),
|
|
|
|
|
"psp,tub");
|
|
|
|
|
|
2024-05-06 18:38:12 +00:00
|
|
|
|
gimp_export_procedure_set_capabilities (GIMP_EXPORT_PROCEDURE (procedure),
|
|
|
|
|
GIMP_EXPORT_CAN_HANDLE_RGB |
|
|
|
|
|
GIMP_EXPORT_CAN_HANDLE_GRAY |
|
|
|
|
|
GIMP_EXPORT_CAN_HANDLE_INDEXED |
|
|
|
|
|
GIMP_EXPORT_CAN_HANDLE_ALPHA |
|
|
|
|
|
GIMP_EXPORT_CAN_HANDLE_LAYERS,
|
app, libgimp*, pdb, plug-ins: review and enhance MR !1549.
- Fix annotations for gimp_export_options_get_image() to make it
actually introspectable with the GimpImage being both input and
output. Even though the logic doesn't change much (the input image may
be overriden or not), it doesn't matter for introspection because
images are handled centrally by libgimp and therefore must not be
freed. Actually deleting the image from the central list of images
though remains a manual action depending on code logic, not some
automatic action to be handled by binding engines.
- Add G_GNUC_WARN_UNUSED_RESULT to gimp_export_options_get_image()
because ignoring the returned value is rarely a good idea (as you
usually want to delete the image).
- Remove gimp_export_options_new(): we don't need this constructor
because at this point, the best is to tell plug-in developers to just
pass NULL everywhere. This leaves us free to create a more useful
default constructor if needed, in the future. Main description for
GimpExportOptions has also been updated to say this.
- Add a data_destroy callback for the user data passed in
gimp_export_procedure_set_capabilities().
- Fixing annotations of 'export_options' object from pdb/pdb.pl: input
args would actually be (nullable) and would not transfer ownership
(calling code must still free the object). Return value's ownership on
the other hand is fully transfered.
- Add C and Python unit testing for GimpExportOptions and
gimp_export_options_get_image() in particular.
- Fix or improve various details.
Note that I have also considered for a long time changing the signature
of gimp_export_options_get_image() to return a boolean indicating
whether `image` had been replaced (hence needed deletion) or not. This
also meant getting rid of the GimpExportReturn enum. Right now it would
work because there are no third case, but I was considering the future
possibility that for instance we got some impossible conversion for some
future capability. I'm not sure it would ever happen; and for sure, this
is not desirable because it implies an export failure a bit late in the
workflow. But just in case, let's keep the enum return value. It does
not even make the using code that much more complicated (well just a
value comparison instead of a simple boolean test).
2024-08-17 15:06:27 +02:00
|
|
|
|
NULL, NULL, NULL);
|
2024-05-06 18:38:12 +00:00
|
|
|
|
|
2024-07-14 04:12:26 +00:00
|
|
|
|
gimp_procedure_add_choice_argument (procedure, "compression",
|
|
|
|
|
_("_Data Compression"),
|
|
|
|
|
_("Type of compression"),
|
|
|
|
|
gimp_choice_new_with_values ("none", PSP_COMP_NONE, _("none"), NULL,
|
|
|
|
|
"rle", PSP_COMP_RLE, _("rle"), NULL,
|
|
|
|
|
"lz77", PSP_COMP_LZ77, _("lz77"), NULL,
|
|
|
|
|
NULL),
|
|
|
|
|
"lz77",
|
|
|
|
|
G_PARAM_READWRITE);
|
2019-08-24 19:52:36 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return procedure;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GimpValueArray *
|
2023-08-05 19:07:04 +02:00
|
|
|
|
psp_load (GimpProcedure *procedure,
|
|
|
|
|
GimpRunMode run_mode,
|
|
|
|
|
GFile *file,
|
|
|
|
|
GimpMetadata *metadata,
|
|
|
|
|
GimpMetadataLoadFlags *flags,
|
|
|
|
|
GimpProcedureConfig *config,
|
|
|
|
|
gpointer run_data)
|
2019-08-24 19:52:36 +02:00
|
|
|
|
{
|
|
|
|
|
GimpValueArray *return_vals;
|
|
|
|
|
GimpImage *image;
|
|
|
|
|
GError *error = NULL;
|
|
|
|
|
|
|
|
|
|
gegl_init (NULL, NULL);
|
|
|
|
|
|
2025-05-29 13:10:15 +00:00
|
|
|
|
image = load_image (file, metadata, &error);
|
2019-08-24 19:52:36 +02:00
|
|
|
|
|
|
|
|
|
if (! image)
|
|
|
|
|
return gimp_procedure_new_return_values (procedure,
|
|
|
|
|
GIMP_PDB_EXECUTION_ERROR,
|
|
|
|
|
error);
|
|
|
|
|
|
|
|
|
|
return_vals = gimp_procedure_new_return_values (procedure,
|
|
|
|
|
GIMP_PDB_SUCCESS,
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
|
|
GIMP_VALUES_SET_IMAGE (return_vals, 1, image);
|
|
|
|
|
|
|
|
|
|
return return_vals;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GimpValueArray *
|
2024-04-13 15:10:25 +00:00
|
|
|
|
psp_export (GimpProcedure *procedure,
|
|
|
|
|
GimpRunMode run_mode,
|
|
|
|
|
GimpImage *image,
|
|
|
|
|
GFile *file,
|
2024-05-06 18:38:12 +00:00
|
|
|
|
GimpExportOptions *options,
|
2024-04-13 15:10:25 +00:00
|
|
|
|
GimpMetadata *metadata,
|
|
|
|
|
GimpProcedureConfig *config,
|
|
|
|
|
gpointer run_data)
|
2019-08-24 19:52:36 +02:00
|
|
|
|
{
|
2023-07-20 23:58:41 +02:00
|
|
|
|
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
|
2024-04-30 04:25:51 +00:00
|
|
|
|
GimpExportReturn export = GIMP_EXPORT_IGNORE;
|
2024-04-30 13:50:24 +00:00
|
|
|
|
GList *drawables;
|
|
|
|
|
gint n_drawables;
|
2023-07-20 23:58:41 +02:00
|
|
|
|
GError *error = NULL;
|
2019-08-24 19:52:36 +02:00
|
|
|
|
|
|
|
|
|
gegl_init (NULL, NULL);
|
|
|
|
|
|
2024-07-14 20:12:57 +00:00
|
|
|
|
if (run_mode == GIMP_RUN_INTERACTIVE)
|
2019-08-24 19:52:36 +02:00
|
|
|
|
{
|
2019-09-20 19:39:00 +02:00
|
|
|
|
gimp_ui_init (PLUG_IN_BINARY);
|
2019-08-24 19:52:36 +02:00
|
|
|
|
|
2023-04-02 12:43:50 +00:00
|
|
|
|
if (! save_dialog (procedure, G_OBJECT (config), image))
|
2019-08-24 19:52:36 +02:00
|
|
|
|
status = GIMP_PDB_CANCEL;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-06 18:38:12 +00:00
|
|
|
|
export = gimp_export_options_get_image (options, &image);
|
2024-07-14 20:12:57 +00:00
|
|
|
|
drawables = gimp_image_list_layers (image);
|
|
|
|
|
n_drawables = g_list_length (drawables);
|
|
|
|
|
|
2019-08-24 19:52:36 +02:00
|
|
|
|
if (status == GIMP_PDB_SUCCESS)
|
|
|
|
|
{
|
2024-04-13 15:10:25 +00:00
|
|
|
|
if (! export_image (file, image, n_drawables, drawables,
|
|
|
|
|
G_OBJECT (config), &error))
|
2019-08-24 19:52:36 +02:00
|
|
|
|
{
|
|
|
|
|
status = GIMP_PDB_EXECUTION_ERROR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (export == GIMP_EXPORT_EXPORT)
|
2024-04-30 13:50:24 +00:00
|
|
|
|
gimp_image_delete (image);
|
2019-08-24 19:52:36 +02:00
|
|
|
|
|
2024-04-30 13:50:24 +00:00
|
|
|
|
g_list_free (drawables);
|
2019-08-24 19:52:36 +02:00
|
|
|
|
return gimp_procedure_new_return_values (procedure, status, error);
|
1999-08-20 00:47:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2004-05-19 17:53:21 +00:00
|
|
|
|
static gboolean
|
2019-10-04 21:51:43 +02:00
|
|
|
|
save_dialog (GimpProcedure *procedure,
|
2023-04-02 12:43:50 +00:00
|
|
|
|
GObject *config,
|
|
|
|
|
GimpImage *image)
|
1999-08-20 00:47:05 +00:00
|
|
|
|
{
|
2024-07-15 18:12:30 +00:00
|
|
|
|
GtkWidget *dialog;
|
|
|
|
|
GtkWidget *frame;
|
|
|
|
|
gint run;
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
2024-04-20 03:08:57 +00:00
|
|
|
|
dialog = gimp_export_procedure_dialog_new (GIMP_EXPORT_PROCEDURE (procedure),
|
|
|
|
|
GIMP_PROCEDURE_CONFIG (config),
|
|
|
|
|
image);
|
2005-09-09 18:38:00 +00:00
|
|
|
|
|
1999-08-20 00:47:05 +00:00
|
|
|
|
/* file save type */
|
2024-07-14 04:12:26 +00:00
|
|
|
|
frame = gimp_procedure_dialog_get_widget (GIMP_PROCEDURE_DIALOG (dialog),
|
|
|
|
|
"compression", GIMP_TYPE_INT_RADIO_FRAME);
|
2004-05-19 17:53:21 +00:00
|
|
|
|
gtk_container_set_border_width (GTK_CONTAINER (frame), 12);
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
2023-04-02 12:43:50 +00:00
|
|
|
|
gimp_procedure_dialog_fill (GIMP_PROCEDURE_DIALOG (dialog), NULL);
|
2005-09-09 18:38:00 +00:00
|
|
|
|
gtk_widget_show (dialog);
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
2019-10-04 21:51:43 +02:00
|
|
|
|
run = gimp_procedure_dialog_run (GIMP_PROCEDURE_DIALOG (dialog));
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
2005-09-09 18:38:00 +00:00
|
|
|
|
gtk_widget_destroy (dialog);
|
2003-11-06 15:27:05 +00:00
|
|
|
|
|
|
|
|
|
return run;
|
1999-08-20 00:47:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2008-04-21 16:31:33 +00:00
|
|
|
|
/* This helper method is used to get the name of the block for the known block
|
|
|
|
|
* types. The enum PSPBlockID must cover the input values.
|
|
|
|
|
*/
|
2008-09-17 08:37:24 +00:00
|
|
|
|
static const gchar *
|
2000-01-25 17:46:56 +00:00
|
|
|
|
block_name (gint id)
|
1999-08-20 00:47:05 +00:00
|
|
|
|
{
|
2008-09-17 08:37:24 +00:00
|
|
|
|
static const gchar *block_names[] =
|
1999-08-20 00:47:05 +00:00
|
|
|
|
{
|
|
|
|
|
"IMAGE",
|
|
|
|
|
"CREATOR",
|
|
|
|
|
"COLOR",
|
|
|
|
|
"LAYER_START",
|
|
|
|
|
"LAYER",
|
|
|
|
|
"CHANNEL",
|
|
|
|
|
"SELECTION",
|
|
|
|
|
"ALPHA_BANK",
|
|
|
|
|
"ALPHA_CHANNEL",
|
|
|
|
|
"THUMBNAIL",
|
|
|
|
|
"EXTENDED_DATA",
|
2008-04-21 16:31:33 +00:00
|
|
|
|
"TUBE",
|
|
|
|
|
"ADJUSTMENT_EXTENSION",
|
|
|
|
|
"VECTOR_EXTENSION_BLOCK",
|
|
|
|
|
"SHAPE_BLOCK",
|
|
|
|
|
"PAINTSTYLE_BLOCK",
|
|
|
|
|
"COMPOSITE_IMAGE_BANK_BLOCK",
|
|
|
|
|
"COMPOSITE_ATTRIBUTES_BLOCK",
|
|
|
|
|
"JPEG_BLOCK",
|
2020-09-05 23:25:35 -04:00
|
|
|
|
"LINESTYLE_BLOCK",
|
|
|
|
|
"TABLE_BANK_BLOCK",
|
|
|
|
|
"TABLE_BLOCK",
|
|
|
|
|
"PAPER_BLOCK",
|
|
|
|
|
"PATTERN_BLOCK",
|
|
|
|
|
"GRADIENT_BLOCK",
|
|
|
|
|
"GROUP_EXTENSION_BLOCK",
|
|
|
|
|
"MASK_EXTENSION_BLOCK",
|
|
|
|
|
"BRUSH_BLOCK",
|
|
|
|
|
"ART_MEDIA_BLOCK",
|
|
|
|
|
"ART_MEDIA_MAP_BLOCK",
|
|
|
|
|
"ART_MEDIA_TILE_BLOCK",
|
|
|
|
|
"ART_MEDIA_TEXTURE_BLOCK",
|
|
|
|
|
"COLORPROFILE_BLOCK",
|
|
|
|
|
"RASTER_EXTENSION_BLOCK",
|
|
|
|
|
};
|
1999-08-20 00:47:05 +00:00
|
|
|
|
static gchar *err_name = NULL;
|
|
|
|
|
|
2020-09-05 23:25:35 -04:00
|
|
|
|
if (id >= 0 && id <= PSP_RASTER_EXTENSION_BLOCK)
|
1999-08-20 00:47:05 +00:00
|
|
|
|
return block_names[id];
|
|
|
|
|
|
|
|
|
|
g_free (err_name);
|
|
|
|
|
err_name = g_strdup_printf ("id=%d", id);
|
|
|
|
|
|
|
|
|
|
return err_name;
|
|
|
|
|
}
|
|
|
|
|
|
2008-04-21 16:31:33 +00:00
|
|
|
|
/* This helper method is used during loading. It verifies the block we are
|
|
|
|
|
* reading has a valid header. Fills the variables init_len and total_len
|
|
|
|
|
*/
|
2000-01-25 17:46:56 +00:00
|
|
|
|
static gint
|
2017-12-21 20:42:41 +01:00
|
|
|
|
read_block_header (FILE *f,
|
|
|
|
|
guint32 *init_len,
|
|
|
|
|
guint32 *total_len,
|
|
|
|
|
GError **error)
|
1999-08-20 00:47:05 +00:00
|
|
|
|
{
|
|
|
|
|
guchar buf[4];
|
|
|
|
|
guint16 id;
|
1999-08-20 23:52:19 +00:00
|
|
|
|
long header_start;
|
|
|
|
|
guint32 len;
|
|
|
|
|
|
1999-08-21 03:06:07 +00:00
|
|
|
|
IFDBG(3) header_start = ftell (f);
|
1999-08-20 23:52:19 +00:00
|
|
|
|
|
1999-08-20 00:47:05 +00:00
|
|
|
|
if (fread (buf, 4, 1, f) < 1
|
|
|
|
|
|| fread (&id, 2, 1, f) < 1
|
1999-08-20 23:52:19 +00:00
|
|
|
|
|| fread (&len, 4, 1, f) < 1
|
2007-09-11 22:48:03 +00:00
|
|
|
|
|| (psp_ver_major < 4 && fread (total_len, 4, 1, f) < 1))
|
1999-08-20 00:47:05 +00:00
|
|
|
|
{
|
2017-12-21 20:42:41 +01:00
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading block header"));
|
1999-08-20 00:47:05 +00:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (memcmp (buf, "~BK\0", 4) != 0)
|
|
|
|
|
{
|
2003-11-06 15:27:05 +00:00
|
|
|
|
IFDBG(3)
|
2017-12-21 20:42:41 +01:00
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Invalid block header at %ld"), header_start);
|
2000-01-08 10:55:05 +00:00
|
|
|
|
else
|
2017-12-21 20:42:41 +01:00
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Invalid block header"));
|
1999-08-20 00:47:05 +00:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
1999-08-20 23:52:19 +00:00
|
|
|
|
|
2003-06-13 14:37:00 +00:00
|
|
|
|
IFDBG(3) g_message ("%s at %ld", block_name (id), header_start);
|
1999-08-20 23:52:19 +00:00
|
|
|
|
|
2007-09-11 22:48:03 +00:00
|
|
|
|
if (psp_ver_major < 4)
|
1999-08-20 23:52:19 +00:00
|
|
|
|
{
|
|
|
|
|
*init_len = GUINT32_FROM_LE (len);
|
|
|
|
|
*total_len = GUINT32_FROM_LE (*total_len);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Version 4.0 seems to have dropped the initial data chunk length
|
|
|
|
|
* field.
|
|
|
|
|
*/
|
2008-04-19 17:59:32 +00:00
|
|
|
|
*init_len = 0xDEADBEEF; /* Intentionally bogus, should not be used */
|
1999-08-21 03:06:07 +00:00
|
|
|
|
*total_len = GUINT32_FROM_LE (len);
|
1999-08-20 23:52:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-05-18 20:42:02 +00:00
|
|
|
|
return GUINT16_FROM_LE (id);
|
1999-08-20 00:47:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-18 15:59:58 -04:00
|
|
|
|
static gint
|
|
|
|
|
try_fseek (FILE *f,
|
|
|
|
|
glong pos,
|
|
|
|
|
gint whence,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
|
|
|
|
if (fseek (f, pos, whence) < 0)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
|
|
|
|
_("Seek error: %s"), g_strerror (errno));
|
|
|
|
|
fclose (f);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-04-21 16:31:33 +00:00
|
|
|
|
/* Read the PSP_IMAGE_BLOCK */
|
2000-01-25 17:46:56 +00:00
|
|
|
|
static gint
|
|
|
|
|
read_general_image_attribute_block (FILE *f,
|
2008-04-19 17:59:32 +00:00
|
|
|
|
guint init_len,
|
|
|
|
|
guint total_len,
|
2020-08-18 15:59:58 -04:00
|
|
|
|
PSPimage *ia,
|
|
|
|
|
GError **error)
|
1999-08-20 00:47:05 +00:00
|
|
|
|
{
|
2000-01-25 17:46:56 +00:00
|
|
|
|
gchar buf[6];
|
2003-01-07 06:16:02 +00:00
|
|
|
|
guint64 res;
|
2008-04-21 16:31:33 +00:00
|
|
|
|
gchar graphics_content[4];
|
2020-08-18 15:59:58 -04:00
|
|
|
|
long chunk_start;
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
|
|
|
|
if (init_len < 38 || total_len < 38)
|
|
|
|
|
{
|
2020-09-05 22:41:59 -04:00
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Invalid general image attribute chunk size."));
|
1999-08-20 00:47:05 +00:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-18 15:59:58 -04:00
|
|
|
|
chunk_start = ftell (f);
|
|
|
|
|
if ((psp_ver_major >= 4
|
2020-10-08 13:54:24 -04:00
|
|
|
|
&& (fread (&init_len, 4, 1, f) < 1 || ((init_len = GUINT32_FROM_LE (init_len)) < 46)))
|
2020-08-18 15:59:58 -04:00
|
|
|
|
|| fread (&ia->width, 4, 1, f) < 1
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|| fread (&ia->height, 4, 1, f) < 1
|
2003-01-07 06:16:02 +00:00
|
|
|
|
|| fread (&res, 8, 1, f) < 1
|
1999-08-21 03:06:07 +00:00
|
|
|
|
|| fread (&ia->metric, 1, 1, f) < 1
|
|
|
|
|
|| fread (&ia->compression, 2, 1, f) < 1
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|| fread (&ia->depth, 2, 1, f) < 1
|
2013-06-06 23:26:16 +02:00
|
|
|
|
|| fread (buf, 2+4, 1, f) < 1 /* Skip plane and color count */
|
|
|
|
|
|| fread (&ia->grayscale, 1, 1, f) < 1
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|| fread (buf, 4, 1, f) < 1 /* Skip total image size */
|
|
|
|
|
|| fread (&ia->active_layer, 4, 1, f) < 1
|
2008-04-21 16:31:33 +00:00
|
|
|
|
|| fread (&ia->layer_count, 2, 1, f) < 1
|
2020-08-18 15:59:58 -04:00
|
|
|
|
|| (psp_ver_major >= 4 && fread (graphics_content, 4, 1, f) < 1)
|
|
|
|
|
|| try_fseek (f, chunk_start + init_len, SEEK_SET, error) < 0)
|
1999-08-20 00:47:05 +00:00
|
|
|
|
{
|
2020-09-05 22:41:59 -04:00
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading general image attribute block."));
|
1999-08-20 00:47:05 +00:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
ia->width = GUINT32_FROM_LE (ia->width);
|
1999-08-22 01:54:06 +00:00
|
|
|
|
ia->height = GUINT32_FROM_LE (ia->height);
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
2003-01-07 06:16:02 +00:00
|
|
|
|
res = GUINT64_FROM_LE (res);
|
|
|
|
|
memcpy (&ia->resolution, &res, 8);
|
1999-08-21 03:06:07 +00:00
|
|
|
|
if (ia->metric == PSP_METRIC_CM)
|
1999-08-20 00:47:05 +00:00
|
|
|
|
ia->resolution /= 2.54;
|
|
|
|
|
|
1999-08-21 03:06:07 +00:00
|
|
|
|
ia->compression = GUINT16_FROM_LE (ia->compression);
|
|
|
|
|
if (ia->compression > PSP_COMP_LZ77)
|
|
|
|
|
{
|
2020-09-05 22:41:59 -04:00
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Unknown compression type %d"), ia->compression);
|
1999-08-21 03:06:07 +00:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
1999-08-22 01:54:06 +00:00
|
|
|
|
|
|
|
|
|
ia->depth = GUINT16_FROM_LE (ia->depth);
|
2020-09-08 22:14:50 -04:00
|
|
|
|
switch (ia->depth)
|
|
|
|
|
{
|
|
|
|
|
case 24:
|
|
|
|
|
case 48:
|
|
|
|
|
ia->base_type = GIMP_RGB;
|
|
|
|
|
if (ia->depth == 24)
|
|
|
|
|
ia->precision = GIMP_PRECISION_U8_NON_LINEAR;
|
|
|
|
|
else
|
|
|
|
|
ia->precision = GIMP_PRECISION_U16_NON_LINEAR;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
|
case 4:
|
|
|
|
|
case 8:
|
|
|
|
|
case 16:
|
|
|
|
|
if (ia->grayscale && ia->depth >= 8)
|
|
|
|
|
{
|
|
|
|
|
ia->base_type = GIMP_GRAY;
|
|
|
|
|
if (ia->depth == 8)
|
|
|
|
|
ia->precision = GIMP_PRECISION_U8_NON_LINEAR;
|
|
|
|
|
else
|
|
|
|
|
ia->precision = GIMP_PRECISION_U16_NON_LINEAR;
|
|
|
|
|
}
|
|
|
|
|
else if (ia->depth <= 8 && ! ia->grayscale)
|
|
|
|
|
{
|
|
|
|
|
ia->base_type = GIMP_INDEXED;
|
|
|
|
|
ia->precision = GIMP_PRECISION_U8_NON_LINEAR;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Unsupported bit depth %d"), ia->depth);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Unsupported bit depth %d"), ia->depth);
|
|
|
|
|
return -1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ia->precision == GIMP_PRECISION_U16_NON_LINEAR)
|
|
|
|
|
ia->bytes_per_sample = 2;
|
|
|
|
|
else
|
|
|
|
|
ia->bytes_per_sample = 1;
|
|
|
|
|
|
1999-08-21 03:06:07 +00:00
|
|
|
|
ia->active_layer = GUINT32_FROM_LE (ia->active_layer);
|
|
|
|
|
ia->layer_count = GUINT16_FROM_LE (ia->layer_count);
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-01-25 17:46:56 +00:00
|
|
|
|
static gint
|
2017-12-21 20:42:41 +01:00
|
|
|
|
read_creator_block (FILE *f,
|
2019-08-24 19:52:36 +02:00
|
|
|
|
GimpImage *image,
|
2017-12-21 20:42:41 +01:00
|
|
|
|
guint total_len,
|
|
|
|
|
PSPimage *ia,
|
|
|
|
|
GError **error)
|
1999-08-20 00:47:05 +00:00
|
|
|
|
{
|
2008-09-17 08:37:24 +00:00
|
|
|
|
long data_start;
|
|
|
|
|
guchar buf[4];
|
|
|
|
|
guint16 keyword;
|
|
|
|
|
guint32 length;
|
|
|
|
|
gchar *string;
|
|
|
|
|
gchar *title = NULL, *artist = NULL, *copyright = NULL, *description = NULL;
|
|
|
|
|
guint32 dword;
|
2016-10-31 01:08:38 +01:00
|
|
|
|
guint32 __attribute__((unused))cdate = 0;
|
|
|
|
|
guint32 __attribute__((unused))mdate = 0;
|
|
|
|
|
guint32 __attribute__((unused))appid;
|
|
|
|
|
guint32 __attribute__((unused))appver;
|
2008-09-17 08:37:24 +00:00
|
|
|
|
GString *comment;
|
2000-05-26 22:28:40 +00:00
|
|
|
|
GimpParasite *comment_parasite;
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
|
|
|
|
data_start = ftell (f);
|
|
|
|
|
comment = g_string_new (NULL);
|
|
|
|
|
|
|
|
|
|
while (ftell (f) < data_start + total_len)
|
|
|
|
|
{
|
|
|
|
|
if (fread (buf, 4, 1, f) < 1
|
2008-04-19 17:59:32 +00:00
|
|
|
|
|| fread (&keyword, 2, 1, f) < 1
|
|
|
|
|
|| fread (&length, 4, 1, f) < 1)
|
|
|
|
|
{
|
2017-12-21 20:42:41 +01:00
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading creator keyword chunk"));
|
2008-04-19 17:59:32 +00:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
1999-08-20 00:47:05 +00:00
|
|
|
|
if (memcmp (buf, "~FL\0", 4) != 0)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
2017-12-21 20:42:41 +01:00
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Invalid keyword chunk header"));
|
2008-04-19 17:59:32 +00:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
1999-08-20 00:47:05 +00:00
|
|
|
|
keyword = GUINT16_FROM_LE (keyword);
|
|
|
|
|
length = GUINT32_FROM_LE (length);
|
|
|
|
|
switch (keyword)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
|
|
|
|
case PSP_CRTR_FLD_TITLE:
|
|
|
|
|
case PSP_CRTR_FLD_ARTIST:
|
|
|
|
|
case PSP_CRTR_FLD_CPYRGHT:
|
|
|
|
|
case PSP_CRTR_FLD_DESC:
|
|
|
|
|
string = g_malloc (length + 1);
|
|
|
|
|
if (fread (string, length, 1, f) < 1)
|
|
|
|
|
{
|
2017-12-21 20:42:41 +01:00
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading creator keyword data"));
|
2010-11-09 16:03:47 -02:00
|
|
|
|
g_free (string);
|
2008-04-19 17:59:32 +00:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
2020-08-05 18:05:01 -04:00
|
|
|
|
/* PSP does not zero terminate strings */
|
|
|
|
|
string[length] = '\0';
|
2008-04-19 17:59:32 +00:00
|
|
|
|
switch (keyword)
|
|
|
|
|
{
|
|
|
|
|
case PSP_CRTR_FLD_TITLE:
|
|
|
|
|
g_free (title); title = string; break;
|
|
|
|
|
case PSP_CRTR_FLD_ARTIST:
|
|
|
|
|
g_free (artist); artist = string; break;
|
|
|
|
|
case PSP_CRTR_FLD_CPYRGHT:
|
|
|
|
|
g_free (copyright); copyright = string; break;
|
|
|
|
|
case PSP_CRTR_FLD_DESC:
|
|
|
|
|
g_free (description); description = string; break;
|
|
|
|
|
default:
|
|
|
|
|
g_free (string);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case PSP_CRTR_FLD_CRT_DATE:
|
|
|
|
|
case PSP_CRTR_FLD_MOD_DATE:
|
|
|
|
|
case PSP_CRTR_FLD_APP_ID:
|
|
|
|
|
case PSP_CRTR_FLD_APP_VER:
|
|
|
|
|
if (fread (&dword, 4, 1, f) < 1)
|
|
|
|
|
{
|
2017-12-21 20:42:41 +01:00
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading creator keyword data"));
|
2008-04-19 17:59:32 +00:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
switch (keyword)
|
|
|
|
|
{
|
|
|
|
|
case PSP_CRTR_FLD_CRT_DATE:
|
|
|
|
|
cdate = dword; break;
|
|
|
|
|
case PSP_CRTR_FLD_MOD_DATE:
|
|
|
|
|
mdate = dword; break;
|
|
|
|
|
case PSP_CRTR_FLD_APP_ID:
|
|
|
|
|
appid = dword; break;
|
|
|
|
|
case PSP_CRTR_FLD_APP_VER:
|
|
|
|
|
appver = dword; break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2017-12-21 20:42:41 +01:00
|
|
|
|
if (try_fseek (f, length, SEEK_CUR, error) < 0)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
1999-08-20 00:47:05 +00:00
|
|
|
|
}
|
1999-08-20 23:52:19 +00:00
|
|
|
|
|
1999-08-20 00:47:05 +00:00
|
|
|
|
if (title)
|
|
|
|
|
{
|
|
|
|
|
g_string_append (comment, title);
|
|
|
|
|
g_free (title);
|
|
|
|
|
g_string_append (comment, "\n");
|
|
|
|
|
}
|
|
|
|
|
if (artist)
|
|
|
|
|
{
|
|
|
|
|
g_string_append (comment, artist);
|
|
|
|
|
g_free (artist);
|
|
|
|
|
g_string_append (comment, "\n");
|
|
|
|
|
}
|
|
|
|
|
if (copyright)
|
|
|
|
|
{
|
|
|
|
|
g_string_append (comment, "Copyright ");
|
|
|
|
|
g_string_append (comment, copyright);
|
|
|
|
|
g_free (copyright);
|
|
|
|
|
g_string_append (comment, "\n");
|
|
|
|
|
}
|
|
|
|
|
if (description)
|
|
|
|
|
{
|
|
|
|
|
g_string_append (comment, description);
|
|
|
|
|
g_free (description);
|
|
|
|
|
g_string_append (comment, "\n");
|
|
|
|
|
}
|
|
|
|
|
if (comment->len > 0)
|
1999-08-22 01:54:06 +00:00
|
|
|
|
{
|
2000-05-26 22:28:40 +00:00
|
|
|
|
comment_parasite = gimp_parasite_new ("gimp-comment",
|
2008-04-19 17:59:32 +00:00
|
|
|
|
GIMP_PARASITE_PERSISTENT,
|
|
|
|
|
strlen (comment->str) + 1,
|
|
|
|
|
comment->str);
|
2019-08-24 19:52:36 +02:00
|
|
|
|
gimp_image_attach_parasite (image, comment_parasite);
|
2000-05-26 22:28:40 +00:00
|
|
|
|
gimp_parasite_free (comment_parasite);
|
1999-08-22 01:54:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-05-24 14:32:00 +02:00
|
|
|
|
g_string_free (comment, TRUE);
|
2000-01-08 10:55:05 +00:00
|
|
|
|
|
|
|
|
|
return 0;
|
1999-08-20 00:47:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-11 23:43:48 -04:00
|
|
|
|
static gint
|
|
|
|
|
read_color_block (FILE *f,
|
|
|
|
|
GimpImage *image,
|
|
|
|
|
guint total_len,
|
|
|
|
|
PSPimage *ia,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
|
|
|
|
long block_start;
|
|
|
|
|
guint32 chunk_len, entry_count, pal_size;
|
|
|
|
|
guint32 color_palette_entries;
|
|
|
|
|
guchar *color_palette;
|
|
|
|
|
|
|
|
|
|
block_start = ftell (f);
|
|
|
|
|
|
|
|
|
|
if (psp_ver_major >= 4)
|
|
|
|
|
{
|
|
|
|
|
if (fread (&chunk_len, 4, 1, f) < 1
|
|
|
|
|
|| fread (&entry_count, 4, 1, f) < 1)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading color block"));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
chunk_len = GUINT32_FROM_LE (chunk_len);
|
|
|
|
|
|
|
|
|
|
if (try_fseek (f, block_start + chunk_len, SEEK_SET, error) < 0)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading color block"));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (fread (&entry_count, 4, 1, f) < 1)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading color block"));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
color_palette_entries = GUINT32_FROM_LE (entry_count);
|
2023-09-23 02:41:57 +00:00
|
|
|
|
/* TODO: GIMP currently only supports a maximum of 256 colors
|
|
|
|
|
* in an indexed image. If this changes, we can change this check */
|
|
|
|
|
if (color_palette_entries > 256)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error: Unsupported palette size"));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-11 23:43:48 -04:00
|
|
|
|
/* psp color palette entries are stored as RGBA so 4 bytes per entry
|
2023-09-23 02:41:57 +00:00
|
|
|
|
* where the fourth bytes is always zero */
|
2020-09-11 23:43:48 -04:00
|
|
|
|
pal_size = color_palette_entries * 4;
|
|
|
|
|
color_palette = g_malloc (pal_size);
|
|
|
|
|
if (fread (color_palette, pal_size, 1, f) < 1)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading color palette"));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
guchar *tmpmap;
|
|
|
|
|
gint i;
|
|
|
|
|
|
|
|
|
|
/* Convert to BGR palette */
|
|
|
|
|
tmpmap = g_malloc (3 * color_palette_entries);
|
|
|
|
|
for (i = 0; i < color_palette_entries; ++i)
|
|
|
|
|
{
|
|
|
|
|
tmpmap[i*3 ] = color_palette[i*4+2];
|
|
|
|
|
tmpmap[i*3+1] = color_palette[i*4+1];
|
|
|
|
|
tmpmap[i*3+2] = color_palette[i*4];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memcpy (color_palette, tmpmap, color_palette_entries * 3);
|
|
|
|
|
g_free (tmpmap);
|
|
|
|
|
|
2024-09-23 15:17:04 +02:00
|
|
|
|
gimp_palette_set_colormap (gimp_image_get_palette (image), babl_format ("R'G'B' u8"), color_palette, color_palette_entries * 3);
|
2020-09-11 23:43:48 -04:00
|
|
|
|
g_free (color_palette);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
1999-08-20 23:52:19 +00:00
|
|
|
|
static void inline
|
|
|
|
|
swab_rect (guint32 *rect)
|
|
|
|
|
{
|
|
|
|
|
rect[0] = GUINT32_FROM_LE (rect[0]);
|
|
|
|
|
rect[1] = GUINT32_FROM_LE (rect[1]);
|
|
|
|
|
rect[2] = GUINT32_FROM_LE (rect[2]);
|
|
|
|
|
rect[3] = GUINT32_FROM_LE (rect[3]);
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-08 23:00:19 +01:00
|
|
|
|
static GimpLayerMode
|
2008-04-21 16:31:33 +00:00
|
|
|
|
gimp_layer_mode_from_psp_blend_mode (PSPBlendModes mode)
|
1999-08-20 23:52:19 +00:00
|
|
|
|
{
|
|
|
|
|
switch (mode)
|
|
|
|
|
{
|
|
|
|
|
case PSP_BLEND_NORMAL:
|
2017-02-26 16:26:34 +01:00
|
|
|
|
return GIMP_LAYER_MODE_NORMAL_LEGACY;
|
2017-01-08 23:00:19 +01:00
|
|
|
|
|
1999-08-20 23:52:19 +00:00
|
|
|
|
case PSP_BLEND_DARKEN:
|
2017-01-09 01:27:20 +01:00
|
|
|
|
return GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY;
|
2017-01-08 23:00:19 +01:00
|
|
|
|
|
1999-08-21 03:06:07 +00:00
|
|
|
|
case PSP_BLEND_LIGHTEN:
|
2017-01-09 01:27:20 +01:00
|
|
|
|
return GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY;
|
2017-01-08 23:00:19 +01:00
|
|
|
|
|
1999-08-20 23:52:19 +00:00
|
|
|
|
case PSP_BLEND_HUE:
|
2017-01-09 01:27:20 +01:00
|
|
|
|
return GIMP_LAYER_MODE_HSV_HUE_LEGACY;
|
2017-01-08 23:00:19 +01:00
|
|
|
|
|
1999-08-20 23:52:19 +00:00
|
|
|
|
case PSP_BLEND_SATURATION:
|
2017-01-09 01:27:20 +01:00
|
|
|
|
return GIMP_LAYER_MODE_HSV_SATURATION_LEGACY;
|
2017-01-08 23:00:19 +01:00
|
|
|
|
|
1999-08-20 23:52:19 +00:00
|
|
|
|
case PSP_BLEND_COLOR:
|
2017-03-16 06:17:05 -04:00
|
|
|
|
return GIMP_LAYER_MODE_HSL_COLOR_LEGACY;
|
2017-01-08 23:00:19 +01:00
|
|
|
|
|
2008-04-21 16:31:33 +00:00
|
|
|
|
case PSP_BLEND_LUMINOSITY:
|
2017-01-09 01:27:20 +01:00
|
|
|
|
return GIMP_LAYER_MODE_HSV_VALUE_LEGACY; /* ??? */
|
2017-01-08 23:00:19 +01:00
|
|
|
|
|
1999-08-20 23:52:19 +00:00
|
|
|
|
case PSP_BLEND_MULTIPLY:
|
2017-01-09 01:27:20 +01:00
|
|
|
|
return GIMP_LAYER_MODE_MULTIPLY_LEGACY;
|
2017-01-08 23:00:19 +01:00
|
|
|
|
|
1999-08-20 23:52:19 +00:00
|
|
|
|
case PSP_BLEND_SCREEN:
|
2017-01-09 01:27:20 +01:00
|
|
|
|
return GIMP_LAYER_MODE_SCREEN_LEGACY;
|
2017-01-08 23:00:19 +01:00
|
|
|
|
|
1999-08-20 23:52:19 +00:00
|
|
|
|
case PSP_BLEND_DISSOLVE:
|
2017-01-08 23:00:19 +01:00
|
|
|
|
return GIMP_LAYER_MODE_DISSOLVE;
|
|
|
|
|
|
1999-08-20 23:52:19 +00:00
|
|
|
|
case PSP_BLEND_OVERLAY:
|
2017-01-08 23:00:19 +01:00
|
|
|
|
return GIMP_LAYER_MODE_OVERLAY;
|
|
|
|
|
|
1999-08-20 23:52:19 +00:00
|
|
|
|
case PSP_BLEND_HARD_LIGHT:
|
2017-01-09 01:27:20 +01:00
|
|
|
|
return GIMP_LAYER_MODE_HARDLIGHT_LEGACY;
|
2017-01-08 23:00:19 +01:00
|
|
|
|
|
1999-08-20 23:52:19 +00:00
|
|
|
|
case PSP_BLEND_SOFT_LIGHT:
|
2017-01-09 01:27:20 +01:00
|
|
|
|
return GIMP_LAYER_MODE_SOFTLIGHT_LEGACY;
|
2017-01-08 23:00:19 +01:00
|
|
|
|
|
1999-08-20 23:52:19 +00:00
|
|
|
|
case PSP_BLEND_DIFFERENCE:
|
2017-01-09 01:27:20 +01:00
|
|
|
|
return GIMP_LAYER_MODE_DIFFERENCE_LEGACY;
|
2017-01-08 23:00:19 +01:00
|
|
|
|
|
1999-08-20 23:52:19 +00:00
|
|
|
|
case PSP_BLEND_DODGE:
|
2017-01-09 01:27:20 +01:00
|
|
|
|
return GIMP_LAYER_MODE_DODGE_LEGACY;
|
2017-01-08 23:00:19 +01:00
|
|
|
|
|
1999-08-20 23:52:19 +00:00
|
|
|
|
case PSP_BLEND_BURN:
|
2017-01-09 01:27:20 +01:00
|
|
|
|
return GIMP_LAYER_MODE_BURN_LEGACY;
|
2017-01-08 23:00:19 +01:00
|
|
|
|
|
1999-08-20 23:52:19 +00:00
|
|
|
|
case PSP_BLEND_EXCLUSION:
|
2020-08-27 13:31:17 -04:00
|
|
|
|
return GIMP_LAYER_MODE_EXCLUSION;
|
2017-01-08 23:00:19 +01:00
|
|
|
|
|
2008-04-21 16:31:33 +00:00
|
|
|
|
case PSP_BLEND_TRUE_HUE:
|
2020-08-27 13:31:17 -04:00
|
|
|
|
return GIMP_LAYER_MODE_HSV_HUE;
|
2017-01-08 23:00:19 +01:00
|
|
|
|
|
2008-04-21 16:31:33 +00:00
|
|
|
|
case PSP_BLEND_TRUE_SATURATION:
|
2020-08-27 13:31:17 -04:00
|
|
|
|
return GIMP_LAYER_MODE_HSV_SATURATION;
|
2017-01-08 23:00:19 +01:00
|
|
|
|
|
2008-04-21 16:31:33 +00:00
|
|
|
|
case PSP_BLEND_TRUE_COLOR:
|
2020-08-27 13:31:17 -04:00
|
|
|
|
return GIMP_LAYER_MODE_HSL_COLOR;
|
2017-01-08 23:00:19 +01:00
|
|
|
|
|
2008-04-21 16:31:33 +00:00
|
|
|
|
case PSP_BLEND_TRUE_LIGHTNESS:
|
2020-08-27 13:31:17 -04:00
|
|
|
|
return GIMP_LAYER_MODE_HSV_VALUE;
|
|
|
|
|
|
|
|
|
|
case PSP_BLEND_ADJUST:
|
2008-04-21 16:31:33 +00:00
|
|
|
|
return -1; /* ??? */
|
1999-08-20 23:52:19 +00:00
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-17 08:37:24 +00:00
|
|
|
|
static const gchar *
|
2008-04-21 16:31:33 +00:00
|
|
|
|
blend_mode_name (PSPBlendModes mode)
|
1999-08-20 23:52:19 +00:00
|
|
|
|
{
|
2008-09-17 08:37:24 +00:00
|
|
|
|
static const gchar *blend_mode_names[] =
|
1999-08-20 23:52:19 +00:00
|
|
|
|
{
|
|
|
|
|
"NORMAL",
|
|
|
|
|
"DARKEN",
|
|
|
|
|
"LIGHTEN",
|
|
|
|
|
"HUE",
|
|
|
|
|
"SATURATION",
|
|
|
|
|
"COLOR",
|
2008-04-21 16:31:33 +00:00
|
|
|
|
"LUMINOSITY",
|
1999-08-20 23:52:19 +00:00
|
|
|
|
"MULTIPLY",
|
|
|
|
|
"SCREEN",
|
|
|
|
|
"DISSOLVE",
|
|
|
|
|
"OVERLAY",
|
|
|
|
|
"HARD_LIGHT",
|
|
|
|
|
"SOFT_LIGHT",
|
|
|
|
|
"DIFFERENCE",
|
|
|
|
|
"DODGE",
|
|
|
|
|
"BURN",
|
2020-08-27 13:31:17 -04:00
|
|
|
|
"EXCLUSION",
|
|
|
|
|
"TRUE HUE",
|
|
|
|
|
"TRUE SATURATION",
|
|
|
|
|
"TRUE COLOR",
|
|
|
|
|
"TRUE LIGHTNESS",
|
|
|
|
|
/* ADJUST should always be the last one. */
|
|
|
|
|
"ADJUST"
|
1999-08-20 23:52:19 +00:00
|
|
|
|
};
|
|
|
|
|
static gchar *err_name = NULL;
|
|
|
|
|
|
2020-08-27 13:31:17 -04:00
|
|
|
|
if (mode >= 0 && mode <= PSP_BLEND_TRUE_LIGHTNESS)
|
1999-08-20 23:52:19 +00:00
|
|
|
|
return blend_mode_names[mode];
|
2020-08-27 13:31:17 -04:00
|
|
|
|
else if (mode == PSP_BLEND_ADJUST)
|
|
|
|
|
return blend_mode_names[PSP_BLEND_TRUE_LIGHTNESS+1];
|
1999-08-20 23:52:19 +00:00
|
|
|
|
|
|
|
|
|
g_free (err_name);
|
1999-08-21 03:06:07 +00:00
|
|
|
|
err_name = g_strdup_printf ("unknown layer blend mode %d", mode);
|
|
|
|
|
|
|
|
|
|
return err_name;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-06 11:58:59 -04:00
|
|
|
|
static const gchar *
|
|
|
|
|
layer_type_name (PSPLayerTypePSP6 type)
|
|
|
|
|
{
|
|
|
|
|
static const gchar *layer_type_names[] =
|
|
|
|
|
{
|
|
|
|
|
"Undefined",
|
|
|
|
|
"Raster",
|
|
|
|
|
"Floating Raster Selection",
|
|
|
|
|
"Vector",
|
|
|
|
|
"Adjustment",
|
|
|
|
|
"Group",
|
|
|
|
|
"Mask",
|
|
|
|
|
"Art Media",
|
|
|
|
|
};
|
|
|
|
|
static gchar *err_name = NULL;
|
|
|
|
|
|
|
|
|
|
if (type >= 0 && type <= keGLTArtMedia)
|
|
|
|
|
return layer_type_names[type];
|
|
|
|
|
|
|
|
|
|
g_free (err_name);
|
|
|
|
|
err_name = g_strdup_printf ("unknown layer type %d", type);
|
|
|
|
|
|
|
|
|
|
return err_name;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-17 08:37:24 +00:00
|
|
|
|
static const gchar *
|
2000-01-25 17:46:56 +00:00
|
|
|
|
bitmap_type_name (gint type)
|
1999-08-21 03:06:07 +00:00
|
|
|
|
{
|
2008-09-17 08:37:24 +00:00
|
|
|
|
static const gchar *bitmap_type_names[] =
|
1999-08-21 03:06:07 +00:00
|
|
|
|
{
|
|
|
|
|
"IMAGE",
|
|
|
|
|
"TRANS_MASK",
|
|
|
|
|
"USER_MASK",
|
|
|
|
|
"SELECTION",
|
|
|
|
|
"ALPHA_MASK",
|
|
|
|
|
"THUMBNAIL"
|
|
|
|
|
};
|
|
|
|
|
static gchar *err_name = NULL;
|
|
|
|
|
|
|
|
|
|
if (type >= 0 && type <= PSP_DIB_THUMBNAIL)
|
|
|
|
|
return bitmap_type_names[type];
|
|
|
|
|
|
|
|
|
|
g_free (err_name);
|
|
|
|
|
err_name = g_strdup_printf ("unknown bitmap type %d", type);
|
|
|
|
|
|
|
|
|
|
return err_name;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-17 08:37:24 +00:00
|
|
|
|
static const gchar *
|
2000-01-25 17:46:56 +00:00
|
|
|
|
channel_type_name (gint type)
|
1999-08-21 03:06:07 +00:00
|
|
|
|
{
|
2008-09-17 08:37:24 +00:00
|
|
|
|
static const gchar *channel_type_names[] =
|
1999-08-21 03:06:07 +00:00
|
|
|
|
{
|
|
|
|
|
"COMPOSITE",
|
|
|
|
|
"RED",
|
|
|
|
|
"GREEN",
|
|
|
|
|
"BLUE"
|
|
|
|
|
};
|
|
|
|
|
static gchar *err_name = NULL;
|
|
|
|
|
|
2010-10-08 22:53:04 +05:30
|
|
|
|
if (type >= 0 && type <= PSP_CHANNEL_BLUE)
|
1999-08-21 03:06:07 +00:00
|
|
|
|
return channel_type_names[type];
|
|
|
|
|
|
|
|
|
|
g_free (err_name);
|
|
|
|
|
err_name = g_strdup_printf ("unknown channel type %d", type);
|
1999-08-20 23:52:19 +00:00
|
|
|
|
|
|
|
|
|
return err_name;
|
|
|
|
|
}
|
|
|
|
|
|
1999-08-21 19:00:41 +00:00
|
|
|
|
static void *
|
2000-01-25 17:46:56 +00:00
|
|
|
|
psp_zalloc (void *opaque,
|
2008-04-19 17:59:32 +00:00
|
|
|
|
guint items,
|
|
|
|
|
guint size)
|
1999-08-21 19:00:41 +00:00
|
|
|
|
{
|
|
|
|
|
return g_malloc (items*size);
|
|
|
|
|
}
|
|
|
|
|
|
1999-08-21 03:06:07 +00:00
|
|
|
|
static void
|
2000-01-25 17:46:56 +00:00
|
|
|
|
psp_zfree (void *opaque,
|
2008-04-19 17:59:32 +00:00
|
|
|
|
void *ptr)
|
1999-08-21 19:00:41 +00:00
|
|
|
|
{
|
|
|
|
|
g_free (ptr);
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-11 23:43:48 -04:00
|
|
|
|
static void
|
|
|
|
|
upscale_indexed_sub_8 (FILE *f,
|
|
|
|
|
gint width,
|
|
|
|
|
gint height,
|
|
|
|
|
gint bpp,
|
|
|
|
|
guchar *buf)
|
|
|
|
|
{
|
|
|
|
|
gint x, y, b, line_width;
|
|
|
|
|
gint bpp_zero_based = bpp - 1;
|
|
|
|
|
gint current_bit = 0;
|
|
|
|
|
guchar *tmpbuf, *buf_start, *src;
|
|
|
|
|
|
|
|
|
|
/* Scanlines for 1 and 4 bit only end on a 4-byte boundary. */
|
|
|
|
|
line_width = (((width * bpp + 7) / 8) + bpp_zero_based) / 4 * 4;
|
|
|
|
|
buf_start = g_malloc0 (width * height);
|
|
|
|
|
tmpbuf = buf_start;
|
|
|
|
|
|
|
|
|
|
for (y = 0; y < height; tmpbuf += width, ++y)
|
|
|
|
|
{
|
|
|
|
|
src = buf + y * line_width;
|
|
|
|
|
for (x = 0; x < width; ++x)
|
|
|
|
|
{
|
|
|
|
|
for (b = 0; b < bpp; b++)
|
|
|
|
|
{
|
|
|
|
|
current_bit = bpp * x + b;
|
|
|
|
|
if (src[current_bit / 8] & (128 >> (current_bit % 8)))
|
|
|
|
|
tmpbuf[x] += (1 << (bpp_zero_based - b));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memcpy (buf, buf_start, width * height);
|
|
|
|
|
g_free (buf_start);
|
|
|
|
|
}
|
|
|
|
|
|
1999-08-21 19:00:41 +00:00
|
|
|
|
static int
|
2012-11-19 00:10:17 +01:00
|
|
|
|
read_channel_data (FILE *f,
|
|
|
|
|
PSPimage *ia,
|
|
|
|
|
guchar **pixels,
|
|
|
|
|
guint bytespp,
|
|
|
|
|
guint offset,
|
|
|
|
|
GeglBuffer *buffer,
|
2017-12-21 20:42:41 +01:00
|
|
|
|
guint32 compressed_len,
|
|
|
|
|
GError **error)
|
1999-08-21 03:06:07 +00:00
|
|
|
|
{
|
2020-09-11 23:43:48 -04:00
|
|
|
|
gint i, y, line_width;
|
2012-11-19 00:10:17 +01:00
|
|
|
|
gint width = gegl_buffer_get_width (buffer);
|
|
|
|
|
gint height = gegl_buffer_get_height (buffer);
|
1999-08-21 19:00:41 +00:00
|
|
|
|
gint npixels = width * height;
|
2011-10-12 19:24:09 +05:30
|
|
|
|
guchar *buf;
|
2000-01-08 10:55:05 +00:00
|
|
|
|
guchar *buf2 = NULL; /* please the compiler */
|
1999-08-21 15:41:13 +00:00
|
|
|
|
guchar runcount, byte;
|
1999-08-21 19:00:41 +00:00
|
|
|
|
z_stream zstream;
|
1999-08-21 03:06:07 +00:00
|
|
|
|
|
2020-09-08 22:14:50 -04:00
|
|
|
|
g_assert (ia->bytes_per_sample <= 2);
|
|
|
|
|
|
2020-09-11 23:43:48 -04:00
|
|
|
|
if (ia->depth < 8)
|
|
|
|
|
{
|
|
|
|
|
/* Scanlines for 1 and 4 bit only end on a 4-byte boundary. */
|
|
|
|
|
line_width = (((width * ia->depth + 7) / 8) + ia->depth - 1) / 4 * 4;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
line_width = width * ia->bytes_per_sample;
|
|
|
|
|
}
|
|
|
|
|
|
1999-08-21 19:00:41 +00:00
|
|
|
|
switch (ia->compression)
|
1999-08-21 03:06:07 +00:00
|
|
|
|
{
|
|
|
|
|
case PSP_COMP_NONE:
|
1999-08-21 15:41:13 +00:00
|
|
|
|
if (bytespp == 1)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
2020-09-11 23:43:48 -04:00
|
|
|
|
fread (pixels[0], height * line_width, 1, f);
|
2008-04-19 17:59:32 +00:00
|
|
|
|
}
|
1999-08-21 03:06:07 +00:00
|
|
|
|
else
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
2020-09-11 23:43:48 -04:00
|
|
|
|
buf = g_malloc (line_width);
|
2020-09-08 22:14:50 -04:00
|
|
|
|
if (ia->bytes_per_sample == 1)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
2020-09-08 22:14:50 -04:00
|
|
|
|
for (y = 0; y < height; y++)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
2020-09-08 22:14:50 -04:00
|
|
|
|
guchar *p, *q;
|
|
|
|
|
|
|
|
|
|
fread (buf, width, 1, f);
|
|
|
|
|
/* Contrary to what the PSP specification seems to suggest
|
|
|
|
|
scanlines are not stored on a 4-byte boundary. */
|
|
|
|
|
p = buf;
|
|
|
|
|
q = pixels[y] + offset;
|
|
|
|
|
for (i = 0; i < width; i++)
|
|
|
|
|
{
|
|
|
|
|
*q = *p++;
|
|
|
|
|
q += bytespp;
|
|
|
|
|
}
|
2008-04-19 17:59:32 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-08 22:14:50 -04:00
|
|
|
|
else if (ia->bytes_per_sample == 2)
|
|
|
|
|
{
|
|
|
|
|
for (y = 0; y < height; y++)
|
|
|
|
|
{
|
|
|
|
|
guint16 *p, *q;
|
|
|
|
|
|
|
|
|
|
fread (buf, width * ia->bytes_per_sample, 1, f);
|
|
|
|
|
/* Contrary to what the PSP specification seems to suggest
|
|
|
|
|
scanlines are not stored on a 4-byte boundary. */
|
|
|
|
|
p = (guint16 *) buf;
|
|
|
|
|
q = (guint16 *) (pixels[y] + offset);
|
|
|
|
|
for (i = 0; i < width; i++)
|
|
|
|
|
{
|
|
|
|
|
*q = GUINT16_FROM_LE (*p++);
|
|
|
|
|
q += bytespp / 2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-04-19 17:59:32 +00:00
|
|
|
|
g_free (buf);
|
|
|
|
|
}
|
1999-08-21 15:41:13 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PSP_COMP_RLE:
|
2011-10-12 19:24:09 +05:30
|
|
|
|
{
|
|
|
|
|
guchar *q, *endq;
|
|
|
|
|
|
|
|
|
|
q = pixels[0] + offset;
|
2020-09-11 23:43:48 -04:00
|
|
|
|
if (ia->depth >= 8)
|
|
|
|
|
endq = q + npixels * bytespp;
|
|
|
|
|
else
|
|
|
|
|
endq = q + line_width * height;
|
|
|
|
|
|
2023-09-23 02:16:24 +00:00
|
|
|
|
buf = g_malloc (128);
|
2011-10-12 19:24:09 +05:30
|
|
|
|
while (q < endq)
|
|
|
|
|
{
|
|
|
|
|
fread (&runcount, 1, 1, f);
|
|
|
|
|
if (runcount > 128)
|
|
|
|
|
{
|
|
|
|
|
runcount -= 128;
|
|
|
|
|
fread (&byte, 1, 1, f);
|
|
|
|
|
memset (buf, byte, runcount);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
fread (buf, runcount, 1, f);
|
|
|
|
|
|
|
|
|
|
/* prevent buffer overflow for bogus data */
|
2020-09-08 22:14:50 -04:00
|
|
|
|
if (runcount > (endq - q) / bytespp + ia->bytes_per_sample - 1)
|
|
|
|
|
{
|
|
|
|
|
g_printerr ("Buffer overflow decompressing RLE data.\n");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-10-12 19:24:09 +05:30
|
|
|
|
|
|
|
|
|
if (bytespp == 1)
|
|
|
|
|
{
|
|
|
|
|
memmove (q, buf, runcount);
|
|
|
|
|
q += runcount;
|
|
|
|
|
}
|
2020-09-08 22:14:50 -04:00
|
|
|
|
else if (ia->bytes_per_sample == 1)
|
2011-10-12 19:24:09 +05:30
|
|
|
|
{
|
|
|
|
|
guchar *p = buf;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < runcount; i++)
|
|
|
|
|
{
|
|
|
|
|
*q = *p++;
|
|
|
|
|
q += bytespp;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-08 22:14:50 -04:00
|
|
|
|
else if (ia->bytes_per_sample == 2)
|
|
|
|
|
{
|
|
|
|
|
guint16 *p = (guint16 *) buf;
|
|
|
|
|
guint16 *r = (guint16 *) q;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < runcount / 2; i++)
|
|
|
|
|
{
|
|
|
|
|
*r = GUINT16_FROM_LE (*p++);
|
|
|
|
|
r += bytespp / 2;
|
|
|
|
|
}
|
|
|
|
|
q = (guchar *) r;
|
|
|
|
|
}
|
2011-10-12 19:24:09 +05:30
|
|
|
|
}
|
|
|
|
|
g_free (buf);
|
|
|
|
|
}
|
1999-08-21 15:41:13 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PSP_COMP_LZ77:
|
1999-08-21 19:00:41 +00:00
|
|
|
|
buf = g_malloc (compressed_len);
|
|
|
|
|
fread (buf, compressed_len, 1, f);
|
|
|
|
|
zstream.next_in = buf;
|
|
|
|
|
zstream.avail_in = compressed_len;
|
|
|
|
|
zstream.zalloc = psp_zalloc;
|
|
|
|
|
zstream.zfree = psp_zfree;
|
|
|
|
|
zstream.opaque = f;
|
|
|
|
|
if (inflateInit (&zstream) != Z_OK)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
2017-12-21 20:42:41 +01:00
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("zlib error"));
|
2008-04-19 17:59:32 +00:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
1999-08-21 19:00:41 +00:00
|
|
|
|
if (bytespp == 1)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
zstream.next_out = pixels[0];
|
1999-08-21 19:00:41 +00:00
|
|
|
|
else
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
2020-09-08 22:14:50 -04:00
|
|
|
|
buf2 = g_malloc (npixels * ia->bytes_per_sample);
|
2008-04-19 17:59:32 +00:00
|
|
|
|
zstream.next_out = buf2;
|
|
|
|
|
}
|
2020-09-08 22:14:50 -04:00
|
|
|
|
zstream.avail_out = npixels * ia->bytes_per_sample;
|
1999-08-21 19:00:41 +00:00
|
|
|
|
if (inflate (&zstream, Z_FINISH) != Z_STREAM_END)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
2017-12-21 20:42:41 +01:00
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("zlib error"));
|
2008-04-19 17:59:32 +00:00
|
|
|
|
inflateEnd (&zstream);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
1999-08-21 19:00:41 +00:00
|
|
|
|
inflateEnd (&zstream);
|
|
|
|
|
g_free (buf);
|
2003-11-06 15:27:05 +00:00
|
|
|
|
|
1999-08-21 19:00:41 +00:00
|
|
|
|
if (bytespp > 1)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
2020-09-08 22:14:50 -04:00
|
|
|
|
if (ia->bytes_per_sample == 1)
|
|
|
|
|
{
|
|
|
|
|
guchar *p, *q;
|
2011-10-12 19:24:09 +05:30
|
|
|
|
|
2020-09-08 22:14:50 -04:00
|
|
|
|
p = buf2;
|
|
|
|
|
q = pixels[0] + offset;
|
|
|
|
|
for (i = 0; i < npixels; i++)
|
|
|
|
|
{
|
|
|
|
|
*q = *p++;
|
|
|
|
|
q += bytespp;
|
|
|
|
|
}
|
|
|
|
|
g_free (buf2);
|
|
|
|
|
}
|
|
|
|
|
else if (ia->bytes_per_sample == 2)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
2020-09-08 22:14:50 -04:00
|
|
|
|
guint16 *p, *q;
|
|
|
|
|
|
|
|
|
|
p = (guint16 *) buf2;
|
|
|
|
|
q = (guint16 *) (pixels[0] + offset);
|
|
|
|
|
for (i = 0; i < npixels; i++)
|
|
|
|
|
{
|
|
|
|
|
*q = GUINT16_FROM_LE (*p++);
|
|
|
|
|
q += bytespp / 2;
|
|
|
|
|
}
|
|
|
|
|
g_free (buf2);
|
2008-04-19 17:59:32 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
1999-08-21 19:00:41 +00:00
|
|
|
|
break;
|
1999-08-21 03:06:07 +00:00
|
|
|
|
}
|
2000-01-08 10:55:05 +00:00
|
|
|
|
|
2020-09-11 23:43:48 -04:00
|
|
|
|
if (ia->base_type == GIMP_INDEXED && ia->depth < 8)
|
|
|
|
|
{
|
|
|
|
|
/* We need to convert 1 and 4 bit to 8 bit indexed */
|
|
|
|
|
upscale_indexed_sub_8 (f, width, height, ia->depth, pixels[0]);
|
|
|
|
|
}
|
|
|
|
|
|
2000-01-08 10:55:05 +00:00
|
|
|
|
return 0;
|
1999-08-21 03:06:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-26 14:43:31 -04:00
|
|
|
|
static gboolean
|
|
|
|
|
read_raster_layer_info (FILE *f,
|
|
|
|
|
long layer_extension_start,
|
|
|
|
|
guint16 *bitmap_count,
|
|
|
|
|
guint16 *channel_count,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
2020-08-26 14:51:30 -04:00
|
|
|
|
long block_start;
|
|
|
|
|
guint32 layer_extension_len, block_len;
|
|
|
|
|
gint block_id;
|
2020-08-26 14:43:31 -04:00
|
|
|
|
|
|
|
|
|
if (fseek (f, layer_extension_start, SEEK_SET) < 0
|
|
|
|
|
|| fread (&layer_extension_len, 4, 1, f) < 1)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading layer extension information"));
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-26 14:51:30 -04:00
|
|
|
|
/* Newer versions of PSP have an extra block here for raster layers
|
|
|
|
|
with block id = 0x21. Most likely this was to fix an oversight in
|
|
|
|
|
the specification since vector and adjustment layers already were
|
|
|
|
|
using a similar extension block since file version 4.
|
|
|
|
|
The old chunk with bitmap_count and channel_count starts after this block.
|
|
|
|
|
We do not know starting from which version this change was implemented
|
|
|
|
|
but most likely version 9 (could also be version 10) so we can't test
|
|
|
|
|
based on version number only.
|
|
|
|
|
Although this is kind of a hack we can safely test for the block starting
|
|
|
|
|
code since the layer_extension_len here is always a small number.
|
|
|
|
|
*/
|
|
|
|
|
if (psp_ver_major > 8 && memcmp (&layer_extension_len, "~BK\0", 4) == 0)
|
|
|
|
|
{
|
|
|
|
|
if (fread (&block_id, 2, 1, f) < 1
|
|
|
|
|
|| fread (&block_len, 4, 1, f) < 1)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading block information"));
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
block_id = GUINT16_FROM_LE (block_id);
|
|
|
|
|
block_len = GUINT32_FROM_LE (block_len);
|
|
|
|
|
|
|
|
|
|
block_start = ftell (f);
|
|
|
|
|
layer_extension_start = block_start + block_len;
|
|
|
|
|
|
|
|
|
|
if (fseek (f, layer_extension_start, SEEK_SET) < 0
|
|
|
|
|
|| fread (&layer_extension_len, 4, 1, f) < 1)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading layer extension information"));
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-26 14:43:31 -04:00
|
|
|
|
layer_extension_len = GUINT32_FROM_LE (layer_extension_len);
|
|
|
|
|
|
|
|
|
|
if (fread (bitmap_count, 2, 1, f) < 1
|
|
|
|
|
|| fread (channel_count, 2, 1, f) < 1)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading layer extension information"));
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
if (try_fseek (f, layer_extension_start + layer_extension_len, SEEK_SET, error) < 0)
|
|
|
|
|
{
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-24 19:52:36 +02:00
|
|
|
|
static GimpLayer *
|
2017-12-21 20:42:41 +01:00
|
|
|
|
read_layer_block (FILE *f,
|
2019-08-24 19:52:36 +02:00
|
|
|
|
GimpImage *image,
|
2017-12-21 20:42:41 +01:00
|
|
|
|
guint total_len,
|
|
|
|
|
PSPimage *ia,
|
|
|
|
|
GError **error)
|
1999-08-20 00:47:05 +00:00
|
|
|
|
{
|
2000-01-25 17:46:56 +00:00
|
|
|
|
gint i;
|
1999-08-20 23:52:19 +00:00
|
|
|
|
long block_start, sub_block_start, channel_start;
|
2020-08-18 15:59:58 -04:00
|
|
|
|
long layer_extension_start;
|
2000-01-25 17:46:56 +00:00
|
|
|
|
gint sub_id;
|
1999-08-20 00:47:05 +00:00
|
|
|
|
guint32 sub_init_len, sub_total_len;
|
2020-08-26 14:43:31 -04:00
|
|
|
|
guint32 chunk_len;
|
2007-07-05 17:03:27 +00:00
|
|
|
|
gchar *name = NULL;
|
2020-08-22 15:00:47 -04:00
|
|
|
|
gchar *layer_name = NULL;
|
1999-08-20 23:52:19 +00:00
|
|
|
|
guint16 namelen;
|
|
|
|
|
guchar type, opacity, blend_mode, visibility, transparency_protected;
|
|
|
|
|
guchar link_group_id, mask_linked, mask_disabled;
|
|
|
|
|
guint32 image_rect[4], saved_image_rect[4], mask_rect[4], saved_mask_rect[4];
|
2020-08-18 15:59:58 -04:00
|
|
|
|
gboolean null_layer, can_handle_layer;
|
1999-08-20 23:52:19 +00:00
|
|
|
|
guint16 bitmap_count, channel_count;
|
2000-08-22 01:26:57 +00:00
|
|
|
|
GimpImageType drawable_type;
|
2019-08-24 19:52:36 +02:00
|
|
|
|
GimpLayer *layer = NULL;
|
2017-01-08 23:00:19 +01:00
|
|
|
|
GimpLayerMode layer_mode;
|
1999-08-20 23:52:19 +00:00
|
|
|
|
guint32 channel_init_len, channel_total_len;
|
|
|
|
|
guint32 compressed_len, uncompressed_len;
|
|
|
|
|
guint16 bitmap_type, channel_type;
|
1999-08-21 15:41:13 +00:00
|
|
|
|
gint width, height, bytespp, offset;
|
1999-08-21 03:06:07 +00:00
|
|
|
|
guchar **pixels, *pixel;
|
2012-11-19 00:10:17 +01:00
|
|
|
|
GeglBuffer *buffer;
|
1999-08-20 23:52:19 +00:00
|
|
|
|
|
|
|
|
|
block_start = ftell (f);
|
|
|
|
|
|
|
|
|
|
while (ftell (f) < block_start + total_len)
|
1999-08-20 00:47:05 +00:00
|
|
|
|
{
|
2020-08-18 15:59:58 -04:00
|
|
|
|
null_layer = FALSE;
|
|
|
|
|
can_handle_layer = FALSE;
|
|
|
|
|
|
1999-08-20 23:52:19 +00:00
|
|
|
|
/* Read the layer sub-block header */
|
2017-12-21 20:42:41 +01:00
|
|
|
|
sub_id = read_block_header (f, &sub_init_len, &sub_total_len, error);
|
1999-08-20 00:47:05 +00:00
|
|
|
|
if (sub_id == -1)
|
2019-08-24 19:52:36 +02:00
|
|
|
|
return NULL;
|
2007-07-05 17:03:27 +00:00
|
|
|
|
|
1999-08-20 23:52:19 +00:00
|
|
|
|
if (sub_id != PSP_LAYER_BLOCK)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
2017-12-21 20:42:41 +01:00
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Invalid layer sub-block %s, should be LAYER"),
|
|
|
|
|
block_name (sub_id));
|
2019-08-24 19:52:36 +02:00
|
|
|
|
return NULL;
|
2008-04-19 17:59:32 +00:00
|
|
|
|
}
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
1999-08-20 23:52:19 +00:00
|
|
|
|
sub_block_start = ftell (f);
|
|
|
|
|
|
|
|
|
|
/* Read layer information chunk */
|
2007-09-11 22:48:03 +00:00
|
|
|
|
if (psp_ver_major >= 4)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
2020-08-18 15:59:58 -04:00
|
|
|
|
if (fread (&chunk_len, 4, 1, f) < 1
|
2008-04-19 17:59:32 +00:00
|
|
|
|
|| fread (&namelen, 2, 1, f) < 1
|
2020-08-23 18:30:41 -04:00
|
|
|
|
/* A zero length layer name is apparently valid. To not get a warning for
|
|
|
|
|
namelen < 0 always being false we use this more complicated comparison. */
|
|
|
|
|
|| ((namelen = GUINT16_FROM_LE (namelen)) && (FALSE || namelen == 0))
|
2008-04-19 17:59:32 +00:00
|
|
|
|
|| (name = g_malloc (namelen + 1)) == NULL
|
2020-08-23 18:30:41 -04:00
|
|
|
|
|| (namelen > 0 && fread (name, namelen, 1, f) < 1)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
|| fread (&type, 1, 1, f) < 1
|
|
|
|
|
|| fread (&image_rect, 16, 1, f) < 1
|
|
|
|
|
|| fread (&saved_image_rect, 16, 1, f) < 1
|
|
|
|
|
|| fread (&opacity, 1, 1, f) < 1
|
|
|
|
|
|| fread (&blend_mode, 1, 1, f) < 1
|
|
|
|
|
|| fread (&visibility, 1, 1, f) < 1
|
|
|
|
|
|| fread (&transparency_protected, 1, 1, f) < 1
|
|
|
|
|
|| fread (&link_group_id, 1, 1, f) < 1
|
|
|
|
|
|| fread (&mask_rect, 16, 1, f) < 1
|
|
|
|
|
|| fread (&saved_mask_rect, 16, 1, f) < 1
|
|
|
|
|
|| fread (&mask_linked, 1, 1, f) < 1
|
2020-08-18 15:59:58 -04:00
|
|
|
|
|| fread (&mask_disabled, 1, 1, f) < 1)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
2017-12-21 20:42:41 +01:00
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading layer information chunk"));
|
2007-07-05 17:03:27 +00:00
|
|
|
|
g_free (name);
|
2019-08-24 19:52:36 +02:00
|
|
|
|
return NULL;
|
2008-04-19 17:59:32 +00:00
|
|
|
|
}
|
2007-07-05 17:03:27 +00:00
|
|
|
|
|
2008-04-19 17:59:32 +00:00
|
|
|
|
name[namelen] = 0;
|
2020-08-22 15:00:47 -04:00
|
|
|
|
layer_name = g_convert (name, -1, "utf-8", "iso8859-1", NULL, NULL, NULL);
|
|
|
|
|
g_free (name);
|
2020-08-18 15:59:58 -04:00
|
|
|
|
|
2020-08-26 14:43:31 -04:00
|
|
|
|
chunk_len = GUINT32_FROM_LE (chunk_len);
|
2020-08-18 15:59:58 -04:00
|
|
|
|
layer_extension_start = sub_block_start + chunk_len;
|
2020-08-26 14:43:31 -04:00
|
|
|
|
|
2020-08-18 15:59:58 -04:00
|
|
|
|
switch (type)
|
|
|
|
|
{
|
|
|
|
|
case keGLTFloatingRasterSelection:
|
2020-08-22 15:00:47 -04:00
|
|
|
|
g_message ("Floating selection restored as normal layer (%s)", layer_name);
|
2020-08-18 15:59:58 -04:00
|
|
|
|
case keGLTRaster:
|
2020-08-26 14:43:31 -04:00
|
|
|
|
if (! read_raster_layer_info (f, layer_extension_start,
|
|
|
|
|
&bitmap_count,
|
|
|
|
|
&channel_count,
|
|
|
|
|
error))
|
2020-08-18 15:59:58 -04:00
|
|
|
|
{
|
2020-08-22 15:00:47 -04:00
|
|
|
|
g_free (layer_name);
|
2020-08-18 15:59:58 -04:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2020-08-26 14:43:31 -04:00
|
|
|
|
can_handle_layer = TRUE;
|
2020-08-18 15:59:58 -04:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
bitmap_count = 0;
|
|
|
|
|
channel_count = 0;
|
2020-09-06 11:58:59 -04:00
|
|
|
|
g_message ("Unsupported layer type %s (%s)", layer_type_name(type), layer_name);
|
2020-08-18 15:59:58 -04:00
|
|
|
|
break;
|
|
|
|
|
}
|
2008-04-19 17:59:32 +00:00
|
|
|
|
}
|
1999-08-20 23:52:19 +00:00
|
|
|
|
else
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
|
|
|
|
name = g_malloc (257);
|
|
|
|
|
name[256] = 0;
|
|
|
|
|
|
|
|
|
|
if (fread (name, 256, 1, f) < 1
|
|
|
|
|
|| fread (&type, 1, 1, f) < 1
|
|
|
|
|
|| fread (&image_rect, 16, 1, f) < 1
|
|
|
|
|
|| fread (&saved_image_rect, 16, 1, f) < 1
|
|
|
|
|
|| fread (&opacity, 1, 1, f) < 1
|
|
|
|
|
|| fread (&blend_mode, 1, 1, f) < 1
|
|
|
|
|
|| fread (&visibility, 1, 1, f) < 1
|
|
|
|
|
|| fread (&transparency_protected, 1, 1, f) < 1
|
|
|
|
|
|| fread (&link_group_id, 1, 1, f) < 1
|
|
|
|
|
|| fread (&mask_rect, 16, 1, f) < 1
|
|
|
|
|
|| fread (&saved_mask_rect, 16, 1, f) < 1
|
|
|
|
|
|| fread (&mask_linked, 1, 1, f) < 1
|
|
|
|
|
|| fread (&mask_disabled, 1, 1, f) < 1
|
|
|
|
|
|| fseek (f, 43, SEEK_CUR) < 0
|
|
|
|
|
|| fread (&bitmap_count, 2, 1, f) < 1
|
|
|
|
|
|| fread (&channel_count, 2, 1, f) < 1)
|
|
|
|
|
{
|
2017-12-21 20:42:41 +01:00
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading layer information chunk"));
|
2008-04-19 17:59:32 +00:00
|
|
|
|
g_free (name);
|
2019-08-24 19:52:36 +02:00
|
|
|
|
return NULL;
|
2008-04-19 17:59:32 +00:00
|
|
|
|
}
|
2020-08-22 15:00:47 -04:00
|
|
|
|
layer_name = g_convert (name, -1, "utf-8", "iso8859-1", NULL, NULL, NULL);
|
|
|
|
|
g_free (name);
|
2020-08-18 15:59:58 -04:00
|
|
|
|
if (type == PSP_LAYER_FLOATING_SELECTION)
|
|
|
|
|
g_message ("Floating selection restored as normal layer");
|
|
|
|
|
type = keGLTRaster;
|
|
|
|
|
can_handle_layer = TRUE;
|
|
|
|
|
if (try_fseek (f, sub_block_start + sub_init_len, SEEK_SET, error) < 0)
|
|
|
|
|
{
|
2020-08-22 15:00:47 -04:00
|
|
|
|
g_free (layer_name);
|
2020-08-18 15:59:58 -04:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2008-04-19 17:59:32 +00:00
|
|
|
|
}
|
1999-08-20 23:52:19 +00:00
|
|
|
|
|
|
|
|
|
swab_rect (image_rect);
|
|
|
|
|
swab_rect (saved_image_rect);
|
|
|
|
|
swab_rect (mask_rect);
|
|
|
|
|
swab_rect (saved_mask_rect);
|
1999-08-21 03:06:07 +00:00
|
|
|
|
bitmap_count = GUINT16_FROM_LE (bitmap_count);
|
|
|
|
|
channel_count = GUINT16_FROM_LE (channel_count);
|
1999-08-20 23:52:19 +00:00
|
|
|
|
|
|
|
|
|
layer_mode = gimp_layer_mode_from_psp_blend_mode (blend_mode);
|
|
|
|
|
if ((int) layer_mode == -1)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
|
|
|
|
g_message ("Unsupported PSP layer blend mode %s "
|
|
|
|
|
"for layer %s, setting layer invisible",
|
2020-08-22 15:00:47 -04:00
|
|
|
|
blend_mode_name (blend_mode), layer_name);
|
2017-02-26 16:26:34 +01:00
|
|
|
|
layer_mode = GIMP_LAYER_MODE_NORMAL_LEGACY;
|
2008-04-19 17:59:32 +00:00
|
|
|
|
visibility = FALSE;
|
|
|
|
|
}
|
1999-08-20 23:52:19 +00:00
|
|
|
|
|
1999-08-21 03:06:07 +00:00
|
|
|
|
width = saved_image_rect[2] - saved_image_rect[0];
|
|
|
|
|
height = saved_image_rect[3] - saved_image_rect[1];
|
|
|
|
|
|
2007-07-05 10:50:38 +00:00
|
|
|
|
if ((width < 0) || (width > GIMP_MAX_IMAGE_SIZE) /* w <= 2^18 */
|
|
|
|
|
|| (height < 0) || (height > GIMP_MAX_IMAGE_SIZE) /* h <= 2^18 */
|
|
|
|
|
|| ((width / 256) * (height / 256) >= 8192)) /* w * h < 2^29 */
|
2007-07-04 23:32:15 +00:00
|
|
|
|
{
|
2017-12-21 20:42:41 +01:00
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Invalid layer dimensions: %dx%d"),
|
|
|
|
|
width, height);
|
2020-08-22 15:00:47 -04:00
|
|
|
|
g_free (layer_name);
|
2019-08-24 19:52:36 +02:00
|
|
|
|
return NULL;
|
2007-07-04 23:32:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-08-22 19:02:52 +00:00
|
|
|
|
IFDBG(2) g_message
|
2008-04-19 17:59:32 +00:00
|
|
|
|
("layer: %s %dx%d (%dx%d) @%d,%d opacity %d blend_mode %s "
|
|
|
|
|
"%d bitmaps %d channels",
|
2020-08-22 15:00:47 -04:00
|
|
|
|
layer_name,
|
2008-04-19 17:59:32 +00:00
|
|
|
|
image_rect[2] - image_rect[0], image_rect[3] - image_rect[1],
|
|
|
|
|
width, height,
|
2020-08-21 19:35:15 -04:00
|
|
|
|
image_rect[0]+saved_image_rect[0], image_rect[1]+saved_image_rect[1],
|
2008-04-19 17:59:32 +00:00
|
|
|
|
opacity, blend_mode_name (blend_mode),
|
|
|
|
|
bitmap_count, channel_count);
|
1999-08-22 19:02:52 +00:00
|
|
|
|
|
|
|
|
|
IFDBG(2) g_message
|
2008-04-19 17:59:32 +00:00
|
|
|
|
("mask %dx%d (%dx%d) @%d,%d",
|
|
|
|
|
mask_rect[2] - mask_rect[0],
|
|
|
|
|
mask_rect[3] - mask_rect[1],
|
|
|
|
|
saved_mask_rect[2] - saved_mask_rect[0],
|
|
|
|
|
saved_mask_rect[3] - saved_mask_rect[1],
|
|
|
|
|
saved_mask_rect[0], saved_mask_rect[1]);
|
1999-08-21 19:00:41 +00:00
|
|
|
|
|
1999-08-21 03:06:07 +00:00
|
|
|
|
if (width == 0)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
|
|
|
|
width++;
|
|
|
|
|
null_layer = TRUE;
|
|
|
|
|
}
|
1999-08-21 03:06:07 +00:00
|
|
|
|
if (height == 0)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
|
|
|
|
height++;
|
|
|
|
|
null_layer = TRUE;
|
|
|
|
|
}
|
1999-08-20 23:52:19 +00:00
|
|
|
|
|
2020-09-08 22:29:45 -04:00
|
|
|
|
if (ia->base_type == GIMP_RGB)
|
|
|
|
|
if (bitmap_count == 1)
|
|
|
|
|
drawable_type = GIMP_RGB_IMAGE, bytespp = 3;
|
|
|
|
|
else
|
|
|
|
|
drawable_type = GIMP_RGBA_IMAGE, bytespp = 4;
|
|
|
|
|
else if (ia->base_type == GIMP_GRAY)
|
|
|
|
|
if (bitmap_count == 1)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
drawable_type = GIMP_GRAY_IMAGE, bytespp = 1;
|
|
|
|
|
else
|
2020-09-08 22:29:45 -04:00
|
|
|
|
drawable_type = GIMP_GRAYA_IMAGE, bytespp = 2;
|
1999-08-21 03:06:07 +00:00
|
|
|
|
else
|
2020-09-08 22:29:45 -04:00
|
|
|
|
if (bitmap_count == 1)
|
|
|
|
|
drawable_type = GIMP_INDEXED_IMAGE, bytespp = 1;
|
2008-04-19 17:59:32 +00:00
|
|
|
|
else
|
2020-09-08 22:29:45 -04:00
|
|
|
|
drawable_type = GIMP_INDEXEDA_IMAGE, bytespp = 2;
|
2020-09-08 22:14:50 -04:00
|
|
|
|
bytespp *= ia->bytes_per_sample;
|
1999-08-21 03:06:07 +00:00
|
|
|
|
|
2020-08-22 15:00:47 -04:00
|
|
|
|
layer = gimp_layer_new (image, layer_name,
|
2019-08-24 19:52:36 +02:00
|
|
|
|
width, height,
|
|
|
|
|
drawable_type,
|
|
|
|
|
100.0 * opacity / 255.0,
|
|
|
|
|
layer_mode);
|
2020-08-22 15:00:47 -04:00
|
|
|
|
g_free (layer_name);
|
2019-08-24 19:52:36 +02:00
|
|
|
|
if (! layer)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
2017-12-21 20:42:41 +01:00
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error creating layer"));
|
2019-08-24 19:52:36 +02:00
|
|
|
|
return NULL;
|
2008-04-19 17:59:32 +00:00
|
|
|
|
}
|
1999-08-20 23:52:19 +00:00
|
|
|
|
|
2019-08-24 19:52:36 +02:00
|
|
|
|
gimp_image_insert_layer (image, layer, NULL, -1);
|
1999-08-22 01:54:06 +00:00
|
|
|
|
|
2020-08-21 19:35:15 -04:00
|
|
|
|
if (image_rect[0] != 0 || image_rect[1] != 0 || saved_image_rect[0] != 0 || saved_image_rect[1] != 0)
|
2019-08-24 19:52:36 +02:00
|
|
|
|
gimp_layer_set_offsets (layer,
|
2020-08-21 19:35:15 -04:00
|
|
|
|
image_rect[0] + saved_image_rect[0], image_rect[1] + saved_image_rect[1]);
|
1999-08-20 23:52:19 +00:00
|
|
|
|
|
2019-08-24 19:52:36 +02:00
|
|
|
|
if (! visibility)
|
|
|
|
|
gimp_item_set_visible (GIMP_ITEM (layer), FALSE);
|
2003-11-06 15:27:05 +00:00
|
|
|
|
|
2019-08-24 19:52:36 +02:00
|
|
|
|
gimp_layer_set_lock_alpha (layer, transparency_protected);
|
1999-08-20 23:52:19 +00:00
|
|
|
|
|
2020-08-18 15:59:58 -04:00
|
|
|
|
if (can_handle_layer)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
2020-08-18 15:59:58 -04:00
|
|
|
|
pixel = g_malloc0 (height * width * bytespp);
|
|
|
|
|
if (null_layer)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
2020-08-18 15:59:58 -04:00
|
|
|
|
pixels = NULL;
|
2008-04-19 17:59:32 +00:00
|
|
|
|
}
|
2020-08-18 15:59:58 -04:00
|
|
|
|
else
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
2020-08-18 15:59:58 -04:00
|
|
|
|
pixels = g_new (guchar *, height);
|
|
|
|
|
for (i = 0; i < height; i++)
|
|
|
|
|
pixels[i] = pixel + width * bytespp * i;
|
2008-04-19 17:59:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-18 15:59:58 -04:00
|
|
|
|
buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));
|
2008-04-19 17:59:32 +00:00
|
|
|
|
|
2020-08-18 15:59:58 -04:00
|
|
|
|
/* Read the layer channel sub-blocks */
|
|
|
|
|
while (ftell (f) < sub_block_start + sub_total_len)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
2020-08-18 15:59:58 -04:00
|
|
|
|
sub_id = read_block_header (f, &channel_init_len,
|
|
|
|
|
&channel_total_len, error);
|
|
|
|
|
if (sub_id == -1)
|
|
|
|
|
{
|
|
|
|
|
gimp_image_delete (image);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2008-04-19 17:59:32 +00:00
|
|
|
|
|
2020-08-18 15:59:58 -04:00
|
|
|
|
if (sub_id != PSP_CHANNEL_BLOCK)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Invalid layer sub-block %s, should be CHANNEL"),
|
|
|
|
|
block_name (sub_id));
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2008-04-19 17:59:32 +00:00
|
|
|
|
|
2020-08-18 15:59:58 -04:00
|
|
|
|
channel_start = ftell (f);
|
|
|
|
|
chunk_len = channel_init_len; /* init chunk_len for psp_ver_major == 3 */
|
|
|
|
|
if ((psp_ver_major >= 4
|
|
|
|
|
&& (fread (&chunk_len, 4, 1, f) < 1
|
|
|
|
|
|| ((chunk_len = GUINT32_FROM_LE (chunk_len)) < 16)))
|
|
|
|
|
|| fread (&compressed_len, 4, 1, f) < 1
|
|
|
|
|
|| fread (&uncompressed_len, 4, 1, f) < 1
|
|
|
|
|
|| fread (&bitmap_type, 2, 1, f) < 1
|
|
|
|
|
|| fread (&channel_type, 2, 1, f) < 1)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading channel information chunk"));
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2008-04-19 17:59:32 +00:00
|
|
|
|
|
2020-08-18 15:59:58 -04:00
|
|
|
|
compressed_len = GUINT32_FROM_LE (compressed_len);
|
|
|
|
|
uncompressed_len = GUINT32_FROM_LE (uncompressed_len);
|
|
|
|
|
bitmap_type = GUINT16_FROM_LE (bitmap_type);
|
|
|
|
|
channel_type = GUINT16_FROM_LE (channel_type);
|
2008-04-19 17:59:32 +00:00
|
|
|
|
|
2020-08-18 15:59:58 -04:00
|
|
|
|
if (bitmap_type > PSP_DIB_USER_MASK)
|
|
|
|
|
{
|
|
|
|
|
g_message ("Conversion of bitmap type %d is not supported.", bitmap_type);
|
|
|
|
|
}
|
|
|
|
|
else if (bitmap_type == PSP_DIB_USER_MASK)
|
|
|
|
|
{
|
|
|
|
|
/* FIXME: Add as layer mask */
|
|
|
|
|
g_message ("Conversion of layer mask is not supported");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (channel_type > PSP_CHANNEL_BLUE)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Invalid channel type %d in channel information chunk"),
|
|
|
|
|
channel_type);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IFDBG(2) g_message ("channel: %s %s %d (%d) bytes %d bytespp",
|
|
|
|
|
bitmap_type_name (bitmap_type),
|
|
|
|
|
channel_type_name (channel_type),
|
|
|
|
|
uncompressed_len, compressed_len,
|
|
|
|
|
bytespp);
|
|
|
|
|
|
2020-09-11 23:43:48 -04:00
|
|
|
|
if (bitmap_type == PSP_DIB_TRANS_MASK || channel_type == PSP_CHANNEL_COMPOSITE)
|
2020-09-08 22:14:50 -04:00
|
|
|
|
offset = bytespp - ia->bytes_per_sample;
|
2020-08-18 15:59:58 -04:00
|
|
|
|
else
|
2020-09-08 22:14:50 -04:00
|
|
|
|
offset = (channel_type - PSP_CHANNEL_RED) * ia->bytes_per_sample;
|
2020-08-18 15:59:58 -04:00
|
|
|
|
|
|
|
|
|
if (!null_layer)
|
|
|
|
|
{
|
|
|
|
|
if (try_fseek (f, channel_start + chunk_len, SEEK_SET, error) < 0)
|
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (read_channel_data (f, ia, pixels, bytespp, offset,
|
|
|
|
|
buffer, compressed_len, error) == -1)
|
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (try_fseek (f, channel_start + channel_total_len, SEEK_SET, error) < 0)
|
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-04-19 17:59:32 +00:00
|
|
|
|
|
2020-08-18 15:59:58 -04:00
|
|
|
|
gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, width, height), 0,
|
|
|
|
|
NULL, pixel, GEGL_AUTO_ROWSTRIDE);
|
2008-04-19 17:59:32 +00:00
|
|
|
|
|
2020-08-18 15:59:58 -04:00
|
|
|
|
g_object_unref (buffer);
|
2008-04-19 17:59:32 +00:00
|
|
|
|
|
2020-08-18 15:59:58 -04:00
|
|
|
|
g_free (pixels);
|
|
|
|
|
g_free (pixel);
|
|
|
|
|
if (psp_ver_major >= 4)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
2020-08-18 15:59:58 -04:00
|
|
|
|
if (try_fseek (f, sub_block_start + sub_total_len, SEEK_SET, error) < 0)
|
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Can't handle this type of layer, skip the data so we can read the next layer. */
|
|
|
|
|
if (psp_ver_major >= 4)
|
|
|
|
|
{
|
|
|
|
|
if (try_fseek (f, sub_block_start + sub_total_len, SEEK_SET, error) < 0)
|
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2008-04-19 17:59:32 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
1999-08-20 00:47:05 +00:00
|
|
|
|
}
|
2007-07-05 17:03:27 +00:00
|
|
|
|
|
2017-12-21 20:42:41 +01:00
|
|
|
|
if (try_fseek (f, block_start + total_len, SEEK_SET, error) < 0)
|
1999-08-20 23:52:19 +00:00
|
|
|
|
{
|
2019-08-24 19:52:36 +02:00
|
|
|
|
return NULL;
|
1999-08-20 23:52:19 +00:00
|
|
|
|
}
|
1999-08-21 03:06:07 +00:00
|
|
|
|
|
2019-08-24 19:52:36 +02:00
|
|
|
|
return layer;
|
1999-08-20 00:47:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-01-25 17:46:56 +00:00
|
|
|
|
static gint
|
2017-12-21 20:42:41 +01:00
|
|
|
|
read_tube_block (FILE *f,
|
2019-08-24 19:52:36 +02:00
|
|
|
|
GimpImage *image,
|
2017-12-21 20:42:41 +01:00
|
|
|
|
guint total_len,
|
|
|
|
|
PSPimage *ia,
|
|
|
|
|
GError **error)
|
1999-08-22 01:54:06 +00:00
|
|
|
|
{
|
2008-09-17 08:37:24 +00:00
|
|
|
|
guint16 version;
|
|
|
|
|
guchar name[514];
|
|
|
|
|
guint32 step_size, column_count, row_count, cell_count;
|
|
|
|
|
guint32 placement_mode, selection_mode;
|
2020-08-26 19:09:28 -04:00
|
|
|
|
guint32 chunk_len;
|
2008-09-17 08:37:24 +00:00
|
|
|
|
gint i;
|
|
|
|
|
GimpPixPipeParams params;
|
|
|
|
|
GimpParasite *pipe_parasite;
|
|
|
|
|
gchar *parasite_text;
|
1999-08-22 01:54:06 +00:00
|
|
|
|
|
2000-05-26 22:28:40 +00:00
|
|
|
|
gimp_pixpipe_params_init (¶ms);
|
1999-09-02 00:01:41 +00:00
|
|
|
|
|
2020-08-26 19:09:28 -04:00
|
|
|
|
if (psp_ver_major >= 4)
|
1999-08-22 01:54:06 +00:00
|
|
|
|
{
|
2020-08-26 19:09:28 -04:00
|
|
|
|
name[0] = 0;
|
|
|
|
|
if (fread (&chunk_len, 4, 1, f) < 1
|
|
|
|
|
|| fread (&version, 2, 1, f) < 1
|
|
|
|
|
|| fread (&step_size, 4, 1, f) < 1
|
|
|
|
|
|| fread (&column_count, 4, 1, f) < 1
|
|
|
|
|
|| fread (&row_count, 4, 1, f) < 1
|
|
|
|
|
|| fread (&cell_count, 4, 1, f) < 1
|
|
|
|
|
|| fread (&placement_mode, 4, 1, f) < 1
|
|
|
|
|
|| fread (&selection_mode, 4, 1, f) < 1)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading tube data chunk"));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
chunk_len = 0;
|
|
|
|
|
if (fread (&version, 2, 1, f) < 1
|
|
|
|
|
|| fread (name, 513, 1, f) < 1
|
|
|
|
|
|| fread (&step_size, 4, 1, f) < 1
|
|
|
|
|
|| fread (&column_count, 4, 1, f) < 1
|
|
|
|
|
|| fread (&row_count, 4, 1, f) < 1
|
|
|
|
|
|| fread (&cell_count, 4, 1, f) < 1
|
|
|
|
|
|| fread (&placement_mode, 4, 1, f) < 1
|
|
|
|
|
|| fread (&selection_mode, 4, 1, f) < 1)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading tube data chunk"));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
name[513] = 0;
|
1999-08-22 01:54:06 +00:00
|
|
|
|
}
|
2007-07-05 17:03:27 +00:00
|
|
|
|
|
1999-08-22 01:54:06 +00:00
|
|
|
|
version = GUINT16_FROM_LE (version);
|
1999-09-02 00:01:41 +00:00
|
|
|
|
params.step = GUINT32_FROM_LE (step_size);
|
|
|
|
|
params.cols = GUINT32_FROM_LE (column_count);
|
|
|
|
|
params.rows = GUINT32_FROM_LE (row_count);
|
|
|
|
|
params.ncells = GUINT32_FROM_LE (cell_count);
|
1999-08-22 01:54:06 +00:00
|
|
|
|
placement_mode = GUINT32_FROM_LE (placement_mode);
|
|
|
|
|
selection_mode = GUINT32_FROM_LE (selection_mode);
|
|
|
|
|
|
2007-09-10 13:37:39 +00:00
|
|
|
|
for (i = 1; i < params.cols; i++)
|
2019-08-24 19:52:36 +02:00
|
|
|
|
gimp_image_add_vguide (image, (ia->width * i)/params.cols);
|
2007-09-10 13:37:39 +00:00
|
|
|
|
for (i = 1; i < params.rows; i++)
|
2019-08-24 19:52:36 +02:00
|
|
|
|
gimp_image_add_hguide (image, (ia->height * i)/params.rows);
|
1999-08-22 01:54:06 +00:00
|
|
|
|
|
1999-08-24 05:30:14 +00:00
|
|
|
|
/* We use a parasite to pass in the tube (pipe) parameters in
|
1999-08-27 06:25:22 +00:00
|
|
|
|
* case we will have any use of those, for instance in the gpb
|
2016-02-16 02:35:43 +01:00
|
|
|
|
* plug-in that exports a GIMP image pipe.
|
1999-08-22 01:54:06 +00:00
|
|
|
|
*/
|
1999-09-02 00:01:41 +00:00
|
|
|
|
params.dim = 1;
|
|
|
|
|
params.cellwidth = ia->width / params.cols;
|
|
|
|
|
params.cellheight = ia->height / params.rows;
|
|
|
|
|
params.placement = (placement_mode == tpmRandom ? "random" :
|
2008-04-19 17:59:32 +00:00
|
|
|
|
(placement_mode == tpmConstant ? "constant" :
|
|
|
|
|
"default"));
|
1999-09-02 00:01:41 +00:00
|
|
|
|
params.rank[0] = params.ncells;
|
|
|
|
|
params.selection[0] = (selection_mode == tsmRandom ? "random" :
|
2008-04-19 17:59:32 +00:00
|
|
|
|
(selection_mode == tsmIncremental ? "incremental" :
|
|
|
|
|
(selection_mode == tsmAngular ? "angular" :
|
|
|
|
|
(selection_mode == tsmPressure ? "pressure" :
|
|
|
|
|
(selection_mode == tsmVelocity ? "velocity" :
|
|
|
|
|
"default")))));
|
2000-05-26 22:28:40 +00:00
|
|
|
|
parasite_text = gimp_pixpipe_params_build (¶ms);
|
1999-09-02 00:01:41 +00:00
|
|
|
|
|
|
|
|
|
IFDBG(2) g_message ("parasite: %s", parasite_text);
|
|
|
|
|
|
2000-05-26 22:28:40 +00:00
|
|
|
|
pipe_parasite = gimp_parasite_new ("gimp-brush-pipe-parameters",
|
2008-04-19 17:59:32 +00:00
|
|
|
|
GIMP_PARASITE_PERSISTENT,
|
|
|
|
|
strlen (parasite_text) + 1, parasite_text);
|
2019-08-24 19:52:36 +02:00
|
|
|
|
gimp_image_attach_parasite (image, pipe_parasite);
|
2000-05-26 22:28:40 +00:00
|
|
|
|
gimp_parasite_free (pipe_parasite);
|
1999-08-22 01:54:06 +00:00
|
|
|
|
g_free (parasite_text);
|
2000-01-08 10:55:05 +00:00
|
|
|
|
|
|
|
|
|
return 0;
|
1999-08-22 01:54:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2008-09-17 08:37:24 +00:00
|
|
|
|
static const gchar *
|
2000-01-25 17:46:56 +00:00
|
|
|
|
compression_name (gint compression)
|
1999-08-21 03:06:07 +00:00
|
|
|
|
{
|
|
|
|
|
switch (compression)
|
|
|
|
|
{
|
|
|
|
|
case PSP_COMP_NONE:
|
|
|
|
|
return "no compression";
|
|
|
|
|
case PSP_COMP_RLE:
|
|
|
|
|
return "RLE";
|
|
|
|
|
case PSP_COMP_LZ77:
|
|
|
|
|
return "LZ77";
|
|
|
|
|
}
|
|
|
|
|
g_assert_not_reached ();
|
2000-01-08 10:55:05 +00:00
|
|
|
|
|
|
|
|
|
return NULL;
|
1999-08-21 03:06:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-07 13:25:08 +00:00
|
|
|
|
static gint
|
|
|
|
|
read_selection_block (FILE *f,
|
|
|
|
|
GimpImage *image,
|
|
|
|
|
guint total_len,
|
|
|
|
|
PSPimage *ia,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
|
|
|
|
gsize current_location;
|
|
|
|
|
gsize file_size;
|
|
|
|
|
guint32 chunk_size;
|
|
|
|
|
guint32 rect[4];
|
|
|
|
|
|
|
|
|
|
current_location = ftell (f);
|
|
|
|
|
fseek (f, 0, SEEK_END);
|
|
|
|
|
file_size = ftell (f);
|
|
|
|
|
fseek (f, current_location, SEEK_SET);
|
|
|
|
|
|
|
|
|
|
if (fread (&chunk_size, 4, 1, f) < 1)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading selection chunk"));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
chunk_size = GUINT32_FROM_LE (chunk_size);
|
|
|
|
|
|
|
|
|
|
current_location = ftell (f);
|
|
|
|
|
if (chunk_size > (file_size - current_location))
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Invalid selection chunk size"));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fread (&rect, 16, 1, f) < 1)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading selection chunk"));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
swab_rect (rect);
|
|
|
|
|
gimp_image_select_rectangle (image, GIMP_CHANNEL_OP_ADD,
|
|
|
|
|
rect[0], rect[1],
|
|
|
|
|
rect[2] - rect[0],
|
|
|
|
|
rect[3] - rect[1]);
|
|
|
|
|
|
|
|
|
|
/* The file format specifies multiple selections, but
|
|
|
|
|
* as of PSP 8 they only allow one. Skipping the remaining
|
|
|
|
|
* information in the block. */
|
|
|
|
|
total_len -= sizeof (guint32) + sizeof (rect);
|
|
|
|
|
if (try_fseek (f, total_len, SEEK_SET, error) < 0)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading end of selection chunk"));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-14 23:52:02 +00:00
|
|
|
|
static gint
|
2025-05-29 13:10:15 +00:00
|
|
|
|
read_extended_block (FILE *f,
|
|
|
|
|
GimpImage *image,
|
|
|
|
|
guint total_len,
|
|
|
|
|
PSPimage *ia,
|
|
|
|
|
GimpMetadata *metadata,
|
|
|
|
|
GError **error)
|
2023-10-14 23:52:02 +00:00
|
|
|
|
{
|
|
|
|
|
guchar header[4];
|
|
|
|
|
guint16 field_type;
|
|
|
|
|
guint32 field_length;
|
|
|
|
|
|
|
|
|
|
while (total_len > 0)
|
|
|
|
|
{
|
|
|
|
|
if (fread (header, 4, 1, f) < 1 ||
|
|
|
|
|
fread (&field_type, 2, 1, f) < 1 ||
|
|
|
|
|
fread (&field_length, 4, 1, f) < 1)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading extended block chunk header"));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (memcmp (header, "~FL\0", 4) != 0)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Invalid extended block chunk header"));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
total_len -= sizeof (header) + sizeof (field_type) + sizeof (field_length);
|
|
|
|
|
|
|
|
|
|
field_type = GUINT16_FROM_LE (field_type);
|
|
|
|
|
field_length = GUINT32_FROM_LE (field_length);
|
|
|
|
|
|
|
|
|
|
if (field_length > total_len)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Invalid extended block chunk size"));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (field_type)
|
|
|
|
|
{
|
|
|
|
|
case PSP_XDATA_GRID:
|
|
|
|
|
{
|
2023-12-21 15:46:15 +09:00
|
|
|
|
GeglColor *color;
|
|
|
|
|
guchar rgb[4];
|
|
|
|
|
guint32 h_spacing;
|
|
|
|
|
guint32 v_spacing;
|
|
|
|
|
guint16 unit; /* Unused */
|
2023-10-14 23:52:02 +00:00
|
|
|
|
|
|
|
|
|
if (field_length != 14 ||
|
|
|
|
|
fread (rgb, 4, 1, f) < 1 ||
|
|
|
|
|
fread (&h_spacing, 4, 1, f) < 1 ||
|
|
|
|
|
fread (&v_spacing, 4, 1, f) < 1 ||
|
|
|
|
|
fread (&unit, 2, 1, f) < 1)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading extended chunk grid data"));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
h_spacing = GUINT32_FROM_LE (h_spacing);
|
|
|
|
|
v_spacing = GUINT32_FROM_LE (v_spacing);
|
|
|
|
|
gimp_image_grid_set_spacing (image, h_spacing, v_spacing);
|
|
|
|
|
|
2023-12-21 15:46:15 +09:00
|
|
|
|
color = gegl_color_new (NULL);
|
|
|
|
|
gegl_color_set_pixel (color, babl_format ("R'G'B' u8"), rgb);
|
|
|
|
|
gimp_image_grid_set_foreground_color (image, color);
|
|
|
|
|
gimp_image_grid_set_background_color (image, color);
|
|
|
|
|
g_object_unref (color);
|
2023-10-14 23:52:02 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PSP_XDATA_GUIDE:
|
|
|
|
|
{
|
|
|
|
|
guchar rgb[4]; /* Unused */
|
|
|
|
|
guint32 offset;
|
|
|
|
|
guint16 orientation;
|
|
|
|
|
|
|
|
|
|
if (field_length != 10 ||
|
|
|
|
|
fread (rgb, 4, 1, f) < 1 ||
|
|
|
|
|
fread (&offset, 4, 1, f) < 1 ||
|
|
|
|
|
fread (&orientation, 2, 1, f) < 1)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading extended chunk guide data"));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
offset = GUINT32_FROM_LE (offset);
|
|
|
|
|
orientation = GUINT16_FROM_LE (orientation);
|
|
|
|
|
|
|
|
|
|
if (orientation == keHorizontalGuide)
|
|
|
|
|
{
|
|
|
|
|
gimp_image_add_hguide (image, offset);
|
|
|
|
|
}
|
|
|
|
|
else if (orientation == keVerticalGuide)
|
|
|
|
|
{
|
|
|
|
|
gimp_image_add_vguide (image, offset);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Invalid guide orientation"));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2025-05-29 13:10:15 +00:00
|
|
|
|
case PSP_XDATA_EXIF:
|
|
|
|
|
{
|
|
|
|
|
gchar exif_header[6];
|
|
|
|
|
guchar *data;
|
|
|
|
|
|
|
|
|
|
data = g_malloc0 (field_length - 6);
|
|
|
|
|
if (fread (exif_header, 6, 1, f) < 1 ||
|
|
|
|
|
fread (data, (field_length - 6), 1, f) < 1)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading extended chunk EXIF data"));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! gexiv2_metadata_open_buf (GEXIV2_METADATA (metadata),
|
|
|
|
|
data, (field_length - 6),
|
|
|
|
|
error))
|
|
|
|
|
{
|
|
|
|
|
g_free (data);
|
|
|
|
|
g_printerr ("%s: Failed to set EXIF metadata: %s\n", G_STRFUNC,
|
|
|
|
|
(*error)->message);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
g_free (data);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PSP_XDATA_XMP:
|
|
|
|
|
{
|
|
|
|
|
guchar *data;
|
|
|
|
|
|
|
|
|
|
data = g_malloc0 (field_length);
|
|
|
|
|
if (fread (data, (field_length), 1, f) < 1)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading extended chunk XMP data"));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! gimp_metadata_set_from_xmp (metadata, data, field_length,
|
|
|
|
|
error))
|
|
|
|
|
{
|
|
|
|
|
g_free (data);
|
|
|
|
|
g_printerr ("%s: Failed to set XMP metadata: %s\n", G_STRFUNC,
|
|
|
|
|
(*error)->message);
|
|
|
|
|
g_clear_error (error);
|
|
|
|
|
}
|
|
|
|
|
g_free (data);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2023-10-14 23:52:02 +00:00
|
|
|
|
/* Not yet implemented */
|
|
|
|
|
case PSP_XDATA_TRNS_INDEX:
|
|
|
|
|
case PSP_XDATA_IPTC:
|
|
|
|
|
default:
|
|
|
|
|
if (try_fseek (f, field_length, SEEK_CUR, error) < 0)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading extended block chunk"));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
total_len -= field_length;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-25 02:29:51 +00:00
|
|
|
|
static gint
|
|
|
|
|
read_colorprofile_block (FILE *f,
|
|
|
|
|
GimpImage *image,
|
|
|
|
|
guint total_len,
|
|
|
|
|
PSPimage *ia,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
|
|
|
|
guint psp_header_size;
|
|
|
|
|
guint icc_profile_size;
|
|
|
|
|
gchar *icc_profile;
|
|
|
|
|
GimpColorProfile *profile;
|
|
|
|
|
|
|
|
|
|
if (fread (&psp_header_size, 4, 1, f) < 1)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading colorprofile chunk"));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
psp_header_size = GUINT32_FROM_LE (psp_header_size);
|
|
|
|
|
|
|
|
|
|
/* The next part is a 2 byte length value, followed by PSP's
|
|
|
|
|
* internal name for the profile. We'll skip most of it,
|
|
|
|
|
* except for the last 4 bytes which is the profile size */
|
|
|
|
|
if (try_fseek (f, psp_header_size - 8, SEEK_CUR, error) < 0 ||
|
|
|
|
|
fread (&icc_profile_size, 4, 1, f) < 1)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading colorprofile chunk"));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
icc_profile_size = GUINT32_FROM_LE (icc_profile_size);
|
|
|
|
|
|
|
|
|
|
/* The rest of the code references load_resource_1039 ()
|
|
|
|
|
* in psd-image-res-load.c */
|
|
|
|
|
icc_profile = g_malloc (icc_profile_size);
|
|
|
|
|
if (fread (icc_profile, icc_profile_size, 1, f) < 1)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading colorprofile chunk"));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
profile = gimp_color_profile_new_from_icc_profile ((guint8 *) icc_profile,
|
|
|
|
|
icc_profile_size,
|
|
|
|
|
NULL);
|
|
|
|
|
gimp_image_set_color_profile (image, profile);
|
|
|
|
|
|
|
|
|
|
g_object_unref (profile);
|
|
|
|
|
g_free (icc_profile);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-04-21 16:31:33 +00:00
|
|
|
|
/* The main function for loading PSP-images
|
|
|
|
|
*/
|
2019-08-24 19:52:36 +02:00
|
|
|
|
static GimpImage *
|
2025-05-29 13:10:15 +00:00
|
|
|
|
load_image (GFile *file,
|
|
|
|
|
GimpMetadata *metadata,
|
|
|
|
|
GError **error)
|
1999-08-20 00:47:05 +00:00
|
|
|
|
{
|
2019-09-14 16:20:12 +00:00
|
|
|
|
FILE *f;
|
|
|
|
|
GStatBuf st;
|
|
|
|
|
char buf[32];
|
|
|
|
|
PSPimage ia;
|
|
|
|
|
guint32 block_init_len, block_total_len;
|
|
|
|
|
long block_start;
|
2000-01-08 10:55:05 +00:00
|
|
|
|
PSPBlockID id = -1;
|
2019-09-14 16:20:12 +00:00
|
|
|
|
gint block_number;
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
2019-08-24 19:52:36 +02:00
|
|
|
|
GimpImage *image = NULL;
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
2021-10-01 18:56:12 +02:00
|
|
|
|
if (g_stat (g_file_peek_path (file), &st) == -1)
|
2019-09-11 21:48:34 +02:00
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
2021-10-01 18:56:12 +02:00
|
|
|
|
f = g_fopen (g_file_peek_path (file), "rb");
|
2019-09-11 21:48:34 +02:00
|
|
|
|
|
|
|
|
|
if (! f)
|
2003-06-13 14:37:00 +00:00
|
|
|
|
{
|
2008-08-18 06:53:21 +00:00
|
|
|
|
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
|
|
|
|
_("Could not open '%s' for reading: %s"),
|
2019-09-11 21:48:34 +02:00
|
|
|
|
gimp_file_get_utf8_name (file), g_strerror (errno));
|
2019-08-24 19:52:36 +02:00
|
|
|
|
return NULL;
|
2003-06-13 14:37:00 +00:00
|
|
|
|
}
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
2008-04-21 16:31:33 +00:00
|
|
|
|
/* Read the PSP File Header and determine file version */
|
1999-08-20 00:47:05 +00:00
|
|
|
|
if (fread (buf, 32, 1, f) < 1
|
2007-09-11 22:48:03 +00:00
|
|
|
|
|| fread (&psp_ver_major, 2, 1, f) < 1
|
|
|
|
|
|| fread (&psp_ver_minor, 2, 1, f) < 1)
|
1999-08-20 00:47:05 +00:00
|
|
|
|
{
|
2017-12-21 20:32:17 +01:00
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Error reading file header."));
|
2007-07-05 17:26:32 +00:00
|
|
|
|
goto error;
|
1999-08-20 00:47:05 +00:00
|
|
|
|
}
|
2007-07-05 17:03:27 +00:00
|
|
|
|
|
1999-08-20 00:47:05 +00:00
|
|
|
|
if (memcmp (buf, "Paint Shop Pro Image File\n\032\0\0\0\0\0", 32) != 0)
|
|
|
|
|
{
|
2017-12-21 20:32:17 +01:00
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Incorrect file signature."));
|
2007-07-05 17:26:32 +00:00
|
|
|
|
goto error;
|
1999-08-20 00:47:05 +00:00
|
|
|
|
}
|
1999-08-20 23:52:19 +00:00
|
|
|
|
|
2007-09-11 22:48:03 +00:00
|
|
|
|
psp_ver_major = GUINT16_FROM_LE (psp_ver_major);
|
|
|
|
|
psp_ver_minor = GUINT16_FROM_LE (psp_ver_minor);
|
2007-07-05 17:03:27 +00:00
|
|
|
|
|
2020-08-25 15:04:24 -04:00
|
|
|
|
/* We don't have the documentation for file format versions before 3.0,
|
|
|
|
|
* but newer versions should be mostly backwards compatible so that
|
|
|
|
|
* we can still read the image and skip unknown parts safely.
|
1999-08-20 23:52:19 +00:00
|
|
|
|
*/
|
2020-08-25 15:04:24 -04:00
|
|
|
|
if (psp_ver_major < 3)
|
1999-08-20 23:52:19 +00:00
|
|
|
|
{
|
2017-12-21 20:32:17 +01:00
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Unsupported PSP file format version %d.%d."),
|
|
|
|
|
psp_ver_major, psp_ver_minor);
|
2007-07-05 17:26:32 +00:00
|
|
|
|
goto error;
|
1999-08-20 00:47:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read all the blocks */
|
|
|
|
|
block_number = 0;
|
|
|
|
|
|
2003-06-13 14:37:00 +00:00
|
|
|
|
IFDBG(3) g_message ("size = %d", (int)st.st_size);
|
1999-08-20 00:47:05 +00:00
|
|
|
|
while (ftell (f) != st.st_size
|
2008-04-19 17:59:32 +00:00
|
|
|
|
&& (id = read_block_header (f, &block_init_len,
|
2017-12-21 20:42:41 +01:00
|
|
|
|
&block_total_len, error)) != -1)
|
1999-08-20 00:47:05 +00:00
|
|
|
|
{
|
1999-08-20 23:52:19 +00:00
|
|
|
|
block_start = ftell (f);
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
2017-12-20 16:44:20 +01:00
|
|
|
|
if (block_start + block_total_len > st.st_size)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Could not open '%s' for reading: %s"),
|
2019-09-11 21:48:34 +02:00
|
|
|
|
gimp_file_get_utf8_name (file),
|
2017-12-20 16:44:20 +01:00
|
|
|
|
_("invalid block size"));
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
1999-08-20 00:47:05 +00:00
|
|
|
|
if (id == PSP_IMAGE_BLOCK)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (block_number != 0)
|
|
|
|
|
{
|
2017-12-21 20:32:17 +01:00
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Duplicate General Image Attributes block."));
|
2007-07-05 17:26:32 +00:00
|
|
|
|
goto error;
|
2008-04-19 17:59:32 +00:00
|
|
|
|
}
|
|
|
|
|
if (read_general_image_attribute_block (f, block_init_len,
|
2020-08-18 15:59:58 -04:00
|
|
|
|
block_total_len, &ia, error) == -1)
|
2007-07-05 17:26:32 +00:00
|
|
|
|
{
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
2008-04-19 17:59:32 +00:00
|
|
|
|
IFDBG(2) g_message ("%d dpi %dx%d %s",
|
|
|
|
|
(int) ia.resolution,
|
|
|
|
|
ia.width, ia.height,
|
|
|
|
|
compression_name (ia.compression));
|
1999-08-20 23:52:19 +00:00
|
|
|
|
|
2020-09-08 22:14:50 -04:00
|
|
|
|
image = gimp_image_new_with_precision (ia.width, ia.height,
|
|
|
|
|
ia.base_type, ia.precision);
|
2019-08-24 19:52:36 +02:00
|
|
|
|
if (! image)
|
2007-07-05 17:03:27 +00:00
|
|
|
|
{
|
2007-07-05 17:26:32 +00:00
|
|
|
|
goto error;
|
2007-07-05 17:03:27 +00:00
|
|
|
|
}
|
1999-08-20 23:52:19 +00:00
|
|
|
|
|
2019-08-24 19:52:36 +02:00
|
|
|
|
gimp_image_set_resolution (image, ia.resolution, ia.resolution);
|
2008-04-19 17:59:32 +00:00
|
|
|
|
}
|
1999-08-20 00:47:05 +00:00
|
|
|
|
else
|
2008-04-19 17:59:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (block_number == 0)
|
|
|
|
|
{
|
2017-12-21 20:32:17 +01:00
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Missing General Image Attributes block."));
|
2007-07-05 17:03:27 +00:00
|
|
|
|
goto error;
|
2008-04-19 17:59:32 +00:00
|
|
|
|
}
|
2007-07-05 17:03:27 +00:00
|
|
|
|
|
2008-04-19 17:59:32 +00:00
|
|
|
|
switch (id)
|
|
|
|
|
{
|
|
|
|
|
case PSP_CREATOR_BLOCK:
|
2019-08-24 19:52:36 +02:00
|
|
|
|
if (read_creator_block (f, image, block_total_len, &ia, error) == -1)
|
2007-07-05 17:03:27 +00:00
|
|
|
|
goto error;
|
2008-04-19 17:59:32 +00:00
|
|
|
|
break;
|
1999-08-22 01:54:06 +00:00
|
|
|
|
|
2008-04-19 17:59:32 +00:00
|
|
|
|
case PSP_COLOR_BLOCK:
|
2020-09-11 23:43:48 -04:00
|
|
|
|
if (read_color_block (f, image, block_total_len, &ia, error) == -1)
|
|
|
|
|
goto error;
|
|
|
|
|
break;
|
1999-08-22 01:54:06 +00:00
|
|
|
|
|
2008-04-19 17:59:32 +00:00
|
|
|
|
case PSP_LAYER_START_BLOCK:
|
2019-08-24 19:52:36 +02:00
|
|
|
|
if (! read_layer_block (f, image, block_total_len, &ia, error))
|
2008-04-19 17:59:32 +00:00
|
|
|
|
goto error;
|
|
|
|
|
break;
|
1999-08-22 01:54:06 +00:00
|
|
|
|
|
2008-04-19 17:59:32 +00:00
|
|
|
|
case PSP_SELECTION_BLOCK:
|
2023-10-07 13:25:08 +00:00
|
|
|
|
if (read_selection_block (f, image, block_total_len, &ia,
|
|
|
|
|
error) == -1)
|
|
|
|
|
goto error;
|
|
|
|
|
break;
|
1999-08-22 01:54:06 +00:00
|
|
|
|
|
2008-04-19 17:59:32 +00:00
|
|
|
|
case PSP_ALPHA_BANK_BLOCK:
|
|
|
|
|
break; /* Not yet implemented */
|
1999-08-22 01:54:06 +00:00
|
|
|
|
|
2008-04-19 17:59:32 +00:00
|
|
|
|
case PSP_THUMBNAIL_BLOCK:
|
|
|
|
|
break; /* No use for it */
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
2008-04-19 17:59:32 +00:00
|
|
|
|
case PSP_EXTENDED_DATA_BLOCK:
|
2023-10-14 23:52:02 +00:00
|
|
|
|
if (read_extended_block (f, image, block_total_len, &ia,
|
2025-05-29 13:10:15 +00:00
|
|
|
|
metadata, error) == -1)
|
2023-10-14 23:52:02 +00:00
|
|
|
|
goto error;
|
|
|
|
|
break;
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
2008-04-19 17:59:32 +00:00
|
|
|
|
case PSP_TUBE_BLOCK:
|
2019-08-24 19:52:36 +02:00
|
|
|
|
if (read_tube_block (f, image, block_total_len, &ia, error) == -1)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
goto error;
|
|
|
|
|
break;
|
|
|
|
|
|
2008-04-21 16:31:33 +00:00
|
|
|
|
case PSP_COMPOSITE_IMAGE_BANK_BLOCK:
|
|
|
|
|
break; /* Not yet implemented */
|
|
|
|
|
|
2020-09-05 23:25:35 -04:00
|
|
|
|
case PSP_TABLE_BANK_BLOCK:
|
|
|
|
|
break; /* Not yet implemented */
|
|
|
|
|
|
|
|
|
|
case PSP_BRUSH_BLOCK:
|
|
|
|
|
break; /* Not yet implemented */
|
|
|
|
|
|
|
|
|
|
case PSP_ART_MEDIA_BLOCK:
|
|
|
|
|
case PSP_ART_MEDIA_MAP_BLOCK:
|
|
|
|
|
case PSP_ART_MEDIA_TILE_BLOCK:
|
|
|
|
|
case PSP_ART_MEDIA_TEXTURE_BLOCK:
|
|
|
|
|
break; /* Not yet implemented */
|
|
|
|
|
|
|
|
|
|
case PSP_COLORPROFILE_BLOCK:
|
2023-08-25 02:29:51 +00:00
|
|
|
|
if (read_colorprofile_block (f, image, block_total_len, &ia,
|
|
|
|
|
error) == -1)
|
|
|
|
|
goto error;
|
|
|
|
|
break;
|
2020-09-05 23:25:35 -04:00
|
|
|
|
|
2008-04-19 17:59:32 +00:00
|
|
|
|
case PSP_LAYER_BLOCK:
|
|
|
|
|
case PSP_CHANNEL_BLOCK:
|
|
|
|
|
case PSP_ALPHA_CHANNEL_BLOCK:
|
2008-04-21 16:31:33 +00:00
|
|
|
|
case PSP_ADJUSTMENT_EXTENSION_BLOCK:
|
|
|
|
|
case PSP_VECTOR_EXTENSION_BLOCK:
|
|
|
|
|
case PSP_SHAPE_BLOCK:
|
|
|
|
|
case PSP_PAINTSTYLE_BLOCK:
|
|
|
|
|
case PSP_COMPOSITE_ATTRIBUTES_BLOCK:
|
|
|
|
|
case PSP_JPEG_BLOCK:
|
2020-09-05 23:25:35 -04:00
|
|
|
|
case PSP_LINESTYLE_BLOCK:
|
|
|
|
|
case PSP_TABLE_BLOCK:
|
|
|
|
|
case PSP_PAPER_BLOCK:
|
|
|
|
|
case PSP_PATTERN_BLOCK:
|
|
|
|
|
case PSP_GRADIENT_BLOCK:
|
|
|
|
|
case PSP_GROUP_EXTENSION_BLOCK:
|
|
|
|
|
case PSP_MASK_EXTENSION_BLOCK:
|
|
|
|
|
case PSP_RASTER_EXTENSION_BLOCK:
|
2008-04-19 17:59:32 +00:00
|
|
|
|
g_message ("Sub-block %s should not occur "
|
|
|
|
|
"at main level of file",
|
|
|
|
|
block_name (id));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
g_message ("Unrecognized block id %d", id);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
1999-08-20 00:47:05 +00:00
|
|
|
|
|
1999-08-20 23:52:19 +00:00
|
|
|
|
if (block_start + block_total_len >= st.st_size)
|
2008-04-19 17:59:32 +00:00
|
|
|
|
break;
|
1999-08-20 23:52:19 +00:00
|
|
|
|
|
2017-12-21 20:42:41 +01:00
|
|
|
|
if (try_fseek (f, block_start + block_total_len, SEEK_SET, error) < 0)
|
2007-07-05 17:03:27 +00:00
|
|
|
|
goto error;
|
|
|
|
|
|
1999-08-20 00:47:05 +00:00
|
|
|
|
block_number++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (id == -1)
|
|
|
|
|
{
|
2007-07-05 17:03:27 +00:00
|
|
|
|
error:
|
1999-08-20 00:47:05 +00:00
|
|
|
|
fclose (f);
|
2019-08-24 19:52:36 +02:00
|
|
|
|
if (image)
|
|
|
|
|
gimp_image_delete (image);
|
|
|
|
|
return NULL;
|
1999-08-20 00:47:05 +00:00
|
|
|
|
}
|
1999-08-20 23:52:19 +00:00
|
|
|
|
|
1999-08-20 00:47:05 +00:00
|
|
|
|
fclose (f);
|
|
|
|
|
|
2019-08-24 19:52:36 +02:00
|
|
|
|
return image;
|
1999-08-20 00:47:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gint
|
2024-04-13 15:10:25 +00:00
|
|
|
|
export_image (GFile *file,
|
|
|
|
|
GimpImage *image,
|
|
|
|
|
gint n_drawables,
|
2024-04-30 13:50:24 +00:00
|
|
|
|
GList *drawables,
|
2024-04-13 15:10:25 +00:00
|
|
|
|
GObject *config,
|
|
|
|
|
GError **error)
|
1999-08-20 00:47:05 +00:00
|
|
|
|
{
|
2017-12-21 20:32:17 +01:00
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
|
_("Exporting not implemented yet."));
|
1999-08-21 19:00:41 +00:00
|
|
|
|
|
2000-01-25 17:46:56 +00:00
|
|
|
|
return FALSE;
|
1999-08-20 00:47:05 +00:00
|
|
|
|
}
|