New Magic tool: Reflection

Click to make a reflection of what's above the cursor,
below the cursor.

Click and drag down to stretch the lower reflection, or
drag up to make an reflection of the bottom of the picture
above where you clicked.

Or, drag left or right to make a reflection on the left of
what's on the right, or vice-versa.

It's not perfect, but my math skills are failing me, and it's
probably suitable.

Demo videos posted to Twitter
 * https://twitter.com/TuxPaintTweets/status/1456541738667954181
 * https://twitter.com/TuxPaintTweets/status/1456542229376294924

Closes https://sourceforge.net/p/tuxpaint/feature-requests/202/
This commit is contained in:
Bill Kendrick 2021-11-05 01:31:53 -07:00
parent 1486aa8145
commit 760423e84f
23 changed files with 540 additions and 0 deletions

BIN
magic/icons/reflection.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View file

@ -48,6 +48,7 @@
<li><a href="mirror.html">Mirror</a></li>
<li><a href="panels.html">Panels</a></li>
<li><a href="perspective.html">Perspective</a></li>
<li><a href="reflection.html">Reflection</a></li>
<li><a href="shift.html">Shift</a></li>
<li><a href="stretch.html">Stretch</a></li>
<li><a href="waves.html">Waves</a></li>

View file

@ -0,0 +1,13 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<body><html><head><title>Tux Paint "Magic" Tool: Reflection</title>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
</head>
<body bgcolor="#FFFFFF" text="#000000" link="#0000FF" vlink="#FF0000" alink="#FF00FF">
<h1 align="center">Tux Paint "Magic" Tool: Reflection</h1>
<h2 align="center">Group: Picture Warps</h2>
<h3 align="center">Author:
Bill Kendrick &lt;<a href="mailto:bill@newbreedsoftware.com">bill@newbreedsoftware.com</a>&gt;</h3>
<p>Click and drag down, up, left, or right, to add a reflection to you picture.</p>
<hr size="1" noshade />
<p align="center">Tux Paint 0.9.27</p>
</body></html>

View file

@ -44,6 +44,7 @@ Picture Warps
* Mirror
* Panels
* Perspective
* Reflection
* Shift
* Stretch
* Waves

View file

@ -0,0 +1,12 @@
Tux Paint "Magic" Tool: Reflection
Group: Picture Warps
Author: Bill Kendrick <bill@newbreedsoftware.com>
Click and drag down, up, left, or right, to add a reflection to you
picture.
----------------------------------------------------------------------
Tux Paint 0.9.27

View file

@ -48,6 +48,7 @@
<li><a href="mirror.html">Mirror</a></li>
<li><a href="panels.html">Panels</a></li>
<li><a href="perspective.html">Perspective</a></li>
<li><a href="reflection.html">Reflection</a></li>
<li><a href="shift.html">Shift</a></li>
<li><a href="stretch.html">Stretch</a></li>
<li><a href="waves.html">Waves</a></li>

View file

@ -0,0 +1,13 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<body><html><head><title>Tux Paint "Magic" Tool: Reflection</title>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
</head>
<body bgcolor="#FFFFFF" text="#000000" link="#0000FF" vlink="#FF0000" alink="#FF00FF">
<h1 align="center">Tux Paint "Magic" Tool: Reflection</h1>
<h2 align="center">Group: Picture Warps</h2>
<h3 align="center">Author:
Bill Kendrick &lt;<a href="mailto:bill@newbreedsoftware.com">bill@newbreedsoftware.com</a>&gt;</h3>
<p>Click and drag down, up, left, or right, to add a reflection to you picture.</p>
<hr size="1" noshade />
<p align="center">Tux Paint 0.9.27</p>
</body></html>

View file

@ -44,6 +44,7 @@ Picture Warps
* Mirror
* Panels
* Perspective
* Reflection
* Shift
* Stretch
* Waves

View file

@ -0,0 +1,12 @@
Tux Paint "Magic" Tool: Reflection
Group: Picture Warps
Author: Bill Kendrick <bill@newbreedsoftware.com>
Click and drag down, up, left, or right, to add a reflection to you
picture.
----------------------------------------------------------------------
Tux Paint 0.9.27

View file

