| OLD | NEW |
| 1 // Copyright 2011 Google Inc. All Rights Reserved. | 1 // Copyright 2011 Google Inc. All Rights Reserved. |
| 2 // | 2 // |
| 3 // Use of this source code is governed by a BSD-style license | 3 // Use of this source code is governed by a BSD-style license |
| 4 // that can be found in the COPYING file in the root of the source | 4 // that can be found in the COPYING file in the root of the source |
| 5 // tree. An additional intellectual property rights grant can be found | 5 // tree. An additional intellectual property rights grant can be found |
| 6 // in the file PATENTS. All contributing project authors may | 6 // in the file PATENTS. All contributing project authors may |
| 7 // be found in the AUTHORS file in the root of the source tree. | 7 // be found in the AUTHORS file in the root of the source tree. |
| 8 // ----------------------------------------------------------------------------- | 8 // ----------------------------------------------------------------------------- |
| 9 // | 9 // |
| 10 // VP8Iterator: block iterator | 10 // VP8Iterator: block iterator |
| 11 // | 11 // |
| 12 // Author: Skal (pascal.massimino@gmail.com) | 12 // Author: Skal (pascal.massimino@gmail.com) |
| 13 | 13 |
| 14 #include <string.h> | 14 #include <string.h> |
| 15 | 15 |
| 16 #include "./vp8enci.h" | 16 #include "./vp8enci.h" |
| 17 | 17 |
| 18 #if defined(__cplusplus) || defined(c_plusplus) | |
| 19 extern "C" { | |
| 20 #endif | |
| 21 | |
| 22 //------------------------------------------------------------------------------ | 18 //------------------------------------------------------------------------------ |
| 23 // VP8Iterator | 19 // VP8Iterator |
| 24 //------------------------------------------------------------------------------ | 20 //------------------------------------------------------------------------------ |
| 25 | 21 |
| 26 static void InitLeft(VP8EncIterator* const it) { | 22 static void InitLeft(VP8EncIterator* const it) { |
| 27 const VP8Encoder* const enc = it->enc_; | 23 it->y_left_[-1] = it->u_left_[-1] = it->v_left_[-1] = |
| 28 enc->y_left_[-1] = enc->u_left_[-1] = enc->v_left_[-1] = | |
| 29 (it->y_ > 0) ? 129 : 127; | 24 (it->y_ > 0) ? 129 : 127; |
| 30 memset(enc->y_left_, 129, 16); | 25 memset(it->y_left_, 129, 16); |
| 31 memset(enc->u_left_, 129, 8); | 26 memset(it->u_left_, 129, 8); |
| 32 memset(enc->v_left_, 129, 8); | 27 memset(it->v_left_, 129, 8); |
| 33 it->left_nz_[8] = 0; | 28 it->left_nz_[8] = 0; |
| 34 } | 29 } |
| 35 | 30 |
| 36 static void InitTop(VP8EncIterator* const it) { | 31 static void InitTop(VP8EncIterator* const it) { |
| 37 const VP8Encoder* const enc = it->enc_; | 32 const VP8Encoder* const enc = it->enc_; |
| 38 const size_t top_size = enc->mb_w_ * 16; | 33 const size_t top_size = enc->mb_w_ * 16; |
| 39 memset(enc->y_top_, 127, 2 * top_size); | 34 memset(enc->y_top_, 127, 2 * top_size); |
| 40 memset(enc->nz_, 0, enc->mb_w_ * sizeof(*enc->nz_)); | 35 memset(enc->nz_, 0, enc->mb_w_ * sizeof(*enc->nz_)); |
| 41 } | 36 } |
| 42 | 37 |
| 38 void VP8IteratorSetRow(VP8EncIterator* const it, int y) { |
| 39 VP8Encoder* const enc = it->enc_; |
| 40 it->x_ = 0; |
| 41 it->y_ = y; |
| 42 it->bw_ = &enc->parts_[y & (enc->num_parts_ - 1)]; |
| 43 it->preds_ = enc->preds_ + y * 4 * enc->preds_w_; |
| 44 it->nz_ = enc->nz_; |
| 45 it->mb_ = enc->mb_info_ + y * enc->mb_w_; |
| 46 it->y_top_ = enc->y_top_; |
| 47 it->uv_top_ = enc->uv_top_; |
| 48 InitLeft(it); |
| 49 } |
| 50 |
| 43 void VP8IteratorReset(VP8EncIterator* const it) { | 51 void VP8IteratorReset(VP8EncIterator* const it) { |
| 44 VP8Encoder* const enc = it->enc_; | 52 VP8Encoder* const enc = it->enc_; |
| 45 it->x_ = 0; | 53 VP8IteratorSetRow(it, 0); |
| 46 it->y_ = 0; | 54 VP8IteratorSetCountDown(it, enc->mb_w_ * enc->mb_h_); // default |
| 47 it->y_offset_ = 0; | |
| 48 it->uv_offset_ = 0; | |
| 49 it->mb_ = enc->mb_info_; | |
| 50 it->preds_ = enc->preds_; | |
| 51 it->nz_ = enc->nz_; | |
| 52 it->bw_ = &enc->parts_[0]; | |
| 53 it->done_ = enc->mb_w_* enc->mb_h_; | |
| 54 InitTop(it); | 55 InitTop(it); |
| 55 InitLeft(it); | 56 InitLeft(it); |
| 56 memset(it->bit_count_, 0, sizeof(it->bit_count_)); | 57 memset(it->bit_count_, 0, sizeof(it->bit_count_)); |
| 57 it->do_trellis_ = 0; | 58 it->do_trellis_ = 0; |
| 58 } | 59 } |
| 59 | 60 |
| 61 void VP8IteratorSetCountDown(VP8EncIterator* const it, int count_down) { |
| 62 it->count_down_ = it->count_down0_ = count_down; |
| 63 } |
| 64 |
| 65 int VP8IteratorIsDone(const VP8EncIterator* const it) { |
| 66 return (it->count_down_ <= 0); |
| 67 } |
| 68 |
| 60 void VP8IteratorInit(VP8Encoder* const enc, VP8EncIterator* const it) { | 69 void VP8IteratorInit(VP8Encoder* const enc, VP8EncIterator* const it) { |
| 61 it->enc_ = enc; | 70 it->enc_ = enc; |
| 62 it->y_stride_ = enc->pic_->y_stride; | 71 it->y_stride_ = enc->pic_->y_stride; |
| 63 it->uv_stride_ = enc->pic_->uv_stride; | 72 it->uv_stride_ = enc->pic_->uv_stride; |
| 64 // TODO(later): for multithreading, these should be owned by 'it'. | 73 it->yuv_in_ = (uint8_t*)DO_ALIGN(it->yuv_mem_); |
| 65 it->yuv_in_ = enc->yuv_in_; | 74 it->yuv_out_ = it->yuv_in_ + YUV_SIZE; |
| 66 it->yuv_out_ = enc->yuv_out_; | 75 it->yuv_out2_ = it->yuv_out_ + YUV_SIZE; |
| 67 it->yuv_out2_ = enc->yuv_out2_; | 76 it->yuv_p_ = it->yuv_out2_ + YUV_SIZE; |
| 68 it->yuv_p_ = enc->yuv_p_; | |
| 69 it->lf_stats_ = enc->lf_stats_; | 77 it->lf_stats_ = enc->lf_stats_; |
| 70 it->percent0_ = enc->percent_; | 78 it->percent0_ = enc->percent_; |
| 79 it->y_left_ = (uint8_t*)DO_ALIGN(it->yuv_left_mem_ + 1); |
| 80 it->u_left_ = it->y_left_ + 16 + 16; |
| 81 it->v_left_ = it->u_left_ + 16; |
| 71 VP8IteratorReset(it); | 82 VP8IteratorReset(it); |
| 72 } | 83 } |
| 73 | 84 |
| 74 int VP8IteratorProgress(const VP8EncIterator* const it, int delta) { | 85 int VP8IteratorProgress(const VP8EncIterator* const it, int delta) { |
| 75 VP8Encoder* const enc = it->enc_; | 86 VP8Encoder* const enc = it->enc_; |
| 76 if (delta && enc->pic_->progress_hook) { | 87 if (delta && enc->pic_->progress_hook != NULL) { |
| 77 const int percent = (enc->mb_h_ <= 1) | 88 const int done = it->count_down0_ - it->count_down_; |
| 89 const int percent = (it->count_down0_ <= 0) |
| 78 ? it->percent0_ | 90 ? it->percent0_ |
| 79 : it->percent0_ + delta * it->y_ / (enc->mb_h_ - 1); | 91 : it->percent0_ + delta * done / it->count_down0_; |
| 80 return WebPReportProgress(enc->pic_, percent, &enc->percent_); | 92 return WebPReportProgress(enc->pic_, percent, &enc->percent_); |
| 81 } | 93 } |
| 82 return 1; | 94 return 1; |
| 83 } | 95 } |
| 84 | 96 |
| 85 //------------------------------------------------------------------------------ | 97 //------------------------------------------------------------------------------ |
| 86 // Import the source samples into the cache. Takes care of replicating | 98 // Import the source samples into the cache. Takes care of replicating |
| 87 // boundary pixels if necessary. | 99 // boundary pixels if necessary. |
| 88 | 100 |
| 101 static WEBP_INLINE int MinSize(int a, int b) { return (a < b) ? a : b; } |
| 102 |
| 89 static void ImportBlock(const uint8_t* src, int src_stride, | 103 static void ImportBlock(const uint8_t* src, int src_stride, |
| 90 uint8_t* dst, int w, int h, int size) { | 104 uint8_t* dst, int w, int h, int size) { |
| 91 int i; | 105 int i; |
| 92 for (i = 0; i < h; ++i) { | 106 for (i = 0; i < h; ++i) { |
| 93 memcpy(dst, src, w); | 107 memcpy(dst, src, w); |
| 94 if (w < size) { | 108 if (w < size) { |
| 95 memset(dst + w, dst[w - 1], size - w); | 109 memset(dst + w, dst[w - 1], size - w); |
| 96 } | 110 } |
| 97 dst += BPS; | 111 dst += BPS; |
| 98 src += src_stride; | 112 src += src_stride; |
| 99 } | 113 } |
| 100 for (i = h; i < size; ++i) { | 114 for (i = h; i < size; ++i) { |
| 101 memcpy(dst, dst - BPS, size); | 115 memcpy(dst, dst - BPS, size); |
| 102 dst += BPS; | 116 dst += BPS; |
| 103 } | 117 } |
| 104 } | 118 } |
| 105 | 119 |
| 106 void VP8IteratorImport(const VP8EncIterator* const it) { | 120 static void ImportLine(const uint8_t* src, int src_stride, |
| 121 uint8_t* dst, int len, int total_len) { |
| 122 int i; |
| 123 for (i = 0; i < len; ++i, src += src_stride) dst[i] = *src; |
| 124 for (; i < total_len; ++i) dst[i] = dst[len - 1]; |
| 125 } |
| 126 |
| 127 void VP8IteratorImport(VP8EncIterator* const it, uint8_t* tmp_32) { |
| 107 const VP8Encoder* const enc = it->enc_; | 128 const VP8Encoder* const enc = it->enc_; |
| 108 const int x = it->x_, y = it->y_; | 129 const int x = it->x_, y = it->y_; |
| 109 const WebPPicture* const pic = enc->pic_; | 130 const WebPPicture* const pic = enc->pic_; |
| 110 const uint8_t* const ysrc = pic->y + (y * pic->y_stride + x) * 16; | 131 const uint8_t* const ysrc = pic->y + (y * pic->y_stride + x) * 16; |
| 111 const uint8_t* const usrc = pic->u + (y * pic->uv_stride + x) * 8; | 132 const uint8_t* const usrc = pic->u + (y * pic->uv_stride + x) * 8; |
| 112 const uint8_t* const vsrc = pic->v + (y * pic->uv_stride + x) * 8; | 133 const uint8_t* const vsrc = pic->v + (y * pic->uv_stride + x) * 8; |
| 113 uint8_t* const ydst = it->yuv_in_ + Y_OFF; | 134 const int w = MinSize(pic->width - x * 16, 16); |
| 114 uint8_t* const udst = it->yuv_in_ + U_OFF; | 135 const int h = MinSize(pic->height - y * 16, 16); |
| 115 uint8_t* const vdst = it->yuv_in_ + V_OFF; | 136 const int uv_w = (w + 1) >> 1; |
| 116 int w = (pic->width - x * 16); | 137 const int uv_h = (h + 1) >> 1; |
| 117 int h = (pic->height - y * 16); | |
| 118 | 138 |
| 119 if (w > 16) w = 16; | 139 ImportBlock(ysrc, pic->y_stride, it->yuv_in_ + Y_OFF, w, h, 16); |
| 120 if (h > 16) h = 16; | 140 ImportBlock(usrc, pic->uv_stride, it->yuv_in_ + U_OFF, uv_w, uv_h, 8); |
| 141 ImportBlock(vsrc, pic->uv_stride, it->yuv_in_ + V_OFF, uv_w, uv_h, 8); |
| 121 | 142 |
| 122 // Luma plane | 143 if (tmp_32 == NULL) return; |
| 123 ImportBlock(ysrc, pic->y_stride, ydst, w, h, 16); | |
| 124 | 144 |
| 125 { // U/V planes | 145 // Import source (uncompressed) samples into boundary. |
| 126 const int uv_w = (w + 1) >> 1; | 146 if (x == 0) { |
| 127 const int uv_h = (h + 1) >> 1; | 147 InitLeft(it); |
| 128 ImportBlock(usrc, pic->uv_stride, udst, uv_w, uv_h, 8); | 148 } else { |
| 129 ImportBlock(vsrc, pic->uv_stride, vdst, uv_w, uv_h, 8); | 149 if (y == 0) { |
| 150 it->y_left_[-1] = it->u_left_[-1] = it->v_left_[-1] = 127; |
| 151 } else { |
| 152 it->y_left_[-1] = ysrc[- 1 - pic->y_stride]; |
| 153 it->u_left_[-1] = usrc[- 1 - pic->uv_stride]; |
| 154 it->v_left_[-1] = vsrc[- 1 - pic->uv_stride]; |
| 155 } |
| 156 ImportLine(ysrc - 1, pic->y_stride, it->y_left_, h, 16); |
| 157 ImportLine(usrc - 1, pic->uv_stride, it->u_left_, uv_h, 8); |
| 158 ImportLine(vsrc - 1, pic->uv_stride, it->v_left_, uv_h, 8); |
| 159 } |
| 160 |
| 161 it->y_top_ = tmp_32 + 0; |
| 162 it->uv_top_ = tmp_32 + 16; |
| 163 if (y == 0) { |
| 164 memset(tmp_32, 127, 32 * sizeof(*tmp_32)); |
| 165 } else { |
| 166 ImportLine(ysrc - pic->y_stride, 1, tmp_32, w, 16); |
| 167 ImportLine(usrc - pic->uv_stride, 1, tmp_32 + 16, uv_w, 8); |
| 168 ImportLine(vsrc - pic->uv_stride, 1, tmp_32 + 16 + 8, uv_w, 8); |
| 130 } | 169 } |
| 131 } | 170 } |
| 132 | 171 |
| 133 //------------------------------------------------------------------------------ | 172 //------------------------------------------------------------------------------ |
| 134 // Copy back the compressed samples into user space if requested. | 173 // Copy back the compressed samples into user space if requested. |
| 135 | 174 |
| 136 static void ExportBlock(const uint8_t* src, uint8_t* dst, int dst_stride, | 175 static void ExportBlock(const uint8_t* src, uint8_t* dst, int dst_stride, |
| 137 int w, int h) { | 176 int w, int h) { |
| 138 while (h-- > 0) { | 177 while (h-- > 0) { |
| 139 memcpy(dst, src, w); | 178 memcpy(dst, src, w); |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 235 nz |= (left_nz[0] << 3) | (left_nz[1] << 7); | 274 nz |= (left_nz[0] << 3) | (left_nz[1] << 7); |
| 236 nz |= (left_nz[2] << 11); | 275 nz |= (left_nz[2] << 11); |
| 237 nz |= (left_nz[4] << 17) | (left_nz[6] << 21); | 276 nz |= (left_nz[4] << 17) | (left_nz[6] << 21); |
| 238 | 277 |
| 239 *it->nz_ = nz; | 278 *it->nz_ = nz; |
| 240 } | 279 } |
| 241 | 280 |
| 242 #undef BIT | 281 #undef BIT |
| 243 | 282 |
| 244 //------------------------------------------------------------------------------ | 283 //------------------------------------------------------------------------------ |
| 245 // Advance to the next position, doing the bookeeping. | 284 // Advance to the next position, doing the bookkeeping. |
| 246 | 285 |
| 247 int VP8IteratorNext(VP8EncIterator* const it, | 286 void VP8IteratorSaveBoundary(VP8EncIterator* const it) { |
| 248 const uint8_t* const block_to_save) { | |
| 249 VP8Encoder* const enc = it->enc_; | 287 VP8Encoder* const enc = it->enc_; |
| 250 if (block_to_save) { | 288 const int x = it->x_, y = it->y_; |
| 251 const int x = it->x_, y = it->y_; | 289 const uint8_t* const ysrc = it->yuv_out_ + Y_OFF; |
| 252 const uint8_t* const ysrc = block_to_save + Y_OFF; | 290 const uint8_t* const uvsrc = it->yuv_out_ + U_OFF; |
| 253 const uint8_t* const usrc = block_to_save + U_OFF; | 291 if (x < enc->mb_w_ - 1) { // left |
| 254 if (x < enc->mb_w_ - 1) { // left | 292 int i; |
| 255 int i; | 293 for (i = 0; i < 16; ++i) { |
| 256 for (i = 0; i < 16; ++i) { | 294 it->y_left_[i] = ysrc[15 + i * BPS]; |
| 257 enc->y_left_[i] = ysrc[15 + i * BPS]; | |
| 258 } | |
| 259 for (i = 0; i < 8; ++i) { | |
| 260 enc->u_left_[i] = usrc[7 + i * BPS]; | |
| 261 enc->v_left_[i] = usrc[15 + i * BPS]; | |
| 262 } | |
| 263 // top-left (before 'top'!) | |
| 264 enc->y_left_[-1] = enc->y_top_[x * 16 + 15]; | |
| 265 enc->u_left_[-1] = enc->uv_top_[x * 16 + 0 + 7]; | |
| 266 enc->v_left_[-1] = enc->uv_top_[x * 16 + 8 + 7]; | |
| 267 } | 295 } |
| 268 if (y < enc->mb_h_ - 1) { // top | 296 for (i = 0; i < 8; ++i) { |
| 269 memcpy(enc->y_top_ + x * 16, ysrc + 15 * BPS, 16); | 297 it->u_left_[i] = uvsrc[7 + i * BPS]; |
| 270 memcpy(enc->uv_top_ + x * 16, usrc + 7 * BPS, 8 + 8); | 298 it->v_left_[i] = uvsrc[15 + i * BPS]; |
| 271 } | 299 } |
| 300 // top-left (before 'top'!) |
| 301 it->y_left_[-1] = it->y_top_[15]; |
| 302 it->u_left_[-1] = it->uv_top_[0 + 7]; |
| 303 it->v_left_[-1] = it->uv_top_[8 + 7]; |
| 272 } | 304 } |
| 305 if (y < enc->mb_h_ - 1) { // top |
| 306 memcpy(it->y_top_, ysrc + 15 * BPS, 16); |
| 307 memcpy(it->uv_top_, uvsrc + 7 * BPS, 8 + 8); |
| 308 } |
| 309 } |
| 273 | 310 |
| 274 it->mb_++; | 311 int VP8IteratorNext(VP8EncIterator* const it) { |
| 275 it->preds_ += 4; | 312 it->preds_ += 4; |
| 276 it->nz_++; | 313 it->mb_ += 1; |
| 277 it->x_++; | 314 it->nz_ += 1; |
| 278 if (it->x_ == enc->mb_w_) { | 315 it->y_top_ += 16; |
| 279 it->x_ = 0; | 316 it->uv_top_ += 16; |
| 280 it->y_++; | 317 it->x_ += 1; |
| 281 it->bw_ = &enc->parts_[it->y_ & (enc->num_parts_ - 1)]; | 318 if (it->x_ == it->enc_->mb_w_) { |
| 282 it->preds_ = enc->preds_ + it->y_ * 4 * enc->preds_w_; | 319 VP8IteratorSetRow(it, ++it->y_); |
| 283 it->nz_ = enc->nz_; | |
| 284 InitLeft(it); | |
| 285 } | 320 } |
| 286 return (0 < --it->done_); | 321 return (0 < --it->count_down_); |
| 287 } | 322 } |
| 288 | 323 |
| 289 //------------------------------------------------------------------------------ | 324 //------------------------------------------------------------------------------ |
| 290 // Helper function to set mode properties | 325 // Helper function to set mode properties |
| 291 | 326 |
| 292 void VP8SetIntra16Mode(const VP8EncIterator* const it, int mode) { | 327 void VP8SetIntra16Mode(const VP8EncIterator* const it, int mode) { |
| 293 uint8_t* preds = it->preds_; | 328 uint8_t* preds = it->preds_; |
| 294 int y; | 329 int y; |
| 295 for (y = 0; y < 4; ++y) { | 330 for (y = 0; y < 4; ++y) { |
| 296 memset(preds, mode, 4); | 331 memset(preds, mode, 4); |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 363 | 398 |
| 364 void VP8IteratorStartI4(VP8EncIterator* const it) { | 399 void VP8IteratorStartI4(VP8EncIterator* const it) { |
| 365 const VP8Encoder* const enc = it->enc_; | 400 const VP8Encoder* const enc = it->enc_; |
| 366 int i; | 401 int i; |
| 367 | 402 |
| 368 it->i4_ = 0; // first 4x4 sub-block | 403 it->i4_ = 0; // first 4x4 sub-block |
| 369 it->i4_top_ = it->i4_boundary_ + VP8TopLeftI4[0]; | 404 it->i4_top_ = it->i4_boundary_ + VP8TopLeftI4[0]; |
| 370 | 405 |
| 371 // Import the boundary samples | 406 // Import the boundary samples |
| 372 for (i = 0; i < 17; ++i) { // left | 407 for (i = 0; i < 17; ++i) { // left |
| 373 it->i4_boundary_[i] = enc->y_left_[15 - i]; | 408 it->i4_boundary_[i] = it->y_left_[15 - i]; |
| 374 } | 409 } |
| 375 for (i = 0; i < 16; ++i) { // top | 410 for (i = 0; i < 16; ++i) { // top |
| 376 it->i4_boundary_[17 + i] = enc->y_top_[it->x_ * 16 + i]; | 411 it->i4_boundary_[17 + i] = it->y_top_[i]; |
| 377 } | 412 } |
| 378 // top-right samples have a special case on the far right of the picture | 413 // top-right samples have a special case on the far right of the picture |
| 379 if (it->x_ < enc->mb_w_ - 1) { | 414 if (it->x_ < enc->mb_w_ - 1) { |
| 380 for (i = 16; i < 16 + 4; ++i) { | 415 for (i = 16; i < 16 + 4; ++i) { |
| 381 it->i4_boundary_[17 + i] = enc->y_top_[it->x_ * 16 + i]; | 416 it->i4_boundary_[17 + i] = it->y_top_[i]; |
| 382 } | 417 } |
| 383 } else { // else, replicate the last valid pixel four times | 418 } else { // else, replicate the last valid pixel four times |
| 384 for (i = 16; i < 16 + 4; ++i) { | 419 for (i = 16; i < 16 + 4; ++i) { |
| 385 it->i4_boundary_[17 + i] = it->i4_boundary_[17 + 15]; | 420 it->i4_boundary_[17 + i] = it->i4_boundary_[17 + 15]; |
| 386 } | 421 } |
| 387 } | 422 } |
| 388 VP8IteratorNzToBytes(it); // import the non-zero context | 423 VP8IteratorNzToBytes(it); // import the non-zero context |
| 389 } | 424 } |
| 390 | 425 |
| 391 int VP8IteratorRotateI4(VP8EncIterator* const it, | 426 int VP8IteratorRotateI4(VP8EncIterator* const it, |
| (...skipping 20 matching lines...) Expand all Loading... |
| 412 if (it->i4_ == 16) { // we're done | 447 if (it->i4_ == 16) { // we're done |
| 413 return 0; | 448 return 0; |
| 414 } | 449 } |
| 415 | 450 |
| 416 it->i4_top_ = it->i4_boundary_ + VP8TopLeftI4[it->i4_]; | 451 it->i4_top_ = it->i4_boundary_ + VP8TopLeftI4[it->i4_]; |
| 417 return 1; | 452 return 1; |
| 418 } | 453 } |
| 419 | 454 |
| 420 //------------------------------------------------------------------------------ | 455 //------------------------------------------------------------------------------ |
| 421 | 456 |
| 422 #if defined(__cplusplus) || defined(c_plusplus) | |
| 423 } // extern "C" | |
| 424 #endif | |
| OLD | NEW |