/* 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 */
PangoFont *font; /* Police utilisée à l'analyse */
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;
char *max;
char *iter;
GBufferSegmentClass *class;
size_t i;
PangoGlyphInfo *info;
gint *log_clusters;
PangoGlyphInfo *ref;
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);
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;
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[text[i]];
log_clusters[i] = i;
}
segment->font = class->ascii_font;
pango_glyph_string_extents(glyphs, class->ascii_font, NULL, &segment->logical);
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);
segment->font = item->analysis.font;/* TODO : ref ! */
pango_glyph_string_extents(glyphs, item->analysis.font, NULL, &segment->logical);
next:
//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 : 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 : - *
* *
******************************************************************************/
GBufferSegment *g_buffer_segment_new(PangoContext *context, PangoAttrList *attribs, const char *text, size_t length)
{
GBufferSegment *result; /* Composant à retourner */
result = g_object_new(G_TYPE_BUFFER_SEGMENT, NULL);
//result = g_new(GBufferSegment, 1);
result->text = strdup(text);
result->hash = fnv_64a_hash(text);
result->attribs = pango_attr_list_ref(attribs);
g_buffer_segment_prepare(result, 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. *
* drawable = surface de rendu où travailler. *
* gc = contexte graphique à utiliser pour les pinceaux. *
* 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, GdkDrawable *drawable, GdkGC *gc, gint *x, gint y)
{
/* Fond du texte */
if (segment->style != SRS_CLASSIC)
{
gdk_gc_set_rgb_fg_color(gc, &segment->cache_bg);
gdk_draw_rectangle(drawable, gc, TRUE,
*x, y, segment->logical.width, segment->logical.height);
}
/* Couleur d'impression */
if (segment->cache_fg.pixel == COLOR_SET)
gdk_gc_set_rgb_fg_color(gc, segment->cache_used_fg);
/* Impression du texte */
gdk_draw_glyphs(drawable, gc, segment->font,
*x, y - segment->logical.y, segment->glyphs);
*x += segment->logical.width;
}