static void run (const gchar *name, gint n_params, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals) { static GimpParam values[1]; GimpDrawable *drawable; GimpRunMode run_mode; GimpPDBStatusType status = GIMP_PDB_SUCCESS; gint32 image_id; *nreturn_vals = 1; *return_vals = values; run_mode = param[0].data.d_int32; if (run_mode == GIMP_RUN_NONINTERACTIVE) { if (n_params != 3) { status = GIMP_PDB_CALLING_ERROR; } } if (status == GIMP_PDB_SUCCESS) { /* Get the specified drawable */ drawable = gimp_drawable_get(param[2].data.d_drawable); image_id = param[1].data.d_image; /* Make sure that the drawable is gray or RGB or indexed */ if (gimp_drawable_is_rgb (drawable->drawable_id) || gimp_drawable_is_gray (drawable->drawable_id) || gimp_drawable_is_indexed (drawable->drawable_id)) { gimp_progress_init ("Autocropping..."); gimp_tile_cache_ntiles (1 + (drawable->width > drawable->height ? (drawable->width / gimp_tile_width()) : (drawable->height / gimp_tile_height()))); do_acrop(drawable, image_id); if (run_mode != GIMP_RUN_NONINTERACTIVE) gimp_displays_flush (); gimp_drawable_detach (drawable); } else { status = GIMP_PDB_EXECUTION_ERROR; } } values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = status; }
/** * gimp_tile_cache_ntiles: * @ntiles: number of tiles that should fit into the cache * * Sets the size of the tile cache on the plug-in side. This function * is similar to gimp_tile_cache_size() but supports specifying the * number of tiles directly. * * If your plug-in access pixels tile-by-tile, it doesn't need a tile * cache at all. If however the plug-in accesses drawable pixel data * row-by-row, it should set the tile cache large enough to hold the * number of tiles per row. Double this size if your plug-in uses * shadow tiles. **/ void gimp_tile_cache_ntiles (gulong ntiles) { max_cache_size = (ntiles * gimp_tile_width () * gimp_tile_height () * 4); }
static void pixelize (GimpDrawable *drawable, GimpPreview *preview) { gint tile_width; gint tile_height; gint pixelwidth; gint pixelheight; tile_width = gimp_tile_width (); tile_height = gimp_tile_height (); pixelwidth = pvals.pixelwidth; pixelheight = pvals.pixelheight; if (pixelwidth < 0) pixelwidth = - pixelwidth; if (pixelwidth < 1) pixelwidth = 1; if (pixelheight < 0) pixelheight = - pixelheight; if (pixelheight < 1) pixelheight = 1; if (pixelwidth >= tile_width || pixelheight >= tile_height || preview) pixelize_large (drawable, pixelwidth, pixelheight, preview); else pixelize_small (drawable, pixelwidth, pixelheight, tile_width, tile_height); }
/* * sendBMPToGIMP * * Take the captured data and send it across * to GIMP. */ static void sendBMPToGimp(HBITMAP hBMP, HDC hDC, RECT rect) { int width, height; int imageType, layerType; gint32 image_id; gint32 layer_id; GimpPixelRgn pixel_rgn; GimpDrawable *drawable; /* Our width and height */ width = (rect.right - rect.left); height = (rect.bottom - rect.top); /* Check that we got the memory */ if (!capBytes) { g_message (_("No data captured")); return; } /* Flip the red and blue bytes */ flipRedAndBlueBytes(width, height); /* Set up the image and layer types */ imageType = GIMP_RGB; layerType = GIMP_RGB_IMAGE; /* Create the GIMP image and layers */ image_id = gimp_image_new(width, height, imageType); layer_id = gimp_layer_new(image_id, _("Background"), ROUND4(width), height, layerType, 100, GIMP_NORMAL_MODE); gimp_image_insert_layer(image_id, layer_id, -1, 0); /* Get our drawable */ drawable = gimp_drawable_get(layer_id); gimp_tile_cache_size(ROUND4(width) * gimp_tile_height() * 3); /* Initialize a pixel region for writing to the image */ gimp_pixel_rgn_init(&pixel_rgn, drawable, 0, 0, ROUND4(width), height, TRUE, FALSE); gimp_pixel_rgn_set_rect(&pixel_rgn, (guchar *) capBytes, 0, 0, ROUND4(width), height); /* HB: update data BEFORE size change */ gimp_drawable_flush(drawable); /* Now resize the layer down to the correct size if necessary. */ if (width != ROUND4(width)) { gimp_layer_resize (layer_id, width, height, 0, 0); gimp_image_resize (image_id, width, height, 0, 0); } /* Finish up */ gimp_drawable_detach(drawable); gimp_display_new (image_id); return; }
/* Draw the hole at the specified position */ static void draw_hole_rgb (GimpDrawable *drw, gint x, gint y, gint width, gint height, guchar *hole) { GimpPixelRgn rgn; guchar *data; gint tile_height = gimp_tile_height (); gint i, j, scan_lines; gint d_width = gimp_drawable_width (drw->drawable_id); gint length; if ((width <= 0) || (height <= 0)) return; if ((x+width <= 0) || (x >= d_width)) return; length = width; /* Check that we dont draw past the image */ if ((x+length) >= d_width) length = d_width-x; data = g_new (guchar, length * tile_height * drw->bpp); gimp_pixel_rgn_init (&rgn, drw, x, y, length, height, TRUE, FALSE); i = 0; while (i < height) { scan_lines = (i+tile_height-1 < height) ? tile_height : (height-i); if (length == width) { memcpy (data, hole + 3*width*i, width*scan_lines*3); } else /* We have to do some clipping */ { for (j = 0; j < scan_lines; j++) memcpy (data + j*length*3, hole + (i+j)*width*3, length*3); } gimp_pixel_rgn_set_rect (&rgn, data, x, y+i, length, scan_lines); i += scan_lines; } g_free (data); }
/* Decompose an image. It returns the number of new (gray) images. The image IDs for the new images are returned in image_ID_dst. On failure, -1 is returned. */ static gint32 decompose (gint32 image_ID, gint32 drawable_ID, const gchar *extract_type, gint32 *image_ID_dst, gint32 *nlayers, gint32 *layer_ID_dst) { const gchar *layername; gint i, j, extract_idx, scan_lines; gint height, width, tile_height, num_layers; gchar *filename; guchar *src; guchar *dst[MAX_EXTRACT_IMAGES]; GimpDrawable *drawable_src; GimpDrawable *drawable_dst[MAX_EXTRACT_IMAGES]; GimpPixelRgn pixel_rgn_src; GimpPixelRgn pixel_rgn_dst[MAX_EXTRACT_IMAGES]; extract_idx = -1; /* Search extract type */ for (j = 0; j < G_N_ELEMENTS (extract); j++) { if (g_ascii_strcasecmp (extract_type, extract[j].type) == 0) { extract_idx = j; break; } } if (extract_idx < 0) return -1; /* Check structure of source image */ drawable_src = gimp_drawable_get (drawable_ID); if (drawable_src->bpp < 3) { g_message ("Not an RGB image."); return -1; } if ((extract[extract_idx].extract_fun == extract_alpha || extract[extract_idx].extract_fun == extract_rgba) && (!gimp_drawable_has_alpha (drawable_ID))) { g_message ("No alpha channel available."); return -1; } width = drawable_src->width; height = drawable_src->height; tile_height = gimp_tile_height (); gimp_pixel_rgn_init (&pixel_rgn_src, drawable_src, 0, 0, width, height, FALSE, FALSE); /* allocate a buffer for retrieving information from the src pixel region */ src = g_new (guchar, tile_height * width * drawable_src->bpp); /* Create all new gray images */ num_layers = extract[extract_idx].num_images; if (num_layers > MAX_EXTRACT_IMAGES) num_layers = MAX_EXTRACT_IMAGES; for (j = 0; j < num_layers; j++) { /* Build a filename like <imagename>-<channel>.<extension> */ gchar *fname; gchar *extension; gdouble xres, yres; fname = gimp_image_get_filename (image_ID); if (fname) { extension = fname + strlen (fname) - 1; while (extension >= fname) { if (*extension == '.') break; extension--; } if (extension >= fname) { *(extension++) = '\0'; if (decovals.as_layers) filename = g_strdup_printf ("%s-%s.%s", fname, gettext (extract[extract_idx].type), extension); else filename = g_strdup_printf ("%s-%s.%s", fname, gettext (extract[extract_idx].channel_name[j]), extension); } else { if (decovals.as_layers) filename = g_strdup_printf ("%s-%s", fname, gettext (extract[extract_idx].type)); else filename = g_strdup_printf ("%s-%s", fname, gettext (extract[extract_idx].channel_name[j])); } } else { filename = g_strdup (gettext (extract[extract_idx].channel_name[j])); } gimp_image_get_resolution (image_ID, &xres, &yres); if (decovals.as_layers) { layername = gettext (extract[extract_idx].channel_name[j]); if (j == 0) image_ID_dst[j] = create_new_image (filename, layername, width, height, GIMP_GRAY, xres, yres, layer_ID_dst + j, drawable_dst + j, pixel_rgn_dst + j); else layer_ID_dst[j] = create_new_layer (image_ID_dst[0], j, layername, width, height, GIMP_GRAY, drawable_dst + j, pixel_rgn_dst + j); } else { image_ID_dst[j] = create_new_image (filename, NULL, width, height, GIMP_GRAY, xres, yres, layer_ID_dst + j, drawable_dst + j, pixel_rgn_dst + j); } g_free (filename); g_free (fname); dst[j] = g_new (guchar, tile_height * width); } i = 0; while (i < height) { /* Get source pixel region */ scan_lines = (i+tile_height-1 < height) ? tile_height : (height-i); gimp_pixel_rgn_get_rect (&pixel_rgn_src, src, 0, i, width, scan_lines); /* Extract the channel information */ extract[extract_idx].extract_fun (src, drawable_src->bpp, scan_lines*width, dst); /* Transfer the registration color */ if (decovals.use_registration) transfer_registration_color (src, drawable_src->bpp, scan_lines*width, dst, extract[extract_idx].num_images); /* Set destination pixel regions */ for (j = 0; j < num_layers; j++) gimp_pixel_rgn_set_rect (&(pixel_rgn_dst[j]), dst[j], 0, i, width, scan_lines); i += scan_lines; gimp_progress_update ((gdouble) i / (gdouble) height); } g_free (src); for (j = 0; j < num_layers; j++) { gimp_drawable_detach (drawable_dst[j]); gimp_drawable_update (layer_ID_dst[j], 0, 0, gimp_drawable_width (layer_ID_dst[j]), gimp_drawable_height (layer_ID_dst[j])); gimp_layer_add_alpha (layer_ID_dst[j]); g_free (dst[j]); } gimp_drawable_detach (drawable_src); *nlayers = num_layers; return (decovals.as_layers ? 1 : num_layers); }
static gint32 load_image (const gchar *filename, gboolean interactive, GError **error) { gint32 status = -1; EXRLoader *loader; int width; int height; gboolean has_alpha; GimpImageBaseType image_type; GimpPrecision image_precision; gint32 image = -1; GimpImageType layer_type; int layer; const Babl *format; GeglBuffer *buffer = NULL; int bpp; int tile_height; gchar *pixels = NULL; int begin; int end; int num; loader = exr_loader_new (filename); if (!loader) { g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("Error opening file '%s' for reading"), gimp_filename_to_utf8 (filename)); goto out; } width = exr_loader_get_width (loader); height = exr_loader_get_height (loader); if ((width < 1) || (height < 1)) { g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("Error querying image dimensions from '%s'"), gimp_filename_to_utf8 (filename)); goto out; } has_alpha = exr_loader_has_alpha (loader) ? TRUE : FALSE; switch (exr_loader_get_precision (loader)) { case PREC_UINT: image_precision = GIMP_PRECISION_U32; break; case PREC_HALF: image_precision = GIMP_PRECISION_HALF; break; case PREC_FLOAT: image_precision = GIMP_PRECISION_FLOAT; break; default: g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("Error querying image precision from '%s'"), gimp_filename_to_utf8 (filename)); goto out; } switch (exr_loader_get_image_type (loader)) { case IMAGE_TYPE_RGB: image_type = GIMP_RGB; layer_type = has_alpha ? GIMP_RGBA_IMAGE : GIMP_RGB_IMAGE; break; case IMAGE_TYPE_GRAY: image_type = GIMP_GRAY; layer_type = has_alpha ? GIMP_GRAYA_IMAGE : GIMP_GRAY_IMAGE; break; default: g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("Error querying image type from '%s'"), gimp_filename_to_utf8 (filename)); goto out; } gimp_progress_init_printf (_("Opening '%s'"), gimp_filename_to_utf8 (filename)); image = gimp_image_new_with_precision (width, height, image_type, image_precision); if (image == -1) { g_set_error (error, 0, 0, _("Could not create new image for '%s': %s"), gimp_filename_to_utf8 (filename), gimp_get_pdb_error ()); goto out; } gimp_image_set_filename (image, filename); layer = gimp_layer_new (image, _("Background"), width, height, layer_type, 100, GIMP_NORMAL_MODE); gimp_image_insert_layer (image, layer, -1, 0); buffer = gimp_drawable_get_buffer (layer); format = gimp_drawable_get_format (layer); bpp = babl_format_get_bytes_per_pixel (format); tile_height = gimp_tile_height (); pixels = g_new0 (gchar, tile_height * width * bpp); for (begin = 0; begin < height; begin += tile_height) { int retval; int i; end = MIN (begin + tile_height, height); num = end - begin; for (i = 0; i < num; i++) { retval = exr_loader_read_pixel_row (loader, pixels + (i * width * bpp), bpp, begin + i); if (retval < 0) { g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("Error reading pixel data from '%s'"), gimp_filename_to_utf8 (filename)); goto out; } } gegl_buffer_set (buffer, GEGL_RECTANGLE (0, begin, width, num), 0, NULL, pixels, GEGL_AUTO_ROWSTRIDE); gimp_progress_update ((gdouble) begin / (gdouble) height); } gimp_progress_update (1.0); status = image; out: if (buffer) g_object_unref (buffer); if ((status != image) && (image != -1)) { /* This should clean up any associated layers too. */ gimp_image_delete (image); } if (pixels) g_free (pixels); if (loader) exr_loader_unref (loader); return status; }
gint32 xjpg_load_layer (const char *filename, gint32 image_id, int image_type, char *layer_name, gdouble layer_opacity, GimpLayerModeEffects layer_mode ) { GimpPixelRgn l_pixel_rgn; GimpDrawable *l_drawable; gint32 l_layer_id; GimpImageType l_layer_type; struct jpeg_decompress_struct cinfo; struct my_error_mgr jerr; FILE *infile; guchar *l_buf; guchar **l_rowbuf; int l_tile_height; int l_scanlines; int l_idx, l_start, l_end; /* We set up the normal JPEG error routines. */ cinfo.err = jpeg_std_error (&jerr.pub); jerr.pub.error_exit = my_error_exit; l_layer_type = GIMP_GRAY_IMAGE; if ((infile = g_fopen (filename, "rb")) == NULL) { g_warning ("can't open \"%s\"\n", filename); return -1; } /* Establish the setjmp return context for my_error_exit to use. */ if (setjmp (jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. * We need to clean up the JPEG object, close the input file, and return. */ jpeg_destroy_decompress (&cinfo); if (infile) fclose (infile); g_printerr ("XJT: JPEG load error\n"); return -1; } /* Now we can initialize the JPEG decompression object. */ jpeg_create_decompress (&cinfo); /* Step 2: specify data source (eg, a file) */ jpeg_stdio_src (&cinfo, infile); /* Step 3: read file parameters with jpeg_read_header() */ (void) jpeg_read_header (&cinfo, TRUE); /* We can ignore the return value from jpeg_read_header since * (a) suspension is not possible with the stdio data source, and * (b) we passed TRUE to reject a tables-only JPEG file as an error. * See libjpeg.doc for more info. */ /* Step 4: set parameters for decompression */ /* In this example, we don't need to change any of the defaults set by * jpeg_read_header(), so we do nothing here. */ /* Step 5: Start decompressor */ jpeg_start_decompress (&cinfo); /* We may need to do some setup of our own at this point before reading * the data. After jpeg_start_decompress() we have the correct scaled * output image dimensions available, as well as the output colormap * if we asked for color quantization. * In this example, we need to make an output work buffer of the right size. */ /* temporary buffer */ l_tile_height = gimp_tile_height (); l_buf = g_new (guchar, l_tile_height * cinfo.output_width * cinfo.output_components); l_rowbuf = g_new (guchar*, l_tile_height); for (l_idx = 0; l_idx < l_tile_height; l_idx++) { l_rowbuf[l_idx] = l_buf + cinfo.output_width * cinfo.output_components * l_idx; } /* Check jpeg file for layer type */ switch (cinfo.output_components) { case 1: l_layer_type = GIMP_GRAY_IMAGE; break; case 3: l_layer_type = GIMP_RGB_IMAGE; break; default: g_printerr ("XJT: cant load layer %s (type is not GRAY and not RGB)\n", filename); fclose (infile); return -1; } l_layer_id = gimp_layer_new (image_id, layer_name, cinfo.output_width, cinfo.output_height, l_layer_type, layer_opacity, layer_mode); if(l_layer_id < 0) { g_printerr ("XJT: cant create new layer\n"); fclose (infile); return -1; } l_drawable = gimp_drawable_get (l_layer_id); gimp_pixel_rgn_init (&l_pixel_rgn, l_drawable, 0, 0, l_drawable->width, l_drawable->height, TRUE, FALSE); /* Step 6: while (scan lines remain to be read) */ /* jpeg_read_scanlines(...); */ /* Here we use the library's state variable cinfo.output_scanline as the * loop counter, so that we don't have to keep track ourselves. */ while (cinfo.output_scanline < cinfo.output_height) { l_start = cinfo.output_scanline; l_end = cinfo.output_scanline + l_tile_height; l_end = MIN (l_end, cinfo.output_height); l_scanlines = l_end - l_start; for (l_idx = 0; l_idx < l_scanlines; l_idx++) { jpeg_read_scanlines (&cinfo, (JSAMPARRAY) &l_rowbuf[l_idx], 1); } gimp_pixel_rgn_set_rect (&l_pixel_rgn, l_buf, 0, l_start, l_drawable->width, l_scanlines); gimp_progress_update ((double) cinfo.output_scanline / (double) cinfo.output_height); } gimp_progress_update (1.0); /* Step 7: Finish decompression */ jpeg_finish_decompress (&cinfo); /* We can ignore the return value since suspension is not possible * with the stdio data source. */ /* Step 8: Release JPEG decompression object */ /* This is an important step since it will release a good deal of memory. */ jpeg_destroy_decompress (&cinfo); /* free up the temporary buffers */ g_free (l_rowbuf); g_free (l_buf); /* After finish_decompress, we can close the input file. * Here we postpone it until after no more JPEG errors are possible, * so as to simplify the setjmp error logic above. (Actually, I don't * think that jpeg_destroy can do an error exit, but why assume anything...) */ fclose (infile); /* At this point you may want to check to see whether any corrupt-data * warnings occurred (test whether jerr.num_warnings is nonzero). */ return (l_layer_id); } /* end xjpg_load_layer */
gint xjpg_save_drawable (const char *filename, gint32 image_ID, gint32 drawable_ID, gint save_mode, t_JpegSaveVals *jsvals) { GimpPixelRgn pixel_rgn; GimpDrawable *drawable; GimpImageType drawable_type; struct jpeg_compress_struct cinfo; struct my_error_mgr jerr; FILE * volatile outfile; guchar *temp, *t; guchar *data; guchar *src, *s; int has_alpha; int rowstride, yend; int i, j; int alpha_offset; guchar alpha_byte; guchar volatile l_alpha_sum; alpha_offset = 0; has_alpha = 0; src = NULL; temp = NULL; data = NULL; l_alpha_sum = 0xff; drawable = gimp_drawable_get (drawable_ID); drawable_type = gimp_drawable_type (drawable_ID); switch (drawable_type) { case GIMP_RGB_IMAGE: case GIMP_GRAY_IMAGE: if(save_mode == JSVM_ALPHA) return FALSE; /* there is no alpha to save */ break; case GIMP_RGBA_IMAGE: case GIMP_GRAYA_IMAGE: break; case GIMP_INDEXED_IMAGE: /*g_message ("jpeg: cannot operate on indexed color images");*/ return FALSE; break; default: /*g_message ("jpeg: cannot operate on unknown image types");*/ return FALSE; break; } gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, drawable->width, drawable->height, FALSE, FALSE); /* Step 1: allocate and initialize JPEG compression object */ /* We have to set up the error handler first, in case the initialization * step fails. (Unlikely, but it could happen if you are out of memory.) * This routine fills in the contents of struct jerr, and returns jerr's * address which we place into the link field in cinfo. */ cinfo.err = jpeg_std_error (&jerr.pub); jerr.pub.error_exit = my_error_exit; outfile = NULL; /* Establish the setjmp return context for my_error_exit to use. */ if (setjmp (jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. * We need to clean up the JPEG object, close the input file, and return. */ jpeg_destroy_compress (&cinfo); if (outfile) fclose (outfile); if (drawable) gimp_drawable_detach (drawable); return FALSE; } /* Now we can initialize the JPEG compression object. */ jpeg_create_compress (&cinfo); /* Step 2: specify data destination (eg, a file) */ /* Note: steps 2 and 3 can be done in either order. */ /* Here we use the library-supplied code to send compressed data to a * stdio stream. You can also write your own code to do something else. * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that * requires it in order to write binary files. */ if ((outfile = g_fopen (filename, "wb")) == NULL) { g_message ("can't open %s\n", filename); return FALSE; } jpeg_stdio_dest (&cinfo, outfile); /* Get the input image and a pointer to its data. */ switch (drawable_type) { case GIMP_RGB_IMAGE: case GIMP_GRAY_IMAGE: /* # of color components per pixel */ cinfo.input_components = drawable->bpp; has_alpha = 0; alpha_offset = 0; break; case GIMP_RGBA_IMAGE: case GIMP_GRAYA_IMAGE: if(save_mode == JSVM_ALPHA) { cinfo.input_components = 1; } else { /* # of color components per pixel (minus the GIMP alpha channel) */ cinfo.input_components = drawable->bpp - 1; } alpha_offset = drawable->bpp -1; has_alpha = 1; break; default: return FALSE; break; } /* Step 3: set parameters for compression */ /* First we supply a description of the input image. * Four fields of the cinfo struct must be filled in: */ /* image width and height, in pixels */ cinfo.image_width = drawable->width; cinfo.image_height = drawable->height; /* colorspace of input image */ cinfo.in_color_space = ( (save_mode != JSVM_ALPHA) && (drawable_type == GIMP_RGB_IMAGE || drawable_type == GIMP_RGBA_IMAGE)) ? JCS_RGB : JCS_GRAYSCALE; /* Now use the library's routine to set default compression parameters. * (You must set at least cinfo.in_color_space before calling this, * since the defaults depend on the source color space.) */ jpeg_set_defaults (&cinfo); /* Now you can set any non-default parameters you wish to. * Here we just illustrate the use of quality (quantization table) scaling: */ jpeg_set_quality (&cinfo, (int) (jsvals->quality * 100), TRUE /* limit to baseline-JPEG values */); cinfo.smoothing_factor = (int) (jsvals->smoothing * 100); cinfo.optimize_coding = jsvals->optimize; /* Step 4: Start compressor */ /* TRUE ensures that we will write a complete interchange-JPEG file. * Pass TRUE unless you are very sure of what you're doing. */ jpeg_start_compress (&cinfo, TRUE); /* Step 5: while (scan lines remain to be written) */ /* jpeg_write_scanlines(...); */ /* Here we use the library's state variable cinfo.next_scanline as the * loop counter, so that we don't have to keep track ourselves. * To keep things simple, we pass one scanline per call; you can pass * more if you wish, though. */ /* JSAMPLEs per row in image_buffer */ rowstride = drawable->bpp * drawable->width; temp = (guchar *) g_malloc (cinfo.image_width * cinfo.input_components); data = (guchar *) g_malloc (rowstride * gimp_tile_height ()); src = data; while (cinfo.next_scanline < cinfo.image_height) { if ((cinfo.next_scanline % gimp_tile_height ()) == 0) { yend = cinfo.next_scanline + gimp_tile_height (); yend = MIN (yend, cinfo.image_height); gimp_pixel_rgn_get_rect (&pixel_rgn, data, 0, cinfo.next_scanline, cinfo.image_width, (yend - cinfo.next_scanline)); src = data; } t = temp; s = src; i = cinfo.image_width; switch(save_mode) { case JSVM_DRAWABLE: if(jsvals->clr_transparent) { /* save drawable (clear pixels where alpha is full transparent) */ while (i--) { alpha_byte = s[cinfo.input_components]; for (j = 0; j < cinfo.input_components; j++) { if(alpha_byte != 0) { *t++ = *s++; } else { *t++ = 0; s++; } } if (has_alpha) /* ignore alpha channel */ { s++; } } } else { /* save the drawable as it is (ignore alpha channel) */ while (i--) { for (j = 0; j < cinfo.input_components; j++) { *t++ = *s++; } if (has_alpha) /* ignore alpha channel */ { s++; } } } break; case JSVM_ALPHA: /* save the drawable's alpha cahnnel */ while (i--) { s += alpha_offset; l_alpha_sum &= (*s); /* check all alpha bytes for full opacity */ *t++ = *s++; } break; } src += rowstride; jpeg_write_scanlines (&cinfo, (JSAMPARRAY) &temp, 1); if ((cinfo.next_scanline % 5) == 0) gimp_progress_update ((double) cinfo.next_scanline / (double) cinfo.image_height); } gimp_progress_update (1.0); /* Step 6: Finish compression */ jpeg_finish_compress (&cinfo); /* After finish_compress, we can close the output file. */ fclose (outfile); if((save_mode == JSVM_ALPHA) && (l_alpha_sum == 0xff)) { /* all bytes in the alpha channel are set to 0xff * == full opaque image. We can remove the file * to save diskspace */ g_remove(filename); } /* Step 7: release JPEG compression object */ /* This is an important step since it will release a good deal of memory. */ jpeg_destroy_compress (&cinfo); /* free the temporary buffer */ g_free (temp); g_free (data); gimp_drawable_detach (drawable); return TRUE; } /* end xjpg_save_drawable */
static gint32 load_image (char *filename) { GPixelRgn pixel_rgn; TileDrawable *drawable; gint32 image_ID; gint32 layer_ID; struct jpeg_decompress_struct cinfo; struct my_error_mgr jerr; FILE *infile; guchar *buf; guchar **rowbuf; char *name; int image_type; int layer_type; int tile_height; int scanlines; int i, start, end; int m; int depth = 8; /* We set up the normal JPEG error routines. */ cinfo.err = jpeg_std_error (&jerr.pub); jerr.pub.error_exit = my_error_exit; if ((infile = fopen (filename, "rb")) == NULL) { g_warning ("can't open \"%s\"\n", filename); gimp_quit (); } if( strrchr(filename,'.') && strcmp( strrchr(filename, '.'), ".jp4") == 0 ) depth = 16; name = malloc (strlen (filename) + 12); sprintf (name, "%s %s:", _("Loading"), filename); gimp_progress_init (name); free (name); image_ID = -1; /* Establish the setjmp return context for my_error_exit to use. */ if (setjmp (jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. * We need to clean up the JPEG object, close the input file, and return. */ jpeg_destroy_decompress (&cinfo); if (infile) fclose (infile); if (image_ID != -1) gimp_image_delete (image_ID); gimp_quit (); } /* Now we can initialize the JPEG decompression object. */ jpeg_create_decompress (&cinfo); /* Step 2: specify data source (eg, a file) */ jpeg_stdio_src (&cinfo, infile); setup_read_icc_profile(&cinfo); for (m = 0; m < 16; m++) jpeg_save_markers(&cinfo, JPEG_APP0 + m, 0xFFFF); /* Step 3: read file parameters with jpeg_read_header() */ (void) jpeg_read_header (&cinfo, TRUE); /* We can ignore the return value from jpeg_read_header since * (a) suspension is not possible with the stdio data source, and * (b) we passed TRUE to reject a tables-only JPEG file as an error. * See libjpeg.doc for more info. */ /* Step 4: set parameters for decompression */ /* In this example, we don't need to change any of the defaults set by * jpeg_read_header(), so we do nothing here. */ prepareColour( &cinfo ); /* Step 5: Start decompressor */ jpeg_start_decompress (&cinfo); /* We may need to do some setup of our own at this point before reading * the data. After jpeg_start_decompress() we have the correct scaled * output image dimensions available, as well as the output colormap * if we asked for color quantization. * In this example, we need to make an output work buffer of the right size. */ /* temporary buffer */ tile_height = gimp_tile_height (); buf = g_new (guchar, tile_height * cinfo.output_width * cinfo.output_components); rowbuf = g_new (guchar*, tile_height); for (i = 0; i < tile_height; i++) rowbuf[i] = buf + cinfo.output_width * cinfo.output_components * i; /* Create a new image of the proper size and associate the filename with it. */ if(depth == 8) { switch (cinfo.output_components) { case 1: image_type = GRAY; layer_type = GRAY_IMAGE; break; case 3: image_type = RGB; layer_type = RGB_IMAGE; break; case 4: image_type = RGB; layer_type = RGBA_IMAGE; break; default: gimp_quit (); } } else { switch (cinfo.output_components) { case 1: image_type = U16_GRAY; layer_type = U16_GRAY_IMAGE; break; case 3: image_type = U16_RGB; layer_type = U16_RGB_IMAGE; break; case 4: image_type = U16_RGB; layer_type = U16_RGBA_IMAGE; break; default: gimp_quit (); } } image_ID = gimp_image_new (cinfo.output_width / (depth/8), cinfo.output_height, image_type); gimp_image_set_filename (image_ID, filename); layer_ID = gimp_layer_new (image_ID, _("Background"), cinfo.output_width / (depth/8), cinfo.output_height, layer_type, 100, NORMAL_MODE); gimp_image_add_layer (image_ID, layer_ID, 0); drawable = gimp_drawable_get (layer_ID); gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, drawable->width, drawable->height, TRUE, FALSE); /* Step 6: while (scan lines remain to be read) */ /* jpeg_read_scanlines(...); */ /* Here we use the library's state variable cinfo.output_scanline as the * loop counter, so that we don't have to keep track ourselves. */ while (cinfo.output_scanline < cinfo.output_height) { start = cinfo.output_scanline; end = cinfo.output_scanline + tile_height; end = MIN (end,CAST(int) cinfo.output_height); scanlines = end - start; for (i = 0; i < scanlines; i++) jpeg_read_scanlines (&cinfo, (JSAMPARRAY) &rowbuf[i], 1); /* for (i = start; i < end; i++) gimp_pixel_rgn_set_row (&pixel_rgn, tilerow[i - start], 0, i, drawable->width); */ if(cinfo.out_color_space == JCS_CMYK) for(i = 0; i < scanlines*drawable->width*cinfo.output_components; ++i) buf[i] = 255 - buf[i]; if(depth == 16 && 0) for(i = 0; i < scanlines*drawable->width*cinfo.output_components; ++i) { unsigned char c = buf[2*i]; buf[2*i] = buf[2*i+1]; buf[2*i+1] = c; } gimp_pixel_rgn_set_rect (&pixel_rgn, buf, 0, start, drawable->width, scanlines); gimp_progress_update ((double) cinfo.output_scanline / (double) cinfo.output_height); } // Step 6a: read icc profile { LPBYTE Buffer = NULL; size_t Len = 0; cmsHPROFILE hProfile=NULL; if (read_icc_profile(&cinfo, &Buffer, &Len)) { printf ("%s:%d %s() embedded profile found\n",__FILE__,__LINE__,__func__); } else if (read_icc_profile2(&cinfo, &Buffer, &Len)) { printf ("%s:%d %s() default profile selected\n",__FILE__,__LINE__,__func__); } if(Buffer && Len) { hProfile = cmsOpenProfileFromMem(Buffer, Len); if (hProfile) { gimp_image_set_icc_profile_by_mem (image_ID, Len, Buffer, ICC_IMAGE_PROFILE); cmsCloseProfile (hProfile); free(Buffer); printf ("%s:%d %s() set profile\n",__FILE__,__LINE__,__func__); } } } /* Step 7: Finish decompression */ jpeg_finish_decompress (&cinfo); /* We can ignore the return value since suspension is not possible * with the stdio data source. */ /* Step 8: Release JPEG decompression object */ /* This is an important step since it will release a good deal of memory. */ jpeg_destroy_decompress (&cinfo); /* free up the temporary buffers */ g_free (rowbuf); g_free (buf); /* After finish_decompress, we can close the input file. * Here we postpone it until after no more JPEG errors are possible, * so as to simplify the setjmp error logic above. (Actually, I don't * think that jpeg_destroy can do an error exit, but why assume anything...) */ fclose (infile); /* At this point you may want to check to see whether any corrupt-data * warnings occurred (test whether jerr.num_warnings is nonzero). */ /* Tell the GIMP to display the image. */ gimp_drawable_flush (drawable); return image_ID; }
void ptRun(const gchar* Name, gint NrParameters, const GimpParam* Parameter, gint *nreturn_vals, GimpParam **return_vals) { printf("(%s,%d) '%s'\n",__FILE__,__LINE__,__PRETTY_FUNCTION__); printf("Name : '%s'\n",Name); printf("NrParameters : %d\n",NrParameters); if (!strcmp(Name,"photivoSendToGimp")) { printf("RunMode : %d\n",Parameter[0].data.d_int32); printf("FileName1 : '%s'\n",Parameter[1].data.d_string); printf("FileName2 : '%s'\n",Parameter[2].data.d_string); QFile GimpFile(Parameter[1].data.d_string); bool result = GimpFile.open(QIODevice::ReadOnly | QIODevice::Text); assert(result); QTextStream In(&GimpFile); QString ImageFileName = In.readLine(); QString ExifFileName = In.readLine(); QString ICCFileName = In.readLine(); // Read image FILE *InputFile = fopen(ImageFileName.toLocal8Bit().data(),"rb"); if (!InputFile) { ptLogError(1,ImageFileName.toLocal8Bit().data()); return; // ptError_FileOpen; } short Colors; unsigned short Width; unsigned short Height; unsigned short BitsPerColor; char Buffer[128]; // Extremely naive. Probably just enough for testcases. char *s = fgets(Buffer,127,InputFile); assert ( s ); int n = sscanf(Buffer,"P%hd",&Colors); assert ( 1 == n ); assert(Colors == 6 ); do { s = fgets(Buffer,127,InputFile); assert ( s ); } while (Buffer[0] == '#'); sscanf(Buffer,"%hd %hd",&Width,&Height); s = fgets(Buffer,127,InputFile); assert ( s ); sscanf(Buffer,"%hd",&BitsPerColor); assert(BitsPerColor == 0xffff); Colors = 3; unsigned short (* ImageForGimp)[3] = (unsigned short (*)[3]) CALLOC2(Width*Height,sizeof(*ImageForGimp)); ptMemoryError(ImageForGimp,__FILE__,__LINE__); unsigned short* PpmRow = (unsigned short *) CALLOC2(Width*Height,sizeof(*PpmRow)); ptMemoryError(PpmRow,__FILE__,__LINE__); for (unsigned short Row=0; Row<Height; Row++) { size_t RV = fread(PpmRow,Colors*2,Width,InputFile); if (RV != (size_t) Width) { printf("ReadPpm error. Expected %d bytes. Got %d\n",Width,(int)RV); exit(EXIT_FAILURE); } if (htons(0x55aa) != 0x55aa) { swab((char *)PpmRow,(char *)PpmRow,Width*Colors*2); } for (unsigned short Col=0; Col<Width; Col++) { for (short c=0;c<3;c++) { ImageForGimp[Row*Width+Col][c] = PpmRow[Col*Colors+c]; } } } FREE2(PpmRow); FCLOSE(InputFile); QFile ExifFile(ExifFileName); result = ExifFile.open(QIODevice::ReadOnly); assert(result); qint64 FileSize = ExifFile.size(); QDataStream ExifIn(&ExifFile); char* ExifBuffer = (char *) MALLOC2(FileSize); ptMemoryError(ExifBuffer,__FILE__,__LINE__); unsigned ExifBufferLength = ExifIn.readRawData(ExifBuffer,FileSize); ExifFile.close(); QFile ICCFile(ICCFileName); result = ICCFile.open(QIODevice::ReadOnly); assert(result); qint64 FileSize2 = ICCFile.size(); QDataStream ICCIn(&ICCFile); char* ICCBuffer = (char *) MALLOC2(FileSize2); ptMemoryError(ICCBuffer,__FILE__,__LINE__); unsigned ICCBufferLength = ICCIn.readRawData(ICCBuffer,FileSize2); ICCFile.close(); // And now copy to gimp. gint32 GimpImage = gimp_image_new(Width, Height, GIMP_RGB); assert (GimpImage != -1); gint32 GimpLayer = gimp_layer_new(GimpImage, "BG", Width, Height, GIMP_RGB_IMAGE, 100.0, GIMP_NORMAL_MODE); #if GIMP_MINOR_VERSION<=6 gimp_image_add_layer(GimpImage,GimpLayer,0); #else gimp_image_insert_layer(GimpImage,GimpLayer,0,0); #endif GimpDrawable* Drawable = gimp_drawable_get(GimpLayer); GimpPixelRgn PixelRegion; gimp_pixel_rgn_init(&PixelRegion, Drawable, 0, 0, Drawable->width, Drawable->height, true, false); unsigned short TileHeight = gimp_tile_height(); for (unsigned short Row=0; Row<Height; Row+=TileHeight) { unsigned short NrRows = MIN(Height-Row, (int)TileHeight); guint8* Buffer = g_new(guint8,TileHeight*Width*3); for (unsigned short i=0; i<NrRows; i++) { for (unsigned short j=0; j<Width; j++) { for (short c=0;c<3;c++) { Buffer[3*(i*Width+j)+c] = ImageForGimp[(Row+i)*Width+j][c]>>8; } } } gimp_pixel_rgn_set_rect(&PixelRegion, Buffer, 0, Row, Width, NrRows); g_free(Buffer); } gimp_drawable_flush(Drawable); gimp_drawable_detach(Drawable); FREE2(ImageForGimp); GimpParasite* GimpExifData = gimp_parasite_new("exif-data", GIMP_PARASITE_PERSISTENT, ExifBufferLength, ExifBuffer); gimp_image_parasite_attach(GimpImage,GimpExifData); gimp_parasite_free(GimpExifData); FREE2(ExifBuffer); GimpParasite* GimpICCData = gimp_parasite_new("icc-profile", GIMP_PARASITE_PERSISTENT, ICCBufferLength, ICCBuffer); gimp_image_parasite_attach(GimpImage,GimpICCData); gimp_parasite_free(GimpICCData); FREE2(ICCBuffer); static GimpParam Values[2]; *nreturn_vals = 2; *return_vals = Values; Values[0].type = GIMP_PDB_STATUS; Values[0].data.d_status = GIMP_PDB_SUCCESS; Values[1].type = GIMP_PDB_IMAGE; Values[1].data.d_image = GimpImage; QFile::remove(ImageFileName); QFile::remove(ExifFileName); QFile::remove(ICCFileName); QFile::remove(Parameter[1].data.d_string); }
static void rotate_drawable (GimpDrawable *drawable) { GimpPixelRgn srcPR, destPR; gint width, height; gint longside; gint bytes; gint row, col; gint offsetx, offsety; gboolean was_lock_alpha = FALSE; guchar *buffer; guchar *src_row, *dest_row; /* initialize */ row = 0; /* Get the size of the input drawable. */ width = drawable->width; height = drawable->height; bytes = drawable->bpp; if (gimp_layer_get_lock_alpha (drawable->drawable_id)) { was_lock_alpha = TRUE; gimp_layer_set_lock_alpha (drawable->drawable_id, FALSE); } if (rotvals.angle == 2) /* we're rotating by 180° */ { gimp_tile_cache_ntiles (2 * (width / gimp_tile_width() + 1)); gimp_pixel_rgn_init (&srcPR, drawable, 0, 0, width, height, FALSE, FALSE); gimp_pixel_rgn_init (&destPR, drawable, 0, 0, width, height, TRUE, TRUE); src_row = (guchar *) g_malloc (width * bytes); dest_row = (guchar *) g_malloc (width * bytes); for (row = 0; row < height; row++) { gimp_pixel_rgn_get_row (&srcPR, src_row, 0, row, width); for (col = 0; col < width; col++) { memcpy (dest_row + col * bytes, src_row + (width - 1 - col) * bytes, bytes); } gimp_pixel_rgn_set_row (&destPR, dest_row, 0, (height - row - 1), width); if ((row % 5) == 0) gimp_progress_update ((double) row / (double) height); } g_free (src_row); g_free (dest_row); gimp_drawable_flush (drawable); gimp_drawable_merge_shadow (drawable->drawable_id, TRUE); gimp_drawable_update (drawable->drawable_id, 0, 0, width, height); } else /* we're rotating by 90° or 270° */ { (width > height) ? (longside = width) : (longside = height); gimp_layer_resize (drawable->drawable_id, longside, longside, 0, 0); drawable = gimp_drawable_get (drawable->drawable_id); gimp_drawable_flush (drawable); gimp_tile_cache_ntiles ((longside / gimp_tile_width () + 1) + (longside / gimp_tile_height () + 1)); gimp_pixel_rgn_init (&srcPR, drawable, 0, 0, longside, longside, FALSE, FALSE); gimp_pixel_rgn_init (&destPR, drawable, 0, 0, longside, longside, TRUE, TRUE); buffer = g_malloc (longside * bytes); if (rotvals.angle == 1) /* we're rotating by 90° */ { for (row = 0; row < height; row++) { gimp_pixel_rgn_get_row (&srcPR, buffer, 0, row, width); gimp_pixel_rgn_set_col (&destPR, buffer, (height - row - 1), 0, width); if ((row % 5) == 0) gimp_progress_update ((double) row / (double) height); } } else /* we're rotating by 270° */ { for (col = 0; col < width; col++) { gimp_pixel_rgn_get_col (&srcPR, buffer, col, 0, height); gimp_pixel_rgn_set_row (&destPR, buffer, 0, (width - col - 1), height); if ((col % 5) == 0) gimp_progress_update ((double) col / (double) width); } } g_free (buffer); gimp_progress_update (1.0); gimp_drawable_flush (drawable); gimp_drawable_merge_shadow (drawable->drawable_id, TRUE); gimp_drawable_update (drawable->drawable_id, 0, 0, height, width); gimp_layer_resize (drawable->drawable_id, height, width, 0, 0); drawable = gimp_drawable_get (drawable->drawable_id); gimp_drawable_flush (drawable); gimp_drawable_update (drawable->drawable_id, 0, 0, height, width); } gimp_drawable_offsets (drawable->drawable_id, &offsetx, &offsety); rotate_compute_offsets (&offsetx, &offsety, gimp_image_width (image_ID), gimp_image_height (image_ID), width, height); gimp_layer_set_offsets (drawable->drawable_id, offsetx, offsety); if (was_lock_alpha) gimp_layer_set_lock_alpha (drawable->drawable_id, TRUE); return; }
static void run (const gchar *name, /* name of plugin */ gint nparams, /* number of in-paramters */ const GimpParam * param, /* in-parameters */ gint *nreturn_vals, /* number of out-parameters */ GimpParam ** return_vals) /* out-parameters */ { const gchar *l_env; gint32 image_id = -1; gboolean doProgress; gboolean doFlush; GapLastvalAnimatedCallInfo animCallInfo; /* Get the runmode from the in-parameters */ GimpRunMode run_mode = param[0].data.d_int32; /* status variable, use it to check for errors in invocation usualy only during non-interactive calling */ GimpPDBStatusType status = GIMP_PDB_SUCCESS; /* always return at least the status to the caller. */ static GimpParam values[2]; INIT_I18N(); l_env = g_getenv("GAP_DEBUG"); if(l_env != NULL) { if((*l_env != 'n') && (*l_env != 'N')) gap_debug = 1; } if(gap_debug) { printf("\n\nDEBUG: run %s\n", name); } if(strcmp(name, GAP_DETAIL_TRACKING_XML_ALIGNER_PLUG_IN_NAME) == 0) { runXmlAlign(name, nparams, param, nreturn_vals, return_vals); return; } if(strcmp(name, GAP_EXACT_ALIGNER_PLUG_IN_NAME) == 0) { runExactAlign(name, nparams, param, nreturn_vals, return_vals); return; } doProgress = FALSE; doFlush = FALSE; /* initialize the return of the status */ values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = status; values[1].type = GIMP_PDB_DRAWABLE; values[1].data.d_drawable = -1; *nreturn_vals = 2; *return_vals = values; /* init default values and Possibly retrieve data from a previous interactive run */ gap_detail_tracking_get_values(&fiVals); /* get image and drawable */ image_id = param[1].data.d_int32; /* how are we running today? */ switch (run_mode) { case GIMP_RUN_INTERACTIVE: /* detail tracking primary feature is intended to work without dialog interaction * when ivoked by menu or keyboard shortcut using PLUG_IN_NAME. * This plug in also registers with a 2nd variant PLUG_IN_NAME_CFG * where the user can configure the options (for one gimp session) */ if(strcmp(name, PLUG_IN_NAME_CFG) ==0) { gboolean dialogOk; if (fiVals.coordsRelToFrame1) { /* default offsets for handle at center */ fiVals.offsX = gimp_image_width(image_id) / 2.0; fiVals.offsY = gimp_image_height(image_id) / 2.0; } dialogOk = gap_detail_tracking_dialog(&fiVals); if( dialogOk != TRUE) { status = GIMP_PDB_CALLING_ERROR; } } doProgress = TRUE; doFlush = TRUE; break; case GIMP_RUN_NONINTERACTIVE: /* check to see if invoked with the correct number of parameters */ if (nparams == global_number_in_args) { fiVals.refShapeRadius = param[3].data.d_int32; fiVals.targetMoveRadius = param[4].data.d_int32; fiVals.loacteColodiffThreshold = param[5].data.d_float; fiVals.coordsRelToFrame1 = (param[6].data.d_int32 == 0) ? FALSE : TRUE; fiVals.offsX = param[7].data.d_int32; fiVals.offsY = param[8].data.d_int32; fiVals.offsRotate = param[9].data.d_float; fiVals.enableScaling = (param[10].data.d_int32 == 0) ? FALSE : TRUE; fiVals.bgLayerIsReference = (param[11].data.d_int32 == 0) ? FALSE : TRUE; fiVals.removeMidlayers = (param[12].data.d_int32 == 0) ? FALSE : TRUE; fiVals.moveLogFile[0] = '\0'; if(param[13].data.d_string != NULL) { g_snprintf(fiVals.moveLogFile, sizeof(fiVals.moveLogFile) -1, "%s", param[13].data.d_string); } } else { status = GIMP_PDB_CALLING_ERROR; } break; case GIMP_RUN_WITH_LAST_VALS: animCallInfo.animatedCallInProgress = FALSE; gimp_get_data(GAP_LASTVAL_KEY_ANIMATED_CALL_INFO, &animCallInfo); if(animCallInfo.animatedCallInProgress != TRUE) { doProgress = TRUE; doFlush = TRUE; } break; default: break; } if (status == GIMP_PDB_SUCCESS) { gulong cache_ntiles; gulong regionTileWidth; gulong regionTileHeight; gimp_image_undo_group_start (image_id); /* this plug in repeatedly accesses the same tiles in the same pixel regionsarea * therefore tile caching is essential for performance reason * therefore calculate optimal tile cache size (but limit to 300 tiles that should be enogh * in most practical use cases) */ regionTileWidth = 1 + (2 * (fiVals.targetMoveRadius + fiVals.refShapeRadius))/ gimp_tile_width() ; regionTileHeight = 1 + (2 * (fiVals.targetMoveRadius + fiVals.refShapeRadius))/ gimp_tile_height() ; /* processing may track 2 details in different regions of same size */ cache_ntiles = (regionTileWidth * regionTileHeight) * 2; gimp_tile_cache_ntiles (MAX(300, cache_ntiles)); /* Run the main function */ values[1].data.d_drawable = gap_track_detail_on_top_layers(image_id, doProgress, &fiVals); gimp_image_undo_group_end (image_id); if (values[1].data.d_drawable < 0) { status = GIMP_PDB_CALLING_ERROR; } /* If run mode is interactive, flush displays, else (script) don't * do it, as the screen updates would make the scripts slow */ if (doFlush) { gimp_displays_flush (); } } values[0].data.d_status = status; } /* end run */
gint32 load_thumbnail_image (const gchar *filename, gint *width, gint *height, GError **error) { gint32 volatile image_ID; ExifData *exif_data; GimpPixelRgn pixel_rgn; GimpDrawable *drawable; gint32 layer_ID; struct jpeg_decompress_struct cinfo; struct my_error_mgr jerr; guchar *buf; guchar **rowbuf; gint image_type; gint layer_type; gint tile_height; gint scanlines; gint i, start, end; gint orientation; my_src_ptr src; FILE *infile; image_ID = -1; exif_data = jpeg_exif_data_new_from_file (filename, NULL); if (! ((exif_data) && (exif_data->data) && (exif_data->size > 0))) return -1; orientation = jpeg_exif_get_orientation (exif_data); cinfo.err = jpeg_std_error (&jerr.pub); jerr.pub.error_exit = my_error_exit; jerr.pub.output_message = my_output_message; gimp_progress_init_printf (_("Opening thumbnail for '%s'"), gimp_filename_to_utf8 (filename)); /* Establish the setjmp return context for my_error_exit to use. */ if (setjmp (jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. We * need to clean up the JPEG object, close the input file, * and return. */ jpeg_destroy_decompress (&cinfo); if (image_ID != -1) gimp_image_delete (image_ID); if (exif_data) { exif_data_unref (exif_data); exif_data = NULL; } return -1; } /* Now we can initialize the JPEG decompression object. */ jpeg_create_decompress (&cinfo); /* Step 2: specify data source (eg, a file) */ if (cinfo.src == NULL) cinfo.src = (struct jpeg_source_mgr *)(*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT, sizeof (my_source_mgr)); src = (my_src_ptr) cinfo.src; src->pub.init_source = init_source; src->pub.fill_input_buffer = fill_input_buffer; src->pub.skip_input_data = skip_input_data; src->pub.resync_to_restart = jpeg_resync_to_restart; src->pub.term_source = term_source; src->pub.bytes_in_buffer = exif_data->size; src->pub.next_input_byte = exif_data->data; src->buffer = exif_data->data; src->size = exif_data->size; /* Step 3: read file parameters with jpeg_read_header() */ jpeg_read_header (&cinfo, TRUE); /* Step 4: set parameters for decompression */ /* In this example, we don't need to change any of the defaults set by * jpeg_read_header(), so we do nothing here. */ /* Step 5: Start decompressor */ jpeg_start_decompress (&cinfo); /* We may need to do some setup of our own at this point before * reading the data. After jpeg_start_decompress() we have the * correct scaled output image dimensions available, as well as * the output colormap if we asked for color quantization. In * this example, we need to make an output work buffer of the * right size. */ /* temporary buffer */ tile_height = gimp_tile_height (); buf = g_new (guchar, tile_height * cinfo.output_width * cinfo.output_components); rowbuf = g_new (guchar *, tile_height); for (i = 0; i < tile_height; i++) rowbuf[i] = buf + cinfo.output_width * cinfo.output_components * i; /* Create a new image of the proper size and associate the * filename with it. */ switch (cinfo.output_components) { case 1: image_type = GIMP_GRAY; layer_type = GIMP_GRAY_IMAGE; break; case 3: image_type = GIMP_RGB; layer_type = GIMP_RGB_IMAGE; break; case 4: if (cinfo.out_color_space == JCS_CMYK) { image_type = GIMP_RGB; layer_type = GIMP_RGB_IMAGE; break; } /*fallthrough*/ default: g_message ("Don't know how to load JPEG images " "with %d color channels, using colorspace %d (%d).", cinfo.output_components, cinfo.out_color_space, cinfo.jpeg_color_space); if (exif_data) { exif_data_unref (exif_data); exif_data = NULL; } return -1; break; } image_ID = gimp_image_new (cinfo.output_width, cinfo.output_height, image_type); gimp_image_undo_disable (image_ID); gimp_image_set_filename (image_ID, filename); jpeg_load_resolution (image_ID, &cinfo); layer_ID = gimp_layer_new (image_ID, _("Background"), cinfo.output_width, cinfo.output_height, layer_type, 100, GIMP_NORMAL_MODE); drawable_global = drawable = gimp_drawable_get (layer_ID); gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, drawable->width, drawable->height, TRUE, FALSE); /* Step 6: while (scan lines remain to be read) */ /* jpeg_read_scanlines(...); */ /* Here we use the library's state variable cinfo.output_scanline as the * loop counter, so that we don't have to keep track ourselves. */ while (cinfo.output_scanline < cinfo.output_height) { start = cinfo.output_scanline; end = cinfo.output_scanline + tile_height; end = MIN (end, cinfo.output_height); scanlines = end - start; for (i = 0; i < scanlines; i++) jpeg_read_scanlines (&cinfo, (JSAMPARRAY) &rowbuf[i], 1); if (cinfo.out_color_space == JCS_CMYK) jpeg_load_cmyk_to_rgb (buf, drawable->width * scanlines, NULL); gimp_pixel_rgn_set_rect (&pixel_rgn, buf, 0, start, drawable->width, scanlines); gimp_progress_update ((gdouble) cinfo.output_scanline / (gdouble) cinfo.output_height); } /* Step 7: Finish decompression */ jpeg_finish_decompress (&cinfo); /* We can ignore the return value since suspension is not possible * with the stdio data source. */ /* Step 8: Release JPEG decompression object */ /* This is an important step since it will release a good deal * of memory. */ jpeg_destroy_decompress (&cinfo); /* free up the temporary buffers */ g_free (rowbuf); g_free (buf); /* At this point you may want to check to see whether any * corrupt-data warnings occurred (test whether * jerr.num_warnings is nonzero). */ gimp_image_insert_layer (image_ID, layer_ID, -1, 0); /* NOW to get the dimensions of the actual image to return the * calling app */ cinfo.err = jpeg_std_error (&jerr.pub); jerr.pub.error_exit = my_error_exit; jerr.pub.output_message = my_output_message; if ((infile = g_fopen (filename, "rb")) == NULL) { g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), _("Could not open '%s' for reading: %s"), gimp_filename_to_utf8 (filename), g_strerror (errno)); if (exif_data) { exif_data_unref (exif_data); exif_data = NULL; } return -1; } /* Establish the setjmp return context for my_error_exit to use. */ if (setjmp (jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. We * need to clean up the JPEG object, close the input file, * and return. */ jpeg_destroy_decompress (&cinfo); if (image_ID != -1) gimp_image_delete (image_ID); if (exif_data) { exif_data_unref (exif_data); exif_data = NULL; } return -1; } /* Now we can initialize the JPEG decompression object. */ jpeg_create_decompress (&cinfo); /* Step 2: specify data source (eg, a file) */ jpeg_stdio_src (&cinfo, infile); /* Step 3: read file parameters with jpeg_read_header() */ jpeg_read_header (&cinfo, TRUE); jpeg_start_decompress (&cinfo); *width = cinfo.output_width; *height = cinfo.output_height; /* Step 4: Release JPEG decompression object */ /* This is an important step since it will release a good deal * of memory. */ jpeg_destroy_decompress (&cinfo); fclose (infile); if (exif_data) { exif_data_unref (exif_data); exif_data = NULL; } jpeg_exif_rotate (image_ID, orientation); return image_ID; }
static gint save_image (char *filename, gint32 image_ID, gint32 drawable_ID) { GPixelRgn pixel_rgn; TileDrawable *drawable; GDrawableType drawable_type; struct jpeg_compress_struct cinfo; struct my_error_mgr jerr; FILE *outfile; guchar *temp, *t; guchar *data; guchar *src, *s; char *name; int has_alpha; GimpExportReturnType export = GIMP_EXPORT_CANCEL; gint32 e_image_ID = -1; gint32 e_drawable_ID = -1; GPrecisionType precision; int rowstride, yend; int i, j; drawable = gimp_drawable_get (drawable_ID); drawable_type = gimp_drawable_type (drawable_ID); gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, drawable->width, drawable->height, FALSE, FALSE); name = malloc (strlen (filename) + 11); sprintf (name, "%s %s:",_("Saving"), filename); gimp_progress_init (name); free (name); /* Step 1: allocate and initialize JPEG compression object */ /* We have to set up the error handler first, in case the initialization * step fails. (Unlikely, but it could happen if you are out of memory.) * This routine fills in the contents of struct jerr, and returns jerr's * address which we place into the link field in cinfo. */ cinfo.err = jpeg_std_error (&jerr.pub); jerr.pub.error_exit = my_error_exit; outfile = NULL; /* Establish the setjmp return context for my_error_exit to use. */ if (setjmp (jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. * We need to clean up the JPEG object, close the input file, and return. */ jpeg_destroy_compress (&cinfo); if (outfile) fclose (outfile); if (drawable) gimp_drawable_detach (drawable); return FALSE; } /* Now we can initialize the JPEG compression object. */ jpeg_create_compress (&cinfo); /* Step 2: specify data destination (eg, a file) */ /* Note: steps 2 and 3 can be done in either order. */ /* Here we use the library-supplied code to send compressed data to a * stdio stream. You can also write your own code to do something else. * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that * requires it in order to write binary files. */ if ((outfile = fopen (filename, "wb")) == NULL) { fprintf (stderr, "can't open %s\n", filename); return FALSE; } jpeg_stdio_dest (&cinfo, outfile); /* Get the input image and a pointer to its data. */ cinfo.input_components = gimp_drawable_num_channels(drawable_ID); has_alpha = gimp_drawable_has_alpha(drawable_ID); if(has_alpha) --cinfo.input_components; /* Step 3: set parameters for compression */ /* First we supply a description of the input image. * Four fields of the cinfo struct must be filled in: */ /* image width and height, in pixels */ cinfo.image_width = drawable->width; cinfo.image_height = drawable->height; /* colorspace of input image */ cinfo.in_color_space = gimp_drawable_color(drawable_ID) ? JCS_RGB : JCS_GRAYSCALE; /* Now use the library's routine to set default compression parameters. * (You must set at least cinfo.in_color_space before calling this, * since the defaults depend on the source color space.) */ jpeg_set_defaults (&cinfo); /* Now you can set any non-default parameters you wish to. * Here we just illustrate the use of quality (quantization table) scaling: */ jpeg_set_quality (&cinfo, (int) (jsvals.quality * 100), TRUE /* limit to baseline-JPEG values */); cinfo.smoothing_factor = (int) (jsvals.smoothing * 100); cinfo.optimize_coding = jsvals.optimize; /* Step 4: Start compressor */ /* TRUE ensures that we will write a complete interchange-JPEG file. * Pass TRUE unless you are very sure of what you're doing. */ jpeg_start_compress (&cinfo, TRUE); // Step 4a: write icc profile { char *buffer = NULL; int size = 0; if (gimp_image_has_icc_profile (image_ID, ICC_IMAGE_PROFILE)) { buffer = gimp_image_get_icc_profile_by_mem (image_ID, &size, ICC_IMAGE_PROFILE); if (buffer) { write_icc_profile (&cinfo, buffer, size); printf ("%s:%d %s() embedded icc profile\n",__FILE__,__LINE__,__func__); } } } /* Step 5: while (scan lines remain to be written) */ /* jpeg_write_scanlines(...); */ /* Here we use the library's state variable cinfo.next_scanline as the * loop counter, so that we don't have to keep track ourselves. * To keep things simple, we pass one scanline per call; you can pass * more if you wish, though. */ /* JSAMPLEs per row in image_buffer */ rowstride = drawable->bpp * drawable->width; temp = (guchar *) malloc (cinfo.image_width * cinfo.input_components * 4); data = (guchar *) malloc (rowstride * gimp_tile_height ()); export = gimp_export_image (&image_ID, &drawable_ID, "Jpeg",
/* This function is nearly identical to the function 'tile_cache_insert' * in the file 'tile_cache.c' which is part of the main gimp application. */ static void gimp_tile_cache_insert (GimpTile *tile) { GList *list; if (!tile_hash_table) { tile_hash_table = g_hash_table_new (g_direct_hash, NULL); max_tile_size = gimp_tile_width () * gimp_tile_height () * 4; } /* First check and see if the tile is already * in the cache. In that case we will simply place * it at the end of the tile list to indicate that * it was the most recently accessed tile. */ list = g_hash_table_lookup (tile_hash_table, tile); if (list) { /* The tile was already in the cache. Place it at * the end of the tile list. */ /* If the tile is already at the end of the list, we are done */ if (list == tile_list_tail) return; /* At this point we have at least two elements in our list */ g_assert (tile_list_head != tile_list_tail); tile_list_head = g_list_remove_link (tile_list_head, list); tile_list_tail = g_list_last (g_list_concat (tile_list_tail, list)); } else { /* The tile was not in the cache. First check and see * if there is room in the cache. If not then we'll have * to make room first. Note: it might be the case that the * cache is smaller than the size of a tile in which case * it won't be possible to put it in the cache. */ if ((cur_cache_size + max_tile_size) > max_cache_size) { while (tile_list_head && (cur_cache_size + max_cache_size * FREE_QUANTUM) > max_cache_size) { gimp_tile_cache_flush ((GimpTile *) tile_list_head->data); } if ((cur_cache_size + max_tile_size) > max_cache_size) return; } /* Place the tile at the end of the tile list. */ tile_list_tail = g_list_append (tile_list_tail, tile); if (! tile_list_head) tile_list_head = tile_list_tail; tile_list_tail = g_list_last (tile_list_tail); /* Add the tiles list node to the tile hash table. */ g_hash_table_insert (tile_hash_table, tile, tile_list_tail); /* Note the increase in the number of bytes the cache * is referencing. */ cur_cache_size += max_tile_size; /* Reference the tile so that it won't be returned to * the main gimp application immediately. */ tile->ref_count++; } }
gboolean save_image (const gchar *filename, gint32 image_ID, gint32 drawable_ID, gint32 orig_image_ID, gboolean preview, GError **error) { GimpImageType drawable_type; GeglBuffer *buffer = NULL; const Babl *format; GimpParasite *parasite; static struct jpeg_compress_struct cinfo; static struct my_error_mgr jerr; JpegSubsampling subsampling; FILE * volatile outfile; guchar *data; guchar *src; gboolean has_alpha; gint rowstride, yend; drawable_type = gimp_drawable_type (drawable_ID); buffer = gimp_drawable_get_buffer (drawable_ID); if (! preview) gimp_progress_init_printf (_("Saving '%s'"), gimp_filename_to_utf8 (filename)); /* Step 1: allocate and initialize JPEG compression object */ /* We have to set up the error handler first, in case the initialization * step fails. (Unlikely, but it could happen if you are out of memory.) * This routine fills in the contents of struct jerr, and returns jerr's * address which we place into the link field in cinfo. */ cinfo.err = jpeg_std_error (&jerr.pub); jerr.pub.error_exit = my_error_exit; outfile = NULL; /* Establish the setjmp return context for my_error_exit to use. */ if (setjmp (jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. * We need to clean up the JPEG object, close the input file, and return. */ jpeg_destroy_compress (&cinfo); if (outfile) fclose (outfile); if (buffer) g_object_unref (buffer); return FALSE; } /* Now we can initialize the JPEG compression object. */ jpeg_create_compress (&cinfo); /* Step 2: specify data destination (eg, a file) */ /* Note: steps 2 and 3 can be done in either order. */ /* Here we use the library-supplied code to send compressed data to a * stdio stream. You can also write your own code to do something else. * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that * requires it in order to write binary files. */ if ((outfile = g_fopen (filename, "wb")) == NULL) { g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), _("Could not open '%s' for writing: %s"), gimp_filename_to_utf8 (filename), g_strerror (errno)); return FALSE; } jpeg_stdio_dest (&cinfo, outfile); /* Get the input image and a pointer to its data. */ switch (drawable_type) { case GIMP_RGB_IMAGE: /* # of color components per pixel */ cinfo.input_components = 3; has_alpha = FALSE; format = babl_format ("R'G'B' u8"); break; case GIMP_GRAY_IMAGE: /* # of color components per pixel */ cinfo.input_components = 1; has_alpha = FALSE; format = babl_format ("Y' u8"); break; case GIMP_RGBA_IMAGE: /* # of color components per pixel (minus the GIMP alpha channel) */ cinfo.input_components = 4 - 1; has_alpha = TRUE; format = babl_format ("R'G'B' u8"); break; case GIMP_GRAYA_IMAGE: /* # of color components per pixel (minus the GIMP alpha channel) */ cinfo.input_components = 2 - 1; has_alpha = TRUE; format = babl_format ("Y' u8"); break; case GIMP_INDEXED_IMAGE: default: return FALSE; } /* Step 3: set parameters for compression */ /* First we supply a description of the input image. * Four fields of the cinfo struct must be filled in: */ /* image width and height, in pixels */ cinfo.image_width = gegl_buffer_get_width (buffer); cinfo.image_height = gegl_buffer_get_height (buffer); /* colorspace of input image */ cinfo.in_color_space = (drawable_type == GIMP_RGB_IMAGE || drawable_type == GIMP_RGBA_IMAGE) ? JCS_RGB : JCS_GRAYSCALE; /* Now use the library's routine to set default compression parameters. * (You must set at least cinfo.in_color_space before calling this, * since the defaults depend on the source color space.) */ jpeg_set_defaults (&cinfo); jpeg_set_quality (&cinfo, (gint) (jsvals.quality + 0.5), jsvals.baseline); if (jsvals.use_orig_quality && num_quant_tables > 0) { guint **quant_tables; gint t; /* override tables generated by jpeg_set_quality() with custom tables */ quant_tables = jpeg_restore_original_tables (image_ID, num_quant_tables); if (quant_tables) { for (t = 0; t < num_quant_tables; t++) { jpeg_add_quant_table (&cinfo, t, quant_tables[t], 100, jsvals.baseline); g_free (quant_tables[t]); } g_free (quant_tables); } } if (arithc_supported) { cinfo.arith_code = jsvals.arithmetic_coding; if (!jsvals.arithmetic_coding) cinfo.optimize_coding = jsvals.optimize; } else cinfo.optimize_coding = jsvals.optimize; subsampling = (gimp_drawable_is_rgb (drawable_ID) ? jsvals.subsmp : JPEG_SUBSAMPLING_1x1_1x1_1x1); /* smoothing is not supported with nonstandard sampling ratios */ if (subsampling != JPEG_SUBSAMPLING_2x1_1x1_1x1 && subsampling != JPEG_SUBSAMPLING_1x2_1x1_1x1) { cinfo.smoothing_factor = (gint) (jsvals.smoothing * 100); } if (jsvals.progressive) { jpeg_simple_progression (&cinfo); } switch (subsampling) { case JPEG_SUBSAMPLING_2x2_1x1_1x1: default: cinfo.comp_info[0].h_samp_factor = 2; cinfo.comp_info[0].v_samp_factor = 2; cinfo.comp_info[1].h_samp_factor = 1; cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; cinfo.comp_info[2].v_samp_factor = 1; break; case JPEG_SUBSAMPLING_2x1_1x1_1x1: cinfo.comp_info[0].h_samp_factor = 2; cinfo.comp_info[0].v_samp_factor = 1; cinfo.comp_info[1].h_samp_factor = 1; cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; cinfo.comp_info[2].v_samp_factor = 1; break; case JPEG_SUBSAMPLING_1x1_1x1_1x1: cinfo.comp_info[0].h_samp_factor = 1; cinfo.comp_info[0].v_samp_factor = 1; cinfo.comp_info[1].h_samp_factor = 1; cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; cinfo.comp_info[2].v_samp_factor = 1; break; case JPEG_SUBSAMPLING_1x2_1x1_1x1: cinfo.comp_info[0].h_samp_factor = 1; cinfo.comp_info[0].v_samp_factor = 2; cinfo.comp_info[1].h_samp_factor = 1; cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; cinfo.comp_info[2].v_samp_factor = 1; break; } cinfo.restart_interval = 0; cinfo.restart_in_rows = jsvals.restart; switch (jsvals.dct) { case 0: default: cinfo.dct_method = JDCT_ISLOW; break; case 1: cinfo.dct_method = JDCT_IFAST; break; case 2: cinfo.dct_method = JDCT_FLOAT; break; } { gdouble xresolution; gdouble yresolution; gimp_image_get_resolution (orig_image_ID, &xresolution, &yresolution); if (xresolution > 1e-5 && yresolution > 1e-5) { gdouble factor; factor = gimp_unit_get_factor (gimp_image_get_unit (orig_image_ID)); if (factor == 2.54 /* cm */ || factor == 25.4 /* mm */) { cinfo.density_unit = 2; /* dots per cm */ xresolution /= 2.54; yresolution /= 2.54; } else { cinfo.density_unit = 1; /* dots per inch */ } cinfo.X_density = xresolution; cinfo.Y_density = yresolution; } } /* Step 4: Start compressor */ /* TRUE ensures that we will write a complete interchange-JPEG file. * Pass TRUE unless you are very sure of what you're doing. */ jpeg_start_compress (&cinfo, TRUE); /* Step 4.1: Write the comment out - pw */ if (image_comment && *image_comment) { #ifdef GIMP_UNSTABLE g_print ("jpeg-save: saving image comment (%d bytes)\n", (int) strlen (image_comment)); #endif jpeg_write_marker (&cinfo, JPEG_COM, (guchar *) image_comment, strlen (image_comment)); } /* Step 4.2: store the color profile if there is one */ parasite = gimp_image_get_parasite (orig_image_ID, "icc-profile"); if (parasite) { jpeg_icc_write_profile (&cinfo, gimp_parasite_data (parasite), gimp_parasite_data_size (parasite)); gimp_parasite_free (parasite); } /* Step 5: while (scan lines remain to be written) */ /* jpeg_write_scanlines(...); */ /* Here we use the library's state variable cinfo.next_scanline as the * loop counter, so that we don't have to keep track ourselves. * To keep things simple, we pass one scanline per call; you can pass * more if you wish, though. */ /* JSAMPLEs per row in image_buffer */ rowstride = cinfo.input_components * cinfo.image_width; data = g_new (guchar, rowstride * gimp_tile_height ()); /* fault if cinfo.next_scanline isn't initially a multiple of * gimp_tile_height */ src = NULL; /* * sg - if we preview, we want this to happen in the background -- do * not duplicate code in the future; for now, it's OK */ if (preview) { PreviewPersistent *pp = g_new (PreviewPersistent, 1); /* pass all the information we need */ pp->cinfo = cinfo; pp->tile_height = gimp_tile_height(); pp->data = data; pp->outfile = outfile; pp->has_alpha = has_alpha; pp->rowstride = rowstride; pp->data = data; pp->buffer = buffer; pp->format = format; pp->src = NULL; pp->file_name = filename; pp->abort_me = FALSE; g_warn_if_fail (prev_p == NULL); prev_p = pp; pp->cinfo.err = jpeg_std_error(&(pp->jerr)); pp->jerr.error_exit = background_error_exit; gtk_label_set_text (GTK_LABEL (preview_size), _("Calculating file size...")); pp->source_id = g_idle_add ((GSourceFunc) background_jpeg_save, pp); /* background_jpeg_save() will cleanup as needed */ return TRUE; } while (cinfo.next_scanline < cinfo.image_height) { if ((cinfo.next_scanline % gimp_tile_height ()) == 0) { yend = cinfo.next_scanline + gimp_tile_height (); yend = MIN (yend, cinfo.image_height); gegl_buffer_get (buffer, GEGL_RECTANGLE (0, cinfo.next_scanline, cinfo.image_width, (yend - cinfo.next_scanline)), 1.0, format, data, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); src = data; } jpeg_write_scanlines (&cinfo, (JSAMPARRAY) &src, 1); src += rowstride; if ((cinfo.next_scanline % 32) == 0) gimp_progress_update ((gdouble) cinfo.next_scanline / (gdouble) cinfo.image_height); } /* Step 6: Finish compression */ jpeg_finish_compress (&cinfo); /* After finish_compress, we can close the output file. */ fclose (outfile); /* Step 7: release JPEG compression object */ /* This is an important step since it will release a good deal of memory. */ jpeg_destroy_compress (&cinfo); /* free the temporary buffer */ g_free (data); /* And we're done! */ gimp_progress_update (1.0); g_object_unref (buffer); return TRUE; }
guchar *p_drawable_encode_jpeg(GDrawable *drawable, gint32 jpeg_interlaced, gint32 *JPEG_size, gint32 jpeg_quality, gint32 odd_even, gint32 use_YUV411, void *app0_buffer, gint32 app0_length) { GPixelRgn pixel_rgn; GDrawableType drawable_type; struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; guchar *temp, *t; guchar *data; guchar *src, *s; int has_alpha; int rowstride, yend; int i, j, y; guchar *JPEG_data; size_t *JPEG_buf_remain; size_t totalsize = 0; drawable_type = gimp_drawable_type (drawable->id); gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, drawable->width, drawable->height, FALSE, FALSE); /* Step 1: allocate and initialize JPEG compression object */ /* We have to set up the error handler first, in case the initialization * step fails. (Unlikely, but it could happen if you are out of memory.) * This routine fills in the contents of struct jerr, and returns jerr's * address which we place into the link field in cinfo. */ cinfo.err = jpeg_std_error(&jerr); /* Now we can initialize the JPEG compression object. */ jpeg_create_compress (&cinfo); /* Step 2: specify data destination (eg, a file) */ /* Note: steps 2 and 3 can be done in either order. */ /* We use our own jpeg destination mgr */ JPEG_data = (guchar *)g_malloc(OUTPUT_BUF_SIZE * sizeof(guchar)); /* Install my memory destination manager (instead of stdio_dest) */ jpeg_memio_dest (&cinfo, JPEG_data, &JPEG_buf_remain); if (gap_debug) fprintf(stderr, "GAP_AVI: encode_jpeg: Cleared the initilization !\n"); /* Get the input image and a pointer to its data. */ switch (drawable_type) { case RGB_IMAGE: case GRAY_IMAGE: /* # of color components per pixel */ cinfo.input_components = drawable->bpp; has_alpha = 0; break; case RGBA_IMAGE: case GRAYA_IMAGE: gimp_message ("jpeg: image contains a-channel info which will be lost"); /* # of color components per pixel (minus the GIMP alpha channel) */ cinfo.input_components = drawable->bpp - 1; has_alpha = 1; break; case INDEXED_IMAGE: gimp_message ("jpeg: cannot operate on indexed color images"); return FALSE; break; default: gimp_message ("jpeg: cannot operate on unknown image types"); return FALSE; break; } /* Step 3: set parameters for compression */ /* First we supply a description of the input image. * Four fields of the cinfo struct must be filled in: */ /* image width and height, in pixels */ cinfo.image_width = drawable->width; cinfo.image_height = drawable->height; /* colorspace of input image */ cinfo.in_color_space = (drawable_type == RGB_IMAGE || drawable_type == RGBA_IMAGE) ? JCS_RGB : JCS_GRAYSCALE; /* Now use the library's routine to set default compression parameters. * (You must set at least cinfo.in_color_space before calling this, * since the defaults depend on the source color space.) */ jpeg_set_defaults (&cinfo); jpeg_set_quality (&cinfo, (int) (jpeg_quality), TRUE); /* Smoothing is not possible for nonstandard sampling rates */ /* cinfo.smoothing_factor = (int) (50); */ /* I wonder if this is slower: */ /* cinfo.optimize_coding = 1; */ /* Are these the evil AVI destroyers ? */ cinfo.write_Adobe_marker = FALSE; cinfo.write_JFIF_header = FALSE; /* That's the only allowed encoding in a movtar */ if (!use_YUV411) { cinfo.comp_info[0].h_samp_factor = 2; cinfo.comp_info[0].v_samp_factor = 1; cinfo.comp_info[1].h_samp_factor = 1; cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; cinfo.comp_info[2].v_samp_factor = 1; } else if (gap_debug) fprintf(stderr, "Using YUV 4:1:1 encoding !"); cinfo.restart_interval = 0; cinfo.restart_in_rows = FALSE; /* could be _ISLOW or _FLOAT, too, but this is fastest */ cinfo.dct_method = JDCT_ISLOW; if (gap_debug) fprintf(stderr, "GAP_AVI: encode_jpeg: Cleared parameter setting !\n"); if (jpeg_interlaced) { cinfo.image_height = drawable->height/2; rowstride = drawable->bpp * drawable->width; temp = (guchar *) g_malloc (cinfo.image_width * cinfo.input_components); data = (guchar *) g_malloc (rowstride * gimp_tile_height ()); for (y = (odd_even) ? 1 : 0; (odd_even) ? (y >= 0) : (y <= 1); (odd_even) ? (y--) : (y++)) { if (gap_debug) fprintf(stderr, "GAP_AVI: encode_jpeg: interlaced picture, now coding %s lines\n", y ? "odd" : "even"); jpeg_start_compress (&cinfo, TRUE); /* Step 4.1: Write the app0 marker out */ if(app0_buffer) jpeg_write_marker(&cinfo, JPEG_APP0, app0_buffer, app0_length); while (cinfo.next_scanline < cinfo.image_height) { if (gap_debug) fprintf(stderr, "GAP_AVI: encode_jpeg: Line %d !", cinfo.next_scanline); gimp_pixel_rgn_get_rect (&pixel_rgn, data, 0, 2 * cinfo.next_scanline + y, cinfo.image_width, 1); /* Get rid of all the bad stuff (tm) (= get the right pixel order for JPEG encoding) */ t = temp; s = data; i = cinfo.image_width; while (i--) { for (j = 0; j < cinfo.input_components; j++) *t++ = *s++; if (has_alpha) /* ignore alpha channel */ s++; } src += 2*rowstride; jpeg_write_scanlines (&cinfo, (JSAMPARRAY) &temp, 1); } jpeg_finish_compress(&cinfo); totalsize += (OUTPUT_BUF_SIZE - *JPEG_buf_remain); //JPEG_data = (guchar *)realloc(JPEG_data, sizeof(guchar)* *JPEG_size); } /* fprintf(stderr, "2 fields written.\n"); */ } else { /* Step 4: Start compressor */ /* TRUE ensures that we will write a complete interchange-JPEG file. * Pass TRUE unless you are very sure of what you're doing. */ if (gap_debug) fprintf(stderr, "GAP_AVI: encode_jpeg: non-interlaced picture.\n"); jpeg_start_compress (&cinfo, TRUE); /* Step 4.1: Write the app0 marker out */ if(app0_buffer) jpeg_write_marker(&cinfo, JPEG_APP0, app0_buffer, app0_length); /* Step 5: while (scan lines remain to be written) */ /* jpeg_write_scanlines(...); */ /* Here we use the library's state variable cinfo.next_scanline as the * loop counter, so that we don't have to keep track ourselves. * To keep things simple, we pass one scanline per call; you can pass * more if you wish, though. */ /* JSAMPLEs per row in image_buffer */ rowstride = drawable->bpp * drawable->width; temp = (guchar *) g_malloc (cinfo.image_width * cinfo.input_components); data = (guchar *) g_malloc (rowstride * gimp_tile_height ()); /* fault if cinfo.next_scanline isn't initially a multiple of * gimp_tile_height */ src = NULL; while (cinfo.next_scanline < cinfo.image_height) { if (gap_debug) fprintf(stderr, "GAP_AVI: encode_jpeg: Line %d !", cinfo.next_scanline); if ((cinfo.next_scanline % gimp_tile_height ()) == 0) { yend = cinfo.next_scanline + gimp_tile_height (); yend = MIN (yend, cinfo.image_height); gimp_pixel_rgn_get_rect (&pixel_rgn, data, 0, cinfo.next_scanline, cinfo.image_width, (yend - cinfo.next_scanline)); src = data; } t = temp; s = src; i = cinfo.image_width; while (i--) { for (j = 0; j < cinfo.input_components; j++) *t++ = *s++; if (has_alpha) /* ignore alpha channel */ s++; } src += rowstride; jpeg_write_scanlines (&cinfo, (JSAMPARRAY) &temp, 1); } /* Step 6: Finish compression */ jpeg_finish_compress (&cinfo); totalsize += (OUTPUT_BUF_SIZE - *JPEG_buf_remain); // JPEG_data = (guchar *)realloc(JPEG_data, sizeof(guchar)* *JPEG_size); } /* Step 7: release JPEG compression object */ /* This is an important step since it will release a good deal of memory. */ jpeg_destroy_compress (&cinfo); /* free the temporary buffer */ g_free (temp); g_free (data); *JPEG_size = totalsize; return JPEG_data; }
gint32 load_image (const gchar *filename, GimpRunMode runmode, gboolean preview, gboolean *resolution_loaded, GError **error) { gint32 volatile image_ID; gint32 layer_ID; struct jpeg_decompress_struct cinfo; struct my_error_mgr jerr; jpeg_saved_marker_ptr marker; FILE *infile; guchar *buf; guchar **rowbuf; GimpImageBaseType image_type; GimpImageType layer_type; GeglBuffer *buffer = NULL; const Babl *format; gint tile_height; gint scanlines; gint i, start, end; cmsHTRANSFORM cmyk_transform = NULL; /* We set up the normal JPEG error routines. */ cinfo.err = jpeg_std_error (&jerr.pub); jerr.pub.error_exit = my_error_exit; if (!preview) { jerr.pub.output_message = my_output_message; gimp_progress_init_printf (_("Opening '%s'"), gimp_filename_to_utf8 (filename)); } if ((infile = g_fopen (filename, "rb")) == NULL) { g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), _("Could not open '%s' for reading: %s"), gimp_filename_to_utf8 (filename), g_strerror (errno)); return -1; } image_ID = -1; /* Establish the setjmp return context for my_error_exit to use. */ if (setjmp (jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. * We need to clean up the JPEG object, close the input file, and return. */ jpeg_destroy_decompress (&cinfo); if (infile) fclose (infile); if (image_ID != -1 && !preview) gimp_image_delete (image_ID); if (preview) destroy_preview (); if (buffer) g_object_unref (buffer); return -1; } /* Now we can initialize the JPEG decompression object. */ jpeg_create_decompress (&cinfo); /* Step 2: specify data source (eg, a file) */ jpeg_stdio_src (&cinfo, infile); if (! preview) { /* - step 2.1: tell the lib to save the comments */ jpeg_save_markers (&cinfo, JPEG_COM, 0xffff); /* - step 2.2: tell the lib to save APP1 data (Exif or XMP) */ jpeg_save_markers (&cinfo, JPEG_APP0 + 1, 0xffff); /* - step 2.3: tell the lib to save APP2 data (ICC profiles) */ jpeg_save_markers (&cinfo, JPEG_APP0 + 2, 0xffff); } /* Step 3: read file parameters with jpeg_read_header() */ jpeg_read_header (&cinfo, TRUE); /* We can ignore the return value from jpeg_read_header since * (a) suspension is not possible with the stdio data source, and * (b) we passed TRUE to reject a tables-only JPEG file as an error. * See libjpeg.doc for more info. */ /* Step 4: set parameters for decompression */ /* In this example, we don't need to change any of the defaults set by * jpeg_read_header(), so we do nothing here. */ /* Step 5: Start decompressor */ jpeg_start_decompress (&cinfo); /* We may need to do some setup of our own at this point before reading * the data. After jpeg_start_decompress() we have the correct scaled * output image dimensions available, as well as the output colormap * if we asked for color quantization. */ /* temporary buffer */ tile_height = gimp_tile_height (); buf = g_new (guchar, tile_height * cinfo.output_width * cinfo.output_components); rowbuf = g_new (guchar *, tile_height); for (i = 0; i < tile_height; i++) rowbuf[i] = buf + cinfo.output_width * cinfo.output_components * i; switch (cinfo.output_components) { case 1: image_type = GIMP_GRAY; layer_type = GIMP_GRAY_IMAGE; break; case 3: image_type = GIMP_RGB; layer_type = GIMP_RGB_IMAGE; break; case 4: if (cinfo.out_color_space == JCS_CMYK) { image_type = GIMP_RGB; layer_type = GIMP_RGB_IMAGE; break; } /*fallthrough*/ default: g_message ("Don't know how to load JPEG images " "with %d color channels, using colorspace %d (%d).", cinfo.output_components, cinfo.out_color_space, cinfo.jpeg_color_space); return -1; break; } if (preview) { image_ID = preview_image_ID; } else { image_ID = gimp_image_new_with_precision (cinfo.output_width, cinfo.output_height, image_type, GIMP_PRECISION_U8_GAMMA); gimp_image_undo_disable (image_ID); gimp_image_set_filename (image_ID, filename); } if (preview) { preview_layer_ID = gimp_layer_new (preview_image_ID, _("JPEG preview"), cinfo.output_width, cinfo.output_height, layer_type, 100, GIMP_NORMAL_MODE); layer_ID = preview_layer_ID; } else { layer_ID = gimp_layer_new (image_ID, _("Background"), cinfo.output_width, cinfo.output_height, layer_type, 100, GIMP_NORMAL_MODE); } if (! preview) { GString *comment_buffer = NULL; guint8 *profile = NULL; guint profile_size = 0; /* Step 5.0: save the original JPEG settings in a parasite */ jpeg_detect_original_settings (&cinfo, image_ID); /* Step 5.1: check for comments, or Exif metadata in APP1 markers */ for (marker = cinfo.marker_list; marker; marker = marker->next) { const gchar *data = (const gchar *) marker->data; gsize len = marker->data_length; if (marker->marker == JPEG_COM) { #ifdef GIMP_UNSTABLE g_print ("jpeg-load: found image comment (%d bytes)\n", marker->data_length); #endif if (! comment_buffer) { comment_buffer = g_string_new_len (data, len); } else { /* concatenate multiple comments, separate them with LF */ g_string_append_c (comment_buffer, '\n'); g_string_append_len (comment_buffer, data, len); } } else if ((marker->marker == JPEG_APP0 + 1) && (len > sizeof (JPEG_APP_HEADER_EXIF) + 8) && ! strcmp (JPEG_APP_HEADER_EXIF, data)) { #ifdef GIMP_UNSTABLE g_print ("jpeg-load: found Exif block (%d bytes)\n", (gint) (len - sizeof (JPEG_APP_HEADER_EXIF))); #endif } } if (jpeg_load_resolution (image_ID, &cinfo)) { if (resolution_loaded) *resolution_loaded = TRUE; } /* if we found any comments, then make a parasite for them */ if (comment_buffer && comment_buffer->len) { GimpParasite *parasite; jpeg_load_sanitize_comment (comment_buffer->str); parasite = gimp_parasite_new ("gimp-comment", GIMP_PARASITE_PERSISTENT, strlen (comment_buffer->str) + 1, comment_buffer->str); gimp_image_attach_parasite (image_ID, parasite); gimp_parasite_free (parasite); g_string_free (comment_buffer, TRUE); } /* Step 5.3: check for an embedded ICC profile in APP2 markers */ jpeg_icc_read_profile (&cinfo, &profile, &profile_size); if (cinfo.out_color_space == JCS_CMYK) { cmyk_transform = jpeg_load_cmyk_transform (profile, profile_size); } else if (profile) /* don't attach the profile if we are transforming */ { GimpParasite *parasite; parasite = gimp_parasite_new ("icc-profile", GIMP_PARASITE_PERSISTENT | GIMP_PARASITE_UNDOABLE, profile_size, profile); gimp_image_attach_parasite (image_ID, parasite); gimp_parasite_free (parasite); } g_free (profile); /* Do not attach the "jpeg-save-options" parasite to the image * because this conflicts with the global defaults (bug #75398). */ } /* Step 6: while (scan lines remain to be read) */ /* jpeg_read_scanlines(...); */ /* Here we use the library's state variable cinfo.output_scanline as the * loop counter, so that we don't have to keep track ourselves. */ buffer = gimp_drawable_get_buffer (layer_ID); format = babl_format (image_type == GIMP_RGB ? "R'G'B' u8" : "Y' u8"); while (cinfo.output_scanline < cinfo.output_height) { start = cinfo.output_scanline; end = cinfo.output_scanline + tile_height; end = MIN (end, cinfo.output_height); scanlines = end - start; for (i = 0; i < scanlines; i++) jpeg_read_scanlines (&cinfo, (JSAMPARRAY) &rowbuf[i], 1); if (cinfo.out_color_space == JCS_CMYK) jpeg_load_cmyk_to_rgb (buf, cinfo.output_width * scanlines, cmyk_transform); gegl_buffer_set (buffer, GEGL_RECTANGLE (0, start, cinfo.output_width, scanlines), 0, format, buf, GEGL_AUTO_ROWSTRIDE); if (! preview && (cinfo.output_scanline % 32) == 0) gimp_progress_update ((gdouble) cinfo.output_scanline / (gdouble) cinfo.output_height); } /* Step 7: Finish decompression */ jpeg_finish_decompress (&cinfo); /* We can ignore the return value since suspension is not possible * with the stdio data source. */ if (cmyk_transform) cmsDeleteTransform (cmyk_transform); /* Step 8: Release JPEG decompression object */ /* This is an important step since it will release a good deal of memory. */ jpeg_destroy_decompress (&cinfo); g_object_unref (buffer); /* free up the temporary buffers */ g_free (rowbuf); g_free (buf); /* After finish_decompress, we can close the input file. * Here we postpone it until after no more JPEG errors are possible, * so as to simplify the setjmp error logic above. (Actually, I don't * think that jpeg_destroy can do an error exit, but why assume anything...) */ fclose (infile); /* At this point you may want to check to see whether any corrupt-data * warnings occurred (test whether jerr.num_warnings is nonzero). */ /* Detach from the drawable and add it to the image. */ if (! preview) { gimp_progress_update (1.0); } gimp_image_insert_layer (image_ID, layer_ID, -1, 0); return image_ID; }
gint xjpg_load_layer_alpha (const char *filename, gint32 image_id, gint32 layer_id) { GimpPixelRgn l_pixel_rgn; GimpDrawable *l_drawable; struct jpeg_decompress_struct cinfo; struct my_error_mgr jerr; FILE *infile; guchar *l_buf; guchar *l_dstbuf; guchar **l_rowbuf; int l_tile_height; int l_scanlines; int l_idx, l_start, l_end; int l_alpha_offset; guchar *l_buf_ptr; guchar *l_dstbuf_ptr; /* We set up the normal JPEG error routines. */ cinfo.err = jpeg_std_error (&jerr.pub); jerr.pub.error_exit = my_error_exit; /* add alpha channel */ gimp_layer_add_alpha (layer_id); if ((infile = g_fopen (filename, "rb")) == NULL) { /* No alpha found, thats OK, use full opaque alpha channel * (there is no need not store alpha channels on full opaque channels) * (fixme: if filename exists but is not readable * we should return -1 to indicate an error */ return 0; /* OK */ } /* Establish the setjmp return context for my_error_exit to use. */ if (setjmp (jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. * We need to clean up the JPEG object, close the input file, and return. */ jpeg_destroy_decompress (&cinfo); if (infile) fclose (infile); g_printerr ("XJT: JPEG alpha load error\n"); return -1; } /* Now we can initialize the JPEG decompression object. */ jpeg_create_decompress (&cinfo); /* Step 2: specify data source (eg, a file) */ jpeg_stdio_src (&cinfo, infile); /* Step 3: read file parameters with jpeg_read_header() */ (void) jpeg_read_header (&cinfo, TRUE); /* We can ignore the return value from jpeg_read_header since * (a) suspension is not possible with the stdio data source, and * (b) we passed TRUE to reject a tables-only JPEG file as an error. * See libjpeg.doc for more info. */ /* Step 4: set parameters for decompression */ /* In this example, we don't need to change any of the defaults set by * jpeg_read_header(), so we do nothing here. */ /* Step 5: Start decompressor */ jpeg_start_decompress (&cinfo); /* We may need to do some setup of our own at this point before reading * the data. After jpeg_start_decompress() we have the correct scaled * output image dimensions available, as well as the output colormap * if we asked for color quantization. * In this example, we need to make an output work buffer of the right size. */ /* temporary buffer (for read in jpeg lines) */ l_tile_height = gimp_tile_height (); l_buf = g_new (guchar, l_tile_height * cinfo.output_width * cinfo.output_components); l_rowbuf = g_new (guchar*, l_tile_height); for (l_idx = 0; l_idx < l_tile_height; l_idx++) { l_rowbuf[l_idx] = l_buf + cinfo.output_width * cinfo.output_components * l_idx; } l_drawable = gimp_drawable_get (layer_id); if(l_drawable == NULL) { g_printerr ("XJT: gimp_drawable_get failed on layer id %d\n", (int)layer_id); fclose(infile); return -1; } /* Check if jpeg file can be used as alpha channel */ if((cinfo.output_components != 1) || (cinfo.output_width != l_drawable->width) || (cinfo.output_height != l_drawable->height)) { g_printerr ("XJT: cant load %s as alpha channel\n", filename); fclose (infile); return -1; } /* buffer to read in the layer and merge with the alpha from jpeg file */ l_dstbuf = g_new (guchar, l_tile_height * l_drawable->width * l_drawable->bpp); gimp_pixel_rgn_init (&l_pixel_rgn, l_drawable, 0, 0, l_drawable->width, l_drawable->height, TRUE, FALSE); l_alpha_offset = l_drawable->bpp -1; /* Step 6: while (scan lines remain to be read) */ /* jpeg_read_scanlines(...); */ /* Here we use the library's state variable cinfo.output_scanline as the * loop counter, so that we don't have to keep track ourselves. */ while (cinfo.output_scanline < cinfo.output_height) { l_start = cinfo.output_scanline; l_end = cinfo.output_scanline + l_tile_height; l_end = MIN (l_end, cinfo.output_height); l_scanlines = l_end - l_start; for (l_idx = 0; l_idx < l_scanlines; l_idx++) { jpeg_read_scanlines (&cinfo, (JSAMPARRAY) &l_rowbuf[l_idx], 1); } gimp_pixel_rgn_get_rect (&l_pixel_rgn, l_dstbuf, 0, l_start, l_drawable->width, l_scanlines); /* copy the loaded jpeg data (from buf) to the layers alpha channel data */ l_idx = l_tile_height * l_drawable->width; l_buf_ptr = l_buf; l_dstbuf_ptr = l_dstbuf; while(l_idx--) { l_dstbuf_ptr += l_alpha_offset; *l_dstbuf_ptr++ = *l_buf_ptr++; } gimp_pixel_rgn_set_rect (&l_pixel_rgn, l_dstbuf, 0, l_start, l_drawable->width, l_scanlines); gimp_progress_update ((double) cinfo.output_scanline / (double) cinfo.output_height); } gimp_progress_update (1.0); /* Step 7: Finish decompression */ jpeg_finish_decompress (&cinfo); /* We can ignore the return value since suspension is not possible * with the stdio data source. */ /* Step 8: Release JPEG decompression object */ /* This is an important step since it will release a good deal of memory. */ jpeg_destroy_decompress (&cinfo); /* free up the temporary buffers */ g_free (l_rowbuf); g_free (l_buf); g_free (l_dstbuf); /* After finish_decompress, we can close the input file. * Here we postpone it until after no more JPEG errors are possible, * so as to simplify the setjmp error logic above. (Actually, I don't * think that jpeg_destroy can do an error exit, but why assume anything...) */ fclose (infile); /* At this point you may want to check to see whether any corrupt-data * warnings occurred (test whether jerr.num_warnings is nonzero). */ return (0); /* OK */ } /* xjpg_load_layer_alpha */
static void run (const gchar *name, gint nparams, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals) { static GimpParam values[1]; GimpPDBStatusType status = GIMP_PDB_SUCCESS; GimpDrawable *drawable; GimpRunMode run_mode; // create working directory char* home = getenv("HOME"); char dir[PATH_MAX + 1]; strcpy ( dir, home ); strcat ( dir, "/.gimp-octave" ); mkdir( dir, S_IRWXU ); run_mode = (GimpRunMode)param[0].data.d_int32; *return_vals = values; *nreturn_vals = 1; values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = status; /* * Get drawable information... */ drawable = gimp_drawable_get (param[2].data.d_drawable); gimp_tile_cache_ntiles (2 * MAX (drawable->width / gimp_tile_width () + 1 , drawable->height / gimp_tile_height () + 1)); switch (run_mode) { case GIMP_RUN_INTERACTIVE: gimp_get_data (PLUG_IN_PROC, &octave_params); /* Reset default values show preview unmodified */ /* initialize pixel regions and buffer */ if (! octave_dialog (drawable)) return; break; case GIMP_RUN_NONINTERACTIVE: if (nparams != 6) { status = GIMP_PDB_CALLING_ERROR; } else { reset_default(); } break; case GIMP_RUN_WITH_LAST_VALS: gimp_get_data (PLUG_IN_PROC, &octave_params); break; default: break; } if (status == GIMP_PDB_SUCCESS) { drawable = gimp_drawable_get (param[2].data.d_drawable); /* here we go */ octave (drawable); gimp_displays_flush (); /* set data for next use of filter */ if (run_mode == GIMP_RUN_INTERACTIVE) gimp_set_data (PLUG_IN_PROC, &octave_params, sizeof (OctaveParams)); gimp_drawable_detach(drawable); values[0].data.d_status = status; } }