FFmpeg进阶教程之给视频添加文字水印

目录
  • 前言
  • 文字水印配置项
  • 文字水印关键点
  • 定义滤镜实现
  • 项目工程源码
  • 使用效果
  • 总结

前言

和图片水印一样,很多时候为了声明视频的原创性,我们会给视频添加文字水印进行版权保护。添加文字水印和添加图片水印的流程相似,但又略有不同,这里介绍一下如何通过FFmpeg给视频添加文字水印。添加文字水印的流程图如下图所示:

文字水印配置项

在讲文字水印之前先介绍一下文字水印支持的那些配置,方便大家的使用。

项目 介绍
使用格式 drawtext=fontfile=font_f:text=text1…(通过冒号分割配置项,通过=给配置项赋值)
fontfile 用于绘制文本的字体文件的正确路径,强制参数
text 要绘制的文本字符串,必须是UTF-8编码的字符序列
x,y 绘制的位置的起始坐标值
fontcolor 字体颜色名称或0xRRGGBB[AA]格式的颜色,默认为黑色
fontsize 要绘制的文本字体大小,默认值为16
tabsize 用于呈现选项卡的空间大小,默认值为4
line_h,lh 每个文本行的高度
main_h,h,H 输入的高度
main_w,w,W 输入的宽度

常用的配置项主要有这些,如果需要其他的配置可以参考官方文档介绍。

文字水印关键点

中文的支持

和QT一样,FFmpeg绘制文字水印也存在中文乱码的问题。在windows下解决中文乱码主要需要以下几点:

1.将源码文件修改为utf-8编码

2.将编译编码类型修改为utf-8编码对应的配置如下:

#pragma execution_character_set("utf-8")

同时我们还应该确保使用的字体支持中文。

字体路径问题

指定字体文件路径是强制参数,可以使用绝对路径和相对路径

//使用工程相对路径下的字体
fontfile=.//simsun.ttc
//使用D盘绝对路径下的字体,要对斜杠进行转义
fontfile=D\\\\:simun.ttc

定义滤镜实现

文字水印对应的绘制流程图如下图所示:

文字水印滤镜的实现如下:

int InitFilter(AVCodecContext * codecContext)
{
	char args[512];
	int ret = 0;
	//缓存输入和缓存输出
	const AVFilter *buffersrc = avfilter_get_by_name("buffer");
	const AVFilter *buffersink = avfilter_get_by_name("buffersink");

	//创建输入输出参数
	AVFilterInOut *outputs = avfilter_inout_alloc();
	AVFilterInOut *inputs = avfilter_inout_alloc();

	//滤镜的描述
	//使用simhei字体,绘制的字体大小为100,文本内容为"鬼灭之刃",绘制位置为(100,100)
	//绘制的字体颜色为白色
	string  filters_descr = "drawtext=fontfile=.//simsun.ttc:fontsize=100:text=鬼灭之刃:x=100:y=100:fontcolor=0xFFFFFF";
	enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P };

	//创建滤镜容器
	filter_graph = avfilter_graph_alloc();
	if (!outputs || !inputs || !filter_graph)
	{
		ret = AVERROR(ENOMEM);
		goto end;
	}

	//初始化数据帧的格式
	sprintf_s(args, sizeof(args),
		"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
		codecContext->width, codecContext->height, codecContext->pix_fmt,
		codecContext->time_base.num, codecContext->time_base.den,
		codecContext->sample_aspect_ratio.num, codecContext->sample_aspect_ratio.den);

	//输入数据缓存
	ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in",
		args, NULL, filter_graph);

	if (ret < 0) {
		goto end;
	}

	//输出数据缓存
	ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out",
		NULL, NULL, filter_graph);

	if (ret < 0)
	{
		av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\n");
		goto end;
	}

	//设置元素样式
	ret = av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts,
		AV_PIX_FMT_YUV420P, AV_OPT_SEARCH_CHILDREN);
	if (ret < 0)
	{
		av_log(NULL, AV_LOG_ERROR, "Cannot set output pixel format\n");
		goto end;
	}

	//设置滤镜的端点
	outputs->name = av_strdup("in");
	outputs->filter_ctx = buffersrc_ctx;
	outputs->pad_idx = 0;
	outputs->next = NULL;

	inputs->name = av_strdup("out");
	inputs->filter_ctx = buffersink_ctx;
	inputs->pad_idx = 0;
	inputs->next = NULL;

	//初始化滤镜
	if ((ret = avfilter_graph_parse_ptr(filter_graph, filters_descr.c_str(),
		&inputs, &outputs, NULL)) < 0)
		goto end;

	//滤镜生效
	if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0)
		goto end;
