libpngのインストールとC言語での使用方法・使用例

こちらではMacにlibpngをインストールする手順とlibpngを用いたプログラムの例を紹介します。

メモ

試したOSは下記の通りです。他の環境では試せていませんのでご了承ください。

macOS HighSierra

バージョン10.13.4

更新情報

RGB+αの画像形式にも対応しました

ダウンロードされるフォルダ違う・改行コードが異なる、などの違いがありますが、LINUX環境でも手順は参考になると思います。

libpngの入手・インストール

まずは libpng の入手とインストール方法について解説します。

libpngのダウンロード

まずlibpngをダウンロードするためにhttps://sourceforge.net/projects/libpng/files/にアクセスしましょう。最新バージョンは下図の緑色のボタンからダウンロードできます。

2018/9/7現在だと1.6.35が最新のようでした。

続いてターミナルでダウンロードしたファイルがあるフォルダにアクセスします。ターミナルアプリは「アプリケーション」→「ユーティリティ」にあります。


私がダウンロードしたファイルをダウンロードフォルダに保存しましたので下記コマンドでフォルダにアクセスしました。

cd /Users/[私のユーザ名]/Downloads/

続いてダウンロードした.tar.gzを下記コマンドにより展開します。「libpng-1.6.35.tar.gz」の部分はダウンロードしてきたファイル名に合わせて変更してください

tar xvzf libpng-1.6.35.tar.gz

フォルダが作成され、その中に様々なファイルが展開されるはずです。私の場合は「libpng-1.6.35」というフォルダが作成されました。展開が完了したら、その作成したフォルダにcdコマンドで移動しましょう。

libpngインストールのための設定

続いてそのフォルダで下記コマンドを実行します。これによりlibをインストールするための設定が行われます。

./configure

このコマンド実行後たくさんのメッセージが表示されながら処理が実行されますが1分かからずに処理が完了するはずです。

最後の方のメッセージは下記のような感じでした。良ければご参考に。

checking for size_t… yes

checking whether struct tm is in sys/time.h or time.h… time.h

checking for C/C++ restrict keyword… __restrict

checking for working strtod… yes

checking for memset… yes

checking for pow… yes

checking for clock_gettime… yes

checking for zlibVersion in -lz… yes

checking for feenableexcept in -lm… no

checking for feenableexcept… no

checking if using Solaris linker… no

checking if libraries can be versioned… no

configure: WARNING: *** You have not enabled versioned symbols.

configure: pkgconfig directory is ${libdir}/pkgconfig

configure: Extra options for compiler: 

checking that generated files are newer than configure… done

configure: creating ./config.status

config.status: creating Makefile

config.status: creating libpng.pc

config.status: creating libpng-config

config.status: creating config.h

config.status: executing depfiles commands

config.status: executing libtool commands

libpngライブラリのmake

続いて同じフォルダ内で下記コマンドを実行します。これによりlibpngライブラリが生成されます。

make

こちらも1分ほどで処理完了します。最後の方のメッセージはこんな感じ。

mv -f $depbase.Tpo $depbase.Po

/bin/sh ./libtool  –tag=CC   –mode=link gcc  -g -O2   -o pngimage contrib/libtests/pngimage.o libpng16.la -lz 

libtool: link: gcc -g -O2 -o .libs/pngimage contrib/libtests/pngimage.o  ./.libs/libpng16.dylib -lz

depbase=`echo contrib/tools/pngcp.o | sed ‘s|[^/]*$|.deps/&|;s|\.o$||’`;\

gcc -DHAVE_CONFIG_H -I.     -g -O2 -MT contrib/tools/pngcp.o -MD -MP -MF $depbase.Tpo -c -o contrib/tools/pngcp.o contrib/tools/pngcp.c &&\

mv -f $depbase.Tpo $depbase.Po

/bin/sh ./libtool  –tag=CC   –mode=link gcc  -g -O2   -o pngcp contrib/tools/pngcp.o libpng16.la -lz 

libtool: link: gcc -g -O2 -o .libs/pngcp contrib/tools/pngcp.o  ./.libs/libpng16.dylib -lz

depbase=`echo contrib/libtests/timepng.o | sed ‘s|[^/]*$|.deps/&|;s|\.o$||’`;\

gcc -DHAVE_CONFIG_H -I.     -g -O2 -MT contrib/libtests/timepng.o -MD -MP -MF $depbase.Tpo -c -o contrib/libtests/timepng.o contrib/libtests/timepng.c &&\

