1 /* -*- mode: C++; tab-width: 4; c-basic-offset: 4; -*- */
2
3 /* AbiWord
4 * Copyright (C) 1998 AbiSource, Inc.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301 USA.
20 */
21
22 #include <stdlib.h>
23 #include "ut_string.h"
24 #include "ut_assert.h"
25
26 #include "ie_impGraphic_BMP.h"
27 #include "fg_GraphicRaster.h"
28 #include "ut_debugmsg.h"
29 #include "xap_Module.h"
30
31 #ifdef ABI_PLUGIN_BUILTIN
32 #define abi_plugin_register abipgn_bmp_register
33 #define abi_plugin_unregister abipgn_bmp_unregister
34 #define abi_plugin_supports_version abipgn_bmp_supports_version
35 // dll exports break static linking
36 #define ABI_BUILTIN_FAR_CALL extern "C"
37 #else
38 #define ABI_BUILTIN_FAR_CALL ABI_FAR_CALL
39 ABI_PLUGIN_DECLARE("BMP")
40 #endif
41
42 /*******************************************************************/
43 /*******************************************************************/
44
45 // we use a reference-counted sniffer
46 static IE_ImpGraphicBMP_Sniffer * m_impSniffer = 0;
47
48 ABI_BUILTIN_FAR_CALL
abi_plugin_register(XAP_ModuleInfo * mi)49 int abi_plugin_register (XAP_ModuleInfo * mi)
50 {
51
52 if (!m_impSniffer)
53 {
54 m_impSniffer = new IE_ImpGraphicBMP_Sniffer();
55 }
56
57 mi->name = "BMP Import Plugin";
58 mi->desc = "Import Windows Bitmap Images";
59 mi->version = ABI_VERSION_STRING;
60 mi->author = "Abi the Ant";
61 mi->usage = "No Usage";
62
63 IE_ImpGraphic::registerImporter (m_impSniffer);
64 return 1;
65 }
66
67 ABI_BUILTIN_FAR_CALL
abi_plugin_unregister(XAP_ModuleInfo * mi)68 int abi_plugin_unregister (XAP_ModuleInfo * mi)
69 {
70 mi->name = 0;
71 mi->desc = 0;
72 mi->version = 0;
73 mi->author = 0;
74 mi->usage = 0;
75
76 UT_ASSERT (m_impSniffer);
77
78 IE_ImpGraphic::unregisterImporter (m_impSniffer);
79 delete m_impSniffer;
80 m_impSniffer = 0;
81
82 return 1;
83 }
84
85 ABI_BUILTIN_FAR_CALL
abi_plugin_supports_version(UT_uint32,UT_uint32,UT_uint32)86 int abi_plugin_supports_version (UT_uint32 /*major*/, UT_uint32 /*minor*/,
87 UT_uint32 /*release*/)
88 {
89 return 1;
90 }
91
92 /*******************************************************************/
93 /*******************************************************************/
94
_write_png(png_structp png_ptr,png_bytep data,png_size_t length)95 static void _write_png( png_structp png_ptr,
96 png_bytep data,
97 png_size_t length )
98 {
99 UT_ByteBuf* bb = static_cast<UT_ByteBuf*>(png_get_io_ptr(png_ptr));
100 bb->append(data, length);
101 }
102
_write_flush(png_structp)103 static void _write_flush(png_structp /*png_ptr*/) { } // Empty Fuction.
104
105 // supported suffixes
106 static IE_SuffixConfidence IE_ImpGraphicBMP_Sniffer__SuffixConfidence[] = {
107 { "bmp", UT_CONFIDENCE_PERFECT },
108 { "", UT_CONFIDENCE_ZILCH }
109 };
110
getSuffixConfidence()111 const IE_SuffixConfidence * IE_ImpGraphicBMP_Sniffer::getSuffixConfidence ()
112 {
113 return IE_ImpGraphicBMP_Sniffer__SuffixConfidence;
114 }
115
recognizeContents(const char * szBuf,UT_uint32)116 UT_Confidence_t IE_ImpGraphicBMP_Sniffer::recognizeContents(const char * szBuf, UT_uint32 /*iNumbytes*/)
117 {
118 if ( !(strncmp(szBuf, "BM", 2)) )
119 return UT_CONFIDENCE_PERFECT;
120 return UT_CONFIDENCE_ZILCH;
121 }
122
getDlgLabels(const char ** pszDesc,const char ** pszSuffixList,IEGraphicFileType * ft)123 bool IE_ImpGraphicBMP_Sniffer::getDlgLabels(const char ** pszDesc,
124 const char ** pszSuffixList,
125 IEGraphicFileType * ft)
126 {
127 *pszDesc = "Windows Bitmap (.bmp)";
128 *pszSuffixList = "*.bmp";
129 *ft = getType ();
130 return true;
131 }
132
constructImporter(IE_ImpGraphic ** ppieg)133 UT_Error IE_ImpGraphicBMP_Sniffer::constructImporter(IE_ImpGraphic **ppieg)
134 {
135 *ppieg = new IE_ImpGraphic_BMP();
136 if (*ppieg == NULL)
137 return UT_IE_NOMEMORY;
138
139 return UT_OK;
140 }
141
_convertGraphic(UT_ByteBuf * pBB)142 UT_Error IE_ImpGraphic_BMP::_convertGraphic(UT_ByteBuf * pBB)
143 {
144 UT_Error err;
145 InitializePrivateClassData();
146
147 /* Read Header Data */
148 if ((err = Read_BMP_Header(pBB))) return err;
149 if ((err = Initialize_PNG())) return err;
150
151 /* Read Palette, if no palette set Header accordingly */
152 if(m_iBitsPerPlane < 24)
153 {
154 if ((err = Convert_BMP_Pallet(pBB))) return err;
155 }
156 else
157 {
158 UT_uint16 bitsPerChannel;
159 UT_uint16 colorType;
160 if (m_iBitsPerPlane == 24) {
161 bitsPerChannel = 8;
162 colorType = PNG_COLOR_TYPE_RGB;
163 } else if (m_iBitsPerPlane == 32) {
164 bitsPerChannel = 8;
165 colorType = PNG_COLOR_TYPE_RGB_ALPHA;
166 } else if (m_iBitsPerPlane == 48) {
167 bitsPerChannel = 16;
168 colorType = PNG_COLOR_TYPE_RGB;
169 } else if (m_iBitsPerPlane == 64) {
170 bitsPerChannel = 16;
171 colorType = PNG_COLOR_TYPE_RGB_ALPHA;
172 } else {
173 // wow! this is one impression graphic coming at us.
174 // i would have though 64 bits of RGBA glory would
175 // hold us for a while, but alas, we are obsolete...
176 return UT_ERROR;
177 }
178
179 png_set_IHDR ( m_pPNG,
180 m_pPNGInfo,
181 m_iWidth,
182 m_iHeight,
183 bitsPerChannel,
184 colorType,
185 PNG_INTERLACE_NONE,
186 PNG_COMPRESSION_TYPE_DEFAULT,
187 PNG_FILTER_TYPE_DEFAULT );
188
189 }
190 if ((err = Convert_BMP(pBB))) return err;
191
192 /* Clean Up Memory Used */
193
194 // FREEP(m_pPNGInfo->palette);
195 DELETEP(pBB);
196 png_destroy_write_struct(&m_pPNG, &m_pPNGInfo);
197
198 return UT_OK;
199 }
200
201
202 // This actually creates our FG_Graphic object for a PNG
importGraphic(UT_ByteBuf * pBB,FG_Graphic ** ppfg)203 UT_Error IE_ImpGraphic_BMP::importGraphic(UT_ByteBuf* pBB,
204 FG_Graphic ** ppfg)
205 {
206 UT_Error err = _convertGraphic(pBB);
207 if (err != UT_OK) return err;
208
209 /* Send Data back to AbiWord as PNG */
210 FG_GraphicRaster *pFGR;
211 pFGR = new FG_GraphicRaster();
212
213 if(pFGR == NULL)
214 return UT_IE_NOMEMORY;
215
216 if(!pFGR->setRaster_PNG(m_pBB)) {
217 DELETEP(pFGR);
218 return UT_IE_FAKETYPE;
219 }
220
221 *ppfg = static_cast<FG_Graphic *>(pFGR);
222
223 return UT_OK;
224 }
225
Read_BMP_Header(UT_ByteBuf * pBB)226 UT_Error IE_ImpGraphic_BMP::Read_BMP_Header(UT_ByteBuf* pBB)
227 {
228 /* Stepping Through the Header Data first all the File Info
229 * Then the Image Info until reached the end of the image Header Size
230 * Note some simple checks for data out of bounds are included
231 */
232
233 /* File Info Starts Here */
234 m_iBytesRead = 0;
235 m_iFileType = Read2Bytes(pBB,m_iBytesRead);
236 if (m_iFileType != 0x4D42) return UT_IE_BOGUSDOCUMENT;
237 m_iFileSize = Read4Bytes(pBB,m_iBytesRead);
238 m_iXHotspot = Read2Bytes(pBB,m_iBytesRead);
239 m_iYHotspot = Read2Bytes(pBB,m_iBytesRead);
240 m_iOffset = Read4Bytes(pBB,m_iBytesRead);
241
242 /* Image Info Starts Here */
243 m_iHeaderSize = Read4Bytes(pBB,m_iBytesRead);
244 if (m_bHeaderDone) return UT_IE_BOGUSDOCUMENT; /* More Header Info Needed */
245 m_bOldBMPFormat = (m_iHeaderSize <=12) ? true : false;
246 m_iWidth = (m_bOldBMPFormat) ?
247 static_cast<UT_sint32>(Read2Bytes(pBB,m_iBytesRead) ):
248 static_cast<UT_sint32>(Read4Bytes(pBB,m_iBytesRead) );
249 m_iHeight = (m_bOldBMPFormat) ?
250 static_cast<UT_sint32>(Read2Bytes(pBB,m_iBytesRead) ):
251 static_cast<UT_sint32>(Read4Bytes(pBB,m_iBytesRead) );
252 if (m_bHeaderDone) return UT_IE_BOGUSDOCUMENT; /* More Header Info Needed */
253 m_iPlanes = Read2Bytes(pBB,m_iBytesRead);
254 if (m_bHeaderDone) return UT_IE_BOGUSDOCUMENT; /* More Header Info Needed */
255 if (m_iPlanes != 1) return UT_IE_BOGUSDOCUMENT;
256 m_iBitsPerPlane = Read2Bytes(pBB,m_iBytesRead);
257 if (m_bHeaderDone) return UT_OK;
258
259 /* This rest of the header is read but not normally required */
260 m_iCompression = Read4Bytes(pBB,m_iBytesRead);
261 if (m_iCompression != 0) return UT_IE_BOGUSDOCUMENT;
262 if (m_bHeaderDone) return UT_OK;
263 m_iImageSize = Read4Bytes(pBB,m_iBytesRead);
264 if (m_bHeaderDone) return UT_OK;
265 m_iXResolution = Read4Bytes(pBB,m_iBytesRead);
266 if (m_bHeaderDone) return UT_OK;
267 m_iYResolution = Read4Bytes(pBB,m_iBytesRead);
268 if (m_bHeaderDone) return UT_OK;
269 m_iClrUsed = Read4Bytes(pBB,m_iBytesRead);
270 if (m_bHeaderDone) return UT_OK;
271 m_iClrImportant = Read4Bytes(pBB,m_iBytesRead);
272 if (m_bHeaderDone) return UT_OK;
273 m_iResolutionUnits = Read2Bytes(pBB,m_iBytesRead);
274 if (m_bHeaderDone) return UT_OK;
275 m_iPadding = Read2Bytes(pBB,m_iBytesRead);
276 if (m_bHeaderDone) return UT_OK;
277 m_iOrigin = Read2Bytes(pBB,m_iBytesRead);
278 if (m_bHeaderDone) return UT_OK;
279 m_iHalfToning = Read2Bytes(pBB,m_iBytesRead);
280 if (m_bHeaderDone) return UT_OK;
281 m_iHalfToningParam1 = Read4Bytes(pBB,m_iBytesRead);
282 if (m_bHeaderDone) return UT_OK;
283 m_iHalfToningParam2 = Read4Bytes(pBB,m_iBytesRead);
284 if (m_bHeaderDone) return UT_OK;
285 m_iClrEncoding = Read4Bytes(pBB,m_iBytesRead);
286 if (m_bHeaderDone) return UT_OK;
287 m_iIdentifier = Read4Bytes(pBB,m_iBytesRead);
288 if (m_bHeaderDone) return UT_OK;
289 /* Document Using non-standard HeaderSize Assume OK */
290 return UT_OK;
291 }
292
Initialize_PNG()293 UT_Error IE_ImpGraphic_BMP::Initialize_PNG()
294 {
295 /* Set up png structures for writing */
296 m_pPNG = png_create_write_struct( PNG_LIBPNG_VER_STRING,
297 static_cast<void*>(NULL),
298 NULL,
299 NULL );
300 if( m_pPNG == NULL )
301 {
302 return UT_ERROR;
303 }
304
305 m_pPNGInfo = png_create_info_struct(m_pPNG);
306 if ( m_pPNGInfo == NULL )
307 {
308 png_destroy_write_struct(&m_pPNG, static_cast<png_infopp>(NULL));
309 return UT_ERROR;
310 }
311
312 /* Set error handling if you are using the setjmp/longjmp method (this is
313 * the normal method of doing things with libpng). REQUIRED unless you
314 * set up your own error handlers in the png_create_read_struct() earlier.
315 */
316 if (setjmp(png_jmpbuf(m_pPNG)))
317 {
318 /* Free all of the memory associated with the png_ptr and info_ptr */
319 png_destroy_write_struct(&m_pPNG, &m_pPNGInfo);
320
321 /* If we get here, we had a problem reading the file */
322 return UT_ERROR;
323 }
324 m_pBB = new UT_ByteBuf; /* Byte Buffer for Converted Data */
325
326 /* Setting up the Data Writing Function */
327 png_set_write_fn(m_pPNG, static_cast<void *>(m_pBB), static_cast<png_rw_ptr>(_write_png), static_cast<png_flush_ptr>(_write_flush));
328
329 return UT_OK;
330 }
331
Convert_BMP_Pallet(UT_ByteBuf * pBB)332 UT_Error IE_ImpGraphic_BMP::Convert_BMP_Pallet(UT_ByteBuf* pBB)
333 {
334 /* Reset error handling for libpng */
335 if (setjmp(png_jmpbuf(m_pPNG)))
336 {
337 png_destroy_write_struct(&m_pPNG, &m_pPNGInfo);
338 return UT_ERROR;
339 }
340
341 png_set_IHDR ( m_pPNG,
342 m_pPNGInfo,
343 m_iWidth,
344 m_iHeight,
345 m_iBitsPerPlane,
346 PNG_COLOR_TYPE_PALETTE,
347 PNG_INTERLACE_NONE,
348 PNG_COMPRESSION_TYPE_DEFAULT,
349 PNG_FILTER_TYPE_DEFAULT );
350
351 UT_uint32 iOffset = m_iHeaderSize + 14;
352 UT_uint32 numClrs = (m_iClrUsed > 0) ?
353 m_iClrUsed :
354 (m_iOffset - iOffset)/((m_bOldBMPFormat)?3:4);
355
356 png_colorp palette = static_cast<png_colorp>(png_malloc(m_pPNG, numClrs * sizeof(png_color)));
357
358 for (UT_uint32 i=0; i < numClrs; i++)
359 {
360 palette[i].blue = ReadByte(pBB,iOffset++);
361 palette[i].green = ReadByte(pBB,iOffset++);
362 palette[i].red = ReadByte(pBB,iOffset++);
363 if(!m_bOldBMPFormat) iOffset++;
364 }
365 if (iOffset > m_iOffset) return UT_IE_BOGUSDOCUMENT;
366
367 png_set_PLTE( m_pPNG, m_pPNGInfo, palette, numClrs );
368
369 return UT_OK;
370 }
371
Convert_BMP(UT_ByteBuf * pBB)372 UT_Error IE_ImpGraphic_BMP::Convert_BMP(UT_ByteBuf* pBB)
373 {
374 /* Reset error handling for libpng */
375 if (setjmp(png_jmpbuf(m_pPNG)))
376 {
377 png_destroy_write_struct(&m_pPNG, &m_pPNGInfo);
378 return UT_ERROR;
379 }
380 png_write_info(m_pPNG,m_pPNGInfo);
381
382 const UT_Byte* row_data;
383 UT_sint32 row;
384 UT_uint32 position;
385 UT_uint32 row_width = m_iWidth * m_iBitsPerPlane / 8;
386 while ((row_width & 3) != 0) row_width++;
387 UT_Byte* row_transformed_data = new UT_Byte[row_width];
388
389 switch (m_iBitsPerPlane)
390 {
391 case 1:
392 case 4:
393 case 8:
394 case 16:
395 for (row=m_iHeight-1; row >= 0; row--)
396 {
397 /* Calculating the start of each row */
398 position=m_iOffset + row*row_width;
399 row_data = reinterpret_cast<const unsigned char *>(pBB->getPointer(position));
400 png_write_rows(m_pPNG,const_cast<png_byte **>(reinterpret_cast<const png_byte **>(&row_data)),1);
401 }
402 break;
403 case 24:
404 case 48:
405 for (row=m_iHeight-1; row >= 0; row--)
406 {
407 /* Calculating the start of each row */
408 position=m_iOffset + row*row_width;
409 /* Transforming the b/r to r/b */
410 for (UT_sint32 i=0, col=0; i < m_iWidth; i++,col+=3)
411 {
412 row_transformed_data[col+0] = (UT_Byte)*pBB->getPointer(position+col+2);
413 row_transformed_data[col+1] = (UT_Byte)*pBB->getPointer(position+col+1);
414 row_transformed_data[col+2] = (UT_Byte)*pBB->getPointer(position+col+0);
415 }
416 png_write_rows(m_pPNG,&row_transformed_data,1);
417 }
418 break;
419 case 32:
420 case 64:
421 for (row=m_iHeight-1; row >= 0; row--)
422 {
423 /* Calculating the start of each row */
424 position=m_iOffset + row*row_width;
425 /* Transforming the b/r to r/b */
426 for (UT_sint32 i=0, col=0; i < m_iWidth; i++,col+=4)
427 {
428 row_transformed_data[col+0] = (UT_Byte)*pBB->getPointer(position+col+2);
429 row_transformed_data[col+1] = (UT_Byte)*pBB->getPointer(position+col+1);
430 row_transformed_data[col+2] = (UT_Byte)*pBB->getPointer(position+col+0);
431 row_transformed_data[col+3] = (UT_Byte)*pBB->getPointer(position+col+3);
432 }
433 png_write_rows(m_pPNG,&row_transformed_data,1);
434 }
435 break;
436 default:
437 return UT_IE_BOGUSDOCUMENT;
438 break;
439 }
440 delete [] row_transformed_data;
441
442 png_write_end(m_pPNG,m_pPNGInfo);
443 return UT_OK;
444 }
445
ReadByte(UT_ByteBuf * pBB,UT_uint32 offset)446 UT_Byte IE_ImpGraphic_BMP::ReadByte (UT_ByteBuf* pBB,
447 UT_uint32 offset)
448 {
449 return ( static_cast<const UT_Byte>(ReadBytes(pBB,offset,1) ));
450 }
451
Read2Bytes(UT_ByteBuf * pBB,UT_uint32 offset)452 UT_uint16 IE_ImpGraphic_BMP::Read2Bytes(UT_ByteBuf* pBB,
453 UT_uint32 offset)
454 {
455 return ( static_cast<const UT_uint16>(ReadBytes(pBB,offset,2) ));
456 }
457
458
Read4Bytes(UT_ByteBuf * pBB,UT_uint32 offset)459 UT_uint32 IE_ImpGraphic_BMP::Read4Bytes(UT_ByteBuf* pBB,
460 UT_uint32 offset)
461 {
462 return ( ReadBytes(pBB,offset,4) );
463 }
464
465
ReadBytes(UT_ByteBuf * pBB,UT_uint32 offset,UT_uint32 num_bytes)466 UT_uint32 IE_ImpGraphic_BMP::ReadBytes(UT_ByteBuf* pBB,
467 UT_uint32 offset,
468 UT_uint32 num_bytes)
469 {
470 UT_ASSERT(num_bytes <= 4);
471 m_iBytesRead+=num_bytes;
472
473 if (m_iHeaderSize)
474 {
475 m_bHeaderDone = (m_iBytesRead >= m_iHeaderSize + 14) ?
476 true :
477 false;
478 }
479
480 UT_uint32 result = 0;
481 const UT_Byte* pByte;
482 for (UT_uint32 i=0; i<num_bytes; i++)
483 {
484 pByte = pBB->getPointer(offset+i);
485 result |= *pByte << (i*8);
486 }
487 return (result);
488 }
489
InitializePrivateClassData()490 void IE_ImpGraphic_BMP::InitializePrivateClassData()
491 {
492 m_iFileType=0;
493 m_iFileSize=0;
494 m_iXHotspot=0;
495 m_iYHotspot=0;
496 m_iOffset=0;
497 m_iHeaderSize=0;
498 m_iWidth=0;
499 m_iHeight=0;
500 m_iPlanes=0;
501 m_iBitsPerPlane=0;
502 m_iCompression=0;
503 m_iImageSize=0;
504 m_iXResolution=0;
505 m_iYResolution=0;
506 m_iClrUsed=0;
507 m_iClrImportant=0;
508 m_iResolutionUnits=0;
509 m_iPadding=0;
510 m_iOrigin=0;
511 m_iHalfToning=0;
512 m_iHalfToningParam1=0;
513 m_iHalfToningParam2=0;
514 m_iClrEncoding=0;
515 m_iIdentifier=0;
516 m_iBytesRead=0;
517 m_bOldBMPFormat=false;
518 m_bHeaderDone=false;
519 }
520