end:
	//释放对应的输入输出
	avfilter_inout_free(&inputs);
	avfilter_inout_free(&outputs);
	return ret;
}

项目工程源码

给视频文件添加文字水印的工程源码如下,欢迎参考,如有问题欢迎反馈。

#pragma execution_character_set("utf-8")
#include <string>
#include <iostream>
#include <thread>
#include <memory>
#include <iostream>
#include <fstream>

extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavdevice/avdevice.h>
#include <libavfilter/avfilter.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavutil/avutil.h>
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>
#include <libavutil/frame.h>
#include <libavutil/imgutils.h>
#include <libavformat/avformat.h>
#include <libavutil/time.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#include <libavdevice/avdevice.h>
}

using namespace std;

//输入媒体文件的上下文
AVFormatContext * input_format_ctx = nullptr;

//输出媒体文件的上下文
AVFormatContext* output_format_ctx;

//输出视频编码器
AVCodecContext*	ouput_video_encode_ctx = NULL;

//音视频解码器
AVCodecContext *video_decode_ctx = NULL;
AVCodecContext *audio_decode_ctx = NULL;

//视频索引和音频索引
int video_stream_index = -1;
int audio_stream_index = -1;

//输出编码器
static AVCodec *  output_video_codec;

//滤镜容器和缓存
AVFilterGraph * filter_graph = nullptr;
AVFilterContext *buffersink_ctx = nullptr;;
AVFilterContext *buffersrc_ctx = nullptr;;
AVPacket packet;

//起始时间
static int64_t startTime;

int OpenOutput(char *fileName)
{
	//创建输出流,输出flv格式视频
	int ret = 0;
	ret = avformat_alloc_output_context2(&output_format_ctx, NULL, "flv", fileName);
	if (ret < 0)
	{
		return -1;
	}

	//打开输出流
	ret = avio_open(&output_format_ctx->pb, fileName, AVIO_FLAG_READ_WRITE);
	if (ret < 0)
	{
		return -2;
	}

	//创建输出流
	for (int index = 0; index < input_format_ctx->nb_streams; index++)
	{
		if (index == video_stream_index)
		{
			AVStream * stream = avformat_new_stream(output_format_ctx, output_video_codec);
			avcodec_parameters_from_context(stream->codecpar, ouput_video_encode_ctx);
			stream->codecpar->codec_tag = 0;
		}
		else if (index == audio_stream_index)
		{
			AVStream * stream = avformat_new_stream(output_format_ctx, NULL);
			stream->codecpar = input_format_ctx->streams[audio_stream_index]->codecpar;
			stream->codecpar->codec_tag = 0;
		}
	}
	//写文件头
	ret = avformat_write_header(output_format_ctx, nullptr);
	if (ret < 0)
	{
		return -3;
	}
	if (ret >= 0)
		cout << "open output stream successfully" << endl;
	return ret;
}

//初始化输出视频的编码器
int InitEncoderCodec(int iWidth, int iHeight)
{
	output_video_codec = avcodec_find_encoder(AV_CODEC_ID_H264);
	if (NULL == output_video_codec)
	{
		return  -1;
	}
	//指定编码器的参数
	ouput_video_encode_ctx = avcodec_alloc_context3(output_video_codec);
	ouput_video_encode_ctx->time_base = input_format_ctx->streams[video_stream_index]->time_base;
	ouput_video_encode_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
	ouput_video_encode_ctx->sample_fmt = AV_SAMPLE_FMT_S16;
	ouput_video_encode_ctx->width = iWidth;
	ouput_video_encode_ctx->height = iHeight;
	ouput_video_encode_ctx->bit_rate = input_format_ctx->streams[video_stream_index]->codecpar->bit_rate;
	ouput_video_encode_ctx->pix_fmt = (AVPixelFormat)*output_video_codec->pix_fmts;
	ouput_video_encode_ctx->profile = FF_PROFILE_H264_MAIN;
	ouput_video_encode_ctx->level = 41;
	ouput_video_encode_ctx->thread_count = 8;
	return 0;
}

