]> git.sesse.net Git - vlc/blob - modules/gui/skins2/x11/x11_graphics.cpp
Add --data-path option. Access the src share directory now works from build tree.
[vlc] / modules / gui / skins2 / x11 / x11_graphics.cpp
1 /*****************************************************************************
2  * x11_graphics.cpp
3  *****************************************************************************
4  * Copyright (C) 2003 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Cyril Deguet     <asmax@via.ecp.fr>
8  *          Olivier Teulière <ipkiss@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #ifdef X11_SKINS
26
27 #include <X11/Xlib.h>
28 #include <X11/Xutil.h>
29 #include <X11/extensions/shape.h>
30
31 #include "x11_display.hpp"
32 #include "x11_graphics.hpp"
33 #include "x11_window.hpp"
34 #include "../src/generic_bitmap.hpp"
35
36
37 X11Graphics::X11Graphics( intf_thread_t *pIntf, X11Display &rDisplay,
38                           int width, int height ):
39     OSGraphics( pIntf ), m_rDisplay( rDisplay ), m_width( width ),
40     m_height( height )
41 {
42     // Get the display paramaters
43     int screen = DefaultScreen( XDISPLAY );
44     int depth = DefaultDepth( XDISPLAY, screen );
45
46     // X11 doesn't accept that !
47     if( width == 0 || height == 0 )
48     {
49         // Avoid a X11 Bad Value error
50         width = height = 1;
51         msg_Err( getIntf(), "invalid image size (null width or height)" );
52     }
53
54     // Create a pixmap
55     m_pixmap = XCreatePixmap( XDISPLAY, DefaultRootWindow( XDISPLAY ),
56                               width, height, depth);
57
58     // Create the transparency mask (everything is transparent initially)
59     m_mask = XCreateRegion();
60
61     // Create a Graphics Context that does not generate GraphicsExpose events
62     XGCValues xgcvalues;
63     xgcvalues.graphics_exposures = False;
64     m_gc = XCreateGC( XDISPLAY, m_pixmap, GCGraphicsExposures, &xgcvalues );
65 }
66
67
68 X11Graphics::~X11Graphics()
69 {
70     XFreeGC( XDISPLAY, m_gc );
71     XDestroyRegion( m_mask );
72     XFreePixmap( XDISPLAY, m_pixmap );
73 }
74
75
76 void X11Graphics::clear()
77 {
78     // Clear the transparency mask
79     XDestroyRegion( m_mask );
80     m_mask = XCreateRegion();
81 }
82
83
84 void X11Graphics::drawGraphics( const OSGraphics &rGraphics, int xSrc,
85                                 int ySrc, int xDest, int yDest, int width,
86                                 int height )
87 {
88     if( width == -1 )
89     {
90         width = rGraphics.getWidth();
91     }
92     if( height == -1 )
93     {
94         height = rGraphics.getHeight();
95     }
96
97     // Source drawable
98     Drawable src = ((X11Graphics&)rGraphics).getDrawable();
99
100     // Create the mask for transparency
101     Region voidMask = XCreateRegion();
102     XRectangle rect;
103     rect.x = xSrc;
104     rect.y = ySrc;
105     rect.width = width;
106     rect.height = height;
107     Region clipMask = XCreateRegion();
108     XUnionRectWithRegion( &rect, voidMask, clipMask );
109     Region mask = XCreateRegion();
110     XIntersectRegion( ((X11Graphics&)rGraphics).getMask(), clipMask, mask );
111     XDestroyRegion( clipMask );
112     XDestroyRegion( voidMask );
113     XOffsetRegion( mask, xDest - xSrc, yDest - ySrc );
114
115     // Copy the pixmap
116     XSetRegion( XDISPLAY, m_gc, mask );
117     XCopyArea( XDISPLAY, src, m_pixmap, m_gc, xSrc, ySrc, width, height,
118                xDest, yDest );
119
120     // Add the source mask to the mask of the graphics
121     Region newMask = XCreateRegion();
122     XUnionRegion( m_mask, mask, newMask );
123     XDestroyRegion( mask );
124     XDestroyRegion( m_mask );
125     m_mask = newMask;
126 }
127
128
129 void X11Graphics::drawBitmap( const GenericBitmap &rBitmap, int xSrc,
130                               int ySrc, int xDest, int yDest, int width,
131                               int height, bool blend )
132 {
133     // Get the bitmap size if necessary
134     if( width == -1 )
135     {
136         width = rBitmap.getWidth();
137     }
138     else if( width > rBitmap.getWidth() )
139     {
140         msg_Dbg( getIntf(), "bitmap width too small (%i)", rBitmap.getWidth() );
141         width = rBitmap.getWidth();
142     }
143     if( height == -1 )
144     {
145         height = rBitmap.getHeight();
146     }
147     else if( height > rBitmap.getHeight() )
148     {
149         msg_Dbg( getIntf(), "bitmap height too small (%i)", rBitmap.getHeight()
150                                  );
151         height = rBitmap.getHeight();
152     }
153
154     // Nothing to draw if width or height is null
155     if( width == 0 || height == 0 )
156     {
157         return;
158     }
159
160     // Safety check for debugging purpose
161     if( xDest + width > m_width || yDest + height > m_height )
162     {
163         msg_Dbg( getIntf(), "bitmap too large" );
164         return;
165     }
166
167     // Get a buffer on the image data
168     uint8_t *pBmpData = rBitmap.getData();
169     if( pBmpData == NULL )
170     {
171         // Nothing to draw
172         return;
173     }
174
175     // Get the image from the pixmap
176     XImage *pImage = XGetImage( XDISPLAY, m_pixmap, xDest, yDest, width,
177                                 height, AllPlanes, ZPixmap );
178     if( pImage == NULL )
179     {
180         msg_Dbg( getIntf(), "XGetImage returned NULL" );
181         return;
182     }
183     char *pData = pImage->data;
184
185     // Get the padding of this image
186     int pad = pImage->bitmap_pad >> 3;
187     int shift = ( pad - ( (width * XPIXELSIZE) % pad ) ) % pad;
188
189     // Mask for transparency
190     Region mask = XCreateRegion();
191
192     // Get a pointer on the right X11Display::makePixel method
193     X11Display::MakePixelFunc_t makePixelFunc = ( blend ?
194         m_rDisplay.getBlendPixel() : m_rDisplay.getPutPixel() );
195
196     // Skip the first lines of the image
197     pBmpData += 4 * ySrc * rBitmap.getWidth();
198
199     // Copy the bitmap on the image and compute the mask
200     for( int y = 0; y < height; y++ )
201     {
202         // Skip uninteresting bytes at the beginning of the line
203         pBmpData += 4 * xSrc;
204         // Flag to say whether the previous pixel on the line was visible
205         bool wasVisible = false;
206         // Beginning of the current visible segment on the line
207         int visibleSegmentStart = 0;
208         for( int x = 0; x < width; x++ )
209         {
210             uint8_t b = *(pBmpData++);
211             uint8_t g = *(pBmpData++);
212             uint8_t r = *(pBmpData++);
213             uint8_t a = *(pBmpData++);
214             // Draw the pixel
215             (m_rDisplay.*makePixelFunc)( (uint8_t*)pData, r, g, b, a );
216             pData += XPIXELSIZE;
217             if( a > 0 )
218             {
219                 // Pixel is visible
220                 if( ! wasVisible )
221                 {
222                     // Beginning of a visible segment
223                     visibleSegmentStart = x;
224                 }
225                 wasVisible = true;
226             }
227             else
228             {
229                 // Pixel is transparent
230                 if( wasVisible )
231                 {
232                     // End of a visible segment: add it to the mask
233                     addHSegmentInRegion( mask, visibleSegmentStart, x, y );
234                 }
235                 wasVisible = false;
236             }
237         }
238         if( wasVisible )
239         {
240             // End of a visible segment: add it to the mask
241             addHSegmentInRegion( mask, visibleSegmentStart, width, y );
242         }
243         pData += shift;
244         // Skip uninteresting bytes at the end of the line
245         pBmpData += 4 * (rBitmap.getWidth() - width - xSrc);
246     }
247
248     // Apply the mask to the graphics context
249     XOffsetRegion( mask, xDest, yDest );
250     XSetRegion( XDISPLAY, m_gc, mask );
251     // Copy the image on the pixmap
252     XPutImage( XDISPLAY, m_pixmap, m_gc, pImage, 0, 0, xDest, yDest, width,
253                height);
254     XDestroyImage( pImage );
255
256     // Add the bitmap mask to the global graphics mask
257     Region newMask = XCreateRegion();
258     XUnionRegion( mask, m_mask, newMask );
259     XDestroyRegion( m_mask );
260     m_mask = newMask;
261
262     XDestroyRegion( mask );
263 }
264
265
266 void X11Graphics::fillRect( int left, int top, int width, int height,
267                             uint32_t color )
268 {
269     // Update the mask with the rectangle area
270     Region newMask = XCreateRegion();
271     XRectangle rect;
272     rect.x = left;
273     rect.y = top;
274     rect.width = width;
275     rect.height = height;
276     XUnionRectWithRegion( &rect, m_mask, newMask );
277     XDestroyRegion( m_mask );
278     m_mask = newMask;
279
280     // Draw the rectangle
281     XGCValues gcVal;
282     gcVal.foreground = m_rDisplay.getPixelValue( color >> 16, color >> 8, color );
283     XChangeGC( XDISPLAY, m_gc, GCForeground,  &gcVal );
284     XSetRegion( XDISPLAY, m_gc, m_mask );
285     XFillRectangle( XDISPLAY, m_pixmap, m_gc, left, top, width, height );
286 }
287
288
289 void X11Graphics::drawRect( int left, int top, int width, int height,
290                             uint32_t color )
291 {
292     // Update the mask with the rectangle
293     addHSegmentInRegion( m_mask, left, left + width, top );
294     addHSegmentInRegion( m_mask, left, left + width, top + height );
295     addVSegmentInRegion( m_mask, top, top + height, left );
296     addVSegmentInRegion( m_mask, top, top + height, left + width );
297
298     // Draw the rectangle
299     XGCValues gcVal;
300     gcVal.foreground = m_rDisplay.getPixelValue( color >> 16, color >> 8, color );
301     XChangeGC( XDISPLAY, m_gc, GCForeground,  &gcVal );
302     XSetRegion( XDISPLAY, m_gc, m_mask );
303     XDrawRectangle( XDISPLAY, m_pixmap, m_gc, left, top, width - 1, height - 1 );
304 }
305
306
307 void X11Graphics::applyMaskToWindow( OSWindow &rWindow )
308 {
309     // Get the target window
310     Window win = ((X11Window&)rWindow).getDrawable();
311
312     // Change the shape of the window
313     XShapeCombineRegion( XDISPLAY, win, ShapeBounding, 0, 0, m_mask,
314                          ShapeSet );
315 }
316
317
318 void X11Graphics::copyToWindow( OSWindow &rWindow, int xSrc,  int ySrc,
319                                 int width, int height, int xDest, int yDest )
320 {
321     // Destination window
322     Drawable dest = ((X11Window&)rWindow).getDrawable();
323
324     XCopyArea( XDISPLAY, m_pixmap, dest, XGC, xSrc, ySrc, width, height,
325                xDest, yDest );
326 }
327
328
329 bool X11Graphics::hit( int x, int y ) const
330 {
331     return XPointInRegion( m_mask, x, y );
332 }
333
334
335 inline void X11Graphics::addHSegmentInRegion( Region &rMask, int xStart,
336                                               int xEnd, int y )
337 {
338     XRectangle rect;
339     rect.x = xStart;
340     rect.y = y;
341     rect.width = xEnd - xStart;
342     rect.height = 1;
343     Region newMask = XCreateRegion();
344     XUnionRectWithRegion( &rect, rMask, newMask );
345     XDestroyRegion( rMask );
346     rMask = newMask;
347 }
348
349
350 inline void X11Graphics::addVSegmentInRegion( Region &rMask, int yStart,
351                                               int yEnd, int x )
352 {
353     XRectangle rect;
354     rect.x = x;
355     rect.y = yStart;
356     rect.width = 1;
357     rect.height = yEnd - yStart;
358     Region newMask = XCreateRegion();
359     XUnionRectWithRegion( &rect, rMask, newMask );
360     XDestroyRegion( rMask );
361     rMask = newMask;
362 }
363
364 #endif