mv -f $depbase.Tpo $depbase.Po

/bin/sh ./libtool  –tag=CC   –mode=link gcc  -g -O2   -o timepng contrib/libtests/timepng.o libpng16.la -lz 

libtool: link: gcc -g -O2 -o .libs/timepng contrib/libtests/timepng.o  ./.libs/libpng16.dylib -lz

libpngのインストール

次にlibpngをインストールします。次のコマンドによりインストールが行われるはずです。

sudo make install

コマンドを実行するとパスワードが求められますので、管理者のパスワード(Macのパスワード)を入力してください。管理者権限でないとファイルの編集や追加削除の権限がなくてコマンド実行しても失敗します。sudoも使用頻度が高いコマンドですので覚えておきましょう。

インストールはすぐ処理が完了しますね。最後の方のメッセージを念のため載せておきます。

 ./install-sh -c -d ‘/usr/local/share/man/man3’

 /usr/bin/install -c -m 644 libpng.3 libpngpf.3 ‘/usr/local/share/man/man3’

 ./install-sh -c -d ‘/usr/local/share/man/man5’

 /usr/bin/install -c -m 644 png.5 ‘/usr/local/share/man/man5’

 ./install-sh -c -d ‘/usr/local/include/libpng16’

 /usr/bin/install -c -m 644 pnglibconf.h ‘/usr/local/include/libpng16’

 ./install-sh -c -d ‘/usr/local/lib/pkgconfig’

 /usr/bin/install -c -m 644 libpng16.pc ‘/usr/local/lib/pkgconfig’

 ./install-sh -c -d ‘/usr/local/include/libpng16’

 /usr/bin/install -c -m 644 png.h pngconf.h ‘/usr/local/include/libpng16’

/Applications/Xcode.app/Contents/Developer/usr/bin/make  install-data-hook

+ cd /usr/local/include

+ for f in pnglibconf.h png.h pngconf.h

+ rm -f pnglibconf.h

+ ln -s libpng16/pnglibconf.h pnglibconf.h

+ for f in pnglibconf.h png.h pngconf.h

+ rm -f png.h

+ ln -s libpng16/png.h png.h

+ for f in pnglibconf.h png.h pngconf.h

+ rm -f pngconf.h

+ ln -s libpng16/pngconf.h pngconf.h

+ cd /usr/local/lib/pkgconfig

+ rm -f libpng.pc

+ ln -s libpng16.pc libpng.pc

インストールの確認

最後にインストールされたことを確認しておきましょう。

今まで作成したC言語のプログラムファイルをコンパイルしてやるときに、最後に「-lpng」をつけてからコンパイルしてみてください。下記のような感じです。

-lpngを付けるとlibpngをリンクしてやることが可能になります。これにより自分のプログラムでpng関連の様々な関数が使用可能になります。

gcc japanese.c -o japanese.exe -lpng

エラーが表示されずコンパイルが完了すれば、libpngのインストールに成功です。

インストールがうまくできていなければ下記のようなメッセージが表示されますので再度手順を確認してみてください。

ld: library not found for -lpng

clang: error: linker command failed with exit code 1 (use -v to see invocation)

libpngを使用したプログラム例

続いて libpng を使用したプログラム例を紹介します。簡単にプログラムの説明も書いていますが、lipng関数の使い方を詳細に知りたい方はlibpngが提供する関数で紹介しているlibpngのマニュアルを読んで見てください。

PNGファイル読み込み・書き込み

まずは libpng を用いた PNG ファイルの読み込み・書き込みを行うプログラムです。

ヘッダーファイル

myPng.h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "png.h"

typedef struct{
  unsigned char *data;
  unsigned int width;
  unsigned int height;
  unsigned int ch;
} RAWDATA_t;

int pngFileReadDecode(RAWDATA_t *, const char*);
int pngFileEncodeWrite(RAWDATA_t *, const char*);
int freeRawData(RAWDATA_t *);

#define HEADER_NUM 8
ポイントだけ説明します。

・libpngの関数を使用するためにヘッダファイル

#include "png.h"

・RAW形式データの情報格納用構造体を定義

dataにはPNGをデコードしたRAW形式画像データの先頭ポインタを指すアドレス、widthには画像の横幅、heightには画像の高さ、chには画像の色数(カラーの時は3、カラーでアルファチャネル付きの場合は4)がそれぞれ格納されます。