int InitFilter(AVCodecContext * codecContext)
{
	char args[512];
	int ret = 0;
	//缓存输入和缓存输出
	const AVFilter *buffersrc = avfilter_get_by_name("buffer");
	const AVFilter *buffersink = avfilter_get_by_name("buffersink");

	//创建输入输出参数
	AVFilterInOut *outputs = avfilter_inout_alloc();
	AVFilterInOut *inputs = avfilter_inout_alloc();

	//滤镜的描述
	//使用simhei字体,绘制的字体大小为100,文本内容为"鬼灭之刃",绘制位置为(100,100)
	//绘制的字体颜色为白色
	string  filters_descr = "drawtext=fontfile=.//simsun.ttc:fontsize=100:text=鬼灭之刃:x=100:y=100:fontcolor=0xFFFFFF";
	enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P };

	//创建滤镜容器
	filter_graph = avfilter_graph_alloc();
	if (!outputs || !inputs || !filter_graph)
	{
		ret = AVERROR(ENOMEM);
		goto end;
	}

	//初始化数据帧的格式
	sprintf_s(args, sizeof(args),
		"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
		codecContext->width, codecContext->height, codecContext->pix_fmt,
		codecContext->time_base.num, codecContext->time_base.den,
		codecContext->sample_aspect_ratio.num, codecContext->sample_aspect_ratio.den);

	//输入数据缓存
	ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in",
		args, NULL, filter_graph);

	if (ret < 0) {
		goto end;
	}

	//输出数据缓存
	ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out",
		NULL, NULL, filter_graph);

	if (ret < 0)
	{
		av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\n");
		goto end;
	}

	//设置元素样式
	ret = av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts,
		AV_PIX_FMT_YUV420P, AV_OPT_SEARCH_CHILDREN);
	if (ret < 0)
	{
		av_log(NULL, AV_LOG_ERROR, "Cannot set output pixel format\n");
		goto end;
	}

	//设置滤镜的端点
	outputs->name = av_strdup("in");
	outputs->filter_ctx = buffersrc_ctx;
	outputs->pad_idx = 0;
	outputs->next = NULL;

	inputs->name = av_strdup("out");
	inputs->filter_ctx = buffersink_ctx;
	inputs->pad_idx = 0;
	inputs->next = NULL;

	//初始化滤镜
	if ((ret = avfilter_graph_parse_ptr(filter_graph, filters_descr.c_str(),
		&inputs, &outputs, NULL)) < 0)
		goto end;

	//滤镜生效
	if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0)
		goto end;
end:
	//释放对应的输入输出
	avfilter_inout_free(&inputs);
	avfilter_inout_free(&outputs);
	return ret;
}

//将加水印之后的图像帧输出到文件中
static int output_frame(AVFrame *frame, AVRational time_base)
{
	int code;
	AVPacket packet = { 0 };
	av_init_packet(&packet);

	int ret = avcodec_send_frame(ouput_video_encode_ctx, frame);
	if (ret < 0)
	{
		printf("Error sending a frame for encoding\n");
		return -1;
	}
	while (ret >= 0)
	{
		ret = avcodec_receive_packet(ouput_video_encode_ctx, &packet);
		if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
		{
			return (ret == AVERROR(EAGAIN)) ? 0 : 1;
		}
		else if (ret < 0) {
			printf("Error during encoding\n");
			exit(1);
		}

		AVRational avTimeBaseQ = { 1, AV_TIME_BASE };
		int64_t ptsTime = av_rescale_q(frame->pts, input_format_ctx->streams[video_stream_index]->time_base, avTimeBaseQ);
		int64_t nowTime = av_gettime() - startTime;

		if ((ptsTime > nowTime))
		{
			int64_t sleepTime = ptsTime - nowTime;
			av_usleep((sleepTime));
		}
		else
		{
			printf("not sleeping\n");
		}

		packet.pts = av_rescale_q_rnd(packet.pts, time_base, output_format_ctx->streams[video_stream_index]->time_base, (AVRounding)(AV_ROUND_INF | AV_ROUND_PASS_MINMAX));
		packet.dts = av_rescale_q_rnd(packet.dts, time_base, output_format_ctx->streams[video_stream_index]->time_base, (AVRounding)(AV_ROUND_INF | AV_ROUND_PASS_MINMAX));
		packet.stream_index = video_stream_index;
		code = av_interleaved_write_frame(output_format_ctx, &packet);
		av_packet_unref(&packet);

		if (code < 0)
		{
			av_log(NULL, AV_LOG_ERROR, "[ERROR] Writing Live Stream Interleaved Frame");
		}

		if (ret < 0) {
			exit(1);
		}
		av_packet_unref(&packet);
	}
}

