GtkApplicationを使用します。 Librsvgレンダリング機能

記事の要約。





以前、GTK +ハブには、例でvoid gtk_main(void)関数を使用する記事(私の記事ではありません)がありました。 GtkApplicationクラスを使用すると、application_activateおよびapplication_shutdownコールバック関数を明示的に強調表示できます。 gtk_mainを使用すると、gtk_main_quitを明示的にフックして、クロスをクリックしたときにアプリケーションが終了するようにする必要があります。 GtkApplicationは、十字をクリックすることでアプリケーションを終了しますが、これはより論理的です。 アプリケーションフレームワーク自体は、main.h、Makefile、string.gresource.xml、main.cで構成されます。



main.h



#ifndef MAIN_H #define MAIN_H #include <gtk/gtk.h> typedef struct{ GtkApplication *restrict app; GtkWidget *restrict win; GtkBuilder *restrict builder; }appdata; appdata data; appdata *data_ptr; #endif
      
      





メイクファイル



ここではユニバーサルで、特定のファイル名を指定せずにすべてのソースファイルをコンパイルできますが、フォルダーに余分なファイルがある場合、コンパイラーは誓います。

CC = g ++ -std = c ++ 11を使用することもできますが、コールバック関数を追加します

extern "C"。



 CC = gcc -std=c99 PKGCONFIG = $(shell which pkg-config) CFLAGS = $(shell $(PKGCONFIG) --cflags gio-2.0 gtk+-3.0 librsvg-2.0) -rdynamic -O3 LIBS = $(shell $(PKGCONFIG) --libs gio-2.0 gtk+-3.0 gmodule-2.0 librsvg-2.0 epoxy) -lm GLIB_COMPILE_RESOURCES = $(shell $(PKGCONFIG) --variable=glib_compile_resources gio-2.0) SRC = $(wildcard *.c) GEN = gresources.c BIN = main ALL = $(GEN) $(SRC) OBJS = $(ALL:.c=.o) all: $(BIN) gresources.c: string.gresource.xml $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=. --generate-dependencies string.gresource.xml) $(GLIB_COMPILE_RESOURCES) string.gresource.xml --target=$@ --sourcedir=. --generate-source %.o: %.c $(CC) $(CFLAGS) -c -o $(@F) $< $(BIN): $(OBJS) $(CC) -o $(@F) $(OBJS) $(LIBS) clean: @rm -f $(GEN) $(OBJS) $(BIN)
      
      





string.gresource.xml



実行可能ファイル(この場合はwindow.gladeインターフェース記述ファイル)にリソースを含めるのに役立ちます



 <?xml version="1.0" encoding="UTF-8"?> <gresources> <gresource prefix="/com/example/YourApp"> <file preprocess="xml-stripblanks" compressed="true">window.glade</file> </gresource> </gresources>
      
      





main.c



 #include "main.h" GtkBuilder* builder_init(void) { GError *error = NULL; data.builder = gtk_builder_new(); if (!gtk_builder_add_from_resource (data.builder, "/com/example/YourApp/window.glade", &error)) { //     g_critical ("   : %s", error->message); g_error_free (error); } gtk_builder_connect_signals (data.builder,NULL); return data.builder; } void application_activate(GtkApplication *application, gpointer user_data) { GtkBuilder *builder=builder_init(); data_ptr=&data; data.win=GTK_WIDGET(gtk_builder_get_object(builder, "window1")); gtk_widget_set_size_request(data.win,360,240); gtk_application_add_window(data.app,GTK_WINDOW(data.win)); gtk_widget_show_all(data.win); } void application_shutdown(GtkApplication *application, gpointer user_data) { g_object_unref(data.builder); } int main (int argc, char *argv[]) { gtk_init (&argc, &argv); gint res; data.app = gtk_application_new("gtk.org", G_APPLICATION_FLAGS_NONE); g_signal_connect(data.app, "activate", G_CALLBACK(application_activate), NULL); g_signal_connect(data.app, "shutdown", G_CALLBACK(application_shutdown), NULL); res = g_application_run(G_APPLICATION(data.app), 0, NULL); return 0; }
      
      





