added alpha channel support to wxDFB's wxBitmap
[wxWidgets.git] / src / dfb / bitmap.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/dfb/bitmap.cpp
3 // Purpose: wxBitmap implementation
4 // Author: Vaclav Slavik
5 // Created: 2006-08-04
6 // RCS-ID: $Id$
7 // Copyright: (c) 2006 REA Elektronik GmbH
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13
14 #ifdef __BORLANDC__
15 #pragma hdrstop
16 #endif
17
18 #ifndef WX_PRECOMP
19 #include "wx/app.h"
20 #include "wx/log.h"
21 #endif
22
23 #include "wx/bitmap.h"
24 #include "wx/colour.h"
25 #include "wx/image.h"
26
27 #include "wx/dfb/private.h"
28
29 //-----------------------------------------------------------------------------
30 // helpers
31 //-----------------------------------------------------------------------------
32
33 // NB: Most of this conversion code is needed because of differences between
34 // wxImage and wxDFB's wxBitmap representations:
35 // (1) wxImage uses RGB order, while DirectFB uses BGR
36 // (2) wxImage has alpha channel in a separate plane, while DirectFB puts
37 // all components into single BGRA plane
38
39 // pitch = stride = # of bytes between the start of N-th line and (N+1)-th line
40 // {Src,Dst}PixSize = # of bytes used to represent one pixel
41 template<int SrcPixSize, int DstPixSize>
42 static void CopyPixelsAndSwapRGB(unsigned w, unsigned h,
43 const unsigned char *src,
44 unsigned src_pitch,
45 unsigned char *dst,
46 unsigned dst_pitch)
47 {
48 unsigned src_advance = src_pitch - SrcPixSize * w;
49 unsigned dst_advance = dst_pitch - DstPixSize * w;
50 for ( unsigned y = 0; y < h; y++, src += src_advance, dst += dst_advance )
51 {
52 for ( unsigned x = 0; x < w; x++, src += SrcPixSize, dst += DstPixSize )
53 {
54 // copy with RGB -> BGR translation:
55 dst[0] = src[2];
56 dst[1] = src[1];
57 dst[2] = src[0];
58 }
59 }
60 }
61
62 static void CopySurfaceToImage(const wxIDirectFBSurfacePtr& surface,
63 wxImage& image)
64 {
65 wxIDirectFBSurface::Locked locked(surface, DSLF_READ);
66 wxCHECK_RET( locked.ptr, _T("failed to lock surface") );
67
68 const unsigned width = image.GetWidth();
69 const unsigned height = image.GetHeight();
70 const DFBSurfacePixelFormat format = surface->GetPixelFormat();
71
72 // copy RGB data from the surface:
73 switch ( format )
74 {
75 case DSPF_RGB24:
76 CopyPixelsAndSwapRGB<3,3>
77 (
78 width, height,
79 (unsigned char*)locked.ptr, locked.pitch,
80 image.GetData(), width * 3
81 );
82 break;
83
84 case DSPF_RGB32:
85 case DSPF_ARGB:
86 CopyPixelsAndSwapRGB<4,3>
87 (
88 width, height,
89 (unsigned char*)locked.ptr, locked.pitch,
90 image.GetData(), width * 3
91 );
92 break;
93
94 default:
95 wxFAIL_MSG( "unexpected pixel format" );
96 return;
97 }
98
99 // extract alpha channel if the bitmap has it:
100 if ( format == DSPF_ARGB )
101 {
102 // create alpha plane:
103 image.SetAlpha();
104
105 // and copy alpha data to it:
106 const unsigned advance = locked.pitch - 4 * width;
107 unsigned char *alpha = image.GetAlpha();
108 // NB: "+3" is to get pointer to alpha component
109 const unsigned char *src = ((unsigned char*)locked.ptr) + 3;
110
111 for ( unsigned y = 0; y < height; y++, src += advance )
112 for ( unsigned x = 0; x < width; x++, src += 4 )
113 *(alpha++) = *src;
114 }
115 }
116
117 static void CopyImageToSurface(const wxImage& image,
118 const wxIDirectFBSurfacePtr& surface)
119 {
120 wxIDirectFBSurface::Locked locked(surface, DSLF_WRITE);
121 wxCHECK_RET( locked.ptr, "failed to lock surface" );
122
123 const unsigned width = image.GetWidth();
124 const unsigned height = image.GetHeight();
125 const DFBSurfacePixelFormat format = surface->GetPixelFormat();
126
127 // copy RGB data to the surface:
128 switch ( format )
129 {
130 case DSPF_RGB24:
131 CopyPixelsAndSwapRGB<3,3>
132 (
133 width, height,
134 image.GetData(), width * 3,
135 (unsigned char*)locked.ptr, locked.pitch
136 );
137 break;
138
139 case DSPF_RGB32:
140 case DSPF_ARGB:
141 CopyPixelsAndSwapRGB<3,4>
142 (
143 width, height,
144 image.GetData(), width * 3,
145 (unsigned char*)locked.ptr, locked.pitch
146 );
147 break;
148
149 default:
150 wxFAIL_MSG( "unexpected pixel format" );
151 return;
152 }
153
154 // if the image has alpha channel, merge it in:
155 if ( format == DSPF_ARGB )
156 {
157 wxCHECK_RET( image.HasAlpha(), "logic error - ARGB, but no alpha" );
158
159 const unsigned advance = locked.pitch - 4 * width;
160 const unsigned char *alpha = image.GetAlpha();
161 // NB: "+3" is to get pointer to alpha component
162 unsigned char *dest = ((unsigned char*)locked.ptr) + 3;
163
164 for ( unsigned y = 0; y < height; y++, dest += advance )
165 for ( unsigned x = 0; x < width; x++, dest += 4 )
166 *dest = *(alpha++);
167 }
168 }
169
170 // Creates a surface that will use wxImage's pixel data (RGB only)
171 static wxIDirectFBSurfacePtr CreateSurfaceForImage(const wxImage& image)
172 {
173 wxCHECK_MSG( image.Ok(), NULL, _T("invalid image") );
174 // FIXME_DFB: implement alpha handling by merging alpha buffer with RGB
175 // into a temporary RGBA surface
176 wxCHECK_MSG( !image.HasAlpha(), NULL, _T("alpha channel not supported") );
177
178 // NB: wxImage uses RGB order of bytes while DirectFB uses BGR, so we
179 // cannot use preallocated surface that shares data with wxImage, we
180 // have to copy the data to temporary surface instead
181 DFBSurfaceDescription desc;
182 desc.flags = (DFBSurfaceDescriptionFlags)
183 (DSDESC_CAPS | DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT);
184 desc.caps = DSCAPS_NONE;
185 desc.width = image.GetWidth();
186 desc.height = image.GetHeight();
187 desc.pixelformat = DSPF_RGB24;
188
189 return wxIDirectFB::Get()->CreateSurface(&desc);
190 }
191
192 //-----------------------------------------------------------------------------
193 // wxBitmapRefData
194 //-----------------------------------------------------------------------------
195
196 class wxBitmapRefData: public wxObjectRefData
197 {
198 public:
199 wxBitmapRefData()
200 {
201 m_mask = NULL;
202 #if wxUSE_PALETTE
203 m_palette = NULL;
204 #endif
205 }
206
207 wxBitmapRefData(const wxBitmapRefData& data)
208 {
209 m_surface = data.m_surface ? data.m_surface->Clone() : NULL;
210
211 m_mask = data.m_mask ? new wxMask(*data.m_mask) : NULL;
212 #if wxUSE_PALETTE
213 m_palette = data.m_palette ? new wxPalette(*data.m_palette) : NULL;
214 #endif
215 }
216
217 virtual ~wxBitmapRefData()
218 {
219 delete m_mask;
220 #if wxUSE_PALETTE
221 delete m_palette;
222 #endif
223 }
224
225 wxIDirectFBSurfacePtr m_surface;
226 wxMask *m_mask;
227 #if wxUSE_PALETTE
228 wxPalette *m_palette;
229 #endif
230 };
231
232 #define M_BITMAP ((wxBitmapRefData *)m_refData)
233
234 //-----------------------------------------------------------------------------
235 // wxBitmap
236 //-----------------------------------------------------------------------------
237
238 IMPLEMENT_ABSTRACT_CLASS(wxBitmapHandler, wxBitmapHandlerBase)
239 IMPLEMENT_DYNAMIC_CLASS(wxBitmap, wxBitmapBase)
240
241 wxBitmap::wxBitmap(int width, int height, int depth)
242 {
243 Create(width, height, depth);
244 }
245
246 bool wxBitmap::Create(const wxIDirectFBSurfacePtr& surface)
247 {
248 UnRef();
249
250 wxCHECK_MSG( surface, false, _T("invalid surface") );
251
252 m_refData = new wxBitmapRefData();
253 M_BITMAP->m_surface = surface;
254 return true;
255 }
256
257 bool wxBitmap::Create(int width, int height, int depth)
258 {
259 wxCHECK_MSG( depth == -1, false, wxT("only default depth supported now") );
260
261 return CreateWithFormat(width, height, -1);
262 }
263
264 bool wxBitmap::CreateWithFormat(int width, int height, int dfbFormat)
265 {
266 UnRef();
267
268 wxCHECK_MSG( width > 0 && height > 0, false, wxT("invalid bitmap size") );
269
270 DFBSurfaceDescription desc;
271 desc.flags = (DFBSurfaceDescriptionFlags)(
272 DSDESC_CAPS | DSDESC_WIDTH | DSDESC_HEIGHT);
273 desc.caps = DSCAPS_NONE;
274 desc.width = width;
275 desc.height = height;
276
277 if ( dfbFormat != -1 )
278 {
279 desc.flags = (DFBSurfaceDescriptionFlags)(
280 desc.flags | DSDESC_PIXELFORMAT);
281 desc.pixelformat = (DFBSurfacePixelFormat)dfbFormat;
282 }
283
284 return Create(wxIDirectFB::Get()->CreateSurface(&desc));
285 }
286
287 #if wxUSE_IMAGE
288 wxBitmap::wxBitmap(const wxImage& image, int depth)
289 {
290 wxCHECK_RET( image.Ok(), wxT("invalid image") );
291 wxCHECK_RET( depth == -1, wxT("only default depth supported now") );
292
293 // create surface in screen's format (unless we need alpha channel,
294 // in which case use ARGB):
295 if ( !CreateWithFormat(image.GetWidth(), image.GetHeight(),
296 image.HasAlpha() ? DSPF_ARGB : -1) )
297 return;
298
299 // then copy the image to it:
300 wxIDirectFBSurfacePtr dst = M_BITMAP->m_surface;
301
302 switch ( dst->GetPixelFormat() )
303 {
304 case DSPF_RGB24:
305 case DSPF_RGB32:
306 case DSPF_ARGB:
307 CopyImageToSurface(image, dst);
308 break;
309
310 default:
311 {
312 // wxBitmap uses different pixel format, so we have to use a
313 // temporary surface and blit to the bitmap via it:
314 wxIDirectFBSurfacePtr src(CreateSurfaceForImage(image));
315 CopyImageToSurface(image, src);
316
317 if ( !dst->SetBlittingFlags(DSBLIT_NOFX) )
318 return;
319 if ( !dst->Blit(src->GetRaw(), NULL, 0, 0) )
320 return;
321 }
322 }
323
324 // FIXME: implement mask creation from image's mask (or alpha channel?)
325 wxASSERT_MSG( !image.HasMask(), _T("image masks are ignored for now") );
326 }
327
328 wxImage wxBitmap::ConvertToImage() const
329 {
330 wxCHECK_MSG( Ok(), wxNullImage, wxT("invalid bitmap") );
331
332 wxImage img(GetWidth(), GetHeight());
333 wxIDirectFBSurfacePtr src = M_BITMAP->m_surface;
334
335 switch ( src->GetPixelFormat() )
336 {
337 case DSPF_RGB24:
338 case DSPF_RGB32:
339 case DSPF_ARGB:
340 CopySurfaceToImage(src, img);
341 break;
342 default:
343 {
344 // wxBitmap uses different pixel format, so we have to use a
345 // temporary surface and blit to the bitmap via it:
346 wxIDirectFBSurfacePtr dst(CreateSurfaceForImage(img));
347
348 if ( !dst->SetBlittingFlags(DSBLIT_NOFX) )
349 return wxNullImage;
350 if ( !dst->Blit(src->GetRaw(), NULL, 0, 0) )
351 return wxNullImage;
352
353 CopySurfaceToImage(dst, img);
354 }
355 }
356
357 // FIXME: implement mask setting in the image
358 wxASSERT_MSG( GetMask() == NULL, _T("bitmap masks are ignored for now") );
359
360 return img;
361 }
362 #endif // wxUSE_IMAGE
363
364 wxBitmap::wxBitmap(const wxString &filename, wxBitmapType type)
365 {
366 LoadFile(filename, type);
367 }
368
369 wxBitmap::wxBitmap(const char bits[], int width, int height, int depth)
370 {
371 wxCHECK_RET( depth == 1, wxT("can only create mono bitmap from XBM data") );
372
373 wxFAIL_MSG( _T("not implemented") );
374 }
375
376 bool wxBitmap::IsOk() const
377 {
378 return (m_refData != NULL && M_BITMAP->m_surface);
379 }
380
381 int wxBitmap::GetHeight() const
382 {
383 wxCHECK_MSG( Ok(), -1, wxT("invalid bitmap") );
384
385 int h = -1;
386 M_BITMAP->m_surface->GetSize(NULL, &h);
387 return h;
388 }
389
390 int wxBitmap::GetWidth() const
391 {
392 wxCHECK_MSG( Ok(), -1, wxT("invalid bitmap") );
393
394 int w = -1;
395 M_BITMAP->m_surface->GetSize(&w, NULL);
396 return w;
397 }
398
399 int wxBitmap::GetDepth() const
400 {
401 wxCHECK_MSG( Ok(), -1, wxT("invalid bitmap") );
402
403 return M_BITMAP->m_surface->GetDepth();
404 }
405
406 wxMask *wxBitmap::GetMask() const
407 {
408 wxCHECK_MSG( Ok(), NULL, wxT("invalid bitmap") );
409
410 return M_BITMAP->m_mask;
411 }
412
413 void wxBitmap::SetMask(wxMask *mask)
414 {
415 wxCHECK_RET( Ok(), wxT("invalid bitmap") );
416
417 AllocExclusive();
418 delete M_BITMAP->m_mask;
419 M_BITMAP->m_mask = mask;
420 }
421
422 bool wxBitmap::CopyFromIcon(const wxIcon& icon)
423 {
424 *this = *((wxBitmap*)(&icon));
425 return true;
426 }
427
428 wxBitmap wxBitmap::GetSubBitmap(const wxRect& rect) const
429 {
430 wxCHECK_MSG( Ok() &&
431 rect.x >= 0 && rect.y >= 0 &&
432 rect.x+rect.width <= GetWidth() &&
433 rect.y+rect.height <= GetHeight(),
434 wxNullBitmap,
435 wxT("invalid bitmap or bitmap region") );
436
437 // NB: DirectFB subsurfaces share the same pixels buffer, so we must
438 // clone the obtained subsurface
439 DFBRectangle r = { rect.x, rect.y, rect.width, rect.height };
440 return wxBitmap(M_BITMAP->m_surface->GetSubSurface(&r)->Clone());
441 }
442
443 #warning "to common code"
444 bool wxBitmap::LoadFile(const wxString &name, wxBitmapType type)
445 {
446 UnRef();
447
448 wxBitmapHandler *handler = FindHandler(type);
449
450 if ( handler == NULL )
451 {
452 wxImage image;
453 if ( !image.LoadFile(name, type) || !image.Ok() )
454 {
455 wxLogError(_("No bitmap handler for type %d defined."), type);
456 return false;
457 }
458 else
459 {
460 *this = wxBitmap(image);
461 return true;
462 }
463 }
464
465 m_refData = new wxBitmapRefData();
466
467 return handler->LoadFile(this, name, type, -1, -1);
468 }
469
470 #warning "to common code"
471 bool wxBitmap::SaveFile(const wxString& filename, wxBitmapType type, const wxPalette *palette) const
472 {
473 wxCHECK_MSG( Ok(), false, wxT("invalid bitmap") );
474
475 wxBitmapHandler *handler = FindHandler(type);
476
477 if ( handler == NULL )
478 {
479 wxImage image = ConvertToImage();
480 #if wxUSE_PALETTE
481 if ( palette )
482 image.SetPalette(*palette);
483 #endif // wxUSE_PALETTE
484
485 if ( image.Ok() )
486 return image.SaveFile(filename, type);
487 else
488 {
489 wxLogError(_("No bitmap handler for type %d defined."), type);
490 return false;
491 }
492 }
493
494 return handler->SaveFile(this, filename, type, palette);
495 }
496
497 #if wxUSE_PALETTE
498 wxPalette *wxBitmap::GetPalette() const
499 {
500 wxCHECK_MSG( Ok(), NULL, wxT("invalid bitmap") );
501
502 return M_BITMAP->m_palette;
503 }
504
505 void wxBitmap::SetPalette(const wxPalette& palette)
506 {
507 wxCHECK_RET( Ok(), wxT("invalid bitmap") );
508 wxCHECK_RET( GetDepth() > 1 && GetDepth() <= 8, wxT("cannot set palette for bitmap of this depth") );
509
510 AllocExclusive();
511 delete M_BITMAP->m_palette;
512 M_BITMAP->m_palette = NULL;
513
514 if ( !palette.Ok() ) return;
515
516 M_BITMAP->m_palette = new wxPalette(palette);
517 }
518 #endif // wxUSE_PALETTE
519
520 void wxBitmap::SetHeight(int height)
521 {
522 AllocExclusive();
523
524 wxFAIL_MSG( _T("SetHeight not implemented") );
525 }
526
527 void wxBitmap::SetWidth(int width)
528 {
529 AllocExclusive();
530
531 wxFAIL_MSG( _T("SetWidth not implemented") );
532 }
533
534 void wxBitmap::SetDepth(int depth)
535 {
536 AllocExclusive();
537
538 wxFAIL_MSG( _T("SetDepth not implemented") );
539 }
540
541 wxIDirectFBSurfacePtr wxBitmap::GetDirectFBSurface() const
542 {
543 wxCHECK_MSG( Ok(), NULL, wxT("invalid bitmap") );
544
545 return M_BITMAP->m_surface;
546 }
547
548 wxObjectRefData *wxBitmap::CreateRefData() const
549 {
550 return new wxBitmapRefData;
551 }
552
553 wxObjectRefData *wxBitmap::CloneRefData(const wxObjectRefData *data) const
554 {
555 return new wxBitmapRefData(*(wxBitmapRefData *)data);
556 }
557
558
559 /*static*/
560 void wxBitmap::InitStandardHandlers()
561 {
562 // not wxBitmap handlers, we rely on wxImage
563 }