int main(int argc, char* argv[])
{
	if (argc != 3)
	{
		printf("usage:%1 input filepath %2 outputfilepath");
		return -1;
	}

	//输入文件地址、输出文件地址
	string fileInput = std::string(argv[1]);
	string fileOutput = std::string(argv[2]);

	//初始化各种配置
	avformat_network_init();
	av_log_set_level(AV_LOG_ERROR);

	//打开输入文件
	int ret = avformat_open_input(&input_format_ctx, fileInput.c_str(), NULL, NULL);
	if (ret < 0)
	{
		return  ret;
	}
	ret = avformat_find_stream_info(input_format_ctx, NULL);
	if (ret < 0)
	{
		return ret;
	}

	//查找音视频流的索引
	for (int index = 0; index < input_format_ctx->nb_streams; ++index)
	{
		if (index == AVMEDIA_TYPE_AUDIO)
		{
			audio_stream_index = index;
		}
		else if (index == AVMEDIA_TYPE_VIDEO)
		{
			video_stream_index = index;
		}
	}

	//打开视频解码器
	const AVCodec* codec = avcodec_find_decoder(input_format_ctx->streams[video_stream_index]->codecpar->codec_id);
	if (!codec)
	{
		return -1;
	}
	video_decode_ctx = avcodec_alloc_context3(codec);
	if (!video_decode_ctx)
	{
		fprintf(stderr, "Could not allocate video codec context\n");
		return -2;
	}
	avcodec_parameters_to_context(video_decode_ctx, input_format_ctx->streams[video_stream_index]->codecpar);
	if (codec->capabilities & AV_CODEC_CAP_TRUNCATED)
		video_decode_ctx->flags |= AV_CODEC_FLAG_TRUNCATED;

	ret = avcodec_open2(video_decode_ctx, codec, NULL);
	if (ret < 0)
	{
		av_free(video_decode_ctx);
		return -3;
	}

	//初始化视频编码器
	ret = InitEncoderCodec(video_decode_ctx->width, video_decode_ctx->height);
	if (ret < 0)
	{
		return 0;
	}

	//初始化滤镜
	ret = InitFilter(ouput_video_encode_ctx);

	//打开编码器
	ret = avcodec_open2(ouput_video_encode_ctx, output_video_codec, NULL);
	if (ret < 0)
	{
		return  ret;
	}

	//初始化输出
	if (OpenOutput((char *)fileOutput.c_str()) < 0)
	{
		cout << "Open file Output failed!" << endl;
		this_thread::sleep_for(chrono::seconds(10));
		return 0;
	}

	AVFrame* pSrcFrame = av_frame_alloc();
	AVFrame*  filterFrame = av_frame_alloc();

	av_init_packet(&packet);
	startTime = av_gettime();

	while (true)
	{
		int ret = av_read_frame(input_format_ctx, &packet);
		if (ret < 0)
		{
			break;
		}
		//视频帧通过滤镜处理之后编码输出
		if (packet.stream_index == video_stream_index)
		{
			int ret = avcodec_send_packet(video_decode_ctx, &packet);
			if (ret < 0)
			{
				break;
			}
			while (ret >= 0)
			{
				ret = avcodec_receive_frame(video_decode_ctx, pSrcFrame);
				if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
				{
					break;
				}
				else if (ret < 0)
				{
					goto End;
				}
				pSrcFrame->pts = pSrcFrame->best_effort_timestamp;

				//添加到滤镜中
				if (av_buffersrc_add_frame_flags(buffersrc_ctx, pSrcFrame, AV_BUFFERSRC_FLAG_KEEP_REF) < 0)
				{
					break;
				}
				while (1)
				{
					//获取滤镜输出
					int ret = av_buffersink_get_frame(buffersink_ctx, filterFrame);
					if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
					{
						break;
					}
					else if (ret < 0)
					{
						goto End;
					}
					//编码之后输出
					output_frame(filterFrame, buffersink_ctx->inputs[0]->time_base);
					av_frame_unref(filterFrame);
				}
				av_frame_unref(pSrcFrame);
			}

		}
		else if (packet.stream_index == audio_stream_index)
		{
			packet.pts = av_rescale_q_rnd(packet.pts, input_format_ctx->streams[audio_stream_index]->time_base, output_format_ctx->streams[audio_stream_index]->time_base, (AVRounding)(AV_ROUND_INF | AV_ROUND_PASS_MINMAX));
			packet.dts = av_rescale_q_rnd(packet.dts, input_format_ctx->streams[audio_stream_index]->time_base, output_format_ctx->streams[audio_stream_index]->time_base, (AVRounding)(AV_ROUND_INF | AV_ROUND_PASS_MINMAX));
			packet.stream_index = audio_stream_index;
			av_interleaved_write_frame(output_format_ctx, &packet);
		}
		av_packet_unref(&packet);
	}
	av_write_trailer(output_format_ctx);

End:
    //结束的时候清理资源
	avfilter_graph_free(&filter_graph);
	if (input_format_ctx != NULL)
	{
		avformat_close_input(&input_format_ctx);
	}
	avcodec_free_context(&video_decode_ctx);
	avcodec_free_context(&ouput_video_encode_ctx);
	return 0;
}

