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