462 lines
14 KiB
C
462 lines
14 KiB
C
|
/* PIKA - Photo and Image Kooker Application
|
||
|
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||
|
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||
|
*
|
||
|
* Original copyright, applying to most contents (license remains unchanged):
|
||
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||
|
*
|
||
|
* pikatilebackendplugin.c
|
||
|
* Copyright (C) 2011-2019 Øyvind Kolås <pippin@gimp.org>
|
||
|
* Michael Natterer <mitch@gimp.org>
|
||
|
*
|
||
|
* This program is free software: you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 3 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
#include "config.h"
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include "pika.h"
|
||
|
|
||
|
#include "libpikabase/pikaprotocol.h"
|
||
|
#include "libpikabase/pikawire.h"
|
||
|
|
||
|
#include "pika-shm.h"
|
||
|
#include "pikaplugin-private.h"
|
||
|
#include "pikatilebackendplugin.h"
|
||
|
|
||
|
|
||
|
#define TILE_WIDTH pika_tile_width()
|
||
|
#define TILE_HEIGHT pika_tile_height()
|
||
|
|
||
|
|
||
|
typedef struct _PikaTile PikaTile;
|
||
|
|
||
|
struct _PikaTile
|
||
|
{
|
||
|
guint tile_num; /* the number of this tile within the drawable */
|
||
|
|
||
|
guint ewidth; /* the effective width of the tile */
|
||
|
guint eheight; /* the effective height of the tile */
|
||
|
|
||
|
guchar *data; /* the pixel data for the tile */
|
||
|
};
|
||
|
|
||
|
|
||
|
struct _PikaTileBackendPluginPrivate
|
||
|
{
|
||
|
gint32 drawable_id;
|
||
|
gboolean shadow;
|
||
|
gint width;
|
||
|
gint height;
|
||
|
gint bpp;
|
||
|
gint ntile_rows;
|
||
|
gint ntile_cols;
|
||
|
};
|
||
|
|
||
|
|
||
|
static gpointer pika_tile_backend_plugin_command (GeglTileSource *tile_store,
|
||
|
GeglTileCommand command,
|
||
|
gint x,
|
||
|
gint y,
|
||
|
gint z,
|
||
|
gpointer data);
|
||
|
|
||
|
static gboolean pika_tile_write (PikaTileBackendPlugin *backend_plugin,
|
||
|
gint x,
|
||
|
gint y,
|
||
|
GeglTile *tile);
|
||
|
static GeglTile * pika_tile_read (PikaTileBackendPlugin *backend_plugin,
|
||
|
gint x,
|
||
|
gint y);
|
||
|
|
||
|
static gboolean pika_tile_init (PikaTileBackendPlugin *backend_plugin,
|
||
|
PikaTile *tile,
|
||
|
gint row,
|
||
|
gint col);
|
||
|
static void pika_tile_unset (PikaTileBackendPlugin *backend_plugin,
|
||
|
PikaTile *tile);
|
||
|
static void pika_tile_get (PikaTileBackendPlugin *backend_plugin,
|
||
|
PikaTile *tile);
|
||
|
static void pika_tile_put (PikaTileBackendPlugin *backend_plugin,
|
||
|
PikaTile *tile);
|
||
|
|
||
|
|
||
|
G_DEFINE_TYPE_WITH_PRIVATE (PikaTileBackendPlugin, _pika_tile_backend_plugin,
|
||
|
GEGL_TYPE_TILE_BACKEND)
|
||
|
|
||
|
#define parent_class _pika_tile_backend_plugin_parent_class
|
||
|
|
||
|
|
||
|
static GMutex backend_plugin_mutex;
|
||
|
|
||
|
|
||
|
static void
|
||
|
_pika_tile_backend_plugin_class_init (PikaTileBackendPluginClass *klass)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_pika_tile_backend_plugin_init (PikaTileBackendPlugin *backend)
|
||
|
{
|
||
|
GeglTileSource *source = GEGL_TILE_SOURCE (backend);
|
||
|
|
||
|
backend->priv = _pika_tile_backend_plugin_get_instance_private (backend);
|
||
|
|
||
|
source->command = pika_tile_backend_plugin_command;
|
||
|
}
|
||
|
|
||
|
static gpointer
|
||
|
pika_tile_backend_plugin_command (GeglTileSource *tile_store,
|
||
|
GeglTileCommand command,
|
||
|
gint x,
|
||
|
gint y,
|
||
|
gint z,
|
||
|
gpointer data)
|
||
|
{
|
||
|
PikaTileBackendPlugin *backend_plugin = PIKA_TILE_BACKEND_PLUGIN (tile_store);
|
||
|
gpointer result = NULL;
|
||
|
|
||
|
switch (command)
|
||
|
{
|
||
|
case GEGL_TILE_GET:
|
||
|
/* TODO: fetch mipmapped tiles directly from pika, instead of returning
|
||
|
* NULL to render them locally
|
||
|
*/
|
||
|
if (z == 0)
|
||
|
{
|
||
|
g_mutex_lock (&backend_plugin_mutex);
|
||
|
|
||
|
result = pika_tile_read (backend_plugin, x, y);
|
||
|
|
||
|
g_mutex_unlock (&backend_plugin_mutex);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case GEGL_TILE_SET:
|
||
|
/* TODO: actually store mipmapped tiles */
|
||
|
if (z == 0)
|
||
|
{
|
||
|
g_mutex_lock (&backend_plugin_mutex);
|
||
|
|
||
|
pika_tile_write (backend_plugin, x, y, data);
|
||
|
|
||
|
g_mutex_unlock (&backend_plugin_mutex);
|
||
|
}
|
||
|
|
||
|
gegl_tile_mark_as_stored (data);
|
||
|
break;
|
||
|
|
||
|
case GEGL_TILE_FLUSH:
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
result = gegl_tile_backend_command (GEGL_TILE_BACKEND (tile_store),
|
||
|
command, x, y, z, data);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* public functions */
|
||
|
|
||
|
GeglTileBackend *
|
||
|
_pika_tile_backend_plugin_new (PikaDrawable *drawable,
|
||
|
gint shadow)
|
||
|
{
|
||
|
GeglTileBackend *backend;
|
||
|
PikaTileBackendPlugin *backend_plugin;
|
||
|
const Babl *format = pika_drawable_get_format (drawable);
|
||
|
gint width = pika_drawable_get_width (drawable);
|
||
|
gint height = pika_drawable_get_height (drawable);
|
||
|
|
||
|
backend = g_object_new (PIKA_TYPE_TILE_BACKEND_PLUGIN,
|
||
|
"tile-width", TILE_WIDTH,
|
||
|
"tile-height", TILE_HEIGHT,
|
||
|
"format", format,
|
||
|
NULL);
|
||
|
|
||
|
backend_plugin = PIKA_TILE_BACKEND_PLUGIN (backend);
|
||
|
|
||
|
backend_plugin->priv->drawable_id = pika_item_get_id (PIKA_ITEM (drawable));
|
||
|
backend_plugin->priv->shadow = shadow;
|
||
|
backend_plugin->priv->width = width;
|
||
|
backend_plugin->priv->height = height;
|
||
|
backend_plugin->priv->bpp = pika_drawable_get_bpp (drawable);
|
||
|
backend_plugin->priv->ntile_rows = (height + TILE_HEIGHT - 1) / TILE_HEIGHT;
|
||
|
backend_plugin->priv->ntile_cols = (width + TILE_WIDTH - 1) / TILE_WIDTH;
|
||
|
|
||
|
gegl_tile_backend_set_extent (backend,
|
||
|
GEGL_RECTANGLE (0, 0, width, height));
|
||
|
|
||
|
return backend;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* private functions */
|
||
|
|
||
|
static GeglTile *
|
||
|
pika_tile_read (PikaTileBackendPlugin *backend_plugin,
|
||
|
gint x,
|
||
|
gint y)
|
||
|
{
|
||
|
PikaTileBackendPluginPrivate *priv = backend_plugin->priv;
|
||
|
GeglTileBackend *backend = GEGL_TILE_BACKEND (backend_plugin);
|
||
|
GeglTile *tile;
|
||
|
PikaTile pika_tile = { 0, };
|
||
|
gint tile_size;
|
||
|
guchar *tile_data;
|
||
|
|
||
|
if (! pika_tile_init (backend_plugin, &pika_tile, y, x))
|
||
|
return NULL;
|
||
|
|
||
|
tile_size = gegl_tile_backend_get_tile_size (backend);
|
||
|
tile = gegl_tile_new (tile_size);
|
||
|
tile_data = gegl_tile_get_data (tile);
|
||
|
|
||
|
pika_tile_get (backend_plugin, &pika_tile);
|
||
|
|
||
|
if (pika_tile.ewidth * pika_tile.eheight * priv->bpp == tile_size)
|
||
|
{
|
||
|
memcpy (tile_data, pika_tile.data, tile_size);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
gint tile_stride = TILE_WIDTH * priv->bpp;
|
||
|
gint pika_tile_stride = pika_tile.ewidth * priv->bpp;
|
||
|
guint row;
|
||
|
|
||
|
for (row = 0; row < pika_tile.eheight; row++)
|
||
|
{
|
||
|
memcpy (tile_data + row * tile_stride,
|
||
|
pika_tile.data + row * pika_tile_stride,
|
||
|
pika_tile_stride);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pika_tile_unset (backend_plugin, &pika_tile);
|
||
|
|
||
|
return tile;
|
||
|
}
|
||
|
|
||
|
static gboolean
|
||
|
pika_tile_write (PikaTileBackendPlugin *backend_plugin,
|
||
|
gint x,
|
||
|
gint y,
|
||
|
GeglTile *tile)
|
||
|
{
|
||
|
PikaTileBackendPluginPrivate *priv = backend_plugin->priv;
|
||
|
GeglTileBackend *backend = GEGL_TILE_BACKEND (backend_plugin);
|
||
|
PikaTile pika_tile = { 0, };
|
||
|
gint tile_size;
|
||
|
guchar *tile_data;
|
||
|
|
||
|
if (! pika_tile_init (backend_plugin, &pika_tile, y, x))
|
||
|
return FALSE;
|
||
|
|
||
|
tile_size = gegl_tile_backend_get_tile_size (backend);
|
||
|
tile_data = gegl_tile_get_data (tile);
|
||
|
|
||
|
pika_tile.data = g_new (guchar,
|
||
|
pika_tile.ewidth * pika_tile.eheight *
|
||
|
priv->bpp);
|
||
|
|
||
|
if (pika_tile.ewidth * pika_tile.eheight * priv->bpp == tile_size)
|
||
|
{
|
||
|
memcpy (pika_tile.data, tile_data, tile_size);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
gint tile_stride = TILE_WIDTH * priv->bpp;
|
||
|
gint pika_tile_stride = pika_tile.ewidth * priv->bpp;
|
||
|
guint row;
|
||
|
|
||
|
for (row = 0; row < pika_tile.eheight; row++)
|
||
|
{
|
||
|
memcpy (pika_tile.data + row * pika_tile_stride,
|
||
|
tile_data + row * tile_stride,
|
||
|
pika_tile_stride);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pika_tile_put (backend_plugin, &pika_tile);
|
||
|
pika_tile_unset (backend_plugin, &pika_tile);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
static gboolean
|
||
|
pika_tile_init (PikaTileBackendPlugin *backend_plugin,
|
||
|
PikaTile *tile,
|
||
|
gint row,
|
||
|
gint col)
|
||
|
{
|
||
|
PikaTileBackendPluginPrivate *priv = backend_plugin->priv;
|
||
|
|
||
|
if (row > priv->ntile_rows - 1 ||
|
||
|
col > priv->ntile_cols - 1)
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
tile->tile_num = row * priv->ntile_cols + col;
|
||
|
|
||
|
if (col == (priv->ntile_cols - 1))
|
||
|
tile->ewidth = priv->width - ((priv->ntile_cols - 1) * TILE_WIDTH);
|
||
|
else
|
||
|
tile->ewidth = TILE_WIDTH;
|
||
|
|
||
|
if (row == (priv->ntile_rows - 1))
|
||
|
tile->eheight = priv->height - ((priv->ntile_rows - 1) * TILE_HEIGHT);
|
||
|
else
|
||
|
tile->eheight = TILE_HEIGHT;
|
||
|
|
||
|
tile->data = NULL;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pika_tile_unset (PikaTileBackendPlugin *backend_plugin,
|
||
|
PikaTile *tile)
|
||
|
{
|
||
|
g_clear_pointer (&tile->data, g_free);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pika_tile_get (PikaTileBackendPlugin *backend_plugin,
|
||
|
PikaTile *tile)
|
||
|
{
|
||
|
PikaTileBackendPluginPrivate *priv = backend_plugin->priv;
|
||
|
PikaPlugIn *plug_in = pika_get_plug_in ();
|
||
|
GPTileReq tile_req;
|
||
|
GPTileData *tile_data;
|
||
|
PikaWireMessage msg;
|
||
|
|
||
|
tile_req.drawable_id = priv->drawable_id;
|
||
|
tile_req.tile_num = tile->tile_num;
|
||
|
tile_req.shadow = priv->shadow;
|
||
|
|
||
|
if (! gp_tile_req_write (_pika_plug_in_get_write_channel (plug_in),
|
||
|
&tile_req, plug_in))
|
||
|
pika_quit ();
|
||
|
|
||
|
_pika_plug_in_read_expect_msg (plug_in, &msg, GP_TILE_DATA);
|
||
|
|
||
|
tile_data = msg.data;
|
||
|
if (tile_data->drawable_id != priv->drawable_id ||
|
||
|
tile_data->tile_num != tile->tile_num ||
|
||
|
tile_data->shadow != priv->shadow ||
|
||
|
tile_data->width != tile->ewidth ||
|
||
|
tile_data->height != tile->eheight ||
|
||
|
tile_data->bpp != priv->bpp)
|
||
|
{
|
||
|
#if 0
|
||
|
g_printerr ("tile_data: %d %d %d %d %d %d\n"
|
||
|
"tile: %d %d %d %d %d %d\n",
|
||
|
tile_data->drawable_id,
|
||
|
tile_data->tile_num,
|
||
|
tile_data->shadow,
|
||
|
tile_data->width,
|
||
|
tile_data->height,
|
||
|
tile_data->bpp,
|
||
|
priv->drawable_id,
|
||
|
tile->tile_num,
|
||
|
priv->shadow,
|
||
|
tile->ewidth,
|
||
|
tile->eheight,
|
||
|
priv->bpp);
|
||
|
#endif
|
||
|
g_printerr ("received tile info did not match computed tile info");
|
||
|
pika_quit ();
|
||
|
}
|
||
|
|
||
|
if (tile_data->use_shm)
|
||
|
{
|
||
|
tile->data = g_memdup2 (_pika_shm_addr (),
|
||
|
tile->ewidth * tile->eheight * priv->bpp);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
tile->data = tile_data->data;
|
||
|
tile_data->data = NULL;
|
||
|
}
|
||
|
|
||
|
if (! gp_tile_ack_write (_pika_plug_in_get_write_channel (plug_in),
|
||
|
plug_in))
|
||
|
pika_quit ();
|
||
|
|
||
|
pika_wire_destroy (&msg);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pika_tile_put (PikaTileBackendPlugin *backend_plugin,
|
||
|
PikaTile *tile)
|
||
|
{
|
||
|
PikaTileBackendPluginPrivate *priv = backend_plugin->priv;
|
||
|
PikaPlugIn *plug_in = pika_get_plug_in ();
|
||
|
GPTileReq tile_req;
|
||
|
GPTileData tile_data;
|
||
|
GPTileData *tile_info;
|
||
|
PikaWireMessage msg;
|
||
|
|
||
|
tile_req.drawable_id = -1;
|
||
|
tile_req.tile_num = 0;
|
||
|
tile_req.shadow = 0;
|
||
|
|
||
|
if (! gp_tile_req_write (_pika_plug_in_get_write_channel (plug_in),
|
||
|
&tile_req, plug_in))
|
||
|
pika_quit ();
|
||
|
|
||
|
_pika_plug_in_read_expect_msg (plug_in, &msg, GP_TILE_DATA);
|
||
|
|
||
|
tile_info = msg.data;
|
||
|
|
||
|
tile_data.drawable_id = priv->drawable_id;
|
||
|
tile_data.tile_num = tile->tile_num;
|
||
|
tile_data.shadow = priv->shadow;
|
||
|
tile_data.bpp = priv->bpp;
|
||
|
tile_data.width = tile->ewidth;
|
||
|
tile_data.height = tile->eheight;
|
||
|
tile_data.use_shm = tile_info->use_shm;
|
||
|
tile_data.data = NULL;
|
||
|
|
||
|
if (tile_info->use_shm)
|
||
|
{
|
||
|
memcpy (_pika_shm_addr (),
|
||
|
tile->data,
|
||
|
tile->ewidth * tile->eheight * priv->bpp);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
tile_data.data = tile->data;
|
||
|
}
|
||
|
|
||
|
if (! gp_tile_data_write (_pika_plug_in_get_write_channel (plug_in),
|
||
|
&tile_data, plug_in))
|
||
|
pika_quit ();
|
||
|
|
||
|
if (! tile_info->use_shm)
|
||
|
tile_data.data = NULL;
|
||
|
|
||
|
pika_wire_destroy (&msg);
|
||
|
|
||
|
_pika_plug_in_read_expect_msg (plug_in, &msg, GP_TILE_ACK);
|
||
|
|
||
|
pika_wire_destroy (&msg);
|
||
|
}
|