Boost.Asio で Web から画像を読み込んでくる

Boost.Asio を使用して、HTTP 通信で、画像の読み込みを行います。
ついでに読み込んだデータを OGLplus で描画とかしてみます。

[ソース]

#include <vector>
#include <GL/glew.h>

#include <oglplus/gl.hpp>
// OGLplus 0.5.0 以降だと near far を undef しておかないと動かない
#undef near
#undef far

// #include <oglplus/all.hpp>
#include <oglplus/context.hpp>
#include <oglplus/buffer.hpp>
#include <oglplus/vertex_attrib.hpp>
#include <oglplus/vertex_array.hpp>
#include <oglplus/shader.hpp>
#include <oglplus/program.hpp>
#include <oglplus/pixel_data.hpp>
#include <oglplus/texture.hpp>
#include <oglplus/image.hpp>
#include <oglplus/images/load.hpp>
#include <oglplus/shapes/draw.hpp>

#include <GL/Glut.h>

#define BOOST_DATE_TIME_NO_LIB
#define BOOST_REGEX_NO_LIB
#define BOOST_ERROR_CODE_HEADER_ONLY
#define BOOST_SYSTEM_NO_LIB

#include <boost/asio.hpp>
#include <gl/graphics.hpp>
#include <fstream>
#include <sstream>


namespace glp = oglplus;
typedef glp::Context glc;


template<typename Stream>
Stream&
load_image(
    Stream& s,
    std::string const& path,
    std::string const& host
){
    if(!s){
        std::cout << "Unable to connect" << std::endl;
        abort();
    }

    s << "GET " << path << " HTTP/1.0\r\n";
    s << "Host: "<< host << "\r\n";
    s << "\r\n";
    s << std::flush;  // バッファに溜めずに確実にネットワーク送信

    // 受信
    std::string line;
    while(std::getline(s, line) && !line.empty() && line != "\r"){
        std::cout << line << std::endl;
    }
    return s;
}


struct texture : boost::noncopyable{
    texture(){

        vs.Source(R"delimiter(
            #version 330
            in vec4 Position;
            in vec2 TexCoord;
            out vec2 vertTexCoord;

            void main(void){
                vertTexCoord = TexCoord;
                gl_Position = Position;
            }
        )delimiter");
        vs.Compile();

        fs.Source(R"delimiter(
            #version 330
            uniform sampler2D TexUnit;
            in vec2 vertTexCoord;
            out vec4 fragColor;

            void main(void){
                fragColor = texture(TexUnit, vertTexCoord);
                vec4 t  = texture(TexUnit, vertTexCoord);
                fragColor = vec4(t.rgb, 1.0);
            }
        )delimiter");
        fs.Compile();

        // attach the shaders to the program
        prog.AttachShader(vs);
        prog.AttachShader(fs);
        // link and use it
        prog.Link();
        prog.Use();
        // bind the VAO for the texture
        vertex_array.Bind();

        // bind the VBO for the texture vertices
        verts.Bind(glp::Buffer::Target::Array);
        {
            GLfloat data[12] = {
                1.0f, 1.0f,
                1.0f, -1.0f,
                -1.0f, 1.0f,
                -1.0f, 1.0f,
                1.0f, -1.0f,
                -1.0f, -1.0f
            };

            // upload the data
            glp::Buffer::Data(glp::Buffer::Target::Array,
                12,
                data
            );
            // setup the vertex attribs array for the vertices
            glp::VertexAttribArray vert_attr(prog, "Position");
            vert_attr.Setup(2, glp::DataType::Float);
            vert_attr.Enable();
        }
        
        texcoords.Bind(glp::Buffer::Target::Array);
        {
            GLfloat data[12] = {
                1.0f, 1.0f,
                1.0f, 0.0f,
                0.0f, 1.0f,
                0.0f, 1.0f,
                1.0f, 0.0f,
                0.0f, 0.0f
            };
            // upload the data
            glp::Buffer::Data(glp::Buffer::Target::Array,
                12,
                data
            );
            glp::VertexAttribArray attr(prog, "TexCoord");
            attr.Setup(2, glp::DataType::Float);
            attr.Enable();
        }

        // setup the texture
        tex.Bind(glp::Texture::Target::_2D);
        {
            tex.MinFilter(glp::Texture::Target::_2D, glp::TextureMinFilter::Linear);
            tex.MagFilter(glp::Texture::Target::_2D, glp::TextureMagFilter::Linear);
            tex.WrapS(glp::Texture::Target::_2D, glp::TextureWrap::Repeat);
            tex.WrapT(glp::Texture::Target::_2D, glp::TextureWrap::Repeat);

            namespace ip = boost::asio::ip;
            // web から画像を読み込んでくる
            // @manga_osyo のアイコン
            ip::tcp::iostream s( "a2.twimg.com", "http" );
            if(!s){
                std::cout << "Unable to connect" << std::endl;
                abort();
            }
            std::string const path = "/profile_images/1662110818/bambi.PNG";
            std::string const host = "a2.twimg.com";

            tex.Image2D(
                glp::Texture::Target::_2D,
                glp::images::PNG(load_image(
                    s, path, host
                ))
            );
        }

    }
    
