--- /dev/null
+#ifndef MEMCURSOR_H
+#define MEMCURSOR_H
+
+class MemCursor {
+ public:
+ uiterator b;
+ uiterator e;
+ int bitOffset;
+ uint32_t bitPool;
+
+ MemCursor (const ustring* data) {
+ b = data->begin ();
+ e = data->end ();
+ };
+ virtual ~MemCursor () {};
+
+ virtual bool empty () {
+ return b >= e;
+ };
+ virtual ssize_t size () {
+ return e - b;
+ };
+// virtual bool cap (size_t size) {
+// return b + size <= e;
+// };
+ virtual uint32_t ui8 () {
+ uint8_t ans = *b;
+ ++ b;
+ return ans;
+ };
+ virtual uint32_t ui16n () {
+ return ui8 () * 256 + ui8 ();
+ };
+ virtual uint32_t ui16 () {
+ return ui8 () + ui8 () * 256;
+ };
+ virtual uint32_t ui32n () {
+ return ((ui8 () * 256 + ui8 ()) * 256 + ui8 ()) * 256 + ui8 ();
+ };
+// virtual uint32_t ui32 ();
+// virtual int32_t si16 ();
+// virtual int32_t si32 ();
+// virtual ustring block (size_t len);
+// virtual ustring block ();
+ virtual void skip (size_t len) {
+ b += len;
+ };
+// virtual ustring cstring ();
+// virtual ustring pstring ();
+ virtual bool matchSkip (const char* t, size_t s) {
+ return ::matchSkip (b, e, t, s);
+ };
+ virtual void initBit () {
+ bitOffset = 0;
+ };
+ virtual uint32_t ub (int n) {
+ uint32_t ans = 0;
+ int nc = n;
+
+ while (nc > 0) {
+ if (bitOffset == 0) {
+ bitPool = ui8 ();
+ bitOffset = 8;
+ }
+ if (nc >= bitOffset) {
+ nc -= bitOffset;
+ ans |= bitPool << nc;
+ bitOffset = 0;
+ } else {
+ bitOffset -= nc;
+ ans |= bitPool >> bitOffset;
+ bitPool &= (1 << bitOffset) - 1;
+ nc = 0;
+ }
+ }
+ return ans;
+ };
+ virtual int32_t sb (int n) {
+ int32_t ans = 0;
+
+ if (n > 0) {
+ if (ub (1)) {
+ ans = ub (n - 1) - (1L << (n - 1));
+ } else {
+ ans = ub (n - 1);
+ }
+ } else {
+ ans = 0;
+ }
+ return ans;
+ };
+};
+
+#endif /* MEMCURSOR_H */
--- /dev/null
+#include "ml-imagesize.h"
+#include "ml-store.h"
+#include "ml.h"
+#include "mlenv.h"
+#include "expr.h"
+#include "util_string.h"
+#include "util_const.h"
+#include "memcursor.h"
+#include "ustring.h"
+#include "filemacro.h"
+
+/*DOC:
+==image size function==
+画像ファイルのサイズを取得する。
+
+*/
+
+static bool gifSize (ustring* data, size_t& x, size_t& y) {
+ MemCursor csr (data);
+
+ csr.skip (6);
+// if (csr.cap (4)) {
+ if (csr.size () >= 4) {
+ x = csr.ui16 ();
+ y = csr.ui16 ();
+ return true;
+ }
+ return false;
+}
+
+static bool jpegSize (ustring* data, size_t& x, size_t& y) {
+ MemCursor csr (data);
+ int n;
+ uint32_t marker, code;
+
+ csr.skip (2);
+#ifdef MSJFIFHACK
+ if (matchSkip (b, e, CharConst ("\xff\xfe"))) { // JPEG_COM
+ n = readWord (b, e);
+ b += n;
+ }
+#endif
+ while (! csr.empty ()) {
+ marker = csr.ui8 ();
+ code = csr.ui8 ();
+ while (code == 0xff) {
+ code = csr.ui8 ();
+ }
+ n = csr.ui16n ();
+ if (marker != 0xff || csr.empty ())
+ return false;
+ if (0xc0 <= code && code <= 0xc3) {
+ csr.ui8 ();
+ y = csr.ui16n ();
+ x = csr.ui16n ();
+ return true;
+ }
+ csr.skip (n - 2);
+ }
+}
+
+static bool pngSize (ustring* data, size_t& x, size_t& y) {
+ MemCursor csr (data);
+
+ csr.skip (12);
+ if (csr.matchSkip (CharConst ("IHDR"))) {
+// if (csr.cap (8)) {
+ if (csr.size () >= 8) {
+ x = csr.ui32n ();
+ y = csr.ui32n ();
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool swfSize (ustring* data, size_t& x, size_t& y) {
+ MemCursor csr (data);
+ int n;
+ int minx, maxx, miny, maxy;
+
+ csr.b += 8;
+ csr.initBit ();
+ n = csr.ub (5);
+ minx = csr.sb (n);
+ maxx = csr.sb (n);
+ miny = csr.sb (n);
+ maxy = csr.sb (n);
+ if (! csr.empty ()) {
+ x = (maxx - minx) / 20. + 0.5;
+ y = (maxy - miny) / 20. + 0.5;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static bool imageSize (ustring* data, size_t& x, size_t& y, ustring& type) {
+ if (matchHead (*data, CharConst ("GIF87a"))) {
+ type.assign (CharConst ("gif"));
+ return gifSize (data, x, y);
+ } else if (matchHead (*data, CharConst ("GIF89a"))) {
+ type.assign (CharConst ("gif"));
+ return gifSize (data, x, y);
+ } else if (matchHead (*data, CharConst ("\xFF\xD8"))) {
+ type.assign (CharConst ("jpeg"));
+ return jpegSize (data, x, y);
+ } else if (matchHead (*data, CharConst ("\x89PNG\x0d\x0a\x1a\x0a"))) {
+ type.assign (CharConst ("png"));
+ return pngSize (data, x, y);
+#if 0
+ } else if (matchHead (*data, CharConst ("MM\x00\x2a"))) {
+ type.assign (CharConst ("tiff"));
+ return tiffSize (data, x, y);
+ } else if (matchHead (*data, CharConst ("II\x2a\x00"))) {
+ type.assign (CharConst ("tiff"));
+ return tiffSize (data, x, y);
+ } else if (matchHead (*data, CharConst ("BM"))) {
+ type.assign (CharConst ("bmp"));
+ return bmpSize (data, x, y);
+#endif
+ } else if (matchHead (*data, CharConst ("FWS"))) {
+ type.assign (CharConst ("swf"));
+ return swfSize (data, x, y);
+#if 0
+ } else if (matchHead (*data, CharConst ("CWS"))) {
+ type.assign (CharConst ("swf"));
+ return cswfSize (data, x, y);
+#endif
+ } else {
+ type = uEmpty;
+ return false;
+ }
+}
+
+/*DOC:
+===image-size===
+ (image-size FILENAME [#serial | #named | #static | :serial BOOL | :named BOOL | :static BOOL]) -> (WIDTH HEIGHT TYPE) or NIL
+
+TYPEは、gif, jpeg, png, swf。
+
+*/
+//#AFUNC image-size ml_image_size
+MNode* ml_image_size (MNode* cell, MlEnv* mlenv) {
+ MNode* arg = cell->cdr ();
+ ustring filename;
+ StoreType storetype;
+ ustring src;
+ std::vector<MNode*> params;
+ std::vector<MNode*> keywords;
+ static paramList kwlist[] = {
+ {CharConst ("serial"), true},
+ {CharConst ("named"), true},
+ {CharConst ("static"), true},
+ {NULL, 0, 0}
+ };
+
+ setParams (arg, 1, ¶ms, kwlist, &keywords, NULL);
+ filename = eval_str (params[0], mlenv);
+ if (keywords[0] && eval_bool (keywords[0], mlenv)) // serial
+ storetype.setSerial ();
+ if (keywords[1] && eval_bool (keywords[1], mlenv)) // named
+ storetype.setNamed ();
+ if (keywords[2] && eval_bool (keywords[2], mlenv)) // static
+ storetype.setStatic ();
+ src = storetype.src (mlenv, filename);
+
+ if (src.length () > 0) {
+ FileMacro f;
+ ustring data;
+ size_t x, y;
+ ustring type;
+
+ if (f.openRead (src.c_str ())) {
+ data.resize (32768);
+ data.resize (f.read ((char*)data.data (), 32768));
+ if (imageSize (&data, x, y, type)) {
+ MNodeList ans;
+ ans.append (newMNode_num (x));
+ ans.append (newMNode_num (y));
+ ans.append (newMNode_str (new ustring (type)));
+ return ans.release ();
+ }
+ }
+ }
+ return NULL;
+}