使用效果

没有添加水印之前的视频截图如下:

添加水印之后的效果图如下图所示:

总结

到此这篇关于FFmpeg进阶教程之给视频添加文字水印的文章就介绍到这了,更多相关FFmpeg视频加文字水印内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++ opencv ffmpeg图片序列化实现代码解析

    0.如果路径中存在空格,用""把路径包括起来 1.使用ffmpeg命令 ffmpeg -y -framerate 10 -start_number 1 -i E:\Image\Image_%d.bmp E:\test.mp4 -y 表示输出时覆盖输出目录已存在的同名文件 -framerate 10 表示视频帧率 -start_number 1 表示图片序号从1开始 -i E:\Image\Image_%d.bmp 表示图片输入流格式 2.c++ 实现 ffmpeg命令 2.1.syst

  • C++使用ffmpeg实现rtsp取流的代码

    目录 C++ 使用ffmpeg实现rtsp取流 环境 下载 安装编译依赖 配置 ffmepg采用rtsp取流流程图 CMakeLists.txt编写方法 实现代码 C++ 使用ffmpeg实现rtsp取流 flyfish 环境 Ubuntu 18.04Qt 5.14.2FFmpeg-n5.0.1 下载 https://git.ffmpeg.org/ffmpeg.githttps://github.com/FFmpeg/FFmpeg 这里选择n5.0.1版本 安装编译依赖 sudo apt-get

  • C++ ffmpeg硬件解码的实现方法

    目录 什么是硬件解码 为什么要使用硬件解码 怎样使用硬件解码 注意事项 关键函数解析 什么是硬件解码 普通解码是利用cpu去解码也就是软件解码 硬件解码就是利用gpu去解码 为什么要使用硬件解码 首先最大的好处 快硬解播放出来的视频较为流畅,并且能够延长移动设备播放视频的时间: 而软解由于软解加大CPU工作负荷,会占用过多的移动CPU资源,如果CPU能力不足,则软件也将受到影响 最主要就是一个字 快 怎样使用硬件解码 ffmpeg内部为我们提供了友好的接口去实现硬件解码 注意事项 ffmpeg内

  • php面向对象与面向过程两种方法给图片添加文字水印

    目前绝大多数PHP程序员使用面向过程的方式,因为解析WEB页面本身就非常"过程化"(从一个标签到另一个标签).在HTML中嵌入过程处理代码是很直接自然的作法,所以PHP程序员通常使用这种方式. 如果你是刚接触PHP,用面向过程的风格来书写代码很可能是你唯一的选择.但是如果你经常上PHP论坛和新闻组的话,你应该会看到有关"对象"的文章.你也可能看到过如何书写面向对象的PHP代码的教程.或者你也可能下载过一些现成的类库,并尝试着去实例化其中的对象和使用类方法--尽管你可

  • php给图片添加文字水印方法汇总

    1: 面向过程的编写方法 //指定图片路径 $src = '001.png'; //获取图片信息 $info = getimagesize($src); //获取图片扩展名 $type = image_type_to_extension($info[2],false); //动态的把图片导入内存中 $fun = "imagecreatefrom{$type}"; $image = $fun('001.png'); //指定字体颜色 $col = imagecolorallocateal

  • Python 使用 Pillow 模块给图片添加文字水印的方法

    像微博一类的平台上传图片时,平台都会添加一个水印,宣誓着对图片的所有权,我们自己的博客平台也可以给自己的图片添加上水印. 还是用 Pillow 模块来实现 先来看一个简单的例子 >>> from PIL import Image >>> from PIL import ImageDraw >>> >>> image = Image.open('/Users/wxnacy/Downloads/vm-error1.png') >&g

  • PHP添加文字水印或图片水印的水印类完整源代码与使用示例

    PHP实现的给图片添加水印功能,可添加文字水印或图片水印,使用文字水印时需要提供字体文件,使用图片水印时需要提供水印图片,水印图片不能比要添加水印的图片大,请使用背景透明的水印图片. 该水印类支持自定义水印位置.自定义水印大小和水印的透明度,字体水印可自定义颜色等,功能已相应完善. 完整源代码如下(注解中已给出使用示例): <?php /** * 图片加水印类,支持文字水印.透明度设置.自定义水印位置等. * 使用示例: * $obj = new WaterMask($imgFileName);

  • golang中实现给gif、png、jpeg图片添加文字水印

    添加水印示例 添加main文件:"watermark/main.go" package main import ( "fmt" "watermark/textwatermark" ) func main() { SavePath := "./kaf" str := textwatermark.FontInfo{18, "努力向上", textwatermark.TopLeft, 20, 20, 255, 2

  • 如何利用Java在图片上添加文字水印效果

    目录 前言 [1]获取原图片对象 (1.1)读取本地图片 (1.2)读取网络图片 [2]创建画笔 [3]添加文字水印 [4]获取处理图片 [5]源代码 总结 前言 今天分享一个:通过Java代码,给图片添加文字. 比如下面这个图片,我们在左下角就添加了一个文字版的水印,那么这是如何实现的呢 ? [1]获取原图片对象 首先,第一步,肯定是要让我们的程序,拿到需要处理的图片. 我们程序获取图片的方式,通常有两种,一种是通过下载到本地,从本地读取:另外一种就是通过网络地址进行获取. (1.1)读取本地

  • Java实现给Word文件添加文字水印

    目录 方法思路 Jar引入 Java代码 Word中设置水印时,可预设的文字或自定义文字设置为水印效果,但通常添加水印效果时,会对所有页面都设置成统一效果,如果需要对每一页或者某个页面设置不同的水印效果,则可以参考本文中的方法.下面,将以Java代码为例,对Word每一页设置不同的文字水印效果作详细介绍. 方法思路 在给Word每一页添加水印前,首先需要在Word文档每一页正文的最后一个字符后面插入“连续”分节符,然后在每一节的页眉段落里添加艺术字类型的形状对象,并设置艺术字的坐标位置.样式.对

  • php图片添加文字水印实现代码

    php类库给现有的图片加文字水印,代码不是很完善,欢迎大家多多指教!代码如下: <?php /*PHP图片加文字水印类库 QQ:3697578482 伤心的歌 该类库暂时只支持文字水印,位置为右下角,颜色随机 调用方法: 1.在需要加水印的文件顶部引入类库: include_once 'imageClass.php'; 2.声明新类: $tpl=new image_fu; 3.给图片水印提供参数: $tpl->img(图片路径,水印文字,字体路径,字体大小,字体角度); 比如:$tpl->

  • C# 添加文字水印类代码

    复制代码 代码如下: using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.IO; using System.Drawing.Imaging; namespace Chen { public class warterfont { public void addtexttoimg(string filename, string text) { if

  • Java实现给图片添加图片水印,文字水印及马赛克的方法示例

    本文实例讲述了Java实现给图片添加图片水印,文字水印及马赛克的方法.分享给大家供大家参考,具体如下: 可以在eclipse中新建个Utils类,把以下代码复制进去直接使用,以下方法实现单个或多个水印的添加 package com.rzxt.fyx.common.util; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import

随机推荐