Package dtk :: Package ui :: Module draw

Source Code for Module dtk.ui.draw

  1  #! /usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3   
  4  # Copyright (C) 2011 ~ 2012 Deepin, Inc. 
  5  #               2011 ~ 2012 Wang Yong 
  6  #  
  7  # Author:     Wang Yong <lazycat.manatee@gmail.com> 
  8  # Maintainer: Wang Yong <lazycat.manatee@gmail.com> 
  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 3 of the License, or 
 13  # 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, see <http://www.gnu.org/licenses/>. 
 22   
 23  from constant import DEFAULT_FONT, DEFAULT_FONT_SIZE 
 24  from math import pi 
 25  import cairo 
 26  import dtk_cairo_blur     
 27  import gtk 
 28  import math 
 29  import pango 
 30  import pangocairo 
 31  from utils import (cairo_state, cairo_disable_antialias, color_hex_to_cairo,  
 32                     add_color_stop_rgba, propagate_expose,  
 33                     alpha_color_hex_to_cairo, layout_set_markup) 
 34   
35 -def draw_radial_ring(cr, x, y, outer_radius, inner_radius, color_infos):
36 ''' 37 Draw radial ring. 38 39 @param cr: Cairo context. 40 @param x: X coordinate of draw area. 41 @param y: Y coordinate of draw area. 42 @param outer_radius: Radious for outter ring. 43 @param inner_radius: Radious for inner ring. 44 @param color_infos: A list of ColorInfo, ColorInfo format as [(color_pos, (color_hex_value, color_alpha))]. 45 ''' 46 with cairo_state(cr): 47 # Clip. 48 cr.arc(x, y, outer_radius, 0, pi * 2) 49 cr.arc(x, y, inner_radius, 0, pi * 2) 50 cr.set_fill_rule(cairo.FILL_RULE_EVEN_ODD) 51 cr.clip() 52 53 # Draw radial round. 54 draw_radial_round(cr, x, y, outer_radius, color_infos)
55
56 -def get_desktop_pixbuf():
57 ''' 58 Get screenshot of desktop. 59 60 @return: Return desktop screenshot as gtk.gdk.Pixbuf. 61 ''' 62 rootWindow = gtk.gdk.get_default_root_window() 63 [width, height] = rootWindow.get_size() 64 pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, width, height) 65 return pixbuf.get_from_drawable(rootWindow, rootWindow.get_colormap(), 0, 0, 0, 0, width, height)
66
67 -def draw_round_rectangle(cr, x, y, width, height, r):
68 ''' 69 Draw round rectangle. 70 71 @param cr: Cairo context. 72 @param x: X coordiante of rectangle area. 73 @param y: Y coordiante of rectangle area. 74 @param width: Width of rectangle area. 75 @param height: Width of rectangle area. 76 @param r: Radious of rectangle corner. 77 ''' 78 # Adjust coordinate when width and height is negative. 79 if width < 0: 80 x = x + width 81 width = -width 82 if height < 0: 83 y = y + height 84 height = -height 85 86 # Top side. 87 cr.move_to(x + r, y) 88 cr.line_to(x + width - r, y) 89 90 # Top-right corner. 91 cr.arc(x + width - r, y + r, r, pi * 3 / 2, pi * 2) 92 93 # Right side. 94 cr.line_to(x + width, y + height - r) 95 96 # Bottom-right corner. 97 cr.arc(x + width - r, y + height - r, r, 0, pi / 2) 98 99 # Bottom side. 100 cr.line_to(x + r, y + height) 101 102 # Bottom-left corner. 103 cr.arc(x + r, y + height - r, r, pi / 2, pi) 104 105 # Left side. 106 cr.line_to(x, y + r) 107 108 # Top-left corner. 109 cr.arc(x + r, y + r, r, pi, pi * 3 / 2) 110 111 # Close path. 112 cr.close_path()
113
114 -def draw_pixbuf(cr, pixbuf, x=0, y=0, alpha=1.0):
115 ''' 116 Draw pixbuf on cairo context, this function use frequently for image render. 117 118 @param cr: Cairo context. 119 @param pixbuf: gtk.gdk.Pixbuf 120 @param x: X coordiante of draw area. 121 @param y: Y coordiante of draw area. 122 @param alpha: Alpha value to render pixbuf, float value between 0 and 1.0 123 ''' 124 if pixbuf != None: 125 cr.set_source_pixbuf(pixbuf, x, y) 126 cr.paint_with_alpha(alpha)
127
128 -def draw_window_frame(cr, x, y, w, h, 129 color_frame_outside_1, 130 color_frame_outside_2, 131 color_frame_outside_3, 132 color_frame_inside_1, 133 color_frame_inside_2, 134 ):
135 ''' 136 Draw window frame. 137 138 @param cr: Cairo context. 139 @param x: X coordiante of draw area. 140 @param y: Y coordiante of draw area. 141 @param w: Width of draw area. 142 @param h: Height of draw area. 143 @param color_frame_outside_1: Use for draw outside 8 points. 144 @param color_frame_outside_2: Use for draw middle 4 points. 145 @param color_frame_outside_3: Use for draw inside 4 points. 146 @param color_frame_inside_1: Use for draw outside frame. 147 @param color_frame_inside_2: Use for draw inner frame and inside 4 points. 148 ''' 149 with cairo_disable_antialias(cr): 150 # Set line width. 151 cr.set_line_width(1) 152 153 # Set OPERATOR_OVER operator. 154 cr.set_operator(cairo.OPERATOR_OVER) 155 156 # Draw outside 8 points. 157 cr.set_source_rgba(*alpha_color_hex_to_cairo(color_frame_outside_1.get_color_info())) 158 159 cr.rectangle(x, y + 1, 1, 1) # top-left 160 cr.rectangle(x + 1, y, 1, 1) 161 162 cr.rectangle(x + w - 1, y + 1, 1, 1) # top-right 163 cr.rectangle(x + w - 2, y, 1, 1) 164 165 cr.rectangle(x, y + h - 2, 1, 1) # bottom-left 166 cr.rectangle(x + 1, y + h - 1, 1, 1) 167 168 cr.rectangle(x + w - 1, y + h - 2, 1, 1) # bottom-right 169 cr.rectangle(x + w - 2, y + h - 1, 1, 1) 170 171 cr.fill() 172 173 # Draw outside 4 points. 174 cr.set_source_rgba(*alpha_color_hex_to_cairo(color_frame_outside_2.get_color_info())) 175 176 cr.rectangle(x + 1, y + 1, 1, 1) # top-left 177 178 cr.rectangle(x + w - 2, y + 1, 1, 1) # top-right 179 180 cr.rectangle(x + 1, y + h - 2, 1, 1) # bottom-left 181 182 cr.rectangle(x + w - 2, y + h - 2, 1, 1) # bottom-right 183 184 cr.fill() 185 186 # Draw outside frame. 187 cr.set_source_rgba(*alpha_color_hex_to_cairo(color_frame_outside_3.get_color_info())) 188 189 cr.rectangle(x + 2, y, w - 4, 1) # top side 190 191 cr.rectangle(x + 2, y + h - 1, w - 4, 1) # bottom side 192 193 cr.rectangle(x, y + 2, 1, h - 4) # left side 194 195 cr.rectangle(x + w - 1, y + 2, 1, h - 4) # right side 196 197 cr.fill() 198 199 # Draw outside 4 points. 200 cr.set_source_rgba(*alpha_color_hex_to_cairo(color_frame_inside_1.get_color_info())) 201 202 cr.rectangle(x + 1, y + 1, 1, 1) # top-left 203 204 cr.rectangle(x + w - 2, y + 1, 1, 1) # top-right 205 206 cr.rectangle(x + 1, y + h - 2, 1, 1) # bottom-left 207 208 cr.rectangle(x + w - 2, y + h - 2, 1, 1) # bottom-right 209 210 cr.fill() 211 212 # Draw inside 4 points. 213 cr.set_source_rgba(*alpha_color_hex_to_cairo(color_frame_inside_1.get_color_info())) 214 215 cr.rectangle(x + 2, y + 2, 1, 1) # top-left 216 217 cr.rectangle(x + w - 3, y + 2, 1, 1) # top-right 218 219 cr.rectangle(x + 2, y + h - 3, 1, 1) # bottom-left 220 221 cr.rectangle(x + w - 3, y + h - 3, 1, 1) # bottom-right 222 223 cr.fill() 224 225 # Draw inside frame. 226 cr.set_source_rgba(*alpha_color_hex_to_cairo(color_frame_inside_2.get_color_info())) 227 228 cr.rectangle(x + 2, y + 1, w - 4, 1) # top side 229 230 cr.rectangle(x + 2, y + h - 2, w - 4, 1) # bottom side 231 232 cr.rectangle(x + 1, y + 2, 1, h - 4) # left side 233 234 cr.rectangle(x + w - 2, y + 2, 1, h - 4) # right side 235 236 cr.fill()
237
238 -def draw_window_rectangle(cr, sx, sy, ex, ey, r):
239 ''' 240 Draw window rectangle. 241 242 @param cr: Cairo context. 243 @param sx: Source x coordinate. 244 @param sy: Source y coordinate. 245 @param ex: Target x coordinate. 246 @param ey: Target x coordinate. 247 @param r: Window frame radious. 248 ''' 249 with cairo_disable_antialias(cr): 250 # Set line width. 251 cr.set_line_width(1) 252 253 # Set OPERATOR_OVER operator. 254 cr.set_operator(cairo.OPERATOR_OVER) 255 256 cr.move_to(sx + r, sy) # top line 257 cr.line_to(ex - r, sy) 258 cr.stroke() 259 260 cr.move_to(ex, sy + r) # right side 261 cr.line_to(ex, ey - r) 262 cr.stroke() 263 264 cr.move_to(ex - r, ey) # bottom side 265 cr.line_to(sx + r, ey) 266 cr.stroke() 267 268 cr.move_to(sx, ey - r) # left side 269 cr.line_to(sx, sy + r) 270 cr.stroke() 271 272 cr.arc(sx + r, sy + r, r, pi, pi * 3 / 2) # top-left 273 cr.stroke() 274 275 cr.arc(ex - r, sy + r, r, pi * 3 / 2, pi * 2) # top-right 276 cr.stroke() 277 278 cr.arc(ex - r, ey - r, r, 0, pi / 2) # bottom-right 279 cr.stroke() 280 281 cr.arc(sx + r, ey - r, r, pi / 2, pi) # bottom-left 282 cr.stroke()
283
284 -def draw_text(cr, markup, x, y, w, h, text_size=DEFAULT_FONT_SIZE, text_color="#000000", 285 text_font=DEFAULT_FONT, alignment=pango.ALIGN_LEFT, 286 gaussian_radious=None, gaussian_color=None, 287 border_radious=None, border_color=None, 288 wrap_width=None, 289 ):
290 ''' 291 Standard function for draw text. 292 293 @param cr: Cairo context. 294 @param markup: Pango markup string. 295 @param x: X coordinate of draw area. 296 @param y: Y coordinate of draw area. 297 @param w: Width of draw area. 298 @param h: Height of draw area. 299 @param text_size: Text size, default is DEFAULT_FONT_SIZE. 300 @param text_color: Text color, default is \"#000000\". 301 @param text_font: Text font, default is DEFAULT_FONT. 302 @param alignment: Font alignment option, default is pango.ALIGN_LEFT. You can set pango.ALIGN_MIDDLE or pango.ALIGN_RIGHT. 303 @param gaussian_radious: Gaussian radious, default is None. 304 @param gaussian_color: Gaussian color, default is None. 305 @param border_radious: Border radious, default is None. 306 @param border_color: Border color, default is None. 307 @param wrap_width: Wrap width of text, default is None. 308 ''' 309 if border_radious == None and border_color == None and gaussian_radious == None and gaussian_color == None: 310 render_text(cr, markup, x, y, w, h, text_size, text_color, text_font, alignment, 311 wrap_width=wrap_width) 312 elif (border_radious != None and border_color != None) or (gaussian_radious != None and gaussian_color != None): 313 # Create text cairo context. 314 surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h) 315 text_cr = cairo.Context(surface) 316 317 # Draw gaussian light. 318 if gaussian_radious != None and gaussian_color != None: 319 text_cr.save() 320 render_text(text_cr, markup, gaussian_radious, 321 gaussian_radious, w - gaussian_radious * 2, h - gaussian_radious * 2, 322 text_size, gaussian_color, alignment=alignment, 323 wrap_width=wrap_width) 324 dtk_cairo_blur.gaussian_blur(surface, gaussian_radious) 325 text_cr.restore() 326 327 # Make sure border can render correctly. 328 if gaussian_radious == None: 329 gaussian_radious = 0 330 331 # Draw gaussian border. 332 if border_radious != None and border_radious != 0 and border_color != None: 333 render_text(text_cr, markup, gaussian_radious, gaussian_radious, w - gaussian_radious * 2, 334 h - gaussian_radious * 2, text_size, border_color, alignment=alignment, 335 wrap_width=wrap_width) 336 dtk_cairo_blur.gaussian_blur(surface, border_radious) 337 338 # Draw font. 339 render_text(text_cr, markup, gaussian_radious, gaussian_radious, w - gaussian_radious * 2, 340 h - gaussian_radious * 2, text_size, text_color, alignment=alignment, 341 wrap_width=wrap_width) 342 343 # Render gaussian text to target cairo context. 344 cr.set_source_surface(surface, x, y) 345 cr.paint()
346
347 -def render_text(cr, markup, x, y, w, h, text_size=DEFAULT_FONT_SIZE, text_color="#000000", 348 text_font=DEFAULT_FONT, alignment=pango.ALIGN_LEFT, 349 wrap_width=None):
350 ''' 351 Render text for function L{ I{draw_text} <draw_text>}, you can use this function individually. 352 353 @param cr: Cairo context. 354 @param markup: Pango markup string. 355 @param x: X coordinate of draw area. 356 @param y: Y coordinate of draw area. 357 @param w: Width of draw area. 358 @param h: Height of draw area. 359 @param text_size: Text size, default is DEFAULT_FONT_SIZE. 360 @param text_color: Text color, default is \"#000000\". 361 @param text_font: Text font, default is DEFAULT_FONT. 362 @param alignment: Font alignment option, default is pango.ALIGN_LEFT. You can set pango.ALIGN_MIDDLE or pango.ALIGN_RIGHT. 363 @param wrap_width: Wrap width of text, default is None. 364 ''' 365 # Create pangocairo context. 366 context = pangocairo.CairoContext(cr) 367 368 # Set layout. 369 layout = context.create_layout() 370 layout.set_font_description(pango.FontDescription("%s %s" % (text_font, text_size))) 371 layout_set_markup(layout, markup) 372 layout.set_alignment(alignment) 373 if wrap_width == None: 374 layout.set_single_paragraph_mode(True) 375 layout.set_width(w * pango.SCALE) 376 layout.set_ellipsize(pango.ELLIPSIZE_END) 377 else: 378 layout.set_width(wrap_width * pango.SCALE) 379 layout.set_wrap(pango.WRAP_WORD) 380 (text_width, text_height) = layout.get_pixel_size() 381 382 # Draw text. 383 cr.move_to(x, y + (h - text_height) / 2) 384 cr.set_source_rgb(*color_hex_to_cairo(text_color)) 385 context.update_layout(layout) 386 context.show_layout(layout)
387
388 -def draw_line(cr, sx, sy, ex, ey, line_width=1, antialias_status=cairo.ANTIALIAS_NONE):
389 ''' 390 Draw line. 391 392 @param cr: Cairo context. 393 @param sx: Souce X coordinate. 394 @param sy: Souce Y coordinate. 395 @param ex: Target X coordinate. 396 @param ey: Target Y coordinate. 397 @param line_width: Line width, default is 1 pixel. 398 @param antialias_status: Antialias status, default is cairo.ANTIALITAS_NONE. 399 ''' 400 # Save antialias. 401 antialias = cr.get_antialias() 402 403 # Draw line. 404 cr.set_line_width(line_width) 405 cr.set_antialias(antialias_status) 406 cr.move_to(sx, sy) 407 cr.line_to(ex, ey) 408 cr.stroke() 409 410 # Restore antialias. 411 cr.set_antialias(antialias)
412
413 -def draw_vlinear(cr, x, y, w, h, color_infos, radius=0, top_to_bottom=True):
414 ''' 415 Draw linear area vertically. 416 417 @param cr: Cairo context. 418 @param x: X coordinate of draw area. 419 @param y: Y coordinate of draw area. 420 @param w: Width of draw area. 421 @param h: Height of draw area. 422 @param color_infos: A list of ColorInfo, ColorInfo format: (color_stop_position, (color_hex_value, color_alpha)) 423 @param radius: Rectangle corner radious. 424 @param top_to_bottom: Draw direction, default is from top to bottom, function will draw from bottom to top if set option as False. 425 ''' 426 with cairo_state(cr): 427 # Translate y coordinate, otherwise y is too big for LinearGradient cause render bug. 428 cr.translate(0, y) 429 430 if top_to_bottom: 431 pat = cairo.LinearGradient(0, 0, 0, h) 432 else: 433 pat = cairo.LinearGradient(0, h, 0, 0) 434 435 for (pos, color_info) in color_infos: 436 add_color_stop_rgba(pat, pos, color_info) 437 cr.set_operator(cairo.OPERATOR_OVER) 438 cr.set_source(pat) 439 draw_round_rectangle(cr, x, 0, w, h, radius) 440 441 cr.fill()
442
443 -def draw_hlinear(cr, x, y, w, h, color_infos, radius=0, left_to_right=True):
444 ''' 445 Draw linear area horticulturally. 446 447 @param cr: Cairo context. 448 @param x: X coordinate of draw area. 449 @param y: Y coordinate of draw area. 450 @param w: Width of draw area. 451 @param h: Height of draw area. 452 @param color_infos: A list of ColorInfo, ColorInfo format: (color_stop_position, (color_hex_value, color_alpha)) 453 @param radius: Rectangle corner radious. 454 @param left_to_right: Draw direction, default is from left to right, function will draw from right to left if set option as False. 455 ''' 456 with cairo_state(cr): 457 # Translate x coordinate, otherwise x is too big for LinearGradient cause render bug. 458 cr.translate(x, 0) 459 460 if left_to_right: 461 pat = cairo.LinearGradient(0, 0, w, 0) 462 else: 463 pat = cairo.LinearGradient(w, 0, 0, 0) 464 for (pos, color_info) in color_infos: 465 add_color_stop_rgba(pat, pos, color_info) 466 cr.set_operator(cairo.OPERATOR_OVER) 467 cr.set_source(pat) 468 draw_round_rectangle(cr, 0, y, w, h, radius) 469 cr.fill()
470
471 -def expose_linear_background(widget, event, color_infos):
472 ''' 473 Expose linear background. 474 475 @param widget: Gtk.Widget instance. 476 @param event: Expose event. 477 @param color_infos: A list of ColorInfo, ColorInfo format: (color_stop_position, (color_hex_value, color_alpha)) 478 ''' 479 # Init. 480 cr = widget.window.cairo_create() 481 rect = widget.allocation 482 483 # Draw linear background. 484 draw_vlinear(cr, rect.x, rect.y, rect.width, rect.height, color_infos) 485 486 # Propagate expose. 487 propagate_expose(widget, event) 488 489 return True
490
491 -def draw_window_shadow(cr, x, y, w, h, r, p, color_window_shadow):
492 ''' 493 Draw window shadow. 494 495 @param cr: Cairo context. 496 @param x: X coordinate of draw area. 497 @param y: Y coordinate of draw area. 498 @param w: Width of draw area. 499 @param h: Height of draw area. 500 @param r: Radious of window shadow corner. 501 @param p: Padding between window shadow and window frame. 502 @param color_window_shadow: theme.DyanmicShadowColor. 503 ''' 504 color_infos = color_window_shadow.get_color_info() 505 with cairo_state(cr): 506 # Clip four corner. 507 cr.rectangle(x, y, r - 1, r - 1) # top-left 508 cr.rectangle(x + r - 1, y, 1, r - 2) # vertical 509 cr.rectangle(x, y + r - 1, r - 2, 1) # horizontal 510 511 cr.rectangle(x + w - r + 1, y, r - 1, r - 1) # top-right 512 cr.rectangle(x + w - r, y, 1, r - 2) # vertical 513 cr.rectangle(x + w - r + 2, y + r - 1, r - 2, 1) # horizontal 514 515 cr.rectangle(x, y + h - r + 1, r - 1, r - 1) # bottom-left 516 cr.rectangle(x + r - 1, y + h - r + 2, 1, r - 2) # vertical 517 cr.rectangle(x, y + h - r, r - 2, 1) # horizontal 518 519 cr.rectangle(x + w - r + 1, y + h - r + 1, r - 1, r - 1) # bottom-right 520 cr.rectangle(x + w - r, y + h - r + 2, 1, r - 2) # vertical 521 cr.rectangle(x + w - r + 2, y + h - r, r - 2, 1) # horizontal 522 523 cr.clip() 524 525 # Draw four round. 526 draw_radial_round(cr, x + r, y + r, r, color_infos) 527 draw_radial_round(cr, x + r, y + h - r, r, color_infos) 528 draw_radial_round(cr, x + w - r, y + r, r, color_infos) 529 draw_radial_round(cr, x + w - r, y + h - r, r, color_infos) 530 531 with cairo_state(cr): 532 # Clip four side. 533 cr.rectangle(x, y + r, p, h - r * 2) 534 cr.rectangle(x + w - p, y + r, p, h - r * 2) 535 cr.rectangle(x + r, y, w - r * 2, p) 536 cr.rectangle(x + r, y + h - p, w - r * 2, p) 537 cr.clip() 538 539 # Draw four side. 540 draw_vlinear( 541 cr, 542 x + r, y, 543 w - r * 2, r, color_infos) 544 draw_vlinear( 545 cr, 546 x + r, y + h - r, 547 w - r * 2, r, color_infos, 0, False) 548 draw_hlinear( 549 cr, 550 x, y + r, 551 r, h - r * 2, color_infos) 552 draw_hlinear( 553 cr, 554 x + w - r, y + r, 555 r, h - r * 2, color_infos, 0, False)
556
557 -def draw_radial_round(cr, x, y, r, color_infos):
558 ''' 559 Draw radial round. 560 561 @param cr: Cairo context. 562 @param x: X coordinate of draw area. 563 @param y: Y coordinate of draw area. 564 @param r: Radious of radial round. 565 @param color_infos: A list of ColorInfo, ColorInfo format: (color_stop_position, (color_hex_value, color_alpha)) 566 ''' 567 radial = cairo.RadialGradient(x, y, r, x, y, 0) 568 for (pos, color_info) in color_infos: 569 add_color_stop_rgba(radial, pos, color_info) 570 cr.arc(x, y, r, 0, 2 * math.pi) 571 cr.set_source(radial) 572 cr.fill()
573
574 -def draw_blank_mask(cr, x, y, w, h):
575 ''' 576 Draw blank mask, use for default mask function. 577 578 @param cr: Cairo context. 579 @param x: X coordiante of rectangle area. 580 @param y: Y coordiante of rectangle area. 581 @param w: Width of rectangle area. 582 @param h: Width of rectangle area. 583 ''' 584 pass
585