OLD | NEW |
---|---|
1 // Copyright 2011 Google Inc. All Rights Reserved. | 1 // Copyright 2011 Google Inc. All Rights Reserved. |
2 // | 2 // |
3 // This code is licensed under the same terms as WebM: | 3 // This code is licensed under the same terms as WebM: |
4 // Software License Agreement: http://www.webmproject.org/license/software/ | 4 // Software License Agreement: http://www.webmproject.org/license/software/ |
5 // Additional IP Rights Grant: http://www.webmproject.org/license/additional/ | 5 // Additional IP Rights Grant: http://www.webmproject.org/license/additional/ |
6 // ----------------------------------------------------------------------------- | 6 // ----------------------------------------------------------------------------- |
7 // | 7 // |
8 // functions for sample output. | 8 // functions for sample output. |
9 // | 9 // |
10 // Author: Skal (pascal.massimino@gmail.com) | 10 // Author: Skal (pascal.massimino@gmail.com) |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
104 int num_lines_out = io->mb_h; // a priori guess | 104 int num_lines_out = io->mb_h; // a priori guess |
105 const WebPRGBABuffer* const buf = &p->output->u.RGBA; | 105 const WebPRGBABuffer* const buf = &p->output->u.RGBA; |
106 uint8_t* dst = buf->rgba + io->mb_y * buf->stride; | 106 uint8_t* dst = buf->rgba + io->mb_y * buf->stride; |
107 WebPUpsampleLinePairFunc upsample = WebPUpsamplers[p->output->colorspace]; | 107 WebPUpsampleLinePairFunc upsample = WebPUpsamplers[p->output->colorspace]; |
108 const uint8_t* cur_y = io->y; | 108 const uint8_t* cur_y = io->y; |
109 const uint8_t* cur_u = io->u; | 109 const uint8_t* cur_u = io->u; |
110 const uint8_t* cur_v = io->v; | 110 const uint8_t* cur_v = io->v; |
111 const uint8_t* top_u = p->tmp_u; | 111 const uint8_t* top_u = p->tmp_u; |
112 const uint8_t* top_v = p->tmp_v; | 112 const uint8_t* top_v = p->tmp_v; |
113 int y = io->mb_y; | 113 int y = io->mb_y; |
114 int y_end = io->mb_y + io->mb_h; | 114 const int y_end = io->mb_y + io->mb_h; |
115 const int mb_w = io->mb_w; | 115 const int mb_w = io->mb_w; |
116 const int uv_w = (mb_w + 1) / 2; | 116 const int uv_w = (mb_w + 1) / 2; |
117 | 117 |
118 if (y == 0) { | 118 if (y == 0) { |
119 // First line is special cased. We mirror the u/v samples at boundary. | 119 // First line is special cased. We mirror the u/v samples at boundary. |
120 upsample(NULL, cur_y, cur_u, cur_v, cur_u, cur_v, NULL, dst, mb_w); | 120 upsample(NULL, cur_y, cur_u, cur_v, cur_u, cur_v, NULL, dst, mb_w); |
121 } else { | 121 } else { |
122 // We can finish the left-over line from previous call. | 122 // We can finish the left-over line from previous call. |
123 upsample(p->tmp_y, cur_y, top_u, top_v, cur_u, cur_v, | 123 upsample(p->tmp_y, cur_y, top_u, top_v, cur_u, cur_v, |
124 dst - buf->stride, dst, mb_w); | 124 dst - buf->stride, dst, mb_w); |
(...skipping 18 matching lines...) Expand all Loading... | |
143 memcpy(p->tmp_y, cur_y, mb_w * sizeof(*p->tmp_y)); | 143 memcpy(p->tmp_y, cur_y, mb_w * sizeof(*p->tmp_y)); |
144 memcpy(p->tmp_u, cur_u, uv_w * sizeof(*p->tmp_u)); | 144 memcpy(p->tmp_u, cur_u, uv_w * sizeof(*p->tmp_u)); |
145 memcpy(p->tmp_v, cur_v, uv_w * sizeof(*p->tmp_v)); | 145 memcpy(p->tmp_v, cur_v, uv_w * sizeof(*p->tmp_v)); |
146 // The fancy upsampler leaves a row unfinished behind | 146 // The fancy upsampler leaves a row unfinished behind |
147 // (except for the very last row) | 147 // (except for the very last row) |
148 num_lines_out--; | 148 num_lines_out--; |
149 } else { | 149 } else { |
150 // Process the very last row of even-sized picture | 150 // Process the very last row of even-sized picture |
151 if (!(y_end & 1)) { | 151 if (!(y_end & 1)) { |
152 upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, | 152 upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, |
153 dst + buf->stride, NULL, mb_w); | 153 dst + buf->stride, NULL, mb_w); |
154 } | 154 } |
155 } | 155 } |
156 return num_lines_out; | 156 return num_lines_out; |
157 } | 157 } |
158 | 158 |
159 #endif /* FANCY_UPSAMPLING */ | 159 #endif /* FANCY_UPSAMPLING */ |
160 | 160 |
161 //------------------------------------------------------------------------------ | 161 //------------------------------------------------------------------------------ |
162 | 162 |
163 static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p) { | 163 static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p) { |
(...skipping 13 matching lines...) Expand all Loading... | |
177 } else if (buf->a != NULL) { | 177 } else if (buf->a != NULL) { |
178 // the user requested alpha, but there is none, set it to opaque. | 178 // the user requested alpha, but there is none, set it to opaque. |
179 for (j = 0; j < mb_h; ++j) { | 179 for (j = 0; j < mb_h; ++j) { |
180 memset(dst, 0xff, mb_w * sizeof(*dst)); | 180 memset(dst, 0xff, mb_w * sizeof(*dst)); |
181 dst += buf->a_stride; | 181 dst += buf->a_stride; |
182 } | 182 } |
183 } | 183 } |
184 return 0; | 184 return 0; |
185 } | 185 } |
186 | 186 |
187 static int GetAlphaSourceRow(const VP8Io* const io, | |
188 const uint8_t** alpha, int* const num_rows) { | |
189 int start_y = io->mb_y; | |
190 *num_rows = io->mb_h; | |
191 | |
192 // Compensate for the 1-line delay of the fancy upscaler. | |
193 // This is similar to EmitFancyRGB(). | |
194 if (io->fancy_upsampling) { | |
195 if (start_y == 0) { | |
196 // We don't process the last row yet. It'll be done during the next call. | |
197 --*num_rows; | |
fbarchard
2013/03/22 18:56:44
this may be a tad slower than you hope. If perfor
| |
198 } else { | |
199 --start_y; | |
200 // Fortunately, *alpha data is persistent, so we can go back | |
201 // one row and finish alpha blending, now that the fancy upscaler | |
202 // completed the YUV->RGB interpolation. | |
203 *alpha -= io->width; | |
204 } | |
205 if (io->crop_top + io->mb_y + io->mb_h == io->crop_bottom) { | |
206 // If it's the very last call, we process all the remaining rows! | |
207 *num_rows = io->crop_bottom - io->crop_top - start_y; | |
208 } | |
209 } | |
210 return start_y; | |
211 } | |
212 | |
187 static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) { | 213 static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) { |
188 const uint8_t* alpha = io->a; | 214 const uint8_t* alpha = io->a; |
189 if (alpha != NULL) { | 215 if (alpha != NULL) { |
190 const int mb_w = io->mb_w; | 216 const int mb_w = io->mb_w; |
191 const int mb_h = io->mb_h; | |
192 int i, j; | |
193 const WEBP_CSP_MODE colorspace = p->output->colorspace; | 217 const WEBP_CSP_MODE colorspace = p->output->colorspace; |
194 const int alpha_first = | 218 const int alpha_first = |
195 (colorspace == MODE_ARGB || colorspace == MODE_Argb); | 219 (colorspace == MODE_ARGB || colorspace == MODE_Argb); |
196 const WebPRGBABuffer* const buf = &p->output->u.RGBA; | 220 const WebPRGBABuffer* const buf = &p->output->u.RGBA; |
197 int start_y = io->mb_y; | 221 int num_rows; |
198 int num_rows = mb_h; | 222 const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows); |
223 uint8_t* const base_rgba = buf->rgba + start_y * buf->stride; | |
224 uint8_t* dst = base_rgba + (alpha_first ? 0 : 3); | |
fbarchard
2013/03/22 18:56:44
this is a little scarey, but works
argb, abgr, bgr
| |
225 uint32_t alpha_mask = 0xff; | |
226 int i, j; | |
199 | 227 |
200 // We compensate for the 1-line delay of fancy upscaler. | 228 for (j = 0; j < num_rows; ++j) { |
201 // This is similar to EmitFancyRGB(). | 229 for (i = 0; i < mb_w; ++i) { |
202 if (io->fancy_upsampling) { | 230 const uint32_t alpha_value = alpha[i]; |
fbarchard
2013/03/22 18:56:44
this code would lend itself to simd.
but as C, it'
| |
203 if (start_y == 0) { | 231 dst[4 * i] = alpha_value; |
204 // We don't process the last row yet. It'll be done during next call. | 232 alpha_mask &= alpha_value; |
205 --num_rows; | |
206 } else { | |
207 --start_y; | |
208 // Fortunately, *alpha data is persistent, so we can go back | |
209 // one row and finish alpha blending, now that the fancy upscaler | |
210 // completed the YUV->RGB interpolation. | |
211 alpha -= io->width; | |
212 } | 233 } |
213 if (io->crop_top + io->mb_y + mb_h == io->crop_bottom) { | 234 alpha += io->width; |
214 // If it's the very last call, we process all the remaing rows! | 235 dst += buf->stride; |
215 num_rows = io->crop_bottom - io->crop_top - start_y; | |
216 } | |
217 } | 236 } |
218 { | 237 // alpha_mask is < 0xff if there's non-trivial alpha to premultiply with. |
219 uint8_t* const base_rgba = buf->rgba + start_y * buf->stride; | 238 if (alpha_mask != 0xff && WebPIsPremultipliedMode(colorspace)) { |
220 uint8_t* dst = base_rgba + (alpha_first ? 0 : 3); | 239 WebPApplyAlphaMultiply(base_rgba, alpha_first, |
221 for (j = 0; j < num_rows; ++j) { | 240 mb_w, num_rows, buf->stride); |
222 for (i = 0; i < mb_w; ++i) dst[4 * i] = alpha[i]; | |
223 alpha += io->width; | |
224 dst += buf->stride; | |
225 } | |
226 if (WebPIsPremultipliedMode(colorspace)) { | |
227 WebPApplyAlphaMultiply(base_rgba, alpha_first, | |
228 mb_w, num_rows, buf->stride); | |
229 } | |
230 } | 241 } |
231 } | 242 } |
232 return 0; | 243 return 0; |
233 } | 244 } |
234 | 245 |
235 static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p) { | 246 static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p) { |
236 const uint8_t* alpha = io->a; | 247 const uint8_t* alpha = io->a; |
237 if (alpha != NULL) { | 248 if (alpha != NULL) { |
238 const int mb_w = io->mb_w; | 249 const int mb_w = io->mb_w; |
239 const int mb_h = io->mb_h; | 250 const WEBP_CSP_MODE colorspace = p->output->colorspace; |
251 const WebPRGBABuffer* const buf = &p->output->u.RGBA; | |
252 int num_rows; | |
253 const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows); | |
254 uint8_t* const base_rgba = buf->rgba + start_y * buf->stride; | |
255 uint8_t* alpha_dst = base_rgba + 1; | |
256 uint32_t alpha_mask = 0x0f; | |
240 int i, j; | 257 int i, j; |
241 const WebPRGBABuffer* const buf = &p->output->u.RGBA; | 258 |
242 uint8_t* const base_rgba = buf->rgba + io->mb_y * buf->stride; | 259 for (j = 0; j < num_rows; ++j) { |
243 uint8_t* alpha_dst = base_rgba + 1; | |
244 for (j = 0; j < mb_h; ++j) { | |
245 for (i = 0; i < mb_w; ++i) { | 260 for (i = 0; i < mb_w; ++i) { |
fbarchard
2013/03/22 18:56:44
ditto. unrolling to 2 at a time will help
| |
246 // Fill in the alpha value (converted to 4 bits). | 261 // Fill in the alpha value (converted to 4 bits). |
247 const uint32_t alpha_val = VP8Clip4Bits(alpha[i]); | 262 const uint32_t alpha_value = alpha[i] >> 4; |
248 alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_val; | 263 alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value; |
264 alpha_mask &= alpha_value; | |
249 } | 265 } |
250 alpha += io->width; | 266 alpha += io->width; |
251 alpha_dst += buf->stride; | 267 alpha_dst += buf->stride; |
252 } | 268 } |
253 if (p->output->colorspace == MODE_rgbA_4444) { | 269 if (alpha_mask != 0x0f && WebPIsPremultipliedMode(colorspace)) { |
254 WebPApplyAlphaMultiply4444(base_rgba, mb_w, mb_h, buf->stride); | 270 WebPApplyAlphaMultiply4444(base_rgba, mb_w, num_rows, buf->stride); |
255 } | 271 } |
256 } | 272 } |
257 return 0; | 273 return 0; |
258 } | 274 } |
259 | 275 |
260 //------------------------------------------------------------------------------ | 276 //------------------------------------------------------------------------------ |
261 // YUV rescaling (no final RGB conversion needed) | 277 // YUV rescaling (no final RGB conversion needed) |
262 | 278 |
263 static int Rescale(const uint8_t* src, int src_stride, | 279 static int Rescale(const uint8_t* src, int src_stride, |
264 int new_lines, WebPRescaler* const wrk) { | 280 int new_lines, WebPRescaler* const wrk) { |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
389 | 405 |
390 static int ExportAlpha(WebPDecParams* const p, int y_pos) { | 406 static int ExportAlpha(WebPDecParams* const p, int y_pos) { |
391 const WebPRGBABuffer* const buf = &p->output->u.RGBA; | 407 const WebPRGBABuffer* const buf = &p->output->u.RGBA; |
392 uint8_t* const base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride; | 408 uint8_t* const base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride; |
393 const WEBP_CSP_MODE colorspace = p->output->colorspace; | 409 const WEBP_CSP_MODE colorspace = p->output->colorspace; |
394 const int alpha_first = | 410 const int alpha_first = |
395 (colorspace == MODE_ARGB || colorspace == MODE_Argb); | 411 (colorspace == MODE_ARGB || colorspace == MODE_Argb); |
396 uint8_t* dst = base_rgba + (alpha_first ? 0 : 3); | 412 uint8_t* dst = base_rgba + (alpha_first ? 0 : 3); |
397 int num_lines_out = 0; | 413 int num_lines_out = 0; |
398 const int is_premult_alpha = WebPIsPremultipliedMode(colorspace); | 414 const int is_premult_alpha = WebPIsPremultipliedMode(colorspace); |
415 uint32_t alpha_mask = 0xff; | |
399 const int width = p->scaler_a.dst_width; | 416 const int width = p->scaler_a.dst_width; |
400 | 417 |
401 while (WebPRescalerHasPendingOutput(&p->scaler_a)) { | 418 while (WebPRescalerHasPendingOutput(&p->scaler_a)) { |
402 int i; | 419 int i; |
403 assert(p->last_y + y_pos + num_lines_out < p->output->height); | 420 assert(p->last_y + y_pos + num_lines_out < p->output->height); |
404 WebPRescalerExportRow(&p->scaler_a); | 421 WebPRescalerExportRow(&p->scaler_a); |
405 for (i = 0; i < width; ++i) dst[4 * i] = p->scaler_a.dst[i]; | 422 for (i = 0; i < width; ++i) { |
423 const uint32_t alpha_value = p->scaler_a.dst[i]; | |
424 dst[4 * i] = alpha_value; | |
425 alpha_mask &= alpha_value; | |
426 } | |
406 dst += buf->stride; | 427 dst += buf->stride; |
407 ++num_lines_out; | 428 ++num_lines_out; |
408 } | 429 } |
409 if (is_premult_alpha) { | 430 if (is_premult_alpha && alpha_mask != 0xff) { |
410 WebPApplyAlphaMultiply(base_rgba, alpha_first, | 431 WebPApplyAlphaMultiply(base_rgba, alpha_first, |
411 width, num_lines_out, buf->stride); | 432 width, num_lines_out, buf->stride); |
412 } | 433 } |
413 return num_lines_out; | 434 return num_lines_out; |
414 } | 435 } |
415 | 436 |
416 static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos) { | 437 static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos) { |
417 const WebPRGBABuffer* const buf = &p->output->u.RGBA; | 438 const WebPRGBABuffer* const buf = &p->output->u.RGBA; |
418 uint8_t* const base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride; | 439 uint8_t* const base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride; |
419 uint8_t* alpha_dst = base_rgba + 1; | 440 uint8_t* alpha_dst = base_rgba + 1; |
420 int num_lines_out = 0; | 441 int num_lines_out = 0; |
421 const WEBP_CSP_MODE colorspace = p->output->colorspace; | 442 const WEBP_CSP_MODE colorspace = p->output->colorspace; |
422 const int width = p->scaler_a.dst_width; | 443 const int width = p->scaler_a.dst_width; |
423 const int is_premult_alpha = WebPIsPremultipliedMode(colorspace); | 444 const int is_premult_alpha = WebPIsPremultipliedMode(colorspace); |
445 uint32_t alpha_mask = 0x0f; | |
424 | 446 |
425 while (WebPRescalerHasPendingOutput(&p->scaler_a)) { | 447 while (WebPRescalerHasPendingOutput(&p->scaler_a)) { |
426 int i; | 448 int i; |
427 assert(p->last_y + y_pos + num_lines_out < p->output->height); | 449 assert(p->last_y + y_pos + num_lines_out < p->output->height); |
428 WebPRescalerExportRow(&p->scaler_a); | 450 WebPRescalerExportRow(&p->scaler_a); |
429 for (i = 0; i < width; ++i) { | 451 for (i = 0; i < width; ++i) { |
430 // Fill in the alpha value (converted to 4 bits). | 452 // Fill in the alpha value (converted to 4 bits). |
431 const uint32_t alpha_val = VP8Clip4Bits(p->scaler_a.dst[i]); | 453 const uint32_t alpha_value = p->scaler_a.dst[i] >> 4; |
432 alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_val; | 454 alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value; |
455 alpha_mask &= alpha_value; | |
433 } | 456 } |
434 alpha_dst += buf->stride; | 457 alpha_dst += buf->stride; |
435 ++num_lines_out; | 458 ++num_lines_out; |
436 } | 459 } |
437 if (is_premult_alpha) { | 460 if (is_premult_alpha && alpha_mask != 0x0f) { |
438 WebPApplyAlphaMultiply4444(base_rgba, width, num_lines_out, buf->stride); | 461 WebPApplyAlphaMultiply4444(base_rgba, width, num_lines_out, buf->stride); |
439 } | 462 } |
440 return num_lines_out; | 463 return num_lines_out; |
441 } | 464 } |
442 | 465 |
443 static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p) { | 466 static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p) { |
444 if (io->a != NULL) { | 467 if (io->a != NULL) { |
445 WebPRescaler* const scaler = &p->scaler_a; | 468 WebPRescaler* const scaler = &p->scaler_a; |
446 int j = 0; | 469 int j = 0; |
447 int pos = 0; | 470 int pos = 0; |
(...skipping 16 matching lines...) Expand all Loading... | |
464 int32_t* work; // rescalers work area | 487 int32_t* work; // rescalers work area |
465 uint8_t* tmp; // tmp storage for scaled YUV444 samples before RGB conversion | 488 uint8_t* tmp; // tmp storage for scaled YUV444 samples before RGB conversion |
466 size_t tmp_size1, tmp_size2; | 489 size_t tmp_size1, tmp_size2; |
467 | 490 |
468 tmp_size1 = 3 * work_size; | 491 tmp_size1 = 3 * work_size; |
469 tmp_size2 = 3 * out_width; | 492 tmp_size2 = 3 * out_width; |
470 if (has_alpha) { | 493 if (has_alpha) { |
471 tmp_size1 += work_size; | 494 tmp_size1 += work_size; |
472 tmp_size2 += out_width; | 495 tmp_size2 += out_width; |
473 } | 496 } |
474 p->memory = | 497 p->memory = calloc(1, tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp)); |
475 calloc(1, tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp)); | |
476 if (p->memory == NULL) { | 498 if (p->memory == NULL) { |
477 return 0; // memory error | 499 return 0; // memory error |
478 } | 500 } |
479 work = (int32_t*)p->memory; | 501 work = (int32_t*)p->memory; |
480 tmp = (uint8_t*)(work + tmp_size1); | 502 tmp = (uint8_t*)(work + tmp_size1); |
481 WebPRescalerInit(&p->scaler_y, io->mb_w, io->mb_h, | 503 WebPRescalerInit(&p->scaler_y, io->mb_w, io->mb_h, |
482 tmp + 0 * out_width, out_width, out_height, 0, 1, | 504 tmp + 0 * out_width, out_width, out_height, 0, 1, |
483 io->mb_w, out_width, io->mb_h, out_height, | 505 io->mb_w, out_width, io->mb_h, out_height, |
484 work + 0 * work_size); | 506 work + 0 * work_size); |
485 WebPRescalerInit(&p->scaler_u, uv_in_width, uv_in_height, | 507 WebPRescalerInit(&p->scaler_u, uv_in_width, uv_in_height, |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
562 | 584 |
563 if (is_rgb) { | 585 if (is_rgb) { |
564 VP8YUVInit(); | 586 VP8YUVInit(); |
565 } | 587 } |
566 return 1; | 588 return 1; |
567 } | 589 } |
568 | 590 |
569 //------------------------------------------------------------------------------ | 591 //------------------------------------------------------------------------------ |
570 | 592 |
571 static int CustomPut(const VP8Io* io) { | 593 static int CustomPut(const VP8Io* io) { |
572 WebPDecParams* p = (WebPDecParams*)io->opaque; | 594 WebPDecParams* const p = (WebPDecParams*)io->opaque; |
573 const int mb_w = io->mb_w; | 595 const int mb_w = io->mb_w; |
574 const int mb_h = io->mb_h; | 596 const int mb_h = io->mb_h; |
575 int num_lines_out; | 597 int num_lines_out; |
576 assert(!(io->mb_y & 1)); | 598 assert(!(io->mb_y & 1)); |
577 | 599 |
578 if (mb_w <= 0 || mb_h <= 0) { | 600 if (mb_w <= 0 || mb_h <= 0) { |
579 return 0; | 601 return 0; |
580 } | 602 } |
581 num_lines_out = p->emit(io, p); | 603 num_lines_out = p->emit(io, p); |
582 if (p->emit_alpha) { | 604 if (p->emit_alpha) { |
(...skipping 19 matching lines...) Expand all Loading... | |
602 io->setup = CustomSetup; | 624 io->setup = CustomSetup; |
603 io->teardown = CustomTeardown; | 625 io->teardown = CustomTeardown; |
604 io->opaque = params; | 626 io->opaque = params; |
605 } | 627 } |
606 | 628 |
607 //------------------------------------------------------------------------------ | 629 //------------------------------------------------------------------------------ |
608 | 630 |
609 #if defined(__cplusplus) || defined(c_plusplus) | 631 #if defined(__cplusplus) || defined(c_plusplus) |
610 } // extern "C" | 632 } // extern "C" |
611 #endif | 633 #endif |
OLD | NEW |