Hoy te voy a presentar un software gratuito y open source para poner marcas de agua a una imagen. Lo que tienes que hacer es simplemente seleccionar la imagen, la marca de agua y listo.
Además, puedes ajustar la opacidad y la separación entre marcas de agua. Por otro lado, si no te parece algún aspecto, puedes modificar el código fuente pues es totalmente open source.
He escrito este programa en PHP y JavaScript. Básicamente el procesamiento lo hace PHP, y JS solo es el frontend.
Sé bien que esto se podría hacer con JavaScript para no cargar al servidor, pero a mí me funciona así y por eso es que lo hice de esa manera. A lo largo de este post te mostraré todos los detalles sobre este programa.
El programa es muy simple. Seleccionamos las dos imágenes y ajustamos los parámetros. Por cierto, se ponen varias marcas de agua que cubren toda la imagen, contrario a lo que hice con WaterPy en donde una sola marca de agua se coloca en alguna posición.
Después de eso la imagen se puede descargar. Y todo eso se va mostrando en tiempo real, así que siéntete libre de probar con varios estilos y marcas de agua.
En el lado del servidor se utiliza PHP con las funciones de la librería GD. Esto es una mejora a un post que ya había hecho anteriormente, solo que ahora le he colocado una interfaz además de brindar la posibilidad de cambiar la opacidad de la marca de agua.
Recuerda habilitar la librería GD en caso de que utilices este proyecto.
Yo utilizo dos funciones, la primera es para cambiar la opacidad y la segunda es para colocar la marca de agua. Solo estoy usando funciones de la biblioteca GD tales como imagepng
, imagejpeg
, etcétera.
Cabe mencionar que la función que coloca la marca de agua no regresa nada, en su lugar simplemente muestra la imagen, es decir, la devuelve al navegador o al cliente.
<?php
/*
* @param resource $imageSrc Image resource. Not being modified.
* @param float $opacity Opacity to set from 0 (fully transparent) to 1 (no change)
* @return resource Transparent image resource
*/function imagesetopacity($imageSrc, $opacity)
{
$width = imagesx($imageSrc);
$height = imagesy($imageSrc);
// Duplicate image and convert to TrueColor
$imageDst = imagecreatetruecolor($width, $height);
imagealphablending($imageDst, false);
imagefill($imageDst, 0, 0, imagecolortransparent($imageDst));
imagecopy($imageDst, $imageSrc, 0, 0, 0, 0, $width, $height);
// Set new opacity to each pixel
for ($x = 0; $x < $width; ++$x)
for ($y = 0; $y < $height; ++$y) {
$pixelColor = imagecolorat($imageDst, $x, $y);
$pixelOpacity = 127 - (($pixelColor >> 24) & 0xFF);
if ($pixelOpacity > 0) {
$pixelOpacity = $pixelOpacity * $opacity;
$pixelColor = ($pixelColor & 0xFFFFFF) | ((int)round(127 - $pixelOpacity) << 24);
imagesetpixel($imageDst, $x, $y, $pixelColor);
}
}
return $imageDst;
}
function ponerMarcaDeAgua($rutaImagenOriginal, $rutaMarcaDeAgua, $separacionPixeles, $opacidad)
{
$imagenEsPng = false;
$marcaEsPng = false;
if (mime_content_type($rutaImagenOriginal) === "image/png") {
$imagenEsPng = true;
}
if (mime_content_type($rutaMarcaDeAgua) === "image/png") {
$marcaEsPng = true;
}
$original = $imagenEsPng ? imagecreatefrompng($rutaImagenOriginal) : imagecreatefromjpeg($rutaImagenOriginal);
$marcaDeAgua = $marcaEsPng ? imagecreatefrompng($rutaMarcaDeAgua) : imagecreatefromjpeg($rutaMarcaDeAgua);
$marcaDeAgua = imagesetopacity($marcaDeAgua, $opacidad);
# Como vamos a centrar necesitamos sacar antes las anchuras y alturas
$anchuraOriginal = imagesx($original);
$alturaOriginal = imagesy($original);
$alturaMarcaDeAgua = imagesy($marcaDeAgua);
$anchuraMarcaDeAgua = imagesx($marcaDeAgua);
# Desde dónde comenzar a cortar la marca de agua (si son 0, se comienza desde el inicio)
for ($fila = 0; $fila < $alturaOriginal; $fila += $alturaMarcaDeAgua + $separacionPixeles) {
for ($columna = 0; $columna < $anchuraOriginal; $columna += $anchuraMarcaDeAgua + $separacionPixeles) {
imagecopy(
$original,
$marcaDeAgua,
$columna,
$fila,
# No modificar, creo...
0,
0,
$anchuraMarcaDeAgua,
$alturaMarcaDeAgua
);
}
}
imagealphablending($original, false);
imagesavealpha($original, true);
header("Content-Type: " . mime_content_type($rutaImagenOriginal));
if ($imagenEsPng) {
imagepng($original);
} else {
imagejpeg($original);
}
imagedestroy($original);
imagedestroy($marcaDeAgua);
}
Ahora simplemente exponemos las funciones anteriores a través de PHP. Vamos a leer los archivos de $_FILES
y luego mostrar la salida al cliente:
<?php
include_once "funciones.php";
$imagen = $_FILES["imagen"]["tmp_name"];
$marca = $_FILES["marca"]["tmp_name"];
$opacidad = floatval($_POST["opacidad"]);
$separacion = intval($_POST["separacion"]);
ponerMarcaDeAgua($imagen, $marca, $separacion, $opacidad);
En el lado del cliente es en donde ajustamos los parámetros como la opacidad y la separación de las marcas de agua. También verificamos el cambio de los input para generar la nueva imagen.
El código JavaScript queda así:
const $imagenSeleccionada = document.querySelector("#imagen"),
$marcaSeleccionada = document.querySelector("#marca"),
$imagen = document.querySelector("#imagenResultado"),
$opacidad = document.querySelector("#opacidad"),
$separacion = document.querySelector("#separacion"),
$botonDescargar = document.querySelector("#botonDescargar"),
$nombreImagen = document.querySelector("#nombreImagen"),
$nombreMarca = document.querySelector("#nombreMarca");
let ultimoUrl, ultimoNombre; // Para cuando se descarga la imagen
// Desencadenada cuando se cambia la imagen o la marca de agua
const onCambioDeArchivo = async () => {
if ($imagenSeleccionada.files.length != 1 || $marcaSeleccionada.files.length != 1) {
return;
}
const fd = new FormData();
fd.append("imagen", $imagenSeleccionada.files[0]);
fd.append("marca", $marcaSeleccionada.files[0]);
fd.append("opacidad", parseFloat($opacidad.value));
fd.append("separacion", parseInt($separacion.value));
const respuesta = await fetch("./api.php", {
method: "POST",
body: fd,
});
const imagenBlob = await respuesta.blob();
const objectUrl = URL.createObjectURL(imagenBlob);
ultimoUrl = objectUrl;
$imagen.src = objectUrl;
}
$imagenSeleccionada.onchange = () => {
if ($imagenSeleccionada.files.length != 1) {
return;
}
const nombreArchivo = $imagenSeleccionada.files[0].name;
ultimoNombre = nombreArchivo;
$nombreImagen.innerHTML = nombreArchivo;
onCambioDeArchivo();
}
$marcaSeleccionada.onchange = () => {
if ($marcaSeleccionada.files.length != 1) {
return;
}
const nombreArchivo = $marcaSeleccionada.files[0].name;
$nombreMarca.innerHTML = nombreArchivo;
onCambioDeArchivo();
}
[$opacidad, $separacion].forEach($elemento => $elemento.onchange = onCambioDeArchivo);
const descargar = () => {
if (!ultimoUrl) {
return;
}
const a = document.createElement("a");
a.href = ultimoUrl;
a.download = ultimoNombre;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
$botonDescargar.onclick = descargar;
Además se hacen ciertos ajustes para obtener la imagen como un BLOB usando fetch y colocarla en una etiqueta img
, del mismo modo que agregamos un enlace temporal para cuando se descarga la imagen.
El código HTML completo junto con el script queda así:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Marca de agua</title>
<link rel="stylesheet" href="./bulma.min.css">
</head>
<body>
<section class="section">
<div class="columns">
<div class="column has-text-centered">
<h2 class="is-size-3">Colocar marca de agua</h2>
</div>
</div>
<div class="columns">
<div class="column is-one-fifth">
<strong>Imagen</strong>
<div class="file has-name is-fullwidth">
<label class="file-label">
<input class="file-input" type="file" accept="image/png,image/jpeg" id="imagen">
<span class="file-cta">
<span class="file-label">
Seleccione
</span>
</span>
<span class="file-name" id="nombreImagen">
</span>
</label>
</div>
<strong>Marca de agua</strong>
<div class="file has-name is-fullwidth">
<label class="file-label">
<input class="file-input" type="file" accept="image/png,image/jpeg" id="marca">
<span class="file-cta">
<span class="file-label">
Seleccione
</span>
</span>
<span class="file-name" id="nombreMarca">
</span>
</label>
</div>
<div class="field">
<label class="label">Opacidad</label>
<div class="control">
<input id="opacidad" type="range" min="0.1" max="1.0" step="0.05">
</div>
</div>
<div class="field">
<label class="label">Separación entre cada marca</label>
<div class="control">
<input id="separacion" type="range" min="1" max="500">
</div>
</div>
<button class="button is-success" id="botonDescargar">Descargar</button>
</div>
<div class="column has-text-centered">
<img id="imagenResultado" src="" alt="">
</div>
</div>
<div class="columns">
<div class="column has-text-centered">
<a href="https://parzibyte.me/blog">By Parzibyte</a>
</div>
</div>
</section>
<script>
const $imagenSeleccionada = document.querySelector("#imagen"),
$marcaSeleccionada = document.querySelector("#marca"),
$imagen = document.querySelector("#imagenResultado"),
$opacidad = document.querySelector("#opacidad"),
$separacion = document.querySelector("#separacion"),
$botonDescargar = document.querySelector("#botonDescargar"),
$nombreImagen = document.querySelector("#nombreImagen"),
$nombreMarca = document.querySelector("#nombreMarca");
let ultimoUrl, ultimoNombre; // Para cuando se descarga la imagen
// Desencadenada cuando se cambia la imagen o la marca de agua
const onCambioDeArchivo = async () => {
if ($imagenSeleccionada.files.length != 1 || $marcaSeleccionada.files.length != 1) {
return;
}
const fd = new FormData();
fd.append("imagen", $imagenSeleccionada.files[0]);
fd.append("marca", $marcaSeleccionada.files[0]);
fd.append("opacidad", parseFloat($opacidad.value));
fd.append("separacion", parseInt($separacion.value));
const respuesta = await fetch("./api.php", {
method: "POST",
body: fd,
});
const imagenBlob = await respuesta.blob();
const objectUrl = URL.createObjectURL(imagenBlob);
ultimoUrl = objectUrl;
$imagen.src = objectUrl;
}
$imagenSeleccionada.onchange = () => {
if ($imagenSeleccionada.files.length != 1) {
return;
}
const nombreArchivo = $imagenSeleccionada.files[0].name;
ultimoNombre = nombreArchivo;
$nombreImagen.innerHTML = nombreArchivo;
onCambioDeArchivo();
}
$marcaSeleccionada.onchange = () => {
if ($marcaSeleccionada.files.length != 1) {
return;
}
const nombreArchivo = $marcaSeleccionada.files[0].name;
$nombreMarca.innerHTML = nombreArchivo;
onCambioDeArchivo();
}
[$opacidad, $separacion].forEach($elemento => $elemento.onchange = onCambioDeArchivo);
const descargar = () => {
if (!ultimoUrl) {
return;
}
const a = document.createElement("a");
a.href = ultimoUrl;
a.download = ultimoNombre;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
$botonDescargar.onclick = descargar;
</script>
</body>
</html>
Y así es como podemos agregar fácilmente marcas de agua con JavaScript y PHP. Este programa soporta imágenes JPG así como PNG, puedes probarlo y verás que funciona.
Además, al cambiar cualquier valor, la imagen se refresca de manera inmediata.
Te dejo el código completo en mi GitHub, solo necesitas contar con Apache, PHP y la librería GD instalada y habilitada.
Recuerda que puedes usar esto de manera local o subirlo a un servidor de internet, aunque recuerda que el trabajo con imágenes siempre es algo pesado, en especial cuando son varios clientes.
Por otro lado te invito a leer más sobre PHP y JavaScript en mi blog.
El día de hoy te mostraré cómo crear un servidor HTTP (servidor web) en Android…
En este post te voy a enseñar a designar una carpeta para imprimir todos los…
En este artículo te voy a enseñar la guía para imprimir en una impresora térmica…
Hoy te voy a mostrar un ejemplo de programación para agregar un módulo de tasa…
Los usuarios del plugin para impresoras térmicas pueden contratar licencias, y en ocasiones me han…
Hoy voy a enseñarte cómo imprimir el € en una impresora térmica. Vamos a ver…
Esta web usa cookies.
Ver comentarios
Cómo hago para suscribirme a tu blog?
Al final de los posts hay una caja de texto para escribir su correo