deinterlace.c 10.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
/* This file is an image processing operation for GEGL
 *
 * GEGL is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * GEGL 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
 *
 * Copyright (C) 1997 Andrew Kieschnick (andrewk@mail.utexas.edu)
 *
 * Original deinterlace for GIMP 0.54 API by Federico Mena Quintero
 *
 * Copyright (C) 1996 Federico Mena Quintero
 *
 * Copyright (C) 2011 Robert Sasu <sasu.robert@gmail.com>
 */

#include "config.h"
#include <glib/gi18n-lib.h>

28
#ifdef GEGL_PROPERTIES
29

30
enum_start (gegl_deinterlace_keep)
31 32
  enum_value (GEGL_DEINTERLACE_KEEP_EVEN, "even", N_("Keep even fields"))
  enum_value (GEGL_DEINTERLACE_KEEP_ODD,  "odd",  N_("Keep odd fields"))
33
enum_end (GeglDeinterlaceKeep)
34

35 36 37 38
property_enum (keep, _("Keep"),
  GeglDeinterlaceKeep, gegl_deinterlace_keep,
  GEGL_DEINTERLACE_KEEP_EVEN)
  description(_("Keep even or odd fields"))
39

40 41 42 43
property_enum (orientation, _("Orientation"),
  GeglOrientation, gegl_orientation,
  GEGL_ORIENTATION_HORIZONTAL)
  description(_("Deinterlace horizontally or vertically"))
44

45 46 47
property_int  (size, _("Block size"), 1)
  value_range (0, 100)
  description (_("Block size of deinterlacing rows/columns"))
48 49 50

#else

51 52
#define GEGL_OP_AREA_FILTER
#define GEGL_OP_C_FILE "deinterlace.c"
53

54
#include "gegl-op.h"
55 56 57
#include <stdio.h>
#include <math.h>

58 59
static void
prepare (GeglOperation *operation)
60 61
{
  GeglOperationAreaFilter *op_area = GEGL_OPERATION_AREA_FILTER (operation);
62
  GeglProperties              *o       = GEGL_PROPERTIES (operation);
63

64
  if (o->orientation == GEGL_ORIENTATION_HORIZONTAL)
65 66 67 68
    {
      op_area->left = op_area->right = 0;
      op_area->top = op_area->bottom = o->size + 1;
    }
69
  else
70 71 72 73
    {
      op_area->left = op_area->right = o->size + 1;
      op_area->top = op_area->bottom = 0;
    }
74

75
  gegl_operation_set_format (operation, "input",
76 77 78 79 80 81
                             babl_format ("RGBA float"));
  gegl_operation_set_format (operation, "output",
                             babl_format ("RGBA float"));
}

static void
82 83 84 85 86 87 88 89
deinterlace_horizontal (gfloat              *src_buf,
                        gfloat              *dest,
                        const GeglRectangle *result,
                        const GeglRectangle *extended,
                        const GeglRectangle *boundary,
                        gint                 inter,
                        gint                 y,
                        gint                 size)
90
{
91 92 93 94 95 96 97 98
  gfloat upper[4];
  gfloat lower[4];
  gfloat temp_buf[4];
  gint   x;
  gint   up_offset;
  gint   low_offset;
  gint   offset = 0;
  gint   i;
99

100
  for (x=0; x < result->width; x++)
101 102 103
    {
      gfloat ualpha, lalpha, temp;
      gfloat alpha = 0;
104

105
      temp_buf[0] = temp_buf[1] = temp_buf[2] = temp_buf[3] = 0;
106

107
      for (i = 0; i < size; i++)
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
        {
          gint b;

          if (y  - i> 0)
            up_offset = (y - i - extended->y) * extended->width * 4;
          else
            up_offset = inter * extended->width * 4;

          if (y + i + 1 < boundary->height)
            low_offset = (y + i + 1 - extended->y) * extended->width * 4;
          else
            low_offset = (y - 1 + inter - extended->y) * extended->width * 4;

          offset = (y - result->y) * extended->width * 4;

          for (b=0; b<4; b++)
124 125 126 127
            {
              upper[b] = src_buf[up_offset + x * 4 + b];
              lower[b] = src_buf[low_offset + x * 4 + b];
            }
128 129 130 131 132 133 134

          ualpha = upper[3];
          lalpha = lower[3];
          temp   = ualpha + lalpha;
          alpha += temp;

          for (b=0; b < 3; b++)
135 136 137 138 139 140 141 142 143 144
            temp_buf[b] += (upper[b] * ualpha +
                            lower[b] * lalpha);
        }

      if ((dest[offset + x * 4 + 3] = alpha / (2 * size)))
        {
          gint b;
          for (b=0; b < 3; b++)
            dest[offset + x * 4 + b] = temp_buf[b] / alpha;

145
        }
146
    }
147 148 149
}