typedef struct{
  unsigned char *data;
  unsigned int width;
  unsigned int height;
  unsigned int ch;
} RAWDATA_t;

RAW形式画像データについては下の記事で説明していますので是非参考にしてください。

画像データの構造・画素・RAWデータについて解説画像データの構造・画素・RAWデータについて解説

この記事でも出てきているようにdataのポインタの先は下図のように各ピクセルのRGB順にデータが並んでいます。

MEMO

RGB+α形式の場合は、各ピクセルがRGBα順にデータが並びます

ソースファイル

myPng.c
#include "myPng.h"

int pngFileReadDecode(RAWDATA_t *rawData, const char* filename){

  FILE *fi;
  unsigned int ch = 0;
  int a;
  int i, j, c;
  unsigned int width, height;
  unsigned int readSize;

  png_structp png;
  png_infop info;
  png_bytepp datap;
  png_byte type;
  png_byte header[8];

  fi = fopen(filename, "rb");
  if(fi == NULL){
    printf("%sは開けません\n", filename);
    return -1;
  }

  readSize = fread(header, 1, HEADER_NUM, fi);

  if(png_sig_cmp(header, 0, HEADER_NUM)){
    printf("png_sig_cmp error!\n");
    return -1;
  }

  png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  if(png == NULL){
    printf("png_create_read_struct error!\n");
    return -1;
  }

  info = png_create_info_struct(png);
  if(info == NULL){
    printf("png_crete_info_struct error!\n");
    return -1;
  }

  png_init_io(png, fi);
  png_set_sig_bytes(png, readSize);
  png_read_png(png, info, PNG_TRANSFORM_PACKING | PNG_TRANSFORM_STRIP_16, NULL);

  width = png_get_image_width(png, info);
  height = png_get_image_height(png, info);

  datap = png_get_rows(png, info);

  type = png_get_color_type(png, info);
  /* とりあえずRGBだけ対応 */
  if(type != PNG_COLOR_TYPE_RGB && type != PNG_COLOR_TYPE_RGB_ALPHA){
    printf("color type is not RGB or RGBA\n");
    return -1;
  }

  rawData->width = width;
  rawData->height = height;
  if(type == PNG_COLOR_TYPE_RGB) {
    rawData->ch = 3;
  } else if(type == PNG_COLOR_TYPE_RGBA) {
    rawData->ch = 4;
  }
  printf("width = %d, height = %d, ch = %d\n", rawData->width, rawData->height, rawData->ch);

  rawData->data =
    (unsigned char*)malloc(sizeof(unsigned char) * rawData->width * rawData->height * rawData->ch);
  if(rawData->data == NULL){
    printf("data malloc error\n");
    return -1;
  }

  for(j = 0; j < rawData->height; j++){
    memcpy(rawData->data + j * rawData->width * rawData->ch, datap[j], rawData->width * rawData->ch);
  }

  png_destroy_read_struct(&png, &info, NULL);
  fclose(fi);

  return 0;
}

int pngFileEncodeWrite(RAWDATA_t *rawData, const char *filename){
  FILE *fo;
  int j;

  png_structp png;
  png_infop info;
  png_bytepp datap;
  png_byte type;
  png_byte sig_bytes[8];

  fo = fopen(filename, "wb");
  if(fo == NULL){
    printf("%sは開けません\n", filename);
    return -1;
  }

  png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  info = png_create_info_struct(png);

  if(rawData->ch == 3) {
    type = PNG_COLOR_TYPE_RGB;
  } else if(rawData->ch == 4) {
    type = PNG_COLOR_TYPE_RGB_ALPHA;
  } else {
    printf("ch num is invalid!\n");
    return -1;
  }
  png_init_io(png, fo);
  png_set_IHDR(png, info, rawData->width, rawData->height, 8, type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);

  datap = png_malloc(png, sizeof(png_bytep) * rawData->height);

  png_set_rows(png, info, datap);

  for(j = 0; j < rawData->height; j++){
    datap[j] = png_malloc(png, rawData->width * rawData->ch);
    memcpy(datap[j], rawData->data + j * rawData->width * rawData->ch, rawData->width * rawData->ch);
  }
  png_write_png(png, info, PNG_TRANSFORM_IDENTITY, NULL);

  for(j = 0; j < rawData->height; j++){
    png_free(png, datap[j]);
  }
  png_free(png, datap);

  png_destroy_write_struct(&png, &info);
  fclose(fo);
  return 0;
}


