OSDN Git Service

イニシャルコミット。
[marathon/ShapeFusion.git] / Shapes / ShapesElements.cpp
1 /*
2  * This file is part of ShapeFusion (Copyright 2000 Tito Dal Canton)
3  *
4  * ShapeFusion is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * ShapeFusion is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with ShapeFusion; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18 #include <fstream>
19 #include "ShapesElements.h"
20 #include "utilities.h"
21 #include "../LittleEndianBuffer.h"
22 #include <algorithm>
23 #include <memory>
24
25 // on-file struct sizes
26 #define SIZEOF_collection_definition            544
27 #define SIZEOF_rgb_color_value                          8
28 #define SIZEOF_bitmap_definition                        30
29 #define SIZEOF_low_level_shape_definition       36
30 #define SIZEOF_high_level_shape_definition      88
31 // NOTE about SIZEOF_high_level_shape_definition. The original engine
32 // sets SIZEOF_high_level_shape_definition=90 because the first frame
33 // index is included in the high_level_shape_definition. I don't like
34 // this, so I do my way (but be careful!)
35 #define COLLECTION_VERSION                                      3
36
37 // color flags
38 enum {
39         SELF_LUMINESCENT_COLOR  = 1 << 7
40 };
41 // bitmap flags
42 enum {
43         COLUMN_ORDER                    = 1 << 15,
44         TRANSPARENCY_ENABLED    = 1 << 14
45 };
46 // frame flags
47 enum {
48         X_MIRROR                        = 1 << 15,      // mirror along vertical axis
49         Y_MIRROR                        = 1 << 14,      // mirror along horizontal axis
50         KEYPOINT_OBSCURED       = 1 << 13       // "host obscures parasite" (RenderPlaceObjs.cpp)
51 };
52
53 #define FOUR_CHARS_TO_INT(a,b,c,d) (((unsigned int)(a) << 24) | ((unsigned int)(b) << 16) | ((unsigned int)(c) << 8) | (unsigned int)(d))
54
55 ShapesColor::ShapesColor(bool verbose): ShapesElement(verbose)
56 {
57
58 }
59
60 ShapesColor::ShapesColor(unsigned int r, unsigned int g, unsigned int b, unsigned int value, bool luminescent, bool verbose):
61          ShapesElement(verbose), mLuminescent(luminescent), mValue(value), mRed(r), mGreen(g), mBlue(b)
62 {
63
64 }
65
66 ShapesColor::~ShapesColor(void)
67 {
68
69 }
70
71 bool ShapesColor::operator==(const ShapesColor& other) const
72 {
73         return mLuminescent == other.mLuminescent 
74                 && mValue == other.mValue
75                 && mRed == other.mRed
76                 && mGreen == other.mGreen 
77                 && mBlue == other.mBlue;
78 }
79
80 BigEndianBuffer& ShapesColor::SaveObject(BigEndianBuffer& buffer)
81 {
82         unsigned char   flags = mLuminescent ? SELF_LUMINESCENT_COLOR : 0;
83         
84         buffer.WriteUChar(flags);
85         buffer.WriteUChar(mValue);
86         buffer.WriteUShort(mRed);
87         buffer.WriteUShort(mGreen);
88         buffer.WriteUShort(mBlue);
89         
90         return buffer;
91 }
92
93 BigEndianBuffer& ShapesColor::LoadObject(BigEndianBuffer& buffer)
94 {
95         unsigned char   flags;
96
97         flags = buffer.ReadUChar();
98         mValue = buffer.ReadUChar();
99         mRed = buffer.ReadUShort();
100         mGreen = buffer.ReadUShort();
101         mBlue = buffer.ReadUShort();
102         
103         mLuminescent = flags & SELF_LUMINESCENT_COLOR;
104         
105         mGoodData = true;
106         return buffer;
107 }
108
109 ShapesColorTable::ShapesColorTable(bool verbose): ShapesElement(verbose)
110 {
111
112 }
113
114 ShapesColorTable::ShapesColorTable(std::ifstream& ifs, wxString file_ext): ShapesElement(false)
115 {
116         // FIXME better error checking and reporting
117         if (file_ext == wxString(wxT("act"))) {
118                 // Photoshop binary color table file (Adobe Color Table, .act)
119                 ifs.seekg(0, std::ios::end);
120                 unsigned int    fileSize = ifs.tellg(),
121                                                 colorCount = 0;
122                 ifs.seekg(0, std::ios::beg);
123
124                 if (fileSize >= 3*256+4) {
125                         // extra info found - get the color count from that
126                         BigEndianBuffer extraInfo(4);
127
128                         ifs.seekg(3 * 256);
129                         ifs.read((char *)extraInfo.Data(), 4);
130                         colorCount = (unsigned int)extraInfo.ReadShort();
131                         if (colorCount > 256)
132                                 colorCount = 256;
133                 } else if (fileSize == 3*256) {
134                         // no extra info - exactly 256 colors and no transparent color
135                         colorCount = 256;
136                 } else {
137                         // we need at least 3*256 bytes
138                         wxLogError(wxT("This Adobe Color Table file has an invalid size: will not try to load it."));
139                         return;
140                 }
141                 ifs.seekg(0, std::ios::beg);
142                 for (unsigned int value = 0; value < colorCount; value++) {
143                         unsigned char   rgb[3];
144                         ifs.read((char *)rgb, 3);
145                         ShapesColor             *newColor = new ShapesColor(rgb[0]<<8, rgb[1]<<8, rgb[2]<<8, value);
146                         mColors.push_back(newColor);
147                 }
148         } else if (file_ext == wxString(wxT("gpl"))) {
149                 // Gimp ASCII palette file
150                 unsigned int    value = 0;
151
152                 while (ifs.good() && value < 256) {
153                         char                    buffer[256] = "";
154                         unsigned int    red, green, blue;
155
156                         ifs.getline(buffer, 255);
157                         if (sscanf(buffer, "%u %u %u", &red, &green, &blue) == 3) {
158                                 ShapesColor     *newColor = new ShapesColor(red<<8, green<<8, blue<<8, value);
159
160                                 mColors.push_back(newColor);
161                                 value++;
162                         }
163                 }
164         }
165 }
166
167 ShapesColorTable::~ShapesColorTable(void)
168 {
169         for (unsigned int i = 0; i < mColors.size(); i++)
170                 delete mColors[i];
171 }
172
173 static bool compareShapesColorPtrs(ShapesColor* rhs, ShapesColor* lhs)
174 {
175         return (*rhs == *lhs);
176 }
177
178 bool ShapesColorTable::operator==(const ShapesColorTable& other) const
179 {
180         if (mColors.size() == other.mColors.size()) {
181                 return std::equal(mColors.begin(), mColors.end(), other.mColors.begin(), compareShapesColorPtrs);
182         } else {
183                 return false;
184         }
185 }
186
187 BigEndianBuffer& ShapesColorTable::SaveObject(BigEndianBuffer& stream)
188 {
189         for (unsigned int i = 0; i < mColors.size(); i++) {
190                 ShapesColor     *color = mColors[i];
191
192                 color->SaveObject(stream);
193         }
194         return stream;
195 }
196
197 BigEndianBuffer& ShapesColorTable::SavePatch(BigEndianBuffer& buffer, int index)
198 {
199         buffer.WriteLong(FOUR_CHARS_TO_INT('c','t','a','b'));
200         buffer.WriteLong(index);
201         return SaveObject(buffer);
202 }
203
204 BigEndianBuffer& ShapesColorTable::LoadObject(BigEndianBuffer& buffer, unsigned int offset, unsigned int color_count)
205 {
206         buffer.Position(offset);
207
208         for (unsigned int i = 0; i < color_count; i++) {
209                 ShapesColor     *color = new ShapesColor(IsVerbose());
210
211                 color->LoadObject(buffer);
212                 
213                 if (!color->IsGood()) {
214                         wxLogError(wxT("[ShapesColorTable] Error loading color table"));
215                         mGoodData = false;
216                         return buffer;
217                 }
218                 mColors.push_back(color);
219         }
220         
221         mGoodData = true;
222         return buffer;
223 }
224
225 // export a color table to Gimp ASCII format
226 int ShapesColorTable::SaveToGimp(wxString path) const
227 {
228         std::ofstream   cts(path.fn_str(), std::ios::binary);
229
230         if (cts.good()) {
231                 cts << "GIMP Palette\n";
232                 cts << "Name: ShapeFusion exported palette\n";
233                 cts << "#\n";
234                 for (unsigned int i = 0; i < ColorCount(); i++) {
235                         ShapesColor     *color = GetColor(i);
236
237                         cts << (color->Red() >> 8) << ' ';
238                         cts << (color->Green() >> 8) << ' ';
239                         cts << (color->Blue() >> 8) << '\n';
240                 }
241                 cts.close();
242                 return 0;
243         } else {
244                 return -1;
245         }
246 }
247
248 // export a color table to Photoshop binary format
249 // (MacOS file type is '8BCT', extension '.act', Adobe Color Table)
250 int ShapesColorTable::SaveToPhotoshop(wxString path) const
251 {
252         std::ofstream   cts(path.fn_str(), std::ios::binary);
253
254         if (cts.good()) {
255                 BigEndianBuffer actData(3*256+4);
256                 
257                 actData.WriteZeroes(3*256+4);
258                 actData.Position(0);
259                 // write the RGB byte triplets for our colors
260                 for (unsigned int i = 0; i < ColorCount(); i++) {
261                         ShapesColor *color = GetColor(i);
262                         
263                         actData.WriteChar(color->Red() >> 8);
264                         actData.WriteChar(color->Green() >> 8);
265                         actData.WriteChar(color->Blue() >> 8);
266                 }
267                 // write the extra info at the end:
268                 // number of colors and index of the transparent color
269                 actData.Position(3*256);
270                 actData.WriteShort(ColorCount());
271                 actData.WriteShort(0);
272                 cts.write((char *)actData.Data(), actData.Size());
273                 cts.close();
274                 return 0;
275         } else {
276                 return -1;
277         }
278 }
279
280 ShapesBitmap::ShapesBitmap(bool verbose): ShapesElement(verbose), mPixels(NULL)
281 {
282 }
283
284 ShapesBitmap::ShapesBitmap(wxImage image, ShapesColorTable *colortable):
285         ShapesElement(false), mPixels(NULL)
286 {
287         FromImage(image, colortable);
288 }
289
290 ShapesBitmap::~ShapesBitmap(void)
291 {
292         if (mPixels)
293                 delete mPixels;
294         mPixels = NULL;
295 }
296
297 bool ShapesBitmap::operator==(const ShapesBitmap& other) const
298 {
299         if (mWidth == other.mWidth 
300             && mHeight == other.mHeight
301             && mBytesPerRow == other.mBytesPerRow
302             && mBitDepth == other.mBitDepth
303             && mColumnOrder == other.mColumnOrder
304             && mTransparent == other.mTransparent) {
305                 return std::equal(mPixels, mPixels + (mWidth * mHeight), other.mPixels);
306         } else {
307                 return false;
308         }
309 }
310
311 unsigned int ShapesBitmap::SizeInFile(void) const
312 {
313         unsigned int size = 0;
314
315         // scanline pointer placeholder
316         if (mColumnOrder)
317                 size += 4 * mWidth;
318         else
319                 size += 4 * mHeight;
320         if (mBytesPerRow == -1) {
321                 // compressed
322                 size += mWidth * 4;
323                 for (int x = 0; x < mWidth; x++) {
324                         unsigned char   *pp = mPixels + x;
325                         int                             p0 = -1,
326                                 p1;
327                         
328                         for (int y = 0; y < mHeight; y++) {
329                                 if (*pp != 0) {
330                                         p0 = y;
331                                         break;
332                                 }
333                                 pp += mWidth;
334                         }
335                         if (p0 == -1)
336                                 continue;       // no opaque mPixels in this column
337                         p1 = p0;
338                         pp = mPixels + x + mWidth * (mHeight - 1);
339                         for (int y = mHeight - 1; y >= 0; y--) {
340                                 if (*pp != 0) {
341                                         p1 = y;
342                                         break;
343                                 }
344                                 pp -= mWidth;
345                         }
346                         size += p1 - p0 + 1;
347                 }
348         } else {
349                 // plain
350                 size += mWidth * mHeight;
351         }
352
353         return size;
354 }
355
356 BigEndianBuffer& ShapesBitmap::SaveObject(BigEndianBuffer& buffer)
357 {
358         short   flags = 0;
359
360         if (mColumnOrder)
361                 flags |= COLUMN_ORDER;
362         if (mTransparent)
363                 flags |= TRANSPARENCY_ENABLED;
364         buffer.WriteShort(mWidth);
365         buffer.WriteShort(mHeight);
366         buffer.WriteShort(mBytesPerRow);
367         buffer.WriteShort(flags);
368         buffer.WriteShort(mBitDepth);
369         buffer.WriteZeroes(20 + 4 * (mColumnOrder ? mWidth : mHeight));
370         if (mBytesPerRow == -1) {
371                 // compress
372                 for (int x = 0; x < mWidth; x++) {
373                         unsigned char   *pp = mPixels + x;
374                         int                             p0 = -1, p1;
375                         
376                         for (int y = 0; y < mHeight; y++) {
377                                 if (*pp != 0) {
378                                         p0 = y;
379                                         break;
380                                 }
381                                 pp += mWidth;
382                         }
383                         if (p0 == -1) {
384                                 // no opaque pixels in this column
385                                 buffer.WriteShort(0);
386                                 buffer.WriteShort(0);
387                         } else {
388                                 // found opaque pixels, go on
389                                 p1 = p0;
390                                 pp = mPixels + x + mWidth * (mHeight - 1);
391                                 for (int y = mHeight - 1; y >= 0; y--) {
392                                         if (*pp != 0) {
393                                                 p1 = y;
394                                                 break;
395                                         }
396                                         pp -= mWidth;
397                                 }
398                                 buffer.WriteShort(p0);
399                                 buffer.WriteShort(p1 + 1);
400                                 pp = mPixels + x + p0 * mWidth;
401                                 for (int y = p0; y <= p1; y++) {
402                                         buffer.WriteChar(*pp);
403                                         pp += mWidth;
404                                 }
405                         }
406                 }
407         } else {
408                 if (mColumnOrder) {
409                         for (int x = 0; x < mWidth; x++) {
410                                 for (int y = 0; y < mHeight; y++)
411                                         buffer.WriteChar(*(mPixels + x + y * mWidth));
412                         }
413                 } else {
414                         buffer.WriteBlock(mWidth * mHeight, mPixels);
415                 }
416         }
417         return buffer;
418 }
419
420 BigEndianBuffer& ShapesBitmap::SavePatch(BigEndianBuffer& buffer, int index)
421 {
422         buffer.WriteLong(FOUR_CHARS_TO_INT('b', 'm', 'a', 'p'));
423         buffer.WriteLong(index);
424         buffer.WriteLong(SizeInFile() + SIZEOF_bitmap_definition);
425         return SaveObject(buffer);
426 }
427
428 BigEndianBuffer& ShapesBitmap::LoadObject(BigEndianBuffer& buffer, unsigned int offset)
429 {
430         buffer.Position(offset);
431
432         mWidth = buffer.ReadShort();
433         mHeight = buffer.ReadShort();
434         mBytesPerRow = buffer.ReadShort();
435
436         if (mWidth < 0) {
437                 wxLogError(wxT("[ShapesBitmap] Invalid bitmap width %d"), mWidth);
438                 return buffer;
439         }
440         if (mHeight < 0) {
441                 wxLogError(wxT("[ShapesBitmap] Invalid bitmap height %d"), mHeight);
442                 return buffer;
443         }
444         if (mBytesPerRow < -1) {
445                 wxLogError(wxT("[ShapesBitmap] Invalid bitmap bytes-per-row %d"), mBytesPerRow);
446                 return buffer;
447         }
448
449         short   flags = buffer.ReadShort();
450
451         mColumnOrder = flags & COLUMN_ORDER;
452         mTransparent = flags & TRANSPARENCY_ENABLED;
453
454         mBitDepth = buffer.ReadShort();
455         if (mBitDepth != 8) {
456                 wxLogError(wxT("[ShapesBitmap] Invalid bitmap depth %d"), mBitDepth);
457                 return buffer;
458         }
459
460         if (IsVerbose()) {
461                 wxLogDebug(wxT("[ShapesBitmap]         Width:           %d"), mWidth);
462                 wxLogDebug(wxT("[ShapesBitmap]         Height:          %d"), mHeight);
463                 wxLogDebug(wxT("[ShapesBitmap]         Bytes/Row:       %d"), mBytesPerRow);
464                 wxLogDebug(wxT("[ShapesBitmap]         Flags:           %d"), flags);
465                 wxLogDebug(wxT("[ShapesBitmap]         Bit Depth:       %d"), mBitDepth);
466         }
467
468         // skip unused fields and placeholders
469         unsigned int    numscanlines = (mColumnOrder ? mWidth : mHeight);
470
471         buffer.Position(buffer.Position() + 20 + numscanlines * 4);
472
473         // load pixel data
474         mPixels = new unsigned char[mWidth * mHeight];
475         if (mPixels == NULL) {
476                 wxLogError(wxT("[ShapesBitmap] Could not allocate pixel buffer"));
477                 return buffer;
478         }
479         if (mBytesPerRow > -1) {
480                 // uncompressed bitmap
481                 if (mColumnOrder) {
482                         // column order
483                         unsigned char   *dstp;
484                         
485                         for (int x = 0; x < mWidth; x++) {
486                                 dstp = mPixels + x;
487                                 for (int y = 0; y < mHeight; y++) {
488                                         *dstp = buffer.ReadUChar();
489                                         dstp += mWidth;
490                                 }
491                         }
492                 } else {
493                         // row order
494                         buffer.ReadBlock(mWidth * mHeight, mPixels);
495                 }
496         } else {
497                 // compressed bitmap (always column order)
498                 memset(mPixels, 0, mWidth * mHeight);
499                 for (int x = 0; x < mWidth; x++) {
500                         short                   p0, p1;
501                         unsigned char   *dstp;
502
503                         p0 = buffer.ReadShort();
504                         p1 = buffer.ReadShort();
505                         dstp = mPixels + x + p0 * mWidth;
506                         while (p0 != p1) {
507                                 *dstp = buffer.ReadUChar();
508                                 dstp += mWidth;
509                                 p0++;
510                         }
511                 }
512         }
513
514         mGoodData = true;
515         return buffer;
516 }
517
518 // export the ShapesBitmap to an indexed BMP file specified by path
519 void ShapesBitmap::SaveToBMP(wxString path, ShapesColorTable *colorTable) const
520 {
521         std::ofstream   stream(path.fn_str(), std::ios::binary);
522
523         if (stream.good()) {
524                 unsigned int    colorCount = colorTable->ColorCount();
525                 unsigned long   paddedWidth = (mWidth + 3) & 0xfffffffc;
526
527                 // header
528                 LittleEndianBuffer      headerBlock(54);
529
530                 headerBlock.WriteChar('B');
531                 headerBlock.WriteChar('M');
532                 headerBlock.WriteULong(54 + 4*colorCount + paddedWidth * mHeight);      // file size
533                 headerBlock.WriteULong(0);      // reserved
534                 headerBlock.WriteULong(54 + 4*colorCount);      // raster data offset
535                 headerBlock.WriteULong(40);     // info header size
536                 headerBlock.WriteULong(mWidth);
537                 headerBlock.WriteULong(mHeight);
538                 headerBlock.WriteUShort(1);     // plane count
539                 headerBlock.WriteUShort(8);     // bits per pixel
540                 headerBlock.WriteULong(0);      // no compression
541                 headerBlock.WriteULong(0);      // compressed size of image
542                 headerBlock.WriteULong(0);
543                 headerBlock.WriteULong(0);
544                 headerBlock.WriteULong(colorCount);     // FIXME
545                 headerBlock.WriteULong(0);      // FIXME
546                 stream.write((const char *)headerBlock.Data(), headerBlock.Size());
547                 
548                 // palette
549                 LittleEndianBuffer      paletteBlock(4*colorCount);
550
551                 for (unsigned int i = 0; i < colorCount; i++) {
552                         ShapesColor     *color = colorTable->GetColor(i);
553
554                         paletteBlock.WriteUChar(color->Blue() >> 8);
555                         paletteBlock.WriteUChar(color->Green() >> 8);
556                         paletteBlock.WriteUChar(color->Red() >> 8);
557                         paletteBlock.WriteUChar(0);
558                 }
559                 stream.write((const char *)paletteBlock.Data(), paletteBlock.Size());
560
561                 // 8-bit raster data
562                 LittleEndianBuffer      rasterBlock(paddedWidth * mHeight);
563
564                 for (int y = 0; y < mHeight; y++) {
565                         rasterBlock.WriteBlock(mWidth, mPixels + (mHeight - y - 1) * mWidth);
566                         rasterBlock.WriteZeroes(paddedWidth - mWidth);
567                 }
568                 stream.write((const char *)rasterBlock.Data(), rasterBlock.Size());
569
570                 stream.close();
571         }
572 }
573
574 // export the ShapesBitmap mask to a 1-bit BMP file specified by path
575 void ShapesBitmap::SaveMaskToBMP(wxString path) const
576 {
577         std::ofstream   stream(path.fn_str(), std::ios::binary);
578
579         if (stream.good()) {
580                 unsigned long   rowBytes = ((mWidth + 31) & 0xffffffe0) >> 3;
581
582                 // header
583                 LittleEndianBuffer      headerBlock(54);
584                 
585                 headerBlock.WriteChar('B');
586                 headerBlock.WriteChar('M');
587                 headerBlock.WriteULong(54 + 4*2 + rowBytes * mHeight);  // file size
588                 headerBlock.WriteULong(0);      // reserved
589                 headerBlock.WriteULong(54 + 4*2);       // raster data offset
590                 headerBlock.WriteULong(40);     // info header size
591                 headerBlock.WriteULong(mWidth);
592                 headerBlock.WriteULong(mHeight);
593                 headerBlock.WriteUShort(1);     // plane count
594                 headerBlock.WriteUShort(1);     // bits per pixel
595                 headerBlock.WriteULong(0);      // no compression
596                 headerBlock.WriteULong(0);      // compressed size of image
597                 headerBlock.WriteULong(0);
598                 headerBlock.WriteULong(0);
599                 headerBlock.WriteULong(2);
600                 headerBlock.WriteULong(0);
601                 stream.write((const char *)headerBlock.Data(), headerBlock.Size());
602
603                 // black & white palette
604                 LittleEndianBuffer      paletteBlock(4*2);
605
606                 paletteBlock.WriteUChar(0);
607                 paletteBlock.WriteUChar(0);
608                 paletteBlock.WriteUChar(0);
609                 paletteBlock.WriteUChar(0);
610                 paletteBlock.WriteUChar(255);
611                 paletteBlock.WriteUChar(255);
612                 paletteBlock.WriteUChar(255);
613                 paletteBlock.WriteUChar(0);
614                 stream.write((const char *)paletteBlock.Data(), paletteBlock.Size());
615
616                 // 1-bit raster data
617                 LittleEndianBuffer      rasterBlock(rowBytes * mHeight);
618                 
619                 rasterBlock.WriteZeroes(rowBytes * mHeight);
620                 for (unsigned int y = 0; (int)y < mHeight; y++) {
621                         unsigned char   *p = mPixels + y * mWidth,
622                                                         bit = 128,
623                                                         byte = 0;
624
625                         rasterBlock.Position((mHeight - y - 1) * rowBytes);
626                         for (unsigned int x = 0; (int)x < mWidth; x++) {
627                                 if (*p++ != 0)
628                                         byte |= bit;
629                                 bit >>= 1;
630                                 if (bit == 0) {
631                                         bit = 128;
632                                         rasterBlock.WriteUChar(byte);
633                                         byte = 0;
634                                 }
635                         }
636                         if (bit != 128)
637                                 rasterBlock.WriteUChar(byte);
638                 }
639                 stream.write((const char *)rasterBlock.Data(), rasterBlock.Size());
640
641                 stream.close();
642          }
643 }
644
645 void ShapesBitmap::ClipboardCopy(ShapesColorTable* colortable) const
646 {
647         if (wxTheClipboard->Open()) {
648                 // create an RGBA wxImage
649                 wxImage image;
650                 image.Create(mWidth, mHeight, false);
651
652                 unsigned char* src = mPixels;
653                 unsigned char* dst = image.GetData();
654                 for (int x = 0; x < mWidth; ++x) {
655                         for (int y = 0; y < mHeight; ++y) {
656                                 unsigned char c = *src++;
657                                 ShapesColor* color = colortable->GetColor(c);
658                                 *dst++ = color->Red();
659                                 *dst++ = color->Green();
660                                 *dst++ = color->Blue();
661                         }
662                 }
663
664                 wxTheClipboard->SetData(new wxBitmapDataObject(wxBitmap(image)));
665                 wxTheClipboard->Close();
666         }
667 }
668
669 void ShapesBitmap::ClipboardPaste(ShapesColorTable* colortable)
670 {
671         if (wxTheClipboard->Open()) {
672                 wxBitmapDataObject clipboardData;
673                 if (wxTheClipboard->GetData(clipboardData)) {
674                         FromImage(clipboardData.GetBitmap().ConvertToImage(), colortable);
675                 }
676                 wxTheClipboard->Close();
677         }
678 }
679
680 void ShapesBitmap::FromImage(wxImage image, ShapesColorTable* colortable)
681 {
682         mWidth = image.GetWidth();
683         mHeight = image.GetHeight();
684         mBytesPerRow = image.GetWidth();
685         mBitDepth = 8;
686         mColumnOrder = true;
687         mTransparent = false;
688         
689         if (mPixels) {
690                 delete mPixels;
691         }
692
693         unsigned char* srcpixels = image.GetData(), *src = srcpixels, *dst;
694
695         mPixels = new unsigned char[mWidth * mHeight];
696         if (mPixels == NULL) {
697                 wxLogError(wxT("Could not allocate new %dx%d bitmap\n"), mWidth, mHeight);
698                 return;
699         }
700         dst = mPixels;
701         // quantize from 8-bit RGB pixels to an indexed bitmap
702         for (int i = 0; i < mWidth * mHeight; i++) {
703                 unsigned char   r = *src++, g = *src++, b = *src++,
704                                                 best_value = 0;
705                 float                   min_dist = 0;
706
707                 for (unsigned int j = 0; j < colortable->ColorCount(); j++) {
708                         unsigned short  ct_r = colortable->GetColor(j)->Red(),
709                                                         ct_g = colortable->GetColor(j)->Green(),
710                                                         ct_b = colortable->GetColor(j)->Blue();
711                         float                   dist = ColourDistance(r/255.0, g/255.0, b/255.0,
712                                                                                                         ct_r/65535.0, ct_g/65535.0, ct_b/65535.0);
713
714                         if (dist < min_dist || j == 0) {
715                                 min_dist = dist;
716                                 best_value = colortable->GetColor(j)->Value();
717                         }
718                 }
719                 *dst++ = best_value;
720                 if (best_value == 0)
721                         mTransparent = true;    // guess the user will want transparency
722         }
723 }
724
725 ShapesFrame::ShapesFrame(bool verbose): ShapesElement(verbose)
726 {
727         // initialize values to something reasonable
728         mBitmapIndex = -1;
729         mXmirror = mYmirror = mKeypointObscured = false;
730         mMinimumLightIntensity = 0;
731         mOriginX = mOriginY = mKeyX = mKeyY = 0;
732         mScaleFactor = 0;
733         mWorldLeft = mWorldRight = mWorldTop = mWorldBottom = 0;
734         mWorldX0 = mWorldY0 = 0;
735 }
736
737 ShapesFrame::~ShapesFrame(void)
738 {
739
740 }
741
742 bool ShapesFrame::operator==(const ShapesFrame& other) const
743 {
744         return mXmirror == other.mXmirror
745                 && mYmirror == other.mYmirror
746                 && mKeypointObscured == other.mKeypointObscured
747                 && mMinimumLightIntensity == other.mMinimumLightIntensity
748                 && mBitmapIndex == other.mBitmapIndex
749                 && mScaleFactor == other.mScaleFactor
750                 && mOriginX == other.mOriginX
751                 && mOriginY == other.mOriginY
752                 && mKeyX == other.mKeyX
753                 && mKeyY == other.mKeyY;
754 }
755
756 BigEndianBuffer& ShapesFrame::SaveObject(BigEndianBuffer& buffer)
757 {
758         unsigned short  flags = 0;
759         float                   mli_integer, mli_fractional;
760         long                    min_light_intensity = 0;
761         
762         if (mXmirror)
763                 flags |= X_MIRROR;
764         if (mYmirror)
765                 flags |= Y_MIRROR;
766         if (mKeypointObscured)
767                 flags |= KEYPOINT_OBSCURED;
768                 
769         min_light_intensity = static_cast<long>(mMinimumLightIntensity * 65536.0 + 0.5); // convert float to 16.16 fixed
770
771         buffer.WriteUShort(flags);
772         buffer.WriteLong(min_light_intensity);
773         buffer.WriteShort(mBitmapIndex);
774         buffer.WriteShort(mOriginX);
775         buffer.WriteShort(mOriginY);
776         buffer.WriteShort(mKeyX);
777         buffer.WriteShort(mKeyY);
778         buffer.WriteShort(mWorldLeft);
779         buffer.WriteShort(mWorldRight);
780         buffer.WriteShort(mWorldTop);
781         buffer.WriteShort(mWorldBottom);
782         buffer.WriteShort(mWorldX0);
783         buffer.WriteShort(mWorldY0);
784         buffer.WriteZeroes(8);
785         
786         return buffer;
787 }
788
789 BigEndianBuffer& ShapesFrame::SavePatch(BigEndianBuffer& buffer, int index)
790 {
791         buffer.WriteLong(FOUR_CHARS_TO_INT('l','l','s','h'));
792         buffer.WriteLong(index);
793         return SaveObject(buffer);
794 }
795
796 BigEndianBuffer& ShapesFrame::LoadObject(BigEndianBuffer& buffer, unsigned int offset)
797 {
798         unsigned short  flags;
799         wxInt32                 mli_fixed;
800
801         buffer.Position(offset);
802
803         flags = buffer.ReadUShort();
804
805         mXmirror = flags & X_MIRROR;
806         mYmirror = flags & Y_MIRROR;
807         mKeypointObscured = flags & KEYPOINT_OBSCURED;
808         
809         mli_fixed = buffer.ReadLong();
810         
811         mMinimumLightIntensity = mli_fixed / 65536.0; // convert 16.16 fixed to float
812         
813         mBitmapIndex = buffer.ReadShort();
814         mOriginX = buffer.ReadShort();
815         mOriginY = buffer.ReadShort();
816         mKeyX = buffer.ReadShort();
817         mKeyY = buffer.ReadShort();
818         mWorldLeft = buffer.ReadShort();
819         mWorldRight = buffer.ReadShort();
820         mWorldTop = buffer.ReadShort();
821         mWorldBottom = buffer.ReadShort();
822         mWorldX0 = buffer.ReadShort();
823         mWorldY0 = buffer.ReadShort();
824         
825         if (IsVerbose()) {
826                 wxLogDebug(wxT("[ShapesFrame]         Flags:                    %d"), flags);
827                 wxLogDebug(wxT("[ShapesFrame]         Min. Light Intensity:     %f"), mMinimumLightIntensity);
828                 wxLogDebug(wxT("[ShapesFrame]         Bitmap Index:     %d"), mBitmapIndex);
829                 wxLogDebug(wxT("[ShapesFrame]         Origin (X):               %d"), mOriginX);
830                 wxLogDebug(wxT("[ShapesFrame]         Origin (Y):               %d"), mOriginY);
831                 wxLogDebug(wxT("[ShapesFrame]         Key (X):          %d"), mKeyX);
832                 wxLogDebug(wxT("[ShapesFrame]         Key (Y):          %d"), mKeyY);
833                 wxLogDebug(wxT("[ShapesFrame]         World (Left):     %d"), mWorldLeft);
834                 wxLogDebug(wxT("[ShapesFrame]         World (Right):    %d"), mWorldRight);
835                 wxLogDebug(wxT("[ShapesFrame]         World (Top):      %d"), mWorldTop);
836                 wxLogDebug(wxT("[ShapesFrame]         World (Bottom):   %d"), mWorldBottom);
837                 wxLogDebug(wxT("[ShapesFrame]         World (X0):               %d"), mWorldX0);
838                 wxLogDebug(wxT("[ShapesFrame]         World (Y0):               %d"), mWorldY0);
839         }
840
841         mGoodData = true;
842         return buffer;
843 }
844
845 ShapesSequence::ShapesSequence(bool verbose): ShapesElement(verbose)
846 {
847         // initialize values to something reasonable
848         mType = 0;
849         mFlags = 0;
850         mName = _T("new sequence");
851         mNumberOfViews = UNANIMATED;
852         mFramesPerView = 0;
853         mTicksPerFrame = 1;
854         mKeyFrame = 0;
855         mTransferMode = 0;
856         mTransferModePeriod = 1;
857         mFirstFrameSound = mKeyFrameSound = mLastFrameSound = -1;
858         mPixelsToWorld = 0;
859         mLoopFrame = 0;
860 }
861
862 ShapesSequence::~ShapesSequence(void)
863 {
864
865 }
866
867 bool ShapesSequence::operator==(const ShapesSequence& other) const
868 {
869         if (mType == other.mType 
870             && mFlags == other.mFlags 
871             && mName == other.mName
872             && mNumberOfViews == other.mNumberOfViews
873             && mFramesPerView == other.mFramesPerView
874             && mTicksPerFrame == other.mTicksPerFrame
875             && mKeyFrame == other.mKeyFrame
876             && mTransferMode == other.mTransferMode
877             && mTransferModePeriod == other.mTransferModePeriod
878             && mFirstFrameSound == other.mFirstFrameSound
879             && mKeyFrameSound == other.mKeyFrameSound
880             && mLastFrameSound == other.mLastFrameSound
881             && mPixelsToWorld == other.mPixelsToWorld
882             && mLoopFrame == other.mLoopFrame
883             && mFrameIndexes.size() == other.mFrameIndexes.size()) {
884                 return std::equal(mFrameIndexes.begin(), mFrameIndexes.end(), other.mFrameIndexes.begin());
885         } else {
886                 return false;
887         }
888 }
889
890 unsigned int ShapesSequence::SizeInFile() const
891 {
892         return 2 * (FrameIndexCount() + 1);
893 }
894
895 BigEndianBuffer& ShapesSequence::SaveObject(BigEndianBuffer& buffer)
896 {
897         char            name[33] = "";
898
899         // Ugh--wxGTK doesn't recognize wxFONTENCODING_MACROMAN, and
900         // if you try to create a new wsCSConv with it, successive
901         // attempts to create wxT("macintosh") fail. Windows, on the
902         // other hand, doesn't recognize wxT("macintosh"), although if
903         // you ask it if seqnameconv.IsOk() it returns true and only
904         // fails when you try to convert something. So, #ifdef to
905         // success
906 #ifdef __WIN32__
907         wxCSConv seqnameconv(wxFONTENCODING_MACROMAN);
908 #else
909         wxCSConv seqnameconv(wxT("macintosh"));
910 #endif
911
912         buffer.WriteShort(mType);
913         buffer.WriteUShort(mFlags);
914         buffer.WriteChar(mName.Length());
915         strncpy(name, seqnameconv.cWC2MB(mName.wc_str(*wxConvCurrent)), 33);
916         buffer.WriteBlock(33, (unsigned char *)name);
917         buffer.WriteShort(mNumberOfViews);
918         buffer.WriteShort(mFramesPerView);
919         buffer.WriteShort(mTicksPerFrame);
920         buffer.WriteShort(mKeyFrame);
921         buffer.WriteShort(mTransferMode);
922         buffer.WriteShort(mTransferModePeriod);
923         buffer.WriteShort(mFirstFrameSound);
924         buffer.WriteShort(mKeyFrameSound);
925         buffer.WriteShort(mLastFrameSound);
926         buffer.WriteShort(mPixelsToWorld);
927         buffer.WriteShort(mLoopFrame);
928         buffer.WriteZeroes(28);
929         for (unsigned int i = 0; i < mFrameIndexes.size(); i++)
930                 buffer.WriteShort(mFrameIndexes[i]);
931         buffer.WriteShort(0);
932         
933         return buffer;
934 }
935
936 BigEndianBuffer& ShapesSequence::SavePatch(BigEndianBuffer& buffer, int index)
937 {
938         buffer.WriteLong(FOUR_CHARS_TO_INT('h','l','s','h'));
939         buffer.WriteLong(index);
940         buffer.WriteLong(SizeInFile() + SIZEOF_high_level_shape_definition);
941         return SaveObject(buffer);
942 }
943
944 BigEndianBuffer& ShapesSequence::LoadObject(BigEndianBuffer& buffer, long offset)
945 {
946         buffer.Position(offset);
947         mType = buffer.ReadShort();
948         mFlags = buffer.ReadUShort();
949         
950         // the mName is a Mac Pascal string, not a C string (length,chars)
951         unsigned char   namelen = buffer.ReadUChar();
952
953         if (namelen > 32) {
954                 wxLogError(wxT("[ShapesSequence] Sequence name too long (%d/32)"), namelen);
955                 return buffer;
956         }
957
958         char            name[33];
959 #ifdef __WIN32__
960         wxCSConv seqnameconv(wxFONTENCODING_MACROMAN);
961 #else
962         wxCSConv seqnameconv(wxT("macintosh"));
963 #endif
964
965         buffer.ReadBlock(33, (unsigned char *)name);
966         name[namelen] = 0;
967         mName = wxString(seqnameconv.cMB2WC(name), *wxConvCurrent, namelen);
968
969         mNumberOfViews = buffer.ReadShort();
970         mFramesPerView = buffer.ReadShort();
971         mTicksPerFrame = buffer.ReadShort();
972         mKeyFrame = buffer.ReadShort();
973         mTransferMode = buffer.ReadShort();
974         mTransferModePeriod = buffer.ReadShort();
975         mFirstFrameSound = buffer.ReadShort();
976         mKeyFrameSound = buffer.ReadShort();
977         mLastFrameSound = buffer.ReadShort();
978         mPixelsToWorld = buffer.ReadShort();
979         mLoopFrame = buffer.ReadShort();
980         buffer.Position(buffer.Position() + 28);
981
982         if (IsVerbose()) {
983                 wxLogDebug(wxT("[ShapesSequence]         Type:                                  %d"), mType);
984                 wxLogDebug(wxT("[ShapesSequence]         Flags:                                 %d"), mFlags);
985                 wxLogDebug(wxT("[ShapesSequence]         Name:                                  %s"), mName.c_str());
986                 wxLogDebug(wxT("[ShapesSequence]         Number of Views:               %d"), mNumberOfViews);
987                 wxLogDebug(wxT("[ShapesSequence]         Frames/Views:                  %d"), mFramesPerView);
988                 wxLogDebug(wxT("[ShapesSequence]         Ticks/Frame:                   %d"), mTicksPerFrame);
989                 wxLogDebug(wxT("[ShapesSequence]         Key Frame:                             %d"), mKeyFrame);
990                 wxLogDebug(wxT("[ShapesSequence]         Transfer Mode:                 %d"), mTransferMode);
991                 wxLogDebug(wxT("[ShapesSequence]         Transfer Mode Period:  %d"), mTransferModePeriod);
992                 wxLogDebug(wxT("[ShapesSequence]         First Frame Sound:             %d"), mFirstFrameSound);
993                 wxLogDebug(wxT("[ShapesSequence]         Key Frame Sound:               %d"), mKeyFrameSound);
994                 wxLogDebug(wxT("[ShapesSequence]         Last Frame Sound:              %d"), mLastFrameSound);
995                 wxLogDebug(wxT("[ShapesSequence]         Pixels to World:               %d"), mPixelsToWorld);
996                 wxLogDebug(wxT("[ShapesSequence]         Loop Frame:                    %d"), mLoopFrame);
997         }
998
999         if (mNumberOfViews < 0 || mFramesPerView < 0) {
1000                 wxLogError(wxT("[ShapesSequence] Invalid sequence type parameters: numberOfViews=%d, framesPerView=%d"),
1001                                                 mNumberOfViews, mFramesPerView);
1002                 return buffer;
1003         }
1004         // guess these shouldn't be < 0, but RED Shapes have a case with mKeyFrame=-1
1005         if (mKeyFrame < -1 || mLoopFrame < -1) {
1006                 wxLogError(wxT("[ShapesSequence] Invalid key/loop frame values in sequence data: keyFrame=%d, loopFrame=%d"),
1007                                                 mKeyFrame, mLoopFrame);
1008                 return buffer;
1009         }
1010         if (mFirstFrameSound < -1 || mKeyFrameSound < -1 || mLastFrameSound < -1) {
1011                 wxLogError(wxT("[ShapesSequence] Invalid sound values in sequence data: firstFrameSound=%d, keyFrameSound=%d, lastFrameSound=%d"),
1012                                                 mFirstFrameSound, mKeyFrameSound, mLastFrameSound);
1013                 return buffer;
1014         }
1015
1016         // load frame indexes
1017         int     n = ActualNumberOfViews(mNumberOfViews) * mFramesPerView;
1018
1019         if (n > 0) {
1020                 for (int k = 0; k < n; k++)
1021                         mFrameIndexes.push_back(buffer.ReadShort());
1022         }
1023
1024         buffer.ReadShort();     // terminating index (usually 0 but can be garbage)
1025
1026         mGoodData = true;
1027         return buffer;
1028 }
1029
1030 // given a high_level_shape_definition.mNumberOfViews value,
1031 // return the real number of views
1032 int ActualNumberOfViews(int t)
1033 {
1034         switch (t) {
1035                 case UNANIMATED:
1036                 case ANIMATED_1:
1037                         return 1;
1038                 case ANIMATED_3TO4:
1039                 case ANIMATED_4:
1040                         return 4;
1041                 case ANIMATED_3TO5:
1042                 case ANIMATED_5:
1043                         return 5;
1044                 case ANIMATED_2TO8:
1045                 case ANIMATED_5TO8:
1046                 case ANIMATED_8:
1047                         return 8;
1048                 default:
1049                         wxLogError(wxT("[ShapesSequence] Unknown sequence type %d, don't know the number of views"), t);
1050                         return t;
1051         }
1052         return -1;
1053 }
1054
1055 ShapesChunk::ShapesChunk(bool verbose): ShapesElement(verbose)
1056 {
1057         
1058 }
1059
1060 ShapesChunk::~ShapesChunk(void)
1061 {
1062         Clear();
1063 }
1064
1065 void ShapesChunk::Clear(void)
1066 {
1067         unsigned int i;
1068
1069         for (i = 0; i < mColorTables.size(); i++)
1070                 delete mColorTables[i];
1071         for (i = 0; i < mSequences.size(); i++)
1072                 delete mSequences[i];
1073         for (i = 0; i < mFrames.size(); i++)
1074                 delete mFrames[i];
1075         for (i = 0; i < mBitmaps.size(); i++)
1076                 delete mBitmaps[i];
1077
1078         mColorTables.clear();
1079         mSequences.clear();
1080         mFrames.clear();
1081         mBitmaps.clear();
1082
1083         mGoodData = false;
1084 }
1085
1086 static bool compareColorTablePtrs(ShapesColorTable* lhs, ShapesColorTable* rhs) {
1087         return *lhs == *rhs;
1088 }
1089
1090 static bool compareSequencePtrs(ShapesSequence* lhs, ShapesSequence* rhs) {
1091         return *lhs == *rhs;
1092 }
1093
1094 static bool compareFramePtrs(ShapesFrame* lhs, ShapesFrame* rhs) {
1095         return *lhs == *rhs;
1096 }
1097
1098 static bool compareBitmapPtrs(ShapesBitmap* lhs, ShapesBitmap* rhs) {
1099         return *lhs == *rhs;
1100 }
1101
1102 bool ShapesChunk::operator==(const ShapesChunk& other) const
1103 {
1104         if (mVersion == other.mVersion
1105             && mType == other.mType
1106             && mFlags == other.mFlags 
1107             && mPixelsToWorld == other.mPixelsToWorld
1108             && mColorTables.size() == other.mColorTables.size() 
1109             && mSequences.size() == other.mSequences.size()
1110             && mFrames.size() == other.mFrames.size()
1111             && mBitmaps.size() == other.mBitmaps.size()) {
1112                 if (!std::equal(mColorTables.begin(), mColorTables.end(), other.mColorTables.begin(), compareColorTablePtrs))
1113                         return false;
1114                 if (!std::equal(mSequences.begin(), mSequences.end(), other.mSequences.begin(), compareSequencePtrs))
1115                         return false;
1116                 if (!std::equal(mFrames.begin(), mFrames.end(), other.mFrames.begin(), compareFramePtrs))
1117                         return false;
1118                 return (std::equal(mBitmaps.begin(), mBitmaps.end(), other.mBitmaps.begin(), compareBitmapPtrs));
1119         } else {
1120                 return false;
1121         }
1122 }
1123
1124 ShapesColorTable* ShapesChunk::GetColorTable(unsigned int index) const
1125 {
1126         if (index < 0 || index > mColorTables.size())
1127                 return NULL;
1128         return mColorTables[index];
1129 }
1130
1131 ShapesBitmap* ShapesChunk::GetBitmap(unsigned int index) const
1132 {
1133         if (index < 0 || index > mBitmaps.size())
1134                 return NULL;
1135         return mBitmaps[index];
1136 }
1137
1138 ShapesFrame* ShapesChunk::GetFrame(unsigned int index) const
1139 {
1140         if (index < 0 || index > mFrames.size())
1141                 return NULL;
1142         return mFrames[index];
1143 }
1144
1145 ShapesSequence* ShapesChunk::GetSequence(unsigned int index) const
1146 {
1147         if (index < 0 || index > mSequences.size())
1148                 return NULL;
1149         return mSequences[index];
1150 }
1151
1152 void ShapesChunk::InsertColorTable(ShapesColorTable *ct)
1153 {
1154         mColorTables.push_back(ct);
1155 }
1156
1157 void ShapesChunk::DeleteColorTable(unsigned int ct)
1158 {
1159         mColorTables.erase(mColorTables.begin() + ct);
1160 }
1161
1162 void ShapesChunk::InsertBitmap(ShapesBitmap *b)
1163 {
1164         mBitmaps.push_back(b);
1165 }
1166
1167 void ShapesChunk::DeleteBitmap(unsigned int b)
1168 {
1169         if (b < mBitmaps.size()) {
1170                 // preserve existing frame-bitmap associations and associate
1171                 // a null bitmap to frames using the bitmap we're deleting
1172                 for (unsigned int i = 0; i < mFrames.size(); i++) {
1173                         short   bitmap_index = mFrames[i]->BitmapIndex();
1174
1175                         if (bitmap_index == (int)b)
1176                                 mFrames[i]->SetBitmapIndex(-1);
1177                         else if (bitmap_index > (int)b)
1178                                 mFrames[i]->SetBitmapIndex(bitmap_index - 1);
1179                 }
1180                 // now actually delete the bitmap
1181                 mBitmaps.erase(mBitmaps.begin() + b);
1182         }
1183 }
1184
1185 void ShapesChunk::InsertFrame(ShapesFrame *f)
1186 {
1187         mFrames.push_back(f);
1188 }
1189
1190 void ShapesChunk::DeleteFrame(unsigned int f)
1191 {
1192         if (f < mFrames.size()) {
1193                 // preserve existing sequence-frame associations and
1194                 // unreference this frame index from any sequence using it
1195                 for (unsigned int i = 0; i < mSequences.size(); i++) {
1196                         for (unsigned int j = 0; j < mSequences[i]->FrameIndexCount(); j++) {
1197                                 short frame_index = mSequences[i]->GetFrameIndex(j);
1198
1199                                 if (frame_index == (int)f)
1200                                         mSequences[i]->SetFrameIndex(j, -1);
1201                                 else if (frame_index > (int)f)
1202                                         mSequences[i]->SetFrameIndex(j, frame_index - 1);
1203                         }
1204                 }
1205                 // now actually delete the frame
1206                 mFrames.erase(mFrames.begin() + f);
1207         }
1208 }
1209
1210 void ShapesChunk::InsertSequence(ShapesSequence *s)
1211 {
1212         mSequences.push_back(s);
1213 }
1214
1215 void ShapesChunk::DeleteSequence(unsigned int s)
1216 {
1217         if (s < mSequences.size())
1218                 mSequences.erase(mSequences.begin() + s);
1219 }
1220
1221 void ShapesChunk::ClipboardCopy()
1222 {
1223         if (wxTheClipboard->Open()) {
1224                 size_t size = SizeInFile();
1225                 unsigned char* data = new unsigned char[size];
1226                 BigEndianBuffer buffer(data, size);
1227                 
1228                 SaveObject(buffer);
1229                 
1230                 wxCustomDataObject* dataObject = new wxCustomDataObject(wxDataFormat(wxT("application/vnd.shapefusion.shapeschunk")));
1231                 dataObject->TakeData(size, data);
1232                 
1233                 wxTheClipboard->SetData(dataObject);
1234
1235                 wxTheClipboard->Close();
1236         }
1237 }
1238
1239 void ShapesChunk::ClipboardPaste()
1240 {
1241         if (wxTheClipboard->Open()) {
1242                 wxCustomDataObject dataObject(wxDataFormat(wxT("application/vnd.shapefusion.shapeschunk")));
1243                 if (wxTheClipboard->GetData(dataObject)) {
1244                         BigEndianBuffer buffer(reinterpret_cast<unsigned char *>(dataObject.GetData()), dataObject.GetSize());
1245
1246                         Clear();
1247
1248                         LoadObject(buffer);
1249                 }
1250
1251                 wxTheClipboard->Close();
1252         }
1253 }
1254
1255 unsigned int ShapesChunk::SizeInFile(void) const
1256 {
1257         unsigned int    bitmap_count = BitmapCount(),
1258                                         frame_count = FrameCount(),
1259                                         sequence_count = SequenceCount(),
1260                                         color_table_count = ColorTableCount(),
1261                                         size;
1262         
1263         // size of our definition
1264         size = SIZEOF_collection_definition;
1265         // add contribute of sequence offset table
1266         size += 4 * sequence_count;
1267         // add contribute of bitmap offset table
1268         size += 4 * bitmap_count;
1269         // add contribute of frame offset table
1270         size += 4 * frame_count;
1271         // add contribute of color tables
1272         if (color_table_count > 0)
1273                 size += SIZEOF_rgb_color_value * color_table_count * GetColorTable(0)->ColorCount();
1274         // add contribute of bitmaps
1275         size += SIZEOF_bitmap_definition * bitmap_count;
1276         for (unsigned int i = 0; i < bitmap_count; i++) {
1277                 ShapesBitmap    *bitmap = mBitmaps[i];
1278
1279                 size += bitmap->SizeInFile();
1280         }               
1281         // add contribute of frame definitions
1282         size += SIZEOF_low_level_shape_definition * frame_count;
1283         // add contribute of sequence definitions (and following frame indexes)
1284         size += SIZEOF_high_level_shape_definition * sequence_count;
1285         for (unsigned int i = 0 ; i < sequence_count ; i++) {
1286                 ShapesSequence  *seq = mSequences[i];
1287
1288                 size += seq->SizeInFile();
1289         }
1290
1291         return size;
1292 }
1293
1294 unsigned int ShapesChunk::SizeInPatch(const ShapesChunk* other) const {
1295         unsigned int size = 4; // 'cldf'
1296
1297         size += SIZEOF_collection_definition;
1298         
1299         for (unsigned int i = 0; i < mColorTables.size(); ++i) {
1300                 if (other == NULL || i >= other->mColorTables.size() || *mColorTables[i] != *other->mColorTables[i]) {
1301                         size += SIZEOF_rgb_color_value * mColorTables[i]->ColorCount() + 8;
1302                 }
1303         }
1304
1305         for (unsigned int i = 0; i < mSequences.size(); ++i) {
1306                 if (other == NULL || i >= other->mSequences.size() || *mSequences[i] != *other->mSequences[i]) {
1307                         size += SIZEOF_high_level_shape_definition + mSequences[i]->SizeInPatch();
1308                 }
1309         }
1310
1311
1312         for (unsigned int i = 0; i < mFrames.size(); ++i) {
1313                 if (other == NULL || i >= other->mFrames.size() || *mFrames[i] != *other->mFrames[i]) {
1314                         size += SIZEOF_low_level_shape_definition + 8;
1315                 }
1316         }
1317         
1318         for (unsigned int i = 0; i < mBitmaps.size(); ++i) {
1319                 if (other == NULL || i >= other->mBitmaps.size() || *mBitmaps[i] != *other->mBitmaps[i]) {
1320                         size += SIZEOF_bitmap_definition + mBitmaps[i]->SizeInPatch();
1321                 }
1322         }
1323
1324         return size;
1325 }
1326
1327 BigEndianBuffer& ShapesChunk::SaveObject(BigEndianBuffer& buffer)
1328 {
1329         unsigned int    bitmap_count = BitmapCount(),
1330                                         frame_count = FrameCount(),
1331                                         sequence_count = SequenceCount(),
1332                                         i;
1333         long                    sequence_table_offset,
1334                                         sequence_offsets[sequence_count],
1335                                         frame_table_offset,
1336                                         frame_offsets[frame_count],
1337                                         bitmap_table_offset,
1338                                         bitmap_offsets[bitmap_count];
1339                                 
1340         // skip the collection definition, we'll fill it at the end
1341         buffer.Position(SIZEOF_collection_definition);
1342         // write color tables
1343         for (i = 0; i < ColorTableCount(); i++)
1344                 mColorTables[i]->SaveObject(buffer);
1345         
1346         // write sequences
1347         sequence_table_offset = buffer.Position();
1348         if (sequence_count > 0) {
1349                 buffer.Position(buffer.Position() + sequence_count * 4);
1350                 
1351                 for (i = 0; i < sequence_count; i++) {
1352                         sequence_offsets[i] = buffer.Position();
1353                         
1354                         mSequences[i]->SaveObject(buffer);
1355                 }
1356         }
1357         // write frames
1358         frame_table_offset = buffer.Position();
1359         buffer.Position(buffer.Position() + frame_count * 4);
1360         for (i = 0; i < frame_count; i++) {
1361                 frame_offsets[i] = buffer.Position();
1362                 
1363                 mFrames[i]->SaveObject(buffer);
1364         }
1365         
1366         // write bitmaps
1367         bitmap_table_offset = buffer.Position();
1368         buffer.Position(buffer.Position() + bitmap_count * 4);
1369         for (i = 0; i < bitmap_count; i++) {
1370                 bitmap_offsets[i] = buffer.Position();
1371                 
1372                 mBitmaps[i]->SaveObject(buffer);
1373         }
1374         
1375         // go back and write the collection definition (with correct offsets)
1376         buffer.Position(0);
1377         buffer.WriteShort(mVersion);
1378         buffer.WriteShort(mType);
1379         buffer.WriteUShort(mFlags);
1380         buffer.WriteShort(GetColorTable(0)->ColorCount());
1381         buffer.WriteShort(ColorTableCount());
1382         buffer.WriteLong(SIZEOF_collection_definition);
1383         buffer.WriteShort(sequence_count);
1384         buffer.WriteLong(sequence_table_offset);
1385         buffer.WriteShort(frame_count);
1386         buffer.WriteLong(frame_table_offset);
1387         buffer.WriteShort(bitmap_count);
1388         buffer.WriteLong(bitmap_table_offset);
1389         buffer.WriteShort(mPixelsToWorld);
1390         buffer.WriteLong(SizeInFile());
1391         buffer.WriteZeroes(506);
1392         
1393         // fill offset tables
1394         if (bitmap_count > 0) {
1395                 buffer.Position(bitmap_table_offset);
1396                 for (i = 0; i < bitmap_count; i++)
1397                         buffer.WriteLong(bitmap_offsets[i]);
1398         }
1399         if (frame_count > 0) {
1400                 buffer.Position(frame_table_offset);
1401                 for (i = 0; i < frame_count; i++)
1402                         buffer.WriteLong(frame_offsets[i]);
1403         }
1404         if (sequence_count > 0) {
1405                 buffer.Position(sequence_table_offset);
1406                 for (i = 0; i < sequence_count; i++)
1407                         buffer.WriteLong(sequence_offsets[i]);
1408         }
1409         
1410         return buffer;
1411 }
1412
1413 BigEndianBuffer& ShapesChunk::SavePatch(BigEndianBuffer& buffer, const ShapesChunk* other)
1414 {
1415         buffer.WriteLong(FOUR_CHARS_TO_INT('c','l','d','f'));
1416
1417         // collection header
1418         buffer.WriteShort(mVersion);
1419         buffer.WriteShort(mType);
1420         buffer.WriteUShort(mFlags);
1421         buffer.WriteShort(GetColorTable(0)->ColorCount());
1422         buffer.WriteShort(ColorTableCount());
1423         buffer.WriteLong(SIZEOF_collection_definition);
1424         buffer.WriteShort(SequenceCount());
1425         buffer.WriteLong(0);
1426         buffer.WriteShort(FrameCount());
1427         buffer.WriteLong(0);
1428         buffer.WriteShort(BitmapCount());
1429         buffer.WriteLong(0);
1430         buffer.WriteShort(mPixelsToWorld);
1431         buffer.WriteLong(0);
1432         buffer.WriteZeroes(506);
1433
1434         for (unsigned int i = 0; i < mColorTables.size(); ++i) {
1435                 if (other == NULL || i >= other->mColorTables.size() || *mColorTables[i] != *other->mColorTables[i]) {
1436                         mColorTables[i]->SavePatch(buffer, i);
1437                 }
1438         }
1439
1440         for (unsigned int i = 0; i < mSequences.size(); ++i) {
1441                 if (other == NULL || i >= other->mSequences.size() || *mSequences[i] != *other->mSequences[i]) {
1442                         mSequences[i]->SavePatch(buffer, i);
1443                 }
1444         }
1445
1446         for (unsigned int i = 0; i < mFrames.size(); ++i) {
1447                 if (other == NULL || i >= other->mFrames.size() || *mFrames[i] != *other->mFrames[i]) {
1448                         mFrames[i]->SavePatch(buffer, i);
1449                 }
1450         }
1451
1452         for (unsigned int i = 0; i < mBitmaps.size(); ++i) {
1453                 if (other == NULL || i >= other->mBitmaps.size() || *mBitmaps[i] != *other->mBitmaps[i]) {
1454                         mBitmaps[i]->SavePatch(buffer, i);
1455                 }
1456         }
1457
1458         return buffer;
1459 }
1460
1461 BigEndianBuffer& ShapesChunk::LoadObject(BigEndianBuffer& buffer)
1462 {
1463         short   color_count,
1464                         clut_count,
1465                         bitmap_count,
1466                         high_level_shape_count,
1467                         low_level_shape_count,
1468                         i;
1469         long    color_table_offset,
1470                         high_level_shape_offset_table_offset,
1471                         low_level_shape_offset_table_offset,
1472                         bitmap_offset_table_offset,
1473                         oldpos,
1474                         offset,
1475                         size;
1476
1477         mVersion = buffer.ReadShort();
1478         mType = buffer.ReadShort();
1479         mFlags = buffer.ReadUShort();
1480         color_count = buffer.ReadShort();
1481         clut_count = buffer.ReadShort();
1482         color_table_offset = buffer.ReadLong();
1483         high_level_shape_count = buffer.ReadShort();
1484         high_level_shape_offset_table_offset = buffer.ReadLong();
1485         low_level_shape_count = buffer.ReadShort();
1486         low_level_shape_offset_table_offset = buffer.ReadLong();
1487         bitmap_count = buffer.ReadShort();
1488         bitmap_offset_table_offset = buffer.ReadLong();
1489         mPixelsToWorld = buffer.ReadShort();
1490         size = buffer.ReadLong();
1491
1492         // validate values
1493         if (mVersion != COLLECTION_VERSION) {
1494                 wxLogError(wxT("[ShapesChunk] Unknown collection version %d"), mVersion);
1495                 return buffer;
1496         }
1497
1498         if ((unsigned long)size != buffer.Size()) {
1499                 wxLogError(wxT("[ShapesChunk] Chunk size mismatch (%ld/%d): this may not be a Marathon shapes file"), size, buffer.Size());
1500                 return buffer;
1501         }
1502         if (color_table_offset < SIZEOF_collection_definition
1503                 || color_table_offset >= size
1504                 || high_level_shape_offset_table_offset < SIZEOF_collection_definition
1505                 || high_level_shape_offset_table_offset >= size
1506                 || low_level_shape_offset_table_offset < SIZEOF_collection_definition
1507                 || low_level_shape_offset_table_offset >= size
1508                 || bitmap_offset_table_offset < SIZEOF_collection_definition
1509                 || bitmap_offset_table_offset >= size) {
1510                 wxLogError(wxT("[ShapesChunk] Invalid offsets in collection definition: this may not be a Marathon shapes file"));
1511                 return buffer;
1512         }
1513         if (color_count < 0 || clut_count < 0 || high_level_shape_count < 0 || low_level_shape_count < 0 || bitmap_count < 0) {
1514                 wxLogError(wxT("[ShapesChunk] Invalid object counts in collection definition: this may not be a Marathon shapes file"));
1515                 return buffer;
1516         }
1517
1518         if (IsVerbose()) {
1519                 wxLogDebug(wxT("[ShapesChunk]         Version: %d"), mVersion);
1520                 wxLogDebug(wxT("[ShapesChunk]         Type:    %d"), mType);
1521                 wxLogDebug(wxT("[ShapesChunk]         Flags:   %d"), mFlags);
1522                 wxLogDebug(wxT("[ShapesChunk]         %d color tables, %d colors per table"), clut_count, color_count);
1523                 wxLogDebug(wxT("[ShapesChunk]         %d sequences"), high_level_shape_count);
1524                 wxLogDebug(wxT("[ShapesChunk]         %d frames"), low_level_shape_count);
1525                 wxLogDebug(wxT("[ShapesChunk]         %d bitmaps"), bitmap_count);
1526         }
1527
1528         // load color tables
1529         for (i = 0; i < clut_count; i++) {
1530                 ShapesColorTable        *color_table = new ShapesColorTable(IsVerbose());
1531
1532                 if (IsVerbose())
1533                         wxLogDebug(wxT("[ShapesChunk] Loading colortable %d/%d"), i+1, clut_count);
1534
1535                 oldpos = buffer.Position();
1536
1537                 color_table->LoadObject(buffer, color_table_offset + i * color_count * SIZEOF_rgb_color_value, color_count);
1538
1539                 buffer.Position(oldpos);
1540                 
1541                 // we stop if an error occured
1542                 if (!color_table->IsGood()) {
1543                         wxLogError(wxT("[ShapesChunk] Error loading color table %d... Dropped"), i);
1544                         return buffer;
1545                 }
1546                 
1547                 mColorTables.push_back(color_table);
1548         }
1549         
1550         // load bitmaps, decoding compressed ones
1551         buffer.Position(bitmap_offset_table_offset);
1552         for (i = 0; i < bitmap_count; i++) {
1553                 offset = buffer.ReadLong();
1554                 if (offset < SIZEOF_collection_definition || offset >= size) {
1555                         wxLogError(wxT("[ShapesChunk] Invalid bitmap offset: this may not be a Marathon shapes file"));
1556                         return buffer;
1557                 }
1558                 
1559                 ShapesBitmap    *bitmap = new ShapesBitmap(IsVerbose());
1560                 if (IsVerbose())
1561                         wxLogDebug(wxT("[ShapesChunk] Loading bitmap %d/%d"), i+1, bitmap_count);
1562                         
1563                 oldpos = buffer.Position();
1564                 
1565                 bitmap->LoadObject(buffer, offset);
1566                 
1567                 buffer.Position(oldpos);
1568                 
1569                 // we stop if an error occured
1570                 if (!bitmap->IsGood()) {
1571                         wxLogError(wxT("[ShapesDocument] Error loading bitmap %d... Dropped"), i);
1572                         return buffer;
1573                 }
1574                 
1575                 mBitmaps.push_back(bitmap);
1576         }
1577
1578         // load sequences
1579         buffer.Position(high_level_shape_offset_table_offset);
1580         for (i = 0; i < high_level_shape_count; i++) {
1581                 offset = buffer.ReadLong();
1582                 if (offset < SIZEOF_collection_definition || offset >= size) {
1583                         wxLogError(wxT("[ShapesChunk] Invalid sequence offset: this may not be a Marathon shapes file"));
1584                         return buffer;
1585                 }
1586                 
1587                 ShapesSequence  *sequence = new ShapesSequence(IsVerbose());
1588                 if (IsVerbose())
1589                         wxLogDebug(wxT("[ShapesChunk] Loading sequence %d/%d"), i+1, high_level_shape_count);
1590                 
1591                 oldpos = buffer.Position();
1592                 
1593                 sequence->LoadObject(buffer, offset);
1594                 
1595                 buffer.Position(oldpos);
1596                 
1597                 // we stop if an error occured
1598                 if (!sequence->IsGood()) {
1599                         wxLogError(wxT("[ShapesDocument] Error loading sequence... Dropped"));
1600                         return buffer;
1601                 }
1602                 
1603                 mSequences.push_back(sequence);
1604         }
1605         
1606         // load frames
1607         buffer.Position(low_level_shape_offset_table_offset);
1608         for (i = 0; i < low_level_shape_count; i++) {
1609                 offset = buffer.ReadLong();
1610                 if (offset < SIZEOF_collection_definition || offset >= size) {
1611                         wxLogError(wxT("[ShapesChunk] Invalid frame offset: this may not be a Marathon shapes file"));
1612                         return buffer;
1613                 }
1614                 
1615                 ShapesFrame     *frame = new ShapesFrame(IsVerbose());
1616                 if (IsVerbose())
1617                         wxLogDebug(wxT("[ShapesChunk] Loading frame %d/%d"), i+1, low_level_shape_count);
1618
1619                 oldpos = buffer.Position();
1620                 
1621                 frame->LoadObject(buffer, offset);
1622                 
1623                 buffer.Position(oldpos);
1624                 // calculate scale factor from world_* fields and associated bitmap dimensions.
1625                 // If this fails, default to collection global scale factor
1626                 if (frame->BitmapIndex() >= 0 && frame->BitmapIndex() < (int)mBitmaps.size()) {
1627                         int     bitmapWidth = mBitmaps[frame->BitmapIndex()]->Width();
1628
1629                         if (bitmapWidth > 0)
1630                                 frame->SetScaleFactor((frame->WorldRight() - frame->WorldLeft()) / bitmapWidth);
1631                         else
1632                                 frame->SetScaleFactor(mPixelsToWorld);
1633                 } else {
1634                         frame->SetScaleFactor(mPixelsToWorld);
1635                 }
1636                 
1637                 // store if correct
1638                 if (!frame->IsGood()) {
1639                         wxLogError(wxT("[ShapesDocument] Error loading frame %d... Dropped"), i);
1640                         return buffer;
1641                 }
1642                 
1643                 mFrames.push_back(frame);
1644         }
1645         
1646         mGoodData = true;
1647         return buffer;
1648 }
1649
1650 BigEndianBuffer& ShapesChunk::LoadPatch(BigEndianBuffer& buffer)
1651 {
1652         mGoodData = true;
1653
1654         long tag = buffer.ReadLong();
1655         short color_count = 0;
1656
1657         while (tag != FOUR_CHARS_TO_INT('e','n','d','c')) {
1658                 switch (tag) {
1659                 case FOUR_CHARS_TO_INT('c','l','d','f'): {
1660                         mVersion = buffer.ReadShort();
1661                         if (mVersion != COLLECTION_VERSION) {
1662                                 wxLogError(wxT("[ShapesChunk] Unknown 'cldf' version %d in patch"), mVersion);
1663                                 mGoodData = false;
1664                                 return buffer;
1665                         }
1666
1667                         mType = buffer.ReadShort();
1668                         mFlags = buffer.ReadUShort();
1669                         color_count = buffer.ReadShort();
1670                         mColorTables.resize(buffer.ReadShort());
1671                         buffer.ReadLong(); // color table offset
1672                         mSequences.resize(buffer.ReadShort());
1673                         buffer.ReadLong(); // high level shape offset
1674                         mFrames.resize(buffer.ReadShort());
1675                         buffer.ReadLong(); // low level shape offset
1676                         mBitmaps.resize(buffer.ReadShort());
1677                         buffer.ReadLong(); // bitmap offsets
1678                         mPixelsToWorld = buffer.ReadShort();
1679                         buffer.ReadLong(); // size
1680                         buffer.Position(buffer.Position() + 506);
1681                         break;
1682                 }
1683                 case FOUR_CHARS_TO_INT('c','t','a','b'): {
1684                         long index = buffer.ReadLong();
1685                         if (index < mColorTables.size()) {
1686                                 ShapesColorTable* c = new ShapesColorTable(IsVerbose());
1687                                 c->LoadObject(buffer, buffer.Position(), color_count);
1688                                 if (c->IsGood()) {
1689                                         delete mColorTables[index];
1690                                         mColorTables[index] = c;
1691                                 } else {
1692                                         mGoodData = false;
1693                                         return buffer;
1694                                 }
1695                         } else {
1696                                 wxLogError(wxT("[ShapesChunk] Invliad 'ctab' index"));
1697                                 mGoodData = false;
1698                                 return buffer;
1699                         }
1700                         break;
1701                 }
1702                 case FOUR_CHARS_TO_INT('h','l','s','h'): {
1703                         long index = buffer.ReadLong();
1704                         long chunk_size = buffer.ReadLong();
1705                         long position = buffer.Position();
1706                         if (index < mSequences.size()) {
1707                                 ShapesSequence* ss = new ShapesSequence(IsVerbose());
1708                                 ss->LoadObject(buffer, position);
1709                                 buffer.Position(position + chunk_size);
1710                                 if (ss->IsGood()) {
1711                                         delete mSequences[index];
1712                                         mSequences[index] = ss;
1713                                 } else {
1714                                         mGoodData = false;
1715                                         return buffer;
1716                                 }
1717                         } else {
1718                                 wxLogError(wxT("[ShapesChunk] Invalid 'hlsh' index"));
1719                                 mGoodData = false;
1720                                 return buffer;
1721                         }
1722                         break;
1723                 }
1724                 case FOUR_CHARS_TO_INT('l','l','s','h'): {
1725                         long index = buffer.ReadLong();
1726                         if (index < mFrames.size()) {
1727                                 ShapesFrame* sf = new ShapesFrame(IsVerbose());
1728                                 sf->LoadObject(buffer, buffer.Position());
1729                                 if (sf->IsGood()) {
1730                                         delete mFrames[index];
1731                                         mFrames[index] = sf;
1732                                 } else {
1733                                         mGoodData = false;
1734                                         return buffer;
1735                                 }
1736                         } else {
1737                                 wxLogError(wxT("[ShapesChunk] Invalid 'llsh' index"));
1738                                 mGoodData = false;
1739                                 return buffer;
1740                         }
1741                         break;
1742                 }
1743                 case FOUR_CHARS_TO_INT('b','m','a','p'): {
1744                         long index = buffer.ReadLong();
1745                         long size = buffer.ReadLong();
1746                         long position = buffer.Position();
1747
1748                         if (index < mBitmaps.size()) {
1749                                 ShapesBitmap* b = new ShapesBitmap(IsVerbose());
1750                                 b->LoadObject(buffer, position);
1751                                 buffer.Position(position + size);
1752                                 if (b->IsGood()) {
1753                                         delete mBitmaps[index];
1754                                         mBitmaps[index] = b;
1755                                 } else {
1756                                         mGoodData = false;
1757                                         return buffer;
1758                                 }
1759                         } else {
1760                                 wxLogError(wxT("[ShapesChunk] Invalid 'bmap' index"));
1761                                 mGoodData = false;
1762                                 return buffer;
1763                         }
1764                         break;
1765                 }
1766                 }
1767
1768                 tag = buffer.ReadLong();
1769         }
1770
1771         return buffer;
1772 }
1773
1774 ShapesCollection::ShapesCollection(bool verbose): ShapesElement(verbose)
1775 {
1776         mChunks[0] = NULL;
1777         mChunks[1] = NULL;
1778 }
1779
1780 ShapesCollection::~ShapesCollection(void)
1781 {
1782         if (mChunks[0])
1783                 delete mChunks[0];
1784         if (mChunks[1])
1785                 delete mChunks[1];
1786 }
1787
1788 bool ShapesCollection::Defined(unsigned int chunk) const
1789 {
1790         if (chunk > COLL_VERSION_TRUECOLOR)
1791                 return false;
1792         return mChunks[chunk] != NULL;
1793 }
1794
1795 int ShapesCollection::Version(unsigned int chunk) const
1796 {
1797         return (Defined(chunk) ? mChunks[chunk]->Version() : 0);
1798 }
1799
1800 int ShapesCollection::Type(unsigned int chunk) const
1801 {
1802         return (Defined(chunk) ? mChunks[chunk]->Type() : 0);
1803 }
1804
1805 int ShapesCollection::Flags(unsigned int chunk) const
1806 {
1807         return (Defined(chunk) ? mChunks[chunk]->Flags() : 0);
1808 }
1809
1810 int ShapesCollection::ScaleFactor(unsigned int chunk) const
1811 {
1812         return (Defined(chunk) ? mChunks[chunk]->ScaleFactor() : 0);
1813 }
1814
1815 int ShapesCollection::ColorTableCount(unsigned int chunk) const
1816 {
1817         return (Defined(chunk) ? mChunks[chunk]->ColorTableCount() : 0);
1818 }
1819
1820 int ShapesCollection::BitmapCount(unsigned int chunk) const
1821 {
1822         return (Defined(chunk) ? mChunks[chunk]->BitmapCount() : 0);
1823 }
1824
1825 int ShapesCollection::FrameCount(unsigned int chunk) const
1826 {
1827         return (Defined(chunk) ? mChunks[chunk]->FrameCount() : 0);
1828 }
1829
1830 int ShapesCollection::SequenceCount(unsigned int chunk) const
1831 {
1832         return (Defined(chunk) ? mChunks[chunk]->SequenceCount() : 0);
1833 }
1834
1835 ShapesColorTable* ShapesCollection::GetColorTable(unsigned int chunk, unsigned int index) const
1836 {
1837         return (Defined(chunk) ? mChunks[chunk]->GetColorTable(index) : NULL);
1838 }
1839
1840 ShapesBitmap* ShapesCollection::GetBitmap(unsigned int chunk, unsigned int index) const
1841 {
1842         return (Defined(chunk) ? mChunks[chunk]->GetBitmap(index) : NULL);
1843 }
1844
1845 ShapesFrame* ShapesCollection::GetFrame(unsigned int chunk, unsigned int index) const
1846 {
1847         return (Defined(chunk) ? mChunks[chunk]->GetFrame(index) : NULL);
1848 }
1849
1850 ShapesSequence* ShapesCollection::GetSequence(unsigned int chunk, unsigned int index) const
1851 {
1852         return (Defined(chunk) ? mChunks[chunk]->GetSequence(index) : NULL);
1853 }
1854
1855 ShapesChunk* ShapesCollection::GetChunk(unsigned int chunk) const
1856 {
1857         return (Defined(chunk) ? mChunks[chunk] : NULL);
1858 }
1859
1860 void ShapesCollection::InsertColorTable(ShapesColorTable *ct, unsigned int chunk)
1861 {
1862         if (Defined(chunk))
1863                 mChunks[chunk]->InsertColorTable(ct);
1864 }
1865
1866 void ShapesCollection::DeleteColorTable(unsigned int chunk, unsigned int ct)
1867 {
1868         if (Defined(chunk))
1869                 mChunks[chunk]->DeleteColorTable(ct);
1870 }
1871
1872 void ShapesCollection::InsertBitmap(ShapesBitmap *b, unsigned int chunk)
1873 {
1874         if (Defined(chunk))
1875                 mChunks[chunk]->InsertBitmap(b);
1876 }
1877
1878 void ShapesCollection::DeleteBitmap(unsigned int chunk, unsigned int b)
1879 {
1880         if (Defined(chunk))
1881                 mChunks[chunk]->DeleteBitmap(b);
1882 }
1883
1884 void ShapesCollection::InsertFrame(ShapesFrame *f, unsigned int chunk)
1885 {
1886         if (Defined(chunk))
1887                 mChunks[chunk]->InsertFrame(f);
1888 }
1889
1890 void ShapesCollection::DeleteFrame(unsigned int chunk, unsigned int f)
1891 {
1892         if (Defined(chunk))
1893                 mChunks[chunk]->DeleteFrame(f);
1894 }
1895
1896 void ShapesCollection::InsertSequence(ShapesSequence *s, unsigned int chunk)
1897 {
1898         if (Defined(chunk))
1899                 mChunks[chunk]->InsertSequence(s);
1900 }
1901
1902 void ShapesCollection::DeleteSequence(unsigned int chunk, unsigned int s)
1903 {
1904         if (Defined(chunk))
1905                 mChunks[chunk]->DeleteSequence(s);
1906 }
1907
1908 // calculate how much space a collection is going
1909 // to take when encoded to its on-file format.
1910 unsigned int ShapesCollection::SizeInFile(unsigned int chunk) const
1911 {
1912         if (!Defined(chunk))
1913                 return 0;
1914
1915         unsigned int    size = 0;
1916
1917         size += mChunks[chunk]->SizeInFile();
1918         return size;
1919 }
1920
1921 #if wxUSE_STD_IOSTREAM
1922 wxSTD ostream& ShapesCollection::SaveObject(wxSTD ostream& stream)
1923 #else
1924 wxOutputStream& ShapesCollection::SaveObject(wxOutputStream& stream)
1925 #endif
1926 {
1927         for (unsigned int i = 0; i < 2 ; i++) {
1928                 if (Defined(i)) {
1929                         BigEndianBuffer chunkbuffer(mChunks[i]->SizeInFile());
1930
1931                         mChunks[i]->SaveObject(chunkbuffer);
1932 #if wxUSE_STD_IOSTREAM
1933                         stream.write((char *)chunkbuffer.Data(), chunkbuffer.Size());
1934 #else
1935                         stream.Write((char *)chunkbuffer.Data(), chunkbuffer.Size());
1936 #endif
1937                 }
1938         }
1939         return stream;
1940 }
1941
1942 #if wxUSE_STD_IOSTREAM
1943 wxSTD ostream& ShapesCollection::SavePatch(wxSTD ostream& stream, const ShapesCollection& other, int index, int depth)
1944 #else
1945 wxOutputStream& ShapesCollection::SavePatch(wxOutputStream& stream, const ShapesCollection& other, int index, int depth)
1946 #endif
1947 {
1948         bool diff = Defined(depth) && (other.mChunks[depth] == NULL || *mChunks[depth] != *other.mChunks[depth]);
1949         unsigned int size = 12;
1950         if (diff) {
1951                 size += mChunks[depth]->SizeInPatch(other.mChunks[depth]);
1952         }
1953
1954         BigEndianBuffer chunkbuffer(size);
1955         chunkbuffer.WriteLong(index);
1956         chunkbuffer.WriteLong(depth ? 16 : 8);
1957         if (diff) {
1958                 mChunks[depth]->SavePatch(chunkbuffer, other.mChunks[depth]);
1959         }
1960         chunkbuffer.WriteLong(FOUR_CHARS_TO_INT('e','n','d','c'));
1961 #if wxUSE_STD_IOSTREAM
1962         stream.write((char *)chunkbuffer.Data(), chunkbuffer.Size());
1963 #else
1964         stream.Write((char *)chunkbuffer.Data(), chunkbuffer.Size());
1965 #endif
1966
1967         return stream;
1968 }
1969
1970 #if wxUSE_STD_IOSTREAM
1971 wxSTD istream& ShapesCollection::LoadObject(wxSTD istream& stream)
1972 #else
1973 wxInputStream& ShapesCollection::LoadObject(wxInputStream& stream)
1974 #endif
1975 {
1976         BigEndianBuffer coll_header(SIZEOF_collection_header);
1977
1978 #if wxUSE_STD_IOSTREAM
1979         stream.read((char *)coll_header.Data(), coll_header.Size());
1980 #else
1981         stream.Read((char *)coll_header.Data(), coll_header.Size());
1982 #endif
1983
1984 #if wxUSE_STD_IOSTREAM
1985         stream.seekg(0, std::ios::end);
1986         wxInt32 filesize = stream.tellg();
1987         stream.seekg(0, std::ios::beg);
1988 #else
1989         wxInt32 filesize = stream.GetSize();
1990 #endif
1991         
1992         long    offset8, length8,
1993                         offset16, length16;
1994         
1995         mStatus = coll_header.ReadShort();
1996         mFlags = coll_header.ReadUShort();      
1997         offset8 = coll_header.ReadLong();
1998         length8 = coll_header.ReadLong();
1999         offset16 = coll_header.ReadLong();
2000         length16 = coll_header.ReadLong();
2001         
2002         if (offset8 < -1 || length8 < 0 || offset16 < -1 || length16 < 0)
2003                 return stream;
2004         if ((offset8 + length8) > filesize)
2005                 return stream;
2006         if ((offset16 + length16) > filesize)
2007                 return stream;
2008         
2009         if (IsVerbose()) {
2010                 wxLogDebug(wxT("[ShapesCollection]     Status: %d"), mStatus);
2011                 wxLogDebug(wxT("[ShapesCollection]     Flags:  %d"), mFlags);
2012         }
2013         
2014         // is there the 8-bit version?
2015         if (offset8 != -1) {
2016                 if (IsVerbose())
2017                         wxLogDebug(wxT("[ShapesCollection]     8-bit chunk present"));
2018                 
2019                 BigEndianBuffer chunkbuffer(length8);
2020                 
2021 #if wxUSE_STD_IOSTREAM
2022                 stream.seekg(offset8, std::ios::beg);
2023                 stream.read((char *)chunkbuffer.Data(), chunkbuffer.Size());
2024 #else
2025                 stream.SeekI(offset8, wxFromStart);
2026                 stream.Read((char *)chunkbuffer.Data(), chunkbuffer.Size());
2027 #endif
2028                 
2029                 ShapesChunk     *pc = new ShapesChunk(IsVerbose());
2030                 pc->LoadObject(chunkbuffer);
2031                 
2032                 if (!pc->IsGood()) {
2033                         wxLogError(wxT("[ShapesCollection] Error loading 8-bit chunk... Dropped"));
2034                         return stream;
2035                 }
2036                 mChunks[0] = pc;
2037         }
2038         
2039         // is there the 16-bit version?
2040         if (offset16 != -1) {
2041                 if (IsVerbose())
2042                         wxLogDebug(wxT("[ShapesCollection]     16/32-bit chunk present"));
2043                 
2044                 BigEndianBuffer chunkbuffer(length16);
2045                 
2046 #if wxUSE_STD_IOSTREAM
2047                 stream.seekg(offset16, std::ios::beg);
2048                 stream.read((char *)chunkbuffer.Data(), chunkbuffer.Size());
2049 #else
2050                 stream.SeekI(offset16, wxFromStart);
2051                 stream.Read((char *)chunkbuffer.Data(), chunkbuffer.Size());
2052 #endif
2053                 
2054                 ShapesChunk     *pc = new ShapesChunk(IsVerbose());
2055                 pc->LoadObject(chunkbuffer);
2056                 
2057                 if (!pc->IsGood()) {
2058                         wxLogError(wxT("[ShapesCollection] Error loading 16/32-bit chunk... Dropped"));
2059                         return stream;
2060                 }
2061                 mChunks[1] = pc;
2062         }
2063         
2064         mGoodData = true;
2065         return stream;
2066 }
2067
2068 BigEndianBuffer& ShapesCollection::LoadPatch(BigEndianBuffer& buffer)
2069 {
2070         int depth = buffer.ReadLong();
2071         ShapesChunk* chunk = 0;
2072         if (depth == 8) {
2073                 if (!mChunks[0]) {
2074                         mChunks[0] = new ShapesChunk(IsVerbose());
2075                 }
2076                 chunk = mChunks[0];
2077         } else if (depth == 16) {
2078                 if (!mChunks[1]) {
2079                         mChunks[1] = new ShapesChunk(IsVerbose());
2080                 }
2081                 chunk = mChunks[1];
2082         } else {
2083                 wxLogError(wxT("[ShapesCollection] Error loading patch chunk; invalid depth"));
2084                 mGoodData = false;
2085                 return buffer;
2086         }
2087
2088         chunk->LoadPatch(buffer);
2089         if (!chunk->IsGood()) {
2090                 mGoodData = false;
2091         }
2092         return buffer;
2093 }