1 #include "config.h"
2 
3 #include "broadway-buffer.h"
4 
5 #include <string.h>
6 
7 /* This code is based on some code from weston with this license:
8  *
9  * Copyright © 2012 Intel Corporation
10  *
11  * Permission to use, copy, modify, distribute, and sell this software and
12  * its documentation for any purpose is hereby granted without fee, provided
13  * that the above copyright notice appear in all copies and that both that
14  * copyright notice and this permission notice appear in supporting
15  * documentation, and that the name of the copyright holders not be used in
16  * advertising or publicity pertaining to distribution of the software
17  * without specific, written prior permission.  The copyright holders make
18  * no representations about the suitability of this software for any
19  * purpose.  It is provided "as is" without express or implied warranty.
20  *
21  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
22  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
23  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
24  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
25  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
26  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
27  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
28  */
29 
30 struct entry {
31   int count;
32   int matches;
33   guint32 hash;
34   int x, y;
35   int index;
36 };
37 
38 struct _BroadwayBuffer {
39   guint8 *data;
40   struct entry *table;
41   int width, height, stride;
42   int encoded;
43   int block_stride, length, block_count, shift;
44   int stats[5];
45   int clashes;
46 };
47 
48 static const guint32 prime = 0x1f821e2d;
49 static const guint32 end_prime = 0xf907ec81;	/* prime^block_size */
50 #if 0
51 static const guint32 vprime = 0x0137b89d;
52 static const guint32 end_vprime = 0xaea9a281;	/* vprime^block_size */
53 #else
54 static const guint32 vprime = 0xf907ec81;
55 static const guint32 end_vprime = 0xcdb99001;	/* vprime^block_size */
56 #endif
57 static const guint32 step = 0x0ac93019;
58 static const int block_size = 32, block_mask = 31;
59 
60 static gboolean
verify_block_match(BroadwayBuffer * buffer,int x,int y,BroadwayBuffer * prev,struct entry * entry)61 verify_block_match (BroadwayBuffer *buffer, int x, int y,
62                     BroadwayBuffer *prev, struct entry *entry)
63 {
64   int i;
65   void *old, *match;
66   int w1, w2, h1, h2;
67 
68   w1 = block_size;
69   if (x + block_size > buffer->width)
70     w1 = buffer->width - x;
71 
72   h1 = block_size;
73   if (y + block_size > buffer->height)
74     h1 = buffer->height - y;
75 
76   w2 = block_size;
77   if (entry->x + block_size > prev->width)
78     w2 = prev->width - entry->x;
79 
80   h2 = block_size;
81   if (entry->y + block_size > prev->height)
82     h2 = prev->height - entry->y;
83 
84   if (w1 != w2 || h1 != h2)
85     return FALSE;
86 
87   for (i = 0; i < h1; i++)
88     {
89       match = buffer->data + (y + i) * buffer->stride + x * 4;
90       old = prev->data + (entry->y + i) * prev->stride + entry->x * 4;
91       if (memcmp (match, old, w1 * 4) != 0)
92         {
93           buffer->clashes++;
94           return FALSE;
95         }
96     }
97 
98   return TRUE;
99 }
100 
101 static void
insert_block(BroadwayBuffer * buffer,guint32 h,int x,int y)102 insert_block (BroadwayBuffer *buffer, guint32 h, int x, int y)
103 {
104   struct entry *entry;
105   int i;
106   guint32 collision = 0;
107 
108   entry = &buffer->table[h >> buffer->shift];
109   for (i = step; entry->count > 0 && entry->hash != h; i += step)
110     {
111       entry = &buffer->table[(h + i) >> buffer->shift];
112       collision++;
113     }
114 
115   entry->hash = h;
116   entry->count++;
117   entry->x = x;
118   entry->y = y;
119   entry->index = (buffer->block_stride * y + x) / block_size;
120 
121   if (collision > G_N_ELEMENTS (buffer->stats) - 1)
122     collision = G_N_ELEMENTS (buffer->stats) - 1;
123   buffer->stats[collision]++;
124 }
125 
126 static struct entry *
lookup_block(BroadwayBuffer * prev,guint32 h)127 lookup_block (BroadwayBuffer *prev, guint32 h)
128 {
129   guint32 i;
130   struct entry *entry;
131   int shift = prev->shift;
132 
133   for (i = h;
134        entry = &prev->table[i >> shift], entry->count > 0;
135        i += step)
136     {
137       if (entry->hash == h)
138         return entry;
139     }
140 
141   return NULL;
142 }
143 
144 struct encoder {
145   guint32 color;
146   guint32 color_run;
147   guint32 delta;
148   guint32 delta_run;
149   GString *dest;
150   int bytes;
151 };
152 
153 /* Encoding:
154  *
155  *  - all 1 pixel colors are encoded literally
156  *
157  *  - We don’t need to support colors with alpha 0 and non-zero
158  *    color components, as they mean the same on the canvas anyway.
159  *    So we use these as special codes:
160  *
161  *     - 0x00 00 00 00 : one alpha 0 pixel
162  *     - 0xaa rr gg bb : one color pixel, alpha > 0
163  *     - 0x00 1x xx xx : delta 0 run, x is length, (20 bits)
164  *     - 0x00 2x xx xx 0x xxxx yyyy: block ref, block number x (20 bits) at x, y
165  *     - 0x00 3x xx xx 0xaarrggbb : solid color run, length x
166  *     - 0x00 4x xx xx 0xaarrggbb : delta run, length x
167  *
168  */
169 
170 static void
emit(struct encoder * encoder,guint32 symbol)171 emit (struct encoder *encoder, guint32 symbol)
172 {
173   g_string_append_len (encoder->dest, (char *)&symbol, sizeof (guint32));
174   encoder->bytes += sizeof (guint32);
175 }
176 
177 static void
encode_run(struct encoder * encoder)178 encode_run (struct encoder *encoder)
179 {
180   if (encoder->color_run == 0 && encoder->delta_run == 0)
181     return;
182 
183   if (encoder->color_run >= encoder->delta_run)
184     {
185       if (encoder->color_run == 1)
186         emit (encoder, encoder->color);
187       else
188         {
189           emit (encoder, 0x00300000 | encoder->color_run);
190           emit (encoder, encoder->color);
191         }
192     }
193   else
194     {
195       if (encoder->delta == 0)
196         emit(encoder, 0x00100000 | encoder->delta_run);
197       else
198         {
199           emit(encoder, 0x00400000 | encoder->delta_run);
200           emit(encoder, encoder->delta);
201         }
202     }
203 }
204 
205 static void
encode_pixel(struct encoder * encoder,guint32 color,guint32 prev_color)206 encode_pixel (struct encoder *encoder, guint32 color, guint32 prev_color)
207 {
208   guint32 delta = 0;
209   guint32 a, r, g, b;
210 
211   if (color == prev_color)
212     delta = 0;
213   else if (prev_color == 0)
214     delta = color;
215   else
216     {
217       a = ((color & 0xff000000) - (prev_color & 0xff000000)) & 0xff000000;
218       r = ((color & 0x00ff0000) - (prev_color & 0x00ff0000)) & 0x00ff0000;
219       g = ((color & 0x0000ff00) - (prev_color & 0x0000ff00)) & 0x0000ff00;
220       b = ((color & 0x000000ff) - (prev_color & 0x000000ff)) & 0x000000ff;
221 
222       delta = a | r | g | b;
223     }
224 
225   if ((encoder->color != color &&
226        encoder->color_run > encoder->delta_run) ||
227 
228       (encoder->delta != delta &&
229        encoder->delta_run > encoder->color_run) ||
230 
231       (encoder->delta != delta && encoder->color != color) ||
232 
233       (encoder->delta_run == 0xFFFFF || encoder->color_run == 0xFFFFF))
234     {
235       encode_run (encoder);
236 
237       encoder->color_run = 1;
238       encoder->color = color;
239       encoder->delta_run = 1;
240       encoder->delta = delta;
241       return;
242     }
243 
244   if (encoder->color == color)
245     encoder->color_run++;
246   else
247     {
248       encoder->color_run = 1;
249       encoder->color = color;
250     }
251 
252   if (encoder->delta == delta)
253     encoder->delta_run++;
254   else
255     {
256       encoder->delta_run = 1;
257       encoder->delta = delta;
258     }
259 }
260 
261 static void
encoder_flush(struct encoder * encoder)262 encoder_flush (struct encoder *encoder)
263 {
264   encode_run (encoder);
265 }
266 
267 
268 static void
encode_block(struct encoder * encoder,struct entry * entry,int x,int y)269 encode_block (struct encoder *encoder, struct entry *entry, int x, int y)
270 {
271   /* 0x00 2x xx xx 0x xxxx yyyy:
272    *	block ref, block number x (20 bits) at x, y */
273 
274   /* FIXME: Maybe don't encode pixels under blocks and just emit
275    * blocks at their position within the stream. */
276 
277   emit (encoder, 0x00200000 | entry->index);
278   emit (encoder, (x << 16) | y);
279 }
280 
281 void
broadway_buffer_destroy(BroadwayBuffer * buffer)282 broadway_buffer_destroy (BroadwayBuffer *buffer)
283 {
284   g_free (buffer->data);
285   g_free (buffer->table);
286   g_free (buffer);
287 }
288 
289 int
broadway_buffer_get_width(BroadwayBuffer * buffer)290 broadway_buffer_get_width (BroadwayBuffer *buffer)
291 {
292   return buffer->width;
293 }
294 
295 int
broadway_buffer_get_height(BroadwayBuffer * buffer)296 broadway_buffer_get_height (BroadwayBuffer *buffer)
297 {
298   return buffer->height;
299 }
300 
301 static void
unpremultiply_line(void * destp,void * srcp,int width)302 unpremultiply_line (void *destp, void *srcp, int width)
303 {
304   guint32 *src = srcp;
305   guint32 *dest = destp;
306   guint32 *end = src + width;
307   while (src < end)
308     {
309       guint32 pixel;
310       guint8 alpha, r, g, b;
311 
312       pixel = *src++;
313 
314       alpha = (pixel & 0xff000000) >> 24;
315 
316       if (alpha == 0xff)
317         *dest++ = pixel;
318       else if (alpha == 0)
319         *dest++ = 0;
320       else
321         {
322           r = (((pixel & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
323           g = (((pixel & 0x00ff00) >>  8) * 255 + alpha / 2) / alpha;
324           b = (((pixel & 0x0000ff) >>  0) * 255 + alpha / 2) / alpha;
325           *dest++ = (guint32)alpha << 24 | (guint32)r << 16 | (guint32)g << 8 | (guint32)b;
326         }
327     }
328 }
329 
330 BroadwayBuffer *
broadway_buffer_create(int width,int height,guint8 * data,int stride)331 broadway_buffer_create (int width, int height, guint8 *data, int stride)
332 {
333   BroadwayBuffer *buffer;
334   int y, bits_required;
335 
336   buffer = g_new0 (BroadwayBuffer, 1);
337   buffer->width = width;
338   buffer->stride = width * 4;
339   buffer->height = height;
340 
341   buffer->block_stride = (width + block_size - 1) / block_size;
342   buffer->block_count =
343     buffer->block_stride * ((height + block_size - 1) / block_size);
344   bits_required = g_bit_storage (buffer->block_count * 4);
345   buffer->shift = 32 - bits_required;
346   buffer->length = 1 << bits_required;
347 
348   buffer->table = g_malloc0 (buffer->length * sizeof buffer->table[0]);
349 
350   memset (buffer->stats, 0, sizeof buffer->stats);
351   buffer->clashes = 0;
352 
353   buffer->data = g_malloc (buffer->stride * height);
354 
355   for (y = 0; y < height; y++)
356     unpremultiply_line (buffer->data + y * buffer->stride, data + y * stride, width);
357 
358   return buffer;
359 }
360 
361 void
broadway_buffer_encode(BroadwayBuffer * buffer,BroadwayBuffer * prev,GString * dest)362 broadway_buffer_encode (BroadwayBuffer *buffer, BroadwayBuffer *prev, GString *dest)
363 {
364   struct entry *entry;
365   int i, j, k;
366   int x0, x1, y0, y1;
367   guint32 *block_hashes;
368   guint32 hash, bottom_hash, h, *line, *bottom, *prev_line;
369   int width, height;
370   struct encoder encoder = { 0 };
371   int *skyline, skyline_pixels;
372   int matches;
373 
374   width = buffer->width;
375   height = buffer->height;
376   x0 = 0;
377   x1 = width;
378   y0 = 0;
379   y1 = height;
380 
381   skyline = g_malloc0 ((width + block_size) * sizeof skyline[0]);
382 
383   block_hashes = g_malloc0 (width * sizeof block_hashes[0]);
384 
385   matches = 0;
386   encoder.dest = dest;
387 
388   // Calculate the block hashes for the first row
389   for (i = y0; i < MIN(y1, y0 + block_size); i++)
390     {
391       line = (guint32 *)(buffer->data + i * buffer->stride);
392       hash = 0;
393       for (j = x0; j < MIN(x1, x0 + block_size); j++)
394         hash = hash * prime + line[j];
395       for (; j < x0 + block_size; j++)
396         hash = hash * prime;
397 
398       for (j = x0; j < x1; j++)
399         {
400           block_hashes[j] = block_hashes[j] * vprime + hash;
401 
402           hash = hash * prime - line[j] * end_prime;
403           if (j + block_size < width)
404             hash += line[j + block_size];
405         }
406     }
407   // Do the last rows if height < block_size
408   for (; i < y0 + block_size; i++)
409     {
410       for (j = x0; j < x1; j++)
411         block_hashes[j] = block_hashes[j] * vprime;
412     }
413 
414   for (i = y0; i < y1; i++)
415     {
416       line = (guint32 *) (buffer->data + i * buffer->stride);
417       bottom = (guint32 *) (buffer->data + (i + block_size) * buffer->stride);
418       bottom_hash = 0;
419       hash = 0;
420       skyline_pixels = 0;
421 
422       if (prev && i < prev->height)
423         prev_line = (guint32 *) (prev->data + i * prev->stride);
424       else
425         prev_line = NULL;
426 
427       for (j = x0; j < x0 + block_size; j++)
428         {
429           hash = hash * prime;
430           if (j < width)
431             hash += line[j];
432           if (i + block_size < height)
433             {
434               bottom_hash = bottom_hash * prime;
435               if (j < width)
436                 bottom_hash += bottom[j];
437             }
438           if (i < skyline[j])
439             skyline_pixels = 0;
440           else
441             skyline_pixels++;
442         }
443 
444       for (j = x0; j < x1; j++)
445         {
446           if (i < skyline[j])
447             encode_pixel (&encoder, line[j], line[j]);
448           else if (prev)
449             {
450               /* FIXME: Add back overlap exception
451                * for consecutive blocks */
452 
453               h = block_hashes[j];
454               entry = lookup_block (prev, h);
455               if (entry && entry->count < 2 &&
456                   skyline_pixels >= block_size &&
457                   verify_block_match (buffer, j, i, prev, entry) &&
458                   (entry->x != j || entry->y != i))
459                 {
460                   matches++;
461                   encode_block (&encoder, entry, j, i);
462 
463                   for (k = 0; k < block_size; k++)
464                     skyline[j + k] = i + block_size;
465 
466                   encode_pixel (&encoder, line[j], line[j]);
467                 }
468               else
469                 {
470                   if (prev_line && j < prev->width)
471                     encode_pixel (&encoder, line[j],
472                                   prev_line[j]);
473                   else
474                     encode_pixel (&encoder, line[j], 0);
475                 }
476             }
477           else
478             encode_pixel (&encoder, line[j], 0);
479 
480           if (i < skyline[j + block_size])
481             skyline_pixels = 0;
482           else
483             skyline_pixels++;
484 
485           /* Insert block in hash table if we're on a
486            * grid point. */
487           if (((i | j) & block_mask) == 0 && !buffer->encoded)
488             insert_block (buffer, block_hashes[j], j, i);
489 
490           /* Update sliding block hash */
491           block_hashes[j] =
492             block_hashes[j] * vprime + bottom_hash -
493             hash * end_vprime;
494 
495           if (i + block_size < height)
496             {
497               bottom_hash = bottom_hash * prime - bottom[j] * end_prime;
498               if (j + block_size < width)
499                 bottom_hash += bottom[j + block_size];
500             }
501           hash = hash * prime - line[j] * end_prime;
502           if  (j + block_size < width)
503             hash += line[j + block_size] ;
504         }
505     }
506 
507   encoder_flush (&encoder);
508 
509 #if 0
510   fprintf(stderr, "collision stats:");
511   for (i = 0; i < (int) G_N_ELEMENTS(buffer->stats); i++)
512     fprintf(stderr, "%c%d", i == 0 ? ' ' : '/', buffer->stats[i]);
513   fprintf(stderr, "\n");
514 
515   fprintf(stderr, "%d / %d blocks (%d%%) matched, %d clashes\n",
516           matches, buffer->block_count,
517           100 * matches / buffer->block_count, buffer->clashes);
518 
519   fprintf(stderr, "output stream %d bytes, raw buffer %d bytes (%d%%)\n",
520           encoder.bytes, height * buffer->stride,
521           100 * encoder.bytes / (height * buffer->stride));
522 #endif
523 
524   g_free (skyline);
525   g_free (block_hashes);
526 
527   buffer->encoded = TRUE;
528 }
529