1// Copyright 2011 Google Inc. All Rights Reserved.
2//
3// This code is licensed under the same terms as WebM:
4//  Software License Agreement:  http://www.webmproject.org/license/software/
5//  Additional IP Rights Grant:  http://www.webmproject.org/license/additional/
6// -----------------------------------------------------------------------------
7//
8// VP8Iterator: block iterator
9//
10// Author: Skal (pascal.massimino@gmail.com)
11
12#include <string.h>
13
14#include "./vp8enci.h"
15
16#if defined(__cplusplus) || defined(c_plusplus)
17extern "C" {
18#endif
19
20//------------------------------------------------------------------------------
21// VP8Iterator
22//------------------------------------------------------------------------------
23
24static void InitLeft(VP8EncIterator* const it) {
25  const VP8Encoder* const enc = it->enc_;
26  enc->y_left_[-1] = enc->u_left_[-1] = enc->v_left_[-1] =
27      (it->y_ > 0) ? 129 : 127;
28  memset(enc->y_left_, 129, 16);
29  memset(enc->u_left_, 129, 8);
30  memset(enc->v_left_, 129, 8);
31  it->left_nz_[8] = 0;
32}
33
34static void InitTop(VP8EncIterator* const it) {
35  const VP8Encoder* const enc = it->enc_;
36  const size_t top_size = enc->mb_w_ * 16;
37  memset(enc->y_top_, 127, 2 * top_size);
38  memset(enc->nz_, 0, enc->mb_w_ * sizeof(*enc->nz_));
39}
40
41void VP8IteratorReset(VP8EncIterator* const it) {
42  VP8Encoder* const enc = it->enc_;
43  it->x_ = 0;
44  it->y_ = 0;
45  it->y_offset_ = 0;
46  it->uv_offset_ = 0;
47  it->mb_ = enc->mb_info_;
48  it->preds_ = enc->preds_;
49  it->nz_ = enc->nz_;
50  it->bw_ = &enc->parts_[0];
51  it->done_ = enc->mb_w_* enc->mb_h_;
52  InitTop(it);
53  InitLeft(it);
54  memset(it->bit_count_, 0, sizeof(it->bit_count_));
55  it->do_trellis_ = 0;
56}
57
58void VP8IteratorInit(VP8Encoder* const enc, VP8EncIterator* const it) {
59  it->enc_ = enc;
60  it->y_stride_  = enc->pic_->y_stride;
61  it->uv_stride_ = enc->pic_->uv_stride;
62  // TODO(later): for multithreading, these should be owned by 'it'.
63  it->yuv_in_   = enc->yuv_in_;
64  it->yuv_out_  = enc->yuv_out_;
65  it->yuv_out2_ = enc->yuv_out2_;
66  it->yuv_p_    = enc->yuv_p_;
67  it->lf_stats_ = enc->lf_stats_;
68  it->percent0_ = enc->percent_;
69  VP8IteratorReset(it);
70}
71
72int VP8IteratorProgress(const VP8EncIterator* const it, int delta) {
73  VP8Encoder* const enc = it->enc_;
74  if (delta && enc->pic_->progress_hook) {
75    const int percent = (enc->mb_h_ <= 1)
76                      ? it->percent0_
77                      : it->percent0_ + delta * it->y_ / (enc->mb_h_ - 1);
78    return WebPReportProgress(enc->pic_, percent, &enc->percent_);
79  }
80  return 1;
81}
82
83//------------------------------------------------------------------------------
84// Import the source samples into the cache. Takes care of replicating
85// boundary pixels if necessary.
86
87static void ImportBlock(const uint8_t* src, int src_stride,
88                        uint8_t* dst, int w, int h, int size) {
89  int i;
90  for (i = 0; i < h; ++i) {
91    memcpy(dst, src, w);
92    if (w < size) {
93      memset(dst + w, dst[w - 1], size - w);
94    }
95    dst += BPS;
96    src += src_stride;
97  }
98  for (i = h; i < size; ++i) {
99    memcpy(dst, dst - BPS, size);
100    dst += BPS;
101  }
102}
103
104void VP8IteratorImport(const VP8EncIterator* const it) {
105  const VP8Encoder* const enc = it->enc_;
106  const int x = it->x_, y = it->y_;
107  const WebPPicture* const pic = enc->pic_;
108  const uint8_t* const ysrc = pic->y + (y * pic->y_stride + x) * 16;
109  const uint8_t* const usrc = pic->u + (y * pic->uv_stride + x) * 8;
110  const uint8_t* const vsrc = pic->v + (y * pic->uv_stride + x) * 8;
111  uint8_t* const ydst = it->yuv_in_ + Y_OFF;
112  uint8_t* const udst = it->yuv_in_ + U_OFF;
113  uint8_t* const vdst = it->yuv_in_ + V_OFF;
114  int w = (pic->width - x * 16);
115  int h = (pic->height - y * 16);
116
117  if (w > 16) w = 16;
118  if (h > 16) h = 16;
119
120  // Luma plane
121  ImportBlock(ysrc, pic->y_stride, ydst, w, h, 16);
122
123  {   // U/V planes
124    const int uv_w = (w + 1) >> 1;
125    const int uv_h = (h + 1) >> 1;
126    ImportBlock(usrc, pic->uv_stride, udst, uv_w, uv_h, 8);
127    ImportBlock(vsrc, pic->uv_stride, vdst, uv_w, uv_h, 8);
128  }
129}
130
131//------------------------------------------------------------------------------
132// Copy back the compressed samples into user space if requested.
133
134static void ExportBlock(const uint8_t* src, uint8_t* dst, int dst_stride,
135                        int w, int h) {
136  while (h-- > 0) {
137    memcpy(dst, src, w);
138    dst += dst_stride;
139    src += BPS;
140  }
141}
142
143void VP8IteratorExport(const VP8EncIterator* const it) {
144  const VP8Encoder* const enc = it->enc_;
145  if (enc->config_->show_compressed) {
146    const int x = it->x_, y = it->y_;
147    const uint8_t* const ysrc = it->yuv_out_ + Y_OFF;
148    const uint8_t* const usrc = it->yuv_out_ + U_OFF;
149    const uint8_t* const vsrc = it->yuv_out_ + V_OFF;
150    const WebPPicture* const pic = enc->pic_;
151    uint8_t* const ydst = pic->y + (y * pic->y_stride + x) * 16;
152    uint8_t* const udst = pic->u + (y * pic->uv_stride + x) * 8;
153    uint8_t* const vdst = pic->v + (y * pic->uv_stride + x) * 8;
154    int w = (pic->width - x * 16);
155    int h = (pic->height - y * 16);
156
157    if (w > 16) w = 16;
158    if (h > 16) h = 16;
159
160    // Luma plane
161    ExportBlock(ysrc, ydst, pic->y_stride, w, h);
162
163    {   // U/V planes
164      const int uv_w = (w + 1) >> 1;
165      const int uv_h = (h + 1) >> 1;
166      ExportBlock(usrc, udst, pic->uv_stride, uv_w, uv_h);
167      ExportBlock(vsrc, vdst, pic->uv_stride, uv_w, uv_h);
168    }
169  }
170}
171
172//------------------------------------------------------------------------------
173// Non-zero contexts setup/teardown
174
175// Nz bits:
176//  0  1  2  3  Y
177//  4  5  6  7
178//  8  9 10 11
179// 12 13 14 15
180// 16 17        U
181// 18 19
182// 20 21        V
183// 22 23
184// 24           DC-intra16
185
186// Convert packed context to byte array
187#define BIT(nz, n) (!!((nz) & (1 << (n))))
188
189void VP8IteratorNzToBytes(VP8EncIterator* const it) {
190  const int tnz = it->nz_[0], lnz = it->nz_[-1];
191  int* const top_nz = it->top_nz_;
192  int* const left_nz = it->left_nz_;
193
194  // Top-Y
195  top_nz[0] = BIT(tnz, 12);
196  top_nz[1] = BIT(tnz, 13);
197  top_nz[2] = BIT(tnz, 14);
198  top_nz[3] = BIT(tnz, 15);
199  // Top-U
200  top_nz[4] = BIT(tnz, 18);
201  top_nz[5] = BIT(tnz, 19);
202  // Top-V
203  top_nz[6] = BIT(tnz, 22);
204  top_nz[7] = BIT(tnz, 23);
205  // DC
206  top_nz[8] = BIT(tnz, 24);
207
208  // left-Y
209  left_nz[0] = BIT(lnz,  3);
210  left_nz[1] = BIT(lnz,  7);
211  left_nz[2] = BIT(lnz, 11);
212  left_nz[3] = BIT(lnz, 15);
213  // left-U
214  left_nz[4] = BIT(lnz, 17);
215  left_nz[5] = BIT(lnz, 19);
216  // left-V
217  left_nz[6] = BIT(lnz, 21);
218  left_nz[7] = BIT(lnz, 23);
219  // left-DC is special, iterated separately
220}
221
222void VP8IteratorBytesToNz(VP8EncIterator* const it) {
223  uint32_t nz = 0;
224  const int* const top_nz = it->top_nz_;
225  const int* const left_nz = it->left_nz_;
226  // top
227  nz |= (top_nz[0] << 12) | (top_nz[1] << 13);
228  nz |= (top_nz[2] << 14) | (top_nz[3] << 15);
229  nz |= (top_nz[4] << 18) | (top_nz[5] << 19);
230  nz |= (top_nz[6] << 22) | (top_nz[7] << 23);
231  nz |= (top_nz[8] << 24);  // we propagate the _top_ bit, esp. for intra4
232  // left
233  nz |= (left_nz[0] << 3) | (left_nz[1] << 7);
234  nz |= (left_nz[2] << 11);
235  nz |= (left_nz[4] << 17) | (left_nz[6] << 21);
236
237  *it->nz_ = nz;
238}
239
240#undef BIT
241
242//------------------------------------------------------------------------------
243// Advance to the next position, doing the bookeeping.
244
245int VP8IteratorNext(VP8EncIterator* const it,
246                    const uint8_t* const block_to_save) {
247  VP8Encoder* const enc = it->enc_;
248  if (block_to_save) {
249    const int x = it->x_, y = it->y_;
250    const uint8_t* const ysrc = block_to_save + Y_OFF;
251    const uint8_t* const usrc = block_to_save + U_OFF;
252    if (x < enc->mb_w_ - 1) {   // left
253      int i;
254      for (i = 0; i < 16; ++i) {
255        enc->y_left_[i] = ysrc[15 + i * BPS];
256      }
257      for (i = 0; i < 8; ++i) {
258        enc->u_left_[i] = usrc[7 + i * BPS];
259        enc->v_left_[i] = usrc[15 + i * BPS];
260      }
261      // top-left (before 'top'!)
262      enc->y_left_[-1] = enc->y_top_[x * 16 + 15];
263      enc->u_left_[-1] = enc->uv_top_[x * 16 + 0 + 7];
264      enc->v_left_[-1] = enc->uv_top_[x * 16 + 8 + 7];
265    }
266    if (y < enc->mb_h_ - 1) {  // top
267      memcpy(enc->y_top_ + x * 16, ysrc + 15 * BPS, 16);
268      memcpy(enc->uv_top_ + x * 16, usrc + 7 * BPS, 8 + 8);
269    }
270  }
271
272  it->mb_++;
273  it->preds_ += 4;
274  it->nz_++;
275  it->x_++;
276  if (it->x_ == enc->mb_w_) {
277    it->x_ = 0;
278    it->y_++;
279    it->bw_ = &enc->parts_[it->y_ & (enc->num_parts_ - 1)];
280    it->preds_ = enc->preds_ + it->y_ * 4 * enc->preds_w_;
281    it->nz_ = enc->nz_;
282    InitLeft(it);
283  }
284  return (0 < --it->done_);
285}
286
287//------------------------------------------------------------------------------
288// Helper function to set mode properties
289
290void VP8SetIntra16Mode(const VP8EncIterator* const it, int mode) {
291  uint8_t* preds = it->preds_;
292  int y;
293  for (y = 0; y < 4; ++y) {
294    memset(preds, mode, 4);
295    preds += it->enc_->preds_w_;
296  }
297  it->mb_->type_ = 1;
298}
299
300void VP8SetIntra4Mode(const VP8EncIterator* const it, const uint8_t* modes) {
301  uint8_t* preds = it->preds_;
302  int y;
303  for (y = 4; y > 0; --y) {
304    memcpy(preds, modes, 4 * sizeof(*modes));
305    preds += it->enc_->preds_w_;
306    modes += 4;
307  }
308  it->mb_->type_ = 0;
309}
310
311void VP8SetIntraUVMode(const VP8EncIterator* const it, int mode) {
312  it->mb_->uv_mode_ = mode;
313}
314
315void VP8SetSkip(const VP8EncIterator* const it, int skip) {
316  it->mb_->skip_ = skip;
317}
318
319void VP8SetSegment(const VP8EncIterator* const it, int segment) {
320  it->mb_->segment_ = segment;
321}
322
323//------------------------------------------------------------------------------
324// Intra4x4 sub-blocks iteration
325//
326//  We store and update the boundary samples into an array of 37 pixels. They
327//  are updated as we iterate and reconstructs each intra4x4 blocks in turn.
328//  The position of the samples has the following snake pattern:
329//
330// 16|17 18 19 20|21 22 23 24|25 26 27 28|29 30 31 32|33 34 35 36  <- Top-right
331// --+-----------+-----------+-----------+-----------+
332// 15|         19|         23|         27|         31|
333// 14|         18|         22|         26|         30|
334// 13|         17|         21|         25|         29|
335// 12|13 14 15 16|17 18 19 20|21 22 23 24|25 26 27 28|
336// --+-----------+-----------+-----------+-----------+
337// 11|         15|         19|         23|         27|
338// 10|         14|         18|         22|         26|
339//  9|         13|         17|         21|         25|
340//  8| 9 10 11 12|13 14 15 16|17 18 19 20|21 22 23 24|
341// --+-----------+-----------+-----------+-----------+
342//  7|         11|         15|         19|         23|
343//  6|         10|         14|         18|         22|
344//  5|          9|         13|         17|         21|
345//  4| 5  6  7  8| 9 10 11 12|13 14 15 16|17 18 19 20|
346// --+-----------+-----------+-----------+-----------+
347//  3|          7|         11|         15|         19|
348//  2|          6|         10|         14|         18|
349//  1|          5|          9|         13|         17|
350//  0| 1  2  3  4| 5  6  7  8| 9 10 11 12|13 14 15 16|
351// --+-----------+-----------+-----------+-----------+
352
353// Array to record the position of the top sample to pass to the prediction
354// functions in dsp.c.
355static const uint8_t VP8TopLeftI4[16] = {
356  17, 21, 25, 29,
357  13, 17, 21, 25,
358  9,  13, 17, 21,
359  5,   9, 13, 17
360};
361
362void VP8IteratorStartI4(VP8EncIterator* const it) {
363  const VP8Encoder* const enc = it->enc_;
364  int i;
365
366  it->i4_ = 0;    // first 4x4 sub-block
367  it->i4_top_ = it->i4_boundary_ + VP8TopLeftI4[0];
368
369  // Import the boundary samples
370  for (i = 0; i < 17; ++i) {    // left
371    it->i4_boundary_[i] = enc->y_left_[15 - i];
372  }
373  for (i = 0; i < 16; ++i) {    // top
374    it->i4_boundary_[17 + i] = enc->y_top_[it->x_ * 16 + i];
375  }
376  // top-right samples have a special case on the far right of the picture
377  if (it->x_ < enc->mb_w_ - 1) {
378    for (i = 16; i < 16 + 4; ++i) {
379      it->i4_boundary_[17 + i] = enc->y_top_[it->x_ * 16 + i];
380    }
381  } else {    // else, replicate the last valid pixel four times
382    for (i = 16; i < 16 + 4; ++i) {
383      it->i4_boundary_[17 + i] = it->i4_boundary_[17 + 15];
384    }
385  }
386  VP8IteratorNzToBytes(it);  // import the non-zero context
387}
388
389int VP8IteratorRotateI4(VP8EncIterator* const it,
390                        const uint8_t* const yuv_out) {
391  const uint8_t* const blk = yuv_out + VP8Scan[it->i4_];
392  uint8_t* const top = it->i4_top_;
393  int i;
394
395  // Update the cache with 7 fresh samples
396  for (i = 0; i <= 3; ++i) {
397    top[-4 + i] = blk[i + 3 * BPS];   // store future top samples
398  }
399  if ((it->i4_ & 3) != 3) {  // if not on the right sub-blocks #3, #7, #11, #15
400    for (i = 0; i <= 2; ++i) {        // store future left samples
401      top[i] = blk[3 + (2 - i) * BPS];
402    }
403  } else {  // else replicate top-right samples, as says the specs.
404    for (i = 0; i <= 3; ++i) {
405      top[i] = top[i + 4];
406    }
407  }
408  // move pointers to next sub-block
409  ++it->i4_;
410  if (it->i4_ == 16) {    // we're done
411    return 0;
412  }
413
414  it->i4_top_ = it->i4_boundary_ + VP8TopLeftI4[it->i4_];
415  return 1;
416}
417
418//------------------------------------------------------------------------------
419
420#if defined(__cplusplus) || defined(c_plusplus)
421}    // extern "C"
422#endif
423