static void
150 151 152 153 154 155 156 157
deinterlace_vertical (gfloat              *src_buf,
                      gfloat              *dest,
                      const GeglRectangle *result,
                      const GeglRectangle *extended,
                      const GeglRectangle *boundary,
                      gint                 inter,
                      gint                 x,
                      gint                 size)
158
{
159 160 161 162 163 164 165 166
  gfloat upper[4];
  gfloat lower[4];
  gfloat temp_buf[4];
  gint   y;
  gint   up_offset;
  gint   low_offset;
  gint   offset = 0;
  gint   i;
167

168
  for (y=result->y; y < result->y + result->height; y++)
169 170 171
    {
      gfloat ualpha, lalpha, temp;
      gfloat alpha = 0;
172

173
      temp_buf[0] = temp_buf[1] = temp_buf[2] = temp_buf[3] = 0;
174

175
      for (i = 0; i < size; i++)
176 177 178 179 180
        {
          gint b;

          if (x  - i > 0)
            up_offset = (y - extended->y) * extended->width * 4
181
              + (x - i - extended->x) * 4;
182 183 184 185
          else
            up_offset = (y - extended->y) * extended->width * 4 + inter * 4;

          if (x + i + 1 < boundary->width)
186 187
            low_offset = (y - extended->y) * extended->width * 4 +
              (x + i + 1 - extended->x) * 4;
188
          else
189 190
            low_offset = (y - extended->y) * extended->width * 4 +
              (x + i - 1 + inter - extended->x) * 4;
191 192 193 194

          offset = (y - result->y) * result->width * 4 + (x - result->x) * 4;

          for (b=0; b<4; b++)
195 196 197 198
            {
              upper[b] = src_buf[up_offset + b];
              lower[b] = src_buf[low_offset + b];
            }
199 200 201 202 203 204 205

          ualpha = upper[3];
          lalpha = lower[3];
          temp   = ualpha + lalpha;
          alpha += temp;

          for (b=0; b < 3; b++)
206 207 208 209 210 211 212 213 214
            temp_buf[b] += (upper[b] * ualpha +
                            lower[b] * lalpha);
        }

      if ((dest[offset + 3] = alpha / (2 * size)))
        {
          gint b;
          for (b=0; b < 3; b++)
            dest[offset + b] = temp_buf[b] / alpha;
215
        }
216
    }
217 218 219 220 221
}

static GeglRectangle
get_effective_area (GeglOperation *operation)
{
222 223 224
  GeglRectangle  result  = { 0, 0, 0, 0 };
  GeglRectangle *in_rect = gegl_operation_source_get_bounding_box (operation,
                                                                   "input");
225

226
  gegl_rectangle_copy (&result, in_rect);
227 228 229 230 231 232 233 234

  return result;
}

static gboolean
process (GeglOperation       *operation,
         GeglBuffer          *input,
         GeglBuffer          *output,
235 236
         const GeglRectangle *result,
         gint                 level)
