/* * vf_logo.c - MPlayer video filter * * This filter 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, or (at your option) * any later version. * * This filter 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 MPlayer; see the file LICENSE. If not, write to the * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * -- * * Adds an image logo to the video stream. * - Supports libpng, or ImageMagick if USE_MAGICK is defined. * - Supports transparency * - Note: MPlayer (as of 1.0pre5) only tries to add the 'scale' filter * automatically at the *beginning* of the filter list. * - Using the 'scale' filter explicitly might help then, since the * current one only supports RGB/BGR colorspaces. * - Usage: -vf logo=filename:x:y (defaults: filename=logo.png x=0 y=0) * * Author: Julien 'dJeyL' Laurent * * Credits: * - MaxF's Textorizer * * - Tilmann Bitterberg's logo filter for Transcode * * * Changelog: * - 2004-09-23: first version (ImageMagick support) * - 2004-09-24: added libpng support * - 2004-09-25: added some comments and verbose messages * */ #include #include #include #include "mp_image.h" #include "img_format.h" #include "../mp_msg.h" #include "vf.h" #include "../libvo/fastmemcpy.h" #ifdef USE_MAGICK // ImageMagick #include #else // libpng #include #endif #ifndef USE_MAGICK // libpng struct pixel { unsigned char r,g,b,a; }; #endif struct vf_priv_s { int x, y; unsigned maxx, maxy, minx, miny; # ifdef USE_MAGICK // ImageMagick Image *image; ImageInfo *image_info; PixelPacket *pixel_packet; # else // libnpg struct pixel *pixels; unsigned width, height; # endif }; static int put_image(struct vf_instance_s* vf, mp_image_t* mpi) { mp_image_t* dmpi; unsigned int bpp = mpi->bpp / 8; long x, y, w, h; int planes,p,i; unsigned char *pix; unsigned char rgb[3]; # ifdef USE_MAGICK PixelPacket *pp; # else struct pixel *pp; # endif // Get video source image dmpi = vf_get_image(vf->next, mpi->imgfmt, MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE | MP_IMGFLAG_PREFER_ALIGNED_STRIDE, mpi->w, mpi->h); // Copy to destination image memcpy_pic(dmpi->planes[0],mpi->planes[0],mpi->w*bpp, mpi->h, dmpi->stride[0],mpi->stride[0]); // Add logo for(y=vf->priv->miny; ypriv->maxy; y++) { for(x=vf->priv->minx; xpriv->maxx; x++) { // Get current pixel pix = dmpi->planes[0] + y*dmpi->stride[0] + x*bpp; # ifdef USE_MAGICK // ImageMagick pp = &(vf->priv->pixel_packet[(y-vf->priv->y)*vf->priv->image->columns + (x-vf->priv->x)]); #define PIXR ((pp->red)/256) #define PIXG ((pp->green)/256) #define PIXB ((pp->blue)/256) #define OP ((float)((float)pp->opacity/(float)0xFFFF)) #define APP(v,p) pix[v] = p * (1.0-OP) + pix[v] * OP # else // libpng pp = &(vf->priv->pixels[(y-vf->priv->y)*vf->priv->width + (x-vf->priv->x)]); #define PIXR (pp->r) #define PIXG (pp->g) #define PIXB (pp->b) #define OP ((float)((float)pp->a/(float)0xFF)) #define APP(v,p) pix[v] = p * OP + pix[v] * (1.0-OP) # endif // Apply logo to current pixel's R, G & B components switch(mpi->imgfmt) { case IMGFMT_RGB24: case IMGFMT_RGB32: APP(0,PIXR); APP(1,PIXG); APP(2,PIXB); break; case IMGFMT_BGR24: case IMGFMT_BGR32: APP(2,PIXR); APP(1,PIXG); APP(0,PIXB); break; } // Apply rectangle (red in RGB, blue in BGR) to see the area modified by the filter // (useful with transparent images) # ifdef CHECK_RECT if(x == vf->priv->minx || x+1 == vf->priv->maxx || y == vf->priv->miny || y+1 == vf->priv->maxy) { pix[0] = 0xFF; pix[1] = 0; pix[2] = 0; } # endif } } // Call next filter return vf_next_put_image(vf, dmpi); } #ifndef USE_MAGICK #define PNG_CHECK_SIZE 8 // Open a PNG file static int open_png(char *file_name, FILE **fp) { char hdr[PNG_CHECK_SIZE]; int res; if (!(*fp = fopen(file_name, "rb"))) return 0; if (fread(hdr, 1, PNG_CHECK_SIZE, *fp) != PNG_CHECK_SIZE) { fclose(*fp); return 0; } res = !png_sig_cmp(hdr, 0, PNG_CHECK_SIZE); if(!res) fclose(*fp); return res; } // Load a PNG file static int load_png(FILE *fp, struct vf_instance_s* vf) { unsigned long i,j,k; png_structp png_ptr; png_infop info_ptr; png_uint_32 width, height; png_bytep *row_pointers; int bit_depth, color_type; int interlace_type, compression_type, filter_type; png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) return 0; info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); return 0; } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return 0; } png_init_io(png_ptr, fp); png_set_sig_bytes(png_ptr, 8); png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_type); png_set_strip_16(png_ptr); png_set_packing(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand(png_ptr); if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand(png_ptr); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_expand(png_ptr); png_set_filler(png_ptr, 0x000000ff, PNG_FILLER_AFTER); png_read_update_info(png_ptr, info_ptr); row_pointers = (png_bytep *)malloc(height*sizeof(png_bytep)); for (i = 0; i < height; i++) row_pointers[i] = malloc(png_get_rowbytes(png_ptr, info_ptr)); png_read_image(png_ptr, row_pointers); vf->priv->width = width; vf->priv->height = height; vf->priv->pixels = (struct pixel *)malloc(width*height*sizeof(struct pixel)); if(!vf->priv->pixels) return 0; k=0; for (i=0;ipriv->pixels[k].r = row_pointers[i][4*j]; vf->priv->pixels[k].g = row_pointers[i][4*j+1]; vf->priv->pixels[k].b = row_pointers[i][4*j+2]; vf->priv->pixels[k++].a = row_pointers[i][4*j+3]; } } png_read_end(png_ptr, info_ptr); png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); for (i = 0; i < height; i++) free(row_pointers[i]); free(row_pointers); return 1; } #endif #define MAX(a,b) ((a)>(b)?(a):(b)) #define MIN(a,b) ((a)<(b)?(a):(b)) static int config(struct vf_instance_s* vf, int width, int height, int d_width, int d_height, unsigned int flags, unsigned int outfmt) { vf->priv->minx = MAX(0,vf->priv->x); vf->priv->miny = MAX(0,vf->priv->y); # ifdef USE_MAGICK vf->priv->maxx = MIN(width,vf->priv->x+vf->priv->image->columns); vf->priv->maxy = MIN(height,vf->priv->y+vf->priv->image->rows); # else vf->priv->maxx = MIN(width,vf->priv->x+vf->priv->width); vf->priv->maxy = MIN(height,vf->priv->y+vf->priv->height); # endif return vf_next_config(vf, width, height, d_width, d_height, flags, outfmt); } static void uninit(struct vf_instance_s* vf) { # ifdef USE_MAGICK if(vf->priv->image) { DestroyImage(vf->priv->image); DestroyImageInfo(vf->priv->image_info); } DestroyMagick(); # else if(vf->priv->pixels) free(vf->priv->pixels); # endif free(vf->priv); } static int query_format(struct vf_instance_s* vf, unsigned int fmt) { // We support BGR/RGB colorspaces only switch(fmt) { case IMGFMT_BGR24: case IMGFMT_BGR32: case IMGFMT_RGB24: case IMGFMT_RGB32: return vf_next_query_format(vf,fmt); } return 0; } static int open(vf_instance_t* vf, char* args) { #ifdef USE_MAGICK #define MAX_PATH_SIZE MaxTextExtent #else #define MAX_PATH_SIZE 512 FILE *fp; #endif char path[MAX_PATH_SIZE]; vf->put_image = put_image; vf->uninit = uninit; vf->config = config; vf->query_format = query_format; vf->priv = malloc(sizeof(struct vf_priv_s)); if(!vf->priv) return 0; vf->priv->x = 0; vf->priv->y = 0; if (args) { char *c; strncpy(path,args,MAX_PATH_SIZE); path[MAX_PATH_SIZE-1] = '\0'; for(c=path; *c && *c!=':'; c++) { } if(*c) { *c = '\0'; sscanf(c+1,"%d:%d",&vf->priv->x,&vf->priv->y); } } else strcpy(path,"logo.png"); # ifdef USE_MAGICK // ImageMagick: load pixels from image ExceptionInfo exception_info; InitializeMagick(""); GetExceptionInfo (&exception_info); vf->priv->image_info = CloneImageInfo(NULL); strcpy(vf->priv->image_info->filename,path); vf->priv->image = ReadImage(vf->priv->image_info,&exception_info); if (!vf->priv->image) { MagickWarning (exception_info.severity, exception_info.reason, exception_info.description); return 0; } vf->priv->pixel_packet = GetImagePixels(vf->priv->image, 0, 0, vf->priv->image->columns, vf->priv->image->rows); "Logo: %dx%d, will be applied at (%d,%d)\n", vf->priv->image->columns, vf->priv->image->rows, vf->priv->x, vf->priv->y); # else // libpng: load pixels from PNG image vf->priv->pixels = NULL; if(!open_png(path,&fp)) { mp_msg(MSGT_VFILTER, MSGL_ERR, "Logo: couldn't open image '%s'\n", path); return 0; } if(!load_png(fp,vf)) { mp_msg(MSGT_VFILTER, MSGL_ERR, "Logo: couldn't load image '%s'\n", path); return 0; } mp_msg(MSGT_VFILTER, MSGL_INFO, "Logo: %dx%d, will be applied at (%d,%d)\n", vf->priv->width, vf->priv->height, vf->priv->x, vf->priv->y); # endif return 1; } vf_info_t vf_info_logo = { # ifdef USE_MAGICK "add logo from image", # else "add logo from PNG image", # endif "logo", "Julien 'dJeyL' Laurent", "v0.2 - 20040924", open, NULL };