    void
    render() const{
        vertex_array.Bind();
        glc::DrawArrays(glp::PrimitiveType::Triangles, 0, 6);
    }
private:

    glp::VertexShader vs;
    glp::FragmentShader fs;
    glp::Program prog;

    glp::VertexArray vertex_array;
    glp::Buffer verts;
    glp::Buffer texcoords;

    glp::Texture tex;
};


int
main(int argc, char *argv[]) try{
    namespace glp = oglplus;
    typedef glp::Context glc;
    
    gl::graphics g(argc, argv, 500, 500, "test");
    
    // glew の初期化
    auto err = glewInit();
    if (err != GLEW_OK)
    {
        fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
    }
    
    glc::ClearColor(0.5f, 0.7f, 0.9f, 1.0f);
    glc::ClearDepth(1.0f);

    // テクスチャポリゴンの生成
    texture tex;
    
    gl::displayfunc(g, [&](){
        glc::Clear().ColorBuffer().DepthBuffer();
        tex.render();
    });
    
    gl::reshapefunc(g, [&](int width, int height){
        glc::Viewport(width, height);
    });

    // キー入力イベント
    gl::keyboardfunc(g, [&tex](unsigned char key, int x, int y){
        if(key == 'q') return exit(0);
    });
    
    g.run();
    return 0;
}
catch(glp::Error const& error){
    std::cout << error.what() << std::endl;
    std::cout << "GLSymbol          " << error.GLSymbol          () << std::endl;
    std::cout << "File              " << error.File              () << std::endl;
    std::cout << "Func              " << error.Func              () << std::endl;
    std::cout << "Line              " << error.Line              () << std::endl;
    std::cout << "ClassName         " << error.ClassName         () << std::endl;
    std::cout << "ObjectDescription " << error.ObjectDescription () << std::endl;
}

[出力]

HTTP/1.1 200 OK
x-amz-id-2: Wa09wGDzkwFuok1YsW693owl0hz7lumLYccbjNqFFYKWQnbpxlb1BZh+9+BY+bu+
x-amz-request-id: 6251203F97195E3D
Date: Mon, 28 Nov 2011 08:18:39 GMT
Last-Modified: Mon, 28 Nov 2011 08:04:41 GMT
ETag: "43078357c49e2a0719d4be75c028e01b"
Accept-Ranges: bytes
Content-Type: image/png
Content-Length: 364906
Server: AmazonS3
Cache-Control: private, max-age=604800
Age: 0
Expires: Mon, 05 Dec 2011 08:18:39 GMT
Connection: close

思ったよりも簡単に出来たので満足。

[boost]

  • ver 1.48.0

[コンパイラ]

  • g++ (GCC) 4.7.0 20111112 (experimental)