/* -*- c-file-style: "ruby"; indent-tabs-mode: nil -*- */ /********************************************************************** rbart_canvas.c - $Author: ggc $ $Date: 2007/07/10 13:22:40 $ Copyright (C) 2004 Ruby-GNOME2 Project Team Copyright (C) 2003 Tom Payne **********************************************************************/ #include "rbart.h" #include #include #define ART_CANVAS_PNG_INTERLACE_ADAM7 1 #define ART_CANVAS_ALPHA_MASK 2 VALUE artCanvas; VALUE artCanvasColor; typedef struct { int flags; int width; int height; art_u8 *rgb; art_u8 *mask; } ArtCanvas; #define Canvas_Ptr(r_obj) ((ArtCanvas *)RDATA(r_obj)->data) /*************************************************/ ArtCanvas * rbart_get_art_canvas(r_obj) VALUE r_obj; { if (!rb_obj_is_instance_of(r_obj, artCanvas)) rb_raise(rb_eTypeError, "not an Art::Canvas"); return Canvas_Ptr(r_obj); } /*************************************************/ static void canvas_free(canvas) ArtCanvas *canvas; { xfree(canvas->rgb); if (canvas->flags & ART_CANVAS_ALPHA_MASK) xfree(canvas->mask); xfree(canvas); } static VALUE canvas_s_allocate(klass) VALUE klass; { return Data_Wrap_Struct(klass, 0, canvas_free, 0); } static VALUE canvas_initialize(argc, argv, self) int argc; VALUE *argv; VALUE self; { VALUE r_width; VALUE r_height; VALUE r_color; VALUE r_flags; ArtCanvas *canvas; art_u32 color; rb_scan_args(argc, argv, "22", &r_width, &r_height, &r_color, &r_flags); canvas = (ArtCanvas *) xmalloc(sizeof(ArtCanvas)); canvas->flags = NIL_P(r_flags) ? 0 : NUM2INT(r_flags); canvas->width = NUM2INT(r_width); canvas->height = NUM2INT(r_height); canvas->rgb = (art_u8 *) xcalloc(3 * canvas->width * canvas->height, sizeof(art_u8)); color = NIL_P(r_color) ? 0xffffffff : NUM2ULONG(r_color); art_rgb_run_alpha(canvas->rgb, color >> 24, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff, canvas->width * canvas->height); if (canvas->flags & ART_CANVAS_ALPHA_MASK) { canvas->mask = (art_u8 *) xcalloc(3 * canvas->width * canvas->height, sizeof(art_u8)); art_rgb_run_alpha(canvas->mask, 0x00, 0x00, 0x00, color & 0xff, canvas->width * canvas->height); } else canvas->mask = NULL; DATA_PTR(self) = canvas; return Qnil; } static VALUE canvas_aref(r_self, r_x, r_y) VALUE r_self; VALUE r_x; VALUE r_y; { ArtCanvas *canvas; int x; int y; art_u8 *rgb_pixel; art_u8 mask_value; canvas = rbart_get_art_canvas(r_self); x = NUM2INT(r_x); y = NUM2INT(r_y); if (x < 0 || canvas->width <= x || y < 0 || canvas->height <= y) rb_raise(rb_eIndexError, "index out of range"); rgb_pixel = canvas->rgb + 3 * (x + y * canvas->width); if (canvas->flags & ART_CANVAS_ALPHA_MASK) mask_value = canvas->mask[3 * (x + y * canvas->width)]; else mask_value = 0xff; return ULONG2NUM((rgb_pixel[0] << 24) | (rgb_pixel[1] << 16) | (rgb_pixel[2] << 8) | mask_value); } static VALUE canvas_aref_set(r_self, r_x, r_y, r_color) VALUE r_self; VALUE r_x; VALUE r_y; VALUE r_color; { ArtCanvas *canvas; int x; int y; art_u32 color; art_u8 *pixel; canvas = rbart_get_art_canvas(r_self); x = NUM2INT(r_x); y = NUM2INT(r_y); if (x < 0 || canvas->width <= x || y < 0 || canvas->height <= y) rb_raise(rb_eIndexError, "index out of range"); color = NUM2ULONG(r_color); pixel = canvas->rgb + 3 * (x + y * canvas->width); pixel[0] = color >> 24; pixel[1] = (color >> 16) & 0xff; pixel[2] = (color >> 8) & 0xff; if (canvas->flags & ART_CANVAS_ALPHA_MASK) { art_u8 *mask_pixel; mask_pixel = canvas->mask + 3 * (x + y * canvas->width); mask_pixel[0] = mask_pixel[1] = mask_pixel[2] = color & 0xff; } return r_color; } static VALUE canvas_flags(r_self) VALUE r_self; { return INT2NUM(rbart_get_art_canvas(r_self)->flags); } static VALUE canvas_height(r_self) VALUE r_self; { return INT2NUM(rbart_get_art_canvas(r_self)->height); } static VALUE canvas_render_svp(r_self, r_svp, r_color) VALUE r_self; VALUE r_svp; VALUE r_color; { ArtCanvas *canvas; ArtSVP *svp; art_u32 color; canvas = rbart_get_art_canvas(r_self); svp = get_art_svp(r_svp); color = NUM2ULONG(r_color); art_rgb_svp_alpha(svp, 0, 0, canvas->width, canvas->height, color, canvas->rgb, 3 * canvas->width, NULL); if (canvas->flags & ART_CANVAS_ALPHA_MASK) art_rgb_svp_alpha(svp, 0, 0, canvas->width, canvas->height, 0xffffff00 | (color & 0xff), canvas->mask, 3 * canvas->width, NULL); return r_self; } #if 0 static void user_init_destination(cinfo) j_compress_ptr cinfo; { VALUE r_buffer; r_buffer = (VALUE) cinfo->client_data; cinfo->dest->next_output_byte = (JOCTET *) RSTRING(r_buffer)->ptr; cinfo->dest->free_in_buffer = RSTRING(r_buffer)->aux.capa; } static boolean user_empty_output_buffer(cinfo) j_compress_ptr cinfo; { VALUE r_buffer; int old_capa; r_buffer = (VALUE) cinfo->client_data; old_capa = RSTRING(r_buffer)->aux.capa; rb_str_resize(r_buffer, old_capa + 16384); cinfo->dest->next_output_byte = (JOCTET *) RSTRING(r_buffer)->ptr + old_capa; cinfo->dest->free_in_buffer += RSTRING(r_buffer)->aux.capa - old_capa; return TRUE; } static void user_term_destination(cinfo) j_compress_ptr cinfo; { VALUE r_buffer; r_buffer = (VALUE) cinfo->client_data; RSTRING(r_buffer)->len = RSTRING(r_buffer)->aux.capa - cinfo->dest->free_in_buffer; RSTRING(r_buffer)->ptr[RSTRING(r_buffer)->len] = '\0'; } #endif struct client_data { VALUE r_result; JOCTET buffer[BUFSIZ]; }; static void user_init_destination(cinfo) j_compress_ptr cinfo; { struct client_data *cd; cd = (struct client_data *) cinfo->client_data; cinfo->dest->next_output_byte = cd->buffer; cinfo->dest->free_in_buffer = BUFSIZ; } static boolean user_empty_output_buffer(cinfo) j_compress_ptr cinfo; { struct client_data *cd; cd = (struct client_data *) cinfo->client_data; rb_str_cat(cd->r_result, (const char*)cd->buffer, BUFSIZ); cinfo->dest->next_output_byte = cd->buffer; cinfo->dest->free_in_buffer = BUFSIZ; return TRUE; } static void user_term_destination(cinfo) j_compress_ptr cinfo; { struct client_data *cd; cd = (struct client_data *) cinfo->client_data; rb_str_cat(cd->r_result, (const char*)cd->buffer, cinfo->dest->next_output_byte - cd->buffer); } static VALUE canvas_to_jpeg(argc, argv, r_self) int argc; VALUE *argv; VALUE r_self; { VALUE r_quality; ArtCanvas *canvas; struct jpeg_error_mgr err; struct jpeg_compress_struct cinfo; struct jpeg_destination_mgr dest; JSAMPLE **row_pointers; int i; struct client_data cd; rb_scan_args(argc, argv, "01", &r_quality); canvas = rbart_get_art_canvas(r_self); cinfo.err = jpeg_std_error(&err); jpeg_create_compress(&cinfo); cinfo.image_width = canvas->width; cinfo.image_height = canvas->height; cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; jpeg_set_defaults(&cinfo); if (r_quality != Qnil) jpeg_set_quality(&cinfo, NUM2INT(r_quality), TRUE); cinfo.optimize_coding = TRUE; cd.r_result = rb_str_new(NULL, 0); cinfo.client_data = (void *) &cd; dest.next_output_byte = NULL; dest.free_in_buffer = 0; dest.init_destination = user_init_destination; dest.empty_output_buffer = user_empty_output_buffer; dest.term_destination = user_term_destination; cinfo.dest = &dest; jpeg_start_compress(&cinfo, TRUE); row_pointers = (JSAMPLE **) xcalloc(canvas->height, sizeof(JSAMPLE *)); for (i = 0; i < canvas->height; ++i) row_pointers[i] = canvas->rgb + 3 * i * canvas->width; jpeg_write_scanlines(&cinfo, row_pointers, canvas->height); xfree(row_pointers); jpeg_finish_compress(&cinfo); jpeg_destroy_compress(&cinfo); return cd.r_result; } static void user_write_data(png_ptr, data, length) png_structp png_ptr; png_bytep data; png_uint_32 length; { VALUE r_buffer; r_buffer = (VALUE) png_get_io_ptr(png_ptr); rb_str_cat(r_buffer, (const char*)data, length); } static void user_flush_data(png_ptr) png_structp png_ptr; { } static VALUE canvas_to_png(argc, argv, r_self) int argc; VALUE *argv; VALUE r_self; { VALUE r_flags; int flags; ArtCanvas *canvas; png_structp png_ptr; png_infop info_ptr; VALUE r_result; art_u8 *rgba = NULL; png_bytepp row_pointers; int i; rb_scan_args(argc, argv, "01", &r_flags); flags = NIL_P(r_flags) ? 0 : NUM2INT(r_flags); canvas = rbart_get_art_canvas(r_self); png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) return Qnil; info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_write_struct(&png_ptr, NULL); return Qnil; } if (setjmp(png_ptr->jmpbuf)) { png_destroy_write_struct(&png_ptr, &info_ptr); return Qnil; } png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); r_result = rb_str_new(NULL, 0); png_set_write_fn(png_ptr, (void *) r_result, user_write_data, user_flush_data); png_set_IHDR(png_ptr, info_ptr, canvas->width, canvas->height, 8, canvas->flags & ART_CANVAS_ALPHA_MASK ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB, flags & ART_CANVAS_PNG_INTERLACE_ADAM7 ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_write_info(png_ptr, info_ptr); row_pointers = (png_bytepp) xcalloc(canvas->height, sizeof(png_bytep)); if (canvas->flags & ART_CANVAS_ALPHA_MASK) { rgba = (art_u8 *) xcalloc(4 * canvas->width * canvas->height, sizeof(art_u8)); for (i = 0; i < canvas->width * canvas->height; ++i) { rgba[4 * i + 0] = canvas->rgb[3 * i + 0]; rgba[4 * i + 1] = canvas->rgb[3 * i + 1]; rgba[4 * i + 2] = canvas->rgb[3 * i + 2]; rgba[4 * i + 3] = canvas->mask[3 * i + 0]; } for (i = 0; i < canvas->height; ++i) row_pointers[i] = rgba + 4 * i * canvas->width; } else for (i = 0; i < canvas->height; ++i) row_pointers[i] = canvas->rgb + 3 * i * canvas->width; png_write_image(png_ptr, row_pointers); xfree(row_pointers); if (canvas->flags & ART_CANVAS_ALPHA_MASK) xfree(rgba); png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); return r_result; } static VALUE canvas_width(r_self) VALUE r_self; { return INT2NUM(rbart_get_art_canvas(r_self)->width); } static VALUE color_new(argc, argv, r_klass) int argc; VALUE *argv; VALUE r_klass; { art_u8 components[4]; int i; components[3] = 0xff; switch (argc) { #if 0 case 2: switch (TYPE(argv[1])) { case T_BIGNUM: case T_FIXNUM: components[3] = NUM2INT(argv[1]); break; case T_FLOAT: components[3] = 0xff * NUM2DBL(argv[1]) + 0.5; break; default: rb_raise(rb_eTypeError, "expect an Integer or Float"); break; } case 1: switch (TYPE(argv[0])) { case T_STRING: if ((RSTRING(argv[0])->len - 1) % 3 != 0 || RSTRING(argv[0])->ptr[0] != '#')) rb_raise(rb_eTypeError, "invalid color"); break; default: rb_raise(rb_eTypeError, "expect a String"); break; } break; #endif case 3: case 4: for (i = 0; i < argc; ++i) switch (TYPE(argv[i])) { case T_BIGNUM: case T_FIXNUM: components[i] = NUM2INT(argv[i]); break; case T_FLOAT: components[i] = 0xff * NUM2DBL(argv[i]) + 0.5; break; default: rb_raise(rb_eTypeError, "expect an Integer or Float"); break; } break; default: rb_raise(rb_eArgError, "wrong number of arguments (expect 1, 3, or 4)"); break; } return ULONG2NUM((components[0] << 24) | (components[1] << 16) | (components[2] << 8) | (components[3])); } void Init_art_canvas(mArt) VALUE mArt; { artCanvas = rb_define_class_under(mArt, "Canvas", rb_cObject); rb_define_const(artCanvas, "ALPHA_MASK", INT2NUM(ART_CANVAS_ALPHA_MASK)); rb_define_const(artCanvas, "PNG_INTERLACE_ADAM7", INT2NUM(ART_CANVAS_PNG_INTERLACE_ADAM7)); RBART_INIT_FUNC2(artCanvas, canvas_s_allocate); rb_define_method(artCanvas, "initialize", canvas_initialize, -1); rb_define_method(artCanvas, "[]", canvas_aref, 2); rb_define_method(artCanvas, "[]=", canvas_aref_set, 3); rb_define_method(artCanvas, "flags", canvas_flags, 0); rb_define_method(artCanvas, "height", canvas_height, 0); rb_define_method(artCanvas, "render_svp", canvas_render_svp, 2); rb_define_method(artCanvas, "to_jpeg", canvas_to_jpeg, -1); rb_define_method(artCanvas, "to_png", canvas_to_png, -1); rb_define_method(artCanvas, "width", canvas_width, 0); rb_define_singleton_method(artCanvas, "color", color_new, -1); artCanvasColor = rb_define_module_under(artCanvas, "Color"); rb_define_const(artCanvasColor, "BLACK", ULONG2NUM(0x000000ff)); rb_define_const(artCanvasColor, "BLUE", ULONG2NUM(0x0000ffff)); rb_define_const(artCanvasColor, "CYAN", ULONG2NUM(0x00ffffff)); rb_define_const(artCanvasColor, "GREEN", ULONG2NUM(0x00ff00ff)); rb_define_const(artCanvasColor, "MAGENTA", ULONG2NUM(0xff00ffff)); rb_define_const(artCanvasColor, "RED", ULONG2NUM(0xff0000ff)); rb_define_const(artCanvasColor, "WHITE", ULONG2NUM(0xffffffff)); rb_define_const(artCanvasColor, "YELLOW", ULONG2NUM(0xffff00ff)); }