2次元描画ライブラリの Cairo と AGG (Anti-Grain Geometry) を組み合わせて処理してみた。

C++によるサンプルコード。


#include <string>
#include <cairo/cairo.h>
#include <agg_rendering_buffer.h>
#include <agg_rasterizer_scanline_aa.h>
#include <agg_pixfmt_rgba.h>
#include <agg_renderer_scanline.h>
 
static cairo_surface_t* create_scaling_surface(cairo_surface_t& src, int width, int height){
  double w = cairo_image_surface_get_width(&src);
  double h = cairo_image_surface_get_height(&src);
  cairo_surface_t* s = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
  cairo_t* c = cairo_create(s);
  cairo_scale(c, width / w, height / h);
  cairo_set_source_surface(c, &src, 0.0, 0.0);
  cairo_paint(c);
  cairo_destroy(c);
  return s;
}
 
static agg::rendering_buffer* create_rendering_buffer(cairo_surface_t& src){
  unsigned char* d = cairo_image_surface_get_data(&src);
  int w = cairo_image_surface_get_width(&src);
  int h = cairo_image_surface_get_height(&src);
  int s = cairo_image_surface_get_stride(&src);
  agg::rendering_buffer* rb = new agg::rendering_buffer();
  rb->attach(d, w, h, s);
  return rb;
}
 
static void draw_image(agg::renderer_base<agg::pixfmt_rgba32>& rbase, cairo_surface_t& image, double dx, double dy, double scale){
  cairo_surface_t* s = &image;
  cairo_surface_t* ss = NULL;
  int w = cairo_image_surface_get_width(s);
  int h = cairo_image_surface_get_height(s);
  if(scale != 1.0){
    w = (int)(w * scale);
    h = (int)(h * scale);
    ss = create_scaling_surface(*s, w, h);
    s = ss;
  }
  agg::rendering_buffer* rb = create_rendering_buffer(*s);
  agg::pixfmt_alpha_blend_rgba<agg::blender_rgba<agg::rgba8, agg::order_rgba>, agg::rendering_buffer, agg::int32u> srcPixelFormatRenderer(*rb);
  agg::rect_i rect_src(0, 0, w, h);
  rbase.blend_from(srcPixelFormatRenderer, &rect_src, dx, dy, agg::cover_full);
  delete rb;
  if(ss){
    cairo_surface_destroy(ss);
  }
}
 
int main(void){
 
  int width = 600;
  int height = 400;
  int bytes_per_pixel = 4;
 
  // canvas
  cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
  unsigned char* data = cairo_image_surface_get_data(surface);
  agg::rendering_buffer rbuf;
  rbuf.attach(data, width, height, width * bytes_per_pixel);
  agg::pixfmt_rgba32 pixf(rbuf);
  agg::renderer_base<agg::pixfmt_rgba32> rbase(pixf);
  rbase.clear(agg::rgba8(255, 255, 255, 255));
 
  // load png image
  std::string image1_path = "input1.png";
  cairo_surface_t* image1_surface = cairo_image_surface_create_from_png(image1_path.c_str());
 
  // load png image
  std::string image2_path = "input2.png";
  cairo_surface_t* image2_surface = cairo_image_surface_create_from_png(image2_path.c_str());
 
  // drawing
  draw_image(rbase, *image1_surface,  10,  20, 1.0);
  draw_image(rbase, *image2_surface,  30,  40, 1.0);
  draw_image(rbase, *image1_surface,  50,  60, 1.5);
  draw_image(rbase, *image2_surface,  70,  80, 1.5);
  draw_image(rbase, *image1_surface,  90, 100, 2.0);
  draw_image(rbase, *image2_surface, 110, 120, 2.0);
 
  // save image
  std::string path = "output.png";
  cairo_status_t status = cairo_surface_write_to_png(surface, path.c_str());
  
  // delete cairo surface
  cairo_surface_destroy(surface);
  cairo_surface_destroy(image1_surface);
  cairo_surface_destroy(image2_surface);
 
  return 0;
}

コンパイルと実行は Mac OS X Lion 上にて、以下のシェルスクリプトでやっている。image_scaling.cpp がサンプルコードのファイル。


#!/bin/bash
 
rm ./a.out
rm ./output.png
 
g++ -I/usr/local/include/agg2 -I/usr/local/Cellar/cairo/1.10.2/include -L/usr/local/lib -L/usr/local/Cellar/cairo/1.10.2/lib -lagg -lcairo ./image_scaling.cpp
 
chmod 744 ./a.out
./a.out

実行環境は Mac OS X Lion + AGG 2.5 + cairo 1.10.2

重ねる入力画像: input1.png
input1.png

重ねる入力画像: input2.png
input2.png
この画像は [ヅ] Javaで透過度(アルファ値)のあるPNGファイルを出力する で作成した「背景が new Color(255, 255, 255, 0), // white (transparent)」の画像。

サンプルコードが出力する画像: output.png
output.png

拡大して重ねて描画することはできたけど、なぜかちょっと黒っぽくなっている。やっぱりアルファ半透明画像の処理がAGGは苦手なのかなぁ。。。

Ref.

tags: agg cairo c++

Posted by NI-Lab. (@nilab)