summaryrefslogtreecommitdiff
path: root/pngwtran.c
blob: 3aaf2205e0f9ac2774f2c32fd59e6464ad186c7f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148

/* pngwtran.c - transforms the data in a row for PNG writers
 *
 * Last changed in libpng 1.7.0 [(PENDING RELEASE)]
 * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson
 * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
 * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
 *
 * This code is released under the libpng license.
 * For conditions of distribution and use, see the disclaimer
 * and license in png.h
 */

#include "pngpriv.h"
#define PNG_SRC_FILE PNG_SRC_FILE_pngwtran

#ifdef PNG_WRITE_PACK_SUPPORTED
/* Pack pixels into bytes. */
static void
png_do_write_pack(png_transformp *transform, png_transform_controlp tc)
{
   png_alloc_size_t rowbytes = PNG_TC_ROWBYTES(*tc);
   png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
   png_const_bytep ep = png_upcast(png_const_bytep, tc->sp) + rowbytes;
   png_bytep dp = png_voidcast(png_bytep, tc->dp);

   png_debug(1, "in png_do_pack");

#  define png_ptr tc->png_ptr

   switch ((*transform)->args)
   {
      case 1:
      {
         unsigned int mask = 0x80, v = 0;

         while (sp < ep)
         {
            if (*sp++ != 0)
               v |= mask;

            mask >>= 1;

            if (mask == 0)
            {
               mask = 0x80;
               *dp++ = PNG_BYTE(v);
               v = 0;
            }
         }

         if (mask != 0x80)
            *dp++ = PNG_BYTE(v);
         break;
      }

      case 2:
      {
         unsigned int shift = 8, v = 0;

         while (sp < ep)
         {
            shift -= 2;
            v |= (*sp++ & 0x3) << shift;

            if (shift == 0)
            {
               shift = 8;
               *dp++ = PNG_BYTE(v);
               v = 0;
            }
         }

         if (shift != 8)
            *dp++ = PNG_BYTE(v);
         break;
      }

      case 4:
      {
         unsigned int shift = 8, v = 0;

         while (sp < ep)
         {
            shift -= 4;
            v |= ((*sp++ & 0xf) << shift);

            if (shift == 0)
            {
               shift = 8;
               *dp++ = PNG_BYTE(v);
               v = 0;
            }
         }

         if (shift != 8)
            *dp++ = PNG_BYTE(v);
         break;
      }

      default:
         impossible("bit depth");
   }

   if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0 &&
       --(tc->range) == 0)
      tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_RANGE);

   tc->bit_depth = (*transform)->args;
   tc->sp = tc->dp;
#  undef png_ptr
}

void /* PRIVATE */
png_init_write_pack(png_transformp *transform, png_transform_controlp tc)
{
#  define png_ptr tc->png_ptr
   debug(tc->init);
#  undef png_ptr

   /* The init routine is called *forward* so the transform control we get has
    * the required bit depth and the transform routine will increase it to 8
    * bits per channel.  The code doesn't really care how many channels there
    * are, but the only way to get a channel depth of less than 8 is to have
    * just one channel.
    */
   if (tc->bit_depth < 8) /* else no packing/unpacking */
   {
      if (tc->init == PNG_TC_INIT_FINAL)
      {
         (*transform)->fn = png_do_write_pack;
         /* Record this for the backwards run: */
         (*transform)->args = tc->bit_depth & 0xf;
      }

      if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0)
      {
         tc->range++;
         tc->format |= PNG_FORMAT_FLAG_RANGE; /* forwards: backwards cancels */
      }

      tc->bit_depth = 8;
   }

   else /* the transform is not applicable */
      (*transform)->fn = NULL;
}
#endif /* WRITE_PACK */