/src/ghostpdl/base/gshtx.c
Line | Count | Source |
1 | | /* Copyright (C) 2001-2023 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., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* Stand-alone halftone/transfer function related code */ |
18 | | #include "memory_.h" |
19 | | #include "gx.h" |
20 | | #include "gserrors.h" |
21 | | #include "gsstruct.h" |
22 | | #include "gsutil.h" /* for gs_next_ids */ |
23 | | #include "gxfmap.h" |
24 | | #include "gzstate.h" |
25 | | #include "gzht.h" |
26 | | #include "gshtx.h" /* must come after g*ht.h */ |
27 | | |
28 | | /* |
29 | | * Procedure to free the set of components when a halftone is released. |
30 | | */ |
31 | | static void |
32 | | free_comps( |
33 | | gs_memory_t * pmem, |
34 | | void *pvht, |
35 | | client_name_t cname |
36 | | ) |
37 | 0 | { |
38 | 0 | gs_ht *pht = (gs_ht *) pvht; |
39 | |
|
40 | 0 | gs_free_object(pmem, pht->params.ht_multiple.components, cname); |
41 | 0 | gs_free_object(pmem, pvht, cname); |
42 | 0 | } |
43 | | |
44 | | /* |
45 | | * Stub transfer function, to be applied to components that are not provided |
46 | | * with a transfer function. |
47 | | */ |
48 | | static float |
49 | | null_closure_transfer( |
50 | | double val, |
51 | | const gx_transfer_map * pmap_dummy, /* NOTUSED */ |
52 | | const void *dummy /* NOTUSED */ |
53 | | ) |
54 | 0 | { |
55 | 0 | return val; |
56 | 0 | } |
57 | | |
58 | | /* |
59 | | * Build a gs_ht halftone structure. |
60 | | */ |
61 | | int |
62 | | gs_ht_build( |
63 | | gs_ht ** ppht, |
64 | | uint num_comps, |
65 | | gs_memory_t * pmem |
66 | | ) |
67 | 0 | { |
68 | 0 | gs_ht *pht; |
69 | 0 | gs_ht_component *phtc; |
70 | 0 | int i; |
71 | | |
72 | | /* must have at least one component */ |
73 | 0 | *ppht = 0; |
74 | 0 | if (num_comps == 0) |
75 | 0 | return_error(gs_error_rangecheck); |
76 | | |
77 | | /* allocate the halftone and the array of components */ |
78 | 0 | rc_alloc_struct_1(pht, |
79 | 0 | gs_ht, |
80 | 0 | &st_gs_ht, |
81 | 0 | pmem, |
82 | 0 | return_error(gs_error_VMerror), |
83 | 0 | "gs_ht_build" |
84 | 0 | ); |
85 | 0 | phtc = gs_alloc_struct_array(pmem, |
86 | 0 | num_comps, |
87 | 0 | gs_ht_component, |
88 | 0 | &st_ht_comp_element, |
89 | 0 | "gs_ht_build" |
90 | 0 | ); |
91 | 0 | if (phtc == 0) { |
92 | 0 | gs_free_object(pmem, pht, "gs_ht_build"); |
93 | 0 | return_error(gs_error_VMerror); |
94 | 0 | } |
95 | | /* initialize the halftone */ |
96 | 0 | pht->type = ht_type_multiple; |
97 | 0 | pht->rc.free = free_comps; |
98 | 0 | pht->objtype = HT_OBJTYPE_DEFAULT; |
99 | 0 | pht->params.ht_multiple.components = phtc; |
100 | 0 | pht->params.ht_multiple.num_comp = num_comps; |
101 | |
|
102 | 0 | for (i = 0; i < num_comps; i++) { |
103 | 0 | phtc[i].comp_number = i; |
104 | 0 | phtc[i].cname = 0; |
105 | 0 | phtc[i].type = ht_type_none; |
106 | 0 | } |
107 | |
|
108 | 0 | *ppht = pht; |
109 | |
|
110 | 0 | return 0; |
111 | 0 | } |
112 | | |
113 | | /* |
114 | | * Set a spot-function halftone component in a gs_ht halftone. |
115 | | */ |
116 | | int |
117 | | gs_ht_set_spot_comp( |
118 | | gs_ht * pht, |
119 | | int comp, |
120 | | double freq, |
121 | | double angle, |
122 | | float (*spot_func) (double, double), |
123 | | bool accurate, |
124 | | gs_ht_transfer_proc transfer, |
125 | | const void *client_data |
126 | | ) |
127 | 0 | { |
128 | 0 | gs_ht_component *phtc = &(pht->params.ht_multiple.components[comp]); |
129 | |
|
130 | 0 | if (comp >= pht->params.ht_multiple.num_comp) |
131 | 0 | return_error(gs_error_rangecheck); |
132 | 0 | if (phtc->type != ht_type_none) |
133 | 0 | return_error(gs_error_invalidaccess); |
134 | | |
135 | 0 | phtc->type = ht_type_spot; |
136 | 0 | phtc->params.ht_spot.screen.frequency = freq; |
137 | 0 | phtc->params.ht_spot.screen.angle = angle; |
138 | 0 | phtc->params.ht_spot.screen.spot_function = spot_func; |
139 | 0 | phtc->params.ht_spot.accurate_screens = accurate; |
140 | 0 | phtc->params.ht_spot.transfer = gs_mapped_transfer; |
141 | |
|
142 | 0 | phtc->params.ht_spot.transfer_closure.proc = |
143 | 0 | (transfer == 0 ? null_closure_transfer : transfer); |
144 | 0 | phtc->params.ht_spot.transfer_closure.data = client_data; |
145 | |
|
146 | 0 | return 0; |
147 | 0 | } |
148 | | |
149 | | /* |
150 | | * Set a threshold halftone component in a gs_ht halftone. Note that the |
151 | | * caller is responsible for releasing the threshold data. |
152 | | */ |
153 | | int |
154 | | gs_ht_set_threshold_comp( |
155 | | gs_ht * pht, |
156 | | int comp, |
157 | | int width, |
158 | | int height, |
159 | | const gs_const_string * thresholds, |
160 | | gs_ht_transfer_proc transfer, |
161 | | const void *client_data |
162 | | ) |
163 | 0 | { |
164 | 0 | gs_ht_component *phtc = &(pht->params.ht_multiple.components[comp]); |
165 | |
|
166 | 0 | if (comp >= pht->params.ht_multiple.num_comp) |
167 | 0 | return_error(gs_error_rangecheck); |
168 | 0 | if (phtc->type != ht_type_none) |
169 | 0 | return_error(gs_error_invalidaccess); |
170 | | |
171 | 0 | phtc->type = ht_type_threshold; |
172 | 0 | phtc->params.ht_threshold.width = width; |
173 | 0 | phtc->params.ht_threshold.height = height; |
174 | 0 | phtc->params.ht_threshold.thresholds = *thresholds; |
175 | 0 | phtc->params.ht_threshold.transfer = gs_mapped_transfer; |
176 | |
|
177 | 0 | phtc->params.ht_threshold.transfer_closure.proc = |
178 | 0 | (transfer == 0 ? null_closure_transfer : transfer); |
179 | 0 | phtc->params.ht_threshold.transfer_closure.data = client_data; |
180 | |
|
181 | 0 | return 0; |
182 | 0 | } |
183 | | |
184 | | /* |
185 | | * Increase the reference count of a gs_ht structure by 1. |
186 | | */ |
187 | | void |
188 | | gs_ht_reference( |
189 | | gs_ht * pht |
190 | | ) |
191 | 0 | { |
192 | 0 | rc_increment(pht); |
193 | 0 | } |
194 | | |
195 | | /* |
196 | | * Decrement the reference count of a gs_ht structure by 1. Free the |
197 | | * structure if the reference count reaches 0. |
198 | | */ |
199 | | void |
200 | | gs_ht_release( |
201 | | gs_ht * pht |
202 | | ) |
203 | 0 | { |
204 | 0 | rc_decrement_only(pht, "gs_ht_release"); |
205 | 0 | } |
206 | | |
207 | | /* |
208 | | * Verify that a gs_ht halftone is legitimate. |
209 | | */ |
210 | | static int |
211 | | check_ht( |
212 | | gs_ht * pht |
213 | | ) |
214 | 0 | { |
215 | 0 | int i; |
216 | 0 | int num_comps = pht->params.ht_multiple.num_comp; |
217 | |
|
218 | 0 | if (pht->type != ht_type_multiple) |
219 | 0 | return_error(gs_error_unregistered); |
220 | 0 | for (i = 0; i < num_comps; i++) { |
221 | 0 | gs_ht_component *phtc = &(pht->params.ht_multiple.components[i]); |
222 | 0 | if ((phtc->type != ht_type_spot) && (phtc->type != ht_type_threshold)) |
223 | 0 | return_error(gs_error_unregistered); |
224 | 0 | } |
225 | 0 | return 0; |
226 | 0 | } |
227 | | |
228 | | /* |
229 | | * Load a transfer map from a gs_ht_transfer_proc function. |
230 | | */ |
231 | | static void |
232 | | build_transfer_map( |
233 | | gs_ht_component * phtc, |
234 | | gx_transfer_map * pmap |
235 | | ) |
236 | 0 | { |
237 | 0 | gs_ht_transfer_proc proc; |
238 | 0 | const void *client_info; |
239 | 0 | int i; |
240 | 0 | frac *values = pmap->values; |
241 | |
|
242 | 0 | if (phtc->type == ht_type_spot) { |
243 | 0 | proc = phtc->params.ht_spot.transfer_closure.proc; |
244 | 0 | client_info = phtc->params.ht_spot.transfer_closure.data; |
245 | 0 | } else { |
246 | 0 | proc = phtc->params.ht_threshold.transfer_closure.proc; |
247 | 0 | client_info = phtc->params.ht_threshold.transfer_closure.data; |
248 | 0 | } |
249 | |
|
250 | 0 | for (i = 0; i < transfer_map_size; i++) { |
251 | 0 | float fval = |
252 | 0 | proc(i * (1 / (double)(transfer_map_size - 1)), pmap, client_info); |
253 | |
|
254 | 0 | values[i] = |
255 | 0 | (fval <= 0.0 ? frac_0 : fval >= 1.0 ? frac_1 : |
256 | 0 | float2frac(fval)); |
257 | 0 | } |
258 | 0 | } |
259 | | |
260 | | /* |
261 | | * Allocate the order and transfer maps required by a halftone, and perform |
262 | | * some elementary initialization. This will also build the component index |
263 | | * to order index map. |
264 | | */ |
265 | | static gx_ht_order_component * |
266 | | alloc_ht_order( |
267 | | const gs_ht * pht, |
268 | | gs_memory_t * pmem, |
269 | | byte * comp2order |
270 | | ) |
271 | 0 | { |
272 | 0 | int num_comps = pht->params.ht_multiple.num_comp; |
273 | 0 | gx_ht_order_component *pocs = gs_alloc_struct_array( |
274 | 0 | pmem, |
275 | 0 | pht->params.ht_multiple.num_comp, |
276 | 0 | gx_ht_order_component, |
277 | 0 | &st_ht_order_component_element, |
278 | 0 | "alloc_ht_order" |
279 | 0 | ); |
280 | 0 | int inext = 0; |
281 | 0 | int i; |
282 | |
|
283 | 0 | if (pocs == 0) |
284 | 0 | return 0; |
285 | 0 | pocs->corder.transfer = 0; |
286 | |
|
287 | 0 | for (i = 0; i < num_comps; i++) { |
288 | 0 | gs_ht_component *phtc = &(pht->params.ht_multiple.components[i]); |
289 | 0 | gx_transfer_map *pmap = gs_alloc_struct(pmem, |
290 | 0 | gx_transfer_map, |
291 | 0 | &st_transfer_map, |
292 | 0 | "alloc_ht_order" |
293 | 0 | ); |
294 | |
|
295 | 0 | if (pmap == 0) { |
296 | 0 | int j; |
297 | |
|
298 | 0 | for (j = 0; j < inext; j++) |
299 | 0 | gs_free_object(pmem, pocs[j].corder.transfer, "alloc_ht_order"); |
300 | 0 | gs_free_object(pmem, pocs, "alloc_ht_order"); |
301 | 0 | return 0; |
302 | 0 | } |
303 | 0 | pmap->proc = gs_mapped_transfer; |
304 | 0 | pmap->id = gs_next_ids(pmem, 1); |
305 | 0 | pocs[inext].corder.levels = 0; |
306 | 0 | pocs[inext].corder.bit_data = 0; |
307 | 0 | pocs[inext].corder.cache = 0; |
308 | 0 | pocs[inext].corder.transfer = pmap; |
309 | 0 | pocs[inext].cname = phtc->cname; |
310 | 0 | pocs[inext].comp_number = phtc->comp_number; |
311 | 0 | comp2order[i] = inext++; |
312 | 0 | } |
313 | | |
314 | 0 | return pocs; |
315 | 0 | } |
316 | | |
317 | | /* |
318 | | * Build the halftone order for one component. |
319 | | */ |
320 | | static int |
321 | | build_component( |
322 | | gs_ht_component * phtc, |
323 | | gx_ht_order * porder, |
324 | | gs_gstate * pgs, |
325 | | gs_memory_t * pmem |
326 | | ) |
327 | 0 | { |
328 | 0 | if (phtc->type == ht_type_spot) { |
329 | 0 | gs_screen_enum senum; |
330 | 0 | int code; |
331 | |
|
332 | 0 | code = gx_ht_process_screen_memory(&senum, |
333 | 0 | pgs, |
334 | 0 | &phtc->params.ht_spot.screen, |
335 | 0 | phtc->params.ht_spot.accurate_screens, |
336 | 0 | pmem |
337 | 0 | ); |
338 | 0 | if (code < 0) |
339 | 0 | return code; |
340 | | |
341 | | /* avoid wiping out the transfer structure pointer */ |
342 | 0 | senum.order.transfer = porder->transfer; |
343 | 0 | *porder = senum.order; |
344 | |
|
345 | 0 | } else { /* ht_type_threshold */ |
346 | 0 | int code; |
347 | 0 | gx_transfer_map *transfer = porder->transfer; |
348 | |
|
349 | 0 | porder->params.M = phtc->params.ht_threshold.width; |
350 | 0 | porder->params.N = 0; |
351 | 0 | porder->params.R = 1; |
352 | 0 | porder->params.M1 = phtc->params.ht_threshold.height; |
353 | 0 | porder->params.N1 = 0; |
354 | 0 | porder->params.R1 = 1; |
355 | 0 | code = gx_ht_alloc_threshold_order(porder, |
356 | 0 | phtc->params.ht_threshold.width, |
357 | 0 | phtc->params.ht_threshold.height, |
358 | 0 | 256, |
359 | 0 | pmem |
360 | 0 | ); |
361 | 0 | if (code < 0) |
362 | 0 | return code; |
363 | 0 | gx_ht_construct_threshold_order( |
364 | 0 | porder, |
365 | 0 | phtc->params.ht_threshold.thresholds.data |
366 | 0 | ); |
367 | | /* |
368 | | * gx_ht_construct_threshold_order wipes out transfer map pointer, |
369 | | * restore it here. |
370 | | */ |
371 | 0 | porder->transfer = transfer; |
372 | 0 | } |
373 | | |
374 | 0 | build_transfer_map(phtc, porder->transfer); |
375 | 0 | return 0; |
376 | 0 | } |
377 | | |
378 | | /* |
379 | | * Free an order array and all elements it points to. |
380 | | */ |
381 | | static void |
382 | | free_order_array( |
383 | | gx_ht_order_component * pocs, |
384 | | int num_comps, |
385 | | gs_memory_t * pmem |
386 | | ) |
387 | 0 | { |
388 | 0 | int i; |
389 | |
|
390 | 0 | for (i = 0; i < num_comps; i++) |
391 | 0 | gx_ht_order_release(&(pocs[i].corder), pmem, true); |
392 | 0 | gs_free_object(pmem, pocs, "gs_ht_install"); |
393 | 0 | } |
394 | | |
395 | | /* |
396 | | * Install a gs_ht halftone as the current halftone in the graphic state. |
397 | | */ |
398 | | int |
399 | | gs_ht_install( |
400 | | gs_gstate * pgs, |
401 | | gs_ht * pht |
402 | | ) |
403 | 0 | { |
404 | 0 | int code = 0; |
405 | 0 | gs_memory_t *pmem = pht->rc.memory; |
406 | 0 | gx_device_halftone dev_ht; |
407 | 0 | gx_ht_order_component *pocs; |
408 | 0 | byte comp2order[32]; /* ample component to order map */ |
409 | 0 | int num_comps = pht->params.ht_multiple.num_comp; |
410 | 0 | int i; |
411 | | |
412 | | /* perform so sanity checks (must have one default component) */ |
413 | 0 | if ((code = check_ht(pht)) != 0) |
414 | 0 | return code; |
415 | | |
416 | | /* allocate the halftone order structure and transfer maps */ |
417 | 0 | if ((pocs = alloc_ht_order(pht, pmem, comp2order)) == 0) |
418 | 0 | return_error(gs_error_VMerror); |
419 | | |
420 | | /* build all of the order for each component */ |
421 | 0 | for (i = 0; i < num_comps; i++) { |
422 | 0 | int j = comp2order[i]; |
423 | |
|
424 | 0 | code = build_component(&(pht->params.ht_multiple.components[i]), |
425 | 0 | &(pocs[j].corder), |
426 | 0 | pgs, |
427 | 0 | pmem |
428 | 0 | ); |
429 | |
|
430 | 0 | if ((code >= 0) && (j != 0)) { |
431 | 0 | gx_ht_cache *pcache; |
432 | |
|
433 | 0 | pcache = gx_ht_alloc_cache(pmem, |
434 | 0 | 4, |
435 | 0 | pocs[j].corder.raster * |
436 | 0 | (pocs[j].corder.num_bits / |
437 | 0 | pocs[j].corder.width) * 4 |
438 | 0 | ); |
439 | |
|
440 | 0 | if (pcache == 0) |
441 | 0 | code = gs_note_error(gs_error_VMerror); |
442 | 0 | else { |
443 | 0 | pocs[j].corder.cache = pcache; |
444 | 0 | gx_ht_init_cache(pmem, pcache, &(pocs[j].corder)); |
445 | 0 | } |
446 | 0 | } |
447 | 0 | if (code < 0) |
448 | 0 | break; |
449 | 0 | } |
450 | |
|
451 | 0 | if (code < 0) { |
452 | 0 | free_order_array(pocs, num_comps, pmem); |
453 | 0 | return code; |
454 | 0 | } |
455 | | /* initialize the device halftone structure */ |
456 | 0 | dev_ht.rc.memory = pmem; |
457 | 0 | dev_ht.order = pocs[0].corder; /* Default */ |
458 | 0 | if (num_comps == 1) { |
459 | | /* we have only a Default; we don't need components. */ |
460 | 0 | gs_free_object(pmem, pocs, "gs_ht_install"); |
461 | 0 | dev_ht.components = 0; |
462 | 0 | } else { |
463 | 0 | dev_ht.components = pocs; |
464 | 0 | dev_ht.num_comp = num_comps; |
465 | 0 | } |
466 | | |
467 | | /* at last, actually install the halftone in the graphic state */ |
468 | 0 | if ((code = gx_ht_install(pgs, (gs_halftone *) pht, &dev_ht)) < 0) |
469 | 0 | gx_device_halftone_release(&dev_ht, pmem); |
470 | 0 | return code; |
471 | 0 | } |
472 | | |
473 | | /* ---------------- Mask-defined halftones ---------------- */ |
474 | | |
475 | | /* |
476 | | * Create a halftone order from an array of explicit masks. This is |
477 | | * silly, because the rendering machinery actually wants masks, but doing |
478 | | * it right seems to require too many changes in existing code. |
479 | | */ |
480 | | static int |
481 | | create_mask_bits(const byte * mask1, const byte * mask2, |
482 | | int width, int height, gx_ht_bit * bits) |
483 | 0 | { |
484 | | /* |
485 | | * We do this with the slowest, simplest possible algorithm.... |
486 | | */ |
487 | 0 | int width_bytes = (width + 7) >> 3; |
488 | 0 | int x, y; |
489 | 0 | int count = 0; |
490 | |
|
491 | 0 | for (y = 0; y < height; ++y) |
492 | 0 | for (x = 0; x < width; ++x) { |
493 | 0 | int offset = y * width_bytes + (x >> 3); |
494 | 0 | byte bit_mask = 0x80 >> (x & 7); |
495 | |
|
496 | 0 | if ((mask1[offset] ^ mask2[offset]) & bit_mask) { |
497 | 0 | if (bits) |
498 | 0 | gx_ht_construct_bit(&bits[count], width, y * width + x); |
499 | 0 | ++count; |
500 | 0 | } |
501 | 0 | } |
502 | 0 | return count; |
503 | 0 | } |
504 | | static int |
505 | | create_mask_order(gx_ht_order * porder, gs_gstate * pgs, |
506 | | const gs_client_order_halftone * phcop, |
507 | | gs_memory_t * mem) |
508 | 0 | { |
509 | 0 | int width_bytes = (phcop->width + 7) >> 3; |
510 | 0 | const byte *masks = (const byte *)phcop->client_data; |
511 | 0 | int bytes_per_mask = width_bytes * phcop->height; |
512 | 0 | const byte *prev_mask; |
513 | 0 | int num_levels = phcop->num_levels; |
514 | 0 | int num_bits = 0; |
515 | 0 | int i; |
516 | 0 | int code; |
517 | | |
518 | | /* Do a first pass to compute how many bits entries will be needed. */ |
519 | 0 | for (prev_mask = masks, num_bits = 0, i = 0; |
520 | 0 | i < num_levels - 1; |
521 | 0 | ++i, prev_mask += bytes_per_mask |
522 | 0 | ) |
523 | 0 | num_bits += create_mask_bits(prev_mask, prev_mask + bytes_per_mask, |
524 | 0 | phcop->width, phcop->height, NULL); |
525 | 0 | code = gx_ht_alloc_client_order(porder, phcop->width, phcop->height, |
526 | 0 | num_levels, num_bits, mem); |
527 | 0 | if (code < 0) |
528 | 0 | return code; |
529 | | /* Fill in the bits and levels entries. */ |
530 | 0 | for (prev_mask = masks, num_bits = 0, i = 0; |
531 | 0 | i < num_levels - 1; |
532 | 0 | ++i, prev_mask += bytes_per_mask |
533 | 0 | ) { |
534 | 0 | porder->levels[i] = num_bits; |
535 | 0 | num_bits += create_mask_bits(prev_mask, prev_mask + bytes_per_mask, |
536 | 0 | phcop->width, phcop->height, |
537 | 0 | ((gx_ht_bit *)porder->bit_data) + |
538 | 0 | num_bits); |
539 | 0 | } |
540 | 0 | porder->levels[num_levels - 1] = num_bits; |
541 | 0 | return 0; |
542 | 0 | } |
543 | | |
544 | | /* Define the client-order halftone procedure structure. */ |
545 | | static const gs_client_order_ht_procs_t mask_order_procs = |
546 | | { |
547 | | create_mask_order |
548 | | }; |
549 | | |
550 | | /* |
551 | | * Define a halftone by an explicit set of masks. We translate these |
552 | | * internally into a threshold array, since that's what the halftone |
553 | | * rendering machinery knows how to deal with. |
554 | | */ |
555 | | int |
556 | | gs_ht_set_mask_comp(gs_ht * pht, |
557 | | int component_index, |
558 | | int width, int height, int num_levels, |
559 | | const byte * masks, /* width x height x num_levels bits */ |
560 | | gs_ht_transfer_proc transfer, |
561 | | const void *client_data) |
562 | 0 | { |
563 | 0 | gs_ht_component *phtc = |
564 | 0 | &(pht->params.ht_multiple.components[component_index]); |
565 | |
|
566 | 0 | if (component_index >= pht->params.ht_multiple.num_comp) |
567 | 0 | return_error(gs_error_rangecheck); |
568 | 0 | if (phtc->type != ht_type_none) |
569 | 0 | return_error(gs_error_invalidaccess); |
570 | | |
571 | 0 | phtc->type = ht_type_client_order; |
572 | 0 | phtc->params.client_order.width = width; |
573 | 0 | phtc->params.client_order.height = height; |
574 | 0 | phtc->params.client_order.num_levels = num_levels; |
575 | 0 | phtc->params.client_order.procs = &mask_order_procs; |
576 | 0 | phtc->params.client_order.client_data = masks; |
577 | 0 | phtc->params.client_order.transfer_closure.proc = |
578 | 0 | (transfer == 0 ? null_closure_transfer : transfer); |
579 | 0 | phtc->params.client_order.transfer_closure.data = client_data; |
580 | |
|
581 | 0 | return 0; |
582 | |
|
583 | 0 | } |