237
{
238
  GeglProperties              *o        = GEGL_PROPERTIES (operation);
239
  GeglOperationAreaFilter *op_area  = GEGL_OPERATION_AREA_FILTER (operation);
240
  const Babl              *format   = babl_format ("RGBA float");
241 242 243 244
  GeglRectangle            rect;
  GeglRectangle            boundary = get_effective_area (operation);
  gint                     x, y;
  gfloat                  *dst_buf, *src_buf;
245 246

  rect.x      = CLAMP (result->x - op_area->left, boundary.x, boundary.x +
247
                       boundary.width);
248
  rect.width  = CLAMP (result->width + op_area->left + op_area->right, 0,
249
                       boundary.width);
250
  rect.y      = CLAMP (result->y - op_area->top, boundary.y, boundary.y +
251
                       boundary.width);
252
  rect.height = CLAMP (result->height + op_area->top + op_area->bottom, 0,
253
                       boundary.height);
254 255 256 257

  dst_buf = g_new0 (gfloat, result->height * result->width * 4);
  src_buf = g_new0 (gfloat, rect.height * rect.width * 4);

258 259 260 261
  gegl_buffer_get (input, result, 1.0, format, dst_buf,
                   GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
  gegl_buffer_get (input, &rect, 1.0, format, src_buf,
                   GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
262

263
  if (o->orientation == GEGL_ORIENTATION_HORIZONTAL)
264
    {
265
      for (y = result->y; y < result->y + result->height; y++)
266 267 268 269 270 271 272
        if ((o->keep == GEGL_DEINTERLACE_KEEP_EVEN && (y % 2 == 0)) ||
            (o->keep == GEGL_DEINTERLACE_KEEP_ODD  && (y % 2 != 0)))
          {
            deinterlace_horizontal (src_buf, dst_buf, result, &rect, &boundary,
                                    o->keep,
                                    y, o->size);
          }
273
    }
274
  else
275
    {
276
      for (x = result->x; x < result->x + result->width; x++)
277 278 279 280 281 282 283
        if ((o->keep == GEGL_DEINTERLACE_KEEP_EVEN && (x % 2 == 0)) ||
            (o->keep == GEGL_DEINTERLACE_KEEP_ODD  && (x % 2 != 0)))
          {
            deinterlace_vertical (src_buf, dst_buf, result, &rect, &boundary,
                                  o->keep,
                                  x, o->size);
          }
284 285
    }

286
  gegl_buffer_set (output, result, 0, format, dst_buf, GEGL_AUTO_ROWSTRIDE);
287 288 289 290 291 292 293 294 295 296

  g_free (src_buf);
  g_free (dst_buf);

  return  TRUE;
}

static GeglRectangle
get_bounding_box (GeglOperation *operation)
{
297
  GeglRectangle  result = { 0, 0, 0, 0 };
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
  GeglRectangle *in_rect;

  in_rect = gegl_operation_source_get_bounding_box (operation, "input");
  if (!in_rect)
    return result;

  return *in_rect;
}

static GeglRectangle
get_required_for_output (GeglOperation       *operation,
                         const gchar         *input_pad,
                         const GeglRectangle *roi)
{
  return get_bounding_box (operation);
}

static void
316
gegl_op_class_init (GeglOpClass *klass)
317 318 319 320 321 322 323
{
  GeglOperationClass       *operation_class;
  GeglOperationFilterClass *filter_class;

  operation_class = GEGL_OPERATION_CLASS (klass);
  filter_class    = GEGL_OPERATION_FILTER_CLASS (klass);

324
  operation_class->prepare                 = prepare;
325 326
  operation_class->get_bounding_box        = get_bounding_box;
  operation_class->get_required_for_output = get_required_for_output;
327
  filter_class->process                    = process;
328

329
  gegl_operation_class_set_keys (operation_class,
330
    "name",               "gegl:deinterlace",
331
    "title",              _("Deinterlace"),
332
    "categories",         "enhance",
333
    "license",            "GPL3+",
334
    "position-dependent", "true",
335
    "description", _("Fix images where every other row or column is missing"),
336
    NULL);
337 338 339
}

#endif