1206 lines
33 KiB
C
1206 lines
33 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
|
|
*
|
|
* 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 <gtk/gtk.h>
|
|
|
|
#include <libpika/pika.h>
|
|
|
|
#include "pikaressionist.h"
|
|
#include "brush.h"
|
|
#include "orientation.h"
|
|
#include "placement.h"
|
|
#include "ppmtool.h"
|
|
#include "preview.h"
|
|
#include "random.h"
|
|
#include "size.h"
|
|
#include "infile.h"
|
|
|
|
#include <libpika/stdplugins-intl.h>
|
|
|
|
static pikaressionist_vals_t runningvals;
|
|
|
|
static double
|
|
get_siz_from_pcvals (double x, double y)
|
|
{
|
|
return getsiz_proto (x, y, pcvals.num_size_vectors, pcvals.size_vectors,
|
|
pcvals.size_strength_exponent, pcvals.size_voronoi);
|
|
}
|
|
|
|
static int
|
|
get_pixel_value (double dir)
|
|
{
|
|
while (dir < 0.0)
|
|
dir += 360.0;
|
|
while (dir >= 360.0)
|
|
dir -= 360.0;
|
|
return dir * 255.0 / 360.0;
|
|
}
|
|
|
|
static void
|
|
prepare_brush (ppm_t *p)
|
|
{
|
|
int x, y;
|
|
int rowstride = p->width * 3;
|
|
|
|
for (y = 0; y< p->height; y++)
|
|
{
|
|
for (x = 0; x < p->width; x++)
|
|
{
|
|
p->col[y * rowstride + x * 3 + 1] = 0;
|
|
}
|
|
}
|
|
|
|
for (y = 1; y< p->height; y++)
|
|
{
|
|
for (x = 1; x < p->width; x++)
|
|
{
|
|
int v = p->col[y * rowstride + x * 3] -
|
|
p->col[(y - 1) * rowstride + (x - 1) * 3];
|
|
if (v < 0)
|
|
v = 0;
|
|
p->col[y * rowstride + x * 3 + 1] = v;
|
|
}
|
|
}
|
|
}
|
|
|
|
static double
|
|
sum_brush (ppm_t *p)
|
|
{
|
|
double sum = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < p->width*3*p->height; i += 3)
|
|
sum += p->col[i];
|
|
return sum;
|
|
}
|
|
|
|
/* TODO : Use r = rgb[0]; g = rgb[1] ; b = rgb[2]; instead of
|
|
* the direct references here.
|
|
* */
|
|
static int
|
|
get_hue (guchar *rgb)
|
|
{
|
|
double h, v, temp, diff;
|
|
/* TODO : There seems to be some typos in the comments here.
|
|
* Ask vidar what he meant.
|
|
* */
|
|
if ((rgb[0] == rgb[1]) && (rgb[0] == rgb[2])) /* Gray */
|
|
return 0;
|
|
v = (rgb[0] > rgb[1] ? rgb[0] : rgb[1]); /* v = st<F8>rste verdi */
|
|
if (rgb[2] > v)
|
|
v = rgb[2];
|
|
temp = (rgb[0] > rgb[1] ? rgb[1] : rgb[0] ); /* temp = minste */
|
|
if (rgb[2] < temp)
|
|
temp = rgb[2];
|
|
diff = v - temp;
|
|
|
|
if (v == rgb[0])
|
|
h = ((double)rgb[1] - rgb[2]) / diff;
|
|
else if(v == rgb[1])
|
|
h = ((double)rgb[2] - rgb[0]) / diff + 2;
|
|
else /* v == rgb[2] */
|
|
h = ((double)rgb[0] - rgb[1]) / diff + 4;
|
|
if(h < 0) h += 6;
|
|
return h * 255.0 / 6.0;
|
|
}
|
|
|
|
static int
|
|
choose_best_brush (ppm_t *p, ppm_t *a, int tx, int ty,
|
|
ppm_t *brushes, int num_brushes,
|
|
double *brushes_sum, int start, int step)
|
|
{
|
|
double dev, thissum;
|
|
double bestdev = 0.0;
|
|
double r, g, b;
|
|
int best = -1;
|
|
int x, y, h;
|
|
long i;
|
|
GList *brlist = NULL;
|
|
|
|
for (i = start; i < num_brushes; i += step)
|
|
{
|
|
ppm_t *brush = &brushes[i];
|
|
#if 0
|
|
thissum = 0.0;
|
|
#endif
|
|
thissum = brushes_sum[i];
|
|
|
|
/* TODO: Pointer-arithmeticize this code */
|
|
r = g = b = 0.0;
|
|
for (y = 0; y < brush->height; y++)
|
|
{
|
|
guchar *row = p->col + (ty + y) * p->width * 3;
|
|
|
|
for (x = 0; x < brush->width; x++)
|
|
{
|
|
int k = (tx + x) * 3;
|
|
double v;
|
|
|
|
if ((h = brush->col[(y * brush->width * 3) + x * 3]))
|
|
{
|
|
#if 0
|
|
thissum += h;
|
|
#endif
|
|
v = h / 255.0;
|
|
r += row[k+0] * v;
|
|
g += row[k+1] * v;
|
|
b += row[k+2] * v;
|
|
}
|
|
}
|
|
}
|
|
r = r * 255.0 / thissum;
|
|
g = g * 255.0 / thissum;
|
|
b = b * 255.0 / thissum;
|
|
|
|
dev = 0.0;
|
|
for (y = 0; y < brush->height; y++)
|
|
{
|
|
guchar *row = p->col + (ty + y) * p->width * 3;
|
|
|
|
for (x = 0; x < brush->width; x++)
|
|
{
|
|
int k = (tx + x) * 3;
|
|
double v;
|
|
|
|
if ((h = brush->col[(y * brush->width * 3) + x * 3]))
|
|
{
|
|
v = h / 255.0;
|
|
dev += fabs (row[k+0] - r) * v;
|
|
dev += fabs (row[k+1] - g) * v;
|
|
dev += fabs (row[k+2] - b) * v;
|
|
if (img_has_alpha)
|
|
dev += a->col[(ty + y) * a->width * 3 + (tx + x) * 3] * v;
|
|
}
|
|
}
|
|
}
|
|
dev /= thissum;
|
|
|
|
if ((best == -1) || (dev < bestdev))
|
|
{
|
|
if (brlist)
|
|
g_list_free (brlist);
|
|
brlist = NULL;
|
|
}
|
|
|
|
if (dev <= bestdev || best < 0)
|
|
{
|
|
best = i;
|
|
bestdev = dev;
|
|
brlist = g_list_append (brlist, GINT_TO_POINTER (i));
|
|
}
|
|
if (dev < runningvals.devthresh)
|
|
break;
|
|
}
|
|
|
|
if (!brlist)
|
|
{
|
|
g_printerr("What!? No brushes?!\n");
|
|
return 0;
|
|
}
|
|
|
|
i = g_rand_int_range (random_generator, 0, g_list_length (brlist));
|
|
best = GPOINTER_TO_INT ((g_list_nth (brlist,i))->data);
|
|
g_list_free (brlist);
|
|
|
|
return best;
|
|
}
|
|
|
|
static void
|
|
apply_brush (ppm_t *brush,
|
|
ppm_t *shadow,
|
|
ppm_t *p, ppm_t *a,
|
|
int tx, int ty, int r, int g, int b)
|
|
{
|
|
ppm_t tmp;
|
|
ppm_t atmp;
|
|
double v, h;
|
|
int x, y;
|
|
double edgedarken = 1.0 - runningvals.general_dark_edge;
|
|
double relief = runningvals.brush_relief / 100.0;
|
|
int shadowdepth = pcvals.general_shadow_depth;
|
|
int shadowblur = pcvals.general_shadow_blur;
|
|
|
|
atmp.col = 0;
|
|
atmp.width = 0;
|
|
|
|
tmp = *p;
|
|
if (img_has_alpha)
|
|
atmp = *a;
|
|
|
|
if (shadow)
|
|
{
|
|
int sx = tx + shadowdepth - shadowblur * 2;
|
|
int sy = ty + shadowdepth - shadowblur * 2;
|
|
|
|
for (y = 0; y < shadow->height; y++)
|
|
{
|
|
guchar *row, *arow = NULL;
|
|
|
|
if ((sy + y) < 0)
|
|
continue;
|
|
if ((sy + y) >= tmp.height)
|
|
break;
|
|
row = tmp.col + (sy + y) * tmp.width * 3;
|
|
|
|
if (img_has_alpha)
|
|
arow = atmp.col + (sy + y) * atmp.width * 3;
|
|
|
|
for (x = 0; x < shadow->width; x++)
|
|
{
|
|
int k = (sx + x) * 3;
|
|
|
|
if ((sx + x) < 0)
|
|
continue;
|
|
if ((sx + x) >= tmp.width)
|
|
break;
|
|
|
|
h = shadow->col[y * shadow->width * 3 + x * 3 + 2];
|
|
|
|
if (!h)
|
|
continue;
|
|
v = 1.0 - (h / 255.0 * runningvals.general_shadow_darkness / 100.0);
|
|
|
|
row[k+0] *= v;
|
|
row[k+1] *= v;
|
|
row[k+2] *= v;
|
|
if (img_has_alpha)
|
|
arow[k] *= v;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (y = 0; y < brush->height; y++)
|
|
{
|
|
guchar *row = tmp.col + (ty + y) * tmp.width * 3;
|
|
guchar *arow = NULL;
|
|
|
|
if (img_has_alpha)
|
|
arow = atmp.col + (ty + y) * atmp.width * 3;
|
|
|
|
for (x = 0; x < brush->width; x++)
|
|
{
|
|
int k = (tx + x) * 3;
|
|
h = brush->col[y * brush->width * 3 + x * 3];
|
|
|
|
if (!h) continue;
|
|
|
|
if (runningvals.color_brushes)
|
|
{
|
|
v = 1.0 - brush->col[y * brush->width * 3 + x * 3 + 2] / 255.0;
|
|
row[k+0] *= v;
|
|
row[k+1] *= v;
|
|
row[k+2] *= v;
|
|
if (img_has_alpha)
|
|
arow[(tx + x) * 3] *= v;
|
|
}
|
|
v = (1.0 - h / 255.0) * edgedarken;
|
|
row[k+0] *= v;
|
|
row[k+1] *= v;
|
|
row[k+2] *= v;
|
|
if(img_has_alpha) arow[k] *= v;
|
|
|
|
v = h / 255.0;
|
|
row[k+0] += r * v;
|
|
row[k+1] += g * v;
|
|
row[k+2] += b * v;
|
|
}
|
|
}
|
|
|
|
if (relief > 0.001)
|
|
{
|
|
for (y = 1; y < brush->height; y++)
|
|
{
|
|
guchar *row = tmp.col + (ty + y) * tmp.width * 3;
|
|
|
|
for (x = 1; x < brush->width; x++)
|
|
{
|
|
int k = (tx + x) * 3;
|
|
h = brush->col[y * brush->width * 3 + x * 3 + 1] * relief;
|
|
if (h < 0.001)
|
|
continue;
|
|
if (h > 255) h = 255;
|
|
row[k+0] = (row[k+0] * (255-h) + 255 * h) / 255;
|
|
row[k+1] = (row[k+1] * (255-h) + 255 * h) / 255;
|
|
row[k+2] = (row[k+2] * (255-h) + 255 * h) / 255;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
repaint (ppm_t *p, ppm_t *a)
|
|
{
|
|
int x, y;
|
|
int tx = 0, ty = 0;
|
|
ppm_t tmp = {0, 0, NULL};
|
|
ppm_t atmp = {0, 0, NULL};
|
|
int r, g, b, h, i, j, on, sn;
|
|
int num_brushes, maxbrushwidth, maxbrushheight;
|
|
guchar back[3] = {0, 0, 0};
|
|
ppm_t *brushes, *shadows;
|
|
ppm_t *brush, *shadow = NULL;
|
|
double *brushes_sum;
|
|
int cx, cy, maxdist;
|
|
double scale, relief, startangle, anglespan, density, bgamma;
|
|
int max_progress;
|
|
ppm_t paper_ppm = {0, 0, NULL};
|
|
ppm_t dirmap = {0, 0, NULL};
|
|
ppm_t sizmap = {0, 0, NULL};
|
|
int *xpos = NULL, *ypos = NULL;
|
|
int progstep;
|
|
static int running = 0;
|
|
|
|
int dropshadow = pcvals.general_drop_shadow;
|
|
int shadowblur = pcvals.general_shadow_blur;
|
|
|
|
if (running)
|
|
return;
|
|
running++;
|
|
|
|
runningvals = pcvals;
|
|
|
|
/* Shouldn't be necessary, but... */
|
|
if (img_has_alpha)
|
|
if ((p->width != a->width) || (p->height != a->height))
|
|
{
|
|
g_printerr ("Huh? Image size != alpha size?\n");
|
|
return;
|
|
}
|
|
|
|
num_brushes = runningvals.orient_num * runningvals.size_num;
|
|
startangle = runningvals.orient_first;
|
|
anglespan = runningvals.orient_last;
|
|
|
|
density = runningvals.brush_density;
|
|
|
|
if (runningvals.place_type == PLACEMENT_TYPE_EVEN_DIST)
|
|
density /= 3.0;
|
|
|
|
bgamma = runningvals.brushgamma;
|
|
|
|
brushes = g_malloc (num_brushes * sizeof (ppm_t));
|
|
brushes_sum = g_malloc (num_brushes * sizeof (double));
|
|
|
|
if (dropshadow)
|
|
shadows = g_malloc (num_brushes * sizeof (ppm_t));
|
|
else
|
|
shadows = NULL;
|
|
|
|
brushes[0].col = NULL;
|
|
brush_get_selected (&brushes[0]);
|
|
|
|
resize (&brushes[0],
|
|
brushes[0].width,
|
|
brushes[0].height * pow (10, runningvals.brush_aspect));
|
|
scale = runningvals.size_last / MAX (brushes[0].width, brushes[0].height);
|
|
|
|
if (bgamma != 1.0)
|
|
ppm_apply_gamma (&brushes[0], 1.0 / bgamma, 1,1,1);
|
|
|
|
resize (&brushes[0], brushes[0].width * scale, brushes[0].height * scale);
|
|
i = 1 + sqrt (brushes[0].width * brushes[0].width +
|
|
brushes[0].height * brushes[0].height);
|
|
ppm_pad (&brushes[0], i-brushes[0].width, i-brushes[0].width,
|
|
i - brushes[0].height, i - brushes[0].height, back);
|
|
|
|
for (i = 1; i < num_brushes; i++)
|
|
{
|
|
brushes[i].col = NULL;
|
|
ppm_copy (&brushes[0], &brushes[i]);
|
|
}
|
|
|
|
for (i = 0; i < runningvals.size_num; i++)
|
|
{
|
|
double sv;
|
|
if (runningvals.size_num > 1)
|
|
sv = i / (runningvals.size_num - 1.0);
|
|
else sv = 1.0;
|
|
for (j = 0; j < runningvals.orient_num; j++)
|
|
{
|
|
h = j + i * runningvals.orient_num;
|
|
free_rotate (&brushes[h],
|
|
startangle + j * anglespan / runningvals.orient_num);
|
|
rescale (&brushes[h],
|
|
( sv * runningvals.size_first +
|
|
(1.0-sv) * runningvals.size_last ) / runningvals.size_last);
|
|
autocrop (&brushes[h],1);
|
|
}
|
|
}
|
|
|
|
/* Brush-debugging */
|
|
#if 0
|
|
for (i = 0; i < num_brushes; i++)
|
|
{
|
|
char tmp[1000];
|
|
g_snprintf (tmp, sizeof (tmp), "/tmp/_brush%03d.ppm", i);
|
|
ppm_save (&brushes[i], tmp);
|
|
}
|
|
#endif
|
|
|
|
for (i = 0; i < num_brushes; i++)
|
|
{
|
|
if (!runningvals.color_brushes)
|
|
prepare_brush (&brushes[i]);
|
|
brushes_sum[i] = sum_brush (&brushes[i]);
|
|
}
|
|
|
|
brush = &brushes[0];
|
|
|
|
maxbrushwidth = maxbrushheight = 0;
|
|
for (i = 0; i < num_brushes; i++)
|
|
{
|
|
if (brushes[i].width > maxbrushwidth)
|
|
maxbrushwidth = brushes[i].width;
|
|
if (brushes[i].height > maxbrushheight)
|
|
maxbrushheight = brushes[i].height;
|
|
}
|
|
|
|
for (i = 0; i < num_brushes; i++)
|
|
{
|
|
int xp, yp;
|
|
guchar blk[3] = {0, 0, 0};
|
|
|
|
xp = maxbrushwidth - brushes[i].width;
|
|
yp = maxbrushheight - brushes[i].height;
|
|
if (xp || yp)
|
|
ppm_pad (&brushes[i], xp / 2, xp - xp / 2, yp / 2, yp - yp / 2, blk);
|
|
}
|
|
|
|
if (dropshadow)
|
|
{
|
|
for (i = 0; i < num_brushes; i++)
|
|
{
|
|
shadows[i].col = NULL;
|
|
ppm_copy (&brushes[i], &shadows[i]);
|
|
ppm_apply_gamma (&shadows[i], 0, 1,1,0);
|
|
ppm_pad (&shadows[i], shadowblur*2, shadowblur*2,
|
|
shadowblur*2, shadowblur*2, back);
|
|
for (j = 0; j < shadowblur; j++)
|
|
blur (&shadows[i], 2, 2);
|
|
#if 0
|
|
autocrop (&shadows[i],1);
|
|
#endif
|
|
}
|
|
#if 0
|
|
maxbrushwidth += shadowdepth*3;
|
|
maxbrushheight += shadowdepth*3;
|
|
#endif
|
|
}
|
|
|
|
/* For extra annoying debugging :-) */
|
|
#if 0
|
|
ppm_save (brushes, "/tmp/__brush.ppm");
|
|
if (shadows) ppm_save (shadows, "/tmp/__shadow.ppm");
|
|
system ("xv /tmp/__brush.ppm & xv /tmp/__shadow.ppm & ");
|
|
#endif
|
|
|
|
if (runningvals.general_paint_edges)
|
|
{
|
|
edgepad (p, maxbrushwidth, maxbrushwidth,
|
|
maxbrushheight, maxbrushheight);
|
|
if (img_has_alpha)
|
|
edgepad (a, maxbrushwidth, maxbrushwidth,
|
|
maxbrushheight, maxbrushheight);
|
|
}
|
|
|
|
if (img_has_alpha)
|
|
{
|
|
/* Initially fully transparent */
|
|
if (runningvals.general_background_type == BG_TYPE_TRANSPARENT)
|
|
{
|
|
guchar tmpcol[3] = {255, 255, 255};
|
|
|
|
ppm_new (&atmp, a->width, a->height);
|
|
fill (&atmp, tmpcol);
|
|
}
|
|
else
|
|
{
|
|
ppm_copy (a, &atmp);
|
|
}
|
|
}
|
|
|
|
if (runningvals.general_background_type == BG_TYPE_SOLID)
|
|
{
|
|
guchar tmpcol[3];
|
|
|
|
ppm_new (&tmp, p->width, p->height);
|
|
pika_rgb_get_uchar (&runningvals.color,
|
|
&tmpcol[0], &tmpcol[1], &tmpcol[2]);
|
|
fill (&tmp, tmpcol);
|
|
}
|
|
else if (runningvals.general_background_type == BG_TYPE_KEEP_ORIGINAL)
|
|
{
|
|
ppm_copy (p, &tmp);
|
|
}
|
|
else
|
|
{
|
|
int dx, dy;
|
|
|
|
ppm_new (&tmp, p->width, p->height);
|
|
ppm_load (runningvals.selected_paper, &paper_ppm);
|
|
|
|
if (runningvals.paper_scale != 100.0)
|
|
{
|
|
scale = runningvals.paper_scale / 100.0;
|
|
resize (&paper_ppm, paper_ppm.width * scale, paper_ppm.height * scale);
|
|
}
|
|
|
|
if (runningvals.paper_invert)
|
|
ppm_apply_gamma (&paper_ppm, -1.0, 1, 1, 1);
|
|
|
|
dx = runningvals.general_paint_edges ? paper_ppm.width - maxbrushwidth : 0;
|
|
dy = runningvals.general_paint_edges ? paper_ppm.height - maxbrushheight : 0;
|
|
|
|
for (y = 0; y < tmp.height; y++)
|
|
{
|
|
int lx;
|
|
int ry = (y + dy) % paper_ppm.height;
|
|
|
|
for (x = 0; x < tmp.width; x+=lx)
|
|
{
|
|
int rx = (x + dx) % paper_ppm.width;
|
|
|
|
lx = MIN (tmp.width - x, paper_ppm.width - rx);
|
|
|
|
memcpy (&tmp.col[y * tmp.width * 3 + x * 3],
|
|
&paper_ppm.col[ry * paper_ppm.width * 3 + rx * 3],
|
|
3 * lx);
|
|
}
|
|
}
|
|
}
|
|
|
|
cx = p->width / 2;
|
|
cy = p->height / 2;
|
|
maxdist = sqrt (cx * cx + cy * cy);
|
|
|
|
switch (runningvals.orient_type)
|
|
{
|
|
case ORIENTATION_VALUE:
|
|
ppm_new (&dirmap, p->width, p->height);
|
|
for (y = 0; y < dirmap.height; y++)
|
|
{
|
|
guchar *dstrow = &dirmap.col[y * dirmap.width * 3];
|
|
guchar *srcrow = &p->col[y * p->width * 3];
|
|
for (x = 0; x < dirmap.width; x++)
|
|
{
|
|
dstrow[x * 3] =
|
|
(srcrow[x * 3] + srcrow[x * 3 + 1] + srcrow[x * 3 + 2]) / 3;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ORIENTATION_RADIUS:
|
|
ppm_new (&dirmap, p->width, p->height);
|
|
for (y = 0; y < dirmap.height; y++)
|
|
{
|
|
guchar *dstrow = &dirmap.col[y * dirmap.width * 3];
|
|
double ysqr = (cy - y) * (cy - y);
|
|
|
|
for (x = 0; x < dirmap.width; x++)
|
|
{
|
|
dstrow[x*3] = sqrt ((cx - x) * (cx - x) + ysqr) * 255 / maxdist;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ORIENTATION_RADIAL:
|
|
ppm_new (&dirmap, p->width, p->height);
|
|
for (y = 0; y < dirmap.height; y++)
|
|
{
|
|
guchar *dstrow = &dirmap.col[y * dirmap.width * 3];
|
|
|
|
for (x = 0; x < dirmap.width; x++)
|
|
{
|
|
dstrow[x * 3] = (G_PI + atan2 (cy - y, cx - x)) *
|
|
255.0 / (G_PI * 2);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ORIENTATION_FLOWING:
|
|
ppm_new (&dirmap, p->width / 6 + 5, p->height / 6 + 5);
|
|
mkgrayplasma (&dirmap, 15);
|
|
blur (&dirmap, 2, 2);
|
|
blur (&dirmap, 2, 2);
|
|
resize (&dirmap, p->width, p->height);
|
|
blur (&dirmap, 2, 2);
|
|
if (runningvals.general_paint_edges)
|
|
edgepad (&dirmap, maxbrushwidth, maxbrushheight,
|
|
maxbrushwidth, maxbrushheight);
|
|
break;
|
|
|
|
case ORIENTATION_HUE:
|
|
ppm_new (&dirmap, p->width, p->height);
|
|
for (y = 0; y < dirmap.height; y++)
|
|
{
|
|
guchar *dstrow = &dirmap.col[y * dirmap.width * 3];
|
|
guchar *srcrow = &p->col[y * p->width * 3];
|
|
|
|
for (x = 0; x < dirmap.width; x++)
|
|
{
|
|
dstrow[x * 3] = get_hue (&srcrow[x * 3]);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ORIENTATION_ADAPTIVE:
|
|
{
|
|
guchar tmpcol[3] = {0, 0, 0};
|
|
|
|
ppm_new (&dirmap, p->width, p->height);
|
|
fill (&dirmap, tmpcol);
|
|
}
|
|
break;
|
|
|
|
case ORIENTATION_MANUAL:
|
|
ppm_new (&dirmap, p->width-maxbrushwidth*2, p->height-maxbrushheight*2);
|
|
for (y = 0; y < dirmap.height; y++)
|
|
{
|
|
guchar *dstrow = &dirmap.col[y * dirmap.width * 3];
|
|
double tmpy = y / (double)dirmap.height;
|
|
for (x = 0; x < dirmap.width; x++)
|
|
{
|
|
dstrow[x * 3] = get_pixel_value(90 -
|
|
get_direction(x /
|
|
(double)dirmap.width,
|
|
tmpy, 1));
|
|
}
|
|
}
|
|
edgepad (&dirmap,
|
|
maxbrushwidth, maxbrushwidth,
|
|
maxbrushheight, maxbrushheight);
|
|
break;
|
|
}
|
|
|
|
if (runningvals.size_type == SIZE_TYPE_VALUE)
|
|
{
|
|
ppm_new (&sizmap, p->width, p->height);
|
|
for (y = 0; y < sizmap.height; y++)
|
|
{
|
|
guchar *dstrow = &sizmap.col[y * sizmap.width * 3];
|
|
guchar *srcrow = &p->col[y * p->width * 3];
|
|
|
|
for (x = 0; x < sizmap.width; x++)
|
|
{
|
|
dstrow[x * 3] =
|
|
(srcrow[x * 3] + srcrow[x * 3 + 1] + srcrow[x * 3 + 2]) / 3;
|
|
}
|
|
}
|
|
}
|
|
else if (runningvals.size_type == SIZE_TYPE_RADIUS)
|
|
{
|
|
ppm_new (&sizmap, p->width, p->height);
|
|
for (y = 0; y < sizmap.height; y++)
|
|
{
|
|
guchar *dstrow = &sizmap.col[y * sizmap.width * 3];
|
|
double ysqr = (cy - y) * (cy - y);
|
|
|
|
for (x = 0; x < sizmap.width; x++)
|
|
{
|
|
dstrow[x * 3] =
|
|
sqrt ((cx - x) * (cx - x) + ysqr) * 255 / maxdist;
|
|
}
|
|
}
|
|
}
|
|
else if (runningvals.size_type == SIZE_TYPE_RADIAL)
|
|
{
|
|
ppm_new (&sizmap, p->width, p->height);
|
|
for (y = 0; y < sizmap.height; y++)
|
|
{
|
|
guchar *dstrow = &sizmap.col[y * sizmap.width * 3];
|
|
|
|
for (x = 0; x < sizmap.width; x++)
|
|
{
|
|
dstrow[x * 3] = (G_PI + atan2 (cy - y, cx - x)) *
|
|
255.0 / (G_PI * 2);
|
|
}
|
|
}
|
|
}
|
|
else if (runningvals.size_type == SIZE_TYPE_FLOWING)
|
|
{
|
|
ppm_new (&sizmap, p->width / 6 + 5, p->height / 6 + 5);
|
|
mkgrayplasma (&sizmap, 15);
|
|
blur (&sizmap, 2, 2);
|
|
blur (&sizmap, 2, 2);
|
|
resize (&sizmap, p->width, p->height);
|
|
blur (&sizmap, 2, 2);
|
|
if (runningvals.general_paint_edges)
|
|
edgepad (&sizmap,
|
|
maxbrushwidth, maxbrushheight,
|
|
maxbrushwidth, maxbrushheight);
|
|
}
|
|
else if (runningvals.size_type == SIZE_TYPE_HUE)
|
|
{
|
|
ppm_new (&sizmap, p->width, p->height);
|
|
for (y = 0; y < sizmap.height; y++)
|
|
{
|
|
guchar *dstrow = &sizmap.col[y * sizmap.width * 3];
|
|
guchar *srcrow = &p->col[y * p->width * 3];
|
|
|
|
for (x = 0; x < sizmap.width; x++)
|
|
{
|
|
dstrow[ x * 3] = get_hue (&srcrow[x * 3]);
|
|
}
|
|
}
|
|
}
|
|
else if (runningvals.size_type == SIZE_TYPE_ADAPTIVE)
|
|
{
|
|
guchar tmpcol[3] = {0, 0, 0};
|
|
|
|
ppm_new (&sizmap, p->width, p->height);
|
|
fill (&sizmap, tmpcol);
|
|
}
|
|
else if (runningvals.size_type == SIZE_TYPE_MANUAL)
|
|
{
|
|
ppm_new (&sizmap,
|
|
p->width-maxbrushwidth * 2,
|
|
p->height-maxbrushheight * 2);
|
|
|
|
for (y = 0; y < sizmap.height; y++)
|
|
{
|
|
guchar *dstrow = &sizmap.col[y * sizmap.width * 3];
|
|
double tmpy = y / (double)sizmap.height;
|
|
|
|
for (x = 0; x < sizmap.width; x++)
|
|
{
|
|
dstrow[x * 3] = 255 * (1.0 - get_siz_from_pcvals (x / (double)sizmap.width, tmpy));
|
|
}
|
|
}
|
|
edgepad (&sizmap,
|
|
maxbrushwidth, maxbrushwidth,
|
|
maxbrushheight, maxbrushheight);
|
|
}
|
|
#if 0
|
|
ppm_save(&sizmap, "/tmp/_sizmap.ppm");
|
|
#endif
|
|
if (runningvals.place_type == PLACEMENT_TYPE_RANDOM)
|
|
{
|
|
i = tmp.width * tmp.height / (maxbrushwidth * maxbrushheight);
|
|
i *= density;
|
|
}
|
|
else if (runningvals.place_type == PLACEMENT_TYPE_EVEN_DIST)
|
|
{
|
|
i = (int)(tmp.width * density / maxbrushwidth) *
|
|
(int)(tmp.height * density / maxbrushheight);
|
|
#if 0
|
|
g_printerr("i=%d\n", i);
|
|
#endif
|
|
}
|
|
|
|
if (i < 1)
|
|
i = 1;
|
|
|
|
max_progress = i;
|
|
progstep = max_progress / 30;
|
|
if (progstep < 10)
|
|
progstep = 10;
|
|
|
|
if (runningvals.place_type == PLACEMENT_TYPE_EVEN_DIST)
|
|
{
|
|
int j;
|
|
|
|
xpos = g_new (int, i);
|
|
ypos = g_new (int, i);
|
|
for (j = 0; j < i; j++)
|
|
{
|
|
int factor = (int)(tmp.width * density / maxbrushwidth + 0.5);
|
|
|
|
if (factor < 1)
|
|
factor = 1;
|
|
xpos[j] = maxbrushwidth/2 + (j % factor) * maxbrushwidth / density;
|
|
ypos[j] = maxbrushheight/2 + (j / factor) * maxbrushheight / density;
|
|
}
|
|
for (j = 0; j < i; j++)
|
|
{
|
|
int a, b;
|
|
|
|
a = g_rand_int_range (random_generator, 0, i);
|
|
b = xpos[j]; xpos[j] = xpos[a]; xpos[a] = b;
|
|
b = ypos[j]; ypos[j] = ypos[a]; ypos[a] = b;
|
|
}
|
|
}
|
|
|
|
for (; i; i--)
|
|
{
|
|
int n;
|
|
double thissum;
|
|
|
|
if (i % progstep == 0)
|
|
{
|
|
if(runningvals.run)
|
|
{
|
|
pika_progress_update (0.8 - 0.8 * ((double)i / max_progress));
|
|
}
|
|
else
|
|
{
|
|
char tmps[40];
|
|
|
|
g_snprintf (tmps, sizeof (tmps),
|
|
"%.1f %%", 100 * (1.0 - ((double)i / max_progress)));
|
|
preview_set_button_label (tmps);
|
|
|
|
while(gtk_events_pending())
|
|
gtk_main_iteration();
|
|
}
|
|
}
|
|
|
|
if (runningvals.place_type == PLACEMENT_TYPE_RANDOM)
|
|
{
|
|
tx = g_rand_int_range (random_generator, maxbrushwidth / 2,
|
|
tmp.width - maxbrushwidth / 2);
|
|
ty = g_rand_int_range (random_generator, maxbrushheight / 2,
|
|
tmp.height - maxbrushheight / 2);
|
|
}
|
|
else if (runningvals.place_type == PLACEMENT_TYPE_EVEN_DIST)
|
|
{
|
|
tx = xpos[i - 1];
|
|
ty = ypos[i - 1];
|
|
}
|
|
if (runningvals.placement_center)
|
|
{
|
|
double z = g_rand_double_range (random_generator, 0, 0.75);
|
|
tx = tx * (1.0 - z) + tmp.width / 2 * z;
|
|
ty = ty * (1.0 - z) + tmp.height / 2 * z;
|
|
}
|
|
|
|
if ((tx < maxbrushwidth / 2) ||
|
|
(ty < maxbrushwidth / 2) ||
|
|
(tx + maxbrushwidth / 2 >= p->width) ||
|
|
(ty + maxbrushheight / 2 >= p->height))
|
|
{
|
|
#if 0
|
|
g_printerr("Internal Error; invalid coords: (%d,%d) i=%d\n", tx, ty, i);
|
|
#endif
|
|
continue;
|
|
}
|
|
|
|
if (img_has_alpha)
|
|
{
|
|
if (a->col[ty * a->width * 3 + tx * 3] > 128)
|
|
continue;
|
|
}
|
|
|
|
sn = on = 0;
|
|
|
|
switch (runningvals.orient_type)
|
|
{
|
|
case ORIENTATION_RANDOM:
|
|
on = g_rand_int_range (random_generator, 0, runningvals.orient_num);
|
|
break;
|
|
|
|
case ORIENTATION_VALUE:
|
|
case ORIENTATION_RADIUS:
|
|
case ORIENTATION_RADIAL:
|
|
case ORIENTATION_FLOWING:
|
|
case ORIENTATION_HUE:
|
|
case ORIENTATION_MANUAL:
|
|
on = runningvals.orient_num *
|
|
dirmap.col[ty * dirmap.width * 3 + tx * 3] / 256;
|
|
break;
|
|
|
|
case ORIENTATION_ADAPTIVE:
|
|
break; /* Handled below */
|
|
|
|
default:
|
|
g_printerr ("Internal error; Unknown orientationtype\n");
|
|
on = 0;
|
|
break;
|
|
}
|
|
|
|
switch (runningvals.size_type)
|
|
{
|
|
case SIZE_TYPE_RANDOM:
|
|
sn = g_rand_int_range (random_generator, 0, runningvals.size_num);
|
|
break;
|
|
|
|
case SIZE_TYPE_VALUE:
|
|
case SIZE_TYPE_RADIUS:
|
|
case SIZE_TYPE_RADIAL:
|
|
case SIZE_TYPE_FLOWING:
|
|
case SIZE_TYPE_HUE:
|
|
case SIZE_TYPE_MANUAL:
|
|
sn = runningvals.size_num * sizmap.col[ty*sizmap.width*3+tx*3] / 256;
|
|
break;
|
|
|
|
case SIZE_TYPE_ADAPTIVE:
|
|
break; /* Handled below */
|
|
|
|
default:
|
|
g_printerr ("Internal error; Unknown size_type\n");
|
|
sn = 0;
|
|
break;
|
|
}
|
|
|
|
/* Handle Adaptive selections */
|
|
if (runningvals.orient_type == ORIENTATION_ADAPTIVE)
|
|
{
|
|
if (runningvals.size_type == SIZE_TYPE_ADAPTIVE)
|
|
n = choose_best_brush (p, a, tx-maxbrushwidth/2,
|
|
ty-maxbrushheight/2, brushes,
|
|
num_brushes, brushes_sum, 0, 1);
|
|
else
|
|
{
|
|
int st = sn * runningvals.orient_num;
|
|
n = choose_best_brush (p, a, tx-maxbrushwidth/2,
|
|
ty-maxbrushheight/2, brushes,
|
|
st+runningvals.orient_num, brushes_sum,
|
|
st, 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (runningvals.size_type == SIZE_TYPE_ADAPTIVE)
|
|
n = choose_best_brush (p, a, tx-maxbrushwidth/2,
|
|
ty-maxbrushheight/2, brushes,
|
|
num_brushes, brushes_sum,
|
|
on, runningvals.orient_num);
|
|
else
|
|
n = sn * runningvals.orient_num + on;
|
|
}
|
|
/* Should never happen, but hey... */
|
|
if (n < 0)
|
|
n = 0;
|
|
else if (n >= num_brushes)
|
|
n = num_brushes - 1;
|
|
|
|
tx -= maxbrushwidth/2;
|
|
ty -= maxbrushheight/2;
|
|
|
|
brush = &brushes[n];
|
|
if (dropshadow)
|
|
shadow = &shadows[n];
|
|
thissum = brushes_sum[n];
|
|
|
|
/* Calculate color - avg. of in-brush pixels */
|
|
if (runningvals.color_type == 0)
|
|
{
|
|
r = g = b = 0;
|
|
for (y = 0; y < brush->height; y++)
|
|
{
|
|
guchar *row = &p->col[(ty + y) * p->width * 3];
|
|
|
|
for (x = 0; x < brush->width; x++)
|
|
{
|
|
int k = (tx + x) * 3;
|
|
double v;
|
|
|
|
if ((h = brush->col[y * brush->width * 3 + x * 3]))
|
|
{
|
|
v = h / 255.0;
|
|
r += row[k+0] * v;
|
|
g += row[k+1] * v;
|
|
b += row[k+2] * v;
|
|
}
|
|
}
|
|
}
|
|
r = r * 255.0 / thissum;
|
|
g = g * 255.0 / thissum;
|
|
b = b * 255.0 / thissum;
|
|
}
|
|
else if (runningvals.color_type == 1)
|
|
{
|
|
guchar *pixel;
|
|
|
|
y = ty + (brush->height / 2);
|
|
x = tx + (brush->width / 2);
|
|
pixel = &p->col[y*p->width * 3 + x * 3];
|
|
r = pixel[0];
|
|
g = pixel[1];
|
|
b = pixel[2];
|
|
}
|
|
else
|
|
{
|
|
/* No such color_type! */
|
|
r = g = b = 0;
|
|
}
|
|
if (runningvals.color_noise > 0.0)
|
|
{
|
|
double v = runningvals.color_noise;
|
|
#define BOUNDS(a) (((a) < 0) ? (0) : ((a) > 255) ? 255 : (a))
|
|
#define MYASSIGN(a) \
|
|
{ \
|
|
a = a + g_rand_double_range (random_generator, -v/2.0, v/2.0); \
|
|
a = BOUNDS(a) ; \
|
|
}
|
|
MYASSIGN (r);
|
|
MYASSIGN (g);
|
|
MYASSIGN (b);
|
|
#undef BOUNDS
|
|
#undef MYASSIGN
|
|
}
|
|
|
|
apply_brush (brush, shadow, &tmp, &atmp, tx,ty, r,g,b);
|
|
|
|
if (runningvals.general_tileable && runningvals.general_paint_edges)
|
|
{
|
|
int orig_width = tmp.width - 2 * maxbrushwidth;
|
|
int orig_height = tmp.height - 2 * maxbrushheight;
|
|
int dox = 0, doy = 0;
|
|
|
|
if (tx < maxbrushwidth)
|
|
{
|
|
apply_brush (brush, shadow, &tmp, &atmp, tx+orig_width,ty, r,g,b);
|
|
dox = -1;
|
|
}
|
|
else if (tx > orig_width)
|
|
{
|
|
apply_brush (brush, shadow, &tmp, &atmp, tx-orig_width,ty, r,g,b);
|
|
dox = 1;
|
|
}
|
|
if (ty < maxbrushheight)
|
|
{
|
|
apply_brush (brush, shadow, &tmp, &atmp, tx,ty+orig_height, r,g,b);
|
|
doy = 1;
|
|
}
|
|
else if (ty > orig_height)
|
|
{
|
|
apply_brush (brush, shadow, &tmp, &atmp, tx,ty-orig_height, r,g,b);
|
|
doy = -1;
|
|
}
|
|
if (doy)
|
|
{
|
|
if (dox < 0)
|
|
apply_brush (brush, shadow, &tmp, &atmp,
|
|
tx+orig_width, ty + doy * orig_height, r, g, b);
|
|
if (dox > 0)
|
|
apply_brush (brush, shadow, &tmp, &atmp,
|
|
tx-orig_width, ty + doy * orig_height, r, g, b);
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < num_brushes; i++)
|
|
{
|
|
ppm_kill (&brushes[i]);
|
|
}
|
|
g_free (brushes);
|
|
g_free (shadows);
|
|
g_free (brushes_sum);
|
|
|
|
g_free (xpos);
|
|
g_free (ypos);
|
|
|
|
if (runningvals.general_paint_edges)
|
|
{
|
|
crop (&tmp,
|
|
maxbrushwidth, maxbrushheight,
|
|
tmp.width - maxbrushwidth, tmp.height - maxbrushheight);
|
|
if (img_has_alpha)
|
|
crop (&atmp,
|
|
maxbrushwidth, maxbrushheight,
|
|
atmp.width - maxbrushwidth, atmp.height - maxbrushheight);
|
|
}
|
|
|
|
ppm_kill (p);
|
|
p->width = tmp.width;
|
|
p->height = tmp.height;
|
|
p->col = tmp.col;
|
|
|
|
if (img_has_alpha)
|
|
{
|
|
ppm_kill (a);
|
|
a->width = atmp.width;
|
|
a->height = atmp.height;
|
|
a->col = atmp.col;
|
|
}
|
|
|
|
relief = runningvals.paper_relief / 100.0;
|
|
if (relief > 0.001)
|
|
{
|
|
scale = runningvals.paper_scale / 100.0;
|
|
|
|
if (PPM_IS_INITED (&paper_ppm))
|
|
{
|
|
tmp = paper_ppm;
|
|
paper_ppm.col = NULL;
|
|
}
|
|
else
|
|
{
|
|
tmp.col = NULL;
|
|
ppm_load (runningvals.selected_paper, &tmp);
|
|
resize (&tmp, tmp.width * scale, tmp.height * scale);
|
|
if (runningvals.paper_invert)
|
|
ppm_apply_gamma (&tmp, -1.0, 1,1,1);
|
|
}
|
|
for (x = 0; x < p->width; x++)
|
|
{
|
|
double h, v;
|
|
int px = x % tmp.width, py;
|
|
|
|
for (y = 0; y < p->height; y++)
|
|
{
|
|
int k = y * p->width * 3 + x * 3;
|
|
|
|
py = y % tmp.height;
|
|
if (runningvals.paper_overlay)
|
|
h = (tmp.col[py * tmp.width * 3 + px * 3]-128) * relief;
|
|
else
|
|
h = (tmp.col[py * tmp.width * 3 + px * 3] -
|
|
(int)tmp.col[((py + 1) % tmp.height) * tmp.width * 3 +
|
|
((px + 1) % tmp.width) * 3]) /
|
|
-2.0 * relief;
|
|
if (h <= 0.0)
|
|
{
|
|
v = 1.0 + h/128.0;
|
|
if (v < 0.0)
|
|
v = 0.0;
|
|
else if (v > 1.0)
|
|
v = 1.0;
|
|
p->col[k+0] *= v;
|
|
p->col[k+1] *= v;
|
|
p->col[k+2] *= v;
|
|
}
|
|
else
|
|
{
|
|
v = h/128.0;
|
|
if (v < 0.0)
|
|
v = 0.0;
|
|
else if (v > 1.0)
|
|
v = 1.0;
|
|
p->col[k+0] = p->col[k+0] * (1.0-v) + 255 * v;
|
|
p->col[k+1] = p->col[k+1] * (1.0-v) + 255 * v;
|
|
p->col[k+2] = p->col[k+2] * (1.0-v) + 255 * v;
|
|
}
|
|
}
|
|
}
|
|
ppm_kill (&tmp);
|
|
}
|
|
|
|
ppm_kill (&paper_ppm);
|
|
ppm_kill (&dirmap);
|
|
ppm_kill (&sizmap);
|
|
|
|
if (runningvals.run)
|
|
{
|
|
pika_progress_update (0.8);
|
|
}
|
|
else
|
|
{
|
|
preview_set_button_label (_("_Update"));
|
|
}
|
|
|
|
running = 0;
|
|
}
|