スポンサーリンク

zipファイル内から画像を取り出して表示

libzipでzipファイルから画像データを取り出し、skiaでデコードし、wxWidgetsで表示する。

左右カーソルで画像切り替えをする。

LoadImageZip.hpp

zipから画像を取り出す関数群。

#pragma once

#include <include/core/SkBitmap.h>
#include <zip.h>
#include <memory>
#include <string>
#include <vector>

struct ImageData
{
  std::unique_ptr<SkBitmap> bitmap;
  std::string filename;
};

std::vector<ImageData> FilesInZip(const char* pathname);
std::vector<std::uint8_t> GetZipData(zip_t* zipArchive, zip_int64_t index);
std::unique_ptr<SkBitmap> ImageFromBinary(const std::vector<std::uint8_t>& contents);

LoadImageZip.cpp

#include <include/core/SkStream.h>
#include <include/codec/SkCodec.h>

#include <iostream>
#include <zip.h>
#include <vector>

#include "LoadImageZip.hpp"

// zipファイルから画像データを取り出す
std::vector<ImageData> FilesInZip(const char* pathname) {

    std::vector<ImageData> files;

    int error = 0;

    // zipファイルを読み取り専用で開く
    zip_t* zipArchive = zip_open(pathname, ZIP_RDONLY, &error);
    if (zipArchive == nullptr) {
        return std::vector<ImageData>();
    }

    // zipファイル内のエントリ数を取得
    zip_int64_t item_count = zip_get_num_entries(zipArchive, 0);

    // 各エントリの名前を取得して表示
    for (zip_uint64_t i = 0; i < static_cast<zip_uint64_t>(item_count); ++i) {

        // ファイル名を取得
        const char* file_name = zip_get_name(zipArchive, i, 0);

        // Zipファイル内のデータを取得
        std::vector<std::uint8_t> binary = GetZipData(zipArchive, i);

        // バイナリデータから画像を作成
        std::unique_ptr<SkBitmap> bitmap = ImageFromBinary(binary);

        // ファイル名と画像を一覧に追加
        files.emplace_back(ImageData{ std::move(bitmap),file_name });
    }

    // zipファイルを閉じる
    zip_close(zipArchive);

    return files;
}

// zipファイルからバイナリデータを取得
std::vector<std::uint8_t> GetZipData(zip_t* zipArchive, zip_int64_t index) {
    struct zip_stat sb;
    zip_stat_index(zipArchive, index, 0, &sb);

    // 2番目のファイルのファイルサイズと同じメモリを確保する
    std::vector<std::uint8_t> contents(sb.size);

    // ファイルの内容をメモリに読み込む
    zip_file* zf = zip_fopen(zipArchive, sb.name, 0);
    zip_fread(zf, contents.data(), sb.size);
    zip_fclose(zf);

    return contents;

}

// バイナリデータをSkiaの画像に変換
std::unique_ptr<SkBitmap> ImageFromBinary(const std::vector<std::uint8_t>& contents) {

    sk_sp<SkData> skdata = SkData::MakeWithoutCopy(contents.data(), contents.size());
    if (!skdata) {
        std::cerr << "SkData作成に失敗しました" << std::endl;
        return nullptr;
    }

    // データからSkCodecを作成
    std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(skdata));
    if (!codec) {
        return nullptr;
    }

    SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType);

    std::unique_ptr<SkBitmap> bitmap = std::make_unique<SkBitmap>();

    if (!bitmap->tryAllocPixels(info)) {
        return nullptr; // メモリ確保に失敗
    }

    SkCodec::Result ret = codec->getPixels(
        info, // 画像の情報
        bitmap->getPixels(), // ロード先のメモリ
        bitmap->rowBytes()   // ロード先の1行のバイト数
    );

    return bitmap;
}

main.cpp

// https://docs.wxwidgets.org/3.0/overview_helloworld.html

// プリプロセッサに以下二つを追加
// __WXMSW__
// WXUSINGDLL

// サブシステムをWindowsに設定(WinMainで呼び出すので)
// Windows (/SUBSYSTEM:WINDOWS)

#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif

#include <wx/gdicmn.h> // wxPointに必要
#include <wx/frame.h>  // wxFrameに必要

#ifdef _DEBUG
#pragma comment(lib,"wxbase32ud.lib")
#pragma comment(lib,"wxbase32ud_net.lib")
#pragma comment(lib,"wxbase32ud_xml.lib")
#pragma comment(lib,"wxmsw32ud_adv.lib")
#pragma comment(lib,"wxmsw32ud_aui.lib")
#pragma comment(lib,"wxmsw32ud_core.lib")
#pragma comment(lib,"wxmsw32ud_gl.lib")
#pragma comment(lib,"wxmsw32ud_html.lib")
#pragma comment(lib,"wxmsw32ud_media.lib")
#pragma comment(lib,"wxmsw32ud_propgrid.lib")
#pragma comment(lib,"wxmsw32ud_qa.lib")
#pragma comment(lib,"wxmsw32ud_ribbon.lib")
#pragma comment(lib,"wxmsw32ud_richtext.lib")
#pragma comment(lib,"wxmsw32ud_stc.lib")
#pragma comment(lib,"wxmsw32ud_webview.lib")
#pragma comment(lib,"wxmsw32ud_xrc.lib")

#else

