void AntiAliasOperation::executePixel(float output[4], int x, int y, void *data) { MemoryBuffer *input_buffer = (MemoryBuffer *)data; const int buffer_width = input_buffer->getWidth(), buffer_height = input_buffer->getHeight(); if (y < 0 || y >= buffer_height || x < 0 || x >= buffer_width) { output[0] = 0.0f; } else { const float *buffer = input_buffer->getBuffer(); const float *row_curr = &buffer[y * buffer_width]; if (x == 0 || x == buffer_width - 1 || y == 0 || y == buffer_height - 1) { output[0] = row_curr[x]; return; } const float *row_prev = &buffer[(y - 1) * buffer_width], *row_next = &buffer[(y + 1) * buffer_width]; float ninepix[9]; if (extrapolate9(&ninepix[0], &ninepix[1], &ninepix[2], &ninepix[3], &ninepix[4], &ninepix[5], &ninepix[6], &ninepix[7], &ninepix[8], &row_prev[x - 1], &row_prev[x], &row_prev[x + 1], &row_curr[x - 1], &row_curr[x], &row_curr[x + 1], &row_next[x - 1], &row_next[x], &row_next[x + 1])) { /* Some rounding magic to so make weighting correct with the * original coefficients. */ unsigned char result = ((3 * ninepix[0] + 5 * ninepix[1] + 3 * ninepix[2] + 5 * ninepix[3] + 6 * ninepix[4] + 5 * ninepix[5] + 3 * ninepix[6] + 5 * ninepix[7] + 3 * ninepix[8]) * 255.0f + 19.0f) / 38.0f; output[0] = result / 255.0f; } else { output[0] = row_curr[x]; } } }
static gboolean process (GeglOperation *operation, GeglBuffer *input, GeglBuffer *output, const GeglRectangle *roi, gint level) { gint components; gint col, c; gfloat *rowbefore; gfloat *rowthis; gfloat *rowafter; gfloat *dest; gfloat *ninepix; gboolean has_alpha; guint alpha; const Babl *format = gegl_operation_get_format (operation, "input"); /* The rectangle of the current row we are working on */ GeglRectangle rowrect; /* The rectangle of the sample we are going to take for the * next line (this does include the interpolation distance!) */ GeglRectangle rownext_bufrect; components = babl_format_get_n_components (format); has_alpha = babl_format_has_alpha (format); alpha = components - 1; /* The original algorithm that appeared in GIMP did a manual clamping * of samples outside the input rectangle, by always allocating a * buffer which is 1-pixel wider than necessary, and then filling the * edges of the buffer with repetitions of the edges. * We don't need this complexity here thanks to the CLAMP abyss policy * which is implemented by GEGL. We still allocate buffers which are * larger, but we let GEGL fill the edges with the clamped values. */ rowbefore = g_new (gfloat, (roi->width + 2) * components); rowthis = g_new (gfloat, (roi->width + 2) * components); rowafter = g_new (gfloat, (roi->width + 2) * components); dest = g_new (gfloat, roi->width * components); ninepix = g_new (gfloat, 9 * components); gegl_rectangle_set (&rowrect, roi->x, roi->y, roi->width, 1); gegl_rectangle_set (&rownext_bufrect, roi->x - 1, roi->y - 1, roi->width + 2, 1); gegl_buffer_get (input, &rownext_bufrect, 1.0, format, rowbefore, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP); ++rownext_bufrect.y; gegl_buffer_get (input, &rownext_bufrect, 1.0, format, rowthis, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP); ++rownext_bufrect.y; gegl_buffer_get (input, &rownext_bufrect, 1.0, format, rowafter, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP); ++rownext_bufrect.y; for (rowrect.y = roi->y; rowrect.y < (roi->y + roi->height); ++rowrect.y) { gfloat *tmp; /* this macro returns the current pixel if it has some opacity. Otherwise * it returns the center pixel of the current 3x3 area. */ #define USE_IF_ALPHA(p) (((!has_alpha) || *((p)+alpha)) ? (p) : &rowthis[(col+1) * components]) for (col = 0; col < roi->width; ++col) { /* do 9x extrapolation pass */ if (((!has_alpha) || (rowthis[(col + 1) * components + alpha] > 0)) && extrapolate9 (components, &ninepix[0 * components], &ninepix[1 * components], &ninepix[2 * components], &ninepix[3 * components], &ninepix[4 * components], &ninepix[5 * components], &ninepix[6 * components], &ninepix[7 * components], &ninepix[8 * components], USE_IF_ALPHA (&rowbefore[(col + 0) * components]), USE_IF_ALPHA (&rowbefore[(col + 1) * components]), USE_IF_ALPHA (&rowbefore[(col + 2) * components]), USE_IF_ALPHA (&rowthis [(col + 0) * components]), &rowthis [(col + 1) * components], USE_IF_ALPHA (&rowthis [(col + 2) * components]), USE_IF_ALPHA (&rowafter [(col + 0) * components]), USE_IF_ALPHA (&rowafter [(col + 1) * components]), USE_IF_ALPHA (&rowafter [(col + 2) * components]) )) { /* subsample results and put into dest */ for (c = 0; c < components; ++c) { #define NINEPIX(index, c) ninepix[(index) * components + (c)] dest[(col * components) + c] = (3 * NINEPIX(0, c) + 5 * NINEPIX(1, c) + 3 * NINEPIX(2, c) + 5 * NINEPIX(3, c) + 6 * NINEPIX(4, c) + 5 * NINEPIX(5, c) + 3 * NINEPIX(6, c) + 5 * NINEPIX(7, c) + 3 * NINEPIX(8, c) /* The GIMP implementation added 19 (out of 255) here before * normalizing (dividing by 38), which is equivalent to a * call to "ceil". We don't need this since we are working * with floating point numbers... */ ) / 38; #undef NINEPIX } } else { memcpy (&dest[col * components], &rowthis[(col + 1) * components], components * sizeof(gfloat)); } } #undef USE_IF_ALPHA /* write result row to dest */ gegl_buffer_set (output, &rowrect, 0, format, &dest[0], GEGL_AUTO_ROWSTRIDE); /* rotate pointers */ tmp = rowbefore; rowbefore = rowthis; rowthis = rowafter; rowafter = tmp; /* populate new after-row */ gegl_buffer_get (input, &rownext_bufrect, 1.0, format, rowafter, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP); ++rownext_bufrect.y; } g_free (rowbefore); g_free (rowthis); g_free (rowafter); g_free (dest); g_free (ninepix); return TRUE; }