/src/ghostpdl/base/gsdevice.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2022 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, |
13 | | CA 94945, U.S.A., +1(415)492-9861, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* Device operators for Ghostscript library */ |
18 | | #include "ctype_.h" |
19 | | #include "memory_.h" /* for memchr, memcpy */ |
20 | | #include "string_.h" |
21 | | #include "gx.h" |
22 | | #include "gp.h" |
23 | | #include "gscdefs.h" /* for gs_lib_device_list */ |
24 | | #include "gserrors.h" |
25 | | #include "gsfname.h" |
26 | | #include "gsstruct.h" |
27 | | #include "gspath.h" /* gs_initclip prototype */ |
28 | | #include "gspaint.h" /* gs_erasepage prototype */ |
29 | | #include "gsmatrix.h" /* for gscoord.h */ |
30 | | #include "gscoord.h" /* for gs_initmatrix */ |
31 | | #include "gzstate.h" |
32 | | #include "gxcmap.h" |
33 | | #include "gxdevice.h" |
34 | | #include "gxdevmem.h" |
35 | | #include "gxdevsop.h" |
36 | | #include "gxiodev.h" |
37 | | #include "gxcspace.h" |
38 | | #include "gsicc_manage.h" |
39 | | #include "gscms.h" |
40 | | #include "gxgetbit.h" |
41 | | |
42 | | /* Include the extern for the device list. */ |
43 | | extern_gs_lib_device_list(); |
44 | | |
45 | | /* |
46 | | * Finalization for devices: do any special finalization first, then |
47 | | * close the device if it is open, and finally free the structure |
48 | | * descriptor if it is dynamic. |
49 | | */ |
50 | | void |
51 | | gx_device_finalize(const gs_memory_t *cmem, void *vptr) |
52 | 2.73M | { |
53 | 2.73M | gx_device * const dev = (gx_device *)vptr; |
54 | 2.73M | (void)cmem; /* unused */ |
55 | | |
56 | 2.73M | discard(gs_closedevice(dev)); |
57 | | |
58 | 2.73M | if (dev->icc_struct != NULL) { |
59 | 48.2k | rc_decrement(dev->icc_struct, "gx_device_finalize(icc_profile)"); |
60 | 48.2k | } |
61 | | |
62 | | /* Deal with subclassed devices. Ordinarily these should not be a problem, we |
63 | | * will never see them, but if ths is a end of job restore we can end up |
64 | | * with the 'child' device(s) being freed before their parents. We need to make |
65 | | * sure we don't leave any dangling pointers in that case. |
66 | | */ |
67 | 2.73M | if (dev->child) |
68 | 0 | dev->child->parent = dev->parent; |
69 | 2.73M | if (dev->parent) |
70 | 0 | dev->parent->child = dev->child; |
71 | 2.73M | if (dev->PageList) { |
72 | 0 | rc_decrement(dev->PageList, "gx_device_finalize(PageList)"); |
73 | 0 | dev->PageList = 0; |
74 | 0 | } |
75 | 2.73M | if (dev->NupControl) { |
76 | 0 | rc_decrement(dev->NupControl, "gx_device_finalize(NupControl)"); |
77 | 0 | dev->NupControl = 0; |
78 | 0 | } |
79 | | |
80 | 2.73M | if (dev->finalize) |
81 | 2.73M | dev->finalize(dev); |
82 | | |
83 | 2.73M | if (dev->stype_is_dynamic) |
84 | 683 | gs_free_const_object(dev->memory->non_gc_memory, dev->stype, |
85 | 683 | "gx_device_finalize"); |
86 | | |
87 | | #ifdef DEBUG |
88 | | /* Slightly ugly hack: because the garbage collector makes no promises |
89 | | * about the order objects can be garbage collected, it is possible for |
90 | | * a forwarding device to remain in existence (awaiting garbage collection |
91 | | * itself) after it's target marked as free memory by the garbage collector. |
92 | | * In such a case, the normal reference counting is fine (since the garbage |
93 | | * collector leaves the object contents alone until is has completed its |
94 | | * sweep), but the reference counting debugging attempts to access the |
95 | | * memory header to output type information - and the header has been |
96 | | * overwritten by the garbage collector, causing a crash. |
97 | | * Setting the rc memory to NULL here should be safe, since the memory |
98 | | * is now in the hands of the garbage collector, and means we can check in |
99 | | * debugging code to ensure we don't try to use values that not longer exist |
100 | | * in the memmory header. |
101 | | * In the non-gc case, finalize is the very last thing to happen before the |
102 | | * memory is actually freed, so the rc.memory pointer is moot. |
103 | | * See rc_object_type_name() |
104 | | */ |
105 | | if (gs_debug_c('^')) |
106 | | dev->rc.memory = NULL; |
107 | | #endif |
108 | 2.73M | } |
109 | | |
110 | | /* "Free" a device locally allocated on the stack, by finalizing it. */ |
111 | | void |
112 | | gx_device_free_local(gx_device *dev) |
113 | 0 | { |
114 | 0 | gx_device_finalize(dev->memory, dev); |
115 | 0 | } |
116 | | |
117 | | /* GC procedures */ |
118 | | static |
119 | 0 | ENUM_PTRS_WITH(device_enum_ptrs, gx_device *dev) return 0; |
120 | 0 | case 0:ENUM_RETURN(gx_device_enum_ptr(dev->parent)); |
121 | 0 | case 1:ENUM_RETURN(gx_device_enum_ptr(dev->child)); |
122 | 0 | ENUM_PTRS_END |
123 | 0 | static RELOC_PTRS_WITH(device_reloc_ptrs, gx_device *dev) |
124 | 0 | { |
125 | 0 | dev->parent = gx_device_reloc_ptr(dev->parent, gcst); |
126 | 0 | dev->child = gx_device_reloc_ptr(dev->child, gcst); |
127 | 0 | } |
128 | 0 | RELOC_PTRS_END |
129 | | static |
130 | 4.20k | ENUM_PTRS_WITH(device_forward_enum_ptrs, gx_device_forward *fdev) return 0; |
131 | 1.40k | case 0: ENUM_RETURN(gx_device_enum_ptr(fdev->target)); |
132 | 4.20k | ENUM_PTRS_END |
133 | 1.40k | static RELOC_PTRS_WITH(device_forward_reloc_ptrs, gx_device_forward *fdev) |
134 | 1.40k | { |
135 | 1.40k | fdev->target = gx_device_reloc_ptr(fdev->target, gcst); |
136 | 1.40k | } |
137 | 1.40k | RELOC_PTRS_END |
138 | | |
139 | | /* |
140 | | * Structure descriptors. These must follow the procedures, because |
141 | | * we can't conveniently forward-declare the procedures. |
142 | | * (See gxdevice.h for details.) |
143 | | */ |
144 | | public_st_device(); |
145 | | public_st_device_forward(); |
146 | | public_st_device_null(); |
147 | | |
148 | | /* GC utilities */ |
149 | | /* Enumerate or relocate a device pointer for a client. */ |
150 | | gx_device * |
151 | | gx_device_enum_ptr(gx_device * dev) |
152 | 395k | { |
153 | 395k | if (dev == 0 || dev->memory == 0) |
154 | 4.20k | return 0; |
155 | 391k | return dev; |
156 | 395k | } |
157 | | gx_device * |
158 | | gx_device_reloc_ptr(gx_device * dev, gc_state_t * gcst) |
159 | 395k | { |
160 | 395k | if (dev == 0 || dev->memory == 0) |
161 | 4.20k | return dev; |
162 | 391k | return RELOC_OBJ(dev); /* gcst implicit */ |
163 | 395k | } |
164 | | |
165 | | /* Flush buffered output to the device */ |
166 | | int |
167 | | gs_flushpage(gs_gstate * pgs) |
168 | 683 | { |
169 | 683 | gx_device *dev = gs_currentdevice(pgs); |
170 | | |
171 | 683 | return (*dev_proc(dev, sync_output)) (dev); |
172 | 683 | } |
173 | | |
174 | | /* Make the device output the accumulated page description */ |
175 | | int |
176 | | gs_copypage(gs_gstate * pgs) |
177 | 0 | { |
178 | 0 | return gs_output_page(pgs, 1, 0); |
179 | 0 | } |
180 | | int |
181 | | gs_output_page(gs_gstate * pgs, int num_copies, int flush) |
182 | 1.23k | { |
183 | 1.23k | gx_device *dev = gs_currentdevice(pgs); |
184 | 1.23k | cmm_dev_profile_t *dev_profile; |
185 | 1.23k | int code; |
186 | | |
187 | | /* for devices that hook 'fill_path' in order to pick up gs_gstate */ |
188 | | /* values such as dev_ht (such as tiffsep1), make a dummy call here */ |
189 | | /* to make sure that it has been called at least once */ |
190 | 1.23k | code = gs_gsave(pgs); |
191 | 1.23k | if (code < 0) |
192 | 0 | return code; |
193 | 1.23k | if (((code = gs_newpath(pgs)) < 0) || |
194 | 1.23k | ((code = gs_moveto(pgs, 0.0, 0.0)) < 0) || |
195 | 1.23k | ((code = gs_setgray(pgs, 0.0)) < 0) || |
196 | 1.23k | ((code = gs_fill(pgs)) < 0)) |
197 | 0 | { |
198 | 0 | gs_grestore(pgs); |
199 | 0 | return code; |
200 | 0 | } |
201 | 1.23k | code = gs_grestore(pgs); |
202 | 1.23k | if (code < 0) |
203 | 0 | return code; |
204 | | |
205 | 1.23k | if (dev->IgnoreNumCopies) |
206 | 0 | num_copies = 1; |
207 | 1.23k | if ((code = (*dev_proc(dev, output_page)) (dev, num_copies, flush)) < 0) |
208 | 9 | return code; |
209 | | |
210 | 1.22k | code = dev_proc(dev, get_profile)(dev, &(dev_profile)); |
211 | 1.22k | if (code < 0) |
212 | 0 | return code; |
213 | 1.22k | if (dev_profile->graydetection && !dev_profile->pageneutralcolor) { |
214 | 0 | dev_profile->pageneutralcolor = true; /* start detecting again */ |
215 | 0 | code = gsicc_mcm_begin_monitor(pgs->icc_link_cache, dev); |
216 | 0 | } |
217 | 1.22k | return code; |
218 | 1.22k | } |
219 | | |
220 | | /* |
221 | | * Do generic work for output_page. All output_page procedures must call |
222 | | * this as the last thing they do, unless an error has occurred earlier. |
223 | | */ |
224 | | int |
225 | | gx_finish_output_page(gx_device *dev, int num_copies, int flush) |
226 | 1.22k | { |
227 | 1.22k | dev->PageCount += num_copies; |
228 | 1.22k | return 0; |
229 | 1.22k | } |
230 | | |
231 | | /* Copy scan lines from an image device */ |
232 | | int |
233 | | gs_copyscanlines(gx_device * dev, int start_y, byte * data, uint size, |
234 | | int *plines_copied, uint * pbytes_copied) |
235 | 0 | { |
236 | 0 | uint line_size = gx_device_raster(dev, 0); |
237 | 0 | uint count = size / line_size; |
238 | 0 | uint i; |
239 | 0 | byte *dest = data; |
240 | 0 | gs_int_rect rect; |
241 | 0 | gs_get_bits_params_t params; |
242 | |
|
243 | 0 | rect.p.x = 0; |
244 | 0 | rect.q.x = dev->width; |
245 | 0 | params.x_offset = 0; |
246 | 0 | params.raster = bitmap_raster(dev->width * dev->color_info.depth); |
247 | |
|
248 | 0 | for (i = 0; i < count; i++, dest += line_size) { |
249 | 0 | int code; |
250 | |
|
251 | 0 | rect.p.y = start_y+i; |
252 | 0 | rect.q.y = start_y+i+1; |
253 | |
|
254 | 0 | params.options = (GB_ALIGN_ANY | |
255 | 0 | GB_RETURN_COPY | |
256 | 0 | GB_OFFSET_0 | |
257 | 0 | GB_RASTER_STANDARD | GB_PACKING_CHUNKY | |
258 | 0 | GB_COLORS_NATIVE | GB_ALPHA_NONE); |
259 | 0 | params.data[0] = dest; |
260 | 0 | code = (*dev_proc(dev, get_bits_rectangle))(dev, &rect, ¶ms); |
261 | 0 | if (code < 0) { |
262 | | /* Might just be an overrun. */ |
263 | 0 | if (start_y + i == dev->height) |
264 | 0 | break; |
265 | 0 | return_error(code); |
266 | 0 | } |
267 | 0 | } |
268 | 0 | if (plines_copied != NULL) |
269 | 0 | *plines_copied = i; |
270 | 0 | if (pbytes_copied != NULL) |
271 | 0 | *pbytes_copied = i * line_size; |
272 | 0 | return 0; |
273 | 0 | } |
274 | | |
275 | | /* Get the current device from the graphics state. */ |
276 | | gx_device * |
277 | | gs_currentdevice(const gs_gstate * pgs) |
278 | 136k | { |
279 | 136k | return pgs->device; |
280 | 136k | } |
281 | | |
282 | | /* Get the name of a device. */ |
283 | | const char * |
284 | | gs_devicename(const gx_device * dev) |
285 | 0 | { |
286 | 0 | return dev->dname; |
287 | 0 | } |
288 | | |
289 | | /* Get the initial matrix of a device. */ |
290 | | void |
291 | | gs_deviceinitialmatrix(gx_device * dev, gs_matrix * pmat) |
292 | 2.63M | { |
293 | 2.63M | fill_dev_proc(dev, get_initial_matrix, gx_default_get_initial_matrix); |
294 | 2.63M | (*dev_proc(dev, get_initial_matrix)) (dev, pmat); |
295 | 2.63M | } |
296 | | |
297 | | /* Get the N'th device from the known device list */ |
298 | | const gx_device * |
299 | | gs_getdevice(int index) |
300 | 13.6k | { |
301 | 13.6k | const gx_device *const *list; |
302 | 13.6k | int count = gs_lib_device_list(&list, NULL); |
303 | | |
304 | 13.6k | if (index < 0 || index >= count) |
305 | 1.36k | return 0; /* index out of range */ |
306 | 12.2k | return list[index]; |
307 | 13.6k | } |
308 | | |
309 | | /* Get the default device from the known device list */ |
310 | | const gx_device * |
311 | | gs_getdefaultlibdevice(gs_memory_t *mem) |
312 | 0 | { |
313 | 0 | const gx_device *const *list; |
314 | 0 | int count = gs_lib_device_list(&list, NULL); |
315 | 0 | const char *name, *end, *fin; |
316 | 0 | int i; |
317 | | |
318 | | /* Search the compiled in device list for a known device name */ |
319 | | /* In the case the lib ctx hasn't been initialised */ |
320 | 0 | if (mem && mem->gs_lib_ctx && mem->gs_lib_ctx->default_device_list) { |
321 | 0 | name = mem->gs_lib_ctx->default_device_list; |
322 | 0 | fin = name + strlen(name); |
323 | 0 | } |
324 | 0 | else { |
325 | 0 | name = gs_dev_defaults; |
326 | 0 | fin = name + strlen(name); |
327 | 0 | } |
328 | | |
329 | | /* iterate through each name in the string */ |
330 | 0 | while (name < fin) { |
331 | | |
332 | | /* split a name from any whitespace */ |
333 | 0 | while ((name < fin) && (*name == ' ' || *name == '\t')) |
334 | 0 | name++; |
335 | 0 | end = name; |
336 | 0 | while ((end < fin) && (*end != ' ') && (*end != '\t')) |
337 | 0 | end++; |
338 | | |
339 | | /* return any matches */ |
340 | 0 | for (i = 0; i < count; i++) |
341 | 0 | if ((end - name) == strlen(list[i]->dname)) |
342 | 0 | if (!memcmp(name, list[i]->dname, end - name)) |
343 | 0 | return gs_getdevice(i); |
344 | | |
345 | | /* otherwise, try the next device name */ |
346 | 0 | name = end; |
347 | 0 | } |
348 | | |
349 | | /* Fall back to the first device in the list. */ |
350 | 0 | return gs_getdevice(0); |
351 | 0 | } |
352 | | |
353 | | const gx_device * |
354 | | gs_getdefaultdevice(void) |
355 | 0 | { |
356 | 0 | return gs_getdefaultlibdevice(NULL); |
357 | 0 | } |
358 | | |
359 | | /* Fill in the GC structure descriptor for a device. */ |
360 | | static void |
361 | | gx_device_make_struct_type(gs_memory_struct_type_t *st, |
362 | | const gx_device *dev) |
363 | 683 | { |
364 | 683 | if (dev->stype) |
365 | 683 | *st = *dev->stype; |
366 | 0 | else if (dev_proc(dev, get_page_device) == gx_forward_get_page_device) |
367 | 0 | *st = st_device_forward; |
368 | 0 | else |
369 | 0 | *st = st_device; |
370 | 683 | st->ssize = dev->params_size; |
371 | 683 | } |
372 | | |
373 | | /* Clone an existing device. */ |
374 | | int |
375 | | gs_copydevice2(gx_device ** pnew_dev, const gx_device * dev, bool keep_open, |
376 | | gs_memory_t * mem) |
377 | 48.9k | { |
378 | 48.9k | gx_device *new_dev; |
379 | 48.9k | const gs_memory_struct_type_t *std = dev->stype; |
380 | 48.9k | const gs_memory_struct_type_t *new_std; |
381 | 48.9k | gs_memory_struct_type_t *a_std = 0; |
382 | 48.9k | int code; |
383 | | |
384 | 48.9k | if (dev->stype_is_dynamic) { |
385 | | /* |
386 | | * We allocated the stype for this device previously. |
387 | | * Just allocate a new stype and copy the old one into it. |
388 | | */ |
389 | 0 | a_std = (gs_memory_struct_type_t *) |
390 | 0 | gs_alloc_bytes_immovable(mem->non_gc_memory, sizeof(*std), |
391 | 0 | "gs_copydevice(stype)"); |
392 | 0 | if (!a_std) |
393 | 0 | return_error(gs_error_VMerror); |
394 | 0 | *a_std = *std; |
395 | 0 | new_std = a_std; |
396 | 48.9k | } else if (std != 0 && std->ssize == dev->params_size) { |
397 | | /* Use the static stype. */ |
398 | 48.2k | new_std = std; |
399 | 48.2k | } else { |
400 | | /* We need to figure out or adjust the stype. */ |
401 | 683 | a_std = (gs_memory_struct_type_t *) |
402 | 683 | gs_alloc_bytes_immovable(mem->non_gc_memory, sizeof(*std), |
403 | 683 | "gs_copydevice(stype)"); |
404 | 683 | if (!a_std) |
405 | 0 | return_error(gs_error_VMerror); |
406 | 683 | gx_device_make_struct_type(a_std, dev); |
407 | 683 | new_std = a_std; |
408 | 683 | } |
409 | | /* |
410 | | * Because command list devices have complicated internal pointer |
411 | | * structures, we allocate all device instances as immovable. |
412 | | */ |
413 | 48.9k | new_dev = gs_alloc_struct_immovable(mem, gx_device, new_std, |
414 | 48.9k | "gs_copydevice(device)"); |
415 | 48.9k | if (new_dev == 0) { |
416 | 0 | gs_free_object(mem->non_gc_memory, a_std, "gs_copydevice(stype)"); |
417 | 0 | return_error(gs_error_VMerror); |
418 | 0 | } |
419 | 48.9k | code = gx_device_init(new_dev, dev, mem, false); |
420 | 48.9k | new_dev->stype = new_std; |
421 | 48.9k | new_dev->stype_is_dynamic = new_std != std; |
422 | | /* |
423 | | * keep_open is very dangerous. On the other hand, so is copydevice in |
424 | | * general, since it just copies the bits without any regard to pointers |
425 | | * (including self-pointers) that they may contain. |
426 | | */ |
427 | 48.9k | new_dev->is_open = dev->is_open && keep_open; |
428 | 48.9k | if (code < 0) { |
429 | 0 | gs_free_object(mem, new_dev, "gs_copydevice(device)"); |
430 | | #if 0 /* gs_free_object above calls gx_device_finalize, |
431 | | which closes the device and releases its stype, i.e. a_std. */ |
432 | | if (a_std) |
433 | | gs_free_object(dev->memory->non_gc_memory, a_std, "gs_copydevice(stype)"); |
434 | | #endif |
435 | 0 | return code; |
436 | 0 | } |
437 | | /* We really want to be able to interrogate the device for capabilities |
438 | | * and/or preferences right from when it is created, so set dev_spec_op |
439 | | * now (if not already set). |
440 | | */ |
441 | 48.9k | fill_dev_proc(new_dev, dev_spec_op, gx_default_dev_spec_op); |
442 | 48.9k | *pnew_dev = new_dev; |
443 | 48.9k | return 0; |
444 | 48.9k | } |
445 | | int |
446 | | gs_copydevice(gx_device ** pnew_dev, const gx_device * dev, gs_memory_t * mem) |
447 | 48.2k | { |
448 | 48.2k | return gs_copydevice2(pnew_dev, dev, false, mem); |
449 | 48.2k | } |
450 | | |
451 | | /* Open a device if not open already. Return 0 if the device was open, */ |
452 | | /* 1 if it was closed. */ |
453 | | int |
454 | | gs_opendevice(gx_device *dev) |
455 | 3.94k | { |
456 | 3.94k | if (dev->is_open) |
457 | 0 | return 0; |
458 | 3.94k | check_device_separable(dev); |
459 | 3.94k | gx_device_fill_in_procs(dev); |
460 | 3.94k | { |
461 | 3.94k | int code = (*dev_proc(dev, open_device))(dev); |
462 | | |
463 | 3.94k | if (code < 0) |
464 | 0 | return_error(code); |
465 | 3.94k | dev->is_open = true; |
466 | 3.94k | return 1; |
467 | 3.94k | } |
468 | 3.94k | } |
469 | | |
470 | | static void |
471 | | gs_gstate_update_device(gs_gstate *pgs, gx_device *dev) |
472 | 17.5k | { |
473 | 17.5k | gx_set_cmap_procs(pgs, dev); |
474 | 17.5k | gx_unset_both_dev_colors(pgs); |
475 | 17.5k | } |
476 | | |
477 | | int |
478 | | gs_gstate_putdeviceparams(gs_gstate *pgs, gx_device *dev, gs_param_list *plist) |
479 | 0 | { |
480 | 0 | int code; |
481 | 0 | gx_device *dev2; |
482 | |
|
483 | 0 | if (dev) |
484 | 0 | dev2 = dev; |
485 | 0 | else |
486 | 0 | dev2 = pgs->device; |
487 | |
|
488 | 0 | code = gs_putdeviceparams(dev2, plist); |
489 | 0 | if (code >= 0) |
490 | 0 | gs_gstate_update_device(pgs, dev2); |
491 | 0 | return code; |
492 | 0 | } |
493 | | |
494 | | /* Set the device in the graphics state */ |
495 | | int |
496 | | gs_setdevice(gs_gstate * pgs, gx_device * dev) |
497 | 0 | { |
498 | 0 | int code = gs_setdevice_no_erase(pgs, dev); |
499 | |
|
500 | 0 | if (code == 1) |
501 | 0 | code = gs_erasepage(pgs); |
502 | 0 | return code; |
503 | 0 | } |
504 | | int |
505 | | gs_setdevice_no_erase(gs_gstate * pgs, gx_device * dev) |
506 | 8.42k | { |
507 | 8.42k | int open_code = 0, code; |
508 | 8.42k | gs_lib_ctx_t *libctx = gs_lib_ctx_get_interp_instance(pgs->memory); |
509 | | |
510 | | /* If the ICC manager is not yet initialized, set it up now. But only |
511 | | if we have file io capability now */ |
512 | 8.42k | if (libctx->io_device_table != NULL) { |
513 | 7.74k | cmm_dev_profile_t *dev_profile; |
514 | 7.74k | if (pgs->icc_manager->lab_profile == NULL) { /* pick one not set externally */ |
515 | 1.35k | code = gsicc_init_iccmanager(pgs); |
516 | 1.35k | if (code < 0) |
517 | 0 | return(code); |
518 | 1.35k | } |
519 | | /* Also, if the device profile is not yet set then take care of that |
520 | | before we start filling pages, if we can */ |
521 | | /* Although device methods should not be NULL, they are not completely filled in until |
522 | | * gx_device_fill_in_procs is called, and its possible for us to get here before this |
523 | | * happens, so we *must* make sure the method is not NULL before we use it. |
524 | | */ |
525 | 7.74k | if (dev->procs.get_profile != NULL) { |
526 | 6.39k | code = dev_proc(dev, get_profile)(dev, &dev_profile); |
527 | 6.39k | if (code < 0) { |
528 | 0 | return(code); |
529 | 0 | } |
530 | 6.39k | if (dev_profile == NULL || |
531 | 6.39k | dev_profile->device_profile[gsDEFAULTPROFILE] == NULL) { |
532 | 0 | if ((code = gsicc_init_device_profile_struct(dev, NULL, |
533 | 0 | gsDEFAULTPROFILE)) < 0) |
534 | 0 | return(code); |
535 | | /* set the intent too */ |
536 | 0 | if ((code = gsicc_set_device_profile_intent(dev, gsRINOTSPECIFIED, |
537 | 0 | gsDEFAULTPROFILE)) < 0) |
538 | 0 | return(code); |
539 | 0 | } |
540 | 6.39k | } |
541 | 7.74k | } |
542 | | |
543 | | /* Initialize the device */ |
544 | 8.42k | if (!dev->is_open) { |
545 | 3.94k | gx_device_fill_in_procs(dev); |
546 | | |
547 | | /* If we have not yet done so, and if we can, set the device profile |
548 | | * Doing so *before* the device is opened means that a device which |
549 | | * opens other devices can pass a profile on - for example, pswrite |
550 | | * also opens a bbox device |
551 | | */ |
552 | 3.94k | if (libctx->io_device_table != NULL) { |
553 | 3.26k | cmm_dev_profile_t *dev_profile; |
554 | | /* Although device methods should not be NULL, they are not completely filled in until |
555 | | * gx_device_fill_in_procs is called, and its possible for us to get here before this |
556 | | * happens, so we *must* make sure the method is not NULL before we use it. |
557 | | */ |
558 | 3.26k | if (dev->procs.get_profile != NULL) { |
559 | 3.26k | code = dev_proc(dev, get_profile)(dev, &dev_profile); |
560 | 3.26k | if (code < 0) { |
561 | 0 | return(code); |
562 | 0 | } |
563 | 3.26k | if (dev_profile == NULL || |
564 | 3.26k | dev_profile->device_profile[gsDEFAULTPROFILE] == NULL) { |
565 | 669 | if ((code = gsicc_init_device_profile_struct(dev, NULL, |
566 | 669 | gsDEFAULTPROFILE)) < 0) |
567 | 0 | return(code); |
568 | 669 | } |
569 | 3.26k | } |
570 | 3.26k | } |
571 | | |
572 | 3.94k | if (gs_device_is_memory(dev)) { |
573 | | /* Set the target to the current device. */ |
574 | 0 | gx_device *odev = gs_currentdevice_inline(pgs); |
575 | |
|
576 | 0 | while (odev != 0 && gs_device_is_memory(odev)) |
577 | 0 | odev = ((gx_device_memory *)odev)->target; |
578 | 0 | gx_device_set_target(((gx_device_forward *)dev), odev); |
579 | 0 | } |
580 | 3.94k | code = open_code = gs_opendevice(dev); |
581 | 3.94k | if (code < 0) |
582 | 0 | return code; |
583 | 3.94k | } |
584 | 8.42k | gs_setdevice_no_init(pgs, dev); |
585 | 8.42k | pgs->ctm_default_set = false; |
586 | 8.42k | if ((code = gs_initmatrix(pgs)) < 0 || |
587 | 8.42k | (code = gs_initclip(pgs)) < 0 |
588 | 8.42k | ) |
589 | 0 | return code; |
590 | | /* If we were in a charpath or a setcachedevice, */ |
591 | | /* we aren't any longer. */ |
592 | 8.42k | pgs->in_cachedevice = 0; |
593 | 8.42k | pgs->in_charpath = (gs_char_path_mode) 0; |
594 | 8.42k | return open_code; |
595 | 8.42k | } |
596 | | int |
597 | | gs_setdevice_no_init(gs_gstate * pgs, gx_device * dev) |
598 | 17.5k | { |
599 | | /* |
600 | | * Just set the device, possibly changing color space but no other |
601 | | * device parameters. |
602 | | * |
603 | | * Make sure we don't close the device if dev == pgs->device |
604 | | * This could be done by allowing the rc_assign to close the |
605 | | * old 'dev' if the rc goes to 0 (via the device structure's |
606 | | * finalization procedure), but then the 'code' from the dev |
607 | | * closedevice would not be propagated up. We want to allow |
608 | | * the code to be handled, particularly for the pdfwrite |
609 | | * device. |
610 | | */ |
611 | 17.5k | if (pgs->device != NULL && pgs->device->rc.ref_count == 1 && |
612 | 17.5k | pgs->device != dev) { |
613 | 683 | int code = gs_closedevice(pgs->device); |
614 | | |
615 | 683 | if (code < 0) |
616 | 0 | return code; |
617 | 683 | } |
618 | 17.5k | rc_assign(pgs->device, dev, "gs_setdevice_no_init"); |
619 | 17.5k | gs_gstate_update_device(pgs, dev); |
620 | 17.5k | return 0; |
621 | 17.5k | } |
622 | | |
623 | | /* Initialize a just-allocated device. */ |
624 | | int |
625 | | gx_device_init(gx_device * dev, const gx_device * proto, gs_memory_t * mem, |
626 | | bool internal) |
627 | 2.88M | { |
628 | 2.88M | memcpy(dev, proto, proto->params_size); |
629 | 2.88M | dev->initialize_device_procs = proto->initialize_device_procs; |
630 | 2.88M | if (dev->initialize_device_procs != NULL) |
631 | 2.88M | dev->initialize_device_procs(dev); |
632 | 2.88M | dev->memory = mem; /* must precede initialize_device call so devices can use it */ |
633 | 2.88M | if (dev->procs.initialize_device) { |
634 | 44.6k | int code = dev->procs.initialize_device(dev); |
635 | 44.6k | if (code < 0) |
636 | 0 | return code; |
637 | 44.6k | } |
638 | 2.88M | dev->retained = !internal; |
639 | 2.88M | rc_init(dev, mem, (internal ? 0 : 1)); |
640 | 2.88M | rc_increment(dev->icc_struct); |
641 | | |
642 | 2.88M | return 0; |
643 | 2.88M | } |
644 | | |
645 | | void |
646 | | gx_device_init_on_stack(gx_device * dev, const gx_device * proto, |
647 | | gs_memory_t * mem) |
648 | 583k | { |
649 | 583k | memcpy(dev, proto, proto->params_size); |
650 | 583k | dev->initialize_device_procs = proto->initialize_device_procs; |
651 | 583k | dev->initialize_device_procs(dev); |
652 | 583k | if (dev->procs.initialize_device) { |
653 | | /* A condition of devices inited on the stack is that they can |
654 | | * never fail to initialize! */ |
655 | 0 | (void)dev->procs.initialize_device(dev); |
656 | 0 | } |
657 | 583k | gx_device_fill_in_procs(dev); |
658 | 583k | dev->memory = mem; |
659 | 583k | dev->retained = 0; |
660 | 583k | dev->pad = proto->pad; |
661 | 583k | dev->log2_align_mod = proto->log2_align_mod; |
662 | 583k | dev->is_planar = proto->is_planar; |
663 | 583k | rc_init(dev, NULL, 0); |
664 | 583k | } |
665 | | |
666 | | /* Make a null device. */ |
667 | | void |
668 | | gs_make_null_device(gx_device_null *dev_null, gx_device *dev, |
669 | | gs_memory_t * mem) |
670 | 8.45k | { |
671 | | /* Can never fail */ |
672 | 8.45k | (void)gx_device_init((gx_device *)dev_null, |
673 | 8.45k | (const gx_device *)&gs_null_device, |
674 | 8.45k | mem, true); |
675 | 8.45k | gx_device_fill_in_procs((gx_device *)dev_null); |
676 | 8.45k | gx_device_set_target((gx_device_forward *)dev_null, dev); |
677 | 8.45k | if (dev) { |
678 | | /* The gx_device_copy_color_params() call below should |
679 | | probably copy over these new-style color mapping procs, as |
680 | | well as the old-style (map_rgb_color and friends). However, |
681 | | the change was made here instead, to minimize the potential |
682 | | impact of the patch. |
683 | | */ |
684 | 8.45k | gx_device *dn = (gx_device *)dev_null; |
685 | 8.45k | set_dev_proc(dn, get_color_mapping_procs, gx_forward_get_color_mapping_procs); |
686 | 8.45k | set_dev_proc(dn, get_color_comp_index, gx_forward_get_color_comp_index); |
687 | 8.45k | set_dev_proc(dn, encode_color, gx_forward_encode_color); |
688 | 8.45k | set_dev_proc(dn, decode_color, gx_forward_decode_color); |
689 | 8.45k | set_dev_proc(dn, get_profile, gx_forward_get_profile); |
690 | 8.45k | set_dev_proc(dn, set_graphics_type_tag, gx_forward_set_graphics_type_tag); |
691 | 8.45k | set_dev_proc(dn, begin_transparency_group, gx_default_begin_transparency_group); |
692 | 8.45k | set_dev_proc(dn, end_transparency_group, gx_default_end_transparency_group); |
693 | 8.45k | set_dev_proc(dn, begin_transparency_mask, gx_default_begin_transparency_mask); |
694 | 8.45k | set_dev_proc(dn, end_transparency_mask, gx_default_end_transparency_mask); |
695 | 8.45k | set_dev_proc(dn, discard_transparency_layer, gx_default_discard_transparency_layer); |
696 | 8.45k | set_dev_proc(dn, push_transparency_state, gx_default_push_transparency_state); |
697 | 8.45k | set_dev_proc(dn, pop_transparency_state, gx_default_pop_transparency_state); |
698 | 8.45k | set_dev_proc(dn, put_image, gx_default_put_image); |
699 | 8.45k | set_dev_proc(dn, copy_planes, gx_default_copy_planes); |
700 | 8.45k | set_dev_proc(dn, copy_alpha_hl_color, gx_default_no_copy_alpha_hl_color); |
701 | 8.45k | dn->graphics_type_tag = dev->graphics_type_tag; /* initialize to same as target */ |
702 | 8.45k | gx_device_copy_color_params(dn, dev); |
703 | 8.45k | } |
704 | 8.45k | } |
705 | | |
706 | | /* Is a null device ? */ |
707 | | bool gs_is_null_device(gx_device *dev) |
708 | 202k | { |
709 | | /* Assuming null_fill_path isn't used elswhere. */ |
710 | 202k | return dev->procs.fill_path == gs_null_device.procs.fill_path; |
711 | 202k | } |
712 | | |
713 | | /* Mark a device as retained or not retained. */ |
714 | | void |
715 | | gx_device_retain(gx_device *dev, bool retained) |
716 | 2.78M | { |
717 | 2.78M | int delta = (int)retained - (int)dev->retained; |
718 | | |
719 | 2.78M | if (delta) { |
720 | 2.78M | dev->retained = retained; /* do first in case dev is freed */ |
721 | 2.78M | rc_adjust_only(dev, delta, "gx_device_retain"); |
722 | 2.78M | } |
723 | 2.78M | } |
724 | | |
725 | | /* Select a null device. */ |
726 | | int |
727 | | gs_nulldevice(gs_gstate * pgs) |
728 | 3.26k | { |
729 | 3.26k | int code = 0; |
730 | 3.26k | gs_gstate *spgs; |
731 | 3.26k | bool saveLockSafety = false; |
732 | 3.26k | if (pgs->device == NULL || !gx_device_is_null(pgs->device)) { |
733 | 3.26k | gx_device *ndev; |
734 | 3.26k | code = gs_copydevice(&ndev, (const gx_device *)&gs_null_device, |
735 | 3.26k | pgs->memory); |
736 | | |
737 | 3.26k | if (code < 0) |
738 | 0 | return code; |
739 | 3.26k | if (gs_currentdevice_inline(pgs) != NULL) |
740 | 1.91k | saveLockSafety = gs_currentdevice_inline(pgs)->LockSafetyParams; |
741 | | /* |
742 | | * Internal devices have a reference count of 0, not 1, |
743 | | * aside from references from graphics states. |
744 | | */ |
745 | | /* There is some strange use of the null device in the code. I need |
746 | | to sort out how the icc profile is best handled with this device. |
747 | | It seems to inherit properties from the current device if there |
748 | | is one */ |
749 | 3.26k | rc_init(ndev, pgs->memory, 0); |
750 | 3.26k | if (pgs->device != NULL) { |
751 | 1.91k | if ((code = dev_proc(pgs->device, get_profile)(pgs->device, |
752 | 1.91k | &(ndev->icc_struct))) < 0) |
753 | 0 | return code; |
754 | 1.91k | rc_increment(ndev->icc_struct); |
755 | 1.91k | set_dev_proc(ndev, get_profile, gx_default_get_profile); |
756 | 1.91k | } |
757 | | |
758 | 3.26k | if (gs_setdevice_no_erase(pgs, ndev) < 0) { |
759 | 0 | gs_free_object(pgs->memory, ndev, "gs_copydevice(device)"); |
760 | | /* We are out of options: find the device we installed in |
761 | | the initial graphics state, and put that in place. |
762 | | We just need something so we can end this job cleanly. |
763 | | */ |
764 | 0 | spgs = pgs->saved; |
765 | 0 | if (spgs != NULL) { |
766 | 0 | while (spgs->saved) spgs = spgs->saved; |
767 | 0 | gs_currentdevice_inline(pgs) = gs_currentdevice_inline(spgs); |
768 | 0 | rc_increment(gs_currentdevice_inline(pgs)); |
769 | 0 | } |
770 | 0 | code = gs_note_error(gs_error_Fatal); |
771 | 0 | } |
772 | 3.26k | if (gs_currentdevice_inline(pgs) != NULL) |
773 | 3.26k | gs_currentdevice_inline(pgs)->LockSafetyParams = saveLockSafety; |
774 | 3.26k | } |
775 | 3.26k | return code; |
776 | 3.26k | } |
777 | | |
778 | | /* Close a device. The client is responsible for ensuring that */ |
779 | | /* this device is not current in any graphics state. */ |
780 | | int |
781 | | gs_closedevice(gx_device * dev) |
782 | 2.73M | { |
783 | 2.73M | int code = 0; |
784 | | |
785 | 2.73M | if (dev->is_open) { |
786 | 14.1k | code = (*dev_proc(dev, close_device))(dev); |
787 | 14.1k | dev->is_open = false; |
788 | 14.1k | if (code < 0) |
789 | 0 | return_error(code); |
790 | 14.1k | } |
791 | 2.73M | return code; |
792 | 2.73M | } |
793 | | |
794 | | /* |
795 | | * Just set the device without any reinitializing. |
796 | | * (For internal use only.) |
797 | | */ |
798 | | void |
799 | | gx_set_device_only(gs_gstate * pgs, gx_device * dev) |
800 | 134k | { |
801 | 134k | rc_assign(pgs->device, dev, "gx_set_device_only"); |
802 | 134k | } |
803 | | |
804 | | /* Compute the size of one scan line for a device. */ |
805 | | /* If pad = 0 return the line width in bytes. If pad = 1, |
806 | | * return the actual raster value (the number of bytes to offset from |
807 | | * a byte on one scanline to the same byte on the scanline below.) */ |
808 | | uint |
809 | | gx_device_raster(const gx_device * dev, bool pad) |
810 | 5.78M | { |
811 | 5.78M | int depth = dev->color_info.depth; |
812 | 5.78M | ulong bits = (ulong) dev->width * depth; |
813 | 5.78M | ulong raster; |
814 | 5.78M | int l2align; |
815 | | |
816 | 5.78M | if (dev->is_planar) { |
817 | 0 | int num_components = dev->color_info.num_components; |
818 | | /* bpc accounts for unused bits, e.g. depth==4, num_comp==3, or depth==8, num_comps==5 */ |
819 | 0 | int bpc = depth / num_components; |
820 | | |
821 | | /* depth can be <= num_components if planar and MEM_SET_PARAMS has changed it */ |
822 | 0 | if (depth <= num_components || bpc >= 8) { |
823 | 0 | bits /= num_components; |
824 | 0 | } else { |
825 | | /* depth is original depth, not the plane_depth since it is > num_components */ |
826 | 0 | bits /= (depth / bpc); |
827 | 0 | } |
828 | 0 | } |
829 | 5.78M | raster = (uint)((bits + 7) >> 3); |
830 | 5.78M | if (!pad) |
831 | 2.57M | return raster; |
832 | 3.20M | l2align = dev->log2_align_mod; |
833 | 3.20M | if (l2align < log2_align_bitmap_mod) |
834 | 3.20M | l2align = log2_align_bitmap_mod; |
835 | 3.20M | return (uint)(((bits + (8 << l2align) - 1) >> (l2align + 3)) << l2align); |
836 | 5.78M | } |
837 | | |
838 | | uint |
839 | | gx_device_raster_chunky(const gx_device * dev, bool pad) |
840 | 0 | { |
841 | 0 | ulong bits = (ulong) dev->width * dev->color_info.depth; |
842 | 0 | ulong raster; |
843 | 0 | int l2align; |
844 | |
|
845 | 0 | raster = (uint)((bits + 7) >> 3); |
846 | 0 | if (!pad) |
847 | 0 | return raster; |
848 | 0 | l2align = dev->log2_align_mod; |
849 | 0 | if (l2align < log2_align_bitmap_mod) |
850 | 0 | l2align = log2_align_bitmap_mod; |
851 | 0 | return (uint)(((bits + (8 << l2align) - 1) >> (l2align + 3)) << l2align); |
852 | 0 | } |
853 | | uint |
854 | | gx_device_raster_plane(const gx_device * dev, const gx_render_plane_t *render_plane) |
855 | 2.57M | { |
856 | 2.57M | ulong bpc = (render_plane && render_plane->index >= 0 ? |
857 | 2.57M | render_plane->depth : dev->color_info.depth/(dev->is_planar ? dev->color_info.num_components : 1)); |
858 | 2.57M | ulong bits = (ulong) dev->width * bpc; |
859 | 2.57M | int l2align; |
860 | | |
861 | 2.57M | l2align = dev->log2_align_mod; |
862 | 2.57M | if (l2align < log2_align_bitmap_mod) |
863 | 2.57M | l2align = log2_align_bitmap_mod; |
864 | 2.57M | return (uint)(((bits + (8 << l2align) - 1) >> (l2align + 3)) << l2align); |
865 | 2.57M | } |
866 | | |
867 | | /* Adjust the resolution for devices that only have a fixed set of */ |
868 | | /* geometries, so that the apparent size in inches remains constant. */ |
869 | | /* If fit=1, the resolution is adjusted so that the entire image fits; */ |
870 | | /* if fit=0, one dimension fits, but the other one is clipped. */ |
871 | | int |
872 | | gx_device_adjust_resolution(gx_device * dev, |
873 | | int actual_width, int actual_height, int fit) |
874 | 0 | { |
875 | 0 | double width_ratio = (double)actual_width / dev->width; |
876 | 0 | double height_ratio = (double)actual_height / dev->height; |
877 | 0 | double ratio = |
878 | 0 | (fit ? min(width_ratio, height_ratio) : |
879 | 0 | max(width_ratio, height_ratio)); |
880 | |
|
881 | 0 | dev->HWResolution[0] *= ratio; |
882 | 0 | dev->HWResolution[1] *= ratio; |
883 | 0 | gx_device_set_width_height(dev, actual_width, actual_height); |
884 | 0 | return 0; |
885 | 0 | } |
886 | | |
887 | | /* Set the HWMargins to values defined in inches. */ |
888 | | /* If move_origin is true, also reset the Margins. */ |
889 | | /* Note that this assumes a printer-type device (Y axis inverted). */ |
890 | | void |
891 | | gx_device_set_margins(gx_device * dev, const float *margins /*[4] */ , |
892 | | bool move_origin) |
893 | 3.72k | { |
894 | 3.72k | int i; |
895 | | |
896 | 18.6k | for (i = 0; i < 4; ++i) |
897 | 14.9k | dev->HWMargins[i] = margins[i] * 72.0; |
898 | 3.72k | if (move_origin) { |
899 | 0 | dev->Margins[0] = -margins[0] * dev->HWResolution[0]; |
900 | 0 | dev->Margins[1] = -margins[3] * dev->HWResolution[1]; |
901 | 0 | } |
902 | 3.72k | } |
903 | | |
904 | | static void |
905 | | gx_device_set_hwsize_from_media(gx_device *dev) |
906 | 5.42k | { |
907 | 5.42k | int rot = (dev->LeadingEdge & 1); |
908 | 5.42k | double rot_media_x = rot ? dev->MediaSize[1] : dev->MediaSize[0]; |
909 | 5.42k | double rot_media_y = rot ? dev->MediaSize[0] : dev->MediaSize[1]; |
910 | 5.42k | gx_device *parent = dev; |
911 | 5.42k | int hwsize[2]; |
912 | | |
913 | | /* Try the spec_op to give the device to control it */ |
914 | 5.42k | hwsize[0] = (int)(rot_media_x * dev->HWResolution[0] / 72.0 + 0.5); |
915 | 5.42k | hwsize[1] = (int)(rot_media_y * dev->HWResolution[1] / 72.0 + 0.5); |
916 | | |
917 | 5.42k | while (parent->parent != NULL) { |
918 | 0 | parent = parent->parent; |
919 | 0 | } |
920 | 5.42k | if (dev_proc(parent, dev_spec_op)(parent, gxdso_set_HWSize, &hwsize, sizeof(hwsize)) <= 0) { |
921 | | /* just do the default setting */ |
922 | 5.42k | dev->width = hwsize[0]; |
923 | 5.42k | dev->height = hwsize[1]; |
924 | 5.42k | } |
925 | 5.42k | } |
926 | | |
927 | | static void |
928 | | gx_device_set_media_from_hwsize(gx_device *dev) |
929 | 1.01k | { |
930 | 1.01k | int rot = (dev->LeadingEdge & 1); |
931 | 1.01k | double x = dev->width * 72.0 / dev->HWResolution[0]; |
932 | 1.01k | double y = dev->height * 72.0 / dev->HWResolution[1]; |
933 | | |
934 | 1.01k | if (rot) { |
935 | 0 | dev->MediaSize[1] = x; |
936 | 0 | dev->MediaSize[0] = y; |
937 | 1.01k | } else { |
938 | 1.01k | dev->MediaSize[0] = x; |
939 | 1.01k | dev->MediaSize[1] = y; |
940 | 1.01k | } |
941 | 1.01k | } |
942 | | |
943 | | /* Set the width and height, updating MediaSize to remain consistent. */ |
944 | | void |
945 | | gx_device_set_width_height(gx_device * dev, int width, int height) |
946 | 1.01k | { |
947 | 1.01k | dev->width = width; |
948 | 1.01k | dev->height = height; |
949 | 1.01k | gx_device_set_media_from_hwsize(dev); |
950 | 1.01k | } |
951 | | |
952 | | /* Set the resolution, updating width and height to remain consistent. */ |
953 | | void |
954 | | gx_device_set_resolution(gx_device * dev, double x_dpi, double y_dpi) |
955 | 683 | { |
956 | 683 | dev->HWResolution[0] = x_dpi; |
957 | 683 | dev->HWResolution[1] = y_dpi; |
958 | 683 | gx_device_set_hwsize_from_media(dev); |
959 | 683 | } |
960 | | |
961 | | /* Set the MediaSize, updating width and height to remain consistent. */ |
962 | | void |
963 | | gx_device_set_media_size(gx_device * dev, double media_width, double media_height) |
964 | 4.73k | { |
965 | 4.73k | dev->MediaSize[0] = media_width; |
966 | 4.73k | dev->MediaSize[1] = media_height; |
967 | 4.73k | gx_device_set_hwsize_from_media(dev); |
968 | 4.73k | } |
969 | | |
970 | | /* |
971 | | * Copy the color mapping procedures from the target if they are |
972 | | * standard ones (saving a level of procedure call at mapping time). |
973 | | */ |
974 | | void |
975 | | gx_device_copy_color_procs(gx_device *dev, const gx_device *target) |
976 | 2.97M | { |
977 | 2.97M | dev_proc_map_cmyk_color((*from_cmyk)) = |
978 | 2.97M | dev_proc(dev, map_cmyk_color); |
979 | 2.97M | dev_proc_map_rgb_color((*from_rgb)) = |
980 | 2.97M | dev_proc(dev, map_rgb_color); |
981 | 2.97M | dev_proc_map_color_rgb((*to_rgb)) = |
982 | 2.97M | dev_proc(dev, map_color_rgb); |
983 | | |
984 | | /* The logic in this function seems a bit stale; it sets the |
985 | | old-style color procs, but not the new ones |
986 | | (get_color_mapping_procs, get_color_comp_index, encode_color, |
987 | | and decode_color). It should probably copy those as well. |
988 | | */ |
989 | 2.97M | if (from_cmyk == gx_forward_map_cmyk_color || |
990 | 2.97M | from_cmyk == cmyk_1bit_map_cmyk_color || |
991 | 2.97M | from_cmyk == cmyk_8bit_map_cmyk_color) { |
992 | 2.97M | from_cmyk = dev_proc(target, map_cmyk_color); |
993 | 2.97M | set_dev_proc(dev, map_cmyk_color, |
994 | 2.97M | (from_cmyk == cmyk_1bit_map_cmyk_color || |
995 | 2.97M | from_cmyk == cmyk_8bit_map_cmyk_color ? |
996 | 2.97M | from_cmyk : gx_forward_map_cmyk_color)); |
997 | 2.97M | } |
998 | 2.97M | if (from_rgb == gx_forward_map_rgb_color || |
999 | 2.97M | from_rgb == gx_default_rgb_map_rgb_color) { |
1000 | 2.97M | from_rgb = dev_proc(target, map_rgb_color); |
1001 | 2.97M | set_dev_proc(dev, map_rgb_color, |
1002 | 2.97M | (from_rgb == gx_default_rgb_map_rgb_color ? |
1003 | 2.97M | from_rgb : gx_forward_map_rgb_color)); |
1004 | 2.97M | } |
1005 | 2.97M | if (to_rgb == gx_forward_map_color_rgb || |
1006 | 2.97M | to_rgb == cmyk_1bit_map_color_rgb || |
1007 | 2.97M | to_rgb == cmyk_8bit_map_color_rgb) { |
1008 | 2.97M | to_rgb = dev_proc(target, map_color_rgb); |
1009 | 2.97M | set_dev_proc(dev, map_color_rgb, |
1010 | 2.97M | (to_rgb == cmyk_1bit_map_color_rgb || |
1011 | 2.97M | to_rgb == cmyk_8bit_map_color_rgb ? |
1012 | 2.97M | to_rgb : gx_forward_map_color_rgb)); |
1013 | 2.97M | } |
1014 | 2.97M | } |
1015 | | |
1016 | 16.9k | #define COPY_PARAM(p) dev->p = target->p |
1017 | | |
1018 | | /* |
1019 | | * Copy the color-related device parameters back from the target: |
1020 | | * color_info and color mapping procedures. |
1021 | | */ |
1022 | | void |
1023 | | gx_device_copy_color_params(gx_device *dev, const gx_device *target) |
1024 | 8.45k | { |
1025 | 8.45k | COPY_PARAM(color_info); |
1026 | 8.45k | COPY_PARAM(cached_colors); |
1027 | 8.45k | gx_device_copy_color_procs(dev, target); |
1028 | 8.45k | } |
1029 | | |
1030 | | /* |
1031 | | * Copy device parameters back from a target. This copies all standard |
1032 | | * parameters related to page size and resolution, plus color_info |
1033 | | * and (if appropriate) color mapping procedures. |
1034 | | */ |
1035 | | void |
1036 | | gx_device_copy_params(gx_device *dev, const gx_device *target) |
1037 | 0 | { |
1038 | 0 | #define COPY_ARRAY_PARAM(p) memcpy(dev->p, target->p, sizeof(dev->p)) |
1039 | 0 | COPY_PARAM(width); |
1040 | 0 | COPY_PARAM(height); |
1041 | 0 | COPY_ARRAY_PARAM(MediaSize); |
1042 | 0 | COPY_ARRAY_PARAM(ImagingBBox); |
1043 | 0 | COPY_PARAM(ImagingBBox_set); |
1044 | 0 | COPY_ARRAY_PARAM(HWResolution); |
1045 | 0 | COPY_ARRAY_PARAM(Margins); |
1046 | 0 | COPY_ARRAY_PARAM(HWMargins); |
1047 | 0 | COPY_PARAM(PageCount); |
1048 | 0 | COPY_PARAM(MaxPatternBitmap); |
1049 | 0 | #undef COPY_ARRAY_PARAM |
1050 | 0 | gx_device_copy_color_params(dev, target); |
1051 | 0 | } |
1052 | | |
1053 | | #undef COPY_PARAM |
1054 | | |
1055 | | /* |
1056 | | * Parse the output file name detecting and validating any %nnd format |
1057 | | * for inserting the page count. If a format is present, store a pointer |
1058 | | * to its last character in *pfmt, otherwise store 0 there. |
1059 | | * Note that we assume devices have already been scanned, and any % must |
1060 | | * precede a valid format character. |
1061 | | * |
1062 | | * If there was a format, then return the max_width |
1063 | | */ |
1064 | | static int |
1065 | | gx_parse_output_format(gs_parsed_file_name_t *pfn, const char **pfmt) |
1066 | 4.43k | { |
1067 | 4.43k | bool have_format = false, field; |
1068 | 4.43k | int width[2], int_width = sizeof(int) * 3, w = 0; |
1069 | 4.43k | uint i; |
1070 | | |
1071 | | /* Scan the file name for a format string, and validate it if present. */ |
1072 | 4.43k | width[0] = width[1] = 0; |
1073 | 44.3k | for (i = 0; i < pfn->len; ++i) |
1074 | 39.9k | if (pfn->fname[i] == '%') { |
1075 | 0 | if (i + 1 < pfn->len && pfn->fname[i + 1] == '%') { |
1076 | 0 | i++; |
1077 | 0 | continue; |
1078 | 0 | } |
1079 | 0 | if (have_format) /* more than one % */ |
1080 | 0 | return_error(gs_error_undefinedfilename); |
1081 | 0 | have_format = true; |
1082 | 0 | field = -1; /* -1..3 for the 5 components of "%[flags][width][.precision][l]type" */ |
1083 | 0 | for (;;) |
1084 | 0 | if (++i == pfn->len) |
1085 | 0 | return_error(gs_error_undefinedfilename); |
1086 | 0 | else { |
1087 | 0 | switch (field) { |
1088 | 0 | case -1: /* flags */ |
1089 | 0 | if (strchr(" #+-", pfn->fname[i])) |
1090 | 0 | continue; |
1091 | 0 | else |
1092 | 0 | field++; |
1093 | | /* falls through */ |
1094 | 0 | default: /* width (field = 0) and precision (field = 1) */ |
1095 | 0 | if (strchr("0123456789", pfn->fname[i])) { |
1096 | 0 | width[field] = width[field] * 10 + pfn->fname[i] - '0'; |
1097 | 0 | continue; |
1098 | 0 | } else if (0 == field && '.' == pfn->fname[i]) { |
1099 | 0 | field++; |
1100 | 0 | continue; |
1101 | 0 | } else |
1102 | 0 | field = 2; |
1103 | | /* falls through */ |
1104 | 0 | case 2: /* "long" indicator */ |
1105 | 0 | field++; |
1106 | 0 | if ('l' == pfn->fname[i]) { |
1107 | 0 | int_width = sizeof(long) * 3; |
1108 | 0 | continue; |
1109 | 0 | } |
1110 | | /* falls through */ |
1111 | 0 | case 3: /* type */ |
1112 | 0 | if (strchr("diuoxX", pfn->fname[i])) { |
1113 | 0 | *pfmt = &pfn->fname[i]; |
1114 | 0 | break; |
1115 | 0 | } else |
1116 | 0 | return_error(gs_error_undefinedfilename); |
1117 | 0 | } |
1118 | 0 | break; |
1119 | 0 | } |
1120 | 0 | } |
1121 | 4.43k | if (have_format) { |
1122 | | /* Calculate a conservative maximum width. */ |
1123 | 0 | w = max(width[0], width[1]); |
1124 | 0 | w = max(w, int_width) + 5; |
1125 | 0 | } |
1126 | 4.43k | return w; |
1127 | 4.43k | } |
1128 | | |
1129 | | /* |
1130 | | * Parse the output file name for a device, recognizing "-" and "|command", |
1131 | | * and also detecting and validating any %nnd format for inserting the |
1132 | | * page count. If a format is present, store a pointer to its last |
1133 | | * character in *pfmt, otherwise store 0 there. Note that an empty name |
1134 | | * is currently allowed. |
1135 | | */ |
1136 | | int |
1137 | | gx_parse_output_file_name(gs_parsed_file_name_t *pfn, const char **pfmt, |
1138 | | const char *fname, uint fnlen, gs_memory_t *memory) |
1139 | 4.43k | { |
1140 | 4.43k | int code; |
1141 | | |
1142 | 4.43k | *pfmt = 0; |
1143 | 4.43k | pfn->memory = 0; |
1144 | 4.43k | pfn->iodev = NULL; |
1145 | 4.43k | pfn->fname = NULL; /* irrelevant since length = 0 */ |
1146 | 4.43k | pfn->len = 0; |
1147 | 4.43k | if (fnlen == 0) /* allow null name */ |
1148 | 0 | return 0; |
1149 | | /* |
1150 | | * If the file name begins with a %, it might be either an IODevice |
1151 | | * or a %nnd format. Check (carefully) for this case. |
1152 | | */ |
1153 | 4.43k | code = gs_parse_file_name(pfn, fname, fnlen, memory); |
1154 | 4.43k | if (code < 0) { |
1155 | 0 | if (fname[0] == '%') { |
1156 | | /* not a recognized iodev -- may be a leading format descriptor */ |
1157 | 0 | pfn->len = fnlen; |
1158 | 0 | pfn->fname = fname; |
1159 | 0 | code = gx_parse_output_format(pfn, pfmt); |
1160 | 0 | } |
1161 | 0 | if (code < 0) |
1162 | 0 | return code; |
1163 | 0 | } |
1164 | 4.43k | if (!pfn->iodev) { |
1165 | 4.43k | if ( (pfn->len == 1) && (pfn->fname[0] == '-') ) { |
1166 | 0 | pfn->iodev = gs_findiodevice(memory, (const byte *)"%stdout", 7); |
1167 | 0 | pfn->fname = NULL; |
1168 | 4.43k | } else if (pfn->fname[0] == '|') { |
1169 | 0 | pfn->iodev = gs_findiodevice(memory, (const byte *)"%pipe", 5); |
1170 | 0 | pfn->fname++, pfn->len--; |
1171 | 0 | } else |
1172 | 4.43k | pfn->iodev = iodev_default(memory); |
1173 | 4.43k | if (!pfn->iodev) |
1174 | 0 | return_error(gs_error_undefinedfilename); |
1175 | 4.43k | } |
1176 | 4.43k | if (!pfn->fname) |
1177 | 0 | return 0; |
1178 | 4.43k | code = gx_parse_output_format(pfn, pfmt); |
1179 | 4.43k | if (code < 0) |
1180 | 0 | return code; |
1181 | 4.43k | if (strlen(pfn->iodev->dname) + pfn->len + code >= gp_file_name_sizeof) |
1182 | 0 | return_error(gs_error_undefinedfilename); |
1183 | 4.43k | return 0; |
1184 | 4.43k | } |
1185 | | |
1186 | | /* Check if we write each page into separate file. */ |
1187 | | bool |
1188 | | gx_outputfile_is_separate_pages(const char *fname, gs_memory_t *memory) |
1189 | 0 | { |
1190 | 0 | const char *fmt; |
1191 | 0 | gs_parsed_file_name_t parsed; |
1192 | 0 | int code = gx_parse_output_file_name(&parsed, &fmt, fname, |
1193 | 0 | strlen(fname), memory); |
1194 | |
|
1195 | 0 | return (code >= 0 && fmt != 0); |
1196 | 0 | } |
1197 | | |
1198 | | /* Delete the current output file for a device (file must be closed first) */ |
1199 | | int gx_device_delete_output_file(const gx_device * dev, const char *fname) |
1200 | 0 | { |
1201 | 0 | gs_parsed_file_name_t parsed; |
1202 | 0 | const char *fmt; |
1203 | 0 | char *pfname = (char *)gs_alloc_bytes(dev->memory, gp_file_name_sizeof, "gx_device_delete_output_file(pfname)"); |
1204 | 0 | int code; |
1205 | 0 | size_t len; |
1206 | |
|
1207 | 0 | if (pfname == NULL) { |
1208 | 0 | code = gs_note_error(gs_error_VMerror); |
1209 | 0 | goto done; |
1210 | 0 | } |
1211 | | |
1212 | 0 | len = strlen(fname); |
1213 | 0 | code = gx_parse_output_file_name(&parsed, &fmt, fname, len, |
1214 | 0 | dev->memory); |
1215 | 0 | if (code < 0) { |
1216 | 0 | goto done; |
1217 | 0 | } |
1218 | | |
1219 | 0 | if (parsed.iodev && !strcmp(parsed.iodev->dname, "%stdout%")) |
1220 | 0 | goto done; |
1221 | | |
1222 | 0 | if (fmt) { /* filename includes "%nnd" */ |
1223 | 0 | long count1 = dev->PageCount + 1; |
1224 | |
|
1225 | 0 | while (*fmt != 'l' && *fmt != '%') |
1226 | 0 | --fmt; |
1227 | 0 | if (*fmt == 'l') |
1228 | 0 | gs_snprintf(pfname, len, parsed.fname, count1); |
1229 | 0 | else |
1230 | 0 | gs_snprintf(pfname, len, parsed.fname, (int)count1); |
1231 | 0 | } else if (parsed.len && strchr(parsed.fname, '%')) /* filename with "%%" but no "%nnd" */ |
1232 | 0 | gs_snprintf(pfname, len, parsed.fname); |
1233 | 0 | else |
1234 | 0 | pfname[0] = 0; /* 0 to use "fname", not "pfname" */ |
1235 | 0 | if (pfname[0]) { |
1236 | 0 | parsed.fname = pfname; |
1237 | 0 | parsed.len = strlen(parsed.fname); |
1238 | 0 | } |
1239 | 0 | if (parsed.iodev) |
1240 | 0 | code = parsed.iodev->procs.delete_file((gx_io_device *)(parsed.iodev), (const char *)parsed.fname); |
1241 | 0 | else |
1242 | 0 | code = gs_note_error(gs_error_invalidfileaccess); |
1243 | |
|
1244 | 0 | done: |
1245 | 0 | if (pfname != NULL) |
1246 | 0 | gs_free_object(dev->memory, pfname, "gx_device_delete_output_file(pfname)"); |
1247 | |
|
1248 | 0 | return(code); |
1249 | 0 | } |
1250 | | |
1251 | | static int |
1252 | | noclose(FILE *f) |
1253 | 0 | { |
1254 | 0 | return 0; |
1255 | 0 | } |
1256 | | |
1257 | | /* Open the output file for a device. */ |
1258 | | int |
1259 | | gx_device_open_output_file(const gx_device * dev, char *fname, |
1260 | | bool binary, bool positionable, gp_file ** pfile) |
1261 | 645 | { |
1262 | 645 | gs_parsed_file_name_t parsed; |
1263 | 645 | const char *fmt; |
1264 | 645 | char *pfname = (char *)gs_alloc_bytes(dev->memory, gp_file_name_sizeof, "gx_device_open_output_file(pfname)"); |
1265 | 645 | int code; |
1266 | | |
1267 | 645 | if (pfname == NULL) { |
1268 | 0 | code = gs_note_error(gs_error_VMerror); |
1269 | 0 | goto done; |
1270 | 0 | } |
1271 | | |
1272 | 645 | if (strlen(fname) == 0) { |
1273 | 0 | code = gs_note_error(gs_error_undefinedfilename); |
1274 | 0 | emprintf1(dev->memory, "Device '%s' requires an output file but no file was specified.\n", dev->dname); |
1275 | 0 | goto done; |
1276 | 0 | } |
1277 | 645 | code = gx_parse_output_file_name(&parsed, &fmt, fname, strlen(fname), dev->memory); |
1278 | 645 | if (code < 0) { |
1279 | 0 | goto done; |
1280 | 0 | } |
1281 | | |
1282 | 645 | if (parsed.iodev && !strcmp(parsed.iodev->dname, "%stdout%")) { |
1283 | 0 | if (parsed.fname) { |
1284 | 0 | code = gs_note_error(gs_error_undefinedfilename); |
1285 | 0 | goto done; |
1286 | 0 | } |
1287 | 0 | *pfile = gp_file_FILE_alloc(dev->memory); |
1288 | 0 | if (*pfile == NULL) { |
1289 | 0 | code = gs_note_error(gs_error_VMerror); |
1290 | 0 | goto done; |
1291 | 0 | } |
1292 | 0 | gp_file_FILE_set(*pfile, dev->memory->gs_lib_ctx->core->fstdout, noclose); |
1293 | | /* Force stdout to binary. */ |
1294 | 0 | code = gp_setmode_binary_impl(dev->memory->gs_lib_ctx->core->fstdout, true); |
1295 | 0 | goto done; |
1296 | 645 | } else if (parsed.iodev && !strcmp(parsed.iodev->dname, "%pipe%")) { |
1297 | 0 | positionable = false; |
1298 | 0 | } |
1299 | 645 | if (fmt) { /* filename includes "%nnd" */ |
1300 | 0 | long count1 = dev->PageCount + 1; |
1301 | |
|
1302 | 0 | while (*fmt != 'l' && *fmt != '%') |
1303 | 0 | --fmt; |
1304 | 0 | if (*fmt == 'l') |
1305 | 0 | gs_snprintf(pfname, gp_file_name_sizeof, parsed.fname, count1); |
1306 | 0 | else |
1307 | 0 | gs_snprintf(pfname, gp_file_name_sizeof, parsed.fname, (int)count1); |
1308 | 645 | } else if (parsed.len && strchr(parsed.fname, '%')) /* filename with "%%" but no "%nnd" */ |
1309 | 0 | gs_snprintf(pfname, gp_file_name_sizeof, parsed.fname); |
1310 | 645 | else |
1311 | 645 | pfname[0] = 0; /* 0 to use "fname", not "pfname" */ |
1312 | 645 | if (pfname[0]) { |
1313 | 0 | parsed.fname = pfname; |
1314 | 0 | parsed.len = strlen(parsed.fname); |
1315 | 0 | } |
1316 | 645 | if (parsed.iodev && |
1317 | 645 | (positionable || parsed.iodev != iodev_default(dev->memory))) { |
1318 | 0 | char fmode[4]; |
1319 | |
|
1320 | 0 | if (!parsed.fname) { |
1321 | 0 | code = gs_note_error(gs_error_undefinedfilename); |
1322 | 0 | goto done; |
1323 | 0 | } |
1324 | 0 | strcpy(fmode, gp_fmode_wb); |
1325 | 0 | if (positionable) |
1326 | 0 | strcat(fmode, "+"); |
1327 | 0 | code = parsed.iodev->procs.gp_fopen(parsed.iodev, parsed.fname, fmode, |
1328 | 0 | pfile, NULL, 0, dev->memory); |
1329 | 0 | if (code) |
1330 | 0 | emprintf1(dev->memory, |
1331 | 0 | "**** Could not open the file %s .\n", |
1332 | 0 | parsed.fname); |
1333 | 645 | } else { |
1334 | 645 | *pfile = gp_open_printer(dev->memory, (pfname[0] ? pfname : fname), binary); |
1335 | 645 | if (!(*pfile)) { |
1336 | 0 | emprintf1(dev->memory, "**** Could not open the file '%s'.\n", (pfname[0] ? pfname : fname)); |
1337 | |
|
1338 | 0 | code = gs_note_error(gs_error_invalidfileaccess); |
1339 | 0 | } |
1340 | 645 | } |
1341 | | |
1342 | 645 | done: |
1343 | 645 | if (pfname != NULL) |
1344 | 645 | gs_free_object(dev->memory, pfname, "gx_device_open_output_file(pfname)"); |
1345 | | |
1346 | 645 | return(code); |
1347 | 645 | } |
1348 | | |
1349 | | /* Close the output file for a device. */ |
1350 | | int |
1351 | | gx_device_close_output_file(const gx_device * dev, const char *fname, |
1352 | | gp_file *file) |
1353 | 645 | { |
1354 | 645 | gs_parsed_file_name_t parsed; |
1355 | 645 | const char *fmt; |
1356 | 645 | int code = gx_parse_output_file_name(&parsed, &fmt, fname, strlen(fname), |
1357 | 645 | dev->memory); |
1358 | | |
1359 | 645 | if (code < 0) |
1360 | 0 | return code; |
1361 | 645 | if (parsed.iodev) { |
1362 | 645 | if (!strcmp(parsed.iodev->dname, "%stdout%")) |
1363 | 0 | return 0; |
1364 | | /* NOTE: fname is unsubstituted if the name has any %nnd formats. */ |
1365 | 645 | if (parsed.iodev != iodev_default(dev->memory)) |
1366 | 0 | return parsed.iodev->procs.fclose(parsed.iodev, file); |
1367 | 645 | } |
1368 | 645 | gp_close_printer(file, (parsed.fname ? parsed.fname : fname)); |
1369 | 645 | return 0; |
1370 | 645 | } |
1371 | | |
1372 | | bool gx_color_info_equal(const gx_device_color_info * p1, const gx_device_color_info * p2) |
1373 | 0 | { |
1374 | 0 | if (p1->anti_alias.graphics_bits != p2->anti_alias.graphics_bits) |
1375 | 0 | return false; |
1376 | 0 | if (p1->anti_alias.text_bits != p2->anti_alias.text_bits) |
1377 | 0 | return false; |
1378 | 0 | if (p1->black_component != p2->black_component) |
1379 | 0 | return false; |
1380 | 0 | if (strcmp(p1->cm_name, p2->cm_name) != 0) |
1381 | 0 | return false; |
1382 | 0 | if (p1->depth != p2->depth) |
1383 | 0 | return false; |
1384 | 0 | if (p1->dither_colors != p2->dither_colors) |
1385 | 0 | return false; |
1386 | 0 | if (p1->dither_grays != p2->dither_grays) |
1387 | 0 | return false; |
1388 | 0 | if (p1->gray_index != p2->gray_index) |
1389 | 0 | return false; |
1390 | 0 | if (p1->max_color != p2->max_color) |
1391 | 0 | return false; |
1392 | 0 | if (p1->max_components != p2->max_components) |
1393 | 0 | return false; |
1394 | 0 | if (p1->opmsupported != p2->opmsupported) |
1395 | 0 | return false; |
1396 | 0 | if (p1->polarity != p2->polarity) |
1397 | 0 | return false; |
1398 | 0 | if (p1->process_comps != p2->process_comps) |
1399 | 0 | return false; |
1400 | 0 | if (p1->separable_and_linear != p2->separable_and_linear) |
1401 | 0 | return false; |
1402 | 0 | if (p1->use_antidropout_downscaler != p2->use_antidropout_downscaler) |
1403 | 0 | return false; |
1404 | 0 | return true; |
1405 | 0 | } |
1406 | | |
1407 | | int gx_callout(gx_device *dev, int id, int size, void *data) |
1408 | 0 | { |
1409 | 0 | return gs_lib_ctx_callout(dev->memory, dev->dname, |
1410 | 0 | id, size, data); |
1411 | 0 | } |
1412 | | |
1413 | | /* compare two space_params, we can't do this with memcmp since there is padding in the structure */ |
1414 | | int |
1415 | | gdev_space_params_cmp(const gdev_space_params sp1, |
1416 | 7.45k | const gdev_space_params sp2) { |
1417 | 7.45k | if (sp1.MaxBitmap != sp2.MaxBitmap) |
1418 | 0 | return(1); |
1419 | 7.45k | if (sp1.BufferSpace != sp2.BufferSpace) |
1420 | 0 | return(1); |
1421 | 7.45k | if (sp1.band.BandWidth != sp2.band.BandWidth) |
1422 | 0 | return(1); |
1423 | 7.45k | if (sp1.band.BandHeight != sp2.band.BandHeight) |
1424 | 0 | return(1); |
1425 | 7.45k | if (sp1.band.BandBufferSpace != sp2.band.BandBufferSpace) |
1426 | 0 | return(1); |
1427 | 7.45k | if (sp1.band.tile_cache_size != sp2.band.tile_cache_size) |
1428 | 0 | return(1); |
1429 | 7.45k | if (sp1.params_are_read_only != sp2.params_are_read_only) |
1430 | 0 | return(1); |
1431 | 7.45k | if (sp1.banding_type != sp2.banding_type) |
1432 | 0 | return(1); |
1433 | | |
1434 | 7.45k | return(0); |
1435 | 7.45k | } |
1436 | | |
1437 | | static void |
1438 | | release_nts_lock(gx_device *dev) |
1439 | 0 | { |
1440 | 0 | (void)gs_lib_ctx_nts_adjust(dev->memory, -1); |
1441 | 0 | } |
1442 | | |
1443 | | int gx_init_non_threadsafe_device(gx_device *dev) |
1444 | 0 | { |
1445 | 0 | int code; |
1446 | |
|
1447 | 0 | if (dev == NULL || dev->finalize != NULL) |
1448 | 0 | return gs_error_unknownerror; |
1449 | | |
1450 | 0 | code = gs_lib_ctx_nts_adjust(dev->memory, 1); |
1451 | 0 | if (code < 0) |
1452 | 0 | return code; |
1453 | | |
1454 | 0 | dev->finalize = release_nts_lock; |
1455 | |
|
1456 | 0 | return 0; |
1457 | 0 | } |