/* Render.cc Copyright © 2003 David Reveman. This file is part of Waimea. Waimea is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. Waimea is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Waimea; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H # include "../config.h" #endif // HAVE_CONFIG_H extern "C" { #ifdef STDC_HEADERS # include # include #endif // STDC_HEADERS #ifdef HAVE_LIBGEN_H # include #else char *dirname(char *name) { int i = strlen(name); for (; i >= 0 && name[i] != '/'; i--); name[i] = '\0'; return name; } #endif // HAVE_LIBGEN_H } #include "Render.hh" #include "Screen.hh" #ifdef PNG # include "Png.hh" #endif // PNG Tst *filter_tst; Tst *extend_tst; Tst *pattern_type_tst; Tst *line_cap_tst; Tst *line_join_tst; Tst *fill_rule_tst; Tst *drawing_order_tst; Tst *weight_tst; Tst *slant_tst; Tst *image_scale_type_tst; Tst *gravity_type_tst; Tst *horizontal_alignment_tst; Tst *vertical_alignment_tst; Tst *operator_tst; WaSurface::WaSurface(Display *_display, cairo_surface_t *sp, Pixmap p, Pixmap b, unsigned char *_data, unsigned int w, unsigned int h) : RefCounted(this) { display = _display; crsurface = sp; pixmap = p; bitmap = b; width = w; height = h; data = _data; } WaSurface::~WaSurface(void) { if (crsurface) cairo_surface_destroy(crsurface); if (pixmap) XFreePixmap(display, pixmap); if (bitmap) XFreePixmap(display, bitmap); if (data) delete [] data; } CacheSurface::CacheSurface(Display *_display, cairo_surface_t *sp, Pixmap p, unsigned char *_data, unsigned int w, unsigned int h, map *dg) : WaSurface(_display, sp, p, None, _data, w, h) { map::iterator it = dg->begin(); for (; it != dg->end(); it++) dynamic_groups.insert( make_pair((*it).first, (RenderGroup *) ((*it).second)->ref())); group = NULL; } CacheSurface::~CacheSurface(void) { if (group) { group->cache.remove(this); group->unref(); } MAPUNREFSECOND(dynamic_groups); } bool CacheSurface::dynamicGroupsMatch(DWindowObject *dwo) { if (dwo->sb->dynamic_groups.size() != dynamic_groups.size()) return false; map::iterator it = dynamic_groups.begin(); for (; it != dynamic_groups.end(); it++) { map::iterator dwo_it = dwo->sb->dynamic_groups.find((*it).first); if (dwo_it == dwo->sb->dynamic_groups.end()) return false; else if ((*dwo_it).second != (*it).second) return false; } return true; } WaColor::WaColor(double r, double g, double b, double a) : RefCounted(this) { alpha = a; red = r; green = g; blue = b; } bool WaColor::parseColor(WaScreen *ws, const char *spec) { XColor core_color; if (! XParseColor(ws->display, ws->colormap, spec, &core_color)) return false; red = core_color.red / 65535.0; green = core_color.green / 65535.0; blue = core_color.blue / 65535.0; return true; } RenderOp::RenderOp(RenderOpType _type) : RefCounted(this) { xrop_set = false; rotation = RENDER_ROTATION_DEFAULT; type = _type; _x = _x2 = RENDER_X_DEFAULT; _y = _y2 = RENDER_Y_DEFAULT; _xu = _yu = _x2u = _y2u = PXLenghtUnitType; _w = _h = 100; _wu = _hu = PCTLenghtUnitType; halign = RENDER_HALIGN_DEFAULT; valign = RENDER_VALIGN_DEFAULT; gravity = gravity2 = RENDER_GRAVITY_DEFAULT; x2_set = y2_set = false; } void RenderOp::inheritAttributes(RenderOp *inherit) { xrop_set = inherit->xrop_set; rotation = inherit->rotation; _x = inherit->_x; _x2 = inherit->_x2; _y = inherit->_y; _y2 = inherit->_y2; _xu = inherit->_xu; _yu = inherit->_yu; _x2u = inherit->_x2u; _y2u = inherit->_y2u; _w = inherit->_w; _h = inherit->_h; _wu = inherit->_wu; _hu = inherit->_hu; halign = inherit->halign; valign = inherit->valign; gravity = inherit->gravity; gravity2 = inherit->gravity2; x2_set = inherit->x2_set; y2_set = inherit->y2_set; } void RenderOp::applyAttributes(Parser *parser, Tst *attr) { attributes_get_render(parser, attr, &xrop, &xrop_set, &rotation); attributes_get_area_default(parser, attr, &_x, &_y, &_xu, &_yu, &gravity, &_x2, &_y2, &_x2u, &_y2u, &gravity2, &x2_set, &y2_set, &_w, &_h, &_wu, &_hu, &halign, &valign); } void RenderOp::calcWidth(double parent_width, double hdpi, unsigned int *return_width) { double rw; calc_length(_w, _wu, hdpi, parent_width, &rw); *return_width = WA_ROUND_U(rw); } void RenderOp::fcalcWidth(double parent_width, double hdpi, double *return_width) { calc_length(_w, _wu, hdpi, parent_width, return_width); } void RenderOp::calcHeight(double parent_height, double vdpi, unsigned int *return_height) { double rh; calc_length(_h, _hu, vdpi, parent_height, &rh); *return_height = WA_ROUND_U(rh); } void RenderOp::calcPositionAndSize(double parent_width, double parent_height, double hdpi, double vdpi, double *return_x, double *return_y, double *return_width, double *return_height) { calc_position_and_size(halign, valign, gravity, _xu, _yu, _wu, _hu, parent_width, parent_height, hdpi, vdpi, _w, _h, _x, _y, return_x, return_y, return_width, return_height); if (x2_set || y2_set) { double tmp_x2, tmp_y2; calc_position(gravity2, _x2u, _y2u, parent_width, parent_height, hdpi, vdpi, _x2, _y2, &tmp_x2, &tmp_y2); if (x2_set) *return_width = tmp_x2 - *return_x; if (y2_set) *return_height = tmp_y2 - *return_y; } } void RenderOp::calcPositionFromSize(double parent_width, double parent_height, double width, double height, LenghtUnitType wu, LenghtUnitType hu, double hdpi, double vdpi, double *return_x, double *return_y) { calc_position_and_size(halign, valign, gravity, _xu, _yu, wu, hu, parent_width, parent_height, hdpi, vdpi, width, height, _x, _y, return_x, return_y, NULL, NULL); } RenderGroup::RenderGroup(WaScreen *_ws, char *_name) : RenderOp(RenderOpGroupType) { opacity = RENDER_OPACITY_DEFAULT; has_dynamic_op = is_a_style = false; cacheable = true; ws = _ws; if (_name) name = WA_STRDUP(_name); else name = NULL; return_surface = NULL; parent_surface = NULL; } RenderGroup::~RenderGroup(void) { clear(); if (name) delete [] name; } void RenderGroup::clear(void) { clearCache(); while (! operations.empty()) { operations.back()->unref(); operations.pop_back(); } } void RenderGroup::clearCache(void) { while (! cache.empty()) cache.pop_back(); } void RenderGroup::inheritContent(RenderGroup *inherit_group) { list::iterator oit = inherit_group->operations.begin(); for (; oit != inherit_group->operations.end(); oit++) operations.push_back((*oit)->ref()); if (inherit_group->has_dynamic_op) has_dynamic_op = true; if (! inherit_group->cacheable) cacheable = false; } void RenderGroup::inheritAttributes(RenderGroup *inherit_group) { opacity = inherit_group->opacity; if (! inherit_group->cacheable) cacheable = false; ((RenderOp *) this)->inheritAttributes((RenderOp *) inherit_group); } void RenderGroup::applyAttributes(Parser *parser, Tst *attr) { char *value = parser->attrGetString(attr, "inherit", NULL); if (value) { RenderGroup *inherit_group = ws->getRenderGroupNamed(value, false); if (inherit_group == this) { parser->warning("group %s cannot inherit itself\n", name); } else if (inherit_group) { inheritAttributes(inherit_group); } else { if (! parser->attrGetBool(attr, "ignore_missing", false)) parser->warning("undefined group: %s\n", value); } } ((RenderOp *) this)->applyAttributes(parser, attr); opacity = parser->attrGetDouble(attr, "opacity", opacity); cacheable = parser->attrGetBool(attr, "cache", cacheable); } void RenderGroup::addToCache(CacheSurface *cache_item, DWindowObject *dwo) { dwo->sb->cache_table.insert( make_pair(this, (CacheSurface *) cache_item->ref())); cache.push_front(cache_item); cache_item->group = (RenderGroup *) ref(); } CacheSurface *RenderGroup::findCachedSurface(DWindowObject *dwo, unsigned int w, unsigned int h) { CacheSurface *new_cache_item = NULL; list::iterator it = cache.begin(); for (; it != cache.end(); it++) { if ((*it)->width == w && (*it)->height == h && (*it)->dynamicGroupsMatch(dwo)) { new_cache_item = (CacheSurface *) (*it)->ref(); break; } } map::iterator mit = dwo->sb->cache_table.find(this); if (mit != dwo->sb->cache_table.end()) { ((*mit).second)->unref(); dwo->sb->cache_table.erase(mit); } if (new_cache_item) { dwo->sb->cache_table.insert( make_pair(this, (CacheSurface *) new_cache_item->ref())); } return new_cache_item; } void RenderGroup::render(DWindowObject *dwo, cairo_t *cr, cairo_surface_t *crsurface, unsigned int w, unsigned int h) { double x, y, _width, _height; unsigned int width, height; Pixmap pixmap = None; unsigned char *data = NULL; if (is_a_style) { width = w; height = h; x = y = 0.0; } else { calcPositionAndSize(w, h, ws->hdpi, ws->vdpi, &x, &y, &_width, &_height); width = WA_ROUND_U(_width); height = WA_ROUND_U(_height); } CacheSurface *cache_surface = findCachedSurface(dwo, width, height); if (! cache_surface) { cairo_surface_t *groupsurface; if (crsurface) { groupsurface = cairo_surface_create_similar(crsurface, CAIRO_CONTENT_COLOR_ALPHA, width, height); } else if (parent_surface) { groupsurface = cairo_surface_create_similar(parent_surface, CAIRO_CONTENT_COLOR_ALPHA, width, height); } else { if (ws->waimea->client_side_rendering) { data = new unsigned char[width * height * sizeof(WaPixel)]; memset (data, 0, width * height * sizeof(WaPixel)); groupsurface = cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, width, height, width * 4); } else { GC gc; XGCValues gcv; Visual *visual; Colormap colormap; int depth = ws->screen_depth; if (dwo->type == RootType) { XWindowAttributes attr; XGetWindowAttributes (ws->display, ws->id, &attr); visual = attr.visual; depth = attr.depth; colormap = attr.colormap; } else { visual = ws->visual; depth = ws->screen_depth; colormap = ws->colormap; } pixmap = XCreatePixmap(ws->display, ws->id, width, height, depth); gcv.foreground = 0x00000000; gc = XCreateGC(ws->display, pixmap, GCForeground, &gcv); XFillRectangle(ws->display, pixmap, gc, 0, 0, w, h); XFreeGC(ws->display, gc); groupsurface = cairo_xlib_surface_create(ws->display, pixmap, visual, (depth == 32)? CAIRO_FORMAT_ARGB32: CAIRO_FORMAT_RGB24, colormap); } } cairo_destroy(cr); cr = cairo_create(groupsurface); cairo_identity_matrix(cr); cairo_set_operator(cr, RENDER_OPERATOR_DEFAULT); list::iterator oit = operations.begin(); for (; oit != operations.end(); oit++) { cairo_save(cr); (*oit)->render(dwo, cr, groupsurface, width, height); cairo_restore(cr); } cache_surface = new CacheSurface(ws->display, groupsurface, pixmap, data, width, height, &dwo->sb->dynamic_groups); if (cacheable) addToCache(cache_surface, dwo); } if (crsurface) { cairo_destroy(cr); cr = cairo_create(crsurface); cairo_set_operator(cr, RENDER_OPERATOR_DEFAULT); if (xrop_set) cairo_set_operator(cr, xrop); cairo_translate(cr, x, y); cairo_rotate(cr, rotation); // XXX: I think I may've screwed this up, at least as to whether there // should be any clipping. -mjmt cairo_set_source_surface(cr, cache_surface->crsurface, 0., 0.); cairo_rectangle(cr, 0., 0., width, height); cairo_clip(cr); cairo_paint_with_alpha(cr, opacity); } if (return_surface) *return_surface = cache_surface; else cache_surface->unref(); } void RenderGroup::flattenTextOps(list **textopsptr) { if (is_a_style) { Style *s = (Style *) this; if (s->textops) { while (! s->textops->empty()) { s->textops->back()->unref(); s->textops->pop_back(); } delete s->textops; s->textops = NULL; } } list::iterator oit = operations.begin(); for (; oit != operations.end(); oit++) { if ((*oit)->type == RenderOpTextType) { if (! *textopsptr) *textopsptr = new list; (*textopsptr)->push_back((RenderOpText *) (*oit)->ref()); } else if ((*oit)->type == RenderOpGroupType) { ((RenderGroup *) *oit)->flattenTextOps(textopsptr); } } if (is_a_style) { if (((Style *) this)->shapemask) ((Style *) this)->shapemask->flattenTextOps(textopsptr); } } RenderOpDynamic::RenderOpDynamic(void) : RenderOp(RenderOpDynamicType) {} void RenderOpDynamic::applyAttributes(Parser *parser, Tst *attr) { rotation = _x = _y = _w = _h = gravity = gravity2 = INT_MAX; halign = HNullAlignType; valign = VNullAlignType; opacity = parser->attrGetDouble(attr, "opacity", INT_MAX); ((RenderOp *) this)->applyAttributes(parser, attr); } void RenderOpDynamic::render(DWindowObject *dwo, cairo_t *cr, cairo_surface_t *surface, unsigned int w, unsigned int h) { RenderGroup saved_attributes; RenderGroup set_attributes; RenderGroup *dynamic = NULL; list::iterator it = dynamic_order.begin(); for (; it != dynamic_order.end(); it++) { map::iterator dit = dwo->sb->dynamic_groups.find(*it); if (dit != dwo->sb->dynamic_groups.end()) { dynamic = (*dit).second; break; } } if (! dynamic) return; saved_attributes.inheritAttributes(dynamic); set_attributes.inheritAttributes(dynamic); if (_x != INT_MAX) { set_attributes._x = _x; set_attributes._xu = _xu; } if (_y != INT_MAX) { set_attributes._y = _y; set_attributes._yu = _yu; } if (_w != INT_MAX) { set_attributes._w = _w; set_attributes._wu = _wu; } if (_h != INT_MAX) { set_attributes._h = _h; set_attributes._hu = _hu; } if (x2_set) { set_attributes.x2_set = x2_set; set_attributes._x2 = _x2; set_attributes._x2u = _x2u; } if (y2_set) { set_attributes.y2_set = y2_set; set_attributes._y2 = _y2; set_attributes._y2u = _y2u; } if (gravity != INT_MAX) set_attributes.gravity = gravity; if (gravity2 != INT_MAX) set_attributes.gravity2 = gravity2; if (xrop_set) { set_attributes.xrop_set = xrop_set; set_attributes.xrop = xrop; } if (valign != VNullAlignType) set_attributes.valign = valign; if (halign != HNullAlignType) set_attributes.halign = halign; if (rotation != INT_MAX) set_attributes.rotation = rotation; if (opacity != INT_MAX) set_attributes.opacity = opacity; dynamic->inheritAttributes(&set_attributes); dynamic->render(dwo, cr, surface, w, h); dynamic->inheritAttributes(&saved_attributes); } PathOperator::PathOperator(PathOperatorType _type) : RefCounted(this) { type = _type; if (type == PathOperatorCloseType) size = 0; else if (type == PathOperatorCurveToType || type == PathOperatorRelCurveToType || type == PathOperatorArcToType || type == PathOperatorRelArcToType) size = 3; else size = 1; if (size) { _a = new double[size]; _b = new double[size]; _au = new LenghtUnitType[size]; _bu = new LenghtUnitType[size]; gravity = new int[size]; for (int i = 0; i < size; i++) { _a[i] = _b[i] = 0; _au[i] = _bu[i] = PXLenghtUnitType; gravity[i] = RENDER_GRAVITY_DEFAULT; } } } PathOperator::~PathOperator(void) { if (size) { delete [] _a; delete [] _b; delete [] _au; delete [] _bu; delete [] gravity; } } void PathOperator::applyAttributes(Parser *parser, Tst *attr) { switch (type) { case PathOperatorArcToType: case PathOperatorCurveToType: attributes_get_position(parser, attr, "x1", "y1", "gravity1", &_a[1], &_b[1], &_au[1], &_bu[1], &gravity[1]); attributes_get_position(parser, attr, "x2", "y2", "gravity2", &_a[2], &_b[2], &_au[2], &_bu[2], &gravity[2]); if (type == PathOperatorArcToType) { attributes_get_size(parser, attr, "radius", "radius", &_a[0], &_b[0], &_au[0], &_bu[0]); break; } case PathOperatorMoveToType: case PathOperatorLineToType: attributes_get_position_default(parser, attr, &_a[0], &_b[0], &_au[0], &_bu[0], &gravity[0]); break; case PathOperatorRelArcToType: case PathOperatorRelCurveToType: attributes_get_size(parser, attr, "width1", "height1", &_a[1], &_b[1], &_au[1], &_bu[1]); attributes_get_size(parser, attr, "width2", "height2", &_a[2], &_b[2], &_au[2], &_bu[2]); if (type == PathOperatorRelArcToType) { attributes_get_size(parser, attr, "radius", "radius", &_a[0], &_b[0], &_au[0], &_bu[0]); break; } case PathOperatorRelLineToType: case PathOperatorRelMoveToType: attributes_get_size_default(parser, attr, &_a[0], &_b[0], &_au[0], &_bu[0]); break; } } WaColorStop::WaColorStop(double _offset, WaColor *_color) : RefCounted(this) { color = _color; offset = _offset; } WaColorStop::~WaColorStop(void) { color->unref(); } static const struct FilterMap { char *name; cairo_filter_t filter; } filter_map[] = { { "fast", CAIRO_FILTER_FAST }, { "good", CAIRO_FILTER_GOOD }, { "best", CAIRO_FILTER_BEST }, { "nearest", CAIRO_FILTER_NEAREST }, { "bilinear", CAIRO_FILTER_BILINEAR }, { "gaussian", CAIRO_FILTER_GAUSSIAN } }; static const struct ExtendMap { char *name; cairo_extend_t extend; } extend_map[] = { { "none", CAIRO_EXTEND_NONE }, { "repeat", CAIRO_EXTEND_REPEAT }, { "reflect", CAIRO_EXTEND_REFLECT } }; static const struct PatternTypeMap { char *name; PatternType pattern_type; } pattern_type_map[] = { { "linear", LinearPatternType }, { "radial", RadialPatternType }, { "group", GroupPatternType } }; RenderPattern::RenderPattern(void) : RefCounted(this) { filter = RENDER_FILTER_DEFAULT; extend = RENDER_EXTEND_DEFAULT; type = LinearPatternType; start_gravity = end_gravity = RENDER_GRAVITY_DEFAULT; start_x = start_y = 0; end_x = end_y = 100; start_x_u = start_y_u = PXLenghtUnitType; end_x_u = end_y_u = PCTLenghtUnitType; center_gravity = RENDER_GRAVITY_DEFAULT; center_x = center_y = 50; radius_dx = radius_dy = 50; center_x_u = center_y_u = radius_dx_u = radius_dy_u = PCTLenghtUnitType; group = NULL; } RenderPattern::~RenderPattern(void) { clear(); if (group) group->unref(); } void RenderPattern::clear(void) { while (! color_stops.empty()) { color_stops.back()->unref(); color_stops.pop_back(); } } void RenderPattern::inheritContent(RenderPattern *inherit_pattern) { list::iterator it = inherit_pattern->color_stops.begin(); for (; it != inherit_pattern->color_stops.end(); it++) color_stops.push_back((*it)->ref()); } void RenderPattern::inheritAttributes(RenderPattern *inherit) { filter = inherit->filter; extend = inherit->extend; type = inherit->type; start_x = inherit->start_x; start_y = inherit->start_y; start_gravity = inherit->start_gravity; end_x = inherit->end_x; end_y = inherit->end_y; end_gravity = inherit->end_gravity; start_x_u = inherit->start_x_u; start_y_u = inherit->start_y_u; end_x_u = inherit->end_x_u; end_y_u = inherit->end_y_u; center_x = inherit->center_x; center_y = inherit->center_y; center_gravity = inherit->center_gravity; radius_dx = inherit->radius_dx; radius_dy = inherit->radius_dy; center_x_u = inherit->center_x_u; center_y_u = inherit->center_y_u; radius_dx_u = inherit->radius_dx_u; radius_dy_u = inherit->radius_dy_u; if (group) group->unref(); group = NULL; if (inherit->group) group = (RenderGroup *) inherit->group->ref(); } void RenderPattern::applyAttributes(Parser *parser, Tst *attr) { char *value = parser->attrGetString(attr, "inherit", NULL); if (value) { RenderPattern *inherit_pattern = parser->ws->getPatternNamed(value, false); if (inherit_pattern == this) { parser->warning("pattern %s cannot inherit itself\n", value); } else if (inherit_pattern) { inheritAttributes(inherit_pattern); } else { if (! parser->attrGetBool(attr, "ignore_missing", false)) parser->warning("undefined pattern: %s\n", value); } } value = parser->attrGetString(attr, "type", NULL); if (value) { Tst::iterator it = pattern_type_tst->find(value); if (it != pattern_type_tst->end()) type = *it; } attributes_get_position(parser, attr, "start_x", "start_y", "start_gravity", &start_x, &start_y, &start_x_u, &start_y_u, &start_gravity); attributes_get_position(parser, attr, "end_x", "end_y", "end_gravity", &end_x, &end_y, &end_x_u, &end_y_u, &end_gravity); attributes_get_position(parser, attr, "center_x", "center_y", "center_gravity", ¢er_x, ¢er_y, ¢er_x_u, ¢er_y_u, ¢er_gravity); attributes_get_size(parser, attr, "radius_dx", "radius_dy", &radius_dx, &radius_dy, &radius_dx_u, &radius_dy_u); value = parser->attrGetString(attr, "filter", NULL); if (value) { Tst::iterator it = filter_tst->find(value); if (it != filter_tst->end()) filter = *it; } value = parser->attrGetString(attr, "spread", NULL); if (value) { Tst::iterator it = extend_tst->find(value); if (it != extend_tst->end()) extend = *it; } value = parser->attrGetString(attr, "group", NULL); if (value) { group = parser->ws->getRenderGroupNamed(value, false); if (! group) { parser->warning("undefined group: %s\n", value); } else group = (RenderGroup *) group->ref(); } } void RenderPattern::setcairo_pattern(DWindowObject *dwo, cairo_t *cr, cairo_surface_t *crsurface, unsigned int w, unsigned int h) { cairo_pattern_t *pattern; switch (type) { case LinearPatternType: { double x0, y0, x1, y1; calc_position(start_gravity, start_x_u, start_y_u, w, h, dwo->ws->hdpi, dwo->ws->vdpi, start_x, start_y, &x0, &y0); calc_position(end_gravity, end_x_u, end_y_u, w, h, dwo->ws->hdpi, dwo->ws->vdpi, end_x, end_y, &x1, &y1); pattern = cairo_pattern_create_linear (x0, y0, x1, y1); } break; case RadialPatternType: { double cx, cy, rx, ry; calc_position(center_gravity, center_x_u, center_y_u, w, h, dwo->ws->hdpi, dwo->ws->vdpi, center_x, center_y, &cx, &cy); calc_length(radius_dx, radius_dx_u, dwo->ws->hdpi, w, &rx); calc_length(radius_dy, radius_dy_u, dwo->ws->vdpi, h, &ry); if (rx != ry) cx *= (ry / rx); pattern = cairo_pattern_create_radial (cx, cy, 0.0, cx, cy, ry); if (rx != ry) { cairo_matrix_t matrix; cairo_matrix_init_scale (&matrix, ry / rx, 1.0); cairo_pattern_set_matrix (pattern, &matrix); } } break; case GroupPatternType: { if (group) { cairo_save(cr); CacheSurface *subsurface = NULL; group->parent_surface = crsurface; group->return_surface = &subsurface; cairo_save(cr); group->render(dwo, cr, NULL, w, h); cairo_restore(cr); group->return_surface = NULL; group->parent_surface = NULL; cairo_identity_matrix(cr); cairo_move_to(cr, 0.0, 0.0); pattern = cairo_pattern_create_for_surface(subsurface->crsurface); subsurface->unref(); cairo_restore(cr); } else { cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); return; } } break; } int stops = 0; list::iterator it = color_stops.begin(); for (; it != color_stops.end(); ++it) { cairo_pattern_add_color_stop_rgba (pattern, (*it)->offset, (*it)->color->red, (*it)->color->green, (*it)->color->blue, (*it)->color->alpha); stops++; } if (stops == 0) { cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0.0, 0.0, 0.0, 1.0); cairo_pattern_add_color_stop_rgba (pattern, 1.0, 1.0, 1.0, 1.0, 1.0); } cairo_pattern_set_filter (pattern, filter); cairo_pattern_set_extend (pattern, extend); cairo_set_source (cr, pattern); cairo_pattern_destroy (pattern); } static const struct LineCapMap { char *name; cairo_line_cap_t line_cap; } line_cap_map[] = { { "butt", CAIRO_LINE_CAP_BUTT }, { "round", CAIRO_LINE_CAP_ROUND }, { "square", CAIRO_LINE_CAP_SQUARE } }; static const struct LineJoinMap { char *name; cairo_line_join_t line_join; } line_join_map[] = { { "miter", CAIRO_LINE_JOIN_MITER }, { "round", CAIRO_LINE_JOIN_ROUND }, { "bevel", CAIRO_LINE_JOIN_BEVEL } }; static const struct FillRuleMap { char *name; cairo_fill_rule_t fill_rule; } fill_rule_map[] = { { "winding", CAIRO_FILL_RULE_WINDING }, { "evenodd", CAIRO_FILL_RULE_EVEN_ODD } }; static const struct DrawingOrderMap { char *name; DrawingOrder order; } drawing_order_map[] = { { "fillstroke", FillStrokeDrawingOrder }, { "strokefill", StrokeFillDrawingOrder } }; RenderOpDraw::RenderOpDraw(RenderOpType _type) : RenderOp(_type) { linewidth = RENDER_LINE_WIDTH_DEFAULT; linewidth_u = PXLenghtUnitType; linejoin = RENDER_LINE_JOIN_DEFAULT; linecap = RENDER_LINE_CAP_DEFAULT; fillrule = RENDER_FILL_RULE_DEFAULT; tolerance = RENDER_TOLERANCE_DEFAULT; miterlimit = RENDER_MITER_LIMIT_DEFAULT; stroke_pattern = fill_pattern = NULL; dashes = NULL; dashes_u = NULL; dash_offset = 0.0; dash_offset_u = PXLenghtUnitType; ndash = 0; order = RENDER_DRAWING_ORDER_DEFAULT; fill = stroke = false; } RenderOpDraw::~RenderOpDraw(void) { if (fill_pattern) fill_pattern->unref(); if (stroke_pattern) stroke_pattern->unref(); if (dashes) delete [] dashes; if (dashes_u) delete [] dashes_u; } void RenderOpDraw::inheritAttributes(RenderOpDraw *inherit) { linewidth = inherit->linewidth; linewidth_u = inherit->linewidth_u; linejoin = inherit->linejoin; linecap = inherit->linecap; fillrule = inherit->fillrule; tolerance = inherit->tolerance; miterlimit = inherit->miterlimit; stroke_color = inherit->stroke_color; fill_color = inherit->fill_color; fill = inherit->fill; stroke = inherit->stroke; if (fill_pattern) fill_pattern->unref(); fill_pattern = NULL; if (inherit->fill_pattern) fill_pattern = inherit->fill_pattern->ref(); if (stroke_pattern) stroke_pattern->unref(); stroke_pattern = NULL; if (inherit->stroke_pattern) stroke_pattern = inherit->stroke_pattern->ref(); if (dashes) delete [] dashes; if (dashes_u) delete [] dashes_u; dashes = NULL; dashes_u = NULL; ndash = inherit->ndash; if (ndash) { dashes = new double[ndash]; dashes_u = new LenghtUnitType[ndash]; for (int i = 0; i < ndash; i++) { dashes[i] = inherit->dashes[i]; dashes_u[i] = inherit->dashes_u[i]; } } dash_offset = inherit->dash_offset; dash_offset_u = inherit->dash_offset_u; ((RenderOp *) this)->inheritAttributes((RenderOp *) inherit); } void RenderOpDraw::applyAttributes(Parser *parser, Tst *attr) { char *value; ((RenderOp *) this)->applyAttributes(parser, attr); linewidth = parser->attrGetLength(attr, "linewidth", linewidth, &linewidth_u); tolerance = parser->attrGetDouble(attr, "tolerance", tolerance); miterlimit = parser->attrGetDouble(attr, "miterlimit", miterlimit); value = parser->attrGetString(attr, "linecap", NULL); if (value) { Tst::iterator it = line_cap_tst->find(value); if (it != line_cap_tst->end()) linecap = *it; } value = parser->attrGetString(attr, "linejoin", NULL); if (value) { Tst::iterator it = line_join_tst->find(value); if (it != line_join_tst->end()) linejoin = *it; } value = parser->attrGetString(attr, "fillrule", NULL); if (value) { Tst::iterator it = fill_rule_tst->find(value); if (it != fill_rule_tst->end()) fillrule = *it; } value = parser->attrGetString(attr, "fill_pattern", NULL); if (value) { fill_pattern = parser->ws->getPatternNamed(value, false); if (! fill_pattern) { parser->warning("undefined pattern: %s\n", value); } else { fill = true; fill_pattern = (RenderPattern *) fill_pattern->ref(); } } value = parser->attrGetString(attr, "stroke_color", NULL); if (value) { stroke = true; if (! stroke_color.parseColor(parser->ws, value)) parser->warning("error parsing color: %s\n", value); } stroke_color.setOpacity( parser->attrGetDouble(attr, "stroke_opacity", stroke_color.getOpacity())); value = parser->attrGetString(attr, "stroke_pattern", NULL); if (value) { stroke_pattern = parser->ws->getPatternNamed(value, false); if (! stroke_pattern) { parser->warning("undefined pattern: %s\n", value); } else { stroke = true; stroke_pattern = (RenderPattern *) stroke_pattern->ref(); } } value = parser->attrGetString(attr, "fill_color", NULL); if (value) { fill = true; if (! fill_color.parseColor(parser->ws, value)) parser->warning("error parsing color: %s\n", value); } fill_color.setOpacity( parser->attrGetDouble(attr, "fill_opacity", fill_color.getOpacity())); value = parser->attrGetString(attr, "stroke_dasharray", NULL); if (value) { value = WA_STRDUP(value); ndash = 1; for (int i = 0; value[i] != '\0'; i++) { if (value[i] == ',') { ndash++; value[i] = '\0'; } } dashes = new double[ndash]; dashes_u = new LenghtUnitType[ndash]; char *dash = value; for (int i = 0; i < ndash; i++) { dashes[i] = get_double_and_unit(dash, &dashes_u[i]); dash += (strlen(dash) + 1); } delete [] value; dash_offset = parser->attrGetLength(attr, "stroke_dashoffset", dash_offset, &dash_offset_u); } value = parser->attrGetString(attr, "drawing_order", NULL); if (value) { Tst::iterator it = drawing_order_tst->find(value); if (it != drawing_order_tst->end()) order = *it; } } void RenderOpDraw::draw(DWindowObject *dwo, cairo_t *cr, cairo_surface_t *crsurface, unsigned int w, unsigned int h) { cairo_pattern_t *pattern; if (xrop_set) cairo_set_operator(cr, xrop); cairo_rotate(cr, rotation); cairo_set_tolerance(cr, tolerance); for (int i = 0; i < 2; i++) { if (order == FillStrokeDrawingOrder && i == 0 || order == StrokeFillDrawingOrder && i == 1) { if (fill) { cairo_save(cr); cairo_set_fill_rule(cr, fillrule); fill_color.setcairo_color(cr); if (fill_pattern) fill_pattern->setcairo_pattern(dwo, cr, NULL, w, h); //cairo_set_alpha(cr, fill_color.alpha); cairo_set_source_rgba(cr, fill_color.red, fill_color.green, fill_color.blue, fill_color.alpha); cairo_fill(cr); cairo_restore(cr); } } else { if (stroke) { double lwidth; calc_length(linewidth, linewidth_u, (dwo->ws->hdpi + dwo->ws->vdpi) / 2, w, &lwidth); cairo_set_line_width(cr, lwidth); cairo_set_line_join(cr, linejoin); cairo_set_line_cap(cr, linecap); cairo_set_miter_limit(cr, miterlimit); if (dashes) { double *pxdashes = new double[ndash]; double pxdash_offset; calc_length(dash_offset, dash_offset_u, (dwo->ws->hdpi + dwo->ws->vdpi) / 2, w, &pxdash_offset); for (int i = 0; i < ndash; i++) calc_length(dashes[i], dashes_u[i], (dwo->ws->hdpi + dwo->ws->vdpi) / 2, w, &pxdashes[i]); cairo_set_dash(cr, pxdashes, ndash, pxdash_offset); delete [] pxdashes; } cairo_save(cr); stroke_color.setcairo_color(cr); if (stroke_pattern) stroke_pattern->setcairo_pattern(dwo, cr, NULL, w, h); cairo_stroke(cr); cairo_restore(cr); } } } } RenderOpPath::RenderOpPath(char *_name) : RenderOpDraw(RenderOpPathType) { if (_name) name = WA_STRDUP(_name); else name = NULL; } RenderOpPath::~RenderOpPath(void) { clear(); } void RenderOpPath::clear(void) { while (! ops.empty()) { ops.back()->unref(); ops.pop_back(); } } void RenderOpPath::inheritContent(RenderOpPath *inherit_path) { list::iterator it = inherit_path->ops.begin(); for (; it != inherit_path->ops.end(); it++) ops.push_back((*it)->ref()); } void RenderOpPath::inheritAttributes(RenderOpPath *inherit) { ((RenderOpDraw *) this)->inheritAttributes((RenderOpDraw *) inherit); } void RenderOpPath::applyAttributes(Parser *parser, Tst *attr) { char *value = parser->attrGetString(attr, "inherit", NULL); if (value) { RenderOpPath *inherit_path = parser->ws->getPathNamed(value, false); if (inherit_path == this) { parser->warning("path %s cannot inherit itself\n", name); } else if (inherit_path) { inheritAttributes(inherit_path); } else { if (! parser->attrGetBool(attr, "ignore_missing", false)) parser->warning("undefined path: %s\n", value); } } ((RenderOpDraw *) this)->applyAttributes(parser, attr); } void RenderOpPath::render(DWindowObject *dwo, cairo_t *cr, cairo_surface_t *crsurface, unsigned int w, unsigned int h) { double a[3], b[3], x, y; double width, height; double vd = dwo->ws->vdpi; double hd = dwo->ws->hdpi; if (ops.empty()) return; calcPositionAndSize(w, h, hd, vd, &x, &y, &width, &height); cairo_translate(cr, x, y); cairo_new_path(cr); list::iterator it = ops.begin(); for (; it != ops.end(); it++) { switch ((*it)->type) { case PathOperatorMoveToType: (*it)->calcPosition(width, height, hd, vd, a, b); cairo_move_to(cr, *a, *b); break; case PathOperatorRelMoveToType: (*it)->calcSize(width, height, hd, vd, a, b); cairo_rel_move_to(cr, *a, *b); break; case PathOperatorLineToType: (*it)->calcPosition(width, height, hd, vd, a, b); cairo_line_to(cr, *a, *b); break; case PathOperatorRelLineToType: (*it)->calcSize(width, height, hd, vd, a, b); cairo_rel_line_to(cr, *a, *b); break; case PathOperatorCurveToType: (*it)->calcPosition(width, height, hd, vd, a, b); cairo_curve_to(cr, a[1], b[1], a[2], b[2], a[0], b[0]); break; case PathOperatorRelCurveToType: (*it)->calcSize(width, height, hd, vd, a, b); cairo_rel_curve_to(cr, a[1], b[1], a[2], b[2], a[0], b[0]); break; case PathOperatorArcToType: (*it)->calcPosition(width, height, hd, vd, a, b); /* NYI: not yet implemented in cairo cairo_arc_to(cr, a[1], b[1], a[2], b[2], a[0]); */ break; case PathOperatorRelArcToType: (*it)->calcSize(width, height, hd, vd, a, b); /* NYI: not yet implemented in cairo cairo_rel_arc_to(cr, a[1], b[1], a[2], b[2], a[0]); */ break; case PathOperatorCloseType: cairo_close_path(cr); break; } } cairo_move_to (cr, 0.0, 0.0); draw(dwo, cr, crsurface, WA_ROUND_U(width), WA_ROUND_U(height)); } void RenderOpLine::applyAttributes(Parser *parser, Tst *attr) { ((RenderOpDraw *) this)->applyAttributes(parser, attr); } void RenderOpLine::render(DWindowObject *dwo, cairo_t *cr, cairo_surface_t *crsurface, unsigned int w, unsigned int h) { double x, y, width, height; calcPositionAndSize(w, h, dwo->ws->hdpi, dwo->ws->vdpi, &x, &y, &width, &height); cairo_new_path(cr); cairo_move_to(cr, x, y); cairo_rel_line_to(cr, width, height); cairo_move_to (cr, 0.0, 0.0); draw(dwo, cr, crsurface, WA_ROUND_U(width), WA_ROUND_U(height)); } void RenderOpRectangle::applyAttributes(Parser *parser, Tst *attr) { ((RenderOpDraw *) this)->applyAttributes(parser, attr); nw_rx = parser->attrGetLength(attr, "rx", RENDER_DEFAULT_RECT_RX, &nw_rx_u); ne_rx = se_rx = sw_rx = nw_rx; ne_rx_u = se_rx_u = sw_rx_u = nw_rx_u; nw_ry = parser->attrGetLength(attr, "ry", RENDER_DEFAULT_RECT_RY, &nw_ry_u); ne_ry = se_ry = sw_ry = nw_ry; ne_ry_u = se_ry_u = sw_ry_u = nw_ry_u; nw_rx = parser->attrGetLength(attr, "northwest_rx", nw_rx, &nw_rx_u); nw_ry = parser->attrGetLength(attr, "northwest_ry", nw_ry, &nw_ry_u); ne_rx = parser->attrGetLength(attr, "northeast_rx", ne_rx, &ne_rx_u); ne_ry = parser->attrGetLength(attr, "northeast_ry", ne_ry, &ne_ry_u); sw_rx = parser->attrGetLength(attr, "southwest_rx", sw_rx, &sw_rx_u); sw_ry = parser->attrGetLength(attr, "southwest_ry", sw_ry, &sw_ry_u); se_rx = parser->attrGetLength(attr, "southeast_rx", se_rx, &se_rx_u); se_ry = parser->attrGetLength(attr, "southeast_ry", se_ry, &se_ry_u); } #define M_KAPPA 0.5522847498 void RenderOpRectangle::render(DWindowObject *dwo, cairo_t *cr, cairo_surface_t *crsurface, unsigned int w, unsigned int h) { double x, y, width, height; double vd = dwo->ws->vdpi; double hd = dwo->ws->hdpi; calcPositionAndSize(w, h, dwo->ws->hdpi, dwo->ws->vdpi, &x, &y, &width, &height); if (nw_rx && nw_ry || ne_rx && ne_ry || sw_rx && sw_ry || se_rx && se_ry) { double rx, ry, go_w, go_h; cairo_new_path(cr); cairo_move_to(cr, x, y); go_w = width; if (nw_rx && nw_ry) { calc_length(nw_rx, nw_rx_u, hd, width, &rx); calc_length(nw_ry, nw_ry_u, vd, height, &ry); cairo_rel_move_to(cr, 0.0, ry); cairo_rel_curve_to(cr, 0.0, -ry * M_KAPPA, rx - rx * M_KAPPA, -ry, rx, -ry); go_w -= rx; } go_h = height; if (ne_rx && ne_ry) { calc_length(ne_rx, ne_rx_u, hd, width, &rx); calc_length(ne_ry, ne_ry_u, vd, height, &ry); cairo_rel_line_to(cr, go_w - rx, 0.0); cairo_rel_curve_to(cr, rx * M_KAPPA, 0.0, rx, ry - ry * M_KAPPA, rx, ry); go_h -= ry; } else cairo_rel_line_to(cr, go_w, 0.0); go_w = width; if (se_rx && se_ry) { calc_length(se_rx, se_rx_u, hd, width, &rx); calc_length(se_ry, se_ry_u, vd, height, &ry); cairo_rel_line_to(cr, 0.0, go_h - ry); cairo_rel_curve_to(cr, 0.0, ry * M_KAPPA, -rx + rx * M_KAPPA, ry, -rx, ry); go_w -= rx; } else cairo_rel_line_to(cr, 0.0, go_h); if (sw_rx && sw_ry) { calc_length(sw_rx, sw_rx_u, hd, width, &rx); calc_length(sw_ry, sw_ry_u, vd, height, &ry); cairo_rel_line_to(cr, -(go_w - rx), 0.0); cairo_rel_curve_to(cr, -rx * M_KAPPA, 0.0, -rx, -ry + ry * M_KAPPA, -rx, -ry); } else cairo_rel_line_to(cr, -go_w, 0.0); cairo_close_path(cr); } else cairo_rectangle(cr, x, y, width, height); cairo_translate(cr, x, y); cairo_move_to (cr, 0.0, 0.0); draw(dwo, cr, crsurface, WA_ROUND_U(width), WA_ROUND_U(height)); } static const struct WeightMap { char *name; cairo_font_weight_t weight; } weight_map[] = { { "normal", CAIRO_FONT_WEIGHT_NORMAL }, { "bold", CAIRO_FONT_WEIGHT_BOLD } }; static const struct SlantMap { char *name; cairo_font_slant_t slant; } slant_map[] = { { "normal", CAIRO_FONT_SLANT_NORMAL }, { "italic", CAIRO_FONT_SLANT_ITALIC }, { "oblique", CAIRO_FONT_SLANT_OBLIQUE } }; RenderOpText::RenderOpText(char *_name) : RenderOpDraw(RenderOpTextType) { shadow_x_offset = shadow_y_offset = 0.0; size = RENDER_FONT_SIZE_DEFAULT; size_u = _wu = _hu = PXLenghtUnitType; shadow = dynamic_area_width = dynamic_area_height = false; _w = _h = 0.0; line_spacing = RENDER_LINE_SPACING_DEFAULT; line_spacing_u = PXLenghtUnitType; tab_spacing = RENDER_TAB_SPACE_DEFAULT; tab_spacing_u = PXLenghtUnitType; left_spacing = right_spacing = top_spacing = bottom_spacing = 0.0; left_spacing_u = right_spacing_u = top_spacing_u = bottom_spacing_u = PXLenghtUnitType; text_halign = LeftAlignType; utf8 = NULL; is_static = false; family = WA_STRDUP(RENDER_FONT_KEY_DEFAULT); font = NULL; slant = RENDER_FONT_SLANT_DEFAULT; weight = RENDER_FONT_WEIGHT_DEFAULT; bg_group = NULL; if (_name) name = WA_STRDUP(_name); else name = NULL; } RenderOpText::~RenderOpText(void) { delete [] family; if (name) delete [] name; if (bg_group) bg_group->unref(); clear(); if (font) cairo_font_face_destroy(font); } void RenderOpText::clear(void) { if (utf8) delete [] utf8; utf8 = NULL; } void RenderOpText::inheritAttributes(RenderOpText *inherit) { shadow_x_offset = inherit->shadow_x_offset; shadow_y_offset = inherit->shadow_y_offset; size = inherit->size; size_u = inherit->size_u; _wu = inherit->_wu; _hu = inherit->_hu; shadow = inherit->shadow; dynamic_area_width = inherit->dynamic_area_width; dynamic_area_height = inherit->dynamic_area_height; _w = inherit->_w; _h = inherit->_h; line_spacing = inherit->line_spacing; line_spacing_u = inherit->line_spacing_u; tab_spacing = inherit->tab_spacing; tab_spacing_u = inherit->tab_spacing_u; left_spacing = inherit->left_spacing; right_spacing = inherit->right_spacing; top_spacing = inherit->top_spacing; bottom_spacing = inherit->bottom_spacing; left_spacing_u = inherit->left_spacing_u; right_spacing_u = inherit->right_spacing_u; top_spacing_u = inherit->top_spacing_u; bottom_spacing_u = inherit->bottom_spacing_u; text_halign = inherit->text_halign; family = WA_STRDUP((char *) inherit->family); slant = inherit->slant; weight = inherit->weight; if (bg_group) bg_group->unref(); bg_group = NULL; if (inherit->bg_group) bg_group = (RenderGroup *) inherit->bg_group->ref(); ((RenderOpDraw *) this)->inheritAttributes((RenderOpDraw *) inherit); } void RenderOpText::applyAttributes(Parser *parser, Tst *attr) { char *value = parser->attrGetString(attr, "inherit", NULL); if (value) { RenderOpText *inherit_text = parser->ws->getTextNamed(value, false); if (inherit_text == this) { parser->warning("text object %s cannot inherit itself\n", name); } else if (inherit_text) { inheritAttributes(inherit_text); } else { if (! parser->attrGetBool(attr, "ignore_missing", false)) parser->warning("undefined text object: %s\n", value); } } attributes_get_alignment(parser, attr, "text_halign", NULL, &text_halign, NULL); value = parser->attrGetString(attr, "bg_group", NULL); if (value) { bg_group = parser->ws->getRenderGroupNamed(value, false); if (! bg_group) { parser->warning("undefined group: %s\n", value); } else bg_group = (RenderGroup *) bg_group->ref(); } is_static = parser->attrGetBool(attr, "static", is_static); dynamic_area_width = parser->attrGetBool(attr, "dynamic_area_width", dynamic_area_width); dynamic_area_height = parser->attrGetBool(attr, "dynamic_area_height", dynamic_area_height); size = parser->attrGetLength(attr, "size", size, &size_u); tab_spacing = parser->attrGetLength(attr, "tab_spacing", tab_spacing, &tab_spacing_u); line_spacing = parser->attrGetLength(attr, "line_spacing", line_spacing, &line_spacing_u); left_spacing = parser->attrGetLength(attr, "left_spacing", left_spacing, &left_spacing_u); right_spacing = parser->attrGetLength(attr, "right_spacing", right_spacing, &right_spacing_u); top_spacing = parser->attrGetLength(attr, "top_spacing", top_spacing, &top_spacing_u); bottom_spacing = parser->attrGetLength(attr, "bottom_spacing", bottom_spacing, &bottom_spacing_u); shadow = parser->attrGetBool(attr, "shadow", shadow); if (shadow) { shadow_x_offset = parser->attrGetDouble(attr, "shadow_hoffset", shadow_x_offset); shadow_y_offset = parser->attrGetDouble(attr, "shadow_voffset", shadow_y_offset); value = parser->attrGetString(attr, "shadow_color", NULL); if (value) { if (! shadow_color.parseColor(parser->ws, value)) parser->warning("error parsing color: %s\n", value); } shadow_color.setOpacity( parser->attrGetDouble(attr, "shadow_opacity", shadow_color.getOpacity())); } value = parser->attrGetString(attr, "weight", NULL); if (value) { Tst::iterator it = weight_tst->find(value); if (it != weight_tst->end()) weight = *it; } value = parser->attrGetString(attr, "slant", NULL); if (value) { Tst::iterator it = slant_tst->find(value); if (it != slant_tst->end()) slant = *it; } value = parser->attrGetString(attr, "font", NULL); if (value) { delete [] family; family = WA_STRDUP(value); } ((RenderOpDraw *) this)->applyAttributes(parser, attr); } void RenderOpText::calcLinePosition(double width, unsigned int region_width, unsigned int region_height, double *return_x) { int textgravity = NorthWestGravity; switch (text_halign) { case RightAlignType: textgravity = NorthEastGravity; break; case HCenterAlignType: textgravity = CenterGravity; break; default: break; } double d; calc_position_and_size(text_halign, TopAlignType, textgravity, PXLenghtUnitType, PXLenghtUnitType, PXLenghtUnitType, PXLenghtUnitType, region_width, region_height, 1.0, 1.0, WA_ROUND_U(width), 1.0, 0.0, 0.0, return_x, &d, NULL, NULL); if (*return_x < 0.0) *return_x = 0.0; } void RenderOpText::render(DWindowObject *dwo, cairo_t *cr, cairo_surface_t *crsurface, unsigned int w, unsigned int h) { char *text; double x, y, width = 0.0, height = 0.0, font_size, tab_space, line_space, left_space, right_space, top_space, bottom_space, stroke_size; LenghtUnitType width_u = PXLenghtUnitType, height_u = PXLenghtUnitType; list lines; CacheSurface *subsurface = NULL; if (! utf8) return; calc_length(size, size_u, dwo->ws->hdpi, w, &font_size); calc_length(tab_spacing, tab_spacing_u, dwo->ws->hdpi, w, &tab_space); calc_length(line_spacing, line_spacing_u, dwo->ws->vdpi, h, &line_space); calc_length(left_spacing, left_spacing_u, dwo->ws->hdpi, w, &left_space); calc_length(right_spacing, right_spacing_u, dwo->ws->hdpi, w, &right_space); calc_length(top_spacing, top_spacing_u, dwo->ws->vdpi, h, &top_space); calc_length(bottom_spacing, bottom_spacing_u, dwo->ws->vdpi, h, &bottom_space); cairo_identity_matrix(cr); if (!font) { cairo_select_font_face (cr, family, slant, weight); /* font = cairo_current_font(cr); if (!font) return; cairo_font_reference (font); */ } else cairo_set_font_face (cr, font); cairo_set_font_size (cr, font_size); if (is_static) text = utf8; else { map::iterator it = dwo->sb->text_table->find(this); if (it != dwo->sb->text_table->end()) text = (*it).second; else text = utf8; } double space_width = -1.0; double line_height = 0.0, y_pos = 0.0, y_off; char *textptr = text; unsigned int end_offset = 0; unsigned int linenr = 0; cairo_surface_t *text_region = NULL; bool end_of_lines = false; do { RenderLineInfo *linfo = new RenderLineInfo; linfo->width = 0.0; linenr++; do { cairo_text_extents_t extents; char *str; double str_width, str_height, str_y_pos; RenderStringInfo *sinfo = new RenderStringInfo; textptr += end_offset; for (end_offset = 0; textptr[end_offset] != '\n' && textptr[end_offset] != '\t' && textptr[end_offset] != '\0'; end_offset++); str = new char[end_offset + 1]; strncpy(str, textptr, end_offset); str[end_offset] = '\0'; sinfo->text = str; cairo_text_extents(cr, sinfo->text, &extents); str_y_pos = extents.height - extents.y_bearing; str_width = extents.width; str_height = extents.height; if (str_height > line_height) line_height = str_height; if (str_y_pos > y_pos) y_pos = str_y_pos; sinfo->x = linfo->width; linfo->width += str_width; linfo->strings.push_back(sinfo); if (textptr[end_offset] == '\t') { double tab, with_space; if (space_width < 0.0) { cairo_text_extents(cr, "1 1", &extents); with_space = extents.width; cairo_text_extents(cr, "11", &extents); space_width = with_space - extents.width; } tab = ceil((linfo->width + space_width) / tab_space); if (tab == ((linfo->width + space_width) / tab_space)) tab += 1.0; linfo->width = tab * tab_space; } if (textptr[end_offset] == '\0') end_of_lines = true; } while (textptr[end_offset++] == '\t'); lines.push_back(linfo); } while (! end_of_lines); list::iterator it = lines.begin(); for (; it != lines.end(); it++) if ((*it)->width > width) width = (*it)->width; height = y_pos + y_pos + top_space + bottom_space + 1.0; height += (linenr - 1) * line_height + (linenr - 1) * line_space; width += (left_space + right_space + 2.0); if (shadow) { width += shadow_x_offset; height += shadow_y_offset; } if (_w) { if (! dynamic_area_width) { width = _w; width_u = _wu; } else { double max_w; calc_length(_w, _wu, dwo->ws->hdpi, w, &max_w); if (width > max_w) { width = _w; width_u = _wu; } } } if (_h) { if (! dynamic_area_height) { height = _h; height_u = _hu; } else { double max_h; calc_length(_h, _hu, dwo->ws->vdpi, h, &max_h); if (height > max_h) { height = _h; height_u = _hu; } } } calc_position_and_size(halign, valign, gravity, _xu, _yu, width_u, height_u, w, h, dwo->ws->hdpi, dwo->ws->vdpi, width, height, _x, _y, &x, &y, &width, &height); if (x2_set || y2_set) { double tmp_x2, tmp_y2; calc_position(gravity2, _x2u, _y2u, w, h, dwo->ws->hdpi, dwo->ws->vdpi, _x2, _y2, &tmp_x2, &tmp_y2); if (x2_set) width = tmp_x2 - x; if (y2_set) height = tmp_y2 - y; } if (width <= 0 || height <= 0) goto bail; cairo_save(cr); text_region = cairo_surface_create_similar( cairo_get_target(cr), CAIRO_CONTENT_COLOR_ALPHA, (unsigned int) ceil(width), (unsigned int) ceil(height)); cairo_destroy(cr); cr = cairo_create(crsurface); if (bg_group) { bg_group->parent_surface = cairo_get_target(cr); bg_group->return_surface = &subsurface; cairo_save(cr); bg_group->render(dwo, cr, NULL, (unsigned int) ceil(width), (unsigned int) ceil(height)); cairo_restore(cr); bg_group->return_surface = NULL; bg_group->parent_surface = NULL; if (subsurface) { cairo_pattern_t *pattern; cairo_save(cr); pattern = cairo_pattern_create_for_surface(subsurface->crsurface); cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); cairo_set_source(cr, pattern); cairo_pattern_destroy(pattern); cairo_rectangle(cr, 0.0, 0.0, ceil(width), ceil(height)); cairo_fill(cr); cairo_restore(cr); subsurface->unref(); } } y_off = -line_spacing; y_off += top_space; it = lines.begin(); for (; it != lines.end(); it++) { double pos_x; y_off += line_spacing; calcLinePosition((*it)->width, (unsigned int) ceil(width), (unsigned int) ceil(height), &pos_x); list::iterator sit = (*it)->strings.begin(); for (; sit != (*it)->strings.end(); sit++) { if (stroke || fill_pattern) { cairo_move_to(cr, (int) (left_space + (*sit)->x + pos_x), (int) (y_off + y_pos)); cairo_text_path(cr, (*sit)->text); cairo_move_to(cr, 0.0, 0.0); draw(dwo, cr, crsurface, WA_ROUND_U(width), WA_ROUND_U(height)); } else { if (shadow) { shadow_color.setcairo_color(cr); cairo_move_to(cr, (int) (left_space + (*sit)->x + pos_x + shadow_x_offset), (int) (y_off + y_pos + shadow_y_offset)); cairo_show_text(cr, (*sit)->text); } fill_color.setcairo_color(cr); cairo_move_to(cr, (int) (left_space + (*sit)->x + pos_x), (int) (y_off + y_pos)); cairo_show_text(cr, (*sit)->text); } } y_off += line_height; } cairo_restore(cr); if (x || y) cairo_translate(cr, x, y); if (xrop_set) cairo_set_operator(cr, xrop); if (rotation) cairo_rotate(cr, rotation); cairo_set_source_surface(cr, text_region, 0., 0.); cairo_rectangle(cr, 0., 0., ceil(width), ceil(height)); cairo_clip(cr); cairo_paint(cr); cairo_surface_destroy(text_region); bail: while (! lines.empty()) { while (! lines.back()->strings.empty()) { delete [] lines.back()->strings.back()->text; delete lines.back()->strings.back(); lines.back()->strings.pop_back(); } delete lines.back(); lines.pop_back(); } } RenderOpFill::RenderOpFill(RenderOpType _type) : RenderOp(_type) { fillrule = RENDER_FILL_RULE_DEFAULT; } void RenderOpFill::applyAttributes(Parser *parser, Tst *attr) { ((RenderOp *) this)->applyAttributes(parser, attr); } void RenderOpSolid::applyAttributes(Parser *parser, Tst *attr) { ((RenderOpFill *) this)->applyAttributes(parser, attr); char *value = parser->attrGetString(attr, "color", NULL); if (value) { if (! color.parseColor(parser->ws, value)) parser->warning("error parsing color: %s\n", value); } color.setOpacity(parser->attrGetDouble(attr, "opacity", color.getOpacity())); } void RenderOpSolid::render(DWindowObject *dwo, cairo_t *cr, cairo_surface_t *, unsigned int w, unsigned int h) { double x, y, width, height; calcPositionAndSize(w, h, dwo->ws->hdpi, dwo->ws->vdpi, &x, &y, &width, &height); if (color.alpha == 1.0) cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); if (xrop_set) cairo_set_operator(cr, xrop); color.setcairo_color(cr); cairo_rectangle(cr, x, y, width, height); cairo_rotate(cr, rotation); cairo_set_fill_rule(cr, fillrule); cairo_fill(cr); } RenderOpImage::RenderOpImage(void) : RenderOpFill(RenderOpImageType) { scale = RENDER_IMAGE_SCALE_DEFAULT; image = NULL; filter = RENDER_FILTER_DEFAULT; } RenderOpImage::~RenderOpImage(void) { if (image) image->unref(); } static const struct ImageScaleTypeMap { char *name; ImageScaleType type; } image_scale_type_map[] = { { "scaled", ImageNormalScaleType }, { "tiled", ImageTileScaleType } }; bool RenderOpImage::applyAttributes(Parser *parser, Tst *attr) { int image_width, image_height; unsigned char *rgba = NULL; ((RenderOpFill *) this)->applyAttributes(parser, attr); char *value = parser->attrGetString(attr, "src", NULL); if (value) { char *filename = smartfile(value, parser->filename, false); if (filename) { rgba = read_image_to_rgba(filename, &image_width, &image_height); if (rgba) image = parser->ws->rgbaToWaSurface(rgba, image_width, image_height); delete [] filename; } else { parser->warning("unable to open image file: %s\n", value); return false; } if (! image) { parser->warning("failed loading image: %s\n", value); return false; } } else { parser->warning("required attribute=src is missing\n"); return false; } value = parser->attrGetString(attr, "scaletype", NULL); if (value) { Tst::iterator it = image_scale_type_tst->find(value); if (it != image_scale_type_tst->end()) scale = *it; } value = parser->attrGetString(attr, "filter", NULL); if (value) { Tst::iterator it = filter_tst->find(value); if (it != filter_tst->end()) filter = *it; } return true; } void RenderOpImage::render(DWindowObject *dwo, cairo_t *cr, cairo_surface_t *, unsigned int w, unsigned int h) { double x, y, width, height; WaSurface *img; cairo_matrix_t *matrix; cairo_pattern_t *pattern; if (image) img = image->ref(); else return; calcPositionAndSize(w, h, dwo->ws->hdpi, dwo->ws->vdpi, &x, &y, &width, &height); switch (scale) { case ImageNormalScaleType: cairo_identity_matrix(cr); if (width != img->width || height != img->height) { if (xrop_set) cairo_set_operator(cr, xrop); cairo_operator_t op = cairo_get_operator(cr); cairo_surface_t *target = cairo_get_target(cr); cairo_surface_t *scaled = cairo_surface_create_similar(target, CAIRO_CONTENT_COLOR_ALPHA, (int) width + 2, (int) height + 2); cairo_destroy(cr); cr = cairo_create(scaled); cairo_scale(cr, (double) width / (img->width - ((img->width > 1)? 1: 0)), (double) height / (img->height - ((img->height > 1)? 1: 0))); cairo_pattern_t *img_pattern = cairo_pattern_create_for_surface(img->crsurface); cairo_pattern_set_filter(img_pattern, filter); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_set_source(cr, img_pattern); cairo_rectangle(cr, 0., 0., img->width + 1, img->height + 1); cairo_fill(cr); cairo_identity_matrix(cr); cairo_pattern_destroy(img_pattern); cairo_destroy(cr); cr = cairo_create(target); cairo_translate(cr, x, y); cairo_set_operator(cr, op); cairo_set_source_surface(cr, scaled, 0., 0.); cairo_rectangle(cr, 0., 0., width + 2., height + 2.); cairo_fill(cr); cairo_surface_destroy(scaled); } else { if (xrop_set) cairo_set_operator(cr, xrop); cairo_translate(cr, x, y); cairo_rotate(cr, rotation); cairo_set_source_surface(cr, img->crsurface, 0., 0.); cairo_rectangle(cr, 0., 0., img->width + 1., img->height + 1.); cairo_fill(cr); } break; case ImageTileScaleType: { cairo_identity_matrix(cr); cairo_translate(cr, x, y); cairo_rectangle(cr, 0, 0, width, height); cairo_move_to(cr, 0.0, 0.0); cairo_identity_matrix(cr); pattern = cairo_pattern_create_for_surface(img->crsurface); cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); cairo_set_source(cr, pattern); cairo_pattern_destroy(pattern); if (xrop_set) cairo_set_operator(cr, xrop); cairo_fill(cr); //cairo_surface_set_repeat(img->crsurface, false); } break; } img->unref(); } #ifdef SVG RenderOpSvg::RenderOpSvg(void) : RenderOpFill(RenderOpSvgType) { cairo_svg = NULL; } RenderOpSvg::~RenderOpSvg(void) { if (cairo_svg) svg_cairo_destroy(cairo_svg); } bool RenderOpSvg::applyAttributes(Parser *parser, Tst *attr) { ((RenderOpFill *) this)->applyAttributes(parser, attr); char *value = parser->attrGetString(attr, "file", NULL); if (value) { char *filename = smartfile(value, parser->filename, false); if (filename) { FILE *file = fopen(filename, "r"); if (file) { svg_cairo_status_t status; status = svg_cairo_create(&cairo_svg); if (status == SVG_CAIRO_STATUS_SUCCESS) { status = svg_cairo_parse_file(cairo_svg, file); if (status == SVG_CAIRO_STATUS_SUCCESS) { return true; } else { parser->warning("parsing of svg file %s failed\n", value); } } else { parser->warning("creation of svg context failed\n"); } fclose(file); } delete [] filename; } else parser->warning("unable to open svg file: %s\n", value); } else parser->warning("missing required attribute=file'\n"); return false; } void RenderOpSvg::render(DWindowObject *dwo, cairo_t *cr, cairo_surface_t *, unsigned int w, unsigned int h) { double x, y, width, height; unsigned int svg_w, svg_h; calcPositionAndSize(w, h, dwo->ws->hdpi, dwo->ws->vdpi, &x, &y, &width, &height); cairo_save(cr); if (xrop_set) cairo_set_operator(cr, xrop); if (rotation) cairo_rotate(cr, rotation); cairo_translate(cr, x, y); svg_cairo_get_size(cairo_svg, &svg_w, &svg_h); cairo_scale(cr, width / (double) svg_w, height / (double) svg_h); svg_cairo_set_viewport_dimension(cairo_svg, (unsigned int) width, (unsigned int) height); if (svg_cairo_render(cairo_svg, cr) != SVG_CAIRO_STATUS_SUCCESS) { dwo->ws->showWarningMessage(__FUNCTION__, "svg rendering failed"); } cairo_restore(cr); } #endif // SVG void calc_length(double value, LenghtUnitType unit, double dpi, double parent_length, double *return_value) { switch (unit) { case PXLenghtUnitType: *return_value = value; break; case CMLenghtUnitType: *return_value = (value / 2.54) * dpi; break; case MMLenghtUnitType: *return_value = (value / 25.4) * dpi; break; case INLenghtUnitType: *return_value = value * dpi; break; case PTLenghtUnitType: *return_value = (value / 72.0) * dpi; break; case PCLenghtUnitType: *return_value = (value / 6.0) * dpi; break; case PCTLenghtUnitType: *return_value = (value * parent_length) / 100.0; break; } } void calc_position(int gravity, LenghtUnitType _xu, LenghtUnitType _yu, double parent_w, double parent_h, double hdpi, double vdpi, double _x, double _y, double *return_x, double *return_y) { double x, y; calc_length(_x, _xu, hdpi, parent_w, &x); calc_length(_y, _yu, vdpi, parent_h, &y); switch (gravity) { case NorthEastGravity: x = parent_w - x; break; case NorthGravity: x = parent_w / 2 + x; break; case SouthEastGravity: x = parent_w - x; y = parent_h - y; break; case SouthWestGravity: y = parent_h - y; break; case SouthGravity: x = parent_w / 2 + x; y = parent_h - y; break; case EastGravity: x = parent_w - x; y = parent_h / 2 + y; break; case WestGravity: y = parent_h / 2 + y; break; default: break; } *return_x = x; *return_y = y; } void calc_position_and_size(HorizontalAlignment halign, VerticalAlignment valign, int gravity, LenghtUnitType _xu, LenghtUnitType _yu, LenghtUnitType _wu, LenghtUnitType _hu, double parent_w, double parent_h, double hdpi, double vdpi, double _w, double _h, double _x, double _y, double *return_x, double *return_y, double *return_w, double *return_h) { double x, y, w, h; calc_length(_w, _wu, hdpi, parent_w, &w); calc_length(_h, _hu, vdpi, parent_h, &h); calc_length(_x, _xu, hdpi, parent_w, &x); calc_length(_y, _yu, vdpi, parent_h, &y); switch (gravity) { case NorthEastGravity: x = parent_w - x; break; case NorthGravity: x = parent_w / 2 + x; break; case SouthEastGravity: x = parent_w - x; y = parent_h - y; break; case SouthWestGravity: y = parent_h - y; break; case SouthGravity: x = parent_w / 2 + x; y = parent_h - y; break; case EastGravity: x = parent_w - x; y = parent_h / 2 + y; break; case WestGravity: y = parent_h / 2 + y; break; case CenterGravity: x = parent_w / 2 + x; y = parent_h / 2 + y; break; default: break; } switch (halign) { case RightAlignType: x -= w; break; case HCenterAlignType: x -= w / 2; break; default: break; } switch (valign) { case BottomAlignType: y -= h; break; case VCenterAlignType: y -= h / 2; break; default: break; } *return_x = x; *return_y = y; if (return_w) *return_w = w; if (return_h) *return_h = h; } unsigned char *read_image_to_rgba(char *filename, int *image_width, int *image_height) { FILE *fp; bool status = false; unsigned char *rgba = NULL; UNUSED_VARIABLE(image_width); UNUSED_VARIABLE(image_height); if ((fp = fopen(filename, "rb")) == NULL) { WARNING << "`" << filename << "' "; perror(NULL); return NULL; } #ifdef PNG if (! status) { status = check_if_png(fp); rewind(fp); if (status) rgba = read_png_to_rgba(fp, image_width, image_height); } #endif // PNG if (! status) WARNING << "`" << filename << "' unsupported image format" << endl; fclose(fp); return rgba; } static const struct GravityTypeMap { char *name; int gravity; } gravity_type_map[] = { { "north", NorthGravity }, { "northwest", NorthWestGravity }, { "northeast", NorthEastGravity }, { "south", SouthGravity }, { "southwest", SouthWestGravity }, { "southeast", SouthEastGravity }, { "west", WestGravity }, { "east", SouthGravity }, { "center", CenterGravity } }; static const struct HorizontalAlignmentMap { char *name; HorizontalAlignment align; } horizontal_alignment_map[] = { { "left", LeftAlignType }, { "right", RightAlignType }, { "center", HCenterAlignType } }; static const struct VerticalAlignmentMap { char *name; VerticalAlignment align; } vertical_alignment_map[] = { { "top", TopAlignType }, { "bottom", BottomAlignType }, { "center", VCenterAlignType } }; static const struct OperatorMap { char *name; cairo_operator_t crop; } operator_map[] = { { "clear", CAIRO_OPERATOR_CLEAR }, { "src", CAIRO_OPERATOR_SOURCE }, { "dst", CAIRO_OPERATOR_DEST }, { "dst_over", CAIRO_OPERATOR_DEST_OVER }, { "dst_in", CAIRO_OPERATOR_DEST_IN }, { "dst_out", CAIRO_OPERATOR_DEST_OUT }, { "dst_atop", CAIRO_OPERATOR_DEST_ATOP }, { "over", CAIRO_OPERATOR_OVER }, { "in", CAIRO_OPERATOR_IN }, { "out", CAIRO_OPERATOR_OUT }, { "atop", CAIRO_OPERATOR_ATOP }, { "xor", CAIRO_OPERATOR_XOR }, { "add", CAIRO_OPERATOR_ADD }, { "saturate", CAIRO_OPERATOR_SATURATE } }; void attributes_get_position(Parser *parser, Tst *attr, char *xname, char *yname, char *gname, double *x, double *y, LenghtUnitType *_xu, LenghtUnitType *_yu, int *g) { double tmp = parser->attrGetLength(attr, xname, INT_MIN, _xu); if (tmp != INT_MIN) *x = tmp; tmp = parser->attrGetLength(attr, yname, INT_MIN, _yu); if (tmp != INT_MIN) *y = tmp; char *value = parser->attrGetString(attr, gname, NULL); if (value) { Tst::iterator it = gravity_type_tst->find(value); if (it != gravity_type_tst->end()) *g = *it; } } void attributes_get_size(Parser *parser, Tst *attr, char *wname, char *hname, double *w, double *h, LenghtUnitType *_wu, LenghtUnitType *_hu) { double tmp = parser->attrGetLength(attr, wname, INT_MIN, _wu); if (tmp != INT_MIN) *w = tmp; tmp = parser->attrGetLength(attr, hname, INT_MIN, _hu); if (tmp != INT_MIN) *h = tmp; } void attributes_get_alignment(Parser *parser, Tst *attr, char *haname, char *vaname, HorizontalAlignment *ha, VerticalAlignment *va) { char *value; if (ha) { value = parser->attrGetString(attr, haname, NULL); if (value) { Tst::iterator it = horizontal_alignment_tst->find(value); if (it != horizontal_alignment_tst->end()) *ha = *it; } } if (va) { value = parser->attrGetString(attr, vaname, NULL); if (value) { Tst::iterator it = vertical_alignment_tst->find(value); if (it != vertical_alignment_tst->end()) *va = *it; } } } void attributes_get_area(Parser *parser, Tst *attr, char *xname, char *yname, char *gname, char *x2name, char *y2name, char *g2name, char *wname, char *hname, char *haname, char *vaname, double *x, double *y, LenghtUnitType *_xu, LenghtUnitType *_yu, int *g, double *x2, double *y2, LenghtUnitType *_x2u, LenghtUnitType *_y2u, int *g2, bool *x2set, bool *y2set, double *w, double *h, LenghtUnitType *_wu, LenghtUnitType *_hu, HorizontalAlignment *ha, VerticalAlignment *va) { double tmp_x2 = INT_MIN, tmp_y2 = INT_MIN; attributes_get_position(parser, attr, xname, yname, gname, x, y, _xu, _yu, g); attributes_get_position(parser, attr, x2name, y2name, g2name, &tmp_x2, &tmp_y2, _x2u, _y2u, g2); if (tmp_x2 != INT_MIN) { *x2 = tmp_x2; *x2set = true; } if (tmp_y2 != INT_MIN) { *y2 = tmp_y2; *y2set = true; } attributes_get_size(parser, attr, wname, hname, w, h, _wu, _hu); attributes_get_alignment(parser, attr, haname, vaname, ha, va); } void attributes_get_render(Parser *parser, Tst *attr, cairo_operator_t *o, bool *opset, double *r) { char *value = parser->attrGetString(attr, "operator", NULL); if (value) { Tst::iterator it = operator_tst->find(value); if (it != operator_tst->end()) { *o = *it; *opset = true; } } double angle = parser->attrGetDouble(attr, "rotation", (double) -INT_MAX); if (angle != (double) -INT_MAX) *r = angle / (M_PI * 2.0); } void render_create_tsts(void) { int size, i; filter_tst = new Tst; size = sizeof(filter_map) / sizeof(FilterMap); for (i = 0; i < size; i++) filter_tst->insert(filter_map[i].name, filter_map[i].filter); extend_tst = new Tst; size = sizeof(extend_map) / sizeof(ExtendMap); for (i = 0; i < size; i++) extend_tst->insert(extend_map[i].name, extend_map[i].extend); pattern_type_tst = new Tst; size = sizeof(pattern_type_map) / sizeof(PatternTypeMap); for (i = 0; i < size; i++) pattern_type_tst->insert(pattern_type_map[i].name, pattern_type_map[i].pattern_type); line_cap_tst = new Tst; size = sizeof(line_cap_map) / sizeof(LineCapMap); for (i = 0; i < size; i++) line_cap_tst->insert(line_cap_map[i].name, line_cap_map[i].line_cap); line_join_tst = new Tst; size = sizeof(line_join_map) / sizeof(LineJoinMap); for (i = 0; i < size; i++) line_join_tst->insert(line_join_map[i].name, line_join_map[i].line_join); fill_rule_tst = new Tst; size = sizeof(fill_rule_map) / sizeof(FillRuleMap); for (i = 0; i < size; i++) fill_rule_tst->insert(fill_rule_map[i].name, fill_rule_map[i].fill_rule); drawing_order_tst = new Tst; size = sizeof(drawing_order_map) / sizeof(DrawingOrderMap); for (i = 0; i < size; i++) drawing_order_tst->insert(drawing_order_map[i].name, drawing_order_map[i].order); weight_tst = new Tst; size = sizeof(weight_map) / sizeof(WeightMap); for (i = 0; i < size; i++) weight_tst->insert(weight_map[i].name, weight_map[i].weight); slant_tst = new Tst; size = sizeof(slant_map) / sizeof(SlantMap); for (i = 0; i < size; i++) slant_tst->insert(slant_map[i].name, slant_map[i].slant); image_scale_type_tst = new Tst; size = sizeof(image_scale_type_map) / sizeof(ImageScaleTypeMap); for (i = 0; i < size; i++) image_scale_type_tst->insert(image_scale_type_map[i].name, image_scale_type_map[i].type); gravity_type_tst = new Tst; size = sizeof(gravity_type_map) / sizeof(GravityTypeMap); for (i = 0; i < size; i++) gravity_type_tst->insert(gravity_type_map[i].name, gravity_type_map[i].gravity); horizontal_alignment_tst = new Tst; size = sizeof(horizontal_alignment_map) / sizeof(HorizontalAlignmentMap); for (i = 0; i < size; i++) horizontal_alignment_tst->insert(horizontal_alignment_map[i].name, horizontal_alignment_map[i].align); vertical_alignment_tst = new Tst; size = sizeof(vertical_alignment_map) / sizeof(VerticalAlignmentMap); for (i = 0; i < size; i++) vertical_alignment_tst->insert(vertical_alignment_map[i].name, vertical_alignment_map[i].align); operator_tst = new Tst; size = sizeof(operator_map) / sizeof(OperatorMap); for (i = 0; i < size; i++) operator_tst->insert(operator_map[i].name, operator_map[i].crop); }