xref: /trunk/main/vcl/win/source/gdi/salbmp.cxx (revision a893be29343ee97512d484e6e8fefa91df2b44cb)
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 // MARKER(update_precomp.py): autogen include statement, do not remove
23 #include "precompiled_vcl.hxx"
24 
25 #include <tools/svwin.h>
26 #include <vcl/bitmap.hxx> // for BitmapSystemData
27 #include <vcl/salbtype.hxx>
28 #include <win/wincomp.hxx>
29 #include <win/salgdi.h>
30 #include <win/saldata.hxx>
31 #include <win/salbmp.h>
32 #include <string.h>
33 #include <vcl/timer.hxx>
34 #include <comphelper/broadcasthelper.hxx>
35 #include <map>
36 
37 #ifndef min
38 #define min(a,b)    (((a) < (b)) ? (a) : (b))
39 #endif
40 #ifndef max
41 #define max(a,b)    (((a) > (b)) ? (a) : (b))
42 #endif
43 
44 #include <GdiPlus.h>
45 
46 // ------------------------------------------------------------------
47 // - Inlines -
48 
49 inline void ImplSetPixel4( const HPBYTE pScanline, long nX, const BYTE cIndex )
50 {
51     BYTE& rByte = pScanline[ nX >> 1 ];
52 
53     ( nX & 1 ) ? ( rByte &= 0xf0, rByte |= ( cIndex & 0x0f ) ) :
54                  ( rByte &= 0x0f, rByte |= ( cIndex << 4 ) );
55 }
56 
57 // ------------------------------------------------------------------
58 // Helper class to manage Gdiplus::Bitmap instances inside of
59 // WinSalBitmap
60 
61 struct Comparator
62 {
63     bool operator()(WinSalBitmap* pA, WinSalBitmap* pB) const
64     {
65         return pA < pB;
66     }
67 };
68 
69 typedef ::std::map< WinSalBitmap*, sal_uInt32, Comparator > EntryMap;
70 static const sal_uInt32 nDefaultCycles(60);
71 
72 class GdiPlusBuffer : protected comphelper::OBaseMutex, public Timer
73 {
74 private:
75     EntryMap        maEntries;
76 
77 public:
78     GdiPlusBuffer()
79     :   Timer(),
80         maEntries()
81     {
82         SetTimeout(1000);
83         Stop();
84     }
85 
86     ~GdiPlusBuffer()
87     {
88         Stop();
89     }
90 
91     void addEntry(WinSalBitmap& rEntry)
92     {
93         ::osl::MutexGuard aGuard(m_aMutex);
94         EntryMap::iterator aFound = maEntries.find(&rEntry);
95 
96         if(aFound == maEntries.end())
97         {
98             if(maEntries.empty())
99             {
100                 Start();
101             }
102 
103             maEntries[&rEntry] = nDefaultCycles;
104         }
105     }
106 
107     void remEntry(WinSalBitmap& rEntry)
108     {
109         ::osl::MutexGuard aGuard(m_aMutex);
110         EntryMap::iterator aFound = maEntries.find(&rEntry);
111 
112         if(aFound != maEntries.end())
113         {
114             maEntries.erase(aFound);
115 
116             if(maEntries.empty())
117             {
118                 Stop();
119             }
120         }
121     }
122 
123     void touchEntry(WinSalBitmap& rEntry)
124     {
125         ::osl::MutexGuard aGuard(m_aMutex);
126         EntryMap::iterator aFound = maEntries.find(&rEntry);
127 
128         if(aFound != maEntries.end())
129         {
130             aFound->second = nDefaultCycles;
131         }
132     }
133 
134     // from parent Timer
135     virtual void Timeout()
136     {
137         ::osl::MutexGuard aGuard(m_aMutex);
138         EntryMap::iterator aIter(maEntries.begin());
139 
140         while(aIter != maEntries.end())
141         {
142             if(aIter->second)
143             {
144                 aIter->second--;
145                 aIter++;
146             }
147             else
148             {
149                 EntryMap::iterator aDelete(aIter);
150                 WinSalBitmap* pSource = aDelete->first;
151                 aIter++;
152                 maEntries.erase(aDelete);
153 
154                 if(maEntries.empty())
155                 {
156                     Stop();
157                 }
158 
159                 // delete at WinSalBitmap after entry is removed; this
160                 // way it would not hurt to call remEntry from there, too
161                 if(pSource->maGdiPlusBitmap.get())
162                 {
163                     pSource->maGdiPlusBitmap.reset();
164                     pSource->mpAssociatedAlpha = 0;
165                 }
166             }
167         }
168 
169         if(!maEntries.empty())
170         {
171             Start();
172         }
173     }
174 };
175 
176 // ------------------------------------------------------------------
177 // Global instance of GdiPlusBuffer which manages Gdiplus::Bitmap
178 // instances
179 
180 static GdiPlusBuffer aGdiPlusBuffer;
181 
182 // ------------------------------------------------------------------
183 // - WinSalBitmap -
184 
185 WinSalBitmap::WinSalBitmap()
186 :   maSize(),
187     mhDIB(0),
188     mhDDB(0),
189     maGdiPlusBitmap(),
190     mpAssociatedAlpha(0),
191     mnBitCount(0)
192 {
193 }
194 
195 // ------------------------------------------------------------------
196 
197 WinSalBitmap::~WinSalBitmap()
198 {
199     Destroy();
200 }
201 
202 // ------------------------------------------------------------------
203 
204 void WinSalBitmap::Destroy()
205 {
206     if(maGdiPlusBitmap.get())
207     {
208         aGdiPlusBuffer.remEntry(*this);
209     }
210 
211     if( mhDIB )
212         GlobalFree( mhDIB );
213     else if( mhDDB )
214         DeleteObject( mhDDB );
215 
216     maSize = Size();
217     mnBitCount = 0;
218 }
219 
220 // ------------------------------------------------------------------
221 
222 GdiPlusBmpPtr WinSalBitmap::ImplGetGdiPlusBitmap(const WinSalBitmap* pAlphaSource) const
223 {
224     WinSalBitmap* pThat = const_cast< WinSalBitmap* >(this);
225 
226     if(maGdiPlusBitmap.get() && pAlphaSource != mpAssociatedAlpha)
227     {
228         // #122350# if associated alpha with which the GDIPlus was constructed has changed
229         // it is necessary to remove it from buffer, reset reference to it and reconstruct
230         pThat->maGdiPlusBitmap.reset();
231         aGdiPlusBuffer.remEntry(const_cast< WinSalBitmap& >(*this));
232     }
233 
234     if(maGdiPlusBitmap.get())
235     {
236         aGdiPlusBuffer.touchEntry(const_cast< WinSalBitmap& >(*this));
237     }
238     else
239     {
240         if(maSize.Width() > 0 && maSize.Height() > 0)
241         {
242             if(pAlphaSource)
243             {
244                 pThat->maGdiPlusBitmap.reset(pThat->ImplCreateGdiPlusBitmap(*pAlphaSource));
245                 pThat->mpAssociatedAlpha = pAlphaSource;
246             }
247             else
248             {
249                 pThat->maGdiPlusBitmap.reset(pThat->ImplCreateGdiPlusBitmap());
250                 pThat->mpAssociatedAlpha = 0;
251             }
252 
253             if(maGdiPlusBitmap.get())
254             {
255                 aGdiPlusBuffer.addEntry(*pThat);
256             }
257         }
258     }
259 
260     return maGdiPlusBitmap;
261 }
262 
263 // ------------------------------------------------------------------
264 
265 Gdiplus::Bitmap* WinSalBitmap::ImplCreateGdiPlusBitmap()
266 {
267     Gdiplus::Bitmap* pRetval(0);
268     WinSalBitmap* pSalRGB = const_cast< WinSalBitmap* >(this);
269     WinSalBitmap* pExtraWinSalRGB = 0;
270 
271     if(!pSalRGB->ImplGethDIB())
272     {
273         // we need DIB for success with AcquireBuffer, create a replacement WinSalBitmap
274         pExtraWinSalRGB = new WinSalBitmap();
275         pExtraWinSalRGB->Create(*pSalRGB, pSalRGB->GetBitCount());
276         pSalRGB = pExtraWinSalRGB;
277     }
278 
279     BitmapBuffer* pRGB = pSalRGB->AcquireBuffer(true);
280     BitmapBuffer* pExtraRGB = 0;
281 
282     if(pRGB && BMP_FORMAT_24BIT_TC_BGR != (pRGB->mnFormat & ~BMP_FORMAT_TOP_DOWN))
283     {
284         // convert source bitmap to BMP_FORMAT_24BIT_TC_BGR format if not yet in that format
285         SalTwoRect aSalTwoRect;
286 
287         aSalTwoRect.mnSrcX = aSalTwoRect.mnSrcY = aSalTwoRect.mnDestX = aSalTwoRect.mnDestY = 0;
288         aSalTwoRect.mnSrcWidth = aSalTwoRect.mnDestWidth = pRGB->mnWidth;
289         aSalTwoRect.mnSrcHeight = aSalTwoRect.mnDestHeight = pRGB->mnHeight;
290 
291         pExtraRGB = StretchAndConvert(
292             *pRGB,
293             aSalTwoRect,
294             BMP_FORMAT_24BIT_TC_BGR,
295             0);
296 
297         pSalRGB->ReleaseBuffer(pRGB, true);
298         pRGB = pExtraRGB;
299     }
300 
301     if(pRGB
302         && pRGB->mnWidth > 0
303         && pRGB->mnHeight > 0
304         && BMP_FORMAT_24BIT_TC_BGR == (pRGB->mnFormat & ~BMP_FORMAT_TOP_DOWN))
305     {
306         const sal_uInt32 nW(pRGB->mnWidth);
307         const sal_uInt32 nH(pRGB->mnHeight);
308 
309         pRetval = new Gdiplus::Bitmap(nW, nH, PixelFormat24bppRGB);
310 
311         if(pRetval)
312         {
313             sal_uInt8* pSrcRGB(pRGB->mpBits);
314             const sal_uInt32 nExtraRGB(pRGB->mnScanlineSize - (nW * 3));
315             const bool bTopDown(pRGB->mnFormat & BMP_FORMAT_TOP_DOWN);
316             const Gdiplus::Rect aAllRect(0, 0, nW, nH);
317             Gdiplus::BitmapData aGdiPlusBitmapData;
318             pRetval->LockBits(&aAllRect, Gdiplus::ImageLockModeWrite, PixelFormat24bppRGB, &aGdiPlusBitmapData);
319 
320             // copy data to Gdiplus::Bitmap; format is BGR here in both cases, so memcpy is possible
321             for(sal_uInt32 y(0); y < nH; y++)
322             {
323                 const sal_uInt32 nYInsert(bTopDown ? y : nH - y - 1);
324                 sal_uInt8* targetPixels = (sal_uInt8*)aGdiPlusBitmapData.Scan0 + (nYInsert * aGdiPlusBitmapData.Stride);
325 
326                 memcpy(targetPixels, pSrcRGB, nW * 3);
327                 pSrcRGB += nW * 3 + nExtraRGB;
328             }
329 
330             pRetval->UnlockBits(&aGdiPlusBitmapData);
331         }
332     }
333 
334     if(pExtraRGB)
335     {
336         // #123478# shockingly, BitmapBuffer does not free the memory it is controlling
337         // in it's destructor, this *has to be done handish*. Doing it here now
338         delete[] pExtraRGB->mpBits;
339         delete pExtraRGB;
340     }
341     else
342     {
343         pSalRGB->ReleaseBuffer(pRGB, true);
344     }
345 
346     if(pExtraWinSalRGB)
347     {
348         delete pExtraWinSalRGB;
349     }
350 
351     return pRetval;
352 }
353 
354 // ------------------------------------------------------------------
355 
356 Gdiplus::Bitmap* WinSalBitmap::ImplCreateGdiPlusBitmap(const WinSalBitmap& rAlphaSource)
357 {
358     Gdiplus::Bitmap* pRetval(0);
359     WinSalBitmap* pSalRGB = const_cast< WinSalBitmap* >(this);
360     WinSalBitmap* pExtraWinSalRGB = 0;
361 
362     if(!pSalRGB->ImplGethDIB())
363     {
364         // we need DIB for success with AcquireBuffer, create a replacement WinSalBitmap
365         pExtraWinSalRGB = new WinSalBitmap();
366         pExtraWinSalRGB->Create(*pSalRGB, pSalRGB->GetBitCount());
367         pSalRGB = pExtraWinSalRGB;
368     }
369 
370     BitmapBuffer* pRGB = pSalRGB->AcquireBuffer(true);
371     BitmapBuffer* pExtraRGB = 0;
372 
373     if(pRGB && BMP_FORMAT_24BIT_TC_BGR != (pRGB->mnFormat & ~BMP_FORMAT_TOP_DOWN))
374     {
375         // convert source bitmap to BMP_FORMAT_24BIT_TC_BGR format if not yet in that format
376         SalTwoRect aSalTwoRect;
377 
378         aSalTwoRect.mnSrcX = aSalTwoRect.mnSrcY = aSalTwoRect.mnDestX = aSalTwoRect.mnDestY = 0;
379         aSalTwoRect.mnSrcWidth = aSalTwoRect.mnDestWidth = pRGB->mnWidth;
380         aSalTwoRect.mnSrcHeight = aSalTwoRect.mnDestHeight = pRGB->mnHeight;
381 
382         pExtraRGB = StretchAndConvert(
383             *pRGB,
384             aSalTwoRect,
385             BMP_FORMAT_24BIT_TC_BGR,
386             0);
387 
388         pSalRGB->ReleaseBuffer(pRGB, true);
389         pRGB = pExtraRGB;
390     }
391 
392     WinSalBitmap* pSalA = const_cast< WinSalBitmap* >(&rAlphaSource);
393     WinSalBitmap* pExtraWinSalA = 0;
394 
395     if(!pSalA->ImplGethDIB())
396     {
397         // we need DIB for success with AcquireBuffer, create a replacement WinSalBitmap
398         pExtraWinSalA = new WinSalBitmap();
399         pExtraWinSalA->Create(*pSalA, pSalA->GetBitCount());
400         pSalA = pExtraWinSalA;
401     }
402 
403     BitmapBuffer* pA = pSalA->AcquireBuffer(true);
404     BitmapBuffer* pExtraA = 0;
405 
406     if(pA && BMP_FORMAT_8BIT_PAL != (pA->mnFormat & ~BMP_FORMAT_TOP_DOWN))
407     {
408         // convert alpha bitmap to BMP_FORMAT_8BIT_PAL format if not yet in that format
409         SalTwoRect aSalTwoRect;
410 
411         aSalTwoRect.mnSrcX = aSalTwoRect.mnSrcY = aSalTwoRect.mnDestX = aSalTwoRect.mnDestY = 0;
412         aSalTwoRect.mnSrcWidth = aSalTwoRect.mnDestWidth = pA->mnWidth;
413         aSalTwoRect.mnSrcHeight = aSalTwoRect.mnDestHeight = pA->mnHeight;
414         const BitmapPalette& rTargetPalette = Bitmap::GetGreyPalette(256);
415 
416         pExtraA = StretchAndConvert(
417             *pA,
418             aSalTwoRect,
419             BMP_FORMAT_8BIT_PAL,
420             &rTargetPalette);
421 
422         pSalA->ReleaseBuffer(pA, true);
423         pA = pExtraA;
424     }
425 
426     if(pRGB
427         && pA
428         && pRGB->mnWidth > 0
429         && pRGB->mnHeight > 0
430         && pRGB->mnWidth == pA->mnWidth
431         && pRGB->mnHeight == pA->mnHeight
432         && BMP_FORMAT_24BIT_TC_BGR == (pRGB->mnFormat & ~BMP_FORMAT_TOP_DOWN)
433         && BMP_FORMAT_8BIT_PAL == (pA->mnFormat & ~BMP_FORMAT_TOP_DOWN))
434     {
435         // we have alpha and bitmap in known formats, create GdiPlus Bitmap as 32bit ARGB
436         const sal_uInt32 nW(pRGB->mnWidth);
437         const sal_uInt32 nH(pRGB->mnHeight);
438 
439         pRetval = new Gdiplus::Bitmap(nW, nH, PixelFormat32bppARGB);
440 
441         if(pRetval)
442         {
443             sal_uInt8* pSrcRGB(pRGB->mpBits);
444             sal_uInt8* pSrcA(pA->mpBits);
445             const sal_uInt32 nExtraRGB(pRGB->mnScanlineSize - (nW * 3));
446             const sal_uInt32 nExtraA(pA->mnScanlineSize - nW);
447             const bool bTopDown(pRGB->mnFormat & BMP_FORMAT_TOP_DOWN);
448             const Gdiplus::Rect aAllRect(0, 0, nW, nH);
449             Gdiplus::BitmapData aGdiPlusBitmapData;
450             pRetval->LockBits(&aAllRect, Gdiplus::ImageLockModeWrite, PixelFormat32bppARGB, &aGdiPlusBitmapData);
451 
452             // copy data to Gdiplus::Bitmap; format is BGRA; need to mix BGR from Bitmap and
453             // A from alpha, so inner loop is needed (who invented BitmapEx..?)
454             for(sal_uInt32 y(0); y < nH; y++)
455             {
456                 const sal_uInt32 nYInsert(bTopDown ? y : nH - y - 1);
457                 sal_uInt8* targetPixels = (sal_uInt8*)aGdiPlusBitmapData.Scan0 + (nYInsert * aGdiPlusBitmapData.Stride);
458 
459                 for(sal_uInt32 x(0); x < nW; x++)
460                 {
461                     *targetPixels++ = *pSrcRGB++;
462                     *targetPixels++ = *pSrcRGB++;
463                     *targetPixels++ = *pSrcRGB++;
464                     *targetPixels++ = 0xff - *pSrcA++;
465                 }
466 
467                 pSrcRGB += nExtraRGB;
468                 pSrcA += nExtraA;
469             }
470 
471             pRetval->UnlockBits(&aGdiPlusBitmapData);
472         }
473     }
474 
475     if(pExtraA)
476     {
477         // #123478# shockingly, BitmapBuffer does not free the memory it is controlling
478         // in it's destructor, this *has to be done handish*. Doing it here now
479         delete[] pExtraA->mpBits;
480         delete pExtraA;
481     }
482     else
483     {
484         pSalA->ReleaseBuffer(pA, true);
485     }
486 
487     if(pExtraWinSalA)
488     {
489         delete pExtraWinSalA;
490     }
491 
492     if(pExtraRGB)
493     {
494         // #123478# shockingly, BitmapBuffer does not free the memory it is controlling
495         // in it's destructor, this *has to be done handish*. Doing it here now
496         delete[] pExtraRGB->mpBits;
497         delete pExtraRGB;
498     }
499     else
500     {
501         pSalRGB->ReleaseBuffer(pRGB, true);
502     }
503 
504     if(pExtraWinSalRGB)
505     {
506         delete pExtraWinSalRGB;
507     }
508 
509     return pRetval;
510 }
511 
512 // ------------------------------------------------------------------
513 
514 bool WinSalBitmap::Create( HANDLE hBitmap, bool bDIB, bool bCopyHandle )
515 {
516     bool bRet = TRUE;
517 
518     if( bDIB )
519         mhDIB = (HGLOBAL) ( bCopyHandle ? ImplCopyDIBOrDDB( hBitmap, TRUE ) : hBitmap );
520     else
521         mhDDB = (HBITMAP) ( bCopyHandle ? ImplCopyDIBOrDDB( hBitmap, FALSE ) : hBitmap );
522 
523     if( mhDIB )
524     {
525         PBITMAPINFOHEADER pBIH = (PBITMAPINFOHEADER) GlobalLock( mhDIB );
526 
527         maSize = Size( pBIH->biWidth, pBIH->biHeight );
528         mnBitCount = pBIH->biBitCount;
529 
530         if( mnBitCount )
531             mnBitCount = ( mnBitCount <= 1 ) ? 1 : ( mnBitCount <= 4 ) ? 4 : ( mnBitCount <= 8 ) ? 8 : 24;
532 
533         GlobalUnlock( mhDIB );
534     }
535     else if( mhDDB )
536     {
537         BITMAP  aDDBInfo;
538 
539         if( WIN_GetObject( mhDDB, sizeof( BITMAP ), &aDDBInfo ) )
540         {
541             maSize = Size( aDDBInfo.bmWidth, aDDBInfo.bmHeight );
542             mnBitCount = aDDBInfo.bmPlanes * aDDBInfo.bmBitsPixel;
543 
544             if( mnBitCount )
545             {
546                 mnBitCount = ( mnBitCount <= 1 ) ? 1 :
547                              ( mnBitCount <= 4 ) ? 4 :
548                              ( mnBitCount <= 8 ) ? 8 : 24;
549             }
550         }
551         else
552         {
553             mhDDB = 0;
554             bRet = FALSE;
555         }
556     }
557     else
558         bRet = FALSE;
559 
560     return bRet;
561 }
562 
563 // ------------------------------------------------------------------
564 
565 bool WinSalBitmap::Create( const Size& rSize, sal_uInt16 nBitCount, const BitmapPalette& rPal )
566 {
567     bool bRet = FALSE;
568 
569     mhDIB = ImplCreateDIB( rSize, nBitCount, rPal );
570 
571     if( mhDIB )
572     {
573         maSize = rSize;
574         mnBitCount = nBitCount;
575         bRet = TRUE;
576     }
577 
578     return bRet;
579 }
580 
581 // ------------------------------------------------------------------
582 
583 bool WinSalBitmap::Create( const SalBitmap& rSSalBitmap )
584 {
585     bool bRet = FALSE;
586     const WinSalBitmap& rSalBitmap = static_cast<const WinSalBitmap&>(rSSalBitmap);
587 
588     if ( rSalBitmap.mhDIB || rSalBitmap.mhDDB )
589     {
590         HANDLE hNewHdl = ImplCopyDIBOrDDB( rSalBitmap.mhDIB ? rSalBitmap.mhDIB : rSalBitmap.mhDDB,
591                                            rSalBitmap.mhDIB != 0 );
592 
593         if ( hNewHdl )
594         {
595             if( rSalBitmap.mhDIB )
596                 mhDIB = (HGLOBAL) hNewHdl;
597             else if( rSalBitmap.mhDDB )
598                 mhDDB = (HBITMAP) hNewHdl;
599 
600             maSize = rSalBitmap.maSize;
601             mnBitCount = rSalBitmap.mnBitCount;
602 
603             bRet = TRUE;
604         }
605     }
606 
607     return bRet;
608 }
609 
610 // ------------------------------------------------------------------
611 
612 bool WinSalBitmap::Create( const SalBitmap& rSSalBmp, SalGraphics* pSGraphics )
613 {
614     bool bRet = FALSE;
615 
616     const WinSalBitmap& rSalBmp = static_cast<const WinSalBitmap&>(rSSalBmp);
617     WinSalGraphics* pGraphics = static_cast<WinSalGraphics*>(pSGraphics);
618 
619     if( rSalBmp.mhDIB )
620     {
621         PBITMAPINFO         pBI = (PBITMAPINFO) GlobalLock( rSalBmp.mhDIB );
622         PBITMAPINFOHEADER   pBIH = (PBITMAPINFOHEADER) pBI;
623         HDC                 hDC  = pGraphics->getHDC();
624         HBITMAP             hNewDDB;
625         BITMAP              aDDBInfo;
626         PBYTE               pBits = (PBYTE) pBI + *(DWORD*) pBI +
627                             ImplGetDIBColorCount( rSalBmp.mhDIB ) * sizeof( RGBQUAD );
628 
629         if( pBIH->biBitCount == 1 )
630         {
631             hNewDDB = CreateBitmap( pBIH->biWidth, pBIH->biHeight, 1, 1, NULL );
632 
633             if( hNewDDB )
634                 SetDIBits( hDC, hNewDDB, 0, pBIH->biHeight, pBits, pBI, DIB_RGB_COLORS );
635         }
636         else
637             hNewDDB = CreateDIBitmap( hDC, (PBITMAPINFOHEADER) pBI, CBM_INIT, pBits, pBI, DIB_RGB_COLORS );
638 
639         GlobalUnlock( rSalBmp.mhDIB );
640 
641         if( hNewDDB && WIN_GetObject( hNewDDB, sizeof( BITMAP ), &aDDBInfo ) )
642         {
643             mhDDB = hNewDDB;
644             maSize = Size( aDDBInfo.bmWidth, aDDBInfo.bmHeight );
645             mnBitCount = aDDBInfo.bmPlanes * aDDBInfo.bmBitsPixel;
646 
647             bRet = TRUE;
648         }
649         else if( hNewDDB )
650             DeleteObject( hNewDDB );
651     }
652 
653     return bRet;
654 }
655 
656 // ------------------------------------------------------------------
657 
658 bool WinSalBitmap::Create( const SalBitmap& rSSalBmp, sal_uInt16 nNewBitCount )
659 {
660     bool bRet = FALSE;
661 
662     const WinSalBitmap& rSalBmp = static_cast<const WinSalBitmap&>(rSSalBmp);
663 
664     if( rSalBmp.mhDDB )
665     {
666         mhDIB = ImplCreateDIB( rSalBmp.maSize, nNewBitCount, BitmapPalette() );
667 
668         if( mhDIB )
669         {
670             PBITMAPINFO pBI = (PBITMAPINFO) GlobalLock( mhDIB );
671             const int   nLines = (int) rSalBmp.maSize.Height();
672             HDC         hDC = GetDC( 0 );
673             PBYTE       pBits = (PBYTE) pBI + *(DWORD*) pBI +
674                                 ImplGetDIBColorCount( mhDIB ) * sizeof( RGBQUAD );
675             SalData*    pSalData = GetSalData();
676             HPALETTE    hOldPal = 0;
677 
678             if ( pSalData->mhDitherPal )
679             {
680                 hOldPal = SelectPalette( hDC, pSalData->mhDitherPal, TRUE );
681                 RealizePalette( hDC );
682             }
683 
684             if( GetDIBits( hDC, rSalBmp.mhDDB, 0, nLines, pBits, pBI, DIB_RGB_COLORS ) == nLines )
685             {
686                 GlobalUnlock( mhDIB );
687                 maSize = rSalBmp.maSize;
688                 mnBitCount = nNewBitCount;
689                 bRet = TRUE;
690             }
691             else
692             {
693                 GlobalUnlock( mhDIB );
694                 GlobalFree( mhDIB );
695                 mhDIB = 0;
696             }
697 
698             if( hOldPal )
699                 SelectPalette( hDC, hOldPal, TRUE );
700 
701             ReleaseDC( 0, hDC );
702         }
703     }
704 
705     return bRet;
706 }
707 
708 // ------------------------------------------------------------------
709 
710 sal_uInt16 WinSalBitmap::ImplGetDIBColorCount( HGLOBAL hDIB )
711 {
712     sal_uInt16 nColors = 0;
713 
714     if( hDIB )
715     {
716         PBITMAPINFO         pBI = (PBITMAPINFO) GlobalLock( hDIB );
717         PBITMAPINFOHEADER   pBIH = (PBITMAPINFOHEADER) pBI;
718 
719         if ( pBIH->biSize != sizeof( BITMAPCOREHEADER ) )
720         {
721             if( pBIH->biBitCount <= 8 )
722             {
723                 if ( pBIH->biClrUsed )
724                     nColors = (sal_uInt16) pBIH->biClrUsed;
725                 else
726                     nColors = 1 << pBIH->biBitCount;
727             }
728         }
729         else if( ( (PBITMAPCOREHEADER) pBI )->bcBitCount <= 8 )
730             nColors = 1 << ( (PBITMAPCOREHEADER) pBI )->bcBitCount;
731 
732         GlobalUnlock( hDIB );
733     }
734 
735     return nColors;
736 }
737 
738 // ------------------------------------------------------------------
739 
740 HGLOBAL WinSalBitmap::ImplCreateDIB( const Size& rSize, sal_uInt16 nBits, const BitmapPalette& rPal )
741 {
742     DBG_ASSERT( nBits == 1 || nBits == 4 || nBits == 8 || nBits == 16 || nBits == 24, "Unsupported BitCount!" );
743 
744     HGLOBAL hDIB = 0;
745 
746     if( rSize.Width() <= 0 || rSize.Height() <= 0 )
747         return hDIB;
748 
749     // calculate bitmap size in Bytes
750     const sal_uLong nAlignedWidth4Bytes = AlignedWidth4Bytes( nBits * rSize.Width() );
751     const sal_uLong nImageSize = nAlignedWidth4Bytes * rSize.Height();
752     bool bOverflow = (nImageSize / nAlignedWidth4Bytes) != rSize.Height();
753     if( bOverflow )
754         return hDIB;
755 
756     // allocate bitmap memory including header and palette
757     const sal_uInt16 nColors = (nBits <= 8) ? (1 << nBits) : 0;
758     const sal_uLong nHeaderSize = sizeof( BITMAPINFOHEADER ) + nColors * sizeof( RGBQUAD );
759     bOverflow = (nHeaderSize + nImageSize) < nImageSize;
760     if( bOverflow )
761         return hDIB;
762 
763     hDIB = GlobalAlloc( GHND, nHeaderSize + nImageSize );
764     if( !hDIB )
765         return hDIB;
766 
767     PBITMAPINFO pBI = static_cast<PBITMAPINFO>( GlobalLock( hDIB ) );
768     PBITMAPINFOHEADER pBIH = reinterpret_cast<PBITMAPINFOHEADER>( pBI );
769 
770     pBIH->biSize = sizeof( BITMAPINFOHEADER );
771     pBIH->biWidth = rSize.Width();
772     pBIH->biHeight = rSize.Height();
773     pBIH->biPlanes = 1;
774     pBIH->biBitCount = nBits;
775     pBIH->biCompression = BI_RGB;
776     pBIH->biSizeImage = nImageSize;
777     pBIH->biXPelsPerMeter = 0;
778     pBIH->biYPelsPerMeter = 0;
779     pBIH->biClrUsed = 0;
780     pBIH->biClrImportant = 0;
781 
782     if( nColors )
783     {
784         // copy the palette entries if any
785         const sal_uInt16 nMinCount = Min( nColors, rPal.GetEntryCount() );
786         if( nMinCount )
787             memcpy( pBI->bmiColors, rPal.ImplGetColorBuffer(), nMinCount * sizeof(RGBQUAD) );
788     }
789 
790     GlobalUnlock( hDIB );
791 
792     return hDIB;
793 }
794 
795 // ------------------------------------------------------------------
796 
797 HANDLE WinSalBitmap::ImplCopyDIBOrDDB( HANDLE hHdl, bool bDIB )
798 {
799     HANDLE  hCopy = 0;
800 
801     if ( bDIB && hHdl )
802     {
803         const sal_uLong nSize = GlobalSize( hHdl );
804 
805         if ( (hCopy = GlobalAlloc( GHND, nSize  )) != 0 )
806         {
807             memcpy( (LPSTR) GlobalLock( hCopy ), (LPSTR) GlobalLock( hHdl ), nSize );
808 
809             GlobalUnlock( hCopy );
810             GlobalUnlock( hHdl );
811         }
812     }
813     else if ( hHdl )
814     {
815         BITMAP aBmp;
816 
817         // Source-Bitmap nach Groesse befragen
818         WIN_GetObject( hHdl, sizeof( BITMAP ), (LPSTR) &aBmp );
819 
820         // Destination-Bitmap erzeugen
821         if ( (hCopy = CreateBitmapIndirect( &aBmp )) != 0 )
822         {
823             HDC     hBmpDC = CreateCompatibleDC( 0 );
824             HBITMAP hBmpOld = (HBITMAP) SelectObject( hBmpDC, hHdl );
825             HDC     hCopyDC = CreateCompatibleDC( hBmpDC );
826             HBITMAP hCopyOld = (HBITMAP) SelectObject( hCopyDC, hCopy );
827 
828             BitBlt( hCopyDC, 0, 0, aBmp.bmWidth, aBmp.bmHeight, hBmpDC, 0, 0, SRCCOPY );
829 
830             SelectObject( hCopyDC, hCopyOld );
831             DeleteDC( hCopyDC );
832 
833             SelectObject( hBmpDC, hBmpOld );
834             DeleteDC( hBmpDC );
835         }
836     }
837 
838     return hCopy;
839 }
840 
841 // ------------------------------------------------------------------
842 
843 BitmapBuffer* WinSalBitmap::AcquireBuffer( bool /*bReadOnly*/ )
844 {
845     BitmapBuffer* pBuffer = NULL;
846 
847     if( mhDIB )
848     {
849         PBITMAPINFO         pBI = (PBITMAPINFO) GlobalLock( mhDIB );
850         PBITMAPINFOHEADER   pBIH = (PBITMAPINFOHEADER) pBI;
851 
852         if( ( pBIH->biCompression == BI_RLE4 ) || ( pBIH->biCompression == BI_RLE8 ) )
853         {
854             Size    aSizePix( pBIH->biWidth, pBIH->biHeight );
855             HGLOBAL hNewDIB = ImplCreateDIB( aSizePix, pBIH->biBitCount, BitmapPalette() );
856 
857             if( hNewDIB )
858             {
859                 PBITMAPINFO         pNewBI = (PBITMAPINFO) GlobalLock( hNewDIB );
860                 PBITMAPINFOHEADER   pNewBIH = (PBITMAPINFOHEADER) pNewBI;
861                 const sal_uInt16        nColorCount = ImplGetDIBColorCount( hNewDIB );
862                 const sal_uLong         nOffset = *(DWORD*) pBI + nColorCount * sizeof( RGBQUAD );
863                 BYTE*               pOldBits = (PBYTE) pBI + nOffset;
864                 BYTE*               pNewBits = (PBYTE) pNewBI + nOffset;
865 
866                 memcpy( pNewBI, pBI, nOffset );
867                 pNewBIH->biCompression = 0;
868                 ImplDecodeRLEBuffer( pOldBits, pNewBits, aSizePix, pBIH->biCompression == BI_RLE4 );
869 
870                 GlobalUnlock( mhDIB );
871                 GlobalFree( mhDIB );
872                 mhDIB = hNewDIB;
873                 pBI = pNewBI;
874                 pBIH = pNewBIH;
875             }
876         }
877 
878         if( pBIH->biPlanes == 1 )
879         {
880             pBuffer = new BitmapBuffer;
881 
882             pBuffer->mnFormat = BMP_FORMAT_BOTTOM_UP |
883                                 ( pBIH->biBitCount == 1 ? BMP_FORMAT_1BIT_MSB_PAL :
884                                   pBIH->biBitCount == 4 ? BMP_FORMAT_4BIT_MSN_PAL :
885                                   pBIH->biBitCount == 8 ? BMP_FORMAT_8BIT_PAL :
886                                   pBIH->biBitCount == 16 ? BMP_FORMAT_16BIT_TC_LSB_MASK :
887                                   pBIH->biBitCount == 24 ? BMP_FORMAT_24BIT_TC_BGR :
888                                   pBIH->biBitCount == 32 ? BMP_FORMAT_32BIT_TC_MASK : 0UL );
889 
890             if( BMP_SCANLINE_FORMAT( pBuffer->mnFormat ) )
891             {
892                 pBuffer->mnWidth = maSize.Width();
893                 pBuffer->mnHeight = maSize.Height();
894                 pBuffer->mnScanlineSize = AlignedWidth4Bytes( maSize.Width() * pBIH->biBitCount );
895                 pBuffer->mnBitCount = (sal_uInt16) pBIH->biBitCount;
896 
897                 if( pBuffer->mnBitCount <= 8 )
898                 {
899                     const sal_uInt16 nPalCount = ImplGetDIBColorCount( mhDIB );
900 
901                     pBuffer->maPalette.SetEntryCount( nPalCount );
902                     memcpy( pBuffer->maPalette.ImplGetColorBuffer(), pBI->bmiColors, nPalCount * sizeof( RGBQUAD ) );
903                     pBuffer->mpBits = (PBYTE) pBI + *(DWORD*) pBI + nPalCount * sizeof( RGBQUAD );
904                 }
905                 else if( ( pBIH->biBitCount == 16 ) || ( pBIH->biBitCount == 32 ) )
906                 {
907                     sal_uLong nOffset = 0UL;
908 
909                     if( pBIH->biCompression == BI_BITFIELDS )
910                     {
911                         nOffset = 3 * sizeof( RGBQUAD );
912                         pBuffer->maColorMask = ColorMask( *(UINT32*) &pBI->bmiColors[ 0 ],
913                                                           *(UINT32*) &pBI->bmiColors[ 1 ],
914                                                           *(UINT32*) &pBI->bmiColors[ 2 ] );
915                     }
916                     else if( pBIH->biBitCount == 16 )
917                         pBuffer->maColorMask = ColorMask( 0x00007c00UL, 0x000003e0UL, 0x0000001fUL );
918                     else
919                         pBuffer->maColorMask = ColorMask( 0x00ff0000UL, 0x0000ff00UL, 0x000000ffUL );
920 
921                     pBuffer->mpBits = (PBYTE) pBI + *(DWORD*) pBI + nOffset;
922                 }
923                 else
924                     pBuffer->mpBits = (PBYTE) pBI + *(DWORD*) pBI;
925             }
926             else
927             {
928                 GlobalUnlock( mhDIB );
929                 delete pBuffer;
930                 pBuffer = NULL;
931             }
932         }
933         else
934             GlobalUnlock( mhDIB );
935     }
936 
937     return pBuffer;
938 }
939 
940 // ------------------------------------------------------------------
941 
942 void WinSalBitmap::ReleaseBuffer( BitmapBuffer* pBuffer, bool bReadOnly )
943 {
944     if( pBuffer )
945     {
946         if( mhDIB )
947         {
948             if( !bReadOnly && !!pBuffer->maPalette )
949             {
950                 PBITMAPINFO     pBI = (PBITMAPINFO) GlobalLock( mhDIB );
951                 const sal_uInt16    nCount = pBuffer->maPalette.GetEntryCount();
952                 const sal_uInt16    nDIBColorCount = ImplGetDIBColorCount( mhDIB );
953                 memcpy( pBI->bmiColors, pBuffer->maPalette.ImplGetColorBuffer(), Min( nDIBColorCount, nCount ) * sizeof( RGBQUAD ) );
954                 GlobalUnlock( mhDIB );
955             }
956 
957             GlobalUnlock( mhDIB );
958         }
959 
960         delete pBuffer;
961     }
962 }
963 
964 // ------------------------------------------------------------------
965 
966 void WinSalBitmap::ImplDecodeRLEBuffer( const BYTE* pSrcBuf, BYTE* pDstBuf,
967                                      const Size& rSizePixel, bool bRLE4 )
968 {
969     HPBYTE          pRLE = (HPBYTE) pSrcBuf;
970     HPBYTE          pDIB = (HPBYTE) pDstBuf;
971     HPBYTE          pRow = (HPBYTE) pDstBuf;
972     sal_uLong           nWidthAl = AlignedWidth4Bytes( rSizePixel.Width() * ( bRLE4 ? 4UL : 8UL ) );
973     HPBYTE          pLast = pDIB + rSizePixel.Height() * nWidthAl - 1;
974     sal_uLong           nCountByte;
975     sal_uLong           nRunByte;
976     sal_uLong           nX = 0;
977     sal_uLong           i;
978     BYTE            cTmp;
979     bool            bEndDecoding = FALSE;
980 
981     if( pRLE && pDIB )
982     {
983         do
984         {
985             if( ( nCountByte = *pRLE++ ) == 0 )
986             {
987                 nRunByte = *pRLE++;
988 
989                 if( nRunByte > 2UL )
990                 {
991                     if( bRLE4 )
992                     {
993                         nCountByte = nRunByte >> 1UL;
994 
995                         for( i = 0; i < nCountByte; i++ )
996                         {
997                             cTmp = *pRLE++;
998                             ImplSetPixel4( pDIB, nX++, cTmp >> 4 );
999                             ImplSetPixel4( pDIB, nX++, cTmp & 0x0f );
1000                         }
1001 
1002                         if( nRunByte & 1 )
1003                             ImplSetPixel4( pDIB, nX++, *pRLE++ >> 4 );
1004 
1005                         if( ( ( nRunByte + 1 ) >> 1 ) & 1 )
1006                             pRLE++;
1007                     }
1008                     else
1009                     {
1010                         memcpy( &pDIB[ nX ], pRLE, nRunByte );
1011                         pRLE += nRunByte;
1012                         nX += nRunByte;
1013 
1014                         if( nRunByte & 1 )
1015                             pRLE++;
1016                     }
1017                 }
1018                 else if( !nRunByte )
1019                 {
1020                     pDIB = ( pRow += nWidthAl );
1021                     nX = 0UL;
1022                 }
1023                 else if( nRunByte == 1 )
1024                     bEndDecoding = TRUE;
1025                 else
1026                 {
1027                     nX += *pRLE++;
1028                     pDIB = ( pRow += ( *pRLE++ ) * nWidthAl );
1029                 }
1030             }
1031             else
1032             {
1033                 cTmp = *pRLE++;
1034 
1035                 if( bRLE4 )
1036                 {
1037                     nRunByte = nCountByte >> 1;
1038 
1039                     for( i = 0; i < nRunByte; i++ )
1040                     {
1041                         ImplSetPixel4( pDIB, nX++, cTmp >> 4 );
1042                         ImplSetPixel4( pDIB, nX++, cTmp & 0x0f );
1043                     }
1044 
1045                     if( nCountByte & 1 )
1046                         ImplSetPixel4( pDIB, nX++, cTmp >> 4 );
1047                 }
1048                 else
1049                 {
1050                     for( i = 0; i < nCountByte; i++ )
1051                         pDIB[ nX++ ] = cTmp;
1052                 }
1053             }
1054         }
1055         while( !bEndDecoding && ( pDIB <= pLast ) );
1056     }
1057 }
1058 
1059 bool WinSalBitmap::GetSystemData( BitmapSystemData& rData )
1060 {
1061     bool bRet = false;
1062     if( mhDIB || mhDDB )
1063     {
1064         bRet = true;
1065         rData.pDIB = mhDIB;
1066         rData.pDDB = mhDDB;
1067         const Size& rSize = GetSize ();
1068         rData.mnWidth = rSize.Width();
1069         rData.mnHeight = rSize.Height();
1070     }
1071     return bRet;
1072 }
1073