1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/common/imagxpm.cpp
3 // Purpose:     wxXPMHandler
4 // Author:      Vaclav Slavik, Robert Roebling
5 // Copyright:   (c) 2001 Vaclav Slavik
6 // Licence:     wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
8 
9 /*
10 
11 This file is partially based on source code of ImageMagick by John Cristy. Its
12 license is as follows:
13 
14 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15 %                                                                             %
16 %                                                                             %
17 %                                                                             %
18 %                            X   X  PPPP   M   M                              %
19 %                             X X   P   P  MM MM                              %
20 %                              X    PPPP   M M M                              %
21 %                             X X   P      M   M                              %
22 %                            X   X  P      M   M                              %
23 %                                                                             %
24 %                                                                             %
25 %                    Read/Write ImageMagick Image Format.                     %
26 %                                                                             %
27 %                                                                             %
28 %                              Software Design                                %
29 %                                John Cristy                                  %
30 %                                 July 1992                                   %
31 %                                                                             %
32 %                                                                             %
33 %  Copyright (C) 2001 ImageMagick Studio, a non-profit organization dedicated %
34 %  to making software imaging solutions freely available.                     %
35 %                                                                             %
36 %  Permission is hereby granted, free of charge, to any person obtaining a    %
37 %  copy of this software and associated documentation files ("ImageMagick"),  %
38 %  to deal in ImageMagick without restriction, including without limitation   %
39 %  the rights to use, copy, modify, merge, publish, distribute, sublicense,   %
40 %  and/or sell copies of ImageMagick, and to permit persons to whom the       %
41 %  ImageMagick is furnished to do so, subject to the following conditions:    %
42 %                                                                             %
43 %  The above copyright notice and this permission notice shall be included in %
44 %  all copies or substantial portions of ImageMagick.                         %
45 %                                                                             %
46 %  The software is provided "as is", without warranty of any kind, express or %
47 %  implied, including but not limited to the warranties of merchantability,   %
48 %  fitness for a particular purpose and noninfringement.  In no event shall   %
49 %  ImageMagick Studio be liable for any claim, damages or other liability,    %
50 %  whether in an action of contract, tort or otherwise, arising from, out of  %
51 %  or in connection with ImageMagick or the use or other dealings in          %
52 %  ImageMagick.                                                               %
53 %                                                                             %
54 %  Except as contained in this notice, the name of the ImageMagick Studio     %
55 %  shall not be used in advertising or otherwise to promote the sale, use or  %
56 %  other dealings in ImageMagick without prior written authorization from the %
57 %  ImageMagick Studio.                                                        %
58 %                                                                             %
59 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
60 %
61 %
62 */
63 
64 // For compilers that support precompilation, includes "wx.h".
65 #include "wx/wxprec.h"
66 
67 #ifdef __BORLANDC__
68     #pragma hdrstop
69 #endif
70 
71 #if wxUSE_XPM
72 
73 #ifndef WX_PRECOMP
74     #include "wx/log.h"
75     #include "wx/intl.h"
76     #include "wx/utils.h"
77 #endif
78 
79 #include "wx/imagxpm.h"
80 #include "wx/wfstream.h"
81 #include "wx/xpmdecod.h"
82 #include "wx/filename.h"
83 
IMPLEMENT_DYNAMIC_CLASS(wxXPMHandler,wxImageHandler)84 IMPLEMENT_DYNAMIC_CLASS(wxXPMHandler,wxImageHandler)
85 
86 //-----------------------------------------------------------------------------
87 // wxXPMHandler
88 //-----------------------------------------------------------------------------
89 
90 #if wxUSE_STREAMS
91 
92 bool wxXPMHandler::LoadFile(wxImage *image,
93                             wxInputStream& stream,
94                             bool WXUNUSED(verbose), int WXUNUSED(index))
95 {
96     wxXPMDecoder decoder;
97 
98     wxImage img = decoder.ReadFile(stream);
99     if ( !img.IsOk() )
100         return false;
101     *image = img;
102     return true;
103 }
104 
105 namespace
106 {
107 
108 // Make the given string a valid C identifier.
109 //
110 // All invalid characters are simply replaced by underscores and underscore is
111 // also prepended in the beginning if the initial character is not alphabetic.
112 void
MakeValidCIdent(wxString * str)113 MakeValidCIdent(wxString* str)
114 {
115     const wxChar chUnderscore = wxT('_');
116 
117     for ( wxString::iterator it = str->begin(); it != str->end(); ++it )
118     {
119         const wxChar ch = *it;
120         if ( wxIsdigit(ch) )
121         {
122             if ( it == str->begin() )
123             {
124                 // Identifiers can't start with a digit.
125                 str->insert(0, chUnderscore); // prepend underscore
126                 it = str->begin(); // restart as string changed
127                 continue;
128             }
129         }
130         else if ( !wxIsalpha(ch) && ch != chUnderscore )
131         {
132             // Not a valid character in C identifiers.
133             *it = chUnderscore;
134         }
135     }
136 
137     // Double underscores are not allowed in normal C identifiers and are
138     // useless anyhow.
139     str->Replace(wxT("__"), wxT("_"));
140 }
141 
142 } // anonymous namespace
143 
SaveFile(wxImage * image,wxOutputStream & stream,bool WXUNUSED (verbose))144 bool wxXPMHandler::SaveFile(wxImage * image,
145                             wxOutputStream& stream, bool WXUNUSED(verbose))
146 {
147     // 1. count colours:
148     #define MaxCixels  92
149     static const char Cixel[MaxCixels+1] =
150                          " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
151                          "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
152     int i, j, k;
153 
154     wxImageHistogram histogram;
155     int cols = int(image->ComputeHistogram(histogram));
156 
157     int chars_per_pixel = 1;
158     for ( k = MaxCixels; cols > k; k *= MaxCixels)
159         chars_per_pixel++;
160 
161     // 2. write the header:
162     wxString sName;
163     if ( image->HasOption(wxIMAGE_OPTION_FILENAME) )
164     {
165         sName = wxFileName(image->GetOption(wxIMAGE_OPTION_FILENAME)).GetName();
166         MakeValidCIdent(&sName);
167         sName << wxT("_xpm");
168     }
169 
170     if ( !sName.empty() )
171         sName = wxString(wxT("/* XPM */\nstatic const char *")) + sName;
172     else
173         sName = wxT("/* XPM */\nstatic const char *xpm_data");
174     stream.Write( (const char*) sName.ToAscii(), sName.Len() );
175 
176     char tmpbuf[200];
177     // VS: 200b is safe upper bound for anything produced by sprintf below
178     //     (<101 bytes the string, neither %i can expand into more than 10 chars)
179     sprintf(tmpbuf,
180                "[] = {\n"
181                "/* columns rows colors chars-per-pixel */\n"
182                "\"%i %i %i %i\",\n",
183                image->GetWidth(), image->GetHeight(), cols, chars_per_pixel);
184     stream.Write(tmpbuf, strlen(tmpbuf));
185 
186     // 3. create color symbols table:
187     char *symbols_data = new char[cols * (chars_per_pixel+1)];
188     char **symbols = new char*[cols];
189 
190     // 2a. find mask colour:
191     unsigned long mask_key = 0x1000000 /*invalid RGB value*/;
192     if (image->HasMask())
193         mask_key = (image->GetMaskRed() << 16) |
194                    (image->GetMaskGreen() << 8) | image->GetMaskBlue();
195 
196     // 2b. generate colour table:
197     for (wxImageHistogram::iterator entry = histogram.begin();
198          entry != histogram.end(); ++entry )
199     {
200         unsigned long index = entry->second.index;
201         symbols[index] = symbols_data + index * (chars_per_pixel+1);
202         char *sym = symbols[index];
203 
204         for (j = 0; j < chars_per_pixel; j++)
205         {
206             sym[j] = Cixel[index % MaxCixels];
207             index /= MaxCixels;
208         }
209         sym[j] = '\0';
210 
211         unsigned long key = entry->first;
212 
213         if (key == 0)
214             sprintf( tmpbuf, "\"%s c Black\",\n", sym);
215         else if (key == mask_key)
216             sprintf( tmpbuf, "\"%s c None\",\n", sym);
217         else
218         {
219             wxByte r = wxByte(key >> 16);
220             wxByte g = wxByte(key >> 8);
221             wxByte b = wxByte(key);
222             sprintf(tmpbuf, "\"%s c #%02X%02X%02X\",\n", sym, r, g, b);
223         }
224         stream.Write( tmpbuf, strlen(tmpbuf) );
225     }
226 
227     stream.Write("/* pixels */\n", 13);
228 
229     unsigned char *data = image->GetData();
230     for (j = 0; j < image->GetHeight(); j++)
231     {
232         char tmp_c;
233         tmp_c = '\"'; stream.Write(&tmp_c, 1);
234         for (i = 0; i < image->GetWidth(); i++, data += 3)
235         {
236             unsigned long key = (data[0] << 16) | (data[1] << 8) | (data[2]);
237             stream.Write(symbols[histogram[key].index], chars_per_pixel);
238         }
239         tmp_c = '\"'; stream.Write(&tmp_c, 1);
240         if ( j + 1 < image->GetHeight() )
241         {
242             tmp_c = ','; stream.Write(&tmp_c, 1);
243         }
244         tmp_c = '\n'; stream.Write(&tmp_c, 1);
245     }
246     stream.Write("};\n", 3 );
247 
248     // Clean up:
249     delete[] symbols;
250     delete[] symbols_data;
251 
252     return true;
253 }
254 
DoCanRead(wxInputStream & stream)255 bool wxXPMHandler::DoCanRead(wxInputStream& stream)
256 {
257     wxXPMDecoder decoder;
258     return decoder.CanRead(stream);
259          // it's ok to modify the stream position here
260 }
261 
262 #endif  // wxUSE_STREAMS
263 
264 #endif // wxUSE_XPM
265