PHPを利用してサムネイルを生成するために色々調べているのだけれど、なんか、ビルトイン関数が微妙だったり、有用なモジュールが標準で使えなかったりと、やりにくい。

サムネイル生成ルーチンは出来ているんだけれど、どのくらいメモリを消費するのかの判定式がうまくかけない。つまり、imagecreatefromjpegといった関数でどのくらいメモリを消費するんだろうってところ。

これらimagecreatefromXXX関数はGDイメージを生成する。私の予想では、このGDイメージというのは無圧縮のビットマップデータで、画像の幅と高さを引数に生成される。そのバイナリのサイズは幅×高さ×ビット深度×チャンネル数(RGBなら3、YCrCbなら3、BGBAなら4、YCrCbRなら4)で表せて、それに応じたメモリ数を占有するはず。あらかじめここら辺を計算しておいて、PHPが使える余剰メモリ数との比較を取れば、メモリーオーバーすることなく処理を回せるんじゃなかろうかという青写真。

けれど、実際にやってみると壁ばっか。



最近ちょっとCをかじっているので、phpのソースからgdImageの構造体を読んでみた。なるほど、意味分からん。。。もうちょっと勉強すれば、ここらへんがわかるようになるのかしら???

/php5-5.3.2/ext/gd/libgd/gd.h
typedef struct gdImageStruct {
/* Palette-based image pixels */
unsigned char ** pixels;
int sx;
int sy;
/* These are valid in palette images only. See also
'alpha', which appears later in the structure to
preserve binary backwards compatibility */
int colorsTotal;
int red[gdMaxColors];
int green[gdMaxColors];
int blue[gdMaxColors];
int open[gdMaxColors];
/* For backwards compatibility, this is set to the
first palette entry with 100% transparency,
and is also set and reset by the
gdImageColorTransparent function. Newer
applications can allocate palette entries
with any desired level of transparency; however,
bear in mind that many viewers, notably
many web browsers, fail to implement
full alpha channel for PNG and provide
support for full opacity or transparency only. */
int transparent;
int *polyInts;
int polyAllocated;
struct gdImageStruct *brush;
struct gdImageStruct *tile;
int brushColorMap[gdMaxColors];
int tileColorMap[gdMaxColors];
int styleLength;
int stylePos;
int *style;
int interlace;
/* New in 2.0: thickness of line. Initialized to 1. */
int thick;
/* New in 2.0: alpha channel for palettes. Note that only
Macintosh Internet Explorer and (possibly) Netscape 6
really support multiple levels of transparency in
palettes, to my knowledge, as of 2/15/01. Most
common browsers will display 100% opaque and
100% transparent correctly, and do something
unpredictable and/or undesirable for levels
in between. TBB */
int alpha[gdMaxColors];
/* Truecolor flag and pixels. New 2.0 fields appear here at the
end to minimize breakage of existing object code. */
int trueColor;
int ** tpixels;
/* Should alpha channel be copied, or applied, each time a
pixel is drawn? This applies to truecolor images only.
No attempt is made to alpha-blend in palette images,
even if semitransparent palette entries exist.
To do that, build your image as a truecolor image,
then quantize down to 8 bits. */
int alphaBlendingFlag;
/* Should antialias functions be used */
int antialias;
/* Should the alpha channel of the image be saved? This affects
PNG at the moment; other future formats may also
have that capability. JPEG doesn't. */
int saveAlphaFlag;

/* 2.0.12: anti-aliased globals */
int AA;
int AA_color;
int AA_dont_blend;
unsigned char **AA_opacity;
int AA_polygon;
/* Stored and pre-computed variables for determining the perpendicular
* distance from a point to the anti-aliased line being drawn:
*/
int AAL_x1;
int AAL_y1;
int AAL_x2;
int AAL_y2;
int AAL_Bx_Ax;
int AAL_By_Ay;
int AAL_LAB_2;
float AAL_LAB;

/* 2.0.12: simple clipping rectangle. These values must be checked for safety when set; please use gdImageSetClip */
int cx1;
int cy1;
int cx2;
int cy2;
} gdImage;

んで、使うメモリ数を予想する計算をするのに、画像データからビット深度とチャンネル数を知る必要がある。getimagesize関数を使ってみたわけだが、どうもよくない。特にPNGに関してはチャンネル数が定義されてないみたいだ。

/php5-5.3.2/ext/standard/image.c
142-173行目
/* {{{ php_handle_bmp
*/
static struct gfxinfo *php_handle_bmp (php_stream * stream TSRMLS_DC)
{
struct gfxinfo *result = NULL;
unsigned char dim[16];
int size;

if (php_stream_seek(stream, 11, SEEK_CUR))
return NULL;

if (php_stream_read(stream, dim, sizeof(dim)) != sizeof(dim))
return NULL;

size = (((unsigned int)dim[ 3]) << 24) + (((unsigned int)dim[ 2]) << 16) + (((unsigned int)dim[ 1]) << 8) + ((unsigned int) dim[ 0]);
if (size == 12) {
result = (struct gfxinfo *) ecalloc (1, sizeof(struct gfxinfo));
result->width = (((unsigned int)dim[ 5]) << 8) + ((unsigned int) dim[ 4]);
result->height = (((unsigned int)dim[ 7]) << 8) + ((unsigned int) dim[ 6]);
result->bits = ((unsigned int)dim[11]);
} else if (size > 12 && (size <= 64 || size == 108)) {
result = (struct gfxinfo *) ecalloc (1, sizeof(struct gfxinfo));
result->width = (((unsigned int)dim[ 7]) << 24) + (((unsigned int)dim[ 6]) << 16) + (((unsigned int)dim[ 5]) << 8) + ((unsigned int) dim[ 4]);
result->height = (((unsigned int)dim[11]) << 24) + (((unsigned int)dim[10]) << 16) + (((unsigned int)dim[ 9]) << 8) + ((unsigned int) dim[ 8]);
result->bits = (((unsigned int)dim[15]) << 8) + ((unsigned int)dim[14]);
} else {
return NULL;
}

return result;
}
/* }}} */