@ -48,6 +48,7 @@
<li><a href="mirror.html">Mirror</a></li>
<li><a href="panels.html">Panels</a></li>
<li><a href="perspective.html">Perspective</a></li>
<li><a href="reflection.html">Reflection</a></li>
<li><a href="shift.html">Shift</a></li>
<li><a href="stretch.html">Stretch</a></li>
<li><a href="waves.html">Waves</a></li>

View file

@ -0,0 +1,13 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<body><html><head><title>Tux Paint "Magic" Tool: Reflection</title>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
</head>
<body bgcolor="#FFFFFF" text="#000000" link="#0000FF" vlink="#FF0000" alink="#FF00FF">
<h1 align="center">Tux Paint "Magic" Tool: Reflection</h1>
<h2 align="center">Group: Picture Warps</h2>
<h3 align="center">Author:
Bill Kendrick &lt;<a href="mailto:bill@newbreedsoftware.com">bill@newbreedsoftware.com</a>&gt;</h3>
<p>Click and drag down, up, left, or right, to add a reflection to you picture.</p>
<hr size="1" noshade />
<p align="center">Tux Paint 0.9.27</p>
</body></html>

View file

@ -44,6 +44,7 @@ Picture Warps
* Mirror
* Panels
* Perspective
* Reflection
* Shift
* Stretch
* Waves

View file

@ -0,0 +1,12 @@
Tux Paint "Magic" Tool: Reflection
Group: Picture Warps
Author: Bill Kendrick <bill@newbreedsoftware.com>
Click and drag down, up, left, or right, to add a reflection to you
picture.
----------------------------------------------------------------------
Tux Paint 0.9.27

View file

@ -48,6 +48,7 @@
<li><a href="mirror.html">Mirror</a></li>
<li><a href="panels.html">Panels</a></li>
<li><a href="perspective.html">Perspective</a></li>
<li><a href="reflection.html">Reflection</a></li>
<li><a href="shift.html">Maiúsculas</a></li>
<li><a href="stretch.html">Stretch</a></li>
<li><a href="waves.html">Waves</a></li>

View file

@ -0,0 +1,13 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<body><html><head><title>Tux Paint "Magic" Tool: Reflection</title>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
</head>
<body bgcolor="#FFFFFF" text="#000000" link="#0000FF" vlink="#FF0000" alink="#FF00FF">
<h1 align="center">Tux Paint "Magic" Tool: Reflection</h1>
<h2 align="center">Group: Picture Warps</h2>
<h3 align="center">Author:
Bill Kendrick &lt;<a href="mailto:bill@newbreedsoftware.com">bill@newbreedsoftware.com</a>&gt;</h3>
<p>Click and drag down, up, left, or right, to add a reflection to you picture.</p>
<hr size="1" noshade />
<p align="center">Tux Paint 0.9.27</p>
</body></html>

View file

@ -44,6 +44,7 @@ Picture Warps
* Mirror
* Panels
* Perspective
* Reflection
* Mai**sculas
* Stretch
* Waves

View file

@ -0,0 +1,12 @@
Tux Paint "Magic" Tool: Reflection
Group: Picture Warps
Author: Bill Kendrick <bill@newbreedsoftware.com>
Click and drag down, up, left, or right, to add a reflection to you
picture.
----------------------------------------------------------------------
Tux Paint 0.9.27

View file

@ -48,6 +48,7 @@
<li><a href="mirror.html">Mirror</a></li>
<li><a href="panels.html">Panels</a></li>
<li><a href="perspective.html">Perspective</a></li>
<li><a href="reflection.html">Reflection</a></li>
<li><a href="shift.html">Shift</a></li>
<li><a href="stretch.html">Stretch</a></li>
<li><a href="waves.html">Waves</a></li>

View file

@ -0,0 +1,13 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<body><html><head><title>Tux Paint "Magic" Tool: Reflection</title>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
</head>
<body bgcolor="#FFFFFF" text="#000000" link="#0000FF" vlink="#FF0000" alink="#FF00FF">
<h1 align="center">Tux Paint "Magic" Tool: Reflection</h1>
<h2 align="center">Group: Picture Warps</h2>
<h3 align="center">Author:
Bill Kendrick &lt;<a href="mailto:bill@newbreedsoftware.com">bill@newbreedsoftware.com</a>&gt;</h3>
<p>Click and drag down, up, left, or right, to add a reflection to you picture.</p>
<hr size="1" noshade />
<p align="center">Tux Paint 0.9.27</p>
</body></html>

