3
3
** REBOL [R3] Language Interpreter and Run-time Environment
4
4
**
5
5
** Copyright 2012 REBOL Technologies
6
+ ** Copyright 2012-2021 Rebol Open Source Contributors
6
7
** REBOL is a trademark of REBOL Technologies
7
8
**
8
9
** Licensed under the Apache License, Version 2.0 (the "License");
@@ -340,4 +341,229 @@ static const ISzAlloc g_Alloc = { SzAlloc, SzFree };
340
341
return output ;
341
342
}
342
343
343
- #endif //INCLUDE_LZMA
344
+ #endif //INCLUDE_LZMA
345
+
346
+
347
+
348
+ #ifdef INCLUDE_PNG_CODEC
349
+ int paeth_predictor (int a , int b , int c );
350
+ #else
351
+ #define int_abs (a ) (((a)<0)?(-(a)):(a))
352
+ static int paeth_predictor (int a , int b , int c ) {
353
+ int p , pa , pb , pc ;
354
+
355
+ p = a + b - c ;
356
+ pa = int_abs (p - a );
357
+ pb = int_abs (p - b );
358
+ pc = int_abs (p - c );
359
+ if ((pa <= pb ) && (pa <= pc ))
360
+ return a ;
361
+ else if (pb <= pc )
362
+ return b ;
363
+ return c ;
364
+ }
365
+ #endif
366
+
367
+ enum PNG_Filter_Types {
368
+ PNG_FILTER_SUB = 1 ,
369
+ PNG_FILTER_UP ,
370
+ PNG_FILTER_AVERAGE ,
371
+ PNG_FILTER_PAETH
372
+ };
373
+
374
+ static REBYTE get_png_filter_type (REBVAL * val ) {
375
+ if (IS_WORD (val )) {
376
+ switch (VAL_WORD_SYM (val )) {
377
+ case SYM_SUB : return PNG_FILTER_SUB ;
378
+ case SYM_UP : return PNG_FILTER_UP ;
379
+ case SYM_AVERAGE : return PNG_FILTER_AVERAGE ;
380
+ case SYM_PAETH : return PNG_FILTER_PAETH ;
381
+ }
382
+ }
383
+ else if (IS_INTEGER (val )) {
384
+ return MIN (PNG_FILTER_PAETH , MAX (0 , VAL_INT32 (val )));
385
+ }
386
+ Trap1 (RE_INVALID_ARG , val );
387
+ }
388
+
389
+ // See: https://www.rfc-editor.org/rfc/rfc2083.html#page-31
390
+ // https://en.wikipedia.org/wiki/Portable_Network_Graphics#Filtering
391
+ /***********************************************************************
392
+ **
393
+ */ REBNATIVE (filter )
394
+ /*
395
+ // filter: native [
396
+ // "PNG delta filter"
397
+ // data [binary!] "Input"
398
+ // width [number!] "Scanline width"
399
+ // type [integer! word!] "1..4 or one of: [sub up average paeth]"
400
+ // /skip bpp [integer!] "Bytes per pixel"
401
+ // ]
402
+ ***********************************************************************/
403
+ {
404
+ REBVAL * val_data = D_ARG (1 );
405
+ REBVAL * val_width = D_ARG (2 );
406
+ REBVAL * val_type = D_ARG (3 );
407
+ REBOOL ref_skip = D_REF (4 );
408
+ REBVAL * val_bpp = D_ARG (5 );
409
+
410
+ REBSER * ser ;
411
+ REBYTE * bin = (REBCHR * )VAL_BIN_DATA (val_data );
412
+ REBCNT r , c , rows , bytes ;
413
+ REBYTE * scan , * prev , * temp , * out ;
414
+ REBINT width = AS_INT32 (val_width );
415
+ REBYTE filter = get_png_filter_type (val_type );
416
+ REBCNT bpp = ref_skip ? VAL_INT32 (val_bpp ) : 1 ;
417
+
418
+ bytes = VAL_LEN (val_data );
419
+
420
+ if (width <= 1 || width > bytes )
421
+ Trap1 (RE_INVALID_ARG , val_width );
422
+ if (bpp < 1 || bpp > width )
423
+ Trap1 (RE_INVALID_ARG , val_bpp );
424
+
425
+ rows = bytes / width ;
426
+ ser = Make_Binary (bytes );
427
+ out = BIN_DATA (ser );
428
+
429
+ temp = malloc (width );
430
+ if (!temp ) {
431
+ Trap0 (RE_NO_MEMORY );
432
+ return R_NONE ;
433
+ }
434
+ memset (temp , 0 , width );
435
+
436
+ prev = temp ;
437
+ for (r = 0 ; r < rows ; r ++ ) {
438
+ scan = bin + (r * width );
439
+ out = BIN_SKIP (ser , r * width );
440
+
441
+ switch (filter ) {
442
+ case PNG_FILTER_SUB :
443
+ for (c = 0 ; c < bpp ; c ++ )
444
+ out [c ] = scan [c ];
445
+ for (c = bpp ; c < width ; c ++ )
446
+ out [c ] = scan [c ] - scan [c - bpp ];
447
+ break ;
448
+ case PNG_FILTER_UP :
449
+ for (c = 0 ; c < width ; c ++ )
450
+ out [c ] = scan [c ] - prev [c ];
451
+ break ;
452
+ case PNG_FILTER_AVERAGE :
453
+ for (c = 0 ; c < bpp ; c ++ )
454
+ out [c ] = scan [c ] - (prev [c ] >> 1 );
455
+ for (c = bpp ; c < width ; c ++ )
456
+ out [c ] = scan [c ] - ((scan [c - bpp ] + prev [c ]) >> 1 ) & 0xFF ;
457
+ break ;
458
+ case PNG_FILTER_PAETH :
459
+ for (c = 0 ; c < bpp ; c ++ )
460
+ out [c ] = scan [c ] - prev [c ];
461
+ for (c = bpp ; c < width ; c ++ )
462
+ out [c ] = scan [c ] - paeth_predictor (scan [c - bpp ], prev [c ], prev [c - bpp ]);
463
+ break ;
464
+ }
465
+ prev = scan ;
466
+ }
467
+ free (temp );
468
+ SET_BINARY (D_RET , ser );
469
+ VAL_TAIL (D_RET ) = bytes ;
470
+ return R_RET ;
471
+ }
472
+
473
+ /***********************************************************************
474
+ **
475
+ */ REBNATIVE (unfilter )
476
+ /*
477
+ // unfilter: native [
478
+ // "Reversed PNG delta filter"
479
+ // data [binary!] "Input"
480
+ // width [number!] "Scanline width (not counting the type byte)"
481
+ // /as "Filter type. If not used, type is decoded from first byte on each line."
482
+ // type [integer! word!] "1..4 or one of: [sub up average paeth]"
483
+ // /skip
484
+ // bpp [integer!] "Bytes per pixel"
485
+ // ]
486
+ ***********************************************************************/
487
+ {
488
+ REBVAL * val_data = D_ARG (1 );
489
+ REBVAL * val_width = D_ARG (2 );
490
+ REBOOL ref_as = D_REF (3 );
491
+ REBVAL * val_type = D_ARG (4 );
492
+ REBOOL ref_skip = D_REF (5 );
493
+ REBVAL * val_bpp = D_ARG (6 );
494
+
495
+ REBSER * ser ;
496
+ REBYTE * bin = VAL_BIN_DATA (val_data );
497
+ REBINT width = AS_INT32 (val_width );
498
+ REBCNT r , c , rows ;
499
+ REBYTE * scan , * prev , * temp , * out ;
500
+ REBYTE filter ;
501
+ REBCNT bytes = VAL_LEN (val_data );
502
+ REBCNT bpp = ref_skip ? VAL_INT32 (val_bpp ) : 1 ;
503
+
504
+ if (!ref_as ) width ++ ;
505
+ if (width <= 1 || width > bytes )
506
+ Trap1 (RE_INVALID_ARG , val_width );
507
+ if (bpp < 1 || bpp > width )
508
+ Trap1 (RE_INVALID_ARG , val_bpp );
509
+
510
+ rows = ceil (bytes / width );
511
+ ser = Make_Binary (bytes );
512
+ out = BIN_DATA (ser );
513
+
514
+ if (ref_as )
515
+ filter = get_png_filter_type (val_type );
516
+
517
+ temp = malloc (width );
518
+ if (!temp ) {
519
+ Trap0 (RE_NO_MEMORY );
520
+ return R_NONE ;
521
+ }
522
+ memset (temp , 0 , width );
523
+
524
+ if (!ref_as ) width -- ;
525
+
526
+ prev = temp ;
527
+ for (r = 0 ; r < rows ; r ++ ) {
528
+ if (ref_as ) {
529
+ scan = bin + r * width ;
530
+ out = BIN_SKIP (ser , r * width );
531
+ } else {
532
+ scan = bin + r * (width + 1 );
533
+ out = BIN_SKIP (ser , r * (width ));
534
+ filter = scan [0 ];
535
+ scan ++ ;
536
+ //out++;
537
+ }
538
+
539
+ switch (filter ) {
540
+ case PNG_FILTER_SUB :
541
+ for (c = 0 ; c < bpp ; c ++ )
542
+ out [c ] = scan [c ];
543
+ for (c = bpp ; c < width ; c ++ )
544
+ out [c ] = scan [c ] + out [c - bpp ];
545
+ break ;
546
+ case PNG_FILTER_UP :
547
+ for (c = 0 ; c < width ; c ++ )
548
+ out [c ] = scan [c ] + prev [c ];
549
+ break ;
550
+ case PNG_FILTER_AVERAGE :
551
+ for (c = 0 ; c < bpp ; c ++ )
552
+ out [c ] = scan [c ] + (prev [c ] >> 1 ) & 0xFF ;
553
+ for (c = bpp ; c < width ; c ++ )
554
+ out [c ] = scan [c ] + ((out [c - bpp ] + prev [c ]) >> 1 ) & 0xFF ;
555
+ break ;
556
+ case PNG_FILTER_PAETH :
557
+ for (c = 0 ; c < bpp ; c ++ )
558
+ out [c ] = scan [c ] + prev [c ];
559
+ for (c = bpp ; c < width ; c ++ )
560
+ out [c ] = scan [c ] + paeth_predictor (out [c - bpp ], prev [c ], prev [c - bpp ]);
561
+ break ;
562
+ }
563
+ prev = out ;
564
+ }
565
+ free (temp );
566
+ SET_BINARY (D_RET , ser );
567
+ VAL_TAIL (D_RET ) = ref_as ? bytes : bytes - rows ;
568
+ return R_RET ;
569
+ }
0 commit comments