gdImage *fx_rotate(gdImage *src, char *options) { int x, y; gdImage *im; int angle = atoi(options); /* Restrict angle to 0-360 range. */ if((angle %= 360) < 0) angle += 360; /* Round to nearest right angle. */ x = (angle + 45) % 360; angle = x - (x % 90); /* Not rotating 0 degrees. */ if(angle == 0) { MSG("Not rotating 0 degrees."); return(src); } /* 180 can be done with two flips. */ if(angle == 180) { fx_flip(src, "h,v"); return(src); } MSG("Rotating image %i degrees. %ix%i -> %ix%i.", angle, gdImageSX(src), gdImageSY(src), gdImageSY(src), gdImageSX(src)); /* Create rotated image. */ im = gdImageCreateTrueColor(gdImageSY(src), gdImageSX(src)); if(!im) { WARN("Out of memory."); return(src); } for(y = 0; y < gdImageSY(src); y++) for(x = 0; x < gdImageSX(src); x++) { int c = gdImageGetPixel(src, x, y); if(angle == 90) { gdImageSetPixel(im, gdImageSX(im) - y - 1, x, c); } else { gdImageSetPixel(im, y, gdImageSY(im) - x - 1, c); } } gdImageDestroy(src); return(im); }
int fswc_grab(fswebcam_config_t *config) { uint32_t frame; uint32_t x, y; avgbmp_t *abitmap, *pbitmap; gdImage *image, *original; uint8_t modified; src_t src; /* Record the start time. */ config->start = time(NULL); /* Set source options... */ memset(&src, 0, sizeof(src)); src.input = config->input; src.tuner = config->tuner; src.frequency = config->frequency; src.delay = config->delay; src.timeout = 10; /* seconds */ src.use_read = config->use_read; src.list = config->list; src.palette = config->palette; src.width = config->width; src.height = config->height; src.fps = config->fps; src.option = config->option; HEAD("--- Opening %s...", config->device); if(src_open(&src, config->device) == -1) return(-1); /* The source may have adjusted the width and height we passed * to it. Update the main config to match. */ config->width = src.width; config->height = src.height; /* Allocate memory for the average bitmap buffer. */ abitmap = calloc(config->width * config->height * 3, sizeof(avgbmp_t)); if(!abitmap) { ERROR("Out of memory."); return(-1); } if(config->frames == 1) HEAD("--- Capturing frame..."); else HEAD("--- Capturing %i frames...", config->frames); if(config->skipframes == 1) MSG("Skipping frame..."); else if(config->skipframes > 1) MSG("Skipping %i frames...", config->skipframes); /* Grab (and do nothing with) the skipped frames. */ for(frame = 0; frame < config->skipframes; frame++) if(src_grab(&src) == -1) break; /* If frames where skipped, inform when normal capture begins. */ if(config->skipframes) MSG("Capturing %i frames...", config->frames); /* Grab the requested number of frames. */ for(frame = 0; frame < config->frames; frame++) { if(src_grab(&src) == -1) break; if(!frame && config->dumpframe) { /* Dump the raw data from the first frame to file. */ FILE *f; MSG("Dumping raw frame to '%s'...", config->dumpframe); f = fopen(config->dumpframe, "wb"); if(!f) ERROR("fopen: %s", strerror(errno)); else { fwrite(src.img, 1, src.length, f); fclose(f); } } /* Add frame to the average bitmap. */ switch(src.palette) { case SRC_PAL_PNG: fswc_add_image_png(&src, abitmap); break; case SRC_PAL_JPEG: case SRC_PAL_MJPEG: fswc_add_image_jpeg(&src, abitmap); break; case SRC_PAL_S561: fswc_add_image_s561(abitmap, src.img, src.length, src.width, src.height, src.palette); break; case SRC_PAL_RGB32: fswc_add_image_rgb32(&src, abitmap); break; case SRC_PAL_BGR32: fswc_add_image_bgr32(&src, abitmap); break; case SRC_PAL_RGB24: fswc_add_image_rgb24(&src, abitmap); break; case SRC_PAL_BGR24: fswc_add_image_bgr24(&src, abitmap); break; case SRC_PAL_BAYER: case SRC_PAL_SGBRG8: case SRC_PAL_SGRBG8: fswc_add_image_bayer(abitmap, src.img, src.length, src.width, src.height, src.palette); break; case SRC_PAL_YUYV: case SRC_PAL_UYVY: fswc_add_image_yuyv(&src, abitmap); break; case SRC_PAL_YUV420P: fswc_add_image_yuv420p(&src, abitmap); break; case SRC_PAL_NV12MB: fswc_add_image_nv12mb(&src, abitmap); break; case SRC_PAL_RGB565: fswc_add_image_rgb565(&src, abitmap); break; case SRC_PAL_RGB555: fswc_add_image_rgb555(&src, abitmap); break; case SRC_PAL_Y16: fswc_add_image_y16(&src, abitmap); break; case SRC_PAL_GREY: fswc_add_image_grey(&src, abitmap); break; } } /* We are now finished with the capture card. */ src_close(&src); /* Fail if no frames where captured. */ if(!frame) { ERROR("No frames captured."); free(abitmap); return(-1); } HEAD("--- Processing captured image..."); /* Copy the average bitmap image to a gdImage. */ original = gdImageCreateTrueColor(config->width, config->height); if(!original) { ERROR("Out of memory."); free(abitmap); return(-1); } pbitmap = abitmap; for(y = 0; y < config->height; y++) for(x = 0; x < config->width; x++) { int px = x; int py = y; int colour; colour = (*(pbitmap++) / config->frames) << 16; colour += (*(pbitmap++) / config->frames) << 8; colour += (*(pbitmap++) / config->frames); gdImageSetPixel(original, px, py, colour); } free(abitmap); /* Make a copy of the original image. */ image = fswc_gdImageDuplicate(original); if(!image) { ERROR("Out of memory."); gdImageDestroy(image); return(-1); } /* Set the default values for this run. */ if(config->font) free(config->font); if(config->title) free(config->title); if(config->subtitle) free(config->subtitle); if(config->timestamp) free(config->timestamp); if(config->info) free(config->info); if(config->underlay) free(config->underlay); if(config->overlay) free(config->overlay); if(config->filename) free(config->filename); config->banner = BOTTOM_BANNER; config->bg_colour = 0x40263A93; config->bl_colour = 0x00FF0000; config->fg_colour = 0x00FFFFFF; config->font = strdup("sans"); config->fontsize = 10; config->shadow = 1; config->title = NULL; config->subtitle = NULL; config->timestamp = strdup("%Y-%m-%d %H:%M (%Z)"); config->info = NULL; config->underlay = NULL; config->overlay = NULL; config->filename = NULL; config->format = FORMAT_JPEG; config->compression = -1; modified = 1; /* Run through the jobs list. */ for(x = 0; x < config->jobs; x++) { uint16_t id = config->job[x]->id; char *options = config->job[x]->options; switch(id) { case 1: /* A non-option argument: a filename. */ case OPT_SAVE: fswc_output(config, options, image); modified = 0; break; case OPT_EXEC: fswc_exec(config, options); break; case OPT_REVERT: modified = 1; gdImageDestroy(image); image = fswc_gdImageDuplicate(original); break; case OPT_FLIP: modified = 1; image = fx_flip(image, options); break; case OPT_CROP: modified = 1; image = fx_crop(image, options); break; case OPT_SCALE: modified = 1; image = fx_scale(image, options); break; case OPT_ROTATE: modified = 1; image = fx_rotate(image, options); break; case OPT_DEINTERLACE: modified = 1; image = fx_deinterlace(image, options); break; case OPT_INVERT: modified = 1; image = fx_invert(image, options); break; case OPT_GREYSCALE: modified = 1; image = fx_greyscale(image, options); break; case OPT_SWAPCHANNELS: modified = 1; image = fx_swapchannels(image, options); break; case OPT_NO_BANNER: modified = 1; MSG("Disabling banner."); config->banner = NO_BANNER; break; case OPT_TOP_BANNER: modified = 1; MSG("Putting banner at the top."); config->banner = TOP_BANNER; break; case OPT_BOTTOM_BANNER: modified = 1; MSG("Putting banner at the bottom."); config->banner = BOTTOM_BANNER; break; case OPT_BG_COLOUR: modified = 1; MSG("Setting banner background colour to %s.", options); if(sscanf(options, "#%X", &config->bg_colour) != 1) WARN("Bad background colour: %s", options); break; case OPT_BL_COLOUR: modified = 1; MSG("Setting banner line colour to %s.", options); if(sscanf(options, "#%X", &config->bl_colour) != 1) WARN("Bad line colour: %s", options); break; case OPT_FG_COLOUR: modified = 1; MSG("Setting banner text colour to %s.", options); if(sscanf(options, "#%X", &config->fg_colour) != 1) WARN("Bad text colour: %s", options); break; case OPT_FONT: modified = 1; MSG("Setting font to %s.", options); if(parse_font(options, &config->font, &config->fontsize)) WARN("Bad font: %s", options); break; case OPT_NO_SHADOW: modified = 1; MSG("Disabling text shadow."); config->shadow = 0; break; case OPT_SHADOW: modified = 1; MSG("Enabling text shadow."); config->shadow = 1; break; case OPT_TITLE: modified = 1; MSG("Setting title \"%s\".", options); if(config->title) free(config->title); config->title = strdup(options); break; case OPT_NO_TITLE: modified = 1; MSG("Clearing title."); if(config->title) free(config->title); config->title = NULL; break; case OPT_SUBTITLE: modified = 1; MSG("Setting subtitle \"%s\".", options); if(config->subtitle) free(config->subtitle); config->subtitle = strdup(options); break; case OPT_NO_SUBTITLE: modified = 1; MSG("Clearing subtitle."); if(config->subtitle) free(config->subtitle); config->subtitle = NULL; break; case OPT_TIMESTAMP: modified = 1; MSG("Setting timestamp \"%s\".", options); if(config->timestamp) free(config->timestamp); config->timestamp = strdup(options); break; case OPT_NO_TIMESTAMP: modified = 1; MSG("Clearing timestamp."); if(config->timestamp) free(config->timestamp); config->timestamp = NULL; break; case OPT_INFO: modified = 1; MSG("Setting info text \"%s\".", options); if(config->info) free(config->info); config->info = strdup(options); break; case OPT_NO_INFO: modified = 1; MSG("Clearing info text."); if(config->info) free(config->info); config->info = NULL; break; case OPT_UNDERLAY: modified = 1; MSG("Setting underlay image: %s", options); if(config->underlay) free(config->underlay); config->underlay = strdup(options); break; case OPT_NO_UNDERLAY: modified = 1; MSG("Clearing underlay."); if(config->underlay) free(config->underlay); config->underlay = NULL; break; case OPT_OVERLAY: modified = 1; MSG("Setting overlay image: %s", options); if(config->overlay) free(config->overlay); config->overlay = strdup(options); break; case OPT_NO_OVERLAY: modified = 1; MSG("Clearing overlay image."); if(config->overlay) free(config->overlay); config->overlay = NULL; break; case OPT_JPEG: modified = 1; MSG("Setting output format to JPEG, quality %i", atoi(options)); config->format = FORMAT_JPEG; config->compression = atoi(options); break; case OPT_PNG: modified = 1; MSG("Setting output format to PNG, quality %i", atoi(options)); config->format = FORMAT_PNG; config->compression = atoi(options); break; } } gdImageDestroy(image); gdImageDestroy(original); if(modified) WARN("There are unsaved changes to the image."); return(0); }