gtk_application_new関数の最初の引数には、任意のテキストを配置できますが、ドットなしでは機能しませんでした。 この例では、Glade UIエディターで作成できるwindow.gladeファイルも省略しています。



GtkBoxコンテナを含むウィンドウを2つの部分に分割し、一方にはGtkDrawingAreaをもう一方には配置します。







その結果、appdataは変更されます



 typedef struct{ GtkApplication *restrict app; GtkWidget *restrict win; GtkBuilder *restrict builder; GtkDrawingArea *restrict draw; GtkImage *restrict image; GtkEventBox *restrict eventbox1; RsvgHandle *restrict svg_handle_image; RsvgHandle *restrict svg_handle_svg; GdkPixbuf *pixbuf; cairo_t *restrict cr; cairo_surface_t *restrict surf; }appdata;
      
      





そしてそれに応じて初期化。



 void application_activate(GtkApplication *application, gpointer user_data) { GtkBuilder *builder=builder_init(); data_ptr=&data; data.win=GTK_WIDGET(gtk_builder_get_object(builder, "window1")); data.draw=GTK_DRAWING_AREA(gtk_builder_get_object(builder, "drawingarea1")); data.image=GTK_IMAGE(gtk_builder_get_object(builder, "image1")); gtk_widget_set_size_request(data.win,640,480); gtk_application_add_window(data.app,GTK_WINDOW(data.win)); gtk_widget_show_all(data.win); }
      
      





パス#include <librsvg-2.0 / librsvg / rsvg.h>を追加します。 (librsvgおよびlibrsvg-devパッケージをインストールする必要があります)。



コールバック関数の名前は.gladeファイルから取得され、関数がこれを担当します

gtk_builder_connect_signals(data.builder、NULL);



 gboolean drawingarea1_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer user_data) { if(!data.svg_handle_svg) {data.svg_handle_svg=rsvg_handle_new_from_file("compassmarkings.svg",NULL);} gboolean result=rsvg_handle_render_cairo(data.svg_handle_svg,cr); if(result&&cr) {cairo_stroke(cr);} else printf(" \n"); return FALSE; }
      
      





状況によっては(HMIなど)、SVGのサイズを変更する必要がある場合があります。 できる

SVGファイルの幅と高さのパラメーターを変更します。 または、GtkPixbufに転送し、既にスケーリングを行っています。 GtkImageはGtkBinから継承されないため、独自のButtonClickタイプのイベント(カーソル関連のイベント)を持つことはできません。 これには空のコンテナGtkEventBoxがあります。 また、描画自体をGtkImageに直接掛けることができます。



 gboolean image1_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer user_data) { if(!data.svg_handle_image) { data.svg_handle_image=rsvg_handle_new_from_file("compassmarkings.svg",NULL); data.surf=cairo_image_surface_create_from_png("2.png"); data.pixbuf=rsvg_handle_get_pixbuf(data.svg_handle_image); } if(data.pixbuf) { cairo_set_source_surface(cr,data.surf,0,0); GdkPixbuf *dest=gdk_pixbuf_scale_simple (data.pixbuf,250,250,GDK_INTERP_BILINEAR); gtk_image_set_from_pixbuf (data.image,dest); g_object_unref(dest); cairo_paint(cr); } }
      
      





この関数は、背景画像(2.png)を読み込みます。これは、ほとんどの場合、

透明ピクセルを使用した1x1の描画。 そして、この表面に描画(pixbuf)がレンダリングされ、画像(image)へのズームとエクスポートが行われます。



そして、メモリをクリアすることを忘れないでください。



 void application_shutdown(GtkApplication *application, gpointer user_data) { cairo_surface_destroy(data.surf); g_object_unref(data.svg_handle_image); g_object_unref(data.svg_handle_svg); g_object_unref(data.pixbuf); g_object_unref(data.builder); }
      
      





結果は次のとおりです。





SVGパラメータで幅と高さの小さな値が設定されている場合、PNGにエクスポートするときに画像がぼやけることがあります。