View file

@ -44,6 +44,7 @@ Picture Warps
* Mirror
* Panels
* Perspective
* Reflection
* Shift
* Stretch
* Waves

View file

@ -0,0 +1,12 @@
Tux Paint "Magic" Tool: Reflection
Group: Picture Warps
Author: Bill Kendrick <bill@newbreedsoftware.com>
Click and drag down, up, left, or right, to add a reflection to you
picture.
----------------------------------------------------------------------
Tux Paint 0.9.27

BIN
magic/sounds/reflection.ogg Normal file

Binary file not shown.

405
magic/src/reflection.c Normal file
View file

@ -0,0 +1,405 @@
/*
reflection.c
Reflection Magic Tool Plugin
Tux Paint - A simple drawing program for children.
Copyright (c) 2021-2021 by Bill Kendrick and others; see AUTHORS.txt
bill@newbreedsoftware.com
http://www.tuxpaint.org/
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 2 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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
(See COPYING.txt)
Last updated: November 5, 2021
$Id$
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include "tp_magic_api.h"
#include "SDL_image.h"
#include "SDL_mixer.h"
#define REFLECTION_XOR_SIZE 10
/* Our globals: */
static Mix_Chunk *reflection_snd;
int reflection_x1, reflection_y1;
enum reflection_sides
{
REFLECTION_SIDE_TOP,
REFLECTION_SIDE_LEFT,
REFLECTION_SIDE_BOTTOM,
REFLECTION_SIDE_RIGHT
};
int reflection_side_old;
/* Local function prototypes: */
int reflection_init(magic_api * api);
Uint32 reflection_api_version(void);
int reflection_get_tool_count(magic_api * api);
SDL_Surface *reflection_get_icon(magic_api * api, int which);
char *reflection_get_name(magic_api * api, int which);
int reflection_get_group(magic_api * api, int which);
char *reflection_get_description(magic_api * api, int which, int mode);
void reflection_drag(magic_api * api, int which, SDL_Surface * canvas,
SDL_Surface * last, int ox, int oy, int x, int y, SDL_Rect * update_rect);
void do_reflection(magic_api * api, SDL_Surface * canvas,
SDL_Surface * last, int x, int y, SDL_Rect * update_rect, int show_origin);
void reflection_click(magic_api * api, int which, int mode,
SDL_Surface * canvas, SDL_Surface * last, int x, int y, SDL_Rect * update_rect);
void reflection_release(magic_api * api, int which,
SDL_Surface * canvas, SDL_Surface * last, int x, int y, SDL_Rect * update_rect);
void reflection_shutdown(magic_api * api);
void reflection_set_color(magic_api * api, Uint8 r, Uint8 g, Uint8 b);
int reflection_requires_colors(magic_api * api, int which);
void reflection_switchin(magic_api * api, int which, int mode, SDL_Surface * canvas);
void reflection_switchout(magic_api * api, int which, int mode, SDL_Surface * canvas);
int reflection_modes(magic_api * api, int which);
int reflection_init(magic_api * api)
{
char fname[1024];
snprintf(fname, sizeof(fname), "%s/sounds/magic/reflection.ogg", api->data_directory);
reflection_snd = Mix_LoadWAV(fname);
return (1);
}
Uint32 reflection_api_version(void)
{
return (TP_MAGIC_API_VERSION);
}
int reflection_get_tool_count(magic_api * api ATTRIBUTE_UNUSED)
{
return (1);
}
SDL_Surface *reflection_get_icon(magic_api * api, int which ATTRIBUTE_UNUSED)
{
char fname[1024];
snprintf(fname, sizeof(fname), "%s/images/magic/reflection.png", api->data_directory); /* FIXME */
return (IMG_Load(fname));
}
char *reflection_get_name(magic_api * api ATTRIBUTE_UNUSED, int which ATTRIBUTE_UNUSED)
{
return (strdup(gettext_noop("Reflection")));
}
int reflection_get_group(magic_api * api ATTRIBUTE_UNUSED, int which ATTRIBUTE_UNUSED)
{
return MAGIC_TYPE_PICTURE_WARPS;
}
char *reflection_get_description(magic_api * api ATTRIBUTE_UNUSED, int which ATTRIBUTE_UNUSED, int mode ATTRIBUTE_UNUSED)
{
return (strdup(gettext_noop("Click and drag the mouse around to add a reflection to your picture.")));
}
void reflection_drag(magic_api * api, int which ATTRIBUTE_UNUSED, SDL_Surface * canvas,
SDL_Surface * last, int ox ATTRIBUTE_UNUSED, int oy ATTRIBUTE_UNUSED, int x, int y,
SDL_Rect * update_rect)
{
do_reflection(api, canvas, last, x, y, update_rect, 1);
}
void do_reflection(magic_api * api, SDL_Surface * canvas,
SDL_Surface * last, int x, int y, SDL_Rect * update_rect, int show_origin)
{
float scale;
int xx, yy;
SDL_Rect src, dest;
int reflection_side;
int update_all = 0;
if (x <= 0)
x = 1;
else if (x >= canvas->w)
x = canvas->w - 1;
if (y <= 0)
y = 1;
else if (y >= canvas->h)
y = canvas->h - 1;
/* Determine what direction to go */
if (abs(x - reflection_x1) < 32)
{
/* +/-32 pixels of wiggle room before we switch from vertical to horizontal */
if (y > reflection_y1)
reflection_side = REFLECTION_SIDE_BOTTOM;
else
reflection_side = REFLECTION_SIDE_TOP;
}
else
{
if (x < reflection_x1)
reflection_side = REFLECTION_SIDE_LEFT;
else
reflection_side = REFLECTION_SIDE_RIGHT;
}
/* If we've changed direction, reset the canvas back
to the snapshot before proceeding */
if (reflection_side != reflection_side_old)
{
SDL_BlitSurface(last, NULL, canvas, NULL);
reflection_side_old = reflection_side;
update_all = 1;
}
/* Note: This isn't very good, and I basically
brute-forced the code until it seemed to work
well enough. There's a ton of room for improvement!
-bjk 2021.11.05 */
if (reflection_side == REFLECTION_SIDE_BOTTOM)
{
/* Starting from `reflection_y1` and moving down,
we'll copy from `reflection_y1` and moving up */
scale = (float) reflection_y1 / (float) y;
for (yy = reflection_y1; yy < canvas->h; yy++)
{
dest.x = 0;
dest.y = yy;
dest.w = canvas->w;
dest.h = 1;
src.x = 0;
src.y = (reflection_y1 * scale) + ((y - yy) * scale);
src.w = canvas->w;
src.h = 1;
if (src.y < 0)
{
src.y = yy;
}
SDL_BlitSurface(last, &src, canvas, &dest);
}
update_rect->x = 0;
update_rect->y = reflection_y1;
update_rect->w = canvas->w;
update_rect->h = canvas->h - reflection_y1 + 1;
}
else if (reflection_side == REFLECTION_SIDE_TOP)
{
/* Starting from `reflection_y1` and moving up,
we'll copy from `reflection_y1` and moving down */
scale = ((float) reflection_y1 / (float) y);
for (yy = reflection_y1; yy >= 0; yy--)
{
dest.x = 0;
dest.y = yy;
dest.w = canvas->w;
dest.h = 1;
src.x = 0;
src.y = (reflection_y1 / scale) + (y * scale) - (yy / scale);
src.w = canvas->w;
src.h = 1;
if (src.y >= canvas->h)
{
src.y = yy;
}
SDL_BlitSurface(last, &src, canvas, &dest);
}
update_rect->x = 0;
update_rect->y = 0;
update_rect->w = canvas->w;
update_rect->h = reflection_y1;
}
else if (reflection_side == REFLECTION_SIDE_RIGHT)
{
/* Starting from `reflection_x1` and moving right,
we'll copy from `reflection_x1` and moving left */
scale = (float) reflection_x1 / (float) x;
for (xx = reflection_x1; xx < canvas->w; xx++)
{
dest.x = xx;
dest.y = 0;
dest.w = 1;
dest.h = canvas->h;
src.x = (reflection_x1 * scale) + ((x - xx) * scale);
src.y = 0;
src.w = 1;
src.h = canvas->h;
if (src.x < 0)
{
src.x = xx;
}
SDL_BlitSurface(last, &src, canvas, &dest);
}
update_rect->x = reflection_x1;
update_rect->y = 0;
update_rect->w = canvas->w - reflection_x1 + 1;
update_rect->h = canvas->h;
}
else if (reflection_side == REFLECTION_SIDE_LEFT)
{
/* Starting from `reflection_x1` and left up,
we'll copy from `reflection_x1` and right down */
scale = (float) reflection_x1 / (float) x;
for (xx = reflection_x1; xx >= 0; xx--)
{
dest.x = xx;
dest.y = 0;
dest.w = 1;
dest.h = canvas->h;
src.x = (reflection_x1 / scale) + (x * scale) - (xx / scale);
src.y = 0;
src.w = 1;
src.h = canvas->h;
if (src.x >= canvas->w)
{
src.x = xx;
}
SDL_BlitSurface(last, &src, canvas, &dest);
}
update_rect->x = 0;
update_rect->y = 0;
update_rect->w = reflection_x1;
update_rect->h = canvas->h;
}
/* TODO: Support reflecting at arbitrary angles!
(a la linear gradient fill tool) */
if (update_all)
{
update_rect->x = 0;
update_rect->y = 0;
update_rect->w = canvas->w;
update_rect->h = canvas->h;
}
else
{
for (yy = reflection_y1 - REFLECTION_XOR_SIZE; yy < reflection_y1 + REFLECTION_XOR_SIZE; yy++)
{
if (show_origin)
api->xorpixel(canvas, reflection_x1, yy);
else
api->putpixel(canvas, reflection_x1, yy, api->getpixel(last, reflection_x1, yy));
}
for (xx = reflection_x1 - REFLECTION_XOR_SIZE; xx < reflection_x1 + REFLECTION_XOR_SIZE; xx++)
{
if (show_origin)
api->xorpixel(canvas, xx, reflection_y1);
else
api->putpixel(canvas, xx, reflection_y1, api->getpixel(last, xx, reflection_y1));
}
update_rect->x -= REFLECTION_XOR_SIZE;
update_rect->w += (REFLECTION_XOR_SIZE * 2);
update_rect->y -= REFLECTION_XOR_SIZE;
update_rect->h += (REFLECTION_XOR_SIZE * 2);
}
api->playsound(reflection_snd, (x * 255) / canvas->w, (y * 255) / canvas->h);
}
void reflection_click(magic_api * api, int which, int mode ATTRIBUTE_UNUSED,
SDL_Surface * canvas, SDL_Surface * last, int x, int y, SDL_Rect * update_rect)
{
if (x <= 0)
x = 1;
else if (x >= canvas->w)
x = canvas->w - 1;
if (y <= 0)
y = 1;
else if (y >= canvas->h)
y = canvas->h - 1;
reflection_x1 = x;
reflection_y1 = y - 1;
reflection_side_old = REFLECTION_SIDE_BOTTOM;
reflection_drag(api, which, canvas, last, x, y, x, y, update_rect);
}
void reflection_release(magic_api * api, int which ATTRIBUTE_UNUSED,
SDL_Surface * canvas, SDL_Surface * last,
int x, int y, SDL_Rect * update_rect)
{
do_reflection(api, canvas, last, x, y, update_rect, 0);
}
void reflection_shutdown(magic_api * api ATTRIBUTE_UNUSED)
{
if (reflection_snd != NULL)
Mix_FreeChunk(reflection_snd);
}
void reflection_set_color(magic_api * api ATTRIBUTE_UNUSED, Uint8 r ATTRIBUTE_UNUSED, Uint8 g ATTRIBUTE_UNUSED,
Uint8 b ATTRIBUTE_UNUSED)
{
}
int reflection_requires_colors(magic_api * api ATTRIBUTE_UNUSED, int which ATTRIBUTE_UNUSED)
{
return 0;
}
void reflection_switchin(magic_api * api ATTRIBUTE_UNUSED, int which ATTRIBUTE_UNUSED, int mode ATTRIBUTE_UNUSED,
SDL_Surface * canvas)
{
reflection_x1 = canvas->w / 2;
reflection_y1 = canvas->h / 2;
}
void reflection_switchout(magic_api * api ATTRIBUTE_UNUSED, int which ATTRIBUTE_UNUSED, int mode ATTRIBUTE_UNUSED,
SDL_Surface * canvas ATTRIBUTE_UNUSED)
{
}
int reflection_modes(magic_api * api ATTRIBUTE_UNUSED, int which ATTRIBUTE_UNUSED)
{
return MODE_PAINT;
}