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