プログラムで幅と高さを変更することもできます。 このために、個別のファイルを作成しました

svg_to_pixbuf_class.cおよびsvg_to_pixbuf_class.h。 つまり、ファイルは、変化する幅、高さで開きます。



/ dev / shm /に保存されます。 情報をsvg_handleにエクスポートした後、ファイル自体とファイルへの行パスを削除する必要があります。 小数の幅/長さの値もサポートされています。



svg_to_pixbuf_class.c
 #include <string.h> #include <stdlib.h> #include <stdio.h> #include <gtk/gtk.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <math.h> #include <stdbool.h> int char_to_digit(char num) { switch(num) { case '0': return 0; case '1': return 1; case '2': return 2; case '3': return 3; case '4': return 4; case '5': return 5; case '6': return 6; case '7': return 7; case '8': return 8; case '9': return 9; case '.': return -1; default: return -2; } } //     text double read_num_in_text(char* text) { double result=0; int i=0; bool fractional_flag=FALSE; char whole_part[16]={0}; char whole_digits=0; char fractional_part[16]={0}; char fractional_digits=0; while(char_to_digit(text[i])!=-2) { if(char_to_digit(text[i])!=-1&&!fractional_flag) { whole_part[whole_digits]=char_to_digit(text[i]); printf("text_num=%d|%c\n",char_to_digit(text[i]),text[i]); ++whole_digits; ++i; } else { if(char_to_digit(text[i])==-1) { printf("fractional flag is true\n"); fractional_flag=TRUE; ++i; } else { fractional_part[fractional_digits]=char_to_digit(text[i]); ++fractional_digits; printf("frac_digit=%d|%c\n",char_to_digit(text[i]),text[i]); ++i; } } } ///    i=whole_digits; result=whole_part[whole_digits]; while(i>0) { --i; printf("whole=%d\n",whole_part[i]); result=result+pow(10,whole_digits-i-1)*whole_part[i]; } i=0; while(i<=fractional_digits) { result=result+pow(0.1,i+1)*fractional_part[i]; ++i; } printf("result_read_num=%lf\n",result); return result; } //  ,    // int count_of_digits_for_delete(char* text) { int i=0; bool fractional_flag=FALSE; char whole_part[16]={0}; int whole_digits=0; char fractional_part[16]={0}; int fractional_digits=0; while(char_to_digit(text[i])!=-2) { if(char_to_digit(text[i])!=-1&&!fractional_flag) { whole_part[whole_digits]=char_to_digit(text[i]); printf("text_num=%d|%c\n",char_to_digit(text[i]),text[i]); ++whole_digits; ++i; } else { if(char_to_digit(text[i])==-1) { printf("fractional flag is true\n"); fractional_flag=TRUE; ++i; } else { fractional_part[fractional_digits]=char_to_digit(text[i]); ++fractional_digits; printf("frac_digit=%d|%c\n",char_to_digit(text[i]),text[i]); ++i; } } } if(fractional_flag) return whole_digits+1+fractional_digits; else return whole_digits; } //      /dev/shm //      char* create_dump_file(char *file_with_path) { char *file=NULL; int i=0; while(file_with_path[i]!='\0') {++i;} while(file_with_path[i]!='/'&&i>0) {--i;} file=file_with_path+i; GString *string=g_string_new("test -f /dev/shm"); g_string_append(string,file); g_string_append(string,"|| touch /dev/shm/"); g_string_append(string,file); system(string->str); ///  -  GString *full_path=g_string_new("/dev/shm"); g_string_append(full_path,file); char *result=g_string_free(full_path,FALSE); return result; } //result must be freed with g_string_free GString* read_file_in_buffer(char *file_with_path) { FILE *input = NULL; struct stat buf; int fh, result; char *body=NULL; // GString *resultat=g_string_new(""); fh=open(file_with_path, O_RDONLY); result=fstat(fh, &buf); if (result !=0) printf("  \n"); else { printf("%s",file_with_path); printf(" : %ld\n", buf.st_size); printf(" : %lu\n", buf.st_dev); printf(" : %s", ctime(&buf.st_atime)); input = fopen(file_with_path, "r"); if (input == NULL) { printf("Error opening file"); } body=(char*)calloc(buf.st_size+64,sizeof(char)); //    //    if(body==NULL) { printf("      body\n"); } int size_count=fread(body,sizeof(char),buf.st_size, input); if(size_count!=buf.st_size) printf("   "); resultat=g_string_append(resultat,body); free(body); } fclose(input); return resultat; } void* write_string_to_file(char* writed_file, char* str_for_write, int lenght) { FILE * ptrFile = fopen (writed_file ,"wb"); size_t writed_byte_count=fwrite(str_for_write,1,lenght,ptrFile); //if(writed_byte_count>4) return TRUE; //else return FALSE; fclose(ptrFile); } //      g_free char* get_resized_svg(char *file_with_path, int width, int height) { char *writed_file=create_dump_file(file_with_path); //       GString *body=read_file_in_buffer(file_with_path); char *start_search=NULL; char *end_search=NULL; char *width_start=NULL; char *width_end=NULL; char *height_start=NULL; char *height_end=NULL; start_search=strstr(body->str,"<svg"); int j=0; //   if(start_search) { end_search=strstr(start_search,">"); if(end_search) { ///  width width_start=strstr(start_search,"width"); width_end=width_start+strlen("width"); ///   width    while(width_end[j]==0x0A||width_end[j]==0x20) ++j; if(width_end[j]=='=') ++j; while(width_end[j]==0x0A||width_end[j]==0x20) ++j; if(width_end[j]!='"') printf("   svg.     width=%c\n",width_end[j]); else ++j; ///  ///  ,   gssize size=count_of_digits_for_delete(width_end+j); ///   (1  - 1 ) gssize pos=width_end+j-body->str; ///       g_string_erase(body,pos,size); char width_new[8]; g_snprintf(width_new,8,"%d",width); g_string_insert(body, pos, width_new); ///  height height_start=strstr(start_search,"height"); height_end=height_start+strlen("height"); ///   height    j=0; while(height_end[j]==0x0A||height_end[j]==0x20) ++j; if(height_end[j]=='=') ++j; while(height_end[j]==0x0A||height_end[j]==0x20) ++j; if(height_end[j]!='"') printf("   svg. \    height=%c%c%c\n",height_end[j-1],height_end[j],height_end[j+1]); else ++j; ///  ///  ,   size=count_of_digits_for_delete(height_end+j); ///   (1  - 1 ) pos=height_end+j-body->str; ///       g_string_erase(body,pos,size); char height_new[8]; g_snprintf(height_new,8,"%d",height); g_string_insert(body, pos, height_new); ///      dev/shm/ ///   write_string_to_file(writed_file,body->str,strlen(body->str)); return writed_file; //g_free(writed_file); g_string_free(body,TRUE); } else printf(" :      svg"); } } void resized_svg_free(char *path) { if (remove (path)==-1 ) { printf("    %s\n",path); } }
      
      







