PIKApp/libpika/pikatilebackendplugin.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);
}