#pragma comment(lib,"wxbase32u.lib")
#pragma comment(lib,"wxbase32u_net.lib")
#pragma comment(lib,"wxbase32u_xml.lib")
#pragma comment(lib,"wxmsw32u_adv.lib")
#pragma comment(lib,"wxmsw32u_aui.lib")
#pragma comment(lib,"wxmsw32u_core.lib")
#pragma comment(lib,"wxmsw32u_gl.lib")
#pragma comment(lib,"wxmsw32u_html.lib")
#pragma comment(lib,"wxmsw32u_media.lib")
#pragma comment(lib,"wxmsw32u_propgrid.lib")
#pragma comment(lib,"wxmsw32u_qa.lib")
#pragma comment(lib,"wxmsw32u_ribbon.lib")
#pragma comment(lib,"wxmsw32u_richtext.lib")
#pragma comment(lib,"wxmsw32u_stc.lib")
#pragma comment(lib,"wxmsw32u_webview.lib")
#pragma comment(lib,"wxmsw32u_xrc.lib")

#endif

#if defined(_DEBUG)
#pragma comment(lib, "skia.dll.lib")
#pragma comment(lib, "skshaper.dll.lib")
#else
#pragma comment(lib, "skia.dll.lib")
#endif
#pragma comment(lib, "zip.lib")

/////////////////////////////////////

#include <include/core/SkCanvas.h>
#include <include/core/SkBitmap.h>
#include <include/core/SkSurface.h>

/////////////////////////////////////
/////////////////////////////////////
/////////////////////////////////////

#include "LoadImageZip.hpp"

#include <string>

// SkBitmapからRGBデータを取得
void PixelRGB(std::vector<std::uint8_t>& rgb, const SkBitmap& skbmp);


// ウィンドウ作成
class MyFrame : public wxFrame
{

    std::vector<ImageData> files;
    std::vector<wxBitmap> wxbitmaps;
    size_t index = 0;
public:

    void ToBitmaps() {
        for (auto& file : files) {
            const SkBitmap& my_bitmap = *file.bitmap;
            int width = my_bitmap.width();
            int height = my_bitmap.height();
            std::vector<std::uint8_t> rgb;
            rgb.resize(width * height * 3);
            PixelRGB(rgb, my_bitmap);
            wxImage img(width, height, (unsigned char*)rgb.data(), true);
            wxBitmap wxbitmap(img);
            wxbitmaps.push_back(wxbitmap);
        }
    }

    void PostCreate() {

        files = FilesInZip("C:/temp/images.zip");
        ToBitmaps();
        index = 0;

        // OnPaintイベントを設定
        Bind(wxEVT_PAINT, &MyFrame::OnPaint, this);

        // キーボードイベント
        Bind(wxEVT_KEY_DOWN, &MyFrame::OnKeyDown, this);

        this->Layout(); // レイアウトの更新
    }

    // キーボードイベント
    void OnKeyDown(wxKeyEvent& event) {
        if (event.GetKeyCode() == WXK_RIGHT) {
            index++;
            if (index >= files.size()) {
                index = 0;
            }
            Refresh();
        }
        else if (event.GetKeyCode() == WXK_LEFT) {
            if (index == 0) {
                index = files.size() - 1;
            }
            else {
                index--;
            }
            Refresh();
        }
        
    }

    MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
        : wxFrame(NULL, wxID_ANY, title, pos, size)

    {
        // CallAfter : 現在処理中のイベントが終わったらPostCreateを実行
        // コンストラクタはウィンドウ生成イベント扱い
        CallAfter(&MyFrame::PostCreate);
    }

    // OnPaintイベント
    void OnPaint(wxPaintEvent& event) {

		if (wxbitmaps.empty()) {
			return;
		}

        const wxBitmap& wxbitmap = wxbitmaps[index];

        // wxWidgetsのウィンドウに表示
        wxPaintDC dc(this);
        dc.DrawBitmap(wxbitmap, 0, 0, true);
    }

};

/////////////////////////////////////
/////////////////////////////////////
/////////////////////////////////////

// wxWidgetsのアプリケーション作成
class MyApp : public wxApp {
public:

    virtual bool OnInit() {
        MyFrame* frame = new MyFrame("Hello World", wxPoint(50, 50), wxSize(550, 550));
        frame->Show(true);

        return true;
    }

};

/////////////////////////////////////
/////////////////////////////////////
/////////////////////////////////////

// WinMainをマクロで定義
wxIMPLEMENT_APP(MyApp);





void PixelRGB(std::vector<std::uint8_t>& rgb, const SkBitmap& skbmp) {
    if (!skbmp.readyToDraw()) {
        return;
    }

    if (skbmp.colorType() != kRGBA_8888_SkColorType && skbmp.colorType() != kBGRA_8888_SkColorType) {
        return;
    }

    rgb.clear();

    int Width = skbmp.width();
    int Height = skbmp.height();
    size_t rowBytes = skbmp.rowBytes();
    const SkImageInfo& info = skbmp.info();
    int channels = info.bytesPerPixel();
    std::uint8_t* pixels = (std::uint8_t*)skbmp.getPixels();


    rgb.resize(Width * Height * 3);
    for (int i = 0; i < Height; i++) {
        for (int j = 0; j < Width; j++) {
            size_t pos_ppm = (i * Width + j) * 3;
            size_t pos_sk = (i * rowBytes + j * channels);
            rgb[pos_ppm + 0] = pixels[pos_sk + 2];
            rgb[pos_ppm + 1] = pixels[pos_sk + 1];
            rgb[pos_ppm + 2] = pixels[pos_sk + 0];
        }
    }
}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


この記事のトラックバックURL: