xref: /trunk/main/vcl/source/gdi/bmpacc.cxx (revision f8c0d554)
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_vcl.hxx"
26 
27 #include <vcl/salbtype.hxx>
28 #include <vcl/bitmap.hxx>
29 #include <vcl/bmpacc.hxx>
30 
31 #include <impbmp.hxx>
32 
33 #include <string.h>
34 
35 // --------------------
36 // - BitmapReadAccess -
37 // --------------------
38 
39 BitmapReadAccess::BitmapReadAccess( Bitmap& rBitmap, sal_Bool bModify ) :
40 			mpBuffer		( NULL ),
41 			mpScanBuf		( NULL ),
42 			mFncGetPixel	( NULL ),
43 			mFncSetPixel	( NULL ),
44 			mbModify		( bModify )
45 {
46 	ImplCreate( rBitmap );
47 }
48 
49 // ------------------------------------------------------------------
50 
51 BitmapReadAccess::BitmapReadAccess( Bitmap& rBitmap ) :
52 			mpBuffer		( NULL ),
53 			mpScanBuf		( NULL ),
54 			mFncGetPixel	( NULL ),
55 			mFncSetPixel	( NULL ),
56 			mbModify		( sal_False )
57 {
58 	ImplCreate( rBitmap );
59 }
60 
61 // ------------------------------------------------------------------
62 
63 BitmapReadAccess::~BitmapReadAccess()
64 {
65 	ImplDestroy();
66 }
67 
68 // ------------------------------------------------------------------
69 
70 void BitmapReadAccess::ImplCreate( Bitmap& rBitmap )
71 {
72 	ImpBitmap* pImpBmp = rBitmap.ImplGetImpBitmap();
73 
74 	DBG_ASSERT( pImpBmp, "Forbidden Access to empty bitmap!" );
75 
76 	if( pImpBmp )
77 	{
78 		if( mbModify && !maBitmap.ImplGetImpBitmap() )
79 		{
80 			rBitmap.ImplMakeUnique();
81 			pImpBmp = rBitmap.ImplGetImpBitmap();
82 		}
83 		else
84 		{
85 			DBG_ASSERT( !mbModify || pImpBmp->ImplGetRefCount() == 2,
86 						"Unpredictable results: bitmap is referenced more than once!" );
87 		}
88 
89 		mpBuffer = pImpBmp->ImplAcquireBuffer( !mbModify );
90 
91 		if( !mpBuffer )
92 		{
93 			ImpBitmap* pNewImpBmp = new ImpBitmap;
94 
95 			if( pNewImpBmp->ImplCreate( *pImpBmp, rBitmap.GetBitCount()  ) )
96 			{
97 				pImpBmp = pNewImpBmp;
98 				rBitmap.ImplSetImpBitmap( pImpBmp );
99 				mpBuffer = pImpBmp->ImplAcquireBuffer( !mbModify );
100 			}
101 			else
102 				delete pNewImpBmp;
103 		}
104 
105 		if( mpBuffer )
106 		{
107 			const long	nHeight = mpBuffer->mnHeight;
108 			Scanline	pTmpLine = mpBuffer->mpBits;
109 
110 			mpScanBuf = new Scanline[ nHeight ];
111 			maColorMask = mpBuffer->maColorMask;
112 
113 			if( BMP_SCANLINE_ADJUSTMENT( mpBuffer->mnFormat ) == BMP_FORMAT_TOP_DOWN )
114 			{
115 				for( long nY = 0L; nY < nHeight; nY++, pTmpLine += mpBuffer->mnScanlineSize )
116 					mpScanBuf[ nY ] = pTmpLine;
117 			}
118 			else
119 			{
120 				for( long nY = nHeight - 1; nY >= 0; nY--, pTmpLine += mpBuffer->mnScanlineSize )
121 					mpScanBuf[ nY ] = pTmpLine;
122 			}
123 
124 			if( !ImplSetAccessPointers( BMP_SCANLINE_FORMAT( mpBuffer->mnFormat ) ) )
125 			{
126 				delete[] mpScanBuf;
127 				mpScanBuf = NULL;
128 
129 				pImpBmp->ImplReleaseBuffer( mpBuffer, !mbModify );
130 				mpBuffer = NULL;
131 			}
132 			else
133 				maBitmap = rBitmap;
134 		}
135 	}
136 }
137 
138 // ------------------------------------------------------------------
139 
140 void BitmapReadAccess::ImplDestroy()
141 {
142 	ImpBitmap* pImpBmp = maBitmap.ImplGetImpBitmap();
143 
144 	delete[] mpScanBuf;
145 	mpScanBuf = NULL;
146 
147 	if( mpBuffer && pImpBmp )
148 	{
149 		pImpBmp->ImplReleaseBuffer( mpBuffer, !mbModify );
150 		mpBuffer = NULL;
151 	}
152 }
153 
154 // ------------------------------------------------------------------
155 
156 sal_Bool BitmapReadAccess::ImplSetAccessPointers( sal_uLong nFormat )
157 {
158 	sal_Bool bRet = sal_True;
159 
160 	switch( nFormat )
161 	{
162 		CASE_FORMAT( _1BIT_MSB_PAL )
163 		CASE_FORMAT( _1BIT_LSB_PAL )
164 		CASE_FORMAT( _4BIT_MSN_PAL )
165 		CASE_FORMAT( _4BIT_LSN_PAL )
166 		CASE_FORMAT( _8BIT_PAL )
167 		CASE_FORMAT( _8BIT_TC_MASK )
168 		CASE_FORMAT( _16BIT_TC_MSB_MASK )
169 		CASE_FORMAT( _16BIT_TC_LSB_MASK )
170 		CASE_FORMAT( _24BIT_TC_BGR )
171 		CASE_FORMAT( _24BIT_TC_RGB )
172 		CASE_FORMAT( _24BIT_TC_MASK )
173 		CASE_FORMAT( _32BIT_TC_ABGR )
174 		CASE_FORMAT( _32BIT_TC_ARGB )
175 		CASE_FORMAT( _32BIT_TC_BGRA )
176 		CASE_FORMAT( _32BIT_TC_RGBA )
177 		CASE_FORMAT( _32BIT_TC_MASK )
178 
179 		default:
180 			bRet = sal_False;
181 		break;
182 	}
183 
184 	return bRet;
185 }
186 
187 // ------------------------------------------------------------------
188 
189 void BitmapReadAccess::ImplZeroInitUnusedBits()
190 {
191 	const sal_uInt32 nWidth = Width(), nHeight = Height(), nScanSize = GetScanlineSize();
192 
193 	if( nWidth && nHeight && nScanSize && GetBuffer() )
194 	{
195 		sal_uInt32 nBits;
196         bool       bMsb;
197 
198         const sal_uLong nScanlineFormat = GetScanlineFormat();
199 		switch( nScanlineFormat )
200 		{
201             case( BMP_FORMAT_1BIT_MSB_PAL ):
202 				nBits = 1;
203 				bMsb = true;
204                 break;
205 
206             case( BMP_FORMAT_1BIT_LSB_PAL ):
207 				nBits = 1;
208 				bMsb = false;
209                 break;
210 
211 			case( BMP_FORMAT_4BIT_MSN_PAL ):
212 				nBits = 4;
213 				bMsb = true;
214                 break;
215 
216             case( BMP_FORMAT_4BIT_LSN_PAL ):
217 				nBits = 4;
218 				bMsb = false;
219                 break;
220 
221 			case( BMP_FORMAT_8BIT_PAL ):
222 			case( BMP_FORMAT_8BIT_TC_MASK ):
223 				bMsb = true;
224 				nBits = 8;
225 			break;
226 
227 			case( BMP_FORMAT_16BIT_TC_MSB_MASK ):
228 			case( BMP_FORMAT_16BIT_TC_LSB_MASK ):
229 				bMsb = true;
230 				nBits = 16;
231 			break;
232 
233 			case( BMP_FORMAT_24BIT_TC_BGR ):
234 			case( BMP_FORMAT_24BIT_TC_RGB ):
235 			case( BMP_FORMAT_24BIT_TC_MASK ):
236 				bMsb = true;
237 				nBits = 24;
238 			break;
239 
240 			case( BMP_FORMAT_32BIT_TC_ABGR ):
241 			case( BMP_FORMAT_32BIT_TC_ARGB ):
242 			case( BMP_FORMAT_32BIT_TC_BGRA ):
243 			case( BMP_FORMAT_32BIT_TC_RGBA ):
244 			case( BMP_FORMAT_32BIT_TC_MASK ):
245 				bMsb = true;
246 				nBits = 32;
247 			break;
248 
249 			default:
250 			{
251 				DBG_ERROR( "BitmapWriteAccess::ZeroInitUnusedBits: Unsupported pixel format");
252 				nBits = 0;
253 				bMsb = true;
254 			}
255 			break;
256 		}
257 
258         nBits *= nWidth;
259 		if( nScanSize % 4 || !bMsb )
260         {
261             DBG_ASSERT( 8*nScanSize >= nBits,
262                         "BitmapWriteAccess::ZeroInitUnusedBits: span size smaller than width?!");
263 			const sal_uInt32 nLeftOverBits = 8*sizeof(sal_uInt8)*nScanSize - nBits;
264             if( nLeftOverBits != 0 ) // else there is really nothing to do
265             {
266                 const sal_uInt32 nBytes = (nLeftOverBits + 7U) >> 3U;
267                 sal_uInt8	     nMask;
268 
269                 if( bMsb )
270                     nMask = static_cast<sal_uInt8>(0xffU << (nLeftOverBits & 3UL));
271                 else
272                     nMask = static_cast<sal_uInt8>(0xffU >> (nLeftOverBits & 3UL));
273 
274                 sal_uInt8* pLastBytes = (sal_uInt8*)GetBuffer() + ( nScanSize - nBytes );
275                 for( sal_uInt32 i = 0; i < nHeight; i++, pLastBytes += nScanSize )
276                 {
277                     *pLastBytes &= nMask;
278                     for( sal_uInt32 j = 1; j < nBytes; j++ )
279                         pLastBytes[j] = 0;
280                 }
281             }
282         }
283         else if( nBits & 0x1f )
284 		{
285 			sal_uInt32	nMask = 0xffffffff << ( ( nScanSize << 3 ) - nBits );
286 			sal_uInt8*		pLast4Bytes = (sal_uInt8*) GetBuffer() + ( nScanSize - 4 );
287 
288 #ifdef OSL_LITENDIAN
289 			nMask = SWAPLONG( nMask );
290 #endif
291 			for( sal_uInt32 i = 0; i < nHeight; i++, pLast4Bytes += nScanSize )
292 				( *(sal_uInt32*) pLast4Bytes ) &= nMask;
293 		}
294 	}
295 }
296 
297 // ------------------------------------------------------------------
298 
299 void BitmapReadAccess::Flush()
300 {
301 	ImplDestroy();
302 }
303 
304 // ------------------------------------------------------------------
305 
306 void BitmapReadAccess::ReAccess( sal_Bool bModify )
307 {
308 	const ImpBitmap* pImpBmp = maBitmap.ImplGetImpBitmap();
309 
310 	DBG_ASSERT( !mpBuffer, "No ReAccess possible while bitmap is being accessed!" );
311 	DBG_ASSERT( pImpBmp && ( pImpBmp->ImplGetRefCount() > 1UL ), "Accessed bitmap does not exist anymore!" );
312 
313 	if( !mpBuffer && pImpBmp && ( pImpBmp->ImplGetRefCount() > 1UL ) )
314 	{
315 		mbModify = bModify;
316 		ImplCreate( maBitmap );
317 	}
318 }
319 
320 // ------------------------------------------------------------------
321 
322 sal_uInt16 BitmapReadAccess::GetBestPaletteIndex( const BitmapColor& rBitmapColor ) const
323 {
324 	return( HasPalette() ? mpBuffer->maPalette.GetBestIndex( rBitmapColor ) : 0 );
325 }
326 
327 BitmapColor BitmapReadAccess::GetInterpolatedColorWithFallback( double fY, double fX, const BitmapColor& rFallback ) const
328 {
329     // ask directly doubles >= 0.0 here to avoid rounded values of 0 at small negative
330     // double values, e.g. static_cast< sal_Int32 >(-0.25) is 0, not -1, but *has* to be outside (!)
331     if(mpBuffer && fX >= 0.0 && fY >= 0.0)
332     {
333         const sal_Int32 nX(static_cast< sal_Int32 >(fX));
334         const sal_Int32 nY(static_cast< sal_Int32 >(fY));
335 
336         if(nX < mpBuffer->mnWidth && nY < mpBuffer->mnHeight)
337         {
338             // get base-return value from inside pixel
339             BitmapColor aRetval(GetColor(nY, nX));
340 
341             // calculate deltas and indices for neighbour accesses
342             sal_Int16 nDeltaX((fX - (nX + 0.5)) * 255.0); // [-255 .. 255]
343             sal_Int16 nDeltaY((fY - (nY + 0.5)) * 255.0); // [-255 .. 255]
344             sal_Int16 nIndX(0);
345             sal_Int16 nIndY(0);
346 
347             if(nDeltaX > 0)
348             {
349                 nIndX = nX + 1;
350             }
351             else
352             {
353                 nIndX = nX - 1;
354                 nDeltaX = -nDeltaX;
355             }
356 
357             if(nDeltaY > 0)
358             {
359                 nIndY = nY + 1;
360             }
361             else
362             {
363                 nIndY = nY - 1;
364                 nDeltaY = -nDeltaY;
365             }
366 
367             // get right/left neighbour
368             BitmapColor aXCol(rFallback);
369 
370             if(nDeltaX && nIndX >= 0 && nIndX < mpBuffer->mnWidth)
371             {
372                 aXCol = GetColor(nY, nIndX);
373             }
374 
375             // get top/bottom neighbour
376             BitmapColor aYCol(rFallback);
377 
378             if(nDeltaY && nIndY >= 0 && nIndY < mpBuffer->mnHeight)
379             {
380                 aYCol = GetColor(nIndY, nX);
381             }
382 
383             // get one of four edge neighbours
384             BitmapColor aXYCol(rFallback);
385 
386             if(nDeltaX && nDeltaY && nIndX >=0 && nIndY >= 0 && nIndX < mpBuffer->mnWidth && nIndY < mpBuffer->mnHeight)
387             {
388                 aXYCol = GetColor(nIndY, nIndX);
389             }
390 
391             // merge return value with right/left neighbour
392             if(aXCol != aRetval)
393             {
394                 aRetval.Merge(aXCol, 255 - nDeltaX);
395             }
396 
397             // merge top/bottom neighbour with edge
398             if(aYCol != aXYCol)
399             {
400                 aYCol.Merge(aXYCol, 255 - nDeltaX);
401             }
402 
403             // merge return value with already merged top/bottom neighbour
404             if(aRetval != aYCol)
405             {
406                 aRetval.Merge(aYCol, 255 - nDeltaY);
407             }
408 
409             return aRetval;
410         }
411     }
412 
413     return rFallback;
414 }
415 
416 BitmapColor BitmapReadAccess::GetColorWithFallback( double fY, double fX, const BitmapColor& rFallback ) const
417 {
418     // ask directly doubles >= 0.0 here to avoid rounded values of 0 at small negative
419     // double values, e.g. static_cast< sal_Int32 >(-0.25) is 0, not -1, but *has* to be outside (!)
420     if(mpBuffer && fX >= 0.0 && fY >= 0.0)
421     {
422         const sal_Int32 nX(static_cast< sal_Int32 >(fX));
423         const sal_Int32 nY(static_cast< sal_Int32 >(fY));
424 
425         if(nX < mpBuffer->mnWidth && nY < mpBuffer->mnHeight)
426         {
427             return GetColor(nY, nX);
428         }
429     }
430 
431     return rFallback;
432 }
433 
434 BitmapColor BitmapReadAccess::GetColorWithFallback( long nY, long nX, const BitmapColor& rFallback ) const
435 {
436     if(mpBuffer)
437     {
438         if(nX >= 0 && nY >= 0 && nX < mpBuffer->mnWidth && nY < mpBuffer->mnHeight)
439         {
440             return GetColor(nY, nX);
441         }
442     }
443 
444     return rFallback;
445 }
446 
447 // ---------------------
448 // - BitmapWriteAccess -
449 // ---------------------
450 
451 BitmapWriteAccess::BitmapWriteAccess( Bitmap& rBitmap ) :
452             BitmapReadAccess( rBitmap, sal_True ),
453             mpLineColor     ( NULL ),
454             mpFillColor     ( NULL )
455 {
456 }
457 
458 // ------------------------------------------------------------------
459 
460 BitmapWriteAccess::~BitmapWriteAccess()
461 {
462 	delete mpLineColor;
463 	delete mpFillColor;
464 }
465 
466 // ------------------------------------------------------------------
467 
468 void BitmapWriteAccess::CopyScanline( long nY, const BitmapReadAccess& rReadAcc )
469 {
470 	DBG_ASSERT( ( nY >= 0 ) && ( nY < mpBuffer->mnHeight ), "y-coordinate in destination out of range!" );
471 	DBG_ASSERT( nY < rReadAcc.Height(), "y-coordinate in source out of range!" );
472 	DBG_ASSERT( ( HasPalette() && rReadAcc.HasPalette() ) || ( !HasPalette() && !rReadAcc.HasPalette() ), "No copying possible between palette bitmap and TC bitmap!" );
473 
474 	if( ( GetScanlineFormat() == rReadAcc.GetScanlineFormat() ) &&
475 		( GetScanlineSize() >= rReadAcc.GetScanlineSize() ) )
476 	{
477 		memcpy( mpScanBuf[ nY ], rReadAcc.GetScanline( nY ), rReadAcc.GetScanlineSize() );
478 	}
479 	else
480         // TODO: use fastbmp infrastructure
481 		for( long nX = 0L, nWidth = Min( mpBuffer->mnWidth, rReadAcc.Width() ); nX < nWidth; nX++ )
482 			SetPixel( nY, nX, rReadAcc.GetPixel( nY, nX ) );
483 }
484 
485 // ------------------------------------------------------------------
486 
487 void BitmapWriteAccess::CopyScanline( long nY, ConstScanline aSrcScanline,
488 									  sal_uLong nSrcScanlineFormat, sal_uLong nSrcScanlineSize )
489 {
490 	const sal_uLong nFormat = BMP_SCANLINE_FORMAT( nSrcScanlineFormat );
491 
492 	DBG_ASSERT( ( nY >= 0 ) && ( nY < mpBuffer->mnHeight ), "y-coordinate in destination out of range!" );
493 	DBG_ASSERT( ( HasPalette() && nFormat <= BMP_FORMAT_8BIT_PAL ) ||
494 				( !HasPalette() && nFormat > BMP_FORMAT_8BIT_PAL ),
495 				"No copying possible between palette and non palette scanlines!" );
496 
497 	const sal_uLong nCount = Min( GetScanlineSize(), nSrcScanlineSize );
498 
499 	if( nCount )
500 	{
501 		if( GetScanlineFormat() == BMP_SCANLINE_FORMAT( nSrcScanlineFormat ) )
502 			memcpy( mpScanBuf[ nY ], aSrcScanline, nCount );
503 		else
504 		{
505 			DBG_ASSERT( nFormat != BMP_FORMAT_8BIT_TC_MASK &&
506                         nFormat != BMP_FORMAT_16BIT_TC_MSB_MASK && nFormat != BMP_FORMAT_16BIT_TC_LSB_MASK &&
507 						nFormat != BMP_FORMAT_24BIT_TC_MASK && nFormat != BMP_FORMAT_32BIT_TC_MASK,
508 						"No support for pixel formats with color masks yet!" );
509 
510             // TODO: use fastbmp infrastructure
511             FncGetPixel pFncGetPixel;
512 
513 			switch( nFormat )
514 			{
515 				case( BMP_FORMAT_1BIT_MSB_PAL ):	pFncGetPixel = GetPixelFor_1BIT_MSB_PAL; break;
516 				case( BMP_FORMAT_1BIT_LSB_PAL ):	pFncGetPixel = GetPixelFor_1BIT_LSB_PAL; break;
517 				case( BMP_FORMAT_4BIT_MSN_PAL ):	pFncGetPixel = GetPixelFor_4BIT_MSN_PAL; break;
518 				case( BMP_FORMAT_4BIT_LSN_PAL ):	pFncGetPixel = GetPixelFor_4BIT_LSN_PAL; break;
519 				case( BMP_FORMAT_8BIT_PAL ):		pFncGetPixel = GetPixelFor_8BIT_PAL; break;
520 				case( BMP_FORMAT_8BIT_TC_MASK ):	pFncGetPixel = GetPixelFor_8BIT_TC_MASK; break;
521 				case( BMP_FORMAT_16BIT_TC_MSB_MASK ):	pFncGetPixel = GetPixelFor_16BIT_TC_MSB_MASK; break;
522 				case( BMP_FORMAT_16BIT_TC_LSB_MASK ):	pFncGetPixel = GetPixelFor_16BIT_TC_LSB_MASK; break;
523 				case( BMP_FORMAT_24BIT_TC_BGR ):	pFncGetPixel = GetPixelFor_24BIT_TC_BGR; break;
524 				case( BMP_FORMAT_24BIT_TC_RGB ):	pFncGetPixel = GetPixelFor_24BIT_TC_RGB; break;
525 				case( BMP_FORMAT_24BIT_TC_MASK ):	pFncGetPixel = GetPixelFor_24BIT_TC_MASK; break;
526 				case( BMP_FORMAT_32BIT_TC_ABGR ):	pFncGetPixel = GetPixelFor_32BIT_TC_ABGR; break;
527 				case( BMP_FORMAT_32BIT_TC_ARGB ):	pFncGetPixel = GetPixelFor_32BIT_TC_ARGB; break;
528 				case( BMP_FORMAT_32BIT_TC_BGRA ):	pFncGetPixel = GetPixelFor_32BIT_TC_BGRA; break;
529 				case( BMP_FORMAT_32BIT_TC_RGBA ):	pFncGetPixel = GetPixelFor_32BIT_TC_RGBA; break;
530 				case( BMP_FORMAT_32BIT_TC_MASK ):	pFncGetPixel = GetPixelFor_32BIT_TC_MASK; break;
531 
532 				default:
533 					pFncGetPixel = NULL;
534 				break;
535 			}
536 
537 			if( pFncGetPixel )
538 			{
539 				const ColorMask aDummyMask;
540 
541 				for( long nX = 0L, nWidth = mpBuffer->mnWidth; nX < nWidth; nX++ )
542 					SetPixel( nY, nX, pFncGetPixel( aSrcScanline, nX, aDummyMask ) );
543 			}
544 		}
545 	}
546 }
547 
548 
549 // ------------------------------------------------------------------
550 
551 void BitmapWriteAccess::CopyBuffer( const BitmapReadAccess& rReadAcc )
552 {
553 	DBG_ASSERT( ( HasPalette() && rReadAcc.HasPalette() ) || ( !HasPalette() && !rReadAcc.HasPalette() ), "No copying possible between palette bitmap and TC bitmap!" );
554 
555 	if( ( GetScanlineFormat() == rReadAcc.GetScanlineFormat() ) &&
556 		( GetScanlineSize() == rReadAcc.GetScanlineSize() ) )
557 	{
558 		const long	nHeight = Min( mpBuffer->mnHeight, rReadAcc.Height() );
559 		const sal_uLong nCount = nHeight * mpBuffer->mnScanlineSize;
560 
561 		memcpy( mpBuffer->mpBits, rReadAcc.GetBuffer(), nCount );
562 	}
563 	else
564 		for( long nY = 0L, nHeight = Min( mpBuffer->mnHeight, rReadAcc.Height() ); nY < nHeight; nY++ )
565 			CopyScanline( nY, rReadAcc );
566 }
567