123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- // Flexlay - A Generic 2D Game Editor
- // Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
- //
- // 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 <http://www.gnu.org/licenses/>.
- /* Most of the code below is taken from Gimp2.2:
- gimp_brush_generated module Copyright 1998 Jay Cox <jaycox@earthlink.net>
- */
- #include <iostream>
- #include <string.h>
- #include <ClanLib/Display/pixel_format.h>
- #include "brushmask.hpp"
- #define OVERSAMPLING 5
- typedef int gint;
- typedef unsigned char guchar;
- struct TempBuf {
- gint width;
- gint height;
- guchar *data; /* The data buffer. Do never access this field
- directly, use temp_buf_data() instead !! */
- };
- void
- temp_buf_free (TempBuf *temp_buf)
- {
- // FIXME:
- delete temp_buf;
- }
- guchar *
- temp_buf_data (TempBuf *temp_buf)
- {
- return temp_buf->data;
- }
- TempBuf *
- temp_buf_new (gint width,
- gint height,
- gint bytes,
- gint x,
- gint y,
- guchar *col)
- {
- TempBuf* buf = new TempBuf;
- buf->data = new guchar[width*height];
- memset(buf->data, 0, width*height*sizeof(guchar));
- buf->width = width;
- buf->height = height;
- return buf;
- }
- #define gimp_deg_to_rad(angle) ((angle) * (2.0 * M_PI) / 360.0)
- #define gimp_rad_to_deg(angle) ((angle) * 360.0 / (2.0 * M_PI))
- static double
- gauss (double f)
- {
- /* this aint' a real gauss function */
- if (f < -0.5)
- {
- f = -1.0 - f;
- return (2.0 * f*f);
- }
- if (f < 0.5)
- return (1.0 - 2.0 * f*f);
- f = 1.0 - f;
- return (2.0 * f*f);
- }
- struct GimpVector2
- {
- double x, y;
- };
- struct GimpBrushGenerated
- {
- TempBuf *mask; /* the actual mask */
- GimpVector2 x_axis; /* for calculating brush spacing */
- GimpVector2 y_axis; /* for calculating brush spacing */
- BrushShape shape;
- float radius;
- gint spikes; /* 2 - 20 */
- float hardness; /* 0.0 - 1.0 */
- float aspect_ratio; /* y/x */
- float angle; /* in degrees */
- };
- static void
- gimp_brush_generated_dirty (GimpBrushGenerated *brush)
- {
- gint x, y;
- guchar *centerp;
- double d;
- double exponent;
- guchar a;
- gint length;
- gint width = 0;
- gint height = 0;
- guchar *lookup;
- double sum;
- double c, s, cs, ss;
- double short_radius;
- double buffer[OVERSAMPLING];
- if (brush->mask)
- temp_buf_free (brush->mask);
- s = sin (gimp_deg_to_rad (brush->angle));
- c = cos (gimp_deg_to_rad (brush->angle));
- short_radius = brush->radius / brush->aspect_ratio;
- brush->x_axis.x = c * brush->radius;
- brush->x_axis.y = -1.0 * s * brush->radius;
- brush->y_axis.x = s * short_radius;
- brush->y_axis.y = c * short_radius;
- switch (brush->shape)
- {
- case BRUSH_SHAPE_CIRCLE:
- width = static_cast<int>(ceil (sqrt (brush->x_axis.x * brush->x_axis.x +
- brush->y_axis.x * brush->y_axis.x)));
- height = static_cast<int>(ceil (sqrt (brush->x_axis.y * brush->x_axis.y +
- brush->y_axis.y * brush->y_axis.y)));
- break;
- case BRUSH_SHAPE_SQUARE:
- width = static_cast<int>(ceil (fabs (brush->x_axis.x) + fabs (brush->y_axis.x)));
- height = static_cast<int>(ceil (fabs (brush->x_axis.y) + fabs (brush->y_axis.y)));
- break;
- case BRUSH_SHAPE_DIAMOND:
- width = static_cast<int>(ceil (std::max(fabs (brush->x_axis.x), fabs (brush->y_axis.x))));
- height = static_cast<int>(ceil (std::max(fabs (brush->x_axis.y), fabs (brush->y_axis.y))));
- break;
- default:
- return;
- }
- if (brush->spikes > 2)
- {
- /* could be optimized by respecting the angle */
- width = height = static_cast<int>(ceil (sqrt (brush->radius * brush->radius +
- short_radius * short_radius)));
- brush->y_axis.x = s * brush->radius;
- brush->y_axis.y = c * brush->radius;
- }
- brush->mask = temp_buf_new (width * 2 + 1,
- height * 2 + 1,
- 1, width, height, NULL);
- centerp = temp_buf_data (brush->mask) + height * brush->mask->width + width;
- /* set up lookup table */
- length = static_cast<int>(OVERSAMPLING * ceil (1 + sqrt (2 *
- ceil (brush->radius + 1.0) *
- ceil (brush->radius + 1.0))));
- if ((1.0 - brush->hardness) < 0.0000004)
- exponent = 1000000.0;
- else
- exponent = 0.4 / (1.0 - brush->hardness);
- lookup = new guchar[length];
- sum = 0.0;
- for (x = 0; x < OVERSAMPLING; x++)
- {
- d = fabs ((x + 0.5) / OVERSAMPLING - 0.5);
- if (d > brush->radius)
- buffer[x] = 0.0;
- else
- buffer[x] = gauss (pow (d / brush->radius, exponent));
- sum += buffer[x];
- }
- for (x = 0; d < brush->radius || sum > 0.00001; d += 1.0 / OVERSAMPLING)
- {
- sum -= buffer[x % OVERSAMPLING];
- if (d > brush->radius)
- buffer[x % OVERSAMPLING] = 0.0;
- else
- buffer[x % OVERSAMPLING] = gauss (pow (d / brush->radius, exponent));
- sum += buffer[x % OVERSAMPLING];
- lookup[x++] = static_cast<int>(rint(sum * (255.0 / OVERSAMPLING)));
- }
- while (x < length)
- {
- lookup[x++] = 0;
- }
- cs = cos (- 2 * M_PI / brush->spikes);
- ss = sin (- 2 * M_PI / brush->spikes);
- /* for an even number of spikes compute one half and mirror it */
- for (y = (brush->spikes % 2 ? -height : 0); y <= height; y++)
- {
- for (x = -width; x <= width; x++)
- {
- double tx, ty, angle;
- tx = c*x - s*y;
- ty = fabs (s*x + c*y);
- if (brush->spikes > 2)
- {
- angle = atan2 (ty, tx);
- while (angle > M_PI / brush->spikes)
- {
- double sx = tx, sy = ty;
- tx = cs * sx - ss * sy;
- ty = ss * sx + cs * sy;
- angle -= 2 * M_PI / brush->spikes;
- }
- }
- ty *= brush->aspect_ratio;
- switch (brush->shape)
- {
- case BRUSH_SHAPE_CIRCLE:
- d = sqrt (tx*tx + ty*ty);
- break;
- case BRUSH_SHAPE_SQUARE:
- d = std::max (fabs (tx), fabs (ty));
- break;
- case BRUSH_SHAPE_DIAMOND:
- d = fabs (tx) + fabs (ty);
- break;
- }
- if (d < brush->radius + 1)
- a = lookup[(gint) rint (d * OVERSAMPLING)];
- else
- a = 0;
- centerp[ y * brush->mask->width + x] = a;
- if (brush->spikes % 2 == 0)
- centerp[-1 * y * brush->mask->width - x] = a;
- }
- }
- delete lookup;
- }
- CL_PixelBuffer generate_brushmask(BrushShape shape,
- float radius,
- int spikes, /* 2 - 20 */
- float hardness, /* 0.0 - 1.0 */
- float aspect_ratio, /* y/x */
- float angle) /* in degrees */
- {
- GimpBrushGenerated brush;
- brush.mask = 0;
- brush.shape = shape;
- brush.radius = radius;
- brush.spikes = spikes;
- brush.hardness = hardness;
- brush.aspect_ratio = aspect_ratio;
- brush.angle = angle;
- gimp_brush_generated_dirty(&brush);
- CL_PixelBuffer buffer(brush.mask->width, brush.mask->height, brush.mask->width*4,
- CL_PixelFormat::rgba8888);
- buffer.lock();
- unsigned char* buf = static_cast<unsigned char*>(buffer.get_data());
- // FIXME: Leaving out the right/bottom border, since thats full of
- // random spots... more a workaround than a fix really
- for (int i = 0; i < brush.mask->height * brush.mask->width; ++i)
- {
- buf[i*4+0] = brush.mask->data[i];
- buf[i*4+1] = 255;
- buf[i*4+2] = 255;
- buf[i*4+3] = 255;
- }
- buffer.unlock();
- return buffer;
- }
- #ifdef TEST
- int main()
- {
- GimpBrushGenerated brush;
- brush.mask = 0;
- brush.shape = BRUSH_SHAPE_DIAMOND;
- brush.radius = 512;
- brush.spikes = 19;
- brush.hardness = 0.9;
- brush.aspect_ratio = 1;
- brush.angle = 0;
- gimp_brush_generated_dirty(&brush);
- std::cout << "P2\n";
- std::cout << "# Gimp Brush Generator\n";
- std::cout << brush.mask->width << " " << brush.mask->height << "\n";
- std::cout << "255\n";
- for (int i = 0; i < brush.mask->width * brush.mask->height; ++i)
- std::cout << int(brush.mask->data[i]) << " ";
- temp_buf_free(brush.mask);
- std::cout << std::endl;
- return 0;
- }
- #endif
- /* EOF */
|