int freeRawData(RAWDATA_t *raw){
  if(raw->data != NULL){
    free(raw->data);
    raw->data = NULL;
  }
  return 0;
}

pngFileReadDecode関数

pngFileReadDecode関数下記を行う関数です。

  • 引数で指定されたfilenameファイルの読み込みとデコードの準備のためのlibpng関数実行
  • デコード後のRAW形式画像データを格納するためにメモリ領域確保してrawDataのdataメンバにアドレスセット
  • 確保したメモリ領域にPNGを1ラインずつデコード
  • 画像データの画像の幅や高さを引数のrawDataにセット

詳細を見ていくと下記のような処理を行っています。

・ファイルがPNGかどうかのチェック

  if(png_sig_cmp(header, 0, HEADER_NUM)){
    printf("png_sig_cmp error!\n");
    return -1;
  }

・read構造体とinfo構造体の生成

  png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  if(png == NULL){
    printf("png_create_read_struct error!\n");
    return -1;
  }

  info = png_create_info_struct(png);
  if(info == NULL){
    printf("png_crete_info_struct error!\n");
    return -1;
  }

・読み込み先ファイルポインタの設定

  png_init_io(png, fi);

・すでに読み込んだバイト数を設定(PNGかどうかのチェックをする時に読み込んだサイズ)

  png_set_sig_bytes(png, readSize);

・画像の読み込みの開始

  png_read_png(png, info, PNG_TRANSFORM_PACKING | PNG_TRANSFORM_STRIP_16, NULL);

・画像情報取得

  width = png_get_image_width(png, info);
  height = png_get_image_height(png, info);
  datap = png_get_rows(png, info);
  type = png_get_color_type(png, info);

typeはPNGの画像形式。このプログラムではRGBとRGB+αのみをサポートしている。 datapはデコードされたRAWデータへのポインタとなる。

・デコード結果のメモリ領域へのコピー

  for(j = 0; j < rawData->height; j++){
    memcpy(rawData->data + j * rawData->width * rawData->ch, datap[j], rawData->width * rawData->ch);
  }

・生成した構造体の削除

  png_destroy_read_struct(&png, &info, NULL);

一応iPhoneのスクリーンショット時に作成されるPNG形式データの読み込みの動作確認はしています。

pngFileEncodeWrite関数

pngFileEncodeWrite関数下記を行う関数です。

  • 引数で指定されたfilenameファイルの作成とエンコードの準備のためのlibpng関数の実行
  • PNGのヘッダーの設定
  • 引数のrawDataのdataメンバで指定されるアドレスのデータを1ラインずつエンコードしながらファイルに書き込み

詳細を見ていくと下記のような処理を行っています。

・pngのwrite構造体とinfo構造体を生成

  png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  info = png_create_info_struct(png);

・書き込み先ファイルポインタ設定

  png_init_io(png, fo);

・エンコーダ結果の出力先ファイル設定

  jpeg_stdio_dest(&jpeg, fo);

・カラータイプ設定

エンコードする画像の情報であるRAW_DATA_t構造体のchに色数が格納されているので、色数が3の時はRGBを色数が4の時はRGB+αのカラータイプを設定。

  if(rawData->ch == 3) {
    type = PNG_COLOR_TYPE_RGB;
  } else if(rawData->ch == 4) {
    type = PNG_COLOR_TYPE_RGB_ALPHA;
  } else {
    printf("ch num is invalid!\n");
    return -1;
  }

・ヘッダ情報の設定

  png_set_IHDR(png, info, rawData->width, rawData->height, 8, type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);

・libpng用のRAWデータ格納先メモリ確保

  datap = png_malloc(png, sizeof(png_bytep) * rawData->height);

・構造体へ確保したメモリのアドレス設定

  png_set_rows(png, info, datap);

・PNGにエンコードする画像データコピー

  for(j = 0; j < rawData->height; j++){
    datap[j] = png_malloc(png, rawData->width * rawData->ch);
    memcpy(datap[j], rawData->data + j * rawData->width * rawData->ch, rawData->width * rawData->ch);
  }

・エンコード&ファイル書き込み

  png_write_png(png, info, PNG_TRANSFORM_IDENTITY, NULL);

・確保したメモリを解放

  for(j = 0; j < rawData->height; j++){
    png_free(png, datap[j]);
  }
  png_free(png, datap);

freeRawData関数

freeRawData関数は引数のrawのdataが指す先の領域を解放します。

