/* OpenIDA - Outil d'analyse de fichiers binaires * gbuffersegment.c - concentration d'un fragment de caractères aux propriétés communes * * Copyright (C) 2010 Cyrille Bagard * * This file is part of OpenIDA. * * OpenIDA 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 3 of the License, or * (at your option) any later version. * * OpenIDA 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 Foobar. If not, see . */ #include "gbuffersegment.h" #include #include #include "../common/fnv1a.h" /* Utilisation du champ pixel des couleurs cachées */ #define COLOR_NOT_SET 0 #define COLOR_SET 1 /* Fragment de caractères aux propriétés communes (instance) */ struct _GBufferSegment { GObject parent; /* A laisser en premier */ char *text; /* Texte brut conservé */ fnv64_t hash; /* Empreinte pour comparaisons */ PangoAttrList *attribs; /* Propriétés du rendu */ SegRenderingStyle style; /* Apparence du segment */ GdkColor cache_bg; /* Fond d'impression */ GdkColor cache_fg; /* Couleur d'impression #1 */ GdkColor cache_alt_fg; /* Couleur d'impression #2 */ GdkColor *cache_used_fg; /* Couleur d'impression utile */ PangoGlyphString *glyphs; /* Caractères traités */ PangoFontDescription *desc; /* Description de police */ PangoRectangle logical; /* Dimension du texte */ }; /* Fragment de caractères aux propriétés communes (classe) */ struct _GBufferSegmentClass { GObjectClass parent; /* A laisser en premier */ PangoGlyphString *ascii_glyphs; /* Caractères ASCII prêts */ PangoFont *ascii_font; /* Police utilisée pour ASCII */ bool ascii_ready; /* Utilisation possible ? */ bool ascii_init_done; /* Initialisation tentée ? */ }; /* Procède à l'initialisation d'une classe de fragment de texte. */ static void g_buffer_segment_class_init(GBufferSegmentClass *); /* Procède à l'initialisation d'un fragment de texte. */ static void g_buffer_segment_init(GBufferSegment *); static bool ascii_glyph_table_init(GBufferSegmentClass *class, PangoContext *context) { gint i; /* Boucle de parcours */ char ascii_chars[128]; /* Table de caractères ASCII */ PangoAttrList *attribs; /* Liste d'attributs (vide) */ GList *list; /* Liste d'éléments distincts */ if (!class->ascii_init_done) { class->ascii_init_done = true; /* Construction d'une chaîne adéquate */ for (i = 0; i < 128; ++i) switch (i) { case 0 ... 31: ascii_chars[i] = '?'; break; case 32 ... 127: ascii_chars[i] = i; break; default: ascii_chars[i] = '?'; break; } /* Analyse de la chaîne créée */ attribs = pango_attr_list_new(); list = pango_itemize(context, ascii_chars, 0, 128, attribs, NULL); class->ascii_ready = (list != NULL && list->next == NULL); if (class->ascii_ready) { PangoItem *item; //int width; item = (PangoItem *)list->data; //width = gui.char_width * PANGO_SCALE; /* Remember the shape engine used for ASCII. */ //default_shape_engine = item->analysis.shape_engine; class->ascii_font = item->analysis.font; g_object_ref(class->ascii_font); class->ascii_glyphs = pango_glyph_string_new(); pango_shape(ascii_chars, 128, &item->analysis, class->ascii_glyphs); class->ascii_ready = (class->ascii_glyphs->num_glyphs == 128); #if 0 for (i = 0; i < class->ascii_glyphs->num_glyphs; i++) { PangoGlyphGeometry *geom; geom = &class->ascii_glyphs->glyphs[i].geometry; //geom->x_offset += MAX(0, width - geom->width) / 2; //geom->width = /*width*/8 * PANGO_SCALE; } #endif } //g_list_foreach(list, (GFunc)&pango_item_free, NULL); //g_list_free(list); //pango_attr_list_unref(attribs); } return class->ascii_ready; } /****************************************************************************** * * * Paramètres : context = contexte Pango pour l'analyse des caractères. * * attribs = propriétés de la zone de texte. * * text = chaîne de caractères à traiter. * * length = quantité de ces caractères. * * * * Description : Crée un nouveau fragment de texte avec des propriétés. * * * * Retour : Composant GTK créé. * * * * Remarques : - * * * ******************************************************************************/ static void g_buffer_segment_prepare(GBufferSegment *segment, PangoContext *context, PangoAttrList *attribs, const char *text, size_t length) { PangoGlyphString *glyphs; /* Caractères traités */ bool must_use_pango; /* Passage par Pango obligé ? */ PangoAttrIterator *iterator; /* Guide de parcours */ PangoAttribute *attrib; /* Attribut générique */ GList *item_list; PangoItem *item; //PangoRectangle logical; const char *max; const char *iter; GBufferSegmentClass *class; size_t i; PangoGlyphInfo *info; gint *log_clusters; PangoGlyphInfo *ref; PangoFont *font; /* Modèle de police */ PangoFontDescription *desc; /* Description de cette police */ glyphs = pango_glyph_string_new(); /* Existe-t-il des attributs particuliers ? */ must_use_pango = false; iterator = pango_attr_list_get_iterator(attribs); attrib = pango_attr_iterator_get(iterator, PANGO_ATTR_WEIGHT); must_use_pango |= (attrib != NULL); attrib = pango_attr_iterator_get(iterator, PANGO_ATTR_STYLE); must_use_pango |= (attrib != NULL); pango_attr_iterator_destroy(iterator); if (must_use_pango) goto not_ascii; /** * Petite astuce empruntée à Vim... * (cf. src/gui_gtk_x11.c, fonction gui_gtk2_draw_string()). * On essaie de traiter à la main les morceaux de * texte. Pour ceux en ASCII pur, le gain est non négligeable. */ max = text + length; goto not_ascii; for (iter = text; iter < max; iter++) if (*iter & 0x80) goto not_ascii; class = G_BUFFER_SEGMENT_GET_CLASS(segment); if (!ascii_glyph_table_init(class, context)) goto not_ascii; pango_glyph_string_set_size(glyphs, length); info = glyphs->glyphs; log_clusters = glyphs->log_clusters; ref = class->ascii_glyphs->glyphs; for (i = 0; i < length; i++) { info[i] = ref[(unsigned int)text[i]]; log_clusters[i] = i; } font = class->ascii_font; goto next; not_ascii: item_list = pango_itemize(context, text, 0, length, attribs, NULL); /* if (!(item_list != NULL && item_list->next == NULL)) printf("ouich\n"); */ item = (PangoItem *)item_list->data; pango_shape(text, length, &item->analysis, glyphs); font = item->analysis.font; next: desc = pango_font_describe(font); segment->desc = pango_font_description_copy(desc); pango_glyph_string_extents(glyphs, font, NULL, &segment->logical); //pango_shape(text, length, &item->analysis, glyphs); //pango_glyph_string_extents(glyphs, class->ascii_font, NULL, &segment->logical); segment->logical.y /= PANGO_SCALE; segment->logical.width /= PANGO_SCALE; segment->logical.height /= PANGO_SCALE; segment->glyphs = glyphs; } /* Détermine le type du fragment de caractères aux propriétés communes. */ G_DEFINE_TYPE(GBufferSegment, g_buffer_segment, G_TYPE_OBJECT); /****************************************************************************** * * * Paramètres : class = classe de composant GTK à initialiser. * * * * Description : Procède à l'initialisation d'une classe de fragment de texte.* * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_buffer_segment_class_init(GBufferSegmentClass *class) { } /****************************************************************************** * * * Paramètres : segment = composant GTK à initialiser. * * * * Description : Procède à l'initialisation d'un fragment de texte. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_buffer_segment_init(GBufferSegment *segment) { } /****************************************************************************** * * * Paramètres : attribs = propriétés de la zone de texte. * * text = chaîne de caractères à traiter. * * length = quantité de ces caractères. * * * * Description : Crée un nouveau fragment de texte avec des propriétés. * * * * Retour : Composant GTK créé. * * * * Remarques : - * * * ******************************************************************************/ GBufferSegment *g_buffer_segment_new(PangoAttrList *attribs, const char *text, size_t length) { GBufferSegment *result; /* Composant à retourner */ result = g_object_new(G_TYPE_BUFFER_SEGMENT, NULL); result->text = strdup(text); result->hash = fnv_64a_hash(text); result->attribs = pango_attr_list_ref(attribs); g_buffer_segment_prepare(result, get_global_pango_context(), attribs, text, length); g_buffer_segment_cache_colors(result); g_buffer_segment_set_style(result, SRS_CLASSIC); return result; } /****************************************************************************** * * * Paramètres : segment = fragment de texte à consulter. * * ref = segment de référence servant à la comparaison. * * * * Description : Indique si les textes de deux segments sont identiques. * * * * Retour : Bilan de la comparaison. * * * * Remarques : - * * * ******************************************************************************/ bool g_buffer_segment_compare(const GBufferSegment *segment, const GBufferSegment *ref) { bool result; /* Bilan à retourner */ result = cmp_fnv_64a(segment->hash, ref->hash); result &= (strcmp(segment->text, ref->text) == 0); return result; } /****************************************************************************** * * * Paramètres : segment = fragment de texte à consulter. * * * * Description : Fournit le texte brut conservé dans le segment. * * * * Retour : Texte conservé en interne. * * * * Remarques : - * * * ******************************************************************************/ const char *g_buffer_segment_get_text(const GBufferSegment *segment) { return segment->text; } /****************************************************************************** * * * Paramètres : segment = fragment de texte à consulter. * * * * Description : Fournit la quantité de pixels requise pour l'impression. * * * * Retour : Largeur requise par la colonne, en pixel. * * * * Remarques : - * * * ******************************************************************************/ gint g_buffer_segment_get_width(const GBufferSegment *segment) { return segment->logical.width; } /****************************************************************************** * * * Paramètres : segment = fragment de texte à manipuler. * * * * Description : (Re)charge les couleurs à partir de la liste d'attributs. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_buffer_segment_cache_colors(GBufferSegment *segment) { PangoAttrIterator *iterator; /* Guide de parcours */ PangoAttribute *attrib; /* Attribut générique */ PangoAttrColor *color_attrib; /* Propriété de couleur */ iterator = pango_attr_list_get_iterator(segment->attribs); /* Couleur d'impression */ attrib = pango_attr_iterator_get(iterator, PANGO_ATTR_FOREGROUND); segment->cache_fg.pixel = (attrib != NULL ? COLOR_SET : COLOR_NOT_SET); if (segment->cache_fg.pixel == COLOR_SET) { color_attrib = (PangoAttrColor *)attrib; segment->cache_fg.red = color_attrib->color.red; segment->cache_fg.green = color_attrib->color.green; segment->cache_fg.blue = color_attrib->color.blue; } pango_attr_iterator_destroy(iterator); } /****************************************************************************** * * * Paramètres : segment = fragment de texte à manipuler. * * style = style de rendu pour le segment. * * * * Description : Module l'apparence finale du composant. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_buffer_segment_set_style(GBufferSegment *segment, SegRenderingStyle style) { segment->style = style; switch (style) { default: case SRS_CLASSIC: segment->cache_used_fg = &segment->cache_fg; break; case SRS_HIGHLIGHT_SAME: segment->cache_bg.red = 0; segment->cache_bg.green = 0; segment->cache_bg.blue = 65535; segment->cache_alt_fg.red = 65535 - segment->cache_fg.red; segment->cache_alt_fg.green = 65535 - segment->cache_fg.green; segment->cache_alt_fg.blue = 65535 - segment->cache_fg.blue; segment->cache_used_fg = &segment->cache_alt_fg; break; } } /****************************************************************************** * * * Paramètres : segment = fragment de texte à manipuler. * * cairo = contexte graphique à utiliser pour les pinceaux. * * fcache = gestionnaire des polices pour l'impression. * * x = abscisse du point d'impression (à maj). [OUT] * * y = ordonnée du point d'impression. * * * * Description : Imprime le fragment de texte représenté. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_buffer_segment_draw(GBufferSegment *segment, cairo_t *cairo, GFontCache *fcache, gint *x, gint y) { PangoFont *font; /* Police d'impression */ /* FIXME */ g_buffer_segment_prepare(segment, get_global_pango_context(), segment->attribs, segment->text, strlen(segment->text)); /* Fond du texte */ if (segment->style != SRS_CLASSIC) { cairo_set_source_rgb(cairo, segment->cache_bg.red / 65535.0, segment->cache_bg.green / 65535.0, segment->cache_bg.blue / 65535.0); cairo_rectangle(cairo, *x, y, segment->logical.width, segment->logical.height); cairo_fill(cairo); } /* Couleur d'impression */ if (segment->cache_fg.pixel == COLOR_SET) cairo_set_source_rgb(cairo, segment->cache_used_fg->red / 65535.0, segment->cache_used_fg->green / 65535.0, segment->cache_used_fg->blue / 65535.0); /* Impression du texte */ cairo_move_to(cairo, *x, y - segment->logical.y); font = g_font_cache_lookup(fcache, segment->desc); pango_cairo_show_glyph_string(cairo, font, segment->glyphs); *x += segment->logical.width; }