brushmask.cxx 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. // Code taken from the Gimp
  2. #include <iostream>
  3. #include <math.h>
  4. #include <string.h>
  5. #define OVERSAMPLING 5
  6. typedef int gint;
  7. typedef unsigned char guchar;
  8. struct TempBuf {
  9. gint width;
  10. gint height;
  11. guchar *data; /* The data buffer. Do never access this field
  12. directly, use temp_buf_data() instead !! */
  13. };
  14. void
  15. temp_buf_free (TempBuf *temp_buf)
  16. {
  17. // FIXME:
  18. delete temp_buf;
  19. }
  20. guchar *
  21. temp_buf_data (TempBuf *temp_buf)
  22. {
  23. return temp_buf->data;
  24. }
  25. TempBuf *
  26. temp_buf_new (gint width,
  27. gint height,
  28. gint bytes,
  29. gint x,
  30. gint y,
  31. guchar *col)
  32. {
  33. TempBuf* buf = new TempBuf;
  34. buf->data = new guchar[width*height];
  35. buf->width = width;
  36. buf->height = height;
  37. return buf;
  38. }
  39. #define gimp_deg_to_rad(angle) ((angle) * (2.0 * M_PI) / 360.0)
  40. #define gimp_rad_to_deg(angle) ((angle) * 360.0 / (2.0 * M_PI))
  41. static double
  42. gauss (double f)
  43. {
  44. /* this aint' a real gauss function */
  45. if (f < -0.5)
  46. {
  47. f = -1.0 - f;
  48. return (2.0 * f*f);
  49. }
  50. if (f < 0.5)
  51. return (1.0 - 2.0 * f*f);
  52. f = 1.0 - f;
  53. return (2.0 * f*f);
  54. }
  55. enum GimpBrushGeneratedShape /*< pdb-skip >*/
  56. {
  57. GIMP_BRUSH_GENERATED_CIRCLE, /*< desc="Circle" >*/
  58. GIMP_BRUSH_GENERATED_SQUARE, /*< desc="Square" >*/
  59. GIMP_BRUSH_GENERATED_DIAMOND /*< desc="Diamond" >*/
  60. };
  61. struct GimpVector2
  62. {
  63. double x, y;
  64. };
  65. struct GimpBrushGenerated
  66. {
  67. TempBuf *mask; /* the actual mask */
  68. GimpVector2 x_axis; /* for calculating brush spacing */
  69. GimpVector2 y_axis; /* for calculating brush spacing */
  70. GimpBrushGeneratedShape shape;
  71. float radius;
  72. gint spikes; /* 2 - 20 */
  73. float hardness; /* 0.0 - 1.0 */
  74. float aspect_ratio; /* y/x */
  75. float angle; /* in degrees */
  76. };
  77. static void
  78. gimp_brush_generated_dirty (GimpBrushGenerated *brush)
  79. {
  80. gint x, y;
  81. guchar *centerp;
  82. double d;
  83. double exponent;
  84. guchar a;
  85. gint length;
  86. gint width = 0;
  87. gint height = 0;
  88. guchar *lookup;
  89. double sum;
  90. double c, s, cs, ss;
  91. double short_radius;
  92. double buffer[OVERSAMPLING];
  93. if (brush->mask)
  94. temp_buf_free (brush->mask);
  95. s = sin (gimp_deg_to_rad (brush->angle));
  96. c = cos (gimp_deg_to_rad (brush->angle));
  97. short_radius = brush->radius / brush->aspect_ratio;
  98. brush->x_axis.x = c * brush->radius;
  99. brush->x_axis.y = -1.0 * s * brush->radius;
  100. brush->y_axis.x = s * short_radius;
  101. brush->y_axis.y = c * short_radius;
  102. switch (brush->shape)
  103. {
  104. case GIMP_BRUSH_GENERATED_CIRCLE:
  105. width = static_cast<int>(ceil (sqrt (brush->x_axis.x * brush->x_axis.x +
  106. brush->y_axis.x * brush->y_axis.x)));
  107. height = static_cast<int>(ceil (sqrt (brush->x_axis.y * brush->x_axis.y +
  108. brush->y_axis.y * brush->y_axis.y)));
  109. break;
  110. case GIMP_BRUSH_GENERATED_SQUARE:
  111. width = static_cast<int>(ceil (fabs (brush->x_axis.x) + fabs (brush->y_axis.x)));
  112. height = static_cast<int>(ceil (fabs (brush->x_axis.y) + fabs (brush->y_axis.y)));
  113. break;
  114. case GIMP_BRUSH_GENERATED_DIAMOND:
  115. width = static_cast<int>(ceil (std::max(fabs (brush->x_axis.x), fabs (brush->y_axis.x))));
  116. height = static_cast<int>(ceil (std::max(fabs (brush->x_axis.y), fabs (brush->y_axis.y))));
  117. break;
  118. default:
  119. return;
  120. }
  121. if (brush->spikes > 2)
  122. {
  123. /* could be optimized by respecting the angle */
  124. width = height = static_cast<int>(ceil (sqrt (brush->radius * brush->radius +
  125. short_radius * short_radius)));
  126. brush->y_axis.x = s * brush->radius;
  127. brush->y_axis.y = c * brush->radius;
  128. }
  129. brush->mask = temp_buf_new (width * 2 + 1,
  130. height * 2 + 1,
  131. 1, width, height, NULL);
  132. centerp = temp_buf_data (brush->mask) + height * brush->mask->width + width;
  133. /* set up lookup table */
  134. length = static_cast<int>(OVERSAMPLING * ceil (1 + sqrt (2 *
  135. ceil (brush->radius + 1.0) *
  136. ceil (brush->radius + 1.0))));
  137. if ((1.0 - brush->hardness) < 0.0000004)
  138. exponent = 1000000.0;
  139. else
  140. exponent = 0.4 / (1.0 - brush->hardness);
  141. lookup = new guchar[length];
  142. sum = 0.0;
  143. for (x = 0; x < OVERSAMPLING; x++)
  144. {
  145. d = fabs ((x + 0.5) / OVERSAMPLING - 0.5);
  146. if (d > brush->radius)
  147. buffer[x] = 0.0;
  148. else
  149. buffer[x] = gauss (pow (d / brush->radius, exponent));
  150. sum += buffer[x];
  151. }
  152. for (x = 0; d < brush->radius || sum > 0.00001; d += 1.0 / OVERSAMPLING)
  153. {
  154. sum -= buffer[x % OVERSAMPLING];
  155. if (d > brush->radius)
  156. buffer[x % OVERSAMPLING] = 0.0;
  157. else
  158. buffer[x % OVERSAMPLING] = gauss (pow (d / brush->radius, exponent));
  159. sum += buffer[x % OVERSAMPLING];
  160. lookup[x++] = static_cast<int>(rint(sum * (255.0 / OVERSAMPLING)));
  161. }
  162. while (x < length)
  163. {
  164. lookup[x++] = 0;
  165. }
  166. cs = cos (- 2 * M_PI / brush->spikes);
  167. ss = sin (- 2 * M_PI / brush->spikes);
  168. /* for an even number of spikes compute one half and mirror it */
  169. for (y = (brush->spikes % 2 ? -height : 0); y <= height; y++)
  170. {
  171. for (x = -width; x <= width; x++)
  172. {
  173. double tx, ty, angle;
  174. tx = c*x - s*y;
  175. ty = fabs (s*x + c*y);
  176. if (brush->spikes > 2)
  177. {
  178. angle = atan2 (ty, tx);
  179. while (angle > M_PI / brush->spikes)
  180. {
  181. double sx = tx, sy = ty;
  182. tx = cs * sx - ss * sy;
  183. ty = ss * sx + cs * sy;
  184. angle -= 2 * M_PI / brush->spikes;
  185. }
  186. }
  187. ty *= brush->aspect_ratio;
  188. switch (brush->shape)
  189. {
  190. case GIMP_BRUSH_GENERATED_CIRCLE:
  191. d = sqrt (tx*tx + ty*ty);
  192. break;
  193. case GIMP_BRUSH_GENERATED_SQUARE:
  194. d = std::max (fabs (tx), fabs (ty));
  195. break;
  196. case GIMP_BRUSH_GENERATED_DIAMOND:
  197. d = fabs (tx) + fabs (ty);
  198. break;
  199. }
  200. if (d < brush->radius + 1)
  201. a = lookup[(gint) rint (d * OVERSAMPLING)];
  202. else
  203. a = 0;
  204. centerp[ y * brush->mask->width + x] = a;
  205. if (brush->spikes % 2 == 0)
  206. centerp[-1 * y * brush->mask->width - x] = a;
  207. }
  208. }
  209. delete lookup;
  210. }
  211. int main()
  212. {
  213. GimpBrushGenerated brush;
  214. brush.mask = 0;
  215. brush.shape = GIMP_BRUSH_GENERATED_DIAMOND;
  216. brush.radius = 512;
  217. brush.spikes = 19;
  218. brush.hardness = 0.9;
  219. brush.aspect_ratio = 1;
  220. brush.angle = 0;
  221. gimp_brush_generated_dirty(&brush);
  222. std::cout << "P2\n";
  223. std::cout << "# Gimp Brush Generator\n";
  224. std::cout << brush.mask->width << " " << brush.mask->height << "\n";
  225. std::cout << "255\n";
  226. for (int i = 0; i < brush.mask->width * brush.mask->height; ++i)
  227. std::cout << int(brush.mask->data[i]) << " ";
  228. temp_buf_free(brush.mask);
  229. std::cout << std::endl;
  230. return 0;
  231. }
  232. /* EOF */