79f7f404b8895a6c6be6b2055a4bfdbd1193c2dc
[sdk] / ecere / src / gfx / bitmaps / PCXFormat.ec
1 namespace gfx::bitmaps;
2
3 import "Display"
4
5 static struct RGBA32
6 {
7    byte b,g,r,a;
8 };
9
10 static struct PCXHead
11 {
12    byte manufacturer;
13    byte version;
14    byte encoding;
15    byte bitsPerPixel;
16    uint16 xMin,yMin        __attribute__((packed));
17    uint16 xMax,yMax        __attribute__((packed));
18    uint16 hRes             __attribute__((packed));
19    uint16 vRes             __attribute__((packed));
20    byte palette16[48];
21    byte reserved;
22    byte colorPlanes;
23    uint16 bytesPerLine     __attribute__((packed));
24    uint16 paletteType      __attribute__((packed));
25    byte filler[58];
26 };
27
28 #define BUFLEN 1024
29
30 static char * extensions[] = { "pcx", null };
31
32 class PCXFormat : BitmapFormat
33 {
34    class_property(extensions) = extensions;
35
36    bool Load(Bitmap bitmap, File f)
37    {
38       bool result = false;
39
40       PCXHead header;
41       if(f.Read(&header,sizeof(header),1))
42       {
43          if(header.xMax > header.xMin && header.yMax > header.yMin)
44          {
45             switch(header.colorPlanes)
46             {
47                case 1:
48                   result = bitmap.Allocate(null,
49                      header.xMax-header.xMin+1,header.yMax-header.yMin+1,
50                      header.xMax-header.xMin+1, pixelFormat8, true);
51                   break;
52                case 3:
53                   result = bitmap.Allocate(null,
54                      header.xMax-header.xMin+1,header.yMax-header.yMin+1,
55                      header.xMax-header.xMin+1, pixelFormat888, false);
56                   break;
57             }
58          }
59          if(result)
60          {
61             byte b = 0, count = 0;
62             byte buf[BUFLEN];
63             uint bptr = BUFLEN+1;
64             uint iptr=0, y = 0, x = 0;
65             uint16 c;
66             uint16 colorPlane = 0;
67
68             result = false;
69
70             //Read bitmap data
71             f.Seek(128, start);
72             for(; y <= header.yMax; )
73             {
74                if(bptr >= BUFLEN)
75                {
76                   if(!f.Read(buf, 1, BUFLEN)) break;
77                   bptr=0;
78                }
79                if(!count)
80                {
81                   b = buf[bptr++];
82                   if(b > 192)
83                   {
84                      count = b - 192;
85                      if(bptr >= BUFLEN)
86                      {
87                         if(!f.Read(buf, 1, BUFLEN)) break;
88                         bptr=0;
89                      }
90                      b=buf[bptr++];
91                      continue;
92                   }
93                }
94                else
95                   count--;
96                if(bitmap.pixelFormat == pixelFormat888)
97                {
98                   iptr = (y*bitmap.width) + x;
99                   if(x <= header.xMax && y <= header.yMax)
100                   {
101                      switch(colorPlane)
102                      {
103                         case 0:
104                            ((RGBA32*)bitmap.picture)[iptr].a = 255;
105                            ((RGBA32*)bitmap.picture)[iptr].r = b;
106                            break;
107                         case 1: ((RGBA32*)bitmap.picture)[iptr].g = b; break;
108                         case 2: ((RGBA32*)bitmap.picture)[iptr].b = b; break;
109                      }
110                   }
111                   x++;
112                   if(x == header.bytesPerLine)
113                   {
114                      colorPlane++;
115                      x = 0;
116                      if(colorPlane == 3)
117                      {
118                         colorPlane = 0;
119                         y++;
120                      }
121                   }
122                }
123                else if(bitmap.pixelFormat == pixelFormat8)
124                {
125                   if(x <= header.xMax && y <= header.yMax)
126                      bitmap.picture[iptr] = b;
127                   x++;
128                   iptr++;
129                   if(x == header.bytesPerLine)
130                   {
131                      iptr+= bitmap.stride - x;
132                      x = 0;
133                      y++;
134                   }
135                }
136             }
137             if(y > header.yMax)
138             {
139                if(bitmap.pixelFormat == pixelFormat8)
140                {
141                   byte palette[768];
142
143                   f.Seek(-768, end);
144                   if(f.Read(palette, 768, 1))
145                   {
146                      for(c=0; c<256; c++)
147                         bitmap.palette[c] = ColorAlpha { 255, { palette[c*3], palette[c*3+1], palette[c*3+2] } };
148                      result = true;
149                   }
150                }
151                else
152                   result = true;
153             }
154          }
155          if(!result)
156             bitmap.Free();
157       }
158       return result;
159    }
160
161    bool Save(Bitmap bitmap, char *filename, void * options)
162    {
163       bool result = false;
164       if(bitmap.pixelFormat == pixelFormat8 || bitmap.pixelFormat == pixelFormat888)
165       {
166          File f = FileOpen(filename, write);
167          if(f)
168          {
169             PCXHead header;
170             int div = (bitmap.pixelFormat == pixelFormat8) ? (bitmap.width % 2) : 0;
171             if(div) div = 2-div;
172
173             // Write the header
174             header.manufacturer=10;
175             header.version=5;
176             header.encoding=1;
177             header.bitsPerPixel=8;
178             header.xMin=0;
179             header.yMin=0;
180             header.xMax=(uint16)(bitmap.width-1);
181             header.yMax=(uint16)(bitmap.height-1);
182             header.hRes=72;
183             header.vRes=72;
184             FillBytes(header.palette16, 0, 48);
185             header.reserved=0;
186             if(bitmap.pixelFormat == pixelFormat8)
187                header.colorPlanes = 1;
188             else if(bitmap.pixelFormat == pixelFormat888)
189                header.colorPlanes=3;
190             header.bytesPerLine=(uint16)(bitmap.width+div);
191             header.paletteType=1;
192             FillBytes(header.filler, 0, 58);
193
194             if(f.Write(&header, sizeof(header), 1))
195             {
196                uint16 c;
197                uint16 x=0;
198                int count=0;
199                byte b = 0;
200                byte last=0;
201                uint iptr=0, y = 0;
202                uint16 colorPlane = 0;
203                bool pad = false;
204                byte padder;
205                bool errorWriting = false;
206
207                padder = (bitmap.pixelFormat == pixelFormat8) ? 0xFF : 0x00;
208
209                for(; (iptr<bitmap.size || count) && !errorWriting; )
210                {
211                   iptr = (y*bitmap.stride) + x;
212                   for(;iptr<bitmap.size;)
213                   {
214                      if(bitmap.pixelFormat == pixelFormat888)
215                      {
216                         switch(colorPlane)
217                         {
218                            case 0: b = ((RGBA32*)bitmap.picture)[iptr].r; break;
219                            case 1: b = ((RGBA32*)bitmap.picture)[iptr].g; break;
220                            case 2: b = ((RGBA32*)bitmap.picture)[iptr].b; break;
221                         }
222                      }
223                      else
224                         b = bitmap.picture[iptr];
225                      if(count)
226                      {
227                         if(b != last)
228                            break;
229                      }
230                      else
231                      {
232                         last = b;
233                      }
234                      count++,x++;iptr++;
235
236                      if(x==bitmap.width)
237                      {
238                         x = 0;
239                         if(bitmap.pixelFormat == pixelFormat888)
240                         {
241                            colorPlane ++;
242                            if(colorPlane == 3)
243                            {
244                               colorPlane = 0;
245                               y++;
246                            }
247                         }
248                         else
249                            y++;
250                         iptr = y*bitmap.stride;
251                         if(div)
252                         {
253                            if(last == padder && count <= 63-div &&
254                              (colorPlane || bitmap.pixelFormat == pixelFormat8))
255                               count += div;
256                            else
257                            {
258                               pad = true;
259                               break;
260                            }
261                         }
262                         if(!colorPlane)
263                            break;
264                      }
265                      if(count >= 63)
266                         break;
267                   }
268                   if((count>1)||(last>=192))
269                      if(!f.Putc((byte)(count+192))) { errorWriting = true; break; }
270                   if(!f.Putc(last)) { errorWriting = true; break; }
271                   count = 0;
272                   if(pad)
273                   {
274                      pad = false;
275                      // Padding can mix with rest between 2 color planes in a scan line
276                      if(colorPlane || bitmap.pixelFormat == pixelFormat8)
277                      {
278                         count = div;
279                         last = padder;
280                      }
281                      else
282                      {
283                         if(div>1)
284                            if(f.Putc((byte)(div+192))) { errorWriting = true; break; }
285                         if(f.Putc(padder)) { errorWriting = true; break; }
286                      }
287                   }
288                }
289
290                if(!errorWriting)
291                {
292                   // Save palette
293                   if(bitmap.pixelFormat == pixelFormat8)
294                   {
295                      byte palette[768];
296                      // Shift palette 2 bytes left
297                      for(c=0; c<256; c++)
298                      {
299                         palette[c*3]   = bitmap.palette[c].color.r;
300                         palette[c*3+1] = bitmap.palette[c].color.g;
301                         palette[c*3+2] = bitmap.palette[c].color.b;
302                      }
303                      if(f.Putc(0x0C))
304                         if(f.Write(palette, 768, 1))
305                            result = true;
306                   }
307                   else
308                      result = true;
309                }
310             }
311             delete f;
312          }
313       }
314       return result;
315    }
316
317    ColorAlpha * LoadPalette(char * fileName, char * type)
318    {
319       ColorAlpha * result = null;
320       File f = FileOpen(fileName, read);
321
322       if(f)
323       {
324          PCXHead header;
325          if(f.Read(&header,sizeof(header),1))
326          {
327             if(header.colorPlanes == 1)
328             {
329                byte palette[768];
330
331                f.Seek(-768, end);
332                if(f.Read(palette, 768, 1))
333                {
334                   if((result = new ColorAlpha[256]))
335                   {
336                      uint c;
337                      for(c=0; c<256; c++)
338                         result[c] = ColorAlpha { 255, { palette[c*3], palette[c*3+1], palette[c*3+2] } };
339                   }
340                }
341             }
342          }
343          delete f;
344       }
345       return result;
346    }
347 }