svg_to_pixbuf_class.h
 #ifndef SVG_TO_PIXBUF_CLASS_H #define SVG_TO_PIXBUF_CLASS_H void resized_svg_free(char *path); char* get_resized_svg(char *file_with_path, int width, int height); //result must be freed with g_free() #endif
      
      







左側のサイズを変更します(GtkDrawingArea)

 gboolean drawingarea1_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer user_data) { if(!data.svg_handle_svg) { char* path=get_resized_svg("/home/alex/svg_habr/compassmarkings.svg", 220, 220); data.svg_handle_svg=rsvg_handle_new_from_file(path,NULL); resized_svg_free(path); g_free(path); } gboolean result=rsvg_handle_render_cairo(data.svg_handle_svg,cr); if(result&&cr) {cairo_stroke(cr);} else printf(" \n"); return FALSE; }
      
      





ご覧のとおり、不快な機能があります-フルパスです。 つまり、左側の部分(GtkDrawingArea)が表示されなくなるため、フォルダーを移動する価値があります。 同じことが、実行可能ファイルに含まれていないすべてのリソースに適用されます。 これを行うために、実行方法に関係なく、実行可能ファイルへのフルパスを計算する関数を作成しました。



 //   data.path void get_real_path(char *argv0) { char* result=(char*)calloc(1024,sizeof(char)); char* cwd=(char*)calloc(1024,sizeof(char)); getcwd(cwd, 1024); int i=0; while(argv0[i]!='\0'&&i<1024) ++i; while(argv0[i]!='/'&&i>0) --i; result[i]='\0'; while(i>0) { --i; result[i]=argv0[i]; } /*alex@alex-System-Product-Name:~/project_manager$ ./manager.elf argv[0]=./manager.elf path=/home/alex/project_manager*/ if(strlen(result)<=strlen(cwd)) //   { free(result); strcpy(data.path,cwd); strcat(data.path,"/"); //printf("path_cwd=%s\n",cwd); free(cwd);} else { /*alex@alex-System-Product-Name:/home$ '/home/alex/project_manager/manager.elf' argv[0]=/home/alex/project_manager/manager.elf path=/home*/ free(cwd); strcpy(data.path,result); strcat(data.path,"/"); //printf("path_result=%s\n",result); free(result); } }
      
      





