int gs_push_device_filter(gs_memory_t *mem, gs_state *pgs, gs_device_filter_t *df) { gs_device_filter_stack_t *dfs; gx_device *new_dev = NULL; int code; dfs = gs_alloc_struct(mem, gs_device_filter_stack_t, &st_gs_device_filter_stack, "gs_push_device_filter"); if (dfs == NULL) return_error(gs_error_VMerror); rc_increment(pgs->device); dfs->next_device = pgs->device; code = df->push(df, mem, pgs, &new_dev, pgs->device); if (code < 0) { gs_free_object(mem, dfs, "gs_push_device_filter"); return code; } dfs->next = pgs->dfilter_stack; pgs->dfilter_stack = dfs; dfs->df = df; rc_init(dfs, mem, 1); gs_setdevice_no_init(pgs, new_dev); rc_decrement_only(new_dev, "gs_push_device_filter"); return code; }
/* * DeviceN and NChannel color spaces can have an attributes dict. In the * attribute dict can be a Colorants dict which contains Separation color * spaces. If the Colorant dict is present, the PS logic will build each of * the Separation color spaces in a temp gstate and then call this procedure * to attach the Separation color space to the DeviceN color space. * The parameter to this procedure is a colorant name. The Separation * color space is in the current (temp) gstate. The DeviceN color space is * in the next gstate down in the gstate list (pgs->saved). */ int gs_attachattributecolorspace(gs_separation_name sep_name, gs_state * pgs) { gs_color_space * pdevncs; gs_device_n_attributes * patt; /* Verify that we have a DeviceN color space */ if (!pgs->saved) return_error(gs_error_rangecheck); pdevncs = pgs->saved->color_space; if (pdevncs->type != &gs_color_space_type_DeviceN) return_error(gs_error_rangecheck); /* Allocate an attribute list element for our linked list of attributes */ rc_alloc_struct_1(patt, gs_device_n_attributes, &st_device_n_attributes, pgs->memory, return_error(gs_error_VMerror), "gs_attachattributrescolorspace"); /* Point our attribute list entry to the attribute color space */ patt->colorant_name = sep_name; patt->cspace = pgs->color_space; rc_increment(pgs->color_space); /* Link our new attribute color space to the DeviceN color space */ patt->next = pdevncs->params.device_n.colorants; pdevncs->params.device_n.colorants = patt; return 0; }
/* * Allocate a path on the heap, and initialize it. If shared is NULL, * allocate a segments object; if shared is an existing path, share its * segments. */ gx_path * gx_path_alloc_shared(const gx_path * shared, gs_memory_t * mem, client_name_t cname) { gx_path *ppath = gs_alloc_struct(mem, gx_path, &st_path, cname); if (ppath == 0) return 0; ppath->procs = &default_path_procs; if (shared) { if (shared->segments == &shared->local_segments) { lprintf1("Attempt to share (local) segments of path 0x%lx!\n", (ulong) shared); gs_free_object(mem, ppath, cname); return 0; } *ppath = *shared; rc_increment(ppath->segments); } else { int code = path_alloc_segments(&ppath->segments, mem, cname); if (code < 0) { gs_free_object(mem, ppath, cname); return 0; } gx_path_init_contents(ppath); } ppath->memory = mem; ppath->allocation = path_allocated_on_heap; return ppath; }
/* Install a DeviceN color space. */ static int gx_install_DeviceN(gs_color_space * pcs, gs_state * pgs) { int code; code = check_DeviceN_component_names(pcs, pgs); if (code < 0) return code; /* See if we have an ICC profile that we can associate with this DeviceN color space */ if (pgs->icc_manager->device_n != NULL) { /* An nclr profile is in the manager. Grab one that matches. */ cmm_profile_t *profdata = gsicc_finddevicen(pcs, pgs->icc_manager); if (profdata != NULL) rc_increment(profdata); if (pcs->cmm_icc_profile_data != NULL) rc_decrement(pcs->cmm_icc_profile_data, "gx_install_DeviceN"); pcs->cmm_icc_profile_data = profdata; } /* {csrc} was pgs->color_space->params.device_n.use_alt_cspace */ ((gs_color_space *)pcs)->params.device_n.use_alt_cspace = using_alt_color_space(pgs); if (pcs->params.device_n.use_alt_cspace && pcs->cmm_icc_profile_data == NULL ) { /* No nclr ICC profile */ code = (pcs->base_space->type->install_cspace) (pcs->base_space, pgs); } else if (pcs->params.device_n.use_alt_cspace) { gs_color_space *nclr_pcs; /* Need to install the nclr cspace */ code = gs_cspace_build_ICC(&nclr_pcs, NULL, pgs->memory); nclr_pcs->cmm_icc_profile_data = pcs->cmm_icc_profile_data; rc_increment(pcs->cmm_icc_profile_data); rc_increment_cs(nclr_pcs); /* Suspicious - RJW */ rc_decrement_cs(pcs->base_space, "gx_install_DeviceN"); pcs->base_space = nclr_pcs; } /* * Give the device an opportunity to capture equivalent colors for any * spot colors which might be present in the color space. */ if (code >= 0) { if (dev_proc(pgs->device, update_spot_equivalent_colors)) code = dev_proc(pgs->device, update_spot_equivalent_colors) (pgs->device, pgs); } return code; }
RELOC_PTRS_END /* ------ Public procedures ------ */ /* * Create a new DeviceN colorspace. */ int gs_cspace_new_DeviceN( gs_color_space **ppcs, uint num_components, gs_color_space *palt_cspace, gs_memory_t *pmem ) { gs_color_space *pcs; gs_device_n_params *pcsdevn; gs_separation_name *pnames; int code; if (palt_cspace == 0 || !palt_cspace->type->can_be_alt_space) return_error(gs_error_rangecheck); pcs = gs_cspace_alloc(pmem, &gs_color_space_type_DeviceN); if (pcs == NULL) return_error(gs_error_VMerror); pcsdevn = &pcs->params.device_n; pcsdevn->names = NULL; pcsdevn->map = NULL; pcsdevn->colorants = NULL; /* Allocate space for color names list. */ code = alloc_device_n_map(&pcsdevn->map, pmem, "gs_cspace_build_DeviceN"); if (code < 0) { gs_free_object(pmem, pcs, "gs_cspace_new_DeviceN"); return code; } /* Allocate space for color names list. */ pnames = (gs_separation_name *) gs_alloc_byte_array(pmem, num_components, sizeof(gs_separation_name), ".gs_cspace_build_DeviceN(names)"); if (pnames == 0) { gs_free_object(pmem, pcsdevn->map, ".gs_cspace_build_DeviceN(map)"); gs_free_object(pmem, pcs, "gs_cspace_new_DeviceN"); return_error(gs_error_VMerror); } pcs->base_space = palt_cspace; rc_increment(palt_cspace); pcsdevn->names = pnames; pcsdevn->num_components = num_components; *ppcs = pcs; return 0; }
/* Install a ICC type color space and use the ICC LABLUT profile. */ int seticc_lab(i_ctx_t * i_ctx_p, float *white, float *black, float *range_buff) { int code; gs_color_space * pcs; gs_color_space * palt_cs; gs_imager_state * pis = (gs_imager_state *)igs; int i; static const char *const rfs = LAB_ICC; gs_param_string val, *pval; val.data = (const byte *)rfs; val.size = strlen(rfs); val.persistent = true; pval = &val; palt_cs = gs_currentcolorspace(igs); /* build the color space object */ code = gs_cspace_build_ICC(&pcs, NULL, gs_state_memory(igs)); if (code < 0) return gs_rethrow(code, "building color space object"); /* record the current space as the alternative color space */ /* pcs->base_space = palt_cs; rc_increment_cs(palt_cs); */ /* Get the lab profile. It may already be set in the icc manager. If not then lets populate it. */ if (pis->icc_manager->lab_profile == NULL ) { /* This can't happen as the profile should be initialized during the setting of the user params */ return gs_rethrow(code, "cannot find lab icc profile"); } /* Assign the LAB to LAB profile to this color space */ code = gsicc_set_gscs_profile(pcs, pis->icc_manager->lab_profile, gs_state_memory(igs)); rc_increment(pis->icc_manager->lab_profile); if (code < 0) return gs_rethrow(code, "installing the lab profile"); pcs->cmm_icc_profile_data->Range.ranges[0].rmin = 0.0; pcs->cmm_icc_profile_data->Range.ranges[0].rmax = 100.0; for (i = 1; i < 3; i++) { pcs->cmm_icc_profile_data->Range.ranges[i].rmin = range_buff[2 * (i-1)]; pcs->cmm_icc_profile_data->Range.ranges[i].rmax = range_buff[2 * (i-1) + 1]; } /* Set the color space. We are done. */ code = gs_setcolorspace(igs, pcs); return code; }
/* * Assign one path to another, adjusting reference counts appropriately. * Note that this requires that segments of the two paths (but not the path * objects themselves) were allocated with the same allocator. Note also * that since it does the equivalent of a gx_path_new(ppto), it may allocate * a new segments object for ppto. */ int gx_path_assign_preserve(gx_path * ppto, gx_path * ppfrom) { gx_path_segments *fromsegs = ppfrom->segments; gx_path_segments *tosegs = ppto->segments; gs_memory_t *mem = ppto->memory; gx_path_allocation_t allocation = ppto->allocation; if (fromsegs == &ppfrom->local_segments) { /* We can't use ppfrom's segments object. */ if (tosegs == &ppto->local_segments || gx_path_is_shared(ppto)) { /* We can't use ppto's segments either. Allocate a new one. */ int code = path_alloc_segments(&tosegs, ppto->memory, "gx_path_assign"); if (code < 0) return code; rc_decrement(ppto->segments, "gx_path_assign"); } else { /* Use ppto's segments object. */ rc_free_path_segments_local(tosegs->rc.memory, tosegs, "gx_path_assign"); } tosegs->contents = fromsegs->contents; ppfrom->segments = tosegs; rc_increment(tosegs); /* for reference from ppfrom */ } else { /* We can use ppfrom's segments object. */ rc_increment(fromsegs); rc_decrement(tosegs, "gx_path_assign"); } *ppto = *ppfrom; ppto->memory = mem; ppto->allocation = allocation; return 0; }
int gs_pop_device_filter(gs_memory_t *mem, gs_state *pgs) { gs_device_filter_stack_t *dfs_tos = pgs->dfilter_stack; gx_device *tos_device = pgs->device; gs_device_filter_t *df; int code; if (dfs_tos == NULL) return_error(gs_error_rangecheck); df = dfs_tos->df; pgs->dfilter_stack = dfs_tos->next; code = df->prepop(df, mem, pgs, tos_device); rc_increment(tos_device); gs_setdevice_no_init(pgs, dfs_tos->next_device); rc_decrement_only(dfs_tos->next_device, "gs_pop_device_filter"); dfs_tos->df = NULL; rc_decrement_only(dfs_tos, "gs_pop_device_filter"); code = df->postpop(df, mem, pgs, tos_device); rc_decrement_only(tos_device, "gs_pop_device_filter"); return code; }
/* * Initialize a stack-allocated path. This doesn't allocate anything, * but may still share the segments. */ int gx_path_init_local_shared(gx_path * ppath, const gx_path * shared, gs_memory_t * mem) { if (shared) { if (shared->segments == &shared->local_segments) { lprintf1("Attempt to share (local) segments of path 0x%lx!\n", (ulong) shared); return_error(gs_error_Fatal); } *ppath = *shared; rc_increment(ppath->segments); } else { rc_init_free(&ppath->local_segments, mem, 1, rc_free_path_segments_local); ppath->segments = &ppath->local_segments; gx_path_init_contents(ppath); } ppath->memory = mem; ppath->allocation = path_allocated_on_stack; ppath->procs = &default_path_procs; return 0; }
/* * Assign one path to another and free the first path at the same time. * (This may do less work than assign_preserve + free.) */ int gx_path_assign_free(gx_path * ppto, gx_path * ppfrom) { /* * Detect the special case where both paths have non-shared local * segments, since we can avoid allocating new segments in this case. */ if (ppto->segments == &ppto->local_segments && ppfrom->segments == &ppfrom->local_segments && !gx_path_is_shared(ppto) ) { #define fromsegs (&ppfrom->local_segments) #define tosegs (&ppto->local_segments) gs_memory_t *mem = ppto->memory; gx_path_allocation_t allocation = ppto->allocation; rc_free_path_segments_local(tosegs->rc.memory, tosegs, "gx_path_assign_free"); /* We record a bogus reference to fromsegs, which */ /* gx_path_free will undo. */ *ppto = *ppfrom; rc_increment(fromsegs); ppto->segments = tosegs; ppto->memory = mem; ppto->allocation = allocation; #undef fromsegs #undef tosegs } else { /* In all other cases, just do assign + free. */ int code = gx_path_assign_preserve(ppto, ppfrom); if (code < 0) return code; } gx_path_free(ppfrom, "gx_path_assign_free"); return 0; }
int gx_path_init_contained_shared(gx_path * ppath, const gx_path * shared, gs_memory_t * mem, client_name_t cname) { if (shared) { if (shared->segments == &shared->local_segments) { lprintf1("Attempt to share (local) segments of path 0x%lx!\n", (ulong) shared); return_error(gs_error_Fatal); } *ppath = *shared; rc_increment(ppath->segments); } else { int code = path_alloc_segments(&ppath->segments, mem, cname); if (code < 0) return code; gx_path_init_contents(ppath); } ppath->memory = mem; ppath->allocation = path_allocated_contained; ppath->procs = &default_path_procs; return 0; }
/* Set up the color space information for a bitmap image or pattern. */ int px_image_color_space(gs_image_t *pim, const px_bitmap_params_t *params, const gs_string *palette, const gs_state *pgs) { int depth = params->depth; gs_color_space *pbase_pcs = NULL; gs_color_space *pcs = NULL; bool cie_space = false; int code = 0; switch ( params->color_space ) { case eGray: pbase_pcs = gs_cspace_new_DeviceGray(pgs->memory); pbase_pcs->cmm_icc_profile_data = pgs->icc_manager->default_gray; pbase_pcs->type = &gs_color_space_type_ICC; rc_increment(pbase_pcs->cmm_icc_profile_data); break; case eRGB: pbase_pcs = gs_cspace_new_DeviceRGB(pgs->memory); pbase_pcs->cmm_icc_profile_data = pgs->icc_manager->default_rgb; pbase_pcs->type = &gs_color_space_type_ICC; rc_increment(pbase_pcs->cmm_icc_profile_data); break; case eSRGB: case eCRGB: if ( pl_cspace_init_SRGB(&pbase_pcs, pgs) < 0 ) /* should not happen */ return_error(errorInsufficientMemory); cie_space = true; pbase_pcs->cmm_icc_profile_data = pgs->icc_manager->default_rgb; pbase_pcs->type = &gs_color_space_type_ICC; rc_increment(pbase_pcs->cmm_icc_profile_data); break; default: return_error(errorIllegalAttributeValue); } if (pbase_pcs == NULL) return_error(errorInsufficientMemory); if ( params->indexed ) { pcs = gs_cspace_alloc(pgs->memory, &gs_color_space_type_Indexed); if ( pcs == NULL ) { /* free the base space also */ rc_decrement(pbase_pcs, "px_image_color_space"); return_error(errorInsufficientMemory); } pcs->base_space = pbase_pcs; pcs->params.indexed.hival = (1 << depth) - 1; pcs->params.indexed.lookup.table.size = palette->size; { uint n = palette->size; byte *p = gs_alloc_string(pgs->memory, n, "px_image_color_space(palette)"); if ( p == 0 ) { rc_decrement(pbase_pcs, "px_image_color_space"); return_error(errorInsufficientMemory); } memcpy(p, palette->data, n); pcs->params.indexed.lookup.table.data = p; } pcs->params.indexed.use_proc = 0; } else { pcs = pbase_pcs; } gs_image_t_init(pim, pcs); pim->ColorSpace = pcs; pim->BitsPerComponent = depth; if ( params->indexed ) pim->Decode[1] = (1 << depth) - 1; /* NB - this needs investigation */ if (cie_space && !px_is_currentcolor_pattern(pgs)) { code = pl_setSRGBcolor((gs_state *)pgs, 0.0, 0.0, 0.0); } return code; }
int seticc(i_ctx_t * i_ctx_p, int ncomps, ref *ICCdict, float *range_buff) { int code, k; gs_color_space * pcs; gs_color_space * palt_cs; ref * pstrmval; stream * s = 0L; cmm_profile_t *picc_profile; gs_imager_state * pis = (gs_imager_state *)igs; int i, expected = 0; ref * pnameval; static const char *const icc_std_profile_names[] = { GSICC_STANDARD_PROFILES }; static const char *const icc_std_profile_keys[] = { GSICC_STANDARD_PROFILES_KEYS }; palt_cs = gs_currentcolorspace(igs); /* verify the DataSource entry */ if (dict_find_string(ICCdict, "DataSource", &pstrmval) <= 0) return_error(e_undefined); check_read_file(i_ctx_p, s, pstrmval); /* build the color space object */ code = gs_cspace_build_ICC(&pcs, NULL, gs_state_memory(igs)); if (code < 0) return gs_rethrow(code, "building color space object"); /* For now, dump the profile into a buffer and obtain handle from the buffer when we need it. We may want to change this later. This depends to some degree on what the CMS is capable of doing. I don't want to get bogged down on stream I/O at this point. Note also, if we are going to be putting these into the clist we will want to have this buffer. */ /* Check if we have the /Name entry. This is used to associate with specs that have enumerated types to indicate sRGB sGray etc */ if (dict_find_string(ICCdict, "Name", &pnameval) > 0){ uint size = r_size(pnameval); char *str = (char *)gs_alloc_bytes(gs_state_memory(igs), size+1, "seticc"); memcpy(str, (const char *)pnameval->value.bytes, size); str[size] = 0; /* Compare this to the standard profile names */ picc_profile = NULL; for (k = 0; k < GSICC_NUMBER_STANDARD_PROFILES; k++) { if ( strcmp( str, icc_std_profile_keys[k] ) == 0 ) { picc_profile = gsicc_get_profile_handle_file(icc_std_profile_names[k], strlen(icc_std_profile_names[k]), gs_state_memory(igs)); break; } } } else { picc_profile = gsicc_profile_new(s, gs_state_memory(igs), NULL, 0); } if (picc_profile == NULL) { rc_decrement(picc_profile,"seticc"); rc_decrement(pcs,"seticc"); return -1; } code = gsicc_set_gscs_profile(pcs, picc_profile, gs_state_memory(igs)); if (code < 0) { rc_decrement(picc_profile,"seticc"); rc_decrement(pcs,"seticc"); return code; } picc_profile->num_comps = ncomps; /* We have to get the profile handle due to the fact that we need to know if it has a data space that is CIELAB */ picc_profile->profile_handle = gsicc_get_profile_handle_buffer(picc_profile->buffer, picc_profile->buffer_size, gs_state_memory(igs)); if (picc_profile->profile_handle == NULL) { /* Free up everything, the profile is not valid. We will end up going ahead and using a default based upon the number of components */ rc_decrement(picc_profile,"seticc"); rc_decrement(pcs,"seticc"); return -1; } picc_profile->data_cs = gscms_get_profile_data_space(picc_profile->profile_handle); switch( picc_profile->data_cs ) { case gsCIEXYZ: case gsCIELAB: case gsRGB: expected = 3; break; case gsGRAY: expected = 1; break; case gsCMYK: expected = 4; break; case gsNCHANNEL: case gsNAMED: /* Silence warnings */ case gsUNDEFINED: /* Silence warnings */ break; } if (!expected || ncomps != expected) { rc_decrement(picc_profile,"seticc"); rc_decrement(pcs,"seticc"); return_error(e_rangecheck); } /* Lets go ahead and get the hash code and check if we match one of the default spaces */ /* Later we may want to delay this, but for now lets go ahead and do it */ gsicc_init_hash_cs(picc_profile, pis); /* Set the range according to the data type that is associated with the ICC input color type. Occasionally, we will run into CIELAB to CIELAB profiles for spot colors in PDF documents. These spot colors are typically described as separation colors with tint transforms that go from a tint value to a linear mapping between the CIELAB white point and the CIELAB tint color. This results in a CIELAB value that we need to use to fill. We need to detect this to make sure we do the proper scaling of the data. For CIELAB images in PDF, the source is always normal 8 or 16 bit encoded data in the range from 0 to 255 or 0 to 65535. In that case, there should not be any encoding and decoding to CIELAB. The PDF content will not include an ICC profile but will set the color space to \Lab. In this case, we use our seticc_lab operation to install the LAB to LAB profile, but we detect that we did that through the use of the is_lab flag in the profile descriptor. When then avoid the CIELAB encode and decode */ if (picc_profile->data_cs == gsCIELAB) { /* If the input space to this profile is CIELAB, then we need to adjust the limits */ /* See ICC spec ICC.1:2004-10 Section 6.3.4.2 and 6.4. I don't believe we need to worry about CIEXYZ profiles or any of the other odds ones. Need to check that though at some point. */ picc_profile->Range.ranges[0].rmin = 0.0; picc_profile->Range.ranges[0].rmax = 100.0; picc_profile->Range.ranges[1].rmin = -128.0; picc_profile->Range.ranges[1].rmax = 127.0; picc_profile->Range.ranges[2].rmin = -128.0; picc_profile->Range.ranges[2].rmax = 127.0; picc_profile->islab = true; } else { for (i = 0; i < ncomps; i++) { picc_profile->Range.ranges[i].rmin = range_buff[2 * i]; picc_profile->Range.ranges[i].rmax = range_buff[2 * i + 1]; } } /* Now see if we are in an overide situation. We have to wait until now in case this is an LAB profile which we will not overide */ if (gs_currentoverrideicc(pis) && picc_profile->data_cs != gsCIELAB) { /* Free up the profile structure */ switch( picc_profile->data_cs ) { case gsRGB: pcs->cmm_icc_profile_data = pis->icc_manager->default_rgb; break; case gsGRAY: pcs->cmm_icc_profile_data = pis->icc_manager->default_gray; break; case gsCMYK: pcs->cmm_icc_profile_data = pis->icc_manager->default_cmyk; break; default: break; } /* Have one increment from the color space. Having these tied together is not really correct. Need to fix that. ToDo. MJV */ rc_adjust(picc_profile, -2, "seticc"); rc_increment(pcs->cmm_icc_profile_data); } /* Set the color space. We are done. No joint cache here... */ code = gs_setcolorspace(igs, pcs); /* The context has taken a reference to the colorspace. We no longer need * ours, so drop it. */ rc_decrement_only(pcs, "seticc"); /* In this case, we already have a ref count of 2 on the icc profile one for when it was created and one for when it was set. We really only want one here so adjust */ rc_decrement(picc_profile,"seticc"); /* Remove the ICC dict from the stack */ pop(1); return code; }
/* * <dict> .set_outputintent - * * Set and use the specified output intent. * */ static int zset_outputintent(i_ctx_t * i_ctx_p) { os_ptr op = osp; int code = 0; gx_device *dev = gs_currentdevice(igs); gs_imager_state *pis = (gs_imager_state *)igs; cmm_dev_profile_t *dev_profile; stream * s = 0L; ref * pnval; ref * pstrmval; int ncomps, dev_comps; cmm_profile_t *picc_profile; int expected = 0; gs_color_space_index index; gsicc_manager_t *icc_manager = pis->icc_manager; cmm_profile_t *source_profile = NULL; check_type(*op, t_dictionary); check_dict_read(*op); if_debug0m(gs_debug_flag_icc, imemory, "[icc] Using OutputIntent\n"); /* Get the device structure */ code = dev_proc(dev, get_profile)(dev, &dev_profile); if (dev_profile == NULL) { code = gsicc_init_device_profile_struct(dev, NULL, 0); code = dev_proc(dev, get_profile)(dev, &dev_profile); } if (dev_profile->oi_profile != NULL) { return 0; /* Allow only one setting of this object */ } code = dict_find_string(op, "N", &pnval); if (code < 0) return code; ncomps = pnval->value.intval; /* verify the DataSource entry. Creat profile from stream */ if (dict_find_string(op, "DataSource", &pstrmval) <= 0) return_error(e_undefined); check_read_file(i_ctx_p, s, pstrmval); picc_profile = gsicc_profile_new(s, gs_state_memory(igs), NULL, 0); picc_profile->num_comps = ncomps; picc_profile->profile_handle = gsicc_get_profile_handle_buffer(picc_profile->buffer, picc_profile->buffer_size, gs_state_memory(igs)); if (picc_profile->profile_handle == NULL) { rc_decrement(picc_profile,"zset_outputintent"); return -1; } picc_profile->data_cs = gscms_get_profile_data_space(picc_profile->profile_handle); switch (picc_profile->data_cs) { case gsCIEXYZ: case gsCIELAB: case gsRGB: expected = 3; source_profile = icc_manager->default_rgb; break; case gsGRAY: expected = 1; source_profile = icc_manager->default_gray; break; case gsCMYK: expected = 4; source_profile = icc_manager->default_cmyk; break; case gsNCHANNEL: expected = 0; break; case gsNAMED: case gsUNDEFINED: break; } if (expected && ncomps != expected) { rc_decrement(picc_profile,"zset_outputintent"); return_error(e_rangecheck); } gsicc_init_hash_cs(picc_profile, pis); /* All is well with the profile. Lets set the stuff that needs to be set */ dev_profile->oi_profile = picc_profile; picc_profile->name = (char *) gs_alloc_bytes(picc_profile->memory, MAX_DEFAULT_ICC_LENGTH, "zset_outputintent"); strncpy(picc_profile->name, OI_PROFILE, strlen(OI_PROFILE)); picc_profile->name[strlen(OI_PROFILE)] = 0; picc_profile->name_length = strlen(OI_PROFILE); /* Set the range of the profile */ gscms_set_icc_range(&picc_profile); /* If the output device has a different number of componenets, then we are going to set the output intent as the proofing profile, unless the proofing profile has already been set. If the device has the same number of components (and color model) then as the profile we will use this as the output profile, unless someone has explicitly set the output profile. Finally, we will use the output intent profile for the default profile of the proper Device profile in the icc manager, again, unless someone has explicitly set this default profile. */ dev_comps = dev_profile->device_profile[0]->num_comps; index = gsicc_get_default_type(dev_profile->device_profile[0]); if (ncomps == dev_comps && index < gs_color_space_index_DevicePixel) { /* The OI profile is the same type as the profile for the device and a "default" profile for the device was not externally set. So we go ahead and use the OI profile as the device profile. Care needs to be taken here to keep from screwing up any device parameters. We will use a keyword of OIProfile for the user/device parameter to indicate its usage. Also, note conflicts if one is setting object dependent color management */ rc_assign(dev_profile->device_profile[0], picc_profile, "zset_outputintent"); if_debug0m(gs_debug_flag_icc, imemory, "[icc] OutputIntent used for device profile\n"); } else { if (dev_profile->proof_profile == NULL) { /* This means that we should use the OI profile as the proofing profile. Note that if someone already has specified a proofing profile it is unclear what they are trying to do with the output intent. In this case, we will use it just for the source data below */ dev_profile->proof_profile = picc_profile; rc_increment(picc_profile); if_debug0m(gs_debug_flag_icc, imemory, "[icc] OutputIntent used for proof profile\n"); } } /* Now the source colors. See which source color space needs to use the output intent ICC profile */ index = gsicc_get_default_type(source_profile); if (index < gs_color_space_index_DevicePixel) { /* source_profile is currently the default. Set it to the OI profile */ switch (picc_profile->data_cs) { case gsGRAY: if_debug0m(gs_debug_flag_icc, imemory, "[icc] OutputIntent used source Gray\n"); rc_assign(icc_manager->default_gray, picc_profile, "zset_outputintent"); break; case gsRGB: if_debug0m(gs_debug_flag_icc, imemory, "[icc] OutputIntent used source RGB\n"); rc_assign(icc_manager->default_rgb, picc_profile, "zset_outputintent"); break; case gsCMYK: if_debug0m(gs_debug_flag_icc, imemory, "[icc] OutputIntent used source CMYK\n"); rc_assign(icc_manager->default_cmyk, picc_profile, "zset_outputintent"); break; default: break; } } /* Remove the output intent dict from the stack */ pop(1); return code; }
int gs_begin_transparency_mask(gs_state * pgs, const gs_transparency_mask_params_t * ptmp, const gs_rect * pbbox, bool mask_is_image) { gs_pdf14trans_params_t params = { 0 }; gs_pdf14trans_params_t params_color = { 0 }; const int l = sizeof(params.Background[0]) * ptmp->Background_components; int i, code; gs_color_space *blend_color_space; gsicc_manager_t *icc_manager = pgs->icc_manager; if (check_for_nontrans_pattern(pgs, (unsigned char *)"gs_pop_transparency_state")) { return(0); } params.pdf14_op = PDF14_BEGIN_TRANS_MASK; params.bbox = *pbbox; params.subtype = ptmp->subtype; params.Background_components = ptmp->Background_components; memcpy(params.Background, ptmp->Background, l); params.GrayBackground = ptmp->GrayBackground; params.transfer_function = ptmp->TransferFunction_data; params.function_is_identity = (ptmp->TransferFunction == mask_transfer_identity); params.mask_is_image = mask_is_image; params.replacing = ptmp->replacing; /* The eventual state that we want this smask to be moved to is always gray. This should provide us with a significant speed improvement over the old code. This does not keep us from having groups within the softmask getting blended in different color spaces, it just makes the final space be gray, which is what we will need to get to eventually anyway. In this way we avoid a final color conversion on a potentially large buffer. */ /* Also check if we have loaded in the transparency icc profiles. If not go ahead and take care of that now */ if (icc_manager->smask_profiles == NULL) { code = gsicc_initialize_iccsmask(icc_manager); if (code < 0) return(code); } /* A new soft mask group, make sure the profiles are set */ if_debug0m('v', pgs->memory, "[v]pushing soft mask color sending\n"); if (params.subtype != TRANSPARENCY_MASK_None) { params_color.pdf14_op = PDF14_PUSH_SMASK_COLOR; code = gs_state_update_pdf14trans(pgs, ¶ms_color); if (code < 0) return(code); blend_color_space = gs_cspace_new_DeviceGray(pgs->memory); blend_color_space->cmm_icc_profile_data = pgs->icc_manager->default_gray; rc_increment(blend_color_space->cmm_icc_profile_data); if_debug8m('v', pgs->memory, "[v](0x%lx)gs_begin_transparency_mask [%g %g %g %g]\n\ subtype = %d Background_components = %d %s\n", (ulong)pgs, pbbox->p.x, pbbox->p.y, pbbox->q.x, pbbox->q.y, (int)ptmp->subtype, ptmp->Background_components, (ptmp->TransferFunction == mask_transfer_identity ? "no TR" : "has TR")); /* Sample the transfer function */ for (i = 0; i < MASK_TRANSFER_FUNCTION_SIZE; i++) { float in = (float)(i * (1.0 / (MASK_TRANSFER_FUNCTION_SIZE - 1))); float out; ptmp->TransferFunction(in, &out, ptmp->TransferFunction_data); params.transfer_fn[i] = (byte)floor((double)(out * 255 + 0.5)); } /* Note: This function is called during the c-list writer side. */ if ( blend_color_space->cmm_icc_profile_data != NULL ) { /* Blending space is ICC based. If we are doing c-list rendering we will need to write this color space into the clist. */ params.group_color = ICC; params.group_color_numcomps = blend_color_space->cmm_icc_profile_data->num_comps; /* Get the ICC profile */ /* We don't reference count this - see comment in * pdf14_update_device_color_procs_pop_c() */ params.iccprofile = blend_color_space->cmm_icc_profile_data; params.icc_hash = blend_color_space->cmm_icc_profile_data->hashcode; } else { params.group_color = GRAY_SCALE; params.group_color_numcomps = 1; /* Need to check */ } /* Explicitly decrement the profile data since blend_color_space may not * be an ICC color space object. */ rc_decrement(blend_color_space->cmm_icc_profile_data, "gs_begin_transparency_mask"); rc_decrement_only_cs(blend_color_space, "gs_begin_transparency_mask"); }
int main(int argc, const char *argv[]) { char achar = '0'; gs_memory_t *mem; gs_state *pgs; const gx_device *const *list; gx_device *dev; gx_device_bbox *bbdev; int code; gp_init(); mem = gs_malloc_init(); gs_lib_init1(mem); if (argc < 2 || (achar = argv[1][0]) < '1' || achar > '0' + countof(tests) - 1 ) { lprintf1("Usage: gslib 1..%c\n", '0' + (char)countof(tests) - 1); gs_abort(mem); } gs_debug['@'] = 1; gs_debug['?'] = 1; /*gs_debug['B'] = 1; *//****** PATCH ******/ /*gs_debug['L'] = 1; *//****** PATCH ******/ /* * gs_iodev_init must be called after the rest of the inits, for * obscure reasons that really should be documented! */ gs_iodev_init(mem); /****** WRONG ******/ gs_lib_device_list(&list, NULL); gs_copydevice(&dev, list[0], mem); check_device_separable(dev); gx_device_fill_in_procs(dev); bbdev = gs_alloc_struct_immovable(mem, gx_device_bbox, &st_device_bbox, "bbox"); gx_device_bbox_init(bbdev, dev, mem); code = dev_proc(dev, get_profile)(dev, &bbdev->icc_struct); rc_increment(bbdev->icc_struct); /* Print out the device name just to test the gsparam.c API. */ { gs_c_param_list list; gs_param_string nstr; gs_c_param_list_write(&list, mem); code = gs_getdeviceparams(dev, (gs_param_list *) & list); if (code < 0) { lprintf1("getdeviceparams failed! code = %d\n", code); gs_abort(mem); } gs_c_param_list_read(&list); code = param_read_string((gs_param_list *) & list, "Name", &nstr); if (code < 0) { lprintf1("reading Name failed! code = %d\n", code); gs_abort(mem); } dputs("Device name = "); debug_print_string(nstr.data, nstr.size); dputs("\n"); gs_c_param_list_release(&list); } /* * If this is a device that takes an OutputFile, set the OutputFile * to "-" in the copy. */ { gs_c_param_list list; gs_param_string nstr; gs_c_param_list_write(&list, mem); param_string_from_string(nstr, "-"); code = param_write_string((gs_param_list *)&list, "OutputFile", &nstr); if (code < 0) { lprintf1("writing OutputFile failed! code = %d\n", code); gs_abort(mem); } gs_c_param_list_read(&list); code = gs_putdeviceparams(dev, (gs_param_list *)&list); gs_c_param_list_release(&list); if (code < 0 && code != gs_error_undefined) { lprintf1("putdeviceparams failed! code = %d\n", code); gs_abort(mem); } } dev = (gx_device *) bbdev; pgs = gs_state_alloc(mem); gs_setdevice_no_erase(pgs, dev); /* can't erase yet */ { gs_point dpi; gs_screen_halftone ht; gs_dtransform(pgs, 72.0, 72.0, &dpi); ht.frequency = min(fabs(dpi.x), fabs(dpi.y)) / 16.001; ht.angle = 0; ht.spot_function = odsf; gs_setscreen(pgs, &ht); } /* gsave and grestore (among other places) assume that */ /* there are at least 2 gstates on the graphics stack. */ /* Ensure that now. */ gs_gsave(pgs); gs_erasepage(pgs); code = (*tests[achar - '1']) (pgs, mem); gs_output_page(pgs, 1, 1); { gs_rect bbox; gx_device_bbox_bbox(bbdev, &bbox); dprintf4("Bounding box: [%g %g %g %g]\n", bbox.p.x, bbox.p.y, bbox.q.x, bbox.q.y); } if (code) dprintf1("**** Test returned code = %d.\n", code); dputs("Done. Press <enter> to exit."); fgetc(mem->gs_lib_ctx->fstdin); gs_lib_finit(0, 0, mem); return 0; #undef mem }
/* Exported for use by background printing. */ gx_device * setup_device_and_mem_for_thread(gs_memory_t *chunk_base_mem, gx_device *dev, bool bg_print, gsicc_link_cache_t **cachep) { int i, code; char fmode[4]; gs_memory_t *thread_mem; gx_device_clist *cldev = (gx_device_clist *)dev; gx_device_printer *pdev = (gx_device_printer *)dev; gx_device_clist_common *cdev = (gx_device_clist_common *)cldev; gx_device *ndev; gx_device_clist *ncldev; gx_device_clist_common *ncdev; gx_device_printer *npdev; gx_device *protodev; gs_c_param_list paramlist; gs_devn_params *pclist_devn_params; /* Every thread will have a 'chunk allocator' to reduce the interaction * with the 'base' allocator which has 'mutex' (locking) protection. * This improves performance of the threads. */ if ((code = gs_memory_chunk_wrap(&(thread_mem), chunk_base_mem )) < 0) { emprintf1(dev->memory, "chunk_wrap returned error code: %d\n", code); return NULL; } /* Find the prototype for this device (needed so we can copy from it) */ for (i=0; (protodev = (gx_device *)gs_getdevice(i)) != NULL; i++) if (strcmp(protodev->dname, dev->dname) == 0) break; /* Clone the device from the prototype device */ if (protodev == NULL || (code = gs_copydevice((gx_device **) &ndev, protodev, thread_mem)) < 0) { gs_memory_chunk_release(thread_mem); return NULL; } ncldev = (gx_device_clist *)ndev; ncdev = (gx_device_clist_common *)ndev; npdev = (gx_device_printer *)ndev; gx_device_fill_in_procs(ndev); ((gx_device_printer *)ncdev)->buffer_memory = ncdev->memory = ncdev->bandlist_memory = thread_mem; ndev->PageCount = dev->PageCount; /* copy to prevent mismatch error */ npdev->file = pdev->file; /* For background printing when doing N copies with %d */ strcpy((npdev->fname), (pdev->fname)); ndev->color_info = dev->color_info; /* copy before putdeviceparams */ ndev->pad = dev->pad; ndev->log2_align_mod = dev->log2_align_mod; ndev->is_planar = dev->is_planar; #if CMM_THREAD_SAFE ndev->icc_struct = dev->icc_struct; /* Set before put params */ rc_increment(ndev->icc_struct); #endif /* get the current device parameters to put into the cloned device */ gs_c_param_list_write(¶mlist, dev->memory); if ((code = gs_getdeviceparams(dev, (gs_param_list *)¶mlist)) < 0) { emprintf1(dev->memory, "Error getting device params, code=%d. Rendering threads not started.\n", code); goto out_cleanup; } gs_c_param_list_read(¶mlist); if ((code = gs_putdeviceparams(ndev, (gs_param_list *)¶mlist)) < 0) goto out_cleanup; gs_c_param_list_release(¶mlist); /* In the case of a separation device, we need to make sure we get the devn params copied over */ pclist_devn_params = dev_proc(dev, ret_devn_params)(dev); if (pclist_devn_params != NULL) { code = devn_copy_params(dev, ndev); if (code < 0) { #ifdef DEBUG /* suppress a warning on a release build */ gs_note_error(gs_error_VMerror); #endif goto out_cleanup; } } /* Also make sure supports_devn is set correctly */ ndev->icc_struct->supports_devn = cdev->icc_struct->supports_devn; ncdev->page_uses_transparency = cdev->page_uses_transparency; if_debug3m(gs_debug_flag_icc, cdev->memory, "[icc] MT clist device = 0x%p profile = 0x%p handle = 0x%p\n", ncdev, ncdev->icc_struct->device_profile[0], ncdev->icc_struct->device_profile[0]->profile_handle); /* If the device is_planar, then set the flag in the new_device and the procs */ if ((ncdev->is_planar = cdev->is_planar)) gdev_prn_set_procs_planar(ndev); /* gdev_prn_allocate_memory sets the clist for writing, creating new files. * We need to unlink those files and open the main thread's files, then * reset the clist state for reading/rendering */ if ((code = gdev_prn_allocate_memory(ndev, NULL, ndev->width, ndev->height)) < 0) goto out_cleanup; /* close and unlink the temp files just created */ ncdev->page_info.io_procs->fclose(ncdev->page_info.cfile, ncdev->page_info.cfname, true); ncdev->page_info.io_procs->fclose(ncdev->page_info.bfile, ncdev->page_info.bfname, true); ncdev->page_info.cfile = ncdev->page_info.bfile = NULL; /* open the main thread's files for this thread */ strcpy(fmode, "r"); /* read access for threads */ strncat(fmode, gp_fmode_binary_suffix, 1); if ((code=cdev->page_info.io_procs->fopen(cdev->page_info.cfname, fmode, &ncdev->page_info.cfile, thread_mem, thread_mem, true)) < 0 || (code=cdev->page_info.io_procs->fopen(cdev->page_info.bfname, fmode, &ncdev->page_info.bfile, thread_mem, thread_mem, false)) < 0) goto out_cleanup; strcpy((ncdev->page_info.cfname), (cdev->page_info.cfname)); strcpy((ncdev->page_info.bfname), (cdev->page_info.bfname)); clist_render_init(ncldev); /* Initialize clist device for reading */ ncdev->page_info.bfile_end_pos = cdev->page_info.bfile_end_pos; /* The threads are maintained until clist_finish_page. At which point, the threads are torn down, the master clist reader device is changed to writer, and the icc_table and the icc_cache_cl freed */ #if CMM_THREAD_SAFE /* safe to share the link cache */ ncdev->icc_cache_cl = cdev->icc_cache_cl; rc_increment(cdev->icc_cache_cl, "setup_render_thread"); #else /* each thread needs its own link cache */ if (cachep != NULL) { if (*cachep == NULL) { /* We don't have one cached that we can reuse, so make one. */ if ((*cachep = gsicc_cache_new(thread_mem->thread_safe_memory)) == NULL) goto out_cleanup; } rc_increment(*cachep); ncdev->icc_cache_cl = *cachep; } else if ((ncdev->icc_cache_cl = gsicc_cache_new(thread_mem)) == NULL) goto out_cleanup; #endif if (bg_print) { gx_device_clist_reader *ncrdev = (gx_device_clist_reader *)ncdev; if (cdev->icc_table != NULL) { /* This is a background printing thread, so it cannot share the icc_table */ /* since this probably was created with a GC'ed allocator and the bg_print */ /* thread can't deal with the relocation. Free the cdev->icc_table and get */ /* a new one from the clist. */ clist_free_icc_table(cdev->icc_table, cdev->memory); cdev->icc_table = NULL; if ((code = clist_read_icctable((gx_device_clist_reader *)ncdev)) < 0) goto out_cleanup; } /* Similarly for the color_usage_array, when the foreground device switches to */ /* writer mode, the foreground's array will be freed. */ if ((code = clist_read_color_usage_array(ncrdev)) < 0) goto out_cleanup; } else { /* Use the same profile table and color usage array in each thread */ ncdev->icc_table = cdev->icc_table; /* OK for multiple rendering threads */ ((gx_device_clist_reader *)ncdev)->color_usage_array = ((gx_device_clist_reader *)cdev)->color_usage_array; } /* Needed for case when the target has cielab profile and pdf14 device has a RGB profile stored in the profile list of the clist */ ncdev->trans_dev_icc_hash = cdev->trans_dev_icc_hash; /* success */ return ndev; out_cleanup: /* Close the file handles, but don't delete (unlink) the files */ if (ncdev->page_info.bfile != NULL) ncdev->page_info.io_procs->fclose(ncdev->page_info.bfile, ncdev->page_info.bfname, false); if (ncdev->page_info.cfile != NULL) ncdev->page_info.io_procs->fclose(ncdev->page_info.cfile, ncdev->page_info.cfname, false); ncdev->do_not_open_or_close_bandfiles = true; /* we already closed the files */ if (ndev != NULL) { gdev_prn_free_memory(ndev); gs_free_object(thread_mem, ndev, "setup_device_and_mem_for_thread"); } gs_memory_chunk_release(thread_mem); return NULL; }
/* Open the output file and stream. */ int gdev_vector_open_file_options(gx_device_vector * vdev, uint strmbuf_size, int open_options) { bool binary = !(open_options & VECTOR_OPEN_FILE_ASCII); int code = -1; /* (only for testing, never returned) */ cmm_dev_profile_t *icc_struct; /* Open the file as seekable or sequential, as requested. */ if (!(open_options & VECTOR_OPEN_FILE_SEQUENTIAL)) { /* Try to open as seekable. */ code = gx_device_open_output_file((gx_device *)vdev, vdev->fname, binary, true, &vdev->file); } if (code < 0 && (open_options & (VECTOR_OPEN_FILE_SEQUENTIAL | VECTOR_OPEN_FILE_SEQUENTIAL_OK))) { /* Try to open as sequential. */ code = gx_device_open_output_file((gx_device *)vdev, vdev->fname, binary, false, &vdev->file); } if ((code >= 0) && (dev_proc(vdev, get_profile) != NULL)) { code = dev_proc(vdev, get_profile)((gx_device *)vdev, &icc_struct); } if (code < 0) return code; if ((vdev->strmbuf = gs_alloc_bytes(vdev->v_memory, strmbuf_size, "vector_open(strmbuf)")) == 0 || (vdev->strm = s_alloc(vdev->v_memory, "vector_open(strm)")) == 0 || ((open_options & VECTOR_OPEN_FILE_BBOX) && (vdev->bbox_device = gs_alloc_struct_immovable(vdev->v_memory, gx_device_bbox, &st_device_bbox, "vector_open(bbox_device)")) == 0) ) { if (vdev->bbox_device) gs_free_object(vdev->v_memory, vdev->bbox_device, "vector_open(bbox_device)"); vdev->bbox_device = 0; if (vdev->strm) gs_free_object(vdev->v_memory, vdev->strm, "vector_open(strm)"); vdev->strm = 0; if (vdev->strmbuf) gs_free_object(vdev->v_memory, vdev->strmbuf, "vector_open(strmbuf)"); vdev->strmbuf = 0; gx_device_close_output_file((gx_device *)vdev, vdev->fname, vdev->file); vdev->file = 0; return_error(gs_error_VMerror); } vdev->strmbuf_size = strmbuf_size; swrite_file(vdev->strm, vdev->file, vdev->strmbuf, strmbuf_size); vdev->open_options = open_options; /* * We don't want finalization to close the file, but we do want it * to flush the stream buffer. */ vdev->strm->procs.close = vdev->strm->procs.flush; if (vdev->bbox_device) { gx_device_bbox_init(vdev->bbox_device, NULL, vdev->v_memory); rc_increment(vdev->bbox_device); vdev->bbox_device->icc_struct = icc_struct; rc_increment(vdev->bbox_device->icc_struct); gx_device_set_resolution((gx_device *) vdev->bbox_device, vdev->HWResolution[0], vdev->HWResolution[1]); /* Do the right thing about upright vs. inverted. */ /* (This is dangerous in general, since the procedure */ /* might reference non-standard elements.) */ set_dev_proc(vdev->bbox_device, get_initial_matrix, dev_proc(vdev, get_initial_matrix)); (*dev_proc(vdev->bbox_device, open_device)) ((gx_device *) vdev->bbox_device); } return 0; }
/* Common framework for building shadings. */ static int build_shading(i_ctx_t *i_ctx_p, build_shading_proc_t proc) { os_ptr op = osp; int code; float box[4]; gs_shading_params_t params; gs_shading_t *psh; ref *pvalue; check_type(*op, t_dictionary); params.ColorSpace = 0; params.cie_joint_caches = 0; params.Background = 0; /* Collect parameters common to all shading types. */ { gs_color_space *pcs = gs_currentcolorspace(igs); int num_comp = gs_color_space_num_components(pcs); if (num_comp < 0) { /* Pattern color space */ gs_errorinfo_put_pair_from_dict(i_ctx_p, op, "ColorSpace"); return_error(gs_error_typecheck); } params.ColorSpace = pcs; rc_increment_cs(pcs); if (dict_find_string(op, "Background", &pvalue) > 0) { gs_client_color *pcc = ialloc_struct(gs_client_color, &st_client_color, "build_shading"); if (pcc == 0) { code = gs_note_error(gs_error_VMerror); goto fail; } pcc->pattern = 0; params.Background = pcc; code = dict_floats_param(imemory, op, "Background", gs_color_space_num_components(pcs), pcc->paint.values, NULL); if (code < 0) { gs_errorinfo_put_pair_from_dict(i_ctx_p, op, "Background"); goto fail; } } } if (dict_find_string(op, "BBox", &pvalue) <= 0) params.have_BBox = false; else if ((code = dict_floats_param(imemory, op, "BBox", 4, box, NULL)) == 4) { /* Adobe Interpreters accept denormalised BBox - bug 688937 */ if (box[0] <= box[2]) { params.BBox.p.x = box[0]; params.BBox.q.x = box[2]; } else { params.BBox.p.x = box[2]; params.BBox.q.x = box[0]; } if (box[1] <= box[3]) { params.BBox.p.y = box[1]; params.BBox.q.y = box[3]; } else { params.BBox.p.y = box[3]; params.BBox.q.y = box[1]; } params.have_BBox = true; } else { gs_errorinfo_put_pair_from_dict(i_ctx_p, op, "BBox"); goto fail; } code = dict_bool_param(op, "AntiAlias", false, ¶ms.AntiAlias); if (code < 0) { gs_errorinfo_put_pair_from_dict(i_ctx_p, op, "AntiAlias"); goto fail; } /* Finish building the shading. */ code = (*proc)(i_ctx_p, op, ¶ms, &psh, imemory); if (code < 0) goto fail; if (gx_color_space_needs_cie_caches(psh->params.ColorSpace)) { rc_decrement(psh->params.cie_joint_caches, "build_shading"); psh->params.cie_joint_caches = gx_currentciecaches(igs); rc_increment(psh->params.cie_joint_caches); } make_istruct_new(op, 0, psh); return code; fail: gs_free_object(imemory, params.Background, "Background"); if (params.ColorSpace) { rc_decrement_only_cs(params.ColorSpace, "build_shading"); } return (code < 0 ? code : gs_note_error(gs_error_rangecheck)); }
/* shfill */ int gs_shfill(gs_gstate * pgs, const gs_shading_t * psh) { /* * shfill is equivalent to filling the current clipping path (or, if * clipping, its bounding box) with the shading, disregarding the * Background if any. In order to produce reasonable high-level output, * we must implement this by calling gs_fill_path. */ gs_pattern2_template_t pat; gs_matrix imat; gs_client_color cc; gs_color_space *pcs; gx_device_color devc; int code; /* Must install the shading color space to allow check_DeviceN_component_names initialize the color component map. */ /* Don't bother with saving the old color space, color, and cie_joint_caches, because .shfill is always called within gsave-grestore - see gs/lib . */ code = gs_setcolorspace(pgs, psh->params.ColorSpace); if (code < 0) return 0; if (psh->params.cie_joint_caches != NULL) { pgs->cie_joint_caches = psh->params.cie_joint_caches; rc_increment(pgs->cie_joint_caches); } gs_pattern2_init(&pat); pat.Shading = psh; gs_make_identity(&imat); code = gs_make_pattern(&cc, (gs_pattern_template_t *)&pat, &imat, pgs, pgs->memory); if (code < 0) return code; code = gs_pattern2_set_shfill(&cc); if (code < 0) return code; pcs = gs_cspace_alloc(pgs->memory, &gs_color_space_type_Pattern); if (pcs == NULL) return_error(gs_error_VMerror); pcs->params.pattern.has_base_space = false; code = pcs->type->remap_color(&cc, pcs, &devc, pgs, pgs->device, gs_color_select_texture); if (code >= 0) { gx_device *dev = pgs->device; bool need_path = !dev_proc(dev, dev_spec_op)(dev, gxdso_pattern_shfill_doesnt_need_path, NULL, 0); if (need_path) { gx_path cpath; gx_path_init_local(&cpath, pgs->memory); code = gx_cpath_to_path(pgs->clip_path, &cpath); if (code >= 0) code = gx_fill_path(&cpath, &devc, pgs, gx_rule_winding_number, pgs->fill_adjust.x, pgs->fill_adjust.y); gx_path_free(&cpath, "gs_shfill"); } else code = gx_fill_path(NULL, &devc, pgs, gx_rule_winding_number, pgs->fill_adjust.x, pgs->fill_adjust.y); } rc_decrement_cs(pcs, "gs_shfill"); gs_pattern_reference(&cc, -1); return code; }
int seticc(i_ctx_t * i_ctx_p, int ncomps, ref *ICCdict, float *range_buff) { os_ptr op = osp; int edepth = ref_stack_count(&e_stack); int code, reuse_op = 0; gs_color_space * pcs; gs_color_space * palt_cs; int i; gs_cie_icc * picc_info; ref * pstrmval; stream * s = 0L; palt_cs = gs_currentcolorspace(igs); /* verify the DataSource entry */ if (dict_find_string(ICCdict, "DataSource", &pstrmval) <= 0) return_error(e_undefined); check_read_file(s, pstrmval); /* build the color space object */ code = gs_cspace_build_CIEICC(&pcs, NULL, gs_state_memory(igs)); if (code < 0) return code; picc_info = pcs->params.icc.picc_info; picc_info->num_components = ncomps; picc_info->instrp = s; picc_info->file_id = (s->read_id | s->write_id); for (i = 0; i < ncomps; i++) { picc_info->Range.ranges[i].rmin = range_buff[2 * i]; picc_info->Range.ranges[i].rmax = range_buff[2 * i + 1]; } /* record the current space as the alternative color space */ pcs->base_space = palt_cs; rc_increment(palt_cs); code = gx_load_icc_profile(picc_info); if (code < 0) return code; /* If the input space to this profile is CIELAB, then we need to adjust the limits */ /* See ICC spec ICC.1:2004-10 Section 6.3.4.2 and 6.4 */ if(picc_info->plu->e_inSpace == icSigLabData) { picc_info->Range.ranges[0].rmin = 0.0; picc_info->Range.ranges[0].rmax = 100.0; picc_info->Range.ranges[1].rmin = -128.0; picc_info->Range.ranges[1].rmax = 127.0; picc_info->Range.ranges[2].rmin = -128.0; picc_info->Range.ranges[2].rmax = 127.0; } /* If the input space is icSigXYZData, then we should do the limits based upon the white point of the profile. */ if(picc_info->plu->e_inSpace == icSigXYZData) { for (i = 0; i < 3; i++) { picc_info->Range.ranges[i].rmin = 0; } picc_info->Range.ranges[0].rmax = picc_info->common.points.WhitePoint.u; picc_info->Range.ranges[1].rmax = picc_info->common.points.WhitePoint.v; picc_info->Range.ranges[2].rmax = picc_info->common.points.WhitePoint.w; } code = cie_cache_joint(i_ctx_p, &istate->colorrendering.procs, (gs_cie_common *)picc_info, igs); if (code < 0) return code; return cie_set_finish( i_ctx_p, pcs, &istate->colorspace.procs.cie, edepth, code ); }
/* Set up and start the render threads */ static int clist_setup_render_threads(gx_device *dev, int y) { gx_device_printer *pdev = (gx_device_printer *)dev; gx_device_clist *cldev = (gx_device_clist *)dev; gx_device_clist_common *cdev = (gx_device_clist_common *)cldev; gx_device_clist_reader *crdev = &cldev->reader; gs_memory_t *mem = cdev->bandlist_memory; gs_memory_t *chunk_base_mem = mem->thread_safe_memory; gs_memory_status_t mem_status; gx_device *protodev; gs_c_param_list paramlist; int i, code, band; int band_count = cdev->nbands; char fmode[4]; gs_devn_params *pclist_devn_params; crdev->num_render_threads = pdev->num_render_threads_requested; if(gs_debug[':'] != 0) dprintf1("%% %d rendering threads requested.\n", pdev->num_render_threads_requested); if (crdev->num_render_threads > band_count) crdev->num_render_threads = band_count; /* don't bother starting more threads than bands */ /* Allocate and initialize an array of thread control structures */ crdev->render_threads = (clist_render_thread_control_t *) gs_alloc_byte_array(mem, crdev->num_render_threads, sizeof(clist_render_thread_control_t), "clist_setup_render_threads" ); /* fallback to non-threaded if allocation fails */ if (crdev->render_threads == NULL) { emprintf(mem, " VMerror prevented threads from starting.\n"); return_error(gs_error_VMerror); } memset(crdev->render_threads, 0, crdev->num_render_threads * sizeof(clist_render_thread_control_t)); crdev->main_thread_data = cdev->data; /* save data area */ /* Based on the line number requested, decide the order of band rendering */ /* Almost all devices go in increasing line order (except the bmp* devices ) */ crdev->thread_lookahead_direction = (y < (cdev->height - 1)) ? 1 : -1; band = y / crdev->page_info.band_params.BandHeight; /* Close the files so we can open them in multiple threads */ if ((code = cdev->page_info.io_procs->fclose(cdev->page_cfile, cdev->page_cfname, false)) < 0 || (code = cdev->page_info.io_procs->fclose(cdev->page_bfile, cdev->page_bfname, false)) < 0) { gs_free_object(mem, crdev->render_threads, "clist_setup_render_threads"); crdev->render_threads = NULL; emprintf(mem, "Closing clist files prevented threads from starting.\n"); return_error(gs_error_unknownerror); /* shouldn't happen */ } cdev->page_cfile = cdev->page_bfile = NULL; strcpy(fmode, "r"); /* read access for threads */ strncat(fmode, gp_fmode_binary_suffix, 1); /* Find the prototype for this device (needed so we can copy from it) */ for (i=0; (protodev = (gx_device *)gs_getdevice(i)) != NULL; i++) if (strcmp(protodev->dname, dev->dname) == 0) break; if (protodev == NULL) { emprintf(mem, "Could not find prototype device. Rendering threads not started.\n"); return gs_error_rangecheck; } gs_c_param_list_write(¶mlist, mem); if ((code = gs_getdeviceparams(dev, (gs_param_list *)¶mlist)) < 0) { emprintf1(mem, "Error getting device params, code=%d. Rendering threads not started.\n", code); return code; } /* If the 'mem' is not thread safe, we need to wrap it in a locking memory */ gs_memory_status(chunk_base_mem, &mem_status); if (mem_status.is_thread_safe == false) { return_error(gs_error_VMerror); } /* Loop creating the devices and semaphores for each thread, then start them */ for (i=0; (i < crdev->num_render_threads) && (band >= 0) && (band < band_count); i++, band += crdev->thread_lookahead_direction) { gx_device *ndev; gx_device_clist *ncldev; gx_device_clist_common *ncdev; clist_render_thread_control_t *thread = &(crdev->render_threads[i]); /* Every thread will have a 'chunk allocator' to reduce the interaction * with the 'base' allocator which has 'mutex' (locking) protection. * This improves performance of the threads. */ if ((code = gs_memory_chunk_wrap(&(thread->memory), chunk_base_mem )) < 0) { emprintf1(mem, "chunk_wrap returned error code: %d\n", code); break; } thread->band = -1; /* a value that won't match any valid band */ if ((code = gs_copydevice((gx_device **) &ndev, protodev, thread->memory)) < 0) { code = 0; /* even though we failed, no cleanup needed */ break; } ncldev = (gx_device_clist *)ndev; ncdev = (gx_device_clist_common *)ndev; gx_device_fill_in_procs(ndev); ((gx_device_printer *)ncdev)->buffer_memory = ncdev->memory = ncdev->bandlist_memory = thread->memory; gs_c_param_list_read(¶mlist); ndev->PageCount = dev->PageCount; /* copy to prevent mismatch error */ #if CMM_THREAD_SAFE ndev->icc_struct = dev->icc_struct; /* Set before put params */ rc_increment(ndev->icc_struct); #endif if ((code = gs_putdeviceparams(ndev, (gs_param_list *)¶mlist)) < 0) break; /* In the case of a separation device, we need to make sure we get the devn params copied over */ pclist_devn_params = dev_proc(dev, ret_devn_params)(dev); if (pclist_devn_params != NULL) { code = devn_copy_params(dev, (gx_device*) ncdev); if (code < 0) return_error(gs_error_VMerror); } ncdev->page_uses_transparency = cdev->page_uses_transparency; if_debug3('{',"[{]MT clist device = 0x%x profile = 0x%x handle = 0x%x\n", ncdev, ncdev->icc_struct->device_profile[0], ncdev->icc_struct->device_profile[0]->profile_handle); /* gdev_prn_allocate_memory sets the clist for writing, creating new files. * We need to unlink those files and open the main thread's files, then * reset the clist state for reading/rendering */ if ((code = gdev_prn_allocate_memory(ndev, NULL, ndev->width, ndev->height)) < 0) break; /* Needed for case when the target has cielab profile and pdf14 device has a RGB profile stored in the profile list of the clist */ ncdev->trans_dev_icc_hash = cdev->trans_dev_icc_hash; thread->cdev = ndev; /* close and unlink the temp files just created */ cdev->page_info.io_procs->fclose(ncdev->page_cfile, ncdev->page_cfname, true); cdev->page_info.io_procs->fclose(ncdev->page_bfile, ncdev->page_bfname, true); /* open the main thread's files for this thread */ if ((code=cdev->page_info.io_procs->fopen(cdev->page_cfname, fmode, &ncdev->page_cfile, thread->memory, thread->memory, true)) < 0 || (code=cdev->page_info.io_procs->fopen(cdev->page_bfname, fmode, &ncdev->page_bfile, thread->memory, thread->memory, false)) < 0) break; clist_render_init(ncldev); /* Initialize clist device for reading */ ncdev->page_bfile_end_pos = cdev->page_bfile_end_pos; /* Use the same link cache in each thread and the same profile table. The threads are maintained until clist_finish_page. At which point, the threads are torn down and the master clist reader device is destroyed along with the icc_table and the icc_cache_cl */ #if CMM_THREAD_SAFE ncdev->icc_cache_cl = cdev->icc_cache_cl; #else ncdev->icc_cache_cl = gsicc_cache_new(crdev->memory); #endif ncdev->icc_table = cdev->icc_table; /* create the buf device for this thread, and allocate the semaphores */ if ((code = gdev_create_buf_device(cdev->buf_procs.create_buf_device, &(thread->bdev), cdev->target, band*crdev->page_band_height, NULL, thread->memory, clist_get_band_complexity(dev,y)) < 0)) break; if ((thread->sema_this = gx_semaphore_alloc(thread->memory)) == NULL || (thread->sema_group = gx_semaphore_alloc(thread->memory)) == NULL) { code = gs_error_VMerror; break; } /* Start thread 'i' to do band */ if ((code = clist_start_render_thread(dev, i, band)) < 0) break; } gs_c_param_list_release(¶mlist); /* If the code < 0, the last thread creation failed -- clean it up */ if (code < 0) { /* the following relies on 'free' ignoring NULL pointers */ gx_semaphore_free(crdev->render_threads[i].sema_group); gx_semaphore_free(crdev->render_threads[i].sema_this); if (crdev->render_threads[i].bdev != NULL) cdev->buf_procs.destroy_buf_device(crdev->render_threads[i].bdev); if (crdev->render_threads[i].cdev != NULL) { gx_device_clist_common *thread_cdev = (gx_device_clist_common *)crdev->render_threads[i].cdev; /* Close the file handles, but don't delete (unlink) the files */ thread_cdev->page_info.io_procs->fclose(thread_cdev->page_bfile, thread_cdev->page_bfname, false); thread_cdev->page_info.io_procs->fclose(thread_cdev->page_cfile, thread_cdev->page_cfname, false); thread_cdev->do_not_open_or_close_bandfiles = true; /* we already closed the files */ gdev_prn_free_memory((gx_device *)thread_cdev); gs_free_object(crdev->render_threads[i].memory, thread_cdev, "clist_setup_render_threads"); } if (crdev->render_threads[i].memory != NULL) gs_memory_chunk_release(crdev->render_threads[i].memory); } /* If we weren't able to create at least one thread, punt */ /* Although a single thread isn't any more efficient, the */ /* machinery still works, so that's OK. */ if (i == 0) { if (crdev->render_threads[0].memory != NULL) { gs_memory_chunk_release(crdev->render_threads[0].memory); /* free up the locking wrapper if we allocated one */ if (chunk_base_mem != mem) { gs_memory_locked_release((gs_memory_locked_t *)chunk_base_mem); gs_free_object(mem, chunk_base_mem, "clist_setup_render_threads(locked allocator)"); } } gs_free_object(mem, crdev->render_threads, "clist_setup_render_threads"); crdev->render_threads = NULL; /* restore the file pointers */ if (cdev->page_cfile == NULL) { char fmode[4]; strcpy(fmode, "a+"); /* file already exists and we want to re-use it */ strncat(fmode, gp_fmode_binary_suffix, 1); cdev->page_info.io_procs->fopen(cdev->page_cfname, fmode, &cdev->page_cfile, mem, cdev->bandlist_memory, true); cdev->page_info.io_procs->fseek(cdev->page_cfile, 0, SEEK_SET, cdev->page_cfname); cdev->page_info.io_procs->fopen(cdev->page_bfname, fmode, &cdev->page_bfile, mem, cdev->bandlist_memory, false); cdev->page_info.io_procs->fseek(cdev->page_bfile, 0, SEEK_SET, cdev->page_bfname); } emprintf1(mem, "Rendering threads not started, code=%d.\n", code); return_error(code); } crdev->num_render_threads = i; crdev->curr_render_thread = 0; if(gs_debug[':'] != 0) dprintf1("%% Using %d rendering threads\n", i); return 0; }