98-119行目
/* {{{ php_handle_gif
* routine to handle GIF files. If only everything were that easy... ;} */
static struct gfxinfo *php_handle_gif (php_stream * stream TSRMLS_DC)
{
struct gfxinfo *result = NULL;
unsigned char dim[5];

if (php_stream_seek(stream, 3, SEEK_CUR))
return NULL;

if (php_stream_read(stream, dim, sizeof(dim)) != sizeof(dim))
return NULL;

result = (struct gfxinfo *) ecalloc(1, sizeof(struct gfxinfo));
result->width = (unsigned int)dim[0] | (((unsigned int)dim[1])<<8);
result->height = (unsigned int)dim[2] | (((unsigned int)dim[3])<<8);
result->bits = dim[4]&0x80 ? ((((unsigned int)dim[4])&0x07) + 1) : 0;
result->channels = 3; /* allways */

return result;
}
/* }}} */

/php5/php5-5.3.2/ext/standard/image.c
290-317行目
/* {{{ php_handle_png
* routine to handle PNG files */
static struct gfxinfo *php_handle_png (php_stream * stream TSRMLS_DC)
{
struct gfxinfo *result = NULL;
unsigned char dim[9];
/* Width: 4 bytes
* Height: 4 bytes
* Bit depth: 1 byte
* Color type: 1 byte
* Compression method: 1 byte
* Filter method: 1 byte
* Interlace method: 1 byte
*/

if (php_stream_seek(stream, 8, SEEK_CUR))
return NULL;

if((php_stream_read(stream, dim, sizeof(dim))) < sizeof(dim))
return NULL;

result = (struct gfxinfo *) ecalloc(1, sizeof(struct gfxinfo));
result->width = (((unsigned int)dim[0]) << 24) + (((unsigned int)dim[1]) << 16) + (((unsigned int)dim[2]) << 8) + ((unsigned int)dim[3]);
result->height = (((unsigned int)dim[4]) << 24) + (((unsigned int)dim[5]) << 16) + (((unsigned int)dim[6]) << 8) + ((unsigned int)dim[7]);
result->bits = (unsigned int)dim[8];
return result;
}
/* }}} */

473-560行目
/* {{{ php_handle_jpeg
main loop to parse JPEG structure */
static struct gfxinfo *php_handle_jpeg (php_stream * stream, zval *info TSRMLS_DC)
{
struct gfxinfo *result = NULL;
unsigned int marker = M_PSEUDO;
unsigned short length, ff_read=1;

for (;;) {
marker = php_next_marker(stream, marker, 1, ff_read TSRMLS_CC);
ff_read = 0;
switch (marker) {
case M_SOF0:
case M_SOF1:
case M_SOF2:
case M_SOF3:
case M_SOF5:
case M_SOF6:
case M_SOF7:
case M_SOF9:
case M_SOF10:
case M_SOF11:
case M_SOF13:
case M_SOF14:
case M_SOF15:
if (result == NULL) {
/* handle SOFn block */
result = (struct gfxinfo *) ecalloc(1, sizeof(struct gfxinfo));
length = php_read2(stream TSRMLS_CC);
result->bits = php_stream_getc(stream);
result->height = php_read2(stream TSRMLS_CC);
result->width = php_read2(stream TSRMLS_CC);
result->channels = php_stream_getc(stream);
if (!info || length < 8) { /* if we don't want an extanded info -> return */
return result;
}
if (php_stream_seek(stream, length - 8, SEEK_CUR)) { /* file error after info */
return result;
}
} else {
if (!php_skip_variable(stream TSRMLS_CC)) {
return result;
}
}
break;

case M_APP0:
case M_APP1:
case M_APP2:
case M_APP3:
case M_APP4:
case M_APP5:
case M_APP6:
case M_APP7:
case M_APP8:
case M_APP9:
case M_APP10:
case M_APP11:
case M_APP12:
case M_APP13:
case M_APP14:
case M_APP15:
if (info) {
if (!php_read_APP(stream, marker, info TSRMLS_CC)) { /* read all the app markes... */
return result;
}
} else {
if (!php_skip_variable(stream TSRMLS_CC)) {
return result;
}
}
break;

case M_SOS:
case M_EOI:
return result; /* we're about to hit image data, or are at EOF. stop processing. */

default:
if (!php_skip_variable(stream TSRMLS_CC)) { /* anything else isn't interesting */
return result;
}
break;
}
}

return result; /* perhaps image broken -> no info but size */
}
/* }}} */

もっと新しいソースを読めばいいんだろうか???ううむ。というわけで頓挫。