manager.elfファイルを実行する方法のコードには2つの例があります。 また、最初にmain()関数を配置する必要があります



 char cwd[1024]; getcwd(cwd, sizeof(cwd)); get_real_path(argv[0]);
      
      





描画関数は次の形式を取ります



 gboolean drawingarea1_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer user_data) { if(!data.svg_handle_svg) { char image_path[1024]; strcat(image_path,data.path); strcat(image_path,"compassmarkings.svg"); printf("image_path=%s\n",image_path); char* path=get_resized_svg(image_path, 220, 220); data.svg_handle_svg=rsvg_handle_new_from_file(path,NULL); resized_svg_free(path); g_free(path); } gboolean result=rsvg_handle_render_cairo(data.svg_handle_svg,cr); if(result&&cr) {cairo_stroke(cr);} else printf(" \n"); return FALSE; }
      
      





クイックアクションテスト。



2つの描画関数(GtkDrawingAreaとGtkImage)があります。



それぞれを型のデザインに配置します(<time.h>を接続することを忘れずに)



 clock_t tic = clock(); clock_t toc = clock(); printf("image1_draw_cb elapsed : %f seconds\n", (double)(toc - tic) / CLOCKS_PER_SEC);
      
      





また、htopアプリケーションでは、プログラムが各コアAthlon 2 X3 2.5 GHzの20〜30%を消費する様子を確認できます。



エラーはすぐに見つかりました。



 gboolean image1_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer user_data) { clock_t tic = clock(); if(!data.svg_handle_image) { data.svg_handle_image=rsvg_handle_new_from_file("compassmarkings.svg",NULL); data.surf=cairo_image_surface_create_from_png("2.png"); data.pixbuf=rsvg_handle_get_pixbuf(data.svg_handle_image); //} //if(data.pixbuf) // { cairo_set_source_surface(cr,data.surf,0,0); GdkPixbuf *dest=gdk_pixbuf_scale_simple (data.pixbuf,250,250,GDK_INTERP_BILINEAR); gtk_image_set_from_pixbuf (data.image,dest); g_object_unref(dest); //cairo_paint(cr); } clock_t toc = clock(); printf("image1_draw_cb elapsed : %f seconds\n", (double)(toc - tic) / CLOCKS_PER_SEC); return FALSE; }
      
      





判明したように、GtkImageには独自のレンダリングシステムがあり、image1_draw_cbのコンテンツは1回しか初期化できません。 コメント行は冗長でした。







ご覧のとおり、GtkImageを使用した最初のレンダリングはGtkDrawingAreaよりも時間がかかりますが、理論的には画像の更新が高速になります。 220px * 220pxの画像の再描画ごとに400万プロセッササイクルは少し多く、pixbufを介してのみキャッシュできます(少なくとも、他の方法は知りません)。



ご清聴ありがとうございました。



All Articles