1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_svtools.hxx"
26
27 #ifndef _BMPACC_HXX
28 #include <vcl/bmpacc.hxx>
29 #endif
30 #ifndef _GRAPH_HXX
31 #include <vcl/graph.hxx>
32 #endif
33 #include "rgbtable.hxx"
34 #define _XPMPRIVATE
35 #include "xpmread.hxx"
36
37 // -------------
38 // - XPMReader -
39 // -------------
40
XPMReader(SvStream & rStm)41 XPMReader::XPMReader( SvStream& rStm ) :
42 mrIStm ( rStm ),
43 mpAcc ( NULL ),
44 mpMaskAcc ( NULL ),
45 mnLastPos ( rStm.Tell() ),
46 mnWidth ( 0 ),
47 mnHeight ( 0 ),
48 mnColors ( 0 ),
49 mnCpp ( 0 ),
50 mbTransparent ( sal_False ),
51 mbStatus ( sal_True ),
52 mnStatus ( 0 ),
53 mnIdentifier ( XPMIDENTIFIER ),
54 mcThisByte ( 0 ),
55 mnTempAvail ( 0 ),
56 mpFastColorTable( NULL ),
57 mpColMap ( NULL )
58 {
59
60 }
61
62 // ------------------------------------------------------------------------
63
~XPMReader()64 XPMReader::~XPMReader()
65 {
66 if( mpAcc )
67 maBmp.ReleaseAccess( mpAcc );
68 }
69
70 // ------------------------------------------------------------------------
71
72 #ifdef _MSC_VER
73 #pragma optimize ("",off)
74 #endif
75
ReadXPM(Graphic & rGraphic)76 ReadState XPMReader::ReadXPM( Graphic& rGraphic )
77 {
78 ReadState eReadState;
79 sal_uInt8 cDummy;
80
81 // sehen, ob wir _alles_ lesen koennen
82 mrIStm.Seek( STREAM_SEEK_TO_END );
83 mrIStm >> cDummy;
84
85 // falls wir nicht alles lesen koennen
86 // kehren wir zurueck und warten auf neue Daten
87 if ( mrIStm.GetError() != ERRCODE_IO_PENDING )
88 {
89 mrIStm.Seek( mnLastPos );
90 mbStatus = sal_True;
91
92 if ( mbStatus )
93 {
94 mpStringBuf = new sal_uInt8 [ XPMSTRINGBUF ];
95 mpTempBuf = new sal_uInt8 [ XPMTEMPBUFSIZE ];
96
97 if ( ( mbStatus = ImplGetString() ) == sal_True )
98 {
99 mnIdentifier = XPMVALUES; // Bitmap informationen einholen
100 mnWidth = ImplGetULONG( 0 );
101 mnHeight = ImplGetULONG( 1 );
102 mnColors = ImplGetULONG( 2 );
103 mnCpp = ImplGetULONG( 3 );
104 }
105 if ( mnColors > ( SAL_MAX_UINT32 / ( 4 + mnCpp ) ) )
106 mbStatus = sal_False;
107 if ( ( mnWidth * mnCpp ) >= XPMSTRINGBUF )
108 mbStatus = sal_False;
109 if ( mbStatus && mnWidth && mnHeight && mnColors && mnCpp )
110 {
111 mnIdentifier = XPMCOLORS;
112
113 // mpColMap beinhaltet fuer jede vorhandene
114 // Farbe: ( mnCpp )Byte(s)-> ASCII Eintrag der der Farbe zugeordnet ist
115 // 1 Byte -> 0xff wenn Farbe transparent ist
116 // 3 Bytes -> RGB Wert der Farbe
117 mpColMap = new sal_uInt8[ mnColors * ( 4 + mnCpp ) ];
118 if ( mpColMap )
119 {
120 for ( sal_uLong i = 0; i < mnColors; i++ )
121 {
122 if ( ImplGetColor( i ) == sal_False )
123 {
124 mbStatus = sal_False;
125 break;
126 }
127 }
128 }
129 else
130 mbStatus = sal_False;
131
132 if ( mbStatus )
133 {
134 // bei mehr als 256 Farben wird eine 24 Bit Grafik erstellt
135 sal_uInt16 nBits = 1;
136 if ( mnColors > 256 )
137 nBits = 24;
138 else if ( mnColors > 16 )
139 nBits = 8;
140 else if ( mnColors > 2 )
141 nBits = 4;
142 else
143 nBits = 1;
144
145 maBmp = Bitmap( Size( mnWidth, mnHeight ), nBits );
146 mpAcc = maBmp.AcquireWriteAccess();
147
148 // mbTransparent ist sal_True wenn mindestens eine Farbe Transparent ist
149 if ( mbTransparent )
150 {
151 maMaskBmp = Bitmap( Size( mnWidth, mnHeight ), 1 );
152 if ( ( mpMaskAcc = maMaskBmp.AcquireWriteAccess() ) == NULL )
153 mbStatus = sal_False;
154 }
155 if( mpAcc && mbStatus )
156 {
157 sal_uLong i;
158 if ( mnColors <=256 ) // palette is only needed by using less than 257
159 { // colors
160
161 sal_uInt8* pPtr = &mpColMap[mnCpp];
162
163 for ( i = 0; i < mnColors; i++ )
164 {
165 mpAcc->SetPaletteColor( (sal_uInt8)i, Color( pPtr[1], pPtr[2], pPtr[3] ) );
166 pPtr += ( mnCpp + 4 );
167 }
168 // using 2 charakters per pixel and less than 257 Colors we speed up
169 if ( mnCpp == 2 ) // by using a 64kb indexing table
170 {
171 mpFastColorTable = new sal_uInt8[ 256 * 256 ];
172 for ( pPtr = mpColMap, i = 0; i < mnColors; i++, pPtr += mnCpp + 4 )
173 {
174 sal_uLong j = pPtr[ 0 ] << 8;
175 j += pPtr[ 1 ];
176 mpFastColorTable[ j ] = (sal_uInt8)i;
177 }
178 }
179 }
180 // now we get the bitmap data
181 mnIdentifier = XPMPIXELS;
182 for ( i = 0; i < mnHeight; i++ )
183 {
184 if ( ImplGetScanLine( i ) == sal_False )
185 {
186 mbStatus = sal_False;
187 break;
188 }
189 }
190 mnIdentifier = XPMEXTENSIONS;
191 }
192 }
193 }
194
195 delete[] mpFastColorTable;
196 delete[] mpColMap;
197 delete[] mpStringBuf;
198 delete[] mpTempBuf;
199
200 }
201 if( mbStatus )
202 {
203 if ( mpMaskAcc )
204 {
205 maMaskBmp.ReleaseAccess ( mpMaskAcc), mpMaskAcc = NULL;
206 maBmp.ReleaseAccess( mpAcc ), mpAcc = NULL;
207 rGraphic = Graphic( BitmapEx( maBmp, maMaskBmp ) );
208 }
209 else
210 {
211 maBmp.ReleaseAccess( mpAcc ), mpAcc = NULL;
212 rGraphic = maBmp;
213 }
214 eReadState = XPMREAD_OK;
215 }
216 else
217 {
218 if ( mpMaskAcc ) maMaskBmp.ReleaseAccess ( mpMaskAcc), mpMaskAcc = NULL;
219 if ( mpAcc ) maBmp.ReleaseAccess( mpAcc ), mpAcc = NULL;
220 eReadState = XPMREAD_ERROR;
221 }
222 }
223 else
224 {
225 mrIStm.ResetError();
226 eReadState = XPMREAD_NEED_MORE;
227 }
228 return eReadState;
229 }
230
231 #ifdef _MSC_VER
232 #pragma optimize ("",on)
233 #endif
234
235 // ------------------------------------------------------------------------
236 // ImplGetColor ermittelt saemtliche Farbwerte,
237 // die Rueckgabe ist sal_True wenn saemtliche Farben zugeordnet werden konnten
238
ImplGetColor(sal_uLong nNumb)239 sal_Bool XPMReader::ImplGetColor( sal_uLong nNumb )
240 {
241 sal_uInt8* pString = mpStringBuf;
242 sal_uInt8* pPtr = ( mpColMap + nNumb * ( 4 + mnCpp ) );
243 sal_Bool bStatus = ImplGetString();
244
245 if ( bStatus )
246 {
247 for ( sal_uLong i = 0; i < mnCpp; i++ )
248 *pPtr++ = *pString++;
249 bStatus = ImplGetColSub ( pPtr );
250 }
251 return bStatus;
252 }
253
254 // ------------------------------------------------------------------------
255 // ImpGetScanLine liest den String mpBufSize aus und schreibt die Pixel in die
256 // Bitmap. Der Parameter nY gibt die horizontale Position an.
257
ImplGetScanLine(sal_uLong nY)258 sal_Bool XPMReader::ImplGetScanLine( sal_uLong nY )
259 {
260 sal_Bool bStatus = ImplGetString();
261 sal_uInt8* pString = mpStringBuf;
262 sal_uInt8* pColor;
263 BitmapColor aWhite;
264 BitmapColor aBlack;
265
266 if ( bStatus )
267 {
268 if ( mpMaskAcc )
269 {
270 aWhite = mpMaskAcc->GetBestMatchingColor( Color( COL_WHITE ) );
271 aBlack = mpMaskAcc->GetBestMatchingColor( Color( COL_BLACK ) );
272 }
273 if ( mnStringSize != ( mnWidth * mnCpp ))
274 bStatus = sal_False;
275 else
276 {
277 sal_uLong i, j;
278 if ( mpFastColorTable )
279 {
280 for ( i = 0; i < mnWidth; i++ )
281 {
282 j = (*pString++) << 8;
283 j += *pString++;
284 sal_uInt8 k = (sal_uInt8)mpFastColorTable[ j ];
285 mpAcc->SetPixel( nY, i, BitmapColor( (sal_uInt8)k ) );
286
287 if ( mpMaskAcc )
288 mpMaskAcc->SetPixel( nY, i,
289 ( mpColMap[ k * (mnCpp + 4) + mnCpp] ) ? aWhite : aBlack );
290 }
291 }
292 else for ( i = 0; i < mnWidth; i++ )
293 {
294 pColor = mpColMap;
295 for ( j = 0; j < mnColors; j++ )
296 {
297 if ( ImplCompare( pString, pColor, mnCpp, XPMCASESENSITIVE ) == sal_True )
298 {
299 if ( mnColors > 256 )
300 mpAcc->SetPixel( nY, i, Color ( pColor[3], pColor[4], pColor[5] ) );
301 else
302 mpAcc->SetPixel( nY, i, BitmapColor( (sal_uInt8) j ) );
303
304 if ( mpMaskAcc )
305 mpMaskAcc->SetPixel( nY, i, (
306 pColor[ mnCpp ] ) ? aWhite : aBlack );
307
308 break;
309 }
310 pColor += ( mnCpp + 4 );
311 }
312 pString += mnCpp;
313 }
314
315 }
316 }
317 return bStatus;
318 }
319
320 // ------------------------------------------------------------------------
321 // versucht aus mpStringBuf einen Farbwert zu uebermitteln
322 // wurde eine Farbe gefunden wird an pDest[1]..pDest[2] der RGB wert geschrieben
323 // pDest[0] enthaelt 0xff wenn die Farbe transparent ist sonst 0
324
ImplGetColSub(sal_uInt8 * pDest)325 sal_Bool XPMReader::ImplGetColSub( sal_uInt8* pDest )
326 {
327 unsigned char cTransparent[] = "None";
328
329 sal_Bool bColStatus = sal_False;
330
331 if ( ImplGetColKey( 'c' ) || ImplGetColKey( 'm' ) || ImplGetColKey( 'g' ) )
332 {
333 // hexentry for RGB or HSV color ?
334 if ( *mpPara == '#' )
335 {
336 *pDest++ = 0;
337 bColStatus = sal_True;
338 switch ( mnParaSize )
339 {
340 case 25 :
341 ImplGetRGBHex ( pDest, 6 );
342 break;
343 case 13 :
344 ImplGetRGBHex ( pDest, 2 );
345 break;
346 case 7 :
347 ImplGetRGBHex ( pDest, 0 );
348 break;
349 default:
350 bColStatus = sal_False;
351 break;
352 }
353 }
354 // maybe pixel is transparent
355 else if ( ImplCompare( &cTransparent[0], mpPara, 4 ))
356 {
357 *pDest++ = 0xff;
358 bColStatus = sal_True;
359 mbTransparent = sal_True;
360 }
361 // last we will try to get the colorname
362 else if ( mnParaSize > 2 ) // name must enlarge the minimum size
363 {
364 sal_uLong i = 0;
365 while ( sal_True )
366 {
367 if ( pRGBTable[ i ].name == NULL )
368 break;
369 if ( pRGBTable[ i ].name[ mnParaSize ] == 0 )
370 {
371 if ( ImplCompare ( (unsigned char*)pRGBTable[ i ].name,
372 mpPara, mnParaSize, XPMCASENONSENSITIVE ) )
373 {
374 bColStatus = sal_True;
375 *pDest++ = 0;
376 *pDest++ = pRGBTable[ i ].red;
377 *pDest++ = pRGBTable[ i ].green;
378 *pDest++ = pRGBTable[ i ].blue;
379 }
380 }
381 i++;
382 }
383 }
384 }
385 return bColStatus;
386 }
387
388 // ------------------------------------------------------------------------
389 // ImplGetColKey durchsuch den String mpStringBuf nach einem Parameter 'nKey'
390 // und gibt einen sal_Bool zurueck. ( wenn sal_True werden mpPara und mnParaSize gesetzt )
391
ImplGetColKey(sal_uInt8 nKey)392 sal_Bool XPMReader::ImplGetColKey( sal_uInt8 nKey )
393 {
394 sal_uInt8 nTemp, nPrev = ' ';
395
396 mpPara = mpStringBuf + mnCpp + 1;
397 mnParaSize = 0;
398
399 while ( *mpPara != 0 )
400 {
401 if ( *mpPara == nKey )
402 {
403 nTemp = *( mpPara + 1 );
404 if ( nTemp == ' ' || nTemp == 0x09 )
405 {
406 if ( nPrev == ' ' || nPrev == 0x09 )
407 break;
408 }
409 }
410 nPrev = *mpPara;
411 mpPara++;
412 }
413 if ( *mpPara )
414 {
415 mpPara++;
416 while ( (*mpPara == ' ') || (*mpPara == 0x09) )
417 {
418 mpPara++;
419 }
420 if ( *mpPara != 0 )
421 {
422 while ( *(mpPara+mnParaSize) != ' ' && *(mpPara+mnParaSize) != 0x09 &&
423 *(mpPara+mnParaSize) != 0 )
424 {
425 mnParaSize++;
426 }
427 }
428 }
429 return ( mnParaSize ) ? sal_True : sal_False;
430 }
431
432 // ------------------------------------------------------------------------
433 // ImplGetRGBHex uebersetzt den ASCII-Hexadezimalwert der sich bei mpPara befindet
434 // in einen RGB wert und schreibt diesen nach pDest
435 // folgende Formate muessen sich bei mpPara befinden:
436 // wenn nAdd = 0 : '#12ab12' -> RGB = 0x12, 0xab, 0x12
437 // 2 : '#1234abcd1234' " " " "
438 // 6 : '#12345678abcdefab12345678' " " " "
439
440
ImplGetRGBHex(sal_uInt8 * pDest,sal_uLong nAdd)441 void XPMReader::ImplGetRGBHex( sal_uInt8* pDest,sal_uLong nAdd )
442 {
443 sal_uInt8* pPtr = mpPara+1;
444 sal_uInt8 nHex, nTemp;
445
446 for ( sal_uLong i = 0; i < 3; i++ )
447 {
448 nHex = (*pPtr++) - '0';
449 if ( nHex > 9 )
450 nHex = ((nHex - 'A' + '0') & 7) + 10;
451
452 nTemp = (*pPtr++) - '0';
453 if ( nTemp > 9 )
454 nTemp = ((nTemp - 'A' + '0') & 7) + 10;
455 nHex = ( nHex << 4 ) + nTemp;
456
457 pPtr += nAdd;
458 *pDest++ = (sal_uInt8)nHex;
459 }
460 }
461
462 // ------------------------------------------------------------------------
463 // ImplGetUlong gibt den wert einer bis zu 6stelligen ASCII-Dezimalzahl zurueck.
464
ImplGetULONG(sal_uLong nPara)465 sal_uLong XPMReader::ImplGetULONG( sal_uLong nPara )
466 {
467 if ( ImplGetPara ( nPara ) )
468 {
469 sal_uLong nRetValue = 0;
470 sal_uInt8* pPtr = mpPara;
471
472 if ( ( mnParaSize > 6 ) || ( mnParaSize == 0 ) ) return 0;
473 for ( sal_uLong i = 0; i < mnParaSize; i++ )
474 {
475 sal_uInt8 j = (*pPtr++) - 48;
476 if ( j > 9 ) return 0; // ascii is invalid
477 nRetValue*=10;
478 nRetValue+=j;
479 }
480 return nRetValue;
481 }
482 else return 0;
483 }
484
485 // ------------------------------------------------------------------------
486
ImplCompare(sal_uInt8 * pSource,sal_uInt8 * pDest,sal_uLong nSize,sal_uLong nMode)487 sal_Bool XPMReader::ImplCompare( sal_uInt8* pSource, sal_uInt8* pDest, sal_uLong nSize, sal_uLong nMode )
488 {
489 sal_Bool bRet = sal_True;
490
491 if ( nMode == XPMCASENONSENSITIVE )
492 {
493 for ( sal_uLong i = 0; i < nSize; i++ )
494 {
495 if ( ( pSource[i]&~0x20 ) != ( pDest[i]&~0x20 ) )
496 {
497 bRet = sal_False;
498 break;
499 }
500 }
501 }
502 else
503 {
504 for ( sal_uLong i = 0; i < nSize; i++ )
505 {
506 if ( pSource[i] != pDest[i] )
507 {
508 bRet = sal_False;
509 break;
510 }
511 }
512 }
513 return bRet;
514 }
515
516 // ------------------------------------------------------------------------
517 // ImplGetPara versucht den nNumb ( 0...x ) Parameter aus mpStringBuf zu ermitteln.
518 // Ein Parameter ist durch Spaces oder Tabs von den anderen getrennt.
519 // Konnte der Parameter gefunden werden ist der Rueckgabewert sal_True und mpPara + mnParaSize
520 // werden gesetzt.
521
ImplGetPara(sal_uLong nNumb)522 sal_Bool XPMReader::ImplGetPara ( sal_uLong nNumb )
523 {
524 sal_uInt8 nByte;
525 sal_uLong pSize = 0;
526 sal_uInt8* pPtr = mpStringBuf;
527 sal_uLong nCount = 0;
528
529 if ( ( *pPtr != ' ' ) && ( *pPtr != 0x09 ) )
530 {
531 mpPara = pPtr;
532 mnParaSize = 0;
533 nCount = 0;
534 }
535 else
536 {
537 mpPara = NULL;
538 nCount = 0xffffffff;
539 }
540
541 while ( pSize < mnStringSize )
542 {
543 nByte = *pPtr;
544
545 if ( mpPara )
546 {
547 if ( ( nByte == ' ' ) || ( nByte == 0x09 ) )
548 {
549 if ( nCount == nNumb )
550 break;
551 else
552 mpPara = NULL;
553 }
554 else
555 mnParaSize++;
556 }
557 else
558 {
559 if ( ( nByte != ' ' ) && ( nByte != 0x09 ) )
560 {
561 mpPara = pPtr;
562 mnParaSize = 1;
563 nCount++;
564 }
565 }
566 pSize++;
567 pPtr++;
568 }
569 return ( ( nCount == nNumb ) && ( mpPara ) ) ? sal_True : sal_False;
570 }
571
572 // ------------------------------------------------------------------------
573 // Der naechste String wird ausgelesen und in mpStringBuf (mit 0 abgeschlossen) abgelegt;
574 // mnStringSize enthaelt die Groesse des gelesenen Strings.
575 // Bemerkungen wie '//' und '/*.....*/' werden uebersprungen.
576
ImplGetString(void)577 sal_Bool XPMReader::ImplGetString( void )
578 {
579 sal_uInt8 sID[] = "/* XPM */";
580 sal_uInt8* pString = mpStringBuf;
581
582 mnStringSize = 0;
583 mpStringBuf[0] = 0;
584
585 while( mbStatus && ( mnStatus != XPMFINISHED ) )
586 {
587 if ( mnTempAvail == 0 )
588 {
589 mnTempAvail = mrIStm.Read( mpTempBuf, XPMTEMPBUFSIZE );
590 if ( mnTempAvail == 0 )
591 break;
592
593 mpTempPtr = mpTempBuf;
594
595 if ( mnIdentifier == XPMIDENTIFIER )
596 {
597 if ( mnTempAvail <= 50 )
598 {
599 mbStatus = sal_False; // file is too short to be a correct XPM format
600 break;
601 }
602 for ( int i = 0; i < 9; i++ ) // searching for "/* XPM */"
603 if ( *mpTempPtr++ != sID[i] )
604 {
605 mbStatus = sal_False;
606 break;
607 }
608 mnTempAvail-=9;
609 mnIdentifier++;
610 }
611 }
612 mcLastByte = mcThisByte;
613 mcThisByte = *mpTempPtr++;
614 mnTempAvail--;
615
616 if ( mnStatus & XPMDOUBLE )
617 {
618 if ( mcThisByte == 0x0a )
619 mnStatus &=~XPMDOUBLE;
620 continue;
621 }
622 if ( mnStatus & XPMREMARK )
623 {
624 if ( ( mcThisByte == '/' ) && ( mcLastByte == '*' ) )
625 mnStatus &=~XPMREMARK;
626 continue;
627 }
628 if ( mnStatus & XPMSTRING ) // characters in string
629 {
630 if ( mcThisByte == '"' )
631 {
632 mnStatus &=~XPMSTRING; // end of parameter by eol
633 break;
634 }
635 if ( mnStringSize >= ( XPMSTRINGBUF - 1 ) )
636 {
637 mbStatus = sal_False;
638 break;
639 }
640 *pString++ = mcThisByte;
641 pString[0] = 0;
642 mnStringSize++;
643 continue;
644 }
645 else
646 { // characters beside string
647 switch ( mcThisByte )
648 {
649 case '*' :
650 if ( mcLastByte == '/' ) mnStatus |= XPMREMARK;
651 break;
652 case '/' :
653 if ( mcLastByte == '/' ) mnStatus |= XPMDOUBLE;
654 break;
655 case '"' : mnStatus |= XPMSTRING;
656 break;
657 case '{' :
658 if ( mnIdentifier == XPMDEFINITION )
659 mnIdentifier++;
660 break;
661 case '}' :
662 if ( mnIdentifier == XPMENDEXT )
663 mnStatus = XPMFINISHED;
664 break;
665 }
666 }
667 }
668 return mbStatus;
669 }
670
671 // -------------
672 // - ImportXPM -
673 // -------------
674
ImportXPM(SvStream & rStm,Graphic & rGraphic)675 sal_Bool ImportXPM( SvStream& rStm, Graphic& rGraphic )
676 {
677 XPMReader* pXPMReader = (XPMReader*) rGraphic.GetContext();
678 ReadState eReadState;
679 sal_Bool bRet = sal_True;
680
681 if( !pXPMReader )
682 pXPMReader = new XPMReader( rStm );
683
684 rGraphic.SetContext( NULL );
685 eReadState = pXPMReader->ReadXPM( rGraphic );
686
687 if( eReadState == XPMREAD_ERROR )
688 {
689 bRet = sal_False;
690 delete pXPMReader;
691 }
692 else if( eReadState == XPMREAD_OK )
693 delete pXPMReader;
694 else
695 rGraphic.SetContext( pXPMReader );
696
697 return bRet;
698 }
699