2006-12-10 05:33:38 +08:00
|
|
|
/* GIMP - The GNU Image Manipulation Program
|
1998-07-09 13:31:06 +08:00
|
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
|
|
*
|
2007-06-04 18:43:31 +08:00
|
|
|
* gimpbrush-load.c
|
|
|
|
*
|
2009-01-18 06:28:01 +08:00
|
|
|
* This program is free software: you can redistribute it and/or modify
|
1998-07-09 13:31:06 +08:00
|
|
|
* it under the terms of the GNU General Public License as published by
|
2009-01-18 06:28:01 +08:00
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
1998-07-09 13:31:06 +08: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
|
2009-01-18 06:28:01 +08:00
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
1998-07-09 13:31:06 +08:00
|
|
|
*/
|
2000-12-17 05:37:03 +08:00
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
2013-10-15 07:58:39 +08:00
|
|
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
|
|
|
#include <gegl.h>
|
Jens Lautenbacher <jtl@gimp.org>
2000-12-18 Sven Neumann <sven@gimp.org>
Jens Lautenbacher <jtl@gimp.org>
* app/Makefile.am
* app/gimpbrushlistP.h
* app/gimpbrushpipeP.h
* app/gimpobjectP.h: removed these three files
* app/parasitelistP.h
* app/channels_dialog.c
* app/docindex.c
* app/gimpdrawable.c
* app/gimpdrawableP.h
* app/gimpimage.c
* app/gimpimageP.h
* app/gimplist.[ch]
* app/gimpobject.c
* app/gimpobject.h
* app/gimpsetP.h: changed according to header removal
* app/airbrush.c
* app/brush_select.[ch]
* app/brushes_cmds.c
* app/gimpbrush.[ch]
* app/gimpbrushgenerated.[ch]
* app/gimpbrushlist.[ch]
* app/gimpbrushpipe.[ch]
* app/gimpcontextpreview.c
* app/paint_core.c
* app/paintbrush.c
* app/pencil.c
* tools/pdbgen/pdb/brushes.pdb: Big Brushes Cleanup.
The GimpBrush* object hierarchy and the file formats were broken by
"design". This made it overly difficult to read and write pixmap
brushes and brush pipes, leading to the situation that The GIMP was
not able to read it's very own file formats. Since the GimpBrush
format did support arbitrary color depths, the introduction of a
file format for pixmap brushes was unnecessary.
The GimpBrushPixmap object is dead. GimpBrush has an additional
pixmap temp_buf and handles pixmap brushes transparently. The file
format of pixmap brushes is not any longer a grayscale brush plus
a pattern, but a simple brush with RGBA data. The old brushes can
still be loaded, but the .gpb format is deprecated.
GimpBrushPipe derives from GimpBrush. The fileformat is still a text
header, followed by a number of brushes, but those brushes are stored
in the new GimpBrush format (no pattern anymore). The pipe does not
care about the depth of the contained GimpBrushes, so we get
grayscale BrushPipes for free. Since the brush loader still loads the
old format, old .gih files can also still be loaded.
Since the brushes in the GimpBrushPipe do not any longer contain a
pointer to the pipe object, we do only temporarily switch brushes
in the paint_core routines. This is not very elegant, but the best
we can do without a major redesign.
* app/patterns.[ch]: changed the loader to work with a filedescriptor
instead of a filehandle to make it work with the new brush loading
code.
* plug-ins/common/.cvsignore
* plug-ins/common/Makefile.am
* plug-ins/common/plugin-defs.pl
* plug-ins/common/gih.c: new plug-in that saves GIH files in the
new format (loader will follow soon)
* plug-ins/common/gpb.c: removed since Pixmap Brushes are no longer
supported as a special file format.
* plug-ins/common/gbr.c: load and save brushes in the new brush format
which allows RGBA brushes too.
* plug-ins/common/pat.c: load and save grayscale patterns too
2000-12-18 23:14:08 +08:00
|
|
|
|
2003-10-16 20:24:58 +08:00
|
|
|
#include "libgimpbase/gimpbase.h"
|
|
|
|
|
2001-05-10 06:34:59 +08:00
|
|
|
#include "core-types.h"
|
Jens Lautenbacher <jtl@gimp.org>
2000-12-18 Sven Neumann <sven@gimp.org>
Jens Lautenbacher <jtl@gimp.org>
* app/Makefile.am
* app/gimpbrushlistP.h
* app/gimpbrushpipeP.h
* app/gimpobjectP.h: removed these three files
* app/parasitelistP.h
* app/channels_dialog.c
* app/docindex.c
* app/gimpdrawable.c
* app/gimpdrawableP.h
* app/gimpimage.c
* app/gimpimageP.h
* app/gimplist.[ch]
* app/gimpobject.c
* app/gimpobject.h
* app/gimpsetP.h: changed according to header removal
* app/airbrush.c
* app/brush_select.[ch]
* app/brushes_cmds.c
* app/gimpbrush.[ch]
* app/gimpbrushgenerated.[ch]
* app/gimpbrushlist.[ch]
* app/gimpbrushpipe.[ch]
* app/gimpcontextpreview.c
* app/paint_core.c
* app/paintbrush.c
* app/pencil.c
* tools/pdbgen/pdb/brushes.pdb: Big Brushes Cleanup.
The GimpBrush* object hierarchy and the file formats were broken by
"design". This made it overly difficult to read and write pixmap
brushes and brush pipes, leading to the situation that The GIMP was
not able to read it's very own file formats. Since the GimpBrush
format did support arbitrary color depths, the introduction of a
file format for pixmap brushes was unnecessary.
The GimpBrushPixmap object is dead. GimpBrush has an additional
pixmap temp_buf and handles pixmap brushes transparently. The file
format of pixmap brushes is not any longer a grayscale brush plus
a pattern, but a simple brush with RGBA data. The old brushes can
still be loaded, but the .gpb format is deprecated.
GimpBrushPipe derives from GimpBrush. The fileformat is still a text
header, followed by a number of brushes, but those brushes are stored
in the new GimpBrush format (no pattern anymore). The pipe does not
care about the depth of the contained GimpBrushes, so we get
grayscale BrushPipes for free. Since the brush loader still loads the
old format, old .gih files can also still be loaded.
Since the brushes in the GimpBrushPipe do not any longer contain a
pointer to the pipe object, we do only temporarily switch brushes
in the paint_core routines. This is not very elegant, but the best
we can do without a major redesign.
* app/patterns.[ch]: changed the loader to work with a filedescriptor
instead of a filehandle to make it work with the new brush loading
code.
* plug-ins/common/.cvsignore
* plug-ins/common/Makefile.am
* plug-ins/common/plugin-defs.pl
* plug-ins/common/gih.c: new plug-in that saves GIH files in the
new format (loader will follow soon)
* plug-ins/common/gpb.c: removed since Pixmap Brushes are no longer
supported as a special file format.
* plug-ins/common/gbr.c: load and save brushes in the new brush format
which allows RGBA brushes too.
* plug-ins/common/pat.c: load and save grayscale patterns too
2000-12-18 23:14:08 +08:00
|
|
|
|
1998-07-09 13:31:06 +08:00
|
|
|
#include "gimpbrush.h"
|
2005-04-16 01:31:04 +08:00
|
|
|
#include "gimpbrush-header.h"
|
2005-04-15 23:56:34 +08:00
|
|
|
#include "gimpbrush-load.h"
|
2014-10-12 07:16:32 +08:00
|
|
|
#include "gimpbrush-private.h"
|
2012-04-09 06:59:20 +08:00
|
|
|
#include "gimptempbuf.h"
|
Jens Lautenbacher <jtl@gimp.org>
2000-12-18 Sven Neumann <sven@gimp.org>
Jens Lautenbacher <jtl@gimp.org>
* app/Makefile.am
* app/gimpbrushlistP.h
* app/gimpbrushpipeP.h
* app/gimpobjectP.h: removed these three files
* app/parasitelistP.h
* app/channels_dialog.c
* app/docindex.c
* app/gimpdrawable.c
* app/gimpdrawableP.h
* app/gimpimage.c
* app/gimpimageP.h
* app/gimplist.[ch]
* app/gimpobject.c
* app/gimpobject.h
* app/gimpsetP.h: changed according to header removal
* app/airbrush.c
* app/brush_select.[ch]
* app/brushes_cmds.c
* app/gimpbrush.[ch]
* app/gimpbrushgenerated.[ch]
* app/gimpbrushlist.[ch]
* app/gimpbrushpipe.[ch]
* app/gimpcontextpreview.c
* app/paint_core.c
* app/paintbrush.c
* app/pencil.c
* tools/pdbgen/pdb/brushes.pdb: Big Brushes Cleanup.
The GimpBrush* object hierarchy and the file formats were broken by
"design". This made it overly difficult to read and write pixmap
brushes and brush pipes, leading to the situation that The GIMP was
not able to read it's very own file formats. Since the GimpBrush
format did support arbitrary color depths, the introduction of a
file format for pixmap brushes was unnecessary.
The GimpBrushPixmap object is dead. GimpBrush has an additional
pixmap temp_buf and handles pixmap brushes transparently. The file
format of pixmap brushes is not any longer a grayscale brush plus
a pattern, but a simple brush with RGBA data. The old brushes can
still be loaded, but the .gpb format is deprecated.
GimpBrushPipe derives from GimpBrush. The fileformat is still a text
header, followed by a number of brushes, but those brushes are stored
in the new GimpBrush format (no pattern anymore). The pipe does not
care about the depth of the contained GimpBrushes, so we get
grayscale BrushPipes for free. Since the brush loader still loads the
old format, old .gih files can also still be loaded.
Since the brushes in the GimpBrushPipe do not any longer contain a
pointer to the pipe object, we do only temporarily switch brushes
in the paint_core routines. This is not very elegant, but the best
we can do without a major redesign.
* app/patterns.[ch]: changed the loader to work with a filedescriptor
instead of a filehandle to make it work with the new brush loading
code.
* plug-ins/common/.cvsignore
* plug-ins/common/Makefile.am
* plug-ins/common/plugin-defs.pl
* plug-ins/common/gih.c: new plug-in that saves GIH files in the
new format (loader will follow soon)
* plug-ins/common/gpb.c: removed since Pixmap Brushes are no longer
supported as a special file format.
* plug-ins/common/gbr.c: load and save brushes in the new brush format
which allows RGBA brushes too.
* plug-ins/common/pat.c: load and save grayscale patterns too
2000-12-18 23:14:08 +08:00
|
|
|
|
2003-03-26 00:38:19 +08:00
|
|
|
#include "gimp-intl.h"
|
1998-11-23 22:47:09 +08:00
|
|
|
|
2001-01-10 08:36:54 +08:00
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
/* stuff from abr2gbr Copyright (C) 2001 Marco Lamberto <lm@sunnyspot.org> */
|
2007-05-04 19:51:55 +08:00
|
|
|
/* the above is GPL see http://the.sunnyspot.org/gimp/ */
|
2001-06-30 03:25:03 +08:00
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
typedef struct _AbrHeader AbrHeader;
|
|
|
|
typedef struct _AbrBrushHeader AbrBrushHeader;
|
|
|
|
typedef struct _AbrSampledBrushHeader AbrSampledBrushHeader;
|
2001-08-11 22:39:19 +08:00
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
struct _AbrHeader
|
|
|
|
{
|
|
|
|
gint16 version;
|
|
|
|
gint16 count;
|
|
|
|
};
|
2001-08-11 22:39:19 +08:00
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
struct _AbrBrushHeader
|
|
|
|
{
|
|
|
|
gint16 type;
|
|
|
|
gint32 size;
|
|
|
|
};
|
2002-01-31 00:14:26 +08:00
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
struct _AbrSampledBrushHeader
|
|
|
|
{
|
|
|
|
gint32 misc;
|
|
|
|
gint16 spacing;
|
|
|
|
gchar antialiasing;
|
|
|
|
gint16 bounds[4];
|
|
|
|
gint32 bounds_long[4];
|
|
|
|
gint16 depth;
|
|
|
|
gboolean wide;
|
|
|
|
};
|
2001-02-05 23:22:20 +08:00
|
|
|
|
Overhaul of pixmap brushes and pipes: No separate pixmap pipe
brush tool any longer. The paintbrush, airbrush and pencil
tools, which already knew how to handle the single-pixmap
brushes now also handle the pipes as well.
* app/pixmapbrush.{h,c}
* app/gimpbrushpixmap.{h,c}: Removed these files.
* app/Makefile.am
* app/makefile.{cygwin,msc}: Remove from here, too.
* app/gimpbrushpipe.{h,c}: Total overhaul.
* app/paint_core.h
* app/apptypes.h: Some more types moved to apptypes.h
* app/context_manager.c
* app/tool_options.c
* app/tools.c
* app/toolsF.h: Remove PIXMAPBRUSH tool.
* app/gimpbrush.h: New method: select_brush. Used to change the
brush in paint_core, for pipe brushes.
* app/gimpbrush.c: Add gimp_brush_select_brush, which is dummy for
the normal brushes (returns the same brush).
* app/paint_core.c: Call the brush's select_brush method to get a
potential new brush before calling the paint_func.
* app/gimpbrushlist.c: Various changes related to the pixmap and
pipe overhaul.
* app/airbrush.c
* app/pencil.c: Reorder code a bit in the tool motion function to
avoid executing unnecessary code in the case of a pixmap brush.
Other changes in the same commit:
* app/install.c: Make quote_spaces extern.
* app/appenv.h: Declare it.
* libgimp/gimpui.def: Add missing entry points.
* libgimp/makefile.{cygwin,msc}: Add missing objects to gimpui.
1999-08-26 08:54:30 +08:00
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
/* local function prototypes */
|
2000-02-24 19:39:26 +08:00
|
|
|
|
2014-07-04 08:18:52 +08:00
|
|
|
static GList * gimp_brush_load_abr_v12 (GDataInputStream *input,
|
|
|
|
AbrHeader *abr_hdr,
|
|
|
|
GFile *file,
|
|
|
|
GError **error);
|
|
|
|
static GList * gimp_brush_load_abr_v6 (GDataInputStream *input,
|
|
|
|
AbrHeader *abr_hdr,
|
|
|
|
GFile *file,
|
|
|
|
GError **error);
|
|
|
|
static GimpBrush * gimp_brush_load_abr_brush_v12 (GDataInputStream *input,
|
|
|
|
AbrHeader *abr_hdr,
|
|
|
|
gint index,
|
|
|
|
GFile *file,
|
|
|
|
GError **error);
|
|
|
|
static GimpBrush * gimp_brush_load_abr_brush_v6 (GDataInputStream *input,
|
|
|
|
AbrHeader *abr_hdr,
|
|
|
|
gint32 max_offset,
|
|
|
|
gint index,
|
|
|
|
GFile *file,
|
|
|
|
GError **error);
|
|
|
|
|
|
|
|
static gchar abr_read_char (GDataInputStream *input,
|
|
|
|
GError **error);
|
|
|
|
static gint16 abr_read_short (GDataInputStream *input,
|
|
|
|
GError **error);
|
|
|
|
static gint32 abr_read_long (GDataInputStream *input,
|
|
|
|
GError **error);
|
|
|
|
static gchar * abr_read_ucs2_text (GDataInputStream *input,
|
|
|
|
GError **error);
|
|
|
|
static gboolean abr_supported (AbrHeader *abr_hdr,
|
|
|
|
GError **error);
|
|
|
|
static gboolean abr_reach_8bim_section (GDataInputStream *input,
|
|
|
|
const gchar *name,
|
|
|
|
GError **error);
|
|
|
|
static gboolean abr_rle_decode (GDataInputStream *input,
|
|
|
|
gchar *buffer,
|
2018-01-02 22:36:07 +08:00
|
|
|
gsize buffer_size,
|
2014-07-04 08:18:52 +08:00
|
|
|
gint32 height,
|
|
|
|
GError **error);
|
2001-06-30 03:25:03 +08:00
|
|
|
|
2001-01-10 08:36:54 +08:00
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
/* public functions */
|
1998-07-09 13:31:06 +08:00
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
GList *
|
2014-07-04 09:31:03 +08:00
|
|
|
gimp_brush_load (GimpContext *context,
|
|
|
|
GFile *file,
|
|
|
|
GInputStream *input,
|
|
|
|
GError **error)
|
1998-07-09 13:31:06 +08:00
|
|
|
{
|
2014-07-04 09:31:03 +08:00
|
|
|
GimpBrush *brush;
|
2000-09-29 20:00:00 +08:00
|
|
|
|
2014-07-01 08:30:22 +08:00
|
|
|
g_return_val_if_fail (G_IS_FILE (file), NULL);
|
2014-07-04 09:31:03 +08:00
|
|
|
g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL);
|
2005-04-16 01:31:04 +08:00
|
|
|
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
1998-07-09 13:31:06 +08:00
|
|
|
|
2014-07-03 03:11:49 +08:00
|
|
|
brush = gimp_brush_load_brush (context, file, input, error);
|
2005-04-16 01:31:04 +08:00
|
|
|
if (! brush)
|
|
|
|
return NULL;
|
2002-01-31 00:14:26 +08:00
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
return g_list_prepend (NULL, brush);
|
1998-07-09 13:31:06 +08:00
|
|
|
}
|
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
GimpBrush *
|
2014-07-03 03:11:49 +08:00
|
|
|
gimp_brush_load_brush (GimpContext *context,
|
|
|
|
GFile *file,
|
|
|
|
GInputStream *input,
|
|
|
|
GError **error)
|
1998-07-09 13:31:06 +08:00
|
|
|
{
|
2005-04-16 01:31:04 +08:00
|
|
|
GimpBrush *brush;
|
2017-11-01 20:50:05 +08:00
|
|
|
gsize bn_size;
|
2005-04-16 01:31:04 +08:00
|
|
|
BrushHeader header;
|
|
|
|
gchar *name = NULL;
|
|
|
|
guchar *pixmap;
|
|
|
|
guchar *mask;
|
2014-07-03 03:11:49 +08:00
|
|
|
gsize bytes_read;
|
2005-04-16 01:31:04 +08:00
|
|
|
gssize i, size;
|
2014-07-05 00:46:02 +08:00
|
|
|
gboolean success = TRUE;
|
2005-04-16 01:31:04 +08:00
|
|
|
|
2014-07-01 20:25:37 +08:00
|
|
|
g_return_val_if_fail (G_IS_FILE (file), NULL);
|
2014-07-03 03:11:49 +08:00
|
|
|
g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL);
|
2005-04-16 01:31:04 +08:00
|
|
|
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
|
|
|
|
2014-07-03 03:11:49 +08:00
|
|
|
/* read the header */
|
|
|
|
if (! g_input_stream_read_all (input, &header, sizeof (header),
|
2014-07-05 00:46:02 +08:00
|
|
|
&bytes_read, NULL, error) ||
|
2014-07-03 03:11:49 +08:00
|
|
|
bytes_read != sizeof (header))
|
2005-04-16 01:31:04 +08:00
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
1998-07-09 13:31:06 +08:00
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
/* rearrange the bytes in each unsigned int */
|
|
|
|
header.header_size = g_ntohl (header.header_size);
|
|
|
|
header.version = g_ntohl (header.version);
|
|
|
|
header.width = g_ntohl (header.width);
|
|
|
|
header.height = g_ntohl (header.height);
|
|
|
|
header.bytes = g_ntohl (header.bytes);
|
|
|
|
header.magic_number = g_ntohl (header.magic_number);
|
|
|
|
header.spacing = g_ntohl (header.spacing);
|
1999-08-17 08:59:07 +08:00
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
/* Check for correct file format */
|
|
|
|
|
|
|
|
if (header.width == 0)
|
2001-08-11 22:39:19 +08:00
|
|
|
{
|
2005-04-16 01:31:04 +08:00
|
|
|
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
|
2014-07-05 00:46:02 +08:00
|
|
|
_("Fatal parse error in brush file: Width = 0."));
|
2005-04-16 01:31:04 +08:00
|
|
|
return NULL;
|
2001-08-11 22:39:19 +08:00
|
|
|
}
|
2001-02-05 23:22:20 +08:00
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
if (header.height == 0)
|
2001-08-11 22:39:19 +08:00
|
|
|
{
|
2005-04-16 01:31:04 +08:00
|
|
|
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
|
2014-07-05 00:46:02 +08:00
|
|
|
_("Fatal parse error in brush file: Height = 0."));
|
2005-04-16 01:31:04 +08:00
|
|
|
return NULL;
|
2001-08-11 22:39:19 +08:00
|
|
|
}
|
2001-04-25 07:06:51 +08:00
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
if (header.bytes == 0)
|
2003-02-27 21:59:41 +08:00
|
|
|
{
|
2005-04-16 01:31:04 +08:00
|
|
|
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
|
2014-07-05 00:46:02 +08:00
|
|
|
_("Fatal parse error in brush file: Bytes = 0."));
|
2005-04-16 01:31:04 +08:00
|
|
|
return NULL;
|
2003-02-27 21:59:41 +08:00
|
|
|
}
|
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
switch (header.version)
|
2001-02-06 23:57:07 +08:00
|
|
|
{
|
2005-04-16 01:31:04 +08:00
|
|
|
case 1:
|
|
|
|
/* If this is a version 1 brush, set the fp back 8 bytes */
|
2014-07-03 03:11:49 +08:00
|
|
|
if (! g_seekable_seek (G_SEEKABLE (input), -8, G_SEEK_CUR,
|
|
|
|
NULL, error))
|
|
|
|
return NULL;
|
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
header.header_size += 8;
|
|
|
|
/* spacing is not defined in version 1 */
|
|
|
|
header.spacing = 25;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3: /* cinepaint brush */
|
|
|
|
if (header.bytes == 18 /* FLOAT16_GRAY_GIMAGE */)
|
|
|
|
{
|
|
|
|
header.bytes = 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
|
2014-07-05 00:46:02 +08:00
|
|
|
_("Fatal parse error in brush file: Unknown depth %d."),
|
|
|
|
header.bytes);
|
2005-04-16 01:31:04 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* fallthrough */
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
if (header.magic_number == GBRUSH_MAGIC)
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
|
2014-07-05 00:46:02 +08:00
|
|
|
_("Fatal parse error in brush file: Unknown version %d."),
|
|
|
|
header.version);
|
2005-04-16 01:31:04 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2001-02-06 23:57:07 +08:00
|
|
|
|
2017-11-01 20:50:05 +08:00
|
|
|
if (header.header_size < sizeof (BrushHeader))
|
|
|
|
{
|
2017-12-22 04:55:04 +08:00
|
|
|
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
|
|
|
|
_("Unsupported brush format"));
|
2017-11-01 20:50:05 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
/* Read in the brush name */
|
|
|
|
if ((bn_size = (header.header_size - sizeof (header))))
|
|
|
|
{
|
|
|
|
gchar *utf8;
|
2004-12-27 02:18:17 +08:00
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
name = g_new (gchar, bn_size);
|
2001-02-06 23:57:07 +08:00
|
|
|
|
2014-07-03 03:11:49 +08:00
|
|
|
if (! g_input_stream_read_all (input, name, bn_size,
|
2014-07-05 00:46:02 +08:00
|
|
|
&bytes_read, NULL, error) ||
|
2014-07-03 03:11:49 +08:00
|
|
|
bytes_read != bn_size)
|
2001-02-06 23:57:07 +08:00
|
|
|
{
|
2005-04-16 01:31:04 +08:00
|
|
|
g_free (name);
|
|
|
|
return NULL;
|
2001-02-06 23:57:07 +08:00
|
|
|
}
|
|
|
|
|
2017-10-31 19:11:08 +08:00
|
|
|
utf8 = gimp_any_to_utf8 (name, bn_size - 1,
|
2005-04-16 01:31:04 +08:00
|
|
|
_("Invalid UTF-8 string in brush file '%s'."),
|
2014-07-01 20:25:37 +08:00
|
|
|
gimp_file_get_utf8_name (file));
|
2005-04-16 01:31:04 +08:00
|
|
|
g_free (name);
|
|
|
|
name = utf8;
|
2001-02-06 23:57:07 +08:00
|
|
|
}
|
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
if (!name)
|
|
|
|
name = g_strdup (_("Unnamed"));
|
2001-02-06 23:57:07 +08:00
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
brush = g_object_new (GIMP_TYPE_BRUSH,
|
2005-05-26 07:25:45 +08:00
|
|
|
"name", name,
|
|
|
|
"mime-type", "image/x-gimp-gbr",
|
2005-04-16 01:31:04 +08:00
|
|
|
NULL);
|
|
|
|
g_free (name);
|
2001-02-06 23:57:07 +08:00
|
|
|
|
2014-10-12 07:16:32 +08:00
|
|
|
brush->priv->mask = gimp_temp_buf_new (header.width, header.height,
|
|
|
|
babl_format ("Y u8"));
|
2005-04-16 01:31:04 +08:00
|
|
|
|
2014-10-12 07:16:32 +08:00
|
|
|
mask = gimp_temp_buf_get_data (brush->priv->mask);
|
2005-04-16 01:31:04 +08:00
|
|
|
size = header.width * header.height * header.bytes;
|
|
|
|
|
|
|
|
switch (header.bytes)
|
2001-02-06 23:57:07 +08:00
|
|
|
{
|
2005-04-16 01:31:04 +08:00
|
|
|
case 1:
|
2014-07-03 03:11:49 +08:00
|
|
|
success = (g_input_stream_read_all (input, mask, size,
|
2014-07-05 00:46:02 +08:00
|
|
|
&bytes_read, NULL, error) &&
|
2014-07-03 03:11:49 +08:00
|
|
|
bytes_read == size);
|
2005-04-16 01:31:04 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 2: /* cinepaint brush, 16 bit floats */
|
|
|
|
{
|
|
|
|
guchar buf[8 * 1024];
|
|
|
|
|
|
|
|
for (i = 0; success && i < size;)
|
|
|
|
{
|
2014-07-03 03:11:49 +08:00
|
|
|
gssize bytes = MIN (size - i, sizeof (buf));
|
2005-04-16 01:31:04 +08:00
|
|
|
|
2014-07-03 03:11:49 +08:00
|
|
|
success = (g_input_stream_read_all (input, buf, bytes,
|
2014-07-05 00:46:02 +08:00
|
|
|
&bytes_read, NULL, error) &&
|
2014-07-03 03:11:49 +08:00
|
|
|
bytes_read == bytes);
|
2005-04-16 01:31:04 +08:00
|
|
|
|
|
|
|
if (success)
|
|
|
|
{
|
|
|
|
guint16 *b = (guint16 *) buf;
|
|
|
|
|
|
|
|
i += bytes;
|
|
|
|
|
|
|
|
for (; bytes > 0; bytes -= 2, mask++, b++)
|
|
|
|
{
|
|
|
|
union
|
|
|
|
{
|
|
|
|
guint16 u[2];
|
|
|
|
gfloat f;
|
|
|
|
} short_float;
|
|
|
|
|
|
|
|
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
|
|
|
short_float.u[0] = 0;
|
|
|
|
short_float.u[1] = GUINT16_FROM_BE (*b);
|
|
|
|
#else
|
|
|
|
short_float.u[0] = GUINT16_FROM_BE (*b);
|
|
|
|
short_float.u[1] = 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
*mask = (guchar) (short_float.f * 255.0 + 0.5);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2013-11-17 08:22:52 +08:00
|
|
|
case 3:
|
|
|
|
/* The obsolete .gbp format had a 3-byte pattern following a
|
|
|
|
* 1-byte brush, when embedded in a brush pipe, the current code
|
|
|
|
* tries to load that pattern as a brush, and encounters the '3'
|
|
|
|
* in the header.
|
|
|
|
*/
|
|
|
|
g_object_unref (brush);
|
|
|
|
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
|
2014-07-05 00:46:02 +08:00
|
|
|
_("Fatal parse error in brush file:\n"
|
2013-11-17 08:22:52 +08:00
|
|
|
"Unsupported brush depth %d\n"
|
|
|
|
"GIMP brushes must be GRAY or RGBA.\n"
|
|
|
|
"This might be an obsolete GIMP brush file, try "
|
|
|
|
"loading it as image and save it again."),
|
2014-07-05 00:46:02 +08:00
|
|
|
header.bytes);
|
2013-11-17 08:22:52 +08:00
|
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
case 4:
|
|
|
|
{
|
|
|
|
guchar buf[8 * 1024];
|
|
|
|
|
2014-10-12 07:16:32 +08:00
|
|
|
brush->priv->pixmap = gimp_temp_buf_new (header.width, header.height,
|
|
|
|
babl_format ("R'G'B' u8"));
|
|
|
|
pixmap = gimp_temp_buf_get_data (brush->priv->pixmap);
|
2005-04-16 01:31:04 +08:00
|
|
|
|
|
|
|
for (i = 0; success && i < size;)
|
|
|
|
{
|
2014-07-03 03:11:49 +08:00
|
|
|
gssize bytes = MIN (size - i, sizeof (buf));
|
2005-04-16 01:31:04 +08:00
|
|
|
|
2014-07-03 03:11:49 +08:00
|
|
|
success = (g_input_stream_read_all (input, buf, bytes,
|
2014-07-05 00:46:02 +08:00
|
|
|
&bytes_read, NULL, error) &&
|
2014-07-03 03:11:49 +08:00
|
|
|
bytes_read == bytes);
|
2005-04-16 01:31:04 +08:00
|
|
|
|
|
|
|
if (success)
|
|
|
|
{
|
|
|
|
guchar *b = buf;
|
|
|
|
|
|
|
|
i += bytes;
|
|
|
|
|
|
|
|
for (; bytes > 0; bytes -= 4, pixmap += 3, mask++, b += 4)
|
|
|
|
{
|
|
|
|
pixmap[0] = b[0];
|
|
|
|
pixmap[1] = b[1];
|
|
|
|
pixmap[2] = b[2];
|
|
|
|
|
|
|
|
mask[0] = b[3];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
g_object_unref (brush);
|
|
|
|
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
|
2014-07-05 00:46:02 +08:00
|
|
|
_("Fatal parse error in brush file:\n"
|
2005-04-16 01:31:04 +08:00
|
|
|
"Unsupported brush depth %d\n"
|
|
|
|
"GIMP brushes must be GRAY or RGBA."),
|
2014-07-05 00:46:02 +08:00
|
|
|
header.bytes);
|
2005-04-16 01:31:04 +08:00
|
|
|
return NULL;
|
2001-02-06 23:57:07 +08:00
|
|
|
}
|
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
if (! success)
|
2001-02-06 23:57:07 +08:00
|
|
|
{
|
2005-04-16 01:31:04 +08:00
|
|
|
g_object_unref (brush);
|
|
|
|
return NULL;
|
2001-02-06 23:57:07 +08:00
|
|
|
}
|
2001-02-05 23:22:20 +08:00
|
|
|
|
2014-10-12 07:16:32 +08:00
|
|
|
brush->priv->spacing = header.spacing;
|
|
|
|
brush->priv->x_axis.x = header.width / 2.0;
|
|
|
|
brush->priv->x_axis.y = 0.0;
|
|
|
|
brush->priv->y_axis.x = 0.0;
|
|
|
|
brush->priv->y_axis.y = header.height / 2.0;
|
2005-04-16 01:31:04 +08:00
|
|
|
|
|
|
|
return brush;
|
1998-07-09 13:31:06 +08:00
|
|
|
}
|
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
GList *
|
2014-07-04 09:31:03 +08:00
|
|
|
gimp_brush_load_abr (GimpContext *context,
|
|
|
|
GFile *file,
|
|
|
|
GInputStream *input,
|
|
|
|
GError **error)
|
2003-04-09 00:01:01 +08:00
|
|
|
{
|
2014-07-04 08:18:52 +08:00
|
|
|
GDataInputStream *data_input;
|
|
|
|
AbrHeader abr_hdr;
|
|
|
|
GList *brush_list = NULL;
|
|
|
|
GError *my_error = NULL;
|
2003-04-09 00:01:01 +08:00
|
|
|
|
2014-07-01 08:30:22 +08:00
|
|
|
g_return_val_if_fail (G_IS_FILE (file), NULL);
|
2014-07-04 09:31:03 +08:00
|
|
|
g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL);
|
2014-07-01 08:30:22 +08:00
|
|
|
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
|
|
|
|
2014-07-04 08:18:52 +08:00
|
|
|
data_input = g_data_input_stream_new (input);
|
|
|
|
|
|
|
|
g_data_input_stream_set_byte_order (data_input,
|
|
|
|
G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
|
|
|
|
|
|
|
|
abr_hdr.version = abr_read_short (data_input, &my_error);
|
|
|
|
if (my_error)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
/* sub-version for ABR v6 */
|
|
|
|
abr_hdr.count = abr_read_short (data_input, &my_error);
|
|
|
|
if (my_error)
|
|
|
|
goto done;
|
2001-02-14 03:53:07 +08:00
|
|
|
|
2014-07-05 00:46:02 +08:00
|
|
|
if (abr_supported (&abr_hdr, &my_error))
|
2005-04-16 01:31:04 +08:00
|
|
|
{
|
2007-06-04 18:43:31 +08:00
|
|
|
switch (abr_hdr.version)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
case 2:
|
2014-07-04 08:18:52 +08:00
|
|
|
brush_list = gimp_brush_load_abr_v12 (data_input, &abr_hdr,
|
|
|
|
file, &my_error);
|
2007-06-04 18:43:31 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 6:
|
2014-07-04 08:18:52 +08:00
|
|
|
brush_list = gimp_brush_load_abr_v6 (data_input, &abr_hdr,
|
|
|
|
file, &my_error);
|
|
|
|
break;
|
2007-06-04 18:43:31 +08:00
|
|
|
}
|
2005-04-16 01:31:04 +08:00
|
|
|
}
|
|
|
|
|
2014-07-04 08:18:52 +08:00
|
|
|
done:
|
|
|
|
|
|
|
|
g_object_unref (data_input);
|
|
|
|
|
|
|
|
if (! brush_list)
|
|
|
|
{
|
|
|
|
if (! my_error)
|
|
|
|
g_set_error (&my_error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
|
|
|
|
_("Unable to decode abr format version %d."),
|
|
|
|
abr_hdr.version);
|
|
|
|
}
|
|
|
|
|
2014-07-05 01:16:58 +08:00
|
|
|
if (my_error)
|
|
|
|
g_propagate_error (error, my_error);
|
2001-02-14 03:53:07 +08:00
|
|
|
|
2007-06-04 18:43:31 +08:00
|
|
|
return g_list_reverse (brush_list);
|
|
|
|
}
|
2001-02-14 03:53:07 +08:00
|
|
|
|
2007-06-04 18:43:31 +08:00
|
|
|
|
|
|
|
/* private functions */
|
|
|
|
|
|
|
|
static GList *
|
2014-07-04 08:18:52 +08:00
|
|
|
gimp_brush_load_abr_v12 (GDataInputStream *input,
|
|
|
|
AbrHeader *abr_hdr,
|
|
|
|
GFile *file,
|
|
|
|
GError **error)
|
2007-06-04 18:43:31 +08:00
|
|
|
{
|
|
|
|
GList *brush_list = NULL;
|
|
|
|
gint i;
|
|
|
|
|
|
|
|
for (i = 0; i < abr_hdr->count; i++)
|
|
|
|
{
|
|
|
|
GimpBrush *brush;
|
|
|
|
GError *my_error = NULL;
|
|
|
|
|
2014-07-04 08:18:52 +08:00
|
|
|
brush = gimp_brush_load_abr_brush_v12 (input, abr_hdr, i,
|
2014-07-01 20:25:37 +08:00
|
|
|
file, &my_error);
|
2007-06-04 18:43:31 +08:00
|
|
|
|
|
|
|
/* a NULL brush without an error means an unsupported brush
|
|
|
|
* type was encountered, silently skip it and try the next one
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (brush)
|
|
|
|
{
|
2005-04-16 01:31:04 +08:00
|
|
|
brush_list = g_list_prepend (brush_list, brush);
|
|
|
|
}
|
2007-06-04 18:43:31 +08:00
|
|
|
else if (my_error)
|
|
|
|
{
|
|
|
|
g_propagate_error (error, my_error);
|
|
|
|
break;
|
|
|
|
}
|
2005-04-16 01:31:04 +08:00
|
|
|
}
|
2001-12-02 06:59:48 +08:00
|
|
|
|
2007-06-04 18:43:31 +08:00
|
|
|
return brush_list;
|
2001-12-02 06:59:48 +08:00
|
|
|
}
|
|
|
|
|
2007-06-04 18:43:31 +08:00
|
|
|
static GList *
|
2014-07-04 08:18:52 +08:00
|
|
|
gimp_brush_load_abr_v6 (GDataInputStream *input,
|
|
|
|
AbrHeader *abr_hdr,
|
|
|
|
GFile *file,
|
|
|
|
GError **error)
|
2007-06-04 18:43:31 +08:00
|
|
|
{
|
2014-07-04 08:18:52 +08:00
|
|
|
GList *brush_list = NULL;
|
|
|
|
gint32 sample_section_size;
|
|
|
|
goffset sample_section_end;
|
|
|
|
gint i = 1;
|
|
|
|
|
|
|
|
if (! abr_reach_8bim_section (input, "samp", error))
|
|
|
|
return brush_list;
|
2001-12-02 06:59:48 +08:00
|
|
|
|
2014-07-04 08:18:52 +08:00
|
|
|
sample_section_size = abr_read_long (input, error);
|
|
|
|
if (error && *error)
|
2007-06-04 18:43:31 +08:00
|
|
|
return brush_list;
|
|
|
|
|
2014-07-04 08:18:52 +08:00
|
|
|
sample_section_end = (sample_section_size +
|
|
|
|
g_seekable_tell (G_SEEKABLE (input)));
|
2007-06-04 18:43:31 +08:00
|
|
|
|
2014-07-04 08:18:52 +08:00
|
|
|
while (g_seekable_tell (G_SEEKABLE (input)) < sample_section_end)
|
2007-06-04 18:43:31 +08:00
|
|
|
{
|
|
|
|
GimpBrush *brush;
|
|
|
|
GError *my_error = NULL;
|
|
|
|
|
2014-07-04 08:18:52 +08:00
|
|
|
brush = gimp_brush_load_abr_brush_v6 (input, abr_hdr, sample_section_end,
|
2014-07-01 20:25:37 +08:00
|
|
|
i, file, &my_error);
|
2007-06-04 18:43:31 +08:00
|
|
|
|
|
|
|
/* a NULL brush without an error means an unsupported brush
|
|
|
|
* type was encountered, silently skip it and try the next one
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (brush)
|
|
|
|
{
|
|
|
|
brush_list = g_list_prepend (brush_list, brush);
|
|
|
|
}
|
|
|
|
else if (my_error)
|
|
|
|
{
|
|
|
|
g_propagate_error (error, my_error);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return brush_list;
|
|
|
|
}
|
2001-12-02 06:59:48 +08:00
|
|
|
|
Overhaul of pixmap brushes and pipes: No separate pixmap pipe
brush tool any longer. The paintbrush, airbrush and pencil
tools, which already knew how to handle the single-pixmap
brushes now also handle the pipes as well.
* app/pixmapbrush.{h,c}
* app/gimpbrushpixmap.{h,c}: Removed these files.
* app/Makefile.am
* app/makefile.{cygwin,msc}: Remove from here, too.
* app/gimpbrushpipe.{h,c}: Total overhaul.
* app/paint_core.h
* app/apptypes.h: Some more types moved to apptypes.h
* app/context_manager.c
* app/tool_options.c
* app/tools.c
* app/toolsF.h: Remove PIXMAPBRUSH tool.
* app/gimpbrush.h: New method: select_brush. Used to change the
brush in paint_core, for pipe brushes.
* app/gimpbrush.c: Add gimp_brush_select_brush, which is dummy for
the normal brushes (returns the same brush).
* app/paint_core.c: Call the brush's select_brush method to get a
potential new brush before calling the paint_func.
* app/gimpbrushlist.c: Various changes related to the pixmap and
pipe overhaul.
* app/airbrush.c
* app/pencil.c: Reorder code a bit in the tool motion function to
avoid executing unnecessary code in the case of a pixmap brush.
Other changes in the same commit:
* app/install.c: Make quote_spaces extern.
* app/appenv.h: Declare it.
* libgimp/gimpui.def: Add missing entry points.
* libgimp/makefile.{cygwin,msc}: Add missing objects to gimpui.
1999-08-26 08:54:30 +08:00
|
|
|
static GimpBrush *
|
2014-07-04 08:18:52 +08:00
|
|
|
gimp_brush_load_abr_brush_v12 (GDataInputStream *input,
|
|
|
|
AbrHeader *abr_hdr,
|
|
|
|
gint index,
|
|
|
|
GFile *file,
|
|
|
|
GError **error)
|
Overhaul of pixmap brushes and pipes: No separate pixmap pipe
brush tool any longer. The paintbrush, airbrush and pencil
tools, which already knew how to handle the single-pixmap
brushes now also handle the pipes as well.
* app/pixmapbrush.{h,c}
* app/gimpbrushpixmap.{h,c}: Removed these files.
* app/Makefile.am
* app/makefile.{cygwin,msc}: Remove from here, too.
* app/gimpbrushpipe.{h,c}: Total overhaul.
* app/paint_core.h
* app/apptypes.h: Some more types moved to apptypes.h
* app/context_manager.c
* app/tool_options.c
* app/tools.c
* app/toolsF.h: Remove PIXMAPBRUSH tool.
* app/gimpbrush.h: New method: select_brush. Used to change the
brush in paint_core, for pipe brushes.
* app/gimpbrush.c: Add gimp_brush_select_brush, which is dummy for
the normal brushes (returns the same brush).
* app/paint_core.c: Call the brush's select_brush method to get a
potential new brush before calling the paint_func.
* app/gimpbrushlist.c: Various changes related to the pixmap and
pipe overhaul.
* app/airbrush.c
* app/pencil.c: Reorder code a bit in the tool motion function to
avoid executing unnecessary code in the case of a pixmap brush.
Other changes in the same commit:
* app/install.c: Make quote_spaces extern.
* app/appenv.h: Declare it.
* libgimp/gimpui.def: Add missing entry points.
* libgimp/makefile.{cygwin,msc}: Add missing objects to gimpui.
1999-08-26 08:54:30 +08:00
|
|
|
{
|
2005-04-16 01:31:04 +08:00
|
|
|
GimpBrush *brush = NULL;
|
|
|
|
AbrBrushHeader abr_brush_hdr;
|
Overhaul of pixmap brushes and pipes: No separate pixmap pipe
brush tool any longer. The paintbrush, airbrush and pencil
tools, which already knew how to handle the single-pixmap
brushes now also handle the pipes as well.
* app/pixmapbrush.{h,c}
* app/gimpbrushpixmap.{h,c}: Removed these files.
* app/Makefile.am
* app/makefile.{cygwin,msc}: Remove from here, too.
* app/gimpbrushpipe.{h,c}: Total overhaul.
* app/paint_core.h
* app/apptypes.h: Some more types moved to apptypes.h
* app/context_manager.c
* app/tool_options.c
* app/tools.c
* app/toolsF.h: Remove PIXMAPBRUSH tool.
* app/gimpbrush.h: New method: select_brush. Used to change the
brush in paint_core, for pipe brushes.
* app/gimpbrush.c: Add gimp_brush_select_brush, which is dummy for
the normal brushes (returns the same brush).
* app/paint_core.c: Call the brush's select_brush method to get a
potential new brush before calling the paint_func.
* app/gimpbrushlist.c: Various changes related to the pixmap and
pipe overhaul.
* app/airbrush.c
* app/pencil.c: Reorder code a bit in the tool motion function to
avoid executing unnecessary code in the case of a pixmap brush.
Other changes in the same commit:
* app/install.c: Make quote_spaces extern.
* app/appenv.h: Declare it.
* libgimp/gimpui.def: Add missing entry points.
* libgimp/makefile.{cygwin,msc}: Add missing objects to gimpui.
1999-08-26 08:54:30 +08:00
|
|
|
|
2014-07-04 08:18:52 +08:00
|
|
|
abr_brush_hdr.type = abr_read_short (input, error);
|
|
|
|
if (error && *error)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
abr_brush_hdr.size = abr_read_long (input, error);
|
|
|
|
if (error && *error)
|
|
|
|
return NULL;
|
2000-02-24 09:52:31 +08:00
|
|
|
|
2018-01-03 02:19:59 +08:00
|
|
|
if (abr_brush_hdr.size < 0)
|
|
|
|
{
|
|
|
|
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
|
|
|
|
_("Fatal parse error in brush file: "
|
2018-01-23 02:08:29 +08:00
|
|
|
"Brush size value corrupt."));
|
2018-01-03 02:19:59 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
/* g_print(" + BRUSH\n | << type: %i block size: %i bytes\n",
|
|
|
|
* abr_brush_hdr.type, abr_brush_hdr.size);
|
|
|
|
*/
|
1998-07-09 13:31:06 +08:00
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
switch (abr_brush_hdr.type)
|
|
|
|
{
|
|
|
|
case 1: /* computed brush */
|
|
|
|
/* FIXME: support it!
|
|
|
|
*
|
|
|
|
* We can probabaly feed the info into the generated brush code
|
|
|
|
* and get a useable brush back. It seems to support the same
|
|
|
|
* types -akl
|
|
|
|
*/
|
|
|
|
g_printerr ("WARNING: computed brush unsupported, skipping.\n");
|
2014-07-04 08:18:52 +08:00
|
|
|
g_seekable_seek (G_SEEKABLE (input), abr_brush_hdr.size,
|
|
|
|
G_SEEK_CUR, NULL, NULL);
|
2005-04-16 01:31:04 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 2: /* sampled brush */
|
|
|
|
{
|
|
|
|
AbrSampledBrushHeader abr_sampled_brush_hdr;
|
|
|
|
gint width, height;
|
|
|
|
gint bytes;
|
|
|
|
gint size;
|
|
|
|
guchar *mask;
|
|
|
|
gint i;
|
2007-06-04 18:43:31 +08:00
|
|
|
gchar *name;
|
|
|
|
gchar *sample_name = NULL;
|
2005-04-16 01:31:04 +08:00
|
|
|
gchar *tmp;
|
|
|
|
gshort compress;
|
|
|
|
|
2014-07-04 08:18:52 +08:00
|
|
|
abr_sampled_brush_hdr.misc = abr_read_long (input, error);
|
|
|
|
if (error && *error)
|
|
|
|
break;
|
|
|
|
|
|
|
|
abr_sampled_brush_hdr.spacing = abr_read_short (input, error);
|
|
|
|
if (error && *error)
|
|
|
|
break;
|
2007-05-04 19:51:55 +08:00
|
|
|
|
|
|
|
if (abr_hdr->version == 2)
|
2014-07-04 08:18:52 +08:00
|
|
|
{
|
|
|
|
sample_name = abr_read_ucs2_text (input, error);
|
|
|
|
if (error && *error)
|
|
|
|
break;
|
|
|
|
}
|
2007-05-04 19:51:55 +08:00
|
|
|
|
2014-07-04 08:18:52 +08:00
|
|
|
abr_sampled_brush_hdr.antialiasing = abr_read_char (input, error);
|
|
|
|
if (error && *error)
|
|
|
|
break;
|
2005-04-16 01:31:04 +08:00
|
|
|
|
|
|
|
for (i = 0; i < 4; i++)
|
2014-07-04 08:18:52 +08:00
|
|
|
{
|
|
|
|
abr_sampled_brush_hdr.bounds[i] = abr_read_short (input, error);
|
|
|
|
if (error && *error)
|
|
|
|
break;
|
|
|
|
}
|
2007-05-04 19:51:55 +08:00
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
for (i = 0; i < 4; i++)
|
2014-07-04 08:18:52 +08:00
|
|
|
{
|
|
|
|
abr_sampled_brush_hdr.bounds_long[i] = abr_read_long (input, error);
|
|
|
|
if (error && *error)
|
|
|
|
break;
|
|
|
|
}
|
2005-04-16 01:31:04 +08:00
|
|
|
|
2014-07-04 08:18:52 +08:00
|
|
|
abr_sampled_brush_hdr.depth = abr_read_short (input, error);
|
|
|
|
if (error && *error)
|
|
|
|
break;
|
2005-04-16 01:31:04 +08:00
|
|
|
|
|
|
|
height = (abr_sampled_brush_hdr.bounds_long[2] -
|
|
|
|
abr_sampled_brush_hdr.bounds_long[0]); /* bottom - top */
|
|
|
|
width = (abr_sampled_brush_hdr.bounds_long[3] -
|
|
|
|
abr_sampled_brush_hdr.bounds_long[1]); /* right - left */
|
|
|
|
bytes = abr_sampled_brush_hdr.depth >> 3;
|
|
|
|
|
2018-01-02 17:58:25 +08:00
|
|
|
/* g_print ("width %i height %i bytes %i\n", width, height, bytes); */
|
|
|
|
|
|
|
|
if (width < 1 || width > 10000 ||
|
|
|
|
height < 1 || height > 10000 ||
|
|
|
|
bytes < 1 || bytes > 1 ||
|
|
|
|
G_MAXSIZE / width / height / bytes < 1)
|
|
|
|
{
|
|
|
|
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
|
|
|
|
_("Fatal parse error in brush file: "
|
|
|
|
"Brush dimensions out of range."));
|
|
|
|
break;
|
|
|
|
}
|
2005-04-16 01:31:04 +08:00
|
|
|
|
|
|
|
abr_sampled_brush_hdr.wide = height > 16384;
|
|
|
|
|
|
|
|
if (abr_sampled_brush_hdr.wide)
|
|
|
|
{
|
|
|
|
/* FIXME: support wide brushes */
|
|
|
|
|
2007-06-04 18:43:31 +08:00
|
|
|
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
|
2014-07-05 00:46:02 +08:00
|
|
|
_("Fatal parse error in brush file: "
|
|
|
|
"Wide brushes are not supported."));
|
2014-07-04 08:18:52 +08:00
|
|
|
break;
|
2005-04-16 01:31:04 +08:00
|
|
|
}
|
|
|
|
|
2014-07-01 20:25:37 +08:00
|
|
|
tmp = g_path_get_basename (gimp_file_get_utf8_name (file));
|
2007-06-04 18:43:31 +08:00
|
|
|
if (! sample_name)
|
2007-05-04 19:51:55 +08:00
|
|
|
{
|
2007-06-04 18:43:31 +08:00
|
|
|
/* build name from filename and index */
|
2007-05-04 19:51:55 +08:00
|
|
|
name = g_strdup_printf ("%s-%03d", tmp, index);
|
|
|
|
}
|
2007-06-04 18:43:31 +08:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* build name from filename and sample name */
|
|
|
|
name = g_strdup_printf ("%s-%s", tmp, sample_name);
|
|
|
|
g_free (sample_name);
|
|
|
|
}
|
|
|
|
g_free (tmp);
|
2005-04-16 01:31:04 +08:00
|
|
|
|
|
|
|
brush = g_object_new (GIMP_TYPE_BRUSH,
|
2005-05-26 07:25:45 +08:00
|
|
|
"name", name,
|
|
|
|
/* FIXME: MIME type!! */
|
|
|
|
"mime-type", "application/x-photoshop-abr",
|
2005-04-16 01:31:04 +08:00
|
|
|
NULL);
|
|
|
|
|
|
|
|
g_free (name);
|
|
|
|
|
2014-10-12 07:16:32 +08:00
|
|
|
brush->priv->spacing = abr_sampled_brush_hdr.spacing;
|
|
|
|
brush->priv->x_axis.x = width / 2.0;
|
|
|
|
brush->priv->x_axis.y = 0.0;
|
|
|
|
brush->priv->y_axis.x = 0.0;
|
|
|
|
brush->priv->y_axis.y = height / 2.0;
|
|
|
|
brush->priv->mask = gimp_temp_buf_new (width, height,
|
|
|
|
babl_format ("Y u8"));
|
2005-04-16 01:31:04 +08:00
|
|
|
|
2014-10-12 07:16:32 +08:00
|
|
|
mask = gimp_temp_buf_get_data (brush->priv->mask);
|
2005-04-16 01:31:04 +08:00
|
|
|
size = width * height * bytes;
|
|
|
|
|
2014-07-04 08:18:52 +08:00
|
|
|
compress = abr_read_char (input, error);
|
|
|
|
if (error && *error)
|
|
|
|
{
|
|
|
|
g_object_unref (brush);
|
|
|
|
brush = NULL;
|
|
|
|
break;
|
|
|
|
}
|
2005-04-16 01:31:04 +08:00
|
|
|
|
|
|
|
/* g_print(" | << size: %dx%d %d bit (%d bytes) %s\n",
|
|
|
|
* width, height, abr_sampled_brush_hdr.depth, size,
|
|
|
|
* comppres ? "compressed" : "raw");
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (! compress)
|
2014-07-04 08:18:52 +08:00
|
|
|
{
|
|
|
|
gsize bytes_read;
|
|
|
|
|
|
|
|
if (! g_input_stream_read_all (G_INPUT_STREAM (input),
|
|
|
|
mask, size,
|
|
|
|
&bytes_read, NULL, error) ||
|
|
|
|
bytes_read != size)
|
|
|
|
{
|
|
|
|
g_object_unref (brush);
|
|
|
|
brush = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2005-04-16 01:31:04 +08:00
|
|
|
else
|
2014-07-04 08:18:52 +08:00
|
|
|
{
|
2018-01-02 22:36:07 +08:00
|
|
|
if (! abr_rle_decode (input, (gchar *) mask, size, height, error))
|
2014-07-04 08:18:52 +08:00
|
|
|
{
|
|
|
|
g_object_unref (brush);
|
|
|
|
brush = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2007-06-04 18:43:31 +08:00
|
|
|
}
|
|
|
|
break;
|
2005-04-16 01:31:04 +08:00
|
|
|
|
2007-06-04 18:43:31 +08:00
|
|
|
default:
|
|
|
|
g_printerr ("WARNING: unknown brush type, skipping.\n");
|
2014-07-04 08:18:52 +08:00
|
|
|
g_seekable_seek (G_SEEKABLE (input), abr_brush_hdr.size,
|
|
|
|
G_SEEK_CUR, NULL, NULL);
|
2007-06-04 18:43:31 +08:00
|
|
|
break;
|
|
|
|
}
|
2007-05-04 19:51:55 +08:00
|
|
|
|
2007-06-04 18:43:31 +08:00
|
|
|
return brush;
|
|
|
|
}
|
2005-04-16 01:31:04 +08:00
|
|
|
|
2007-06-04 18:43:31 +08:00
|
|
|
static GimpBrush *
|
2014-07-04 08:18:52 +08:00
|
|
|
gimp_brush_load_abr_brush_v6 (GDataInputStream *input,
|
|
|
|
AbrHeader *abr_hdr,
|
|
|
|
gint32 max_offset,
|
|
|
|
gint index,
|
|
|
|
GFile *file,
|
|
|
|
GError **error)
|
2007-06-04 18:43:31 +08:00
|
|
|
{
|
|
|
|
GimpBrush *brush = NULL;
|
|
|
|
guchar *mask;
|
|
|
|
|
|
|
|
gint32 brush_size;
|
|
|
|
gint32 brush_end;
|
2014-07-04 08:18:52 +08:00
|
|
|
goffset next_brush;
|
2007-06-04 18:43:31 +08:00
|
|
|
|
|
|
|
gint32 top, left, bottom, right;
|
|
|
|
gint16 depth;
|
|
|
|
gchar compress;
|
|
|
|
|
|
|
|
gint32 width, height;
|
|
|
|
gint32 size;
|
|
|
|
|
|
|
|
gchar *tmp;
|
|
|
|
gchar *name;
|
2014-07-04 08:18:52 +08:00
|
|
|
gboolean r;
|
|
|
|
|
|
|
|
brush_size = abr_read_long (input, error);
|
|
|
|
if (error && *error)
|
|
|
|
return NULL;
|
2007-06-04 18:43:31 +08:00
|
|
|
|
2018-01-03 02:19:59 +08:00
|
|
|
if (brush_size < 0)
|
|
|
|
{
|
|
|
|
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
|
|
|
|
_("Fatal parse error in brush file: "
|
2018-01-23 02:08:29 +08:00
|
|
|
"Brush size value corrupt."));
|
2018-01-03 02:19:59 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2007-06-04 18:43:31 +08:00
|
|
|
brush_end = brush_size;
|
2011-05-02 05:23:19 +08:00
|
|
|
|
2007-06-04 18:43:31 +08:00
|
|
|
/* complement to 4 */
|
2011-05-02 05:23:19 +08:00
|
|
|
while (brush_end % 4 != 0)
|
|
|
|
brush_end++;
|
|
|
|
|
2014-07-04 08:18:52 +08:00
|
|
|
next_brush = (brush_end + g_seekable_tell (G_SEEKABLE (input)));
|
2007-06-04 18:43:31 +08:00
|
|
|
|
|
|
|
if (abr_hdr->count == 1)
|
2014-07-04 08:18:52 +08:00
|
|
|
{
|
|
|
|
/* discard key and short coordinates and unknown short */
|
|
|
|
r = g_seekable_seek (G_SEEKABLE (input), 47, G_SEEK_CUR,
|
|
|
|
NULL, error);
|
|
|
|
}
|
2007-06-04 18:43:31 +08:00
|
|
|
else
|
2014-07-04 08:18:52 +08:00
|
|
|
{
|
|
|
|
/* discard key and unknown bytes */
|
|
|
|
r = g_seekable_seek (G_SEEKABLE (input), 301, G_SEEK_CUR,
|
|
|
|
NULL, error);
|
|
|
|
}
|
2007-06-04 18:43:31 +08:00
|
|
|
|
2014-07-04 08:18:52 +08:00
|
|
|
if (! r)
|
2007-06-04 18:43:31 +08:00
|
|
|
{
|
2014-07-04 08:18:52 +08:00
|
|
|
g_prefix_error (error,
|
2015-12-01 19:25:15 +08:00
|
|
|
_("Fatal parse error in brush file: "
|
2014-07-05 00:46:02 +08:00
|
|
|
"File appears truncated: "));
|
2007-06-04 18:43:31 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2007-05-04 19:51:55 +08:00
|
|
|
|
2014-07-04 08:18:52 +08:00
|
|
|
top = abr_read_long (input, error); if (error && *error) return NULL;
|
|
|
|
left = abr_read_long (input, error); if (error && *error) return NULL;
|
|
|
|
bottom = abr_read_long (input, error); if (error && *error) return NULL;
|
|
|
|
right = abr_read_long (input, error); if (error && *error) return NULL;
|
|
|
|
depth = abr_read_short (input, error); if (error && *error) return NULL;
|
|
|
|
compress = abr_read_char (input, error); if (error && *error) return NULL;
|
2005-04-16 01:31:04 +08:00
|
|
|
|
2018-01-02 22:36:07 +08:00
|
|
|
depth = depth >> 3;
|
|
|
|
|
2007-06-04 18:43:31 +08:00
|
|
|
width = right - left;
|
|
|
|
height = bottom - top;
|
2018-01-02 22:36:07 +08:00
|
|
|
size = width * depth * height;
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
g_printerr ("width %i height %i depth %i compress %i\n",
|
|
|
|
width, height, depth, compress);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (width < 1 || width > 10000 ||
|
|
|
|
height < 1 || height > 10000 ||
|
|
|
|
depth < 1 || depth > 1 ||
|
|
|
|
G_MAXSIZE / width / height / depth < 1)
|
|
|
|
{
|
|
|
|
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
|
|
|
|
_("Fatal parse error in brush file: "
|
|
|
|
"Brush dimensions out of range."));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (compress < 0 || compress > 1)
|
|
|
|
{
|
|
|
|
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
|
|
|
|
_("Fatal parse error in brush file: "
|
|
|
|
"Unknown compression method."));
|
|
|
|
return NULL;
|
|
|
|
}
|
2005-04-16 01:31:04 +08:00
|
|
|
|
2014-07-01 20:25:37 +08:00
|
|
|
tmp = g_path_get_basename (gimp_file_get_utf8_name (file));
|
2007-06-04 18:43:31 +08:00
|
|
|
name = g_strdup_printf ("%s-%03d", tmp, index);
|
|
|
|
g_free (tmp);
|
2005-04-16 01:31:04 +08:00
|
|
|
|
2007-06-04 18:43:31 +08:00
|
|
|
brush = g_object_new (GIMP_TYPE_BRUSH,
|
|
|
|
"name", name,
|
|
|
|
/* FIXME: MIME type!! */
|
|
|
|
"mime-type", "application/x-photoshop-abr",
|
|
|
|
NULL);
|
2007-05-04 19:51:55 +08:00
|
|
|
|
2007-06-04 18:43:31 +08:00
|
|
|
g_free (name);
|
2005-04-16 01:31:04 +08:00
|
|
|
|
2014-10-12 07:16:32 +08:00
|
|
|
brush->priv->spacing = 25; /* real value needs 8BIMdesc section parser */
|
|
|
|
brush->priv->x_axis.x = width / 2.0;
|
|
|
|
brush->priv->x_axis.y = 0.0;
|
|
|
|
brush->priv->y_axis.x = 0.0;
|
|
|
|
brush->priv->y_axis.y = height / 2.0;
|
|
|
|
brush->priv->mask = gimp_temp_buf_new (width, height,
|
|
|
|
babl_format ("Y u8"));
|
2005-04-16 01:31:04 +08:00
|
|
|
|
2014-10-12 07:16:32 +08:00
|
|
|
mask = gimp_temp_buf_get_data (brush->priv->mask);
|
2005-04-16 01:31:04 +08:00
|
|
|
|
2007-06-04 18:43:31 +08:00
|
|
|
/* data decoding */
|
|
|
|
if (! compress)
|
2014-07-04 08:18:52 +08:00
|
|
|
{
|
|
|
|
/* not compressed - read raw bytes as brush data */
|
|
|
|
gsize bytes_read;
|
|
|
|
|
|
|
|
if (! g_input_stream_read_all (G_INPUT_STREAM (input),
|
|
|
|
mask, size,
|
|
|
|
&bytes_read, NULL, error) ||
|
|
|
|
bytes_read != size)
|
|
|
|
{
|
|
|
|
g_object_unref (brush);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
2007-06-04 18:43:31 +08:00
|
|
|
else
|
2014-07-04 08:18:52 +08:00
|
|
|
{
|
2018-01-02 22:36:07 +08:00
|
|
|
if (! abr_rle_decode (input, (gchar *) mask, size, height, error))
|
2014-07-04 08:18:52 +08:00
|
|
|
{
|
|
|
|
g_object_unref (brush);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
2007-06-04 18:43:31 +08:00
|
|
|
|
2018-01-03 02:19:59 +08:00
|
|
|
if (g_seekable_tell (G_SEEKABLE (input)) <= next_brush)
|
|
|
|
g_seekable_seek (G_SEEKABLE (input), next_brush, G_SEEK_SET,
|
|
|
|
NULL, NULL);
|
Jens Lautenbacher <jtl@gimp.org>
2000-12-18 Sven Neumann <sven@gimp.org>
Jens Lautenbacher <jtl@gimp.org>
* app/Makefile.am
* app/gimpbrushlistP.h
* app/gimpbrushpipeP.h
* app/gimpobjectP.h: removed these three files
* app/parasitelistP.h
* app/channels_dialog.c
* app/docindex.c
* app/gimpdrawable.c
* app/gimpdrawableP.h
* app/gimpimage.c
* app/gimpimageP.h
* app/gimplist.[ch]
* app/gimpobject.c
* app/gimpobject.h
* app/gimpsetP.h: changed according to header removal
* app/airbrush.c
* app/brush_select.[ch]
* app/brushes_cmds.c
* app/gimpbrush.[ch]
* app/gimpbrushgenerated.[ch]
* app/gimpbrushlist.[ch]
* app/gimpbrushpipe.[ch]
* app/gimpcontextpreview.c
* app/paint_core.c
* app/paintbrush.c
* app/pencil.c
* tools/pdbgen/pdb/brushes.pdb: Big Brushes Cleanup.
The GimpBrush* object hierarchy and the file formats were broken by
"design". This made it overly difficult to read and write pixmap
brushes and brush pipes, leading to the situation that The GIMP was
not able to read it's very own file formats. Since the GimpBrush
format did support arbitrary color depths, the introduction of a
file format for pixmap brushes was unnecessary.
The GimpBrushPixmap object is dead. GimpBrush has an additional
pixmap temp_buf and handles pixmap brushes transparently. The file
format of pixmap brushes is not any longer a grayscale brush plus
a pattern, but a simple brush with RGBA data. The old brushes can
still be loaded, but the .gpb format is deprecated.
GimpBrushPipe derives from GimpBrush. The fileformat is still a text
header, followed by a number of brushes, but those brushes are stored
in the new GimpBrush format (no pattern anymore). The pipe does not
care about the depth of the contained GimpBrushes, so we get
grayscale BrushPipes for free. Since the brush loader still loads the
old format, old .gih files can also still be loaded.
Since the brushes in the GimpBrushPipe do not any longer contain a
pointer to the pipe object, we do only temporarily switch brushes
in the paint_core routines. This is not very elegant, but the best
we can do without a major redesign.
* app/patterns.[ch]: changed the loader to work with a filedescriptor
instead of a filehandle to make it work with the new brush loading
code.
* plug-ins/common/.cvsignore
* plug-ins/common/Makefile.am
* plug-ins/common/plugin-defs.pl
* plug-ins/common/gih.c: new plug-in that saves GIH files in the
new format (loader will follow soon)
* plug-ins/common/gpb.c: removed since Pixmap Brushes are no longer
supported as a special file format.
* plug-ins/common/gbr.c: load and save brushes in the new brush format
which allows RGBA brushes too.
* plug-ins/common/pat.c: load and save grayscale patterns too
2000-12-18 23:14:08 +08:00
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
return brush;
|
Jens Lautenbacher <jtl@gimp.org>
2000-12-18 Sven Neumann <sven@gimp.org>
Jens Lautenbacher <jtl@gimp.org>
* app/Makefile.am
* app/gimpbrushlistP.h
* app/gimpbrushpipeP.h
* app/gimpobjectP.h: removed these three files
* app/parasitelistP.h
* app/channels_dialog.c
* app/docindex.c
* app/gimpdrawable.c
* app/gimpdrawableP.h
* app/gimpimage.c
* app/gimpimageP.h
* app/gimplist.[ch]
* app/gimpobject.c
* app/gimpobject.h
* app/gimpsetP.h: changed according to header removal
* app/airbrush.c
* app/brush_select.[ch]
* app/brushes_cmds.c
* app/gimpbrush.[ch]
* app/gimpbrushgenerated.[ch]
* app/gimpbrushlist.[ch]
* app/gimpbrushpipe.[ch]
* app/gimpcontextpreview.c
* app/paint_core.c
* app/paintbrush.c
* app/pencil.c
* tools/pdbgen/pdb/brushes.pdb: Big Brushes Cleanup.
The GimpBrush* object hierarchy and the file formats were broken by
"design". This made it overly difficult to read and write pixmap
brushes and brush pipes, leading to the situation that The GIMP was
not able to read it's very own file formats. Since the GimpBrush
format did support arbitrary color depths, the introduction of a
file format for pixmap brushes was unnecessary.
The GimpBrushPixmap object is dead. GimpBrush has an additional
pixmap temp_buf and handles pixmap brushes transparently. The file
format of pixmap brushes is not any longer a grayscale brush plus
a pattern, but a simple brush with RGBA data. The old brushes can
still be loaded, but the .gpb format is deprecated.
GimpBrushPipe derives from GimpBrush. The fileformat is still a text
header, followed by a number of brushes, but those brushes are stored
in the new GimpBrush format (no pattern anymore). The pipe does not
care about the depth of the contained GimpBrushes, so we get
grayscale BrushPipes for free. Since the brush loader still loads the
old format, old .gih files can also still be loaded.
Since the brushes in the GimpBrushPipe do not any longer contain a
pointer to the pipe object, we do only temporarily switch brushes
in the paint_core routines. This is not very elegant, but the best
we can do without a major redesign.
* app/patterns.[ch]: changed the loader to work with a filedescriptor
instead of a filehandle to make it work with the new brush loading
code.
* plug-ins/common/.cvsignore
* plug-ins/common/Makefile.am
* plug-ins/common/plugin-defs.pl
* plug-ins/common/gih.c: new plug-in that saves GIH files in the
new format (loader will follow soon)
* plug-ins/common/gpb.c: removed since Pixmap Brushes are no longer
supported as a special file format.
* plug-ins/common/gbr.c: load and save brushes in the new brush format
which allows RGBA brushes too.
* plug-ins/common/pat.c: load and save grayscale patterns too
2000-12-18 23:14:08 +08:00
|
|
|
}
|
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
static gchar
|
2014-07-04 08:18:52 +08:00
|
|
|
abr_read_char (GDataInputStream *input,
|
|
|
|
GError **error)
|
1999-04-22 22:34:00 +08:00
|
|
|
{
|
2014-07-04 08:18:52 +08:00
|
|
|
return g_data_input_stream_read_byte (input, NULL, error);
|
1999-04-22 22:34:00 +08:00
|
|
|
}
|
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
static gint16
|
2014-07-04 08:18:52 +08:00
|
|
|
abr_read_short (GDataInputStream *input,
|
|
|
|
GError **error)
|
1999-04-22 22:34:00 +08:00
|
|
|
{
|
2014-07-04 08:18:52 +08:00
|
|
|
return g_data_input_stream_read_int16 (input, NULL, error);
|
2001-06-30 03:25:03 +08:00
|
|
|
}
|
|
|
|
|
2005-04-16 01:31:04 +08:00
|
|
|
static gint32
|
2014-07-04 08:18:52 +08:00
|
|
|
abr_read_long (GDataInputStream *input,
|
|
|
|
GError **error)
|
2001-06-30 03:25:03 +08:00
|
|
|
{
|
2014-07-04 08:18:52 +08:00
|
|
|
return g_data_input_stream_read_int32 (input, NULL, error);
|
1999-04-22 22:34:00 +08:00
|
|
|
}
|
2007-05-04 19:51:55 +08:00
|
|
|
|
|
|
|
static gchar *
|
2014-07-04 08:18:52 +08:00
|
|
|
abr_read_ucs2_text (GDataInputStream *input,
|
|
|
|
GError **error)
|
2007-05-04 19:51:55 +08:00
|
|
|
{
|
|
|
|
gchar *name_ucs2;
|
|
|
|
gchar *name_utf8;
|
|
|
|
gint len;
|
|
|
|
gint i;
|
|
|
|
|
|
|
|
/* two-bytes characters encoded (UCS-2)
|
|
|
|
* format:
|
|
|
|
* long : number of characters in string
|
|
|
|
* data : zero terminated UCS-2 string
|
|
|
|
*/
|
|
|
|
|
2014-07-04 08:18:52 +08:00
|
|
|
len = 2 * abr_read_long (input, error);
|
2007-05-04 19:51:55 +08:00
|
|
|
if (len <= 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
name_ucs2 = g_new (gchar, len);
|
|
|
|
|
|
|
|
for (i = 0; i < len; i++)
|
2014-07-04 08:18:52 +08:00
|
|
|
{
|
|
|
|
name_ucs2[i] = abr_read_char (input, error);
|
|
|
|
if (error && *error)
|
|
|
|
{
|
|
|
|
g_free (name_ucs2);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
2007-05-04 19:51:55 +08:00
|
|
|
|
|
|
|
name_utf8 = g_convert (name_ucs2, len,
|
|
|
|
"UTF-8", "UCS-2BE",
|
|
|
|
NULL, NULL, NULL);
|
|
|
|
|
2007-06-04 18:43:31 +08:00
|
|
|
g_free (name_ucs2);
|
2007-05-04 19:51:55 +08:00
|
|
|
|
|
|
|
return name_utf8;
|
|
|
|
}
|
2007-06-04 18:43:31 +08:00
|
|
|
|
|
|
|
static gboolean
|
2014-07-01 20:25:37 +08:00
|
|
|
abr_supported (AbrHeader *abr_hdr,
|
|
|
|
GError **error)
|
2007-06-04 18:43:31 +08:00
|
|
|
{
|
|
|
|
switch (abr_hdr->version)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
case 2:
|
|
|
|
return TRUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 6:
|
|
|
|
/* in this case, count contains format sub-version */
|
|
|
|
if (abr_hdr->count == 1 || abr_hdr->count == 2)
|
|
|
|
return TRUE;
|
2012-08-04 06:04:47 +08:00
|
|
|
|
2014-07-04 08:18:52 +08:00
|
|
|
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
|
2014-07-05 00:46:02 +08:00
|
|
|
_("Fatal parse error in brush file: "
|
|
|
|
"Unable to decode abr format version %d."),
|
2014-07-04 08:18:52 +08:00
|
|
|
|
|
|
|
/* horrid subversion display, but better than
|
|
|
|
* having yet another translatable string for
|
|
|
|
* this
|
|
|
|
*/
|
|
|
|
abr_hdr->version * 10 + abr_hdr->count);
|
2007-06-04 18:43:31 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
2014-07-04 08:18:52 +08:00
|
|
|
abr_reach_8bim_section (GDataInputStream *input,
|
|
|
|
const gchar *name,
|
|
|
|
GError **error)
|
2007-06-04 18:43:31 +08:00
|
|
|
{
|
2014-07-04 08:18:52 +08:00
|
|
|
while (TRUE)
|
2007-06-04 18:43:31 +08:00
|
|
|
{
|
2018-01-02 17:58:25 +08:00
|
|
|
gchar tag[4];
|
|
|
|
gchar tagname[5];
|
|
|
|
guint32 section_size;
|
|
|
|
gsize bytes_read;
|
2014-07-04 08:18:52 +08:00
|
|
|
|
|
|
|
if (! g_input_stream_read_all (G_INPUT_STREAM (input),
|
|
|
|
tag, 4,
|
|
|
|
&bytes_read, NULL, error) ||
|
|
|
|
bytes_read != 4)
|
2007-06-04 18:43:31 +08:00
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (strncmp (tag, "8BIM", 4))
|
|
|
|
return FALSE;
|
|
|
|
|
2014-07-04 08:18:52 +08:00
|
|
|
if (! g_input_stream_read_all (G_INPUT_STREAM (input),
|
|
|
|
tagname, 4,
|
|
|
|
&bytes_read, NULL, error) ||
|
|
|
|
bytes_read != 4)
|
2007-06-04 18:43:31 +08:00
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
tagname[4] = '\0';
|
|
|
|
|
|
|
|
if (! strncmp (tagname, name, 4))
|
|
|
|
return TRUE;
|
|
|
|
|
2014-07-04 08:18:52 +08:00
|
|
|
section_size = abr_read_long (input, error);
|
|
|
|
if (error && *error)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (! g_seekable_seek (G_SEEKABLE (input), section_size, G_SEEK_CUR,
|
|
|
|
NULL, error))
|
2007-06-04 18:43:31 +08:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2014-07-04 08:18:52 +08:00
|
|
|
static gboolean
|
|
|
|
abr_rle_decode (GDataInputStream *input,
|
|
|
|
gchar *buffer,
|
2018-01-02 22:36:07 +08:00
|
|
|
gsize buffer_size,
|
2014-07-04 08:18:52 +08:00
|
|
|
gint32 height,
|
|
|
|
GError **error)
|
2007-06-04 18:43:31 +08:00
|
|
|
{
|
|
|
|
gchar ch;
|
|
|
|
gint i, j, c;
|
|
|
|
gshort *cscanline_len;
|
|
|
|
gchar *data = buffer;
|
|
|
|
|
|
|
|
/* read compressed size foreach scanline */
|
|
|
|
cscanline_len = g_new0 (gshort, height);
|
|
|
|
for (i = 0; i < height; i++)
|
2014-07-04 08:18:52 +08:00
|
|
|
{
|
|
|
|
cscanline_len[i] = abr_read_short (input, error);
|
|
|
|
if (error && *error)
|
|
|
|
{
|
|
|
|
g_free (cscanline_len);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
2007-06-04 18:43:31 +08:00
|
|
|
|
|
|
|
/* unpack each scanline data */
|
|
|
|
for (i = 0; i < height; i++)
|
|
|
|
{
|
|
|
|
for (j = 0; j < cscanline_len[i];)
|
|
|
|
{
|
2014-07-04 08:18:52 +08:00
|
|
|
gint32 n = abr_read_char (input, error);
|
|
|
|
if (error && *error)
|
|
|
|
{
|
|
|
|
g_free (cscanline_len);
|
|
|
|
return FALSE;
|
|
|
|
}
|
2007-06-04 18:43:31 +08:00
|
|
|
|
|
|
|
j++;
|
|
|
|
|
|
|
|
if (n >= 128) /* force sign */
|
|
|
|
n -= 256;
|
|
|
|
|
|
|
|
if (n < 0)
|
|
|
|
{
|
|
|
|
/* copy the following char -n + 1 times */
|
|
|
|
|
|
|
|
if (n == -128) /* it's a nop */
|
|
|
|
continue;
|
|
|
|
|
|
|
|
n = -n + 1;
|
2014-07-04 08:18:52 +08:00
|
|
|
ch = abr_read_char (input, error);
|
|
|
|
if (error && *error)
|
|
|
|
{
|
|
|
|
g_free (cscanline_len);
|
|
|
|
return FALSE;
|
|
|
|
}
|
2007-06-04 18:43:31 +08:00
|
|
|
j++;
|
|
|
|
|
|
|
|
for (c = 0; c < n; c++, data++)
|
2018-01-02 22:36:07 +08:00
|
|
|
{
|
2018-01-03 02:19:59 +08:00
|
|
|
if (data >= buffer + buffer_size)
|
2018-01-02 22:36:07 +08:00
|
|
|
{
|
|
|
|
g_free (cscanline_len);
|
|
|
|
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
|
|
|
|
_("Fatal parse error in brush file: "
|
|
|
|
"RLE compressed brush data corrupt."));
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
*data = ch;
|
|
|
|
}
|
2007-06-04 18:43:31 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* read the following n + 1 chars (no compr) */
|
|
|
|
|
|
|
|
for (c = 0; c < n + 1; c++, j++, data++)
|
2014-07-04 08:18:52 +08:00
|
|
|
{
|
2018-01-03 02:19:59 +08:00
|
|
|
if (data >= buffer + buffer_size)
|
|
|
|
{
|
|
|
|
g_free (cscanline_len);
|
|
|
|
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
|
|
|
|
_("Fatal parse error in brush file: "
|
|
|
|
"RLE compressed brush data corrupt."));
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2014-07-04 08:18:52 +08:00
|
|
|
*data = abr_read_char (input, error);
|
|
|
|
if (error && *error)
|
|
|
|
{
|
|
|
|
g_free (cscanline_len);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
2007-06-04 18:43:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free (cscanline_len);
|
|
|
|
|
2014-07-04 08:18:52 +08:00
|
|
|
return TRUE;
|
2007-06-04 18:43:31 +08:00
|
|
|
}
|