그래픽 & 멀티미디어
장길석씨가 소개해 주신 PNG파일 출력 샘플을 보고 작성해 봤습니다.
PNG와 JPEG을 처리하는 TImage란 구조체를 만들었습니다.
TBitmap를 인헤릿한 것인데 PNG와 JPEG출력에 필요한 data를 뒤에 더 추가할 수 있습니다. syntax는 gxBmpOpen과 비슷한 gxJpegOpen과 gxPngOpen함수를 만들어 TBitmap*를 return 받도록 하였습니다.
파일에서 JPEG과 PNG를 읽은 후에는 Bitmap으로 전환된다는 기본적인 생각으로 만들었습니다.
JPEG라이브러리는 포함시키지 않았습니다만 아마도 많이들 가지고 계신 라이브러리 일 것입니다.
b_24_SetPixel이란 함수와 b_24_Color란 함수도 수정하였습니다.
저는 윈도우즈에서 gx라이브러리를 emul하여 test해 보았으므로 Big Endian Litte Endian문제가 있을 수 있습니다.
또다시 라이브러리가 바뀌었더라고요.
제가 Editing한 라이브러리는 png샘플의 것이었습니다. 아마도 그에 따라 추가적인 Editing이 필요할 것이라고 생각됩니다. 그럼 보시고 한번 검토해 주시길 바랍니다.
파일이 어태치가 안되네요.
계략적인 내용을 copy합니다.
typedef struct TImage__ TImage;
struct TImage__
{
char DcType; // DC의 형태로 Screen, Bitmap을 구분한다.
int Width; // 도트 단위의 폭
int Height; // 도트 단위의 높이
int Dots; // 전체 도트 갯수 width * height
int Bytes; // 메모리의 전체 Byte 크기
int Colors; // 칼라 깊이
int BytesPerLine; // 라인당 바이트 개수
int BitsPerPixel; // 비트당 픽셀 개수
int xCoor; // 이전에 그리기 했던 마지막 좌표
int yCoor; // 이전에 그리기 했던 마지막 좌표
int PenColor; // 현재의 펜 칼라
int BrushColor; // 현재의 브러쉬 칼라
void *Mapped; // 메모리 매핑된 포인터
void (*ReleaseDC)( Tdc *dc); // Device Context 소멸 및 관련 메모리를 삭제
int (*Color )( int Red, int Green, int Blue); // 칼라값 구하기
void (*Clear )( TBitmap *bmp, int Color); // 색으로 전체 칠하기
void (*GetPixel)( TBitmap *bmp, int xCoor, int yCoor, TColor *Color); // 칼라 값을 읽어 오기
void (*SetPixel)( TBitmap *bmp, int xCoor, int yCoor, int Color ); // 점 찍기
void (*hLine )( TBitmap *bmp, int x1st , int x2nd , int yCoor, int Color); // 수평선 긋기
void (*vLine )( TBitmap *bmp, int xCoor, int y1st , int y2nd , int Color); // 수직선 긋기
int ErrorCode; // 에러 코드
int FileSize; // ^ 이미지 파일 사이즈
int Reserved; // | 예약 영역
int DataOffset; // |
int HeaderSize; // |
int imgWidth; // | 이미지 폭
int imgHeight; // | 이미지 높이
short cntPlanes; // | bmp 헤더 영역 52 bytes
short bpp; // |
int Compression; // | 압축 형식
int BitmapSize; // |
int hRes; // |
int vRes; // |
int cntColors; // |
int ImportantColors; // v
int cntPalette; // 팔레트 개수
TPalette *Palette; // 팔레트 칼라 정보
unsigned char *Data; // 이미지 영역의 포인터
unsigned char *EncodedData;
unsigned bSizeBlue , bSizeGreen , bSizeRed; // R,G,B 각 색상별 비트 크기
unsigned bOffsetBlue, bOffsetGreen, bOffsetRed; // R,G,B 각 색상별 값을 구하기 위한 쉬프트 횟수
};
//extern TBitmap* gxJpegOpen( char *FileName);
extern TBitmap* gxPngOpen( char *FileName);
TBitmap* gxPngOpen( char* FileName )
{
Iterator iter;
TImage* img;
FILE *infile;
unsigned char sig[8];
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
png_uint_32 ihdr_width, ihdr_height;
int width, height ,row;
int bit_depth, color_type;
png_color_16p pBackground;
unsigned char bg_red =0, bg_green=0, bg_blue=0;
unsigned char *image_data = NULL;
int image_channels, image_rowbytes;
double display_exponent = 1.0 * 2.2;
double gamma;
png_uint_32 rowbytes;
png_bytepp row_pointers = NULL;
int i;
if ( NULL == ( img = malloc( sizeof( TImage))) ) return NULL;
memset( img, 0, sizeof( TImage));
memset( &iter, 0x00, sizeof(Iterator));
iter.ima = img;
infile = fopen( FileName, "rb");
fread(sig, 1, 8, infile);
if ( !png_check_sig(sig, 8))
return NULL;
png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); // 사용자 에러 정의 핸들러를 모두 NULL로 설정
if ( !png_ptr)
return NULL;
info_ptr = png_create_info_struct( png_ptr);
if(!info_ptr)
{
png_destroy_read_struct(&png_ptr,
(png_infopp)NULL, (png_infopp)NULL);
return NULL;
}
if(setjmp(png_jmpbuf(png_ptr)))
{
png_destroy_read_struct( &png_ptr, &info_ptr, NULL);
return NULL;
}
png_init_io( png_ptr, infile);
png_set_sig_bytes( png_ptr, 8); // we already read the 8 signature bytes
png_read_info( png_ptr, info_ptr); // read all PNG info up to image data
/* alternatively, could make separate calls to png_get_image_width(),
* etc., but want bit_depth and color_type for later [don't care about
* compression_type and filter_type => NULLs] */
png_get_IHDR(png_ptr, info_ptr, &ihdr_width, &ihdr_height, &bit_depth, &color_type, NULL, NULL, NULL);
width = ihdr_width;
height = ihdr_height;
gxGivenBmpCreate( (TBitmap*) img, ihdr_width, ihdr_height, bit_depth*3, 0 );
//Create(info_ptr->width, info_ptr->height, pixel_depth,
// info_ptr->color_type);
png_get_bKGD(png_ptr, info_ptr, &pBackground);
/* however, it always returns the raw bKGD data, regardless of any
* bit-depth transformations, so check depth and adjust if necessary */
if ( bit_depth == 16)
{
bg_red = pBackground->red >> 8;
bg_green = pBackground->green >> 8;
bg_blue = pBackground->blue >> 8;
}
else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
{
if (bit_depth == 1)
bg_red = bg_green = bg_blue = pBackground->gray? 255 : 0;
else if (bit_depth == 2)
bg_red = bg_green = bg_blue = (255/3) * pBackground->gray;
else /* bit_depth == 4 */
bg_red = bg_green = bg_blue = (255/15) * pBackground->gray;
}
else
{
bg_red = (unsigned char)pBackground->red;
bg_green = (unsigned char)pBackground->green;
bg_blue = (unsigned char)pBackground->blue;
}
if(setjmp(png_jmpbuf(png_ptr)))
{
png_destroy_read_struct( &png_ptr, &info_ptr, NULL);
}
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_expand(png_ptr);
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
png_set_expand(png_ptr);
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
png_set_expand(png_ptr);
if (bit_depth == 16)
png_set_strip_16(png_ptr);
if (color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png_ptr);
/* unlike the example in the libpng documentation, we have *no* idea where
* this file may have come from--so if it doesn't have a file gamma, don't
* do any correction ("do no harm") */
if (png_get_gAMA(png_ptr, info_ptr, &gamma))
png_set_gamma(png_ptr, display_exponent, gamma);
/* all transformations have been registered; now update info_ptr data,
* get rowbytes and channels, and allocate image memory */
png_read_update_info(png_ptr, info_ptr);
image_rowbytes = rowbytes = png_get_rowbytes(png_ptr, info_ptr);
image_channels = (int)png_get_channels(png_ptr, info_ptr);
if ( (image_data = (unsigned char *)malloc(rowbytes*height)) == NULL)
{
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return NULL;
}
if ( (row_pointers = (png_bytepp)malloc(height*sizeof( png_bytep))) == NULL)
{
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
free(image_data);
return NULL;
}
for (i = 0; i < height; i++)
row_pointers[i] = image_data + i*rowbytes;
/* now we can go ahead and just read the whole image */
png_read_image( png_ptr, row_pointers);
{
ulg red, green, blue;
ulg r, g, b, a;
int row;
int clrPixel;
uch *src;
for ( row = 0; row < height; ++row)
{
src = image_data + row*image_rowbytes;
if ( image_channels == 3)
{
for (i = 0; i < width; i++)
{
red = *src++;
green = *src++;
blue = *src++;
clrPixel = gxColor( img, red, green, blue);
gxSetPixel( img, i, row, clrPixel);
}
}
else /* if (image_channels == 4) */
{
for (i = 0; i < width; i++)
{
r = *src++;
g = *src++;
b = *src++;
a = *src++;
if (a == 255)
{
red = r;
green = g;
blue = b;
clrPixel = img->Color( red, green, blue);
img->SetPixel( img, i, row, clrPixel);
} else if (a == 0) { // 투명인 부분
/* red = bg_red;
C green = bg_green;
* blue = bg_blue;
*/
} else {
/* / * this macro (from png.h) composites the foreground
* * and background values and puts the result into the
* * first argument * /
D alpha_composite(red, r, a, bg_red);
* alpha_composite(green, g, a, bg_green);
* alpha_composite(blue, b, a, bg_blue);
*/
}
}
}
}
}
/*
IterUpset(&iter);
row = height-1;
while (row >=0 ) {
IterSetRow( &iter, row_pointers[row], rowbytes );
IterPrevRow( &iter );
row--;
}
*/
free( row_pointers);
row_pointers = NULL;
png_read_end( png_ptr, NULL);
// 화면 출력 끝
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
free(image_data);
/* close the file */
fclose(infile);
return (TBitmap*) img;
}
/*
void TestJpegToDC( Tdc *dc, // 출력 대상 화면 DC
char *bmpFileName // 출력할 Bitmap
)
{
TImage *bmp; // Bitmap DC
bmp = gxJpegOpen( bmpFileName); // 비트맵 파일 읽기
gxBitBlt( dc, 0, 0, (Tdc *)bmp, 0, 0, bmp->Width, bmp->Height); // 바로 화면에 출력
gxBmpClose( bmp); // 비트맵 파일 사용 종료
}
*/
void TestPngToDC( Tdc *dc, // 출력 대상 화면 DC
char *bmpFileName // 출력할 Bitmap
)
{
TImage *bmp; // Bitmap DC
bmp = gxPngOpen( bmpFileName); // 비트맵 파일 읽기
gxBitBlt( dc, 0, 0, (Tdc *)bmp, 0, 0, bmp->Width, bmp->Height); // 바로 화면에 출력
gxBmpClose( bmp); // 비트맵 파일 사용 종료
}
TBitmap *gxGivenBmpCreate( TBitmap* bmp , int Width, int Height, int Depth, int PaletteSize)
{
SetHeader( bmp, Width, Height, Depth, PaletteSize);
if ( NULL == ( bmp->Data = malloc( bmp->BitmapSize)) )
{
return NULL;
}
return SetupBitmap( bmp);
}
static void b24_SetPixel( TBitmap *bmp, int xCoor, int yCoor, int Color)
{
unsigned char *pData;
int offset = ( bmp->Height -yCoor -1) * bmp->BytesPerLine + xCoor * 3;
pData = bmp->Data + offset;
*(pData+3) = (Color&0xFF000000)>>24;
*(pData+2) = (Color&0x00FF0000)>>16;
*(pData+1) = (Color&0x0000FF00)>>8;
*(pData+0) = (Color&0x000000FF);
}
static int b24_Color( int Red, // 0부터 255 사이의 Red 값
int Green, // 0부터 255 사이의 Green 값
int Blue // 0부터 255 사이의 Blue 값
)
// 설명: 칼라 값을 구한다.
{
return (Red << 16) | (Green << 8) | Blue;
}
/*
TBitmap *gxJpegOpen( char *FileName)
{
TImage *img;
Iterator iter;
struct jpeg_decompress_struct cinfo;
struct ima_error_mgr jerr;
FILE * infile;
JSAMPARRAY buffer;
int row_stride;
if ( NULL == ( img = malloc( sizeof( TImage))) ) return NULL;
memset( img, 0, sizeof( TImage));
memset( &iter, 0x00, sizeof(Iterator));
iter.ima = img;
// struct jpeg_error_mgr jerr;
if ((infile = fopen((const char *)FileName, "rb")) == NULL) {
return 0;
}
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = ima_jpeg_error_exit;
if (setjmp(jerr.setjmp_buffer)) {
jpeg_destroy_decompress(&cinfo);
fclose(infile);
return 0;
}
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, infile);
(void) jpeg_read_header(&cinfo, TRUE);
if (cinfo.jpeg_color_space!=JCS_GRAYSCALE) {
cinfo.quantize_colors = TRUE;
cinfo.desired_number_of_colors = 128;
}
jpeg_start_decompress(&cinfo);
if (cinfo.jpeg_color_space==JCS_GRAYSCALE)
{
gxGivenBmpCreate( (TBitmap*) img, cinfo.image_width, cinfo.image_height, 8*cinfo.output_components, 256);
CreateGrayColourMap( img->Palette ,256);
}
else
{
gxGivenBmpCreate( (TBitmap*)img, cinfo.image_width, cinfo.image_height, 8*cinfo.output_components, cinfo.actual_number_of_colors);
SetPalette( img->Palette, cinfo.actual_number_of_colors, cinfo.colormap[0], cinfo.colormap[1], cinfo.colormap[2]);
}
row_stride = cinfo.output_width * cinfo.output_components;
buffer = (*cinfo.mem->alloc_sarray)
((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
IterUpset(&iter);
while (cinfo.output_scanline < cinfo.output_height) {
(void) jpeg_read_scanlines(&cinfo, buffer, 1);
IterSetRow( &iter, buffer[0], row_stride);
IterPrevRow( &iter );
}
(void) jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
fclose(infile);
return (TBitmap*)img;
}
*/
압축한 소스파일을 업로드가 할 수 없네요. 정책적인 것인가요?
그럼 검토해 보시고 의견 주시길 바랍니다.
-->파일 첨부 버튼을 눌러도 아무런 반응이 없습니다.... ~~;;
파일이 업로드되는 동안 잠시만 기다려 주십시오.
따로 대화상자없이 업로드하기 때문에,
업로드 완료 전에 글을 저장하면 파일이 사라지더군요.
제로보드 XE가 예전 4 보다 화려해 졌지만,
깔끔한 맛이 없어진 것 같아요. ^^;
그리고 소스가 공개되어 공부하게 되는 기회가 되어서 좋습니다.
앞으로도 정책이 바뀌지 않았으면 좋겠습니다.^^
제가 글을 잘 쓰지 못하고 빨리 공유하는 편이 낫다고 생각되어
두서없이 글을 올려서 죄송합니다.....
comment를 많이 달아야 하는데 ...시간이 없어서..
죄송합니다.
그야말로 gxLib의 일등공신이세요. 진심입니다.
아울러 앞으로 fee 정신을 잃지 않을 테니, 계속 관심을 가져 주시기를 간곡히 부탁드립니다.
죄송하다니요, 절대 아닙니다.
푸른수염님께서 고심하신 부분을 참고하고 제가 고심했던 부분을 말씀 드려서
gxLib를 정말 쓸만한 그래픽 라이브러리를 만들어 갔으면 좋겠습니다.
아울러 그래픽 라이브러리가 필요하시거나 관심있는 분의 많은 참여를 부탁드립니다.
포럼인 만큼 제 소스와 푸른 수염님의 소스를 함께 비교하면서 학습할 수 있는
좋은 기회가 되리라고 생각합니다.
PNG를 추가한 gxLib를 올리지 않았던 이유는 gxLib에 수정이 많았던 점도 있지만
앞으로 gxLib가 계속 커갈 것이고, 다른 라이브러리도 제공되어야 하기 때문에
미리 "포럼 표준 코딩"을 준비하고 있었기 때문입니다.
표준 코딩에 대한 글을 올린 후에 PNG를 출력하는 gxLib를 올리겠습니다.
이렇게 gxLib에 관심을 많이 가져 주셔서 감사합니다. ^^