main関数

上記で作成した関数の使い方がイメージつくように使用例を書いてみました。

main.c
#include "myPng.h"

int main(int argc, char *argv[]){

  RAWDATA_t raw;
  int i, j, c;
  int ave, sum;
  char outname[256];

  FILE *fo;

  if(argc != 2){
    printf("ファイル名が指定されていません\n");
    return -1;
  }

  if(pngFileReadDecode(&raw, argv[1]) == -1){
    printf("pngFileReadDecode error\n");
    return -1;
  }

  printf("raw->data = %p\n", raw.data);
  printf("raw->width = %d\n", raw.width);
  printf("raw->height = %d\n", raw.height);
  printf("raw->ch = %d\n", raw.ch);

  /* グレースケールに変換 */
  for(j = 0; j < raw.height; j++){
    for(i = 0; i < raw.width; i++){
      sum = 0;
      for(c = 0; c < raw.ch; c++){
        sum += raw.data[raw.ch * (i + j * raw.width) + c];
      }
      ave = sum / raw.ch;
      for(c = 0; c < raw.ch; c++){
        raw.data[raw.ch * (i + j * raw.width) + c] = ave;
      }
    }
  }

  sprintf(outname, "%s", "output.PNG");

  if(pngFileEncodeWrite(&raw, outname) == -1){
    printf("pngFileEncodeWrite error\n");
    freeRawData(&raw);
    return -1;
  }

  freeRawData(&raw);

  return 0;
}

処理の流れは下記の通りです。

  1. pngFileReadDecode関数を使用してPNGファイルの読み込みデコード
  2. 画像処理
  3. pngFileEncodeWrite()関数を使用してエンコードとPNGファイルへの書き込み
  4. freeして終了

画像処理では入力された画像をグレースケールに変換するソースコードを書いています。

画像処理をいろいろ試す場合はここをいろいろ変更して試してみると良いと思います。

  /* グレースケールに変換 */
  for(j = 0; j < raw.height; j++){
    for(i = 0; i < raw.width; i++){
      sum = 0;
      for(c = 0; c < raw.ch; c++){
        sum += raw.data[raw.ch * (i + j * raw.width) + c];
      }
      ave = sum / raw.ch;
      for(c = 0; c < raw.ch; c++){
        raw.data[raw.ch * (i + j * raw.width) + c] = ave;
      }
    }
  }

私はPNG読み込み用・書き込み用のソースファイルを”myPng.c”として分けて作成し、ヘッダーファイルを”myPng.h”としています。ですので上記のソースコードにはところどころ”myPng.h”が出てきています。違う名前にする場合は変更必要ですのでお気をつけください。このファイル名を用いている時、下記のコマンドでコンパイルとプログラムの実行が可能です。

gcc main.c -c
gcc myPng.c -c
gcc main.o myPng.o -o png.exe -lpng
./png.exe input.PNG

重要なのはコンパイル時に”-lpng“を付けてlibpngにリンクすることを忘れないようにしましょう

入力したPNGと出力結果のPNGここに表示しておきます(このファイルであれば動作確認しています)。

・入力PNG

・出力PNG

ここで載せたソースコードはあくまで一例です。PNGファイルを読み込んでデコードするあたりやPNGファイルをエンコードして書き込むあたりはカスタマイズして自分が使用しやすいように変更してください。

スポンサーリンク

libpngが提供する関数

libpngについては下記にてマニュアルが公開されています。これまた英語ですが、ファイルの読み込み方などがかなり詳しく説明されていますので、libpng使ってプログラミングしたい人は一読しておくと良いと思います。

http://www.libpng.org/pub/png/libpng-1.4.0-manual.pdf

注意

このページはlibpngインストール方法や使用例の紹介を目的としたページであり、libpngのライセンス体系については私も詳しくないのでこのページでは言及していません。libpngを使用する方は自己責任で使用してください。

例えば商用利用等を行う場合はライセンス体系についてご自分で調査してから使用の可否をご判断ください。

まとめ

このページでは PNG の読み込み・書き込みを行うライブラリである libpng のインストール方法とその使い方について解説しました。PNG は可逆圧縮フォーマットなので再保存しても劣化がありませんし、アルファチャンネルも使えるのでいろんな画像を扱った画像処理も可能です。libpng は、そんな PNG を簡単に読み込み・書き込みできるライブラリですので、是非このページを参考にしてインストールしてみてください。

コメントを残す

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